// Copyright (C) Microsoft Corporation 1990-1997 #include "header.h" #include "strtable.h" #include #include "system.h" #include #include "resource.h" #include "cdlg.h" // Set the last error for the HTML Help API callers. #include "lasterr.h" #define MAX_MESSAGE 512 #define MAX_TABLE_STRINGS 1024 static const char txtHelpDirKey[] = "Software\\Microsoft\\Windows\\HTML Help"; static const char txtHhIni[] = "hh.ini"; static const char txtFiles[] = "files"; // Can't be const since RegCreateKeyEx() thinks it can modify this static char txtDirectoryClass[] = "Folder"; // persistent local memory class class CDataFM { public: CDataFM::CDataFM() { m_ptblFoundFiles = NULL; } CDataFM::~CDataFM() { if( m_ptblFoundFiles ) delete m_ptblFoundFiles; } CTable* GetFoundFiles(void) { if( !m_ptblFoundFiles ) m_ptblFoundFiles = new CTable(MAX_TABLE_STRINGS * 256); return m_ptblFoundFiles; } private: CTable* m_ptblFoundFiles; }; static CDataFM s_Data; /*************************************************************************** FUNCTION: GetRegWindowsDirectory PURPOSE: Equivalent to GetWindowsDirectory() only it checks the registration first for the proper location PARAMETERS: pszDst RETURNS: COMMENTS: MODIFICATION DATES: 04-Dec-1994 [ralphw] ***************************************************************************/ void GetRegWindowsDirectory(PSTR pszDstPath) { HHGetWindowsDirectory(pszDstPath, MAX_PATH); } /*************************************************************************** FUNCTION: FindThisFile PURPOSE: Searches for the specified file: 1) Searches the registry 2) Searches windows\help 3) Searches hh.ini PARAMETERS: pszFile -- input filename pcsz -- receives full path fAskUser -- TRUE to ask the user to find the file RETURNS: TRUE if the file was found COMMENTS: MODIFICATION DATES: 04-Dec-1994 [ralphw] 10-May-1997 [ralphw] -- ported from WinHelp code ***************************************************************************/ BOOL FindThisFile(HWND hwndParent, PCSTR pszFile, CStr* pcszFile, BOOL fAskUser /*= TRUE*/ ) { char szFullPath[MAX_PATH + MAX_MESSAGE]; LONG result = -1; HKEY hkey; DWORD type; CStr cszCompiledName; PCSTR pszName = GetCompiledName(pszFile, &cszCompiledName); /* * Extract just the filename portion of the path, and use that as the * lookup key in the registry. If found, the key value is the path to * use. */ pszName = FindFilePortion(pszName ? pszName : pszFile); if (!pszName) { // Set the last error. We couldn't find the file. //g_LastError.Set(idProcess, HH_E_FILENOTFOUND) ; // Need idProcess to set. return FALSE; } if( s_Data.GetFoundFiles() ) { HASH hash = HashFromSz(pszName); int pos = s_Data.GetFoundFiles()->IsHashInTable(hash); if (pos > 0) { *pcszFile = s_Data.GetFoundFiles()->GetHashStringPointer(pos); return TRUE; } } // Check all open CHM files for a match for (int i = 0; i < g_cHmSlots; i++) { // BUGBUG: enumerate all CExTitles in collection if (g_phmData[i] && stristr(g_phmData[i]->GetCompiledFile(), pszName)) { if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS) s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), g_phmData[i]->GetCompiledFile()); *pcszFile = g_phmData[i]->GetCompiledFile(); return TRUE; } } if (GetFileAttributes(cszCompiledName) != (DWORD) -1) { GetFullPathName(cszCompiledName, sizeof(szFullPath), szFullPath, NULL); *pcszFile = szFullPath; return TRUE; } // major hack for bug 5909 which is breaking VBA, MSE and external clients, the above BUGBUG // would also fix this but we don't have a reliable way to get all collections.. // look in the directories of the currently open files for this file char szTmp[MAX_PATH]; for (i = 0; i < g_cHmSlots; i++) { if (g_phmData[i]) { // get the path to this file and append the new file and check if it exists SplitPath((LPSTR)g_phmData[i]->GetCompiledFile(), szFullPath, szTmp, NULL, NULL); CatPath(szFullPath, szTmp); CatPath(szFullPath, pszName); if (GetFileAttributes(szFullPath) != HFILE_ERROR ) { *pcszFile = szFullPath; return TRUE; } } } // See if we have a Darwin GUID for this process ID BOOL bDone = FALSE; g_Busy.Set( TRUE ); if (g_pszDarwinGuid) { CStr cszPath; if (FindDarwinURL(g_pszDarwinGuid/*BOGUS g_phmData[i]->m_pszDarwinGuid*/, pszName, &cszPath)) { if (GetFileAttributes(cszPath) != (DWORD) -1) { PSTR ptr = strrchr(cszPath.psz, '\\'); *(++ptr) = '\0'; cszPath += pszName; if (GetFileAttributes(cszPath) != (DWORD) -1) { if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS) s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), cszPath); *pcszFile = cszPath.psz; bDone = TRUE; } } } } // If the first Darwin GUID can't be found, try the alternate GUID else if (g_pszDarwinBackupGuid) { CStr cszPath; if (FindDarwinURL(g_pszDarwinBackupGuid /*BOGUS: g_phmData[i]->m_pszDarwinBackupGuid*/, pszName, &cszPath)) { if (GetFileAttributes(cszPath) != (DWORD) -1) { if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS) s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), cszPath); *pcszFile = cszPath.psz; bDone = TRUE; } } } g_Busy.Set( FALSE ); if( bDone ) return bDone; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, txtHelpDirKey, 0, KEY_READ, &hkey) == ERROR_SUCCESS) { DWORD cbPath = MAX_PATH; result = RegQueryValueEx(hkey, pszName, 0, &type, (PBYTE) szFullPath, &cbPath); RegCloseKey(hkey); } if (result == ERROR_SUCCESS) { AddTrailingBackslash(szFullPath); strcat(szFullPath, pszName); if (GetFileAttributes(szFullPath) != (DWORD) -1) { if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS) s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), szFullPath); *pcszFile = szFullPath; return TRUE; } } // 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) LANGID uilangid = _Module.m_Language.GetUserOsUiLanguage() ; if (uilangid) { // SM 8021: Avoid possible buffer overrun by allocating sufficient memory (was [5]). char szLangId[15] ; wsprintf(szLangId,"\\mui\\%04x", uilangid); GetRegWindowsDirectory(szFullPath); AddTrailingBackslash(szFullPath); strcat(szFullPath, txtHlpDir); strcat(szFullPath, szLangId) ; // Tack on the langid. AddTrailingBackslash(szFullPath); strcat(szFullPath, pszName); if (GetFileAttributes(szFullPath) != (DWORD) -1) { if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS) s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), szFullPath); *pcszFile = szFullPath; return TRUE; } } // Next, try the registered Windows\help directory GetRegWindowsDirectory(szFullPath); AddTrailingBackslash(szFullPath); strcat(szFullPath, txtHlpDir); AddTrailingBackslash(szFullPath); strcat(szFullPath, pszName); if (GetFileAttributes(szFullPath) != (DWORD) -1) { if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS) s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), szFullPath); *pcszFile = szFullPath; return TRUE; } // Next, try the Windows\help directory GetSystemDirectory(szFullPath, MAX_PATH + MAX_MESSAGE); i = (int)strlen(szFullPath); char *p = &szFullPath[i-1]; while (p > szFullPath && *p != '\\') { *p = NULL; p--; } strcat(szFullPath, txtHlpDir); AddTrailingBackslash(szFullPath); strcat(szFullPath, pszName); if (GetFileAttributes(szFullPath) != (DWORD) -1) { if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS) s_Data.GetFoundFiles()->AddString(HashFromSz(pszName), szFullPath); *pcszFile = szFullPath; return TRUE; } // Next, try hh.ini if (GetPrivateProfileString(txtFiles, pszName, txtZeroLength, szFullPath, sizeof(szFullPath), txtHhIni) > 1) { /*--------------------------------------------------------------------*\ | The original profile string looks something like this | a:\setup\helpfiles,Please place fred's disk in drive A: | ^ | We transform this to look like: | a:\setup\helpfiles\foobar.hlp Please place fred's disk in drive A: | \_________________/\________/^\__________________________________/^ | MAX_PATH cchFileName 1 MAX_MESSAGE 1 | \*--------------------------------------------------------------------*/ PCSTR pszMsg = FirstNonSpace(pcszFile->GetArg(szFullPath)); if (GetFileAttributes(*pcszFile) != (DWORD) -1) { return TRUE; } if (fAskUser && !IsEmptyString(pszMsg)) { do { if (MessageBox(hwndParent, pszMsg, _Resource.MsgBoxTitle(), MB_OKCANCEL | MB_TASKMODAL | MB_ICONHAND | g_fuBiDiMessageBox) != IDOK) break; } while (GetFileAttributes(*pcszFile) == (DWORD) -1); if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS) s_Data.GetFoundFiles()->AddString(HashFromSz(*pcszFile), szFullPath); return TRUE; } } #if 0 if (!fAskUser) { // Tell the API callers that we couldn't find the help file. g_LastError.Set(idProcess, HH_E_FILENOTFOUND) ; return FALSE; } // VS98 Bugs 15405. There are numerous bugs assigned against the following dialog // box. We have much more important bugs to fix. Therefore, let's not display the // dialog. Here are the bugs: // Does not work correctly with COL files. // User can change from COL to CHM file or vice versa. // User can change name of CHM file which breaks the registry entry. // There are no filters in this dialog. // NOTE: Most callers assume that the name/path does not change!!! However, it can. /* * At this point, we don't know where the heck this file is, so let's * get the user to find it for us. */ wsprintf(szFullPath, GetStringResource(IDS_FIND_YOURSELF), pszName); if (MessageBox(hwndParent, szFullPath, _Resource.MsgBoxTitle(), MB_YESNO | MB_ICONQUESTION | g_fuBiDiMessageBox) != IDYES) return FALSE; // Save pszName in case pszFile == pcszFile->psz CStr cszName(pszName); if (DlgOpenFile(hwndParent, pszFile, pcszFile)) { DWORD disposition; PSTR psz = (PSTR) FindFilePortion(*pcszFile); if (psz) { char ch = *psz; *psz = '\0'; // Add it to the registry so we can find it next time if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, txtHelpDirKey, 0, txtDirectoryClass, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, &disposition) == ERROR_SUCCESS) { RegSetValueEx(hkey, cszName, 0, REG_SZ, (PBYTE) pcszFile->psz, strlen(*pcszFile) + 1); RegCloseKey(hkey); } *psz = ch; } if (s_Data.GetFoundFiles()->CountStrings() < MAX_TABLE_STRINGS) s_Data.GetFoundFiles()->AddString(HashFromSz(*pcszFile), szFullPath); return TRUE; } #endif // Tell the API callers that we couldn't find the help file. //g_LastError.Set(idProcess, HH_E_FILENOTFOUND) ;// Need idProcess to set. return FALSE; } PCSTR FindFilePortion(PCSTR pszFile) { PCSTR psz = StrRChr(pszFile, '\\'); if (psz) pszFile = psz + 1; psz = StrRChr(pszFile, '/'); if (psz) return psz + 1; psz = StrRChr(pszFile, ':'); return (psz ? psz + 1 : pszFile); } static const char txtGetOpenFile[] = "GetOpenFileNameA"; static const char txtCommDlg[] = "comdlg32.dll"; static const char txtEditHack[] = "Junk"; typedef BOOL (WINAPI* GETOPENFILENAME)(LPOPENFILENAME popn); DWORD_PTR CALLBACK BrowseDialogProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); ///////////////////////////////////////////////////////////////////////////// // Browse Dialog DWORD_PTR CALLBACK BrowseDialogProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch( uMsg ) { case WM_INITDIALOG: { CenterWindow( GetParent( hDlg ), hDlg ); //Let's hide these windows so the user cannot tab to them. Note that in //the private template (in cddemo.dlg) the coordinates for these guys are //*outside* the coordinates of the dlg window itself. Without the following //ShowWindow()'s you would not see them, but could still tab to them. ShowWindow( GetDlgItem( hDlg, stc2 ), SW_HIDE ); ShowWindow( GetDlgItem( hDlg, stc3 ), SW_HIDE ); ShowWindow( GetDlgItem( hDlg, edt1 ), SW_HIDE ); ShowWindow( GetDlgItem( hDlg, lst1 ), SW_HIDE ); ShowWindow( GetDlgItem( hDlg, cmb1 ), SW_HIDE ); //We must put something in this field, even though it is hidden. This is //because if this field is empty, or has something like "*.txt" in it, //and the user hits OK, the dlg will NOT close. We'll jam something in //there (like "Junk") so when the user hits OK, the dlg terminates. //Note that we'll deal with the "Junk" during return processing (see below) SetDlgItemText( hDlg, edt1, txtEditHack ); //Now set the focus to the directories listbox. Due to some painting //problems, we *must* also process the first WM_PAINT that comes through //and set the current selection at that point. Setting the selection //here will NOT work. See comment below in the on paint handler. SetFocus( GetDlgItem( hDlg, lst2 ) ); return FALSE; } case WM_PAINT: { break; } #if 0 case WM_COMMAND: { if( LOWORD(wParam) == IDOK ) { EndDialog( hDlg, LOWORD( wParam ) ); } break; } #endif } return FALSE; } BOOL DlgOpenDirectory(HWND hwndParent, CStr* pcsz) { static HINSTANCE hmodule = NULL; if (hmodule || (hmodule = LoadLibrary(txtCommDlg)) != NULL) { static GETOPENFILENAME qfnbDlg = NULL; if (qfnbDlg || (qfnbDlg = (GETOPENFILENAME) GetProcAddress(hmodule, txtGetOpenFile)) != NULL) { OPENFILENAME ofn; char szPath[MAX_PATH]; strcpy(szPath, ""); for(;;) { ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof ofn; ofn.hwndOwner = hwndParent; ofn.hInstance = _Module.GetResourceInstance(); ofn.lpstrFile = szPath; ofn.nMaxFile = sizeof(szPath); ofn.lpTemplateName = MAKEINTRESOURCE( IDD_BROWSE ); ofn.lpfnHook = BrowseDialogProc; ofn.Flags = OFN_HIDEREADONLY | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK; if (!qfnbDlg(&ofn)) return FALSE; // remove our edit control hack text *(ofn.lpstrFile+strlen(ofn.lpstrFile)-strlen(txtEditHack)) = 0; *pcsz = ofn.lpstrFile; return TRUE; } } else return FALSE; } else return FALSE; } BOOL DlgOpenFile(HWND hwndParent, PCSTR pszFile, CStr* pcsz) { static HINSTANCE hmodule = NULL; if (hmodule || (hmodule = LoadLibrary(txtCommDlg)) != NULL) { static GETOPENFILENAME qfnbDlg = NULL; if (qfnbDlg || (qfnbDlg = (GETOPENFILENAME) GetProcAddress(hmodule, txtGetOpenFile)) != NULL) { OPENFILENAME ofn; char szPath[MAX_PATH]; strcpy(szPath, pszFile); for(;;) { ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof ofn; ofn.hwndOwner = hwndParent; ofn.hInstance = _Module.GetResourceInstance(); ofn.lpstrFile = szPath; ofn.nMaxFile = sizeof(szPath); ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; if (!qfnbDlg(&ofn)) return FALSE; *pcsz = ofn.lpstrFile; return TRUE; } } else return FALSE; } else return FALSE; }