ウィンドウアプリケーション用のシンプルなWinAPIラッパーの作成

しばらく前に、C ++を使用してWindows用のウィンドウライブラリを作成するのが好きでした。 そして今日は、WinAPIの単純なラッパーを作成してウィンドウアプリケーションを作成する方法を説明します。



ご存知のように、ベアAPI上のアプリケーションは、メッセージを処理するためのWinProc関数のWinMain関数(ウィンドウアプリケーションのmainに類似)で構成されています。



クラスでAPI関数をラップする際の主な難点は、WinProcを非表示にして、消化可能なメッセージ処理システムを作成することです。



ラッパーは、CAppとCWndの2つのクラスで構成されます。 最初はアプリケーションクラスで、内部はメインメッセージループです。 2番目はウィンドウクラスです。



最初に、アプリケーションクラスを記述します。 それは非常に簡単です:

//

class CApp

{

public :

//

//

void Run()

{

MSG msg;

while (GetMessage(&msg,0,0,0)!=0)

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

};




* This source code was highlighted with Source Code Highlighter .






単一のRun関数が含まれており、その中にpromiseループがあります。

ループ内で、プログラムはメッセージを受信し、ウィンドウ関数(WinProc)にリダイレクトします。



次に、CWndクラスを作成します。

//

class CWnd

{

//

typedef LRESULT (CWnd::*FuncPointer)(LPARAM,WPARAM);



// -

struct POINTER

{

CWnd* wnd; // ,

FuncPointer func;

};



protected :

HWND _hwnd; //

map<UINT,POINTER> _msgmap;//




* This source code was highlighted with Source Code Highlighter .






これには、ウィンドウのハンドル(HWND)とメッセージのマップが含まれています。

また、ハンドラー関数(FuncPointer)とPOINTER構造体へのポインターの型を宣言します。 この構造体には、関数へのポインターと、それが属するクラスのオブジェクトへのポインターが含まれています。



次に、ウィンドウ作成関数を追加します。

//

bool Create(

HWND parent, // , 0 -

LPCWSTR text, //

DWORD exstyle,DWORD style, //

int x, int y, int w, int h, //

UINT id //

)

{

//

WNDCLASSEX wndc;

wndc.lpszClassName=L "MyWnd" ;

wndc.cbSize= sizeof (WNDCLASSEX);

wndc.lpfnWndProc=WNDPROC(_WndProc); //

wndc.cbClsExtra=0;

wndc.cbWndExtra=0;

wndc.hbrBackground=HBRUSH(COLOR_WINDOW); //

wndc.hInstance=GetModuleHandle(0); //

wndc.hCursor=LoadCursor(0,IDC_ARROW); //

wndc.style=CS_HREDRAW|CS_VREDRAW;

wndc.hIcon=0;

wndc.hIconSm=0;

wndc.lpszMenuName=0;

RegisterClassEx(&wndc);



//

_hwnd=CreateWindowEx(exstyle,L "MyWnd" ,text,

style|WS_CLIPCHILDREN, // WS_CLIPCHILDREN ,

x,y,w,h,parent,HMENU(id),

GetModuleHandle(0),

this //

);



if (!_hwnd) return false ;

return true ;

}




* This source code was highlighted with Source Code Highlighter .






その中で、ウィンドウクラスを登録し、CreateWindowEx関数を使用してウィンドウ自体を作成します。

また、CWndのインスタンスへのポインターをCreateWindowExに渡し、後でAPIハンドル(HWND)とCWndのインスタンスを関連付けることができるようにします。



それでは、要点を見てみましょう。

クラスにウィンドウ関数を追加します。 静的でなければなりません。

//

//

static LRESULT CALLBACK _WndProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)

{

CWnd *wnd=0;

// WM_NCCREATE WM_CREATE

//.

if (message==WM_NCCREATE)

{

// , CreateWindowEx

wnd=(CWnd*)LPCREATESTRUCT(lparam)->lpCreateParams;

// GWL_USERDATA

SetWindowLong(hwnd,GWL_USERDATA,LONG(LPCREATESTRUCT(lparam)->lpCreateParams));

wnd->_hwnd=hwnd;

}

// , GWL_USERDATA

wnd=(CWnd*)GetWindowLong(hwnd,GWL_USERDATA);

if (wnd)

{

//

map<UINT,POINTER>::iterator it;

it=wnd->_msgmap.find(message);



// ,

if (it==wnd->_msgmap.end()) return DefWindowProc(hwnd,message,wparam,lparam);

else

{

POINTER msg=it->second;

//

LRESULT result=(msg.wnd->*msg.func)(lparam,wparam);

if (result) return result;

}

}

return DefWindowProc(hwnd,message,wparam,lparam);

}

};




* This source code was highlighted with Source Code Highlighter .






その中で次のことが起こります。 まず、WM_NCCREATEメッセージをキャッチします。 その中で、CreateWindowExに渡されたポインターを取得し、ウィンドウのGWL_USERDATAフィールドに保存します。 これで、HWNDのみを手元に置いてCWndインスタンスへのポインターをいつでも取得できます。

次に、マップ内の現在のメッセージを探し、もしそうであれば、ポインターでこのマップからハンドラーを呼び出します。



次に、メッセージをマップに追加する関数を作成します。

//

// - T - CWnd

template<typename T>

bool AddMessage(UINT message,CWnd* wnd,LRESULT (T::*funcpointer)(LPARAM,WPARAM))

{

if (!wnd || !funcpointer) return false ;



POINTER msg;

msg.wnd=wnd;

msg.func=reinterpret_cast<FuncPointer>(funcpointer);



_msgmap.insert(pair<UINT,POINTER>(message,msg));



return true ;

}




* This source code was highlighted with Source Code Highlighter .






これは定型的な関数であり、次のことを行います。 CWndの下位クラスのメンバー関数へのポインターを、CWndのメンバー関数へのポインターに変換します。 これは、すべてのポインターを同じ型にキャストするために必要です。



以上で、ラッパーの準備が整いました。

使用例:

// CWnd

class CMyWnd: public CWnd

{

public :

CMyWnd()

{

// WM_CREATE WM_DESTROY

AddMessage(WM_CREATE, this ,&CMyWnd::OnCreate);

AddMessage(WM_DESTROY, this ,&CMyWnd::OnDestroy);

}

LRESULT OnCreate(LPARAM lparam,WPARAM wparam)

{

MessageBox(0,_T( "HelloHabr!" ),_T( "" ),0);

return 0;

}

LRESULT OnDestroy(LPARAM lparam,WPARAM wparam)

{

PostQuitMessage(0);

return 0;

}

};



int APIENTRY WinMain(HINSTANCE hinst,HINSTANCE prev,LPSTR cmd, int showcmd)

{

//

CMyWnd *wnd= new CMyWnd;

wnd->Create(0,L "HelloHabr!" ,0,WS_OVERLAPPEDWINDOW|WS_VISIBLE,300,300,500,400,0);



//

CApp *app= new CApp;

app->Run();

return 0;

}




* This source code was highlighted with Source Code Highlighter .






単純なウィンドウを作成するには、CWndから継承し、メッセージのハンドラーを追加し、それらをマップに追加すると、アプリケーションの準備が整います。








All Articles