訓練
模組
使用 Windows PowerShell 建立和管理背景工作 - Training
本課程模組說明三種類型的作業:本機作業、Windows PowerShell 遠端作業以及一般資訊模型 (CIM)/Windows Management Instrumentation (WMI) 作業。 這些作業類型構成 Windows PowerShell 作業系統的基礎。
從 .NET 程式呼叫 BITS COM 類別的其中一種方法,就是使用 WINDOWS SDK 中的 BITS IDL (介面定義語言) 檔案,使用 MIDL 和 TLBIMP 工具,建立參考 DLL 檔案。 參考 DLL 是一組用於 BITS COM 類別的類別包裝器; 然後,您可以直接從 .NET 使用這些包裝器類別。
使用自動建立參考 DLL 的另一個選擇是使用來自 GitHub 和 NuGet 的第三方 .NET BITS 包裝器。 這些包裝函式通常具有更自然的 .NET 程式設計樣式,但它們可能會落後於BITS介面中的變更和更新。
您將從一組 BITS IDL 檔案開始。 這些是完整定義 BITS COM 介面的檔案。 這些檔案位於 Windows Kits 目錄中,且名稱為 bits版本.idl(例如,bits10_2.idl),但 1.0 版檔案只是 Bits.idl。 建立新版本的 BITS 時,也會建立新的 BITS IDL 檔案。
您也可以修改 SDK BITS IDL 檔案的複本,以使用不會自動轉換成 .NET 對等專案的 BITS 功能。 稍後會討論可能的IDL檔案變更。
BITS IDL 檔案會依參考包含數個其他IDL檔案。 它們也會巢狀,因此如果您使用一個版本,它就會包含所有較低的版本。
針對您想要在程式中作為目標的每個 BITS 版本,您將需要該版本的一個參考 DLL。 例如,如果您想要撰寫可在BITS 1.5和更新上運作的程式,但在BITS 10.2存在時具有其他功能,則必須同時轉換bits1_5.idl和 bits10_2.idl 檔案。
MIDL (Microsoft 介面定義語言) 公用程式會將描述 BITS COM 介面的 IDL 檔案轉換成 TLB (類型連結庫) 檔案。 MIDL 工具取決於 CL 公用程式(C 預處理器)正確讀取 IDL 語言檔案。 CL 公用程式是 Visual Studio 的一部分,且會在 Visual Studio 安裝中包含 C/C++ 功能時安裝。
MIDL 公用程式通常會建立一組 C 和 H (C 語言代碼和 C 語言頭) 檔案。 您可以將輸出傳送至 NUL: 裝置,以隱藏這些額外的檔案。 例如,設定 /dlldata NUL: 參數將會抑制建立 dlldata.c 檔案。 下列範例命令顯示哪些參數應設定為 NUL:。
TLBIMP (類型連結庫匯入工具) 公用程式會在 TLB 檔案中讀取,並建立對應的參考 DLL 檔案。
這是用來產生一組參考檔的完整命令範例。 您可能需要根據 Visual Studio 和 Windows SDK 安裝,以及根據您的目標 BITS 功能和 OS 版本來修改命令。
此範例會建立目錄來放置參考 DLL 檔案,並建立環境變數 BITSTEMP 以指向該目錄。
然後,範例命令會執行 Visual Studio 安裝程式所建立的 vsdevcmd.bat 檔案。 此 BAT 檔案會設定您的路徑和一些環境變數,以便執行 MIDL 和 TLBIMP 命令。 它也會設定 WindowsSdkDir 和 WindowsSDKLibVersion 變數,以指向最新的 Windows SDK 目錄。
REM Create a working directory
REM You can select a different directory based on your needs.
SET BITSTEMP=C:\BITSTEMPDIR
MKDIR "%BITSTEMP%"
REM Run the VsDevCmd.bat file to locate the Windows
REM SDK directory and the tools directories
REM This will be different for different versions of
REM Visual Studio
CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\vsdevcmd.bat"
REM Run the MIDL command on the desired BITS IDL file
REM This will generate a TLB file for the TLBIMP command
REM The IDL file will be different depending on which
REM set of BITS interfaces you need to use.
REM Run the MIDL command once per reference file
REM that you will need to explicitly use.
PUSHD .
CD /D "%WindowsSdkDir%Include\%WindowsSDKLibVersion%um"
MIDL /I ..\shared /out "%BITSTEMP%" bits1_5.idl /dlldata NUL: /header NUL: /iid NUL: /proxy NUL:
MIDL /I ..\shared /out "%BITSTEMP%" bits4_0.idl /dlldata NUL: /header NUL: /iid NUL: /proxy NUL:
MIDL /I ..\shared /out "%BITSTEMP%" bits5_0.idl /dlldata NUL: /header NUL: /iid NUL: /proxy NUL:
MIDL /I ..\shared /out "%BITSTEMP%" bits10_1.idl /dlldata NUL: /header NUL: /iid NUL: /proxy NUL:
MIDL /I ..\shared /out "%BITSTEMP%" bits10_2.idl /dlldata NUL: /header NUL: /iid NUL: /proxy NUL:
REM Run the TLBIMP command on the resulting TLB file(s)
REM Try to keep a parallel set of names.
TLBIMP "%BITSTEMP%"\bits1_5.tlb /out: "%BITSTEMP%"\BITSReference1_5.dll
TLBIMP "%BITSTEMP%"\bits4_0.tlb /out: "%BITSTEMP%"\BITSReference4_0.dll
TLBIMP "%BITSTEMP%"\bits5_0.tlb /out: "%BITSTEMP%"\BITSReference5_0.dll
TLBIMP "%BITSTEMP%"\bits10_1.tlb /out: "%BITSTEMP%"\BITSReference10_1.dll
TLBIMP "%BITSTEMP%"\bits10_2.tlb /out: "%BITSTEMP%"\BITSReference10_2.dll
DEL "%BITSTEMP%"\bits*.tlb
POPD
執行這些命令之後,您會在 BITSTEMP 目錄中有一組參考 DLL。
若要在 C# 專案中使用參考 DLL,請在 Visual Studio 中開啟您的 C# 專案。 在方案總管中,以滑鼠右鍵按兩下 [參考],然後按兩下 [新增參考]。 然後按下 [瀏覽] 按鈕,然後按下 [新增] 按鈕。 流覽至具有參考 DLL 的目錄,選取它們,然後按兩下[新增]。 在 [參考管理員] 視窗中,將會檢查參考 DLL。 然後按兩下 [確定]。
您的專案現在已新增 BITS 參考 DLL。
參考 DLL 檔案中的資訊會內嵌至最終程式。 您不需要向程式寄送參考 DLL 檔案;您只需要寄送.EXE。
您可以變更參考 DLL 是否內嵌至最終 EXE。 使用 內嵌 Interop 類型 屬性來設定是否要內嵌參考 DLL。 這可以依據每個參考來完成。 預設值為 True 以內嵌 DLL。
BITS IDL(Microsoft介面定義語言)檔案可以不更改地用於製作 BackgroundCopyManager DLL 檔案。 不過,產生的 .NET 參考 DLL 將會遺漏一些無法轉譯的聯合體,而且某些結構和列舉具有不易使用的名稱。 本節將說明一些您可以進行的變更,讓 .NET DLL 更完整且更容易使用。
BITS IDL 檔案通常會定義如下的列舉值:
typedef enum
{
BG_AUTH_TARGET_SERVER = 1,
BG_AUTH_TARGET_PROXY
} BG_AUTH_TARGET;
BG_AUTH_TARGET 是 typedef 的名稱;枚舉本身並未被命名。 這通常不會造成 C 程式代碼的問題,但不適用於 .NET 程式。 系統會自動建立新的名稱,但看起來可能像是_MIDL___MIDL_itf_bits4_0_0005_0001_0001,而不是人類可讀取的值。 您可以藉由更新 MIDL 檔案,使其包含列舉名稱,來修正此問題。
typedef enum BG_AUTH_TARGET
{
BG_AUTH_TARGET_SERVER = 1,
BG_AUTH_TARGET_PROXY
} BG_AUTH_TARGET;
允許列舉名稱可以與 typedef 名稱相同。 有些開發人員有命名慣例,使名稱保持不同(例如,在列舉名稱前加上底線),但這只會讓 .NET 的翻譯過程感到混淆。
BITS IDL 檔案會使用 LPWSTR 約定來傳遞字串(寬字元字串的長指標)。 雖然這會在傳遞函式參數時運作(例如 Job.GetDisplayName([out] LPWSTR *pVal) 方法),但是當字元串是聯集的一部分時,它將無法運作。 例如,bits5_0.idl 檔案中包含 BITS_FILE_PROPERTY_VALUE 聯合。
typedef [switch_type(BITS_FILE_PROPERTY_ID)] union
{
[case( BITS_FILE_PROPERTY_ID_HTTP_RESPONSE_HEADERS )]
LPWSTR String;
}
BITS_FILE_PROPERTY_VALUE;
LPWSTR 欄位不會包含在共用體的 .NET 版本中。 若要修正此問題,請將 LPWSTR 變更為 WCHAR*。 產生的欄位 (稱為 String) 會以 IntPtr 的形式傳遞。 使用 System.Runtime.InteropServices.Marshal.PtrToStringAuto(value.String) 方法將此轉換為字串。
有時候內嵌在結構中的工會可能根本不會被納入該結構中。 例如,在 Bits1_5.idl 中,BG_AUTH_CREDENTIALS的定義如下:
typedef struct
{
BG_AUTH_TARGET Target;
BG_AUTH_SCHEME Scheme;
[switch_is(Scheme)] BG_AUTH_CREDENTIALS_UNION Credentials;
}
BG_AUTH_CREDENTIALS;
BG_AUTH_CREDENTIALS_UNION 被定義為一個聯合體,如下所示:
typedef [switch_type(BG_AUTH_SCHEME)] union
{
[case( BG_AUTH_SCHEME_BASIC, BG_AUTH_SCHEME_DIGEST, BG_AUTH_SCHEME_NTLM,
BG_AUTH_SCHEME_NEGOTIATE, BG_AUTH_SCHEME_PASSPORT )] BG_BASIC_CREDENTIALS Basic;
[default] ;
} BG_AUTH_CREDENTIALS_UNION;
BG_AUTH_CREDENTIALS中的 [認證] 字段將不會包含在 .NET 類別定義中。
請注意,不論BG_AUTH_SCHEME為何,聯集都會定義為BG_BASIC_CREDENTIALS。 因為聯集不以聯集方式運作,因此我們可以傳遞 BG_BASIC_CREDENTIALS,如下:
typedef struct
{
BG_AUTH_TARGET Target;
BG_AUTH_SCHEME Scheme;
BG_BASIC_CREDENTIALS Credentials;
}
BG_AUTH_CREDENTIALS;
在 C# 中設定某些 using 語句會減少您需要輸入的字元數目,以使用不同的 BITS 版本。 名稱 「BITSReference」 來自參考 DLL 的名稱。
// Set up the BITS namespaces
using BITS = BITSReference1_5;
using BITS4 = BITSReference4_0;
using BITS5 = BITSReference5_0;
using BITS10_2 = BITSReference10_2;
以下提供從 URL 下載檔案的簡短但完整 C# 代碼段。
var mgr = new BITS.BackgroundCopyManager1_5();
BITS.GUID jobGuid;
BITS.IBackgroundCopyJob job;
mgr.CreateJob("Quick download", BITS.BG_JOB_TYPE.BG_JOB_TYPE_DOWNLOAD, out jobGuid, out job);
job.AddFile("https://aka.ms/WinServ16/StndPDF", @"C:\Server2016.pdf");
job.Resume();
bool jobIsFinal = false;
while (!jobIsFinal)
{
BITS.BG_JOB_STATE state;
job.GetState(out state);
switch (state)
{
case BITS.BG_JOB_STATE.BG_JOB_STATE_ERROR:
case BITS.BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED:
job.Complete();
break;
case BITS.BG_JOB_STATE.BG_JOB_STATE_CANCELLED:
case BITS.BG_JOB_STATE.BG_JOB_STATE_ACKNOWLEDGED:
jobIsFinal = true;
break;
default:
Task.Delay(500); // delay a little bit
break;
}
}
// Job is complete
在此範例程式代碼中,會建立名為 mgr 的 BITS 管理員。 它直接對應至 IBackgroundCopyManager 介面。
從經理創建了一個新工作。 作業是 CreateJob 方法上的 out 參數。 此外,傳入的是作業名稱(不需要是唯一的),也是下載類型,也就是下載作業。 作業識別碼的 BITS GUID 也會被填入。
建立作業之後,您可以使用 AddFile 方法,將新的下載檔新增至作業。 您需要傳入兩個字串,一個用於遠端檔案(URL 或檔案共用),另一個用於本機檔案。
新增檔案之後,請在作業上呼叫 Resume 來啟動它。 然後程式代碼會等候作業處於最終狀態(ERROR 或 TRANSFERRED),然後完成。
您會發現,您通常必須使用早期版本的BITS對象和程式中的較新版本。
例如,當您建立作業物件時,即使您使用的是較新的管理員物件,而且可以使用較新的 IBackgroundCopyJob 物件,您仍然會取得 IBackgroundCopyJob(BITS 1.0 版的一部分)。 因為 CreateJob 方法不接受較新版本的介面,因此您無法直接建立較新版本。
使用 .NET 轉換,從較舊的類型物件轉換成較新的類型物件。 轉換會視需要自動呼叫 COM QueryInterface。
在此範例中,有名為 「job」 的 BITS IBackgroundCopyJob 對象,我們想要將它轉換成名為 「job5」 的 IBackgroundCopyJob5 物件,以便我們可以呼叫 BITS 5.0 GetProperty 方法。 我們只是將其轉換成 IBackgroundCopyJob5 類別,如下所示:
var job5 = (BITS5.IBackgroundCopyJob5)job;
.NET 將會使用正確的 QueryInterface 初始化 job5 變數。
如果您的程式代碼可能會在不支援特定 BITS 版本的系統上執行,您可以嘗試轉換並攔截 System.InvalidCastException。
BITS5.IBackgroundCopyJob5 job5 = null;
try
{
job5 = (BITS5.IBackgroundCopyJob5)Job;
}
catch (System.InvalidCastException)
{
; // Must be running an earlier version of BITS
}
常見的問題是,當您嘗試轉換成錯誤的物件類型時。 .NET 系統不知道 BITS 介面之間的實際關聯性。 如果您要求錯誤的介面類型,.NET 會嘗試為您設定它,而且會因為 InvalidCastException 和 HResult 0x80004002 (E_NOINTERFACE) 而失敗。
在某些版本的 Windows 10 上,您無法使用 10.1 或 10.2 介面直接建立 BITS IBackgroundCopyManager 物件。 相反地,您必須使用多個版本的 BackgroundCopyManager DLL 參考檔案。 例如,您可以使用 1.5 版本建立 IBackgroundCopyManager 物件,然後使用 10.1 或 10.2 版本轉換產生的作業或檔案物件。
訓練
模組
使用 Windows PowerShell 建立和管理背景工作 - Training
本課程模組說明三種類型的作業:本機作業、Windows PowerShell 遠端作業以及一般資訊模型 (CIM)/Windows Management Instrumentation (WMI) 作業。 這些作業類型構成 Windows PowerShell 作業系統的基礎。