Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)

[Out] 형식의 int * 인자를 가진 함수에 대한 P/Invoke 호출 방법

다음과 같은 질문이 있군요. ^^

C# 에서 C++ DLL임포트 질문입니다.
; https://www.sysnet.pe.kr/3/0/4800

질문을 정리해 보면, C++ DLL에서 제공하는 함수가 다음과 같이 int * 형식의 인자를 가지고 있을 때,

__declspec(dllexport) void TestFunc(int *value1);

이 함수를 DllImport를 이용해 다음과 같이 P/Invoke 호출을 할 수 있느냐... 하는 것입니다.

[DllImport("Win32Project1.dll")]
internal static extern int TestFunc(IntPtr pValue);

일단, 저렇게 하는 것이 맞습니다. 하지만, TestFunc에 넘겨주는 IntPtr에 대한 처리를 잘 해야 합니다. 가령 다음과 같이 넘겨주면 어떻게 될까요?

IntPtr pValue = new IntPtr();
TestFunc(pValue);

운이 나쁘다면 이런 경우 다음과 같은 예외를 만날 수 있습니다.

Exception thrown: write access violation.

value1 was nullptr.

If there is a handler for this exception, the program may be safely continued.

왜냐하면, C++ DLL 측에서 대개는 입력 인자가 int 타입 등에 대한 포인터인 경우 다음과 같이 코딩했을 확률이 높기 때문입니다.

__declspec(dllexport) void TestFunc(int *value1)
{
    *value1 = 5; // C#에서 넘겨준 IntPtr은 기본값으로 0을 담고 있기 때문에,
                 // 이는 null ptr을 의미하므로 NULL 참조 예외 발생
}

또는 C++ 개발자가 이런 식으로 만들었을 수도 있습니다.

__declspec(dllexport) void TestFunc(int *value1)
{
    if (value1 != nullptr) // 호출 측에서 명시적으로 필요하다고 한 경우에만 값을 담도록 처리
    {
        *value1 = 5;
    }
}

위와 같은 경우라면 C# 호출 측에서는 예외가 발생하지 않지만 IntPtr에는 아무런 값도 없게 됩니다. 물론, 해당 [Out] 인자 값이 필요하지 않다면 별문제 없겠지만 필요한 경우라면 결국 값을 반환받지 못했으므로 원치 않는 결과가 나온 것입니다.




해결 방법은 간단합니다. 결국, IntPtr로 하여금 값을 담을 수 있는 주소에 대한 포인터가 되도록 만들어 주면 되는 것입니다.

방법은 여러 가지가 있는데요, 가장 간단하게는 C#에서도 직접 포인터를 써 주면 됩니다. 이렇게!

[DllImport("Win32Project1.dll")]
internal unsafe static extern int TestFunc(int *pValue);

private unsafe static void CallTestFunc2()
{
    int intValue;
    TestFunc(&intValue);
    Console.WriteLine("TestFunc2: " + intValue);
}

unsafe 예약어가 싫다면 이렇게도 가능합니다.

[DllImport("Win32Project1.dll")]
internal static extern int TestFunc(IntPtr pValue);

private static void CallTestFunc3()
{
    IntPtr pValue = Marshal.AllocHGlobal(4); // 4바이트 할당
    TestFunc(pValue);

    Console.WriteLine("CallTestFunc3: " + Marshal.ReadInt32(pValue));

    Marshal.FreeHGlobal(pValue);
}

약간 복잡하게 한다면 어쨌든 포인터 접근이 가능합니다.

private unsafe static void CallTestFunc3()
{
    IntPtr pValue = Marshal.AllocHGlobal(4); // 4바이트 할당
    TestFunc(pValue);

    int handle = *(int *)pValue.ToPointer();
    Console.WriteLine("CallTestFunc3: " + handle);

    Marshal.FreeHGlobal(pValue);
}

물론, Marshal.AllocHGlobal을 이용해 할당하는 대신 int 타입의 인스턴스를 가져다 써도 됩니다.

private unsafe static void CallTestFunc4()
{
    int intValue;
    int* pIntValue = &intValue;

    IntPtr pValue = new IntPtr(pIntValue);
    TestFunc(pValue);

    Console.WriteLine("CallTestFunc4: " + intValue);
}

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 2/5/2017]

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

비밀번호

댓글 작성자
 




1  2  [3]  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13566정성태2/27/20241877오류 유형: 897. Windows 7 SDK 설치 시 ".NET Development" 옵션이 비활성으로 선택이 안 되는 경우
13565정성태2/23/20241722닷넷: 2219. .NET CLR2 보안 모델에서의 개별 System.Security.Permissions 제어
13564정성태2/22/20241953Windows: 259. Hyper-V Generation 1 유형의 VM을 Generation 2 유형으로 바꾸는 방법
13563정성태2/21/20241959디버깅 기술: 196. windbg - async/await 비동기인 경우 메모리 덤프 분석의 어려움
13562정성태2/21/20241991오류 유형: 896. ASP.NET - .NET Framework 기본 예제에서 System.Web에 대한 System.IO.FileNotFoundException 예외 발생
13561정성태2/20/20242077닷넷: 2218. C# - (예를 들어, Socket) 비동기 I/O에 대한 await 호출 시 CancellationToken을 이용한 취소파일 다운로드1
13560정성태2/19/20242121디버깅 기술: 195. windbg 분석 사례 - Semaphore 잠금으로 인한 Hang 현상 (닷넷)
13559정성태2/19/20242947오류 유형: 895. ASP.NET - System.Security.SecurityException: 'Requested registry access is not allowed.'
13558정성태2/18/20242193닷넷: 2217. C# - 최댓값이 1인 SemaphoreSlim 보다 Mutex 또는 lock(obj)를 선택하는 것이 나은 이유
13557정성태2/18/20241924Windows: 258. Task Scheduler의 Author 속성 값을 변경하는 방법
13556정성태2/17/20241986Windows: 257. Windows - Symbolic (hard/soft) Link 및 Junction 차이점
13555정성태2/15/20242133닷넷: 2216. C# - SemaphoreSlim 사용 시 주의점
13554정성태2/15/20241865VS.NET IDE: 189. Visual Studio - 닷넷 소스코드 디컴파일 찾기가 안 될 때
13553정성태2/14/20241952닷넷: 2215. windbg - thin/fat lock 없이 동작하는 Monitor.Wait + Pulse
13552정성태2/13/20241901닷넷: 2214. windbg - Monitor.Enter의 thin lock과 fat lock
13551정성태2/12/20242098닷넷: 2213. ASP.NET/Core 웹 응용 프로그램 - 2차 스레드의 예외로 인한 비정상 종료
13550정성태2/11/20242213Windows: 256. C# - Server socket이 닫히면 Accept 시켰던 자식 소켓이 닫힐까요?
13549정성태2/3/20242522개발 환경 구성: 706. C# - 컨테이너에서 실행하기 위한 (소켓) 콘솔 프로젝트 구성
13548정성태2/1/20242341개발 환경 구성: 705. "Docker Desktop for Windows" - ASP.NET Core 응용 프로그램의 소켓 주소 바인딩(IPv4/IPv6 loopback, Any)
13547정성태1/31/20242111개발 환경 구성: 704. Visual Studio - .NET 8 프로젝트부터 dockerfile에 추가된 "USER app" 설정
13546정성태1/30/20241994Windows: 255. (디버거의 영향 등으로) 대상 프로세스가 멈추면 Socket KeepAlive로 연결이 끊길까요?
13545정성태1/30/20241878닷넷: 2212. ASP.NET Core - 우선순위에 따른 HTTP/HTTPS 호스트:포트 바인딩 방법
13544정성태1/30/20241894오류 유형: 894. Microsoft.Data.SqlClient - Could not load file or assembly 'System.Security.Permissions, ...'
13543정성태1/30/20241924Windows: 254. Windows - 기본 사용 중인 5357 포트 비활성화는 방법
13542정성태1/30/20241920오류 유형: 893. Visual Studio - Web Application을 실행하지 못하는 IISExpress - 두 번째 이야기
13541정성태1/29/20241980VS.NET IDE: 188. launchSettings.json의 useSSL 옵션
1  2  [3]  4  5  6  7  8  9  10  11  12  13  14  15  ...