519 lines
17 KiB
C++
519 lines
17 KiB
C++
// Copyright (C) Microsoft Corporation 1990-1997
|
|
|
|
#include "header.h"
|
|
#include "strtable.h"
|
|
#include <commdlg.h>
|
|
#include "system.h"
|
|
#include <dlgs.h>
|
|
#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;
|
|
}
|