Udostępnij przez


TN006: Mapy komunikatów

Ta notatka opisuje mechanizm mapowania komunikatów MFC.

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.

Przegląd

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 znajduje się wpis dla każdego komunikatu, 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óre 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 / Notatka

ClassWizard wymaga użycia słowa kluczowego afx_msg w deklaracjach obsługi map 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 wielką literą. Kolejność parametrów to wParam , a następnie LOWORD(lParam), a następnie HIWORD(lParam). Nieużywane parametry nie są przekazywane. Wszystkie uchwyty opakowane przez klasy MFC są konwertowane na wskaźniki do odpowiednich obiektów MFC. W poniższym przykładzie pokazano, jak obsłużyć komunikat WM_PAINT, aby wywołać funkcję CMyWnd::OnPaint.

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 / Notatka

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

Komunikaty systemu Windows zdefiniowane przez użytkownika

Komunikaty zdefiniowane przez użytkownika mogą być zawarte 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 mieścić się w przedziale od WM_USER do 0x7fff.

Uwaga / Notatka

ClassWizard nie obsługuje wprowadzania procedur obsługi dla ON_MESSAGE z interfejsu użytkownika ClassWizard. Musisz ręcznie wprowadzić je z edytora Visual C++. ClassWizard przeanalizuje te wpisy i umożliwi przeglądanie ich tak jak innych wpisów map 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. Makro ON_REGISTERED_MESSAGE 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 komunikatu 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 / Notatka

ClassWizard nie obsługuje dodawania procedur obsługi ON_REGISTERED_MESSAGE z poziomu interfejsu użytkownika. Należy wprowadzić je ręcznie z edytora tekstów. ClassWizard przeanalizuje te wpisy i umożliwi przeglądanie ich tak jak innych wpisów map 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 formę

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 funkcjonalności ON_COMMAND. Rozszerzone funkcje składowe programu obsługi poleceń przyjmują jeden parametr, UINT zawierający identyfikator polecenia, i zwracają wartość BOOL. Wartość zwracana powinna mieć wartość TRUE , aby wskazać, że polecenie zostało obsłużone. W przeciwnym razie routing będzie kontynuowany do innych docelowych obiektów 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 / Notatka

ClassWizard obsługuje tworzenie procedur obsługi ON_COMMAND i ON_UPDATE_COMMAND_UI, ale nie obsługuje tworzenia procedur obsługi ON_COMMAND_EX ani ON_COMMAND_RANGE. Jednak Kreator klas przeanalizuje wszystkie cztery warianty obsługi poleceń i pozwoli na ich przeglądanie.

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 postać

ON_CONTROL(wNotificationCode, id, memberFxn)

W przypadku zaawansowanego użycia ON_CONTROL_RANGE można użyć aby obsłużyć określone powiadomienie sterujące dla zakresu kontrolek z tym samym programem obsługi.

Uwaga / Notatka

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

Typowe kontrolki systemu Windows używają bardziej wydajnych WM_NOTIFY do złożonych powiadomień kontroli. 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 także

Uwagi techniczne według numeru
Uwagi techniczne według kategorii