C# A.dll을 하나 만들고, C# Main에서 A.dll을 참조하여 사용하는 프로그램에서

A.dll에서 delegate를 클래스로 제공하고

Main에서 A.dll에서 제공하는 클래스의 delegate 함수에 등록 후

A.dll의 함수를 사용하여 Main의 함수를 구동하고자 한다.

 

// SC Manager라는 Class Library를 만들며,

// 이안에는 delegate 함수 포인터 사용을 위한 생성부와

// Start() 함수를 이용한 Thread 구동 방법이 있다.

// Sc_Manager.dll 내부 구조

public class Sc_Manager
{
Thread m_ScThreadID = null;

// USER에게 제공해 주기 위한 함수 포인터 선언.
public delegate void ScReg();
public ScReg[] Sc_Mng;
public int Sc_MaxCnt = 0;
public string Sc_Name = string.Empty;
public bool bStillAlive = false;

public Sc_Manager(int nScCnt = 1)
{
Sc_MaxCnt = nScCnt;

// Scenario 개수만큼 함수포인터 생성
Sc_Mng = new ScReg[Sc_MaxCnt];
}

public bool Start()
{
if (bStillAlive == true) return true;

if (m_ScThreadID != null)
{
m_ScThreadID.Abort();
m_ScThreadID = null;
}

m_ScThreadID = new Thread(Run_ScenarioThreading);
m_ScThreadID.Start();

return true;
}

// threading processing.
void Run_ScenarioThreading()
{
bool nExecute = true;

do
{
if (bStillAlive == true) break;

bStillAlive = true;

int nMaxCnt = Sc_MaxCnt;
for (int SCnt = 0; SCnt < nMaxCnt; SCnt++)
{
// 본 프레임워크에서 함수 포인터로 함수를 실행시켜야 한다.
if (Sc_Mng[SCnt] != null)
Sc_Mng[SCnt]();
}

bStillAlive = false;
break;

} while (nExecute);
}
}

 

// 상기 Sc_Manager.dll을 사용하는 Main 부

private void btnLoadSc_Click(object sender, EventArgs e)
{
// Create Scenario Manager
Sc_Manager Scenario = new Sc_Manager(1);
Scenario.Sc_Name = "LoadControllers";
Scenario.Sc_Mng[0] = new Sc_Manager.ScReg(Load_Step_1);
Scenario.Start();
}

 

delegate를 활용하니 참 좋당^^

 

 

C#에서 ini 파일을 R/W 하기 위해서는 비관리 함수를 extern으로 가져와야 한다.

 

#region DllImport 사용 ini 파일에서 정보 가져오거나 Write하기
public class iniRW
{
public static string m_sFilePath = @"C:\TEST\Table.ini";
[DllImport("kernel32.dll")]
public static extern int GetPrivateProfileString(
string section,
string key,
string def,
StringBuilder retVal,
int nSize,
string lpFilePath);

[DllImport("kernel32.dll")]
public static extern long WritePrivateProfileString(
string section,
string key,
string val,
string lpFilePath);
}

#endregion

 

 

// ReadINI
public string ReadINI(string sID, string sKey, string sDef="")
{
if (!System.IO.File.Exists(iniRW.m_sFilePath)) return null;

StringBuilder sb = new StringBuilder(1024);
iniRW.GetPrivateProfileString(sID, sKey, sDef, sb, sb.Capacity, iniRW.m_sFilePath);
return sb.ToString();
}

 

// WriteINI
public bool WriteINI(string sID, string sKey, string sVal)
{
if (!System.IO.File.Exists(iniRW.m_sFilePath)) return false;
iniRW.WritePrivateProfileString(sID, sKey, sVal, iniRW.m_sFilePath);
return true;
}

 

 

작업하다보면 많은 다름이름을 갖은 컨트롤을 하나씩 제어하는게 번거로운 일이다

이에 다음과 같이 사용되는 컨트롤을 배열에 넣어서 관리하고자 한다.

 

하기 예제는 GroupBox8개, 각 GroupBox내에 TextBox 8개, PictureBox 8개 정보를

컨트롤러 배열에 넣고 제어하기 위한 코드이다.

 

// Form Loading과 동시에 GroupBox에 설정된 PicBox1-8, Intxtbox1-8 control를 배열에 담는다.
GroupBox[,] group = new GroupBox[2, m_nGrpBoxCnt];
group[0, 0] = InGrpbox1; group[0, 1] = InGrpbox2;

group[0, 2] = InGrpbox3; group[0, 3] = InGrpbox4;
group[0, 4] = InGrpbox5; group[0, 5] = InGrpbox6;

group[0, 6] = InGrpbox7; group[0, 7] = InGrpbox8;

group[1, 0] = OutGrpbox1; group[1, 1] = OutGrpbox2;

group[1, 2] = OutGrpbox3; group[1, 3] = OutGrpbox4;
group[1, 4] = OutGrpbox5; group[1, 5] = OutGrpbox6;

group[1, 6] = OutGrpbox7; group[1, 7] = OutGrpbox8;

 

for (int Sep = 0; Sep < 2; Sep++) // Input / Output 구분
{
for (int cnt = 0; cnt < m_nGrpBoxCnt; cnt++) // Group box 구분(1~8)
{
GroupBox tempGrp = new GroupBox();
tempGrp = group[Sep, cnt];
for (int i = 0; i < tempGrp.Controls.Count; i++) // group box내에 있는 control
{
if (tempGrp.Controls[i].GetType() == typeof(PictureBox))
{
string szCtlrName = tempGrp.Controls[i].Name.ToString();
// picture box name different (Inpicbox1-64/OutPicBox1-64)
int nCtlNo = 0;
if (Sep == 0) nCtlNo = Convert.ToInt32(szCtlrName.Remove(0, 8));
else nCtlNo = Convert.ToInt32(szCtlrName.Remove(0, 9));
m_picStsbox[Sep, nCtlNo - 1] = (PictureBox)tempGrp.Controls[i];
}
else if (tempGrp.Controls[i].GetType() == typeof(TextBox))
{
string szCtlrName = tempGrp.Controls[i].Name.ToString();
int nCtlNo = Convert.ToInt32(szCtlrName.Remove(0, 8));
m_txtIOStr[Sep, nCtlNo - 1] = (TextBox)tempGrp.Controls[i];
}
} // for (int i = 0
} // for (int cnt
} // for (int Sep

 

C#에서 using 사용하기
하기 사이트에서 발췌한 글

2012/04/13 08:52

복사 http://blog.naver.com/minsoub/60160239394

오랫동안 C++로만 작업을 하다가 C#을 대충 익혀쓰는 사람들에게 일어나는 실수.
소멸자 호출...

아래 코드에서 소멸자는 언제 불리나요?

1
2
3
4
5
void func()
{
SomeObject obj = new SomeObject();
obj.someFunction();
}

 

 

 

C#의 class 들은 대부분 new 를 통해서 생성이 되죠... 물론 아닌 것도 있긴 합니다. string 같은 것들.
그런데 delete는? Garbage Collection에 의해 이뤄지겠죠?
그리고 그 상황에 Finalize()가 호출되고 C#의 문법에 따라 소멸자를 통해 인스턴스를 정리하게 됩니다.

따라서 System.IO.FileStream 과 같은 객체의 인스턴스의 소멸자는 언제 불릴지 모르고.
소멸자에 의한 열린 파일 핸들을 닫는 행위는 언제가 될지 모르게 됩니다.

이런 부분을 C#에서 해결하기 위한 인터페이스가 IDisposable 입니다.
그리고 이 인터페이스를 구현한 클래스를 사용할 때에 편리한 것이 using 키워드입니다.

1
2
3
4
5
6
7
void func()
{
using (SomeObject obj = new SomeObject())
{
obj.someFunction();
}
}

 

 

 

이렇게 작성을 하면 아래와 같은 결과입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void func()
{
SomeObject obj = null;
try
{
obj = new SomeObject();
obj.someFunction();
}
finally
{
if (obj != null)
((IDisposable)obj).Dispose();
}
}

 

 

 

확실히 using이 간단해 보입니다.
어떤 부분에 의해 그렇게 구현되어 있는지 모르지만 System.Drawing.Font 와 같은 클래스도 이렇게 해야 됩니다.
어떤 클래스를 사용하실 때에는 반드시 해당 클래스가 IDisposable 인터페이스를 구현했는지 확인하시고,
IDisposable 인터페이스를 구현한 클래스라면 되도록 using 문을 사용하여 코딩하세요.

 

<meta name="GENERATOR" content="DEXTWebEditor" /><meta name="GENERATOR" content="DEXTWebEditor" />

 

데브피아에서 발췌한 내용입니다.^^

 

C# 스레드사용시 메서드이름이필요합니다?;오류가떠요 | WinForm Program

 

2012-02-14 오후 12:13:51
꿈달 번호: 143277 추천:0 / 읽음:150

이상하네요

분명 틀린게없는데.... 왜저렇게 메서드 이름이 필요합니다라고 뜨는지

 

gongswat(int[] rnd_x,int[] rnd_y, Graphics g)

{

생략
}

 

이걸 스레드로 돌릴려고하는데 왜 자꾸 저런오류가 뜨는지 ㅠㅠ....

쫌알려주셧으면합니다..

[채택답변] Thread 함수를 넘겨주어야 합니다.
0
2012-02-14 오후 12:52:09
질문자 인사 : 감사합니다 이해하도록 노력하고 한번 써봐야겟어요!
정성태 (kevin25) 번호: 143278

ThreadStart 는 delegate 타입입니다. 따라서, 함수명을 넘겨주어야 하는데, 함수를 호출하듯이 구문을 넣어주었으니 그런 문제가 발생합니다.

 

따라서, 다음과 같이 바꿔야 합니다.

 

new ThreadStart(gongswat);

 

그런데, 틀린 것이 또 있군요. ThreadStart 의 signature는 void func() 라서, gongswat 함수를 다음과 같이 바꿔야 합니다.

 

void gongswat()

{
...

}

 

다시 문제로 돌아가서, 함수를 생성하는 시점에 rnd_x, rnd_y, g 파라미터를 전달해야 하는데, 이걸 어떻게 해야 할까요? 이를 위해 .NET 2.0 부터 ParameterizedThreadStart delegate 가 새롭게 추가된 것입니다. 따라서, 최종적으로 다음과 같이 구성해 주어야 합니다.

 

Thread t1 = new Thread(new ParameterizedThreadStart(gongwat));

MyParam param =new MyParam();

param.rnd_x = rnd_x;

param.rnd_y = rnd_y;

param.g = g;

t1.Start(param);

 

void gongswat(object state)

{

MyParam param = state as MyParam;

 

...

}

 

Partial Class를 계층적으로 보이게 하는법

 

 

예로 frmTest라는 WinForm을 프로젝트에 추가하면

frmTest.cs frmTest.designer.cs frmTest.resx 파일이 생성된다.

 

구조는 다음과 같이 생성된다.

 

그럼 frmTest에 또 다른 partial class frmTest.mypartial.cs를 만들어 보자

만들면 다음과 같은 코드가 만들어 진다.

 

class frmTest
{
}

 

 

아래와 같이 만들면 frmTest의 partial class 가 된다.

public partial class frmTest
{

}

 

 

그래도 문제는 남아 있다 .

다음과 같이 함께 묶여 있지 않다.

 

test.csproj 파일을 열어보면 다음과 같다.

<Compile Include="frmTest.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="frmTest.Designer.cs">
<DependentUpon>frmTest.cs</DependentUpon>
</Compile>
<Compile Include="frmTest.mypartial.cs">
<SubType>Form</SubType>
</Compile>

 

 

아래와 같이 바꾸고 새롭게 로드하면

<Compile Include="frmTest.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="frmTest.Designer.cs">
<DependentUpon>frmTest.cs</DependentUpon>
</Compile>
<Compile Include="frmTest.mypartial.cs">
<DependentUpon>frmTest.cs</DependentUpon>
</Compile>

 

 

제대로 된 모양의 partial class 모양이 나온다.

 

frmTest.mypartial.cs 는 frmTest의 모든 변수를 사용할 수 있다.

가끔 사용하는 아주 유요한 partial 이다.

[출처] partial class를 계층적으로 보이게 하는 방법|작성자 길상

자, 프로젝트를 만듭니다.

프로젝트명 : PartialLesson

추가한클래스 : TestClass

 

Partial 이란, 부분이라는 뜻이지요. 즉, 클래스의 부분만 정의한다는 뜻입니다.

아래와 같이 기본적인 클래스에...

 

Partial 이란 키워드를 붙여줍니다.

테스트를 위해 alpha 라는 필드를 만들어주었답니다 ~_~

그리고, 메인으로 와서요 ㅇㅅㅇ...!

위에, 역시 같은 이름으로 partil class TestClass 라고 만들어주었어요 ㅇㅅㅇ...

아래와 같이 메소드를 구현했는데요.

이 자체에는 alpha 라는 녀석이 없습니다.

alpha 는 다른 쪽에 있지요. 그래도 이 둘을 별개로 구분하지 않고, 같은 것으로 쳐서 에러가 나지 않습니다.

이렇게 partial 키워드를 쓰면, 따로따로 띄어서 클래스를 만들 수 있답니다.

허용 범위는 "컴파일이 한번에 진행되는 모든 곳..!"이랍니다.

여러 인원이 작업을 할 때 한 거대한 클래스가 있다면, 분담해서 작업할 때 쓰면 좋겠지요 ~_~

테스트를 해봅시다. 인스턴스를 만들구요 ~

실행시켰습니다. ㅇㅅㅇ... 간단하죠 ?

이렇게.. 오늘 강의 종료 OTL...

 

 

Queue를 사용하고자 다음과 같이 MyQueue를 만들었다.

MyQueue를 통하여 3가지 변수 type의 데이터를 R/W 할 수 있었당~~!!

 

 

public class MyQueue
{
Queue<int> mtype;
Queue<int> mMsg;
Queue<int> mECode;
public MyQueue()
{
mtype = new Queue<int>();
mMsg = new Queue<int>();
mECode = new Queue<int>();
}

public void Enqueue(int type, int msg, int ecode)
{
mtype.Enqueue(type);
mMsg.Enqueue(msg);
mECode.Enqueue(ecode);
}

public void Dequeue(out int type, out int msg, out int ecode)
{
type = mtype.Dequeue();
msg = mMsg.Dequeue();
ecode = mECode.Dequeue();
}
}

 

 

원문: 하기 사이트에서 발췌한 내용입니다. 감사합니다.

 

http://blog.daum.net/coolprogramming/108

이번 시간은 클래스의 메소드에 대한 이야기입니다.

클래스는 크게 필드(정적인 상태를 표현)와 메소드(동적인 기능을 표현)로 구성됩니다. 대부분의 클래스와 객체에서 필드는 캡슐화되어 사용자(client)에게 감추어지기 때문에 필드보다는 메소드가 그만큼 중요한 요소가 됩니다. 또 대부분 인터페이스(서버와 클라이언트 사이의 약속과 통신)는 메소드를 사용합니다.

 

C#에서 서버와 클라이언트가 통신(메시지를 주고받기)하기 위해서는 인터페이스를 사용해야 하는데 이 인터페이스는

  • public 필드 : 캡슐화를 위배하므로 잘 사용되지 않으며 정적이므로 값만을 주고받을 수 있음.
  • public 메소드 : 대부분의 인터페이스가 이에 해당함.
  • public 속성 : C# 문법으로 제공하는 특수한 메소드 05. C# 클래스 참고.

이라 할 수 있습니다.

1, 접근 한정자(Access Modifiers)

우선 형식의 멤버에 지정될 수 있는 접근 한정자는 5가지입니다.

  • private : 동일한 클래스 내에서만 접근 가능합니다.(지정하지 않으면 기본으로 private)
  • public : 클라이언트 코드 어디에서도 접근 가능합니다.
  • protected : 동일한 클래스와 파생 클래스에서 접근 가능합니다.
  • internal : 동일한 어셈블리에서만 접근 가능합니다.
  • protected internal : 동일한 어셈블리와 다른 어셈블리 파생 클래스에서 접근 가능합니다.

형식에 지정될 수 있는 접근 한정자는 3가지입니다.

  • public : 클라이언트 코드 어디에서도 이 형식을 사용할 수 있습니다.
  • internal : 동일한 어셈블리에서 이 형식을 사용할 수 있습니다. (지정하지 않으면 기본으로 internal)
  • private : 중첩 형식에만 지정하여 외부 형식에서만 사용할 수 있습니다.

접근 한정자 = > http://msdn.microsoft.com/ko-kr/library/ms173121.aspx

접근한정자.PNG

2, 메소드 매개변수 한정자(in, out, ref, params)

매개변수는 변수의 사용 목적에 따라 크게 2가지로 나뉩니다.

  • 출력 매개변수(out parameter): 서버에서 클라이언트로 값을 전달할 목적으로 사용됩니다.
  • 입력 매개변수(in parameter): 클라이언트에서 서버로 값을 전달할 목적으로 사용됩니다.

 

보통 메소드는 매개변수를 사용하여 입력을 받고 리턴을 사용하여 출력하지만 출력 매개변수를 사용해서도 출력할 수 있습니다.

아래는 리턴과 출력 매개변수를 사용한 값 출력 그림입니다.

리턴을사용한출력.PNG

출력_매개변수를사용한출력.PNG

 

C# 문법에서도 입력, 출력 매개변수를 구분하기 위한 키워드를 제공합니다.

  • 입력 매개변수

    • 키워드 생략(없음): 입력 매개변수로 사용됩니다.
  • 출력 매개변수

    • out : 출력 매개변수로만 사용됩니다.
    • ref : 입,출력 매개변수로 사용됩니다.

입,출력 매개변수는 값 형식과 참조 형식이 약간 다르게 동작합니다.

 

2.1, 값 형식의 입,출력 매개변수

첫째, 입력 매개변수(키워드 없음)

  1. using System;

    namespace NetGong

    {

    class Program

    {

    static void PrintInt(int num)

    {

    Console.WriteLine(num);

    }

    static void Main(string[] args)

    {

    int n = 10;

    PrintInt(n);

    }

    }

    }

10

num은 입력 매개변수로 num은 n의 복사본 값입니다. 그러므로 num이 혹 변경되더라도 원본 n은 전혀 변화가 없습니다.

값입력매개변수.PNG

 

 

둘째, 출력 매개변수(out)

  1. using System;

    namespace NetGong

    {

    class Program

    {

    static void Add(int c, int d, out int sum)

    {

    sum = c + d;

    }

    static void Main(string[] args)

    {

    int a=10, b=20;

    int n;

    Add(a, b, out n);

    Console.WriteLine(n);

    }

    }

    }

30

sum은 출력 매개변수(out)로 sum과 n은 같은 메모리 공간의 이름(변수)입니다. sum은 즉 n의 참조입니다. 그러므로 sum이 변경되면 n도 변경됩니다.

값출력(out)매개변수.PNG

 

셋째, 입,출력 매개변수(ref)

  1. using System;

    namespace NetGong

    {

    class Program

    {

    static void Increment(ref int num)

    {

    num++;

    }

    static void Main(string[] args)

    {

    int n=10;

    Console.WriteLine(n);

    Increment(ref n);

    Console.WriteLine(n);

    }

    }

    }

10
11

입,출력 매개변수 키워드 ref는 out과 동일하게 동작합니다. 단지 다른 점은 꼭 값이 초기화되어 있어야 합니다. 입력으로도 사용하고 출력으로도 사용하기 위한 변수니까요. 만약 초기화되지 않은 변수를 ref 인수로 전달하면 에러가 발생합니다. C#은 이렇게 사용 용도에 대한 명확한 문법을 제공합니다.

값입출력(ref)매개변수.PNG

그래서 ref 키워드는 Swap()처럼 입력 값을 매개변수로 받아 다시 출력하고자 할 때 사용합니다.

  1. using System;

    namespace NetGong

    {

    class Program

    {

    static void Swap(ref int a, ref int b)

    {

    int temp = a;

    a = b;

    b = temp;

    }

    static void Main(string[] args)

    {

    int n1 = 10, n2 = 20;

    Console.WriteLine("{0} {1}", n1, n2);

    Swap(ref n1, ref n2);

    Console.WriteLine("{0} {1}", n1, n2);

    }

    }

    }

10 20
20 10

결과는 간단!

Swap입출력매개변수.PNG

 

2.2, 참조 형식의 입,출력 매개변수

첫째, 입력 매개변수(키워드 없음)

  1. using System;

    namespace NetGong

    {

    class Point

    {

    private int x;

    public int X

    {

    get { return x; }

    set { x = value; }

    }

    private int y;

    public int Y

    {

    get { return y; }

    set { y = value; }

    }

    }

    class Program

    {

    static void PrintPoint(Point arg)

    {

    Console.WriteLine("({0}, {1})", arg.X, arg.Y);

    }

    static void Main(string[] args)

    {

    Point pt = new Point(){X=2, Y=3}; //05. 참고

    PrintPoint(pt);

    }

    }

    }

(2, 3)

arg는 입력 매개변수로 arg는 pt의 복사본입니다. 그러므로 arg를 혹 변경되더라도 원본 pt는 전혀 변화가 없습니다. 여기까지는 값 형식과 똑같습니다. 하지만, 참조 형식에서 주의할 점이 있습니다. arg와 pt는 같은 객체를 참조하고 있으므로 만약 arg가 가리키는 객체의 값을 변경하면 원본 객체 pt가 변경됩니다.

위 예제 메모리 그림입니다.

참조입력매개변수.PNG

 

  1. using System;

    namespace NetGong

    {

    class Point

    {

    private int x;

    public int X

    {

    get { return x; }

    set { x = value; }

    }

    private int y;

    public int Y

    {

    get { return y; }

    set { y = value; }

    }

    }

    class Program

    {

    static void PrintPoint(Point arg)

    {

    Console.WriteLine("({0}, {1})", arg.X, arg.Y);

    arg = new Point() { X = 5, Y = 8 };

    }

    static void Main(string[] args)

    {

    Point pt = new Point() { X = 2, Y = 3 };

    PrintPoint(pt);

    Console.WriteLine("({0}, {1})", pt.X, pt.Y);

    }

    }

    }

(2, 3)
(2, 3)

arg가 새로운 객체를 참조하더라도 pt는 여전히 원본 객체를 참조합니다.

위 예제 메모리 그림입니다.

참조입력매개변수2.PNG

 

 

하지만 arg를 사용하여 객체의 상태 값을 변경하면 같은 객체를 참조하고 있으므로 pt 원본 객체의 값이 변경됩니다.

  1. using System;

    namespace NetGong

    {

    class Point

    {

    private int x;

    public int X

    {

    get { return x; }

    set { x = value; }

    }

    private int y;

    public int Y

    {

    get { return y; }

    set { y = value; }

    }

    }

    class Program

    {

    static void PrintPoint(Point arg)

    {

    Console.WriteLine("({0}, {1})", arg.X, arg.Y);

    arg.X = 5;

    arg.Y = 8;

    }

    static void Main(string[] args)

    {

    Point pt = new Point() { X = 2, Y = 3 }; //05. uÆi

    PrintPoint(pt);

    Console.WriteLine("({0}, {1})", pt.X, pt.Y);

    }

    }

    }

(2, 3)
(5, 8)

arg와 pt가 같은 객체를 참조하므로 arg의 상태 값이 변경되면 pt 원본의 상태 값이 변경됩니다.

위 예제 메모리 그림입니다.

참조입력매개변수3.PNG

 

둘째, 출력 매개변수(out)

  1. using System;

    namespace NetGong

    {

    class Point

    {

    private int x;

    public int X

    {

    get { return x; }

    set { x = value; }

    }

    private int y;

    public int Y

    {

    get { return y; }

    set { y = value; }

    }

    }

    class Program

    {

    static void CreatePoint(out Point arg)

    {

    arg = new Point() { X = 2, Y = 3 };

    }

    static void Main(string[] args)

    {

    Point pt;

    CreatePoint(out pt);

    Console.WriteLine("({0}, {1})", pt.X, pt.Y);

    }

    }

    }

(2, 3)

위 예제의 메모리 그림입니다.

참조출력매개변수.PNG

 

셋째, 출력 매개변수(ref)

값 형식처럼 미리 초기화 후 출력 매개변수로 사용되어야 한다는 것입니다. 나머지는 out과 같습니다.

 

2.3, params 매개변수 한정자

params는 C나 C++의 가변 인자처럼 동작합니다. 하지만 더 강력한 기능을 제공합니다.

params 한정자는 배열을 사용하므로 배열을 공부한 후...

 

.....

 

마지막으로 인터페이스로 사용되는 속성은 05. C# 클래스를 참고하세요.

 

dll 함수로 데이터 보내는 일은 string으로 가능하지만

 

반대로 받는건 StringBuilder 를 이용해야 하네요.

 

[DllImport("test.dll")]

public static extern int sample(string in_param, StringBuilder out_param);

 

StringBuilder s = new StringBuilder(100); // 문자열 최대 크기를 잡아주세요.

 

if (sample("test", s) == 0)

MessageBox.Show(s.ToString());

 

 

발췌정보:

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=17&MAEULNo=8&no=77076&ref=77076

 

C#에서 C++이나 VB로 된 DLL을 Interop해서 쓰는 방식이 .NET으로 전환하는 요즘의 대세이건만

언제나 그 반대의 경우도 존재하게 마련이다.

 

C#에서는 Typelib를 tlbimp해서 dll을 참조하는 방법과

dllimport Attribute를 이용해서 dll을 참조하는 방법이 있는데

보통 typelib는 COM이나 ActiveX 형태의 dll을, dllimport는 extern이나 static으로

외부로 인터페이스를 제공하는 dll을 참조하는 경우이다.

 

이와달리, 이전글에서 C++에서 C# DLL을 호출하는 경우는

기존의 legacy 시스템에서 .NET의 특수한 기능(가령, Remoting이나 COM+ 등)을 이용하려는 경우

유용하게 쓰일수 있다.

 

앞글에서 설명한 요점을 살펴보면

1. C#으로 필요한 기능을 구현한다. 이때 dll은 COM으로 등록시킨다.

그러기 위해서 외부로 노출시킬 Interface를 생성하여 이를 상속받는 클래스를

생성한 후, GUID를 부여해야한다.

2. 생성된 C# dll을 regasm으로 등록하고 이때 생성된 *.tlb파일을 C++에서 import하고

Interface를 통해 클래스의 메소드를 호출한다.

 

요게 다구나..

 

비즈니스 로직이 C# DLL에 있는 경우에는 내부적으로 필요한 라이브러리들을

호출하여 이용하겠지만,

만약, C++ 코드에서 C#의 Class들을 이용해야 할 경우 mscorlib.tlb를 import하면

C++에서 C#의 클래스들을 이용할 수 있다.

---------------------------------------------------------------------------------------

[C#예제]

 

[Guid("11111111-1111-1111-1111-111111111111")]

public Interface ICSharpInterface

{

void CallCSharpMethod(string s);

}

 

[Guid("22222222-2222-2222-2222-222222222222")]

public class CSharpClass : ICSharpInterface

{

public void CallCSharpMethod(string s)

{

MessageBox.Show(s);

}

}

--------------------------------------------------------------------------------------

[C++ 예제]

#include "CSharpClass.tlb" no_namespace named_guids

 

ICSharpInterface *csi = NULL;
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(CLSID_CSharpClass,
NULL, CLSCTX_INPROC_SERVER, IID_ICSharpInterface, reinterpret_cast<void**>(&csi));

if (FAILED(hr))
{
printf("C# interface Initialize failed!");
CoUninitialize();
return FALSE;
}

 

try {
csi->CallCSharpMethod("인자값");
} catch (_com_error &e) {
printf("C# Method call error!");
return FALSE;
}

 

csi->Release();
CoUninitialize();
csi = NULL;

--------------------------------------------------------------------------------------

C# 예제를 컴파일하고

c:\>regasm CSharpClass.dll /tlb:CSharpClass.tlb

로 등록한다.

 

생성된 CSharpClass.tlb를 C++ 프로젝트 폴더에 두고 컴파일한다.

 

생성된 C++ 실행파일을 실행하면 C#의 Method가 Call되어 메시지박스가 호출된다

 

+ Recent posts