/* * BUSY.CPP * * Implements the OleUIBusy function which invokes the "Server Busy" * dialog. * * Copyright (c)1992 Microsoft Corporation, All Right Reserved */ #include "precomp.h" #include "common.h" #include "utility.h" OLEDBGDATA // Internally used structure typedef struct tagBUSY { // Keep these items first as the Standard* functions depend on it here. LPOLEUIBUSY lpOBZ; // Original structure passed. UINT nIDD; // IDD of dialog (used for help info) /* * What we store extra in this structure besides the original caller's * pointer are those fields that we need to modify during the life of * the dialog or that we don't want to change in the original structure * until the user presses OK. */ DWORD dwFlags; // Flags passed in HWND hWndBlocked; // HWND of app which is blocking } BUSY, *PBUSY, FAR *LPBUSY; // Internal function prototypes // BUSY.CPP INT_PTR CALLBACK BusyDialogProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam); BOOL GetTaskInfo(HWND hWnd, HTASK htask, LPTSTR* lplpszWindowName, HWND* lphWnd); void BuildBusyDialogString(HWND, DWORD, int, LPTSTR); BOOL FBusyInit(HWND hDlg, WPARAM wParam, LPARAM lParam); void MakeWindowActive(HWND hWndSwitchTo); /* * 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) { HGLOBAL hMemDlg = NULL; UINT uRet = UStandardValidation((LPOLEUISTANDARD)lpOBZ, sizeof(OLEUIBUSY), &hMemDlg); // Error out if the standard validation failed if (OLEUI_SUCCESS != uRet) return uRet; // Error out if our secondary validation failed if (OLEUI_ERR_STANDARDMIN <= uRet) { return uRet; } // Invoke the dialog. uRet = UStandardInvocation(BusyDialogProc, (LPOLEUISTANDARD)lpOBZ, hMemDlg, MAKEINTRESOURCE(IDD_BUSY)); return uRet; } /* * BusyDialogProc * * Purpose: * Implements the OLE Busy dialog as invoked through the OleUIBusy function. * * Parameters: * Standard * * Return Value: * Standard * */ INT_PTR CALLBACK BusyDialogProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam) { // Declare Win16/Win32 compatible WM_COMMAND parameters. COMMANDPARAMS(wID, wCode, hWndMsg); // This will fail under WM_INITDIALOG, where we allocate it. UINT uRet = 0; LPBUSY lpBZ = (LPBUSY)LpvStandardEntry(hDlg, iMsg, wParam, lParam, &uRet); // If the hook processed the message, we're done. if (0 != uRet) return (INT_PTR)uRet; // Process the temination message if (iMsg == uMsgEndDialog) { 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_DESTROY: if (lpBZ) { StandardCleanup(lpBZ, hDlg); } break; 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 IDC_BZ_SWITCHTO: { BOOL fNotRespondingDlg = (BOOL)(lpBZ->dwFlags & BZ_NOTRESPONDINGDIALOG); HWND hwndTaskList = hDlg; // 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) { hwndTaskList = GetParent(hDlg); if (hwndTaskList == NULL) hwndTaskList = GetDesktopWindow(); PostMessage(hDlg, uMsgEndDialog, OLEUI_BZ_SWITCHTOSELECTED, 0L); } // If user selects "Switch To...", switch activation // directly to the window which is causing the problem. if (IsWindow(lpBZ->hWndBlocked)) MakeWindowActive(lpBZ->hWndBlocked); else PostMessage(hwndTaskList, WM_SYSCOMMAND, SC_TASKLIST, 0); } break; case IDC_BZ_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) { HFONT hFont; LPBUSY lpBZ = (LPBUSY)LpvStandardInit(hDlg, sizeof(BUSY), &hFont); // PvStandardInit sent a termination to us already. if (NULL == lpBZ) return FALSE; // Our original structure is in lParam LPOLEUIBUSY lpOBZ = (LPOLEUIBUSY)lParam; // Copy it to our instance of the structure (in lpBZ) lpBZ->lpOBZ = lpOBZ; lpBZ->nIDD = IDD_BUSY; //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(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 LPTSTR lpWindowName; if (GetTaskInfo(hDlg, lpOBZ->hTask, &lpWindowName, &lpBZ->hWndBlocked)) { // Build string to present to user, place in IDC_BZ_MESSAGE1 control BuildBusyDialogString(hDlg, lpBZ->dwFlags, IDC_BZ_MESSAGE1, lpWindowName); OleStdFree(lpWindowName); } // Update icon with the system "exclamation" icon HICON hIcon = LoadIcon(NULL, IDI_EXCLAMATION); SendDlgItemMessage(hDlg, IDC_BZ_ICON, STM_SETICON, (WPARAM)hIcon, 0L); // Disable/Enable controls if ((lpBZ->dwFlags & BZ_DISABLECANCELBUTTON) || (lpBZ->dwFlags & BZ_NOTRESPONDINGDIALOG)) { // Disable cancel for "not responding" dialog StandardEnableDlgItem(hDlg, IDCANCEL, FALSE); } if (lpBZ->dwFlags & BZ_DISABLESWITCHTOBUTTON) StandardEnableDlgItem(hDlg, IDC_BZ_SWITCHTO, FALSE); if (lpBZ->dwFlags & BZ_DISABLERETRYBUTTON) StandardEnableDlgItem(hDlg, IDC_BZ_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)) { 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 lpWindowName) { // Load the format string out of stringtable, choose a different // string depending on what flags are passed in to the dialog UINT uiStringNum; if (dwFlags & BZ_NOTRESPONDINGDIALOG) uiStringNum = IDS_BZRESULTTEXTNOTRESPONDING; else uiStringNum = IDS_BZRESULTTEXTBUSY; TCHAR szFormat[256]; if (LoadString(_g_hOleStdResInst, uiStringNum, szFormat, 256) == 0) return; // Build the string. The format string looks like this: // "This action cannot be completed because the "%1" application // is [busy | not responding]. Choose \"Switch To\" to correct the // problem." TCHAR szMessage[512]; FormatString1(szMessage, szFormat, lpWindowName); SetDlgItemText(hDlg, iControl, szMessage); } /* * 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* lplpszWindowName, HWND* lphWnd) { if (htask == NULL) return FALSE; // initialize 'out' parameters *lplpszWindowName = NULL; // Now, enumerate top-level windows in system HWND 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. DWORD dwProcessID; DWORD dwThreadID = GetWindowThreadProcessId(hwndNext, &dwProcessID); if ((hwndNext != hWnd) && (dwThreadID == HandleToUlong(htask)) && (IsWindowVisible(hwndNext)) && !GetWindow(hwndNext, GW_OWNER)) { // We found our window! Alloc space for new strings LPTSTR lpszWN; if ((lpszWN = (LPTSTR)OleStdMalloc(MAX_PATH_SIZE)) == NULL) break; // We found the window we were looking for, copy info to // local vars GetWindowText(hwndNext, lpszWN, MAX_PATH); // Note: the task name cannot be retrieved with the Win32 API. // everything was successful. Set string pointers to point to our data. *lplpszWindowName = lpszWN; *lphWnd = hwndNext; return TRUE; } hwndNext = GetWindow(hwndNext, GW_HWNDNEXT); } return FALSE; } /* * MakeWindowActive() * * Purpose: Makes specified window the active window. * */ void MakeWindowActive(HWND hWndSwitchTo) { // If it's iconic, we need to restore it. if (IsIconic(hWndSwitchTo)) ShowWindow(hWndSwitchTo, SW_RESTORE); // Move the new window to the top of the Z-order SetForegroundWindow(GetLastActivePopup(hWndSwitchTo)); }