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

时间:2014-02-17 05:30:26

标签: c++ visual-studio message-queue

我有一段旧代码,我在大约15年前编写了一些文件操作,它通过脚本处理1/2源文件并输出到1/2输出文件,我一直在尝试编写它'正确',以便它在Windows 7/8下作为计划任务运行。

我已将其移植到Visual Studio 2013 Express,并设法让它快速工作(执行并生成所需的结果),处理我的文本输入文件,从中删除垃圾,以及生成格式化的CSV文件在大约38秒内,然而它是Ghosts,并且在文件处理完成之前没有响应,这对于用户执行是好的,但是Windows 7和8不喜欢将它作为计划任务运行,并且关闭它一旦鬼魂就会出现。

我已经尝试重写大块处理代码,以便在每行脚本之后返回主消息处理循环,这个函数虽然慢了大约十二十倍(取决于我是不是使用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;
}

感谢您的帮助

1 个答案:

答案 0 :(得分:0)

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