●オーディオプレイヤ開発記 その3
さて、今回も 前回 に引き続き、CWnd クラスの作成です。このウィンドウの基底クラスというのは、いかにうまくウィンドウプロシージャやサブクラス化というのをクラスに取り込むかという部分が、難しいところでしょう。これに関しては、コールバック関数をクラスのメンバとするには? ( http://techtips.belution.com/ja/vc/0009/ ) のページなどが参考になります。
まあそんなこんなで、とりあえず作ってみました。
//----------------------------------------------------------------------------
// Wnd.h : ウィンドウやラベルなどの基底クラス
//----------------------------------------------------------------------------
#ifndef WndH
#define WndH
#include <string>
//----------------------------------------------------------------------------
// ウィンドウやラベルなどの基底クラス
//----------------------------------------------------------------------------
class CWnd
{
public: // 関数
CWnd(): m_hWnd(0), m_lpfnOriginalWndProc(NULL) { }
virtual ~CWnd() { Destroy(); }
virtual BOOL Create(LPCTSTR lpClassName);
virtual void Destroy() {
if(m_hWnd) DestroyWindow(m_hWnd), m_hWnd = 0;
}
virtual ATOM RegisterClass(LPCTSTR lpClassName); // ウィンドウクラスの登録
virtual void SetProc(); // プロシージャ関連の設定
virtual void ResetProc(); // プロシージャ関連のリセット
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam);
virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
// オーバーライド可
protected: // メンバ変数
HWND m_hWnd;
WNDPROC m_lpfnOriginalWndProc; // 元のウィンドウプロシージャのアドレス
static const std::string st_strClassPtr;
// プロパティリスト ( Set/GetProp ) 用文字列
public: // メンバ変数の取得・設定
operator HWND() const { return m_hWnd; }
};
//----------------------------------------------------------------------------
#endif
//----------------------------------------------------------------------------
// Wnd.cpp : ウィンドウやラベルなどの基底クラス
//----------------------------------------------------------------------------
#include <windows.h>
#include "Wnd.h"
const std::string CWnd::st_strClassPtr = TEXT("ClassPointer");
// プロパティリスト ( Set/GetProp ) 用文字列
//----------------------------------------------------------------------------
// 作成
//----------------------------------------------------------------------------
BOOL CWnd::Create(LPCTSTR lpClassName)
{
Destroy();
m_hWnd = CreateWindow(lpClassName, "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
GetModuleHandle(NULL), this);
if(!m_hWnd) return false;
return true;
}
//----------------------------------------------------------------------------
// ウィンドウクラスの登録
//----------------------------------------------------------------------------
ATOM CWnd::RegisterClass(LPCTSTR lpClassName)
{
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = (WNDPROC)CWnd::WndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
wc.lpszClassName = lpClassName;
return RegisterClassEx(&wc);
}
//----------------------------------------------------------------------------
// プロシージャ関連の設定
//----------------------------------------------------------------------------
void CWnd::SetProc()
{
if(!m_hWnd) return;
SetProp(m_hWnd, st_strClassPtr.c_str(), (HANDLE)this);
if(GetWindowLong(m_hWnd, GWL_WNDPROC) != (LONG)WndProc)
m_lpfnOriginalWndProc = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)WndProc);
}
//----------------------------------------------------------------------------
// プロシージャ関連のリセット
//----------------------------------------------------------------------------
void CWnd::ResetProc()
{
if(!m_hWnd) return;
if(m_lpfnOriginalWndProc)
SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_lpfnOriginalWndProc);
RemoveProp(m_hWnd, st_strClassPtr.c_str());
}
//----------------------------------------------------------------------------
// ウィンドウプロシージャ
//----------------------------------------------------------------------------
LRESULT CALLBACK CWnd::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CWnd* pWnd = (CWnd*)GetProp(hWnd, st_strClassPtr.c_str());
if(!pWnd) {
if(uMsg == WM_CREATE || uMsg == WM_NCCREATE)
pWnd = (CWnd*)((LPCREATESTRUCT)lParam)->lpCreateParams;
if(pWnd) {
pWnd->m_hWnd = hWnd;
pWnd->SetProc();
}
}
if(pWnd) {
LRESULT lResult = pWnd->WindowProc(uMsg, wParam, lParam);
if(uMsg == WM_DESTROY) pWnd->ResetProc();
return lResult;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
//----------------------------------------------------------------------------
// ウィンドウプロシージャ ( オーバーライド可 )
//----------------------------------------------------------------------------
LRESULT CWnd::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if(m_lpfnOriginalWndProc)
return CallWindowProc(m_lpfnOriginalWndProc, m_hWnd, uMsg, wParam, lParam);
return DefWindowProc(m_hWnd, uMsg, wParam, lParam);
}
//----------------------------------------------------------------------------
まず、ウィンドウやウィンドウクラスに関する属性については、ウィンドウを作成する時点では適当に決めてしまっています。そのうち、SetWindowLongPtr, SetClassLongPtr などによって、あとから属性を変えられるような関数を作るつもりです。
またさらに、ウィンドウメッセージが来たら、OnCreate やら OnSize やら様々な関数に飛ばすようにする必要もありますね。まあそこらへんはなんせ数が多いので、必要になり次第、追加していくという形で構わないでしょう。
さて、それではついでに、CFrameWnd クラスも作ってしまいます。
//----------------------------------------------------------------------------
// FrameWnd.h : フレームウィンドウクラス
//----------------------------------------------------------------------------
#ifndef FrameWndH
#define FrameWndH
#include "Wnd.h"
//----------------------------------------------------------------------------
// フレームウィンドウクラス
//----------------------------------------------------------------------------
class CFrameWnd : public CWnd
{
public: // 関数
CFrameWnd(): m_bMainWnd(false) { }
virtual ~CFrameWnd() { }
virtual BOOL Create(LPCTSTR lpClassName) {
RegisterClass(lpClassName);
return CWnd::Create(lpClassName);
}
virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) {
if(uMsg == WM_DESTROY && m_bMainWnd) PostQuitMessage(0);
return CWnd::WindowProc(uMsg, wParam, lParam);
}
protected: // メンバ変数
BOOL m_bMainWnd; // メインウィンドウかどうか
public: // メンバ変数の取得・設定
void SetMainWnd(bool bMainWnd) { m_bMainWnd = bMainWnd; }
};
//----------------------------------------------------------------------------
#endif
短いので、全部ヘッダに書いてます。ウィンドウの作成時にウィンドウクラスを登録するのと、メインウィンドウの場合はウィンドウが閉じられたときにアプリケーション自体を終了するようにしました。
では次回、ウィンドウクラスにさらに機能を追加しつつ、いよいよ実際にウィンドウを表示させてみる予定。まあ既に、前回の CApp クラスに、CFrameWnd m_wnd などと定義しておき、Run 関数内で、
m_wnd.Create(TEXT("WindowClass"));
m_wnd.SetMainWnd(true); // メインウィンドウに設定
ShowWindow(m_wnd, m_nCmdShow);
UpdateWindow(m_wnd);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
などとすれば、ウィンドウが表示される気もしますけどね。ただ、
ShowWindow(m_wnd, m_nCmdShow);
UpdateWindow(m_wnd);
あたり、API を直接呼ぶのが不細工なので、次回はそこらへんから作っていきましょうかね。ここまでのソースコードはこちら ( Hayaemon20060203_src.lzh ) 。