Microsoft MVP성태의 닷넷 이야기
.NET Framework: 183. 구조체 포인터 인자에 대한 P/Invoke 정의 [링크 복사], [링크+제목 복사],
조회: 30157
글쓴 사람
정성태 (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




구조체 포인터 인자에 대한 P/Invoke 정의

C/C++와의 연동 문제가 때때로 ^^ 어렵게 다가오기도 합니다. 결국 연동부분에서 직관적이지 않다는 것이 문제겠지요. 덕분에 다음과 같은 글들도 씌여졌었고. ^^

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

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

How to Interop DISPPARAMS
; https://www.sysnet.pe.kr/2/0/617

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

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

이번에는, 다음 질문에 대한 답변으로 씌여지게 되었습니다.

C# Interop 관련 질문입니다. 
; https://www.sysnet.pe.kr/3/0/904
; https://www.sysnet.pe.kr/3/0/905

요점은, 아래의 코드 조각에서 보는 것처럼, P/Invoke된 API에서 "ref DX_CAP" 인자에 대해 NULL이 가능해야 한다는 것입니다.

public struct DX_CAP 
{
    public ushort c1; 
    public ushort c2; 
    public ushort c3; 
};

[DllImport("...")]
public static extern int fnStructTest(ref DX_CAP cap);



해답 1: IntPtr 사용


일단, Null이 가능하도록 하기 위해서는 ref DX_CAP에 대한 처리를 IntPtr로 하는 수 밖에는 없습니다. 결국 Interop이 다음과 같이 지정되어야 한다는 것이고.

[DllImport("...")]
public static extern void fnStructTest(IntPtr cap);

그다음, DX_CAP은 IntPtr과의 변환을 편하게 할 수 있도록 class로 정의합니다.

[StructLayout(LayoutKind.Sequential)]
public class DXC_CAP
{
    public ushort ca_nbrdna;
    public ushort ca_stdely;
    public ushort ca_cnosig;
};

그럼, Null이 넘겨져야 할 경우에는 다음과 같이 간단하게 호출이 가능하겠고,

fnStructTest(IntPtr.Zero);

값을 넘겨 받아야 할 경우에는, fnStructTest를 호출할 때는 IntPtr로 변환해 주고, 호출 후에는 다시 IntPtr로부터 값을 복원해 주면 됩니다. 이를 코드로 정리하면 다음과 같습니다.

DXC_CAP inst2 = new DXC_CAP();
int marshalSize = 0;

unsafe 
{
    marshalSize = sizeof(DX_CAP);
}

IntPtr ptrData = Marshal.AllocCoTaskMem(marshalSize);

// 클래스 값 ==> IntPtr
Marshal.StructureToPtr(inst2, ptrData, false);

// 호출하고,
fnStructTest(ptrData);

// 다시 ptrData로부터 클래스 값 복원
Marshal.PtrToStructure(ptrData, inst2);

// 메모리 해제
Marshal.FreeCoTaskMem(ptrData);

근데... 한 눈에 봐도... 너무 불편합니다. 좀 개선할 필요가 있어보입니다. ^^




해답 2: 포인터 사용


다시 생각해서. 어쨌든 NULL이 가능하도록 해야 하니, unsafe 쪽으로 생각해 보는 것도 답이 될 수 있습니다.

코드를 정리해서, 다음과 같이 아예 구조체를 받는 포인터를 정의해주는 것입니다.

public struct DX_CAP 
{
    public ushort ca_nbrdna; 
    public ushort ca_stdely; 
    public ushort ca_cnosig; 
};

[DllImport("TestNativeAPI.dll")]
unsafe public static extern void fnStructTest(DX_CAP *cap);

그럼, 게임 끝이군요. ^^

NULL을 넘겨주어야 할 때는 다음과 같이 해주면 되고,

unsafe { fnStructTest(null); }

값을 전달받아야 할 때도 역시 unsafe와 포인터 구문을 사용해 주면 됩니다.

DX_CAP inst;
unsafe { fnStructTest(&inst); }

unsafe와 포인터를 써야 하는 것에 대한 부담감이 있는데, 어느 쪽이든 선택을 해야 합니다. 몇 개 안되는 경우라면 그냥 해당 구조체 인자를 IntPtr로 받아들이는 P/Invoke 메서드를 중복 정의해주는 것도 답이겠지요.

*** 첨부된 프로젝트는 예제 코드입니다. ***



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

[연관 글]






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

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)
13818정성태11/15/20245302Windows: 272. Windows 11 24H2 - sudo 추가
13817정성태11/14/20244943Linux: 106. eBPF / bpf2go - (BPF_MAP_TYPE_HASH) Map을 이용한 전역 변수 구현
13816정성태11/14/20245397닷넷: 2312. C#, C++ - Windows / Linux 환경의 Thread Name 설정파일 다운로드1
13815정성태11/13/20244822Linux: 105. eBPF - bpf2go에서 전역 변수 설정 방법
13814정성태11/13/20245290닷넷: 2311. C# - Windows / Linux 환경에서 Native Thread ID 가져오기파일 다운로드1
13813정성태11/12/20245053닷넷: 2310. .NET의 Rune 타입과 emoji 표현파일 다운로드1
13812정성태11/11/20245271오류 유형: 933. Active Directory - The forest functional level is not supported.
13811정성태11/11/20244859Linux: 104. Linux - COLUMNS 환경변수가 언제나 80으로 설정되는 환경
13810정성태11/10/20245390Linux: 103. eBPF (bpf2go) - Tracepoint를 이용한 트레이스 (BPF_PROG_TYPE_TRACEPOINT)
13809정성태11/10/20245264Windows: 271. 윈도우 서버 2025 마이그레이션
13808정성태11/9/20245271오류 유형: 932. Linux - 커널 업그레이드 후 "error: bad shim signature" 오류 발생
13807정성태11/9/20244994Linux: 102. Linux - 커널 이미지 파일 서명 (Ubuntu 환경)
13806정성태11/8/20244917Windows: 270. 어댑터 상세 정보(Network Connection Details) 창의 내용이 비어 있는 경우
13805정성태11/8/20244748오류 유형: 931. Active Directory의 adprep 또는 복제가 안 되는 경우
13804정성태11/7/20245377Linux: 101. eBPF 함수의 인자를 다루는 방법
13803정성태11/7/20245336닷넷: 2309. C# - .NET Core에서 바뀐 DateTime.Ticks의 정밀도
13802정성태11/6/20245707Windows: 269. GetSystemTimeAsFileTime과 GetSystemTimePreciseAsFileTime의 차이점파일 다운로드1
13801정성태11/5/20245493Linux: 100. eBPF의 2가지 방식 - libbcc와 libbpf(CO-RE)
13800정성태11/3/20246339닷넷: 2308. C# - ICU 라이브러리를 활용한 문자열의 대소문자 변환 [2]파일 다운로드1
13799정성태11/2/20244922개발 환경 구성: 732. 모바일 웹 브라우저에서 유니코드 문자가 표시되지 않는 경우
13798정성태11/2/20245524개발 환경 구성: 731. 유니코드 - 출력 예시 및 폰트 찾기
13797정성태11/1/20245514C/C++: 185. C++ - 문자열의 대소문자를 변환하는 transform + std::tolower/toupper 방식의 문제점파일 다운로드1
13796정성태10/31/20245396C/C++: 184. C++ - ICU dll을 이용하는 예제 코드 (Windows)파일 다운로드1
13795정성태10/31/20245179Windows: 268. Windows - 리눅스 환경처럼 공백으로 끝나는 프롬프트 만들기
13794정성태10/30/20245268닷넷: 2307. C# - 윈도우에서 한글(및 유니코드)을 포함한 콘솔 프로그램을 컴파일 및 실행하는 방법
13793정성태10/28/20245149C/C++: 183. C++ - 윈도우에서 한글(및 유니코드)을 포함한 콘솔 프로그램을 컴파일 및 실행하는 방법
1  2  3  4  [5]  6  7  8  9  10  11  12  13  14  15  ...