/*++ Copyright (c) 1989-2000 Microsoft Corporation Module Name: check.c Abstract: This module implements the main API that CreateProcess calls to check if an EXE is shimmed or apphelpped. Author: vadimb created sometime in 2000 Revision History: clupu cleanup 12/27/2000 andyseti added ApphelpCheckExe 03/29/2001 andyseti added ApphelpCheckInstallShieldPackage 06/28/2001 --*/ #include "apphelp.h" #define STRSAFE_NO_CB_FUNCTIONS #include static const WCHAR ShimEngine_ModuleName[] = L"Shimeng.dll"; static const CHAR DynamicShimProcedureName[] = "SE_DynamicShim"; static const CHAR DynamicUnshimProcedureName[] = "SE_DynamicUnshim"; typedef BOOL (WINAPI *_pfn_SE_DynamicShim)(LPCWSTR , HSDB , SDBQUERYRESULT*, LPCSTR, LPDWORD); typedef BOOL (WINAPI *_pfn_SE_DynamicUnshim)(DWORD); typedef struct tagEXE_DYN_SHIM_INFO { LIST_ENTRY entry; LPWSTR pwszFullPath; // full path to the executable. DWORD dwToken; // the dynamic shimming token associated with this exe. } EXE_DYN_SHIM_INFO, *PEXE_DYN_SHIM_INFO; LIST_ENTRY g_ExeDynShimInfoList; BOOL AddExeDynShimInfoToList( IN LPCWSTR pwszFullPath, IN DWORD dwToken ); BOOL RemoveExeDynShimInfoFromList( IN LPCWSTR pwszFullPath ); extern HINSTANCE ghInstance; // // Prototypes of internal functions // void GetExeNTVDMData( IN HSDB hSDB, // the SDB context IN PSDBQUERYRESULT psdbQuery, // the EXEs and LAYERs that are active OUT WCHAR* pszCompatLayer, // The new compat layer variable. with format: // "Alpha Bravo Charlie" OUT PNTVDM_FLAGS pFlags // The flags ); // // Appcompat Infrastructure disable-via-policy-flag // DWORD gdwInfrastructureFlags; // initialized to 0 #define APPCOMPAT_INFRA_DISABLED 0x00000001 #define APPCOMPAT_INFRA_VALID_FLAG 0x80000000 #define IsAppcompatInfrastructureDisabled() \ (!!( (gdwInfrastructureFlags & APPCOMPAT_INFRA_VALID_FLAG) ? \ (gdwInfrastructureFlags & APPCOMPAT_INFRA_DISABLED) : \ (CheckAppcompatInfrastructureFlags() & APPCOMPAT_INFRA_DISABLED)) ) DWORD CheckAppcompatInfrastructureFlags( VOID ); #if DBG BOOL bDebugChum( void ) /*++ Return: TRUE on success, FALSE otherwise. Desc: Checks an env var. If the var is present return TRUE. --*/ { UNICODE_STRING ustrDebugChum; UNICODE_STRING ustrDebugChumVal = { 0 }; NTSTATUS Status; RtlInitUnicodeString(&ustrDebugChum, L"DEBUG_OFFLINE_CONTENT"); Status = RtlQueryEnvironmentVariable_U(NULL, &ustrDebugChum, &ustrDebugChumVal); if (Status == STATUS_BUFFER_TOO_SMALL) { return TRUE; } return FALSE; } #else // DBG #define bDebugChum() TRUE #endif // DBG BOOL GetExeID( IN PDB pdb, // the pointer to the database IN TAGID tiExe, // the TAGID of the EXE for which we need the ID OUT GUID* pGuid // will receive the EXE's ID ) /*++ Return: TRUE on success, FALSE otherwise. Desc: Reads the EXE's ID from the database using the EXE's tag id. --*/ { TAGID tiExeID; tiExeID = SdbFindFirstTag(pdb, tiExe, TAG_EXE_ID); if (tiExeID == TAGID_NULL) { DBGPRINT((sdlError, "GetExeID", "EXE tag 0x%x without an ID !\n", tiExe)); return FALSE; } if (!SdbReadBinaryTag(pdb, tiExeID, (PBYTE)pGuid, sizeof(*pGuid))) { DBGPRINT((sdlError, "GetExeID", "Cannot read the ID for EXE tag 0x%x.\n", tiExe)); return FALSE; } return TRUE; } BOOL GetExeIDByTagRef( IN HSDB hSDB, // handle to the database object IN TAGREF trExe, // EXE tag ref OUT GUID* pGuid // will receive the EXE's ID ) /*++ Return: TRUE on success, FALSE otherwise. Desc: Reads the EXE's ID from the database using the EXE's tag ref. --*/ { PDB pdb; TAGID tiExe; TAGID tiExeID; if (!SdbTagRefToTagID(hSDB, trExe, &pdb, &tiExe)) { DBGPRINT((sdlError, "GetExeIDByTagRef", "Failed to get the tag id from EXE tag ref 0x%x.\n", tiExe)); return FALSE; } return GetExeID(pdb, tiExe, pGuid); } #define APPHELP_CLSID_REG_PATH L"\\Registry\\Machine\\Software\\Classes\\CLSID\\" #define APPHELP_INPROCSERVER32 L"\\InProcServer32" DWORD ResolveCOMServer( IN REFCLSID CLSID, OUT LPWSTR lpPath, OUT DWORD dwBufSize) { DWORD dwReqBufSize = 0; UNICODE_STRING ustrKey = { 0 }; UNICODE_STRING ustrValueName = { 0 }; UNICODE_STRING ustrGuid = { 0 }; UNICODE_STRING ustrUnexpandedValue = { 0 }; UNICODE_STRING ustrValue = { 0 }; NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE KeyHandle = NULL; PKEY_VALUE_FULL_INFORMATION pKeyValueInfo = NULL; DWORD dwKeyValueInfoSize = 0; DWORD dwKeyValueInfoReqSize = 0; LPWSTR wszCLSIDRegFullPath = NULL; DWORD dwCLSIDRegFullPathSize = 0; WCHAR wszCLSID[41] = { 0 }; // Enough for path + CLSID in string form dwCLSIDRegFullPathSize = wcslen(APPHELP_CLSID_REG_PATH) + wcslen(APPHELP_INPROCSERVER32) + 64; wszCLSIDRegFullPath = RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, dwCLSIDRegFullPathSize * sizeof(WCHAR)); if (wszCLSIDRegFullPath == NULL) { DBGPRINT((sdlInfo, "SdbResolveCOMServer", "Memory allocation failure\n")); goto Done; } StringCchCopyW(wszCLSIDRegFullPath, dwCLSIDRegFullPathSize, APPHELP_CLSID_REG_PATH); Status = RtlStringFromGUID(CLSID, &ustrGuid); if (!NT_SUCCESS(Status)) { DBGPRINT((sdlInfo, "SdbResolveCOMServer", "Malformed CLSID\n")); goto Done; } if (ustrGuid.Length/sizeof(WCHAR) > 40) { DBGPRINT((sdlInfo, "SdbResolveCOMServer", "CLSID more than 40 characters\n")); goto Done; } RtlMoveMemory(wszCLSID, ustrGuid.Buffer, ustrGuid.Length); wszCLSID[ustrGuid.Length/sizeof(WCHAR)] = L'\0'; StringCchCatW(wszCLSIDRegFullPath, dwCLSIDRegFullPathSize, wszCLSID); StringCchCatW(wszCLSIDRegFullPath, dwCLSIDRegFullPathSize, APPHELP_INPROCSERVER32); RtlInitUnicodeString(&ustrKey, wszCLSIDRegFullPath); RtlInitUnicodeString(&ustrValueName, L""); InitializeObjectAttributes(&ObjectAttributes, &ustrKey, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&KeyHandle, GENERIC_READ, &ObjectAttributes); if (!NT_SUCCESS(Status)) { DBGPRINT((sdlInfo, "SdbResolveCOMServer", "Failed to open Key \"%s\" Status 0x%x\n", wszCLSIDRegFullPath, Status)); goto Done; } if (lpPath == NULL && dwBufSize != 0) { DBGPRINT((sdlInfo, "SdbResolveCOMServer", "Bad parameters\n")); goto Done; } pKeyValueInfo = RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, dwBufSize * 2); if (pKeyValueInfo == NULL) { DBGPRINT((sdlInfo, "SdbResolveCOMServer", "Memory allocation failure\n")); goto Done; } dwKeyValueInfoSize = dwBufSize * 2; Status = NtQueryValueKey(KeyHandle, &ustrValueName, KeyValueFullInformation, pKeyValueInfo, dwKeyValueInfoSize, &dwKeyValueInfoReqSize); if (!NT_SUCCESS(Status)) { if (Status == STATUS_BUFFER_TOO_SMALL) { RtlFreeHeap(RtlProcessHeap(), 0, pKeyValueInfo); pKeyValueInfo = RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, dwKeyValueInfoReqSize); if (pKeyValueInfo == NULL) { DBGPRINT((sdlInfo, "SdbResolveCOMServer", "Memory allocation failure\n")); goto Done; } dwKeyValueInfoSize = dwKeyValueInfoReqSize; Status = NtQueryValueKey(KeyHandle, &ustrValueName, KeyValueFullInformation, pKeyValueInfo, dwKeyValueInfoSize, &dwKeyValueInfoReqSize); if (!NT_SUCCESS(Status)) { DBGPRINT((sdlInfo, "SdbResolveCOMServer", "Failed to retrieve default key value for \"%s\" Status 0x%x\n", wszCLSIDRegFullPath, Status)); goto Done; } } else { DBGPRINT((sdlInfo, "SdbResolveCOMServer", "Failed to retrieve default key value for \"%s\" Status 0x%x\n", wszCLSIDRegFullPath, Status)); goto Done; } } if (pKeyValueInfo->Type == REG_SZ) { dwReqBufSize = pKeyValueInfo->DataLength + (1 * sizeof(WCHAR)); if (dwBufSize >= dwReqBufSize) { RtlMoveMemory(lpPath, ((PBYTE) pKeyValueInfo) + pKeyValueInfo->DataOffset, pKeyValueInfo->DataLength); lpPath[pKeyValueInfo->DataLength / sizeof(WCHAR)] = '\0'; } } else if (pKeyValueInfo->Type == REG_EXPAND_SZ) { ustrUnexpandedValue.Buffer = (PWSTR) (((PBYTE) pKeyValueInfo) + pKeyValueInfo->DataOffset); ustrUnexpandedValue.Length = (USHORT) pKeyValueInfo->DataLength; ustrUnexpandedValue.MaximumLength = (USHORT) pKeyValueInfo->DataLength; ustrValue.Buffer = lpPath; ustrValue.Length = 0; ustrValue.MaximumLength = (USHORT) dwBufSize; Status = RtlExpandEnvironmentStrings_U(NULL, &ustrUnexpandedValue, &ustrValue, &dwReqBufSize); if (Status == STATUS_BUFFER_TOO_SMALL) { goto Done; } else if (!NT_SUCCESS(Status)) { DBGPRINT((sdlInfo, "SdbResolveCOMServer", "Failed to expand key value for \"%s\" Status 0x%x\n", wszCLSIDRegFullPath, Status)); goto Done; } } DBGPRINT((sdlInfo, "SdbResolveCOMServer", "CLSID %s resolved to \"%s\"\n", wszCLSID, lpPath)); Done: if (KeyHandle != NULL) { NtClose(KeyHandle); } if (wszCLSIDRegFullPath != NULL) { RtlFreeHeap(RtlProcessHeap(), 0, wszCLSIDRegFullPath); } if (pKeyValueInfo != NULL) { RtlFreeHeap(RtlProcessHeap(), 0, pKeyValueInfo); } if (ustrGuid.Buffer != NULL) { RtlFreeUnicodeString(&ustrGuid); } return dwReqBufSize; } VOID ParseSdbQueryResult( IN HSDB hSDB, IN PSDBQUERYRESULT pQuery, OUT TAGREF* ptrAppHelp, // apphelp tagref, optional OUT PAPPHELP_DATA pApphelpData, // apphelp data, optional OUT TAGREF* ptrSxsData // fusion tagref, optional ) { DWORD dwIndex; BOOL bAppHelp = FALSE; BOOL bFusionFix = FALSE; TAGREF trExe; TAGREF trAppHelp = TAGREF_NULL; TAGREF trSxsData = TAGREF_NULL; // // scan matching exes; we extract fusion fix (the first one we find) and apphelp data, // also the first one we find // for (dwIndex = 0; dwIndex < pQuery->dwExeCount; ++dwIndex) { trExe = pQuery->atrExes[dwIndex]; if (ptrAppHelp != NULL && !bAppHelp) { bAppHelp = SdbReadApphelpData(hSDB, trExe, pApphelpData); if (bAppHelp) { trAppHelp = trExe; if (ptrSxsData == NULL || bFusionFix) { break; } } } // see if we have sxs fix as well if (ptrSxsData != NULL && !bFusionFix) { bFusionFix = GetExeSxsData(hSDB, trExe, NULL, NULL); if (bFusionFix) { trSxsData = trExe; } if (bFusionFix && (ptrAppHelp == NULL || bAppHelp)) { break; } } } if (ptrAppHelp != NULL) { *ptrAppHelp = trAppHelp; } if (ptrSxsData != NULL) { *ptrSxsData = trSxsData; } } BOOL InternalCheckRunApp( IN HANDLE hFile, // [Optional] Handle to an open file to check IN LPCWSTR pwszPath, // path to the app in NT format IN LPCWSTR pEnvironment, // pointer to the environment of the process that is // being created or NULL. IN USHORT uExeType, // executable's image type IN OUT PDWORD pdwReason, // collection of flags hinting at why we were called OUT PVOID* ppData, // this will contain the pointer to the allocated buffer // containing the appcompat data. OUT PDWORD pcbData, // if appcompat data is found, the size of the buffer // is returned here. OUT PVOID* ppSxsData, // out: Sxs data block from the compatibility database OUT PDWORD pcbSxsData, // out: sxs data block size OUT PDWORD pdwFusionFlags, // out: flags for sxs IN BOOL bNTVDMMode, // Are we doing the special NTVDM stuff? IN LPCWSTR szModuleName, // the module name (for NTVDM only) OUT LPWSTR pszCompatLayer, // The new compat layer variable. with format: // "__COMPAT_LAYER=Alpha Bravo Charlie" OUT PNTVDM_FLAGS pFlags, // The flags OUT PAPPHELP_INFO pAHInfo, // If there is apphelp to display, this will be filled // in with non-null values OUT HSDB* phSDB, // The handle to the database. OUT PSDBQUERYRESULT pQueryResult // The query result. ) /*++ Return: FALSE if the app should be blocked from running, TRUE otherwise. Desc: This is the main API of apphelp.dll. It is called from CreateProcess to retrieve application compatibility information for the current process. This function does not check whether the appcompat infrastructure has been disabled, (kernel32 checks that) --*/ { APPHELP_DATA ApphelpData; BOOL bSuccess; BOOL bRunApp = TRUE; // run by default BOOL bAppHelp = FALSE; WCHAR* pwszDosPath = NULL; BOOL bBypassCache = FALSE; // this is set if cache bypass occured (as opposed to entry not being found BOOL bGetSxsData = TRUE; BOOL bFusionFix = FALSE; HSDB hSDB = NULL; SDBQUERYRESULT sdbQuery; PSDBQUERYRESULT pSdbQuery = NULL; NTSTATUS Status; TAGREF trAppHelp = TAGREF_NULL; TAGREF trFusionFix = TAGREF_NULL; ULARGE_INTEGER uliFusionFlags = {0}; UNICODE_STRING ExePath; RTL_UNICODE_STRING_BUFFER DosPathBuffer; UCHAR BufferPath[MAX_PATH*2]; if (phSDB) { *phSDB = NULL; } if (pQueryResult) { pSdbQuery = pQueryResult; } else { pSdbQuery = &sdbQuery; } RtlZeroMemory(pSdbQuery, sizeof(sdbQuery)); RtlInitUnicodeStringBuffer(&DosPathBuffer, BufferPath, sizeof(BufferPath)); RtlInitUnicodeString(&ExePath, pwszPath); Status = RtlAssignUnicodeStringBuffer(&DosPathBuffer, &ExePath); if (NT_SUCCESS(Status)) { Status = RtlNtPathNameToDosPathName(0, &DosPathBuffer, NULL, NULL); } if (!NT_SUCCESS(Status)) { DBGPRINT((sdlError, "InternalCheckRunApp", "Failed to convert path \"%S\" to DOS.\n", pwszPath)); goto Done; } // // we have been successful, this is 0-terminated dos path // pwszDosPath = DosPathBuffer.String.Buffer; // // Cache lookup was bypassed by one reason or the other. // We do not update cache after it had been bypassed. // if (pdwReason) { bBypassCache = !!(*pdwReason & SHIM_CACHE_BYPASS); } else { bBypassCache = TRUE; } hSDB = SdbInitDatabaseEx(0, NULL, uExeType); if (phSDB) { *phSDB = hSDB; } if (hSDB == NULL) { DBGPRINT((sdlError, "InternalCheckRunApp", "Failed to initialize the database.\n")); goto Done; } // // We didn't find this EXE in the cache. Query the database // to get all the info about this EXE. // SdbGetMatchingExe(hSDB, pwszDosPath, szModuleName, pEnvironment, 0, pSdbQuery); if (pSdbQuery->dwFlags & SHIMREG_DISABLE_SXS) { bGetSxsData = FALSE; } // // get the flags for fusion. // SdbQueryFlagMask(hSDB, pSdbQuery, TAG_FLAG_MASK_FUSION, &uliFusionFlags.QuadPart, NULL); if (pdwFusionFlags) { *pdwFusionFlags = (DWORD)uliFusionFlags.QuadPart; } // // find apphelp/and/or Fusion fix // ParseSdbQueryResult(hSDB, pSdbQuery, &trAppHelp, &ApphelpData, bGetSxsData ? &trFusionFix : NULL); bAppHelp = (trAppHelp != TAGREF_NULL); if (bAppHelp) { // // Check whether the disable bit is set (the dwFlags has been retrieved from the // registry via the SdbReadApphelpData call) // if (!(pSdbQuery->dwFlags & SHIMREG_DISABLE_APPHELP)) { BOOL bNoUI; // // See whether the user has checked "Don't show this anymore" box before. // bNoUI = ((pSdbQuery->dwFlags & SHIMREG_APPHELP_NOUI) != 0); if (bNoUI) { DBGPRINT((sdlInfo, "InternalCheckRunApp", "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: // // NTVDM needs the severity info. // if (pAHInfo) { pAHInfo->dwSeverity = ApphelpData.dwSeverity; } if (bNoUI) { bRunApp = (ApphelpData.dwSeverity != APPHELP_HARDBLOCK); } else { DWORD dwRet; // // We need to show apphelp -- pack up the info // so we can hand it off to shimeng or ntvdm. // pSdbQuery->trAppHelp = trAppHelp; if (pAHInfo) { PDB pdb; TAGID tiWhich; if (SdbTagRefToTagID(hSDB, trAppHelp, &pdb, &tiWhich)) { if (SdbGetDatabaseGUID(hSDB, pdb, &(pAHInfo->guidDB))) { pAHInfo->tiExe = tiWhich; } } } bRunApp = TRUE; } break; default: // // Some other case was found (e.g. VERSIONSUB which should be replaced // by shims in most cases). // DBGPRINT((sdlWarning, "InternalCheckRunApp", "Unhandled severity flag 0x%x.\n", ApphelpData.dwSeverity)); break; } } } // // Apphelp verification is done. Check for shims if we should still run the app. // if (bRunApp) { if (ppData && (pSdbQuery->atrExes[0] != TAGREF_NULL || pSdbQuery->atrLayers[0] != TAGREF_NULL || pSdbQuery->trAppHelp)) { // // There are shims for this EXE. Pack the appcompat data // so it can be sent to ntdll in the context of the starting EXE. // SdbPackAppCompatData(hSDB, pSdbQuery, ppData, pcbData); } if (ppSxsData && bGetSxsData && trFusionFix != TAGREF_NULL) { // // See if we have Fusion data to report. // GetExeSxsData(hSDB, trFusionFix, ppSxsData, pcbSxsData); bFusionFix = (ppSxsData != NULL && *ppSxsData != NULL); } if (bNTVDMMode) { GetExeNTVDMData(hSDB, pSdbQuery, pszCompatLayer, pFlags); } } // // Update the cache now. // if (!bBypassCache) { // // Do not update the cache if we got the EXE entry from a local database. // bBypassCache = (pSdbQuery->atrExes[0] != TAGREF_NULL && !SdbIsTagrefFromMainDB(pSdbQuery->atrExes[0])); } if (!bBypassCache) { // // We remove from cache only if we have some appcompat data // BOOL bCleanApp = pSdbQuery->atrExes[0] == TAGREF_NULL && pSdbQuery->atrLayers[0] == TAGREF_NULL && !bAppHelp && pSdbQuery->dwFlags == 0 && !bFusionFix; if (hFile != INVALID_HANDLE_VALUE) { *pdwReason |= SHIM_CACHE_ACTION; if (bCleanApp) { *pdwReason |= SHIM_CACHE_UPDATE; } } } Done: RtlFreeUnicodeStringBuffer(&DosPathBuffer); // // NTVDM needs to have the database handle open. // if (!bNTVDMMode) { if (hSDB != NULL) { SdbReleaseDatabase(hSDB); } } return bRunApp; } BOOL ApphelpQueryExe( IN HSDB hSDB, IN LPCWSTR pwszPath, // Unicode path to the executable (DOS_PATH) IN BOOL bAppHelpIfNecessary, // Produce AppHelp dialog if necessary IN DWORD dwGetMatchingExeFlags, OUT SDBQUERYRESULT* pQueryResult // Shim Database Query Result ) /*++ Return: FALSE if the app should be blocked from running, TRUE otherwise. Desc: This function is similar with ApphelpCheckRunApp but without validating cache and Layer flags and doesn't return application compatibility information for given app name. It is intended to be called from a shim / user mode to verify whether an executable is allowed to run or not. --*/ { BOOL bRunApp = TRUE; // run by default DWORD dwDatabaseType = 0; DWORD dwSeverity = 0; TAGREF trAppHelp = TAGREF_NULL; HAPPHELPINFOCONTEXT hApphelpInfoContext = NULL; // // Query the database to get all the info about this EXE. // Note: // This function is intended to be called from user mode. // It doesn't require a call to ConvertToDosPath to string \??\ from the filepath. // DBGPRINT((sdlInfo, "ApphelpCheckExe", "Calling SdbGetMatchingExe for \"%s\"\n", pwszPath)); SdbGetMatchingExe(hSDB, pwszPath, NULL, NULL, dwGetMatchingExeFlags, pQueryResult); // // get info out of the query // ParseSdbQueryResult(hSDB, pQueryResult, &trAppHelp, NULL, // Apphelp Information api is used here NULL); // no sxs fixes are needed // // The last EXE in the list is always the more specific one, and the one we want to // use for checking IDs and flags and whatnot. // if (trAppHelp != TAGREF_NULL) { // // Read the apphelp data if available for this EXE. // if (SdbIsTagrefFromMainDB(trAppHelp)) { dwDatabaseType |= SDB_DATABASE_MAIN; } hApphelpInfoContext = SdbOpenApphelpInformationByID(hSDB, trAppHelp, dwDatabaseType); } // // Check whether the disable bit is set (the dwFlags has been retrieved from the // registry via the SdbReadApphelpData call) // if (hApphelpInfoContext != NULL) { if (!(pQueryResult->dwFlags & SHIMREG_DISABLE_APPHELP)) { BOOL bNoUI; // // See whether the user has checked "Don't show this anymore" box before. // bNoUI = ((pQueryResult->dwFlags & SHIMREG_APPHELP_NOUI) != 0); if (bNoUI) { DBGPRINT((sdlInfo, "ApphelpCheckExe", "NoUI flag is set, apphelp UI disabled for this app.\n")); } SdbQueryApphelpInformation(hApphelpInfoContext, ApphelpProblemSeverity, &dwSeverity, sizeof(dwSeverity)); if (!bAppHelpIfNecessary) { bNoUI = TRUE; } // // depending on severity of the problem... // switch (dwSeverity) { case APPHELP_MINORPROBLEM: case APPHELP_HARDBLOCK: case APPHELP_NOBLOCK: case APPHELP_REINSTALL: bRunApp = (dwSeverity != APPHELP_HARDBLOCK); if (!bNoUI) { DWORD dwRet; APPHELP_INFO AHInfo = { 0 }; SdbQueryApphelpInformation(hApphelpInfoContext, ApphelpDatabaseGUID, &AHInfo.guidDB, sizeof(AHInfo.guidDB)); SdbQueryApphelpInformation(hApphelpInfoContext, ApphelpExeTagID, &AHInfo.tiExe, sizeof(AHInfo.tiExe)); AHInfo.bOfflineContent = bDebugChum(); SdbShowApphelpDialog(&AHInfo, NULL, &bRunApp); // either we succeeded or bInstall package is treated // the same way as No UI } break; default: // // Some other case was found (e.g. VERSIONSUB which should be replaced // by shims in most cases). // DBGPRINT((sdlWarning, "ApphelpCheckExe", "Unhandled severity flag 0x%x.\n", dwSeverity)); break; } } } // // Apphelp verification is done. // if (hApphelpInfoContext != NULL) { SdbCloseApphelpInformation(hApphelpInfoContext); } return bRunApp; } /*++ // This code was used to check for include/exclude list in the database // to eliminate confusion entries should ALWAYS provide the list // // CheckIncludeExcludeList // returns: TRUE - database provides the list // FALSE - no list is provided in the database // BOOL CheckIncludeExcludeList( IN HSDB hSDB, IN SDBQUERYRESULT* pQueryResult ) { INT i; TAGREF trExe; TAGREF trFix; TAGREF trInexclude; for (i = 0; i < SDB_MAX_EXES && pQueryResult->atrExes[i] != TAGREF_NULL; ++i) { trExe = pQueryResult->atrExes[i]; trFix = SdbFindFirstTagRef(hSDB, trExe, TAG_SHIM_REF); while (trFix != TAGREF_NULL) { trInexclude = SdbFindFirstTagRef(hSDB, trFix, TAG_INEXCLUDE); if (trInexclude != TAGREF_NULL) { return TRUE; } trFix = SdbFindNextTagRef(hSDB, trExe, trFix); } } // // layers have their own inclusion/exclusion scheme // return FALSE; } --*/ BOOL AddExeDynShimInfoToList( IN LPCWSTR pwszFullPath, IN DWORD dwToken ) { BOOL bResult = FALSE; PEXE_DYN_SHIM_INFO pDynShimInfo = NULL; DWORD dwLen = 0; static BOOL bInitialized = FALSE; if (pwszFullPath == NULL) { return TRUE; } RtlEnterCriticalSection(&g_csDynShimInfo); if (!bInitialized) { bInitialized = TRUE; InitializeListHead(&g_ExeDynShimInfoList); } pDynShimInfo = RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, sizeof (EXE_DYN_SHIM_INFO)); if (pDynShimInfo == NULL) { DBGPRINT((sdlError,"AddExeDynShimInfoToList", "Failed to allocate a new dynamic shim info entry for %s\n", pwszFullPath)); goto Done; } dwLen = wcslen(pwszFullPath) + 1; pDynShimInfo->pwszFullPath = RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, dwLen * sizeof(WCHAR)); if (pDynShimInfo->pwszFullPath == NULL) { DBGPRINT((sdlError,"AddExeDynShimInfoToList", "Failed to allocate a new dynamic shim info entry for %s\n", pwszFullPath)); goto Done; } StringCchCopyW(pDynShimInfo->pwszFullPath, dwLen, pwszFullPath); pDynShimInfo->dwToken = dwToken; InsertHeadList(&g_ExeDynShimInfoList, &pDynShimInfo->entry); DBGPRINT((sdlInfo,"AddExeDynShimInfoToList", "Added %s(token: %d) to the list\n", pwszFullPath, dwToken)); bResult = TRUE; Done: if (!bResult) { if (pDynShimInfo) { if (pDynShimInfo->pwszFullPath) { RtlFreeHeap(RtlProcessHeap(), 0, pDynShimInfo->pwszFullPath); } RtlFreeHeap(RtlProcessHeap(), 0, pDynShimInfo); } } RtlLeaveCriticalSection(&g_csDynShimInfo); return bResult; } BOOL RemoveExeDynShimInfoFromList( IN LPCWSTR pwszFullPath ) { BOOL bResult = FALSE; _pfn_SE_DynamicUnshim pfnDynamicUnshim = NULL; HMODULE hmodShimEngine = 0; PEXE_DYN_SHIM_INFO pDynShimInfo = NULL; PLIST_ENTRY pEntry, pTempEntry; PLIST_ENTRY pHead = &g_ExeDynShimInfoList; if (pwszFullPath == NULL) { return TRUE; } hmodShimEngine = LoadLibraryW(ShimEngine_ModuleName); if (hmodShimEngine == NULL) { DBGPRINT((sdlError,"RemoveExeDynShimInfoFromList", "Failed to get ShimEngine module handle.\n")); goto Done; } pfnDynamicUnshim = (_pfn_SE_DynamicUnshim) GetProcAddress(hmodShimEngine, DynamicUnshimProcedureName); if (NULL == pfnDynamicUnshim) { DBGPRINT((sdlError, "RemoveExeDynShimInfoFromList", "Failed to get Dynamic Shim procedure address from ShimEngine module.\n")); goto Done; } RtlEnterCriticalSection(&g_csDynShimInfo); pEntry = pHead->Flink; while (pEntry != pHead) { pDynShimInfo = CONTAINING_RECORD(pEntry, EXE_DYN_SHIM_INFO, entry); if (_wcsicmp(pDynShimInfo->pwszFullPath, pwszFullPath) == 0) { pTempEntry = pEntry->Flink; RemoveEntryList(pEntry); if (!(*pfnDynamicUnshim)(pDynShimInfo->dwToken)) { DBGPRINT((sdlInfo,"RemoveExeDynShimInfoFromList", "Unshimming %s(token: %d) failed\n", pwszFullPath, pDynShimInfo->dwToken)); bResult = FALSE; break; } DBGPRINT((sdlInfo,"RemoveExeDynShimInfoFromList", "Removed %s(token: %d) from the list\n", pwszFullPath, pDynShimInfo->dwToken)); RtlFreeHeap(RtlProcessHeap(), 0, pDynShimInfo->pwszFullPath); RtlFreeHeap(RtlProcessHeap(), 0, pDynShimInfo); bResult = TRUE; // // We need to remove all the entries for this exe so keep going. // pEntry = pTempEntry; } else { pEntry = pEntry->Flink; } } RtlLeaveCriticalSection(&g_csDynShimInfo); Done: return bResult; } BOOL ApphelpFixExe( IN HSDB hSDB, IN LPCWSTR pwszPath, // Unicode path to the executable (DOS_PATH) IN SDBQUERYRESULT* pQueryResult, // QueryResult IN BOOL bUseModuleName // if false, module name is not used for dynamic shimming ) { static _pfn_SE_DynamicShim pfnDynamicShim = NULL; HMODULE hmodShimEngine = 0; BOOL bResult = FALSE; ANSI_STRING AnsiModuleName = { 0 }; UNICODE_STRING UnicodeModuleName; NTSTATUS Status; LPCSTR pszModuleName = NULL; LPCWSTR pwszModuleName; DWORD dwDynamicToken = 0; // // Do we need to do anything? // if (pQueryResult->atrExes[0] == TAGREF_NULL && pQueryResult->atrLayers[0] == TAGREF_NULL) { // // Nothing for the shim engine to do. // bResult = TRUE; goto Done; } // // Load additional shims for this exe. // DBGPRINT((sdlInfo,"ApphelpFixExe", "Loading ShimEngine for \"%s\"\n", pwszPath)); hmodShimEngine = LoadLibraryW(ShimEngine_ModuleName); if (hmodShimEngine == NULL) { DBGPRINT((sdlError,"ApphelpFixExe", "Failed to get ShimEngine module handle.\n")); goto Done; } pfnDynamicShim = (_pfn_SE_DynamicShim) GetProcAddress(hmodShimEngine, DynamicShimProcedureName); if (NULL == pfnDynamicShim) { DBGPRINT((sdlError, "ApphelpFixExe", "Failed to get Dynamic Shim procedure address from ShimEngine module.\n")); goto Done; } // // check inclusion/exclusion list // if (pwszPath != NULL && bUseModuleName) { // // no inclusion/exclusion in the xml -- determine module name // pwszModuleName = wcsrchr(pwszPath, L'\\'); // last backslash please if (pwszModuleName == NULL) { pwszModuleName = pwszPath; } else { ++pwszModuleName; } // // convert to ansi // RtlInitUnicodeString(&UnicodeModuleName, pwszModuleName); Status = RtlUnicodeStringToAnsiString(&AnsiModuleName, &UnicodeModuleName, TRUE); if (!NT_SUCCESS(Status)) { DBGPRINT((sdlError, "ApphelpFixExe", "Failed to convert unicode string \"%s\" to ansi, Status 0x%lx.\n", pwszModuleName, Status)); goto Done; } pszModuleName = AnsiModuleName.Buffer; // this will be allocated by RtlUnicodeStringToAnsiString } bResult = (*pfnDynamicShim)(pwszPath, hSDB, pQueryResult, pszModuleName, &dwDynamicToken); if (FALSE == bResult) { DBGPRINT((sdlError, "ApphelpFixExe", "Failed to call Dynamic Shim.\n")); goto Done; } if (pszModuleName == NULL) { if (!AddExeDynShimInfoToList(pwszPath, dwDynamicToken)) { DBGPRINT((sdlError, "ApphelpFixExe", "Failed to add %s(token: %d) to the list\n", pwszPath, dwDynamicToken)); goto Done; } } bResult = TRUE; Done: RtlFreeAnsiString(&AnsiModuleName); // this will do nothing if string is empty return bResult; } BOOL ApphelpCheckExe( IN LPCWSTR pwszPath, // Unicode path to the executable (DOS_PATH) IN BOOL bAppHelpIfNecessary, // Only present AppHelp this executable if TRUE IN BOOL bShimIfNecessary, // Only load shim for this executable if TRUE IN BOOL bUseModuleName // use module name when inclusion/exclusion list is not provided ) /*++ Return: FALSE if the app should be blocked from running, TRUE otherwise. Desc: If you are calling this API with the last parameter set to FALSE and it returns TRUE, you'll have to call ApphelpReleaseExe when you are done running this exe. --*/ { BOOL bRunApp = TRUE; SDBQUERYRESULT QueryResult; HSDB hSDB; if (IsAppcompatInfrastructureDisabled()) { goto Done; } RtlZeroMemory(&QueryResult, sizeof(QueryResult)); hSDB = SdbInitDatabaseEx(0, NULL, IMAGE_FILE_MACHINE_I386); if (hSDB == NULL) { DBGPRINT((sdlError, "ApphelpCheckExe", "Failed to initialize database.\n")); goto Done; } bRunApp = ApphelpQueryExe(hSDB, pwszPath, bAppHelpIfNecessary, SDBGMEF_IGNORE_ENVIRONMENT, &QueryResult); if (TRUE == bRunApp && TRUE == bShimIfNecessary) { ApphelpFixExe(hSDB, pwszPath, &QueryResult, bUseModuleName); } SdbReleaseDatabase(hSDB); Done: return bRunApp; } BOOL ApphelpReleaseExe( IN LPCWSTR pwszPath // Unicode path to the executable (DOS_PATH) ) { return RemoveExeDynShimInfoFromList(pwszPath); } BOOL ApphelpCheckIME( IN LPCWSTR pwszPath // Unicode path to the exe ) { BOOL bRunApp = TRUE; SDBQUERYRESULT QueryResult; HSDB hSDB; BOOL bCleanApp; if (IsAppcompatInfrastructureDisabled()) { return TRUE; } RtlZeroMemory(&QueryResult, sizeof(QueryResult)); hSDB = SdbInitDatabase(0, NULL); if (hSDB == NULL) { DBGPRINT((sdlError, "ApphelpCheckIME", "Failed to initialize database.\n")); goto Done; } bRunApp = ApphelpQueryExe(hSDB, pwszPath, TRUE, SDBGMEF_IGNORE_ENVIRONMENT, &QueryResult); if (TRUE == bRunApp) { ApphelpFixExe(hSDB, pwszPath, &QueryResult, FALSE); } SdbReleaseDatabase(hSDB); // // see that it's in the cache if no fixes // bCleanApp = QueryResult.atrExes[0] == TAGREF_NULL && QueryResult.atrLayers[0] == TAGREF_NULL && QueryResult.trAppHelp == TAGREF_NULL && QueryResult.dwFlags == 0; #ifndef WIN2K_NOCACHE BaseUpdateAppcompatCache(pwszPath, INVALID_HANDLE_VALUE, !bCleanApp); #endif Done: return bRunApp; } BOOL ApphelpCheckShellObject( IN REFCLSID ObjectCLSID, IN BOOL bShimIfNecessary, OUT ULONGLONG* pullFlags ) /*++ Return: FALSE if the object should be blocked from instantiating, TRUE otherwise. Desc: This is a helper function for Explorer and Internet Explorer that will allow those applications to detect bad extension objects and either block them from running or fix them. pullFlags is filled with a 64-bit flag mask that can be used to turn on 'hack' flags in Explorer/IE. These are pulled out of the App Compat database. If the database indicates that a shim should be used to fix the extension and bShimIfNecessary is TRUE, this function will load SHIMENG.DLL and apply the fix. --*/ { BOOL bGoodObject = TRUE; LPWSTR szComServer = NULL; LPWSTR szDLLName = NULL; DWORD dwBufSize = 0; DWORD dwReqBufSize = 0; SDBQUERYRESULT QueryResult; HSDB hSDB = NULL; PVOID pModuleHandle = NULL; UNICODE_STRING ustrDLLName = { 0 }; UNICODE_STRING ustrNtPath = { 0 }; NTSTATUS status; HANDLE hDLL = INVALID_HANDLE_VALUE; DWORD dwReason; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; if (IsAppcompatInfrastructureDisabled()) { return TRUE; } if (pullFlags != NULL) { *pullFlags = 0; } szComServer = RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH); if (szComServer == NULL) { DBGPRINT((sdlInfo,"ApphelpCheckShellObject", "Memory allocation error\n")); goto Done; } dwBufSize = MAX_PATH; // // Turn the CLSID into a filename (ie, the DLL that serves the object) // dwReqBufSize = ResolveCOMServer(ObjectCLSID, szComServer, dwBufSize); if (dwReqBufSize == 0) { // // CLSID could not be resolved to a DLL. // goto Done; } if (dwReqBufSize > dwBufSize) { RtlFreeHeap(RtlProcessHeap(), 0, szComServer); szComServer = RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, dwReqBufSize); if (szComServer == NULL) { DBGPRINT((sdlInfo,"ApphelpCheckShellObject", "Memory allocation error\n")); goto Done; } dwBufSize = dwReqBufSize; dwReqBufSize = ResolveCOMServer(ObjectCLSID, szComServer, dwBufSize); if (dwReqBufSize > dwBufSize || dwReqBufSize == 0) { // // What? Buffer size changed. This could happen if registration of an // object took place between the time we first queried and the next time. // Just being paranoid... // DBGPRINT((sdlInfo,"ApphelpCheckShellObject", "Memory allocation error\n")); goto Done; } } // // Determine DLL name (w/o path). Walk back to first backslash. // szDLLName = szComServer + dwReqBufSize/sizeof(WCHAR); while (szDLLName >= szComServer) { if (*szDLLName == L'\\') { break; } szDLLName--; } szDLLName++; // // Check if this DLL is already loaded. If so, no need to try and do anything // since it's really too late anyway. // RtlInitUnicodeString(&ustrDLLName, szDLLName); status = LdrGetDllHandle(NULL, NULL, &ustrDLLName, &pModuleHandle); if (NT_SUCCESS(status)) { // // Already loaded. // goto Done; } if (!RtlDosPathNameToNtPathName_U(szComServer, &ustrNtPath, NULL, NULL)) { DBGPRINT((sdlError, "ApphelpCheckShellObject", "RtlDosPathNameToNtPathName_U failed, path \"%s\"\n", szComServer)); goto Done; } InitializeObjectAttributes(&ObjectAttributes, &ustrNtPath, OBJ_CASE_INSENSITIVE, NULL, NULL); status = NtCreateFile(&hDLL, GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES, &ObjectAttributes, &IoStatusBlock, 0, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (!NT_SUCCESS(status)) { DBGPRINT((sdlError, "ApphelpCheckShellObject", "SdbpOpenFile failed, path \"%s\"\n", szComServer)); goto Done; } if (BaseCheckAppcompatCache(ustrNtPath.Buffer, hDLL, NULL, &dwReason)) { // // We have this in cache // goto Done; } RtlZeroMemory(&QueryResult, sizeof(QueryResult)); hSDB = SdbInitDatabase(0, NULL); if (hSDB == NULL) { DBGPRINT((sdlError, "ApphelpCheckShellObject", "Failed to initialize database.\n")); goto Done; } bGoodObject = ApphelpQueryExe(hSDB, szComServer, FALSE, SDBGMEF_IGNORE_ENVIRONMENT, &QueryResult); if (TRUE == bGoodObject && TRUE == bShimIfNecessary) { ApphelpFixExe(hSDB, szComServer, &QueryResult, FALSE); } SdbQueryFlagMask(hSDB, &QueryResult, TAG_FLAG_MASK_SHELL, pullFlags, NULL); // // we might want to use Apphelp api for this -- but shell does pass the right // thing to us (most likely) // BaseUpdateAppcompatCache(ustrNtPath.Buffer, hDLL, !(QueryResult.atrExes[0] == TAGREF_NULL && QueryResult.atrLayers[0] == TAGREF_NULL)); Done: RtlFreeUnicodeString(&ustrNtPath); if (hSDB != NULL) { SdbReleaseDatabase(hSDB); } if (hDLL != INVALID_HANDLE_VALUE) { NtClose(hDLL); } if (szComServer != NULL) { RtlFreeHeap(RtlProcessHeap(), 0, szComServer); } return bGoodObject; } BOOL ApphelpGetNTVDMInfo( IN LPCWSTR pwszPath, // path to the app in NT format IN LPCWSTR pwszModule, // module name IN LPCWSTR pEnvironment, // pointer to the environment of the task that is // being created or NULL if we are to use the main NTVDM // environment block. OUT LPWSTR pszCompatLayer, // The new compat layer variable. with format: // "Alpha Bravo Charlie" -- allow 256 chars for this. OUT PNTVDM_FLAGS pFlags, // The flags OUT PAPPHELP_INFO pAHInfo, // If there is apphelp to display, this will be filled // in with non-null values OUT HSDB* phSDB, // The handle to the database. OUT PSDBQUERYRESULT pQueryResult // The query result. ) /*++ Return: FALSE if the app should be blocked from running, TRUE otherwise. Desc: This is essentially the equivalent of ApphelpCheckRunApp, but specific to NTVDM. --*/ { DWORD dwReason = 0; if (IsAppcompatInfrastructureDisabled()) { return TRUE; } return InternalCheckRunApp(INVALID_HANDLE_VALUE, pwszPath, pEnvironment, IMAGE_FILE_MACHINE_I386, &dwReason, NULL, NULL, NULL, NULL, NULL, TRUE, pwszModule, pszCompatLayer, pFlags, pAHInfo, phSDB, pQueryResult); } void GetExeNTVDMData( IN HSDB hSDB, // the SDB context IN PSDBQUERYRESULT psdbQuery, // the EXEs and LAYERs that are active OUT WCHAR* pszCompatLayer, // The new compat layer variable. with format: // "Alpha Bravo Charlie" OUT PNTVDM_FLAGS pFlags // The flags ) { DWORD i; ULARGE_INTEGER uliFlags; LPVOID pFlagContext = NULL; HRESULT hr; ZeroMemory(pFlags, sizeof(NTVDM_FLAGS)); // // Build the layer variable, and look for the two "special" layers // if (pszCompatLayer) { pszCompatLayer[0] = 0; for (i = 0; i < SDB_MAX_LAYERS && psdbQuery->atrLayers[i] != TAGREF_NULL; ++i) { WCHAR* pszEnvVar; // // Get the environment var and tack it onto the full string // pszEnvVar = SdbGetLayerName(hSDB, psdbQuery->atrLayers[i]); if (pszEnvVar) { // // check for one of the two "special" layers // if (_wcsicmp(pszEnvVar, L"640X480") == 0) { // // set the 640x480 flag -- found in base\mvdm\inc\wowcmpat.h // // NOTE: we don't have this flag yet -- waiting on WOW guys } if (_wcsicmp(pszEnvVar, L"256COLOR") == 0) { // // set the 256 color flag -- found in base\mvdm\inc\wowcmpat.h // pFlags->dwWOWCompatFlagsEx |= 0x00000002; } hr = StringCchCatW(pszCompatLayer, COMPATLAYERMAXLEN, pszEnvVar); if (hr == S_OK) { hr = StringCchCatW(pszCompatLayer, COMPATLAYERMAXLEN, L" "); } if (hr == STRSAFE_E_INSUFFICIENT_BUFFER) { DBGPRINT((sdlError, "GetExeNTVDMData", "pszCompatLayer not big enough.\n")); // // If the buffer is not big enough, we just set it to an empty string - // we don't want to fill in incomplete data. // pszCompatLayer[0] = 0; break; } } } } // // Look for compat flags // SdbQueryFlagMask(hSDB, psdbQuery, TAG_FLAGS_NTVDM1, &uliFlags.QuadPart, &pFlagContext); pFlags->dwWOWCompatFlags |= uliFlags.LowPart; pFlags->dwWOWCompatFlagsEx |= uliFlags.HighPart; SdbQueryFlagMask(hSDB, psdbQuery, TAG_FLAGS_NTVDM2, &uliFlags.QuadPart, &pFlagContext); pFlags->dwUserWOWCompatFlags |= uliFlags.LowPart; pFlags->dwWOWCompatFlags2 |= uliFlags.HighPart; SdbQueryFlagMask(hSDB, psdbQuery, TAG_FLAGS_NTVDM3, &uliFlags.QuadPart, &pFlagContext); pFlags->dwWOWCompatFlagsFE |= uliFlags.LowPart; // High Part is unused for now. // now pack command line parameters SdbpPackCmdLineInfo(pFlagContext, &pFlags->pFlagsInfo); SdbpFreeFlagInfoList(pFlagContext); } BOOL ApphelpCheckRunApp( IN HANDLE hFile, // [Optional] Handle to an open file to check IN WCHAR* pwszPath, // path to the app in NT format IN WCHAR* pEnvironment, // pointer to the environment of the process that is // being created or NULL. IN USHORT uExeType, // executable's image type IN PDWORD pdwReason, // collection of flags hinting at why we were called OUT PVOID* ppData, // this will contain the pointer to the allocated buffer // containing the appcompat data. OUT PDWORD pcbData, // if appcompat data is found, the size of the buffer // is returned here. OUT PVOID* ppSxsData, // BUGBUG: describe OUT PDWORD pcbSxsData, // BUGBUG: describe OUT PDWORD pdwFusionFlags ) /*++ Return: FALSE if the app should be blocked from running, TRUE otherwise. Desc: This is the main API of apphelp.dll. It is called from CreateProcess to retrieve application compatibility information for the current process. --*/ { return InternalCheckRunApp(hFile, pwszPath, pEnvironment, uExeType, pdwReason, ppData, pcbData, ppSxsData, pcbSxsData,pdwFusionFlags, FALSE, NULL, NULL, NULL, NULL, NULL, NULL); } // // ============================================================================================= // InstallShield 7 Support // ============================================================================================= // BOOL ApphelpCheckInstallShieldPackage( IN REFCLSID PackageID, IN LPCWSTR lpszPackageFullPath ) { BOOL bPackageGood = TRUE; // This return value MUST TRUE otherwise InstallShield7 will cancel its processes. TAGREF trExe = TAGREF_NULL; DWORD dwNumExes = 0; DWORD dwDataType = 0; DWORD dwSize = 0; DWORD dwReturn = 0; BOOL bMatchFound = FALSE; NTSTATUS Status; BOOL bAppHelpIfNecessary = FALSE; BOOL bResult = TRUE; WCHAR wszCLSID[41]; WCHAR wszPackageCode[41]; GUID guidPackageID; GUID guidPackageCode; HSDB hSDB = NULL; SDBQUERYRESULT QueryResult; if (IsAppcompatInfrastructureDisabled()) { goto Done; } if (NULL == lpszPackageFullPath) { DBGPRINT((sdlInfo, "ApphelpCheckInstallShieldPackage", "lpszPackageFullPath is NULL\n")); goto Done; } SdbGUIDToString((GUID *)&PackageID, wszCLSID, CHARCOUNT(wszCLSID)); DBGPRINT((sdlWarning, "ApphelpCheckInstallShieldPackage", "InstallShield package detected. CLSID: %s FullPath: %s\n", wszCLSID, lpszPackageFullPath)); RtlZeroMemory(&QueryResult, sizeof(QueryResult)); hSDB = SdbInitDatabase(0, NULL); if (hSDB == NULL) { DBGPRINT((sdlError, "ApphelpCheckExe", "Failed to initialize database.\n")); goto Done; } bMatchFound = ApphelpQueryExe(hSDB, lpszPackageFullPath, bAppHelpIfNecessary, SDBGMEF_IGNORE_ENVIRONMENT, &QueryResult); if (!bMatchFound) { DBGPRINT((sdlError, "ApphelpCheckInstallShieldPackage", "No match found.\n")); goto Done; } for (dwNumExes = 0; dwNumExes < SDB_MAX_EXES; ++dwNumExes) { if (TAGREF_NULL == QueryResult.atrExes[dwNumExes]) { break; } trExe = QueryResult.atrExes[dwNumExes]; DBGPRINT((sdlInfo, "ApphelpCheckInstallShieldPackage", "Processing TAGREF atrExes[%d] = 0x%8x.\n", dwNumExes, trExe)); dwSize = sizeof(wszPackageCode); *wszPackageCode = L'\0'; dwReturn = SdbQueryData( hSDB, trExe, L"PackageCode", &dwDataType, wszPackageCode, &dwSize); if (dwReturn == ERROR_SUCCESS) { DBGPRINT((sdlInfo, "ApphelpCheckInstallShieldPackage", "SdbQueryData returns dwSize = %d and dwDataType = %d.\n", dwSize, dwDataType)); if ((dwSize > 0) && (dwSize < sizeof(wszPackageCode))) { // we have some data // check the type (should be string) if (REG_SZ != dwDataType) { DBGPRINT((sdlError, "ApphelpCheckInstallShieldPackage", "SdbQueryData returns non STRING PackageCode data. Exiting.\n")); goto Done; } DBGPRINT((sdlInfo, "ApphelpCheckInstallShieldPackage", "Comparing PackageId = %s and PackageCode = %s.\n", wszCLSID, wszPackageCode)); // convert to guid if (FALSE == SdbGUIDFromString(wszPackageCode, &guidPackageCode)) { DBGPRINT((sdlError, "ApphelpCheckInstallShieldPackage", "Can not convert PackageCode to GUID. Exiting.\n")); goto Done; } if (RtlEqualMemory(PackageID, &guidPackageCode, sizeof(guidPackageCode) )) { DBGPRINT((sdlWarning, "ApphelpCheckInstallShieldPackage", "Found InstallShield package matched with PackageCode: %s.\n", wszPackageCode)); if (TRUE != ApphelpFixExe(hSDB, lpszPackageFullPath, &QueryResult, FALSE)) { DBGPRINT((sdlError, "ApphelpCheckInstallShieldPackage", "Can not load additional shim dynamically for this executable.\n")); } goto Done; } } } } // for DBGPRINT((sdlError, "ApphelpCheckInstallShieldPackage", "No match found.\n")); Done: if (hSDB != NULL) { SdbReleaseDatabase(hSDB); } return bPackageGood; } // // ============================================================================================= // MSI Support // ============================================================================================= // BOOL SDBAPI ApphelpCheckMsiPackage( IN GUID* pguidDB, // database id IN GUID* pguidID, // match id IN DWORD dwFlags, // not used now, set to 0 IN BOOL bNoUI ) { WCHAR szDatabasePath[MAX_PATH]; DWORD dwDatabaseType = 0; DWORD dwPackageFlags = 0; DWORD dwLength; BOOL bInstallPackage = TRUE; HSDB hSDB = NULL; TAGREF trPackage = TAGREF_NULL; HAPPHELPINFOCONTEXT hApphelpInfoContext = NULL; DWORD dwSeverity = 0; if (IsAppcompatInfrastructureDisabled()) { goto out; } hSDB = SdbInitDatabase(HID_NO_DATABASE, NULL); if (hSDB == NULL) { DBGPRINT((sdlError, "ApphelpCheckMsiPackage", "Failed to initialize database\n")); goto out; } SdbSetImageType(hSDB, IMAGE_FILE_MSI); // // First, we need to resolve a db // dwLength = SdbResolveDatabase(hSDB, pguidDB, &dwDatabaseType, szDatabasePath, CHARCOUNT(szDatabasePath)); if (dwLength == 0 || dwLength > CHARCOUNT(szDatabasePath)) { DBGPRINT((sdlError, "ApphelpCheckMsiPackage", "Failed to resolve database path\n")); goto out; } // // open database // if (!SdbOpenLocalDatabase(hSDB, szDatabasePath)) { DBGPRINT((sdlError, "ApphelpCheckMsiPackage", "Failed to open database \"%s\"\n", szDatabasePath)); goto out; } // // find the entry // trPackage = SdbFindMsiPackageByID(hSDB, pguidID); if (trPackage == TAGREF_NULL) { DBGPRINT((sdlError, "ApphelpCheckMsiPackage", "Failed to find msi package by guid id\n")); goto out; } hApphelpInfoContext = SdbOpenApphelpInformationByID(hSDB, trPackage, dwDatabaseType); if (hApphelpInfoContext == NULL) { DBGPRINT((sdlInfo, "ApphelpCheckMsiPackage", "Apphelp information has not been found\n")); goto out; } // // we have apphelp data, check to see if we have flags for this exe // if (!SdbGetEntryFlags(pguidID, &dwPackageFlags)) { DBGPRINT((sdlWarning, "ApphelpCheckMsiPackage", "No flags for trPackage 0x%x\n", trPackage)); dwPackageFlags = 0; } // // Check whether the disable bit is set (the dwFlags has been retrieved from the // registry via the SdbReadApphelpData call) // if (dwPackageFlags & SHIMREG_DISABLE_APPHELP) { goto out; } bNoUI |= !!(dwPackageFlags & SHIMREG_APPHELP_NOUI); if (bNoUI) { DBGPRINT((sdlInfo, "ApphelpCheckMsiPackage", "NoUI flag is set, apphelp UI disabled for this app.\n")); } SdbQueryApphelpInformation(hApphelpInfoContext, ApphelpProblemSeverity, &dwSeverity, sizeof(dwSeverity)); // // depending on severity of the problem... // switch (dwSeverity) { case APPHELP_MINORPROBLEM: case APPHELP_HARDBLOCK: case APPHELP_NOBLOCK: case APPHELP_REINSTALL: // // // bInstallPackage = (APPHELP_HARDBLOCK != dwSeverity); if (!bNoUI) { DWORD dwRet; APPHELP_INFO AHInfo = { 0 }; AHInfo.guidDB = *pguidDB; AHInfo.bMSI = TRUE; SdbQueryApphelpInformation(hApphelpInfoContext, ApphelpExeTagID, &AHInfo.tiExe, sizeof(AHInfo.tiExe)); if (AHInfo.tiExe != TAGID_NULL) { AHInfo.bOfflineContent = bDebugChum(); SdbShowApphelpDialog(&AHInfo, NULL, &bInstallPackage); // either we succeeded or bInstall package is treated // the same way as No UI } } break; default: // // Some other case was found (e.g. VERSIONSUB which should be replaced // by shims in most cases). // DBGPRINT((sdlInfo, "ApphelpCheckMsiPackage", "Unhandled severity flag 0x%x.\n", dwSeverity)); break; } // // at this point we know whether we want to install the package or not // out: if (hApphelpInfoContext != NULL) { SdbCloseApphelpInformation(hApphelpInfoContext); } if (hSDB != NULL) { SdbReleaseDatabase(hSDB); } return bInstallPackage; } BOOL SDBAPI ApphelpFixMsiPackage( IN GUID* pguidDB, IN GUID* pguidID, IN LPCWSTR pszFileName, IN LPCWSTR pszActionName, IN DWORD dwFlags ) { WCHAR szDatabasePath[MAX_PATH]; DWORD dwDatabaseType = 0; HSDB hSDB = NULL; TAGREF trPackage = TAGREF_NULL; TAGREF trAction = TAGREF_NULL; SDBQUERYRESULT QueryResult; BOOL bSuccess = FALSE; DWORD dwLength; TAGREF trLayer, trLayerRef; DWORD dwLayers = 0; if (IsAppcompatInfrastructureDisabled()) { bSuccess = TRUE; goto out; } // // open database. In this case we need to have the default database // for this platform being opened, later on we modify the context // Fixes will be looked up in a default main db though // hSDB = SdbInitDatabase(0, NULL); if (hSDB == NULL) { DBGPRINT((sdlError, "ApphelpCheckMsiPackage", "Failed to initialize database\n")); goto out; } SdbSetImageType(hSDB, IMAGE_FILE_MSI); // // from this point on, all resolutions will be based on msi image type // however all the fixes will come from the database that is standard for // this platform // // // First, we need to resolve a db // dwLength = SdbResolveDatabase(hSDB, pguidDB, &dwDatabaseType, szDatabasePath, CHARCOUNT(szDatabasePath)); if (dwLength == 0 || dwLength > CHARCOUNT(szDatabasePath)) { DBGPRINT((sdlError, "ApphelpCheckMsiPackage", "Failed to resolve database path\n")); goto out; } // // open database // if (!SdbOpenLocalDatabase(hSDB, szDatabasePath)) { DBGPRINT((sdlError, "ApphelpCheckMsiPackage", "Failed to open database \"%s\"\n", szDatabasePath)); goto out; } // // find the entry // trPackage = SdbFindMsiPackageByID(hSDB, pguidID); if (trPackage == TAGREF_NULL) { DBGPRINT((sdlError, "ApphelpCheckMsiPackage", "Failed to find msi package by guid id\n")); goto out; } if (SdbGetEntryFlags(pguidID, &dwFlags) && (dwFlags & SHIMREG_DISABLE_SHIM)) { DBGPRINT((sdlInfo, "ApphelpCheckMsiPackage", "Shims for this package are disabled\n")); goto out; } trAction = SdbFindCustomActionForPackage(hSDB, trPackage, pszActionName); if (trAction == TAGREF_NULL) { DBGPRINT((sdlInfo, "ApphelpCheckMsiPackage", "Failed to find custom action \"%s\"\n", pszActionName)); goto out; } // // we have custom action on our hands which appears to have fixes // attached to it, shim it! // RtlZeroMemory(&QueryResult, sizeof(QueryResult)); QueryResult.guidID = *pguidID; QueryResult.atrExes[0] = trAction; // // get all the layers for this entry // also remember that tthe layers and shims alike might not be in the default // database initialized in the beginning of this call // trLayerRef = SdbFindFirstTagRef(hSDB, trAction, TAG_LAYER); while (trLayerRef != TAGREF_NULL && dwLayers < SDB_MAX_LAYERS) { trLayer = SdbGetNamedLayer(hSDB, trLayerRef); if (trLayer != TAGREF_NULL) { QueryResult.atrLayers[dwLayers++] = trLayer; } trLayerRef = SdbFindNextTagRef(hSDB, trAction, trLayerRef); } // // ready to shim // bSuccess = ApphelpFixExe(hSDB, pszFileName, &QueryResult, TRUE); if (bSuccess) { DBGPRINT((sdlInfo, "ApphelpFixMsiPackage", "Custom action \"%s\" successfully shimmed file \"%s\"\n", pszActionName, pszFileName)); } out: if (hSDB != NULL) { SdbReleaseDatabase(hSDB); } return(bSuccess); } BOOL SDBAPI ApphelpFixMsiPackageExe( IN GUID* pguidDB, IN GUID* pguidID, IN LPCWSTR pszActionName, IN OUT LPWSTR pwszEnv, IN OUT LPDWORD pdwBufferSize ) { WCHAR szDatabasePath[MAX_PATH]; DWORD dwDatabaseType = 0; HSDB hSDB = NULL; TAGREF trPackage = TAGREF_NULL; TAGREF trAction = TAGREF_NULL; SDBQUERYRESULT QueryResult; DWORD dwLength; DWORD dwBufferSize; BOOL bSuccess = FALSE; DWORD dwFlags; int i; TAGREF trLayer; if (pwszEnv != NULL) { *pwszEnv = TEXT('\0'); } if (IsAppcompatInfrastructureDisabled()) { goto out; } hSDB = SdbInitDatabaseEx(HID_NO_DATABASE, NULL, IMAGE_FILE_MSI); if (hSDB == NULL) { DBGPRINT((sdlError, "ApphelpCheckMsiPackage", "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, "ApphelpCheckMsiPackage", "Failed to resolve database path\n")); goto out; } // // open database // if (!SdbOpenLocalDatabase(hSDB, szDatabasePath)) { DBGPRINT((sdlError, "ApphelpCheckMsiPackage", "Failed to open database \"%s\"\n", szDatabasePath)); goto out; } // // find the entry // trPackage = SdbFindMsiPackageByID(hSDB, pguidID); if (trPackage == TAGREF_NULL) { DBGPRINT((sdlError, "ApphelpCheckMsiPackage", "Failed to find msi package by guid id\n")); goto out; } if (SdbGetEntryFlags(pguidID, &dwFlags) && (dwFlags & SHIMREG_DISABLE_SHIM)) { DBGPRINT((sdlInfo, "ApphelpCheckMsiPackage", "Shims for this package are disabled\n")); goto out; } trAction = SdbFindCustomActionForPackage(hSDB, trPackage, pszActionName); if (trAction == TAGREF_NULL) { DBGPRINT((sdlInfo, "ApphelpCheckMsiPackage", "Failed to find custom action \"%s\"\n", pszActionName)); goto out; } // // now -- this action is an exe, do it right for him // RtlZeroMemory(&QueryResult, sizeof(QueryResult)); QueryResult.guidID = *pguidID; for (i = 0; i < SDB_MAX_LAYERS; ++i) { // // check to see if we are doing the first layer, if so - call // find first to obtain the layer, else find the next applicable layer // if (i == 0) { trLayer = SdbFindFirstTagRef(hSDB, trAction, TAG_LAYER); } else { trLayer = SdbFindNextTagRef (hSDB, trAction, trLayer); } if (trLayer == TAGREF_NULL) { break; } QueryResult.atrLayers[i] = trLayer; } dwLength = 0; if (pdwBufferSize != NULL) { dwLength = *pdwBufferSize; } // // build compat layer // dwBufferSize = SdbBuildCompatEnvVariables(hSDB, &QueryResult, 0, NULL, pwszEnv, dwLength, NULL); if (pdwBufferSize != NULL) { *pdwBufferSize = dwBufferSize; } bSuccess = TRUE; out: if (hSDB != NULL) { SdbReleaseDatabase(hSDB); } return bSuccess; } /*++ Function: CheckAppcompatInfrastructureFlags Description: Checks various registry places for infrastructure global flags (just the disabled bit for now) The flags are set into the global variable gdwInfrastructureFlags. Function is used via the macro for perf reasons Return: global infrastructure flags --*/ DWORD CheckAppcompatInfrastructureFlags( VOID ) { gdwInfrastructureFlags = 0; // initialize just in case if (BaseIsAppcompatInfrastructureDisabled()) { gdwInfrastructureFlags |= APPCOMPAT_INFRA_DISABLED; } // // make the bits valid // gdwInfrastructureFlags |= APPCOMPAT_INFRA_VALID_FLAG; return gdwInfrastructureFlags; } /*++ Function: SdbInitDatabaseExport Description: This is "exported" version of the function SdbInitDatabase that checks for the "diabled" flag -- otherwise calls into SdbInitDatabase Return: see SdbInitDatabase --*/ HSDB SDBAPI SdbInitDatabaseExport( IN DWORD dwFlags, // flags that tell how the database should be // initialized. IN LPCWSTR pszDatabasePath // the OPTIONAL full path to the database to // be used. ) { if (IsAppcompatInfrastructureDisabled()) { return NULL; } return SdbInitDatabase(dwFlags, pszDatabasePath); }