c++ - 单独的线程以防止进度条在 C++ 中“冻结”
问题描述
我建立了一个最简单的进度条,它显示了我的 png 帧生成的进度。这是过程
BOOL myProc(HWND hwndParent, HINSTANCE hInst) {
RECT rcClient;
int cyVScroll;
HWND hwndPB;
DWORD cb;
GetClientRect(hwndParent, &rcClient);
cyVScroll = GetSystemMetrics(SM_CYVSCROLL);
hwndPB = CreateWindowEx(0, PROGRESS_CLASS, (LPTSTR) NULL,
WS_CHILD | WS_VISIBLE, rcClient.left,
rcClient.bottom - cyVScroll,
rcClient.right, cyVScroll,
hwndParent, (HMENU) 0, hInst, NULL);
int N = lastFrame;
SendMessage(hwndPB, PBM_SETRANGE, 0, MAKELPARAM(0, N));
SendMessage(hwndPB, PBM_SETSTEP, (WPARAM)1, 0);
for (int i = 0; i < N + 1; i++) {
int frame = create_pngInt(i);
SendMessage(hwndPB, PBM_STEPIT, 0, 0);
}
DestroyWindow(hwndPB);
DestroyWindow(hwndParent);
return TRUE;
}
我希望我的窗口只承载这个进度条。当循环myProc
完成后,主机窗口应该被销毁。
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE PrevInst, LPSTR args, int ncmdshow) {
WNDCLASSW wc = { 0 };
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hInstance = hInst;
wc.lpszClassName = L"GenPng";
wc.lpfnWndProc = WindowProcedure;
wc.hbrBackground = CreateSolidBrush(0x00171c00);
wc.hIcon = hIcon;
if (!RegisterClassW(&wc))
return -1;
hMainWindow = CreateWindowW(L"GenPng", L"Generating pngs", WS_VISIBLE | WS_OVERLAPPEDWINDOW,
200,
100, 300, 58, NULL, NULL, NULL, NULL);
std::thread q(myProc, hMainWindow, hInst);
MSG msg = { 0 };
while (GetMessage(&msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
但是,窗口hMainWindow
不会被破坏(!)并且进程myProc
不会自行终止(在 myProc 中的循环完成之后)。似乎我不明白一些重要的事情。
我检查了。如果我不创建单独的线程而只是myProc
在 WinMain 中运行,则托管进度条的窗口在 myProc
返回后会被破坏。而这正是我想要的。但是进度条会在几秒钟后冻结(。这就是为什么我需要一个单独的线程。
根据您的评论,我修改了如下代码:
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE PrevInst, LPSTR args, int ncmdshow) {
RECT rect;
WNDCLASSW wc = { 0 };
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hInstance = hInst;
wc.lpszClassName = L"GenPng";
wc.lpfnWndProc = WindowProcedure;
wc.hbrBackground = CreateSolidBrush(0x00171c00);
wc.hIcon = hIcon;
if (!RegisterClassW(&wc))
return -1;
hMainWindow = CreateWindowW(L"GenPng", L"Generating pngs", WS_VISIBLE | WS_OVERLAPPEDWINDOW,
200,
100, 300, 58, NULL, NULL, NULL, NULL);
SetWindowPos(hMainWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
RECT rcClient;
int cyVScroll;
HWND hwndPB;
DWORD cb;
GetClientRect(hMainWindow, &rcClient);
cyVScroll = GetSystemMetrics(SM_CYVSCROLL);
hwndPB = CreateWindowEx(0, PROGRESS_CLASS, (LPTSTR)NULL,
WS_CHILD | WS_VISIBLE, rcClient.left,
rcClient.bottom - cyVScroll,
rcClient.right, cyVScroll,
hMainWindow, (HMENU)0, hInst, NULL);
int N = lastFrame;
SendMessage(hwndPB, PBM_SETRANGE, 0, MAKELPARAM(0, N));
SendMessage(hwndPB, PBM_SETSTEP, (WPARAM)1, 0);
std::thread q(myProc, hMainWindow, hInst, hwndPB);
MSG msg = { 0 };
while (GetMessage(&msg, hMainWindow, NULL, NULL))
{
if (msg.message == WM_QUIT) {
DestroyWindow(hMainWindow);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
但是,我知道我没有正确处理 WM_QUIT 事件,整个事情并没有被破坏。请您再详细说明一下,如果其余代码都可以吗?
解决方案
调用的线程CreateWindowEx
必须是执行GetMessage
循环的线程。
它不会被破坏,因为没有任何东西为它处理事件和消息,包括WM_DESTROY
消息。
将您的窗口创建保留在您的主线程中(并且仅限于该线程!),并仅使用PostMessage
/SendMessage
发送更新事件,最后将销毁消息发送到主线程拥有的窗口。
最终处理WM_DESTROY
到你的主窗口,PostQuitMessage
你就完成了。
在全:
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE PrevInst, LPSTR args, int ncmdshow) {
RECT rect;
WNDCLASSW wc = { 0 };
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hInstance = hInst;
wc.lpszClassName = L"GenPng";
wc.lpfnWndProc = WindowProcedure;
wc.hbrBackground = CreateSolidBrush(0x00171c00);
wc.hIcon = hIcon;
if (!RegisterClassW(&wc))
return -1;
hMainWindow = CreateWindowW(
L"GenPng", L"Generating pngs", WS_VISIBLE | WS_OVERLAPPEDWINDOW,
200,
100, 300, 58, NULL, NULL, NULL, NULL
);
SetWindowPos(hMainWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
RECT rcClient;
int cyVScroll;
HWND hwndPB;
DWORD cb;
GetClientRect(hMainWindow, &rcClient);
cyVScroll = GetSystemMetrics(SM_CYVSCROLL);
hwndPB = CreateWindowEx(
0, PROGRESS_CLASS, (LPTSTR)NULL,
WS_CHILD | WS_VISIBLE, rcClient.left,
rcClient.bottom - cyVScroll,
rcClient.right, cyVScroll,
hMainWindow, (HMENU)0, hInst, NULL
);
int N = lastFrame;
SendMessage(hwndPB, PBM_SETRANGE, 0, MAKELPARAM(0, N));
SendMessage(hwndPB, PBM_SETSTEP, (WPARAM)1, 0);
std::thread q([hMainWindow, hInst, hwndPB, N]() {
for (int i = 0; i < N + 1; i++) {
SendMessage(hwndPB, PBM_STEPIT, 0, 0);
}
DestroyWindow(hMainWindow);
});
MSG msg = { 0 };
while (GetMessage(&msg, NULL, NULL, NULL))
{
if (msg.message == WM_DESTROY) {
PostQuitMessage(0);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
q.join();
// Exit code from PostQuitMessage() is still in msg.param
return msg.param;
}
推荐阅读
- .net - System.InvalidProgramException:JIT 编译器遇到内部限制。(CIL/MSIL)
- css - Tailwind CSS 不应用 display: flex on sm:flex
- python - 为什么熊猫(python)从excel导入空行虽然没有数据可循
- python - 列表元组表示
- javascript - express-validator 不呈现错误前端
- python - 在 Python 中使用 ANOVA 测试
- r - 对 ggplot2 中的图例进行故障排除
- python - Django - 多对多关系的“无效 pk - 对象不存在”
- c++ - 如何将模板参数推送到数组
- python - 行拆分后如何删除空格?