1450 lines
43 KiB
C
1450 lines
43 KiB
C
|
|
||
|
/*++
|
||
|
|
||
|
Copyright (c) 1989-2000 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
apphelp.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module implements high-level functions to access apphelp information
|
||
|
|
||
|
Author:
|
||
|
|
||
|
dmunsil created sometime in 1999
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "sdbp.h"
|
||
|
|
||
|
#define SIZE_WSTRING(pwsz) \
|
||
|
(pwsz == NULL ? 0 : (wcslen((LPCWSTR)(pwsz)) * sizeof(WCHAR) + sizeof(UNICODE_NULL)))
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
SdbReadApphelpDetailsData(
|
||
|
IN PDB pdb, // apphelp.sdb handle
|
||
|
OUT PAPPHELP_DATA pData // apphelp data, which is filled with various bits of information
|
||
|
)
|
||
|
/*++
|
||
|
Return: TRUE if the string was read, FALSE if not.
|
||
|
|
||
|
Desc: This function retrieves APPHELP details from apphelp.sdb. The database
|
||
|
should have a valid index on HTMLHELPID. In addition, we assume that
|
||
|
the compiler generated unique entries for the htmlhelpids. Tthat means,
|
||
|
no two items have identical indexes. The logic to do so has been
|
||
|
specifically built into shimdbc. If this ever changes, this function will
|
||
|
have to be changed as well.
|
||
|
--*/
|
||
|
{
|
||
|
BOOL bSuccess = FALSE;
|
||
|
TAGID tiApphelp;
|
||
|
TAGID tiAppTitle;
|
||
|
TAGID tiContact;
|
||
|
TAGID tiDetails;
|
||
|
TAGID tiLink;
|
||
|
TAGID tiURL;
|
||
|
TAGID tiLinkText;
|
||
|
FIND_INFO FindInfo;
|
||
|
|
||
|
if (!SdbIsIndexAvailable(pdb, TAG_APPHELP, TAG_HTMLHELPID)) {
|
||
|
DBGPRINT((sdlError,
|
||
|
"SdbReadApphelpDetailsData",
|
||
|
"HTMLHELPID index in details database is not available.\n"));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
tiApphelp = SdbFindFirstDWORDIndexedTag(pdb,
|
||
|
TAG_APPHELP,
|
||
|
TAG_HTMLHELPID,
|
||
|
pData->dwHTMLHelpID,
|
||
|
&FindInfo);
|
||
|
|
||
|
if (!tiApphelp) {
|
||
|
DBGPRINT((sdlError,
|
||
|
"SdbReadApphelpDetailsData",
|
||
|
"Failed to find HTMLHELPID 0x%x in the details database.\n",
|
||
|
pData->dwHTMLHelpID));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now find the link. We support multiple links but use only one for now.
|
||
|
//
|
||
|
tiLink = SdbFindFirstTag(pdb, tiApphelp, TAG_LINK);
|
||
|
if (tiLink) {
|
||
|
tiURL = SdbFindFirstTag(pdb, tiLink, TAG_LINK_URL);
|
||
|
if (tiURL) {
|
||
|
pData->szURL = SdbGetStringTagPtr(pdb, tiURL);
|
||
|
}
|
||
|
tiLinkText = SdbFindFirstTag(pdb, tiLink, TAG_LINK_TEXT);
|
||
|
if (tiLinkText) {
|
||
|
pData->szLink = SdbGetStringTagPtr(pdb, tiLinkText);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tiDetails = SdbFindFirstTag(pdb, tiApphelp, TAG_APPHELP_DETAILS);
|
||
|
if (tiDetails) {
|
||
|
pData->szDetails = SdbGetStringTagPtr(pdb, tiDetails);
|
||
|
}
|
||
|
|
||
|
tiContact = SdbFindFirstTag(pdb, tiApphelp, TAG_APPHELP_CONTACT);
|
||
|
if (tiContact) {
|
||
|
pData->szContact = SdbGetStringTagPtr(pdb, tiContact);
|
||
|
}
|
||
|
|
||
|
tiAppTitle = SdbFindFirstTag(pdb, tiApphelp, TAG_APPHELP_TITLE);
|
||
|
if (tiAppTitle) {
|
||
|
pData->szAppTitle = SdbGetStringTagPtr(pdb, tiAppTitle);
|
||
|
}
|
||
|
|
||
|
bSuccess = TRUE;
|
||
|
|
||
|
return bSuccess;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
SdbReadApphelpData(
|
||
|
IN HSDB hSDB, // handle to the database channel
|
||
|
IN TAGREF trExe, // TAGREF of the EXE with data to read
|
||
|
OUT PAPPHELP_DATA pData // data that we read
|
||
|
)
|
||
|
/*++
|
||
|
Return: TRUE if the string was read, FALSE if not.
|
||
|
|
||
|
Desc: Read the data associated with the apphelp entry
|
||
|
into APPHELP_DATA structure. If there are no apphelp data
|
||
|
for this exe, then the function returns FALSE.
|
||
|
One or more members of the APPHELP_DATA structure may
|
||
|
be 0.
|
||
|
--*/
|
||
|
{
|
||
|
TAGID tiAppHelp,
|
||
|
tiAppName,
|
||
|
tiProblemSeverity,
|
||
|
tiFlags,
|
||
|
tiHtmlHelpID;
|
||
|
TAGID tiExe;
|
||
|
PDB pdb;
|
||
|
|
||
|
if (pData != NULL) {
|
||
|
RtlZeroMemory(pData, sizeof(APPHELP_DATA));
|
||
|
}
|
||
|
|
||
|
if (!SdbTagRefToTagID(hSDB, trExe, &pdb, &tiExe)) {
|
||
|
DBGPRINT((sdlError,
|
||
|
"SdbReadApphelpData",
|
||
|
"Failed to get the TAGID for TAGREF 0x%x.\n",
|
||
|
trExe));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
tiAppHelp = SdbFindFirstTag(pdb, tiExe, TAG_APPHELP);
|
||
|
|
||
|
if (tiAppHelp == TAGID_NULL) {
|
||
|
//
|
||
|
// This is not an apphelp entry
|
||
|
//
|
||
|
DBGPRINT((sdlInfo,
|
||
|
"SdbReadApphelpData",
|
||
|
"This is not an apphelp entry tiExe 0x%x.\n",
|
||
|
tiExe));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (pData == NULL) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
pData->trExe = trExe;
|
||
|
|
||
|
//
|
||
|
// Read supplemental flags.
|
||
|
//
|
||
|
tiFlags = SdbFindFirstTag(pdb, tiAppHelp, TAG_FLAGS);
|
||
|
|
||
|
if (tiFlags != TAGID_NULL) {
|
||
|
pData->dwFlags = SdbReadDWORDTag(pdb, tiFlags, 0);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Read problem severity for this app.
|
||
|
//
|
||
|
tiProblemSeverity = SdbFindFirstTag(pdb, tiAppHelp, TAG_PROBLEMSEVERITY);
|
||
|
|
||
|
if (tiProblemSeverity != TAGID_NULL) {
|
||
|
pData->dwSeverity = SdbReadDWORDTag(pdb, tiProblemSeverity, 0);
|
||
|
}
|
||
|
|
||
|
if (pData->dwSeverity == 0) {
|
||
|
DBGPRINT((sdlError,
|
||
|
"SdbReadApphelpData",
|
||
|
"Problem severity for tiExe 0x%x missing.\n",
|
||
|
tiExe));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We should have html help id here.
|
||
|
//
|
||
|
tiHtmlHelpID = SdbFindFirstTag(pdb, tiAppHelp, TAG_HTMLHELPID);
|
||
|
|
||
|
if (tiHtmlHelpID != TAGID_NULL) {
|
||
|
pData->dwHTMLHelpID = SdbReadDWORDTag(pdb, tiHtmlHelpID, 0);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// While we are at it, include app's name for now. We might need it.
|
||
|
//
|
||
|
tiAppName = SdbFindFirstTag(pdb, tiExe, TAG_APP_NAME);
|
||
|
|
||
|
if (tiAppName != TAGID_NULL) {
|
||
|
pData->szAppName = SdbGetStringTagPtr(pdb, tiAppName);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
SDBAPI
|
||
|
SdbEscapeApphelpURL(
|
||
|
LPWSTR szResult, // escaped string (output)
|
||
|
LPDWORD pdwCount, // count of tchars in the buffer pointed to by szResult
|
||
|
LPCWSTR szToEscape // string to escape
|
||
|
)
|
||
|
{
|
||
|
static const BYTE s_grfbitEscape[] =
|
||
|
{
|
||
|
0xFF, 0xFF, // 00 - 0F
|
||
|
0xFF, 0xFF, // 10 - 1F
|
||
|
0xFF, 0x13, // 20 - 2F
|
||
|
0x00, 0xFC, // 30 - 3F
|
||
|
0x00, 0x00, // 40 - 4F
|
||
|
0x00, 0x78, // 50 - 5F
|
||
|
0x01, 0x00, // 60 - 6F
|
||
|
0x00, 0xF8, // 70 - 7F
|
||
|
0xFF, 0xFF, // 80 - 8F
|
||
|
0xFF, 0xFF, // 90 - 9F
|
||
|
0xFF, 0xFF, // A0 - AF
|
||
|
0xFF, 0xFF, // B0 - BF
|
||
|
0xFF, 0xFF, // C0 - CF
|
||
|
0xFF, 0xFF, // D0 - DF
|
||
|
0xFF, 0xFF, // E0 - EF
|
||
|
0xFF, 0xFF, // F0 - FF
|
||
|
};
|
||
|
static const WCHAR s_rgchHex[] = L"0123456789ABCDEF";
|
||
|
|
||
|
WCHAR ch;
|
||
|
DWORD nch = 0;
|
||
|
LPCWSTR lpszURL = szToEscape;
|
||
|
DWORD dwCount = *pdwCount;
|
||
|
|
||
|
// part one -- measure length
|
||
|
while ((ch = *lpszURL++) != L'\0') {
|
||
|
if ((ch & 0xFF00) != 0) { // a unicode char ?
|
||
|
nch += 6;
|
||
|
} else if(s_grfbitEscape[ch >> 3] & (1 << (ch & 7))) {
|
||
|
nch += 3;
|
||
|
} else {
|
||
|
nch += 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nch++; // one more for the final \0
|
||
|
|
||
|
if (dwCount < nch) {
|
||
|
DBGPRINT((sdlError,
|
||
|
"SdbEscapeApphelpURL",
|
||
|
"Not enough storage to escape URL \"%S\" need %ld got %ld\n",
|
||
|
szToEscape,
|
||
|
nch,
|
||
|
dwCount));
|
||
|
*pdwCount = nch;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
lpszURL = szToEscape;
|
||
|
|
||
|
while ((ch = *lpszURL++) != L'\0') {
|
||
|
|
||
|
if (ch == L' ') {
|
||
|
*szResult++ = L'+';
|
||
|
} else if ((ch & 0xFF00) != 0) { // a unicode char ?
|
||
|
*szResult++ = L'%';
|
||
|
*szResult++ = L'u';
|
||
|
*szResult++ = s_rgchHex[(ch >> 12) & 0x0F];
|
||
|
*szResult++ = s_rgchHex[(ch >> 8) & 0x0F];
|
||
|
*szResult++ = s_rgchHex[(ch >> 4) & 0x0F];
|
||
|
*szResult++ = s_rgchHex[ ch & 0x0F];
|
||
|
} else if(s_grfbitEscape[ch >> 3] & (1 << (ch & 7))) {
|
||
|
*szResult++ = L'%';
|
||
|
*szResult++ = s_rgchHex[(ch >> 4) & 0x0F];
|
||
|
*szResult++ = s_rgchHex[ ch & 0x0F];
|
||
|
} else {
|
||
|
*szResult++ = ch;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*szResult = L'\0';
|
||
|
*pdwCount = nch - 1; // do not include the term \0 into a char count
|
||
|
|
||
|
return TRUE;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
//
|
||
|
// Retrieving apphelp information
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
|
||
|
|
||
|
|
||
|
PDB
|
||
|
SDBAPI
|
||
|
SdbOpenApphelpDetailsDatabase(
|
||
|
LPCWSTR pwszDetailsDatabasePath
|
||
|
)
|
||
|
{
|
||
|
|
||
|
PDB pdbDetails = NULL;
|
||
|
DWORD dwLength;
|
||
|
WCHAR wszAppHelpSdb[MAX_PATH];
|
||
|
|
||
|
if (pwszDetailsDatabasePath == NULL) {
|
||
|
//
|
||
|
// By default the details database is in %windir%\AppPatch\apphelp.sdb
|
||
|
//
|
||
|
|
||
|
dwLength = SdbpGetStandardDatabasePath(NULL,
|
||
|
SDB_DATABASE_MAIN_DETAILS,
|
||
|
0, // retrieve NT_PATH
|
||
|
wszAppHelpSdb,
|
||
|
CHARCOUNT(wszAppHelpSdb));
|
||
|
if (dwLength != 0 && dwLength < CHARCOUNT(wszAppHelpSdb)) {
|
||
|
pdbDetails = SdbOpenDatabase(wszAppHelpSdb, NT_PATH);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
pdbDetails = SdbOpenDatabase(pwszDetailsDatabasePath, DOS_PATH);
|
||
|
}
|
||
|
|
||
|
if (pdbDetails == NULL) {
|
||
|
DBGPRINT((sdlError, "SdbOpenApphelpDetailsDatabase", "Failed to open the details database.\n"));
|
||
|
}
|
||
|
|
||
|
return pdbDetails;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
SdbpReadApphelpBasicInfo(
|
||
|
IN PDB pdb,
|
||
|
IN TAGID tiEntry,
|
||
|
OUT TAGID* ptiApphelp,
|
||
|
OUT LPDWORD lpdwHtmlHelpID,
|
||
|
OUT LPDWORD lpdwProblemSeverity,
|
||
|
OUT LPDWORD lpdwFlags
|
||
|
)
|
||
|
{
|
||
|
TAGID tiAppHelp = TAGID_NULL;
|
||
|
TAGID tiHtmlHelpID = TAGID_NULL;
|
||
|
TAGID tiSeverity = TAGID_NULL;
|
||
|
TAGID tiFlags = TAGID_NULL;
|
||
|
DWORD dwHtmlHelpID = 0;
|
||
|
DWORD dwSeverity = 0;
|
||
|
DWORD dwFlags = 0;
|
||
|
BOOL bReturn = FALSE;
|
||
|
|
||
|
if (tiEntry == TAGID_NULL) {
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
assert(ptiApphelp != NULL);
|
||
|
|
||
|
tiAppHelp = SdbFindFirstTag(pdb, tiEntry, TAG_APPHELP);
|
||
|
if (tiAppHelp == TAGID_NULL) {
|
||
|
//
|
||
|
// This is not an apphelp entry
|
||
|
//
|
||
|
DBGPRINT((sdlError, "SdbpReadApphelpBasicInfo",
|
||
|
"This is not an apphelp entry tiExe 0x%x.\n", tiEntry));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
if (lpdwHtmlHelpID != NULL) {
|
||
|
tiHtmlHelpID = SdbFindFirstTag(pdb, tiAppHelp, TAG_HTMLHELPID);
|
||
|
if (tiHtmlHelpID != TAGID_NULL) {
|
||
|
dwHtmlHelpID = SdbReadDWORDTag(pdb, tiHtmlHelpID, 0);
|
||
|
}
|
||
|
*lpdwHtmlHelpID = dwHtmlHelpID;
|
||
|
}
|
||
|
|
||
|
if (lpdwProblemSeverity != NULL) {
|
||
|
tiSeverity = SdbFindFirstTag(pdb, tiAppHelp, TAG_PROBLEMSEVERITY);
|
||
|
if (tiSeverity != TAGID_NULL) {
|
||
|
dwSeverity = SdbReadDWORDTag(pdb, tiSeverity, 0);
|
||
|
}
|
||
|
*lpdwProblemSeverity = dwSeverity;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Read supplemental flags.
|
||
|
//
|
||
|
if (lpdwFlags != NULL) {
|
||
|
tiFlags = SdbFindFirstTag(pdb, tiAppHelp, TAG_FLAGS);
|
||
|
if (tiFlags != TAGID_NULL) {
|
||
|
dwFlags = SdbReadDWORDTag(pdb, tiFlags, 0);
|
||
|
}
|
||
|
*lpdwFlags = dwFlags;
|
||
|
}
|
||
|
|
||
|
bReturn = TRUE;
|
||
|
|
||
|
out:
|
||
|
|
||
|
// always set the tiApphelp
|
||
|
|
||
|
*ptiApphelp = tiAppHelp;
|
||
|
|
||
|
return bReturn;
|
||
|
}
|
||
|
|
||
|
|
||
|
HAPPHELPINFOCONTEXT
|
||
|
SDBAPI
|
||
|
SdbOpenApphelpInformationByID(
|
||
|
IN HSDB hSDB,
|
||
|
IN TAGREF trEntry,
|
||
|
IN DWORD dwDatabaseType // pass the type of db you are using
|
||
|
)
|
||
|
{
|
||
|
PAPPHELPINFOCONTEXT pApphelpInfoContext = NULL;
|
||
|
PDB pdb = NULL;
|
||
|
TAGID tiExe = TAGID_NULL;
|
||
|
BOOL bSuccess = FALSE;
|
||
|
|
||
|
if (trEntry == TAGREF_NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// if we are here, it is apphelp for sure, so we create the context
|
||
|
//
|
||
|
pApphelpInfoContext = (PAPPHELPINFOCONTEXT)SdbAlloc(sizeof(APPHELPINFOCONTEXT));
|
||
|
if (pApphelpInfoContext == NULL) {
|
||
|
DBGPRINT((sdlError, "SdbOpenApphelpInformation",
|
||
|
"Error allocating memory for apphelp info context\n"));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
pApphelpInfoContext->hSDB = hSDB;
|
||
|
pApphelpInfoContext->pdb = pdb;
|
||
|
pApphelpInfoContext->dwContextFlags |= AHC_HSDB_NOCLOSE; // external hsdb, do not touch it
|
||
|
//
|
||
|
// all we care for -- is whether it's "main" database or not
|
||
|
//
|
||
|
pApphelpInfoContext->dwDatabaseType = dwDatabaseType;
|
||
|
|
||
|
// get the guid for this db
|
||
|
if (!SdbTagRefToTagID(hSDB, trEntry, &pdb, &tiExe)) {
|
||
|
DBGPRINT((sdlError, "SdbOpenApphelpInformationByID",
|
||
|
"Error converting tagref to tagref 0x%lx\n", trEntry));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
pApphelpInfoContext->tiExe = tiExe;
|
||
|
|
||
|
if (!SdbGetDatabaseGUID(hSDB, pdb, &pApphelpInfoContext->guidDB)) {
|
||
|
DBGPRINT((sdlError, "SdbOpenApphelpInformationByID",
|
||
|
"Error reading database guid for tagref 0x%lx\n", trEntry));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
if (!SdbpReadApphelpBasicInfo(pdb,
|
||
|
tiExe,
|
||
|
&pApphelpInfoContext->tiApphelpExe,
|
||
|
&pApphelpInfoContext->dwHtmlHelpID,
|
||
|
&pApphelpInfoContext->dwSeverity,
|
||
|
&pApphelpInfoContext->dwFlags)) {
|
||
|
|
||
|
DBGPRINT((sdlError, "SdbOpenApphelpInformationByID",
|
||
|
"Error reading apphelp basic information, apphelp may not be present for 0x%lx\n", trEntry));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
bSuccess = TRUE;
|
||
|
|
||
|
//
|
||
|
out:
|
||
|
if (!bSuccess) {
|
||
|
|
||
|
if (pApphelpInfoContext != NULL) {
|
||
|
SdbFree(pApphelpInfoContext);
|
||
|
pApphelpInfoContext = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pApphelpInfoContext;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
HAPPHELPINFOCONTEXT
|
||
|
SDBAPI
|
||
|
SdbOpenApphelpInformation(
|
||
|
IN GUID* pguidDB,
|
||
|
IN GUID* pguidID
|
||
|
)
|
||
|
{
|
||
|
|
||
|
WCHAR szDatabasePath[MAX_PATH];
|
||
|
DWORD dwDatabaseType = 0;
|
||
|
DWORD dwLength;
|
||
|
HSDB hSDB = NULL;
|
||
|
PDB pdb = NULL;
|
||
|
TAGID tiMatch = TAGID_NULL;
|
||
|
FIND_INFO FindInfo;
|
||
|
|
||
|
PAPPHELPINFOCONTEXT pApphelpInfoContext = NULL;
|
||
|
BOOL bSuccess = FALSE;
|
||
|
|
||
|
//
|
||
|
// Resolve and open the database.
|
||
|
//
|
||
|
// NOTE: For now pass the IMAGE_FILE_MACHINE_I386 flag.
|
||
|
// We'll only look into the 32 bit database.
|
||
|
//
|
||
|
hSDB = SdbInitDatabaseEx(HID_NO_DATABASE, NULL, IMAGE_FILE_MACHINE_I386);
|
||
|
if (hSDB == NULL) {
|
||
|
DBGPRINT((sdlError, "SdbOpenApphelpInformation",
|
||
|
"Failed to initialize database\n"));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// First, we need to resolve a db
|
||
|
//
|
||
|
dwLength = SdbResolveDatabase(hSDB,
|
||
|
pguidDB,
|
||
|
&dwDatabaseType,
|
||
|
szDatabasePath,
|
||
|
CHARCOUNT(szDatabasePath));
|
||
|
if (dwLength == 0 || dwLength > CHARCOUNT(szDatabasePath)) {
|
||
|
DBGPRINT((sdlError, "SdbOpenApphelpInformation",
|
||
|
"Failed to resolve database path\n"));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// open database
|
||
|
//
|
||
|
|
||
|
if (!SdbOpenLocalDatabase(hSDB, szDatabasePath)) {
|
||
|
DBGPRINT((sdlError, "SdbOpenApphelpInformation",
|
||
|
"Failed to open database \"%s\"\n", szDatabasePath));
|
||
|
goto out;
|
||
|
}
|
||
|
//
|
||
|
// we have database opened, I presume
|
||
|
//
|
||
|
pdb = ((PSDBCONTEXT)hSDB)->pdbLocal;
|
||
|
|
||
|
//
|
||
|
// we search only the LOCAL database in this case
|
||
|
//
|
||
|
|
||
|
tiMatch = SdbFindFirstGUIDIndexedTag(pdb,
|
||
|
TAG_EXE,
|
||
|
TAG_EXE_ID,
|
||
|
pguidID,
|
||
|
&FindInfo);
|
||
|
// if we have a match...
|
||
|
if (tiMatch == TAGID_NULL) {
|
||
|
DBGPRINT((sdlWarning, "SdbOpenApphelpInformation", "guid was not found in the database\n"));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// if we are here, it is apphelp for sure, so we create the context
|
||
|
//
|
||
|
pApphelpInfoContext = (PAPPHELPINFOCONTEXT)SdbAlloc(sizeof(APPHELPINFOCONTEXT));
|
||
|
if (pApphelpInfoContext == NULL) {
|
||
|
DBGPRINT((sdlError, "SdbOpenApphelpInformation",
|
||
|
"Error allocating memory for apphelp info context\n"));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
pApphelpInfoContext->hSDB = hSDB;
|
||
|
pApphelpInfoContext->pdb = pdb;
|
||
|
pApphelpInfoContext->guidID = *pguidID;
|
||
|
pApphelpInfoContext->guidDB = *pguidDB;
|
||
|
pApphelpInfoContext->tiExe = tiMatch;
|
||
|
pApphelpInfoContext->dwDatabaseType = dwDatabaseType;
|
||
|
|
||
|
if (!SdbpReadApphelpBasicInfo(pdb,
|
||
|
tiMatch,
|
||
|
&pApphelpInfoContext->tiApphelpExe,
|
||
|
&pApphelpInfoContext->dwHtmlHelpID,
|
||
|
&pApphelpInfoContext->dwSeverity,
|
||
|
&pApphelpInfoContext->dwFlags)) {
|
||
|
|
||
|
DBGPRINT((sdlError, "SdbOpenApphelpInformation",
|
||
|
"Error reading apphelp basic information, apphelp may not be present for tagid 0x%lx\n", tiMatch));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
bSuccess = TRUE;
|
||
|
|
||
|
//
|
||
|
// we are done now
|
||
|
//
|
||
|
|
||
|
out:
|
||
|
if (!bSuccess) {
|
||
|
|
||
|
if (hSDB != NULL) {
|
||
|
SdbReleaseDatabase(hSDB);
|
||
|
}
|
||
|
|
||
|
if (pApphelpInfoContext != NULL) {
|
||
|
SdbFree(pApphelpInfoContext);
|
||
|
pApphelpInfoContext = NULL;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
return (HAPPHELPINFOCONTEXT)pApphelpInfoContext;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
SDBAPI
|
||
|
SdbCloseApphelpInformation(
|
||
|
HAPPHELPINFOCONTEXT hctx
|
||
|
)
|
||
|
{
|
||
|
PAPPHELPINFOCONTEXT pApphelpInfoContext = (PAPPHELPINFOCONTEXT)hctx;
|
||
|
|
||
|
if (pApphelpInfoContext != NULL) {
|
||
|
if (pApphelpInfoContext->hSDB != NULL &&
|
||
|
!(pApphelpInfoContext->dwContextFlags & AHC_HSDB_NOCLOSE)) {
|
||
|
SdbReleaseDatabase(pApphelpInfoContext->hSDB);
|
||
|
}
|
||
|
if (pApphelpInfoContext->pdbDetails != NULL &&
|
||
|
!(pApphelpInfoContext->dwContextFlags & AHC_DBDETAILS_NOCLOSE)) {
|
||
|
SdbCloseDatabaseRead(pApphelpInfoContext->pdbDetails);
|
||
|
}
|
||
|
if (pApphelpInfoContext->pwszHelpCtrURL != NULL) {
|
||
|
SdbFree(pApphelpInfoContext->pwszHelpCtrURL);
|
||
|
}
|
||
|
|
||
|
RtlFreeUnicodeString(&pApphelpInfoContext->ustrChmFile);
|
||
|
RtlFreeUnicodeString(&pApphelpInfoContext->ustrDetailsDatabase);
|
||
|
|
||
|
SdbFree(pApphelpInfoContext);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
SDBAPI
|
||
|
SdbpReadApphelpString(
|
||
|
PDB pdb,
|
||
|
TAGID tiParent,
|
||
|
TAG tItem,
|
||
|
LPCWSTR* ppwszCache,
|
||
|
LPVOID* ppResult
|
||
|
)
|
||
|
{
|
||
|
DWORD cbResult = 0;
|
||
|
TAGID tiItem;
|
||
|
LPCWSTR pwszItem = NULL;
|
||
|
|
||
|
if (*ppwszCache != NULL) {
|
||
|
pwszItem = *ppwszCache;
|
||
|
} else {
|
||
|
|
||
|
tiItem = SdbFindFirstTag(pdb, tiParent, tItem);
|
||
|
if (tiItem != TAGID_NULL) {
|
||
|
pwszItem = SdbGetStringTagPtr(pdb, tiItem);
|
||
|
if (pwszItem != NULL) {
|
||
|
*ppwszCache = pwszItem;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cbResult = (DWORD)SIZE_WSTRING(pwszItem);
|
||
|
*ppResult = (LPVOID)pwszItem;
|
||
|
|
||
|
return cbResult;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
SDBAPI
|
||
|
SdbpReadApphelpLinkInformation(
|
||
|
PAPPHELPINFOCONTEXT pApphelpInfoContext
|
||
|
)
|
||
|
{
|
||
|
TAGID tiLink;
|
||
|
TAGID tiApphelp = pApphelpInfoContext->tiApphelpDetails;
|
||
|
PDB pdb = pApphelpInfoContext->pdbDetails;
|
||
|
TAGID tiURL;
|
||
|
TAGID tiLinkText;
|
||
|
|
||
|
if (pApphelpInfoContext->tiLink != TAGID_NULL) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now find the link. We support multiple links but use only one for now.
|
||
|
//
|
||
|
|
||
|
tiLink = SdbFindFirstTag(pdb, tiApphelp, TAG_LINK);
|
||
|
if (tiLink == TAGID_NULL) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
tiURL = SdbFindFirstTag(pdb, tiLink, TAG_LINK_URL);
|
||
|
if (tiURL) {
|
||
|
pApphelpInfoContext->pwszLinkURL = SdbGetStringTagPtr(pdb, tiURL);
|
||
|
}
|
||
|
|
||
|
tiLinkText = SdbFindFirstTag(pdb, tiLink, TAG_LINK_TEXT);
|
||
|
if (tiLinkText) {
|
||
|
pApphelpInfoContext->pwszLinkText = SdbGetStringTagPtr(pdb, tiLinkText);
|
||
|
}
|
||
|
|
||
|
pApphelpInfoContext->tiLink = tiLink;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
SDBAPI
|
||
|
SdbpCreateHelpCenterURL(
|
||
|
IN HAPPHELPINFOCONTEXT hctx,
|
||
|
IN BOOL bOfflineContent OPTIONAL, // pass FALSE
|
||
|
IN BOOL bUseHtmlHelp OPTIONAL, // pass FALSE
|
||
|
IN LPCWSTR pwszChmFile OPTIONAL // pass NULL
|
||
|
);
|
||
|
|
||
|
BOOL
|
||
|
SDBAPI
|
||
|
SdbSetApphelpDebugParameters(
|
||
|
IN HAPPHELPINFOCONTEXT hctx,
|
||
|
IN LPCWSTR pszDetailsDatabase OPTIONAL,
|
||
|
IN BOOL bOfflineContent OPTIONAL, // pass FALSE
|
||
|
IN BOOL bUseHtmlHelp OPTIONAL, // pass FALSE
|
||
|
IN LPCWSTR pszChmFile OPTIONAL // pass NULL
|
||
|
)
|
||
|
{
|
||
|
PAPPHELPINFOCONTEXT pApphelpInfoContext = (PAPPHELPINFOCONTEXT)hctx;
|
||
|
|
||
|
if (pApphelpInfoContext == NULL) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (bUseHtmlHelp && !bOfflineContent) {
|
||
|
DBGPRINT((sdlError, "SdbSetApphelpDebugParameters",
|
||
|
"Inconsistent parameters: when using html help -- offline content flag should also be set\n"));
|
||
|
bOfflineContent = TRUE;
|
||
|
}
|
||
|
|
||
|
RtlFreeUnicodeString(&pApphelpInfoContext->ustrDetailsDatabase);
|
||
|
RtlFreeUnicodeString(&pApphelpInfoContext->ustrChmFile);
|
||
|
|
||
|
pApphelpInfoContext->bOfflineContent = bOfflineContent;
|
||
|
pApphelpInfoContext->bUseHtmlHelp = bUseHtmlHelp;
|
||
|
|
||
|
if (pszDetailsDatabase != NULL) {
|
||
|
if (!RtlCreateUnicodeString(&pApphelpInfoContext->ustrDetailsDatabase, pszDetailsDatabase)) {
|
||
|
DBGPRINT((sdlError, "SdbSetApphelpDebugParameters",
|
||
|
"Failed to create unicode string from \"%S\"\n", pszDetailsDatabase));
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pszChmFile != NULL) {
|
||
|
if (!RtlCreateUnicodeString(&pApphelpInfoContext->ustrChmFile, pszChmFile)) {
|
||
|
DBGPRINT((sdlError, "SdbSetApphelpDebugParameters",
|
||
|
"Failed to create unicode string from \"%S\"\n", pszChmFile));
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
SDBAPI
|
||
|
SdbQueryApphelpInformation(
|
||
|
HAPPHELPINFOCONTEXT hctx,
|
||
|
APPHELPINFORMATIONCLASS InfoClass,
|
||
|
LPVOID pBuffer, // may be NULL
|
||
|
DWORD cbSize // may be 0 if pBuffer is NULL
|
||
|
)
|
||
|
{
|
||
|
PAPPHELPINFOCONTEXT pApphelpInfoContext = (PAPPHELPINFOCONTEXT)hctx;
|
||
|
|
||
|
LPCWSTR *ppwsz;
|
||
|
TAG tag;
|
||
|
TAGID tiParent;
|
||
|
LPVOID pResult = NULL;
|
||
|
DWORD cbResult = 0;
|
||
|
PDB pdb = NULL;
|
||
|
PDB pdbDetails = NULL;
|
||
|
TAGID tiApphelpDetails = TAGID_NULL;
|
||
|
FIND_INFO FindInfo;
|
||
|
|
||
|
|
||
|
switch (InfoClass) {
|
||
|
case ApphelpLinkURL:
|
||
|
case ApphelpLinkText:
|
||
|
case ApphelpTitle:
|
||
|
case ApphelpDetails:
|
||
|
case ApphelpContact:
|
||
|
|
||
|
pdbDetails = pApphelpInfoContext->pdbDetails;
|
||
|
if (pApphelpInfoContext->pdbDetails == NULL) {
|
||
|
//
|
||
|
// see which db we should open
|
||
|
//
|
||
|
if ((pApphelpInfoContext->ustrDetailsDatabase.Buffer != NULL) ||
|
||
|
(pApphelpInfoContext->dwDatabaseType & SDB_DATABASE_MAIN)) {
|
||
|
pdbDetails = SdbOpenApphelpDetailsDatabase(pApphelpInfoContext->ustrDetailsDatabase.Buffer);
|
||
|
} else {
|
||
|
// we have a case when the apphelp details should be in main db
|
||
|
pApphelpInfoContext->dwContextFlags |= AHC_DBDETAILS_NOCLOSE;
|
||
|
pdbDetails = pApphelpInfoContext->pdb;
|
||
|
}
|
||
|
|
||
|
if (pdbDetails == NULL) {
|
||
|
return cbResult; // apphelp db is not available
|
||
|
}
|
||
|
|
||
|
pApphelpInfoContext->pdbDetails = pdbDetails;
|
||
|
}
|
||
|
|
||
|
tiApphelpDetails = pApphelpInfoContext->tiApphelpDetails;
|
||
|
if (tiApphelpDetails == TAGID_NULL) {
|
||
|
if (!SdbIsIndexAvailable(pdbDetails, TAG_APPHELP, TAG_HTMLHELPID)) {
|
||
|
DBGPRINT((sdlError,
|
||
|
"SdbQueryApphelpInformation",
|
||
|
"HTMLHELPID index in details database is not available.\n"));
|
||
|
return cbResult;
|
||
|
}
|
||
|
|
||
|
tiApphelpDetails = SdbFindFirstDWORDIndexedTag(pdbDetails,
|
||
|
TAG_APPHELP,
|
||
|
TAG_HTMLHELPID,
|
||
|
pApphelpInfoContext->dwHtmlHelpID,
|
||
|
&FindInfo);
|
||
|
|
||
|
if (tiApphelpDetails == TAGID_NULL) {
|
||
|
DBGPRINT((sdlError,
|
||
|
"SdbQueryApphelpInformation",
|
||
|
"Failed to find HTMLHELPID 0x%x in the details database.\n",
|
||
|
pApphelpInfoContext->dwHtmlHelpID));
|
||
|
return cbResult;
|
||
|
}
|
||
|
|
||
|
pApphelpInfoContext->tiApphelpDetails = tiApphelpDetails;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
switch(InfoClass) {
|
||
|
case ApphelpExeTagID:
|
||
|
pResult = &pApphelpInfoContext->tiExe;
|
||
|
cbResult = sizeof(pApphelpInfoContext->tiExe);
|
||
|
break;
|
||
|
|
||
|
case ApphelpExeName:
|
||
|
pdb = pApphelpInfoContext->pdb; // main db
|
||
|
tiParent = pApphelpInfoContext->tiExe;
|
||
|
ppwsz = &pApphelpInfoContext->pwszExeName;
|
||
|
tag = TAG_NAME;
|
||
|
cbResult = SdbpReadApphelpString(pdb, tiParent, tag, ppwsz, &pResult);
|
||
|
break;
|
||
|
|
||
|
case ApphelpAppName:
|
||
|
pdb = pApphelpInfoContext->pdb; // main db
|
||
|
tiParent = pApphelpInfoContext->tiExe;
|
||
|
ppwsz = &pApphelpInfoContext->pwszAppName;
|
||
|
tag = TAG_APP_NAME;
|
||
|
cbResult = SdbpReadApphelpString(pdb, tiParent, tag, ppwsz, &pResult);
|
||
|
break;
|
||
|
|
||
|
case ApphelpVendorName:
|
||
|
pdb = pApphelpInfoContext->pdb; // main db
|
||
|
tiParent = pApphelpInfoContext->tiExe;
|
||
|
ppwsz = &pApphelpInfoContext->pwszVendorName;
|
||
|
tag = TAG_VENDOR;
|
||
|
cbResult = SdbpReadApphelpString(pdb, tiParent, tag, ppwsz, &pResult);
|
||
|
break;
|
||
|
|
||
|
case ApphelpHtmlHelpID:
|
||
|
pResult = &pApphelpInfoContext->dwHtmlHelpID;
|
||
|
cbResult = sizeof(pApphelpInfoContext->dwHtmlHelpID);
|
||
|
break;
|
||
|
|
||
|
case ApphelpProblemSeverity:
|
||
|
pResult = &pApphelpInfoContext->dwSeverity;
|
||
|
cbResult = sizeof(pApphelpInfoContext->dwSeverity);
|
||
|
break;
|
||
|
|
||
|
case ApphelpFlags:
|
||
|
pResult = &pApphelpInfoContext->dwFlags;
|
||
|
cbResult = sizeof(pApphelpInfoContext->dwFlags);
|
||
|
break;
|
||
|
|
||
|
case ApphelpLinkURL:
|
||
|
if (!SdbpReadApphelpLinkInformation(pApphelpInfoContext)) {
|
||
|
break;
|
||
|
}
|
||
|
pResult = (LPWSTR)pApphelpInfoContext->pwszLinkURL;
|
||
|
cbResult = (DWORD)SIZE_WSTRING(pResult);
|
||
|
break;
|
||
|
|
||
|
case ApphelpLinkText:
|
||
|
if (!SdbpReadApphelpLinkInformation(pApphelpInfoContext)) {
|
||
|
break;
|
||
|
}
|
||
|
pResult = (LPWSTR)pApphelpInfoContext->pwszLinkText;
|
||
|
cbResult = (DWORD)SIZE_WSTRING(pResult);
|
||
|
break;
|
||
|
|
||
|
case ApphelpTitle:
|
||
|
pdb = pdbDetails;
|
||
|
tiParent = tiApphelpDetails;
|
||
|
ppwsz = &pApphelpInfoContext->pwszTitle;
|
||
|
tag = TAG_APPHELP_TITLE;
|
||
|
cbResult = SdbpReadApphelpString(pdb, tiParent, tag, ppwsz, &pResult);
|
||
|
break;
|
||
|
|
||
|
|
||
|
case ApphelpDetails:
|
||
|
pdb = pdbDetails;
|
||
|
tiParent = tiApphelpDetails;
|
||
|
ppwsz = &pApphelpInfoContext->pwszDetails;
|
||
|
tag = TAG_APPHELP_DETAILS;
|
||
|
cbResult = SdbpReadApphelpString(pdb, tiParent, tag, ppwsz, &pResult);
|
||
|
break;
|
||
|
|
||
|
case ApphelpContact:
|
||
|
pdb = pdbDetails;
|
||
|
tiParent = tiApphelpDetails;
|
||
|
ppwsz = &pApphelpInfoContext->pwszContact;
|
||
|
tag = TAG_APPHELP_CONTACT;
|
||
|
cbResult = SdbpReadApphelpString(pdb, tiParent, tag, ppwsz, &pResult);
|
||
|
break;
|
||
|
|
||
|
case ApphelpHelpCenterURL:
|
||
|
if (!SdbpCreateHelpCenterURL(hctx,
|
||
|
pApphelpInfoContext->bOfflineContent,
|
||
|
pApphelpInfoContext->bUseHtmlHelp,
|
||
|
pApphelpInfoContext->ustrChmFile.Buffer)) {
|
||
|
break;
|
||
|
}
|
||
|
pResult = pApphelpInfoContext->pwszHelpCtrURL;
|
||
|
cbResult = (DWORD)SIZE_WSTRING(pResult);
|
||
|
break;
|
||
|
|
||
|
case ApphelpDatabaseGUID:
|
||
|
pResult = &pApphelpInfoContext->guidDB;
|
||
|
cbResult = sizeof(pApphelpInfoContext->guidDB);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
DBGPRINT((sdlError, "SdbQueryApphelpInformation",
|
||
|
"Bad Apphelp Information class 0x%lx\n", InfoClass));
|
||
|
return 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (pBuffer == NULL || cbResult > cbSize) {
|
||
|
return cbResult;
|
||
|
}
|
||
|
|
||
|
if (pResult != NULL && cbResult > 0) {
|
||
|
RtlCopyMemory(pBuffer, pResult, cbResult);
|
||
|
}
|
||
|
|
||
|
return cbResult;
|
||
|
|
||
|
}
|
||
|
|
||
|
typedef HRESULT (STDAPICALLTYPE *PFNUrlUnescapeW)(
|
||
|
LPWSTR pszUrl,
|
||
|
LPWSTR pszUnescaped,
|
||
|
LPDWORD pcchUnescaped,
|
||
|
DWORD dwFlags);
|
||
|
|
||
|
typedef HRESULT (STDAPICALLTYPE *PFNUrlEscapeW)(
|
||
|
LPCWSTR pszURL,
|
||
|
LPWSTR pszEscaped,
|
||
|
LPDWORD pcchEscaped,
|
||
|
DWORD dwFlags
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// if bUseHtmlHelp is set -- then bOfflineContent is also set to TRUE
|
||
|
//
|
||
|
|
||
|
BOOL
|
||
|
SDBAPI
|
||
|
SdbpCreateHelpCenterURL(
|
||
|
IN HAPPHELPINFOCONTEXT hctx,
|
||
|
IN BOOL bOfflineContent OPTIONAL, // pass FALSE
|
||
|
IN BOOL bUseHtmlHelp OPTIONAL, // pass FALSE
|
||
|
IN LPCWSTR pwszChmFile OPTIONAL // pass NULL
|
||
|
)
|
||
|
{
|
||
|
WCHAR szAppHelpURL[2048];
|
||
|
WCHAR szChmURL[1024];
|
||
|
PAPPHELPINFOCONTEXT pApphelpInfo = (PAPPHELPINFOCONTEXT)hctx;
|
||
|
HMODULE hModShlwapi = NULL;
|
||
|
PFNUrlUnescapeW pfnUnescape;
|
||
|
PFNUrlEscapeW pfnEscape;
|
||
|
BOOL bSuccess = FALSE;
|
||
|
|
||
|
int nChURL = 0; // counts used bytes
|
||
|
int cch = 0;
|
||
|
int nch;
|
||
|
size_t cchRemaining;
|
||
|
LPWSTR lpwszUnescaped = NULL;
|
||
|
HRESULT hr;
|
||
|
DWORD nChars;
|
||
|
WCHAR szWindowsDir[MAX_PATH];
|
||
|
BOOL bCustom;
|
||
|
|
||
|
if (pApphelpInfo->pwszHelpCtrURL != NULL) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
if (bUseHtmlHelp) {
|
||
|
bOfflineContent = TRUE;
|
||
|
}
|
||
|
|
||
|
// ping the database
|
||
|
if (0 == SdbQueryApphelpInformation(hctx, ApphelpLinkURL, NULL, 0)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// check and see whether it's custom apphelp or not
|
||
|
//
|
||
|
bCustom = !(pApphelpInfo->dwDatabaseType & SDB_DATABASE_MAIN);
|
||
|
|
||
|
if (bCustom) {
|
||
|
if (pApphelpInfo->pwszLinkURL != NULL) {
|
||
|
hr = StringCchPrintf(szAppHelpURL,
|
||
|
CHARCOUNT(szAppHelpURL),
|
||
|
L"%ls",
|
||
|
pApphelpInfo->pwszLinkURL);
|
||
|
if (FAILED(hr)) {
|
||
|
DBGPRINT((sdlError, "SdbpCreateHelpCenterURL",
|
||
|
"Custom apphelp URL %s is too long\n", pApphelpInfo->pwszLinkURL));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
// now we are done
|
||
|
goto createApphelpURL;
|
||
|
|
||
|
} else {
|
||
|
// custom apphelp will not fly without a link
|
||
|
DBGPRINT((sdlError, "SdbpCreateHelpCenterURL", "Custom apphelp without a url link\n"));
|
||
|
goto out;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// unescape the URL
|
||
|
hModShlwapi = LoadLibraryW(L"shlwapi.dll");
|
||
|
if (hModShlwapi == NULL) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
pfnUnescape = (PFNUrlUnescapeW)GetProcAddress(hModShlwapi, "UrlUnescapeW");
|
||
|
pfnEscape = (PFNUrlEscapeW) GetProcAddress(hModShlwapi, "UrlEscapeW");
|
||
|
if (pfnUnescape == NULL || pfnEscape == NULL) {
|
||
|
DBGPRINT((sdlError, "SdbpCreateHelpCenterURL", "Cannot get shlwapi functions\n"));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!bUseHtmlHelp) {
|
||
|
StringCchPrintfEx(szAppHelpURL,
|
||
|
CHARCOUNT(szAppHelpURL),
|
||
|
NULL,
|
||
|
&cchRemaining,
|
||
|
0,
|
||
|
L"hcp://services/redirect?online=");
|
||
|
|
||
|
nChURL = CHARCOUNT(szAppHelpURL) - (int)cchRemaining;
|
||
|
}
|
||
|
|
||
|
if (!bOfflineContent && pApphelpInfo->pwszLinkURL != NULL) {
|
||
|
|
||
|
|
||
|
// unescape the url first using shell
|
||
|
cch = (int)wcslen(pApphelpInfo->pwszLinkURL) + 1;
|
||
|
|
||
|
STACK_ALLOC(lpwszUnescaped, cch * sizeof(WCHAR));
|
||
|
if (lpwszUnescaped == NULL) {
|
||
|
DBGPRINT((sdlError, "SdbpCreateHelpCenterURL",
|
||
|
"Error trying to allocate memory for \"%S\"\n", pApphelpInfo->pwszLinkURL));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Unescape round 1 - use the shell function (same as used to encode it for xml/database)
|
||
|
//
|
||
|
|
||
|
hr = pfnUnescape((LPTSTR)pApphelpInfo->pwszLinkURL, lpwszUnescaped, (LPDWORD)&cch, 0);
|
||
|
if (!SUCCEEDED(hr)) {
|
||
|
DBGPRINT((sdlError, "SdbCreateHelpCenterURL", "UrlUnescapeW failed on \"%S\"\n", pApphelpInfo->pwszLinkURL));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// round 2 - use our function borrowed from help center
|
||
|
//
|
||
|
|
||
|
cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
|
||
|
if (!SdbEscapeApphelpURL(szAppHelpURL + nChURL, (LPDWORD)&cch, lpwszUnescaped)) {
|
||
|
DBGPRINT((sdlError, "SdbCreateHelpCenterURL", "Error escaping URL \"%S\"\n", lpwszUnescaped));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
nChURL += (int)cch;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Retrieve the Windows directory.
|
||
|
//
|
||
|
nChars = GetWindowsDirectoryW(szWindowsDir, CHARCOUNT(szWindowsDir));
|
||
|
if (!nChars || nChars > CHARCOUNT(szWindowsDir)) {
|
||
|
DBGPRINT((sdlError, "SdbCreateHelpCenterURL",
|
||
|
"Error trying to retrieve Windows Directory %d.\n", GetLastError()));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
if (pwszChmFile != NULL) {
|
||
|
StringCchPrintf(szChmURL,
|
||
|
CHARCOUNT(szChmURL),
|
||
|
L"mk:@msitstore:%ls::/idh_w2_%d.htm",
|
||
|
pwszChmFile,
|
||
|
pApphelpInfo->dwHtmlHelpID);
|
||
|
} else { // standard chm file
|
||
|
|
||
|
//
|
||
|
// Attention: if we use hDlg here then, upon exit we will need to clean
|
||
|
// up the window.
|
||
|
//
|
||
|
StringCchPrintf(szChmURL,
|
||
|
CHARCOUNT(szChmURL),
|
||
|
L"mk:@msitstore:%ls\\help\\apps.chm::/idh_w2_%d.htm",
|
||
|
szWindowsDir,
|
||
|
pApphelpInfo->dwHtmlHelpID);
|
||
|
|
||
|
}
|
||
|
|
||
|
if (bOfflineContent) {
|
||
|
|
||
|
|
||
|
if (bUseHtmlHelp) {
|
||
|
cch = CHARCOUNT(szAppHelpURL);
|
||
|
hr = pfnEscape(szChmURL, szAppHelpURL, (LPDWORD)&cch, 0);
|
||
|
if (SUCCEEDED(hr)) {
|
||
|
nChURL += (INT)cch;
|
||
|
}
|
||
|
|
||
|
} else { // do not use html help
|
||
|
|
||
|
cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
|
||
|
if (!SdbEscapeApphelpURL(szAppHelpURL+nChURL, (LPDWORD)&cch, szChmURL)) {
|
||
|
DBGPRINT((sdlError, "SdbCreateHelpCenterURL", "Error escaping URL \"%S\"\n", szChmURL));
|
||
|
goto out;
|
||
|
}
|
||
|
nChURL += (INT)cch;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!bUseHtmlHelp) {
|
||
|
|
||
|
//
|
||
|
// now offline sequence
|
||
|
//
|
||
|
cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
|
||
|
|
||
|
hr = StringCchPrintfEx(szAppHelpURL + nChURL,
|
||
|
cch,
|
||
|
NULL,
|
||
|
&cchRemaining,
|
||
|
0,
|
||
|
L"&offline=");
|
||
|
if (FAILED(hr)) {
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
nch = cch - (int)cchRemaining;
|
||
|
nChURL += nch;
|
||
|
|
||
|
cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
|
||
|
|
||
|
if (!SdbEscapeApphelpURL(szAppHelpURL+nChURL, (LPDWORD)&cch, szChmURL)) {
|
||
|
DBGPRINT((sdlError, "SdbCreateHelpCenterURL", "Error escaping URL \"%S\"\n", szChmURL));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
nChURL += (int)cch;
|
||
|
}
|
||
|
|
||
|
*(szAppHelpURL + nChURL) = L'\0';
|
||
|
|
||
|
// we are done
|
||
|
// copy data now
|
||
|
|
||
|
createApphelpURL:
|
||
|
|
||
|
pApphelpInfo->pwszHelpCtrURL = (LPWSTR)SdbAlloc(nChURL * sizeof(WCHAR) + sizeof(UNICODE_NULL));
|
||
|
if (pApphelpInfo->pwszHelpCtrURL == NULL) {
|
||
|
DBGPRINT((sdlError, "SdbCreateHelpCenterURL", "Error allocating memory for the URL 0x%lx chars\n", nChURL));
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
StringCchCopy(pApphelpInfo->pwszHelpCtrURL, nChURL + sizeof(UNICODE_NULL), szAppHelpURL);
|
||
|
bSuccess = TRUE;
|
||
|
|
||
|
out:
|
||
|
|
||
|
if (lpwszUnescaped != NULL) {
|
||
|
STACK_FREE(lpwszUnescaped);
|
||
|
}
|
||
|
|
||
|
if (hModShlwapi) {
|
||
|
FreeLibrary(hModShlwapi);
|
||
|
}
|
||
|
|
||
|
return bSuccess;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// returns TRUE if dialog was shown
|
||
|
// if there was an error, input parameter (pRunApp) will NOT
|
||
|
// be touched
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
SdbShowApphelpDialog( // returns TRUE if success, whether we should run the app is in pRunApp
|
||
|
IN PAPPHELP_INFO pAHInfo, // the info necessary to find the apphelp data
|
||
|
IN PHANDLE phProcess, // [optional] returns the process handle of
|
||
|
// the process displaying the apphelp.
|
||
|
// When the process completes, the return value
|
||
|
// (from GetExitCodeProcess()) will be zero
|
||
|
// if the app should not run, or non-zero
|
||
|
// if it should run.
|
||
|
IN OUT BOOL* pRunApp
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// basically just launch the apphelp.exe and wait for it to return
|
||
|
//
|
||
|
TCHAR szGuid[64];
|
||
|
TCHAR szCommandLine[MAX_PATH * 2 + 64];
|
||
|
LPTSTR pszEnd = szCommandLine;
|
||
|
STARTUPINFO StartupInfo;
|
||
|
PROCESS_INFORMATION ProcessInfo;
|
||
|
DWORD dwExit = 1; // by default and in case of failure, we allow to run the app
|
||
|
BOOL bReturn = FALSE;
|
||
|
BOOL bRunApp = TRUE; // by default we run the app ?
|
||
|
size_t cchRemaining;
|
||
|
UINT unChars;
|
||
|
|
||
|
cchRemaining = CHARCOUNT(szCommandLine);
|
||
|
|
||
|
unChars = GetSystemDirectory(pszEnd, (UINT)cchRemaining);
|
||
|
|
||
|
//
|
||
|
// if we couldn't get the system directory, we'll just look for it on the path
|
||
|
//
|
||
|
if (unChars > cchRemaining || unChars == 0) {
|
||
|
unChars = 0;
|
||
|
}
|
||
|
|
||
|
pszEnd += unChars;
|
||
|
cchRemaining -= unChars;
|
||
|
|
||
|
if (unChars != 0) {
|
||
|
StringCchCopy(pszEnd, cchRemaining, TEXT("\\ahui.exe"));
|
||
|
} else {
|
||
|
StringCchCopy(pszEnd, cchRemaining, TEXT("ahui.exe"));
|
||
|
}
|
||
|
|
||
|
unChars = (UINT)_tcslen(pszEnd);
|
||
|
pszEnd += unChars;
|
||
|
cchRemaining -= unChars;
|
||
|
|
||
|
RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
|
||
|
RtlZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
|
||
|
StartupInfo.cb = sizeof(StartupInfo);
|
||
|
|
||
|
if (pAHInfo->tiExe != TAGID_NULL) {
|
||
|
|
||
|
//
|
||
|
// Figure out what the default return value should be
|
||
|
//
|
||
|
if (!SdbGUIDToString(&pAHInfo->guidDB, szGuid, CHARCOUNT(szGuid))) {
|
||
|
DBGPRINT((sdlError, "SdbShowApphelpDialog",
|
||
|
"Failed to convert guid to string.\n"));
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
StringCchPrintfEx(pszEnd,
|
||
|
cchRemaining,
|
||
|
&pszEnd,
|
||
|
&cchRemaining,
|
||
|
0,
|
||
|
TEXT(" %s 0x%lX"),
|
||
|
szGuid,
|
||
|
pAHInfo->tiExe);
|
||
|
|
||
|
} else {
|
||
|
if (!pAHInfo->dwHtmlHelpID) {
|
||
|
DBGPRINT((sdlError, "SdbShowApphelpDialog",
|
||
|
"Neither HTMLHELPID nor tiExe provided\n"));
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
StringCchPrintfEx(pszEnd,
|
||
|
cchRemaining,
|
||
|
&pszEnd,
|
||
|
&cchRemaining,
|
||
|
0,
|
||
|
TEXT(" /HTMLHELPID:0x%lx"),
|
||
|
pAHInfo->dwHtmlHelpID);
|
||
|
|
||
|
StringCchPrintfEx(pszEnd,
|
||
|
cchRemaining,
|
||
|
&pszEnd,
|
||
|
&cchRemaining,
|
||
|
0,
|
||
|
TEXT(" /SEVERITY:0x%lx"),
|
||
|
pAHInfo->dwSeverity);
|
||
|
|
||
|
if (!SdbIsNullGUID(&pAHInfo->guidID)) {
|
||
|
if (SdbGUIDToString(&pAHInfo->guidID, szGuid, CHARCOUNT(szGuid))) {
|
||
|
StringCchPrintfEx(pszEnd,
|
||
|
cchRemaining,
|
||
|
&pszEnd,
|
||
|
&cchRemaining,
|
||
|
0,
|
||
|
TEXT(" /GUID:%s"),
|
||
|
szGuid);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pAHInfo->lpszAppName != NULL) {
|
||
|
StringCchPrintfEx(pszEnd,
|
||
|
cchRemaining,
|
||
|
&pszEnd,
|
||
|
&cchRemaining,
|
||
|
0,
|
||
|
TEXT(" /APPNAME:\"%s\""),
|
||
|
pAHInfo->lpszAppName);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (pAHInfo->bPreserveChoice) {
|
||
|
StringCchPrintfEx(pszEnd,
|
||
|
cchRemaining,
|
||
|
&pszEnd,
|
||
|
&cchRemaining,
|
||
|
0,
|
||
|
TEXT(" /PRESERVECHOICE"));
|
||
|
}
|
||
|
|
||
|
if (pAHInfo->bMSI) {
|
||
|
StringCchPrintfEx(pszEnd,
|
||
|
cchRemaining,
|
||
|
&pszEnd,
|
||
|
&cchRemaining,
|
||
|
0,
|
||
|
TEXT(" /MSI"));
|
||
|
}
|
||
|
|
||
|
if (!CreateProcessW(NULL,
|
||
|
szCommandLine,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
FALSE,
|
||
|
0,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&StartupInfo, &ProcessInfo)) {
|
||
|
DBGPRINT((sdlError, "SdbShowApphelpDialog",
|
||
|
"Failed to launch apphelp process.\n"));
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// check to see if they want to monitor the process themselves
|
||
|
//
|
||
|
if (phProcess) {
|
||
|
bReturn = TRUE;
|
||
|
pRunApp = NULL; // we do this so that we don't touch the bRunApp
|
||
|
*phProcess = ProcessInfo.hProcess;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// otherwise, we'll do the waiting.
|
||
|
//
|
||
|
|
||
|
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
|
||
|
|
||
|
bReturn = GetExitCodeProcess(ProcessInfo.hProcess, &dwExit);
|
||
|
if (bReturn) {
|
||
|
bRunApp = (0 != dwExit);
|
||
|
}
|
||
|
|
||
|
cleanup:
|
||
|
if (bReturn && pRunApp != NULL) {
|
||
|
*pRunApp = bRunApp;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// process handle is to be closed only when phProcess is NULL
|
||
|
//
|
||
|
|
||
|
if (phProcess == NULL && ProcessInfo.hProcess) {
|
||
|
CloseHandle(ProcessInfo.hProcess);
|
||
|
}
|
||
|
if (ProcessInfo.hThread) {
|
||
|
CloseHandle(ProcessInfo.hThread);
|
||
|
}
|
||
|
|
||
|
return bReturn;
|
||
|
}
|