处理Windows消息,以便我的应用程序正确响应

Handling Windows messages so that my application responds correctly

本文关键字:应用程序 响应 我的 Windows 消息 处理      更新时间:2023-10-16

我有一段大约15年前写的旧代码,用来进行一些文件操作,它通过一个脚本来处理1/2个源文件并输出到1/2个输出文件,我一直在努力"正确"地编写它,以便它能在Windows 7/8下作为计划任务运行。

我已经将其移植到Visual Studio 2013学习版,并设法使其快速工作(执行并生成所需的结果),处理我的文本输入文件,从中清除垃圾,并在大约38秒内生成格式化的CSV文件,但它会Ghosts,并且在文件处理完成之前没有响应,这对用户执行来说是正常的,但Windows7和Windows8不喜欢将其作为计划任务运行,一旦出现问题就关闭它。

我试着重新编写了大量的处理代码,这样它在每行脚本之后都会返回到主消息处理循环,尽管速度慢了大约一二十倍(这取决于我是使用GetMessage还是PeekMessage,但我仍然在努力解决应用程序重影问题,尽管每行脚本只需要几毫秒的运行时间。

我的主窗口代码当前为;

while (msg.message != WM_QUIT)
{
while ((PeekMessage(&msg, NULL, 0, WM_COMMAND - 1, PM_REMOVE) > 0) || 
(PeekMessage(&msg, NULL, WM_COMMAND+1, 0xFFFF, PM_REMOVE) > 0)) 
// While there are any system messages with a value <> WM_COMMAND, 
// Process these first
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) 
// Process one WM_COMMAND message i.e. one of mine.
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

其中,我的WndProc函数有一个巨大的case语句,它处理我发布的所有消息,以执行我的文件处理的一个子任务和所有windows系统类型响应。我删掉了一些"做事"的命令,但你会明白要点的它是一个;直到我们到达脚本文件中的迭代位;工艺阶段1直到我们完成对源文件的处理或进行足够的迭代;工艺阶段2直到我们到达源文件的末尾工艺阶段3结束

switch (message)
{
case WM_COMMAND:
wmId    = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
//////////////////////////////
/* Start of my menu uptions */
//////////////////////////////
case MI_FILE_EXIT:
DestroyWindow(hWnd);
return(0);
case MI_RUN_EXECUTE:
// * Open Necessary Files * //
// * Start Processing ScriptFile * //
phase = 1;
command = 'A';
PostMessage(hWnd, WM_COMMAND, MI_PHASE_1, lParam);
return(0);
case MI_PHASE_1:
nextcommand = ProcessCommand(command);
if (ScriptFileComplete)
{
// We've reached the end of the script - stop processing
PostMessage(hWnd, WM_COMMAND, MI_SCRIPT_COMPLETE, lParam);
break;
}
else if (nextcommand = '<')
{
// prep for start of phase 2
phase = 2;
tiptr = tptr;
command = nextcommand;
PostMessage(hWnd, WM_COMMAND, MI_PHASE_2, lParam);
break;
}
else
{
// process the nextcommand
command = nextcommand;
PostMessage(hWnd, WM_COMMAND, MI_PHASE_1, lParam);
break;
}
return(0);
case MI_PHASE_2:
nextcommand = ProcessCommand(command);
redrawscreen(hWnd);
if (nextcommand == '>')
{
// we're at the end of the iteration
if (UseIterationCount)
{
IterationCount--;
if (IterationCount <= 0)
{
phase = 3;
command = nextcommand;
PostMessage(hWnd, WM_COMMAND, MI_PHASE_3, lParam);
break;
}
}
}
else if (ScriptFileComplete)
{
// We've reached the end of the script - stop processing
PostMessage(hWnd, WM_COMMAND, MI_SCRIPT_COMPLETE, lParam);
break;
}
else if (SourceFileAComplete)
// We've reached the end of the Source File - stop processing
{
PostMessage(hWnd, WM_COMMAND, MI_SCRIPT_COMPLETE, lParam);
break;
}
else // All's normal and we're just processing the next command
{
command = nextcommand;
PostMessage(hWnd, WM_COMMAND, MI_PHASE_2, lParam);
break;
}
return(0);
case MI_PHASE_3:
nextcommand = ProcessCommand(command);
command = nextcommand;
// test to see if we go round phase 3 loop again
if ((!ScriptFileComplete) && (!SourceFileAComplete))
{
PostMessage(hWnd, WM_COMMAND, MI_PHASE_3, lParam);
break;
}
else // we've cleared that and we're at scriptfile EOF
{
PostMessage(hWnd, WM_COMMAND, MI_SCRIPT_COMPLETE, lParam);
break;
}
return(0);
case MI_SCRIPT_COMPLETE:
// We're at the end of the script - close things down.
CloseFiles();
DrawMenuBar(hWnd);
if (AutoExit)
PostMessage(hWnd, WM_QUIT, wParam, lParam);
return(0);
case MI_DO_NOTHING: // blank code for initial entry to main message loop
return(0);
default: // somehow an invalid message was posted
log("invalid message was posted");
return(0);
}
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
redrawscreen(hWnd);
EndPaint(hWnd, &ps);
return(0);
case WM_DESTROY:
PostQuitMessage(0);
return(0);
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return(0);

}

因此,我们的想法是,在每次调用processCommand(即执行我的脚本处理的一行)后,返回到主消息句柄,响应任何事件(如有人移动窗口或单击关闭按钮),然后处理我的脚本的下一行。

我显然错过了一些(或很多)我应该定期检查和处理的信息,所以如果有人可以提供建议/建议的话;我应该先处理哪些信息?我的应用程序应该多久检查一次系统消息?我是不是完全错了?例如,我应该对处理代码进行线程处理吗?-该应用程序在运行时不需要任何用户交互,但允许用户移动/调整大小/退出它将是一种很好的做法,我希望通过将消息发布回WM_COMMAND来实现这一点

非常感谢你读到这里。任何和所有的建议,无论多么尖锐(如果有帮助的话)都会受到赞赏。

理查德。


-更新-

感谢这两个指针-很明显,这确实保证了线程化我的慢速文件访问代码,所以我重新编写了代码来完成这项工作,但现在我只是有点纠结于应该在哪里启动线程/然后连接-有很多例子表明,线程在主窗口中启动,然后直接连接,例如。;

void My_Slow_Task(){
; // Process lots of data
}
int main(){
std::thread t1(My_Slow_Task);
t1.join();
return 0;
}

但正如下面所指出的,在我的消息处理块中这样做只意味着它像柠檬一样坐在那里,直到线程结束,但我需要用户能够在启动文件操作过程之前对输出文件等进行更改(它可以是自动化的,但它需要不那么好),所以目前,我正在追求这样的东西;

while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
// one of these messages is the menuitem for 'Run_my_big_chunk_of_processing' and sets 'ExecutingScript' to TRUE
// another will be to change the location of the output file or edit the script etc.
if (ExecutingScript == TRUE)
{
ExecutingScript = FALSE;
std::thread t1(Run_my_big_chunk_of_processing);
t1.join();
PostMessage(hWnd, WM_COMMAND, MI_RUN_COMPLETE, lParam);
// to do the close files, tidy up and feed back to the user.
}
}

如果有人知道我如何处理用户的请求,以启动缓慢的进程并启动它,而GUI不必等待它完成才能处理其他消息,那么就这样排序了。


-解决方案-

在线程未运行时使用外部控制循环进行用户交互,在线程运行时使用内部控制循环;

_twinMain()
{
// Initialise Stuff
// Pre-exec message loop
// Outer messagehandling loop
while (!AllDone)
// we're !AllDone on entry, and AllDone when the app gets
// a WM_QUIT or other triggered abort e.g. script failure
{
// Pre/post-exec message loop
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (ExecutingScript) 
// this will be set by auto-run in the ini file, 
// or manually by a user clicking the menu option
{
std::thread t1(RunThread);
// Exec message loop
while (ExecutingScript)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0)
// There may be no messages, as we could be in auto-run 
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
t1.join();
PostMessage(hWnd, WM_COMMAND, MI_RUN_COMPLETE, lParam);
}
}
return (int) msg.wParam;
}

感谢您的帮助

VS2013有C++11<thread>的东西,不需要非标准库。std::aync(std::launch::async)将运行后台任务,std::future::wait_for允许您从消息循环中检查操作是否已完成。当然,您也可以使用来自处理线程的旧式PostQuitMessage(WM_APP)。无论哪种方式,在消息循环退出后,在已工作的线程上调用.join()进行清理,然后退出主线程。