본문 바로가기

TechLog

C#에서 Native DLL(WinAPI) 사용하기

C#에서 Native DLL(WinAPI) 사용하기

닷 넷 프레임워크는 관리되는 코드(managed code) 기반으로 프로그램을 실행합니다. 관리되는 코드는 기존 Win32 API같은 관리되지 않는 코드(unmanaged code)와 혼용해서 사용할 수 없습니다만, 닷넷 프레임워크는 System.Runtime.InteropServices 네임스페이스를 통해 관리되지 않는 코드를 호출할 수 있는 방법을 제공하고 있습니다.

 

 

안녕하세요 : )

 

닷 넷 프레임워크는 관리되는 코드(managed code) 기반으로 프로그램을 실행합니다. 관리되는 코드는 기존 Win32 API같은 관리되지 않는 코드(unmanaged code)와 혼용해서 사용할 수 없습니다만, 닷넷 프레임워크는 System.Runtime.InteropServices 네임스페이스를 통해 관리되지 않는 코드를 호출할 수 있는 방법을 제공하고 있습니다. 이 방법은 보통 Platform Invoke(줄여서 P/Invoke, PInvoke라고 부르곤 합니다)라고 불리며, 다음과 같은 과정을 통해서 호출됩니다(MSDN 참고) :

 

clip_image001

 

  1. 함수를 포함한 DLL을 찾는다

  2. DLL을 메모리에 로드한다

  3. 메모리상에 있는 함수의 주소를 찾고, 인자를 스택에 Push한다. 이 과정에서 마샬링(Marshaling)이 일어난다
       
    참고   DLL의 위치를 찾기/로드하는 과정과 함수의 주소를 찾는 과정은 함수가 처음으로 호출될 때만 일어난다.

  4. 제어를 관리되지 않는 코드로 옮긴다.

   (원문에서는 찾는다. 라는 단어가 Locate라고 나와 있는데 이걸 '위치를 정한다'라고 해야할지 '찾는다'라고 해야할지 애매해서.. 일단 '찾는다'로 적어 둡니다)

 

개념적인 부분은 간단히 이 정도로 하고, pinvoke의 예제를 보도록 하겠습니다.

 

그럼 시작합니다 : )

 

 

닷넷으로는 WinAPI/Native DLL을 호출할 수 없나요?

 

닷 넷 프레임워크가 포함하는 기능들을 msdn에서 살펴보자면, 일단 표시되는 목자만으로 기가 질릴 정도로, 닷넷 프레임워크는 정말로 거대한 런타임 환경이다. Visual Studio .net을 사용한다면 아주 넓은 영역의 개발을 커버할 수 있고, 단순히 엔터프라이즈 환경에서만이 아니라 어떤 환경에서든 효율적인 프로그래밍을 수행할 수 있도록 해 준다.

 

하지 만, 문제는 기존 시스템과의 연동이다. 기존에 만들어진 라이브러리는 물론이거니와, 닷넷에서 지원하지 않는 WinAPI 기능 또한 존재한다. (간단한 예를 들자면, 시스템의 시간을 설정하는 SetSystemTime같은 WinAPI가 닷넷에는 없다) 물론 cmd 쉘의 date, time 등을 이용하면 가능한 일이지만 기존에 만들어진 DLL을 사용해야 한다면, 해당 DLL의 함수를 호출하는 것 외에는 별다른 방법이 없다.

 

다음은 GetSystemTime()/SetSystemTime() WinAPI DllImport 키워드를 사용해 호출할 수 있도록 만드는 코드이다 :

 

using System.Runtime.InteropServices;

...

[DllImport("kernel32.dll")]

extern static private void GetSystemTime( ref SYSTEMTIME systemtime );

 

[DllImport("kernel32.dll")]

extern static private bool SetSystemTime( ref SYSTEMTIME systemtime );

 

struct SYSTEMTIME

{

    ushort wYear;

    ushort wMonth;

    ushort wDayOfWeek;

    ushort wDay;

    ushort wHour;

    ushort wMinute;

    ushort wSecond;

    ushort wMilliseconds;

}

 

DllImport 키워드는 어느 DLL로부터 해당 함수를 Import하는지 나타내주는 키워드이다.

 

GetSystemTime(), SetSystemTime() 함수의 원형은 다음과 같다 :

 

void GetSystemTime(

  LPSYSTEMTIME lpSystemTime

);

 

BOOL SetSystemTime(

  const SYSTEMTIME* lpSystemTime

);

 

위 두 함수는 SYSTEMTIME의 구조체 포인터를 파라메터로 사용하고 있기 때문에, ref 키워드를 사용해서 해당 개체를 참조하도록 해 주면 CLR 환경에서 알아서 해당개체를 관리되지 않는 코드에 전달해주게 된다. 단순히 값만 전달하는 경우에는 굳이 ref 키워드를 사용하지 않아도 된다.

 

구조체의 경우에는, DLL에 해당 구조체에 대한 선언이 들어있지는 않기 때문에, msdn을 참고하거나 헤더 파일을 참고해서 직접 struct로 해당 구조체를 적당히 선언해주어야 한다. SYSTEMTIME 구조체의 원형은 다음과 같다 :

 

typedef struct _SYSTEMTIME

{

    WORD wYear;

    WORD wMonth;

    WORD wDayOfWeek;

    WORD wDay;

    WORD wHour;

    WORD wMinute;

    WORD wSecond;

    WORD wMilliseconds;

} SYSTEMTIME;

 

WORD 16비트 부호 없는 정수이므로 ushort로 선언해주면 된다.

구조체가 아니라 const 등으로 상수가 정의되어 있는 경우에도 닷넷 코드에서 해당 상수를 정의해주어야 한다.

 

위와 같이 코드가 준비되면, 다음처럼 호출해서 사용할 수 있다 :

 

SYSTEMTIME systemtime = new SYSTEMTIME();

GetSystemTime(ref systemtime);

System.Diagnostics.Debug.WriteLine(string.Format("systemtime year : {0}", systemtime.wYear));

SetSystemTime(ref systemtime);

 

 

정리

 

사실 이 아티클은 춘하추동님의 VB.net에서 DLL사용하는 방법과 비슷한 내용을 담고 있습니다. 사실 쓰기 시작할 때에는 마샬링 부분에 대해서 자세하게 써 봐야겠다고 생각했는데, 일단 그건 좀 다음으로 미뤄둘까 합니다 ^^;

 

그럼 : )