다음을 통해 공유


참조 DLL을 사용하여 .NET 및 C#에서 BITS로 호출

.NET 프로그램에서 BITS COM 클래스를 호출하는 한 가지 방법은 MIDL 및 TLBIMP 도구를 사용하여 Windows SDK의 BITS IDL(인터페이스 정의 언어) 파일로 시작하는 참조 DLL 파일을 만드는 것입니다. 참조 DLL은 BITS COM 클래스에 대한 클래스 래퍼 집합입니다. 그런 다음 .NET에서 직접 래퍼 클래스를 사용할 수 있습니다.

자동으로 생성된 참조 DLL을 사용하는 대신 GitHubNuGet의 타사 .NET BITS 래퍼를 사용하는 것입니다. 이러한 래퍼는 더 자연스러운 .NET 프로그래밍 스타일을 사용하는 경우가 많지만 BITS 인터페이스의 변경 내용 및 업데이트보다 뒤처질 수 있습니다.

참조 DLL 만들기

BITS IDL 파일

BITS IDL 파일 집합으로 시작합니다. BITS COM 인터페이스를 완전히 정의하는 파일입니다. 파일은 Windows Kits 디렉터리에 있으며 Bits.idl인 버전 1.0 파일을 제외하고 bitsversion.idl(예: bits10_2.idl)으로 명명됩니다. 새 버전의 BITS가 만들어지면 새 BITS IDL 파일도 만들어집니다.

.NET 등가물로 자동으로 변환되지 않는 BITS 기능을 사용하도록 SDK BITS IDL 파일의 복사본을 수정할 수도 있습니다. 가능한 IDL 파일 변경 내용은 나중에 설명합니다.

BITS IDL 파일에는 참조에 의한 다른 여러 IDL 파일이 포함됩니다. 또한 하나의 버전을 사용하는 경우 모든 하위 버전이 포함되도록 중첩됩니다.

프로그램에서 대상으로 지정하려는 BITS의 각 버전에 대해 해당 버전에 대해 하나의 참조 DLL이 필요합니다. 예를 들어 BITS 1.5 이상에서 작동하지만 BITS 10.2가 있을 때 추가 기능이 있는 프로그램을 작성하려면 bits1_5.idl 파일과 bits10_2.idl 파일을 모두 변환해야 합니다.

MIDL 및 TLBIMP 유틸리티

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 파일을 만듭니다.

MIDL 및 TLBIMP에 대한 예제 명령

참조 파일 집합을 생성하는 전체 명령 집합의 예입니다. Visual Studio 및 Windows SDK 설치에 따라 대상인 BITS 기능 및 OS 버전에 따라 명령을 수정해야 할 수 있습니다.

이 예제에서는 참조 DLL 파일을 배치하는 디렉터리를 만들고 해당 디렉터리를 가리키도록 환경 변수 BITSTEMP를 만듭니다.

그런 다음, 예제 명령은 Visual Studio 설치 관리자가 만든 vsdevcmd.bat 파일을 실행합니다. 이 BAT 파일은 MIDL 및 TLBIMP 명령이 실행되도록 경로 및 일부 환경 변수를 설정합니다. 또한 최신 Windows SDK 디렉터리를 가리키도록 WindowsSdkDir 및 WindowsSDKLibVersion 변수를 설정합니다.

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 집합이 있습니다.

프로젝트에 참조 DLL 추가

C# 프로젝트에서 참조 DLL을 사용하려면 Visual Studio에서 C# 프로젝트를 엽니다. 솔루션 탐색기 참조를 마우스 오른쪽 단추로 클릭하고 참조 추가를 클릭합니다. 그런 다음 찾아보기 단추를 클릭한 다음 추가 단추를 클릭합니다. 참조 DLL이 있는 디렉터리로 이동하여 선택하고 추가를 클릭합니다. 참조 관리자 창에서 참조 DLL이 선택됩니다. 그런 후 확인을 클릭합니다.

이제 BITS 참조 DLL이 프로젝트에 추가됩니다.

참조 DLL 파일의 정보는 최종 프로그램에 포함됩니다. 참조 DLL 파일을 프로그램과 함께 제공할 필요가 없습니다. .EXE 배송하기만 하면 됩니다.

참조 DLL이 최종 EXE에 포함되는지 여부를 변경할 수 있습니다. Interop Types 포함 속성을 사용하여 참조 DLL을 포함할지 여부를 설정합니다. 이 작업은 참조별로 수행할 수 있습니다. DLL을 포함하려면 기본값은 True입니다.

보다 완전한 .NET 코드를 위해 IDL 파일 수정

BITS IDL(Microsoft 인터페이스 정의 언어) 파일을 변경되지 않고 사용하여 BackgroundCopyManager DLL 파일을 만들 수 있습니다. 그러나 결과 .NET 참조 DLL에는 일부 변환할 수 없는 공용 구조체가 누락되고 일부 구조체 및 열거형에는 사용하기 어려운 이름이 있습니다. 이 섹션에서는 .NET DLL을 보다 완전하고 사용하기 쉽게 만들기 위해 수행할 수 있는 몇 가지 변경 내용에 대해 설명합니다.

더 간단한 ENUM 이름

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*로 변경합니다. 결과 필드(문자열이라고 함)는 IntPtr로 전달됩니다. System.Runtime.InteropServices.Marshal.PtrToStringAuto(value)를 사용하여 문자열로 변환합니다. 문자열); 메서드.

구조체의 공용 구조체

구조체에 포함된 공용 구조체가 구조체에 전혀 포함되지 않는 경우가 있습니다. 예를 들어 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에서 BITS 사용 #

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 버전, 캐스팅 및 QueryInterface

BITS 개체의 초기 버전과 프로그램의 최신 버전을 모두 사용해야 하는 경우가 많습니다.

예를 들어 작업 개체를 만들 때 최신 관리자 개체를 사용하고 최신 IBackgroundCopyJob 개체를 사용할 수 있는 경우에도 IBackgroundCopyJob(BITS 버전 1.0의 일부)을 가져옵니다. CreateJob 메서드는 최신 버전에 대한 인터페이스를 허용하지 않으므로 최신 버전을 직접 만들 수 없습니다.

.NET 캐스트를 사용하여 이전 형식 개체에서 최신 형식 개체로 변환합니다. 캐스트는 자동으로 COM QueryInterface를 적절하게 호출합니다.

이 예제에는 "job"이라는 BITS IBackgroundCopyJob 개체가 있으며 BITS 5.0 GetProperty 메서드를 호출할 수 있도록 "job5"라는 IBackgroundCopyJob5 개체로 변환하려고 합니다. 다음과 같이 IBackgroundCopyJob5 형식으로 캐스팅했습니다.

var job5 = (BITS5.IBackgroundCopyJob5)job;

job5 변수는 올바른 QueryInterface를 사용하여 .NET에서 초기화됩니다.

코드가 특정 버전의 BITS를 지원하지 않는 시스템에서 실행될 수 있는 경우 캐스트를 시도하고 System.InvalidCastException을 catch할 수 있습니다.

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)으로 실패합니다.

BITS 버전 10_1 및 10_2 작업

일부 버전의 Windows 10 10.1 또는 10.2 인터페이스를 사용하여 BITS IBackgroundCopyManager 개체를 직접 만들 수 없습니다. 대신 여러 버전의 BackgroundCopyManager DLL 참조 파일을 사용해야 합니다. 예를 들어 1.5 버전을 사용하여 IBackgroundCopyManager 개체를 만들고 10.1 또는 10.2 버전을 사용하여 결과 작업 또는 파일 개체를 캐스팅할 수 있습니다.