http://blog.danggun.net/799
클래스 마샬링을 할일이 없엇 신경을 안쓰다가 요번에 스카이프 api나 제가 직접 마샬링 해볼까 해서 클래스 마샬링을 정리해 보았습니다.
근데 왜이렇게 자료가 없지?
겨우 찾은것이 비주얼C++ 팀블로그인데....네...영어입니다 ㅡ.-;
(참고 : Visual C++ Team Blog - Inheriting From a Native C++ Class in C#)
일단 변환방법이 마음에 들지가 않아서 위글에 있는 내용을 그대로 사용하여 만들고 자료를 더 찾는다면 파트2로 돌아오 겠습니다 ㅎㅎㅎㅎ
그전에 이 글은 크게 2부분으로 나누어 설명할 예정입니다.
어찌됬건 프로그래머라면 일단 샘플부터 만들고 생각해야 하지 않겠습니까?
1. C++ DLL 만들기
1-1.프로젝트 생성
진입점 문제 때문인데 해결방법은 있으나....좀더 편한 테스트 환경을 위해 "cppexp"로 합니다 ㅎㅎㅎ
1-2.코드 넣기(CSimpleClass.h)
샘플 코드와 마찬가지로 저도 해더에 모든 코드를 넣습니다.
아래 코드는 전체 코드입니다-_-;
잘 모고 따라하시길 ㅎㅎㅎ
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 |
// cppexp.h #include <stdo.h> #pragma once using namespace System; class __declspec ( dllexport ) CSimpleClass { public : int value; CSimpleClass( int value) : value(value) { } ~CSimpleClass() { printf ( "~CSimpleClass\n" ); } void M1() { printf ( "C++/CSimpleClass::M1()\n" ); V0(); V1(value); V2(); } virtual void V0() { printf ( "C++/CSimpleClass::V0()\n" ); } virtual void V1( int x) { printf ( "C++/CSimpleClass::V1(%d)\n" , x); } virtual void V2() { printf ( "C++/CSimpleClass::V2()\n" , value); } }; |
빌드를 돌려보면 별다른 문제 없이 dll이 생성됩니다.
여기서 생성된 "cppexp.dll", "cppexp.lib" 이 두개 파일이 중요합니다.
2. C# 샘플 만들기
프로젝트는 아무것이나 상관없으나 이샘플자체가 콘솔응용프로그램이므로 'C# 콘솔 응용프로그램'으로 프로젝트를 생성합니다.
2-1. CSimpleClass 클래스 생성
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 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; namespace ConsoleApplication1 { [StructLayout(LayoutKind.Sequential, Pack = 4)] public unsafe struct __CSimpleClass { public IntPtr* _vtable; public int value; } public unsafe class CSimpleClass : IDisposable { private __CSimpleClass* _cpp; // CSimpleClass constructor and destructor [DllImport( "cppexp.dll" , EntryPoint = "??0CSimpleClass@@QAE@H@Z" , CallingConvention = CallingConvention.ThisCall)] private static extern int _CSimpleClass_Constructor(__CSimpleClass* ths, int value); [DllImport( "cppexp.dll" , EntryPoint = "??1CSimpleClass@@QAE@XZ" , CallingConvention = CallingConvention.ThisCall)] private static extern int _CSimpleClass_Destructor(__CSimpleClass* ths); // void M1(); [DllImport( "cppexp.dll" , EntryPoint = "?M1@CSimpleClass@@QAEXXZ" , CallingConvention = CallingConvention.ThisCall)] private static extern void _M1(__CSimpleClass* ths); public CSimpleClass( int value) { //Allocate storage for object _cpp = (__CSimpleClass*)Memory.Alloc( sizeof (__CSimpleClass)); //Call constructor _CSimpleClass_Constructor(_cpp, value); } public void Dispose() { //call destructor _CSimpleClass_Destructor(_cpp); //release memory Memory.Free(_cpp); _cpp = null ; } public void M1() { _M1(_cpp); } } } |
"??0CSimpleClass@@QAE@H@Z"와 같은 알수 없는 코드들은 코드의 진입점입니다-_-;
이 방법으로 클래스를 마샬링 하기 위해서는 위와같이 진입점 코드가 있어야 합니다.
진입점 코드는 어떻게 찾느냐? 아까 말했던 .lib파일을 메모장으로 열어보시면 나와 있습니다.
0CSimpleClass : 0클래스 이름 이 생성자
1CSimpleClass : 1클래스 이름 이 파괴자
나머지 함수들은 이름으로 검색하면 코드를 알수 있습니다.
"Memory"라는 클래스가 없기 때문이죠 ㅎㅎㅎ
2-2. Memory 클래스 생성
Memory이 클래스는 내용이 어떤것인가 하고 찾아보았습니다.
C#에서 동적 메모리 할당을 위해 사용하는 클래스 샘플입니다.
닷넷에서는 메모리관리는 특별한 경우를 제외하면 가비지컬랙터가 처리합니다.
이 가바지컬랙터를 거치지 않고 사용할수 있게 하는 샘풀이죠.
말이 샘플이지 이대로 쓰는대 전혀 손색이 없습니다.
참고 : MSDN - A.8 동적 메모리 할당
아래 코드는 클래스 전체 코드 입니다.
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { using System; using System.Runtime.InteropServices; public unsafe class Memory { // Handle for the process heap. This handle is used in all calls to the // HeapXXX APIs in the methods below. static int ph = GetProcessHeap(); // Private instance constructor to prevent instantiation. private Memory() { } // Allocates a memory block of the given size. The allocated memory is // automatically initialized to zero. public static void * Alloc( int size) { void * result = HeapAlloc(ph, HEAP_ZERO_MEMORY, size); if (result == null ) throw new OutOfMemoryException(); return result; } // Copies count bytes from src to dst. The source and destination // blocks are permitted to overlap. public static void Copy( void * src, void * dst, int count) { byte * ps = ( byte *)src; byte * pd = ( byte *)dst; if (ps > pd) { for (; count != 0; count--) *pd++ = *ps++; } else if (ps < pd) { for (ps += count, pd += count; count != 0; count--) *--pd = *--ps; } } // Frees a memory block. public static void Free( void * block) { if (!HeapFree(ph, 0, block)) throw new InvalidOperationException(); } // Re-allocates a memory block. If the reallocation request is for a // larger size, the additional region of memory is automatically // initialized to zero. public static void * ReAlloc( void * block, int size) { void * result = HeapReAlloc(ph, HEAP_ZERO_MEMORY, block, size); if (result == null ) throw new OutOfMemoryException(); return result; } // Returns the size of a memory block. public static int SizeOf( void * block) { int result = HeapSize(ph, 0, block); if (result == -1) throw new InvalidOperationException(); return result; } // Heap API flags const int HEAP_ZERO_MEMORY = 0x00000008; // Heap API functions [DllImport( "kernel32" )] static extern int GetProcessHeap(); [DllImport( "kernel32" )] static extern void * HeapAlloc( int hHeap, int flags, int size); [DllImport( "kernel32" )] static extern bool HeapFree( int hHeap, int flags, void * block); [DllImport( "kernel32" )] static extern void * HeapReAlloc( int hHeap, int flags, void * block, int size); [DllImport( "kernel32" )] static extern int HeapSize( int hHeap, int flags, void * block); } } |
운영체제에 따라 작동안할수도 있다는 것입니다 ㅡ.-;;
어찌됬건 이렇게 "Memory"라는 클래스를 만들고 유징하시면 에러는 사라 집니다.
2-3.테스트 코드
2-1.에서 'M1()' 만 마샬링을 해두었으므로 'M1()'만 호출이 가능합니다.
프로그램의 진입점인 'Main()'으로 가서 다음 코드를 추가 합니다.
1
2
3
4
5
6 |
CSimpleClass sc = new CSimpleClass(10); using (sc) { //M1 calls all of the virtual functions V0,V1,V2 sc.M1(); } |
3.확인
"Unsafe code may only appear if compiling with /unsafe"
이런 에러가 납니다.
비관리 코드를 사용하기위한 옵션을 켜라는 소리입니다 ㅡ.-;;
3-1.비관리 코드 사용 옵션
옵션을 켜줍니다.
3-2. 출력 확인
정확하게 출력 되는 것을 볼수 있습니다.
5.마무리
당연한 이야기지만 클래스는 무조건 통으로 마샬링해야 합니다.
안그러면 사용을 할수 없어요.
샘플 프로젝트
'[ Program ] > C#' 카테고리의 다른 글
DataTable 에서 원하는 Row만 골라내기 (0) | 2014.11.05 |
---|---|
방법: Windows Forms DataGridView 컨트롤에서 선택한 셀, 행 및 열 가져오기 (0) | 2014.08.22 |
[C#] c++ DLL을 C#에서 사용해보자 ( 함수 마샬링 ) (0) | 2014.07.28 |
C#에서의 Flag attribute를 사용하는 Enum (0) | 2013.01.24 |
Component.DesignMode Property (0) | 2012.12.10 |
댓글