summaryrefslogtreecommitdiffstats
path: root/private/oleutest/letest/ole2ui/busy.c
blob: 7be5c7414d50ec9666ef446714ca2416fee3c377 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
/*
 * BUSY.C
 *
 * Implements the OleUIBusy function which invokes the "Server Busy"
 * dialog.
 *
 * Copyright (c)1992 Microsoft Corporation, All Right Reserved
 */

#define STRICT  1
#include "ole2ui.h"
#include "common.h"
#include "utility.h"
#include "busy.h"
#include <ctype.h> // for tolower() and toupper()

#ifndef WIN32
#include <toolhelp.h>
#endif


/*
 * OleUIBusy
 *
 * Purpose:
 *  Invokes the standard OLE "Server Busy" dialog box which
 *  notifies the user that the server application is not receiving
 *  messages.  The dialog then asks the user to either cancel
 *  the operation, switch to the task which is blocked, or continue
 *  waiting.
 *
 * Parameters:
 *  lpBZ            LPOLEUIBUSY pointing to the in-out structure
 *                  for this dialog.
 *
 * Return Value:
 *              OLEUI_BZERR_HTASKINVALID  : Error
 *              OLEUI_BZ_SWITCHTOSELECTED : Success, user selected "switch to"
 *              OLEUI_BZ_RETRYSELECTED    : Success, user selected "retry"
 *              OLEUI_CANCEL              : Success, user selected "cancel"
 */

STDAPI_(UINT) OleUIBusy(LPOLEUIBUSY lpOBZ)
    {
    UINT        uRet = 0;
    HGLOBAL     hMemDlg=NULL;

#if !defined( WIN32 )
// BUGBUG32:    this is not yet ported to NT

    uRet=UStandardValidation((LPOLEUISTANDARD)lpOBZ, sizeof(OLEUIBUSY)
                             , &hMemDlg);

    // Error out if the standard validation failed
    if (OLEUI_SUCCESS!=uRet)
        return uRet;

    // Validate HTASK
    if (!IsTask(lpOBZ->hTask))
        uRet = OLEUI_BZERR_HTASKINVALID;

    // Error out if our secondary validation failed
    if (OLEUI_ERR_STANDARDMIN <= uRet)
        {
        if (NULL!=hMemDlg)
            FreeResource(hMemDlg);

        return uRet;
        }

    // Invoke the dialog.
    uRet=UStandardInvocation(BusyDialogProc, (LPOLEUISTANDARD)lpOBZ,
                             hMemDlg, MAKEINTRESOURCE(IDD_BUSY));
#endif

    return uRet;
}


/*
 * BusyDialogProc
 *
 * Purpose:
 *  Implements the OLE Busy dialog as invoked through the OleUIBusy function.
 *
 * Parameters:
 *  Standard
 *
 * Return Value:
 *  Standard
 *
 */

BOOL CALLBACK EXPORT BusyDialogProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
    {
    LPBUSY         lpBZ;
    UINT           uRet = 0;

    //Declare Win16/Win32 compatible WM_COMMAND parameters.
    COMMANDPARAMS(wID, wCode, hWndMsg);

    //This will fail under WM_INITDIALOG, where we allocate it.
    lpBZ=(LPBUSY)LpvStandardEntry(hDlg, iMsg, wParam, lParam, &uRet);

    //If the hook processed the message, we're done.
    if (0!=uRet)
        return (BOOL)uRet;

    //Process the temination message
    if (iMsg==uMsgEndDialog)
    {
        BusyCleanup(hDlg);
        StandardCleanup(lpBZ, hDlg);
        EndDialog(hDlg, wParam);
        return TRUE;
    }

    // Process our special "close" message.  If we get this message,
    // this means that the call got unblocked, so we need to
    // return OLEUI_BZ_CALLUNBLOCKED to our calling app.
    if (iMsg == uMsgCloseBusyDlg)
    {
        SendMessage(hDlg, uMsgEndDialog, OLEUI_BZ_CALLUNBLOCKED, 0L);
        return TRUE;
    }

    switch (iMsg)
        {
        case WM_INITDIALOG:
            FBusyInit(hDlg, wParam, lParam);
            return TRUE;

        case WM_ACTIVATEAPP:
        {
            /* try to bring down our Busy/NotResponding dialog as if
            **    the user entered RETRY.
            */
            BOOL fActive = (BOOL)wParam;
            if (fActive) {
                // If this is the app BUSY case, then bring down our
                // dialog when switching BACK to our app
                if (lpBZ && !(lpBZ->dwFlags & BZ_NOTRESPONDINGDIALOG))
                    SendMessage(hDlg,uMsgEndDialog,OLEUI_BZ_RETRYSELECTED,0L);
            } else {
                // If this is the app NOT RESPONDING case, then bring down
                // our dialog when switching AWAY to another app
                if (lpBZ && (lpBZ->dwFlags & BZ_NOTRESPONDINGDIALOG))
                    SendMessage(hDlg,uMsgEndDialog,OLEUI_BZ_RETRYSELECTED,0L);
            }
            return TRUE;
        }

        case WM_COMMAND:
            switch (wID)
                {
                case IDBZ_SWITCHTO:
                {
                    BOOL fNotRespondingDlg =
                            (BOOL)(lpBZ->dwFlags & BZ_NOTRESPONDINGDIALOG);

                    // If user selects "Switch To...", switch activation
                    // directly to the window which is causing the problem.
                    if (IsWindow(lpBZ->hWndBlocked))
                        MakeWindowActive(lpBZ->hWndBlocked);
                    else
                        StartTaskManager(); // Fail safe: Start Task Manager

                    // If this is the app not responding case, then we want
                    // to bring down the dialog when "SwitchTo" is selected.
                    // If the app is busy (RetryRejectedCall situation) then
                    // we do NOT want to bring down the dialog. this is
                    // the OLE2.0 user model design.
                    if (fNotRespondingDlg)
                        SendMessage(hDlg, uMsgEndDialog, OLEUI_BZ_SWITCHTOSELECTED, 0L);
                    break;
                }
                case IDBZ_RETRY:
                    SendMessage(hDlg, uMsgEndDialog, OLEUI_BZ_RETRYSELECTED, 0L);
                    break;

                case IDCANCEL:
                    SendMessage(hDlg, uMsgEndDialog, OLEUI_CANCEL, 0L);
                    break;
                }
            break;
        }
    return FALSE;
    }


/*
 * FBusyInit
 *
 * Purpose:
 *  WM_INITIDIALOG handler for the Busy dialog box.
 *
 * Parameters:
 *  hDlg            HWND of the dialog
 *  wParam          WPARAM of the message
 *  lParam          LPARAM of the message
 *
 * Return Value:
 *  BOOL            Value to return for WM_INITDIALOG.
 */

BOOL FBusyInit(HWND hDlg, WPARAM wParam, LPARAM lParam)
    {
    LPBUSY           lpBZ;
    LPOLEUIBUSY      lpOBZ;
    HFONT            hFont;
    LPTSTR           lpTaskName;
    LPTSTR           lpWindowName;
    HICON            hIcon;

    lpBZ=(LPBUSY)LpvStandardInit(hDlg, sizeof(OLEUIBUSY), TRUE, &hFont);

    // PvStandardInit sent a termination to us already.
    if (NULL==lpBZ)
        return FALSE;

    // Our original structure is in lParam
    lpOBZ = (LPOLEUIBUSY)lParam;

    // Copy it to our instance of the structure (in lpBZ)
    lpBZ->lpOBZ=lpOBZ;

    //Copy other information from lpOBZ that we might modify.
    lpBZ->dwFlags = lpOBZ->dwFlags;

    // Set default information
    lpBZ->hWndBlocked = NULL;

    // Insert HWND of our dialog into the address pointed to by
    // lphWndDialog.  This can be used by the app who called
    // OleUIBusy to bring down the dialog with uMsgCloseBusyDialog
    if (lpOBZ->lphWndDialog &&
        !IsBadWritePtr((VOID FAR *)lpOBZ->lphWndDialog, sizeof(HWND)))
        {
        *lpOBZ->lphWndDialog = hDlg;
        }

    // Update text in text box --
    // GetTaskInfo will return two pointers, one to the task name
    // (file name) and one to the window name.  We need to call
    // OleStdFree on these when we're done with them.  We also
    // get the HWND which is blocked in this call
    //
    // In the case where this call fails, a default message should already
    // be present in the dialog template, so no action is needed

    if (GetTaskInfo(hDlg, lpOBZ->hTask, &lpTaskName, &lpWindowName, &lpBZ->hWndBlocked))
        {
        // Build string to present to user, place in IDBZ_MESSAGE1 control
        BuildBusyDialogString(hDlg, lpBZ->dwFlags, IDBZ_MESSAGE1, lpTaskName, lpWindowName);
        OleStdFree(lpTaskName);
        OleStdFree(lpWindowName);
        }

    // Update icon with the system "exclamation" icon
    hIcon = LoadIcon(NULL, IDI_EXCLAMATION);
    SendDlgItemMessage(hDlg, IDBZ_ICON, STM_SETICON, (WPARAM)hIcon, 0L);

    // Disable/Enable controls
    if ((lpBZ->dwFlags & BZ_DISABLECANCELBUTTON) ||
        (lpBZ->dwFlags & BZ_NOTRESPONDINGDIALOG))              // Disable cancel for "not responding" dialog
        EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);

    if (lpBZ->dwFlags & BZ_DISABLESWITCHTOBUTTON)
        EnableWindow(GetDlgItem(hDlg, IDBZ_SWITCHTO), FALSE);

    if (lpBZ->dwFlags & BZ_DISABLERETRYBUTTON)
        EnableWindow(GetDlgItem(hDlg, IDBZ_RETRY), FALSE);

    // Call the hook with lCustData in lParam
    UStandardHook((LPVOID)lpBZ, hDlg, WM_INITDIALOG, wParam, lpOBZ->lCustData);

    // Update caption if lpszCaption was specified
    if (lpBZ->lpOBZ->lpszCaption && !IsBadReadPtr(lpBZ->lpOBZ->lpszCaption, 1)
          && lpBZ->lpOBZ->lpszCaption[0] != '\0')
        SetWindowText(hDlg, lpBZ->lpOBZ->lpszCaption);

    return TRUE;
    }


/*
 * BuildBusyDialogString
 *
 * Purpose:
 *  Builds the string that will be displayed in the dialog from the
 *  task name and window name parameters.
 *
 * Parameters:
 *  hDlg            HWND of the dialog
 *  dwFlags         DWORD containing flags passed into dialog
 *  iControl        Control ID to place the text string
 *  lpTaskName      LPSTR pointing to name of task (e.g. C:\TEST\TEST.EXE)
 *  lpWindowName    LPSTR for name of window
 *
 * Caveats:
 *  The caller of this function MUST de-allocate the lpTaskName and
 *  lpWindowName pointers itself with OleStdFree
 *
 * Return Value:
 *  void
 */

void BuildBusyDialogString(HWND hDlg, DWORD dwFlags, int iControl, LPTSTR lpTaskName, LPTSTR lpWindowName)
{
    LPTSTR      pszT, psz1, psz2, psz3;
    UINT        cch;
    LPTSTR      pszDot, pszSlash;
    UINT        uiStringNum;

    /*
     * We need scratch memory for loading the stringtable string,
     * the task name, and constructing the final string.  We therefore
     * allocate three buffers as large as the maximum message
     * length (512) plus the object type, guaranteeing that we have enough
     * in all cases.
     */
    cch=512;

    // Use OLE-supplied allocation
    if ((pszT = OleStdMalloc((ULONG)(3*cch))) == NULL)
        return;

    psz1=pszT;
    psz2=psz1+cch;
    psz3=psz2+cch;

    // Parse base name out of path name, use psz2 for the task
    // name to display
    // In Win32, _fstrcpy is mapped to handle UNICODE stuff
    _fstrcpy(psz2, lpTaskName);
    pszDot = _fstrrchr(psz2, TEXT('.'));
    pszSlash = _fstrrchr(psz2, TEXT('\\')); // Find last backslash in path

    if (pszDot != NULL)
#ifdef UNICODE
      *pszDot = TEXT('\0'); // Null terminate at the DOT
#else
      *pszDot = '\0'; // Null terminate at the DOT
#endif

    if (pszSlash != NULL)
      psz2 = pszSlash + 1; // Nuke everything up to this point

#ifdef LOWERCASE_NAME
    // Compile this with /DLOWERCASE_NAME if you want the lower-case
    // module name to be displayed in the dialog rather than the
    // all-caps name.
    {
    int i,l;

    // Now, lowercase all letters except first one
    l = _fstrlen(psz2);
    for(i=0;i<l;i++)
      psz2[i] = tolower(psz2[i]);

    psz2[0] = toupper(psz2[0]);
    }
#endif

    // Check size of lpWindowName.  We can reasonably fit about 80
    // characters into the text control, so truncate more than 80 chars
    if (_fstrlen(lpWindowName)> 80)
#ifdef UNICODE
      lpWindowName[80] = TEXT('\0');
#else
      lpWindowName[80] = '\0';
#endif

    // Load the format string out of stringtable, choose a different
    // string depending on what flags are passed in to the dialog
    if (dwFlags & BZ_NOTRESPONDINGDIALOG)
        uiStringNum = IDS_BZRESULTTEXTNOTRESPONDING;
    else
        uiStringNum = IDS_BZRESULTTEXTBUSY;

    if (LoadString(ghInst, uiStringNum, psz1, cch) == 0)
      return;

    // Build the string. The format string looks like this:
    // "This action cannot be completed because the '%s' application
    // (%s) is [busy | not responding]. Choose \"Switch To\" to activate '%s' and
    // correct the problem."

    wsprintf(psz3, psz1, (LPSTR)psz2, (LPTSTR)lpWindowName, (LPTSTR)psz2);
    SetDlgItemText(hDlg, iControl, (LPTSTR)psz3);
    OleStdFree(pszT);

    return;
}



/*
 * BusyCleanup
 *
 * Purpose:
 *  Performs busy-specific cleanup before termination.
 *
 * Parameters:
 *  hDlg            HWND of the dialog box so we can access controls.
 *
 * Return Value:
 *  None
 */
void BusyCleanup(HWND hDlg)
{
   return;
}



/*
 * GetTaskInfo()
 *
 * Purpose:  Gets information about the specified task and places the
 * module name, window name and top-level HWND for the task in the specified
 * pointers
 *
 * NOTE: The two string pointers allocated in this routine are
 * the responsibility of the CALLER to de-allocate.
 *
 * Parameters:
 *    hWnd             HWND who called this function
 *    htask            HTASK which we want to find out more info about
 *    lplpszTaskName   Location that the module name is returned
 *    lplpszWindowName Location where the window name is returned
 *
 */

BOOL GetTaskInfo(HWND hWnd, HTASK htask, LPTSTR FAR* lplpszTaskName, LPTSTR FAR*lplpszWindowName, HWND FAR*lphWnd)
{
    BOOL        fRet = FALSE;
#if !defined( WIN32 )
    TASKENTRY   te;
#endif
    HWND        hwndNext;
    LPTSTR      lpszTN = NULL;
    LPTSTR      lpszWN = NULL;
    HWND        hwndFind = NULL;

    // Clear out return values in case of error
    *lplpszTaskName = NULL;
    *lplpszWindowName = NULL;

#if !defined( WIN32 )
    te.dwSize = sizeof(TASKENTRY);
    if (TaskFindHandle(&te, htask))
#endif
        {
        // Now, enumerate top-level windows in system
        hwndNext = GetWindow(hWnd, GW_HWNDFIRST);
        while (hwndNext)
            {
            // See if we can find a non-owned top level window whose
            // hInstance matches the one we just got passed.  If we find one,
            // we can be fairly certain that this is the top-level window for
            // the task which is blocked.
            //
            // REVIEW:  Will this filter hold true for InProcServer DLL-created
            // windows?
            //
            if ((hwndNext != hWnd) &&
#if !defined( WIN32 )
                (GetWindowWord(hwndNext, GWW_HINSTANCE) == (WORD)te.hInst) &&
#else
                ((HTASK) GetWindowThreadProcessId(hwndNext,NULL) == htask) &&
#endif
				(IsWindowVisible(hwndNext)) &&
                !GetWindow(hwndNext, GW_OWNER))
                {
                // We found our window!  Alloc space for new strings
                if ((lpszTN = OleStdMalloc(OLEUI_CCHPATHMAX_SIZE)) == NULL)
                    return TRUE;  // continue task window enumeration

                if ((lpszWN = OleStdMalloc(OLEUI_CCHPATHMAX_SIZE)) == NULL)
                    return TRUE;  // continue task window enumeration

                // We found the window we were looking for, copy info to
                // local vars
                GetWindowText(hwndNext, lpszWN, OLEUI_CCHPATHMAX);
#if !defined( WIN32 )
                 LSTRCPYN(lpszTN, te.szModule, OLEUI_CCHPATHMAX);
#else
                /* WIN32 NOTE: we are not able to get a module name
                **    given a thread process id on WIN32. the best we
                **    can do is use the window title as the module/app
                **    name.
                */
                 LSTRCPYN(lpszTN, lpszWN, OLEUI_CCHPATHMAX);
#endif
                hwndFind = hwndNext;

                fRet = TRUE;
                goto OKDone;
                }

            hwndNext = GetWindow(hwndNext, GW_HWNDNEXT);
            }
        }

OKDone:

    // OK, everything was successful. Set string pointers to point to
    // our data.

    *lplpszTaskName = lpszTN;
    *lplpszWindowName = lpszWN;
    *lphWnd = hwndFind;

    return fRet;
}


/*
 * StartTaskManager()
 *
 * Purpose: Starts Task Manager.  Used to bring up task manager to
 * assist in switching to a given blocked task.
 *
 */

StartTaskManager()
{
    WinExec("taskman.exe", SW_SHOW);
    return TRUE;
}



/*
 * MakeWindowActive()
 *
 * Purpose: Makes specified window the active window.
 *
 */

void MakeWindowActive(HWND hWndSwitchTo)
{
    // Move the new window to the top of the Z-order
    SetWindowPos(hWndSwitchTo, HWND_TOP, 0, 0, 0, 0,
              SWP_NOSIZE | SWP_NOMOVE);

    // If it's iconic, we need to restore it.
    if (IsIconic(hWndSwitchTo))
        ShowWindow(hWndSwitchTo, SW_RESTORE);
}