windows-nt/Source/XPSP1/NT/enduser/stuff/hhctrl/fm.cpp
2020-09-26 16:20:57 +08:00

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;
}