소년포비의 세계정복!!

[C#] C/C++ 구조체와 함수를 사용하기 본문

카테고리 없음

[C#] C/C++ 구조체와 함수를 사용하기

소년포비 2009. 10. 29. 14:56

기존의 C/C++ 의 구조체와 함수들을 C# 의 클래스로 캡슐화하는 작업을 설명드립니다.

먼저, C/C++ 에서 DLL 프로젝트를 생성합니다. (프로젝트 이름을 'BND.Native' 라고 하면 출력물은 'BND.Native.dll' 이 됩니다.)

1단계: C/C++ 구조체 정의 (MD5.h)

typedef struct mD5Context

{

    UINT state[4];   /* state (ABCD) */

    UINT count[2];   /* number of bits, modulo 2^64 (lsb first) */

    BYTE buffer[64]; /* input buffer */

}

MD5Context;

구조체 내부에 고정 크기의 배열이 정의되어 있습니다.

2단계: C/C++ 함수 정의 (MD5.cpp)

void MD5Init(MD5Context * context)

{

    ...

}

void MD5Update(MD5Context * context, BYTE * input, UINT input_size)

{

    ...

}

void MD5Final(MD5Context * context, BYTE * output_digest)

{

    ...

}

C/C++ 언어답게(?) 포인터를 마구 사용하고 있습니다.

3단계: C/C++ 함수 선언 (MD5.h)

extern "C" __declspec(dllexport) void MD5Init(MD5Context * context);

extern "C" __declspec(dllexport) void MD5Update(MD5Context * context, BYTE * input, UINT input_size);

extern "C" __declspec(dllexport) void MD5Final(MD5Context * context, BYTE * output_digest);

함수들을 DLL 외부로 노출합니다.

이를 위해서 #include <Windows.h> 라인을 추가해야 합니다.

이상의 3단계를 작업한 후 컴파일하여 C/C++ DLL 을 생성합니다.

다음으로 C# 프로젝트를 생성합니다.

C# 프로그램에서 C/C++ DLL 을 참조하기 위해  [C# 프로젝트의 속성] - [빌드 이벤트] - [빌드 후 이벤트 명령줄] 에 다음을 추가합니다.

COPY /Y "$(SolutionDir)BND.Native$(ConfigurationName)BND.Native.dll" "$(TargetDir)BND.Native.dll"

C/C++ DLL 파일을 C# 프로젝트의 출력 디렉토리로 복사해 오는 것입니다.

이를 위해서 프로젝트 '종속성'에 'BND.Native' 프로젝트를 추가해야 합니다.

4단계: 구조체 캡슐화

    public class MD5Calculator

    {

        // C/C++ 의 구조체와 동일한 형태가 되도록 정의합니다.

        public struct MD5Context

        {

            [MarshalAs(UnmanagedType.ByValArray, SizeConst =  4)] public UInt32[] state;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst =  2)] public UInt32[] count;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public byte[] buffer;

        }

        // 클래스 내부에 구조체 인스턴스를 캡슐화합니다.

        private MD5Context mContext = new MD5Context();

        // 구조체를 초기화하는 코드를 생성자 내부에 작성합니다.

        public MD5Calculator()

        {

            /* 따로 new 하지 않아도 이미 공간이 확보되어 있음.

            mContext.state = new UInt32[4];

            mContext.count = new UInt32[2];

            mContext.buffer = new byte[64];

            */

        }

C/C++ 에서 정의한 구조체와 동일한 구조체를 정의하고, 그 인스턴스를 정의하고, 초기화 코드를 작성하였습니다.

이 작업들을 편의상 하나의 클래스(MD5Calculator) 내에 캡슐화하였습니다.

5단계: 함수 참조 선언

        // C언어 함수들

        [DllImport("BND.Native.dll")] extern public static void MD5Init(ref MD5Context ctx);

        [DllImport("BND.Native.dll")] extern public static void MD5Update(ref MD5Context ctx, byte[] input, int input_size);

        [DllImport("BND.Native.dll")] extern public static void MD5Final(ref MD5Context ctx, byte[] output_digest);

각종 포인터 파라미터가 위와 같이 매핑됩니다.

6단계: 함수 캡슐화

        public void Initial()

        {

            MD5Init(ref mContext);

        }

        public void Update(byte[] input, int input_size)

        {

            MD5Update(ref mContext, input, input_size);

        }

        public byte[] Final()

        {

            byte[] output_digest = new byte[16];

            MD5Final(ref mContext, output_digest);

            return output_digest;

        }

구조체를 캡슐화 했듯이, 함수들도 캡슐화해 줍니다