이 글과 관련된 최신 글
2010/11/15 - 내 맘대로 엔진 제작기 - 23 ( DirectX in C# )
2010/08/06 - gcroot를 이용해 관리 클래스 managed class 를 멤버로 사용
2010/04/20 - Native C++ <-> Managed C++ <-> C# 의 상호교류
2009/09/07 - Native C++에서 Managed C++에 접근이 필요할 때
2010/11/15 - 내 맘대로 엔진 제작기 - 23 ( DirectX in C# )
2010/08/06 - gcroot를 이용해 관리 클래스 managed class 를 멤버로 사용
2010/04/20 - Native C++ <-> Managed C++ <-> C# 의 상호교류
2009/09/07 - Native C++에서 Managed C++에 접근이 필요할 때
요즘 한창 C#에서 빠져 살고 있습니다.
이거 쓰면 쓸수록 물건 갖네요. 특히 강력한 윈폼 기능은 정말 매력적입니다.
이걸로 툴을 제작한다면 왠지 뚝딱 뚝딱 만들수 있을것 같아요.
그래서 알아봤습니다.
현재 제가 스터디 삼아 제작하고 있는 엔진을 C#에 연결하고 싶어졌쬬.
어떻게 하면 Native C++로 작성된 엔진을 C#에 띄울수 있을까?
몇 가지 방법이 있지만, 가장 잘 알려진 랩핑 기법을 쓰기로 했습니다.
이것은 엔진단을 정적 라이브러리로 작성한 후 이것을 C++/CLI(Managed C++)로 랩핑하여 DLL로 작성합니다.
그 다음 빌드된 DLL을 C# 프로젝트에서 참조 추가한 후 사용하면 되는 식이죠.
조금 복잡하고, 중간 랩핑 과정이 귀찮지만, 강력한 C#의 윈폼을 쓸수 있다면 이정도는 감수해야겠죠.
간단히 구조를 살펴 보겠습니다. 엔진부분에 해당하는 Native C++쪽은 정적 라이브러리 프로젝트로 작성합니다.
대충 밑의 구조의 클래스가 있다고 칩니다.
class CFramework
{
public:
void Create( HWND hwnd )
{
// 핸들을 받아 D3D 디바이스 생성
}
};
{
public:
void Create( HWND hwnd )
{
// 핸들을 받아 D3D 디바이스 생성
}
};
원래라면 이곳에 윈도우를 생성하고 생성된 윈도우의 핸들을 가져다 Direct3D를 초기화 할 것입니다.
하지만 이미 C#에 윈폼으로 윈도우를 생성해두었으니 그 윈도우를 쓰면 됩니다.
여기서 한가지 유의 할점은 윈도우 자체의 핸들을 넘겨주면 Direct3D에서 화면을 못 그립니다.
대신 윈도우에 PictureBox 컨트롤을 만들어두고 그 PictureBox 컨트롤의 핸들을 넘겨줍니다. 그러면 Direct3D에서 PictureBox에, 화면을 그릴수 있게 됩니다.
다음은 랩핑 부분입니다.
랩핑 프로젝트는 DLL로 작성을 하게 됩니다.
프로젝트 생성 후 프로젝트 속성에서 공용 언어 런타임 지원(/clr)을 설정해줍니다.
// 엔진의 정적 라이브러리
#ifdef _DEBUG
#pragma comment ( lib, "Engine_d.lib" )
#else
#pragma comment ( lib, "Engine.lib")
#endif
// 랩핑 클래스 구현
#pragma once
#include "Framework.h" // 랩핑할 Native C++ 클래스 헤더를 참조
using namespace System;
namespace CWrapper {
public ref class CFrameworkWrapper
{
public:
CFrameworkWrapper();
~CFrameworkWrapper();
public:
void Create( IntPtr hWnd )
{
m_hHwnd = (HWND)hWnd.ToInt32();
m_pFrameWork = new CFramework();
m_pFrameWork->Run( m_hHwnd );
}
protected:
HWND m_hHwnd;
CFramework* m_pFrameWork;
};
}
#ifdef _DEBUG
#pragma comment ( lib, "Engine_d.lib" )
#else
#pragma comment ( lib, "Engine.lib")
#endif
// 랩핑 클래스 구현
#pragma once
#include "Framework.h" // 랩핑할 Native C++ 클래스 헤더를 참조
using namespace System;
namespace CWrapper {
public ref class CFrameworkWrapper
{
public:
CFrameworkWrapper();
~CFrameworkWrapper();
public:
void Create( IntPtr hWnd )
{
m_hHwnd = (HWND)hWnd.ToInt32();
m_pFrameWork = new CFramework();
m_pFrameWork->Run( m_hHwnd );
}
protected:
HWND m_hHwnd;
CFramework* m_pFrameWork;
};
}
보시면, 랩핑을 하기위한 정적 라이브러리를 불러오고 있습니다.
그리고 해당 클래스의 헤더를 불러와 클래스를 멤버변수로 선언해주고, Create 함수에서 C#에서 보내주는 핸들을 받아 Native 클래스 객체에 넘겨주고 있습니다. 그다지 어려운것 없죠. 이렇게 Create 함수를 랩핑이 완료되었습니다.
이제는 C# 부분입니다. Widnows 응용프로그램으로 작성되어있습니다.
디자이너 페이지에서 창의 크기를 조절해준다음 컨트롤 중 PictureBox를 만들어줍니다.
그리고 솔루션 탐색기에서 C# 프로젝트의 참조 부분에 참조 추가를 해줍니다.
참조 추가 창이 뜨면, [ 찾아 보기 ]탭으로 가서 랩핑 부분인 DLL ( 미리 빌드를 해줍니다 ) 을 추가합니다.
추가가 완료 되면 이제 C#에서 랩핑쪽에 작성해둔 클래스를 가져다 쓸수 있게 됩니다.
public partial class Mainform : Form
{
private CFrameworkWrapper m_Frameowrk;
public Mainform()
{
InitializeComponent();
}
public void InitializeFramework()
{
m_Frameowrk = new CFrameworkWrapper();
m_Frameowrk.Create(pictureBox1.Handle);
}
}
{
private CFrameworkWrapper m_Frameowrk;
public Mainform()
{
InitializeComponent();
}
public void InitializeFramework()
{
m_Frameowrk = new CFrameworkWrapper();
m_Frameowrk.Create(pictureBox1.Handle);
}
}
이것으로 모든 준비가 되었고, C#쪽을 빌드 후, 실행해보면 Native C++쪽에 작성한 화면이 C#의 윈도우 폼에 뜨는 것을 확인할수 있을겁니다.
주의할 점은 빌드 순서입니다.
항상 C++ -> CLI -> C# 순으로 빌드를 해주어야 하고, C++쪽이나 CLI쪽이 변경되면 항상 재빌드를 걸어주셔야 합니다.
그렇지 않으면 C#의 참조 DLL이 갱신 되지않아 원하는 결과가 나오지 않을수 있습니다.