Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

551 lines
16 KiB

  1. /*
  2. * BUSY.C
  3. *
  4. * Implements the OleUIBusy function which invokes the "Server Busy"
  5. * dialog.
  6. *
  7. * Copyright (c)1992 Microsoft Corporation, All Right Reserved
  8. */
  9. #define STRICT 1
  10. #include "ole2ui.h"
  11. #include "common.h"
  12. #include "utility.h"
  13. #include "busy.h"
  14. #include <ctype.h> // for tolower() and toupper()
  15. #ifndef WIN32
  16. #include <toolhelp.h>
  17. #endif
  18. /*
  19. * OleUIBusy
  20. *
  21. * Purpose:
  22. * Invokes the standard OLE "Server Busy" dialog box which
  23. * notifies the user that the server application is not receiving
  24. * messages. The dialog then asks the user to either cancel
  25. * the operation, switch to the task which is blocked, or continue
  26. * waiting.
  27. *
  28. * Parameters:
  29. * lpBZ LPOLEUIBUSY pointing to the in-out structure
  30. * for this dialog.
  31. *
  32. * Return Value:
  33. * OLEUI_BZERR_HTASKINVALID : Error
  34. * OLEUI_BZ_SWITCHTOSELECTED : Success, user selected "switch to"
  35. * OLEUI_BZ_RETRYSELECTED : Success, user selected "retry"
  36. * OLEUI_CANCEL : Success, user selected "cancel"
  37. */
  38. STDAPI_(UINT) OleUIBusy(LPOLEUIBUSY lpOBZ)
  39. {
  40. UINT uRet = 0;
  41. HGLOBAL hMemDlg=NULL;
  42. #if !defined( WIN32 )
  43. // BUGBUG32: this is not yet ported to NT
  44. uRet=UStandardValidation((LPOLEUISTANDARD)lpOBZ, sizeof(OLEUIBUSY)
  45. , &hMemDlg);
  46. // Error out if the standard validation failed
  47. if (OLEUI_SUCCESS!=uRet)
  48. return uRet;
  49. // Validate HTASK
  50. if (!IsTask(lpOBZ->hTask))
  51. uRet = OLEUI_BZERR_HTASKINVALID;
  52. // Error out if our secondary validation failed
  53. if (OLEUI_ERR_STANDARDMIN <= uRet)
  54. {
  55. if (NULL!=hMemDlg)
  56. FreeResource(hMemDlg);
  57. return uRet;
  58. }
  59. // Invoke the dialog.
  60. uRet=UStandardInvocation(BusyDialogProc, (LPOLEUISTANDARD)lpOBZ,
  61. hMemDlg, MAKEINTRESOURCE(IDD_BUSY));
  62. #endif
  63. return uRet;
  64. }
  65. /*
  66. * BusyDialogProc
  67. *
  68. * Purpose:
  69. * Implements the OLE Busy dialog as invoked through the OleUIBusy function.
  70. *
  71. * Parameters:
  72. * Standard
  73. *
  74. * Return Value:
  75. * Standard
  76. *
  77. */
  78. BOOL CALLBACK EXPORT BusyDialogProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
  79. {
  80. LPBUSY lpBZ;
  81. UINT uRet = 0;
  82. //Declare Win16/Win32 compatible WM_COMMAND parameters.
  83. COMMANDPARAMS(wID, wCode, hWndMsg);
  84. //This will fail under WM_INITDIALOG, where we allocate it.
  85. lpBZ=(LPBUSY)LpvStandardEntry(hDlg, iMsg, wParam, lParam, &uRet);
  86. //If the hook processed the message, we're done.
  87. if (0!=uRet)
  88. return (BOOL)uRet;
  89. //Process the temination message
  90. if (iMsg==uMsgEndDialog)
  91. {
  92. BusyCleanup(hDlg);
  93. StandardCleanup(lpBZ, hDlg);
  94. EndDialog(hDlg, wParam);
  95. return TRUE;
  96. }
  97. // Process our special "close" message. If we get this message,
  98. // this means that the call got unblocked, so we need to
  99. // return OLEUI_BZ_CALLUNBLOCKED to our calling app.
  100. if (iMsg == uMsgCloseBusyDlg)
  101. {
  102. SendMessage(hDlg, uMsgEndDialog, OLEUI_BZ_CALLUNBLOCKED, 0L);
  103. return TRUE;
  104. }
  105. switch (iMsg)
  106. {
  107. case WM_INITDIALOG:
  108. FBusyInit(hDlg, wParam, lParam);
  109. return TRUE;
  110. case WM_ACTIVATEAPP:
  111. {
  112. /* try to bring down our Busy/NotResponding dialog as if
  113. ** the user entered RETRY.
  114. */
  115. BOOL fActive = (BOOL)wParam;
  116. if (fActive) {
  117. // If this is the app BUSY case, then bring down our
  118. // dialog when switching BACK to our app
  119. if (lpBZ && !(lpBZ->dwFlags & BZ_NOTRESPONDINGDIALOG))
  120. SendMessage(hDlg,uMsgEndDialog,OLEUI_BZ_RETRYSELECTED,0L);
  121. } else {
  122. // If this is the app NOT RESPONDING case, then bring down
  123. // our dialog when switching AWAY to another app
  124. if (lpBZ && (lpBZ->dwFlags & BZ_NOTRESPONDINGDIALOG))
  125. SendMessage(hDlg,uMsgEndDialog,OLEUI_BZ_RETRYSELECTED,0L);
  126. }
  127. return TRUE;
  128. }
  129. case WM_COMMAND:
  130. switch (wID)
  131. {
  132. case IDBZ_SWITCHTO:
  133. {
  134. BOOL fNotRespondingDlg =
  135. (BOOL)(lpBZ->dwFlags & BZ_NOTRESPONDINGDIALOG);
  136. // If user selects "Switch To...", switch activation
  137. // directly to the window which is causing the problem.
  138. if (IsWindow(lpBZ->hWndBlocked))
  139. MakeWindowActive(lpBZ->hWndBlocked);
  140. else
  141. StartTaskManager(); // Fail safe: Start Task Manager
  142. // If this is the app not responding case, then we want
  143. // to bring down the dialog when "SwitchTo" is selected.
  144. // If the app is busy (RetryRejectedCall situation) then
  145. // we do NOT want to bring down the dialog. this is
  146. // the OLE2.0 user model design.
  147. if (fNotRespondingDlg)
  148. SendMessage(hDlg, uMsgEndDialog, OLEUI_BZ_SWITCHTOSELECTED, 0L);
  149. break;
  150. }
  151. case IDBZ_RETRY:
  152. SendMessage(hDlg, uMsgEndDialog, OLEUI_BZ_RETRYSELECTED, 0L);
  153. break;
  154. case IDCANCEL:
  155. SendMessage(hDlg, uMsgEndDialog, OLEUI_CANCEL, 0L);
  156. break;
  157. }
  158. break;
  159. }
  160. return FALSE;
  161. }
  162. /*
  163. * FBusyInit
  164. *
  165. * Purpose:
  166. * WM_INITIDIALOG handler for the Busy dialog box.
  167. *
  168. * Parameters:
  169. * hDlg HWND of the dialog
  170. * wParam WPARAM of the message
  171. * lParam LPARAM of the message
  172. *
  173. * Return Value:
  174. * BOOL Value to return for WM_INITDIALOG.
  175. */
  176. BOOL FBusyInit(HWND hDlg, WPARAM wParam, LPARAM lParam)
  177. {
  178. LPBUSY lpBZ;
  179. LPOLEUIBUSY lpOBZ;
  180. HFONT hFont;
  181. LPTSTR lpTaskName;
  182. LPTSTR lpWindowName;
  183. HICON hIcon;
  184. lpBZ=(LPBUSY)LpvStandardInit(hDlg, sizeof(OLEUIBUSY), TRUE, &hFont);
  185. // PvStandardInit sent a termination to us already.
  186. if (NULL==lpBZ)
  187. return FALSE;
  188. // Our original structure is in lParam
  189. lpOBZ = (LPOLEUIBUSY)lParam;
  190. // Copy it to our instance of the structure (in lpBZ)
  191. lpBZ->lpOBZ=lpOBZ;
  192. //Copy other information from lpOBZ that we might modify.
  193. lpBZ->dwFlags = lpOBZ->dwFlags;
  194. // Set default information
  195. lpBZ->hWndBlocked = NULL;
  196. // Insert HWND of our dialog into the address pointed to by
  197. // lphWndDialog. This can be used by the app who called
  198. // OleUIBusy to bring down the dialog with uMsgCloseBusyDialog
  199. if (lpOBZ->lphWndDialog &&
  200. !IsBadWritePtr((VOID FAR *)lpOBZ->lphWndDialog, sizeof(HWND)))
  201. {
  202. *lpOBZ->lphWndDialog = hDlg;
  203. }
  204. // Update text in text box --
  205. // GetTaskInfo will return two pointers, one to the task name
  206. // (file name) and one to the window name. We need to call
  207. // OleStdFree on these when we're done with them. We also
  208. // get the HWND which is blocked in this call
  209. //
  210. // In the case where this call fails, a default message should already
  211. // be present in the dialog template, so no action is needed
  212. if (GetTaskInfo(hDlg, lpOBZ->hTask, &lpTaskName, &lpWindowName, &lpBZ->hWndBlocked))
  213. {
  214. // Build string to present to user, place in IDBZ_MESSAGE1 control
  215. BuildBusyDialogString(hDlg, lpBZ->dwFlags, IDBZ_MESSAGE1, lpTaskName, lpWindowName);
  216. OleStdFree(lpTaskName);
  217. OleStdFree(lpWindowName);
  218. }
  219. // Update icon with the system "exclamation" icon
  220. hIcon = LoadIcon(NULL, IDI_EXCLAMATION);
  221. SendDlgItemMessage(hDlg, IDBZ_ICON, STM_SETICON, (WPARAM)hIcon, 0L);
  222. // Disable/Enable controls
  223. if ((lpBZ->dwFlags & BZ_DISABLECANCELBUTTON) ||
  224. (lpBZ->dwFlags & BZ_NOTRESPONDINGDIALOG)) // Disable cancel for "not responding" dialog
  225. EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);
  226. if (lpBZ->dwFlags & BZ_DISABLESWITCHTOBUTTON)
  227. EnableWindow(GetDlgItem(hDlg, IDBZ_SWITCHTO), FALSE);
  228. if (lpBZ->dwFlags & BZ_DISABLERETRYBUTTON)
  229. EnableWindow(GetDlgItem(hDlg, IDBZ_RETRY), FALSE);
  230. // Call the hook with lCustData in lParam
  231. UStandardHook((LPVOID)lpBZ, hDlg, WM_INITDIALOG, wParam, lpOBZ->lCustData);
  232. // Update caption if lpszCaption was specified
  233. if (lpBZ->lpOBZ->lpszCaption && !IsBadReadPtr(lpBZ->lpOBZ->lpszCaption, 1)
  234. && lpBZ->lpOBZ->lpszCaption[0] != '\0')
  235. SetWindowText(hDlg, lpBZ->lpOBZ->lpszCaption);
  236. return TRUE;
  237. }
  238. /*
  239. * BuildBusyDialogString
  240. *
  241. * Purpose:
  242. * Builds the string that will be displayed in the dialog from the
  243. * task name and window name parameters.
  244. *
  245. * Parameters:
  246. * hDlg HWND of the dialog
  247. * dwFlags DWORD containing flags passed into dialog
  248. * iControl Control ID to place the text string
  249. * lpTaskName LPSTR pointing to name of task (e.g. C:\TEST\TEST.EXE)
  250. * lpWindowName LPSTR for name of window
  251. *
  252. * Caveats:
  253. * The caller of this function MUST de-allocate the lpTaskName and
  254. * lpWindowName pointers itself with OleStdFree
  255. *
  256. * Return Value:
  257. * void
  258. */
  259. void BuildBusyDialogString(HWND hDlg, DWORD dwFlags, int iControl, LPTSTR lpTaskName, LPTSTR lpWindowName)
  260. {
  261. LPTSTR pszT, psz1, psz2, psz3;
  262. UINT cch;
  263. LPTSTR pszDot, pszSlash;
  264. UINT uiStringNum;
  265. /*
  266. * We need scratch memory for loading the stringtable string,
  267. * the task name, and constructing the final string. We therefore
  268. * allocate three buffers as large as the maximum message
  269. * length (512) plus the object type, guaranteeing that we have enough
  270. * in all cases.
  271. */
  272. cch=512;
  273. // Use OLE-supplied allocation
  274. if ((pszT = OleStdMalloc((ULONG)(3*cch))) == NULL)
  275. return;
  276. psz1=pszT;
  277. psz2=psz1+cch;
  278. psz3=psz2+cch;
  279. // Parse base name out of path name, use psz2 for the task
  280. // name to display
  281. // In Win32, _fstrcpy is mapped to handle UNICODE stuff
  282. _fstrcpy(psz2, lpTaskName);
  283. pszDot = _fstrrchr(psz2, TEXT('.'));
  284. pszSlash = _fstrrchr(psz2, TEXT('\\')); // Find last backslash in path
  285. if (pszDot != NULL)
  286. #ifdef UNICODE
  287. *pszDot = TEXT('\0'); // Null terminate at the DOT
  288. #else
  289. *pszDot = '\0'; // Null terminate at the DOT
  290. #endif
  291. if (pszSlash != NULL)
  292. psz2 = pszSlash + 1; // Nuke everything up to this point
  293. #ifdef LOWERCASE_NAME
  294. // Compile this with /DLOWERCASE_NAME if you want the lower-case
  295. // module name to be displayed in the dialog rather than the
  296. // all-caps name.
  297. {
  298. int i,l;
  299. // Now, lowercase all letters except first one
  300. l = _fstrlen(psz2);
  301. for(i=0;i<l;i++)
  302. psz2[i] = tolower(psz2[i]);
  303. psz2[0] = toupper(psz2[0]);
  304. }
  305. #endif
  306. // Check size of lpWindowName. We can reasonably fit about 80
  307. // characters into the text control, so truncate more than 80 chars
  308. if (_fstrlen(lpWindowName)> 80)
  309. #ifdef UNICODE
  310. lpWindowName[80] = TEXT('\0');
  311. #else
  312. lpWindowName[80] = '\0';
  313. #endif
  314. // Load the format string out of stringtable, choose a different
  315. // string depending on what flags are passed in to the dialog
  316. if (dwFlags & BZ_NOTRESPONDINGDIALOG)
  317. uiStringNum = IDS_BZRESULTTEXTNOTRESPONDING;
  318. else
  319. uiStringNum = IDS_BZRESULTTEXTBUSY;
  320. if (LoadString(ghInst, uiStringNum, psz1, cch) == 0)
  321. return;
  322. // Build the string. The format string looks like this:
  323. // "This action cannot be completed because the '%s' application
  324. // (%s) is [busy | not responding]. Choose \"Switch To\" to activate '%s' and
  325. // correct the problem."
  326. wsprintf(psz3, psz1, (LPSTR)psz2, (LPTSTR)lpWindowName, (LPTSTR)psz2);
  327. SetDlgItemText(hDlg, iControl, (LPTSTR)psz3);
  328. OleStdFree(pszT);
  329. return;
  330. }
  331. /*
  332. * BusyCleanup
  333. *
  334. * Purpose:
  335. * Performs busy-specific cleanup before termination.
  336. *
  337. * Parameters:
  338. * hDlg HWND of the dialog box so we can access controls.
  339. *
  340. * Return Value:
  341. * None
  342. */
  343. void BusyCleanup(HWND hDlg)
  344. {
  345. return;
  346. }
  347. /*
  348. * GetTaskInfo()
  349. *
  350. * Purpose: Gets information about the specified task and places the
  351. * module name, window name and top-level HWND for the task in the specified
  352. * pointers
  353. *
  354. * NOTE: The two string pointers allocated in this routine are
  355. * the responsibility of the CALLER to de-allocate.
  356. *
  357. * Parameters:
  358. * hWnd HWND who called this function
  359. * htask HTASK which we want to find out more info about
  360. * lplpszTaskName Location that the module name is returned
  361. * lplpszWindowName Location where the window name is returned
  362. *
  363. */
  364. BOOL GetTaskInfo(HWND hWnd, HTASK htask, LPTSTR FAR* lplpszTaskName, LPTSTR FAR*lplpszWindowName, HWND FAR*lphWnd)
  365. {
  366. BOOL fRet = FALSE;
  367. #if !defined( WIN32 )
  368. TASKENTRY te;
  369. #endif
  370. HWND hwndNext;
  371. LPTSTR lpszTN = NULL;
  372. LPTSTR lpszWN = NULL;
  373. HWND hwndFind = NULL;
  374. // Clear out return values in case of error
  375. *lplpszTaskName = NULL;
  376. *lplpszWindowName = NULL;
  377. #if !defined( WIN32 )
  378. te.dwSize = sizeof(TASKENTRY);
  379. if (TaskFindHandle(&te, htask))
  380. #endif
  381. {
  382. // Now, enumerate top-level windows in system
  383. hwndNext = GetWindow(hWnd, GW_HWNDFIRST);
  384. while (hwndNext)
  385. {
  386. // See if we can find a non-owned top level window whose
  387. // hInstance matches the one we just got passed. If we find one,
  388. // we can be fairly certain that this is the top-level window for
  389. // the task which is blocked.
  390. //
  391. // REVIEW: Will this filter hold true for InProcServer DLL-created
  392. // windows?
  393. //
  394. if ((hwndNext != hWnd) &&
  395. #if !defined( WIN32 )
  396. (GetWindowWord(hwndNext, GWW_HINSTANCE) == (WORD)te.hInst) &&
  397. #else
  398. ((HTASK) GetWindowThreadProcessId(hwndNext,NULL) == htask) &&
  399. #endif
  400. (IsWindowVisible(hwndNext)) &&
  401. !GetWindow(hwndNext, GW_OWNER))
  402. {
  403. // We found our window! Alloc space for new strings
  404. if ((lpszTN = OleStdMalloc(OLEUI_CCHPATHMAX_SIZE)) == NULL)
  405. return TRUE; // continue task window enumeration
  406. if ((lpszWN = OleStdMalloc(OLEUI_CCHPATHMAX_SIZE)) == NULL)
  407. return TRUE; // continue task window enumeration
  408. // We found the window we were looking for, copy info to
  409. // local vars
  410. GetWindowText(hwndNext, lpszWN, OLEUI_CCHPATHMAX);
  411. #if !defined( WIN32 )
  412. LSTRCPYN(lpszTN, te.szModule, OLEUI_CCHPATHMAX);
  413. #else
  414. /* WIN32 NOTE: we are not able to get a module name
  415. ** given a thread process id on WIN32. the best we
  416. ** can do is use the window title as the module/app
  417. ** name.
  418. */
  419. LSTRCPYN(lpszTN, lpszWN, OLEUI_CCHPATHMAX);
  420. #endif
  421. hwndFind = hwndNext;
  422. fRet = TRUE;
  423. goto OKDone;
  424. }
  425. hwndNext = GetWindow(hwndNext, GW_HWNDNEXT);
  426. }
  427. }
  428. OKDone:
  429. // OK, everything was successful. Set string pointers to point to
  430. // our data.
  431. *lplpszTaskName = lpszTN;
  432. *lplpszWindowName = lpszWN;
  433. *lphWnd = hwndFind;
  434. return fRet;
  435. }
  436. /*
  437. * StartTaskManager()
  438. *
  439. * Purpose: Starts Task Manager. Used to bring up task manager to
  440. * assist in switching to a given blocked task.
  441. *
  442. */
  443. StartTaskManager()
  444. {
  445. WinExec("taskman.exe", SW_SHOW);
  446. return TRUE;
  447. }
  448. /*
  449. * MakeWindowActive()
  450. *
  451. * Purpose: Makes specified window the active window.
  452. *
  453. */
  454. void MakeWindowActive(HWND hWndSwitchTo)
  455. {
  456. // Move the new window to the top of the Z-order
  457. SetWindowPos(hWndSwitchTo, HWND_TOP, 0, 0, 0, 0,
  458. SWP_NOSIZE | SWP_NOMOVE);
  459. // If it's iconic, we need to restore it.
  460. if (IsIconic(hWndSwitchTo))
  461. ShowWindow(hWndSwitchTo, SW_RESTORE);
  462. }