Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1782 lines
50 KiB

/*--
Copyright (c) 2001 Microsoft Corporation
Module Name:
ahui.cpp
Abstract:
Shows an apphelp message, and returns 0 if the program shouldn't run, and non-
zero if the program should run
Accepts a command line with a GUID and a TAGID, in the following format:
{243B08D7-8CF7-4072-AF64-FD5DF4085E26} 0x0000009E
Author:
dmunsil 04/03/2001
Revision History:
Notes:
--*/
#define _UNICODE
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <memory.h>
#include <malloc.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <windowsx.h>
#include <htmlhelp.h>
extern "C" {
#include "shimdb.h"
}
#include "ids.h"
#include "shlobj.h"
#include "shlobjp.h"
#include "shellapi.h"
#include "shlwapi.h"
#ifndef _WIN64
#include <wow64t.h>
#endif
#include "ahmsg.h"
#include "strsafe.h"
extern "C" VOID AllowForegroundActivation(VOID);
#define APPHELP_DIALOG_FAILED ((DWORD)-1)
//
// TODO: add parameters to apphelp.exe's command line to work with these
// variables.
//
BOOL g_bShowOnlyOfflineContent = FALSE;
BOOL g_bUseHtmlHelp = FALSE;
BOOL g_bMSI = FALSE;
USHORT g_uExeImageType= DEFAULT_IMAGE;
WCHAR g_wszApphelpPath[MAX_PATH];
HFONT g_hFontBold = NULL;
HINSTANCE g_hInstance;
//
// Global variables used while parsing args
//
DWORD g_dwHtmlHelpID;
DWORD g_dwTagID;
DWORD g_dwSeverity;
LPCWSTR g_pAppName;
LPCWSTR g_pszGuid;
BOOL g_bPreserveChoice;
WCHAR wszHtmlHelpID[] = L"HtmlHelpID";
WCHAR wszAppName[] = L"AppName";
WCHAR wszSeverity[] = L"Severity";
WCHAR wszGUID[] = L"GUID";
WCHAR wszTagID[] = L"TagID";
WCHAR wszOfflineContent[] = L"OfflineContent";
WCHAR wszPreserveChoice[] = L"PreserveChoice";
WCHAR wszMSI[] = L"MSI";
WCHAR wszPlatform[] = L"Platform";
WCHAR wszX86[] = L"X86";
WCHAR wszIA64[] = L"IA64";
//
// FORWARD DECLARATIONS OF FUNCTIONS
DWORD
ShowApphelpDialog(
IN PAPPHELP_DATA pApphelpData
);
PSID
GetCurrentUserSID(void)
{
HANDLE hProcessToken = INVALID_HANDLE_VALUE;
BOOL bRet = FALSE;
DWORD dwTokenLength = 0;
DWORD dwReturnLength = 0;
DWORD dwSIDLength = 0;
PTOKEN_USER pTokenUser = NULL;
BOOL bSuccess = FALSE;
PSID pSID = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hProcessToken)) {
goto out;
}
GetTokenInformation(hProcessToken, TokenUser, NULL, dwTokenLength, &dwReturnLength);
if (dwReturnLength == 0) {
goto out;
}
dwTokenLength = dwReturnLength;
dwReturnLength = 0;
pTokenUser = (PTOKEN_USER)malloc(dwTokenLength);
if (!pTokenUser) {
goto out;
}
if (!GetTokenInformation(hProcessToken, TokenUser, (LPVOID)pTokenUser, dwTokenLength, &dwReturnLength)) {
goto out;
}
if (!IsValidSid(pTokenUser->User.Sid)) {
goto out;
}
dwSIDLength = GetLengthSid(pTokenUser->User.Sid);
pSID = malloc(dwSIDLength);
if (!pSID) {
goto out;
}
if (!CopySid(dwSIDLength, pSID, pTokenUser->User.Sid)) {
goto out;
}
bSuccess = TRUE;
out:
if (pTokenUser) {
free(pTokenUser);
}
if (hProcessToken != INVALID_HANDLE_VALUE) {
CloseHandle(hProcessToken);
}
if (!bSuccess && pSID) {
free(pSID);
pSID = NULL;
}
return pSID;
}
void
DeleteCurrentUserSID(PSID pSID)
{
if (pSID) {
free(pSID);
}
}
BOOL
AppHelpLogEnabled(
void
)
{
HKEY hKey;
LONG lResult;
DWORD dwValue, dwSize = sizeof(dwValue);
DWORD dwType;
// First, check for a policy.
lResult = RegOpenKeyExW (HKEY_LOCAL_MACHINE, POLICY_KEY_APPCOMPAT_W, 0,
KEY_READ, &hKey);
if (lResult == ERROR_SUCCESS) {
dwValue = 0;
lResult = RegQueryValueExW (hKey, POLICY_VALUE_APPHELP_LOG_W, 0, &dwType,
(LPBYTE) &dwValue, &dwSize);
RegCloseKey (hKey);
}
// Exit if a policy value was found.
if (lResult == ERROR_SUCCESS && dwValue != 0) {
return TRUE;
}
return FALSE;
}
VOID
LogAppHelpEvent(
APPHELP_DATA *pApphelpData
)
{
if (!AppHelpLogEnabled()) {
return;
}
LPCWSTR apszMessage[1];
HANDLE hEventLog = RegisterEventSourceW(NULL, L"apphelp");
if (!hEventLog) {
DWORD dwErr = GetLastError();
} else {
PSID pSID = GetCurrentUserSID();
apszMessage[0] = pApphelpData->szAppName;
if (pApphelpData->dwSeverity == APPHELP_HARDBLOCK) {
ReportEventW(hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
ID_APPHELP_BLOCK_TRIGGERED,
pSID,
2,
0,
apszMessage,
NULL);
} else {
ReportEventW(hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
ID_APPHELP_TRIGGERED,
pSID,
2,
0,
apszMessage,
NULL);
}
DeleteCurrentUserSID(pSID);
DeregisterEventSource(hEventLog);
}
}
DWORD
ShowApphelp(
IN PAPPHELP_DATA pApphelpData,
IN LPCWSTR pwszDetailsDatabasePath,
IN PDB pdbDetails
)
/*++
Return: The return value can be one of the following based on what the user has
selected:
-1 - failed to show the info
IDOK | 0x8000 - "no ui" checked, run the app
IDCANCEL - do not run the app
IDOK - run the app
Desc: Open the details database, collect the details info and then show it.
--*/
{
DWORD dwRet = APPHELP_DIALOG_FAILED;
BOOL bCloseDetails = FALSE;
if (pdbDetails == NULL) {
//
// Open the database containing the details info, if one wasn't passed in.
//
pdbDetails = SdbOpenApphelpDetailsDatabase(pwszDetailsDatabasePath);
bCloseDetails = TRUE;
if (pdbDetails == NULL) {
DBGPRINT((sdlError, "ShowApphelp", "Failed to open the details database.\n"));
goto Done;
}
}
//
// Read apphelp details data.
//
if (!SdbReadApphelpDetailsData(pdbDetails, pApphelpData)) {
DBGPRINT((sdlError, "ShowApphelp", "Failed to read apphelp details.\n"));
goto Done;
}
//
// log an event, if necessary
//
LogAppHelpEvent(pApphelpData);
//
// Show the dialog box. The return values can be:
// -1 - error
// IDOK | 0x8000 - "no ui" checked, run the app
// IDCANCEL - do not run the app
// IDOK - run the app
//
dwRet = ShowApphelpDialog(pApphelpData);
if (dwRet == APPHELP_DIALOG_FAILED) {
DBGPRINT((sdlError, "ShowApphelp", "Failed to show the apphelp info.\n"));
}
Done:
if (pdbDetails != NULL && bCloseDetails) {
SdbCloseDatabase(pdbDetails);
}
return dwRet;
}
void
FixEditControlScrollBar(
IN HWND hDlg,
IN int nCtrlId
)
/*++
Return: void.
Desc: This function tricks the edit control to not show the vertical scrollbar
unless absolutely necessary.
--*/
{
HFONT hFont = NULL;
HFONT hFontOld = NULL;
HDC hDC = NULL;
TEXTMETRICW tm;
RECT rc;
int nVisibleLines = 0;
int nLines;
DWORD dwStyle;
HWND hCtl;
//
// Get the edit control's rectangle.
//
SendDlgItemMessageW(hDlg, nCtrlId, EM_GETRECT, 0, (LPARAM)&rc);
//
// Retrieve the number of lines.
//
nLines = (int)SendDlgItemMessageW(hDlg, nCtrlId, EM_GETLINECOUNT, 0, 0);
//
// Calculate how many lines will fit.
//
hFont = (HFONT)SendDlgItemMessageW(hDlg, nCtrlId, WM_GETFONT, 0, 0);
if (hFont != NULL) {
hDC = CreateCompatibleDC(NULL);
if (hDC != NULL) {
hFontOld = (HFONT)SelectObject(hDC, hFont);
//
// Now get the metrics
//
if (GetTextMetricsW(hDC, &tm)) {
nVisibleLines = (rc.bottom - rc.top) / tm.tmHeight;
}
SelectObject(hDC, hFontOld);
DeleteDC(hDC);
}
}
if (nVisibleLines && nVisibleLines >= nLines) {
hCtl = GetDlgItem(hDlg, nCtrlId);
dwStyle = (DWORD)GetWindowLongPtrW(hCtl, GWL_STYLE);
SetWindowLongPtrW(hCtl, GWL_STYLE, (LONG)(dwStyle & ~WS_VSCROLL));
SetWindowPos(hCtl,
NULL,
0,
0,
0,
0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
}
BOOL
ShowApphelpHtmlHelp(
HWND hDlg,
PAPPHELP_DATA pApphelpData
)
/*++
Return: TRUE on success, FALSE otherwise.
Desc: Shows html help using hhctrl.ocx
--*/
{
WCHAR szAppHelpURL[2048];
WCHAR szWindowsDir[MAX_PATH];
WCHAR szChmFile[MAX_PATH];
WCHAR szChmURL[1024];
HINSTANCE hInst = NULL;
UINT nChars;
int nChURL, nch;
HRESULT hr;
DWORD cch;
LPWSTR lpwszUnescaped = NULL;
BOOL bSuccess = FALSE;
BOOL bCustom = FALSE;
LCID lcid;
size_t nLen;
BOOL bFound = FALSE;
bCustom = !(pApphelpData->dwData & SDB_DATABASE_MAIN);
// apphelp is not in the main database, then it's custom apphelp
nChars = GetSystemWindowsDirectoryW(szWindowsDir,
CHARCOUNT(szWindowsDir));
if (!nChars || nChars > CHARCOUNT(szWindowsDir)) {
DBGPRINT((sdlError, "ShowApphelpHtmlHelp",
"Error trying to retrieve Windows Directory %d.\n", GetLastError()));
goto errHandle;
}
lcid = GetUserDefaultUILanguage();
if (lcid != MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)) {
StringCchPrintfExW(szChmFile,
CHARCOUNT(szChmFile),
NULL,
&nLen,
0,
L"%s\\Help\\MUI\\%04x\\apps.chm",
szWindowsDir,
lcid);
if (nLen > 0) {
bFound = RtlDoesFileExists_U(szChmFile);
}
}
if (!bFound) {
StringCchPrintfW(szChmFile, CHARCOUNT(szChmFile), L"%s\\Help\\apps.chm", szWindowsDir);
}
if (bCustom) {
//
// this is a custom DB, and therefore the URL in it should be taken
// as-is, without using the MS redirector
//
StringCchCopyW(szAppHelpURL, CHARCOUNT(szAppHelpURL), pApphelpData->szURL);
} else {
StringCchPrintfW(szAppHelpURL,
CHARCOUNT(szAppHelpURL),
L"hcp://services/redirect?online=");
nChURL = wcslen(szAppHelpURL);
//
// When we are compiling retail we check for the offline content as well.
//
if (!g_bShowOnlyOfflineContent) {
//
// First thing, unescape url
//
if (pApphelpData->szURL != NULL) {
//
// Unescape the url first, using shell.
//
cch = wcslen(pApphelpData->szURL) + 1;
lpwszUnescaped = (LPWSTR)malloc(cch * sizeof(WCHAR));
if (lpwszUnescaped == NULL) {
DBGPRINT((sdlError,
"ShowApphelpHtmlHelp",
"Error trying to allocate memory for \"%S\"\n",
pApphelpData->szURL));
goto errHandle;
}
//
// Unescape round 1 - use the shell function (same as used to encode
// it for xml/database).
//
hr = UrlUnescapeW(pApphelpData->szURL, lpwszUnescaped, &cch, 0);
if (!SUCCEEDED(hr)) {
DBGPRINT((sdlError,
"ShowApphelpHtmlHelp",
"UrlUnescapeW failed on \"%S\"\n",
pApphelpData->szURL));
goto errHandle;
}
//
// Unescape round 2 - use our function borrowed from help center
//
cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
if (!SdbEscapeApphelpURL(szAppHelpURL + nChURL, &cch, lpwszUnescaped)) {
DBGPRINT((sdlError,
"ShowApphelpHtmlHelp",
"Error escaping URL \"%S\"\n",
lpwszUnescaped));
goto errHandle;
}
nChURL += (int)cch;
}
}
//
// At this point szAppHelpURL contains redirected URL for online usage
// for custom db szAppHelpURL contains full URL
//
// If Apphelp file is provided -- use it
//
if (*g_wszApphelpPath) {
StringCchPrintfW(szChmURL,
CHARCOUNT(szChmURL),
L"mk:@msitstore:%ls::/idh_w2_%d.htm",
g_wszApphelpPath,
pApphelpData->dwHTMLHelpID);
} else {
StringCchPrintfW(szChmURL,
CHARCOUNT(szChmURL),
L"mk:@msitstore:%ls::/idh_w2_%d.htm",
szChmFile,
pApphelpData->dwHTMLHelpID);
}
//
// at this point szChmURL contains a URL pointing to the offline help file
// we put it into the szAppHelpURL for both online and offline case
//
if (g_bShowOnlyOfflineContent) {
cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
if (g_bUseHtmlHelp) {
hr = UrlEscapeW(szChmURL, szAppHelpURL + nChURL, &cch, 0);
if (SUCCEEDED(hr)) {
nChURL += (INT)cch;
}
} else {
if (!SdbEscapeApphelpURL(szAppHelpURL+nChURL, &cch, szChmURL)) {
DBGPRINT((sdlError, "ShowApphelpHtmlHelp", "Error escaping URL \"%S\"\n", szChmURL));
goto errHandle;
}
nChURL += (int)cch;
}
}
//
// now offline sequence
//
cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
StringCchPrintfW(szAppHelpURL + nChURL, cch, L"&offline=");
nch = wcslen(szAppHelpURL + nChURL);
nChURL += nch;
cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
if (!SdbEscapeApphelpURL(szAppHelpURL+nChURL, &cch, szChmURL)) {
DBGPRINT((sdlError, "ShowApphelpHtmlHelp", "Error escaping URL \"%S\"\n", szChmURL));
goto errHandle;
}
nChURL += (int)cch;
*(szAppHelpURL + nChURL) = L'\0';
}
//
// WARNING: On Whistler execution of the following line will cause
// an AV (it works properly on Win2k) when it's executed twice
// from the same process. We should be able to just call
// shell with szAppHelpURL but we can't.
// So for now, use hh.exe as the stub.
//
// right before we do ShellExecute -- set current directory to windows dir
//
if (g_bUseHtmlHelp && !bCustom) {
WCHAR szHHPath[MAX_PATH];
DBGPRINT((sdlInfo, "ShowApphelpHtmlHelp", "Opening Apphelp URL \"%S\"\n", szChmURL));
StringCchCopyW(szHHPath, ARRAYSIZE(szHHPath), szWindowsDir);
StringCchCatW(szHHPath, ARRAYSIZE(szHHPath), L"\\hh.exe");
hInst = ShellExecuteW(hDlg, L"open", szHHPath, szChmURL, NULL, SW_SHOWNORMAL);
} else if (!bCustom) {
WCHAR szHSCPath[MAX_PATH];
WCHAR* pszParameters;
size_t cchUrl = ARRAYSIZE(szAppHelpURL);
static WCHAR szUrlPrefix[] = L"-url ";
StringCchCopyW(szHSCPath, ARRAYSIZE(szHSCPath), szWindowsDir);
StringCchCatW (szHSCPath, ARRAYSIZE(szHSCPath), L"\\pchealth\\helpctr\\binaries\\helpctr.exe");
// format for parameters is " -url <our url>"
StringCchLengthW(szAppHelpURL, ARRAYSIZE(szAppHelpURL), &cchUrl);
cchUrl += CHARCOUNT(szUrlPrefix) + 1;
pszParameters = new WCHAR[cchUrl];
if (pszParameters == NULL) {
bSuccess = FALSE;
goto errHandle;
}
StringCchCopyW(pszParameters, cchUrl, szUrlPrefix);
StringCchCatW (pszParameters, cchUrl, szAppHelpURL);
DBGPRINT((sdlInfo,
"ShowApphelpHtmlHelp",
"Opening APPHELP URL \"%S\"\n",
szAppHelpURL));
hInst = ShellExecuteW(hDlg, L"open", szHSCPath, pszParameters, NULL, SW_SHOWNORMAL);
delete[] pszParameters;
} else {
DBGPRINT((sdlInfo,
"ShowApphelpHtmlHelp",
"Opening Custom APPHELP URL \"%S\"\n",
szAppHelpURL));
hInst = ShellExecuteW(hDlg, L"open", szAppHelpURL, NULL, NULL, SW_SHOWNORMAL);
}
if (HandleToUlong(hInst) <= 32) {
DBGPRINT((sdlError,
"ShowApphelpHtmlHelp",
"Error 0x%p trying to show help topic \"%ls\"\n",
hInst,
szAppHelpURL));
}
//
// If we unload html help now we'll get weird and unpredictable behavior!
// So don't do it :-(
//
bSuccess = (HandleToUlong(hInst) > 32);
errHandle:
if (lpwszUnescaped != NULL) {
free(lpwszUnescaped);
}
return bSuccess;
}
INT_PTR CALLBACK
AppCompatDlgProc(
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
/*++
Return: void.
Desc: This is the dialog proc for the apphelp dialog.
--*/
{
BOOL bReturn = TRUE;
PAPPHELP_DATA pApphelpData;
pApphelpData = (PAPPHELP_DATA)GetWindowLongPtrW(hDlg, DWLP_USER);
switch (uMsg) {
case WM_INITDIALOG:
{
WCHAR wszMessage[2048];
DWORD dwResActionString;
HFONT hFont;
LOGFONTW LogFont;
WCHAR* pwszAppTitle;
INT nChars;
DWORD dwDefID = IDD_DETAILS;
DWORD dwDefBtn; // old default button id
HICON hIcon;
LPWSTR IconID = MAKEINTRESOURCEW(IDI_WARNING);
pApphelpData = (PAPPHELP_DATA)lParam;
SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)pApphelpData);
//
// Show the app icon.
//
hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDD_ICON_TRASH));
SetClassLongPtr(hDlg, GCLP_HICON, (LONG_PTR)hIcon);
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, 0, 0);
SetForegroundWindow(hDlg);
pwszAppTitle = pApphelpData->szAppTitle;
if (pwszAppTitle == NULL) {
pwszAppTitle = pApphelpData->szAppName;
}
if (pwszAppTitle != NULL) {
SetDlgItemTextW(hDlg, IDD_APPNAME, pwszAppTitle);
//
// Make sure that we only utilize the first line of that text
// for the window title.
//
SetWindowTextW(hDlg, pwszAppTitle);
}
hFont = (HFONT)SendDlgItemMessageW(hDlg,
IDD_APPNAME,
WM_GETFONT,
0, 0);
if (hFont && GetObjectW(hFont, sizeof(LogFont), (LPVOID)&LogFont)) {
LogFont.lfWeight = FW_BOLD;
hFont = CreateFontIndirectW(&LogFont);
if (hFont != NULL) {
g_hFontBold = hFont;
SendDlgItemMessageW(hDlg,
IDD_APPNAME,
WM_SETFONT,
(WPARAM)hFont, TRUE);
}
}
//
// By default, we have both RUN AND CANCEL
//
dwResActionString = IDS_APPCOMPAT_RUNCANCEL;
switch (pApphelpData->dwSeverity) {
case APPHELP_HARDBLOCK:
//
// Disable run button and "don't show this again" box
// Reset the "defpushbutton" style from this one...
//
EnableWindow(GetDlgItem(hDlg, IDD_STATE), FALSE);
EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
dwResActionString = IDS_APPCOMPAT_CANCEL;
dwDefID = IDD_DETAILS; // set for hardblock case since RUN is not avail
IconID = MAKEINTRESOURCEW(IDI_ERROR);
break;
case APPHELP_MINORPROBLEM:
break;
case APPHELP_NOBLOCK:
break;
case APPHELP_REINSTALL:
break;
}
//
// if we have no URL, or the URL begins with "null" gray out the "details" button
//
if (!pApphelpData->szURL || !pApphelpData->szURL[0] ||
_wcsnicmp(pApphelpData->szURL, L"null", 4) == 0) {
EnableWindow(GetDlgItem(hDlg, IDD_DETAILS), FALSE);
}
hIcon = LoadIconW(NULL, IconID);
if (hIcon != NULL) {
SendDlgItemMessageW(hDlg, IDD_ICON, STM_SETICON, (WPARAM)hIcon, 0);
}
//
// Set the default push button
// Reset the current default push button to a regular button.
//
// Update the default push button's control ID.
//
dwDefBtn = (DWORD)SendMessageW(hDlg, DM_GETDEFID, 0, 0);
if (HIWORD(dwDefBtn) == DC_HASDEFID) {
dwDefBtn = LOWORD(dwDefBtn);
SendDlgItemMessageW(hDlg, dwDefBtn, BM_SETSTYLE, BS_PUSHBUTTON, TRUE);
}
SendDlgItemMessageW(hDlg, dwDefID, BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE);
SendMessageW(hDlg, DM_SETDEFID, (WPARAM)dwDefID, 0);
//
// now set the focus
// be careful and do not mess with other focus-related messages, else use PostMessage here
//
SendMessageW(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, dwDefID), TRUE);
//
// If dwHTMLHelpID is not present disable "Details" button
//
if (!pApphelpData->dwHTMLHelpID) {
EnableWindow(GetDlgItem(hDlg, IDD_DETAILS), FALSE);
}
wszMessage[0] = L'\0';
LoadStringW(g_hInstance,
dwResActionString,
wszMessage,
sizeof(wszMessage) / sizeof(WCHAR));
SetDlgItemTextW(hDlg, IDD_LINE_2, wszMessage);
SetDlgItemTextW(hDlg,
IDD_APPHELP_DETAILS,
pApphelpData->szDetails ? pApphelpData->szDetails : L"");
FixEditControlScrollBar(hDlg, IDD_APPHELP_DETAILS);
//
// Return false so that the default focus-setting would not apply.
//
bReturn = FALSE;
break;
}
case WM_DESTROY:
//
// perform cleanup - remove the font we've had created
//
if (g_hFontBold != NULL) {
DeleteObject(g_hFontBold);
g_hFontBold = NULL;
}
AllowForegroundActivation();
PostQuitMessage(0); // we just bailed out
break;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDOK:
//
// Check the NO UI checkbox
//
EndDialog(hDlg, (INT_PTR)(IsDlgButtonChecked(hDlg, IDD_STATE) ? (IDOK | 0x8000) : IDOK));
break;
case IDCANCEL:
EndDialog(hDlg, (INT_PTR)(IsDlgButtonChecked(hDlg, IDD_STATE) && g_bPreserveChoice ? (IDCANCEL | 0x8000) : IDCANCEL));
break;
case IDD_DETAILS:
//
// Launch details.
//
ShowApphelpHtmlHelp(hDlg, pApphelpData);
break;
default:
bReturn = FALSE;
break;
}
break;
default:
bReturn = FALSE;
break;
}
return bReturn;
}
typedef NTSTATUS (NTAPI *PFNUSERTESTTOKENFORINTERACTIVE)(HANDLE Token, PLUID pluidCaller);
PFNUSERTESTTOKENFORINTERACTIVE UserTestTokenForInteractive = NULL;
BOOL
CheckUserToken(
)
/*++
returns TRUE if the apphelp should be shown
FALSE if we should not present apphelp UI
--*/
{
NTSTATUS Status;
HANDLE hToken = NULL;
LUID LuidUser;
HMODULE hWinsrv = NULL;
BOOL bShowUI = FALSE;
UINT nChars;
WCHAR szSystemDir[MAX_PATH];
WCHAR szWinsrvDll[MAX_PATH];
static
WCHAR szWinsrvDllName[] = L"winsrv.dll";
nChars = GetSystemDirectoryW(szSystemDir, CHARCOUNT(szSystemDir));
if (nChars == 0 || nChars > CHARCOUNT(szSystemDir) - 2 - CHARCOUNT(szWinsrvDllName)) {
DBGPRINT((sdlError, "CheckUserToken",
"Error trying to get systemdirectory %d.\n", GetLastError()));
*szSystemDir = L'\0';
} else {
szSystemDir[nChars] = L'\\';
szSystemDir[nChars + 1] = L'\0';
}
StringCchCopyW(szWinsrvDll, ARRAYSIZE(szWinsrvDll), szSystemDir);
StringCchCatW (szWinsrvDll, ARRAYSIZE(szWinsrvDll), szWinsrvDllName);
hWinsrv = LoadLibraryW(szWinsrvDll);
if (hWinsrv == NULL) {
goto ErrHandle;
}
UserTestTokenForInteractive = (PFNUSERTESTTOKENFORINTERACTIVE)GetProcAddress(hWinsrv,
"_UserTestTokenForInteractive");
if (UserTestTokenForInteractive == NULL) {
goto ErrHandle;
}
Status = NtOpenProcessToken(NtCurrentProcess(),
TOKEN_QUERY,
&hToken);
if (NT_SUCCESS(Status)) {
Status = UserTestTokenForInteractive(hToken, &LuidUser);
NtClose(hToken);
if (NT_SUCCESS(Status)) {
bShowUI = TRUE;
goto ErrHandle;
}
}
Status = NtOpenThreadToken(NtCurrentThread(),
TOKEN_QUERY,
TRUE,
&hToken);
if (NT_SUCCESS(Status)) {
Status = UserTestTokenForInteractive(hToken, &LuidUser);
NtClose(hToken);
if (NT_SUCCESS(Status)) {
bShowUI = TRUE;
goto ErrHandle;
}
}
ErrHandle:
if (hWinsrv) {
FreeLibrary(hWinsrv);
}
return bShowUI;
}
BOOL
CheckWindowStation(
)
/*++
returns TRUE if the apphelp should be shown
FALSE if we should not bother with apphelp UI
--*/
{
HWINSTA hWindowStation;
BOOL bShowUI = FALSE;
DWORD dwLength = 0;
DWORD dwBufferSize = 0;
DWORD dwError;
BOOL bSuccess;
LPWSTR pwszWindowStation = NULL;
hWindowStation = GetProcessWindowStation();
if (hWindowStation == NULL) {
DBGPRINT((sdlError,
"ApphelpCheckWindowStation",
"GetProcessWindowStation failed error 0x%lx\n", GetLastError()));
goto ErrHandle; // the app is not a Windows NT/Windows 2000 app??? try to show UI
}
// get the information please
bSuccess = GetUserObjectInformationW(hWindowStation, UOI_NAME, NULL, 0, &dwBufferSize);
if (!bSuccess) {
dwError = GetLastError();
if (dwError != ERROR_INSUFFICIENT_BUFFER) {
DBGPRINT((sdlError,
"ApphelpCheckWindowStation",
"GetUserObjectInformation failed error 0x%lx\n", dwError));
goto ErrHandle;
}
pwszWindowStation = (LPWSTR)malloc(dwBufferSize);
if (pwszWindowStation == NULL) {
DBGPRINT((sdlError,
"ApphelpCheckWindowStation",
"Failed to allocate 0x%lx bytes for Window Station name\n", dwBufferSize));
goto ErrHandle;
}
// ok, call again
bSuccess = GetUserObjectInformationW(hWindowStation,
UOI_NAME,
pwszWindowStation,
dwBufferSize,
&dwLength);
if (!bSuccess) {
DBGPRINT((sdlError,
"ApphelpCheckWindowStation",
"GetUserObjectInformation failed error 0x%lx, buffer size 0x%lx returned 0x%lx\n",
GetLastError(), dwBufferSize, dwLength));
goto ErrHandle;
}
// now we have window station name, compare it to winsta0
//
bShowUI = (_wcsicmp(pwszWindowStation, L"Winsta0") == 0);
if (!bShowUI) {
DBGPRINT((sdlInfo,
"ApphelpCheckWindowStation",
"Apphelp UI will not be shown, running this process on window station \"%s\"\n",
pwszWindowStation));
}
}
//
// presumably we will try and check the token for interactive access
//
ErrHandle:
// should we do a close handle ???
//
if (hWindowStation != NULL) {
CloseWindowStation(hWindowStation);
}
if (pwszWindowStation != NULL) {
free(pwszWindowStation);
}
return bShowUI;
}
DWORD
ShowApphelpDialog(
IN PAPPHELP_DATA pApphelpData
)
/*++
Return: (IDOK | IDCANCEL) | [0x8000]
IDOK | 0x8000 - the user has chosen to run the app and
checked "don't show me this anymore"
IDOK - the user has chosen to run the app, dialog will be shown again
IDCANCEL - the user has chosen not to run the app
-1 - we have failed to import APIs necessary to show dlg box
Desc: Shows the dialog box with apphelp info.
--*/
{
BOOL bSuccess;
INT_PTR retVal = 0;
retVal = DialogBoxParamW(g_hInstance,
MAKEINTRESOURCEW(DLG_APPCOMPAT),
NULL,
AppCompatDlgProc,
(LPARAM)pApphelpData); // parameter happens to be a pointer of type PAPPHELP_DATA
return (DWORD)retVal;
}
VOID
ParseCommandLineArgs(
int argc,
WCHAR* argv[]
)
{
WCHAR ch;
WCHAR* pArg;
WCHAR* pEnd;
while (--argc) {
pArg = argv[argc];
if (*pArg == L'/' || *pArg == '-') {
ch = *++pArg;
switch(towupper(ch)) {
case L'H':
if (!_wcsnicmp(pArg, wszHtmlHelpID, CHARCOUNT(wszHtmlHelpID)-1)) {
pArg = wcschr(pArg, L':');
if (pArg) {
++pArg; // skip over :
g_dwHtmlHelpID = (DWORD)wcstoul(pArg, &pEnd, 0);
}
}
break;
case L'A':
if (!_wcsnicmp(pArg, wszAppName, CHARCOUNT(wszAppName)-1)) {
pArg = wcschr(pArg, L':');
if (pArg) {
++pArg;
g_pAppName = pArg; // this is app name, remove the quotes
}
}
break;
case L'S':
if (!_wcsnicmp(pArg, wszSeverity, CHARCOUNT(wszSeverity)-1)) {
pArg = wcschr(pArg, L':');
if (pArg) {
++pArg;
g_dwSeverity = (DWORD)wcstoul(pArg, &pEnd, 0);
}
}
break;
case L'T':
if (!_wcsnicmp(pArg, wszTagID, CHARCOUNT(wszTagID)-1)) {
pArg = wcschr(pArg, L':');
if (pArg) {
++pArg;
g_dwTagID = (DWORD)wcstoul(pArg, &pEnd, 0);
}
}
break;
case L'G':
if (!_wcsnicmp(pArg, wszGUID, CHARCOUNT(wszGUID)-1)) {
if ((pArg = wcschr(pArg, L':')) != NULL) {
++pArg;
g_pszGuid = pArg;
}
}
break;
case L'O':
if (!_wcsnicmp(pArg, wszOfflineContent, CHARCOUNT(wszOfflineContent)-1)) {
g_bShowOnlyOfflineContent = TRUE;
}
break;
case L'P':
if (!_wcsnicmp(pArg, wszPreserveChoice, CHARCOUNT(wszPreserveChoice)-1)) {
g_bPreserveChoice = TRUE;
} else if (!_wcsnicmp(pArg, wszPlatform, CHARCOUNT(wszPlatform) - 1)) {
if ((pArg = wcschr(pArg, L':')) != NULL) {
++pArg;
//
// identify each of the supported platforms
//
if (!_wcsnicmp(pArg, wszX86, CHARCOUNT(wszX86) - 1)) {
g_uExeImageType = IMAGE_FILE_MACHINE_I386;
} else if (!_wcsnicmp(pArg, wszIA64, CHARCOUNT(wszIA64) - 1)) {
g_uExeImageType = IMAGE_FILE_MACHINE_IA64;
}
}
}
break;
case L'M':
if (!_wcsnicmp(pArg, wszMSI, CHARCOUNT(wszMSI))) {
g_bMSI = TRUE;
}
break;
default:
// unrecognized switch
DBGPRINT((sdlError, "ParseCommandLineArgs",
"Unrecognized parameter %s\n", pArg));
break;
}
} else {
// not a switch
if (*pArg == L'{') {
g_pszGuid = pArg;
} else {
g_dwTagID = (DWORD)wcstoul(pArg, &pEnd, 0);
}
}
}
}
INT_PTR CALLBACK
FeedbackDlgProc(
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
/*++
Return: void.
Desc: This is the dialog proc for the apphelp dialog.
--*/
{
BOOL bReturn = TRUE;
LPWSTR lpszExeName;
lpszExeName = (LPWSTR)GetWindowLongPtrW(hDlg, DWLP_USER);
switch (uMsg) {
case WM_INITDIALOG:
{
HICON hIcon;
lpszExeName = (LPWSTR)lParam;
SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)lpszExeName);
//
// Show the app icon.
//
hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDD_ICON_TRASH));
SetClassLongPtr(hDlg, GCLP_HICON, (LONG_PTR)hIcon);
SendDlgItemMessage(hDlg, IDC_WORKED, BM_SETCHECK, BST_CHECKED, 0);
}
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDOK:
EndDialog(hDlg, IDOK);
break;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
break;
default:
bReturn = FALSE;
break;
}
break;
default:
bReturn = FALSE;
break;
}
return bReturn;
}
void
ShowFeedbackDialog(
LPWSTR lpszAppName
)
{
DialogBoxParamW(g_hInstance,
MAKEINTRESOURCEW(DLG_FEEDBACK),
NULL,
FeedbackDlgProc,
(LPARAM)lpszAppName);
}
BOOL
CheckWOW64Redirection(
int* pReturnCode
)
/*++
Return: TRUE if redirected
FALSE if no redirection is needed
Desc: Checks whether we're running on wow64, if so - redirects to 64-bit ahui.exe
--*/
{
LPWSTR pszCommandLine;
UINT nChars;
WCHAR szWindowsDir[MAX_PATH];
WCHAR szAhuiExePath[MAX_PATH];
STARTUPINFOW StartupInfo = { 0 };
PROCESS_INFORMATION ProcessInfo = { 0 };
static
WCHAR szPlatformX86[] = L"/Platform:X86";
WCHAR* pszAhuiCmdLine = NULL;
DWORD dwExit = 0;
BOOL bWow64 = FALSE;
BOOL bReturn = FALSE;
BOOL bRedirect = FALSE;
WCHAR** argv = NULL;
int nArg, argc;
size_t cchCmd;
size_t cchLeft;
WCHAR* pchCur;
#ifndef _WIN64
if (IsWow64Process(GetCurrentProcess(), &bWow64)) {
if (!bWow64) {
return FALSE;
}
}
// if a check for wow64 failes, we do not redirect and will try to check
// for interactive token. That test will fail and we won't show any UI,
// which is better than the alternative (getting a service stuck on a
// dialog which is invisible
#endif // _WIN64
//
// we are running in the context of a wow64 process, redirect to native 64-bit ahui.exe
//
nChars = GetSystemWindowsDirectoryW(szWindowsDir,
CHARCOUNT(szWindowsDir));
if (!nChars || nChars > CHARCOUNT(szWindowsDir) - 1) {
DBGPRINT((sdlError, "CheckWow64Redirection",
"Error trying to retrieve Windows Directory %d.\n", GetLastError()));
goto out;
}
StringCchPrintfW(szAhuiExePath, CHARCOUNT(szAhuiExePath), L"%s\\system32\\ahui.exe", szWindowsDir);
#ifndef _WIN64
Wow64DisableFilesystemRedirector(szAhuiExePath);
bRedirect = TRUE;
#endif
pszCommandLine = GetCommandLineW();
if (pszCommandLine == NULL) {
goto out;
}
argv = CommandLineToArgvW(pszCommandLine, &argc);
if (argv != NULL) {
for (cchCmd = 0, nArg = 1; nArg < argc; ++nArg) {
cchCmd += wcslen(argv[nArg]) + 2 + 1; // 2 for " " and 1 for space
}
}
// now allocate space for command line, ahui exe path and additional parameter (/Platform:x86)
nChars = wcslen(szAhuiExePath) + cchCmd + CHARCOUNT(szPlatformX86) + 3;
pszAhuiCmdLine = new WCHAR[nChars];
if (pszAhuiCmdLine == NULL) {
goto out;
}
pchCur = pszAhuiCmdLine;
cchLeft = nChars;
StringCchPrintfExW(pszAhuiCmdLine, nChars, &pchCur, &cchLeft, 0, L"%s ", szAhuiExePath);
if (argv != NULL) {
for (nArg = 1; nArg < argc; ++nArg) {
//
// check for platform parameter, if it exists -- skip it
//
if ((*argv[nArg] == L'/' || *argv[nArg] == L'-') &&
_wcsnicmp(argv[nArg] + 1, wszPlatform, CHARCOUNT(wszPlatform) - 1) == 0) {
continue;
}
StringCchPrintfExW(pchCur, cchLeft, &pchCur, &cchLeft, 0, L" \"%s\"", argv[nArg]);
}
}
StringCchPrintfExW(pchCur, cchLeft, &pchCur, &cchLeft, 0, L" %s", szPlatformX86);
StartupInfo.cb = sizeof(StartupInfo);
if (!CreateProcessW(szAhuiExePath,
pszAhuiCmdLine,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&StartupInfo, &ProcessInfo)) {
DBGPRINT((sdlError, "CheckWow64Redirection",
"Failed to launch apphelp process %d.\n", GetLastError()));
goto out;
}
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
bReturn = GetExitCodeProcess(ProcessInfo.hProcess, &dwExit);
if (bReturn) {
*pReturnCode = (int)dwExit;
bReturn = TRUE;
}
out:
if (pszAhuiCmdLine) {
delete[] pszAhuiCmdLine;
}
if (argv) {
HGLOBAL hMem = GlobalHandle(argv);
if (hMem != NULL) {
GlobalFree(hMem);
}
}
#ifndef _WIN64
if (bRedirect) {
Wow64EnableFilesystemRedirector();
}
#endif
return bReturn;
}
extern "C" int APIENTRY
wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nCmdShow
)
/*++
Return: 1 if the app for which the apphelp is shown should run, 0 otherwise.
Desc: The command line looks like this:
apphelp.exe GUID tagID [USELOCALCHM USEHTMLHELP APPHELPPATH]
Ex:
apphelp.exe {243B08D7-8CF7-4072-AF64-FD5DF4085E26} 0x0000009E
apphelp.exe {243B08D7-8CF7-4072-AF64-FD5DF4085E26} 0x0000009E 1 1 c:\temp
--*/
{
int nReturn = 1; // we always default to running, if something goes wrong
LPWSTR szCommandLine;
LPWSTR* argv;
int argc;
UNICODE_STRING ustrGuid;
GUID guidDB = { 0 };
GUID guidExeID = { 0 };
TAGID tiExe = TAGID_NULL;
TAGID tiExeID = TAGID_NULL;
TAGREF trExe = TAGREF_NULL;
WCHAR wszDBPath[MAX_PATH];
DWORD dwType = 0;
APPHELP_DATA ApphelpData;
WCHAR szDBPath[MAX_PATH];
HSDB hSDB = NULL;
PDB pdb = NULL;
DWORD dwFlags = 0;
BOOL bAppHelp = FALSE;
BOOL bRunApp = FALSE;
g_hInstance = hInstance;
#ifndef _WIN64
if (CheckWOW64Redirection(&nReturn)) {
goto out;
}
#endif // _WIN64
InitCommonControls();
ZeroMemory(&ApphelpData, sizeof(ApphelpData));
//
// Note that this memory isn't freed because it will automatically
// be freed on exit anyway, and there are a lot of exit cases from
// this application.
//
szCommandLine = GetCommandLineW();
argv = CommandLineToArgvW(szCommandLine, &argc);
ParseCommandLineArgs(argc, argv);
if (argc > 1) {
if (_wcsicmp(L"feedback", argv[1]) == 0) {
ShowFeedbackDialog(argc > 2 ? argv[2] : NULL);
}
}
if (g_pszGuid == NULL) {
DBGPRINT((sdlError, "AHUI!wWinMain",
"GUID not provided\n"));
goto out;
}
if (!(g_dwTagID ^ g_dwHtmlHelpID)) {
DBGPRINT((sdlError, "AHUI!wWinMain",
"Only TagID or HtmlHelpID should be provided\n"));
goto out;
}
RtlInitUnicodeString(&ustrGuid, g_pszGuid);
if (g_dwHtmlHelpID) {
//
// provided here: guid, severity and html help id along with app name
//
if (!NT_SUCCESS(RtlGUIDFromString(&ustrGuid, &guidExeID))) {
DBGPRINT((sdlError,
"Ahui!wWinMain",
"Error getting GUID from string %s\n", g_pszGuid));
goto out;
}
ApphelpData.dwSeverity = g_dwSeverity;
ApphelpData.dwHTMLHelpID = g_dwHtmlHelpID;
ApphelpData.szAppName = (LPWSTR)g_pAppName;
bAppHelp = TRUE;
dwType = SDB_DATABASE_MAIN_SHIM;
goto ProceedWithApphelp;
}
// non-htmlid case, guid is a database guid
// also dwTagID is specified
if (RtlGUIDFromString(&ustrGuid, &guidDB) != STATUS_SUCCESS) {
DBGPRINT((sdlError,
"AppHelp.exe!wWinMain",
"Error trying to convert GUID string %S.\n",
g_pszGuid));
goto out;
}
tiExe = (TAGID)g_dwTagID;
if (tiExe == TAGID_NULL) {
DBGPRINT((sdlError,
"AppHelp.exe!wWinMain",
"Error getting TAGID from param %S\n",
argv[2]));
goto out;
}
hSDB = SdbInitDatabaseEx(0, NULL, g_uExeImageType);
if (!hSDB) {
DBGPRINT((sdlError,
"AppHelp.exe!wWinMain",
"Error initing database context.\n"));
goto out;
}
if (g_bMSI) {
SdbSetImageType(hSDB, IMAGE_FILE_MSI);
}
pdb = SdbGetPDBFromGUID(hSDB, &guidDB);
if (!pdb) {
DWORD dwLen;
//
// It's not one of the main DBs, try it as a local.
//
dwLen = SdbResolveDatabase(hSDB, &guidDB, &dwType, szDBPath, MAX_PATH);
if (!dwLen || dwLen > MAX_PATH) {
DBGPRINT((sdlError,
"AppHelp.exe!wWinMain",
"Error resolving database from GUID\n"));
goto out;
}
//
// We have many "main" databases -- we should limit the check
//
if (dwType != SDB_DATABASE_MAIN_SHIM && dwType != SDB_DATABASE_MAIN_TEST) {
SdbOpenLocalDatabase(hSDB, szDBPath);
}
pdb = SdbGetPDBFromGUID(hSDB, &guidDB);
if (!pdb) {
DBGPRINT((sdlError,
"AppHelp.exe!wWinMain",
"Error getting pdb from GUID.\n"));
goto out;
}
} else {
dwType |= SDB_DATABASE_MAIN; // we will use details from the main db
}
if (!SdbTagIDToTagRef(hSDB, pdb, tiExe, &trExe)) {
DBGPRINT((sdlError,
"AppHelp.exe!wWinMain",
"Error converting TAGID to TAGREF.\n"));
goto out;
}
tiExeID = SdbFindFirstTag(pdb, tiExe, TAG_EXE_ID);
if (tiExeID == TAGID_NULL) {
DBGPRINT((sdlError,
"AppHelp.exe!wWinMain",
"Error trying to find TAG_EXE_ID.\n"));
goto out;
}
if (!SdbReadBinaryTag(pdb,
tiExeID,
(PBYTE)&guidExeID,
sizeof(GUID))) {
DBGPRINT((sdlError,
"AppHelp.exe!wWinMain",
"Error trying to read TAG_EXE_ID.\n"));
goto out;
}
bAppHelp = SdbReadApphelpData(hSDB, trExe, &ApphelpData);
ProceedWithApphelp:
if (SdbIsNullGUID(&guidExeID) || !SdbGetEntryFlags(&guidExeID, &dwFlags)) {
dwFlags = 0;
}
if (bAppHelp) {
//
// Check whether the disable bit is set.
//
if (!(dwFlags & SHIMREG_DISABLE_APPHELP)) {
BOOL bNoUI;
//
// See whether the user has checked "Don't show this anymore" box before.
//
bNoUI = ((dwFlags & SHIMREG_APPHELP_NOUI) != 0);
if (!bNoUI) {
bNoUI = !CheckWindowStation();
}
if (!bNoUI) {
bNoUI = !CheckUserToken();
}
if (bNoUI) {
DBGPRINT((sdlInfo,
"bCheckRunBadapp",
"NoUI flag is set, apphelp UI disabled for this app.\n"));
}
//
// depending on severity of the problem...
//
switch (ApphelpData.dwSeverity) {
case APPHELP_MINORPROBLEM:
case APPHELP_HARDBLOCK:
case APPHELP_NOBLOCK:
case APPHELP_REINSTALL:
if (bNoUI) {
bRunApp = (ApphelpData.dwSeverity != APPHELP_HARDBLOCK) && !(dwFlags & SHIMREG_APPHELP_CANCEL);
} else {
DWORD dwRet;
//
// Show the UI. This function returns -1 in case of error or one
// of the following values on success:
// IDOK | 0x8000 - "no ui" checked, run app
// IDCANCEL - do not run app
// IDOK - run app
ApphelpData.dwData = dwType; // we use custom data for database type
dwRet = ShowApphelp(&ApphelpData, NULL, (dwType & SDB_DATABASE_MAIN) ? NULL : SdbGetLocalPDB(hSDB));
if (dwRet != APPHELP_DIALOG_FAILED) {
//
// The UI was shown. See whether the user has
// checked the "no ui" box.
//
if (dwRet & 0x8000) {
//
// "no ui" box was checked. Save the appropriate bits
// in the registry.
//
dwFlags |= SHIMREG_APPHELP_NOUI;
if ((dwRet & 0x0FFF) != IDOK) {
dwFlags |= SHIMREG_APPHELP_CANCEL; // we will not be hitting this path unless g_bPreserveChoice is enabled
}
if (!SdbIsNullGUID(&guidExeID)) {
SdbSetEntryFlags(&guidExeID, dwFlags);
}
}
//
// Check whether the user has chosen to run the app.
//
bRunApp = ((dwRet & 0x0FFF) == IDOK) && (ApphelpData.dwSeverity != APPHELP_HARDBLOCK);
} else {
//
// The UI was not shown (some error prevented that).
// If the app is not "Hardblock" run it anyway.
//
bRunApp = (APPHELP_HARDBLOCK != ApphelpData.dwSeverity);
}
}
break;
}
}
}
if (!bRunApp) {
nReturn = 0;
}
out:
return nReturn;
}