본문 바로가기
[ Program ]/C#

[C#] c++ DLL을 C#에서 사용해보자 ( 함수 마샬링 )

by 관이119 2014. 7. 28.

 

 

http://blog.danggun.net/16

 

 

머......피치못할 사정으로 c++로 작성된 dll을 써야된다면.....명복을 크크크크크크크

하지만 어차피 c++ dll은 여러모로 쓸모가 많다보니 재판매(어이)를 어느정도 고려 할겸, 역어셈에도 닷넷보다는 안전해 보여서 dll은 c++로 만들어 씁니다.

근데 이렇게 딴 언어에서 만든 dll을 쓰기위해서 마샬링이 필요합니다. ㅡ,.ㅡ;;;

일종의 컨버전이라고 생각하면 됩니다.
(쉽게 생각하라고 컨버전이라고 한거지 전혀 다른넘 입니다. ㅡ.-; 컨버전은 프로그램자체를 다른 언어에 맞게 변경시키는것을 의미하고. 마샬링은 다른언어에서도 읽을수 있게 해주는 작업입니다. dll자체는 변하지 않는다!)

마샬링하는 방법은 여러가지가 있고 dll불럳다 쓰는 방식도 여러가지가 있으나 편의상 하나만 ㅎㅎㅎ
(절대 귀찬은거 아니라능) 

일단 자주쓰는 넘들만 해봅시다.

 
1. C++ Dll

먼저 C++로 dll을 만들어 봅시다.

 
1-0. 인크루드

 

1
#include <windows.h>

이거 안해놓면 고생 많이 하끼야 ㅎㅎ

 

 
1-1. 구조체 ( test.h )
1
2
3
4
5
6
7
typedef struct tTest
{
    char  strTest[128]; //문자열 128
    int   intTest;    //숫자형
    BYTE byteTest[64]; //바이트형 배열
    UINT  uintTest[4];  //유니트형 배열
} typeTest;

 

 
1-2. 함수 ( test.h )
1
2
3
4
5
extern "C" __declspec(dllexport) void   OnTest1(void);      //기본형
extern "C" __declspec(dllexport) int    intOnTest2(int intTemp);    //입출력 숫자형
extern "C" __declspec(dllexport) char*  strOnTest3(char *strTemp);  //입출력 문자열형
extern "C" __declspec(dllexport) void   OnTest4(typeTest *testTemp);    //입력 구조체(포인터 출력가능)
extern "C" __declspec(dllexport) void   OnTest5(int *intTemp);  //입출력 배열(포인터 출력가능)


 
1-3. 함수 내용물 ( test.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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "claTest.h"
 
void OnTest1(void)
{
    //기본형
}
 
int intOnTest2( int intA)
{
    //입출력 숫자형
 
    ++intA; //입력받은 숫자에 +1
 
    return intA;
}
 
char* strOnTest3(char *strTemp)
{
    //입출력 문자열형
 
    static char strTemp2[128] = {0,};   //임시저장용 문자열
    sprintf_s( strTemp2, "%s strOnTest3 에서 리턴", strTemp);   //문자열 합치기
 
    return strTemp2;
}
 
void OnTest4( typeTest *testTemp )
{
    //입력 구조체형(포인터 출력가능)
 
    testTemp->byteTest[0] = 1;
    testTemp->intTest = testTemp->intTest + 2;
    sprintf_s( testTemp->strTest, "%s OnTest4에서 포인터", testTemp->strTest);
    testTemp->uintTest[0] = 1;
}
 
void OnTest5(int *intTemp)
{
    //입출력 배열형(포인터 출력 가능)
    for( int i = 0 ; i < 10 ; ++i )
    {
        intTemp[i] = intTemp[i] + i;
    }
}

 

 
1-4. 빌드

 빌드하고 dll의 위치를 알아서 정하시면 되겠습니다.

 
2. c# 응용프로그램

 
2-1. C#을 만들자

 


테스트 하기 좋게 이렇게 만들었습니다.
1이라고 적혀있는 텍스트박스가 textBox2, 그 아래가 textBox1 입니다.
버튼은 위에서 부터 button1, button2, button3, button4 입니다.

 
2-2. 구조체 마샬링( Form1.cs )

 클래스 안에 넣어야 합니다,
구조체는 dll에 넣고 뺄때만 사용하기때문에 그때만 양쪽의 형식이 맞으면 되니 알아서들 잘 쓰시고 여기 나와있는건 규격이라고 생각하면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public struct typeTest
{
    //문자열 124
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public String strTest;
 
    //숫자형
    public int intTest;
 
    //바이트 배열
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
    public byte[] byteTest;
 
    //유니트형 배열
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public UInt32[] uintTest;
}


 
2-3. 함수 마샬링

당연한 이야기지만 [DllImport("dll경로 및 이름")] 입니다.
여기서는 함수의 입출력 타입이 정확해야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[DllImport("test1.dll")]
extern public static void OnTest1();
 
[DllImport("test1.dll")]
extern public static int intOnTest2(int intTemp);
 
[DllImport("test1.dll")]
extern public static string strOnTest3(string strTemp);
 
[DllImport("test1.dll")]
extern public static void OnTest4(ref typeTest testTemp);
 
[DllImport("test1.dll")]
extern public static void OnTest5(int[] intTemp);

 

 
2-4. 그럼 사용은?
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
private void button1_Click(object sender, EventArgs e)
{
    OnTest1();
}
 
private void button2_Click(object sender, EventArgs e)
{
    MessageBox.Show(intOnTest2(int.Parse(textBox2.Text)) + " = intOnTest2(" + textBox2.Text + ")");
}
 
private void button3_Click(object sender, EventArgs e)
{
    MessageBox.Show(strOnTest3(textBox1.Text) + " = strOnTest3(" + textBox1.Text + ")");
}
 
private void button4_Click(object sender, EventArgs e)
{
    typeTest testTemp = new typeTest();
 
    testTemp.byteTest = new byte[64];
    testTemp.uintTest = new uint[4];
 
    testTemp.strTest = textBox1.Text;
    testTemp.intTest = int.Parse(textBox2.Text);
    testTemp.byteTest[0] = byte.Parse(textBox2.Text);
    testTemp.uintTest[0] = uint.Parse(textBox2.Text);
 
 
    OnTest4(ref testTemp);
 
    MessageBox.Show(textBox1.Text + " => " + testTemp.strTest + "\r\n"
            + textBox2.Text + " => " + testTemp.intTest + "\r\n"
            + textBox2.Text + " => " + testTemp.byteTest[0] + "\r\n"
            + textBox2.Text + " => " + testTemp.uintTest[0] + "\r\n"
            );
}
 
private void button5_Click(object sender, EventArgs e)
{
    int[] intTemp = new int[5];
 
    intOnTest5(intTemp);
 
    MessageBox.Show( intTemp[0] + "\r\n"
            + intTemp[1] + "\r\n"
            + intTemp[2] + "\r\n"
            + intTemp[3] + "\r\n"
            + intTemp[4] + "\r\n"
            );
}


 

 
2-5. 눌러보자

 

 

마샬링은 여러가지로 힘든 작업 입니다.-_-;;;
귀찬기도하고...
아마도 처음에 기획만 재대로 해도 쓸일 거의 없겠지만 나같은 사람 만나면 맨날 쓰게 될걸요 ㅋㅋㅋㅋ

마샬링에 관한 변수타입에 대하 설명이다. 참고하도록합니다!
http://msdn.microsoft.com/en-us/library/ac7ay120.aspx

p.s. 나는 유니트형과 바이트형을 자주 안쓰다보니..... 두개 사용법이 틀렸을 수도 있다 ㅡ.-;

 

댓글