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

3718 lines
105 KiB
C++

// Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved.
#include "header.h"
#include "secwin.h"
#include "strtable.h"
#include "hha_strtable.h"
#include "htmlhelp.h"
#include <commctrl.h>
#include "sitemap.h" // ReplaceEscapes
#include <shlobj.h> // CSIDL_X defs
// global window type array.
#include "gwintype.h"
#include "cstr.h"
#ifndef _DEBUG
#undef THIS_FILE
static const char THIS_FILE[] = __FILE__;
#endif
#ifndef _DEBUG
#pragma optimize("a", on)
#endif
#define PT_TO_PIXELS(hdc, pt) MulDiv(-(pt), GetDeviceCaps(hdc, LOGPIXELSY), 72)
static POINT GetTextSize(HDC hdc, PCSTR qchBuf, int iCount);
static POINT GetTextSizeW(HDC hdc, WCHAR *qchBuf, int iCount);
BOOL IsDigitW(WCHAR ch) { return (ch >= L'0' && ch <= 'L9'); }
int IEColorToWin32Color( PCWSTR pwsz )
{
// input: "#rrggbb"
// output: int in hex form of 0xbbggrr
char sz[9];
strcpy( sz, "0x" );
// get blue
WideCharToMultiByte( CP_ACP, 0, pwsz+5, 2, &(sz[2]), 2, NULL, NULL );
// get green
WideCharToMultiByte( CP_ACP, 0, pwsz+3, 2, &(sz[4]), 2, NULL, NULL );
// get red
WideCharToMultiByte( CP_ACP, 0, pwsz+1, 2, &(sz[6]), 2, NULL, NULL );
// null terminate the string
sz[8] = 0;
// convert it
return Atoi( sz );
}
//=--------------------------------------------------------------------------=
// MakeWideFromAnsi
//=--------------------------------------------------------------------------=
// given a string, make a BSTR out of it.
//
// Parameters:
// LPSTR - [in]
// BYTE - [in]
//
// Output:
// LPWSTR - needs to be cast to final desired result
//
// Notes:
//
LPWSTR MakeWideStrFromAnsi(LPSTR psz, BYTE bType)
{
LPWSTR pwsz;
int i;
ASSERT(psz)
// compute the length of the required BSTR
i = MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0);
if (i <= 0)
return NULL;
// allocate the widestr
switch (bType) {
case STR_BSTR:
// -1 since it'll add it's own space for a NULL terminator
pwsz = (LPWSTR) SysAllocStringLen(NULL, i - 1);
break;
case STR_OLESTR:
pwsz = (LPWSTR) CoTaskMemAlloc(i * sizeof(WCHAR));
break;
default:
FAIL("Bogus String Type.");
}
if (!pwsz)
return NULL;
MultiByteToWideChar(CP_ACP, 0, psz, -1, pwsz, i);
pwsz[i - 1] = 0;
return pwsz;
}
LPWSTR MakeWideStr(LPSTR psz, UINT codepage)
{
LPWSTR pwsz;
int i;
ASSERT(psz)
// compute the length of the required BSTR
i = MultiByteToWideChar(codepage, 0, psz, -1, NULL, 0);
if (i <= 0)
return NULL;
++i;
pwsz = (WCHAR *) malloc(i * sizeof(WCHAR));
if (!pwsz)
return NULL;
MultiByteToWideChar(codepage, 0, psz, -1, pwsz, i);
return pwsz;
}
//=--------------------------------------------------------------------------=
// MakeWideStrFromResId
//=--------------------------------------------------------------------------=
// given a resource ID, load it, and allocate a wide string for it.
//
// Parameters:
// WORD - [in] resource id.
// BYTE - [in] type of string desired.
//
// Output:
// LPWSTR - needs to be cast to desired string type.
//
// Notes:
//
LPWSTR MakeWideStrFromResourceId (WORD wId, BYTE bType)
{
int i;
char szTmp[512];
// load the string from the resources.
//
i = LoadString(_Module.GetResourceInstance(), wId, szTmp, 512);
if (!i)
return NULL;
return MakeWideStrFromAnsi(szTmp, bType);
}
//=--------------------------------------------------------------------------=
// MakeWideStrFromWide
//=--------------------------------------------------------------------------=
// given a wide string, make a new wide string with it of the given type.
//
// Parameters:
// LPWSTR - [in] current wide str.
// BYTE - [in] desired type of string.
//
// Output:
// LPWSTR
//
// Notes:
//
LPWSTR MakeWideStrFromWide
(
LPWSTR pwsz,
BYTE bType
)
{
LPWSTR pwszTmp;
int i;
if (!pwsz) return NULL;
// just copy the string, depending on what type they want.
//
switch (bType) {
case STR_OLESTR:
i = lstrlenW(pwsz);
pwszTmp = (LPWSTR)CoTaskMemAlloc((i * sizeof(WCHAR)) + sizeof(WCHAR));
if (!pwszTmp) return NULL;
memcpy(pwszTmp, pwsz, (sizeof(WCHAR) * i) + sizeof(WCHAR));
break;
case STR_BSTR:
pwszTmp = (LPWSTR)SysAllocString(pwsz);
break;
}
return pwszTmp;
}
//=--------------------------------------------------------------------------=
// StringFromGuidA
//=--------------------------------------------------------------------------=
// returns an ANSI string from a CLSID or GUID
//
// Parameters:
// REFIID - [in] clsid to make string out of.
// LPSTR - [in] buffer in which to place resultant GUID.
//
// Output:
// int - number of chars written out.
//
// Notes:
//
int StringFromGuidA
(
REFIID riid,
LPSTR pszBuf
)
{
return wsprintf((char *)pszBuf, "{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", riid.Data1,
riid.Data2, riid.Data3, riid.Data4[0], riid.Data4[1], riid.Data4[2],
riid.Data4[3], riid.Data4[4], riid.Data4[5], riid.Data4[6], riid.Data4[7]);
}
//=--------------------------------------------------------------------------=
// RegisterUnknownObject
//=--------------------------------------------------------------------------=
// registers a simple CoCreatable object. nothing terribly serious.
// we add the following information to the registry:
//
// HKEY_CLASSES_ROOT\CLSID\<CLSID> = <ObjectName> Object
// HKEY_CLASSES_ROOT\CLSID\<CLSID>\InprocServer32 = <path to local server>
//
// Parameters:
// LPCSTR - [in] Object Name
// REFCLSID - [in] CLSID of the object
//
// Output:
// BOOL - FALSE means couldn't register it all
BOOL RegisterUnknownObject(LPCSTR pszObjectName, REFCLSID riidObject)
{
HKEY hk = NULL, hkSub = NULL;
char szGuidStr[GUID_STR_LEN];
DWORD dwPathLen, dwDummy;
char szScratch[MAX_PATH];
long l;
// clean out any garbage
UnregisterUnknownObject(riidObject);
// HKEY_CLASSES_ROOT\CLSID\<CLSID> = <ObjectName> Object
// HKEY_CLASSES_ROOT\CLSID\<CLSID>\InprocServer32 = <path to local server>
// HKEY_CLASSES_ROOT\CLSID\<CLSID>\InprocServer32 @ThreadingModel = Apartment
//
if (!StringFromGuidA(riidObject, szGuidStr)) goto CleanUp;
wsprintf(szScratch, "CLSID\\%s", szGuidStr);
l = RegCreateKeyEx(HKEY_CLASSES_ROOT, szScratch, 0, "", REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE, NULL, &hk, &dwDummy);
CLEANUP_ON_ERROR(l);
wsprintf(szScratch, "%s Object", pszObjectName);
l = RegSetValueEx(hk, NULL, 0, REG_SZ, (BYTE *)szScratch, (ULONG)strlen(szScratch) + 1);
CLEANUP_ON_ERROR(l);
l = RegCreateKeyEx(hk, "InprocServer32", 0, "", REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE, NULL, &hkSub, &dwDummy);
CLEANUP_ON_ERROR(l);
dwPathLen = GetModuleFileName(_Module.GetModuleInstance(), szScratch, sizeof(szScratch));
if (!dwPathLen)
goto CleanUp;
l = RegSetValueEx(hkSub, NULL, 0, REG_SZ, (BYTE *)szScratch, dwPathLen + 1);
CLEANUP_ON_ERROR(l);
l = RegSetValueEx(hkSub, "ThreadingModel", 0, REG_SZ, (BYTE *)"Apartment", sizeof("Apartment"));
CLEANUP_ON_ERROR(l);
RegCloseKey(hkSub);
RegCloseKey(hk);
return TRUE;
CleanUp:
if (hk)
RegCloseKey(hk);
if (hkSub)
RegCloseKey(hkSub);
return FALSE;
}
//=--------------------------------------------------------------------------=
// RegisterAutomationObject
//=--------------------------------------------------------------------------=
// given a little bit of information about an automation object, go and put it
// in the registry.
// we add the following information in addition to that set up in
// RegisterUnknownObject:
//
//
// HKEY_CLASSES_ROOT\<LibraryName>.<ObjectName> = <ObjectName> Object
// HKEY_CLASSES_ROOT\<LibraryName>.<ObjectName>\CLSID = <CLSID>
// HKEY_CLASSES_ROOT\<LibraryName>.<ObjectName>\CurVer = <ObjectName>.Object.<VersionNumber>
//
// HKEY_CLASSES_ROOT\<LibraryName>.<ObjectName>.<VersionNumber> = <ObjectName> Object
// HKEY_CLASSES_ROOT\<LibraryName>.<ObjectName>.<VersionNumber>\CLSID = <CLSID>
//
// HKEY_CLASSES_ROOT\CLSID\<CLSID>\TypeLib = <LibidOfTypeLibrary>
// HKEY_CLASSES_ROOT\CLSID\<CLSID>\ProgID = <LibraryName>.<ObjectName>.<VersionNumber>
// HKEY_CLASSES_ROOT\CLSID\<CLSID>\VersionIndependentProgID = <LibraryName>.<ObjectName>
//
// Parameters:
// LPCSTR - [in] Library Name
// LPCSTR - [in] Object Name
// long - [in] Version Number
// REFCLSID - [in] LIBID of type library
// REFCLSID - [in] CLSID of the object
//
// Output:
// BOOL - FALSE means not all of it was registered
BOOL RegisterAutomationObject
(
LPCSTR pszLibName,
LPCSTR pszObjectName,
long lVersion,
REFCLSID riidLibrary,
REFCLSID riidObject
)
{
HKEY hk = NULL, hkSub = NULL;
char szGuidStr[GUID_STR_LEN];
char szScratch[MAX_PATH];
long l;
DWORD dwDummy;
// first register the simple Unknown stuff.
//
if (!RegisterUnknownObject(pszObjectName, riidObject)) return FALSE;
// HKEY_CLASSES_ROOT\<LibraryName>.<ObjectName> = <ObjectName> Object
// HKEY_CLASSES_ROOT\<LibraryName>.<ObjectName>\CLSID = <CLSID>
// HKEY_CLASSES_ROOT\<LibraryName>.<ObjectName>\CurVer = <ObjectName>.Object.<VersionNumber>
//
strcpy(szScratch, pszLibName);
strcat(szScratch, ".");
strcat(szScratch, pszObjectName);
l = RegCreateKeyEx(HKEY_CLASSES_ROOT, szScratch, 0L, "",
REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
NULL, &hk, &dwDummy);
CLEANUP_ON_ERROR(l);
wsprintf(szScratch, "%s Object", pszObjectName);
l = RegSetValueEx(hk, NULL, 0L, REG_SZ, (BYTE *)szScratch, (ULONG)strlen(szScratch)+1);
CLEANUP_ON_ERROR(l);
l = RegCreateKeyEx(hk, "CLSID", 0L, "", REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE, NULL, &hkSub, &dwDummy);
CLEANUP_ON_ERROR(l);
if (!StringFromGuidA(riidObject, szGuidStr))
goto CleanUp;
l = RegSetValueEx(hkSub, NULL, 0L, REG_SZ, (BYTE *)szGuidStr, (ULONG)strlen(szGuidStr) + 1);
CLEANUP_ON_ERROR(l);
RegCloseKey(hkSub);
l = RegCreateKeyEx(hk, "CurVer", 0, "", REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE, NULL, &hkSub, &dwDummy);
CLEANUP_ON_ERROR(l);
wsprintf(szScratch, "%s.%s.%ld", pszLibName, pszObjectName, lVersion);
l = RegSetValueEx(hkSub, NULL, 0, REG_SZ, (BYTE *)szScratch, (ULONG)strlen(szScratch) + 1);
CLEANUP_ON_ERROR(l);
RegCloseKey(hkSub);
RegCloseKey(hk);
// HKEY_CLASSES_ROOT\<LibraryName>.<ObjectName>.<VersionNumber> = <ObjectName> Object
// HKEY_CLASSES_ROOT\<LibraryName>.<ObjectName>.<VersionNumber>\CLSID = <CLSID>
//
l = RegCreateKeyEx(HKEY_CLASSES_ROOT, szScratch, 0, "", REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE, NULL, &hk, &dwDummy);
CLEANUP_ON_ERROR(l);
wsprintf(szScratch, "%s Object", pszObjectName);
l = RegSetValueEx(hk, NULL, 0, REG_SZ, (BYTE *)szScratch, (ULONG)strlen(szScratch) + 1);
CLEANUP_ON_ERROR(l);
l = RegCreateKeyEx(hk, "CLSID", 0, "", REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE, NULL, &hkSub, &dwDummy);
CLEANUP_ON_ERROR(l);
l = RegSetValueEx(hkSub, NULL, 0, REG_SZ, (BYTE *)szGuidStr, (ULONG)strlen(szGuidStr) + 1);
CLEANUP_ON_ERROR(l);
RegCloseKey(hkSub);
RegCloseKey(hk);
// HKEY_CLASSES_ROOT\CLSID\<CLSID>\ProgID = <LibraryName>.<ObjectName>.<VersionNumber>
// HKEY_CLASSES_ROOT\CLSID\<CLSID>\VersionIndependentProgID = <LibraryName>.<ObjectName>
// HKEY_CLASSES_ROOT\CLSID\<CLSID>\TypeLib = <LibidOfTypeLibrary>
//
if (!StringFromGuidA(riidObject, szGuidStr)) goto CleanUp;
wsprintf(szScratch, "CLSID\\%s", szGuidStr);
l = RegCreateKeyEx(HKEY_CLASSES_ROOT, szScratch, 0, "", REG_OPTION_NON_VOLATILE,
KEY_READ|KEY_WRITE, NULL, &hk, &dwDummy);
CLEANUP_ON_ERROR(l);
l = RegCreateKeyEx(hk, "VersionIndependentProgID", 0, "", REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE, NULL, &hkSub, &dwDummy);
CLEANUP_ON_ERROR(l);
wsprintf(szScratch, "%s.%s", pszLibName, pszObjectName);
l = RegSetValueEx(hkSub, NULL, 0, REG_SZ, (BYTE *)szScratch, (ULONG)strlen(szScratch) + 1);
CLEANUP_ON_ERROR(l);
RegCloseKey(hkSub);
l = RegCreateKeyEx(hk, "ProgID", 0, "", REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE, NULL, &hkSub, &dwDummy);
CLEANUP_ON_ERROR(l);
wsprintf(szScratch, "%s.%s.%ld", pszLibName, pszObjectName, lVersion);
l = RegSetValueEx(hkSub, NULL, 0, REG_SZ, (BYTE *)szScratch, (ULONG)strlen(szScratch) + 1);
CLEANUP_ON_ERROR(l);
RegCloseKey(hkSub);
l = RegCreateKeyEx(hk, "TypeLib", 0, "", REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
NULL, &hkSub, &dwDummy);
if (!StringFromGuidA(riidLibrary, szGuidStr)) goto CleanUp;
l = RegSetValueEx(hkSub, NULL, 0, REG_SZ, (BYTE *)szGuidStr, (ULONG)strlen(szGuidStr) + 1);
CLEANUP_ON_ERROR(l);
RegCloseKey(hkSub);
RegCloseKey(hk);
return TRUE;
CleanUp:
if (hk) RegCloseKey(hkSub);
if (hk) RegCloseKey(hk);
return FALSE;
}
//=--------------------------------------------------------------------------=
// RegisterControlObject.
//=--------------------------------------------------------------------------=
// in addition to writing out automation object information, this function
// writes out some values specific to a control.
//
// What we add here:
//
// HKEY_CLASSES_ROOT\CLSID\<CLSID>\Control
// HKEY_CLASSES_ROOT\CLSID\<CLSID>\MiscStatus\1 = <MISCSTATUSBITS>
// HKEY_CLASSES_ROOT\CLSID\<CLSID>\ToolboxBitmap32 = <PATH TO BMP>
// HKEY_CLASSES_ROOT\CLSID\<CLSID>\Version = <VERSION>
//
// Parameters:
// LPCSTR - [in] Library Name
// LPCSTR - [in] Object Name
// long - [in] Version Number
// REFCLSID - [in] LIBID of type library
// REFCLSID - [in] CLSID of the object
// DWORD - [in] misc status flags for ctl
// WORD - [in] toolbox id for control
//
// Output:
// BOOL
//
// Notes:
// - not the most terribly efficient routine.
//
BOOL RegisterControlObject
(
LPCSTR pszLibName,
LPCSTR pszObjectName,
long lVersion,
REFCLSID riidLibrary,
REFCLSID riidObject,
DWORD dwMiscStatus,
WORD wToolboxBitmapId
)
{
HKEY hk, hkSub = NULL, hkSub2 = NULL;
char szTmp[MAX_PATH];
char szGuidStr[GUID_STR_LEN];
DWORD dwDummy;
LONG l;
// first register all the automation information for this sucker.
//
if (!RegisterAutomationObject(pszLibName, pszObjectName, lVersion, riidLibrary, riidObject)) return FALSE;
// then go and register the control specific stuff.
//
StringFromGuidA(riidObject, szGuidStr);
wsprintf(szTmp, "CLSID\\%s", szGuidStr);
l = RegOpenKeyEx(HKEY_CLASSES_ROOT, szTmp, 0, KEY_ALL_ACCESS, &hk);
if (l != ERROR_SUCCESS) return FALSE;
// create the control flag.
//
l = RegCreateKeyEx(hk, "Control", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkSub, &dwDummy);
CLEANUP_ON_ERROR(l);
// now set up the MiscStatus Bits...
//
RegCloseKey(hkSub);
hkSub = NULL;
l = RegCreateKeyEx(hk, "MiscStatus", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkSub, &dwDummy);
CLEANUP_ON_ERROR(l);
szTmp[0] = '0';
szTmp[1] = '\0';
l = RegSetValueEx(hkSub, NULL, 0, REG_SZ, (BYTE *)szTmp, 2);
CLEANUP_ON_ERROR(l);
l = RegCreateKeyEx(hkSub, "1", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkSub2, &dwDummy);
CLEANUP_ON_ERROR(l);
wsprintf(szTmp, "%d", dwMiscStatus);
l = RegSetValueEx(hkSub2, NULL, 0, REG_SZ, (BYTE *)szTmp, (ULONG)strlen(szTmp) + 1);
RegCloseKey(hkSub2);
CLEANUP_ON_ERROR(l);
RegCloseKey(hkSub);
// now set up the toolbox bitmap
//
GetModuleFileName(_Module.GetModuleInstance(), szTmp, MAX_PATH);
wsprintf(szGuidStr, ", %d", wToolboxBitmapId);
strcat(szTmp, szGuidStr);
l = RegCreateKeyEx(hk, "ToolboxBitmap32", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkSub, &dwDummy);
CLEANUP_ON_ERROR(l);
l = RegSetValueEx(hkSub, NULL, 0, REG_SZ, (BYTE *)szTmp, (ULONG)strlen(szTmp) + 1);
CLEANUP_ON_ERROR(l);
// now set up the version information
//
RegCloseKey(hkSub);
l = RegCreateKeyEx(hk, "Version", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkSub, &dwDummy);
CLEANUP_ON_ERROR(l);
wsprintf(szTmp, "%ld.0", lVersion);
l = RegSetValueEx(hkSub, NULL, 0, REG_SZ, (BYTE *)szTmp, (ULONG)strlen(szTmp) + 1);
CleanUp:
if (hk)
RegCloseKey(hk);
if (hkSub)
RegCloseKey(hkSub);
return (l == ERROR_SUCCESS) ? TRUE : FALSE;
}
//=--------------------------------------------------------------------------=
// UnregisterUnknownObject
//=--------------------------------------------------------------------------=
// cleans up all the stuff that RegisterUnknownObject puts in the
// registry.
//
// Parameters:
// REFCLSID - [in] CLSID of the object
//
// Output:
// BOOL - FALSE means not all of it was registered
//
// Notes:
// - WARNING: this routine will blow away all other keys under the CLSID
// for this object. mildly anti-social, but likely not a problem.
//
BOOL UnregisterUnknownObject
(
REFCLSID riidObject
)
{
char szScratch[MAX_PATH];
HKEY hk;
BOOL f;
long l;
// delete everybody of the form
// HKEY_CLASSES_ROOT\CLSID\<CLSID> [\] *
//
if (!StringFromGuidA(riidObject, szScratch))
return FALSE;
l = RegOpenKeyEx(HKEY_CLASSES_ROOT, "CLSID", 0, KEY_ALL_ACCESS, &hk);
if (l != ERROR_SUCCESS) return FALSE;
f = DeleteKeyAndSubKeys(hk, szScratch);
RegCloseKey(hk);
return f;
}
//=--------------------------------------------------------------------------=
// UnregisterAutomationObject
//=--------------------------------------------------------------------------=
// unregisters an automation object, including all of it's unknown object
// information.
//
// Parameters:
// LPCSTR - [in] Library Name
// LPCSTR - [in] Object Name
// long - [in] Version Number
// REFCLSID - [in] CLSID of the object
//
// Output:
// BOOL - FALSE means couldn't get it all unregistered.
//
// Notes:
//
BOOL UnregisterAutomationObject
(
LPCSTR pszLibName,
LPCSTR pszObjectName,
long lVersion,
REFCLSID riidObject
)
{
char szScratch[MAX_PATH];
BOOL f;
// first thing -- unregister Unknown information
//
f = UnregisterUnknownObject(riidObject);
if (!f) return FALSE;
// delete everybody of the form:
// HKEY_CLASSES_ROOT\<LibraryName>.<ObjectName> [\] *
//
wsprintf(szScratch, "%s.%s", pszLibName, pszObjectName);
f = DeleteKeyAndSubKeys(HKEY_CLASSES_ROOT, szScratch);
if (!f) return FALSE;
// delete everybody of the form
// HKEY_CLASSES_ROOT\<LibraryName>.<ObjectName>.<VersionNumber> [\] *
//
wsprintf(szScratch, "%s.%s.%ld", pszLibName, pszObjectName, lVersion);
f = DeleteKeyAndSubKeys(HKEY_CLASSES_ROOT, szScratch);
if (!f) return FALSE;
return TRUE;
}
//=--------------------------------------------------------------------------=
// UnregisterTypeLibrary
//=--------------------------------------------------------------------------=
// blows away the type library keys for a given libid.
//
// Parameters:
// REFCLSID - [in] libid to blow away.
//
// Output:
// BOOL - TRUE OK, FALSE bad.
//
// Notes:
// - WARNING: this function just blows away the entire type library section,
// including all localized versions of the type library. mildly anti-
// social, but not killer.
//
BOOL UnregisterTypeLibrary
(
REFCLSID riidLibrary
)
{
HKEY hk;
char szScratch[GUID_STR_LEN];
long l;
BOOL f;
// convert the libid into a string.
//
if (!StringFromGuidA(riidLibrary, szScratch))
return FALSE;
l = RegOpenKeyEx(HKEY_CLASSES_ROOT, "TypeLib", 0, KEY_ALL_ACCESS, &hk);
if (l != ERROR_SUCCESS) return FALSE;
f = DeleteKeyAndSubKeys(hk, szScratch);
RegCloseKey(hk);
return f;
}
//=--------------------------------------------------------------------------=
// DeleteKeyAndSubKeys
//=--------------------------------------------------------------------------=
// delete's a key and all of it's subkeys.
//
// Parameters:
// HKEY - [in] delete the descendant specified
// LPSTR - [in] i'm the descendant specified
//
// Output:
// BOOL - TRUE OK, FALSE baaaad.
//
// Notes:
// - I don't feel too bad about implementing this recursively, since the
// depth isn't likely to get all the great.
// - Despite the win32 docs claiming it does, RegDeleteKey doesn't seem to
// work with sub-keys under windows 95.
//
BOOL DeleteKeyAndSubKeys
(
HKEY hkIn,
LPSTR pszSubKey
)
{
HKEY hk;
char szTmp[MAX_PATH];
DWORD dwTmpSize;
long l;
BOOL f;
int x;
l = RegOpenKeyEx(hkIn, pszSubKey, 0, KEY_ALL_ACCESS, &hk);
if (l != ERROR_SUCCESS) return FALSE;
// loop through all subkeys, blowing them away.
//
f = TRUE;
x = 0;
while (f) {
dwTmpSize = MAX_PATH;
l = RegEnumKeyEx(hk, x, szTmp, &dwTmpSize, 0, NULL, NULL, NULL);
if (l != ERROR_SUCCESS) break;
f = DeleteKeyAndSubKeys(hk, szTmp);
x++;
}
// there are no subkeys left, [or we'll just generate an error and return FALSE].
// let's go blow this dude away.
//
RegCloseKey(hk);
l = RegDeleteKey(hkIn, pszSubKey);
return (l == ERROR_SUCCESS) ? TRUE : FALSE;
}
//=--------------------------------------------------------------------------=
// Conversion Routines
//=--------------------------------------------------------------------------=
// the following stuff is stuff used for the various conversion routines.
//
#define HIMETRIC_PER_INCH 2540
#define MAP_PIX_TO_LOGHIM(x,ppli) ( (HIMETRIC_PER_INCH*(x) + ((ppli)>>1)) / (ppli) )
#define MAP_LOGHIM_TO_PIX(x,ppli) ( ((ppli)*(x) + HIMETRIC_PER_INCH/2) / HIMETRIC_PER_INCH )
static int s_iXppli; // Pixels per logical inch along width
static int s_iYppli; // Pixels per logical inch along height
static BYTE s_fGotScreenMetrics; // Are above valid?
//=--------------------------------------------------------------------------=
// GetScreenMetrics
//=--------------------------------------------------------------------------=
// private function we call to set up various metrics the conversion routines
// will use.
//
// Notes:
// BUGBUG: 26-Sep-1997 [ralphw] This will fail miserably if the user changes
// screen resolutions. Doah!
static void GetScreenMetrics (void)
{
if (s_fGotScreenMetrics)
return;
// we want the metrics for the screen
HDC hdc = CreateIC("DISPLAY", NULL, NULL, NULL);
ASSERT(hdc);
s_iXppli = GetDeviceCaps(hdc, LOGPIXELSX);
s_iYppli = GetDeviceCaps(hdc, LOGPIXELSY);
DeleteDC( hdc );
s_fGotScreenMetrics = TRUE;
}
//=--------------------------------------------------------------------------=
// HiMetricToPixel
//=--------------------------------------------------------------------------=
// converts from himetric to Pixels.
//
// Parameters:
// const SIZEL * - [in] dudes in himetric
// SIZEL * - [out] size in pixels.
//
// Notes:
//
void HiMetricToPixel(const SIZEL * lpSizeInHiMetric, SIZE* lpSizeInPix)
{
GetScreenMetrics();
// We got logical HIMETRIC along the display, convert them to pixel units
lpSizeInPix->cx = MAP_LOGHIM_TO_PIX(lpSizeInHiMetric->cx, s_iXppli);
lpSizeInPix->cy = MAP_LOGHIM_TO_PIX(lpSizeInHiMetric->cy, s_iYppli);
}
//=--------------------------------------------------------------------------=
// PixelToHiMetric
//=--------------------------------------------------------------------------=
// converts from pixels to himetric.
//
// Parameters:
// const SIZEL * - [in] size in pixels
// SIZEL * - [out] size in himetric
void PixelToHiMetric(const SIZEL * lpSizeInPix, LPSIZEL lpSizeInHiMetric)
{
GetScreenMetrics();
// We got pixel units, convert them to logical HIMETRIC along the display
lpSizeInHiMetric->cx = MAP_PIX_TO_LOGHIM(lpSizeInPix->cx, s_iXppli);
lpSizeInHiMetric->cy = MAP_PIX_TO_LOGHIM(lpSizeInPix->cy, s_iYppli);
}
//=--------------------------------------------------------------------------=
// _MakePath
//=--------------------------------------------------------------------------=
// little helper routine for RegisterLocalizedTypeLibs and GetResourceHandle.
// not terrilby efficient or smart, but it's registration code, so we don't
// really care.
//
// Notes:
//
void _MakePath
(
LPSTR pszFull,
const char * pszName,
LPSTR pszOut
)
{
LPSTR psz;
LPSTR pszLast;
strcpy(pszOut, pszFull);
psz = pszLast = pszOut;
while (*psz) {
if (*psz == '\\')
pszLast = AnsiNext(psz);
psz = AnsiNext(psz);
}
// got the last \ character, so just go and replace the name.
//
strcpy(pszLast, pszName);
}
/***************************************************************************
FUNCTION: GetStringResource
PURPOSE: Copy a string resource to a buffer, returning a pointer
to that buffer
PARAMETERS:
idString -- resource id
RETURNS: Pointer to the string containing the buffer
COMMENTS:
Note that the same buffer is used each time. You will need to
duplicate the returned pointer if you need to call this function
again before the previous value is no longer needed.
***************************************************************************/
PCSTR GetStringResource(int idString)
{
static TCHAR pszStringBuf[MAX_STRING_RESOURCE_LEN + 1];
pszStringBuf[0] = 0;
// special case W2K
//
// The purpose of this code is to get the resource string in Unicode
// and then convert to ANSI using the UI codepage when running under W2K.
//
// The narrow GetStringResource() API fails under MUI configurations
// due to it using the default system locale rather than the UI locale
// for the conversion to ANSI.
//
if(g_bWinNT5)
{
const WCHAR *pswString = GetStringResourceW(idString);
static char pszAnsiStringBuf[MAX_STRING_RESOURCE_LEN + 1];
if(pswString)
{
DWORD cp = CodePageFromLCID(MAKELCID(_Module.m_Language.GetUiLanguage(),SORT_DEFAULT));
pszAnsiStringBuf[0] = 0;
WideCharToMultiByte (cp, 0, pswString, -1, pszAnsiStringBuf, sizeof(pszAnsiStringBuf), NULL, NULL);
return pszAnsiStringBuf;
}
else
return pszStringBuf;
}
if (LoadString(_Module.GetResourceInstance(), idString, pszStringBuf, MAX_STRING_RESOURCE_LEN) == 0) {
#ifdef _DEBUG
wsprintf(pszStringBuf, "invalid string id #%u", idString);
MsgBox(pszStringBuf);
#endif
pszStringBuf[0] = '\0';
}
return (PCSTR) pszStringBuf;
}
/***************************************************************************
FUNCTION: GetStringResource
PURPOSE: Copy a string resource to a buffer, returning a pointer
to that buffer
PARAMETERS:
idString -- resource id
hInstance -- resource instance
RETURNS: Pointer to the string containing the buffer
COMMENTS:
Note that the same buffer is used each time. You will need to
duplicate the returned pointer if you need to call this function
again before the previous value is no longer needed.
***************************************************************************/
PCSTR GetStringResource(int idString, HINSTANCE hInstance)
{
static TCHAR pszStringBuf[MAX_STRING_RESOURCE_LEN + 1];
pszStringBuf[0] = 0;
// special case W2K
//
// The purpose of this code is to get the resource string in Unicode
// and then convert to ANSI using the UI codepage when running under W2K.
//
// The narrow GetStringResource() API fails under MUI configurations
// due to it using the default system locale rather than the UI locale
// for the conversion to ANSI.
//
if(g_bWinNT5)
{
const WCHAR *pswString = GetStringResourceW(idString, hInstance);
static char pszAnsiStringBuf[MAX_STRING_RESOURCE_LEN + 1];
if(pswString)
{
DWORD cp = CodePageFromLCID(MAKELCID(_Module.m_Language.GetUiLanguage(),SORT_DEFAULT));
pszAnsiStringBuf[0] = 0;
WideCharToMultiByte (cp, 0, pswString, -1, pszAnsiStringBuf, sizeof(pszAnsiStringBuf), NULL, NULL);
return pszAnsiStringBuf;
}
else
return pszStringBuf;
}
if (LoadString(hInstance, idString, pszStringBuf, MAX_STRING_RESOURCE_LEN) == 0) {
#ifdef _DEBUG
wsprintf(pszStringBuf, "invalid string id #%u", idString);
MsgBox(pszStringBuf);
#endif
pszStringBuf[0] = '\0';
}
return (PCSTR) pszStringBuf;
}
PCWSTR GetStringResourceW(int idString)
{
static WCHAR pszStringBuf[MAX_STRING_RESOURCE_LEN + 1];
if (0 == ::LoadStringW(_Module.GetResourceInstance(), idString, pszStringBuf, MAX_STRING_RESOURCE_LEN))
{
if (ERROR_CALL_NOT_IMPLEMENTED == ::GetLastError())
{
PCSTR pszA = GetStringResource(idString);
MultiByteToWideChar(CP_ACP, 0, pszA, -1, pszStringBuf, MAX_STRING_RESOURCE_LEN);
}
else
{
ASSERT(0); // bad string id, probably
pszStringBuf[0] = '\0';
}
}
return (PCWSTR) pszStringBuf;
}
// GetStringResourceW
//
PCWSTR GetStringResourceW(int idString, HINSTANCE hInstance)
{
static WCHAR pszStringBuf[MAX_STRING_RESOURCE_LEN + 1];
if (0 == ::LoadStringW(hInstance, idString, pszStringBuf, MAX_STRING_RESOURCE_LEN))
{
if (ERROR_CALL_NOT_IMPLEMENTED == ::GetLastError())
{
PCSTR pszA = GetStringResource(idString, hInstance);
MultiByteToWideChar(CP_ACP, 0, pszA, -1, pszStringBuf, MAX_STRING_RESOURCE_LEN);
}
else
{
ASSERT(0); // bad string id, probably
pszStringBuf[0] = '\0';
}
}
return (PCWSTR) pszStringBuf;
}
/***************************************************************************
FUNCTION: Atoi
PURPOSE: Convert string to an integer
PARAMETERS:
psz
RETURNS:
COMMENTS:
Taken from C runtime code -- this code makes no function calls, and
assumes there is no such thing as a DBCS number. It will read either
decimal or hex (preceeded with an 0x)
MODIFICATION DATES:
04-Dec-1997 [ralphw]
15-Aug-1997 [ralphw] Support hex digits
***************************************************************************/
int FASTCALL Atoi(PCSTR psz)
{
// skip whitespace
while (*psz == ' ' || *psz == '\t')
++psz;
if (!*psz)
return 0;
int c = (int) (unsigned char) *psz++;
int sign = c; // save sign indication
if (c == '-' || c == '+')
c = (int) (unsigned char) *psz++; // skip sign
int total = 0;
if (c == '0' && psz[0] == 'x') {
psz++; // skip over the 'x'
c = (int) (unsigned char) *psz++; // skip sign
for (;;) {
if (c >= '0' && c <= '9') {
total = 16 * total + (c - '0');
c = (int)(unsigned char)*psz++;
}
else if (c >= 'a' && c <= 'f') {
total = 16 * total + ((c - 'a') + 10);
c = (int)(unsigned char)*psz++;
}
else if (c >= 'A' && c <= 'F') {
total = 16 * total + ((c - 'A') + 10);
c = (int)(unsigned char)*psz++;
}
else {
if (sign == '-')
return -total;
else
return total;
}
}
}
while (c >= '0' && c <= '9') {
total = 10 * total + (c - '0');
c = (int)(unsigned char)*psz++;
}
if (sign == '-')
return -total;
else
return total;
}
void OOM(void)
{
#ifdef _DEBUG
ASSERT_COMMENT(FALSE, "Out of memory.");
#else
char szMsg[256];
LoadString(_Module.GetResourceInstance(), IDS_OOM, szMsg, sizeof(szMsg));
// Task modal to allow other applications to run. Note that this will
// disable the caller's windows.
MessageBox(GetActiveWindow(), szMsg, "", MB_OK | MB_TASKMODAL | MB_ICONHAND);
ExitProcess((UINT) -1);
#endif
}
int MsgBox(int idString, UINT nType)
{
if(g_bWinNT5)
{
WCHAR *pszString;
WCHAR wcString[(MAX_STRING_RESOURCE_LEN + 1) + MAX_PATH + 64];
WCHAR wcTitle[(MAX_STRING_RESOURCE_LEN + 1) + MAX_PATH + 64];
wcTitle[0] = 0;
pszString = (WCHAR *) GetStringResourceW(idString);
if(!pszString)
{
#ifdef _DEBUG
char szMsg[(MAX_STRING_RESOURCE_LEN + 1) + MAX_PATH + 64];
wsprintf(szMsg, "invalid string id #%u", idString);
DBWIN(szMsg);
#endif
return 0;
}
else
wcscpy(wcString,pszString);
char *pszTitle = _Resource.MsgBoxTitle();
if(pszTitle)
{
DWORD cp = CodePageFromLCID(MAKELCID(_Module.m_Language.GetUiLanguage(),SORT_DEFAULT));
MultiByteToWideChar(cp, 0, pszTitle, -1, wcTitle, sizeof(wcTitle));
}
else
return 0;
return MessageBoxW(GetActiveWindow(), wcString, wcTitle, nType | g_fuBiDiMessageBox);
}
else
{
CStr pszTemp = (char *) GetStringResource(idString);
if(!pszTemp.psz)
{
#ifdef _DEBUG
char szMsg[(MAX_STRING_RESOURCE_LEN + 1) + MAX_PATH + 64];
wsprintf(szMsg, "invalid string id #%u", idString);
DBWIN(szMsg);
#endif
return 0;
}
return MessageBox(GetActiveWindow(), pszTemp, _Resource.MsgBoxTitle(), nType | g_fuBiDiMessageBox);
}
}
int MsgBox(PCSTR pszMsg, UINT nType)
{
return MessageBox(GetActiveWindow(), pszMsg, _Resource.MsgBoxTitle(), nType | g_fuBiDiMessageBox);
}
int MsgBox(int idFormatString, PCSTR pszSubString, UINT nType)
{
CStr csz(idFormatString, pszSubString);
return MessageBox(GetActiveWindow(), csz.psz, _Resource.MsgBoxTitle(), nType);
}
void AddTrailingBackslash(PSTR psz)
{
int sPos;
if (psz != NULL && *psz != '\0') {
if (g_fDBCSSystem) {
PSTR pszEnd = psz + strlen(psz);
if (*(CharPrev(psz, pszEnd)) != '\\' &&
*(CharPrev(psz, pszEnd)) != '/' &&
*(CharPrev(psz, pszEnd)) != ':') {
*pszEnd++ = '\\';
*pszEnd++ = '\0';
}
}
else {
sPos = (int)strlen(psz) - 1;
if (psz[sPos] != '\\' && psz[sPos] != '/' && psz[sPos] != ':') {
psz[sPos + 1] = '\\';
psz[sPos + 2] = '\0';
}
}
}
}
#if 0
/***************************************************************************
FUNCTION: StrChr
PURPOSE: DBCS-capable version of strchr
PARAMETERS:
pszString
ch
RETURNS: pointer to the character
COMMENTS: This can NOT find a DBCS character. It can only be used to
find a SBCS character imbedded in a DBCS character string.
MODIFICATION DATES:
29-Jul-1994 [ralphw]
***************************************************************************/
extern "C" PSTR StrChr(PCSTR pszString, char ch)
{
if (!g_fDBCSSystem)
return strchr(pszString, ch);
while (*pszString) {
while (IsDBCSLeadByte(*pszString))
pszString += 2;
if (*pszString == ch)
return (PSTR) pszString;
else if (!*pszString)
return NULL;
pszString++;
}
return NULL;
}
#endif
extern "C" PSTR StrRChr(PCSTR pszString, char ch)
{
PSTR psz = StrChr(pszString, ch);
PSTR pszLast;
if (!psz)
return NULL;
do {
pszLast = psz;
psz = StrChr(pszLast + 1, ch);
} while (psz);
return pszLast;
}
extern "C" PSTR FirstNonSpace(PCSTR psz)
{
// note, for various bits of code to work throughout the HH project,
// this function cannot return NULL if the first non-space does not exist,
// instead the caller may count on this function returning non-NULL and
// thus we need to pass back a pointer to the terminating NULL character
// instead of simply a NULL value.
if( !psz /*|| !*psz*/ )
return NULL;
if (g_fDBCSSystem) {
while (!IsDBCSLeadByte(*psz) && (*psz == ' ' || *psz == '\t'))
psz++;
return (PSTR) psz;
}
while(*psz == ' ' || *psz == '\t')
psz++;
return (PSTR) psz;
}
WCHAR *FirstNonSpaceW(WCHAR *psz)
{
// note, for various bits of code to work throughout the HH project,
// this function cannot return NULL if the first non-space does not exist,
// instead the caller may count on this function returning non-NULL and
// thus we need to pass back a pointer to the terminating NULL character
// instead of simply a NULL value.
if( !psz /*|| !*psz*/ )
return NULL;
while(*psz == L' ' || *psz == L'\t')
psz++;
return psz;
}
PSTR SzTrimSz(PSTR pszOrg)
{
if (!pszOrg)
return NULL;
// Skip over leading whitespace
if (g_fDBCSSystem) {
PSTR psz = pszOrg;
while (!IsDBCSLeadByte(*psz) && IsSpace(*psz))
psz++;
if (psz != pszOrg)
strcpy(pszOrg, psz);
}
else if (IsSpace(*pszOrg))
strcpy(pszOrg, FirstNonSpace(pszOrg));
RemoveTrailingSpaces(pszOrg);
return pszOrg;
}
void RemoveTrailingSpaces(PSTR pszString)
{
if (!g_fDBCSSystem) {
PSTR psz = pszString + strlen(pszString) - 1;
while (IsSpace(*psz)) {
if (--psz <= pszString) {
*pszString = '\0';
return;
}
}
psz[1] = '\0';
}
else {
/*
* Removing trailing spaces in DBCS requires stepping through
* from the beginning of the string since we can't know if a
* trailing space is really a space or the second byte of a lead
* byte.
*/
PSTR psz = pszString + strlen(pszString) - 1;
while (IsSpace(*psz) && psz > pszString + 2 &&
!IsDBCSLeadByte(psz[-1])) {
if (--psz <= pszString) {
*pszString = '\0';
return;
}
}
psz[1] = '\0';
}
}
/***************************************************************************
FUNCTION: GetLeftOfEquals
PURPOSE: Allocate a string and copy everything to the left of the
equal character to that string. If there is no equal character,
return NULL.
PARAMETERS:
pszString -- note that we do actually modify this string, but we
restore it before returning, hence we use PCSTR
so the compiler thinks it is unmodified.
RETURNS: Allocated memory or NULL
COMMENTS:
DBCS enabled. A backslash character may be used to escape (ignore)
the equals character.
MODIFICATION DATES:
07-Jul-1997 [ralphw]
***************************************************************************/
PSTR GetLeftOfEquals(PCSTR pszString)
{
PSTR pszEqual = (PSTR) FindEqCharacter(pszString);
if (!pszEqual)
return NULL;
*pszEqual = '\0';
PSTR pszLeft = (PSTR) lcStrDup(pszString);
RemoveTrailingSpaces(pszLeft);
*pszEqual = '=';
return pszLeft;
}
/***************************************************************************
FUNCTION: FindEqCharacter
PURPOSE: Return a pointer to the '=' character in a line
PARAMETERS:
pszLine
RETURNS: Pointer to '=' or NULL if there is no '='
COMMENTS:
We DO modify pszLine in spite of it's being marked as PCSTR, but
we always put it back the way we found it before returning. So, you
can't use this function on a string stored in a code segment.
MODIFICATION DATES:
10-Jul-1997 [ralphw]
***************************************************************************/
PCSTR FindEqCharacter(PCSTR pszLine)
{
PSTR pszEqual = (PSTR) pszLine;
for (;;) {
pszEqual = StrChr(pszEqual, '=');
if (!pszEqual)
return NULL;
*pszEqual = '\0';
/*
* We need to find out if the previous character was a backslash.
* You can't back up in a DBCS string, so we just start from the
* first and search for the last backslash. Don't be fooled into
* thinking CharPrev() will do the trick -- it will, but by doing
* the same thing we do here more efficiently (start from the
* beginning of the string).
*/
PSTR pszBackSlash = StrRChr(pszLine, '\\');
if (pszBackSlash && pszBackSlash == (pszEqual - 1)) {
*pszEqual = '='; // put the character back
pszEqual++;
continue; // keep looking;
}
*pszEqual = '='; // put the character back
return pszEqual;
}
}
/***************************************************************************
FUNCTION: Itoa
PURPOSE: Convert a positive interger to a base 10 string
PARAMETERS:
val
pszDst
RETURNS:
COMMENTS:
Taken from C runtime code, modified for speed and size
MODIFICATION DATES:
04-Dec-1997 [ralphw]
***************************************************************************/
void FASTCALL Itoa(int val, PSTR pszDst)
{
if (val < 0) {
*pszDst++ = '-';
val = abs(val);
}
PSTR firstdig = pszDst; // save pointer to first digit
do {
*pszDst++ = ((char) (val % 10)) + '0';
val /= 10; // get next digit
} while (val > 0);
/*
* We now have the digit of the number in the buffer, but in reverse
* order. Thus we reverse them now.
*/
*pszDst-- = '\0'; // terminate string; p points to last digit
do {
char temp = *pszDst;
*pszDst = *firstdig;
*firstdig = temp; // swap *p and *firstdig
--pszDst;
++firstdig; // advance to next two digits
} while (firstdig < pszDst); // repeat until halfway
}
#if 0
/***************************************************************************
FUNCTION: stristr
PURPOSE: Case-insensitive search for a sub string in a main string
PARAMETERS:
pszMain
pszSub
RETURNS:
COMMENTS:
Not tested
MODIFICATION DATES:
28-Mar-1994 [ralphw]
***************************************************************************/
// REVIEW: should replace this with a version that doesn't use CompareString,
// since this function is typically used to parse sitemap files -- the object
// names of a sitemap file will always be in english.
extern "C"
PSTR stristr(PCSTR pszMain, PCSTR pszSub)
{
if (!pszMain || !pszSub)
return NULL;
PSTR pszCur = (PSTR) pszMain;
char ch = ToLower(*pszSub);
int cb = strlen(pszSub);
if (g_fDBCSSystem) {
for (;;) {
while (ToLower(*pszCur) != ch && *pszCur)
pszCur = CharNext(pszCur);
if (!*pszCur)
return NULL;
if (CompareString(g_lcidSystem, NORM_IGNORECASE,
pszCur, cb, pszSub, cb) == 2)
return pszCur;
pszCur = CharNext(pszCur);
}
}
else {
for (;;) {
while (ToLower(*pszCur) != ch && *pszCur)
pszCur++;
if (!*pszCur)
return NULL;
if (CompareString(g_lcidSystem, NORM_IGNORECASE,
pszCur, cb, pszSub, cb) == 2)
return pszCur;
pszCur++;
}
}
}
#endif
// NOTE: this only works with Unicode strings
BOOL IsSamePrefix(PCWSTR pwszMainIn, PCWSTR pwszSubIn, int cchPrefix)
{
if( !pwszMainIn || !pwszSubIn )
return FALSE;
const WCHAR* pwszMain = pwszMainIn;
const WCHAR* pwszSub = pwszSubIn;
if( cchPrefix == -1 )
cchPrefix = lstrlenW(pwszSub);
// convert both to lowercase and then compare the first few characters
// in pwszSub to pwszMain
while( cchPrefix-- ) {
// if we hit the end of the strings, quit and return
// TRUE if both NULL or FALSE otherwise
if( !(*pwszSub) || !(*pwszMain) ) {
if( (*pwszSub) == (*pwszMain) )
return TRUE;
else
return FALSE;
}
WCHAR wchSub = *(pwszSub++);
WCHAR wchMain = *(pwszMain++);
CharLowerW( &wchSub );
CharLowerW( &wchMain );
// if not the same then quit and return FALSE
if( wchSub != wchMain )
return FALSE;
}
return TRUE;
}
// NOTE: this only works with ANSI strings
BOOL IsSamePrefix(PCSTR pszMain, PCSTR pszSub, int cbPrefix)
{
if (!pszMain || !pszSub)
return FALSE;
if (cbPrefix == -1)
cbPrefix = (int)strlen(pszSub);
int f, l;
while (cbPrefix--) {
if (((f = (BYTE) (*(pszMain++))) >= 'A') && (f <= 'Z'))
f -= 'A' - 'a';
if (((l = (BYTE) (*(pszSub++))) >= 'A') && (l <= 'Z'))
l -= 'A' - 'a';
if (f != l)
return FALSE;
else if (!f)
return (f == l);
}
return TRUE;
}
#ifndef _DEBUG
#pragma optimize("t", on)
#endif
int FASTCALL CompareIntPointers(const void *pval1, const void *pval2)
{
#ifdef _DEBUG
int val1 = *(int*) pval1;
int val2 = *(int*) pval2;
#endif
return *(int*) pval1 - *(int*) pval2;
}
/*
* this parameter defines the cutoff between using quick sort and insertion
* sort for arrays; arrays with lengths shorter or equal to the below value
* use insertion sort
*/
#define CUTOFF 8 // testing shows that this is good value
void INLINE Swap(void* pb1, void* pb2, UINT width)
{
BYTE tmp[256];
ASSERT(width < sizeof(tmp));
CopyMemory(tmp, pb1, width);
CopyMemory(pb1, pb2, width);
CopyMemory(pb2, tmp, width);
}
static void InsertionSort(char *lo, char *hi, unsigned width,
int (FASTCALL *compare)(const void *, const void *))
{
char *p, *max;
/*
* Note: in assertions below, i and j are alway inside original bound
* of array to sort.
*/
while (hi > lo) {
/* A[i] <= A[j] for i <= j, j > hi */
max = lo;
for (p = lo + width; p <= hi; p += width) {
/* A[i] <= A[max] for lo <= i < p */
if (compare(p, max) > 0) {
max = p;
}
/* A[i] <= A[max] for lo <= i <= p */
}
/* A[i] <= A[max] for lo <= i <= hi */
Swap(max, hi, width);
/* A[i] <= A[hi] for i <= hi, so A[i] <= A[j] for i <= j, j >= hi */
hi -= width;
// A[i] <= A[j] for i <= j, j > hi, loop top condition established
}
/* A[i] <= A[j] for i <= j, j > lo, which implies A[i] <= A[j] for i < j,
so array is sorted */
}
void QSort(void *pbase, UINT num, UINT width,
int (FASTCALL *compare)(const void *, const void *))
{
char *lo, *hi; // ends of sub-array currently sorting
char *mid; // points to middle of subarray
char *loguy, *higuy; // traveling pointers for partition step
unsigned size; // size of the sub-array
char *lostk[30], *histk[30];
int stkptr; // stack for saving sub-array to be processed
/* Note: the number of stack entries required is no more than
1 + log2(size), so 30 is sufficient for any array */
if (num < 2 || width == 0)
return; // nothing to do
stkptr = 0; // initialize stack
lo = (char*) pbase;
hi = (char *) pbase + width * (num-1); // initialize limits
/* this entry point is for pseudo-recursion calling: setting
lo and hi and jumping to here is like recursion, but stkptr is
prserved, locals aren't, so we preserve stuff on the stack */
recurse:
size = (unsigned)((hi - lo) / width + 1); // number of el's to sort
// below a certain size, it is faster to use a O(n^2) sorting method
if (size <= CUTOFF) {
InsertionSort(lo, hi, width, compare);
}
else {
/*
* First we pick a partititioning element. The efficiency of the
* algorithm demands that we find one that is approximately the
* median of the values, but also that we select one fast. Using the
* first one produces bad performace if the array is already sorted,
* so we use the middle one, which would require a very wierdly
* arranged array for worst case performance. Testing shows that a
* median-of-three algorithm does not, in general, increase
* performance.
*/
mid = lo + (size / 2) * width; // find middle element
Swap(mid, lo, width); // swap it to beginning of array
/*
* We now wish to partition the array into three pieces, one
* consisiting of elements <= partition element, one of elements
* equal to the parition element, and one of element >= to it. This
* is done below; comments indicate conditions established at every
* step.
*/
loguy = lo;
higuy = hi + width;
/*
* Note that higuy decreases and loguy increases on every
* iteration, so loop must terminate.
*/
for (;;) {
/* lo <= loguy < hi, lo < higuy <= hi + 1,
A[i] <= A[lo] for lo <= i <= loguy,
A[i] >= A[lo] for higuy <= i <= hi */
do {
loguy += width;
} while (loguy <= hi && compare(loguy, lo) <= 0);
/* lo < loguy <= hi+1, A[i] <= A[lo] for lo <= i < loguy,
either loguy > hi or A[loguy] > A[lo] */
do {
higuy -= width;
} while (higuy > lo && compare(higuy, lo) >= 0);
/* lo-1 <= higuy <= hi, A[i] >= A[lo] for higuy < i <= hi,
either higuy <= lo or A[higuy] < A[lo] */
if (higuy < loguy)
break;
/* if loguy > hi or higuy <= lo, then we would have exited, so
A[loguy] > A[lo], A[higuy] < A[lo],
loguy < hi, highy > lo */
Swap(loguy, higuy, width);
/* A[loguy] < A[lo], A[higuy] > A[lo]; so condition at top
of loop is re-established */
}
/* A[i] >= A[lo] for higuy < i <= hi,
A[i] <= A[lo] for lo <= i < loguy,
higuy < loguy, lo <= higuy <= hi
implying:
A[i] >= A[lo] for loguy <= i <= hi,
A[i] <= A[lo] for lo <= i <= higuy,
A[i] = A[lo] for higuy < i < loguy */
Swap(lo, higuy, width); // put partition element in place
/* OK, now we have the following:
A[i] >= A[higuy] for loguy <= i <= hi,
A[i] <= A[higuy] for lo <= i < higuy
A[i] = A[lo] for higuy <= i < loguy */
/* We've finished the partition, now we want to sort the subarrays
[lo, higuy-1] and [loguy, hi].
We do the smaller one first to minimize stack usage.
We only sort arrays of length 2 or more.*/
if ( higuy - 1 - lo >= hi - loguy ) {
if (lo + width < higuy) {
lostk[stkptr] = lo;
histk[stkptr] = higuy - width;
++stkptr;
} // save big recursion for later
if (loguy < hi) {
lo = loguy;
goto recurse; // do small recursion
}
}
else {
if (loguy < hi) {
lostk[stkptr] = loguy;
histk[stkptr] = hi;
++stkptr; // save big recursion for later
}
if (lo + width < higuy) {
hi = higuy - width;
goto recurse; // do small recursion
}
}
}
/*
* We have sorted the array, except for any pending sorts on the
* stack. Check if there are any, and do them.
*/
--stkptr;
if (stkptr >= 0) {
lo = lostk[stkptr];
hi = histk[stkptr];
goto recurse; // pop subarray from stack
}
else
return; // all subarrays done
}
// C Runtime stuff
int __cdecl _purecall()
{
#ifdef _DEBUG
DebugBreak();
#endif
return 0;
}
/***************************************************************************
FUNCTION: MoveClientWindow
PURPOSE: Moves a child window using screen coordinates
PARAMETERS:
hwndParent
hwndChild
prc - rectangle containing coordinates
fRedraw
RETURNS:
COMMENTS:
This function is similar to MoveWindow, only it expects the
coordinates to be in screen coordinates rather then client
coordinates. This makes it possible to use functions like
GetWindowRect() and use the values directly.
MODIFICATION DATES:
25-Feb-1992 [ralphw]
***************************************************************************/
BOOL MoveClientWindow(HWND hwndParent, HWND hwndChild, const RECT *prc, BOOL fRedraw)
{
POINT pt;
pt.x = pt.y = 0;
ScreenToClient(hwndParent, &pt);
return SetWindowPos(hwndChild, NULL, prc->left + pt.x, prc->top + pt.y,
RECT_WIDTH(prc), RECT_HEIGHT(prc),
(fRedraw ? (SWP_NOZORDER | SWP_NOACTIVATE) :
(SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW)));
}
HWND FindTopLevelWindow(HWND hwnd)
{
HWND hwndParent = hwnd;
while (IsValidWindow(hwndParent)) {
char szClass[256];
GetClassName(hwndParent, szClass, sizeof(szClass));
if (IsSamePrefix(szClass, "IEFrame", -2) ||
IsSamePrefix(szClass, txtHtmlHelpWindowClass, -2))
return hwndParent;
hwndParent = GetParent(hwndParent);
}
return hwnd; // no parent found
}
void ConvertBackSlashToForwardSlash(PSTR pszUrl)
{
PSTR psz;
if ( !(psz = pszUrl) )
return;
//
// <mc>
// I added the code to terminate the loop when we encounter a '#' char because that designates
// an anchor name or reference. We cannot modify this part of the URL without breaking the link.
// We have to respect the way the anchor is marked up in the HTM. I made this fix for bug#2058
// 12-15-97.
// </mc>
//
while (*psz && *psz != '#' )
{
if ( *psz == '\\' )
*psz = '/';
psz = AnsiNext(psz);
}
}
//////////////////////////////////////////////////////////////////////////
//
// GetParentSize
//
/*
This function is used by the navigation panes to get the size of the
area with which they have to work.
*/
HWND GetParentSize(RECT* prcParent, HWND hwndParent, int padding, int navpos)
{
GetClientRect(hwndParent, prcParent);
if (padding)
{
InflateRect(prcParent, -padding, -padding);
}
char szClass[256];
GetClassName(hwndParent, szClass, sizeof(szClass)); // NOTE: The tab control is note the parent of the panes. The Navigation window is.
if (IsSamePrefix(szClass, WC_TABCONTROL, -2))
{
// Get the dimensions of a tab.
RECT rectTab ;
TabCtrl_GetItemRect(hwndParent, 0, &rectTab) ;
int RowCount = TabCtrl_GetRowCount( hwndParent );
switch (navpos) {
case HHWIN_NAVTAB_TOP:
prcParent->top += RECT_HEIGHT(rectTab)*RowCount;
break;
case HHWIN_NAVTAB_LEFT:
prcParent->left += RECT_WIDTH(rectTab);
InflateRect(prcParent, 0, 2); // need less space top/bottom
break;
case HHWIN_NAVTAB_BOTTOM:
prcParent->bottom -= RECT_HEIGHT(rectTab)*RowCount;
break;
}
// The following is used by the index and search tabs.
// I think that there has to be a better way.
hwndParent = GetParent(hwndParent);
}
else if (padding)
{
//InflateRect(prcParent, padding, padding);
// If there is no tab control, we need to add some space to clear the top edge.
prcParent->top += GetSystemMetrics(SM_CYSIZEFRAME)*2 ; //TODO: Centralize.
}
return hwndParent ;
}
// pszFont == "facename, pointsize, charset, color and attributes"
//
// color or attributes: 0x??? == specifies a standard win32 COLORREF DWORD == 0xbbggrr
// #??? == specifies an IE color in the form #rrggbb.
//
HFONT CreateUserFont(PCSTR pszFont, COLORREF* pclrFont, HDC hDC, INT charset)
{
LOGFONT logfont;
ZERO_STRUCTURE(logfont);
logfont.lfWeight = FW_NORMAL;
logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
logfont.lfCharSet = 0xff; // lfCharSet is unsigned (don't use -1).
if( pclrFont )
*pclrFont = CLR_INVALID;
// facename[, point size[, charset[, color[, BOLD | ITALIC | UNDERLINE]]]]
CStr cszFont(pszFont); // make a copy so that we can change it
PSTR pszComma = StrChr(cszFont, ',');
int ptSize = 12;
if (pszComma){
*pszComma = '\0'; // So the facename is isolated
pszComma = FirstNonSpace(pszComma + 1); // get the point size
if (IsDigit(*pszComma))
ptSize = Atoi(pszComma);
pszComma = StrChr(pszComma, ',');
if (pszComma) {
pszComma = FirstNonSpace(pszComma + 1); // get the charset
if (IsDigit(*pszComma))
logfont.lfCharSet = (BYTE)Atoi(pszComma);
pszComma = StrChr(pszComma, ',');
if (pszComma) { // get color or attribs
// Get the font attributes first
pszComma = FirstNonSpace(pszComma + 1);
if (stristr(pszComma, "BOLD"))
logfont.lfWeight = FW_BOLD;
if (stristr(pszComma, "ITALIC"))
logfont.lfItalic = TRUE;
if (stristr(pszComma, "UNDERLINE"))
logfont.lfUnderline = TRUE;
// may be a color value instead, if so, save the color
// an repeat this check go fetch the attributes
if( stristr( pszComma, "#" ) ) { // IE color
//*(pszComma+7) = 0;
// IE Color
CWStr pwszColor = pszComma;
if( pclrFont )
*pclrFont = IEColorToWin32Color( pwszColor.pw );
}
else if( stristr( pszComma, "0x" ) || stristr( pszComma, "0X" ) ) {
//*(pszComma+8) = 0;
// Win32 Color
if( pclrFont )
*pclrFont = Atoi( pszComma );
}
}
}
}
lstrcpyn(logfont.lfFaceName, cszFont, LF_FACESIZE);
// REVIEW: we could special-case some common font names to get the
// correct font family for logfont.lfPitchAndFamily
logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
logfont.lfQuality = DEFAULT_QUALITY;
// Deal with charset...
//
if ( charset != -1 )
logfont.lfCharSet = (BYTE)charset;
else
{
if ( logfont.lfCharSet == 0xff)
{
if (isSameString(cszFont, "Symbol") || isSameString(cszFont, "WingDings"))
logfont.lfCharSet = SYMBOL_CHARSET;
else
{
HWND hwndDesktop = GetDesktopWindow();
HDC hdc = GetDC(hwndDesktop);
if (hdc) {
TEXTMETRIC tm;
GetTextMetrics(hdc, &tm);
logfont.lfCharSet = tm.tmCharSet;
ReleaseDC(hwndDesktop, hdc);
}
else
logfont.lfCharSet = ANSI_CHARSET; // REVIEW: should use the current system charset
}
}
}
// fix for Whistler bug #8123
//
if(PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) == LANG_THAI && g_bWinNT5 && ptSize < 11)
ptSize = 11;
LONG dyHeight;
if (! hDC )
{
hDC = CreateIC("DISPLAY", NULL, NULL, NULL);
dyHeight = MulDiv(GetDeviceCaps(hDC, LOGPIXELSY), ptSize * 2, 144);
DeleteDC(hDC);
}
else
dyHeight = MulDiv(GetDeviceCaps(hDC, LOGPIXELSY), ptSize * 2, 144);
logfont.lfHeight = -dyHeight;
return CreateFontIndirect(&logfont);
}
// pszFont == "facename, pointsize, charset, color and attributes"
//
// color or attributes: 0x??? == specifies a standard win32 COLORREF DWORD == 0xbbggrr
// #??? == specifies an IE color in the form #rrggbb.
//
HFONT CreateUserFontW(WCHAR *pwzFont, COLORREF* pclrFont, HDC hDC, INT charset)
{
LOGFONTW logfont;
ZERO_STRUCTURE(logfont);
logfont.lfWeight = FW_NORMAL;
logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
logfont.lfCharSet = -1;
if( pclrFont )
*pclrFont = CLR_INVALID;
// facename[, point size[, charset[, color[, BOLD | ITALIC | UNDERLINE]]]]
WCHAR *cwzFont = _wcsdup(pwzFont); // make a copy so that we can change it
WCHAR *pwzComma = wcschr(cwzFont, L',');
int ptSize = 12;
if (pwzComma){
*pwzComma = '\0'; // So the facename is isolated
pwzComma = FirstNonSpaceW(pwzComma + 1); // get the point size
if (IsDigitW(*pwzComma))
ptSize = _wtoi(pwzComma);
pwzComma = wcschr(pwzComma, L',');
if (pwzComma)
{
pwzComma = FirstNonSpaceW(pwzComma + 1); // get the charset
if (IsDigitW(*pwzComma))
logfont.lfCharSet = (BYTE)_wtoi(pwzComma);
pwzComma = wcschr(pwzComma, L',');
if (pwzComma)
{ // get color or attribs
// Get the font attributes first
pwzComma = FirstNonSpaceW(pwzComma + 1);
if (wcsstr(pwzComma, L"BOLD"))
logfont.lfWeight = FW_BOLD;
if (wcsstr(pwzComma, L"ITALIC"))
logfont.lfItalic = TRUE;
if (wcsstr(pwzComma, L"UNDERLINE"))
logfont.lfUnderline = TRUE;
// may be a color value instead, if so, save the color
// an repeat this check go fetch the attributes
if( wcsstr( pwzComma, L"#" ) ) { // IE color
//*(pszComma+7) = 0;
// IE Color
*pclrFont = IEColorToWin32Color( pwzComma);
}
else if( wcsstr( pwzComma, L"0x" ) || wcsstr( pwzComma, L"0X" ) )
{
//*(pszComma+8) = 0;
// Win32 Color
if( pclrFont )
*pclrFont = _wtoi( pwzComma );
}
}
}
}
wcsncpy(logfont.lfFaceName, cwzFont, LF_FACESIZE);
// REVIEW: we could special-case some common font names to get the
// correct font family for logfont.lfPitchAndFamily
logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
logfont.lfQuality = DEFAULT_QUALITY;
// Deal with charset...
//
if ( charset != -1 )
logfont.lfCharSet = (BYTE)charset;
else
{
if ( logfont.lfCharSet == -1 )
{
if (!wcsicmp(cwzFont, L"Symbol") || !wcsicmp(cwzFont, L"WingDings"))
logfont.lfCharSet = SYMBOL_CHARSET;
else
{
HWND hwndDesktop = GetDesktopWindow();
HDC hdc = GetDC(hwndDesktop);
if (hdc) {
TEXTMETRIC tm;
GetTextMetrics(hdc, &tm);
logfont.lfCharSet = tm.tmCharSet;
ReleaseDC(hwndDesktop, hdc);
}
else
logfont.lfCharSet = ANSI_CHARSET; // REVIEW: should use the current system charset
}
}
}
LONG dyHeight;
if (! hDC )
{
hDC = CreateIC("DISPLAY", NULL, NULL, NULL);
dyHeight = MulDiv(GetDeviceCaps(hDC, LOGPIXELSY), ptSize * 2, 144);
DeleteDC(hDC);
}
else
dyHeight = MulDiv(GetDeviceCaps(hDC, LOGPIXELSY), ptSize * 2, 144);
logfont.lfHeight = -dyHeight;
free(cwzFont);
return CreateFontIndirectW(&logfont);
}
/***************************************************************************
FUNCTION: CreateFolder
PURPOSE: Create a directory, even if it means creating several
subdirectories
PARAMETERS:
pszPath
RETURNS:
COMMENTS:
MODIFICATION DATES:
31-Oct-1996 [ralphw]
***************************************************************************/
BOOL CreateFolder(PCSTR pszPath)
{
CStr cszPath(pszPath); // copy it so that we can change it
if (CreateDirectory(cszPath, NULL))
return TRUE;
PSTR psz = StrRChr(cszPath, CH_BACKSLASH);
if (!psz) {
psz = StrRChr(cszPath, '/');
if (!psz)
return FALSE;
}
*psz = '\0';
BOOL fResult = CreateFolder(cszPath);
*psz = CH_BACKSLASH;
return CreateDirectory(cszPath, NULL);
}
BOOL GetHighContrastFlag(void)
{
HIGHCONTRAST highcontrast;
highcontrast.cbSize = sizeof(highcontrast);
if (SystemParametersInfo(SPI_GETHIGHCONTRAST,
sizeof(highcontrast),
&highcontrast, FALSE)) {
return (highcontrast.dwFlags & HCF_HIGHCONTRASTON);
}
else
return FALSE;
}
#if 0
BOOL IsValidString(LPCWSTR lpsz, int nLength)
{
if (lpsz == NULL)
return FALSE;
return ::IsBadStringPtrW(lpsz, nLength) == 0;
}
BOOL IsValidString(LPCSTR lpsz, int nLength)
{
if (lpsz == NULL)
return FALSE;
return ::IsBadStringPtrA(lpsz, nLength) == 0;
}
BOOL IsValidAddress(const void* lp, UINT nBytes, BOOL bReadWrite)
{
// simple version using Win-32 APIs for pointer validation.
return (lp != NULL && !IsBadReadPtr(lp, nBytes) &&
(!bReadWrite || !IsBadWritePtr((LPVOID)lp, nBytes)));
}
#endif
void ConvertSpacesToEscapes(PCSTR pszSrc, CStr* pcszDst)
{
int cbAlloc = pcszDst->SizeAlloc();
if (!cbAlloc)
pcszDst->ReSize(cbAlloc = (int)(strlen(pszSrc) + 128));
int dstPos = 0;
if (!pszSrc) {
*pcszDst = "";
return;
}
int cbSrc = (int)strlen(pszSrc);
while (*pszSrc) {
if (*pszSrc == ' ') {
if ((size_t) cbAlloc - dstPos <= 4)
pcszDst->ReSize(cbAlloc += 128);
strcpy(pcszDst->psz + dstPos, "%20");
dstPos += (int)strlen("%20");
pszSrc++;
}
else
pcszDst->psz[dstPos++] = *pszSrc++;
if (cbAlloc <= dstPos)
pcszDst->ReSize(cbAlloc += 128);
}
pcszDst->psz[dstPos] = '\0';
}
static const char txtItsExtension[] = ".its";
/***************************************************************************
FUNCTION: IsCompiledURL
PURPOSE: Determines if a specified URL represents one of our compiled
files.
PARAMETERS:
pszURL - URL to check
RETURNS:
TRUE if so, otherwise FALSE.
COMMENTS:
Unlike IsCompiledHtmlFile, this function does not have any side
effects--the way God intended all IsX functions to be!
MODIFICATION DATES:
02-Jan-1998 [paulti]
***************************************************************************/
BOOL IsCompiledURL( PCSTR pszURL )
{
// Check to see if the pszURL is prefixed with the moniker information.
if( IsSamePrefix(pszURL, txtMkStore, (int)strlen(txtMkStore) - 1) ||
IsSamePrefix(pszURL, txtMsItsMoniker, (int)strlen(txtMsItsMoniker) - 1) ||
IsSamePrefix(pszURL, txtItsMoniker, (int)strlen(txtItsMoniker) - 1)) {
return TRUE;
}
// TODO: do we want to verify this further? We could do an existence
// check or make sure the URL is formulated correctly. However,
// doing such will just introduce more overhead.
return FALSE;
}
/***************************************************************************
FUNCTION: GetURLType
PURPOSE: Determines what type of URL we have.
PARAMETERS:
pszURL - URL to check
RETURNS:
HH_URL_PREFIX_LESS if of the form: my.chm::/my.htm
HH_URL_UNQUALIFIED if of the form: mk@MSITStore:my.chm::/my.htm
HH_URL_QUALIFIED if of the form: mk@MSITStore:c:\my.chm::/my.htm
HH_URL_JAVASCRIPT if of the form: javascript:...
HH_URL_UNKNOWN if of an unknown form (just pass these along)
COMMENTS:
The unknown URL type is basically a non-HTML Help URL that
should remain unprocessed!
MODIFICATION DATES:
11-Jun-1998 [paulti] created
29-Oct-1998 [paulti] added ms-its:http://some.server.com/... check
18-Nov-1998 [paulti] added //255.255.255.255 check
***************************************************************************/
UINT GetURLType( PCSTR pszURL )
{
// bail out if we are passed a NULL value
if( !pszURL || !*pszURL )
return HH_URL_UNKNOWN;
// check if it is a javascript
if( !StrNCmpI(pszURL, "javascript:", 11) )
return HH_URL_JAVASCRIPT;
UINT uiReturn = HH_URL_UNKNOWN;
// check if the URL contains a ".chm" string
PSTR pszExt = NULL;
if( (uiReturn == HH_URL_UNKNOWN) && (pszExt = stristr(pszURL, txtDefExtension)) ) {
PSTR pszChm = (PSTR) pszURL;
uiReturn = HH_URL_UNQUALIFIED;
// Check to see if the pszURL is prefixed with the moniker information.
// if it is then we will call is this a unqualified URL (and will prove
// if it is really qualified later).
if( IsSamePrefix(pszURL, txtMkStore, (int)strlen(txtMkStore) - 1) )
pszChm += strlen(txtMkStore);
else if ( IsSamePrefix(pszURL, txtMsItsMoniker, (int)strlen(txtMsItsMoniker) - 1) )
pszChm += strlen(txtMsItsMoniker);
else if ( IsSamePrefix(pszURL, txtItsMoniker, (int)strlen(txtItsMoniker) - 1))
pszChm += strlen(txtItsMoniker);
else
uiReturn = HH_URL_PREFIX_LESS;
// if prefix less lets make sure it really is just prefixed with
// a simple chm filename instead of some other bizarre URL that does
// contain .chm somewhere in the middle
if( uiReturn == HH_URL_PREFIX_LESS ) {
PSTR psz = (PSTR) pszURL;
while( psz != pszExt ) {
if( *psz == ':' || *psz == '\\' || *psz == '/' ) {
uiReturn = HH_URL_UNKNOWN;
break;
}
psz++;
}
}
// if unqualified, check if it is really qualified or not
// if it begins with "X:" or "\\" then it is qualified
// if it is not qualified but contains a '/' or a '\' and the .chm
// extention then it is unknown (probably of the for:"
// ms-its:http://some.server.com/some/file.chm::some.stream.htm
if( uiReturn == HH_URL_UNQUALIFIED ) {
PSTR psz = (PSTR) ((DWORD_PTR)pszChm+1);
if( *psz == ':' || *psz == '\\' || *psz == '/' ) {
// make sure it is not of the type:
// ms-its:\\172.30.161.112\shared\boof\oops.chm::/source/Adam_nt.html
// if it is, call it unknown
BOOL bTCPIPServer = FALSE;
for( psz++; psz && *psz != '\\' && *psz != '/'; psz++ ) {
if( isdigit( *psz ) )
bTCPIPServer = TRUE;
else if( *psz == '.' )
bTCPIPServer = TRUE;
else {
bTCPIPServer = FALSE;
break;
}
}
if( bTCPIPServer ) {
uiReturn = HH_URL_UNKNOWN;
}
else {
uiReturn = HH_URL_QUALIFIED;
}
}
else {
// seek to the first slash/backslash--if .chm is after this then
// we have an unknown type possibly of the form:
// ms-its:http://some.server.com/some/file.chm::some.stream.htm
PSTR pszSlash = strstr( psz, "/" );
if( !pszSlash )
pszSlash = strstr( psz, "\\" );
if( pszSlash && (pszSlash <= pszExt) ) {
uiReturn = HH_URL_UNKNOWN;
}
}
}
}
return uiReturn;
}
/***************************************************************************
FUNCTION: IsCompiledHtmlFile
PURPOSE: Determines if a compiled HTML file is specified. If
pcszFile is specified, and pszFile does not begin with a
moniker, then we find the compiled HTML file before
returning.
PARAMETERS:
pszFile
pcszFile --- If this parameter is null, we don't attempt to find.
RETURNS:
COMMENTS:
MODIFICATION DATES:
03-Jun-1997 [ralphw]
***************************************************************************/
BOOL IsCompiledHtmlFile(PCSTR pszFile, CStr* pcszFile)
{
//--- Check to see if the pszFile is prefixed with the moniker information.
if ( IsCompiledURL(pszFile) ) {
if (pcszFile)
*pcszFile = pszFile;
return TRUE;
}
//--- Look for the chm::topic separator.
PCSTR pszSep = stristr(pszFile, txtDoubleColonSep);
if (!pszSep)
{
// The separator has not been found.
if (stristr(pszFile, txtDefExtension) || stristr(pszFile, txtItsExtension))
pszSep = pszFile;
else
return FALSE; // not a compiled file if no separator
}
// If pcszFile is NULL, don't go look for the file.
if (!pcszFile)
return TRUE; // don't find it, just say it's compiled
//NOTE: We always return true. Whether we find the file or not.
// Remove the topic from the filename, before we find the file.
CStr cszFindFile(pszFile);
if (pszSep > pszFile)
cszFindFile.psz[pszSep - pszFile] = '\0'; // remove separator
//--- Find the file.
if (FindThisFile(NULL, cszFindFile, &cszFindFile, FALSE)) {
// Add on the moniker information. FindThisFile doesn't do this.
char szSep[MAX_PATH];
strcpy(szSep, pszSep); // in case pszFile == *pcszFile
*pcszFile = (g_bMsItsMonikerSupport ? txtMsItsMoniker : txtMkStore);
*pcszFile += cszFindFile.psz;
if (pszSep > pszFile)
*pcszFile += szSep;
return TRUE;
}
else
*pcszFile = pszFile;
// If we get here, it's a compiled file, but we don't know where it is
return TRUE; // we can't find the file, so let IE try to find it
}
/***************************************************************************
FUNCTION: IsCollectionFile
PURPOSE: Determines if a input file is a collection
PARAMETERS:
pszName -- original name
RETURNS:
TRUE if file is a collection
MODIFICATION DATES:
29-Jul-1997 [dondr]
***************************************************************************/
BOOL IsCollectionFile(PCSTR pszName)
{
if (stristr(pszName, txtCollectionExtension))
return TRUE;
return FALSE;
}
/***************************************************************************
FUNCTION: GetCompiledName
PURPOSE: Parse out just the compiled file name (and path)
E.g. "its:c:\foo.chm::/bar.htm" becomes "c:\foo.chm"
Note, we must convert any and all escape sequences like %20 as well [paulti]
PARAMETERS:
pszName -- original name
pcsz -- CStr to store result
RETURNS:
Pointer to any filename after a '::' separator in the
original string (pszName);
MODIFICATION DATES:
10-Jun-1997 [ralphw]
***************************************************************************/
PCSTR GetCompiledName(PCSTR pszName, CStr* pcsz)
{
ASSERT(pcsz != NULL) ;
if( !pszName )
return NULL;
if (IsSamePrefix(pszName, txtMkStore))
pszName += strlen(txtMkStore);
else if (IsSamePrefix(pszName, txtMsItsMoniker))
pszName += strlen(txtMsItsMoniker);
else if (IsSamePrefix(pszName, txtItsMoniker))
pszName += strlen(txtItsMoniker);
*pcsz = pszName;
PSTR pszSep = strstr(*pcsz, txtDoubleColonSep);
if (pszSep) {
*pszSep = '\0';
ReplaceEscapes(pcsz->psz, pcsz->psz, ESCAPE_URL); // remove escapes
return (strstr(pszName, txtDoubleColonSep) + 2);
}
return NULL;
}
/***************************************************************************
FUNCTION: NormalizeFileName
PURPOSE: Take a filename to a CHM or COL and create a definitive
name for the file. For a CHM file, this is the moniker.
For a COL, its the moniker to the master CHM.
PARAMETERS:
cszFileName -- Modifies the name passed in.
RETURNS:
true - success
false - failure.
MODIFICATION DATES:
27-Apr-98 [dalero]
***************************************************************************/
bool
NormalizeFileName(CStr& cszFileName)
{
// We shouldn't be getting any http files. This isn't incorrect. I'm just testing my assumptions.
ASSERT(!IsHttp(cszFileName)) ;
if (IsCollectionFile(cszFileName)) // Is this a collection?
{
//...Much of this was borrowed from OnDisplayTopic...
// Get the master chm file name.
GetCompiledName(cszFileName, &cszFileName); // pszFilePortion is everything is cszFile after the '::', in other words the topic path.
if (!FindThisFile(NULL, cszFileName, &cszFileName, FALSE))
{
//TODO: Move error message into FindThisFile.
//g_LastError.Set(HH_E_FILENOTFOUND) ;
return false;
}
CHmData* phmData;
CExCollection* pCollection = GetCurrentCollection(NULL, (PCSTR)cszFileName);
if ( pCollection )
phmData = pCollection->m_phmData;
if (phmData == NULL)
{
//g_LastError.Set(HH_E_INVALIDHELPFILE) ; // TODO: FindCurFileData should set this.
return false;
}
else
{
// Get the name of the master chm.
cszFileName = phmData->GetCompiledFile();
}
}
// Find the file and get all the moniker information.
if (IsCompiledHtmlFile(cszFileName, &cszFileName))
{
// Remove any filename tacked onto the end.
PSTR pszSep = strstr(cszFileName.psz, txtDoubleColonSep);
if (pszSep)
{
*pszSep = '\0';
}
return true;
}
else
{
return false ;
}
}
/***************************************************************************
FUNCTION: GetButtonDimensions
PURPOSE: Get the width/height of the button
PARAMETERS:
hwnd
psz
RETURNS:
COMMENTS:
MODIFICATION DATES:
04-Feb-1993 [ralphw]
***************************************************************************/
#define CXBUTTONEXTRA 16 // spacing between text and button
#define CYBUTTONEXTRA 7
DWORD GetButtonDimensions(HWND hwnd, HFONT hFont, PCSTR psz)
{
HDC hdc = GetDC(hwnd);
DWORD dwRet;
POINT pt;
if (hdc == NULL)
return 0L;
// Select in the new font. The dialog may not have done this.
HFONT hOldFont = NULL ;
if (hFont)
{
hOldFont = (HFONT)SelectObject(hdc, hFont) ;
}
// Get the size of the text.
pt = GetTextSize(hdc, psz, (int)strlen(psz));
dwRet = MAKELONG(pt.x, pt.y) +
MAKELONG(CXBUTTONEXTRA, CYBUTTONEXTRA);
// Cleanup.
if (hOldFont)
{
SelectObject(hdc, hOldFont) ;
}
ReleaseDC(hwnd, hdc);
return dwRet;
}
//////////////////////////////////////////////////////////////////////////
//
// GetStaticDimensions - used by simple search tab. max_len should include
// any space needed between the static text and buttons on the left or right.
//
DWORD GetStaticDimensions(HWND hwnd, HFONT hFont, PCSTR psz, int max_len )
{
DWORD dwRet;
POINT pt;
int rows;
HDC hdc = GetDC(hwnd);
if ( hdc == NULL )
return 0L;
// Select in the new font. The dialog may not have done this.
HFONT hOldFont = NULL ;
if (hFont)
{
hOldFont = (HFONT)SelectObject(hdc, hFont) ;
}
// Get the text extent.
pt = GetTextSize(hdc, psz, (int)strlen(psz) );
rows = pt.x/(max_len ? max_len : 1);
if ( pt.x%(max_len ? max_len : 1) > 0 )
rows++;
pt.y *= rows;
dwRet = MAKELONG(pt.x, pt.y);
// Cleanup.
if (hOldFont)
{
SelectObject(hdc, hOldFont) ;
}
ReleaseDC(hwnd, hdc);
return dwRet;
}
//////////////////////////////////////////////////////////////////////////
//
// GetStaticDimensions - used by simple search tab. max_len should include
// any space needed between the static text and buttons on the left or right.
//
DWORD GetStaticDimensionsW(HWND hwnd, HFONT hFont, WCHAR *psz, int max_len )
{
DWORD dwRet;
POINT pt;
int rows;
HDC hdc = GetDC(hwnd);
if ( hdc == NULL )
return 0L;
// Select in the new font. The dialog may not have done this.
HFONT hOldFont = NULL ;
if (hFont)
{
hOldFont = (HFONT)SelectObject(hdc, hFont) ;
}
// Get the text extent.
pt = GetTextSizeW(hdc, psz, wcslen(psz) );
rows = pt.x/(max_len ? max_len : 1);
if ( pt.x%(max_len ? max_len : 1) > 0 )
rows++;
pt.y *= rows;
dwRet = MAKELONG(pt.x, pt.y);
// Cleanup.
if (hOldFont)
{
SelectObject(hdc, hOldFont) ;
}
ReleaseDC(hwnd, hdc);
return dwRet;
}
static POINT GetTextSize(HDC hdc, PCSTR qchBuf, int iCount)
{
POINT ptRet;
SIZE size;
GetTextExtentPoint32(hdc, qchBuf, iCount, &size);
ptRet.x = size.cx;
ptRet.y = size.cy;
return ptRet;
}
static POINT GetTextSizeW(HDC hdc, WCHAR *qchBuf, int iCount)
{
POINT ptRet;
SIZE size;
GetTextExtentPoint32W(hdc, qchBuf, iCount, &size);
ptRet.x = size.cx;
ptRet.y = size.cy;
return ptRet;
}
DWORD CreatePath(char *szPath)
{
char szTmp[MAX_PATH],*p,*q,szTmp2[MAX_PATH];
DWORD dwErr;
strcpy(szTmp2,szPath);
memset(szTmp,0,sizeof(szTmp));
q = szTmp2;
p = szTmp;
while (*q)
{
if (*q == '/' || *q == '\\')
{
if (szTmp[1] == ':' && strlen(szTmp) <= 3)
{
if(IsDBCSLeadByte(*q))
{
*p++ = *q++;
if(*q)
*p++ = *q++;
}
else
*p++ = *q++;
continue;
}
if (!::CreateDirectory(szTmp,0))
{
if ( (dwErr = GetLastError()) != ERROR_ALREADY_EXISTS)
return(dwErr);
}
}
if(IsDBCSLeadByte(*q))
{
*p++ = *q++;
if(*q)
*p++ = *q++;
}
else
*p++ = *q++;
}
if (!::CreateDirectory(szTmp,0))
{
if ((dwErr = GetLastError()) != ERROR_ALREADY_EXISTS)
return(dwErr);
}
return(FALSE);
}
BOOL IsFile( LPCSTR lpszPathname )
{
DWORD dwAttribs = GetFileAttributes( lpszPathname );
if( dwAttribs != (DWORD) -1 )
if( (dwAttribs & FILE_ATTRIBUTE_DIRECTORY) == 0 )
return TRUE;
return FALSE;
}
BOOL IsDirectory( LPCSTR lpszPathname )
{
DWORD dwAttribs = GetFileAttributes( lpszPathname );
if( dwAttribs != (DWORD) -1 )
if( dwAttribs & FILE_ATTRIBUTE_DIRECTORY )
return TRUE;
return FALSE;
}
/***
*SplitPath() - split a path name into its individual components
*
*Purpose:
* to split a path name into its individual components
*
*Entry:
* path - pointer to path name to be parsed
* drive - pointer to buffer for drive component, if any
* dir - pointer to buffer for subdirectory component, if any
* fname - pointer to buffer for file base name component, if any
* ext - pointer to buffer for file name extension component, if any
*
*Exit:
* drive - pointer to drive string. Includes ':' if a drive was given.
* dir - pointer to subdirectory string. Includes leading and trailing
* '/' or '\', if any.
* fname - pointer to file base name
* ext - pointer to file extension, if any. Includes leading '.'.
*
*Exceptions:
*
*******************************************************************************/
void __cdecl SplitPath (
const char *path,
char *drive,
char *dir,
char *fname,
char *ext
)
{
char *p;
char *last_slash = NULL, *dot = NULL;
unsigned len;
/* we assume that the path argument has the following form, where any
* or all of the components may be missing.
*
* <drive><dir><fname><ext>
*
* and each of the components has the following expected form(s)
*
* drive:
* 0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
* ':'
* dir:
* 0 to _MAX_DIR-1 characters in the form of an absolute path
* (leading '/' or '\') or relative path, the last of which, if
* any, must be a '/' or '\'. E.g -
* absolute path:
* \top\next\last\ ; or
* /top/next/last/
* relative path:
* top\next\last\ ; or
* top/next/last/
* Mixed use of '/' and '\' within a path is also tolerated
* fname:
* 0 to _MAX_FNAME-1 characters not including the '.' character
* ext:
* 0 to _MAX_EXT-1 characters where, if any, the first must be a
* '.'
*
*/
/* extract drive letter and :, if any */
if ((strlen(path) >= (_MAX_DRIVE - 2)) && (*(path + _MAX_DRIVE - 2) == ':')) {
if (drive) {
lstrcpyn(drive, path, _MAX_DRIVE);
*(drive + _MAX_DRIVE-1) = '\0';
}
path += _MAX_DRIVE - 1;
}
else if (drive) {
*drive = '\0';
}
/* extract path string, if any. Path now points to the first character
* of the path, if any, or the filename or extension, if no path was
* specified. Scan ahead for the last occurence, if any, of a '/' or
* '\' path separator character. If none is found, there is no path.
* We will also note the last '.' character found, if any, to aid in
* handling the extension.
*/
for (last_slash = NULL, p = (char *)path; *p; p++) {
#ifdef _MBCS
if (IsDBCSLeadByte (*p))
p++;
else {
#endif /* _MBCS */
if (*p == '/' || *p == '\\')
/* point to one beyond for later copy */
last_slash = p + 1;
else if (*p == '.')
dot = p;
#ifdef _MBCS
}
#endif /* _MBCS */
}
if (last_slash) {
/* found a path - copy up through last_slash or max. characters
* allowed, whichever is smaller
*/
if (dir) {
len = (unsigned)__min(((char *)last_slash - (char *)path) / sizeof(char),
(_MAX_DIR - 1));
lstrcpyn(dir, path, len+1);
*(dir + len) = '\0';
}
path = last_slash;
}
else if (dir) {
/* no path found */
*dir = '\0';
}
/* extract file name and extension, if any. Path now points to the
* first character of the file name, if any, or the extension if no
* file name was given. Dot points to the '.' beginning the extension,
* if any.
*/
if (dot && (dot >= path)) {
/* found the marker for an extension - copy the file name up to
* the '.'.
*/
if (fname) {
len = (unsigned)__min(((char *)dot - (char *)path) / sizeof(char),
(_MAX_FNAME - 1));
lstrcpyn(fname, path, len+1);
*(fname + len) = '\0';
}
/* now we can get the extension - remember that p still points
* to the terminating nul character of path.
*/
if (ext) {
len = (unsigned)__min(((char *)p - (char *)dot) / sizeof(char),
(_MAX_EXT - 1));
lstrcpyn(ext, dot, len+1);
*(ext + len) = '\0';
}
}
else {
/* found no extension, give empty extension and copy rest of
* string into fname.
*/
if (fname) {
len = (unsigned)__min(((char *)p - (char *)path) / sizeof(char),
(_MAX_FNAME - 1));
lstrcpyn(fname, path, len+1);
*(fname + len) = '\0';
}
if (ext) {
*ext = '\0';
}
}
}
void MemMove(void * dst, const void * src, int count)
{
#if defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC)
{
RtlMoveMemory( dst, src, count );
}
#else /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */
if (dst <= src || (char *)dst >= ((char *)src + count)) {
memcpy(dst, src, count);
}
else {
/*
* Overlapping Buffers
* copy from higher addresses to lower addresses
*/
dst = (char *)dst + count - 1;
src = (char *)src + count - 1;
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}
#endif /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */
}
LPSTR CatPath(LPSTR lpTop, LPCSTR lpTail)
{
//
// make sure we have a slash at the end of the first element
//
LPSTR p;
if (lpTop && lpTop[0])
{
p = lpTop + strlen(lpTop);
p = CharPrev(lpTop,p);
if (*p != '\\' && *p != '/')
{
strcat(lpTop,"\\");
}
//
// strip any leading slash from the second element
//
while (*lpTail == '\\') lpTail = CharNext(lpTail);
//
}
// add them together
//
strcat(lpTop, lpTail);
return lpTop;
}
///////////////////////////////////////////////////////////
//
// NoRun - Checks registry to determine if the no run option is set for this system
//
BOOL NoRun()
{
HKEY hKeyResult;
DWORD dwNoRun;
DWORD count = sizeof(DWORD);
DWORD type;
LONG lReturn;
lReturn = RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer", 0, KEY_READ, &hKeyResult);
if( lReturn == ERROR_SUCCESS )
{
lReturn = RegQueryValueEx(hKeyResult, "NoRun", 0, &type, (BYTE *)&dwNoRun, &count);
RegCloseKey(hKeyResult);
}
if (lReturn == ERROR_SUCCESS)
{
if (dwNoRun == 1)
return TRUE;
else
return FALSE;
}
else
{
count = sizeof(DWORD);
lReturn = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer", 0, KEY_READ, &hKeyResult);
if( lReturn == ERROR_SUCCESS )
{
lReturn = RegQueryValueEx(hKeyResult, "NoRun", 0, &type, (BYTE *)&dwNoRun, &count);
RegCloseKey(hKeyResult);
}
if (lReturn == ERROR_SUCCESS)
{
if (dwNoRun == 1)
return TRUE;
else
return FALSE;
}
}
return FALSE;
}
///////////////////////////////////////////////////////////
//
// NormalizeUrlInPlace
//
void
NormalizeUrlInPlace(LPSTR szURL)
{
//
// Normalize the URL by stripping off protocol, storage type, storage name goo from the URL then make sure
// we only have forward slashes. BUGBUG: May want to consider moving this to util.cpp if it turns up useful.
//
PSTR pszSep = strstr(szURL, txtDoubleColonSep);
if (pszSep)
{
strcpy(szURL, pszSep + 3);
}
ConvertBackSlashToForwardSlash(szURL);
}
void QRect(HDC hdc, INT x, INT y, INT cx, INT cy, INT color)
{
DWORD dwColor;
RECT rc;
dwColor = SetBkColor(hdc,GetSysColor(color));
rc.left = x;
rc.top = y;
rc.right = x + cx;
rc.bottom = y + cy;
ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
SetBkColor(hdc,dwColor);
}
///////////////////////////////////////////////////////////
//
// Determine if the wintype is a global wintype.
//
bool
IsGlobalWinType(const char* szType)
{
// Skip possible leading window separator character.
const char* psz = szType ;
if (psz[0] == '>')
{
psz++ ;
}
if (_strnicmp(psz, GLOBAL_WINDOWTYPE_PREFIX, (int)strlen(GLOBAL_WINDOWTYPE_PREFIX)) == 0)
{
return true ;
}
else if (_strnicmp(psz, GLOBAL_WINDOWTYPE_PREFIX_OFFICE, (int)strlen(GLOBAL_WINDOWTYPE_PREFIX_OFFICE)) == 0)
{
return true ;
}
else if (_Module.m_GlobalWinTypes.Find(psz))
{
return true ;
}
else
{
return false ;
}
}
///////////////////////////////////////////////////////////
//
// Determine if the wintype is a global wintype.
//
bool
IsGlobalWinTypeW(LPCWSTR szType)
{
LPCWSTR psz = szType ;
if (psz[0] == '>')
{
psz++ ;
}
if (_wcsnicmp(psz, GLOBAL_WINDOWTYPE_PREFIX_W, wcslen(GLOBAL_WINDOWTYPE_PREFIX_W)) == 0)
{
return true ;
}
else if (_wcsnicmp(psz, GLOBAL_WINDOWTYPE_PREFIX_OFFICE_W, wcslen(GLOBAL_WINDOWTYPE_PREFIX_OFFICE_W)) == 0)
{
return true ;
}
else if (_Module.m_GlobalWinTypes.Find(psz))
{
return true ;
}
else
{
return false ;
}
}
static const char txtSetupKey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Setup";
static const char txtSharedDir[] = "SharedDir";
///////////////////////////////////////////////////////////
//
// Get the windows directory for the system or the user
//
// Note, Windows NT Terminal Server has changed the system API
// of GetWindowsDirectory to return a per-user system directory.
// Inorder to determine this condtion we need to check kernel32
// for the GetSystemWindowsDirectory API and if it exists use
// this one instead.
//
UINT HHGetWindowsDirectory( LPSTR lpBuffer, UINT uSize, UINT uiType )
{
UINT uiReturn = 0;
static PFN_GETWINDOWSDIRECTORY pfnGetUsersWindowsDirectory = NULL;
static PFN_GETWINDOWSDIRECTORY pfnGetSystemWindowsDirectory = NULL;
// determine which system API to call for each case
if( !pfnGetSystemWindowsDirectory || !pfnGetSystemWindowsDirectory ) {
HINSTANCE hInst = LoadLibrary( "Kernel32" );
if( !hInst )
return uiReturn;
pfnGetSystemWindowsDirectory = (PFN_GETWINDOWSDIRECTORY) GetProcAddress( hInst, "GetSystemWindowsDirectoryA" );
pfnGetUsersWindowsDirectory = (PFN_GETWINDOWSDIRECTORY) GetProcAddress( hInst, "GetWindowsDirectoryA" );
ASSERT( pfnGetUsersWindowsDirectory ); // if NULL then we have a bug!
if( !pfnGetSystemWindowsDirectory ) {
pfnGetSystemWindowsDirectory = pfnGetUsersWindowsDirectory;
}
FreeLibrary( hInst );
}
// for Windows 9x, we need to consult with the registry shareddir first
// [paulti] - I have no idea why we need to do this -- this code came
// from Ralph!
HKEY hkey;
DWORD type;
DWORD cbPath = uSize;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, txtSetupKey, 0, KEY_READ, &hkey) ==
ERROR_SUCCESS) {
RegQueryValueEx(hkey, txtSharedDir, 0, &type, (PBYTE) lpBuffer, &cbPath);
RegCloseKey(hkey);
}
// if this failed, then call the system functions
if( cbPath == uSize ) { // means couldn't read registry key
if( uiType == HH_SYSTEM_WINDOWS_DIRECTORY )
uiReturn = pfnGetSystemWindowsDirectory( lpBuffer, uSize );
else if( uiType == HH_USERS_WINDOWS_DIRECTORY )
uiReturn = pfnGetUsersWindowsDirectory( lpBuffer, uSize );
else
uiReturn = 0;
}
else
uiReturn = cbPath;
return uiReturn;
}
static const char txtGlobal[] = "global.col";
static const char txtColReg[] = "hhcolreg.dat";
static const char txtHelp[] = "help";
static const char txtHHDat[] = "hh.dat";
///////////////////////////////////////////////////////////
//
// Get the help directory
//
// Note, this is always relative to the system's windows
// directory and not the user's windows directory.
// See HHGetWindowsDirectory for details on this.
//
UINT HHGetHelpDirectory( LPTSTR lpBuffer, UINT uSize )
{
UINT uiReturn = 0;
uiReturn = HHGetWindowsDirectory( lpBuffer, uSize );
CatPath( lpBuffer, txtHelp );
return uiReturn;
}
///////////////////////////////////////////////////////////
//
// Get the full pathname to the global collections file
//
// Note, this is in always in the system's help directory.
//
UINT HHGetGlobalCollectionPathname( LPTSTR lpBuffer, UINT uSize, BOOL *pbNewPath )
{
UINT uiReturn = 0;
*pbNewPath = TRUE;
uiReturn = HHGetHelpDataPath( lpBuffer );
if (uiReturn != S_OK)
{
*pbNewPath = FALSE;
uiReturn = HHGetHelpDirectory( lpBuffer, uSize );
if( !IsDirectory(lpBuffer) )
CreatePath( lpBuffer );
}
CatPath( lpBuffer, txtColReg );
return uiReturn;
}
///////////////////////////////////////////////////////////
//
// Get the full pathname to the old global collections file
//
// Note, this is in always in the user's windows\help directory
// since the old code did not handle the Terminal Server path correctly.
//
UINT HHGetOldGlobalCollectionPathname( LPTSTR lpBuffer, UINT uSize )
{
UINT uiReturn = 0;
uiReturn = HHGetHelpDirectory( lpBuffer, uSize );
if( !IsDirectory(lpBuffer) )
CreatePath( lpBuffer );
CatPath( lpBuffer, txtColReg );
return uiReturn;
}
typedef HRESULT (WINAPI *PFN_SHGETSPECIALFOLDERPATH)( HWND hWnd, LPSTR pszPath, int nFolder, BOOL fCreate );
static const char txtProfiles[] = "Profiles";
static const char txtUser[] = "Default User";
static const char txtAllUsers[] = "All Users";
static const char txtApplicationData[] = "Application Data";
static const char txtMicrosoft[] = "Microsoft";
static const char txtHTMLHelp[] = "HTML Help";
///////////////////////////////////////////////////////////
//
// Get the full path to where the user's data file is stored
//
// Note, if the subdirectories of the path does not exist
// we will create them
//
// Note, the Shell32 function SHGetSpecialFolderPathA only exist
// on platforms with IE4 or later. For those platforms that
// do not have this API we will have to simulate the returned
// "%windir%\Profiles\%username%\Application Data" path
HRESULT HHGetUserDataPath( LPSTR pszPath )
{
HRESULT hResult = S_OK;
static PFN_SHGETSPECIALFOLDERPATH pfnSHGetSpecialFolderPath = NULL;
// get the pointer to this function in shell32
if( !pfnSHGetSpecialFolderPath ) {
HINSTANCE hInst = LoadLibrary( "Shell32" );
if( !hInst )
return S_FALSE;
pfnSHGetSpecialFolderPath = (PFN_SHGETSPECIALFOLDERPATH) GetProcAddress( hInst, "SHGetSpecialFolderPathA" );
FreeLibrary( hInst ); // since we already have a copy of Shell32 loaded, free it
}
// if this function does not exist then we need to similate the return path of
// "%windir%\Profiles\%username%\Application Data"
if( !pfnSHGetSpecialFolderPath ) {
HardCodeUserPath:
// get the system's Windows directory
HHGetWindowsDirectory( pszPath, _MAX_PATH, HH_SYSTEM_WINDOWS_DIRECTORY );
// append "Profiles"
CatPath( pszPath, txtProfiles );
if( !IsDirectory(pszPath) )
if( !CreateDirectory( pszPath, NULL ) )
return S_FALSE;
// append "User"
char szUsername[_MAX_PATH];
DWORD dwSize = sizeof(szUsername);
if( !GetUserName( szUsername, &dwSize ) )
strcpy( szUsername, txtUser );
CatPath( pszPath, szUsername );
if( !IsDirectory(pszPath) )
if( !CreateDirectory( pszPath, NULL ) )
return S_FALSE;
// append "Application Data"
CatPath( pszPath, txtApplicationData );
if( !IsDirectory(pszPath) )
if( !CreateDirectory( pszPath, NULL ) )
return S_FALSE;
}
else {
// now call it
hResult = pfnSHGetSpecialFolderPath( NULL, pszPath, CSIDL_APPDATA, 0 );
if (pszPath[0] == NULL)
goto HardCodeUserPath;
}
// append "Microsoft"
CatPath( pszPath, txtMicrosoft );
if( !IsDirectory(pszPath) )
if( !CreateDirectory( pszPath, NULL ) )
return S_FALSE;
// append "HTML Help"
CatPath( pszPath, txtHTMLHelp );
if( !IsDirectory(pszPath) )
if( !CreateDirectory( pszPath, NULL ) )
return S_FALSE;
return hResult;
}
///////////////////////////////////////////////////////////
//
// Get the full pathname to the user's data file
//
// Note, older version of HTML Help always put the hh.dat file
// in the user's windows directory. Thus, if we find one there
// and not in the new user's directory we will copy over this file
// to the new location.
//
HRESULT HHGetUserDataPathname( LPSTR lpBuffer, UINT uSize )
{
HRESULT hResult = S_OK;
// check the new user data path first
if( (SUCCEEDED( hResult = HHGetUserDataPath( lpBuffer ))) ) {
// append the name of the hh.dat file to the path
CatPath( lpBuffer, txtHHDat );
// if file exists there then we are done
if( !IsFile(lpBuffer) ) {
// if the file does not exist in the new user's path then
// check the users's windows directory can copy it if found
char szHHDatPathname[_MAX_PATH];
HHGetWindowsDirectory( szHHDatPathname, sizeof(szHHDatPathname),
HH_USERS_WINDOWS_DIRECTORY );
CatPath( szHHDatPathname, txtHHDat );
if( IsFile( szHHDatPathname ) ) {
CopyFile( szHHDatPathname, lpBuffer, TRUE );
//DeleteFile( szHHDatPathname ); // should we nuke the old one?
}
}
}
return hResult;
}
typedef HRESULT (WINAPI *PFN_SHGETFOLDERPATH)( HWND hWnd, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath );
#ifndef CSIDL_FLAG_CREATE
#define CSIDL_COMMON_APPDATA 0x0023
#define CSIDL_FLAG_CREATE 0x8000
#endif
///////////////////////////////////////////////////////////
//
// Get the full path to where the common help data files lives
// hhcolreg.dat
//
// Note, if the subdirectories of the path does not exist
// we will create them
//
HRESULT HHGetHelpDataPath( LPSTR pszPath )
{
HRESULT hResult = S_OK;
static PFN_SHGETFOLDERPATH pfnSHGetFolderPath = NULL;
// get the pointer to this function in shell32
if( !pfnSHGetFolderPath ) {
HINSTANCE hInst = LoadLibrary( "Shell32" );
if( !hInst )
return S_FALSE;
pfnSHGetFolderPath = (PFN_SHGETFOLDERPATH) GetProcAddress( hInst, "SHGetFolderPathA" );
FreeLibrary( hInst ); // since we already have a copy of Shell32 loaded, free it
}
// if this function does not exist then we need to similate the return path of
// "%windir%\Profiles\All Users\Application Data"
if( pfnSHGetFolderPath ) {
// now call it
hResult = pfnSHGetFolderPath( NULL, CSIDL_FLAG_CREATE | CSIDL_COMMON_APPDATA, NULL, 0, pszPath);
if (pszPath[0] == NULL)
return S_FALSE;
}
else
return S_FALSE;
// append "Microsoft"
CatPath( pszPath, txtMicrosoft );
if( !IsDirectory(pszPath) )
if( !CreateDirectory( pszPath, NULL ) )
return S_FALSE;
// append "HTML Help"
CatPath( pszPath, txtHTMLHelp );
if( !IsDirectory(pszPath) )
if( !CreateDirectory( pszPath, NULL ) )
return S_FALSE;
return hResult;
}
///////////////////////////////////////////////////////////
//
// Get the full path to where the common help data files lives
// hhcolreg.dat
//
// Note, if the subdirectories of the path does not exist
// we will create them
//
HRESULT HHGetCurUserDataPath( LPSTR pszPath )
{
HRESULT hResult = S_OK;
static PFN_SHGETFOLDERPATH pfnSHGetFolderPath = NULL;
// get the pointer to this function in shell32
if( !pfnSHGetFolderPath ) {
HINSTANCE hInst = LoadLibrary( "Shell32" );
if( !hInst )
return S_FALSE;
pfnSHGetFolderPath = (PFN_SHGETFOLDERPATH) GetProcAddress( hInst, "SHGetFolderPathA" );
FreeLibrary( hInst ); // since we already have a copy of Shell32 loaded, free it
}
// if this function does not exist then we need to similate the return path of
// "%windir%\Profiles\"username"\Application Data"
if( pfnSHGetFolderPath ) {
// now call it
hResult = pfnSHGetFolderPath( NULL, CSIDL_FLAG_CREATE | CSIDL_APPDATA , NULL, 0, pszPath);
if (pszPath[0] == NULL)
return S_FALSE;
}
else
return S_FALSE;
// append "Microsoft"
CatPath( pszPath, txtMicrosoft );
if( !IsDirectory(pszPath) )
if( !CreateDirectory( pszPath, NULL ) )
return S_FALSE;
// append "HTML Help"
CatPath( pszPath, txtHTMLHelp );
if( !IsDirectory(pszPath) )
if( !CreateDirectory( pszPath, NULL ) )
return S_FALSE;
return hResult;
}
///////////////////////////////////////////////////////////
//
// NT Keyboard hidden UI support functions.
//
LRESULT SendMessageAnsiOrWide(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (IsWindowUnicode(hwnd))
{
return SendMessageW(hwnd, msg, wParam, lParam);
}
else
{
return SendMessageA(hwnd, msg, wParam, lParam) ;
}
}
void UiStateInitialize(HWND hwnd)
{
if (g_bWinNT5 && IsWindow(hwnd))
{
SendMessageAnsiOrWide(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0) ;
}
}
void UiStateChangeOnTab(HWND hwnd)
{
if (g_bWinNT5 && IsWindow(hwnd))
{
SendMessageAnsiOrWide(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0) ;
}
}
void UiStateChangeOnAlt(HWND hwnd)
{
if (g_bWinNT5 && IsWindow(hwnd))
{
// The only thing we can do is just turn on the underlines.
SendMessageAnsiOrWide(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEACCEL | UISF_HIDEFOCUS), 0) ;
}
}