일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Rxjava2
- FLUTTER
- dart
- NDK
- Java
- UWP
- 코틀린
- livedata
- 안드로이드
- C++
- Android
- Kotlin
- kodility
- Django REST Android
- 프로그래머스
- RxAndroid
- RxJava
- mfc
- android push
- Android P
- 안드로이드 구글맵
- Django REST framework
- android architecture component
- Django REST
- Flutter TextField
- 알고리즘
- flutter firestore
- Python
- C/C++
- C
- Today
- Total
개발하는 두더지
[C/C++/C#/UWP] UWP C#에서 MFC C++ DLL 사용하기 본문
이번에는 C# UWP 프로젝트 환경에서 C++ 로 빌드된 DLL을 로드하여 그 안의 기능을 호출하는 방법을 배워보도록 하겠습니다.
목차
- C++ MFC dll 생성
- C# UWP 프로젝트 생성
- C#에서 C++ MFC dll 및 extern 메소드 로드
- C++ <-> C# 간 타입 캐스팅
프로젝트 속성에 들어가서 '정적 라이브러리에서 MFC 사용' 과 '유니코드 문자 집합 사용'을 선택합니다.
( 다국어 지원을 위해 유니코드 환경으로 프로젝트를 만드는 것이 좋습니다. )
'TestSample' 이라는 이름으로 MFC Dll을 만들었고 프로젝트의 구성을 아래 사진과 같습니다.
헤더 파일 폴더의 'TestSample.h' 에 C#에서 사용할 메소드들을 정의하고
소스 파일 폴더의 'TestSample.cpp' 에서 그 메소드들을 구현하도록 하겠습니다.
### TestSample.h ###
아래 코드를 TestSample.h 안에 적절한 위치에 작성합니다.
1 2 3 4 5 6 7 8 9 10 11 12 | typedef struct testStruct { char strTest[128]; int intTest; BYTE byteTest[64]; } ts; extern "C" __declspec(dllexport) void test1(void); extern "C" __declspec(dllexport) int test2(int value); extern "C" __declspec(dllexport) char* test3(char* value); extern "C" __declspec(dllexport) void test4(ts* structValue); extern "C" __declspec(dllexport) void test5(char* in, char* out); extern "C" __declspec(dllexport) WCHAR* test6(void); | cs |
### TestSample.cpp ###
아래 코드를 TestSample.cpp 안에 적절한 위치에 작성합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | void test1(void) { } int test2(int value) { return value + 1; } char* test3(char* value) { int nSize = strlen(value); //char* returnStr = (char*)malloc(sizeof(char) * (nSize + 15)); char* returnStr = (char*)LocalAlloc(LPTR, sizeof(char) * (nSize + 15)); sprintf(returnStr, "%s success", value); return returnStr; } void test4(ts* structValue) { structValue->byteTest[0] = 1; structValue->intTest = structValue->intTest + 2; sprintf(structValue->strTest, "%s success", structValue->strTest); } void test5(char* in, char* out) { strcpy(out, in); } WCHAR* test6(void) { return L"테스트"; } | cs |
이 파일을 UWP exe파일과 같은 경로에 넣어줘야 합니다.
C# UWP 프로젝트 생성
'App4' 라는 이름의 프로젝트를 생성하면 프로젝트의 구성은 아래와 같습니다.
Grid 블럭안에 TextBlock과 Button의 컴포넌트들이 있습니다.
x:Name은 각 컴포넌트의 고유 ID 값이고 Content="test1" 은 MainPage.cs 의 test1() 메소드와 연결이되고 이것이 컴포넌트의액션 이벤트입니다.
C#에서 C++ MFC dll 및 extern 메소드 로드
C++ DLL을 로드할 클래스를 먼저 만들어줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Runtime.InteropServices; namespace App4 { public struct typeTest { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string strTest; public int intTest; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public byte[] byteTest; } class Class1 { [DllImport("TestSample")] public static extern void test1(); [DllImport("TestSample")] public static extern int test2(int intTemp); [DllImport("TestSample", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr test3(StringBuilder strTemp); [DllImport("TestSample")] public static extern void test4(ref typeTest testTemp); [DllImport("TestSample", CallingConvention = CallingConvention.Cdecl)] public static extern void test5(StringBuilder _in, StringBuilder _out); [DllImport("TestSample", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr test6(); } } | cs |
using System.Runtime.InteropServices; 를 선언해야 Marshal , DllImport 같은 키워드를 네임스페이스 없이 사용할 수 있습니다.
DllImport("TestSample") 라고만 적었는데 App4 UWP 프로젝트에서 앱이 실행되는 폴더에 TestSample.dll 이 있어야 절대경로 없이 dll 이름만 import 시켜서 로드드 할 수있습니다.
각 버튼의 액션 이벤트 구현은 아래와 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | private void test1(object sender, RoutedEventArgs e) { // 내부 함수 구현이 없음 Class1.test1(); textBlock.Text = "Success"; } private void test2(object sender, RoutedEventArgs e) { textBlock.Text = "" + Class1.test2(15); } private void test3(object sender, RoutedEventArgs e) { // Hello 라는 문자열을 가진 StrungBuilder 객체를 초기화합니다. StringBuilder _input = new StringBuilder("Hello"); // MFC C++ Dll 의 test3 메소드의 리턴은 char* 주소값 입니다. // C#에서 C++ 포인터와 매칭되는 키워드는 IntPtr 입니다. IntPtr ptr = Class1.test3(_input); // char*은 Ansi코드 문자열이므로 PtrToStringAnsi를 사용하여 저장합니다. string Message = Marshal.PtrToStringAnsi(ptr); // MFC C++ Dll에서 받은 문자열 포인터는 내부적으로 LocalAlloc 으로 // 메모리를 할당받았으므로 FreeHGlobal을 통해 메모리를 해제합니다. Marshal.FreeHGlobal(ptr); textBlock.Text = Message; } private void test4(object sender, RoutedEventArgs e) { typeTest testTemp = new typeTest(); testTemp.byteTest = new byte[64]; testTemp.strTest = "Hello"; testTemp.intTest = int.Parse("15"); testTemp.byteTest[0] = byte.Parse("15"); Class1.test4(ref testTemp); textBlock.Text = testTemp.strTest + " , " + testTemp.intTest; } private void test5(object sender, RoutedEventArgs e) { StringBuilder _input = new StringBuilder("Hello"); StringBuilder _output = new StringBuilder(); Class1.test5(_input, _output); textBlock.Text = _output.ToString(); } private void test6(object sender, RoutedEventArgs e) { IntPtr ptr = Class1.test6(); // 유니코드 문자열의 주소를 리턴받았으므로 PtrToStringUni를 사용합니다. // DLL에서 메모리를 할당받지 않았으므로 메모리 해제 또한 하지 않습니다. string message = Marshal.PtrToStringUni(ptr); textBlock.Text = message; } | cs |
C++ <-> C# 간 타입 캐스팅
C++ 과 C# 에 동일하게 존재하는 타입의 경우 똑같은 타입을 써주면 변환이 잘되지만
동일하게 존재하지 않는경우 아무 타입으로 변환 시키면 오류가 발생합니다.
타입이 동일한 것이 없을 경우 아래의 표를 참조하면 됩니다.
Win32 |
비관리C 데이터타입 |
C# |
HANDLE |
int |
int |
BYTE |
unsigned char |
byte |
SHORT |
short |
short |
WORD |
unsigned short |
ushort |
INT |
int |
int |
UINT |
unsigned int |
uint 또는 int |
LONG |
long |
int |
BOOL |
long |
int |
DWORD |
unsigned long |
uint |
ULONG |
unsigned long |
uint |
CHAR |
char |
char |
LPSTR |
char* |
string 또는 StringBuilder , 리턴시 IntPtr |
LPCSTR |
const char* |
string |
LPWSTR |
wchar_t* |
string 또는 StringBuilder , 리턴시 IntPtr |
LPCWSTR |
const wchar_t* |
string |
FLOAT |
float |
flaot |
DOUBLE |
double |
double |
'C,C++' 카테고리의 다른 글
[C/C++/C#/UWP] UWP Customized SplitView 코드 - 2 (0) | 2016.10.21 |
---|---|
[C/C++/C#/UWP] UWP Customized SplitView 코드 - 1 (0) | 2016.10.21 |
[C/C++/C#/UWP] UWP App 앱 패키지 배포하기 (0) | 2016.10.10 |
[C/C++/UWP] UWP 로컬 폴더, 임시 폴더에 파일 생성하기 (0) | 2016.08.09 |
[C/C++/UWP] UWP remote debugging error DEP4300 - Could not generate the root folder for app package (0) | 2016.07.22 |