windows-nt/Source/XPSP1/NT/com/ole2ui32/busy.cpp
2020-09-26 16:20:57 +08:00

422 lines
15 KiB
C++

/*
* 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));
}