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.

518 lines
17 KiB

  1. // Copyright (C) Microsoft Corporation 1990-1997
  2. #include "header.h"
  3. #include "strtable.h"
  4. #include <commdlg.h>
  5. #include "system.h"
  6. #include <dlgs.h>
  7. #include "resource.h"
  8. #include "cdlg.h"
  9. // Set the last error for the HTML Help API callers.
  10. #include "lasterr.h"
  11. #define MAX_MESSAGE 512
  12. #define MAX_TABLE_STRINGS 1024
  13. static const char txtHelpDirKey[] = "Software\\Microsoft\\Windows\\HTML Help";
  14. static const char txtHhIni[] = "hh.ini";
  15. static const char txtFiles[] = "files";
  16. // Can't be const since RegCreateKeyEx() thinks it can modify this
  17. static char txtDirectoryClass[] = "Folder";
  18. // persistent local memory class
  19. class CDataFM {
  20. public:
  21. CDataFM::CDataFM() { m_ptblFoundFiles = NULL; }
  22. CDataFM::~CDataFM() { if( m_ptblFoundFiles ) delete m_ptblFoundFiles; }
  23. CTable* GetFoundFiles(void) { if( !m_ptblFoundFiles ) m_ptblFoundFiles = new CTable(MAX_TABLE_STRINGS * 256); return m_ptblFoundFiles; }
  24. private:
  25. CTable* m_ptblFoundFiles;
  26. };
  27. static CDataFM s_Data;
  28. /***************************************************************************
  29. FUNCTION: GetRegWindowsDirectory
  30. PURPOSE: Equivalent to GetWindowsDirectory() only it checks the
  31. registration first for the proper location
  32. PARAMETERS:
  33. pszDst
  34. RETURNS:
  35. COMMENTS:
  36. MODIFICATION DATES:
  37. 04-Dec-1994 [ralphw]
  38. ***************************************************************************/
  39. void GetRegWindowsDirectory(PSTR pszDstPath)
  40. {
  41. HHGetWindowsDirectory(pszDstPath, MAX_PATH);
  42. }
  43. /***************************************************************************
  44. FUNCTION: FindThisFile
  45. PURPOSE: Searches for the specified file:
  46. 1) Searches the registry
  47. 2) Searches windows\help
  48. 3) Searches hh.ini
  49. PARAMETERS:
  50. pszFile -- input filename
  51. pcsz -- receives full path
  52. fAskUser -- TRUE to ask the user to find the file
  53. RETURNS: TRUE if the file was found
  54. COMMENTS:
  55. MODIFICATION DATES:
  56. 04-Dec-1994 [ralphw]
  57. 10-May-1997 [ralphw] -- ported from WinHelp code
  58. ***************************************************************************/
  59. BOOL FindThisFile(HWND hwndParent, PCSTR pszFile, CStr* pcszFile, BOOL fAskUser /*= TRUE*/ )
  60. {
  61. char szFullPath[MAX_PATH + MAX_MESSAGE];
  62. LONG result = -1;
  63. HKEY hkey;
  64. DWORD type;
  65. CStr cszCompiledName;
  66. PCSTR pszName = GetCompiledName(pszFile, &cszCompiledName);
  67. /*
  68. * Extract just the filename portion of the path, and use that as the
  69. * lookup key in the registry. If found, the key value is the path to
  70. * use.
  71. */
  72. pszName = FindFilePortion(pszName ? pszName : pszFile);
  73. if (!pszName)
  74. {
  75. // Set the last error. We couldn't find the file.
  76. //g_LastError.Set(idProcess, HH_E_FILENOTFOUND) ; // Need idProcess to set.
  77. return FALSE;
  78. }
  79. if( s_Data.GetFoundFiles() ) {
  80. HASH hash = HashFromSz(pszName);
  81. int pos = s_Data.GetFoundFiles()->IsHashInTable(hash);
  82. if (pos > 0) {
  83. *pcszFile = s_Data.GetFoundFiles()->GetHashStringPointer(pos);
  84. return TRUE;
  85. }
  86. }
  87. // Check all open CHM files for a match
  88. for (int i = 0; i < g_cHmSlots; i++) {
  89. // BUGBUG: enumerate all CExTitles in collection
  90. if (g_phmData[i] && stristr(g_phmData[i]->GetCompiledFile(), pszName)) {
  91. if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS)
  92. s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), g_phmData[i]->GetCompiledFile());
  93. *pcszFile = g_phmData[i]->GetCompiledFile();
  94. return TRUE;
  95. }
  96. }
  97. if (GetFileAttributes(cszCompiledName) != (DWORD) -1) {
  98. GetFullPathName(cszCompiledName, sizeof(szFullPath), szFullPath,
  99. NULL);
  100. *pcszFile = szFullPath;
  101. return TRUE;
  102. }
  103. // major hack for bug 5909 which is breaking VBA, MSE and external clients, the above BUGBUG
  104. // would also fix this but we don't have a reliable way to get all collections..
  105. // look in the directories of the currently open files for this file
  106. char szTmp[MAX_PATH];
  107. for (i = 0; i < g_cHmSlots; i++) {
  108. if (g_phmData[i]) {
  109. // get the path to this file and append the new file and check if it exists
  110. SplitPath((LPSTR)g_phmData[i]->GetCompiledFile(), szFullPath, szTmp, NULL, NULL);
  111. CatPath(szFullPath, szTmp);
  112. CatPath(szFullPath, pszName);
  113. if (GetFileAttributes(szFullPath) != HFILE_ERROR )
  114. {
  115. *pcszFile = szFullPath;
  116. return TRUE;
  117. }
  118. }
  119. }
  120. // See if we have a Darwin GUID for this process ID
  121. BOOL bDone = FALSE;
  122. g_Busy.Set( TRUE );
  123. if (g_pszDarwinGuid) {
  124. CStr cszPath;
  125. if (FindDarwinURL(g_pszDarwinGuid/*BOGUS g_phmData[i]->m_pszDarwinGuid*/, pszName, &cszPath)) {
  126. if (GetFileAttributes(cszPath) != (DWORD) -1)
  127. {
  128. PSTR ptr = strrchr(cszPath.psz, '\\');
  129. *(++ptr) = '\0';
  130. cszPath += pszName;
  131. if (GetFileAttributes(cszPath) != (DWORD) -1)
  132. {
  133. if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS)
  134. s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), cszPath);
  135. *pcszFile = cszPath.psz;
  136. bDone = TRUE;
  137. }
  138. }
  139. }
  140. }
  141. // If the first Darwin GUID can't be found, try the alternate GUID
  142. else if (g_pszDarwinBackupGuid) {
  143. CStr cszPath;
  144. if (FindDarwinURL(g_pszDarwinBackupGuid /*BOGUS: g_phmData[i]->m_pszDarwinBackupGuid*/, pszName, &cszPath)) {
  145. if (GetFileAttributes(cszPath) != (DWORD) -1) {
  146. if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS)
  147. s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), cszPath);
  148. *pcszFile = cszPath.psz;
  149. bDone = TRUE;
  150. }
  151. }
  152. }
  153. g_Busy.Set( FALSE );
  154. if( bDone )
  155. return bDone;
  156. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, txtHelpDirKey, 0, KEY_READ, &hkey) ==
  157. ERROR_SUCCESS) {
  158. DWORD cbPath = MAX_PATH;
  159. result = RegQueryValueEx(hkey, pszName, 0, &type, (PBYTE) szFullPath, &cbPath);
  160. RegCloseKey(hkey);
  161. }
  162. if (result == ERROR_SUCCESS) {
  163. AddTrailingBackslash(szFullPath);
  164. strcat(szFullPath, pszName);
  165. if (GetFileAttributes(szFullPath) != (DWORD) -1) {
  166. if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS)
  167. s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), szFullPath);
  168. *pcszFile = szFullPath;
  169. return TRUE;
  170. }
  171. }
  172. // If the user's os ui language is different from the os's ui language. try help.xxxx where xxx is the langid of the ui. (NT 5 only)
  173. LANGID uilangid = _Module.m_Language.GetUserOsUiLanguage() ;
  174. if (uilangid)
  175. {
  176. // SM 8021: Avoid possible buffer overrun by allocating sufficient memory (was [5]).
  177. char szLangId[15] ;
  178. wsprintf(szLangId,"\\mui\\%04x", uilangid);
  179. GetRegWindowsDirectory(szFullPath);
  180. AddTrailingBackslash(szFullPath);
  181. strcat(szFullPath, txtHlpDir);
  182. strcat(szFullPath, szLangId) ; // Tack on the langid.
  183. AddTrailingBackslash(szFullPath);
  184. strcat(szFullPath, pszName);
  185. if (GetFileAttributes(szFullPath) != (DWORD) -1)
  186. {
  187. if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS)
  188. s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), szFullPath);
  189. *pcszFile = szFullPath;
  190. return TRUE;
  191. }
  192. }
  193. // Next, try the registered Windows\help directory
  194. GetRegWindowsDirectory(szFullPath);
  195. AddTrailingBackslash(szFullPath);
  196. strcat(szFullPath, txtHlpDir);
  197. AddTrailingBackslash(szFullPath);
  198. strcat(szFullPath, pszName);
  199. if (GetFileAttributes(szFullPath) != (DWORD) -1)
  200. {
  201. if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS)
  202. s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), szFullPath);
  203. *pcszFile = szFullPath;
  204. return TRUE;
  205. }
  206. // Next, try the Windows\help directory
  207. GetSystemDirectory(szFullPath, MAX_PATH + MAX_MESSAGE);
  208. i = (int)strlen(szFullPath);
  209. char *p = &szFullPath[i-1];
  210. while (p > szFullPath && *p != '\\')
  211. {
  212. *p = NULL;
  213. p--;
  214. }
  215. strcat(szFullPath, txtHlpDir);
  216. AddTrailingBackslash(szFullPath);
  217. strcat(szFullPath, pszName);
  218. if (GetFileAttributes(szFullPath) != (DWORD) -1)
  219. {
  220. if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS)
  221. s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), szFullPath);
  222. *pcszFile = szFullPath;
  223. return TRUE;
  224. }
  225. // Next, try hh.ini
  226. if (GetPrivateProfileString(txtFiles, pszName, txtZeroLength, szFullPath,
  227. sizeof(szFullPath), txtHhIni) > 1) {
  228. /*--------------------------------------------------------------------*\
  229. | The original profile string looks something like this
  230. | a:\setup\helpfiles,Please place fred's disk in drive A:
  231. | ^
  232. | We transform this to look like:
  233. | a:\setup\helpfiles\foobar.hlp Please place fred's disk in drive A:
  234. | \_________________/\________/^\__________________________________/^
  235. | MAX_PATH cchFileName 1 MAX_MESSAGE 1
  236. |
  237. \*--------------------------------------------------------------------*/
  238. PCSTR pszMsg = FirstNonSpace(pcszFile->GetArg(szFullPath));
  239. if (GetFileAttributes(*pcszFile) != (DWORD) -1) {
  240. return TRUE;
  241. }
  242. if (fAskUser && !IsEmptyString(pszMsg)) {
  243. do {
  244. if (MessageBox(hwndParent, pszMsg, _Resource.MsgBoxTitle(),
  245. MB_OKCANCEL | MB_TASKMODAL | MB_ICONHAND |
  246. g_fuBiDiMessageBox) != IDOK)
  247. break;
  248. } while (GetFileAttributes(*pcszFile) == (DWORD) -1);
  249. if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS)
  250. s_Data.GetFoundFiles()->AddString(HashFromSz(*pcszFile), szFullPath);
  251. return TRUE;
  252. }
  253. }
  254. #if 0
  255. if (!fAskUser)
  256. {
  257. // Tell the API callers that we couldn't find the help file.
  258. g_LastError.Set(idProcess, HH_E_FILENOTFOUND) ;
  259. return FALSE;
  260. }
  261. // VS98 Bugs 15405. There are numerous bugs assigned against the following dialog
  262. // box. We have much more important bugs to fix. Therefore, let's not display the
  263. // dialog. Here are the bugs:
  264. // Does not work correctly with COL files.
  265. // User can change from COL to CHM file or vice versa.
  266. // User can change name of CHM file which breaks the registry entry.
  267. // There are no filters in this dialog.
  268. // NOTE: Most callers assume that the name/path does not change!!! However, it can.
  269. /*
  270. * At this point, we don't know where the heck this file is, so let's
  271. * get the user to find it for us.
  272. */
  273. wsprintf(szFullPath, GetStringResource(IDS_FIND_YOURSELF), pszName);
  274. if (MessageBox(hwndParent,
  275. szFullPath, _Resource.MsgBoxTitle(), MB_YESNO | MB_ICONQUESTION | g_fuBiDiMessageBox) != IDYES)
  276. return FALSE;
  277. // Save pszName in case pszFile == pcszFile->psz
  278. CStr cszName(pszName);
  279. if (DlgOpenFile(hwndParent, pszFile, pcszFile)) {
  280. DWORD disposition;
  281. PSTR psz = (PSTR) FindFilePortion(*pcszFile);
  282. if (psz) {
  283. char ch = *psz;
  284. *psz = '\0';
  285. // Add it to the registry so we can find it next time
  286. if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, txtHelpDirKey, 0,
  287. txtDirectoryClass, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
  288. NULL, &hkey, &disposition) == ERROR_SUCCESS) {
  289. RegSetValueEx(hkey, cszName, 0, REG_SZ, (PBYTE) pcszFile->psz,
  290. strlen(*pcszFile) + 1);
  291. RegCloseKey(hkey);
  292. }
  293. *psz = ch;
  294. }
  295. if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS)
  296. s_Data.GetFoundFiles()->AddString(HashFromSz(*pcszFile), szFullPath);
  297. return TRUE;
  298. }
  299. #endif
  300. // Tell the API callers that we couldn't find the help file.
  301. //g_LastError.Set(idProcess, HH_E_FILENOTFOUND) ;// Need idProcess to set.
  302. return FALSE;
  303. }
  304. PCSTR FindFilePortion(PCSTR pszFile)
  305. {
  306. PCSTR psz = StrRChr(pszFile, '\\');
  307. if (psz)
  308. pszFile = psz + 1;
  309. psz = StrRChr(pszFile, '/');
  310. if (psz)
  311. return psz + 1;
  312. psz = StrRChr(pszFile, ':');
  313. return (psz ? psz + 1 : pszFile);
  314. }
  315. static const char txtGetOpenFile[] = "GetOpenFileNameA";
  316. static const char txtCommDlg[] = "comdlg32.dll";
  317. static const char txtEditHack[] = "Junk";
  318. typedef BOOL (WINAPI* GETOPENFILENAME)(LPOPENFILENAME popn);
  319. DWORD_PTR CALLBACK BrowseDialogProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
  320. /////////////////////////////////////////////////////////////////////////////
  321. // Browse Dialog
  322. DWORD_PTR CALLBACK BrowseDialogProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
  323. {
  324. switch( uMsg ) {
  325. case WM_INITDIALOG: {
  326. CenterWindow( GetParent( hDlg ), hDlg );
  327. //Let's hide these windows so the user cannot tab to them. Note that in
  328. //the private template (in cddemo.dlg) the coordinates for these guys are
  329. //*outside* the coordinates of the dlg window itself. Without the following
  330. //ShowWindow()'s you would not see them, but could still tab to them.
  331. ShowWindow( GetDlgItem( hDlg, stc2 ), SW_HIDE );
  332. ShowWindow( GetDlgItem( hDlg, stc3 ), SW_HIDE );
  333. ShowWindow( GetDlgItem( hDlg, edt1 ), SW_HIDE );
  334. ShowWindow( GetDlgItem( hDlg, lst1 ), SW_HIDE );
  335. ShowWindow( GetDlgItem( hDlg, cmb1 ), SW_HIDE );
  336. //We must put something in this field, even though it is hidden. This is
  337. //because if this field is empty, or has something like "*.txt" in it,
  338. //and the user hits OK, the dlg will NOT close. We'll jam something in
  339. //there (like "Junk") so when the user hits OK, the dlg terminates.
  340. //Note that we'll deal with the "Junk" during return processing (see below)
  341. SetDlgItemText( hDlg, edt1, txtEditHack );
  342. //Now set the focus to the directories listbox. Due to some painting
  343. //problems, we *must* also process the first WM_PAINT that comes through
  344. //and set the current selection at that point. Setting the selection
  345. //here will NOT work. See comment below in the on paint handler.
  346. SetFocus( GetDlgItem( hDlg, lst2 ) );
  347. return FALSE;
  348. }
  349. case WM_PAINT: {
  350. break;
  351. }
  352. #if 0
  353. case WM_COMMAND: {
  354. if( LOWORD(wParam) == IDOK ) {
  355. EndDialog( hDlg, LOWORD( wParam ) );
  356. }
  357. break;
  358. }
  359. #endif
  360. }
  361. return FALSE;
  362. }
  363. BOOL DlgOpenDirectory(HWND hwndParent, CStr* pcsz)
  364. {
  365. static HINSTANCE hmodule = NULL;
  366. if (hmodule || (hmodule = LoadLibrary(txtCommDlg)) != NULL) {
  367. static GETOPENFILENAME qfnbDlg = NULL;
  368. if (qfnbDlg || (qfnbDlg = (GETOPENFILENAME) GetProcAddress(hmodule,
  369. txtGetOpenFile)) != NULL) {
  370. OPENFILENAME ofn;
  371. char szPath[MAX_PATH];
  372. strcpy(szPath, "");
  373. for(;;) {
  374. ZeroMemory(&ofn, sizeof(ofn));
  375. ofn.lStructSize = sizeof ofn;
  376. ofn.hwndOwner = hwndParent;
  377. ofn.hInstance = _Module.GetResourceInstance();
  378. ofn.lpstrFile = szPath;
  379. ofn.nMaxFile = sizeof(szPath);
  380. ofn.lpTemplateName = MAKEINTRESOURCE( IDD_BROWSE );
  381. ofn.lpfnHook = BrowseDialogProc;
  382. ofn.Flags = OFN_HIDEREADONLY | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
  383. if (!qfnbDlg(&ofn))
  384. return FALSE;
  385. // remove our edit control hack text
  386. *(ofn.lpstrFile+strlen(ofn.lpstrFile)-strlen(txtEditHack)) = 0;
  387. *pcsz = ofn.lpstrFile;
  388. return TRUE;
  389. }
  390. }
  391. else
  392. return FALSE;
  393. }
  394. else
  395. return FALSE;
  396. }
  397. BOOL DlgOpenFile(HWND hwndParent, PCSTR pszFile, CStr* pcsz)
  398. {
  399. static HINSTANCE hmodule = NULL;
  400. if (hmodule || (hmodule = LoadLibrary(txtCommDlg)) != NULL) {
  401. static GETOPENFILENAME qfnbDlg = NULL;
  402. if (qfnbDlg || (qfnbDlg = (GETOPENFILENAME) GetProcAddress(hmodule,
  403. txtGetOpenFile)) != NULL) {
  404. OPENFILENAME ofn;
  405. char szPath[MAX_PATH];
  406. strcpy(szPath, pszFile);
  407. for(;;) {
  408. ZeroMemory(&ofn, sizeof(ofn));
  409. ofn.lStructSize = sizeof ofn;
  410. ofn.hwndOwner = hwndParent;
  411. ofn.hInstance = _Module.GetResourceInstance();
  412. ofn.lpstrFile = szPath;
  413. ofn.nMaxFile = sizeof(szPath);
  414. ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST |
  415. OFN_NOCHANGEDIR;
  416. if (!qfnbDlg(&ofn))
  417. return FALSE;
  418. *pcsz = ofn.lpstrFile;
  419. return TRUE;
  420. }
  421. }
  422. else
  423. return FALSE;
  424. }
  425. else
  426. return FALSE;
  427. }