|
今回はウィンドウをクラス化してみます 今回は3つのファイルを用意します ウィンドウクラスのヘッダーファイル(WindowClass.h)です //---------------------------------------//
// ウィンドウクラス(WindowClass.h)
//
// 作成者:Daiki Terai
// 作成日:2010/11/29
// 修正履歴:
//---------------------------------------//
#ifndef _WINDOWCLASS_H_
#define _WINDOWCLASS_H_
// ウィンドウズバージョン指定
#define _WIN32_WINNT 0x0500
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#pragma comment(lib,"comctl32.lib")
#pragma comment(lib,"winmm.lib")
static const TCHAR* WC_BASIC = _T("Basic"); // 基本ウィンドウクラス名
// ウィンドウクラス
class CSWindowClass
{
private:
static HINSTANCE m_hInstance; // インスタンスハンドル
HWND m_hWnd; // ウィンドウハンドル
static CSWindowClass* m_pThis; // 登録用
public:
// コンストラクタ
CSWindowClass();
// デストラクタ
virtual ~CSWindowClass();
// 基本ウィンドウクラスの登録
static bool RegistBasicWindowClass();
//--------------------------------------------------------------------//
// ウィンドウ作成
//
// 引数: Title タイトル
// Width 幅
// Height 高さ
// X X座標
// Y Y座標
// hParentWnd 親ウィンドウハンドル
// hMenu メニューハンドルもしくはリソースID
// ClassName ウィンドウクラス名
// Style ウィンドウスタイル
// ExStyle ウィンドウ拡張スタイル
//
// 戻り値: true: 作成成功
// false: 作成失敗
//--------------------------------------------------------------------//
virtual bool CreateWnd(
const TCHAR* Title,int Width = CW_USEDEFAULT,int Height = CW_USEDEFAULT,
int X = CW_USEDEFAULT,int Y = CW_USEDEFAULT,
HWND hParentWnd = NULL,HMENU hMenu = NULL,
const TCHAR* ClassName = WC_BASIC,
DWORD Style = WS_OVERLAPPEDWINDOW | WS_VISIBLE,DWORD ExStyle = 0);
private:
// ウィンドウプロシージャの管理者
static LRESULT CALLBACK WndProcManager(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);
// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);
public:
// メッセージループ
static void MsgLoop(int ArrayLength = 0,CSWindowClass* pWindowArray = NULL);
// メッセージ処理していないときにする処理
virtual void Idle();
// ウィンドウ作成メッセージ
virtual BOOL OnCreate(HWND hWnd,LPCREATESTRUCT lpCreateStruct);
// ウィンドウ破棄メッセージ
virtual void OnDestroy(HWND hWnd);
// ウィンドウ描画メッセージ
virtual void OnPaint(HWND hWnd);
// キー入力関係メッセージ
virtual void OnChar(HWND hWnd, TCHAR ch, int cRepeat);
// マウス関連メッセージ
virtual void OnLButtonDown(HWND hWnd, BOOL isDoubleClick, int x, int y, UINT keyFlags);
virtual void OnRButtonDown(HWND hWnd, BOOL isDoubleClick, int x, int y, UINT keyFlags);
virtual void OnMButtonDown(HWND hWnd, BOOL isDoubleClick, int x, int y, UINT keyFlags);
virtual void OnLButtonUp(HWND hWnd,int x, int y, UINT keyFlags);
virtual void OnRButtonUp(HWND hWnd,int x, int y, UINT keyFlags);
virtual void OnMButtonUp(HWND hWnd,int x, int y, UINT keyFlags);
virtual void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
virtual void OnMouseWheel(HWND hWnd, int xPos, int yPos, int zDelta, UINT fwKeys);
};
#endif // _WINDOWCLASS_H_
まずWindowClassのメンバ変数の説明です private: static HINSTANCE m_hInstance; // インスタンスハンドル HWND m_hWnd; // ウィンドウハンドル static CSWindowClass* m_pThis; // 登録用 m_hInstanceはインスタンスハンドル(アプリケーションのハンドル)で 基本ウィンドウクラス登録メソッド private: // ウィンドウプロシージャの管理者 static LRESULT CALLBACK WndProcManager(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam); // ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam); WndProcManagerメンバ関数のほうは、基本ウィンドウクラスの登録時に指定してあるウィンドウプロシージャです メッセージループの処理もクラス化しました // メッセージループ static void MsgLoop(int ArrayLength = 0,CSWindowClass* pWindowArray = NULL); // メッセージ処理していないときにする処理 virtual void Idle(); Idleに関してはメッセージ処理をしていないときにする処理を書きます // メッセージ処理していないときにする処理 virtual void Idle(); // ウィンドウ作成メッセージ virtual BOOL OnCreate(HWND hWnd,LPCREATESTRUCT lpCreateStruct); // ウィンドウ破棄メッセージ virtual void OnDestroy(HWND hWnd); // ウィンドウ描画メッセージ virtual void OnPaint(HWND hWnd); // キー入力関係メッセージ virtual void OnChar(HWND hWnd, TCHAR ch, int cRepeat); // マウス関連メッセージ virtual void OnLButtonDown(HWND hWnd, BOOL isDoubleClick, int x, int y, UINT keyFlags); virtual void OnRButtonDown(HWND hWnd, BOOL isDoubleClick, int x, int y, UINT keyFlags); virtual void OnMButtonDown(HWND hWnd, BOOL isDoubleClick, int x, int y, UINT keyFlags); virtual void OnLButtonUp(HWND hWnd,int x, int y, UINT keyFlags); virtual void OnRButtonUp(HWND hWnd,int x, int y, UINT keyFlags); virtual void OnMButtonUp(HWND hWnd,int x, int y, UINT keyFlags); virtual void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags); virtual void OnMouseWheel(HWND hWnd, int xPos, int yPos, int zDelta, UINT fwKeys); ウィンドウクラスの実装ファイル(WindowClass.cpp)です // WindowClass.cpp
#include "WindowClass.h"
// インスタンスハンドル
HINSTANCE CSWindowClass::m_hInstance = NULL;
// 登録用
CSWindowClass* CSWindowClass::m_pThis = NULL;
// コンストラクタ
CSWindowClass::CSWindowClass()
:m_hWnd(NULL)
{
}
// デストラクタ
CSWindowClass::~CSWindowClass(){
}
// シンプルウィンドウクラスの登録
bool CSWindowClass::RegistBasicWindowClass(){
m_hInstance = GetModuleHandle(NULL);
/********************************************************/
/* ウィンドウクラスの定義と作成 */
/********************************************************/
// シンプルウィンドウクラス設定
WNDCLASSEX wcex ={sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,
WndProcManager, 0,0,m_hInstance,
(HICON)LoadImage(NULL,MAKEINTRESOURCE(IDI_APPLICATION),IMAGE_ICON,0,0,LR_DEFAULTSIZE | LR_SHARED),
(HCURSOR)LoadImage(NULL,MAKEINTRESOURCE(IDC_ARROW),
IMAGE_CURSOR,0,0,LR_DEFAULTSIZE | LR_SHARED),
(HBRUSH)GetStockObject(WHITE_BRUSH), NULL, WC_BASIC , NULL};
// シンプルウィンドウクラス作成
if(!RegisterClassEx(&wcex))
return false;
return true;
}
//--------------------------------------------------------------------//
// ウィンドウ作成
//
// 引数: Title タイトル
// Width 幅
// Height 高さ
// X X座標
// Y Y座標
// hParentWnd 親ウィンドウハンドル
// hMenu メニューハンドルもしくはリソースID
// ClassName ウィンドウクラス名
// Style ウィンドウスタイル
// ExStyle ウィンドウ拡張スタイル
//
// 戻り値: true: 作成成功
// false: 作成失敗
//--------------------------------------------------------------------//
bool CSWindowClass::CreateWnd(
const TCHAR* Title,int Width,int Height,int X ,int Y ,
HWND hParentWnd,HMENU hMenu,const TCHAR* ClassName,DWORD Style,DWORD ExStyle)
{
// 重要:あとで正しいウィンドウプロシージャを呼び出すために覚えておきます
m_pThis = this;
/********************************************************/
/* ウィンドウの作成 */
/********************************************************/
m_hWnd = CreateWindowEx(
ExStyle, //拡張ウィンドウスタイル
ClassName, //ウィンドウクラス名
Title, //タイトルバーにこの名前が表示されます
WS_OVERLAPPEDWINDOW | WS_VISIBLE, //ウィンドウスタイル
X, //X座標
Y, //Y座標
Width, //幅
Height, //高さ
hParentWnd, //親ウィンドウのハンドル、親を作るときはNULL
hMenu, //メニューハンドルorリソースID
m_hInstance, //インスタンスハンドル
NULL);
// ウィンドウ作成失敗
if (m_hWnd == NULL)
return false;
return true;
}
//-----------------------------------------------------//
// ウィンドウプロシージャの管理者
// 基本ウィンドウクラスではこの関数を登録して
// この関数を経由して各ウィンドウの本当のプロシージャを呼び出します
//
// 引数: hWnd ウィンドウハンドル
// msg メッセージ
// wParam パラメータ
// lParam パラメータ
//
// 戻り値:処理結果
//-----------------------------------------------------//
LRESULT CALLBACK CSWindowClass::WndProcManager(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam){
CSWindowClass* pWindow = (CSWindowClass*)(LONG_PTR)GetWindowLong(hWnd,GWL_USERDATA);
//thisポインタが取得できた場合
if( pWindow != NULL)
{
// 本当のウィンドウプロシージャを呼び出しメッセージ処理する
LRESULT lResult = pWindow->WndProc( hWnd, msg, wParam, lParam);
return lResult;
}
//thisポインタが取得できなかった場合
else
{
// ウィンドウの作成時の場合
if( msg == WM_CREATE || msg == WM_INITDIALOG){
// プロパティリストにオブジェクトポインタ(thisポインタ)を設定する
SetWindowLongPtr( hWnd, GWL_USERDATA, (LONG)(LONG_PTR)(m_pThis) );
// 初期化メッセージの再送信
SendMessage(hWnd,msg,wParam,lParam);
m_pThis = NULL; // 登録したので一時インスタンスポインタ格納の解除
}
}
return DefWindowProc(hWnd,msg,wParam,lParam);
}
//-----------------------------------------------------//
// ウィンドウプロシージャ
// デフォルトの処理はDefWindowProc関数で行う
//
// 引数: hWnd ウィンドウハンドル
// msg メッセージ
// wParam パラメータ
// lParam パラメータ
//
// 戻り値:処理結果
//-----------------------------------------------------//
LRESULT CALLBACK CSWindowClass::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch(msg){
HANDLE_MSG(hWnd,WM_CREATE,OnCreate);
HANDLE_MSG(hWnd,WM_DESTROY,OnDestroy);
HANDLE_MSG(hWnd,WM_PAINT,OnPaint);
// キーボード
HANDLE_MSG(hWnd,WM_CHAR,OnChar);
// マウス
HANDLE_MSG(hWnd,WM_LBUTTONDOWN,OnLButtonDown);
HANDLE_MSG(hWnd,WM_RBUTTONDOWN,OnRButtonDown);
HANDLE_MSG(hWnd,WM_MBUTTONDOWN,OnMButtonDown);
HANDLE_MSG(hWnd,WM_LBUTTONDBLCLK,OnLButtonDown);
HANDLE_MSG(hWnd,WM_RBUTTONDBLCLK,OnRButtonDown);
HANDLE_MSG(hWnd,WM_LBUTTONUP,OnLButtonUp);
HANDLE_MSG(hWnd,WM_RBUTTONUP,OnRButtonUp);
HANDLE_MSG(hWnd,WM_MBUTTONUP,OnMButtonUp);
HANDLE_MSG(hWnd,WM_MOUSEMOVE,OnMouseMove);
HANDLE_MSG(hWnd,WM_MOUSEWHEEL,OnMouseWheel);
// MCI処理終了通知メッセージ
case MM_MCINOTIFY:{
MCI_PLAY_PARMS mciPlay;
// 通知メッセージ失敗
if (wParam != MCI_NOTIFY_SUCCESSFUL)
return 0;
// 初期位置に戻す
mciSendCommand((MCIDEVICEID)lParam, MCI_SEEK, MCI_SEEK_TO_START, 0);
mciPlay.dwCallback = (DWORD_PTR)hWnd;
// ループ再生
mciSendCommand((MCIDEVICEID)lParam, MCI_PLAY, MCI_NOTIFY, (DWORD_PTR)&mciPlay);
return 0;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
// メッセージループ
void CSWindowClass::MsgLoop(int ArrayLength,CSWindowClass* pWindowArray){
/********************************************************/
/* メッセージループ */
/********************************************************/
MSG msg = {};
while(msg.message != WM_QUIT) {
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else{
// メッセージ処理をしてないときする処理
for(int i = 0;i < ArrayLength;++i){
pWindowArray[i].Idle();
}
}
}
}
// メッセージ処理していないときにする処理
void CSWindowClass::Idle(){
// 継承時に処理を書く
}
// ウィンドウ作成メッセージ
BOOL CSWindowClass::OnCreate(HWND hWnd,LPCREATESTRUCT lpCreateStruct){
return TRUE;
}
// ウィンドウ破棄メッセージ
void CSWindowClass::OnDestroy(HWND hWnd){
PostQuitMessage(0);
}
// ウィンドウ描画メッセージ
void CSWindowClass::OnPaint(HWND hWnd){
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd,&ps);
EndPaint(hWnd,&ps);
}
// キー入力メッセージ
void CSWindowClass::OnChar(HWND hWnd, TCHAR ch, int cRepeat){}
// マウス左ボタン押下メッセージ
void CSWindowClass::OnLButtonDown(HWND hWnd, BOOL isDoubleClick, int x, int y, UINT keyFlags){}
// マウス右ボタン押下メッセージ
void CSWindowClass::OnRButtonDown(HWND hWnd, BOOL isDoubleClick, int x, int y, UINT keyFlags){}
// マウス真ん中ボタン押下メッセージ
void CSWindowClass::OnMButtonDown(HWND hWnd, BOOL isDoubleClick, int x, int y, UINT keyFlags){}
// マウス左ボタン押上メッセージ
void CSWindowClass::OnLButtonUp(HWND hWnd,int x, int y, UINT keyFlags){}
// マウス右ボタン押上げメッセージ
void CSWindowClass::OnRButtonUp(HWND hWnd,int x, int y, UINT keyFlags){}
// マウス真ん中ボタン押上メッセージ
void CSWindowClass::OnMButtonUp(HWND hWnd,int x, int y, UINT keyFlags){}
// マウス移動メッセージ
void CSWindowClass::OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags){}
// マウスホイールメッセージ
void CSWindowClass::OnMouseWheel(HWND hWnd, int xPos, int yPos, int zDelta, UINT fwKeys){}
ほとんどは今までの回でやったことをそのまま実装しただけです // 基本ウィンドウクラス設定
WNDCLASSEX wcex ={sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,
WndProcManager,
0,0,m_hInstance,
(HICON)LoadImage(NULL,MAKEINTRESOURCE(IDI_APPLICATION),IMAGE_ICON,0,0,LR_DEFAULTSIZE | LR_SHARED),
(HCURSOR)LoadImage(NULL,MAKEINTRESOURCE(IDC_ARROW),IMAGE_CURSOR,0,0,LR_DEFAULTSIZE | LR_SHARED),
(HBRUSH)GetStockObject(WHITE_BRUSH), NULL, WC_BASIC , NULL};
静的メンバ関数のWndProcManager関数ポインタを指定しています // 重要:あとで正しいウィンドウプロシージャを呼び出すために覚えておきます m_pThis = this; このあとCreateWindowEx関数を呼び出します /********************************************************/ /* ウィンドウの作成 */ /********************************************************/ m_hWnd = CreateWindowEx( ExStyle, //拡張ウィンドウスタイル ClassName, //ウィンドウクラス名 Title, //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW | WS_VISIBLE, //ウィンドウスタイル X, //X座標 Y, //Y座標 Width, //幅 Height, //高さ hParentWnd, //親ウィンドウのハンドル、親を作るときはNULL hMenu, //メニューハンドルorリソースID m_hInstance, //インスタンスハンドル NULL); CreateWindowEx関数は基本ウィンドウクラスの //-----------------------------------------------------//
// ウィンドウプロシージャの管理者
// 基本ウィンドウクラスではこの関数を登録して
// この関数を経由して各ウィンドウの本当のプロシージャを呼び出します
//
// 引数: hWnd ウィンドウハンドル
// msg メッセージ
// wParam パラメータ
// lParam パラメータ
//
// 戻り値:処理結果
//-----------------------------------------------------//
LRESULT CALLBACK CSWindowClass::WndProcManager(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam){
CSWindowClass* pWindow = (CSWindowClass*)(LONG_PTR)GetWindowLong(hWnd,GWL_USERDATA);
//thisポインタが取得できた場合
if( pWindow != NULL)
{
// 本当のウィンドウプロシージャを呼び出しメッセージ処理する
LRESULT lResult = pWindow->WndProc( hWnd, msg, wParam, lParam);
return lResult;
}
//thisポインタが取得できなかった場合
else
{
// ウィンドウの作成時の場合
if( msg == WM_CREATE || msg == WM_INITDIALOG){
// プロパティリストにオブジェクトポインタ(thisポインタ)を設定する
SetWindowLongPtr( hWnd, GWL_USERDATA, (LONG)(LONG_PTR)(m_pThis) );
// 初期化メッセージの再送信
SendMessage(hWnd,msg,wParam,lParam);
m_pThis = NULL; // 登録したので一時インスタンスポインタ格納の解除
}
}
return DefWindowProc(hWnd,msg,wParam,lParam);
}
まずいきなりGetWindowLong関数というものが出てきました CSWindowClass* pWindow = (CSWindowClass*)(LONG_PTR)GetWindowLong(hWnd,GWL_USERDATA); GetWindowLong関数の第二引数にGWL_USERDATAを指定すると //thisポインタが取得できなかった場合
else
{
// ウィンドウの作成時の場合
if( msg == WM_CREATE || msg == WM_INITDIALOG){
// プロパティリストにオブジェクトポインタ(thisポインタ)を設定する
SetWindowLongPtr( hWnd, GWL_USERDATA, (LONG)(LONG_PTR)(m_pThis) );
// 初期化メッセージの再送信
SendMessage(hWnd,msg,wParam,lParam);
m_pThis = NULL; // 登録したので一時インスタンスポインタ格納の解除
}
}
WM_INITDIALOGメッセージについてはまだ説明していませんでしたが、 今回の肝はここです // プロパティリストにオブジェクトポインタ(thisポインタ)を設定する SetWindowLongPtr( hWnd, GWL_USERDATA, (LONG)(LONG_PTR)(m_pThis) ); SetWindowLongPtr関数でウィンドウハンドルと対応のあるウィンドウオブジェクト(CSWindowClass型)のポインタをウィンドウデータ領域に保存しておきます そして、WM_CREATEメッセージまたはWM_INITDIALOGメッセージを再送信します // 初期化メッセージの再送信 SendMessage(hWnd,msg,wParam,lParam); メッセージの送信にはSendMessage関数を使います LRESULT SendMessage( HWND hWnd, // 送信先ウィンドウのハンドル UINT Msg, // メッセージ WPARAM wParam, // メッセージの最初のパラメータ LPARAM lParam // メッセージの 2 番目のパラメータ ); SendMessage関数は単純にメッセージ送るだけでなく,コントロールの制御にも使う非常に使い道の広い関数です CSWindowClass* pWindow = (CSWindowClass*)(LONG_PTR)GetWindowLong(hWnd,GWL_USERDATA); これ以降は、ウィンドウハンドルに対応付けられたウィンドウデータ領域には対応したウィンドウオブジェクトが入っています メンバ関数化されたウィンドウプロシージャを呼び出すには //thisポインタが取得できた場合
if( pWindow != NULL)
{
// 本当のウィンドウプロシージャを呼び出しメッセージ処理する
LRESULT lResult = pWindow->WndProc( hWnd, msg, wParam, lParam);
return lResult;
}
なぜ、これだけ回りくどいことをしているのでしょうか? LRESULT CALLBACK CSWindowClass::WndProcManager(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam){
// 本当のウィンドウプロシージャを呼び出しメッセージ処理する
return WndProc( hWnd, msg, wParam, lParam);
}
実は静的(static)なメンバ関数では通常のメンバ関数やメンバ変数は使えない(コンパイルエラーになる)のです メインコード(main.cpp)です
#include "WindowClass.h"
// コンソール画面隠蔽
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
int main(int argc,char* argv[])
{
// 基本ウィンドウクラス登録
CSWindowClass::RegistBasicWindowClass();
// 基本ウィンドウ作成
CSWindowClass Window;
Window.CreateWnd(_T("タイトル"));
// メッセージループ
CSWindowClass::MsgLoop();
return 0;
}
今回のソースファイルは下からダウンロードできます
|