当前位置:首页 > 嵌入式 > 嵌入式软件
[导读]Windows程序和DOS程序的主要不同点之一是:Windows程序是以事件为驱动、消息机制为基础

Windows程序和DOS程序的主要不同点之一是:Windows程序是以事件为驱动、消息机制为基础

本人对Windows系统、MFC谈不上有深入的了解,但对MFC本身包装API的机制很有兴趣,特别是读了候老师的《深入浅出MFC》后,感觉到Visual C++的Application FrameWork十分精制。在以前,我对SDI结构处理消息有一定的认识,但对于模式对话框的消息机制不了解,读了《深入》一书也没能得到解决,近日,通过在网友的帮助和查阅MSDN,自认为已经了解。一时兴起,写下这些文字,没有其它目的,只是希望让后来者少走弯路,也希望和我一样 喜欢“钻牛角尖”的人共同讨论、学习。如果你是牛人,那么你现在要慎重考虑有没有充足的时间读这些幼稚文字。

正文:

Windows程序和DOS程序的主要不同点之一是:Windows程序是以事件为驱动、消息机制为基础。如何理解?

举了例子,当你CLICK Windows “开始”BUTTON时,为什么就会弹出一个菜单呢?

当你单击鼠标左键时,操作系统中与MOUSE相关的驱动程序在第一时间内得到这个信号[LBUTTONDOWN],然后它通知操作系统―――“嗨,鼠标左键被单击了!”,操作系统得到这一信号后,马上要判断――用户单击鼠标左键,这是针对哪个窗口呢?如何判断?这很简单!当前状态中,具有焦点的窗口[或控件]就是了[这里当然是“开始”BUTTON了]。然后操作系统马上向这个窗口发送一条消息到这个窗口所在进程的消息队列,消息内容应是消息本身的代号、附加参数、窗口句柄…等等了。那么,只有操作系统才有资格发送消息至某一窗口的消息队列吗?不然,其它程序也有资格。你可以在你的程序中调用:SendMessage、PostMessage。这样,被单击的窗口得到了一条由操作系统发送的包含CLICK的消息,操作系统已经暂时不再管窗口的任何事,因为它还要忙于处理其它事务。你的程序得到一条消息后如何做呢?Windows对于你在“开始”BUTTON上的单击事件做出如下反映:弹出一菜单。可是,得到消息到做出反映这一过程是如何实现的呢?这就是本文讨论的主要内容[当然只是针对MFC了]。

我首先简要谈一下SDI,然后会花更多文字描述模式对话框。

对于SDI窗口,你的应用程序类的InitInstance()大约如下:

BOOL CEx06aApp::InitInstance()
{
……………
 CSingleDocTemplate* pDocTemplate;
 pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CEx06aDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CEx06aView));
 AddDocTemplate(pDocTemplate);
 CCommandLineInfo cmdInfo;
 ParseCommandLine(cmdInfo);
 if (!ProcessShellCommand(cmdInfo))
return FALSE;
 m_pMainWnd->ShowWindow(SW_SHOW);
 m_pMainWnd->UpdateWindow();
 return TRUE;
}

完成一些如动态生成相关文档、视,显示主框架窗口、处理参数行信息等工作。这些都是显示在你工程中的“明码”。我们现在把断点设置到return TRUE;一句,跟入MFC源码中,看看到底MFC内部做了什么。

程序进入SRCWinMain.cpp,下一个大动作应是:

nReturnCode = pThread->Run();

注意了,重点来了。F11进入

int CWinApp::Run()
{
 if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
 {
// Not launched /Embedding or /Automation, but has no main window!
TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application. ");
AfxPostQuitMessage(0);
 }
 return CWinThread::Run();
}

再次F11进入:

int CWinThread::Run()
{
 ASSERT_VALID(this);

 // for tracking the idle time state
 BOOL bIdle = TRUE;
 LONG lIdleCount = 0;

 // acquire and dispatch messages until a WM_QUIT message is received.
 for (;;)
 {
// phase1: check to see if we can do idle work
while (bIdle && !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}

// phase2: pump messages while available
do
{
// pump message, but quit on WM_QUIT
if (!PumpMessage())
return ExitInstance();

// reset "no idle" state after pumping "normal" message
if (IsIdleMessage(&m_msgCur))
{
bIdle = TRUE;
lIdleCount = 0;
}

} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
 }

 ASSERT(FALSE); // not reachable
}

BOOL CWinThread::IsIdleMessage(MSG* pMsg)
{
 // Return FALSE if the message just dispatched should _not_
 // cause OnIdle to be run. Messages which do not usually
 // affect the state of the user interface and happen very
 // often are checked for.

 // redundant WM_MOUSEMOVE and WM_NCMOUSEMOVE
 if (pMsg->message == WM_MOUSEMOVE || pMsg->message == WM_NCMOUSEMOVE)
 {
// mouse move at same position as last mouse move?
if (m_ptCursorLast == pMsg->pt && pMsg->message == m_nMsgLast)
return FALSE;

m_ptCursorLast = pMsg->pt; // remember for next time
m_nMsgLast = pMsg->message;
return TRUE;
 }

 // WM_PAINT and WM_SYSTIMER (caret blink)
 return pMsg->message != WM_PAINT && pMsg->message != 0x0118;
}

这是SDI处理消息的中心机构,但请注意,它觉对不是核心!

分析一下,在无限循环FOR内部又出现一个WHILE循环

while (bIdle &&
!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
 // call OnIdle while in bIdle state
 if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}

这段代码是当你程序进程的消息队列中没有消息时,会调用OnIdle做一些后备工作,临时对象在这里被删除。当然它是虚函数。其中的PeekMessage,是查看消息队列,如果有消息返回TRUE,如果没有消息返回FALSE,这里指定PM_NOREMOVE,是指查看过后不移走消息队列中刚刚被查看
到的消息,也就是说这里的PeekMessage只起到一个检测作用,显然返回FALSE时[即没有消息],才会进入循环内部,执行OnIdle,当然了,你的OnIdle返回FLASE,会让程序不再执行OnIdle。你可能要问:

当bidle=0或消息队例中有消息时,程序又执行到哪了呢?

do
{
 // pump message, but quit on WM_QUIT
 if (!PumpMessage())
return ExitInstance();

 // reset "no idle" state after pumping "normal" message
 if (IsIdleMessage(&m_msgCur))
 {
bIdle = TRUE;
lIdleCount = 0;
 }

} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));

看啊,又进入一个循环!

其中有个重要的函数,PumpMessage,内容如下:

BOOL CWinThread::PumpMessage()
{
 ASSERT_VALID(this);

 if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
 {
#ifdef _DEBUG
if (afxTraceFlags & traceAppMsg)
TRACE0("CWinThread::PumpMessage - Received WM_QUIT. ");
m_nDisablePumpCount++; // application must die
// Note: prevents calling message loop things in ’ExitInstance’
// will never be decremented
#endif
return FALSE;
 }

 #ifdef _DEBUG
 if (m_nDisablePumpCount != 0)
 {
TRACE0("Error: CWinThread::PumpMessage called when not permitted. ");
ASSERT(FALSE);
 }
 #endif

 #ifdef _DEBUG
 if (afxTraceFlags & traceAppMsg)
_AfxTraceMsg(_T("PumpMessage"), &m_msgCur);
 #endif

 // process this message

 if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
 {
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
 }
 return TRUE;
}

如你所想,这才是MFC消息处理的核心基地[也是我个人认为的]。

GetMessage不同于PeekMessae,它是不得到消息不罢体,PeekMessage如果发现消息队列中没有消息会返回0,而GetMessage如果发现没有消息,等,直到有了消息,而且,GetMessage不同于PeekMessage,它会将消息移走[当然,PeekMessage也可以做到这点]。我想当你读了这个函数后,
你应明白PreTranslateMessage函数的用法了吧[我比较喜欢在程序中充分利用这个函数]。

::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);

将消息发送到窗口的处理函数[它是由窗口类指定的],之后的动作一直到你的程序做出反映的过程,你可以在《深入》一书中得到完美的解释。我们还是通过reurn
TRUE;回到CWinThread::Run()中的Do{}while;循环。然后还是对IDLE的处理,即便刚才你的ONIDLE返回了FALSE,在这里你看到,你的程序还是有机会执行它的。然后又是利用PeekMessage检测消息队列:

如果有消息[这个消息不被移动的原因是因为它要为PumpMessage内的GetMessage所利用。]再次进入PumpMessage[叫它“消息泵”吧]。

如果没有消息,退出DO循环,但它还在FOR内部,所以又执行第一个While循环。

这是CwinThread::Run的一个执行过程。

不用担心退不出for(;;)如果你的消息队列中有一条WM_QUIT,会使GetMessage返回0,然后PumpMessage返回0而RUN()内部:

if (!PumpMessage())
return ExitInstance();

SDI就说到这,下面我来谈一下模式对话框。我分2种情况讨论:

一当你的工程以模式对话框为基础时[没父窗口,或为桌面]。

与SDI不同处在于,在应用程序类的InItInstance内部:

BOOL CComboBoxApp::InitInstance()
{
 AfxEnableControlContainer();
 // Standard initialization
 // If you are not using these features and wish to reduce the size
 // of your final executable, you should remove from the following
 // the specific initialization routines you do not need.
 #ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
 #else
Enable3dControlsStatic(); // Call this when linking to MFC statically
 #endif
 this->m_nCmdShow = SW_HIDE;
 CComboBoxDlg dlg;
 m_pMainWnd = &dlg;
 int nResponse = dlg.DoModal();
 if (nResponse == IDOK)
 {
// TODO: Place code here to handle when the dialog is
// dismissed with OK
 }
 else if (nResponse == IDCANCEL)
 {
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
 }
 // Since the dialog has been closed, return FALSE so that we exit the
 // application, rather than start the application’s message pump.
 return FALSE;
}

int nResponse = dlg.DoModal();一句使你的整个程序都在DoModal()内部进行。而且,你退出DoMal()时[你一定结束了你的对话框],InitInstance返回的是False,我们知道,这样,CwinThread::Run是不会执行的。

但对话框程序是在哪里进行消息处理的呢。

原来,dlg.DoModal()内部会调用CwinThread::RunModalLoop,它起到的作用和RUN()是一样的[当然内部有细小差别,请参考MSDN]!!!

第二种情况,你是在SDI[或其它]程序中调用Dlg.DoModal() 产生了一模式对话框[有父窗口].

这又是如何运作的呢?

建了这样一个工程做为例子。

SDI,在View中处理LBUTTONDOWN:

MyDLg.DoModal();

MyDLg内有按钮,以惫后用.

没有显示模式对话框前,消息处理一直在Cthread::Run()中进行.

你单击后,程序执行点进入DoModal()内部的RunModalLoop,这又是一个消息处理机制.

不过DoModal()中调用RunModalLoop,前会Disable掉它的父窗口.从RunModalLoop中出来后,再 Enable它.

模式对话框和非模式对话框都是通过调用CreateDialogIndirect()产生创建对话框.那它和非模式对话框区别是什么造成的呢?

1 模式对话框将父窗口DISABLE掉.

我原以为被Disable的窗口是不接收消息的.但后来我马上发现我是错的.但,为什么你对被Disable的窗口进行KeyBorad,Mouse动作时,窗口没反映呢,我想,这可能是操作系统从中搞的鬼.我在本文一开始,就写出操作系统向窗口发送消息的过程,我想当它发现目标窗口处理Disabled状态时,
不会将消息发送给它,但这不能说窗口不接收消息,其它程序[或它本身]发送给它的消息还是可以接收并处理的.

2 模式对话框本身有消息处理机制 RunModalLoop.

对以上两点加以实验.

我在我的刚才建的项目中的模式对话框中加上一个BUTTON,其中加入如下代码:

OnButton1()
{
 GetParaent()->EnableWindow(1);
}

单击,后我们发现,此时它已经不再表现为”模态”,我试着点击菜单,还是会作出正常反映.

我想这此消息[对于父窗口的,如:菜单动作]的处理也应是在模式对话框中的RunModalLoop中进行处理的吧[这点我不能确定].
Windows程序和DOS程序的主要不同点之一是:Windows程序是以事件为驱动、消息机制为基础

本人对Windows系统、MFC谈不上有深入的了解,但对MFC本身包装API的机制很有兴趣,特别是读了候老师的《深入浅出MFC》后,感觉到Visual C++的Application
FrameWork十分精制。在以前,我对SDI结构处理消息有一定的认识,但对于模式对话框的消息机制不了解,读了《深入》一书也没能得到解决,近日,通过在网友的帮助和查阅MSDN,自认为已经了解。一时兴起,写下这些文字,没有其它目的,只是希望让后来者少走弯路,也希望和我一样
喜欢“钻牛角尖”的人共同讨论、学习。如果你是牛人,那么你现在要慎重考虑有没有充足的时间读这些幼稚文字。

正文:

Windows程序和DOS程序的主要不同点之一是:Windows程序是以事件为驱动、消息机制为基础。如何理解?

举了例子,当你CLICK Windows “开始”BUTTON时,为什么就会弹出一个菜单呢?

当你单击鼠标左键时,操作系统中与MOUSE相关的驱动程序在第一时间内得到这个信号[LBUTTONDOWN],然后它通知操作系统―――“嗨,鼠标左键被单击了!”,操作系统得到这一信号后,马上要判断――用户单击鼠标左键,这是针对哪个窗口呢?如何判断?这很简单!当前状态中
,具有焦点的窗口[或控件]就是了[这里当然是“开始”BUTTON了]。然后操作系统马上向这个窗口发送一条消息到这个窗口所在进程的消息队列,消息内容应是消息本身的代号、附加参数、窗口句柄…等等了。那么,只有操作系统才有资格发送消息至某一窗口的消息队列吗?不然,其
它程序也有资格。你可以在你的程序中调用:SendMessage
、PostMessage。这样,被单击的窗口得到了一条由操作系统发送的包含CLICK的消息,操作系统已经暂时不再管窗口的任何事,因为它还要忙于处理其它事务。你的程序得到一条消息后如何做呢?Windows对于你在“开始”BUTTON上的单击事件做出如下反映:弹出一菜单。可是,得到消息
到做出反映这一过程是如何实现的呢?这就是本文讨论的主要内容[当然只是针对MFC了]。

我首先简要谈一下SDI,然后会花更多文字描述模式对话框。

对于SDI窗口,你的应用程序类的InitInstance()大约如下:

BOOL CEx06aApp::InitInstance()
{
……………
 CSingleDocTemplate* pDocTemplate;
 pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CEx06aDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CEx06aView));
 AddDocTemplate(pDocTemplate);
 CCommandLineInfo cmdInfo;
 ParseCommandLine(cmdInfo);
 if (!ProcessShellCommand(cmdInfo))
return FALSE;
 m_pMainWnd->ShowWindow(SW_SHOW);
 m_pMainWnd->UpdateWindow();
 return TRUE;
}

完成一些如动态生成相关文档、视,显示主框架窗口、处理参数行信息等工作。这些都是显示在你工程中的“明码”。我们现在把断点设置到return TRUE;一句,跟入MFC源码中,看看到底MFC内部做了什么。

程序进入SRCWinMain.cpp,下一个大动作应是:

nReturnCode = pThread->Run();

注意了,重点来了。F11进入

int CWinApp::Run()
{
 if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
 {
// Not launched /Embedding or /Automation, but has no main window!
TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application. ");
AfxPostQuitMessage(0);
 }
 return CWinThread::Run();
}

再次F11进入:

int CWinThread::Run()
{
 ASSERT_VALID(this);

 // for tracking the idle time state
 BOOL bIdle = TRUE;
 LONG lIdleCount = 0;

 // acquire and dispatch messages until a WM_QUIT message is received.
 for (;;)
 {
// phase1: check to see if we can do idle work
while (bIdle && !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}

// phase2: pump messages while available
do
{
// pump message, but quit on WM_QUIT
if (!PumpMessage())
return ExitInstance();

// reset "no idle" state after pumping "normal" message
if (IsIdleMessage(&m_msgCur))
{
bIdle = TRUE;
lIdleCount = 0;
}

} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
 }

 ASSERT(FALSE); // not reachable
}

BOOL CWinThread::IsIdleMessage(MSG* pMsg)
{
 // Return FALSE if the message just dispatched should _not_
 // cause OnIdle to be run. Messages which do not usually
 // affect the state of the user interface and happen very
 // often are checked for.

 // redundant WM_MOUSEMOVE and WM_NCMOUSEMOVE
 if (pMsg->message == WM_MOUSEMOVE || pMsg->message == WM_NCMOUSEMOVE)
 {
// mouse move at same position as last mouse move?
if (m_ptCursorLast == pMsg->pt && pMsg->message == m_nMsgLast)
return FALSE;

m_ptCursorLast = pMsg->pt; // remember for next time
m_nMsgLast = pMsg->message;
return TRUE;
 }

 // WM_PAINT and WM_SYSTIMER (caret blink)
 return pMsg->message != WM_PAINT && pMsg->message != 0x0118;
}

这是SDI处理消息的中心机构,但请注意,它觉对不是核心!

分析一下,在无限循环FOR内部又出现一个WHILE循环

while (bIdle &&
!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
 // call OnIdle while in bIdle state
 if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}

这段代码是当你程序进程的消息队列中没有消息时,会调用OnIdle做一些后备工作,临时对象在这里被删除。当然它是虚函数。其中的PeekMessage,是查看消息队列,如果有消息返回TRUE,如果没有消息返回FALSE,这里指定PM_NOREMOVE,是指查看过后不移走消息队列中刚刚被查看
到的消息,也就是说这里的PeekMessage只起到一个检测作用,显然返回FALSE时[即没有消息],才会进入循环内部,执行OnIdle,当然了,你的OnIdle返回FLASE,会让程序不再执行OnIdle。你可能要问:

当bidle=0或消息队例中有消息时,程序又执行到哪了呢?

do
{
 // pump message, but quit on WM_QUIT
 if (!PumpMessage())
return ExitInstance();

 // reset "no idle" state after pumping "normal" message
 if (IsIdleMessage(&m_msgCur))
 {
bIdle = TRUE;
lIdleCount = 0;
 }

} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));

看啊,又进入一个循环!

其中有个重要的函数,PumpMessage,内容如下:

BOOL CWinThread::PumpMessage()
{
 ASSERT_VALID(this);

 if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
 {
#ifdef _DEBUG
if (afxTraceFlags & traceAppMsg)
TRACE0("CWinThread::PumpMessage - Received WM_QUIT. ");
m_nDisablePumpCount++; // application must die
// Note: prevents calling message loop things in ’ExitInstance’
// will never be decremented
#endif
return FALSE;
 }

 #ifdef _DEBUG
 if (m_nDisablePumpCount != 0)
 {
TRACE0("Error: CWinThread::PumpMessage called when not permitted. ");
ASSERT(FALSE);
 }
 #endif

 #ifdef _DEBUG
 if (afxTraceFlags & traceAppMsg)
_AfxTraceMsg(_T("PumpMessage"), &m_msgCur);
 #endif

 // process this message

 if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
 {
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
 }
 return TRUE;
}

如你所想,这才是MFC消息处理的核心基地[也是我个人认为的]。

GetMessage不同于PeekMessae,它是不得到消息不罢体,PeekMessage如果发现消息队列中没有消息会返回0,而GetMessage如果发现没有消息,等,直到有了消息,而且,GetMessage不同于PeekMessage,它会将消息移走[当然,PeekMessage也可以做到这点]。我想当你读了这个函数后,
你应明白PreTranslateMessage函数的用法了吧[我比较喜欢在程序中充分利用这个函数]。

::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);

将消息发送到窗口的处理函数[它是由窗口类指定的],之后的动作一直到你的程序做出反映的过程,你可以在《深入》一书中得到完美的解释。我们还是通过reurn
TRUE;回到CWinThread::Run()中的Do{}while;循环。然后还是对IDLE的处理,即便刚才你的ONIDLE返回了FALSE,在这里你看到,你的程序还是有机会执行它的。然后又是利用PeekMessage检测消息队列:

如果有消息[这个消息不被移动的原因是因为它要为PumpMessage内的GetMessage所利用。]再次进入PumpMessage[叫它“消息泵”吧]。

如果没有消息,退出DO循环,但它还在FOR内部,所以又执行第一个While循环。

这是CwinThread::Run的一个执行过程。

不用担心退不出for(;;)如果你的消息队列中有一条WM_QUIT,会使GetMessage返回0,然后PumpMessage返回0而RUN()内部:

if (!PumpMessage())
return ExitInstance();

SDI就说到这,下面我来谈一下模式对话框。我分2种情况讨论:

一当你的工程以模式对话框为基础时[没父窗口,或为桌面]。

与SDI不同处在于,在应用程序类的InItInstance内部:

BOOL CComboBoxApp::InitInstance()
{
 AfxEnableControlContainer();
 // Standard initialization
 // If you are not using these features and wish to reduce the size
 // of your final executable, you should remove from the following
 // the specific initialization routines you do not need.
 #ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
 #else
Enable3dControlsStatic(); // Call this when linking to MFC statically
 #endif
 this->m_nCmdShow = SW_HIDE;
 CComboBoxDlg dlg;
 m_pMainWnd = &dlg;
 int nResponse = dlg.DoModal();
 if (nResponse == IDOK)
 {
// TODO: Place code here to handle when the dialog is
// dismissed with OK
 }
 else if (nResponse == IDCANCEL)
 {
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
 }
 // Since the dialog has been closed, return FALSE so that we exit the
 // application, rather than start the application’s message pump.
 return FALSE;
}

int nResponse = dlg.DoModal();一句使你的整个程序都在DoModal()内部进行。而且,你退出DoMal()时[你一定结束了你的对话框],InitInstance返回的是False,我们知道,这样,CwinThread::Run是不会执行的。

但对话框程序是在哪里进行消息处理的呢。

原来,dlg.DoModal()内部会调用CwinThread::RunModalLoop,它起到的作用和RUN()是一样的[当然内部有细小差别,请参考MSDN]!!!

第二种情况,你是在SDI[或其它]程序中调用Dlg.DoModal() 产生了一模式对话框[有父窗口].

这又是如何运作的呢?

建了这样一个工程做为例子。

SDI,在View中处理LBUTTONDOWN:

MyDLg.DoModal();

MyDLg内有按钮,以惫后用.

没有显示模式对话框前,消息处理一直在Cthread::Run()中进行.

你单击后,程序执行点进入DoModal()内部的RunModalLoop,这又是一个消息处理机制.

不过DoModal()中调用RunModalLoop,前会Disable掉它的父窗口.从RunModalLoop中出来后,再 Enable它.

模式对话框和非模式对话框都是通过调用CreateDialogIndirect()产生创建对话框.那它和非模式对话框区别是什么造成的呢?

1 模式对话框将父窗口DISABLE掉.

我原以为被Disable的窗口是不接收消息的.但后来我马上发现我是错的.但,为什么你对被Disable的窗口进行KeyBorad,Mouse动作时,窗口没反映呢,我想,这可能是操作系统从中搞的鬼.我在本文一开始,就写出操作系统向窗口发送消息的过程,我想当它发现目标窗口处理Disabled状态时,
不会将消息发送给它,但这不能说窗口不接收消息,其它程序[或它本身]发送给它的消息还是可以接收并处理的.

2 模式对话框本身有消息处理机制 RunModalLoop.

对以上两点加以实验.

我在我的刚才建的项目中的模式对话框中加上一个BUTTON,其中加入如下代码:

OnButton1()
{
 GetParaent()->EnableWindow(1);
}

单击,后我们发现,此时它已经不再表现为”模态”,我试着点击菜单,还是会作出正常反映.

我想这此消息[对于父窗口的,如:菜单动作]的处理也应是在模式对话框中的RunModalLoop中进行处理的吧[这点我不能确定].

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭