アプリ版:「スタンプのみでお礼する」機能のリリースについて

   ダイアログのクラス化で仮想関数を用いて派生クラスにしているんですが・・・
ダイアログを基本クラスで静的プロシージャと派生クラスでオーバーライドしてプロシージャを使いたい
のですが、どうしても自身のポインタが取得できません。
以下にソースを載せておきます。

 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でダイアログの初期化中にポインタを取得しようとしますが、アクセス違反が起こり失敗します。
どなたか分かる方がいらっしゃったらご指摘お願いします。

A 回答 (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;
}
    • good
    • 0
この回答へのお礼

詳しく調べてくれてとても感謝しています。
静的プロシージャから動的プロシージャを呼び出すことはできているみたいです。(なぜかはまだ分からないのですが詳しく調べてみます。)
CreateDialogParamといった関数でlParamに値を渡せば可能だとわかりました。

>しかし、まーよくこんなトリッキーなコードが思いつくものだ。。。
全くです。ネットで調べて苦戦していました。
沢山のご回答を貰ったお陰でなんとかなりました。
ありがとうございます。

お礼日時:2010/01/30 10:36

 こんにちは。



 http://msdn.microsoft.com/ja-jp/library/cc410696 …
 http://msdn.microsoft.com/ja-jp/library/cc410761 …
 http://msdn.microsoft.com/en-us/library/ms645428 …

 WM_INITDIALOGのlParamは、CreateDialogParam()/DialogBoxParam()等のLPARAMに指定した値が、其のまま伝わって来るのですから、

if( message == WM_INITDIALOG )
  thisPtr = (CBaseWnd*)lParam;

 とすれば取り出せるの筈です。 

この回答への補足

  これで一回試したのですが、そもそもlParamの値が0なんです。
これでは何をやってもダメなのは納得したんですが、lParamの値が0の状態のようです。
DialogBox()の引数に静的プロシージャである、CallProcを指定しているのですが、これがいけないんでしょうか?

補足日時:2010/01/23 17:33
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
どうやらダイアログボックスだとlParamをキャストすれば値が入るようです。

お礼日時:2010/01/23 17:29

質問とは関係ないかもしれませんが・・・


ダイアログボックスのウィンドウプロシージャにするのなら、デフォルト処理ではDefWindowProcを呼び出してはならないのでは?

この回答への補足

そうですね。
ダイアログ用のプロシージャならBOOL型にすべきなのでそうします。
これはウィンドウプロシージャのクラス化(継承可能)なソースをそのまま
転載したものなんです。

補足日時:2010/01/23 00:32
    • good
    • 0

メッセージが WM_INITDIALOG の時に lParam が LPCREATESTRUCT なんですか?


WM_CREATE ではなくて?

この回答への補足

ダイアログプロシージャを作りたいため、WM_INITDIALOGに変更しました。
しかしWM_INITDIALOGでlParamが自分が得たい値になっているかというと疑問なんです。
というか実際アクセス違反になりました。
どうやったらアドレスを得られるかその方法が分からないでいます。

補足日時:2010/01/23 00:36
    • good
    • 0

Set/GetWindowLong よりも Set/GetWindowLongPtr の方が安全だとは思うけど, それはさておきそ

もそも SetWindowLong は成功しているのでしょうか?

この回答への補足

SetWindowLongを直接呼ぶとthisポインタのせいでエラーがでます。
GetWindowLongからは値は0のままなので、その後からthisPtr->SetWindowというふうに
しても変化はないです。

補足日時:2010/01/23 00:46
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!