C#에서 Win32 API
사용하기
개요
Win32 API를 불러올
때, 함수의 명칭, 인자,
리턴 값을 가지고 불러오게 되어 있다. 하지만,
C#에서 타입들이 모두 객체(Object)의 형식이며,
일반적인 C 의 데이터 형과 상이한 모양을 가진다.
이러한 문제들을 해결할 수 있는 것이 PInvoke 기능이다.
PInvoke( Platform Invocation Service)는 관리화 코드에서 비관리화 코드를 호출할 방법을 제공한다.
일반적인 용도는 Win32 API의 호출을 위해 사용한다.
namespace PinvokeExample { using System;
using System.Runtime.InteropServices; // 반드시 입력해야 한다.
public class Win32
{
[DllImport(“user32.dll”)] public static
extern int FindWindow(string a, string b);
…
} } |
위
예제는 FindWindow라는
user32.dll의 C함수를 사용하는 모습을 보여주고 있다. 실제 FindWindow의 선언은 다음과 같다.
HWND
FindWindow(LPCSTR swClassName, LPCSTR
swTitle); |
HWND는 윈도우 핸들을
표현하는 32비트 정수 이므로, int형으로
치환되고 LPCSTR 형은 NULL로 끝나는 문자열을
표현한다. 이때 PInvoke는 string을 자동으로 LPCSTR로 치환해 주는 역할을 하게
된다.
이
문서에서는 이처럼 Win32 API 함수의 여러 유형들을 어떻게
C#에서 사용 할 것인지에 대하여 알아보자.
WIN32 데이터형의
치환
Win32 API에서 일반적으로
사용하고 있는 데이터형은 모두 C#의 데이터 형으로 치환될 수 있다.
Win32 API
TYPE |
C# |
BOOL, BOOLEAN |
bool |
BYTE |
byte |
CALLBACK |
delegate |
COLORREF |
int |
DWORD |
int |
DWORD_PTR |
long |
DWORD32 |
uint |
DWORD64 |
ulong |
FLOAT |
float |
HACCEL |
int |
HANDLE |
int |
HBITMAP |
int |
HBRUSH |
int |
HCONV |
int |
(모든 HANDLE 타입)
Hxxxx |
int |
LPARAM |
long |
LPCSTR |
[in] string [out]
StringBuilder |
LPBOOL |
ref bool |
이외 LP* 형 |
ref 형식 |
UINT |
uint |
Uxxxx |
unsigned 타입들.. |
WORD |
Short |
WPARAM |
Uint |
Structure 의
전달
예를
들어 POINT 형의 경우,
typedef struct t_Point { int
x; int y; } POINT; |
이것은
기본적으로 다음과 같이 선언될 수 있다.
[순차적] [StructLayout(LayoutKind.Sequential)] public struct Point { public int x; public int y; }
[명시적] [StructLayout(LayoutKind.Explicit)] public struct Point { [FieldOffset(0)] public int x; [FieldOffset(4)] public int y; } |
일차적으로 할당되는 메모리 레이아웃이 동일하다면,
C#에서 바로 받아 들이 수 있다.
// BOOL SetWindowPos(POINT pos); 이런 함수가 있다고 가정하면…
^^ [DllImport (“user32.dll”)] public static extern bool SetWindowPos(Point
pos); |
사용할 함수 이름 바꾸기
여기서
함수의 이름을 바꿔서 사용하고 싶다면 다음과 같이 변경하면 된다.
// BOOL SetWindowPos(POINT pos); [DllImport (“user32.dll”, EntryPoint =
“SetWindowPos”)] public static extern bool ShowAt(Point
pos); |
레퍼런스형 전달하기
LPPOINT형은 POINT의 포인터 형이므로 ref Point와 같이 사용 할 수
있다. 실제 사용하는 형식은 다음과 같다.
C 언어의 포인터의 경우 레퍼런스로
사용하려고 하면, ref 키워드를 사용하는 방법이 있다.
// BOOL SetWindowPos(HWND hWnd, LPRECT
lpRect); [DllImport(“user32.dll”)] public static extern bool SetWindowPos(int hWnd,
ref Rect lpRect); |
Out형 함수 인자
사용하기
MSDN 같은 곳에서 함수의 선언을
살펴보면 다음과 같은 형식의 함수를 볼 수 있을 것이다. 이러한 형식은 레퍼런스 형으로 결과를 함수의
인자에 보내겠다는 말이다. 이러한 형식은 Win32
API에서 많이 쓰이고 있고, 포인터를 사용하므로,
많은 주의를 기울여야 한다.
BOOL GetWindowRect( HWND hWnd, // handle to window LPRECT lpRect // window coordinates ); Parameters
hWnd
[in]
Handle to the window. lpRect
[out]
Pointer to a RECT structure
that receives the screen coordinates of the upper-left and lower-right corners
of the window. |
여기서 LPRECT는 앞 절에서 설명한 Structure의 전달을 참고하여 치환 될 수 있다.
여기서 lpRect는
RECT의 포인터이며, GetWindowRect 함수 내에서 이 포인터에 직접 값을 쓰게
되어 있다. 즉 이 포인터는 값을 기록하기 위한 인자이지,
값을 전달하기 위한 인자는 아닌 것이다. 이것은 또 다른
C# 레퍼런스 연산자인 out
키워드를 사용하여 쉽게 해결 할 수 있다.
public static extern bool GetwindowRect(int hWnd,
out Rect lpRect); |
실제
사용하는 모습은 다음과 같다.
public static extern bool GetWindowRect(int hWnd,
out Rect lpRect); … public static void UseFunction() { Rect
_rect; // 값을 대입하지 않아도 된다.
Win32.GetWindowRect(hwnd, out _rect); … } |
참고로
ref 키워드는 입력과 출력 둘 다 사용 할 수 있다.
그러나 ref를 사용하는 변수가 값이 설정되어 있다는 가정을 하고 있으므로, 이전에 반드시 어떠한 값을 입력해야 한다.
실제
사용 예는 다음과 같다.
public static extern bool GetWindowRect(int hWnd,
ref Rect lpRect); … public static void UseFunction() { Rect
_rect = new Rect(); // 꼭 값을 대입해야 한다.
_rect.top
= 20; _rect.left = 30;
_rect.bottom = 50; _rect.right = 60;
Win32.GetWindowRect(hwnd, ref _rect); … } |
여기서 잠깐
대중없이 Rect라는 구조체가 나오는데 이는 API에서 RECT형을
C#으로 바꾸어 사용하는 structure이다.
앞의 예제들은 다음과 같은 선언을 하였다고 가정한다.
[StructLayout(LayoutKind.Explicit)] public struct Point { [FieldOffset(0)] public int top; [FieldOffset(4)] public int left; [FieldOffset(8)] public int bottom; [FieldOffset(12)] public int right; } |
CALLBACK 함수의
선언
C 언어에서 콜백 함수는 함수
포인터로 존재하게 된다. 이것은 함수 인스턴스의 포인터로,
함수 자체를 전달하게 되는 방식이다. 대표적으로 사용되는 부분은 EnumWindows 함수이다.
//
BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARMAM IParam) |
이
함수는 현재 열려 있는 모든 윈도우 핸들을 열거하기 위한 함수로 실제 처리하는 부분은 함수 포인터,
즉 콜백함수인 lpEnumFunc에서 처리하게 되어 있다.
WNDENUMPROC 타입의 선언은 다음과 같다.
// typedef BOOL (CALLBACK* WNDENUMPROC)(HWND,
LPARAM); public delegate bool Callback(int hWnd, long
lParam); |
이러한
콜백 함수 역할을 하는 C#의 프리미티브는 delegate이다. CALLBACK은 delegate로 지환된다.
결과적으로 다음과 같이 사용하게 된다.
namespace ada.appshare { …
public delegate bool Callback(int hwnd, int
lParam);
…
internal class Win32
{
… internal static
extern int EnumWindows(CallBack x, int y);
[DllImport("user32.dll")]
public static bool EnumWindowsCallback(int hWnd, int
lParam)
System.Console.WriteLine(“” + hWnd);
return true;
}
…
} public
static void Main(String []args)
{ Win32.Callback call
Win32.EnumWindows(call, 0);
} } |
출처 : 데브피아~~~~