5

MFC のように Win32 メッセージを処理する同様の構造を作成するにはどうすればよいですか?

MFCで;

BEGIN_MESSAGE_MAP(CSkinCtrlTestDlg, CDialog)
    //{{AFX_MSG_MAP(CSkinCtrlTestDlg)
    ON_BN_CLICKED(IDC_BROWSE, OnBrowse)
    ON_BN_CLICKED(IDC_DEFAULTSKIN, OnChangeSkin)
    ON_WM_DRAWITEM()
    ON_WM_MEASUREITEM()
    ON_WM_COMPAREITEM()
    ON_BN_CLICKED(IDC_CHECK3, OnCheck3)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP マクロは、この動作を処理します。純粋な Win32 の場合はどうすればよいですか?

4

4 に答える 4

14

Zeusプログラマーズ エディタでこれを行うために使用するコードの簡単な要約を次に示します。

ステップ 1: Windows メッセージの詳細を保持するために、いくつかのメッセージ構造を定義します。

typedef struct
{
  MSG     msg;
  LRESULT lResult;
} xMessage;

struct xWM_COMMAND
{
  HWND hwnd;
  UINT Msg;
  WORD ItemID;
  WORD NotifyCode;
  HWND Ctl;
  LRESULT lResult;
};

//-- unpack a message buffer
#define MSG_UNPACK(var, id, msg) x##id *var = (x##id *)(msg);

ステップ 2:いくつかの特別なメソッドを使用してベース ウィンドウ クラスを定義します。

class xWindow
{
protected:
  //-- windows callback function
  static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, 
                                  WPARAM wParam, 
                                  LPARAM lParam);

  //-- a message dispatch method
  void dispatch(HWND hwnd, UINT uMessageID, WPARAM wParam, 
                LPARAM lParam, LRESULT &Result);

  //-- method for command message dispatching
  virtual void dispatchToCmdMap(xMessage *pMessage);

  //-- method for windows message dispatching
  virtual void dispatchToMsgMap(xMessage *pMessage);
};

ステップ 3: Windows メッセージのディスパッチを行うマクロをいくつか定義します。

#define BEGIN_MSG_MAP                          \
   protected:                                  \
   virtual void dispatchToMsgMap(xMessage *msg)\
   {                                           \
     if (msg->msg.message == WM_NULL)          \
     {                                         \
       return;                                 \
     }

#define MSG_HANDLER(meth, wm_msg)              \
     else if (msg->msg.message == wm_msg)      \
     {                                         \
       this->meth(msg);                        \
       return;                                 \
     }

#define END_MSG_MAP(base)                      \
     else if (msg->msg.message == WM_COMMAND)  \
     {                                         \                       
       this->dispatchToCmdMap(msg);            \                       
       return;                                 \                       
     }                                         \                       
     else if (msg->msg.message == WM_NOTIFY)   \                       
     {                                         \                       
       this->dispatchToNotifyMap(msg);         \                       
       return;                                 \                       
     }                                         \                       
                                               \                       
     base::dispatchToMsgMap(msg);              \                       
   };

#define BEGIN_CMD_MAP                          \
   virtual void dispatchToCmdMap(xMessage *msg)\
   {                                           \                              
     MSG_UNPACK(Cmd, WM_COMMAND, msg);         \                              
                                               \                              
     if (Cmd->ItemID == 0)                     \                              
     {                                         \                              
        /* not allowed */                      \                              
     }                                                                        

#define CMD_HANDLER(meth, cmd_id)              \
     else if (Cmd->ItemID == cmd_id)           \
     {                                         \                                
       this->meth(Cmd->ItemID);                \                                
     }                                                                          

#define END_CMD_MAP(base)                      \
     else                                      \                              
     {                                         \                              
       base::dispatchToCmdMap(msg);        \                              
     }                                         \                              
   };

ステップ 4:ディスパッチャ メソッドを定義します。

void xWindow::dispatch(HWND, UINT uMessageID, WPARAM wParam, 
                       LPARAM lParam, LRESULT &Result)
{
  xMessage message;

  //-- build up a message packet
  message.msg.message = uMessageID;
  message.msg.wParam  = wParam;
  message.msg.lParam  = lParam;
  message.lResult     = 0;

  //-- dispatch the message
  this->dispatchToMsgMap(&message);
}

手順 5:静的ウィンドウ プロシージャ メソッドを定義します (注:このメソッドは、クラスを最初に登録するときにウィンドウ クラスのウィンドウ プロシージャとして使用する必要があります)。

LRESULT CALLBACK xWindow::wndProc(HWND hwnd, UINT msg, 
                                  WPARAM wParam, 
                                  LPARAM lParam)
{
  LRESULT lResult = 0;

  //-- look for the creation message
  if (msg == WM_NCCREATE)
  {
    CREATESTRUCT *pCreateData = (CREATESTRUCT*)lParam;

    //-- get the window object passed in
    xWindow *pWindow = (xWindow)pCreateData->lpCreateParams;

    if (pWindow)
    {
      //-- attach the window object to the hwnd
      SetWindowLong(hwnd, pWindow);

      //-- let the window object dispatch the message
      pWindow->dispatch(hwnd, msg, wParam, lParam, lResult);
    }
    else
    {
      //-- leave the message to windows
      lResult = DefWindowProc(hwnd, msg, wParam, lParam);
    }
  }
  else if (hwnd)
  {
    //-- get the object attached to the hwnd
    xWindow *pWindow = (xWindow *)GetWindowLong(hwnd);

    //-- check to see if we have an object window attached to the handle
    if (pWindow)
    {
      //-- let the window object dispatch the message
      pWindow->dispatch(hwnd, msg, wParam, lParam, lResult);
    }
    else
    {
      //-- leave the message to windows
      lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
    }
  }

  return lResult;
}

ここで、この基本クラスを使用して、次のような新しいウィンドウ クラスを定義できます。

class MyWindow : public xWindow
{
protected:  
  //-- the WM_COMMAND message handlers
  virtual void onAdd(int);
  virtual void onDelete(int);

  //-- the WM_CLOSE message handler
  virtual void onClose(xMessage *pMessage);

  //-- the WM_SIZE message handler
  virtual void onSize(xMessage *pMessage);

public:
  //-- ctor and dtor
  MyWindow();
  virtual ~MyWindow();

  BEGIN_MSG_MAP
    //-- command message handlers
    CMD_HANDLER(onAdd   , IDPB_ADD   )
    CMD_HANDLER(onDelete, IDPB_DELETE)

    //-- other message handling
    MSG_HANDLER(onClose , WM_CLOSE)
    MSG_HANDLER(onSize  , WM_SIZE )
  END_MSG_MAP(xWindow)
};

編集:このコードの仕組み。

このコードがどのように機能するかを理解する秘訣は、xWindowクラスの wndProc が、Win32 Window の登録時にRegisterClassExに渡されるWin32 Window プロシージャに過ぎないことを覚えておくことです。

wndProcコードを見ると、セットアップとチェックが少し行われていることがわかりますが、通常は Windows メッセージをディスパッチメソッドに送信するだけです。

ディスパッチメソッドは、Windows メッセージを移動しやすい構造にパックしてから、dispatchToMsgMapメソッドに送信するだけなので、さらに単純です。

MyWindowクラスを見ると、次のコードが表示されます。

BEGIN_MSG_MAP    
   //-- command message handlers    
   CMD_HANDLER(onAdd   , IDPB_ADD   )    
   CMD_HANDLER(onDelete, IDPB_DELETE)    

   //-- other message handling    
   MSG_HANDLER(onClose , WM_CLOSE)    
   MSG_HANDLER(onSize  , WM_SIZE )  
END_MSG_MAP(xWindow)

このコードは、以前に定義したマクロを使用しているだけです。これらのマクロをよく見ると、上記のコードが実際にdispatchToMsgMapメソッドを作成していることがわかります。これは、 dispatchメソッドによって呼び出されたdispatchToMsgMapメソッドとまったく同じです。

Zeus for Windowsエディターでまったく同じアプローチを使用しているため、この Windows メッセージの処理方法が機能することはわかっています。

于 2009-12-08T00:14:02.330 に答える
0

のようなものでこれを行うのは難しいですstd::map。特に、マップ内のすべてのアイテムが同じタイプである必要がありますが、異なるメッセージには異なる数のパラメーターを受け取るハンドラーがあるため、それらへのポインターは同じタイプではありません。

HANDLE_MSGただし、windowsx.hのメッセージクラッカーマクロ(特に)を確認することをお勧めします。これは実際にはswitchステートメントのケースを生成するだけですが、それでもMFCメッセージマップのようなコードを記述できます。

于 2009-12-07T22:11:34.630 に答える
0

std::map< short, MessageFn > のようなものを使用できます。short はウィンドウ メッセージで、MessageFn はメッセージを処理する関数です。その後、次のようにメッセージを処理できます。

if ( messageMap.find( uMsg ) != messageMap.end() )
{
   messageMap[uMsg]( wParam, lParam );
}

コンパイル時ではなく実行時にメッセージ マップを定義することになりますが、それほどきれいではありませんが、実装は非常に簡単です。

もう 1 つの解決策は、MFC マクロ コードを読み、Microsoft がどのようにそれを行ったかを確認することです ...

オーバーヘッドのない MFC のような動作が必要な場合の別の解決策は、ATL を使用することです。また、ATL のマクロ定義を見て、彼らがどのようにそれを行ったかを確認することもできます....

編集: CommandMap と NotifyMap も保存することで、WM_COMMAND または WM_NOTIFY の処理を​​解決できます。次に、WM_COMMAND ハンドラーを関数に設定します。この関数は同様のことを行い、CommandMap を介してコマンドを渡します。

あなたの最大の問題は、特定のクラス インスタンスを識別するメッセージに何も表示されないという事実です。hWnd のみが必要な場合、これは問題ではありませんが、クラス インスタンスへの HWND のグローバル マップをさらに格納する必要がある場合があります。

これは唯一の解決策です。さまざまな方法で問題を解決できます。私はあなたのためにそこに1つのアイデアを投げかけています。

于 2009-12-07T21:58:38.180 に答える
0

MFC メッセージ マップは、通常の WndProc 自体を使用しません。IIRC、それはある種のフックメカニズムに基づいています。

ただし、マクロを通常の WndProc に適合させることはそれほど難しいことではないと思います。

頭に浮かぶ最初のアプローチは、マクロにメッセージ ID/ハンドラー関数のペアの配列を作成させることです。さらに良いこと: マップを使用してパフォーマンスを向上させます。

WndProc はその配列をループして、指定されたものを識別しWM、対応するハンドラーを実行します。

各行がswitch() 内の行であるステートメントBEGIN_MESSAGE_MAPを模倣するマクロが必要な場合もあります。switchON_BLAH()case

それは難しすぎるはずです。

于 2009-12-07T22:02:03.920 に答える