makefiles: Don't use standard libs for programs that specify -nodefaultlibs.
[wine/zf.git] / dlls / browseui / progressdlg.c
blobbca61f5482fd60c53a573f8a2e5999a751b3f756
1 /*
2 * Progress dialog
4 * Copyright 2007 Mikolaj Zalewski
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "wine/debug.h"
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winreg.h"
29 #include "winuser.h"
30 #include "shlwapi.h"
31 #include "winerror.h"
32 #include "objbase.h"
34 #include "shlguid.h"
35 #include "shlobj.h"
37 #include "wine/heap.h"
39 #include "browseui.h"
40 #include "resids.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(browseui);
44 #define CANCEL_MSG_LINE 2
46 /* Note: to avoid a deadlock we don't want to send messages to the dialog
47 * with the critical section held. Instead we only mark what fields should be
48 * updated and the dialog proc does the update */
49 #define UPDATE_PROGRESS 0x1
50 #define UPDATE_TITLE 0x2
51 #define UPDATE_LINE1 0x4
52 #define UPDATE_LINE2 (UPDATE_LINE1<<1)
53 #define UPDATE_LINE3 (UPDATE_LINE2<<2)
56 #define WM_DLG_UPDATE (WM_APP+1) /* set to the dialog when it should update */
57 #define WM_DLG_DESTROY (WM_APP+2) /* DestroyWindow must be called from the owning thread */
59 typedef struct tagProgressDialog {
60 IProgressDialog IProgressDialog_iface;
61 IOleWindow IOleWindow_iface;
62 LONG refCount;
63 CRITICAL_SECTION cs;
64 HWND hwnd;
65 DWORD dwFlags;
66 DWORD dwUpdate;
67 LPWSTR lines[3];
68 LPWSTR cancelMsg;
69 LPWSTR title;
70 BOOL isCancelled;
71 ULONGLONG ullCompleted;
72 ULONGLONG ullTotal;
73 HWND hwndDisabledParent; /* For modal dialog: the parent that need to be re-enabled when the dialog ends */
74 ULONGLONG startTime;
75 LPWSTR remainingMsg[2];
76 LPWSTR timeMsg[3];
77 } ProgressDialog;
79 static inline ProgressDialog *impl_from_IProgressDialog(IProgressDialog *iface)
81 return CONTAINING_RECORD(iface, ProgressDialog, IProgressDialog_iface);
84 static inline ProgressDialog *impl_from_IOleWindow(IOleWindow *iface)
86 return CONTAINING_RECORD(iface, ProgressDialog, IOleWindow_iface);
89 static const WCHAR empty_string[] = {0};
91 static void set_buffer(LPWSTR *buffer, LPCWSTR string)
93 IMalloc *malloc;
94 ULONG cb;
96 if (string == NULL)
97 string = empty_string;
98 CoGetMalloc(MEMCTX_TASK, &malloc);
100 cb = (lstrlenW(string) + 1)*sizeof(WCHAR);
101 if (*buffer == NULL || cb > IMalloc_GetSize(malloc, *buffer))
102 *buffer = IMalloc_Realloc(malloc, *buffer, cb);
103 memcpy(*buffer, string, cb);
106 struct create_params
108 ProgressDialog *This;
109 HANDLE hEvent;
110 HWND hwndParent;
113 static LPWSTR load_string(HINSTANCE hInstance, UINT uiResourceId)
115 WCHAR string[256];
116 LPWSTR ret;
118 LoadStringW(hInstance, uiResourceId, string, ARRAY_SIZE(string));
119 ret = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(string) + 1) * sizeof(WCHAR));
120 lstrcpyW(ret, string);
121 return ret;
124 static void set_progress_marquee(ProgressDialog *This)
126 HWND hProgress = GetDlgItem(This->hwnd, IDC_PROGRESS_BAR);
127 SetWindowLongW(hProgress, GWL_STYLE,
128 GetWindowLongW(hProgress, GWL_STYLE)|PBS_MARQUEE);
131 static void update_dialog(ProgressDialog *This, DWORD dwUpdate)
133 if (dwUpdate & UPDATE_TITLE)
134 SetWindowTextW(This->hwnd, This->title);
136 if (dwUpdate & UPDATE_LINE1)
137 SetDlgItemTextW(This->hwnd, IDC_TEXT_LINE, (This->isCancelled ? empty_string : This->lines[0]));
138 if (dwUpdate & UPDATE_LINE2)
139 SetDlgItemTextW(This->hwnd, IDC_TEXT_LINE+1, (This->isCancelled ? empty_string : This->lines[1]));
140 if (dwUpdate & UPDATE_LINE3)
141 SetDlgItemTextW(This->hwnd, IDC_TEXT_LINE+2, (This->isCancelled ? This->cancelMsg : This->lines[2]));
143 if (dwUpdate & UPDATE_PROGRESS)
145 ULONGLONG ullTotal = This->ullTotal;
146 ULONGLONG ullCompleted = This->ullCompleted;
148 /* progress bar requires 32-bit coordinates */
149 while (ullTotal >> 32)
151 ullTotal >>= 1;
152 ullCompleted >>= 1;
155 SendDlgItemMessageW(This->hwnd, IDC_PROGRESS_BAR, PBM_SETRANGE32, 0, (DWORD)ullTotal);
156 SendDlgItemMessageW(This->hwnd, IDC_PROGRESS_BAR, PBM_SETPOS, (DWORD)ullCompleted, 0);
160 static void load_time_strings(ProgressDialog *This)
162 int i;
164 for (i = 0; i < 2; i++)
166 if (!This->remainingMsg[i])
167 This->remainingMsg[i] = load_string(BROWSEUI_hinstance, IDS_REMAINING1 + i);
169 for (i = 0; i < 3; i++)
171 if (!This->timeMsg[i])
172 This->timeMsg[i] = load_string(BROWSEUI_hinstance, IDS_SECONDS + i);
176 static void end_dialog(ProgressDialog *This)
178 SendMessageW(This->hwnd, WM_DLG_DESTROY, 0, 0);
179 /* native doesn't re-enable the window? */
180 if (This->hwndDisabledParent)
181 EnableWindow(This->hwndDisabledParent, TRUE);
182 This->hwnd = NULL;
185 static INT_PTR CALLBACK dialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
187 ProgressDialog *This = (ProgressDialog *)GetWindowLongPtrW(hwnd, DWLP_USER);
189 switch (msg)
191 case WM_INITDIALOG:
193 struct create_params *params = (struct create_params *)lParam;
195 /* Note: until we set the hEvent, the object is protected by
196 * the critical section held by StartProgress */
197 SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)params->This);
198 This = params->This;
199 This->hwnd = hwnd;
201 if (This->dwFlags & PROGDLG_NOPROGRESSBAR)
202 ShowWindow(GetDlgItem(hwnd, IDC_PROGRESS_BAR), SW_HIDE);
203 if (This->dwFlags & PROGDLG_NOCANCEL)
204 ShowWindow(GetDlgItem(hwnd, IDCANCEL), SW_HIDE);
205 if (This->dwFlags & PROGDLG_MARQUEEPROGRESS)
206 set_progress_marquee(This);
207 if (This->dwFlags & PROGDLG_NOMINIMIZE)
208 SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) & (~WS_MINIMIZEBOX));
210 update_dialog(This, 0xffffffff);
211 This->dwUpdate = 0;
212 This->isCancelled = FALSE;
213 SetEvent(params->hEvent);
214 return TRUE;
217 case WM_DLG_UPDATE:
218 EnterCriticalSection(&This->cs);
219 update_dialog(This, This->dwUpdate);
220 This->dwUpdate = 0;
221 LeaveCriticalSection(&This->cs);
222 return TRUE;
224 case WM_DLG_DESTROY:
225 DestroyWindow(hwnd);
226 PostThreadMessageW(GetCurrentThreadId(), WM_NULL, 0, 0); /* wake up the GetMessage */
227 return TRUE;
229 case WM_CLOSE:
230 case WM_COMMAND:
231 if (msg == WM_CLOSE || wParam == IDCANCEL)
233 EnterCriticalSection(&This->cs);
234 This->isCancelled = TRUE;
236 if (!This->cancelMsg)
237 This->cancelMsg = load_string(BROWSEUI_hinstance, IDS_CANCELLING);
239 set_progress_marquee(This);
240 EnableWindow(GetDlgItem(This->hwnd, IDCANCEL), FALSE);
241 update_dialog(This, UPDATE_LINE1|UPDATE_LINE2|UPDATE_LINE3);
242 LeaveCriticalSection(&This->cs);
244 return TRUE;
246 return FALSE;
249 static DWORD WINAPI dialog_thread(LPVOID lpParameter)
251 /* Note: until we set the hEvent in WM_INITDIALOG, the ProgressDialog object
252 * is protected by the critical section held by StartProgress */
253 struct create_params *params = lpParameter;
254 ProgressDialog *This = params->This;
255 HWND hwnd;
256 MSG msg;
258 hwnd = CreateDialogParamW(BROWSEUI_hinstance, MAKEINTRESOURCEW(IDD_PROGRESS_DLG),
259 params->hwndParent, dialog_proc, (LPARAM)params);
261 while (GetMessageW(&msg, NULL, 0, 0) > 0)
263 if (!IsWindow(hwnd))
264 break;
265 if(!IsDialogMessageW(hwnd, &msg))
267 TranslateMessage(&msg);
268 DispatchMessageW(&msg);
272 IProgressDialog_Release(&This->IProgressDialog_iface);
273 return 0;
276 static void ProgressDialog_Destructor(ProgressDialog *This)
278 int i;
279 TRACE("destroying %p\n", This);
280 if (This->hwnd)
281 end_dialog(This);
282 for (i = 0; i < 3; i++)
283 heap_free(This->lines[i]);
284 heap_free(This->cancelMsg);
285 heap_free(This->title);
286 for (i = 0; i < 2; i++)
287 heap_free(This->remainingMsg[i]);
288 for (i = 0; i < 3; i++)
289 heap_free(This->timeMsg[i]);
290 This->cs.DebugInfo->Spare[0] = 0;
291 DeleteCriticalSection(&This->cs);
292 heap_free(This);
293 InterlockedDecrement(&BROWSEUI_refCount);
296 static HRESULT WINAPI ProgressDialog_QueryInterface(IProgressDialog *iface, REFIID iid, LPVOID *ppvOut)
298 ProgressDialog *This = impl_from_IProgressDialog(iface);
300 TRACE("(%p, %s, %p)\n", iface, debugstr_guid(iid), ppvOut);
301 if (!ppvOut)
302 return E_POINTER;
304 *ppvOut = NULL;
305 if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IProgressDialog))
307 *ppvOut = iface;
309 else if (IsEqualIID(iid, &IID_IOleWindow))
311 *ppvOut = &This->IOleWindow_iface;
314 if (*ppvOut)
316 IProgressDialog_AddRef(iface);
317 return S_OK;
320 WARN("unsupported interface: %s\n", debugstr_guid(iid));
321 return E_NOINTERFACE;
324 static ULONG WINAPI ProgressDialog_AddRef(IProgressDialog *iface)
326 ProgressDialog *This = impl_from_IProgressDialog(iface);
327 return InterlockedIncrement(&This->refCount);
330 static ULONG WINAPI ProgressDialog_Release(IProgressDialog *iface)
332 ProgressDialog *This = impl_from_IProgressDialog(iface);
333 ULONG ret;
335 ret = InterlockedDecrement(&This->refCount);
336 if (ret == 0)
337 ProgressDialog_Destructor(This);
338 return ret;
341 static HRESULT WINAPI ProgressDialog_StartProgressDialog(IProgressDialog *iface, HWND hwndParent, IUnknown *punkEnableModeless, DWORD dwFlags, LPCVOID reserved)
343 static const INITCOMMONCONTROLSEX init = { sizeof(init), ICC_ANIMATE_CLASS };
344 ProgressDialog *This = impl_from_IProgressDialog(iface);
345 struct create_params params;
346 HANDLE hThread;
348 TRACE("(%p, %p, %x, %p)\n", iface, punkEnableModeless, dwFlags, reserved);
349 if (punkEnableModeless || reserved)
350 FIXME("Reserved parameters not null (%p, %p)\n", punkEnableModeless, reserved);
351 if (dwFlags & PROGDLG_NOTIME)
352 FIXME("Flags PROGDLG_NOTIME not supported\n");
354 InitCommonControlsEx( &init );
356 EnterCriticalSection(&This->cs);
358 if (This->hwnd)
360 LeaveCriticalSection(&This->cs);
361 return S_OK; /* as on XP */
363 This->dwFlags = dwFlags;
365 params.This = This;
366 params.hwndParent = hwndParent;
367 params.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
369 /* thread holds one reference to ensure clean shutdown */
370 IProgressDialog_AddRef(&This->IProgressDialog_iface);
372 hThread = CreateThread(NULL, 0, dialog_thread, &params, 0, NULL);
373 WaitForSingleObject(params.hEvent, INFINITE);
374 CloseHandle(params.hEvent);
375 CloseHandle(hThread);
377 This->hwndDisabledParent = NULL;
378 if (hwndParent && (dwFlags & PROGDLG_MODAL))
380 HWND hwndDisable = GetAncestor(hwndParent, GA_ROOT);
381 if (EnableWindow(hwndDisable, FALSE))
382 This->hwndDisabledParent = hwndDisable;
385 if (dwFlags & PROGDLG_AUTOTIME)
386 load_time_strings(This);
388 This->startTime = GetTickCount64();
389 LeaveCriticalSection(&This->cs);
391 return S_OK;
394 static HRESULT WINAPI ProgressDialog_StopProgressDialog(IProgressDialog *iface)
396 ProgressDialog *This = impl_from_IProgressDialog(iface);
398 EnterCriticalSection(&This->cs);
399 if (This->hwnd)
400 end_dialog(This);
401 LeaveCriticalSection(&This->cs);
403 return S_OK;
406 static HRESULT WINAPI ProgressDialog_SetTitle(IProgressDialog *iface, LPCWSTR pwzTitle)
408 ProgressDialog *This = impl_from_IProgressDialog(iface);
409 HWND hwnd;
411 TRACE("(%p, %s)\n", This, wine_dbgstr_w(pwzTitle));
413 EnterCriticalSection(&This->cs);
414 set_buffer(&This->title, pwzTitle);
415 This->dwUpdate |= UPDATE_TITLE;
416 hwnd = This->hwnd;
417 LeaveCriticalSection(&This->cs);
419 if (hwnd)
420 SendMessageW(hwnd, WM_DLG_UPDATE, 0, 0);
422 return S_OK;
425 static HRESULT WINAPI ProgressDialog_SetAnimation(IProgressDialog *iface, HINSTANCE hInstance, UINT uiResourceId)
427 ProgressDialog *This = impl_from_IProgressDialog(iface);
429 TRACE("(%p, %p, %u)\n", iface, hInstance, uiResourceId);
431 if (IS_INTRESOURCE(uiResourceId))
433 if (!SendDlgItemMessageW(This->hwnd, IDC_ANIMATION, ACM_OPENW, (WPARAM)hInstance, uiResourceId))
434 WARN("Failed to load animation\n");
437 return S_OK;
440 static BOOL WINAPI ProgressDialog_HasUserCancelled(IProgressDialog *iface)
442 ProgressDialog *This = impl_from_IProgressDialog(iface);
443 return This->isCancelled;
446 static void update_time_remaining(ProgressDialog *This, ULONGLONG ullCompleted, ULONGLONG ullTotal)
448 unsigned int remaining, remainder = 0;
449 ULONGLONG elapsed;
450 WCHAR line[128];
451 int i;
452 DWORD_PTR args[4];
454 if (!This->startTime || !ullCompleted || !ullTotal)
455 return;
457 elapsed = GetTickCount64() - This->startTime;
458 remaining = (elapsed * ullTotal / ullCompleted - elapsed) / 1000;
460 for (i = 0; remaining >= 60 && i < 2; i++)
462 remainder = remaining % 60;
463 remaining /= 60;
466 args[0] = remaining;
467 args[1] = (DWORD_PTR)This->timeMsg[i];
468 args[2] = remainder;
469 args[3] = (DWORD_PTR)This->timeMsg[i-1];
471 if (i > 0 && remaining < 2 && remainder != 0)
472 FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
473 This->remainingMsg[1], 0, 0, line, ARRAY_SIZE(line), (__ms_va_list*)args);
474 else
475 FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
476 This->remainingMsg[0], 0, 0, line, ARRAY_SIZE(line), (__ms_va_list*)args);
478 set_buffer(&This->lines[2], line);
479 This->dwUpdate |= UPDATE_LINE3;
482 static HRESULT WINAPI ProgressDialog_SetProgress64(IProgressDialog *iface, ULONGLONG ullCompleted, ULONGLONG ullTotal)
484 ProgressDialog *This = impl_from_IProgressDialog(iface);
485 HWND hwnd;
487 TRACE("(%p, 0x%s, 0x%s)\n", This, wine_dbgstr_longlong(ullCompleted), wine_dbgstr_longlong(ullTotal));
489 EnterCriticalSection(&This->cs);
490 This->ullTotal = ullTotal;
491 This->ullCompleted = ullCompleted;
492 This->dwUpdate |= UPDATE_PROGRESS;
493 hwnd = This->hwnd;
494 if (This->dwFlags & PROGDLG_AUTOTIME)
495 update_time_remaining(This, ullCompleted, ullTotal);
496 LeaveCriticalSection(&This->cs);
498 if (hwnd)
499 SendMessageW(hwnd, WM_DLG_UPDATE, 0, 0);
501 return S_OK; /* Windows sometimes returns S_FALSE */
504 static HRESULT WINAPI ProgressDialog_SetProgress(IProgressDialog *iface, DWORD dwCompleted, DWORD dwTotal)
506 return IProgressDialog_SetProgress64(iface, dwCompleted, dwTotal);
509 static HRESULT WINAPI ProgressDialog_SetLine(IProgressDialog *iface, DWORD dwLineNum, LPCWSTR pwzLine, BOOL bPath, LPCVOID reserved)
511 ProgressDialog *This = impl_from_IProgressDialog(iface);
512 HWND hwnd;
514 TRACE("(%p, %d, %s, %d)\n", This, dwLineNum, wine_dbgstr_w(pwzLine), bPath);
516 if (reserved)
517 FIXME("reserved pointer not null (%p)\n", reserved);
519 dwLineNum--;
520 if (dwLineNum >= 3) /* Windows seems to do something like that */
521 dwLineNum = 0;
523 EnterCriticalSection(&This->cs);
524 set_buffer(&This->lines[dwLineNum], pwzLine);
525 This->dwUpdate |= UPDATE_LINE1 << dwLineNum;
526 hwnd = (This->isCancelled ? NULL : This->hwnd); /* no sense to send the message if window cancelled */
527 LeaveCriticalSection(&This->cs);
529 if (hwnd)
530 SendMessageW(hwnd, WM_DLG_UPDATE, 0, 0);
532 return S_OK;
535 static HRESULT WINAPI ProgressDialog_SetCancelMsg(IProgressDialog *iface, LPCWSTR pwzMsg, LPCVOID reserved)
537 ProgressDialog *This = impl_from_IProgressDialog(iface);
538 HWND hwnd;
540 TRACE("(%p, %s)\n", This, wine_dbgstr_w(pwzMsg));
542 if (reserved)
543 FIXME("reserved pointer not null (%p)\n", reserved);
545 EnterCriticalSection(&This->cs);
546 set_buffer(&This->cancelMsg, pwzMsg);
547 This->dwUpdate |= UPDATE_LINE1 << CANCEL_MSG_LINE;
548 hwnd = (This->isCancelled ? This->hwnd : NULL); /* no sense to send the message if window not cancelled */
549 LeaveCriticalSection(&This->cs);
551 if (hwnd)
552 SendMessageW(hwnd, WM_DLG_UPDATE, 0, 0);
554 return S_OK;
557 static HRESULT WINAPI ProgressDialog_Timer(IProgressDialog *iface, DWORD dwTimerAction, LPCVOID reserved)
559 ProgressDialog *This = impl_from_IProgressDialog(iface);
561 FIXME("(%p, %d, %p) - stub\n", This, dwTimerAction, reserved);
563 if (reserved)
564 FIXME("Reserved field not NULL but %p\n", reserved);
566 return S_OK;
569 static const IProgressDialogVtbl ProgressDialogVtbl =
571 ProgressDialog_QueryInterface,
572 ProgressDialog_AddRef,
573 ProgressDialog_Release,
575 ProgressDialog_StartProgressDialog,
576 ProgressDialog_StopProgressDialog,
577 ProgressDialog_SetTitle,
578 ProgressDialog_SetAnimation,
579 ProgressDialog_HasUserCancelled,
580 ProgressDialog_SetProgress,
581 ProgressDialog_SetProgress64,
582 ProgressDialog_SetLine,
583 ProgressDialog_SetCancelMsg,
584 ProgressDialog_Timer
587 static HRESULT WINAPI OleWindow_QueryInterface(IOleWindow *iface, REFIID iid, LPVOID *ppvOut)
589 ProgressDialog *This = impl_from_IOleWindow(iface);
590 return ProgressDialog_QueryInterface(&This->IProgressDialog_iface, iid, ppvOut);
593 static ULONG WINAPI OleWindow_AddRef(IOleWindow *iface)
595 ProgressDialog *This = impl_from_IOleWindow(iface);
596 return ProgressDialog_AddRef(&This->IProgressDialog_iface);
599 static ULONG WINAPI OleWindow_Release(IOleWindow *iface)
601 ProgressDialog *This = impl_from_IOleWindow(iface);
602 return ProgressDialog_Release(&This->IProgressDialog_iface);
605 static HRESULT WINAPI OleWindow_GetWindow(IOleWindow* iface, HWND* phwnd)
607 ProgressDialog *This = impl_from_IOleWindow(iface);
609 TRACE("(%p, %p)\n", This, phwnd);
610 EnterCriticalSection(&This->cs);
611 *phwnd = This->hwnd;
612 LeaveCriticalSection(&This->cs);
613 return S_OK;
616 static HRESULT WINAPI OleWindow_ContextSensitiveHelp(IOleWindow* iface, BOOL fEnterMode)
618 ProgressDialog *This = impl_from_IOleWindow(iface);
620 FIXME("(%p, %d): stub\n", This, fEnterMode);
621 return E_NOTIMPL;
624 static const IOleWindowVtbl OleWindowVtbl =
626 OleWindow_QueryInterface,
627 OleWindow_AddRef,
628 OleWindow_Release,
629 OleWindow_GetWindow,
630 OleWindow_ContextSensitiveHelp
634 HRESULT ProgressDialog_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut)
636 ProgressDialog *This;
637 if (pUnkOuter)
638 return CLASS_E_NOAGGREGATION;
640 This = heap_alloc_zero(sizeof(ProgressDialog));
641 if (This == NULL)
642 return E_OUTOFMEMORY;
644 This->IProgressDialog_iface.lpVtbl = &ProgressDialogVtbl;
645 This->IOleWindow_iface.lpVtbl = &OleWindowVtbl;
646 This->refCount = 1;
647 InitializeCriticalSection(&This->cs);
648 This->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ProgressDialog.cs");
650 TRACE("returning %p\n", This);
651 *ppOut = (IUnknown *)&This->IProgressDialog_iface;
652 InterlockedIncrement(&BROWSEUI_refCount);
653 return S_OK;