Microsoft MVP성태의 닷넷 이야기
.NET Framework: 168. [in,out] 배열을 C#에서 C/C++로 넘기는 방법 [링크 복사], [링크+제목 복사],
조회: 34564
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 2개 있습니다.)
(시리즈 글이 16개 있습니다.)
.NET Framework: 112. How to Interop DISPPARAMS
; https://www.sysnet.pe.kr/2/0/617

.NET Framework: 137. C#에서 Union 구조체 다루기
; https://www.sysnet.pe.kr/2/0/728

.NET Framework: 141. Win32 Interop - 크기가 정해지지 않은 배열을 C++에서 C#으로 전달하는 경우
; https://www.sysnet.pe.kr/2/0/737

.NET Framework: 168. [in,out] 배열을 C#에서 C/C++로 넘기는 방법
; https://www.sysnet.pe.kr/2/0/810

.NET Framework: 169. [in, out] 배열을 C#에서 C/C++로 넘기는 방법 - 두 번째 이야기
; https://www.sysnet.pe.kr/2/0/811

.NET Framework: 183. 구조체 포인터 인자에 대한 P/Invoke 정의
; https://www.sysnet.pe.kr/2/0/912

.NET Framework: 472. C/C++과 C# 사이의 메모리 할당/해제 방법
; https://www.sysnet.pe.kr/2/0/1784

.NET Framework: 620. C#에서 C/C++ 함수로 콜백 함수를 전달하는 예제 코드
; https://www.sysnet.pe.kr/2/0/11099

.NET Framework: 627. C++로 만든 DLL을 C#에서 사용하기
; https://www.sysnet.pe.kr/2/0/11111

.NET Framework: 686. C# - string 배열을 담은 구조체를 직렬화하는 방법
; https://www.sysnet.pe.kr/2/0/11319

.NET Framework: 757. 포인터 형 매개 변수를 갖는 C++ DLL의 함수를 C#에서 호출하는 방법
; https://www.sysnet.pe.kr/2/0/11533

.NET Framework: 978. C# - GUID 타입 전용의 UnmanagedType.LPStruct
; https://www.sysnet.pe.kr/2/0/12444

C/C++: 158. Visual C++ - IDL 구문 중 "unsigned long"을 인식하지 못하는 #import
; https://www.sysnet.pe.kr/2/0/13128

.NET Framework: 2058. [in,out] 배열을 C#에서 C/C++로 넘기는 방법 - 세 번째 이야기
; https://www.sysnet.pe.kr/2/0/13141

.NET Framework: 2083. C# - C++과의 연동을 위한 구조체의 fixed 배열 필드 사용 (2)
; https://www.sysnet.pe.kr/2/0/13205

닷넷: 2152. Win32 Interop - C/C++ DLL로부터 이중 포인터 버퍼를 C#으로 받는 예제
; https://www.sysnet.pe.kr/2/0/13429




[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 개체만으로 사용하실 분들이라면 이 방법이 나쁘다고 볼 수는 없습니다.

그렇긴 해도,,, 뭔가 개선 방법이 있지 않을까요? ^^
다음 토픽에 그 방법을 알아보겠습니다.



[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/11/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2010-01-05 11시01분
[ohjjang@daishin.com] 제 생각 몇 자 적어봅니다.
1.
midl의 출력은 proxy/stub 코드나 타입라이브러리 로 가능한데요.
size_is 의 경우는 타입라이브러리에서 미지원합니다(참조: http://support.microsoft.com/?scid=kb;en-us;236970&x=14&y=12)
따라서, 원칙적으로 proxy/stub 코드 출력을 사용해야 합니다.
따라서, 위의 경우는 tlbimp 가 감당을 못하는 것이 아니라, testatl.dll 내의 타입라이브러리 정보 자체에 conformant array 정보 표현이
안되어 있기 때문입니다.
2.
다만, 코딩량을 줄이기 위하여 타입라이브러리 출력과 tlbimp 를 사용할 때,
미지원 속성들을 어떻게 할 것인가...에 대한 좋은 방법일 수 는 있겠네요.
3.
추가적으로, size_is 속성을 사용하여 conformant array 를 표현할 때는 IMyTest 인터페이스가 custom 이어야지,
dual 이면 안될텐데요. 안그렇습니까?
[guest]
2010-01-05 11시09분
[ohjjang@daishin.com] 댓글 수정/삭제 할 수 있게 해 주세요. 네~?
[guest]
2010-01-27 12시34분
ohjjang@daishin.com 님 정확한 설명 감사드립니다. ^^ 님의 댓글을 통해 IDL의 출력이 tlb에 모두 반영되지 못한다는 것을 처음 알았습니다. 블로그를 통해 이런 사실을 알게 되어 개인적으로 더 의미가 있는 것 같습니다. ohjjang@daishin.com 님처럼 댓글 달아주시는 분이 많아야 할 텐데... ^^

3번 사항에 저도 동의합니다. 단지 습관적으로 Dual로 예제를 만들었는데요. IDispatch 상으로는 그런 표현이 가능하질 않으니 원래는 custom 인터페이스로 정의해야겠지요. ^^

댓글 수정/삭제는... 시간 날 때 꼭 구현하도록 하겠습니다. (개인적으로 안 불편해서... ^^;)
kevin25

... 121  122  123  124  125  126  127  128  129  130  131  [132]  133  134  135  ...
NoWriterDateCnt.TitleFile(s)
1755정성태9/22/201434278오류 유형: 241. Unity Web Player를 설치해도 여전히 설치하라는 화면이 나오는 경우 [4]
1754정성태9/22/201424627VC++: 80. 내 컴퓨터에서 C++ AMP 코드가 실행이 될까요? [1]
1753정성태9/22/201420607오류 유형: 240. Lync로 세미나 참여 시 소리만 들리지 않는 경우 [1]
1752정성태9/21/201441069Windows: 100. 윈도우 8 - RDP 연결을 이용해 VNC처럼 사용자 로그온 화면을 공유하는 방법 [5]
1751정성태9/20/201438938.NET Framework: 464. 프로세스 간 통신 시 소켓 필요 없이 간단하게 Pipe를 열어 통신하는 방법 [1]파일 다운로드1
1750정성태9/20/201423831.NET Framework: 463. PInvoke 호출을 이용한 비동기 파일 작업파일 다운로드1
1749정성태9/20/201423731.NET Framework: 462. 커널 객체를 위한 null DACL 생성 방법파일 다운로드1
1748정성태9/19/201425383개발 환경 구성: 238. [Synergy] 여러 컴퓨터에서 키보드, 마우스 공유
1747정성태9/19/201428447오류 유형: 239. psexec 실행 오류 - The system cannot find the file specified.
1746정성태9/18/201426102.NET Framework: 461. .NET EXE 파일을 닷넷 프레임워크 버전에 상관없이 실행할 수 있을까요? - 두 번째 이야기 [6]파일 다운로드1
1745정성태9/17/201423035개발 환경 구성: 237. 리눅스 Integration Services 버전 업그레이드 하는 방법 [1]
1744정성태9/17/201431054.NET Framework: 460. GetTickCount / GetTickCount64와 0x7FFE0000 주솟값 [4]파일 다운로드1
1743정성태9/16/201420985오류 유형: 238. 설치 오류 - Failed to get size of pseudo bundle
1742정성태8/27/201426968개발 환경 구성: 236. Hyper-V에 설치한 리눅스 VM의 VHD 크기 늘리는 방법 [2]
1741정성태8/26/201421334.NET Framework: 459. GetModuleHandleEx로 알아보는 .NET 메서드의 DLL 모듈 관계파일 다운로드1
1740정성태8/25/201432503.NET Framework: 458. 닷넷 GC가 순환 참조를 해제할 수 있을까요? [2]파일 다운로드1
1739정성태8/24/201426524.NET Framework: 457. 교착상태(Dead-lock) 해결 방법 - Lock Leveling [2]파일 다운로드1
1738정성태8/23/201422044.NET Framework: 456. C# - CAS를 이용한 Lock 래퍼 클래스파일 다운로드1
1737정성태8/20/201419762VS.NET IDE: 93. Visual Studio 2013 동기화 문제
1736정성태8/19/201425572VC++: 79. [부연] CAS Lock 알고리즘은 과연 빠른가? [2]파일 다운로드1
1735정성태8/19/201418181.NET Framework: 455. 닷넷 사용자 정의 예외 클래스의 최소 구현 코드 - 두 번째 이야기
1734정성태8/13/201419841오류 유형: 237. Windows Media Player cannot access the file. The file might be in use, you might not have access to the computer where the file is stored, or your proxy settings might not be correct.
1733정성태8/13/201426359.NET Framework: 454. EmptyWorkingSet Win32 API를 사용하는 C# 예제파일 다운로드1
1732정성태8/13/201434473Windows: 99. INetCache 폴더가 다르게 보이는 이유
1731정성태8/11/201427080개발 환경 구성: 235. 점(.)으로 시작하는 파일명을 탐색기에서 만드는 방법
1730정성태8/11/201422164개발 환경 구성: 234. Royal TS의 터미널(Terminal) 연결에서 한글이 깨지는 현상 해결 방법
... 121  122  123  124  125  126  127  128  129  130  131  [132]  133  134  135  ...