5 Stimmen

Nachrichtenkarte in Win32 No-MFC

Wie könnte ich eine ähnliche Struktur erstellen, um Win32-Nachrichten zu behandeln, wie es in MFC ist?

Im 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()

Das Makro BEGIN_MESSAGE_MAP behandelt dieses Verhalten. Was ist bei reinem Win32 zu tun?

14voto

jussij Punkte 10221

Hier ist ein kurze Zusammenfassung des Codes, den ich dazu verwende, in der Zeus Editor für Programmierer:

Schritt 1: Definieren Sie eine Reihe von Nachrichtenstrukturen, um die Details der Windows-Nachrichten zu speichern:

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

Schritt 2: Definieren Sie eine Basisklasse für Fenster mit einigen speziellen Methoden:

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

Schritt 3: Definieren Sie einige Makros, die das Dispatching der Windows-Nachrichten übernehmen:

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

Schritt 4: Definieren Sie die Dispatcher-Methode:

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

Schritt 5: Definieren Sie die statische Fensterprozedurmethode ( HINWEIS: diese Methode muss als Window-Prozedur der Fensterklasse verwendet werden, wenn die Klasse zum ersten Mal registriert wird):

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

Nun, mit diesem Basisklasse ist es möglich, eine neue Fensterklasse zu definieren, die wie folgt aussehen wird:

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

Edit: Wie dieser Code funktioniert.

Um zu verstehen, wie dieser Code funktioniert, müssen Sie sich an die wndProc en el xFenster Klasse ist nichts anderes als eine Win32 Fensterprozedur übergeben an RegisterClassEx wenn das Win32-Fenster registriert ist.

Wenn Sie sich nun die wndProc Code werden Sie sehen, dass er einige Einstellungen und Prüfungen vornimmt, aber im Allgemeinen tut er nichts weiter, als die Windows-Nachricht an die Versand Methode.

En Versand Methode ist noch einfacher, da sie nichts weiter tut, als die Windows-Nachricht in eine leicht zu bewegen Struktur und sendet sie dann an die dispatchToMsgMap método.

Sehen Sie sich nun die MeinFenster Klasse und Sie werden diesen Code sehen:

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)

Dieser Code verwendet lediglich die zuvor definierten Makros. Wenn Sie sich diese Makros genau ansehen, werden Sie feststellen, dass der obige Code tatsächlich eine dispatchToMsgMap Methode. Dies ist genau die gleiche dispatchToMsgMap Methode, die durch den Aufruf der Versand Methode.

Ich weiß, dass diese Methode zur Behandlung von Windows-Nachrichten funktioniert, da ich genau den gleichen Ansatz in der Zeus für Windows Herausgeber.

0voto

Goz Punkte 59671

Sie könnten etwas wie eine std::map< short, MessageFn > verwenden. Dabei ist short die Fenstermeldung und MessageFn die Funktion, die die Meldung verarbeitet. Sie können dann Nachrichten wie folgt behandeln:

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

Es wird nicht ganz so ordentlich sein, aber wäre ziemlich einfach zu implementieren, obwohl Sie die Message-Map zur Laufzeit statt zur Kompilierzeit definieren würden.

Eine andere Lösung besteht darin, den MFC-Makrocode durchzulesen und zu sehen, wie Microsoft es gemacht hat ...

Eine andere Lösung, wenn Sie ein MFC-ähnliches Verhalten ohne den Overhead wünschen, ist die Verwendung von ATL. Sie können auch einen Blick auf die Makrodefinitionen von ATL werfen, um zu sehen, wie sie es gemacht haben ....

Bearbeiten: Sie können die WM_COMMAND- oder WM_NOTIFY-Behandlung lösen, indem Sie auch eine CommandMap und eine NotifyMap speichern. Sie setzen dann den WM_COMMAND-Handler auf eine Funktion, die dann etwas Ähnliches tut und den Befehl über die CommandMap weitergibt.

Ihr größtes Problem ist die Tatsache, dass Sie in der Nachricht nichts finden, das eine bestimmte Klasseninstanz identifiziert. Dies ist kein Problem, wenn Sie nur das hWnd benötigen, aber Sie müssen möglicherweise eine weitere globale Zuordnung von HWNDs zu Klasseninstanzen speichern ...

Dies ist nur 1 Lösung. Sie können das Problem auf viele verschiedene Arten lösen. Ich werfe nur 1 Idee für Sie in den Raum.

0voto

Serge Wautier Punkte 20814

Die MFC-Message-Map verwendet kein reguläres WndProc per se. IIRC, ist es auf eine Art von Hook-Mechanismus basiert.

Ich denke jedoch, dass es nicht sehr schwierig sein sollte, die Makros an eine gewöhnliche WndProc anzupassen.

Der erste Ansatz, der mir in den Sinn kommt, ist, die Makros ein Array von Message id/Handler-Funktionspaaren erstellen zu lassen. Noch besser: Verwenden Sie eine Map, um die Leistung zu verbessern.

Ihr WndProc würde in einer Schleife durch dieses Array laufen, um die angegebene WM und führen den entsprechenden Handler aus.

Sie können auch die BEGIN_MESSAGE_MAP Makros zur Nachahmung einer switch Anweisung, bei der jede ON_BLAH() Zeile wäre eine case Zeile innerhalb der switch().

Das dürfte zu schwer sein.

0voto

Jerry Coffin Punkte 452852

Es ist schwierig, dies mit etwas wie einer std::map . Das setzt insbesondere voraus, dass jedes Element in der Map denselben Typ hat, aber verschiedene Nachrichten haben Handler, die eine unterschiedliche Anzahl von Parametern benötigen, so dass Zeiger auf sie nicht vom selben Typ sind.

Sie sollten einen Blick auf die Makros des Message Crackers werfen (insbesondere HANDLE_MSG ) in windowsx.h. Obwohl dies wirklich nur Fälle für eine switch-Anweisung generiert, können Sie dennoch Code schreiben, der wie eine MFC-Nachrichtenübersicht aussieht.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X