성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
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# 개발자를 위한 Win32 DLL export 함수의 호출 규약 (2) - x86 환경의 __fastcall</h1> <p> <a target='tab' href='http://www.sysnet.pe.kr/2/0/11132'>지난번 글에서, x86 환경 하의 __cdecl, __stdcall 호출 규약과 그 이름 풀이</a>를 살펴봤는데요. 이번에는 동일하게 x86 하에서 __fastcall 호출 규약을 알아보겠습니다.<br /> <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;' > __declspec(dllexport) int <span style='color: blue; font-weight: bold'>__fastcall</span> FAST_Func(int value); __declspec(dllexport) int <span style='color: blue; font-weight: bold'>__fastcall</span> FAST_Func_By_DEF(int value); extern "C" { __declspec(dllexport) int <span style='color: blue; font-weight: bold'>__fastcall</span> ExternC_FAST_Func(int value); __declspec(dllexport) int <span style='color: blue; font-weight: bold'>__fastcall</span> ExternC_FAST_Func_By_DEF(int value); } </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;' > __declspec(dllexport) int __fastcall FAST_Func(int value) { printf("FAST_Func: %d\n", value); return 42; } __declspec(dllexport) int __fastcall FAST_Func_By_DEF(int value) { printf("FAST_Func_By_DEF: %d\n", value); return 42; } __declspec(dllexport) int __fastcall ExternC_FAST_Func(int value) { printf("ExternC_FAST_Func: %d\n", value); return 42; } __declspec(dllexport) int __fastcall ExternC_FAST_Func_By_DEF(int value) { printf("ExternC_FAST_Func_By_DEF: %d\n", value); return 42; } </pre> <br /> 위의 함수들 중에 "_By_DEF"로 끝나는 함수들은 .def 파일에 정의를 추가해 보겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > LIBRARY EXPORTS FAST_Func_By_DEF ExternC_FAST_Func_By_DEF </pre> <br /> 빌드하고 export 함수의 이름을 살펴보면 각각 이렇게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ?FAST_Func@@YIHH@Z FAST_Func_By_DEF @ExternC_FAST_Func@4 ExternC_FAST_Func_By_DEF </pre> <br /> extern "C"를 붙이지 않으면 "?FAST_Func@@YIHH@Z"와 같이 나오는 반면, 붙여주면 "@" 글자를 시작으로 "@바이트수"로 __stdcall과 유사하게 작명이 됩니다.<br /> <br /> 또한, .def 파일에 정의해 주면 이름 그대로 "FAST_Func_By_DEF", "ExternC_FAST_Func_By_DEF" 이렇게 출력됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 좋은(?) 소식이 하나 있는데요 ^^ 닷넷 개발자 입장에서는 __fastcall을 전혀 염두에 두지 않아도 됩니다. 왜냐하면, 닷넷에서 사용할 수 있는 방법이 없기 때문입니다. 문서에도 나오지만 DllImport의 CallingConvention 에는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > CallingConvention Enumeration ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.callingconvention'>https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.callingconvention</a> </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > FastCall - This calling convention is not supported. </pre> <br /> 분명히 FastCall을 사용할 수 없다고 나옵니다. 실제로, 다음과 같이 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;' > [DllImport("Win32Project1.dll", CallingConvention = CallingConvention.FastCall)] internal static extern int ExternC_FAST_Func(int value); </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;' > An unhandled exception of type 'System.TypeLoadException' occurred in ConsoleApplication1.exe Additional information: Invalid unmanaged calling convention: must be one of stdcall, cdecl, or thiscall. </pre> <br /> 심지어, CallingConvention을 지정하지 않으면 __cdecl과는 다르게 아예 이름 풀이도 안된다는 다음의 예외가 떨어집니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > An unhandled exception of type 'System.EntryPointNotFoundException' occurred in ConsoleApplication1.exe Additional information: Unable to find an entry point named 'ExternC_FAST_Func' in DLL 'Win32Project1.dll'. </pre> <br /> 강제로 EntryPoint를 이용해 실행은 할 수 있지만,<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", <span style='color: blue; font-weight: bold'>EntryPoint = "?FAST_Func@@YIHH@Z"</span>)] internal static extern int FAST_Func(int value); [DllImport("Win32Project1.dll", <span style='color: blue; font-weight: bold'>EntryPoint = "@ExternC_FAST_Func2@4"</span>)] internal static extern int ExternC_FAST_Func(int value); ...[생략]... { FAST_Func(5); ExternC_FAST_Func(8); } </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;' > FAST_Func: 1918189188 ExternC_FAST_Func: 1918189188 </pre> <br /> (원래 출력은 "FAST_Func: 5"와 "ExternC_FAST_Func: 8"이어야 함.)<br /> <br /> 물론, Visual Studio의 "F5 (Start Debugging)"으로 실행하면 이런 부작용을 방지하기 위해 다음과 같은 경고성 예외를 발생시킵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in 'C:\ConsoleApplication1\bin\x86\Debug\ConsoleApplication1.exe'. Additional information: A call to PInvoke function 'ConsoleApplication1!Program::FAST_Func' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature. If there is a handler for this exception, the program may be safely continued. </pre> <br /> 위의 오류는, <a target='tab' href='http://www.sysnet.pe.kr/2/0/11132'>DllImport의 기본 호출 규약이 __stdcall인데 __cdecl인 경우 "CallingConvention = CallingConvention.Cdecl"을 명시하지 않는 경우 잠재적인 버그가 존재할 수 있다고 했던 지난번 글의 설명</a>과 유사합니다. 단지, 그때는 __stdcall과 __cdecl의 유사성으로 인해 정상적으로 실행되었지만, __fastcall의 경우에는 __stdcall과 너무나 상이한 인자 전달 규칙을 가지기 때문에 저렇게 엉뚱한 값이 나와버리는 것입니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1096&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 정리하면, x86에서의 __fastcall로 export된 함수는 닷넷에서 사용할 수 없으므로 만약 그런 함수가 여러분들의 native DLL에 있다면 해당 개발자에게 호출 규약을 바꿔달라고 요청해야 합니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
5086
(왼쪽의 숫자를 입력해야 합니다.)