Я пытаюсь использовать IProgressDialog в режиме PROGDLG_MODAL, и я нажимаю два коряга.Вопросы IProgressDialog: Не понимаю ли я его многопоточность, поскольку мне, похоже, нужен насос сообщений? Могу ли я избежать открытия другого диалога до его окончания?
Во-первых, из того, что я собираю из MSDN и комментария блока в файле заголовка <shlobj.h>
, описывающего IProgressDialog, вы должны использовать его непосредственно из потока, который вы выполняете, и что IProgressDialog будет выполнять свой материал UI из другого потока ; то есть
while (there_is_still_work_to_do()) {
if (pd->HasUserCancelled())
break;
do_more_work();
completed++;
pd->SetProgress64(completed, total);
}
достаточно, чтобы запустить динамический диалог.
Однако на практике мне все еще нужно передавать сообщения в Итак, мне нужно перекачать сообщения, но теперь я все еще смущен тем, что говорит MSDN, поскольку: какие методы IProgressDialog можно запустить из моего рабочего потока? Зная это, я могу правильно изменить свой код.do_more_work()
; если я этого не сделаю, диалог прогресса не появится до окончания цикла! Так я не понимаю MSDN, когда он говорит: «Объект затем обрабатывает обновление в фоновом потоке»?
Более серьезная проблема, однако, заключается в том, что StopProgressDialog()
не срывает окно сразу. На самом деле, ни Release()
! В немодальных ситуациях, похоже, что диалог прогресса задерживается на экране еще некоторое время. Эта конкретная проблема была рассмотрена ранее; Я доберусь до этого. Однако на этот раз я использую диалог прогресса в виде диалогового окна с интерфейсом UI. Если мне удастся вызвать другую функцию диалогового окна, например MessageBox()
, сразу после выпуска IProgressDialog, мы завершаем двумя модальными диалогами с тем же окном владельца: диалог прогресса отклоняется и снова включает главное окно, в то время как окно сообщения все еще работает.
Немодальный корпус был решен другими by merely hiding the progress dialog after calling StopProgressDialog()
. Хотя это скрывает окно, оно ничего не делает о модальности. Основываясь на гипотезе респондента вопроса в конце связанного вопроса, я также пробовал отправлять и отправлять WM_NULL
с после StopProgressDialog()
. Это тоже не сработало.
Последнее, я попытался установить крюк WinEvents, ожидая, когда окно будет уничтожено и будет запущено объект события, когда он есть. Это работает; MessageBox()
не произойдет, пока диалог прогресса не будет уничтожен. Однако это не работает отлично: главное окно не активируется снова сразу, а MessageBox()
даже не отображается в фоновом режиме, пока я не щелкнул значок панели задач главного окна.
(Даже тогда, потому что я не могу передать LPARAM моему крюку WinEvents, если бы я хотел обрабатывать несколько IProgressDialogs для разных потоков, мне нужно было бы иметь глобальный список оконных ручек для наблюдения (и связанного с ними события объекты), которые синхронизированы, но, к счастью, я не нуждаюсь в этом для своих целей. Кроме того, все вышеперечисленное относится к предположению, что CLSID_ProgressDialog также является IOleWindow, если это когда-либо изменяется, то ...)
Am Я делаю что-то не так с этим способом WinEvents? Я подозреваю, что неправильно звонил MsgWaitForMultipleObjectsEx()
, но если выяснится, что IProgressDialog не реализует модальность должным образом, то я думаю, мне не повезло: S
Пример программы ниже делает все вышеперечисленное. Я тестировал его с помощью Visual Studio 2013 на 64-разрядной версии Windows 7 (в качестве 64-разрядной версии).
// 14 december 2015
#define _UNICODE
#define UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
// get Windows version right; right now Windows XP
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_WINDOWS 0x0501 /* according to Microsoft's winperf.h */
#define _WIN32_IE 0x0600 /* according to Microsoft's sdkddkver.h */
#define NTDDI_VERSION 0x05010000 /* according to Microsoft's sdkddkver.h */
#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>
void chk(HRESULT hr) { if (hr != S_OK) DebugBreak(); }
void doWork(bool pumpMessages)
{
UINT_PTR timer;
MSG msg;
if (!pumpMessages) {
Sleep(2000);
return;
}
timer = SetTimer(NULL, 20, 2000, NULL);
while (GetMessageW(&msg, NULL, 0, 0)) {
if (msg.message == WM_TIMER && msg.hwnd == NULL)
break;
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
KillTimer(NULL, timer);
}
HWINEVENTHOOK hook;
HWND dialogWindow;
HANDLE dialogEvent;
void CALLBACK onDialogClosed(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime)
{
if (hwnd == dialogWindow)
SetEvent(dialogEvent);
}
void waitEvent(void)
{
MSG msg;
DWORD ret;
for (;;) {
ret = MsgWaitForMultipleObjectsEx(1, &dialogEvent,
INFINITE, QS_ALLINPUT, MWMO_INPUTAVAILABLE);
if (ret == WAIT_OBJECT_0) // event
break;
if (GetMessage(&msg, NULL, 0, 0) == 0)
break;
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
void rundialogs(HWND parent, bool pumpMessages, bool tryHide, int tryHideWhat, bool abort)
{
IProgressDialog *pd;
IOleWindow *olewin;
HWND hwnd;
DWORD process;
DWORD thread;
chk(CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pd)));
chk(pd->SetTitle(L"Test"));
chk(pd->StartProgressDialog(parent, NULL,
PROGDLG_NORMAL | PROGDLG_MODAL | PROGDLG_AUTOTIME | PROGDLG_NOMINIMIZE,
NULL));
doWork(pumpMessages);
chk(pd->Timer(PDTIMER_RESET, NULL));
for (ULONGLONG i = 0; i < 10; i++) {
if (pd->HasUserCancelled())
break;
doWork(pumpMessages);
if (i == 5 && abort)
break;
chk(pd->SetProgress64(i + 1, 10));
}
chk(pd->QueryInterface(IID_PPV_ARGS(&olewin)));
chk(olewin->GetWindow(&hwnd));
olewin->Release();
// set up event hoook before stopping the progress dialog
// this way it won't get sdestroyed before the hook is set up
if (tryHide && tryHideWhat == 3) {
thread = GetWindowThreadProcessId(hwnd, &process);
dialogWindow = hwnd;
dialogEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
ResetEvent(dialogEvent);
hook = SetWinEventHook(EVENT_OBJECT_DESTROY, EVENT_OBJECT_DESTROY,
NULL, onDialogClosed,
process, thread,
WINEVENT_OUTOFCONTEXT);
}
chk(pd->StopProgressDialog());
if (tryHide)
switch (tryHideWhat) {
case 0: // hide
ShowWindow(hwnd, SW_HIDE);
break;
case 1: // send WM_NULL
SendMessageW(hwnd, WM_NULL, 0, 0);
break;
case 2: // post WM_NULL
PostMessageW(hwnd, WM_NULL, 0, 0);
break;
case 3: // winevents
waitEvent();
UnhookWinEvent(hook);
break;
}
pd->Release();
MessageBoxW(parent,
L"This should be MODAL to the main window!\n"
L"But you should see that in reality the main window\n"
L"is still enabled!",
L"mainwin",
MB_OK | MB_ICONINFORMATION);
}
HWND button;
HWND checkbox;
HWND checkbox2;
HWND combobox;
HWND checkbox3;
bool ischecked(HWND hwnd) { return SendMessageW(hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED; }
static LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_COMMAND && lParam == (LPARAM) button)
rundialogs(hwnd,
ischecked(checkbox),
ischecked(checkbox2),
(int) SendMessageW(combobox, CB_GETCURSEL, 0, 0),
ischecked(checkbox3));
if (uMsg == WM_CLOSE)
PostQuitMessage(0);
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
WNDCLASSW wc;
HWND mainwin;
MSG msg;
CoInitialize(NULL);
ZeroMemory(&wc, sizeof (WNDCLASSW));
wc.lpszClassName = L"mainwin";
wc.lpfnWndProc = wndproc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
RegisterClassW(&wc);
mainwin = CreateWindowExW(0,
L"mainwin", L"mainwin",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
200, 220,
NULL, NULL, hInstance, NULL);
button = CreateWindowExW(0,
L"button", L"Click Me",
BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
10, 10, 150, 100,
mainwin, (HMENU) 100, hInstance, NULL);
checkbox = CreateWindowExW(0,
L"button", L"Pump Messages",
BS_CHECKBOX | BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE,
10, 110, 150, 20,
mainwin, (HMENU) 101, hInstance, NULL);
checkbox2 = CreateWindowExW(0,
L"button", L"Try",
BS_CHECKBOX | BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE,
10, 130, 50, 20,
mainwin, (HMENU) 101, hInstance, NULL);
combobox = CreateWindowExW(0,
L"combobox", L"",
CBS_DROPDOWNLIST | WS_CHILD | WS_VISIBLE,
60, 130, 100, 100,
mainwin, (HMENU) 102, hInstance, NULL);
SendMessageW(combobox, CB_ADDSTRING, 0, (LPARAM) L"Hide");
SendMessageW(combobox, CB_ADDSTRING, 0, (LPARAM) L"Send");
SendMessageW(combobox, CB_ADDSTRING, 0, (LPARAM) L"Post");
SendMessageW(combobox, CB_ADDSTRING, 0, (LPARAM) L"WinEvents");
SendMessageW(combobox, CB_SETCURSEL, 0, 0);
checkbox3 = CreateWindowExW(0,
L"button", L"Abort Early",
BS_CHECKBOX | BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE,
10, 150, 150, 20,
mainwin, (HMENU) 103, hInstance, NULL);
ShowWindow(mainwin, nCmdShow);
UpdateWindow(mainwin);
while (GetMessageW(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
CoUninitialize();
return 0;
}
UPDATE Да, при дальнейшем рассмотрении оказывается, что, когда я прекращаю диалог выполнения, не показывая MessageBox, окно владелец делает НЕ получить фокус назад либо! Я предполагаю, что диалог прогресса просто не обрабатывает модальность правильно; если я удалю флаг PROGDLG_MODAL
, все будет хорошо. О, хорошо:/Мне нужно либо подделать модальность, либо переключиться на что-то еще.
Я мог бы просто отключить отображение своего окна сообщения над самим диалогом прогресса и надеяться, что будущая версия Windows не отнимет IOleWindow. Разве есть лучший способ? Или, если делать модальность вручную и сохранить прогресс диалог безрежимного после вызова StopProgressDialog()
является достаточно хорошим (ошибка будет сообщена только после того, как StopProgressDialog()
начать с), но then again...
ключевая фраза из документации: «Объект затем обрабатывает обновления ** на фоне потока ** и позволяет пользователю отменить операцию» , Вы не создали нить. Поток пользовательского интерфейса должен оставаться доступным для отображения прогресса, он не может этого сделать, когда он занят выполнением кода. –
Ah; Я подумал, когда он сказал «Объект», он ссылался на IProgressDialog, а не на мой собственный объект. В этом есть смысл; Благодарю. Я догадываюсь о любых операциях, которые авторы IProgressDialog использовали неявно, были сообщения-насосы, так как shlobj.h не упоминает их вообще:/ – andlabs
Фактически это приводит к следующему: «любые методы IProgressDialog безопасны для запуска в фоновом потоке ? "; Я добавлю это к этому вопросу, поскольку он все еще имеет значение, и поскольку это повлияет на то, как я связываю эти два потока. – andlabs