다음을 통해 공유


System.Runtime.InteropServices.ICustomMarshaler 인터페이스

이 문서에서는 이 API에 대한 참조 설명서에 대한 추가 설명서를 제공합니다.

인터페이스는 ICustomMarshaler 메서드 호출을 처리하기 위한 사용자 지정 래퍼를 제공합니다.

마샬러는 이전 인터페이스와 새 인터페이스의 기능 간에 브리지를 제공합니다. 사용자 지정 마샬링에서 제공하는 이점은 다음과 같습니다.

  • 또한 새 인터페이스를 구현 하는 서버로 작업 하도록 이전 인터페이스와 함께 작동 하도록 설계 된 클라이언트 애플리케이션 수 있습니다.
  • 클라이언트 애플리케이션을 새 인터페이스를 사용 하 여 작동 하도록 빌드된 이전 인터페이스를 구현 하는 서버를 작업할 수 있습니다.

다른 마샬링 동작을 도입하거나 다른 방식으로 COM(구성 요소 개체 모델)에 노출되는 인터페이스가 있는 경우 interop 마샬러를 사용하는 대신 사용자 지정 마샬러를 디자인할 수 있습니다. 사용자 지정 마샬러를 사용하면 새 .NET Framework 구성 요소와 기존 COM 구성 요소 간의 차이를 최소화할 수 있습니다.

예를 들어 , 라는 INew관리되는 인터페이스를 개발한다고 가정해 보겠습니다. 이 인터페이스가 표준 COM CCW(호출 가능 래퍼)를 통해 COM에 노출되면 관리되는 인터페이스와 동일한 메서드를 가지며 interop 마샬러에 기본 제공되는 마샬링 규칙을 사용합니다. 이제 호출 IOld 된 잘 알려진 COM 인터페이스가 이미 인터페이스와 동일한 기능을 제공한다고 가정합니다 INew . 사용자 지정 마샬러를 디자인하여 단순히 인터페이스의 IOld 관리되는 구현에 호출을 위임하는 관리되지 않는 구현을 INew 제공할 수 있습니다. 따라서 사용자 지정 마샬러는 관리되는 인터페이스와 관리되지 않는 인터페이스 간의 브리지 역할을 합니다.

참고 항목

디스패치 전용 인터페이스에서 관리 코드에서 비관리 코드로 호출할 때는 사용자 지정 마샬러가 호출되지 않습니다.

마샬링 형식 정의

사용자 지정 마샬러를 빌드하려면 먼저 마샬링될 관리되는 인터페이스와 관리되지 않는 인터페이스를 정의해야 합니다. 이러한 인터페이스는 일반적으로 동일한 함수를 수행하지만 관리되는 개체와 관리되지 않는 개체에 다르게 노출됩니다.

관리되는 컴파일러는 메타데이터에서 관리되는 인터페이스를 생성하며, 결과 인터페이스는 다른 관리되는 인터페이스처럼 보입니다. 다음 예제에서는 일반적인 인터페이스를 보여줍니다.

public interface INew
{
    void NewMethod();
}
Public Interface INew
    Sub NewMethod()
End Interface

IDL(인터페이스 정의 언어)에서 관리되지 않는 형식을 정의하고 MIDL(Microsoft Interface Definition Language) 컴파일러를 사용하여 컴파일합니다. 다음 예제와 같이 라이브러리 문 내에서 인터페이스를 정의하고 UUID(유니버설 고유 식별자) 특성을 사용하여 인터페이스 ID를 할당합니다.

 [uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library OldLib {
     [uuid(9B2BAADD-0705-11D3-A0CD-00C04FA35826)]
     interface IOld : IUnknown
         HRESULT OldMethod();
}

MIDL 컴파일러는 여러 출력 파일을 생성합니다. 인터페이스가 Old.idl에 정의된 경우 출력 파일 Old_i.c는 다음 예제와 같이 인터페이스의 IID(인터페이스 식별자)를 사용하여 변수를 정의 const 합니다.

const IID IID_IOld = {0x9B2BAADD,0x0705,0x11D3,{0xA0,0xCD,0x00,0xC0,0x4F,0xA3,0x58,0x26}};

Old.h 파일도 MIDL에서 생성됩니다. 여기에는 C++ 소스 코드에 포함할 수 있는 인터페이스의 C++ 정의가 포함되어 있습니다.

ICustomMarshaler 인터페이스 구현

사용자 지정 마샬러는 런타임에 ICustomMarshaler 적절한 래퍼를 제공하기 위해 인터페이스를 구현해야 합니다.

다음 C# 코드는 모든 사용자 지정 마샬러에서 구현해야 하는 기본 인터페이스를 표시합니다.

public interface ICustomMarshaler
{
    Object MarshalNativeToManaged(IntPtr pNativeData);
    IntPtr MarshalManagedToNative(Object ManagedObj);
    void CleanUpNativeData(IntPtr pNativeData);
    void CleanUpManagedData(Object ManagedObj);
    int GetNativeDataSize();
}
Public Interface ICustomMarshaler
     Function MarshalNativeToManaged( pNativeData As IntPtr ) As Object
     Function MarshalManagedToNative( ManagedObj As Object ) As IntPtr
     Sub CleanUpNativeData( pNativeData As IntPtr )
     Sub CleanUpManagedData( ManagedObj As Object )
     Function GetNativeDataSize() As Integer
End Interface

인터페이스에는 ICustomMarshaler 변환 지원, 클린up 지원 및 마샬링할 데이터에 대한 정보를 제공하는 메서드가 포함됩니다.

연산 유형 ICustomMarshaler 메서드 설명
변환(네이티브에서 관리 코드로) MarshalNativeToManaged 네이티브 데이터에 대한 포인터를 관리되는 개체로 마샬링합니다. 이 메서드는 인수로 전달되는 관리되지 않는 인터페이스를 마샬링할 수 있는 RCW(사용자 지정 런타임 호출 가능 래퍼)를 반환합니다. 마샬러는 해당 형식에 대한 사용자 지정 RCW의 인스턴스를 반환해야 합니다.
변환(관리 코드에서 네이티브 코드로) MarshalManagedToNative 관리되는 개체를 네이티브 데이터에 대한 포인터로 마샬링합니다. 이 메서드는 인수로 전달되는 관리되는 인터페이스를 마샬링할 수 있는 사용자 지정 COM 호출 가능 래퍼(CCW)를 반환합니다. 마샬러는 해당 형식에 대한 사용자 지정 CCW의 인스턴스를 반환해야 합니다.
정리(네이티브 코드) CleanUpNativeData 마샬러가 메서드에서 반환되는 네이티브 데이터(CCW)를 클린 수 있도록 합니다MarshalManagedToNative.
정리(관리 코드) CleanUpManagedData 마샬러가 메서드에서 반환 MarshalNativeToManaged 하는 관리되는 데이터(RCW)를 클린 수 있습니다.
정보(네이티브 코드 정보) GetNativeDataSize 마샬링할 관리되지 않는 데이터의 크기를 반환합니다.

전환

ICustomMarshaler.MarshalNativeToManaged

네이티브 데이터에 대한 포인터를 관리되는 개체로 마샬링합니다. 이 메서드는 인수로 전달되는 관리되지 않는 인터페이스를 마샬링할 수 있는 RCW(사용자 지정 런타임 호출 가능 래퍼)를 반환합니다. 마샬러는 해당 형식에 대한 사용자 지정 RCW의 인스턴스를 반환해야 합니다.

ICustomMarshaler.MarshalManagedToNative

관리되는 개체를 네이티브 데이터에 대한 포인터로 마샬링합니다. 이 메서드는 인수로 전달되는 관리되는 인터페이스를 마샬링할 수 있는 사용자 지정 COM 호출 가능 래퍼(CCW)를 반환합니다. 마샬러는 해당 형식에 대한 사용자 지정 CCW의 인스턴스를 반환해야 합니다.

정리

ICustomMarshaler.CleanUpNativeData

마샬러가 메서드에서 반환되는 네이티브 데이터(CCW)를 클린 수 있도록 합니다MarshalManagedToNative.

ICustomMarshaler.CleanUpManagedData

마샬러가 메서드에서 반환 MarshalNativeToManaged 하는 관리되는 데이터(RCW)를 클린 수 있습니다.

크기 정보

ICustomMarshaler.GetNativeDataSize

마샬링할 관리되지 않는 데이터의 크기를 반환합니다.

참고 항목

사용자 지정 마샬러가 네이티브에서 관리로 마샬링할 때 또는 클린 때 마지막 P/Invoke 오류를 설정하는 메서드를 호출하는 경우 반환된 Marshal.GetLastWin32Error() 값은 Marshal.GetLastPInvokeError() 마샬링 또는 클린up 호출에서 호출을 나타냅니다. 이렇게 하면 P/Invokes와 함께 DllImportAttribute.SetLastErrortrue사용자 지정 마샬러를 사용할 때 오류가 누락될 수 있습니다. 마지막 P/Invoke 오류를 유지하려면 구현에서 Marshal.GetLastPInvokeError() 메서드와 Marshal.SetLastPInvokeError(Int32) 메서드를 ICustomMarshaler 사용합니다.

GetInstance 메서드 구현

인터페이스를 구현하는 ICustomMarshaler 것 외에도 사용자 지정 마샬러는 매개 변수로 수락 String 하고 반환 형식ICustomMarshaler을 갖는 메서드 GetInstance 를 구현 static 해야 합니다. 이 static 메서드는 사용자 지정 마샬러의 인스턴스를 인스턴스화하기 위해 공용 언어 런타임의 COM interop 계층에서 호출됩니다. 전달되는 문자열은 메서드가 반환된 GetInstance 사용자 지정 마샬러를 사용자 지정하는 데 사용할 수 있는 쿠키입니다. 다음 예제에서는 최소한의 완전한 ICustomMarshaler 구현을 보여 있습니다.

public class NewOldMarshaler : ICustomMarshaler
{
    public static ICustomMarshaler GetInstance(string pstrCookie)
        => new NewOldMarshaler();

    public Object MarshalNativeToManaged(IntPtr pNativeData) => throw new NotImplementedException();
    public IntPtr MarshalManagedToNative(Object ManagedObj) => throw new NotImplementedException();
    public void CleanUpNativeData(IntPtr pNativeData) => throw new NotImplementedException();
    public void CleanUpManagedData(Object ManagedObj) => throw new NotImplementedException();
    public int GetNativeDataSize() => throw new NotImplementedException();
}

MarshalAsAttribute 적용

사용자 지정 마샬러를 사용하려면 마샬링되는 매개 변수 또는 필드에 특성을 적용 MarshalAsAttribute 해야 합니다.

또한 열거형 값을 MarshalAsAttribute 생성자에 전달 UnmanagedType.CustomMarshaler 해야 합니다. 또한 다음 명명된 MarshalType 매개 변수 중 하나를 사용하여 필드를 지정해야 합니다.

  • MarshalType (필수): 사용자 지정 마샬러의 어셈블리 정규화된 이름입니다. 이름에는 사용자 지정 마샬러의 네임스페이스 및 클래스가 포함되어야 합니다. 사용자 지정 마샬러가 사용되는 어셈블리에 정의되지 않은 경우 정의되는 어셈블리의 이름을 지정해야 합니다.

    참고 항목

    필드 대신 MarshalType 필드를 사용할 MarshalTypeRef 수 있습니다. MarshalTypeRef 는 지정하기 쉬운 형식을 취합니다.

  • MarshalCookie (선택 사항): 사용자 지정 마샬러에 전달되는 쿠키입니다. 쿠키를 사용하여 마샬러에 추가 정보를 제공할 수 있습니다. 예를 들어 동일한 마샬러를 사용하여 여러 래퍼를 제공하는 경우 쿠키는 특정 래퍼를 식별합니다. 쿠키는 마샬러의 GetInstance 메서드에 전달됩니다.

특성은 MarshalAsAttribute 적절한 래퍼를 활성화할 수 있도록 사용자 지정 마샬러를 식별합니다. 공용 언어 런타임의 interop 서비스는 특성을 검사하고 인수(매개 변수 또는 필드)를 마샬링해야 할 때 처음으로 사용자 지정 마샬러를 만듭니다.

그런 다음 런타임은 사용자 지정 마샬러의 MarshalNativeToManaged 메서드 및 MarshalManagedToNative 메서드를 호출하여 호출을 처리하는 올바른 래퍼를 활성화합니다.

사용자 지정 마샬러 사용

사용자 지정 마샬러가 완료되면 특정 형식에 대한 사용자 지정 래퍼로 사용할 수 있습니다. 다음 예제에서는 관리되는 인터페이스의 IUserData 정의를 보여줍니다.

interface IUserData
{
    void DoSomeStuff(INew pINew);
}
Public Interface IUserData
    Sub DoSomeStuff(pINew As INew)
End Interface

다음 예제에서 인터페이스는 IUserData 사용자 지정 마샬러를 사용하여 NewOldMarshaler 관리되지 않는 클라이언트 애플리케이션이 인터페이스를 메서드에 IOldDoSomeStuff 전달할 수 있도록 합니다. 메서드의 DoSomeStuff 관리되는 설명은 이전 예제와 같이 인터페이스를 사용하는 INew 반면, 관리되지 않는 버전의 DoSomeStuff 인터페이스 포인터는 IOld 다음 예제와 같이 사용합니다.

[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library UserLib {
     [uuid(9B2BABCD-0705-11D3-A0CD-00C04FA35826)]
     interface IUserData : IUnknown
         HRESULT DoSomeStuff(IUnknown* pIOld);
}

관리되는 정의를 내보내 생성되는 형식 라이브러리는 표준 정의 IUserData 대신 이 예제에 표시된 관리되지 않는 정의를 생성합니다. 메서드의 INewDoSomeStuff 관리되는 정의에서 인수에 적용된 특성은 MarshalAsAttribute 다음 예제와 같이 인수가 사용자 지정 마샬러를 사용한다는 것을 나타냅니다.

using System.Runtime.InteropServices;
Imports System.Runtime.InteropServices
interface IUserData
{
    void DoSomeStuff(
        [MarshalAs(UnmanagedType.CustomMarshaler,
         MarshalType="NewOldMarshaler")]
    INew pINew
    );
}
Public Interface IUserData
    Sub DoSomeStuff( _
        <MarshalAs(UnmanagedType.CustomMarshaler, _
        MarshalType := "MyCompany.NewOldMarshaler")> pINew As INew)
End Interface

이전 예제에서 특성에 제공된 MarshalAsAttribute 첫 번째 매개 변수는 열거형 값UnmanagedType.CustomMarshaler입니다UnmanagedType.CustomMarshaler.

두 번째 매개 변수는 MarshalType 사용자 지정 마샬러의 어셈블리 정규화된 이름을 제공하는 필드입니다. 이 이름은 사용자 지정 마샬러(MarshalType="MyCompany.NewOldMarshaler")의 네임스페이스 및 클래스로 구성됩니다.