ダイアログのクラス化で派生クラスに仮想関数を使用する方法
- ダイアログを基本クラスで静的プロシージャと派生クラスでオーバーライドしてプロシージャを使用する方法について説明します。
- SetWindowLong関数を使用してポインタを設定する方法と、GetWindowLong関数を使用してポインタを取得する方法を紹介します。
- WM_INITDIALOGでのポインタの取得に関する問題についても取り上げ、解決策を提案します。
- ベストアンサー
ダイアログのクラス化で仮想関数を用いて派生クラスにしているんです
ダイアログのクラス化で仮想関数を用いて派生クラスにしているんですが・・・ ダイアログを基本クラスで静的プロシージャと派生クラスでオーバーライドしてプロシージャを使いたい のですが、どうしても自身のポインタが取得できません。 以下にソースを載せておきます。 class CBaseWnd { public: // ポインタの設定 void SetPointer( HWND hWnd ); // ウィンドウプロシージャの呼び出し static LRESULT CALLBACK CallProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ); // ウィンドウプロシージャの実装 virtual LRESULT MainProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ); }; [クラスの実装] //===== ポインタの設定 =====// void CBaseWnd::SetPointer( HWND hWnd ) { SetWindowLong( hWnd, GWL_USERDATA, (LONG)this ); } //===== ウィンドウプロシージャの呼び出し =====// LRESULT CALLBACK CBaseWnd::CallProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { //_プロパティリストからthisポインタを取得 //ここでポインタを取得することができないでいます。値が0です。 //先にSetWindowlongをやっても値が0のままです。 CBaseWnd* thisPtr = (CBaseWnd*)GetWindowLong( hWnd, GWL_USERDATA ); //_thisポインタが取得できなかった場合... if( ! thisPtr ) { //_ウィンドウの作成時の場合... //ここでアクセス違反というエラーが起きる if( message == WM_INITDIALOG ) thisPtr = (CBaseWnd*)((LPCREATESTRUCT)lParam)->lpCreateParams; //_thisポインタが取得できた場合... if( thisPtr ) { //_プロパティリストにオブジェクトハンドル(thisポインタ)を設定する thisPtr->SetPointer( hWnd ); } } //_thisポインタが取得できた場合... if( thisPtr ) { LRESULT lResult = thisPtr->MainProc( hWnd, message, wParam, lParam ); return lResult; } return DefWindowProc( hWnd, message, wParam, lParam ); } //===== ウィンドウプロシージャの実装(継承可能) =====// // ここでの記述はデフォルトの処理 // LRESULT CBaseWnd::MainProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { switch( message ) { //_ウィンドウが破棄された場合 case WM_DESTROY: PostQuitMessage(0); return 0; //_デフォルトの場合 default: return DefWindowProc( hWnd, message, wParam, lParam ); } } WM_INITDIALOGでダイアログの初期化中にポインタを取得しようとしますが、アクセス違反が起こり失敗します。 どなたか分かる方がいらっしゃったらご指摘お願いします。
- dotneer
- お礼率65% (105/161)
- C・C++・C#
- 回答数5
- ありがとう数3
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
クラス設計において、あまり静的プロシージャを使ったことが無かった為、調べるのに時間が掛かりました。 静的プロシージャ(CallProc)から静的でないプロシージャ(SetPointer)は呼び出せないのではないかと思い、VCで以下の検証プログラムを組んでみました。 しかし、VC環境で動くことを確認しました。 まず、DialogBox関数ではWM_INITDIALOG時にlParamを渡すことはできなさそうです。 試したわけではありませんが、以下のソースコードをベースにするとmain関数の中で以下のようにする必要があるのでは? ※CBaseを継承したクラスのインスタンスを生成して、そのインスタンスのアドレスを渡す。 CreateDialogParam( hInstance, "Template", hWndParent, (WNDPROC)CBase::CallProc, &dbobj ); しかし、まーよくこんなトリッキーなコードが思いつくものだ。。。 ●検証プログラム #include <iostream> using namespace std; //**************************** // //**************************** class CBase { private: public: static void gofnc(void *obj) { ((CBase*)obj)->func(); } virtual void func() { } }; //**************************** // //**************************** class CDBox : public CBase { private: public: void func() { cout << "message01" << "moji" << endl; } }; //**************************** // //**************************** int main() { CDBox dbobj; CBase::gofnc(&dbobj); getchar(); return 0; }
その他の回答 (4)
- machongola
- ベストアンサー率60% (434/720)
こんにちは。 http://msdn.microsoft.com/ja-jp/library/cc410696.aspx http://msdn.microsoft.com/ja-jp/library/cc410761.aspx http://msdn.microsoft.com/en-us/library/ms645428(VS.85).aspx WM_INITDIALOGのlParamは、CreateDialogParam()/DialogBoxParam()等のLPARAMに指定した値が、其のまま伝わって来るのですから、 if( message == WM_INITDIALOG ) thisPtr = (CBaseWnd*)lParam; とすれば取り出せるの筈です。
お礼
回答ありがとうございます。 どうやらダイアログボックスだとlParamをキャストすれば値が入るようです。
補足
これで一回試したのですが、そもそもlParamの値が0なんです。 これでは何をやってもダメなのは納得したんですが、lParamの値が0の状態のようです。 DialogBox()の引数に静的プロシージャである、CallProcを指定しているのですが、これがいけないんでしょうか?
- reset_cat
- ベストアンサー率68% (94/138)
質問とは関係ないかもしれませんが・・・ ダイアログボックスのウィンドウプロシージャにするのなら、デフォルト処理ではDefWindowProcを呼び出してはならないのでは?
補足
そうですね。 ダイアログ用のプロシージャならBOOL型にすべきなのでそうします。 これはウィンドウプロシージャのクラス化(継承可能)なソースをそのまま 転載したものなんです。
- m-take0220
- ベストアンサー率60% (472/775)
メッセージが WM_INITDIALOG の時に lParam が LPCREATESTRUCT なんですか? WM_CREATE ではなくて?
補足
ダイアログプロシージャを作りたいため、WM_INITDIALOGに変更しました。 しかしWM_INITDIALOGでlParamが自分が得たい値になっているかというと疑問なんです。 というか実際アクセス違反になりました。 どうやったらアドレスを得られるかその方法が分からないでいます。
- Tacosan
- ベストアンサー率23% (3656/15482)
Set/GetWindowLong よりも Set/GetWindowLongPtr の方が安全だとは思うけど, それはさておきそもそも SetWindowLong は成功しているのでしょうか?
補足
SetWindowLongを直接呼ぶとthisポインタのせいでエラーがでます。 GetWindowLongからは値は0のままなので、その後からthisPtr->SetWindowというふうに しても変化はないです。
関連するQ&A
- WM_NCLBUTTONUPについて
タイトルバー上でのマウスボタンのアップを検出したいので、 WM_NCLBUTTONUPメッセージを拾うために、単純に以下のようなコードを書きました。 が、このコードではうまくWM_NCLBUTTONUPメッセージを拾えません。 ウィンドウを最大化しているときは問題なくメッセージを拾えるのですが、 それ以外の時(縮小表示)はメッセージを拾えません。 ただ、WM_NCLBUTTONDOWNは正しく拾うことができました。 ウィンドウが縮小表示になっている時にWM_NCLBUTTONUPを取得する場合には何か特殊な処理が必要なんでしょうか? ご存知の方がいらっしゃったら、よろしくお願いします。 // ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_NCLBUTTONUP: MessageBox(hWnd, "UP", "LBUTTON", MB_OK); return 0; case WM_DESTROY: // ウインドウが破棄されたときの処理 PostQuitMessage(0); return 0; default: // デフォルトの処理 return DefWindowProc(hWnd, message, wParam, lParam); } }
- ベストアンサー
- C・C++・C#
- HWNDの取得について
環境:WinXP, VS2005 例えばウィンドウプロシージャで LRESULT WINAPI WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) などとHWND型の変数hWndがありますよね。 このhWndをウィンドウプロシージャの外から取得することは可能でしょうか?
- ベストアンサー
- C・C++・C#
- 子ウインドウの作成と破棄について
CALLBACK のみを書きました。 メインウインドウを破棄したら 子ウインドウも破棄したいのですが、 うまく出来ません。 どうすればよろしいでしょうか? よろしくお願いします。 #include<windows.h> #include"ChildWindow.h" char MainWindowClassName[]="mainwindow"; LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { static HWND childWnd; switch(message) { case WM_ACTIVATEAPP: childWnd=Child_CreateWindow(hWnd,message,wParam,lParam); break; case WM_DESTROY: DestroyWindow(childWnd); PostQuitMessage(0); break; default: return DefWindowProc(hWnd,message,wParam,lParam); } } ///////////////////////////////////////////// #include<windows.h> char ChildWindowClassName[]="childwindow"; LRESULT CALLBACK ChildProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { switch(message) { case WM_LBUTTONDOWN: MessageBox(NULL,"","",MB_OK); break; default: return DefWindowProc(hWnd,message,wParam,lParam); } } ATOM Child_RegistWindow(HINSTANCE hInstance){} HWND Child_InitInstance(HWND hParentWnd,HINSTANCE hInst,int CmdShow){} HWND Child_CreateWindow(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { int CmdShow=1; Child_RegistWindow(NULL); HWND ChildWnd=Child_InitInstance(hWnd,NULL,CmdShow); MSG msg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return ChildWnd; }
- ベストアンサー
- C・C++・C#
- msgが定義されていない
Visual Studio2019でwindowsアプリケーションでゲームを作っているんですが、 どうやってもmsgが定義されていないが直りません。 #include <windows.h> LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp); //WinMain関数 int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { //ウィンドウクラスの登録 //メインウィンドウの生成 //メッセージループ return (int)msg.wParam; } //ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { //メッセージごとにやりたいことを記述する } return 0; } ご教示お願いします。
- 締切済み
- C・C++・C#
- ウィンドウアプリが思うように動かない(GetWindowTextの使い方?)
プログラミング初心者です。Windows XP, Visual Studio 2005 PE 使用。MFCは使わない(というより使い方がわからない…)。 簡単なウィンドウプログラムを作っています。エディットボックス1つとボタン1つを含むもので、ボタンを押すとエディットボックスの文字列を取得して、もしそれが close であればプログラムを終了するようにしたいんです。 自分で書いたコードの一部(プロシージャのみ)を以下に載せます。WM_CREATE、WM_DESTROYメッセージは省略。edit1はエディットボックスのハンドル、case式の2はボタンの子ウィンドウIDです。 LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){ char lpString[6]; switch(msg){ case WM_COMMAND: switch(LOWORD(wParam)) { case 2: GetWindowText(edit1,lpString,6); if(lpString == "close") SendMessage(hWnd, WM_CLOSE, 0, 0); else SendMessage(edit1, WM_CLEAR, 0, 0); return 0; } return 0; default : return DefWindowProc(hWnd,msg,wParam,lParam); } return 0; } Visual Studioではちゃんとビルドしてくれるんですが、いざ実行してcloseと入力した上でボタンを押してもうんともすんとも言いません。原因は何でしょうか?素人ながらlpStringをそのまま取り出して使用してるのがまずいのではと思いますが、関数の使い方がわからず対処に困っています。教授いただければ幸いです。
- 締切済み
- C・C++・C#
- コールバック関数のメンバ関数化
コールバック関数のメンバ関数化について質問です。 WindowsAPIでウィンドウプロシージャ(コールバック関数)をクラスのメンバ関数に しようと思っているのですが、出来るのでしょうか? たとえば、 http://wisdom.sakura.ne.jp/system/winapi/win32/win10.html このサイトにある LRESULT CALLBACK WndProc(HWND hWnd , UINT Msg , WPARAM wParam , LPARAM lParam); をクラスのメンバ関数にしたいです。 開発環境 XP C,C++ Visual Studio 2005
- ベストアンサー
- C・C++・C#
- キィーボードをフックしません、何故ですか???
パソコンを現在人間が実際に操作中であるか否かを判定しながら進めるアプリを作っています 簡易的にキィーボードを操作していれば操作中と判断します(マウス操作も含めますが話を簡単にするため今はキィーだけとします) グローバルフックでKEY_DOWNをフックする為に以下のDLL(主要部分のみ)を作りました キィーが押されるとMW_KEYDOWN_DLLというメッセージをアプリに送ります EXPORT LRESULT CALLBACK HookProc( int nCode, WPARAM wParam, LPARAM lParam) { if(nCode == HC_ACTION) { CWPSTRUCT *pcwp = (CWPSTRUCT *)lParam; if(pcwp->message == WM_KEYDOWN) { SendMessage(g_hWnd, WM_KEYDOWN_DLL, pcwp->wParam, pcwp->lParam); ←(1) } } return (CallNextHookEx(g_hHook, nCode, wParam, lParam)); } MW_KEYDOWN_DLLメッセージを受けたアプリは操作中フラグを立てます(主要部分のみ) LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_KEYDOWN_DLL: ここで操作中フラグを立てます ←(2) このフラグは一定時間後にタイマーが倒します break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return (0L); } 実行してみると(1)に来ません、当然(2)のフラグは立ちません WM_KEYDOWNの代わりにWM_CREATE、WM_CLOSEだときちんとフックします WM_CHAR、MW_KEYUPなどのキィーボード系のメッセージだとフックしません なぜでしょうか??? 多分きわめて初歩的な知識の欠如によるものでしょうが分かりません 宜しくご指導願います
- ベストアンサー
- C・C++・C#
- 何もしないメッセージだけ表示するウィンドウ
ゲーム用に何もしないメッセージだけ表示するウィンドウを作りたいのですが、 猫でもわかるゲームプログラミングのコードが間違っているのか、 機能しません。 #include <windows.h> LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp); int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { return (int)msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg){ } return 0; } このように、打ち込んだのですが、どこが間違っているのか解りません。 回答お願いいたします。
- ベストアンサー
- その他([技術者向] コンピューター)
- C++ WIN32 ウィドウの表示
プログラミング初心者です。 WIN32APIの勉強を始めたばかりの状態です。 本を見ながら、ウィンドウを表示させるだけのプログラムを書いてみたのですが、エラーになってしまいます。 #include <windows.h> //ウィンドウ・クラス名 #define MYWNDCLSNAME "MyWindowClass" LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { WNDCLASS wndcls; HWND hWnd; MSG msg; //ウィンドウ・クラスを登録 ZeroMemory(&wndcls, sizeof(wndcls)); wndcls.lpfnWndProc = WndProc; wndcls.hInstance = hInst; wndcls.hIcon = LoadIcon(0, IDI_APPLICATION); wndcls.hCursor = LoadCursor(0, IDC_ARROW); wndcls.hbrBackground = (HBRUSH)COLOR_BACKGROUND; wndcls.lpszClassName = MYWNDCLSNAME; if(RegisterClass(&wndcls) == 0) return -1; //メイン・ウィンドウを作成 hWnd = CreateWindow(MYWNDCLSNAME, "My Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInst, NULL); if(hWnd == 0) return -2; //ウィンドウの表示状態を指定する ShowWindow(hWnd,nCmdShow); UpdateWindow(hWnd); //メッセージループ while(GetMessage(&msg, 0, 0, 0)){ DispatchMessage(&msg); } //WM_QUITメッセージのwParamを終了コードにする return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg){ case WM_DESTROY: PostQuitMessage(0); return 0; } //自分で処理しないメッセージはWindowsに任せる return DefWindowProc(hWnd, uMsg, wParam, lParam); } 本を読み直しても原因がよくわかりません。 どこがいけないのかご指摘いただけるとうれしいです。お願いします。 エラーの内容は error C2440: '=' : 'const char [14]' から 'LPCWSTR' に変換できません。 error C2664: 'CreateWindowExW' : 2 番目の引数を 'const char [14]' から 'LPCWSTR' に変換できません。 です。
- ベストアンサー
- C・C++・C#
- MessageBoxについて
次のようなプログラムを作りました。 /*ウィンドウプロシージャ内*/ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ ・・・ case WM_LBUTTONDOWN: hantei(hWnd); break; ・・・ } /* 判定関数 */ void hantei(HWND hWnd){ MessageBox(hWnd,"テスト","テストです。",MB_OK); } このようになって動かしてみた(左クリックした)ところ、 ウィンドウ自体は非アクティブになったのですが、 BEEP音が鳴るだけで肝心のメッセージボックスが表示されません。 ALTキーを押すと表示されるのですが・・・ 左クリックをするとすぐにメッセージボックスを表示させるにはどうしたらよいのでしょう? 言葉足らずの説明かもしれませんがお願いします。
- ベストアンサー
- C・C++・C#
お礼
詳しく調べてくれてとても感謝しています。 静的プロシージャから動的プロシージャを呼び出すことはできているみたいです。(なぜかはまだ分からないのですが詳しく調べてみます。) CreateDialogParamといった関数でlParamに値を渡せば可能だとわかりました。 >しかし、まーよくこんなトリッキーなコードが思いつくものだ。。。 全くです。ネットで調べて苦戦していました。 沢山のご回答を貰ったお陰でなんとかなりました。 ありがとうございます。