성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>포인터 형 매개 변수를 갖는 C++ DLL의 함수를 C#에서 호출하는 방법</h1> <p> 종종 다음과 같은 질문을 보게 되는데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C++ dll C#에서 사용하는데 보호된 메모리 오류 떠요 한 번만 도와주세요ㅜ ; <a target='tab' href='http://www.sysnet.pe.kr/3/0/4997'>http://www.sysnet.pe.kr/3/0/4997</a> </pre> <br /> 이쯤에서 한번 정리를 해봐야겠습니다. ^^<br /> <br /> 우선, C/C++ DLL의 함수를 정상적으로 호출하려면 Calling Convention이 맞아야 합니다. 이에 대해서는 지난 글에 잘 정리해 두었으니 참고하시고.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# 개발자를 위한 Win32 DLL export 함수의 호출 규약 (1) - x86 환경에서의 __cdecl, __stdcall에 대한 Name mangling ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11132'>http://www.sysnet.pe.kr/2/0/11132</a> </pre> <br /> 일단 호출 규약이 맞았다면 이제 매개 변수에 대한 인자 값을 잘 맞춰주는 문제만 남습니다. 이때 C# 개발자들이 어려워하는 것 중의 하나가 바로 C/C++의 포인터를 받는 함수에 대한 interop입니다. 이를 이해하려면 C/C++에서 포인터 변수를 받는 방식에 대한 이해가 필요합니다.<br /> <br /> 가령 C#에서 다음과 같은 코드를 보겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static unsafe void Main(string[] args) { int value = 5; PassByValue(<span style='color: blue; font-weight: bold'>value</span>); Console.WriteLine(value); // 출력 결과 5 PassByRef(<span style='color: blue; font-weight: bold'>ref value</span>); Console.WriteLine(value); // 출력 결과 50 } private static void PassByValue(<span style='color: blue; font-weight: bold'>int value</span>) { value = 50; } private static void PassByRef(<span style='color: blue; font-weight: bold'>ref int value</span>) { value = 50; } </pre> <br /> 저런 코드를 C/C++에서는 다음과 같이 작성할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int value = 5; PassByValue(<span style='color: blue; font-weight: bold'>value</span>); printf("%d\n", value); // 출력 결과 5 PassByPtr(<span style='color: blue; font-weight: bold'>&value</span>); printf("%d\n", value); // 출력 결과 50 void PassByValue(<span style='color: blue; font-weight: bold'>int value</span>) { value = 50; } void PassByPtr(<span style='color: blue; font-weight: bold'>int *value</span>) { *value = 50; } </pre> <a name='ptr_and_array'></a> <br /> 즉, C#의 ref와 같은 역할을 위해 포인터 변수(int *)를 넘겨줌으로써 다룰 수 있게 하는 것입니다. 그런데, 문제는 C/C++의 경우 포인터와 배열의 차이점이 없다는 것입니다. 그래서 다음 코드에서 보는 것처럼 호출 측에서 동일하게 &value 포인터 변수를 전달했지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int value = 5; PassByPtr(<span style='color: blue; font-weight: bold'>&value</span>); printf("%d\n", value); // 출력 결과 50 PassByArray(<span style='color: blue; font-weight: bold'>&value</span>); printf("%d\n", value); // 출력 결과 500 void PassByPtr(<span style='color: blue; font-weight: bold'>int *value</span>) // 포인터로도 받을 수 있고, { *value = 50; } void PassByArray(<span style='color: blue; font-weight: bold'>int value[]</span>) // 배열로도 받을 수 있음. { value[0] = 500; } </pre> <br /> 받는 측의 함수에서는 &value 변수를 int *와, int []로 취향에 맞게 처리할 수 있습니다. 이 때문에, 표면상으로는 포인터를 받는 함수일지라도 내부적으로 그것을 배열로 처리할 수도 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int value = 5; PassByPtr(<span style='color: blue; font-weight: bold'>&value</span>); printf("%d\n", value); // 출력 결과 500 void PassByPtr(<span style='color: blue; font-weight: bold'>int *value</span>) { <span style='color: blue; font-weight: bold'>value[0]</span> = 500; } </pre> <br /> 대개의 경우, 포인터(또는 배열)를 받는 함수가 그것을 내부적으로 배열로 다룬다면 안전하게 접근할 수 있도록 명시적으로 배열의 수를 함께 전달할 수 있게 만듭니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int value = 5; // 단일 변수이지만, PassByPtr(&value, <span style='color: blue; font-weight: bold'>1</span>); // 1개의 배열로써 전달 int array[10] = { 0 }; // 10개의 요소를 갖는 배열을, int *pArray = &array[0]; PassByPtr(pArray, <span style='color: blue; font-weight: bold'>10</span>); // 명시적으로 10개라고 지정 void PassByPtr(int *value, <span style='color: blue; font-weight: bold'>int len</span>) { for (int i = 0; i < len; i ++) { <span style='color: blue; font-weight: bold'>value[i]</span> = 500; } } </pre> <br /> 물론 이것은 해당 함수를 개발하는 프로그래머의 결정에 따르기 때문에 꼭 저런 식으로 개발되었다고 장담할 수 없습니다. 실제로 과거에 많은 수의 C/C++ 함수들 중 문자열 처리의 경우 입력 버퍼의 수를 명시하지 않고 널(\0) 문자를 인식하는 것으로 작성했기 때문에,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > char buf[256]; strcpy(buf, "test"); // strcpy 함수는 buf의 배열 크기를 알지 못함 </pre> <br /> 수많은 버퍼 오버런 취약점이 발생하는 원인이 되었습니다. 그래서 나중에는 좀 더 안전한(secure) 함수라고 해서 배열의 크기를 명시하는 버전이 나오게 된 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > char buf[256]; strcpy_s(buf, 256, "test"); </pre> <br /> <hr style='width: 50%' /><br /> <br /> 자, 그럼 C/C++ 개발자가 DLL을 만들어 제공하는 함수의 시그니처를 다음과 같이 전달해 줬다고 가정해 보겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > void ExternC_STD_Func_Ptr(<span style='color: blue; font-weight: bold'>int *value</span>); </pre> <br /> C# 개발자는 위의 시그니처를 보면 반드시 C/C++ 개발자에게 물어봐야 합니다. 저 value가 배열이냐? 아니면 순수하게 단일 int 형에 대한 포인터냐? 라고! (물어볼 개발자가 없다면 문서라도 꼼꼼하게 확인해야 합니다. 혹은, 귀찮지만 역공학을 통해 해당 API의 내부 기계어 코드를 분석하거나!)<br /> <br /> 만약 단일 int 형에 대한 포인터라면 C#에서 다음과 같이 처리할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [DllImport("Win32Project1.dll", SetLastError = true)] internal static unsafe extern int ExternC_STD_Func_Ptr(<span style='color: blue; font-weight: bold'>int *value</span>); static unsafe void Main(string[] args) { <span style='color: blue; font-weight: bold'>int value = 5; int* pValue = &value;</span> ExternC_STD_Func_Ptr(pValue); } </pre> <br /> 하지만 배열이라고 한다면, 그 배열의 크기를 묻고 그에 맞게 전달해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [DllImport("Win32Project1.dll", SetLastError = true)] internal static unsafe extern int ExternC_STD_Func_Ptr(<span style='color: blue; font-weight: bold'>int *value</span>); static unsafe void Main(string[] args) { <span style='color: blue; font-weight: bold'>int[] array = new int[10]; fixed (int* pArray = &array[0])</span> { ExternC_STD_Func_Ptr(pArray); for (int i = 0; i < array.Length; i ++) { Console.Write(array[i] + ","); } Console.WriteLine(); } } </pre> <br /> 포인터 변수에 대해 위의 규칙 정도만 맞춰주면 AV(Access Violation) 오류로 인한 비정상 종료 문제는 사라질 것입니다.<br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1264&boardid=331301885'>첨부 파일은 이 글의 예제 - C#, C/C++ 코드를 포함</a>합니다.)<br /> <br /> 참고로 COM DLL에 대해서는 다음의 글도 도움이 될 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [in, out] 배열을 C#에서 C/C++로 넘기는 방법 - 두 번째 이야기 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/811'>http://www.sysnet.pe.kr/2/0/811</a> [in, out] 배열을 C#에서 C/C++로 넘기는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/810'>http://www.sysnet.pe.kr/2/0/810</a> </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1286
(왼쪽의 숫자를 입력해야 합니다.)