MFC Mesaj Döngüsü
API düzeyinde klasik mesaj döngüsü şu biçimdedir:
while (GetMessage(&msg, 0, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
MFC’de mesaj döngüsü CWinThread::Run üye fonksiyonu içerisinde kurulmuştur. Ancak CWinThread::Run üye fonksiyonu içerisinde mesaj döngüsü GetMessage ile değil PeekMessage fonksiyonuyla kurulmuştur. Bilindiği gibi PeekMessage eğer mesaj kuyruğunda mesaj varsa alır, fakat mesaj yoksa thread’i bloke etmez. Böylelikle mesaj döngüsü içerisinde bir fonksiyonun sürekli çağrılabilmesi sağlanmıştır. CWinThread::Run üye fonksiyonu kütüphanede aşağıdakine benzer bir biçimde yazılmıştır:
int CWinThread::Run(void)
{
int bIdle = TRUE;
int idleCount = 0;
for (;;) {
while (!::PeekMessage(…..)) {
if (bIdle && !OnIdle(idleCount++))
bIdle = FALSE;
}
if (!PreTranslateMessage(…..)) {
::TranslateMessage(…..);
::DispatchMessage(…..);
}
}
}
Görüldüğü gibi mesaj döngüsüne bakılırken CWinThread::OnIdle sanal fonksiyonu çağrılmaktadır:
virtual BOOL OnIdle(LONG lCount);
Bu durumda programcı mesaj kuyruğunda mesaj yokken arka plan işler yapmak isterse CWinApp sınıfından türetmiş olduğu sınıf için OnIdle sanal fonksiyonunu yazmalıdır. OnIdle fonksiyonu FALSE ile geri döndürülürse bu fonksiyonun çağrılmasına devam edilmez. TRUE ile geri döndürülürse fonksiyonun çağrılmasına devam edilir. Bu fonksiyon programcı tarafından yazılmazsa duruma göre CWinApp ya da CWinThread’in OnIdle sanal fonksiyonu çağrılır. Kütüphanedeki OnIdle fonksiyonları thread’i belirli bir süre bloke edecek biçimde yazılmışlardır. OnIdle fonksiyonu programcı tarafından yazılacaksa arka plan işlem yapıldıktan sonra thread’in hiç olmazsa bir quanta süresi kadar bloke edilmesi faydalı olur. Aksi halde thread meşgul döngü içerisinde kalacaktır ve OnIdle fonksiyonu çok hızlı ve fazla bir biçimde çağrılacaktır. Ya da OnIdle fonksiyonunu yazan programcı fonksiyonun başında bilinçli olarak CWinThread::OnIdle’ı çağırarak problemi çözebilir. Kuyrukta mesaj varsa mesaj alınır, dispatch edilmeden önce alınan mesaj ile CWinThread’in PreTranslateMessage sanal fonksiyonu çağrılır.
virtual BOOL CWinThread::PreTranslateMessage(MSG *pMsg);
Görüldüğü gibi CWinApp sınıfından türettiğimiz sınıf için PreTranslateMessage fonksiyonunu yazarsak daha mesaj dispatch edilmeden yakalayabiliriz. Bu fonksiyon belirli mesajları devre dışı bırakmak için ya da değiştirmek için yazılabilir. Ya da yalnızca bir araya girme işlemi de yapılabilir. Bu fonksiyonu FALSE ile geri döndürürsek mesaj dispatch edilir, TRUE ile geri döndürürsek dispatch edilmez. Oluşan bir mesaj en erken bu biçimde ele geçirilebilir. Çünkü WindowProc fonksiyonu ile ele geçirilmesi için mesajın dispatch edilmesi gerekir. Örnek bir kullanım şöyle olabilir:
BOOL CMyApp::PreTranslateMessage(MSG *pMsg)
{
if (pMsg->message == WM_RBUTTONDOWN)
pMsg->message = WM_LBUTTONDOWN;
return FALSE;
}
CWinThread::PreTranslateMessage önemli bir işlem yapmamaktadır.
Anımsatma : Bir sınıfın bir üye fonksiyonunun başlangıç adresini tutan göstericilere üye fonksiyon göstericileri denir. Üye fonksiyon göstericileri sınıfın veri elemanı olmak zorunda değildir ve aşağıdaki gibi tanımlanır:
class Sample {
public:
void Func(void);
// …..
};
void (Sample::*pF)(void);
pF = Sample::Func;
Üye fonksiyon göstericisi ile üye fonksiyonu çağırabilmek için bir sınıf nesnesi ya da göstericisine gereksinim duyulur. Ancak çağırma aşağıdaki gibi yapılamaz:
Sample X;
X.pF();
Çünkü nokta operatörünün sağındaki operandın sınıf faaliyet alanında tanımlanmış bir isim olması gerekir. Bu tür çağırmalar için özel iki operatör vardır:
.* ve ->*
Ancak bu operatörler parantez operatöründen daha düşük öncelikli olduğu için aşağıdaki gibi kullanılmalıdır:
Sample X;
Sample *p = new Sample();
void (Sample::*pF)(void);
pF = Sample::Func;
(X.*pF)();
(p->*pF)();
Türetme durumunda türemiş sınıf üye fonksiyon göstericisine taban sınıf üye fonksiyonunun adresini atamak güvenli ve geçerli bir işlemdir. Bunun tersi olan durum güvensiz bir atamadır ve error ile sonuçlanır. Örneğin:
void (A::*pFA)(void);
pFA = B::FuncB; // error
void (B::*pFB)(void);
pFB = A::FuncA; // ok
Tabii türemiş sınıf üye fonksiyonunun adresini taban sınıf üye fonksiyon göstericisine tür dönüştürme operatörüyle atayabiliriz. Örneğin:
void (A::*pFA)(void);
pFA = (void (A::*)(void)) B::FuncB;
