[in,out] 배열을 C#에서 C/C++로 넘기는 방법
가령, long 형 배열을 C/C++에 넘겨주고, C/C++ 측에서 해당 배열의 내용을 채운 후 반환해 주는 메서드라면 다음과 같이 IDL 정의를 해줄 수 있습니다.
[
object,
uuid(1A38076B-3D6D-4F20-8B4D-C72EF6AE1204),
dual,
nonextensible,
helpstring("IMyTest Interface"),
pointer_default(unique)
]
interface IMyTest : IDispatch
{
[id(0x3003), helpstring("method PrepareBuf1")]
HRESULT PrepareBuf([in, out, size_is(bufLength)] __int64 buffer [], [in] int bufLength);
};
그런데, tlbimp.exe(또는 Visual Studio의 DLL 참조)를 이용하여 interop DLL을 생성해 보면, PrepareBuf의 함수 형식이 다음과 같이 정의되는 것을 볼 수 있습니다.
tlbimp testatl.dll /out:interop.testatl.dll
public virtual void PrepareBuf(ref long buffer, int bufLength);
오호... tlbimp.exe로써는, 감당이 안되는 IDL 구문이라는 것인데요. 그렇다면 이를 해결하기 위해서 생각해 볼 수 있는 것이 배열 자체를 포인터로 넘겨보는 정도일텐데, 약간 찜찜하긴 해도 4byte(혹은 8byte) 값으로 넘기는 것은 ^^ 너무 잘 동작합니다.
그래서 C# 측에서, 배열 자체를 IntPtr로 변경하고,
[STAThread]
static void Main(string[] args)
{
interop.testatl.MyTestClass mtc = new interop.testatl.MyTestClass();
long [] test = new long[5];
// IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(test, 0);
// mtc.PrepareBuf(ptr.ToInt64(), 5);
// 코드 변경: 2020-05-07
GCHandle gcHandle = GCHandle.Alloc(test, GCHandleType.Pinned);
try
{
mtc.PrepareBuf(gcHandle.AddrOfPinnedObject().ToInt64(), 5);
}
finally
{
gcHandle.Free();
}
for (int i = 0; i < 5; i++)
{
Console.WriteLine(test[i]);
}
}
C/C++ 에서는 넘겨받은 정수값을 간단하게 포인터로 형변환해서 처리해 주면 됩니다.
STDMETHOD(PrepareBuf2)(__int64 buffer, int bufLength)
{
__int64 *pBuffer = (__int64 *)buffer;
for (int i = 0; i < bufLength; i ++ )
{
pBuffer[i] = i;
}
return S_OK;
}
물론, 이 방법은 out-of-process COM 개체로 만들면 프로세스 주소 공간이 달라지기 때문에 동작하지 않습니다. 하지만, In-proc COM 개체만으로 사용하실 분들이라면 이 방법이 나쁘다고 볼 수는 없습니다.
그렇긴 해도,,, 뭔가 개선 방법이 있지 않을까요? ^^
다음 토픽에 그 방법을 알아보겠습니다.
[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]