Sdílet prostřednictvím


TN006: Mapy zpráv

Tato poznámka popisuje zařízení mapy zpráv MFC.

Problém

Microsoft Windows implementuje virtuální funkce ve třídách oken, které využívají jeho komunikační funkci. Kvůli velkému počtu zpráv, které se zpracovávají, by poskytnutí samostatné virtuální funkce pro každou zprávu Windows vytvořilo nepřiměřeně velkou vtabulku.

Vzhledem k tomu, že se v průběhu času mění počet zpráv windows definovaných systémem a protože aplikace mohou definovat své vlastní zprávy systému Windows, mapy zpráv poskytují úroveň nepřímých informací, která brání změnám rozhraní v přerušení existujícího kódu.

Přehled

MFC poskytuje alternativu k příkazu switch, který byl použit v tradičních aplikacích se systémem Windows ke zpracování zpráv odeslaných do okna. Mapování zpráv na metody lze definovat tak, aby při přijetí zprávy v okně byla volána příslušná metoda automaticky. Toto zařízení mapy zpráv je navržené tak, aby připomínalo virtuální funkce, ale u virtuálních funkcí jazyka C++ není možné využívat další výhody.

Definování mapy zpráv

Makro DECLARE_MESSAGE_MAP deklaruje tři členy třídy.

  • Privátní pole položek AFX_MSGMAP_ENTRY s názvem _messageEntries.

  • Chráněná AFX_MSGMAP struktura s názvem messageMap , která odkazuje na pole _messageEntries .

  • Chráněná virtuální funkce, GetMessageMap která vrací adresu messageMap.

Toto makro by mělo být vloženo do deklarace jakékoli třídy pomocí map zpráv. Podle konvence je na konci deklarace třídy. Například:

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

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

    DECLARE_MESSAGE_MAP()
};

Jedná se o formát vygenerovaný aplikací AppWizard a ClassWizard při vytváření nových tříd. Pro ClassWizard jsou potřeba závorky //{{ a //}}.

Tabulka mapy zpráv je definována pomocí sady maker, která se rozbalí na položky mapy zpráv. Tabulka začíná voláním makra BEGIN_MESSAGE_MAP , která definuje třídu, která je zpracována touto mapou zpráv, a nadřazenou třídou, do které se předávají neošetřené zprávy. Tabulka končí voláním makra END_MESSAGE_MAP.

Mezi těmito dvěma voláními maker je položka pro každou zprávu, kterou má tato mapa zpráv zpracovat. Každá standardní zpráva systému Windows obsahuje makro formuláře ON_WM_MESSAGE_NAME který pro tuto zprávu vygeneruje položku.

Byl definován standardní podpis funkce pro rozbalení parametrů každé zprávy systému Windows a zajištění bezpečnosti typů. Tyto podpisy mohou být nalezeny v souboru Afxwin.h v deklaraci CWnd. Každý z nich je označen klíčovým slovem afx_msg pro snadnou identifikaci.

Poznámka:

ClassWizard vyžaduje použití klíčového slova afx_msg v deklaracích obslužné rutiny mapování zpráv.

Tyto podpisy funkcí byly odvozeny pomocí jednoduché konvence. Název funkce vždy začíná na "On". Následuje název zprávy systému Windows s odebraným znakem "WM_" a prvním písmenem každého slova s velkými písmeny. Pořadí parametrů je wParam následované LOWORD(lParam), pak HIWORD(lParam). Nepoužité parametry nejsou předány. Všechny popisovače zabalené pomocí tříd MFC jsou převedeny na ukazatele na příslušné objekty MFC. Následující příklad ukazuje, jak zpracovat zprávu WM_PAINT a způsobit zavolání funkce CMyWnd::OnPaint.

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

Tabulka mapy zpráv musí být definována mimo rozsah jakékoli definice funkce nebo třídy. Nemělo by být vloženo do externího "C" bloku.

Poznámka:

ClassWizard upraví položky mapy zpráv nacházející se mezi komentářovými závorkami //{{ a //}}.

Zprávy windows definované uživatelem

Uživatelem definované zprávy mohou být zahrnuty do mapy zpráv pomocí ON_MESSAGE makra. Toto makro přijímá číslo zprávy a metodu formuláře:

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

V tomto příkladu vytvoříme obslužnou rutinu pro vlastní zprávu, která má ID zprávy systému Windows odvozené ze standardního WM_USER základu pro uživatelem definované zprávy. Následující příklad ukazuje, jak volat tuto obslužnou rutinu:

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

Rozsah zpráv definovaných uživatelem, které tento přístup používají, musí spadat do rozsahu od WM_USER až do 0x7fff.

Poznámka:

ClassWizard nepodporuje zadávání obslužných rutin ON_MESSAGE z uživatelského rozhraní. Musíte je zadat ručně z editoru Visual C++. ClassWizard tyto položky parsuje a umožní vám je procházet stejně jako všechny ostatní položky mapy zpráv.

Registrované zprávy systému Windows

Funkce RegisterWindowMessage slouží k definování nové zprávy okna, která je zaručena jedinečná v celém systému. Makra ON_REGISTERED_MESSAGE se používá ke zpracování těchto zpráv. Toto makro přijímá název proměnné UINT NEAR , která obsahuje ID registrované zprávy systému Windows. Například

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

Registrovaná proměnná ID zprávy systému Windows (WM_FIND v tomto příkladu) musí být NEAR proměnná kvůli způsobu, jakým je implementována ON_REGISTERED_MESSAGE.

Rozsah zpráv definovaných uživatelem, které tento přístup používají, bude v rozsahu 0xC000 k 0xFFFF.

Poznámka:

ClassWizard nepodporuje zadávání obslužných rutin pro zprávy ON_REGISTERED_MESSAGE z uživatelského rozhraní ClassWizard. Musíte je zadat ručně z textového editoru. ClassWizard tyto položky parsuje a umožní vám je procházet stejně jako všechny ostatní položky mapy zpráv.

Zprávy příkazů

Zprávy příkazů z nabídek a akcelerátorů se zpracovávají v mapách zpráv pomocí ON_COMMAND makra. Toto makro přijímá ID příkazu a metodu. Pouze konkrétní zpráva WM_COMMAND, která má wParam rovné zadanému ID příkazu, je zpracována metodou uvedenou v položce message-map. Členské funkce obslužné rutiny příkazů nepřebírají žádné parametry a vracejí void. Makro má následující formulář:

ON_COMMAND(id, memberFxn)

Zprávy aktualizace příkazů se směrují stejným mechanismem, ale místo toho použijte makro ON_UPDATE_COMMAND_UI. Členské funkce obslužné rutiny aktualizace příkazu přebírají jeden parametr, ukazatel na objekt CCmdUI, a vracejí void. Makro má formu.

ON_UPDATE_COMMAND_UI(id, memberFxn)

Pokročilí uživatelé mohou použít makro ON_COMMAND_EX, což je rozšířená forma obsluhy zpráv příkazů. Makro poskytuje nadmnožinu funkcí ON_COMMAND. Rozšířené členské funkce obslužné rutiny příkazů přijímají jeden parametr typu UINT, který obsahuje ID příkazu, a vrátí BOOL. Vrácená hodnota by měla být PRAVDA , aby bylo možné označit, že byl příkaz zpracován. Jinak směrování bude pokračovat k dalším cílovým objektům příkazů.

Příklady těchto formulářů:

  • Uvnitř Resource.h (obvykle generované Visual C++)

    #define    ID_MYCMD      100
    #define    ID_COMPLEX    101
    
  • Uvnitř deklarace třídy

    afx_msg void OnMyCommand();
    afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI);
    afx_msg BOOL OnComplexCommand(UINT nID);
    
  • Uvnitř definice mapy zpráv

    ON_COMMAND(ID_MYCMD, OnMyCommand)
    ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand)
    ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
    
  • V souboru implementace

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

Pokročilí uživatelé mohou zpracovávat rozsah příkazů pomocí jedné obslužné rutiny příkazů: ON_COMMAND_RANGE nebo ON_COMMAND_RANGE_EX. Další informace o těchto makrech najdete v dokumentaci k produktu.

Poznámka:

ClassWizard podporuje vytváření obslužných rutin ON_COMMAND a ON_UPDATE_COMMAND_UI, ale nepodporuje vytváření obslužných rutin ON_COMMAND_EX nebo ON_COMMAND_RANGE. Průvodce třídou však provádí analýzu a umožňuje procházet všechny čtyři varianty obslužné rutiny příkazů.

Řízení zpráv oznámení

Zprávy odesílané z podřízených ovládacích prvků do okna mají v záznamu v mapě zpráv dodatečnou informaci: ID ovládacího prvku. Obslužná rutina zprávy zadaná v položce mapy zpráv je volána pouze v případě, že jsou splněny následující podmínky.

  • Kód oznámení ovládacího prvku (vysoké slovo lParam), například BN_CLICKED, odpovídá kódu oznámení zadanému v položce mapy zpráv.

  • ID ovládacího prvku (wParam) odpovídá ID ovládacího prvku zadanému v položce mapy zpráv.

Zprávy oznámení vlastního ovládacího prvku můžou použít makro ON_CONTROL k definování položky mapy zpráv s vlastním kódem oznámení. Toto makro má formát.

ON_CONTROL(wNotificationCode, id, memberFxn)

Pro pokročilé použití ON_CONTROL_RANGE lze použít ke zpracování konkrétního oznámení ovládacích prvků z řady ovládacích prvků se stejnou obslužnou rutinou.

Poznámka:

ClassWizard nepodporuje vytvoření obslužné rutiny ON_CONTROL nebo ON_CONTROL_RANGE v uživatelském rozhraní. Musíte je zadat ručně pomocí textového editoru. ClassWizard tyto položky parsuje a umožní vám je procházet stejně jako všechny ostatní položky mapy zpráv.

Běžné ovládací prvky Windows používají výkonnější WM_NOTIFY pro komplexní oznámení ovládacích prvků. Tato verze MFC má přímou podporu pro tuto novou zprávu pomocí maker ON_NOTIFY a ON_NOTIFY_RANGE. Další informace o těchto makrech najdete v dokumentaci k produktu.

Viz také

Technické poznámky podle čísla
Technické poznámky podle kategorie