하기글은 danggun 블러그에서 발췌한 내용입니다.

 

http://blog.danggun.net/799

클래스 마샬링을 할일이 없엇 신경을 안쓰다가 요번에 스카이프 api나 제가 직접 마샬링 해볼까 해서 클래스 마샬링을 정리해 보았습니다.

근데 왜이렇게 자료가 없지?
겨우 찾은것이 비주얼C++ 팀블로그인데....네...영어입니다 ㅡ.-;
(참고 : Visual C++ Team Blog - Inheriting From a Native C++ Class in C#)

일단 변환방법이 마음에 들지가 않아서 위글에 있는 내용을 그대로 사용하여 만들고 자료를 더 찾는다면 파트2로 돌아오 겠습니다 ㅎㅎㅎㅎ
그전에 이 글은 크게 2부분으로 나누어 설명할 예정입니다.

어찌됬건 프로그래머라면 일단 샘플부터 만들고 생각해야 하지 않겠습니까?

1. C++ DLL 만들기

C#에서 노출 시킬 클래스가 담겨있는 C++ DLL을 만들겠습니다.

1-1.프로젝트 생성

프로젝트 이름은 꼭 "cppexp"로 합니다.
진입점 문제 때문인데 해결방법은 있으나....좀더 편한 테스트 환경을 위해 "cppexp"로 합니다 ㅎㅎㅎ

 

 

 



 

1-2.코드 넣기(CSimpleClass.h)

클래스 이름도 "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
// cppexp.h
#include <STDIO.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);
}
};
</STDIO.H>

빌드를 돌려보면 별다른 문제 없이 dll이 생성됩니다.

여기서 생성된 "cppexp.dll", "cppexp.lib" 이 두개 파일이 중요합니다.

2. C# 샘플 만들기
이 샘플코드를 사용하기 위해서는 C#쪽에서 해야할 작업이 많습니다.
프로젝트는 아무것이나 상관없으나 이샘플자체가 콘솔응용프로그램이므로 'C# 콘솔 응용프로그램'으로 프로젝트를 생성합니다.

2-1. CSimpleClass 클래스 생성
cppexp.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
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클래스 이름 이 파괴자
나머지 함수들은 이름으로 검색하면 코드를 알수 있습니다.

 

 

 

<iframe height="90" src="http://cfs.tistory.com/custom/blog/69/693272/skin/images/AD_Contens.html" frameborder="0" width="730" scrolling="no"></iframe>

이 클래스를 만들면 "Memory.Alloc"와 "Memory.Free" 같은 곳에서 에러가 납니다.
"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);
}
}
코드에 [DllImport("kernel32")]가 있는데 이소리는 커널을 불러다 사용한다는 소리입니다.
운영체제에 따라 작동안할수도 있다는 것입니다 ㅡ.-;;

어찌됬건 이렇게 "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. 출력 확인
화면이 순식간에 꺼진다면 알아서 화면 챙기시고 ㅋㅋㅋㅋ(그냥 중단점만 걸어도 됩니다-_-a)

 

 

 


정확하게 출력 되는 것을 볼수 있습니다.

5.마무리
위에 내용들을 정확하게 이해했다면 샘플에 나와있는 다른 클래스 맴버들도 마샬링을 할수 있습니다.
당연한 이야기지만 클래스는 무조건 통으로 마샬링해야 합니다.
안그러면 사용을 할수 없어요.

// 프로그램 시작시간
DateTime startTime = DateTime.Now;

// 1. 현재시간
DateTime now;
now = DateTime.Now;

Console.WriteLine(now); // 현재시간 전체
Console.WriteLine(now.Year); // 현재 년도
Console.WriteLine(now.Month); // 현재 달
Console.WriteLine(now.Day); // 현재 일
Console.WriteLine(now.DayOfWeek); // 현재 주
Console.WriteLine(now.DayOfYear); // 1년중 몇일째인지
Console.WriteLine(now.TimeOfDay); // 금일 자정부터 몇시간
Console.WriteLine(now.Hour); // 시간
Console.WriteLine(now.Minute); // 분
Console.WriteLine(now.Second); // 초
Console.WriteLine(now.Millisecond); // 1000분의 1초

Console.WriteLine(now.Ticks); // 1000분의 1초

// 2. 임의시간 설정
DateTime birthday;
birthday = DateTime.Parse("2009년8월9일"); // 시분초 미지정시 자정으로
birthday = DateTime.Parse("2009/08/09");

Console.WriteLine(birthday);

DateTime xmas;
xmas = new DateTime(2008, 12, 25, 0, 0, 0);

Console.WriteLine(xmas);

// 3. 연산

// 3.1 오늘 + 100일
now = DateTime.Now;
DateTime result = now.AddDays(100); // 각 단위별 add메소드가 존재 MSDN참고

Console.WriteLine(result);

// 3.2 크리스마스까지 남은 날
TimeSpan result2 = xmas - now;
Console.WriteLine(result2);
Console.WriteLine(result2.Days); // NNN일 (내림표현)
Console.WriteLine(result2.TotalDays); // NNN.NNNNNNN일 (더정확)

// 3.3 오늘 - 100일
Console.WriteLine(now - TimeSpan.FromDays(100));
Console.WriteLine(now.AddDays(-100));

// 4. 날짜시간 출력형식 지정
Console.WriteLine(now.ToLocalTime()); // 2008-08-08 오전 10:31:25

Console.WriteLine(now.ToLongDateString()); // 2008년 8월 8일 금요일
Console.WriteLine(now.ToShortDateString()); // 2008-08-08

Console.WriteLine(now.ToLongTimeString()); // 오전 10:31:25
Console.WriteLine(now.ToShortTimeString());   // 오전 10:31

// 프로그램 종료시간
DateTime endTime = DateTime.Now;

Console.WriteLine("프로그램 수행시간 : {0}/ms", (double)(endTime - startTime).

'프로그래밍 > .Net' 카테고리의 다른 글

컴파일과정  (0) 2014.01.17
C++ DLL을 C#에서 사용해보자  (0) 2014.01.17
class 선언하고 사용하기  (0) 2014.01.17
Thread에서 Invoke 사용하기  (0) 2014.01.17
시간을 string으로 표현하기. DateTime  (0) 2014.01.17

학습목표: class를 선언하고, 그 class내에 다른 sub class를 중복 선언한 뒤,

sub class를 배열로 구성하여, 접근하여 보자(class 할당이 필요)

 

=================================================================

샘플 코드...

=================================================================

 

namespace TEST{

public class KOREA

{

string sName;

public class CAR

{

string sCarName;

int[] nItem = new int[10];

public CAR()

{

sCarName=null;

nItem.Initialize();

}

}

...

}

}

 

 

다른 Form에서 사용.

namespace TEST{

public class MAIN_FORM:Form

{

KOREA nKorea = new KOREA();

KOREA.CAR[] nCar = new KOREA.CAR[10]; // car 10개 생성.

}

 

public MAIN_FORM()

{

InitializeComponent();

 

for (int i = 0; i < 10; i++)
nCar [i] = new KOREA.CAR(); // 각 배열에 class 객체을 생성시켜줌.

}

 

.... 하기처럼 사용.

nCar[0].nItem[0] = 100; 뭐 이런식으로 사용가능하네요..

}

학습목표: Cross-Thread 문제로

Main Form에서 사용하고있는 control들을 Sub로 생성시킨 Thread에서

사용하는 방법을 알아보자

 

Thread g_SubThread = new Thread(AddTexMsg_Thread);

g_SubThread.Start();

 

public void AddTexMsg_Thread()

{

string sBuf = string.Format("Example... Date:{0}", DateTime.Now.ToString());

// 출력하고자 하는 Control에 invoke(MethodInvoker(delegate) 를 설정한다

 

// richtextbox가 invoke가 필요한 사항이면, 즉 Main Thread에서 RichEdit Control를
// 제어하고 있는데, 다른 Thread에서 해당 RichEdit를 제어하고 싶은 경우
// Cross-Thread error가 발생하는데 이를 해결하기 위해 invoke 방식을 사용.
if (richDeviceLog.InvokeRequired)
{
richDeviceLog.Invoke(new MethodInvoker(delegate()
{
richDeviceLog.AppendText(sTbuf + Environment.NewLine);
}));
}
else
richDeviceLog.AppendText(sTbuf + Environment.NewLine);

}

시간을 String으로 표현하는 Method

Overload List

Converts the value of this instance to its equivalent string.

[C#] public override string ToString();

Converts the value of this instance to its equivalent string representation using the specified culture-specific format information.

[C#] public virtual string ToString(IFormatProvider);

Converts the value of this instance to its equivalent string representation using the specified format.

[C#] public string ToString(string);

Converts the value of this instance to its equivalent string representation using the specified format and culture-specific format information.

[C#] public virtual string ToString(string, IFormatProvider);

==> 이중 ToString(string)을 주로 쓴다..

[Sample]


using System;
using System.Globalization;

public class MainClass {
public static void Main(string[] args) {
DateTime dt = DateTime.Now;
String[] format = {
"d", "D",
"f", "F",
"g", "G",
"m",
"r",
"s",
"t", "T",
"u", "U",
"y",
"dddd, MMMM dd yyyy",
"ddd, MMM d \"'\"yy",
"dddd, MMMM dd",
"M/yy",
"dd-MM-yy",
};
String date;
for (int i = 0; i < format.Length; i++) {
date = dt.ToString(format[i], DateTimeFormatInfo.InvariantInfo);
Console.WriteLine(String.Concat(format[i], " :" , date));
}

/** Output.
*
* d :08/17/2000
* D :Thursday, August 17, 2000
* f :Thursday, August 17, 2000 16:32
* F :Thursday, August 17, 2000 16:32:32
* g :08/17/2000 16:32
* G :08/17/2000 16:32:32
* m :August 17
* r :Thu, 17 Aug 2000 23:32:32 GMT
* s :2000-08-17T16:32:32
* t :16:32
* T :16:32:32
* u :2000-08-17 23:32:32Z
* U :Thursday, August 17, 2000 23:32:32
* y :August, 2000
* dddd, MMMM dd yyyy :Thursday, August 17 2000
* ddd, MMM d "'"yy :Thu, Aug 17 '00
* dddd, MMMM dd :Thursday, August 17
* M/yy :8/00
* dd-MM-yy :17-08-00
*/
}
}

 

 

발췌 : http://iamgsi.com/289

 

'프로그래밍 > .Net' 카테고리의 다른 글

Thread에서 Invoke 사용하기  (0) 2014.01.17
시간을 string으로 표현하기. DateTime  (0) 2014.01.17
Tool Tip 사용하기  (0) 2014.01.17
string Format 이어서 사용하기  (0) 2014.01.17
Control Location 사용법 알아보자  (0) 2014.01.17

학습목표: ToolTip을 사용하여 보자

 

=============================================================================

private System.Windows.Forms.ToolTip toolTip1;

 

ToolTip은 아래와 같은 방식으로 생성이 가능합니다.

this.toolTip1 = new System.Windows.Forms.ToolTip();

 

그리고 아래와 같은 방식으로 사용될 수 있습니다.

private void checkBox1_MouseHover(object sender, System.EventArgs e)

{

toolTip1.ShowAlways = true;

toolTip1.SetToolTip(this.checkBox, "ToolTip입니다" );

}

 

개행하여 사용하고 싶은경우..

 

string strOutMsg = string.Format(

"TEST1 : {0}\r\n " +
"TEST2 : {1}\r\n " +

"TEST3 : {1}\r\n " +

"TEST4 : {1}\r\n " +

"TEST5 : {1}\r\n " +

............

 

txtboxView.AppendText(strOutMsg);

 

위와 같이 +를 사용하시면 됩니다.

 

학습목표: 작업을 하다보면, Control의 위치를 실해시점에서 변경하고 싶을때가 있는데,

그러할때 Location 속성을 이용해 보자.

 

================================================================================

// 현재 X/Y location 위치를 파악

int nX = rbtnManualMode.Location.X;
int nY = rbtnManualMode.Location.Y;

// 다시 Drawing 한다.
rbtnManualMode.Location = new System.Drawing.Point(nX+100, nY);

 

학습목표: FileInfo Class를 사용하여, File의 속성을 캐치하고,

현재 날짜를 기준으로 D-Day를 설정하여 파일을 지운다.

 

// FileInfo를 사용하기 위한 using..

using System.IO;

 

<key poing = 날자 계산은 일수로 모두 변환하여 하였다.>

 

private void DeleteDDay(int mode, int dday)
{
int TodayYear = DateTime.Now.Year;
int TodayMonth = DateTime.Now.Month;
int Today = DateTime.Now.Day;
int TodayTotalday = (TodayYear * 365) + (TodayMonth * 30) + (Today);
string TargetPath;
//1 = Device Log,
if(mode == 1)
TargetPath = RegStruct.uLocalDeviceLog;
// 2 = Handler Log
else if(mode == 2)
TargetPath= RegStruct.uLocalHandlerLog;
// 3 = Delete Function
else
TargetPath = "";


string[] GetFiles = Directory.GetFiles(TargetPath, "*.rtf", SearchOption.TopDirectoryOnly);
foreach (string strFileName in GetFiles)
{
FileInfo aFile = new FileInfo(strFileName);
int tYear = aFile.LastWriteTime.Year;
int tMonth = aFile.LastWriteTime.Month;
int tDay = aFile.LastWriteTime.Day;
int tTotalday = (tYear * 365) + (tMonth * 30) + (tDay);

if ((TodayTotalday - tTotalday) > dday)
aFile.Delete();
}
}

 

'프로그래밍 > .Net' 카테고리의 다른 글

string Format 이어서 사용하기  (0) 2014.01.17
Control Location 사용법 알아보자  (0) 2014.01.17
SourceGrid2 사용하기 모든것  (0) 2014.01.17
Tab Control index change  (0) 2014.01.17
textbox 글자넣기  (0) 2014.01.17

+ Recent posts