Карта сообщений в Win32 No-MFC - PullRequest
       35

Карта сообщений в Win32 No-MFC

5 голосов
/ 08 декабря 2009

Как я могу создать подобную структуру для обработки сообщений 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 ]

13 голосов
/ 08 декабря 2009

Вот краткое изложение кода, который я использую для этого в редакторе программиста 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: Определите метод процедуры статического окна ( ПРИМЕЧАНИЕ: этот метод необходимо будет использовать в качестве процедуры Window класса окна при первой регистрации класса):

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)
};

Редактировать: Как работает этот код.

Секрет понимания того, как работает этот код, состоит в том, чтобы запомнить wndProc в классе xWindow - не что иное, как Win32 Оконная процедура, переданная в RegisterClassEx при регистрации окна Win32.

Теперь, если вы посмотрите на код wndProc , вы увидите, что он немного настраивает и проверяет, но обычно он не делает ничего, кроме отправки сообщения Windows методу dispatch,

Метод диспетчеризация еще проще, поскольку он просто упаковывает сообщение Windows в структуру easy to move и затем отправляет его в 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 . Это точно такой же метод dispatchToMsgMap , который был вызван методом dispatch .

Я знаю, что этот метод обработки сообщений Windows работает, так как я использую этот же подход в редакторе Zeus для Windows .

0 голосов
/ 08 декабря 2009

Карта сообщений MFC сама по себе не использует обычный WndProc. IIRC, он основан на каком-то крюковом механизме.

Тем не менее, я думаю, что адаптировать макросы к обычному WndProc не составит большого труда.

Первый подход, который приходит на ум - позволить макросам создать массив пар функций ID сообщения / обработчика. Еще лучше: используйте карту для повышения производительности.

Ваш WndProc будет циклически проходить по этому массиву для идентификации данного WM и выполнять соответствующий обработчик.

Вы также можете захотеть, чтобы макросы BEGIN_MESSAGE_MAP имитировали оператор switch, где каждая строка ON_BLAH() была бы строкой case внутри switch ().

Это должно быть слишком сложно.

0 голосов
/ 08 декабря 2009

Трудно сделать это с чем-то вроде std::map. В частности, для этого требуется, чтобы у каждого элемента на карте был один и тот же тип, но разные сообщения имеют обработчики, которые принимают разное количество параметров, поэтому указатели на них имеют разный тип.

Возможно, вы захотите взглянуть на макросы взломщика сообщений (особенно HANDLE_MSG) в windowsx.h. Хотя это действительно просто генерирует регистры для оператора switch, он все же позволяет вам писать код, который выглядит как карта сообщений MFC.

0 голосов
/ 08 декабря 2009

Вы можете использовать что-то вроде std :: map . Где short - сообщение окна, а MessageFn - функция, которая обрабатывает сообщение. Затем вы можете обрабатывать сообщения следующим образом:

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

Это будет не так аккуратно, но довольно просто реализовать, хотя вы будете определять карту сообщений во время выполнения, а не во время компиляции.

Другое решение - прочитать макрос-код MFC и посмотреть, как Microsoft это сделала ...

Другое решение, если вам нужно поведение, подобное MFC, без дополнительных затрат, - это использование ATL. Вы также можете взглянуть на определения макросов ATL, чтобы увидеть, как они это сделали ....

Редактировать: Вы можете решить обработку WM_COMMAND или WM_NOTIFY, сохранив также CommandMap и NotifyMap. Затем вы устанавливаете обработчик WM_COMMAND для функции, которая затем выполняет аналогичные действия и передает команду через CommandMap.

Ваша самая большая проблема заключается в том, что вы не получаете ничего в сообщении, которое идентифицирует конкретный экземпляр класса. Это не проблема, если вам нужен только hWnd, но вам может потребоваться сохранить дополнительную глобальную карту HWND для экземпляров классов ...

Это всего лишь 1 решение. Вы можете решить эту проблему разными способами. Я просто поделюсь с вами одной идеей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...