Visual C++ 2005 Express Edition + SDK
の環境でコーディングしています。
各種 Window をクラスでラップしたいのですが
クラス内のスタティックなウインドウプロックへクラスの this ポインターを
如何にして渡すかで悩んでいます。
MFC は1つのウインドウプロックを用意して独自のRuntimeクラスを使用して
メッセージの伝播を行っています。ATLはヒープ上に動的にウインドウプロックを
用意してウインドウプロックの第一引き数に this ポインターを設定して
各Window のウインドウプロックを呼び出しています。
かなり、大雑把な説明ですが間違ってはいない?と思います。
個人的には、各種Window の実装時にウインドウプロックとメッセージマップを用意して
コーディングするのがSDK的?かなと思っています。
OLE などは SDK で手を付けるには大掛かりになるので考えていません。
ATL 風なフレームワークが良いのかなと思いますが、ウインドウプロックの第一引き数
を殺してしまうので、他の手法を探しています。
クラス内のスタティックなウインドウプロックへクラスの this ポインターを
如何にして渡すかで良い手法があればお教え下さい。
また、皆さんは個人で作成されるフレームワークの実装手法で、メッセージの伝播は
どんな風に作成されているかお教え願えませんか?
--
ちゅ
このようなMFCを使わずにclassでラップしたものを書いたのは、せいぜい
自分の学習用途もしくは書き捨てツールくらいでしか無く、実働用のもの
は書いていません。このごろは書き捨てツールどころか実働用すらもC++で
なくC#とかの簡単な言語に移ってしまいました。
****************************************************************
// testwindowではSetPropによりpropertyを使用し、[this]を記憶させる。
// [this]を記憶させる必要があるため、CreateWindow(ex)での params を使用する。
// そのため、paramsを利用する必要のあるウィンドウでは使用むり。
// WM_NCCREATE, WM_NCDESTROY はstaticWindowProcでのみ使用する。
#define THIS_PROPERTY "the-base-window-this-pointer"
#define TESTWINDOW_CLASSNAME "testwindow-class#albert.einstein"
class testwindow
{
public:
virtual int create(int x, int y, int cx, int cy, UINT sytle = WS_OVERLAPPEDWINDOW|WS_VSCROLL, HWND parent = NULL);
virtual BOOL register();
virtual void show(bool sw);
virtual int attach(HWND hwnd);
virtual void detach();
private:
static LRESULT CALLBACK staticWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
protected:
virtual LRESULT WindowProc(UINT msg, WPARAM wparam, LPARAM lparam);
private:
HWND hwnd;
WNDPROC lpPreviousWindowProc;
};
/*static*/
LRESULT CALLBACK testwindow::staticWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
if (msg == WM_NCCREATE) {
CREATESTRUCT *cp = reinterpret_cast<CREATESTRUCT*>(lparam);
if (cp && cp->lpCreateParams)
SetProp(hwnd, THIS_PROPERTY, reinterpret_cast<HANDLE>(cp->lpCreateParams));
} else if (msg == WM_NCDESTROY) {
RemoveProp(hwnd, THIS_PROPERTY);
}
testwindow *pthis = reinterpret_cast<testwindow*>(GetProp(hwnd, THIS_PROPERTY));
if (pthis) {
pthis->hwnd = hwnd;
return pthis->WindowProc(msg, wparam, lparam);
} else
return DefWindowProc(hwnd, msg, wparam, lparam);
}
LRESULT testwindow::StaticWindowProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg) {
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
case WM_CREATE:
//こんなふうにWindow Messageのハンドリングをする
break;
case WM_DESTROY:
//PostQuitMessageはふつーMainのFrame Windowだけが発行するもの。
//子Windowに入れてはならない
PostQuitMessage(0);
break;
}
}
//------------------
int testwindow::attach(HWND hw)
{
//すでに存在するwindowをサブクラスする/ダイアログ非対応~
//
//すでにhwndに値が入っている場合、エラーとする
//また、PROPERTY_WINDOWPROCで示されるpropertyを強制的に書き換えてしまい、元の値を
//残さないので注意せよ。
if (hwnd)
return -1;
hwnd = hw;
SetProp(hw, PROPERTY_WINDOWPROC, (HANDLE)this);
lpPreviousWindowProc = (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)staticWindowProc);
return 0;
}
void SimpleChildWindowBase::detach()
{
if (lpPreviousWindowProc) {
SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)lpPreviousWindowProc);
RemoveProp(hwnd, PROPERTY_WINDOWPROC);
hwnd = 0;
lpPreviousWindowProc = 0;
}
}
//----- create -----
BOOL testwindow::register()
{
//registerに成功するか、もしくは以前にregisterされていたら真を返す
WNDCLASS wc = {0};
if (!GetClassInfo(GetModuleHandle(0), BASEWINDOW_CLASSNAME, &wc)) {
wc.style = WS_HREDRAW|WS_VREDRAW|CS_DBLCLKS;
wc.lpfnWndProc = basewindow::staticWindowProc;
wc.hInstance = GetModuleHandle(0);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
wc.lpszMenuName = 0;
wc.lpszClassName = TESTWINDOW_CLASSNAME;
return RegisterClass(&wc);
} else
return TRUE;
}
int testwindow::create(int x, int y, int cx, int cy, UINT sytle, HWND parent)
{
if (register()) {
CreateWindow(TESTWINDOW_CLASSNAME, "", style, x, y, cx, cy, parent, NULL, GetModuleHandle(0), this);
return 1;
} else
return 0;
}
//-----------------
void testwindow::show(bool sw)
{
ShowWindow(sw ? SW_SHOW : SW_HIDE);
}
//-----------------
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int cmdshow)
{
testwindow *ap = new testwindow();
if (ap->register()) {
ap->create(0,0,100,100);
ap->show(true);
MSG msg;
while (GetMessage(&msg, 0, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0; //エラーを見ていないからデバグむり
}
--
----- Takeshi SHIGIHARA
cyg...@tka.att.ne.jp -----
うまい、早い、安い/2つだけ選べ--RFC1925.
On Mon, 14 May 2007 14:31:06 +0900
"Takeshi SHIGIHARA" wrote
in message news:134ft0s...@news.supernews.com...
> Re: クラス内のスタティックなウインドウプロックへクラスの this ポインター渡すには
Takeshi SHIGIHARA さん。有難う御座います。
SetProp() や WNDCLASS.cbWndExtra を使用しない方向で考えています。
アプリケーション側で使用したくなった時に出来るだけ制限掛けたくないんで。
グローバル変数で HWND と this ポインターの巨大なマップを管理するのも
なんだかなーって感じですし。
素直に this ポインターを引き数にとる関数を定義してその関数を呼び出す
アセンブラを書いてバイトデータとして ATL の様に動的に行う方が
すっきりするかなーと悩んでいます。環境依存及びメモリー管理がめんどいですが。
素直にコンパイラを購入すればいいんですが、週末プログラマには高いんですよね。
週末プログラマなのでそんなに高機能なフレームワークを作る必要は無いんですが…。
> は書いていません。このごろは書き捨てツールどころか実働用すらもC++で
> なくC#とかの簡単な言語に移ってしまいました。
お遊びプログラムで、部分的ですがあえて「アセンブラ」で書いたりしている自分は化石かな。
時代は .Netなのかな…。
--
ちゅ
On Mon, 14 May 2007 14:31:06 +0900
"Takeshi SHIGIHARA" wrote
in message news:134ft0s...@news.supernews.com...
> Re: クラス内のスタティックなウインドウプロックへクラスの this ポインター渡すには
VCL は SetProp()を使用しているみたいですね。
--
ちゅ
wxWidgets(http://www.wxwindows.org/ 、旧wxWindows)という、オー
プンソースなウィンドウクラスライブラリがあります。かなり大きな
ものですが、一度調べてみてはいかがでしょうか。
目的が「ウィンドウを制御するためのクラスを書く」ならいざ知らず
アプリを書くことが目的ならば、出来合いのラッパを利用するのも
手だと思います。
----------------
やはり慌てて学習用フォルダから引きずり出して、いくつかのソース
を貼り付けて合成したヤツでは、あちこちに間違いがあります。
読むのに支障のありそうな巨大な誤りだけ修正しておきます(それで
もコンパイルしたりして確かめたわけではないので動作の保証はしか
ねます)
--------
Window procedure関数
誤 LRESULT testwindow::StaticWindowProc(UINT msg, WPARAM wparam, LPARAM lparam)
正 LRESULT testwindow::WindowProc(UINT msg, WPARAM wparam, LPARAM lparam)
attach(HWND)関数内
誤 SetProp(hw, PROPERTY_WINDOWPROC, (HANDLE)this);
正 SetProp(hw, THIS_PROPERTY, (HANDLE)this);
--------
On Wed, 16 May 2007 09:52:51 +0900
"Takeshi SHIGIHARA" wrote
in message news:134klf0...@news.supernews.com...
> Re: クラス内のスタティックなウインドウプロックへクラスの this ポインター渡すには
返答、有難う御座います。
もう、作り初めていました…。
> wxWidgets(http://www.wxwindows.org/ 、旧wxWindows)という、オー
> プンソースなウィンドウクラスライブラリがあります。かなり大きな
> ものですが、一度調べてみてはいかがでしょうか。
以前、調べてかなり複雑だった様な気がします…。
もう一度、調べて見ます。
> 目的が「ウィンドウを制御するためのクラスを書く」ならいざ知らず
> アプリを書くことが目的ならば、出来合いのラッパを利用するのも
> 手だと思います。
確かにアプリを作ることが第一の目的ですが、楽しんでコーディングが
したいんです。仕事だと楽しくないので…。
多分、今の第一の目的はアプリを作ることではなくて、
楽しんでコーディングする事なんでしょうね。
基本的にやりたい事はメッセージの伝播で
週末に自分のアプリの超基本的なフレームワーク位どうにか作っとくと
後々楽が出来るかなって感じです。脱C言語!(マイアプリ)
それで、処理しきれないようなアプリは初めらか作らないで、
ネットに転がっていないか探すか購入を検討します。
> やはり慌てて学習用フォルダから引きずり出して、いくつかのソース
> を貼り付けて合成したヤツでは、あちこちに間違いがあります。
> 読むのに支障のありそうな巨大な誤りだけ修正しておきます(それで
> もコンパイルしたりして確かめたわけではないので動作の保証はしか
> ねます)
十分に理解できましたか、丁寧に有難う御座います。
色々と調べましたが、MFC/ATL/VCL と基本的なメッセージの伝播処理は
同じで、ウインドウプロックでの this ポインターの取得方法を
いかにスマートに行うかって事に尽きるって事です。
ATL の手法は一時的にグローバル変数に this ポインターを設定しますが、
CreateWindow()/ CreateWindowEx() も ウインドウプロックで WM_CREATE
の処理が終わるまでは呼び出し元に返って来ないようで、処理が単純化されていて
参考になりました。
超基本的なフレームワークをとことん作ろうかなと。(処理速度が心配ですが)
マイアプリで C++/CLI でコーディング出来ると楽しくなるんですが
道は遠いです。
--
ちゅ