마샬링 변경 사항
다음 단원에서는 가져오기 프로세스의 출력과 관련된 특정 문제를 해결하기 위해 interop 어셈블리에서 변경할 수 있는 몇 가지 사항을 보여 줍니다.
규격에 맞는 C 스타일 배열
In/Out C 스타일 배열
다차원 C 스타일 배열
0에서 시작하지 않는 SAFEARRAY
시그니처 보존
값 형식에 대한 참조 대신 null 전달
이 단원에서 interop 어셈블리를 편집하는 모든 경우를 보여 주는 것은 아닙니다. 예를 들어, 사용을 쉽게 하기 위해 interop 어셈블리를 편집할 수도 있습니다. 필요한 사용자 지정 방식을 결정하는 유일한 방법은 interop 어셈블리를 사용하여 코드를 실제로 작성하는 것입니다. interop 어셈블리 편집에 대한 지침은 방법: interop 어셈블리 편집을 참조하십시오.
마샬링은 클라이언트와 서버가 호환되지 않는 아파트에 있을 때 영향을 받습니다. 다음 예제에서 대부분의 마샬링된 매개 변수는 자동화와 호환되지 않으며 다음 작업 중 하나를 필요로 합니다.
클라이언트와 서버가 모두 호환되는 아파트에 있으며, 따라서 COM 마샬링이 관련되지 않는지 확인합니다.
IDL(Interface Definition Language)에서 생성된 프록시 및 스텁을 등록합니다. 이 경우 마샬링에 필요한 대부분의 정보는 IDL에서 형식 라이브러리로 전파되지 않으므로 형식 라이브러리를 등록하는 것은 도움이 되지 않습니다.
규격에 맞는 C 스타일 배열
다음의 IDL 선언에서는 C 스타일 배열을 보여 줍니다.
HRESULT ConformantArray([in] int cElems, [in, size_is(cElems)] int
aConf[]);
이 형식은 자동화와 호환되지 않으므로 첫 번째 매개 변수와 두 번째 매개 변수 사이의 링크 같은 배열 크기에 대한 정보를 형식 라이브러리에서 표현할 수 없습니다. 형식 라이브러리 가져오기 도구(Tlbimp.exe)에서는 두 번째 매개 변수를 관리되는 배열이 아니라 정수에 대한 참조로 가져옵니다. MSIL을 편집하여 이 매개 변수를 조정할 수 있습니다.
MSIL에서 검색할 내용
method public hidebysig newslot virtual
instance void ConformantArray([in] int32 cElems,
[in] int32& aConf) runtime managed internalcall
바꿀 내용
method public hidebysig newslot virtual
instance void ConformantArray([in] int32 cElems,
[in] int32[] marshal([]) aConf) runtime managed internalcall
관리 코드에서 호출하려면
int[] param1 = { 11, 22, 33 };
tstArrays.ConformantArray( 3, param1 );
In/Out C 스타일 배열
다음의 IDL 선언에서는 In/Out C 스타일 배열을 보여 줍니다.
HRESULT InOutArray([in, out] int* pcElems, [in, out, size_is(,*pcElems)]
int** ppInOut);
이 경우 배열 크기를 조정하여 새로운 크기를 다시 전달할 수 있습니다. 이 형식은 자동화와 호환되지 않으므로 첫 번째 매개 변수와 두 번째 매개 변수 사이의 링크 같은 배열 크기에 대한 정보를 형식 라이브러리에서 표현할 수 없습니다. Tlbimp.exe서는 두 번째 매개 변수를 IntPtr로 가져옵니다. 관리 코드에서도 이 메서드를 호출할 수 있지만 배열 크기를 조정하려면 MSIL을 편집하고 Marshal 클래스의 메서드를 사용하여 메모리의 할당 및 할당 취소를 직접 처리해야 합니다.
MSIL에서 검색할 내용
.method public hidebysig newslot virtual
instance void InOutArray([in][out] int32& pcElems,
[in][out] native int ppInOut) runtime managed internalcall
바꿀 내용
.method public hidebysig newslot virtual
instance void InOutArray([in][out] int32& pcElems,
[in][out] native int& ppInOut) runtime managed internalcall
관리 코드에서 호출하려면
int[] inArray = { 11, 22, 33 };
int arraySize = inArray.Length;
IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( typeof( int )) * inArray.Length );
Marshal.Copy( inArray, 0, buffer, inArray.Length );
tstArrays.InOutArray( ref arraySize, ref buffer );
if( arraySize > 0 )
{
int[] arrayRes = new int[ arraySize ];
Marshal.Copy( buffer, arrayRes, 0, arraySize );
Marshal.FreeCoTaskMem( buffer );
}
다차원 C 스타일 배열
다음의 IDL 선언에서는 2차원 C 스타일 배열을 보여 줍니다.
HRESULT TwoDimArray([in] int cDim, [in, size_is(cDim)] int aMatrix[][3]);
이 형식은 자동화와 호환되지 않으므로 첫 번째 매개 변수와 두 번째 매개 변수 사이의 링크 같은 배열 크기 및 차수에 대한 정보를 형식 라이브러리에서 표현할 수 없습니다. Tlbimp.exe에서는 두 번째 매개 변수를 관리되는 다차원 배열이 아니라 IntPtr 형식으로 가져옵니다. MSIL을 편집하여 이 매개 변수를 조정할 수 있습니다.
MSIL에서 검색할 내용
.method public hidebysig newslot virtual
instance void TwoDimArray([in] int32 cDim,
[in] native int aMatrix) runtime managed internalcall
바꿀 내용
.method public hidebysig newslot virtual
instance void TwoDimArray([in] int32 cDim,
[in] int32[,] marshal([]) aMatrix) runtime managed internalcall
관리 코드에서 호출하려면
int[,] param = {{ 11, 12, 13 }, { 21, 22, 23 }, { 31, 32, 33 }};
tstArrays.TwoDimArray( 3, param );
0에서 시작하지 않는 SAFEARRAY
다음의 IDL 선언에서는 SAFEARRAY 매개 변수를 보여 줍니다.
HRESULT InSArray([in] SAFEARRAY(int) *ppsa);
이 SAFEARRAY는 0에서 시작하지 않습니다. 관리 코드에서 이러한 배열은 System.Array 형식으로 표시됩니다. 그러나 기본적으로 가져오기 도구에서는 모든 SAFEARRAY 매개 변수를 관리되는 배열에 대한 참조로 변환합니다. 다음과 같은 두 가지 방법으로 기본 동작을 변경할 수 있습니다.
Tlbimp.exe와 /sysarray 스위치를 사용하여 형식 라이브러리의 모든 배열을 System.Array 형식으로 가져옵니다.
다음 예제에서처럼 MSIL을 직접 편집하여 일부 매개 변수를 System.Array 형식으로 가져옵니다.
MSIL에서 검색할 내용
.method public hidebysig newslot virtual instance void InSArray([in] int32[]& marshal( safearray int) ppsa) runtime managed internalcall
바꿀 내용
.method public hidebysig newslot virtual instance void InSArray(class [mscorlib]System.Array& marshal( safearray) ppsa) runtime managed internalcall
관리 코드에서 호출하려면
int[] lengthsArray = new int[1] { 3 }; int[] boundsArray = new int[1] { -1 }; Array param2 = Array.CreateInstance( typeof(int), lengthsArray, boundsArray ); for( int i = param2.GetLowerBound( 0 ); i <= param2.GetUpperBound( 0 ); i++ ) param2.SetValue( i * 10, i ); sum = tstArrays.InSArray( ref param2 );
시그니처 보존
다음의 IDL 선언에서는 COM 메서드 시그니처를 보여 줍니다.
HRESULT TestPreserveSig2([in] int inParam, [out,retval] int* outParam);
Tlbimp.exe에서는 COM 메서드의 시그니처를 변경합니다. IDL에서 **[out, retval]**로 표시된 매개 변수는 관리되는 메서드의 반환 값이 됩니다. 실패를 표시하는 모든 HRESULT 값은 관리되는 예외로 변환됩니다. 메서드에서 성공 HRESULT 이외의 값을 반환할 때처럼 원래의 COM 메서드 시그니처를 보존해야 하는 경우가 종종 있습니다. 다음의 관리되는 표현에서는 사용자가 수정할 수 있는 시그니처의 예를 보여 줍니다.
MSIL의 관리되는 표현
.method public hidebysig newslot virtual
instance int32 TestPreserveSig2([in] int32 inParam) runtime managed internalcall
{
바꿀 내용
.method public hidebysig newslot virtual
instance int32 TestPreserveSig2([in] int32 inParam, [out] int32& outParam) runtime managed internalcall preservesig
반환되는 HRESULT를 확인하려면
int hr = tst.TestPreserveSig2( -3, out retValue );
Console.WriteLine( "Return value is {0}", retValue );
if( hr == 0 )
Console.WriteLine( "HRESULT = S_OK" );
else if ( hr == 1 )
Console.WriteLine( "HRESULT = S_FALSE" );
else
Console.WriteLine( "HRESULT = {0}", hr );
값 형식에 대한 참조 대신 Null 전달
다음의 IDL 선언에서는 구조체에 대한 IDL 포인터를 보여 줍니다.
HRESULT TestPassingNull([in, unique] Point* refParam);
Tlbimp.exe에서는 이 매개 변수를 값 형식 Point에 대한 참조로 가져옵니다. C# 및 Visual Basic 2005에서는 값 형식에 대한 참조가 필요할 때 null 참조(Visual Basic에서는 Nothing)를 매개 변수로 전달할 수 없습니다. COM 함수에 null(Nothing) 매개 변수가 필요한 경우에는 다음과 같이 MSIL을 편집하여 시그니처를 변경합니다.
MSIL에서 검색할 내용
.method public hidebysig newslot virtual
instance void TestPassingNull(
[in] valuetype MiscSrv.tagPoint& refParam)
runtime managed internalcall
바꿀 내용
.method public hidebysig newslot virtual
instance void TestPassingNull([in] native int) runtime managed internalcall
변경된 시그니처를 사용하여 null 값을 전달할 수 있습니다. 그러나 실제 값을 전달해야 할 때는 다음 예제에서처럼 Marshal 클래스의 메서드를 사용해야 합니다.
tagPoint p = new tagPoint();
p.x = 3;
p.y = 9;
IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( p ));
Marshal.StructureToPtr( p, buffer, false );
tst.TestPassingNull( buffer );
Marshal.FreeCoTaskMem( buffer );
tst.TestPassingNull( IntPtr.Zero );