#include "stdinc.h" #include "macros.h" #include "util.h" #include "fusionbuffer.h" #include "fusionhandle.h" #include "idp.h" #include "sxsid.h" #include "sxsutil.h" #undef FUSION_DEBUG_HEAP #define ULONG_STRING_LENGTH 8 #define ULONG_STRING_FORMAT L"%08lx" #define MANIFEST_ROOT_DIRECTORY_NAME L"Manifests" #define POLICY_ROOT_DIRECTORY_NAME L"Policies" #define ASSEMBLY_LONGEST_MANIFEST_FILE_NAME_SUFFIX L".Manifest" #define ASSEMBLY_POLICY_FILE_NAME_SUFFIX L".Policy" #define ASSEMBLY_MANIFEST_FILE_NAME_SUFFIX L".Manifest" #define ASSEMBLY_TYPE_WIN32 L"win32" #define ASSEMBLY_TYPE_WIN32_CCH (NUMBER_OF(ASSEMBLY_TYPE_WIN32) - 1) #define ASSEMBLY_TYPE_WIN32_POLICY L"win32-policy" #define ASSEMBLY_TYPE_WIN32_POLICY_CCH (NUMBER_OF(ASSEMBLY_TYPE_WIN32_POLICY) - 1) // // functions copied from sxs.dll // // extern BOOL SxspGetAssemblyIdentityAttributeValue( DWORD Flags, PCASSEMBLY_IDENTITY AssemblyIdentity, PCSXS_ASSEMBLY_IDENTITY_ATTRIBUTE_REFERENCE AttributeReference, OUT PCWSTR *StringOut, OUT SIZE_T *CchOut OPTIONAL ); /////////////////////////////////////////////////////////////////////////////// // function about delete a non-empty directory recursively /////////////////////////////////////////////////////////////////////////////// static VOID SxspDeleteDirectoryHelper( CStringBuffer &dir, WIN32_FIND_DATAW &wfd, DWORD &dwFirstError ) { // // the reason to add this call here is that if installation ends successfully, the directory // would be // C:\WINDOWS\WINSXS\INSTALLTEMP\15349016 // +---Manifests // // and they are "empty" directories (no files). Manifests is a SH dir so set it to be // FILE_ATTRIBUTE_NORMAL be more efficient. // // SetFileAttributesW(dir, FILE_ATTRIBUTE_NORMAL); if (RemoveDirectoryW(dir)) // empty dir return; // // this is the *only* "valid" reason for DeleteDirectory fail // but I am not sure about "only" // DWORD dwLastError = ::GetLastError(); if ( dwLastError != ERROR_DIR_NOT_EMPTY) { if (dwFirstError == 0) dwFirstError = dwLastError; return; } const static WCHAR SlashStar[] = L"\\*"; SIZE_T length = dir.Cch(); CFindFile findFile; if (!dir.Win32Append(SlashStar, NUMBER_OF(SlashStar) - 1)) { if (dwFirstError == NO_ERROR) dwFirstError = ::GetLastError(); goto Exit; } if (!findFile.Win32FindFirstFile(dir, &wfd)) { if (dwFirstError == NO_ERROR) dwFirstError = ::GetLastError(); goto Exit; } do { if (FusionpIsDotOrDotDot(wfd.cFileName)) continue; DWORD dwFileAttributes = wfd.dwFileAttributes; // Trim back to the slash... dir.Left(length + 1); if (dir.Win32Append(wfd.cFileName, ::wcslen(wfd.cFileName))) { if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // recurse SxspDeleteDirectoryHelper(dir, wfd, dwFirstError); } else { if (!DeleteFileW(dir)) { SetFileAttributesW(dir, FILE_ATTRIBUTE_NORMAL); if (!DeleteFileW(dir)) { if (dwFirstError == NO_ERROR) { // // continue even in delete file ( delete files as much as possible) // and record the errorCode for first failure // dwFirstError = ::GetLastError(); } } } } } } while (::FindNextFileW(findFile, &wfd)); if (::GetLastError() != ERROR_NO_MORE_FILES) { if (dwFirstError == NO_ERROR) dwFirstError = ::GetLastError(); } Exit: if (!findFile.Win32Close()) // otherwise RemoveDirectory fails if (dwFirstError == NO_ERROR) dwFirstError = ::GetLastError(); dir.Left(length); if (!RemoveDirectoryW(dir)) // the dir must be empty and NORMAL_ATTRIBUTE : ready to delete { if (dwFirstError == NO_ERROR) dwFirstError = ::GetLastError(); } } /*----------------------------------------------------------------------------- delete a directory recursively, continues upon errors, but returns FALSE if there were any. -----------------------------------------------------------------------------*/ HRESULT ca_SxspDeleteDirectory( const CStringBuffer &dir ) { HRESULT hr = S_OK; CStringBuffer mutableDir; WIN32_FIND_DATAW wfd = {0}; DWORD dwFirstError = ERROR_SUCCESS; IFFALSE_EXIT(mutableDir.Win32Assign(dir)); IFFALSE_EXIT(mutableDir.Win32RemoveTrailingPathSeparators()); ::SxspDeleteDirectoryHelper( mutableDir, wfd, dwFirstError); // // Set wFirstError to Teb->LastWin32Error // if (dwFirstError != ERROR_SUCCESS) goto Exit; Exit: return hr; } /////////////////////////////////////////////////////////////////////////////// // function about assembly Identity /////////////////////////////////////////////////////////////////////////////// HRESULT ca_SxspFormatULONG( ULONG ul, SIZE_T CchBuffer, WCHAR Buffer[], SIZE_T *CchWrittenOrRequired ) { HRESULT hr = S_OK; int cch; if (CchWrittenOrRequired != NULL) *CchWrittenOrRequired = 0; PARAMETER_CHECK_NTC(Buffer != NULL); if (CchBuffer < (ULONG_STRING_LENGTH + 1)) { if (CchWrittenOrRequired != NULL) *CchWrittenOrRequired = ULONG_STRING_LENGTH + 1; SET_HRERR_AND_EXIT(ERROR_INSUFFICIENT_BUFFER); } cch = _snwprintf(Buffer, CchBuffer, ULONG_STRING_FORMAT, ul); INTERNAL_ERROR_CHECK_NTC(cch > 0); if (CchWrittenOrRequired != NULL) *CchWrittenOrRequired = cch; Exit: return hr; } /////////////////////////////////////////////////////////////////////////////// // function about assembly Identity /////////////////////////////////////////////////////////////////////////////// #define ASSEMBLY_NAME_VALID_SPECIAL_CHARACTERS L".-" #define ASSEMBLY_NAME_PRIM_MAX_LENGTH 64 #define ASSEMBLY_NAME_VALID_SEPARATORS L"." #define ASSEMBLY_NAME_TRIM_INDICATOR_LENGTH 2 #define ASSEMBLY_NAME_TRIM_INDICATOR L".." #define SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_PUBLICKEY_MISSING_VALUE L"no-public-key" BOOL IsValidAssemblyNameCharacter(WCHAR ch) { if (((ch >= L'A') && (ch <= L'Z')) || ((ch >= L'a') && (ch <= L'z')) || ((ch >= L'0') && (ch <= L'9')) || (wcschr(ASSEMBLY_NAME_VALID_SPECIAL_CHARACTERS, ch)!= NULL)) { return TRUE; } else return FALSE; /* if (wcschr(ASSEMBLY_NAME_VALID_SPECIAL_CHARACTERS, ch)) return FALSE; else return TRUE; */ } HRESULT ca_SxspGenerateAssemblyNamePrimeFromName( PCWSTR pszAssemblyName, SIZE_T CchAssemblyName, CBaseStringBuffer *Buffer ) { HRESULT hr = S_OK; PWSTR pStart = NULL, pEnd = NULL; PWSTR qEnd = NULL, pszBuffer = NULL; ULONG i, j, len, ulSpaceLeft; ULONG cch; PWSTR pLeftEnd = NULL, pRightStart = NULL, PureNameEnd = NULL, PureNameStart = NULL; CStringBuffer buffTemp; CStringBufferAccessor accessor; PARAMETER_CHECK_NTC(pszAssemblyName != NULL); PARAMETER_CHECK_NTC(Buffer != NULL); // See how many characters we need max in the temporary buffer. cch = 0; for (i=0; iWin32Assign(pszBuffer, cch)); } else { // name is too long, have to trim a little bit ulSpaceLeft = ASSEMBLY_NAME_PRIM_MAX_LENGTH; PureNameStart = pszBuffer; PureNameEnd = pszBuffer + j; pLeftEnd = PureNameStart; pRightStart = PureNameEnd; while (PureNameStart < PureNameEnd) { // left end pStart = PureNameStart; i = 0; while ((wcschr(ASSEMBLY_NAME_VALID_SEPARATORS, pStart[i]) == 0) && (pStart+i != pRightStart)) // not a separator character i++; pEnd = pStart + i ; len = i; // it should be length of WCHAR! not BYTE!!! if (len >= ulSpaceLeft - ASSEMBLY_NAME_TRIM_INDICATOR_LENGTH) {// because we use ".." if trim happen pLeftEnd += (ulSpaceLeft - ASSEMBLY_NAME_TRIM_INDICATOR_LENGTH); break; } ulSpaceLeft -= len; pLeftEnd = pEnd; // "abc.xxxxxxx" pointing to "c" // right end qEnd = PureNameEnd; i = 0 ; while ((qEnd+i != pLeftEnd) && (wcschr(ASSEMBLY_NAME_VALID_SEPARATORS, qEnd[i]) == 0)) i--; len = 0 - i; if (len >= ulSpaceLeft - ASSEMBLY_NAME_TRIM_INDICATOR_LENGTH) {// because we use ".." if trim happen pRightStart -= ulSpaceLeft - ASSEMBLY_NAME_TRIM_INDICATOR_LENGTH; break; } ulSpaceLeft -= len; PureNameStart = pLeftEnd + 1; PureNameEnd = pRightStart - 1; } // end of while IFFALSE_EXIT(Buffer->Win32Assign(pszBuffer, pLeftEnd-pszBuffer)); IFFALSE_EXIT(Buffer->Win32Append(ASSEMBLY_NAME_TRIM_INDICATOR, NUMBER_OF(ASSEMBLY_NAME_TRIM_INDICATOR) - 1)); IFFALSE_EXIT(Buffer->Win32Append(pRightStart, ::wcslen(pRightStart))); // till end of the buffer } Exit: return hr; } HRESULT ca_SxspGenerateSxsPath( IN DWORD Flags, IN ULONG PathType, IN const WCHAR *AssemblyRootDirectory OPTIONAL, IN SIZE_T AssemblyRootDirectoryCch, IN PCASSEMBLY_IDENTITY pAssemblyIdentity, IN OUT CBaseStringBuffer &PathBuffer ) { HRESULT hr = S_OK; SIZE_T cch = 0; PCWSTR pszAssemblyName=NULL, pszVersion=NULL, pszProcessorArchitecture=NULL, pszLanguage=NULL, pszPolicyFileNameWithoutExt = NULL; PCWSTR pszAssemblyStrongName=NULL; SIZE_T AssemblyNameCch = 0, AssemblyStrongNameCch=0, VersionCch=0, ProcessorArchitectureCch=0, LanguageCch=0; SIZE_T PolicyFileNameWithoutExtCch=0; BOOL fNeedSlashAfterRoot = FALSE; ULONG IdentityHash; BOOL fOmitRoot = ((Flags & SXSP_GENERATE_SXS_PATH_FLAG_OMIT_ROOT) != 0); BOOL fPartialPath = ((Flags & SXSP_GENERATE_SXS_PATH_FLAG_PARTIAL_PATH) != 0); WCHAR HashBuffer[ULONG_STRING_LENGTH + 1]; SIZE_T HashBufferCch; CSmallStringBuffer NamePrimeBuffer; #if DBG_SXS ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_INFO, "SXS.DLL: Entered %s()\n" " Flags = 0x%08lx\n" " AssemblyRootDirectory = %p\n" " AssemblyRootDirectoryCch = %lu\n" " PathBuffer = %p\n", __FUNCTION__, Flags, AssemblyRootDirectory, AssemblyRootDirectoryCch, &PathBuffer); #endif // DBG_SXS PARAMETER_CHECK_NTC( (PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_ASSEMBLY) || (PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST) || (PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY)); PARAMETER_CHECK_NTC(pAssemblyIdentity != NULL); PARAMETER_CHECK_NTC((Flags & ~(SXSP_GENERATE_SXS_PATH_FLAG_OMIT_VERSION | SXSP_GENERATE_SXS_PATH_FLAG_OMIT_ROOT | SXSP_GENERATE_SXS_PATH_FLAG_PARTIAL_PATH)) == 0); // Not supplying the assembly root is only legal if you're asking for it to be left out... PARAMETER_CHECK_NTC((AssemblyRootDirectoryCch != 0) || (Flags & SXSP_GENERATE_SXS_PATH_FLAG_OMIT_ROOT)); // You can't combine SXSP_GENERATE_SXS_PATH_FLAG_PARTIAL_PATH with anything else... PARAMETER_CHECK_NTC( ((Flags & SXSP_GENERATE_SXS_PATH_FLAG_PARTIAL_PATH) == 0) || ((Flags & ~(SXSP_GENERATE_SXS_PATH_FLAG_PARTIAL_PATH)) == 0)); // get AssemblyName IFFALSE_EXIT(::SxspGetAssemblyIdentityAttributeValue(0, pAssemblyIdentity, &s_IdentityAttribute_name, &pszAssemblyName, &AssemblyNameCch)); INTERNAL_ERROR_CHECK_NTC((pszAssemblyName != NULL) && (AssemblyNameCch != 0)); // get AssemblyName' based on AssemblyName IFFAILED_EXIT(ca_SxspGenerateAssemblyNamePrimeFromName(pszAssemblyName, AssemblyNameCch, &NamePrimeBuffer)); // get Assembly Version IFFALSE_EXIT(::SxspGetAssemblyIdentityAttributeValue( SXSP_GET_ASSEMBLY_IDENTITY_ATTRIBUTE_VALUE_FLAG_NOT_FOUND_RETURNS_NULL, // for policy_lookup, no version is used pAssemblyIdentity, &s_IdentityAttribute_version, &pszVersion, &VersionCch)); if ((Flags & SXSP_GENERATE_SXS_PATH_FLAG_OMIT_VERSION) || (PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY)) { // for policy file, version of the policy file is used as policy filename pszPolicyFileNameWithoutExt = pszVersion; PolicyFileNameWithoutExtCch = VersionCch; pszVersion = NULL; VersionCch = 0; } else { PARAMETER_CHECK_NTC((pszVersion != NULL) && (VersionCch != 0)); } // get Assembly Langage IFFALSE_EXIT( ::SxspGetAssemblyIdentityAttributeValue( SXSP_GET_ASSEMBLY_IDENTITY_ATTRIBUTE_VALUE_FLAG_NOT_FOUND_RETURNS_NULL, pAssemblyIdentity, &s_IdentityAttribute_language, &pszLanguage, &LanguageCch)); if (pszLanguage == NULL) { pszLanguage = SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_LANGUAGE_MISSING_VALUE; LanguageCch = NUMBER_OF(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_LANGUAGE_MISSING_VALUE) - 1; } // get Assembly ProcessorArchitecture IFFALSE_EXIT( ::SxspGetAssemblyIdentityAttributeValue( SXSP_GET_ASSEMBLY_IDENTITY_ATTRIBUTE_VALUE_FLAG_NOT_FOUND_RETURNS_NULL, pAssemblyIdentity, &s_IdentityAttribute_processorArchitecture, &pszProcessorArchitecture, &ProcessorArchitectureCch)); if (pszProcessorArchitecture == NULL) { pszProcessorArchitecture = L"data"; ProcessorArchitectureCch = 4; } // get Assembly StrongName IFFALSE_EXIT( ::SxspGetAssemblyIdentityAttributeValue( SXSP_GET_ASSEMBLY_IDENTITY_ATTRIBUTE_VALUE_FLAG_NOT_FOUND_RETURNS_NULL, pAssemblyIdentity, &s_IdentityAttribute_publicKeyToken, &pszAssemblyStrongName, &AssemblyStrongNameCch)); if (pszAssemblyStrongName == NULL) { pszAssemblyStrongName = SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_PUBLICKEY_MISSING_VALUE; AssemblyStrongNameCch = NUMBER_OF(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_PUBLICKEY_MISSING_VALUE) - 1; } //get Assembly Hash String if ((PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY) || (Flags & SXSP_GENERATE_SXS_PATH_FLAG_OMIT_VERSION)) { IFFALSE_EXIT(::SxspHashAssemblyIdentityForPolicy(0, pAssemblyIdentity, IdentityHash)); } else { IFFALSE_EXIT(::SxsHashAssemblyIdentity(0, pAssemblyIdentity, &IdentityHash)); } IFFAILED_EXIT(ca_SxspFormatULONG(IdentityHash, NUMBER_OF(HashBuffer), HashBuffer, &HashBufferCch)); INTERNAL_ERROR_CHECK_NTC(HashBufferCch == ULONG_STRING_LENGTH); if (!fOmitRoot) { // If the assembly root was not passed in, get it. fNeedSlashAfterRoot = (! ::FusionpIsPathSeparator(AssemblyRootDirectory[AssemblyRootDirectoryCch-1])); } else { // If we don't want to include the root, then don't account for it below... AssemblyRootDirectoryCch = 0; fNeedSlashAfterRoot = FALSE; } // this computation can be off by one or a few, it's an optimization // to pregrow a string buffer cch = AssemblyRootDirectoryCch + // "C:\WINNT\WinSxS\" (fNeedSlashAfterRoot ? 1 : 0); switch (PathType) { case SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST: // Wacky parens and ... - 1) + 1) to reinforce that it's the number of // characters in the string not including the null and then an extra separator. cch += (NUMBER_OF(MANIFEST_ROOT_DIRECTORY_NAME) - 1) + 1; break; case SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY: // Wacky parens and ... - 1) + 1) to reinforce that it's the number of // characters in the string not including the null and then an extra separator. cch += (NUMBER_OF(POLICY_ROOT_DIRECTORY_NAME) - 1) + 1; break; } cch++; // fPartialPath means that we don't actually want to take the assembly's identity into // account; the caller just wants the path to the manifests or policies directories. if (!fPartialPath) { cch += ProcessorArchitectureCch + // "x86" 1 + // "_" NamePrimeBuffer.Cch() + // "FooBar" 1 + // "_" AssemblyStrongNameCch + // StrongName 1 + // "_" VersionCch + // "5.6.2900.42" 1 + // "_" LanguageCch + // "0409" 1 + // "_" HashBufferCch; if (PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST) { cch += NUMBER_OF(ASSEMBLY_LONGEST_MANIFEST_FILE_NAME_SUFFIX); // ".manifest\0" } else if (PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY) { // "_" has already reserve space for "\" cch += PolicyFileNameWithoutExtCch; cch += NUMBER_OF(ASSEMBLY_POLICY_FILE_NAME_SUFFIX); // ".policy\0" } else { // pathType must be SXSP_GENERATE_SXS_PATH_PATHTYPE_ASSEMBLY // if (!fOmitRoot) // cch++; cch++; // trailing null character } } // We try to ensure that the buffer is big enough up front so that we don't have to do any // dynamic reallocation during the actual process. IFFALSE_EXIT(PathBuffer.Win32ResizeBuffer(cch, eDoNotPreserveBufferContents)); // Note that since when GENERATE_ASSEMBLY_PATH_OMIT_ROOT is set, we force AssemblyRootDirectoryCch to zero // and fNeedSlashAfterRoot to FALSE, so the first two entries in this concatenation actually don't // contribute anything to the string constructed. if (fPartialPath) { IFFALSE_EXIT(PathBuffer.Win32AssignW(5, AssemblyRootDirectory, static_cast(AssemblyRootDirectoryCch), // "C:\WINNT\WINSXS" L"\\", (fNeedSlashAfterRoot ? 1 : 0), // optional '\' // manifests subdir MANIFEST_ROOT_DIRECTORY_NAME, ((PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST) ? NUMBER_OF(MANIFEST_ROOT_DIRECTORY_NAME) -1 : 0), // "manifests" // policies subdir POLICY_ROOT_DIRECTORY_NAME, ((PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY)? NUMBER_OF(POLICY_ROOT_DIRECTORY_NAME) - 1 : 0), // "policies" L"\\", (((PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST) || (PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY)) ? 1 : 0) )); // optional '\' } else { // // create one of below // (1) fully-qualified manifest filename, // eg, [C:\WINNT\WinSxS\]Manifests\X86_DynamicDll_6595b64144ccf1df_2.0.0.0_en-us_2f433926.Manifest // (2) fully-qualified policy filename, // eg, [C:\WINNT\WinSxS\]Policies\x86_policy.1.0.DynamicDll_b54bc117ce08a1e8_en-us_d51541cb\1.1.0.0.cat // (3) fully-qulified assembly name (w. or w/o a version) // eg, [C:\WINNT\WinSxS\]x86_DynamicDll_6595b64144ccf1df_6.0.0.0_x-ww_ff9986d7 // IFFALSE_EXIT( PathBuffer.Win32AssignW(17, AssemblyRootDirectory, static_cast(AssemblyRootDirectoryCch), // "C:\WINNT\WINSXS" L"\\", (fNeedSlashAfterRoot ? 1 : 0), // optional '\' MANIFEST_ROOT_DIRECTORY_NAME, ((PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST) ? NUMBER_OF(MANIFEST_ROOT_DIRECTORY_NAME) - 1 : 0), POLICY_ROOT_DIRECTORY_NAME, ((PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY) ? NUMBER_OF(POLICY_ROOT_DIRECTORY_NAME) - 1 : 0), L"\\", (((PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST) || (PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY)) ? 1 : 0), // optional '\' pszProcessorArchitecture, static_cast(ProcessorArchitectureCch), L"_", 1, static_cast(NamePrimeBuffer), static_cast(NamePrimeBuffer.Cch()), L"_", 1, pszAssemblyStrongName, static_cast(AssemblyStrongNameCch), L"_", (VersionCch != 0) ? 1 : 0, pszVersion, static_cast(VersionCch), L"_", 1, pszLanguage, static_cast(LanguageCch), L"_", 1, static_cast(HashBuffer), static_cast(HashBufferCch), L"\\", ((fOmitRoot ||(PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST)) ? 0 : 1))); if (PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST) IFFALSE_EXIT(PathBuffer.Win32Append(ASSEMBLY_MANIFEST_FILE_NAME_SUFFIX, NUMBER_OF(ASSEMBLY_MANIFEST_FILE_NAME_SUFFIX) - 1)); else if (PathType == SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY) { if ((pszPolicyFileNameWithoutExt != NULL) && (PolicyFileNameWithoutExtCch >0)) { IFFALSE_EXIT(PathBuffer.Win32Append(pszPolicyFileNameWithoutExt, PolicyFileNameWithoutExtCch)); IFFALSE_EXIT(PathBuffer.Win32Append(ASSEMBLY_POLICY_FILE_NAME_SUFFIX, NUMBER_OF(ASSEMBLY_POLICY_FILE_NAME_SUFFIX) - 1)); } } } Exit: return hr; } HRESULT ca_SxspDetermineAssemblyType( PCASSEMBLY_IDENTITY pAssemblyIdentity, BOOL &fIsWin32, BOOL &fIsWin32Policy ) { HRESULT hr = S_OK; PCWSTR pcwszType = NULL; SIZE_T cchType = 0; fIsWin32 = FALSE; fIsWin32Policy = FALSE; PARAMETER_CHECK_NTC(pAssemblyIdentity != NULL); IFFALSE_EXIT( ::SxspGetAssemblyIdentityAttributeValue( SXSP_GET_ASSEMBLY_IDENTITY_ATTRIBUTE_VALUE_FLAG_NOT_FOUND_RETURNS_NULL, pAssemblyIdentity, &s_IdentityAttribute_type, &pcwszType, &cchType)); fIsWin32 = (::FusionpCompareStrings(pcwszType, cchType, ASSEMBLY_TYPE_WIN32, ASSEMBLY_TYPE_WIN32_CCH, false) == 0); if (!fIsWin32) fIsWin32Policy = (::FusionpCompareStrings(pcwszType, cchType, ASSEMBLY_TYPE_WIN32_POLICY, ASSEMBLY_TYPE_WIN32_POLICY_CCH, false) == 0); Exit: return hr; }