【问题标题】:No matter what, I can't get this progress bar to update from a thread无论如何,我无法从线程更新此进度条
【发布时间】:2010-12-21 06:29:25
【问题描述】:

我有一个用 C 编写的 Windows 应用程序(使用 gcc/MinGW),除了一些 UI 问题外,它运行良好。一,我根本无法从线程更新进度条。事实上,我可能无法更新任何 UI 内容。

基本上,我有一个生成的线程进行一些处理,并且我尝试从该线程更新主线程中的进度条。我通过对主 hwnd 使用 PostMessage() 进行了尝试,但即使我可以做其他事情,例如打开消息框,也没有运气。但是,尚不清楚消息框是在线程中调用还是在主线程中调用。

这里有一些代码:

// in header/globally accessible
HWND wnd; // main application window
HWND progress_bar; //progress bar

typedef struct { //to pass to thread
    DWORD mainThreadId;
    HWND mainHwnd;
    char *filename;
} THREADSTUFF;


//callback function
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
    switch (msg){
        case WM_CREATE:{
            // create progress bar
            progress_bar = CreateWindowEx(
                0,
                PROGRESS_CLASS,
                (LPCTSTR) NULL,
                WS_CHILD | WS_VISIBLE,
                79,164,455,15,
                hwnd,
                (HMENU)20,
                NULL,
                NULL);

            SendMessage(progress_bar, PBM_SETSTEP, 1, 0 );
            SendMessage(progress_bar, PBM_SETPOS, 0, 0 );
            //test to make sure it actually works
            SendMessage(progress_bar, PBM_STEPIT, 0, 0 ); //works fine
            SendMessage(progress_bar, PBM_STEPIT, 0, 0 ); //works fine
            SendMessage(progress_bar, PBM_STEPIT, 0, 0 ); //works fine
            SendMessage(progress_bar, PBM_STEPIT, 0, 0 ); //works fine

            break;
        }

        case WM_COMMAND: {
            if(LOWORD(wParam)==2){ //do some processing in a thread

                //struct of stuff I need to pass to thread
                THREADSTUFF *threadStuff;
                threadStuff = (THREADSTUFF*)malloc(sizeof(*threadStuff));
                threadStuff->mainThreadId = GetCurrentThreadId();
                threadStuff->mainHwnd = hwnd;
                threadStuff->filename = (void*)&filename;
                hThread1 = CreateThread(NULL,0,convertFile (LPVOID)threadStuff,0,NULL);

            }else if(LOWORD(wParam)==5){ //update progress bar

                MessageBox(hwnd,"I got a message!", "Message",  MB_OK | MB_ICONINFORMATION);
                PostMessage(progress_bar,PBM_STEPIT,0,0);
            }
            break;
        }
    }
}

这一切似乎都正常。问题出在线程中:

DWORD WINAPI convertFile(LPVOID params){

    //get passed params, this works perfectly fine
    THREADSTUFF *tData = (THREADSTUFF*)params;

    MessageBox(tData->mainHwnd,tData->filename,"File name",MB_OK | MB_ICONINFORMATION); //yep

    PostMessage(tData->mainHwnd,WM_COMMAND,5,0); //only shows message
    PostThreadMessage(tData->mainThreadId,WM_COMMAND,5,0); //does nothing
}

当我说“只显示消息”时,这意味着回调中的 MessageBox() 函数有效,但 PostMessage() 不能更新进度条的位置。

如果我使用 PostThreadMessage() 将消息发送到主线程的消息循环,我可以拦截它并启动 MessageBoxes,因此它肯定可以工作。但是,即使我尝试以这种方式更新进度条。它仍然不会更新。

我错过了什么?

【问题讨论】:

  • 这看起来像你的主窗口的窗口过程,对吧?现场没有模态对话框?假设是这种情况,那么这里有一根救命稻草:MSDN 说 PBM_STEPIT 消息参数都必须为零,但您传递的是 0xff000000 的 CLR_DEFAULT。我还假设您没有将范围设置为 0-0 或类似的东西。
  • 我注意到了,但这并没有什么不同。当从主线程调用时,任何一个值都可以工作,但从工作线程调用时,这两个值都不会工作。我更新了代码以显示 0,0。
  • 我想通了。问题是我在它为自己创建的线程中有另一个 HWND,但 WNDCLASSEX 变量名称与主线程中的名称相同。我将变量名称更改为其他名称,现在它可以工作了。然而,我不知道这是为什么。即使 var 名称相同,它也不应该影响线程,因为它不在同一个范围内,对吧?
  • 对不起,我的意思是“类名”,而不是变量名。您使用 RegisterClassEx() 注册的类。如果它与主线程的窗口类命名相同,则进度条不会更新。
  • 您的意思是您在两次调用RegisterClassEx 时使用了相同的名称(PROGRESS_CLASS)?第二次这样的调用将失败。变量范围在这里不相关——在内部,Windows 保留了一个已注册类的列表。然后,当您创建进度条窗口时,它将与主窗口具有相同的类,这意味着它将使用相同的窗口过程。该窗口过程没有 WM_COMMAND/PBM_STEPIT 的处理程序。

标签: windows multithreading winapi mingw


【解决方案1】:

来自MSDN documentation for PBM_STEPIT

wParam
    Must be zero.
lParam
    Must be zero.

CLR_DEFAULT 定义为 0xFF000000L。如果您将代码更改为:

PostMessage(progress_bar, PBM_STEPIT, 0, 0);

【讨论】:

  • 我试过了。使用 0 或 CLR_DEFAULT 似乎没有区别。
  • 您是否确认progress_bar 不为NULL(即CreateWindowEx() 成功)?如果您使用 SendMessage() 而不是 PostMessage() 来传递 PBM_STEPIT,然后立即通过 UpdateWindow() 强制更新会发生什么?编辑:啊,没关系。我错过了你说从主线程调用相同代码时可以正常工作的部分。
  • progress_bar 有效。事实上,作为一个测试,我在创建 PBM_STEPIT 后立即调用了几次以验证它是否正常工作,并且它是(我看到条形图填满了一点)。如果它发生在主线程上,一切似乎都可以正常工作。一旦我尝试在工作线程中做任何事情(包括向主线程发送消息以进行更新),什么都没有发生。
  • 也许使用 Spy++ 来观察发送到进度控件的消息流量,并比较从主线程与单独线程调用时发生的情况,看看您是否可以发现任何差异(例如,实际上是 PBM_STEPIT已交付,您是否看到在那之后不久发生了 WM_PAINT?)
  • 其他一些建议:(1)摆脱 PostThreadMessage() - 这在您的情况下不起作用,因为发布的消息与任何窗口都没有关联,因此不会被发送到你的 WndProc。 (2) 检查两次调用 PostMessage() 的返回值,如果有失败调用 GetLastError() 以确定失败的原因。
【解决方案2】:

我怀疑问题出在您的消息循环中。无论如何,三件事:

  • 我认为没有任何理由发布 PBM_STEPIT 消息,发送即可。
  • 检查您的消息循环并确保您没有做像 GetMessage(&msg,hwnd,... 这样愚蠢的事情 - 始终为 GetMessage 的 hwnd 参数传递 NULL 否则发送到其他窗口的消息将永远不会被发送。
  • WindowProc 总是由正确线程中的窗口调用。所以你可以SendMessage,或者PostMessage,直接从工作线程到进度控制去更新它。

第 4 点奖励:

  • MessageBox 创建一个窗口,并在调用线程中运行它的消息循环。在您的情况下,所有窗口都已创建并在主线程中运行。但是在工作线程中显示消息框并没有真正的缺点(除了工作线程处理将停止直到消息框关闭的事实)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多