Leaked source code of windows server 2003

891 lines
26 KiB

  1. /**************************************************************************\
  2. * Module Name: help.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * History:
  7. * 23-May-95 BradG Created to consolidate client-side help routines.
  8. *
  9. \**************************************************************************/
  10. #include "precomp.h"
  11. #pragma hdrstop
  12. #define MAX_ATTEMPTS 5 // maximum -1 id controls to search through
  13. char szDefaultHelpFileA[] = "windows.hlp";
  14. CONST WCHAR szEXECHELP[] = TEXT("\\winhlp32.exe");
  15. CONST WCHAR szMS_WINHELP[] = L"MS_WINHELP"; // Application class
  16. CONST WCHAR szMS_POPUPHELP[] = L"MS_POPUPHELP"; // Popup class
  17. CONST WCHAR szMS_TCARDHELP[] = L"MS_TCARDHELP"; // Training card class
  18. CONST WCHAR gawcWinhelpFlags[] = {
  19. L'x', // Execute WinHelp as application help
  20. L'p', // Execute WinHelp as a popup
  21. L'c', // Execute WinHelp as a training card
  22. };
  23. /***************************************************************************\
  24. * SendWinHelpMessage
  25. *
  26. * Attempts to give the winhelp process the right to take the foreground (it
  27. * will fail if the calling processs doesn't have the right itself). Then it
  28. * sends the WM_WINHELP message.
  29. *
  30. * History:
  31. * 02-10-98 GerardoB Created
  32. \***************************************************************************/
  33. LRESULT SendWinHelpMessage(
  34. HWND hwnd,
  35. WPARAM wParam,
  36. LPARAM lParam)
  37. {
  38. DWORD dwProcessId = 0;
  39. GetWindowThreadProcessId(hwnd, &dwProcessId);
  40. AllowSetForegroundWindow(dwProcessId);
  41. return SendMessage(hwnd, WM_WINHELP, wParam, lParam);
  42. }
  43. /***************************************************************************\
  44. * HFill
  45. *
  46. * Builds a data block for communicating with help
  47. *
  48. * LATER 13 Feb 92 GregoryW
  49. * This needs to stay ANSI until we have a Unicode help engine
  50. *
  51. * History:
  52. * 04-15-91 JimA Ported.
  53. * 03-24-95 BradG - YAP of Win95 code. Added code to prevent memory
  54. * overwrite on bad ulData == 0 parameter.
  55. \***************************************************************************/
  56. LPHLP HFill(
  57. LPCSTR lpszHelp,
  58. DWORD ulCommand, // HELP_ constant
  59. ULONG_PTR ulData)
  60. {
  61. DWORD cb; // Size of the data block
  62. DWORD cbStr; // Length of the help file name
  63. DWORD cbData; // Size of the dwData parameter in bytes (0 if not used)
  64. LPHLP phlp; // Pointer to data block
  65. BYTE bType; // dwData parameter type
  66. /*
  67. * Get the length of the help file name
  68. */
  69. cbStr = (lpszHelp) ? strlen(lpszHelp) + 1 : 0;
  70. /*
  71. * Get the length of any dwData parameters
  72. */
  73. bType = HIBYTE(LOWORD(ulCommand));
  74. if (ulData) {
  75. switch (bType) {
  76. case HIBYTE(HELP_HB_STRING):
  77. /*
  78. * ulData is an ANSI string, so compute its length
  79. */
  80. cbData = strlen((LPSTR)ulData) + 1;
  81. break;
  82. case HIBYTE(HELP_HB_STRUCT):
  83. /*
  84. * ulData points to a structure who's first member is an int
  85. * that contains the size of the structure in bytes.
  86. */
  87. cbData = *((int *)ulData);
  88. break;
  89. default:
  90. /*
  91. * dwData has no parameter.
  92. */
  93. cbData = 0;
  94. }
  95. } else {
  96. /*
  97. * No parameter is present.
  98. */
  99. cbData = 0;
  100. }
  101. /*
  102. * Calculate size.
  103. */
  104. cb = sizeof(HLP) + cbStr + cbData;
  105. /*
  106. * Get data block.
  107. */
  108. if ((phlp = (LPHLP)UserLocalAlloc(HEAP_ZERO_MEMORY, cb)) == NULL) {
  109. return NULL;
  110. }
  111. /*
  112. * Fill in info.
  113. */
  114. phlp->cbData = (WORD)cb;
  115. phlp->usCommand = (WORD)ulCommand;
  116. /*
  117. * Fill in file name.
  118. */
  119. if (lpszHelp) {
  120. phlp->offszHelpFile = sizeof(HLP);
  121. strcpy((LPSTR)(phlp + 1), lpszHelp);
  122. }
  123. /*
  124. * Fill in data
  125. */
  126. switch (bType) {
  127. case HIBYTE(HELP_HB_STRING):
  128. if (cbData) {
  129. phlp->offabData = (WORD)(sizeof(HLP) + cbStr);
  130. strcpy((LPSTR)phlp + phlp->offabData, (LPSTR)ulData);
  131. }
  132. break;
  133. case HIBYTE(HELP_HB_STRUCT):
  134. if (cbData) {
  135. phlp->offabData = (WORD)(sizeof(HLP) + cbStr);
  136. RtlCopyMemory((LPBYTE)phlp + phlp->offabData,
  137. (PVOID)ulData,
  138. *((int*)ulData));
  139. }
  140. break;
  141. default:
  142. phlp->ulTopic = ulData;
  143. break;
  144. }
  145. return phlp;
  146. }
  147. /***************************************************************************\
  148. * LaunchHelp
  149. *
  150. * This function launches the WinHlp32 executable with the correct command
  151. * line arguments.
  152. *
  153. * History:
  154. * 03/23/1995 BradG YAP of new changes from Win95
  155. * 03/01/2002 JasonSch Changed to only look for winhlp32.exe in %windir%.
  156. \***************************************************************************/
  157. BOOL LaunchHelp(
  158. DWORD dwType)
  159. {
  160. WCHAR *pwszPath, wszCommandLine[16];
  161. BOOL bRet;
  162. STARTUPINFO StartupInfo;
  163. PROCESS_INFORMATION ProcessInformation;
  164. ULONG cChars;
  165. /*
  166. * Return value of GetSystemWindowsDirectory does not include the
  167. * terminating NULL, so + 1.
  168. */
  169. cChars = GetSystemWindowsDirectoryW(NULL, 0) + 1;
  170. pwszPath = UserLocalAlloc(0, (cChars + ARRAY_SIZE(szEXECHELP)) * sizeof(WCHAR));
  171. if (pwszPath == NULL) {
  172. return FALSE;
  173. }
  174. GetSystemWindowsDirectoryW(pwszPath, cChars);
  175. wcscat(pwszPath, szEXECHELP);
  176. wsprintf(wszCommandLine, L"%ws -%wc", szEXECHELP + 1, gawcWinhelpFlags[dwType]);
  177. /*
  178. * Launch winhelp.
  179. */
  180. RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
  181. StartupInfo.cb = sizeof(StartupInfo);
  182. StartupInfo.wShowWindow = SW_SHOW;
  183. StartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
  184. bRet = CreateProcessW(pwszPath,
  185. wszCommandLine,
  186. NULL,
  187. NULL,
  188. FALSE,
  189. NORMAL_PRIORITY_CLASS,
  190. NULL,
  191. NULL,
  192. &StartupInfo,
  193. &ProcessInformation);
  194. if (bRet) {
  195. WaitForInputIdle(ProcessInformation.hProcess, 10000);
  196. NtClose(ProcessInformation.hProcess);
  197. NtClose(ProcessInformation.hThread);
  198. }
  199. UserLocalFree(pwszPath);
  200. return bRet;
  201. }
  202. /***************************************************************************\
  203. * GetNextDlgHelpItem
  204. *
  205. * This is a reduced version of the GetNextDlgTabItem function that does not
  206. * skip disabled controls.
  207. *
  208. * History:
  209. * 3/25/95 BradG Ported from Win95
  210. \***************************************************************************/
  211. PWND GetNextDlgHelpItem(
  212. PWND pwndDlg,
  213. PWND pwnd)
  214. {
  215. PWND pwndSave;
  216. if (pwnd == pwndDlg) {
  217. pwnd = NULL;
  218. } else {
  219. pwnd = _GetChildControl(pwndDlg, pwnd);
  220. if (pwnd) {
  221. if (!_IsDescendant(pwndDlg, pwnd))
  222. return NULL;
  223. }
  224. }
  225. /*
  226. * BACKWARD COMPATIBILITY
  227. *
  228. * Note that the result when there are no tabstops of
  229. * IGetNextDlgTabItem(hwndDlg, NULL, FALSE) was the last item, now
  230. * will be the first item. We could put a check for fRecurse here
  231. * and do the old thing if not set.
  232. */
  233. /*
  234. * We are going to bug out if we hit the first child a second time.
  235. */
  236. pwndSave = pwnd;
  237. pwnd = _NextControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE);
  238. while ((pwnd != pwndSave) && (pwnd != pwndDlg))
  239. {
  240. UserAssert(pwnd);
  241. if (!pwndSave)
  242. pwndSave = pwnd;
  243. if ((pwnd->style & (WS_TABSTOP | WS_VISIBLE)) == (WS_TABSTOP | WS_VISIBLE))
  244. /*
  245. * Found it.
  246. */
  247. break;
  248. pwnd = _NextControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE);
  249. }
  250. return pwnd;
  251. }
  252. /***************************************************************************\
  253. * HelpMenu
  254. *
  255. * History:
  256. * 01-Feb-1994 mikeke Ported.
  257. \***************************************************************************/
  258. UINT HelpMenu(
  259. HWND hwnd,
  260. PPOINT ppt)
  261. {
  262. INT cmd;
  263. HMENU hmenu = LoadMenu( hmodUser, MAKEINTRESOURCE(ID_HELPMENU));
  264. if (hmenu != NULL) {
  265. cmd = TrackPopupMenu( GetSubMenu(hmenu, 0),
  266. TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON,
  267. ppt->x, ppt->y, 0, hwnd, NULL);
  268. NtUserDestroyMenu(hmenu);
  269. return cmd;
  270. }
  271. return (UINT)-1;
  272. }
  273. /***************************************************************************\
  274. * FindWinHelpWindow
  275. *
  276. * This function attempts to locate the help window. If it fails, it attempts
  277. * to launch WinHlp32.exe and then look for its window.
  278. *
  279. * History:
  280. * 03/24/95 BradG Created by extracting code from xxxWinHelpA
  281. \***************************************************************************/
  282. HWND FindWinHelpWindow(
  283. LPCWSTR lpwstrHelpWindowClass,
  284. DWORD dwType,
  285. BOOL bLaunchIt)
  286. {
  287. HWND hwndHelp;
  288. /*
  289. * Find the current help window. If not found, try and launch
  290. * the WinHlp32 application. We are interested only in 32 bit help.
  291. *
  292. * Note that 16 bit apps don't walk this path, ntvdm takes care of
  293. * starting the 16 bit help for them.
  294. */
  295. hwndHelp = InternalFindWindowExW(NULL, NULL, lpwstrHelpWindowClass, NULL, FW_32BIT);
  296. if (hwndHelp == NULL) {
  297. if (bLaunchIt) {
  298. /*
  299. * Can't find it --> see if we want to launch it.
  300. */
  301. if (LaunchHelp(dwType) == FALSE ||
  302. (hwndHelp = FindWindowW(lpwstrHelpWindowClass, NULL)) == NULL) {
  303. /*
  304. * Can't find help, or not enough memory to load help.
  305. * hwndHelp will be NULL at this point.
  306. */
  307. RIPMSG0(RIP_WARNING, "LaunchHelp or FindWindow failed.");
  308. }
  309. }
  310. }
  311. return hwndHelp;
  312. }
  313. /*
  314. * HWND version of Enumeration function to find controls while
  315. * ignoring group boxes but not disabled controls.
  316. */
  317. BOOL CALLBACK EnumHwndDlgChildProc(
  318. HWND hwnd,
  319. LPARAM lParam)
  320. {
  321. PWND pwnd;
  322. BOOL bResult;
  323. if (pwnd = ValidateHwnd(hwnd)) {
  324. bResult = EnumPwndDlgChildProc(pwnd, lParam);
  325. } else {
  326. bResult = TRUE;
  327. }
  328. return bResult;
  329. }
  330. /***************************************************************************\
  331. * WinHelp
  332. *
  333. * Displays help
  334. *
  335. * History:
  336. * 04-15-91 JimA Ported.
  337. * 01-29-92 GregoryW Neutral version.
  338. * 05-22-92 SanfordS Added support for help structures
  339. * 03-24-95 BradG Moved from Client side WinHelpA to server side
  340. * xxxWinHelpA because of changes in Win95. The
  341. * function xxxServerWinHelp was merged.
  342. \***************************************************************************/
  343. BOOL WinHelpA(
  344. HWND hwnd,
  345. LPCSTR lpszHelp,
  346. UINT uCommand,
  347. ULONG_PTR dwData)
  348. {
  349. LPCWSTR lpwstrHelpWindowClass;
  350. LPHLP lpHlp = NULL;
  351. DWORD dwType;
  352. PWND pwnd;
  353. HWND hwndHelp = NULL; /* Handle of help's main window */
  354. PWND pwndTop = NULL; /* Top level window that WinHelp uses. */
  355. PWND pwndMain; /* pointer to main help control */
  356. LRESULT lResult;
  357. POINT ptCur;
  358. BOOL bResult = TRUE;
  359. pwnd = ValidateHwnd(hwnd);
  360. if (uCommand & HELP_TCARD) {
  361. /*
  362. * For Training Cards, the HELP_TCARD bit is set. We need to
  363. * set our help window class to szMS_TCARDHELP and then remove
  364. * the HELP_TCARD bit.
  365. */
  366. lpwstrHelpWindowClass = szMS_TCARDHELP;
  367. uCommand &= ~HELP_TCARD; // mask out the tcard flag
  368. dwType = TYPE_TCARD;
  369. } else {
  370. if (uCommand == HELP_CONTEXTMENU || uCommand == HELP_CONTEXTPOPUP ||
  371. uCommand == HELP_SETPOPUP_POS || uCommand == HELP_WM_HELP) {
  372. /*
  373. * Popups should be connected to a valid window. pwndMain has
  374. * already been validated as a real window handle or NULL, so we
  375. * just need to check the NULL case here.
  376. */
  377. if (pwnd == NULL) {
  378. RIPERR1(ERROR_INVALID_PARAMETER,
  379. RIP_WARNING,
  380. "WinHelpA: NULL hWnd invalid for this type of help command (0x%x)",
  381. uCommand);
  382. bResult = FALSE;
  383. goto Exit_WinHelp;
  384. }
  385. dwType = TYPE_POPUP;
  386. lpwstrHelpWindowClass = szMS_POPUPHELP;
  387. } else {
  388. dwType = TYPE_NORMAL;
  389. lpwstrHelpWindowClass = szMS_WINHELP;
  390. }
  391. }
  392. /*
  393. * Get the cursor's current location This is where we assume the user
  394. * clicked. We will use this position to search for a child window and
  395. * to set the context sensitive help popup window's location.
  396. *
  397. * If the last input was a keyboard one, use the point in the center
  398. * of the focus window rectangle. MCostea #249270
  399. */
  400. if (TEST_SRVIF(SRVIF_LASTRITWASKEYBOARD)) {
  401. HWND hWndFocus = GetFocus();
  402. RECT rcWindow;
  403. if (GetWindowRect(hWndFocus, &rcWindow)) {
  404. ptCur.x = (rcWindow.left + rcWindow.right)/2;
  405. ptCur.y = (rcWindow.top + rcWindow.bottom)/2;
  406. } else {
  407. goto getCursorPos;
  408. }
  409. } else {
  410. getCursorPos:
  411. GetCursorPos(&ptCur);
  412. }
  413. /*
  414. * If we are handling the HELP_CONTEXTMENU command, see if we
  415. * can determine the correct child window.
  416. */
  417. if (uCommand == HELP_CONTEXTMENU && FIsParentDude(pwnd)) {
  418. LONG lPt;
  419. int nHit;
  420. DLGENUMDATA DlgEnumData;
  421. /*
  422. * If the user really clicked on the caption or the system menu,
  423. * then we want the context menu for the window, not help for a
  424. * control. This makes it consistent across all 3.x and 4.0
  425. * windows.
  426. */
  427. lPt = MAKELONG(ptCur.x,ptCur.y);
  428. nHit = FindNCHit(pwnd, lPt);
  429. if ((nHit == HTCAPTION) || (nHit == HTSYSMENU))
  430. DefWindowProc(hwnd, WM_CONTEXTMENU, (WPARAM)hwnd, lPt);
  431. /*
  432. * If this is a dialog class, then one of three things has
  433. * happened:
  434. *
  435. * o This is a disabled control
  436. * o This is a static text control
  437. * o This is the background of the dialog box.
  438. *
  439. * What we do is enumerate the child windows and see if
  440. * any of them contain the current cursor point. If they do,
  441. * change our window handle and continue on. Otherwise,
  442. * return doing nothing -- we don't want context-sensitive
  443. * help for a dialog background.
  444. *
  445. * If this is a group box, then we might have clicked on a
  446. * disabled control, so we enumerate child windows to see
  447. * if we get another control.
  448. */
  449. DlgEnumData.pwndDialog = pwnd;
  450. DlgEnumData.pwndControl = NULL;
  451. DlgEnumData.ptCurHelp = ptCur;
  452. EnumChildWindows(hwnd, (WNDENUMPROC)EnumHwndDlgChildProc, (LPARAM)&DlgEnumData);
  453. if (DlgEnumData.pwndControl == NULL) {
  454. /*
  455. * Can't find a control, so nothing to do.
  456. */
  457. goto Exit_WinHelp;
  458. } else {
  459. /*
  460. * Remember this control because it will be used as the
  461. * control for context sensitive help.
  462. */
  463. pwndMain = DlgEnumData.pwndControl;
  464. }
  465. } else {
  466. /*
  467. * We will use pwnd as our main control. No need to lock it
  468. * because it is already locked.
  469. */
  470. pwndMain = pwnd;
  471. }
  472. /*
  473. * For HELP_CONTEXTPOPUP and HELP_WM_HELP, see if we can derive the
  474. * context id by looking at the array of double word ID pairs that
  475. * have been passed in in dwData.
  476. */
  477. if (uCommand == HELP_CONTEXTMENU || uCommand == HELP_WM_HELP) {
  478. int id;
  479. int i;
  480. LPDWORD pid;
  481. /*
  482. * Be careful about the cast below. We need the ID, which is stored
  483. * in the LOWORD of spmenu to be sign extended to an int.
  484. * Don't sign extend so IDs like 8008 work
  485. */
  486. id = (DWORD)(PTR_TO_ID(pwndMain->spmenu)); // get control id
  487. pid = (LPDWORD) dwData;
  488. /*
  489. * Is the control's ID -1?
  490. */
  491. if ((SHORT)id == -1) {
  492. /*
  493. * This is a static (i.e., ID'less) control
  494. */
  495. PWND pwndCtrl;
  496. int cAttempts = 0;
  497. /*
  498. * If the control is a group box, with an ID of -1, bail out
  499. * as the UI specs decided to have no context help
  500. * for these cases. MCostea
  501. */
  502. if ((TestWF(pwndMain, BFTYPEMASK) == BS_GROUPBOX) &&
  503. IS_BUTTON(pwndMain)) {
  504. goto Exit_WinHelp;
  505. }
  506. /*
  507. * For non-id controls (typically static controls), step
  508. * through to the next tab item. Keep finding the next tab
  509. * item until we find a valid id, or we have tried
  510. * MAX_ATTEMPTS times.
  511. */
  512. do {
  513. pwndCtrl = GetNextDlgHelpItem(REBASEPWND(pwndMain,spwndParent), pwndMain);
  514. /*
  515. * pwndCtrl will be NULL if hwndMain doesn't have a parent,
  516. * or if there are no tab stops.
  517. */
  518. if (!pwndCtrl) {
  519. /*
  520. * Remember to unlock the control
  521. */
  522. bResult = FALSE;
  523. goto Exit_WinHelp;
  524. }
  525. /*
  526. * Be careful about the cast below. We need the ID, which is
  527. * stored in the LOWORD of spmenu to be sign extended to an int.
  528. * Don't sign extend so IDs like 8008 work
  529. */
  530. id = (DWORD)(PTR_TO_ID(pwndCtrl->spmenu));
  531. } while (((SHORT)id == -1) && (++cAttempts < MAX_ATTEMPTS));
  532. }
  533. if ((SHORT)id == -1) {
  534. id = -1;
  535. }
  536. /*
  537. * Find the id value in array of id/help context values
  538. */
  539. for (i = 0; pid[i]; i += 2) {
  540. if ((int)pid[i] == id) {
  541. break;
  542. }
  543. }
  544. /*
  545. * Since no help was specified for the found control, see if
  546. * the control is one of the known ID (i.e., OK, Cancel...)
  547. */
  548. if (!pid[i]) {
  549. /*
  550. * Help for the standard controls is in the default
  551. * help file windows.hlp. Switch to this file.
  552. */
  553. lpszHelp = szDefaultHelpFileA;
  554. switch (id) {
  555. case IDOK:
  556. dwData = IDH_OK;
  557. break;
  558. case IDCANCEL:
  559. dwData = IDH_CANCEL;
  560. break;
  561. case IDHELP:
  562. dwData = IDH_HELP;
  563. break;
  564. default:
  565. /*
  566. * Unknown control, give a generic missing context info
  567. * popup message in windows.hlp.
  568. */
  569. dwData = IDH_MISSING_CONTEXT;
  570. }
  571. } else {
  572. dwData = pid[i + 1];
  573. if (dwData == (DWORD)-1) {
  574. /*
  575. * Remember, to unlock the control
  576. */
  577. goto Exit_WinHelp; // caller doesn't want help after all
  578. }
  579. }
  580. /*
  581. * Now that we know the caller wants help for this control, display
  582. * the help menu.
  583. */
  584. if (uCommand == HELP_CONTEXTMENU) {
  585. int cmd;
  586. cmd = HelpMenu(HW(pwndMain), &ptCur);
  587. if (cmd <= 0) {
  588. /*
  589. * Probably means user cancelled the menu.
  590. */
  591. goto Exit_WinHelp;
  592. }
  593. }
  594. /*
  595. * Create WM_WINHELP's HLP data structure for HELP_SETPOPUP_POS
  596. */
  597. if (!(lpHlp = HFill(lpszHelp, HELP_SETPOPUP_POS,
  598. MAKELONG(pwndMain->rcWindow.left, pwndMain->rcWindow.top)))) {
  599. bResult = FALSE;
  600. goto Exit_WinHelp;
  601. }
  602. /*
  603. * Tell WinHelp where to put the popup. This is different than Win95
  604. * because we try and avoid a recursive call here. So, we find the
  605. * WinHlp32 window and send the HELP_SETPOPUP_POS. No recursion.
  606. */
  607. hwndHelp = FindWinHelpWindow(lpwstrHelpWindowClass, dwType, TRUE);
  608. if (hwndHelp == NULL) {
  609. /*
  610. * Uable to communicate with WinHlp32.exe.
  611. * Remember to unlock the control
  612. */
  613. bResult = FALSE;
  614. goto Exit_WinHelp;
  615. }
  616. /*
  617. * Send the WM_WINHELP message to WinHlp32's window.
  618. */
  619. lResult = SendWinHelpMessage(hwndHelp, (WPARAM)HW(pwndMain), (LPARAM)lpHlp);
  620. UserLocalFree(lpHlp);
  621. lpHlp = NULL;
  622. if (!lResult) {
  623. /*
  624. * WinHlp32 couldn't process the command. Bail out!
  625. */
  626. bResult = FALSE;
  627. goto Exit_WinHelp;
  628. }
  629. /*
  630. * Make HELP_WM_HELP and HELP_CONTEXTMENU act like HELP_CONTEXTPOPUP
  631. */
  632. uCommand = HELP_CONTEXTPOPUP;
  633. }
  634. if (uCommand == HELP_CONTEXTPOPUP) {
  635. /*
  636. * If no help file was specified, use windows.hlp
  637. */
  638. if (lpszHelp == NULL || *lpszHelp == '\0') {
  639. lpszHelp = szDefaultHelpFileA; // default: use windows.hlp
  640. }
  641. /*
  642. * WINHELP.EXE will call SetForegroundWindow on the hwnd that we pass
  643. * to it below. We really want to pass the parent dialog hwnd of the
  644. * control so that focus will properly be restored to the dialog and
  645. * not the control that wants help.
  646. */
  647. pwndTop = GetTopLevelWindow(pwndMain);
  648. } else {
  649. pwndTop = pwndMain;
  650. }
  651. /*
  652. * Move Help file name to a handle.
  653. */
  654. if (!(lpHlp = HFill(lpszHelp, uCommand, dwData))) {
  655. /*
  656. * Can't allocate memory.
  657. */
  658. bResult = FALSE;
  659. goto Exit_WinHelp;
  660. }
  661. /*
  662. * Get a pointer to the help window.
  663. */
  664. hwndHelp = FindWinHelpWindow(lpwstrHelpWindowClass,
  665. dwType,
  666. (uCommand != HELP_QUIT));
  667. if (hwndHelp == NULL) {
  668. if (uCommand != HELP_QUIT)
  669. /*
  670. * Can't find Winhlp.
  671. */
  672. bResult = FALSE;
  673. goto Exit_WinHelp;
  674. }
  675. /*
  676. * Send the WM_WINHELP message to WinHlp32's window
  677. * Must ThreadLock pwndHelp AND pwndMain (because pwndMain may have been
  678. * reassigned above).
  679. */
  680. SendWinHelpMessage(hwndHelp, (WPARAM)HW(pwndTop), (LPARAM)lpHlp);
  681. /*
  682. * Free the help info data structure (if not already free).
  683. */
  684. Exit_WinHelp:
  685. if (lpHlp != NULL) {
  686. UserLocalFree(lpHlp);
  687. }
  688. return bResult;
  689. }
  690. /***************************************************************************\
  691. * WinHelpW
  692. *
  693. * Calls WinHelpA after doing any necessary translation. Our help engine is
  694. * ASCII only.
  695. \***************************************************************************/
  696. BOOL WinHelpW(
  697. HWND hwndMain,
  698. LPCWSTR lpwszHelp,
  699. UINT uCommand,
  700. ULONG_PTR dwData)
  701. {
  702. BOOL fSuccess = FALSE;
  703. LPSTR lpAnsiHelp = NULL;
  704. LPSTR lpAnsiKey = NULL;
  705. PMULTIKEYHELPA pmkh = NULL;
  706. PHELPWININFOA phwi = NULL;
  707. NTSTATUS Status;
  708. /*
  709. * First convert the string.
  710. */
  711. if (lpwszHelp != NULL && !WCSToMB(lpwszHelp, -1, &lpAnsiHelp, -1, TRUE)) {
  712. return FALSE;
  713. }
  714. /*
  715. * Then convert dwData if needed
  716. */
  717. switch (uCommand) {
  718. case HELP_MULTIKEY:
  719. if (!WCSToMB(((PMULTIKEYHELPW)dwData)->szKeyphrase, -1, &lpAnsiKey,
  720. -1, TRUE)) {
  721. goto FreeAnsiHelp;
  722. }
  723. pmkh = UserLocalAlloc(HEAP_ZERO_MEMORY,
  724. sizeof(MULTIKEYHELPA) + strlen(lpAnsiKey));
  725. if (pmkh == NULL) {
  726. goto FreeAnsiKeyAndHelp;
  727. }
  728. pmkh->mkSize = sizeof(MULTIKEYHELPA) + strlen(lpAnsiKey);
  729. Status = RtlUnicodeToMultiByteN((LPSTR)&pmkh->mkKeylist, sizeof(CHAR),
  730. NULL, (LPWSTR)&((PMULTIKEYHELPW)dwData)->mkKeylist,
  731. sizeof(WCHAR));
  732. strcpy(pmkh->szKeyphrase, lpAnsiKey);
  733. if (!NT_SUCCESS(Status)) {
  734. goto FreeAnsiKeyAndHelp;
  735. }
  736. dwData = (ULONG_PTR)pmkh;
  737. break;
  738. case HELP_SETWINPOS:
  739. if (!WCSToMB(((PHELPWININFOW)dwData)->rgchMember, -1, &lpAnsiKey,
  740. -1, TRUE)) {
  741. goto FreeAnsiKeyAndHelp;
  742. }
  743. phwi = UserLocalAlloc(HEAP_ZERO_MEMORY,
  744. ((PHELPWININFOW)dwData)->wStructSize);
  745. if (phwi == NULL) {
  746. goto FreeAnsiKeyAndHelp;
  747. }
  748. *phwi = *((PHELPWININFOA)dwData); // copies identical parts
  749. strcpy(phwi->rgchMember, lpAnsiKey);
  750. dwData = (ULONG_PTR)phwi;
  751. break;
  752. case HELP_KEY:
  753. case HELP_PARTIALKEY:
  754. case HELP_COMMAND:
  755. if (!WCSToMB((LPCTSTR)dwData, -1, &lpAnsiKey, -1, TRUE)) {
  756. goto FreeAnsiKeyAndHelp;
  757. }
  758. dwData = (ULONG_PTR)lpAnsiKey;
  759. break;
  760. }
  761. /*
  762. * Call the Ansi version
  763. */
  764. fSuccess = WinHelpA(hwndMain, lpAnsiHelp, uCommand, dwData);
  765. if (pmkh) {
  766. UserLocalFree(pmkh);
  767. }
  768. if (phwi) {
  769. UserLocalFree(phwi);
  770. }
  771. FreeAnsiKeyAndHelp:
  772. if (lpAnsiKey) {
  773. UserLocalFree(lpAnsiKey);
  774. }
  775. FreeAnsiHelp:
  776. if (lpAnsiHelp) {
  777. UserLocalFree(lpAnsiHelp);
  778. }
  779. return fSuccess;
  780. }