static HRESULT BeginChangeFile( __in LPCWSTR pwzFile, __in int iCompAttributes, __inout LPWSTR* ppwzCustomActionData ) { Assert(pwzFile && *pwzFile && ppwzCustomActionData); HRESULT hr = S_OK; BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit; LPBYTE pbData = NULL; DWORD cbData = 0; LPWSTR pwzRollbackCustomActionData = NULL; if (fIs64Bit) { hr = WcaWriteIntegerToCaData((int)xaOpenFilex64, ppwzCustomActionData); ExitOnFailure(hr, "failed to write 64-bit file indicator to custom action data"); } else { hr = WcaWriteIntegerToCaData((int)xaOpenFile, ppwzCustomActionData); ExitOnFailure(hr, "failed to write file indicator to custom action data"); } hr = WcaWriteStringToCaData(pwzFile, ppwzCustomActionData); ExitOnFailure1(hr, "failed to write file to custom action data: %ls", pwzFile); // If the file already exits, then we have to put it back the way it was on failure if (FileExistsEx(pwzFile, NULL)) { hr = FileRead(&pbData, &cbData, pwzFile); ExitOnFailure1(hr, "failed to read file: %ls", pwzFile); // Set up the rollback for this file hr = WcaWriteIntegerToCaData((int)fIs64Bit, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to write component bitness to rollback custom action data"); hr = WcaWriteStringToCaData(pwzFile, &pwzRollbackCustomActionData); ExitOnFailure1(hr, "failed to write file name to rollback custom action data: %ls", pwzFile); hr = WcaWriteStreamToCaData(pbData, cbData, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to write file contents to rollback custom action data."); hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"ExecXmlConfigRollback"), pwzRollbackCustomActionData, COST_XMLFILE); ExitOnFailure1(hr, "failed to schedule ExecXmlConfigRollback for file: %ls", pwzFile); ReleaseStr(pwzRollbackCustomActionData); } LExit: ReleaseMem(pbData); return hr; }
extern "C" HRESULT DAPI LocProbeForFile( __in_z LPCWSTR wzBasePath, __in_z LPCWSTR wzLocFileName, __in_z_opt LPCWSTR wzLanguage, __inout LPWSTR* psczPath ) { HRESULT hr = S_OK; LPWSTR sczProbePath = NULL; LANGID langid = 0; LPWSTR sczLangIdFile = NULL; // If a language was specified, look for a loc file in that as a directory. if (wzLanguage && *wzLanguage) { hr = PathConcat(wzBasePath, wzLanguage, &sczProbePath); ExitOnFailure(hr, "Failed to concat base path to language."); hr = PathConcat(sczProbePath, wzLocFileName, &sczProbePath); ExitOnFailure(hr, "Failed to concat loc file name to probe path."); if (FileExistsEx(sczProbePath, NULL)) { ExitFunction(); } } langid = ::GetUserDefaultLangID(); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); ExitOnFailure(hr, "Failed to format user langid."); hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); ExitOnFailure(hr, "Failed to concat user langid file name to base path."); if (FileExistsEx(sczProbePath, NULL)) { ExitFunction(); } if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) { langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); ExitOnFailure(hr, "Failed to format user langid (default sublang)."); hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); ExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); if (FileExistsEx(sczProbePath, NULL)) { ExitFunction(); } } langid = ::GetSystemDefaultUILanguage(); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); ExitOnFailure(hr, "Failed to format system langid."); hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); ExitOnFailure(hr, "Failed to concat system langid file name to base path."); if (FileExistsEx(sczProbePath, NULL)) { ExitFunction(); } if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) { langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); ExitOnFailure(hr, "Failed to format user langid (default sublang)."); hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); ExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); if (FileExistsEx(sczProbePath, NULL)) { ExitFunction(); } } // Finally, look for the loc file in the base path. hr = PathConcat(wzBasePath, wzLocFileName, &sczProbePath); ExitOnFailure(hr, "Failed to concat loc file name to base path."); if (!FileExistsEx(sczProbePath, NULL)) { hr = E_FILENOTFOUND; } LExit: if (SUCCEEDED(hr)) { hr = StrAllocString(psczPath, sczProbePath, 0); } ReleaseStr(sczLangIdFile); ReleaseStr(sczProbePath); return hr; }
HRESULT HandleLock( __inout CFGDB_STRUCT *pcdb ) { HRESULT hr = S_OK; ::EnterCriticalSection(&pcdb->cs); ++pcdb->dwLockRefCount; if (1 < pcdb->dwLockRefCount) { ExitFunction1(hr = S_OK); } // This should only be set to TRUE if the database was successfully completely synced with local upon unlock pcdb->fUpdateLastModified = FALSE; // Connect to database, if it's a remote database if (pcdb->fRemote) { // If database path is present right now, clean up conflicted databases if (FileExistsEx(pcdb->sczDbPath, NULL)) { hr = CleanConflictedDatabases(pcdb->sczDbDir, pcdb->sczDbPath); TraceError(hr, "Failed to clean conflicted databases, continuing"); hr = S_OK; } hr = FileCreateTempW(L"Remote", L".sdf", &pcdb->sczDbCopiedPath, NULL); ExitOnFailure(hr, "Failed to create temp file to copy database file to"); hr = FileEnsureCopy(pcdb->sczDbPath, pcdb->sczDbCopiedPath, TRUE); ExitOnFailure(hr, "Failed to copy remote database locally"); hr = FileGetTime(pcdb->sczDbCopiedPath, NULL, NULL, &pcdb->ftBeforeModify); ExitOnFailure(hr, "Failed to get modified time of copied remote %ls", pcdb->sczDbCopiedPath); hr = SceEnsureDatabase(pcdb->sczDbCopiedPath, wzSqlCeDllPath, L"CfgRemote", 1, &pcdb->dsSceDb, &pcdb->psceDb); ExitOnFailure(hr, "Failed to ensure SQL CE database at %ls exists", pcdb->sczDbPath); // If the remote wasn't up when we initialized, we couldn't get cfg app id or GUID, so get it now if (DWORD_MAX == pcdb->dwCfgAppID) { hr = HandleEnsureSummaryDataTable(pcdb); ExitOnFailure(hr, "Failed to ensure remote database summary data"); hr = GuidListEnsure(pcdb->pcdbLocal, pcdb->sczGuid, &pcdb->sczGuidRemoteInLocalKey); ExitOnFailure(hr, "Failed to ensure remote database is in local database's guid table"); hr = GuidListEnsure(pcdb, pcdb->pcdbLocal->sczGuid, &pcdb->sczGuidLocalInRemoteKey); ExitOnFailure(hr, "Failed to ensure local database is in remote database's guid table"); hr = ProductSet(pcdb, wzCfgProductId, wzCfgVersion, wzCfgPublicKey, TRUE, NULL); ExitOnFailure(hr, "Failed to set product to cfg product id"); pcdb->dwCfgAppID = pcdb->dwAppID; } } LExit: if (FAILED(hr)) { --pcdb->dwLockRefCount; ::LeaveCriticalSection(&pcdb->cs); } return hr; }
static HRESULT DeleteEmptyDirectory( __in LEGACY_FILE_TYPE fileType, __in_z LPCWSTR wzPath ) { HRESULT hr = S_OK; LPWSTR sczParentDirectory = NULL; DWORD dwIndex = 0; LPWSTR pwcLastBackslash = NULL; // If it's an individual file and it exists, no point trying to delete any directories for it if (LEGACY_FILE_PLAIN == fileType) { if (FileExistsEx(wzPath, NULL)) { ExitFunction1(hr = S_OK); } } else { // It's a directory, so delete children first hr = DeleteEmptyDirectoryChildren(wzPath); // This code is just an FYI that the directory was not empty and so wasn't deleted. It's not an error, so ignore it. if (FAILED(hr)) { ExitFunction1(hr = S_OK); } ExitOnFailure(hr, "Failed to check for empty directories and delete them at path: %ls", wzPath); } hr = StrAllocString(&sczParentDirectory, wzPath, 0); ExitOnFailure(hr, "Failed to allocate copy of directory"); // Eliminate any trailing backslashes from the directory first, if there are any dwIndex = lstrlenW(sczParentDirectory); if (0 == dwIndex) { hr = E_INVALIDARG; ExitOnFailure(hr, "Unexpected empty parent directory encountered while deleting empty directories"); } --dwIndex; // Start at the last character of the string while (dwIndex > 0 && sczParentDirectory[dwIndex] == L'\\') { sczParentDirectory[dwIndex] = L'\0'; --dwIndex; } if (0 == dwIndex) { hr = E_INVALIDARG; ExitOnFailure(hr, "Parent directory was entirely composed of backslashes!"); } // Now delete any empty parent directories we see as well while (NULL != (pwcLastBackslash = wcsrchr(sczParentDirectory, L'\\'))) { hr = DirEnsureDelete(sczParentDirectory, FALSE, FALSE); if (FAILED(hr)) { LogErrorString(hr, "Failed to check for empty parent directories and delete them at directory: %ls", sczParentDirectory); hr = S_OK; break; } *pwcLastBackslash = L'\0'; } LExit: ReleaseStr(sczParentDirectory); return hr; }
// Searches subdirectories of the given path for the highest version of ngen.exe available static HRESULT GetNgenVersion( __in LPWSTR pwzParentPath, __out LPWSTR* ppwzVersion ) { Assert(pwzParentPath); HRESULT hr = S_OK; DWORD dwError = 0; DWORD dwNgenFileFlags = 0; LPWSTR pwzVersionSearch = NULL; LPWSTR pwzNgen = NULL; LPWSTR pwzTemp = NULL; LPWSTR pwzTempVersion = NULL; DWORD dwMaxMajorVersion = 0; // This stores the highest major version we've seen so far DWORD dwMaxMinorVersion = 0; // This stores the minor version of the highest major version we've seen so far DWORD dwMajorVersion = 0; // This stores the major version of the directory we're currently considering DWORD dwMinorVersion = 0; // This stores the minor version of the directory we're currently considering BOOL fFound = TRUE; WIN32_FIND_DATAW wfdVersionDirectories; HANDLE hFind = INVALID_HANDLE_VALUE; hr = StrAllocFormatted(&pwzVersionSearch, L"%s*", pwzParentPath); ExitOnFailure1(hr, "failed to create outer directory search string from string %ls", pwzParentPath); hFind = FindFirstFileW(pwzVersionSearch, &wfdVersionDirectories); if (hFind == INVALID_HANDLE_VALUE) { ExitWithLastError1(hr, "failed to call FindFirstFileW with string %ls", pwzVersionSearch); } while (fFound) { pwzTempVersion = (LPWSTR)&(wfdVersionDirectories.cFileName); // Explicitly exclude v1.1.4322, which isn't backwards compatible and is not supported if (wfdVersionDirectories.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (0 != lstrcmpW(L"v1.1.4322", pwzTempVersion)) { // A potential candidate directory was found to run ngen from - let's make sure ngen actually exists here hr = StrAllocFormatted(&pwzNgen, L"%s%s\\ngen.exe", pwzParentPath, pwzTempVersion); ExitOnFailure2(hr, "failed to create inner ngen search string with strings %ls and %ls", pwzParentPath, pwzTempVersion); // If Ngen.exe does exist as a file here, then let's check the file version if (FileExistsEx(pwzNgen, &dwNgenFileFlags) && (0 == (dwNgenFileFlags & FILE_ATTRIBUTE_DIRECTORY))) { hr = FileVersion(pwzNgen, &dwMajorVersion, &dwMinorVersion); if (FAILED(hr)) { WcaLog(LOGMSG_VERBOSE, "Failed to get version of %ls - continuing", pwzNgen); } else if (dwMajorVersion > dwMaxMajorVersion || (dwMajorVersion == dwMaxMajorVersion && dwMinorVersion > dwMaxMinorVersion)) { // If the version we found is the highest we've seen so far in this search, it will be our new best-so-far candidate hr = StrAllocString(ppwzVersion, pwzTempVersion, 0); ExitOnFailure1(hr, "failed to copy temp version string %ls to version string", pwzTempVersion); // Add one for the backslash after the directory name WcaLog(LOGMSG_VERBOSE, "Found highest-so-far version of ngen.exe (in directory %ls, version %u.%u.%u.%u)", *ppwzVersion, (DWORD)HIWORD(dwMajorVersion), (DWORD)LOWORD(dwMajorVersion), (DWORD)HIWORD(dwMinorVersion), (DWORD)LOWORD(dwMinorVersion)); dwMaxMajorVersion = dwMajorVersion; dwMaxMinorVersion = dwMinorVersion; } } else { WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it doesn't contain the file ngen.exe", pwzTempVersion); } } else { WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it is from .NET Framework v1.1, which is not backwards compatible with other versions of the Framework and thus is not supported by this custom action.", pwzTempVersion); } } else { WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it isn't a directory", pwzTempVersion); } fFound = FindNextFileW(hFind, &wfdVersionDirectories); if (!fFound) { dwError = ::GetLastError(); hr = (ERROR_NO_MORE_FILES == dwError) ? ERROR_SUCCESS : HRESULT_FROM_WIN32(dwError); ExitOnFailure1(hr, "Failed to call FindNextFileW() with query %ls", pwzVersionSearch); } } if (NULL == *ppwzVersion) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); ExitOnRootFailure1(hr, "Searched through all subdirectories of %ls, but failed to find any version of ngen.exe", pwzParentPath); } else { WcaLog(LOGMSG_VERBOSE, "Using highest version of ngen found, located in this subdirectory: %ls, version %u.%u.%u.%u", *ppwzVersion, (DWORD)HIWORD(dwMajorVersion), (DWORD)LOWORD(dwMajorVersion), (DWORD)HIWORD(dwMinorVersion), (DWORD)LOWORD(dwMinorVersion)); } LExit: if (hFind != INVALID_HANDLE_VALUE) { if (0 == FindClose(hFind)) { dwError = ::GetLastError(); hr = HRESULT_FROM_WIN32(dwError); WcaLog(LOGMSG_STANDARD, "Failed to close handle created by outer FindFirstFile with error %x - continuing", hr); } hFind = INVALID_HANDLE_VALUE; } ReleaseStr(pwzVersionSearch); ReleaseStr(pwzNgen); ReleaseStr(pwzTemp); // Purposely don't release pwzTempVersion, because it wasn't allocated in this function, it's just a pointer to a string inside wfdVersionDirectories return hr; }