Udostępnij za pośrednictwem


TN006: mapy komunikatów

Ta uwaga opisuje obiekt mapy komunikatów MFC.

The Problem

System Microsoft Windows implementuje funkcje wirtualne w klasach okien korzystających z funkcji obsługi komunikatów. Ze względu na dużą liczbę zaangażowanych komunikatów udostępnienie oddzielnej funkcji wirtualnej dla każdego komunikatu systemu Windows spowoduje utworzenie zbyt dużej tabeli wirtualnej.

Ponieważ liczba komunikatów zdefiniowanych przez system w systemie zmienia się w czasie, a aplikacje mogą definiować własne komunikaty systemu Windows, mapy komunikatów zapewniają poziom pośredni, który uniemożliwia zmianom interfejsu zerwanie istniejącego kodu.

Omówienie

MFC stanowi alternatywę dla instrukcji switch, która była używana w tradycyjnych programach opartych na systemie Windows do obsługi komunikatów wysyłanych do okna. Mapowanie z komunikatów na metody można zdefiniować tak, aby po odebraniu komunikatu przez okno odpowiednia metoda jest wywoływana automatycznie. Ta funkcja mapy komunikatów została zaprojektowana tak, aby przypominała funkcje wirtualne, ale ma dodatkowe korzyści, które nie są możliwe w przypadku funkcji wirtualnych języka C++.

Definiowanie mapy komunikatów

Makro DECLARE_MESSAGE_MAP deklaruje trzy elementy członkowskie dla klasy.

  • Prywatna tablica wpisów AFX_MSGMAP_ENTRY o nazwie _messageEntries.

  • Chroniona struktura AFX_MSGMAP o nazwie messageMap wskazująca tablicę _messageEntries .

  • Chroniona funkcja wirtualna o nazwie GetMessageMap , która zwraca adres messageMap.

To makro należy umieścić w deklaracji dowolnej klasy przy użyciu map komunikatów. Zgodnie z konwencją znajduje się na końcu deklaracji klasy. Przykład:

class CMyWnd : public CMyParentWndClass
{
    // my stuff...

protected:
    //{{AFX_MSG(CMyWnd)
    afx_msg void OnPaint();
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()
};

Jest to format generowany przez aplikacje AppWizard i ClassWizard podczas tworzenia nowych klas. Nawiasy //{{ i //}} są wymagane dla klasy ClassWizard.

Tabela mapy komunikatów jest definiowana przy użyciu zestawu makr, które rozszerzają się na wpisy mapy komunikatów. Tabela rozpoczyna się od wywołania makra BEGIN_MESSAGE_MAP , które definiuje klasę obsługiwaną przez tę mapę komunikatów i klasę nadrzędną, do której przekazywane są nieobsługiwane komunikaty. Tabela kończy się wywołaniem makra END_MESSAGE_MAP .

Między tymi dwoma wywołaniami makra jest wpisem, który ma być obsługiwany przez tę mapę komunikatów. Każdy standardowy komunikat systemu Windows zawiera makro formularza ON_WM_MESSAGE_NAME który generuje wpis dla tego komunikatu.

Standardowy podpis funkcji został zdefiniowany do rozpakowywania parametrów każdego komunikatu systemu Windows i zapewnienia bezpieczeństwa typów. Podpisy te można znaleźć w pliku Afxwin.h w deklaracji CWnd. Każdy z nich jest oznaczony słowem kluczowym afx_msg w celu łatwej identyfikacji.

Uwaga

KlasaWizard wymaga użycia słowa kluczowego afx_msg w deklaracjach obsługi mapy komunikatów.

Te sygnatury funkcji zostały uzyskane przy użyciu prostej konwencji. Nazwa funkcji zawsze zaczyna się od "On". Następuje po nim nazwa komunikatu systemu Windows z usuniętym znakiem "WM_" i pierwszą literą każdego wyrazu wielkich liter. Kolejność parametrów to wParam, a następnie LOWORD(lParam), a następnie HIWORD(lParam). Nieużywane parametry nie są przekazywane. Wszystkie dojścia opakowane przez klasy MFC są konwertowane na wskaźniki do odpowiednich obiektów MFC. W poniższym przykładzie pokazano, jak obsługiwać komunikat WM_PAINT i powodować wywoływanie CMyWnd::OnPaint funkcji:

BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
    //{{AFX_MSG_MAP(CMyWnd)
    ON_WM_PAINT()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

Tabela mapy komunikatów musi być zdefiniowana poza zakresem dowolnej definicji funkcji lub klasy. Nie należy umieszczać go w eksternowym bloku "C".

Uwaga

KlasaWizard zmodyfikuje wpisy mapy komunikatów występujące między nawiasem komentarza //{{ i //}}.

Komunikaty systemu Windows zdefiniowane przez użytkownika

Komunikaty zdefiniowane przez użytkownika mogą być uwzględniane w mapie komunikatów przy użyciu makra ON_MESSAGE . To makro akceptuje numer komunikatu i metodę formularza:

    // inside the class declaration
    afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);

    #define WM_MYMESSAGE (WM_USER + 100)

BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
    ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)
END_MESSAGE_MAP()

W tym przykładzie ustanowimy procedurę obsługi komunikatu niestandardowego, który ma identyfikator komunikatu systemu Windows pochodzący ze standardowego WM_USER podstawowego dla komunikatów zdefiniowanych przez użytkownika. W poniższym przykładzie pokazano, jak wywołać tę procedurę obsługi:

CWnd* pWnd = ...;
pWnd->SendMessage(WM_MYMESSAGE);

Zakres komunikatów zdefiniowanych przez użytkownika, które korzystają z tego podejścia, musi znajdować się w zakresie WM_USER, aby 0x7fff.

Uwaga

Klasa ClassWizard nie obsługuje wprowadzania procedur obsługi ON_MESSAGE z interfejsu użytkownika ClassWizard. Musisz ręcznie wprowadzić je z edytora Visual C++. KlasaWizard przeanalizuje te wpisy i umożliwi przeglądanie ich tak samo jak w przypadku innych wpisów mapy komunikatów.

Zarejestrowane komunikaty systemu Windows

Funkcja RegisterWindowMessage służy do definiowania nowego komunikatu okna, który ma gwarancję unikatowości w całym systemie. ON_REGISTERED_MESSAGE makra służy do obsługi tych komunikatów. To makro akceptuje nazwę zmiennej UINT NEAR , która zawiera zarejestrowany identyfikator komunikatu systemu Windows. Na przykład

class CMyWnd : public CMyParentWndClass
{
public:
    CMyWnd();

    //{{AFX_MSG(CMyWnd)
    afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()
};

static UINT NEAR WM_FIND = RegisterWindowMessage("COMMDLG_FIND");

BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
    //{{AFX_MSG_MAP(CMyWnd)
    ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

Zarejestrowana zmienna identyfikatora komunikatów systemu Windows (WM_FIND w tym przykładzie) musi być zmienną NEAR ze względu na sposób implementacji ON_REGISTERED_MESSAGE.

Zakres komunikatów zdefiniowanych przez użytkownika, które używają tego podejścia, będzie znajdować się w zakresie 0xC000 do 0xFFFF.

Uwaga

KlasaWizard nie obsługuje wprowadzania procedur obsługi ON_REGISTERED_MESSAGE z interfejsu użytkownika ClassWizard. Należy wprowadzić je ręcznie z edytora tekstów. KlasaWizard przeanalizuje te wpisy i umożliwi przeglądanie ich tak samo jak w przypadku innych wpisów mapy komunikatów.

Komunikaty poleceń

Komunikaty poleceń z menu i akceleratorów są obsługiwane w mapach komunikatów za pomocą makra ON_COMMAND. To makro akceptuje identyfikator polecenia i metodę. Tylko określony komunikat WM_COMMAND, który ma parametr wParam równy określonemu identyfikatorowi polecenia, jest obsługiwany przez metodę określoną w wpisie message-map. Funkcje składowe programu obsługi poleceń nie przyjmują parametrów i zwracają wartość void. Makro ma następującą formę:

ON_COMMAND(id, memberFxn)

Komunikaty aktualizacji poleceń są kierowane przez ten sam mechanizm, ale zamiast tego należy użyć makra ON_UPDATE_COMMAND_UI. Funkcje składowe procedury obsługi aktualizacji poleceń przyjmują jeden parametr, wskaźnik do obiektu CCmdUI i zwracają wartość void. Makro ma formularz

ON_UPDATE_COMMAND_UI(id, memberFxn)

Zaawansowani użytkownicy mogą używać makra ON_COMMAND_EX, które jest rozszerzoną formą obsługi komunikatów poleceń. Makro udostępnia nadzbiór funkcji ON_COMMAND. Rozszerzone funkcje składowe programu obsługi poleceń przyjmują jeden parametr, UINT zawierający identyfikator polecenia i zwracają wartość LOGICZNĄ. Wartość zwracana powinna mieć wartość TRUE , aby wskazać, że polecenie zostało obsłużone. W przeciwnym razie routing będzie kontynuowany do innych obiektów docelowych poleceń.

Przykłady tych formularzy:

  • Wewnątrz pliku Resource.h (zwykle generowanego przez program Visual C++)

    #define    ID_MYCMD      100
    #define    ID_COMPLEX    101
    
  • Wewnątrz deklaracji klasy

    afx_msg void OnMyCommand();
    afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI);
    afx_msg BOOL OnComplexCommand(UINT nID);
    
  • Wewnątrz definicji mapy komunikatów

    ON_COMMAND(ID_MYCMD, OnMyCommand)
    ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand)
    ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
    
  • W pliku implementacji

    void CMyClass::OnMyCommand()
    {
        // handle the command
    }
    
    void CMyClass::OnUpdateMyCommand(CCmdUI* pCmdUI)
    {
        // set the UI state with pCmdUI
    }
    
    BOOL CMyClass::OnComplexCommand(UINT nID)
    {
        // handle the command
        return TRUE;
    }
    

Zaawansowani użytkownicy mogą obsługiwać szereg poleceń przy użyciu pojedynczego programu obsługi poleceń: ON_COMMAND_RANGE lub ON_COMMAND_RANGE_EX. Aby uzyskać więcej informacji na temat tych makr, zobacz dokumentację produktu.

Uwaga

Klasa ClassWizard obsługuje tworzenie programów obsługi ON_COMMAND i ON_UPDATE_COMMAND_UI, ale nie obsługuje tworzenia programów obsługi ON_COMMAND_EX ani ON_COMMAND_RANGE. Jednak Kreator klas przeanalizuje i umożliwi przeglądanie wszystkich czterech wariantów obsługi poleceń.

Kontroluj komunikaty powiadomień

Komunikaty wysyłane z kontrolek podrzędnych do okna zawierają dodatkowe informacje we wpisie mapy komunikatów: identyfikator kontrolki. Procedura obsługi komunikatów określona we wpisie mapy komunikatów jest wywoływana tylko wtedy, gdy spełnione są następujące warunki:

  • Kod powiadomienia sterującego (wysoki wyraz lParam), taki jak BN_CLICKED, pasuje do kodu powiadomienia określonego we wpisie message-map.

  • Identyfikator kontrolki (wParam) jest zgodny z identyfikatorem kontrolki określonym we wpisie message-map.

Niestandardowe komunikaty powiadomień sterowania mogą używać makra ON_CONTROL do definiowania wpisu mapy komunikatów z niestandardowym kodem powiadomień. To makro ma formularz

ON_CONTROL(wNotificationCode, id, memberFxn)

W przypadku zaawansowanych ON_CONTROL_RANGE użycia można użyć do obsługi określonego powiadomienia sterującego z zakresu kontrolek z tą samą procedurą obsługi.

Uwaga

Klasa ClassWizard nie obsługuje tworzenia programu obsługi ON_CONTROL ani ON_CONTROL_RANGE w interfejsie użytkownika. Należy wprowadzić je ręcznie za pomocą edytora tekstów. KlasaWizard przeanalizuje te wpisy i umożliwi przeglądanie ich tak samo jak w przypadku innych wpisów mapy komunikatów.

Typowe kontrolki systemu Windows używają bardziej zaawansowanych WM_NOTIFY do obsługi złożonych powiadomień dotyczących kontrolek. Ta wersja MFC ma bezpośrednią obsługę tego nowego komunikatu przy użyciu makr ON_NOTIFY i ON_NOTIFY_RANGE. Aby uzyskać więcej informacji na temat tych makr, zobacz dokumentację produktu.

Zobacz też

Uwagi techniczne według numerów
Uwagi techniczne według kategorii