TN006: İleti Eşlemeleri
Bu not, MFC ileti eşleme özelliğini açıklar.
Sorun
Microsoft Windows, mesajlaşma tesisini kullanan pencere sınıflarında sanal işlevler uygular. Çok sayıda ileti söz konusu olduğundan, her Windows iletisi için ayrı bir sanal işlev sağlanması yasaklayıcı derecede büyük bir vtable oluşturur.
Sistem tanımlı Windows iletilerinin sayısı zaman içinde değiştiğinden ve uygulamalar kendi Windows iletilerini tanımlayabildiği için, ileti eşlemeleri arabirim değişikliklerinin var olan kodu bozmasını engelleyen bir dolaylılık düzeyi sağlar.
Genel Bakış
MFC, pencereye gönderilen iletileri işlemek için geleneksel Windows tabanlı programlarda kullanılan switch deyimine bir alternatif sağlar. İletilerden yöntemlere eşleme tanımlanabilir, böylece bir pencere tarafından bir ileti alındığında uygun yöntem otomatik olarak çağrılır. Bu ileti eşleme özelliği sanal işlevlere benzeyecek şekilde tasarlanmıştır, ancak C++ sanal işlevleriyle ek avantajlar sağlanmaz.
İleti Eşlemesi Tanımlama
DECLARE_MESSAGE_MAP makro, bir sınıf için üç üye bildirir.
_messageEntries adlı özel AFX_MSGMAP_ENTRY girdi dizisi.
_messageEntries dizisini işaret eden messageMap adlı korumalı AFX_MSGMAP yapısı.
adlı
GetMessageMap
ve messageMap adresini döndüren korumalı bir sanal işlev.
Bu makro, ileti eşlemeleri kullanılarak herhangi bir sınıfın bildirimine yerleştirilmelidir. Kural gereği, sınıf bildiriminin sonundadır. Örnek:
class CMyWnd : public CMyParentWndClass
{
// my stuff...
protected:
//{{AFX_MSG(CMyWnd)
afx_msg void OnPaint();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Bu, yeni sınıflar oluştururken AppWizard ve ClassWizard tarafından oluşturulan biçimdir. ClassWizard için //{{ ve //}} köşeli ayraçları gerekir.
İleti eşlemesinin tablosu, ileti eşlemesi girişlerine genişleten bir makro kümesi kullanılarak tanımlanır. Tablo, bu ileti eşlemesi tarafından işlenen sınıfı ve işlenmeyen iletilerin geçirildiği üst sınıfı tanımlayan bir BEGIN_MESSAGE_MAP makro çağrısıyla başlar. Tablo, END_MESSAGE_MAP makro çağrısıyla sona erer.
Bu iki makro çağrısı arasında, bu ileti eşlemesi tarafından işlenecek her ileti için bir girdi bulunur. Her standart Windows iletisinde, ON_WM_MESSAGE_NAME biçiminde bir makro bulunur ve bu makro bu ileti için bir girdi oluşturur.
Her Windows iletisinin parametrelerini açmak ve tür güvenliği sağlamak için standart bir işlev imzası tanımlanmıştır. Bu imzalar, CWnd bildiriminde Afxwin.h dosyasında bulunabilir. Her biri kolay tanımlama için anahtar sözcük afx_msg ile işaretlenir.
Dekont
ClassWizard, ileti eşleme işleyicisi bildirimlerinizde afx_msg anahtar sözcüğünü kullanmanızı gerektirir.
Bu işlev imzaları basit bir kural kullanılarak türetilmiştir. İşlevin adı her zaman "ile "On
başlar. Bunu, "WM_" kaldırılmış ve her sözcüğün ilk harfi büyük harfle yazılmış olan Windows iletisinin adı izler. Parametrelerin sıralanması wParam ve ardından LOWORD
(lParam) ve ardından HIWORD
(lParam) olur. Kullanılmayan parametreler geçirilmez. MFC sınıfları tarafından sarmalanan tüm tanıtıcılar, uygun MFC nesnelerine yönelik işaretçilere dönüştürülür. Aşağıdaki örnek, WM_PAINT iletisinin nasıl işleneceğini ve işlevin CMyWnd::OnPaint
çağrılmasına neden olduğunu gösterir:
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
İleti eşleme tablosu herhangi bir işlev veya sınıf tanımının kapsamı dışında tanımlanmalıdır. Bir extern "C" bloğuna yerleştirilmemelidir.
Dekont
ClassWizard, //{{ ve //}} açıklama köşeli ayracı arasında gerçekleşen ileti eşleme girdilerini değiştirir.
Kullanıcı Tanımlı Windows İletileri
Kullanıcı tanımlı iletiler, ON_MESSAGE makro kullanılarak ileti eşlemesine eklenebilir. Bu makro bir ileti numarası ve formun yöntemini kabul eder:
// 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()
Bu örnekte, kullanıcı tanımlı iletiler için standart WM_USER tabanından türetilmiş bir Windows ileti kimliğine sahip özel ileti için bir işleyici oluşturacağız. Aşağıdaki örnekte bu işleyicinin nasıl çağrılacakları gösterilmektedir:
CWnd* pWnd = ...;
pWnd->SendMessage(WM_MYMESSAGE);
Bu yaklaşımı kullanan kullanıcı tanımlı ileti aralığı, 0x7fff WM_USER aralığında olmalıdır.
Dekont
ClassWizard, ClassWizard kullanıcı arabiriminden ON_MESSAGE işleyici yordamları girmeyi desteklemez. Bunları Visual C++ düzenleyicisinden el ile girmeniz gerekir. ClassWizard bu girdileri ayrıştıracak ve diğer ileti eşleme girdileri gibi bunlara göz atmanıza olanak sağlar.
Kayıtlı Windows İletileri
RegisterWindowMessage işlevi, sistem genelinde benzersiz olması garanti edilen yeni bir pencere iletisi tanımlamak için kullanılır. Makro ON_REGISTERED_MESSAGE bu iletileri işlemek için kullanılır. Bu makro, kayıtlı Windows ileti kimliğini içeren bir UINT NEAR değişkeninin adını kabul eder. Örneğin:
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()
Kayıtlı Windows ileti kimliği değişkeni (bu örnekte WM_FIND) ON_REGISTERED_MESSAGE uygulanma biçiminden dolayı NEAR değişkeni olmalıdır.
Bu yaklaşımı kullanan kullanıcı tanımlı ileti aralığı, 0xFFFF 0xC000 aralığında olacaktır.
Dekont
ClassWizard, ClassWizard kullanıcı arabiriminden ON_REGISTERED_MESSAGE işleyici yordamları girmeyi desteklemez. Bunları metin düzenleyicisinden el ile girmeniz gerekir. ClassWizard bu girdileri ayrıştıracak ve diğer ileti eşleme girdileri gibi bunlara göz atmanıza olanak sağlar.
Komut İletileri
Menülerden ve hızlandırıcılardan gelen komut iletileri, ON_COMMAND makroyla ileti eşlemelerinde işlenir. Bu makro bir komut kimliği ve bir yöntem kabul eder. Yalnızca belirtilen komut kimliğine eşit bir wParam içeren belirli WM_COMMAND iletisi, ileti eşleme girdisinde belirtilen yöntem tarafından işlenir. Komut işleyicisi üye işlevleri parametre almaz ve döndürür void
. Makro aşağıdaki forma sahiptir:
ON_COMMAND(id, memberFxn)
Komut güncelleştirme iletileri aynı mekanizma üzerinden yönlendirilir, ancak bunun yerine ON_UPDATE_COMMAND_UI makrosunu kullanın. Komut güncelleştirme işleyicisi üye işlevleri tek bir parametre, bir CCmdUI nesnesine işaretçi alır ve döndürürvoid
. Makronun formu var
ON_UPDATE_COMMAND_UI(id, memberFxn)
Gelişmiş kullanıcılar, genişletilmiş komut iletisi işleyicileri biçimi olan ON_COMMAND_EX makrosunu kullanabilir. Makro, ON_COMMAND işlevselliğinin üst kümesini sağlar. Genişletilmiş komut işleyicisi üye işlevleri tek bir parametre, komut kimliğini içeren bir UINT alır ve bir BOOL döndürür. Komutun işlendiğini belirtmek için dönüş değeri TRUE olmalıdır. Aksi takdirde yönlendirme diğer komut hedef nesnelerine devam eder.
Bu formlara örnekler:
Resource.h dosyasının içinde (genellikle Visual C++tarafından oluşturulur)
#define ID_MYCMD 100 #define ID_COMPLEX 101
Sınıf bildiriminin içinde
afx_msg void OnMyCommand(); afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI); afx_msg BOOL OnComplexCommand(UINT nID);
İleti eşleme tanımının içinde
ON_COMMAND(ID_MYCMD, OnMyCommand) ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand) ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
Uygulama dosyasında
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; }
Gelişmiş kullanıcılar tek bir komut işleyicisi kullanarak bir dizi komutu işleyebilir: ON_COMMAND_RANGE veya ON_COMMAND_RANGE_EX. Bu makrolar hakkında daha fazla bilgi için ürün belgelerine bakın.
Dekont
ClassWizard, ON_COMMAND ve ON_UPDATE_COMMAND_UI işleyicileri oluşturmayı destekler, ancak ON_COMMAND_EX veya ON_COMMAND_RANGE işleyicileri oluşturmayı desteklemez. Ancak, Sınıf Sihirbazı ayrıştırılır ve dört komut işleyicisi değişkeninin tümüne göz atmanıza olanak sağlar.
Bildirim İletilerini Denetleme
Alt denetimlerden bir pencereye gönderilen iletilerin ileti eşleme girişinde ek bilgi vardır: denetimin kimliği. İleti eşleme girdisinde belirtilen ileti işleyicisi yalnızca aşağıdaki koşullar doğruysa çağrılır:
BN_CLICKED gibi denetim bildirim kodu (lParam'ın yüksek sözcüğü), ileti eşleme girdisinde belirtilen bildirim koduyla eşleşir.
Denetim kimliği (wParam), ileti eşleme girdisinde belirtilen denetim kimliğiyle eşleşir.
Özel denetim bildirim iletileri, özel bildirim koduyla bir ileti eşleme girdisi tanımlamak için ON_CONTROL makroyu kullanabilir. Bu makronun formu var
ON_CONTROL(wNotificationCode, id, memberFxn)
Gelişmiş kullanım ON_CONTROL_RANGE , aynı işleyiciye sahip bir dizi denetimden belirli bir denetim bildirimini işlemek için kullanılabilir.
Dekont
ClassWizard, kullanıcı arabiriminde bir ON_CONTROL veya ON_CONTROL_RANGE işleyicisi oluşturmayı desteklemez. Bunları metin düzenleyicisiyle el ile girmeniz gerekir. ClassWizard bu girdileri ayrıştıracak ve diğer ileti eşleme girdileri gibi bunlara göz atmanıza olanak sağlar.
Windows Ortak Denetimleri, karmaşık denetim bildirimleri için daha güçlü WM_NOTIFY kullanır. MFC'nin bu sürümü, ON_NOTIFY ve ON_NOTIFY_RANGE makrolarını kullanarak bu yeni ileti için doğrudan desteğe sahiptir. Bu makrolar hakkında daha fazla bilgi için ürün belgelerine bakın.