From eb53852d7ae6838e54525eb57df1d8ce8a722f9b Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 24 Jun 2022 12:28:27 -0500 Subject: Add longPathAware to Burn manifest to support long paths. Fixes 3455 --- src/libs/dutil/WixToolset.DUtil/apputil.cpp | 28 +- src/libs/dutil/WixToolset.DUtil/cabcutil.cpp | 86 ++--- src/libs/dutil/WixToolset.DUtil/cabutil.cpp | 68 ++-- src/libs/dutil/WixToolset.DUtil/dirutil.cpp | 64 +--- src/libs/dutil/WixToolset.DUtil/fileutil.cpp | 12 +- src/libs/dutil/WixToolset.DUtil/inc/dirutil.h | 3 +- src/libs/dutil/WixToolset.DUtil/inc/pathutil.h | 67 +++- src/libs/dutil/WixToolset.DUtil/inc/shelutil.h | 15 + src/libs/dutil/WixToolset.DUtil/logutil.cpp | 19 +- src/libs/dutil/WixToolset.DUtil/path2utl.cpp | 145 +++++++- src/libs/dutil/WixToolset.DUtil/pathutil.cpp | 404 +++++++++++++++++---- src/libs/dutil/WixToolset.DUtil/rexutil.cpp | 77 ++-- src/libs/dutil/WixToolset.DUtil/sceutil.cpp | 11 +- src/libs/dutil/WixToolset.DUtil/shelutil.cpp | 210 ++++++++++- src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp | 182 +++++++++- 15 files changed, 1083 insertions(+), 308 deletions(-) (limited to 'src/libs') diff --git a/src/libs/dutil/WixToolset.DUtil/apputil.cpp b/src/libs/dutil/WixToolset.DUtil/apputil.cpp index b70c8cfb..9e75082a 100644 --- a/src/libs/dutil/WixToolset.DUtil/apputil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/apputil.cpp @@ -87,31 +87,27 @@ DAPI_(HRESULT) LoadSystemLibraryWithPath( ) { HRESULT hr = S_OK; - DWORD cch = 0; - WCHAR wzPath[MAX_PATH] = { }; + LPWSTR sczDirectory = NULL; + LPWSTR sczPath = NULL; - cch = ::GetSystemDirectoryW(wzPath, MAX_PATH); - AppExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory."); + hr = PathGetSystemDirectory(&sczDirectory); + AppExitOnFailure(hr, "Failed to get the Windows system directory."); - if (L'\\' != wzPath[cch - 1]) - { - hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1); - AppExitOnRootFailure(hr, "Failed to terminate the string with a backslash."); - } + hr = StrAllocFormatted(&sczPath, L"%ls%ls", sczDirectory, wzModuleName); + AppExitOnFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName); - hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName); - AppExitOnRootFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName); - - *phModule = ::LoadLibraryExW(wzPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); - AppExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", wzModuleName); + *phModule = ::LoadLibraryExW(sczPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + AppExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", sczPath); if (psczPath) { - hr = StrAllocString(psczPath, wzPath, MAX_PATH); - AppExitOnFailure(hr, "Failed to copy the path to library."); + *psczPath = sczPath; + sczPath = NULL; } LExit: + ReleaseStr(sczDirectory); + return hr; } diff --git a/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp b/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp index d1edc54d..294669af 100644 --- a/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp @@ -89,8 +89,8 @@ struct CABC_DATA STRINGDICT_HANDLE shDictHandle; - WCHAR wzCabinetPath[MAX_PATH]; - WCHAR wzEmptyFile[MAX_PATH]; + LPWSTR sczCabinetPath; + LPWSTR sczEmptyFile; HANDLE hEmptyFile; DWORD dwLastFileIndex; @@ -197,33 +197,17 @@ extern "C" HRESULT DAPI CabCBegin( HRESULT hr = S_OK; CABC_DATA *pcd = NULL; - WCHAR wzTempPath[MAX_PATH] = { }; C_ASSERT(sizeof(MSIFILEHASHINFO) == 20); - WCHAR wzPathBuffer [MAX_PATH] = L""; - size_t cchPathBuffer; + LPWSTR pwzPathBuffer = NULL; if (wzCabDir) { - hr = ::StringCchLengthW(wzCabDir, MAX_PATH, &cchPathBuffer); - CabcExitOnFailure(hr, "Failed to get length of cab directory"); - - // Need room to terminate with L'\\' and L'\0' - if((MAX_PATH - 1) <= cchPathBuffer || 0 == cchPathBuffer) - { - hr = E_INVALIDARG; - CabcExitOnFailure(hr, "Cab directory had invalid length: %u", cchPathBuffer); - } - - hr = ::StringCchCopyW(wzPathBuffer, countof(wzPathBuffer), wzCabDir); + hr = StrAllocString(&pwzPathBuffer, wzCabDir, 0); CabcExitOnFailure(hr, "Failed to copy cab directory to buffer"); - if (L'\\' != wzPathBuffer[cchPathBuffer - 1]) - { - hr = ::StringCchCatW(wzPathBuffer, countof(wzPathBuffer), L"\\"); - CabcExitOnFailure(hr, "Failed to cat \\ to end of buffer"); - ++cchPathBuffer; - } + hr = PathBackslashTerminate(&pwzPathBuffer); + CabcExitOnFailure(hr, "Failed to cat \\ to end of buffer"); } pcd = static_cast(MemAlloc(sizeof(CABC_DATA), TRUE)); @@ -290,33 +274,23 @@ extern "C" HRESULT DAPI CabCBegin( CabcExitWithLastError(hr, "failed to convert cab name to multi-byte"); } - if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzPathBuffer, -1, pcd->ccab.szCabPath, sizeof(pcd->ccab.szCab), NULL, NULL)) + if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, pwzPathBuffer, -1, pcd->ccab.szCabPath, sizeof(pcd->ccab.szCab), NULL, NULL)) { CabcExitWithLastError(hr, "failed to convert cab dir to multi-byte"); } // Remember the path to the cabinet. - hr= ::StringCchCopyW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzPathBuffer); - CabcExitOnFailure(hr, "Failed to copy cabinet path from path: %ls", wzPathBuffer); - - hr = ::StringCchCatW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzCab); + hr = PathConcat(pwzPathBuffer, wzCab, &pcd->sczCabinetPath); CabcExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab); // Get the empty file to use as the blank marker for duplicates. - if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) - { - CabcExitWithLastError(hr, "Failed to get temp path."); - } - - if (!::GetTempFileNameW(wzTempPath, L"WSC", 0, pcd->wzEmptyFile)) - { - CabcExitWithLastError(hr, "Failed to create a temp file name."); - } + hr = DirCreateTempPath(L"WSC", &pcd->sczEmptyFile); + CabcExitOnFailure(hr, "Failed to create a temp file name."); // Try to open the newly created empty file (remember, GetTempFileName() is kind enough to create a file for us) // with a handle to automatically delete the file on close. Ignore any failure that might happen, since the worst // case is we'll leave a zero byte file behind in the temp folder. - pcd->hEmptyFile = ::CreateFileW(pcd->wzEmptyFile, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); + pcd->hEmptyFile = ::CreateFileW(pcd->sczEmptyFile, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_CASEINSENSITIVE); CabcExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files"); @@ -358,6 +332,8 @@ extern "C" HRESULT DAPI CabCBegin( *phContext = pcd; LExit: + ReleaseStr(pwzPathBuffer); + if (FAILED(hr) && pcd && pcd->hfci) { ::FCIDestroy(pcd->hfci); @@ -527,7 +503,7 @@ extern "C" HRESULT DAPI CabCFinish( // files point at the same path (the empty file) so there is no point in tracking them with // their path. fileInfo.wzSourcePath = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzSourcePath; - fileInfo.wzEmptyPath = pcd->wzEmptyFile; + fileInfo.wzEmptyPath = pcd->sczEmptyFile; // Use the provided token, otherwise default to the source file name. if (pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken) @@ -643,7 +619,7 @@ extern "C" HRESULT DAPI CabCFinish( if (pcd->fGoodCab && pcd->cDuplicates) { hr = UpdateDuplicateFiles(pcd); - CabcExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->wzCabinetPath); + CabcExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->sczCabinetPath); } LExit: @@ -692,6 +668,9 @@ static void FreeCabCData( ReleaseMem(pcd->prgFiles); ReleaseMem(pcd->prgDuplicates); + ReleaseStr(pcd->sczCabinetPath); + ReleaseStr(pcd->sczEmptyFile); + ReleaseMem(pcd); } } @@ -709,7 +688,7 @@ static HRESULT CheckForDuplicateFile( __in LONGLONG llFileSize ) { - DWORD i; + DWORD i = 0; HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; @@ -916,17 +895,17 @@ static HRESULT UpdateDuplicateFiles( LPVOID pv = NULL; MS_CABINET_HEADER *pCabinetHeader = NULL; - hCabinet = ::CreateFileW(pcd->wzCabinetPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + hCabinet = ::CreateFileW(pcd->sczCabinetPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hCabinet) { - CabcExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->wzCabinetPath); + CabcExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->sczCabinetPath); } // Shouldn't need more than 16 MB to get the whole cabinet header into memory so use that as // the upper bound for the memory map. if (!::GetFileSizeEx(hCabinet, &liCabinetSize)) { - CabcExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->wzCabinetPath); + CabcExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->sczCabinetPath); } if (0 == liCabinetSize.HighPart && liCabinetSize.LowPart < MAX_CABINET_HEADER_SIZE) @@ -942,11 +921,11 @@ static HRESULT UpdateDuplicateFiles( hCabinetMapping = ::CreateFileMappingW(hCabinet, NULL, PAGE_READWRITE | SEC_COMMIT, 0, cbCabinet, NULL); if (NULL == hCabinetMapping || INVALID_HANDLE_VALUE == hCabinetMapping) { - CabcExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->wzCabinetPath); + CabcExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->sczCabinetPath); } pv = ::MapViewOfFile(hCabinetMapping, FILE_MAP_WRITE, 0, 0, 0); - CabcExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->wzCabinetPath); + CabcExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->sczCabinetPath); pCabinetHeader = static_cast(pv); @@ -1155,7 +1134,7 @@ static __callback INT_PTR DIAMONDAPI CabCOpen( if (INVALID_HANDLE_VALUE == reinterpret_cast(pFile)) { - CabcExitOnLastError(hr, "failed to open file: %s", pszFile); + CabcExitOnLastError(hr, "failed to open file: %hs", pszFile); } LExit: @@ -1326,11 +1305,12 @@ static __callback BOOL DIAMONDAPI CabCGetTempFile( HRESULT hr = S_OK; char szTempPath[MAX_PATH] = { }; - DWORD cchTempPath = MAX_PATH; DWORD dwProcessId = ::GetCurrentProcessId(); HANDLE hTempFile = INVALID_HANDLE_VALUE; - if (MAX_PATH < ::GetTempPathA(cchTempPath, szTempPath)) + // TODO: Allow user to pass in different temp path in case the default is too long, + // and/or see if magic similar to CABC_MAGIC_UNICODE_STRING_MARKER can be used to pass ourselves a path longer than MAX_PATH. + if (MAX_PATH < ::GetTempPathA(countof(szTempPath), szTempPath)) { CabcExitWithLastError(hr, "Failed to get temp path during cabinet creation."); } @@ -1339,7 +1319,7 @@ static __callback BOOL DIAMONDAPI CabCGetTempFile( { LONG dwTempIndex = ::InterlockedIncrement(reinterpret_cast(&dwIndex)); - hr = ::StringCbPrintfA(szFile, cbFile, "%s\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId); + hr = ::StringCbPrintfA(szFile, cbFile, "%hs\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId); CabcExitOnFailure(hr, "failed to format log file path."); hTempFile = ::CreateFileA(szFile, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); @@ -1351,7 +1331,11 @@ static __callback BOOL DIAMONDAPI CabCGetTempFile( } else { - hr = E_FAIL; // this file was taken so be pessimistic and assume we're not going to find one. + hr = HRESULT_FROM_WIN32(::GetLastError()); // this file was taken so be pessimistic and assume we're not going to find one. + if (SUCCEEDED(hr)) + { + hr = E_FAIL; + } } } CabcExitOnFailure(hr, "failed to find temporary file."); @@ -1386,7 +1370,7 @@ static __callback BOOL DIAMONDAPI CabCGetNextCabinet( if (pccab->iCab == 1) { pcd->wzFirstCabinetName[0] = '\0'; - LPCWSTR pwzCabinetName = PathFile(pcd->wzCabinetPath); + LPCWSTR pwzCabinetName = PathFile(pcd->sczCabinetPath); size_t len = wcsnlen(pwzCabinetName, sizeof(pwzCabinetName)); if (len > 4) { diff --git a/src/libs/dutil/WixToolset.DUtil/cabutil.cpp b/src/libs/dutil/WixToolset.DUtil/cabutil.cpp index f3629d57..57463e1a 100644 --- a/src/libs/dutil/WixToolset.DUtil/cabutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/cabutil.cpp @@ -261,15 +261,15 @@ static HRESULT DAPI CabOperation( ) { HRESULT hr = S_OK; - BOOL fResult; + BOOL fResult = FALSE; LPWSTR sczCabinet = NULL; LPWSTR pwz = NULL; - CHAR szCabDirectory[MAX_PATH * 4]; // Make sure these are big enough for UTF-8 strings - CHAR szCabFile[MAX_PATH * 4]; + LPSTR pszCabDirectory = NULL; + CHAR szCabFile[MAX_PATH * 4] = { }; // Make sure this is big enough for UTF-8 strings - CAB_CALLBACK_STRUCT ccs; - PFNFDINOTIFY pfnFdiNotify; + CAB_CALLBACK_STRUCT ccs = { }; + PFNFDINOTIFY pfnFdiNotify = NULL; // // ensure the cabinet.dll is loaded @@ -299,15 +299,13 @@ static HRESULT DAPI CabOperation( // If a full path was not provided, use the relative current directory. if (wzCabinet == pwz) { - hr = ::StringCchCopyA(szCabDirectory, countof(szCabDirectory), ".\\"); + hr = StrAnsiAllocStringAnsi(&pszCabDirectory, ".\\", 0); CabExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory."); } else { - if (!::WideCharToMultiByte(CP_UTF8, 0, sczCabinet, -1, szCabDirectory, countof(szCabDirectory), NULL, NULL)) - { - CabExitWithLastError(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet); - } + hr = StrAnsiAllocString(&pszCabDirectory, sczCabinet, 0, CP_UTF8); + CabExitOnFailure(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet); } // @@ -331,7 +329,7 @@ static HRESULT DAPI CabOperation( v_pfnNetFx11Notify = pfnNotify; pfnFdiNotify = FDINotify; } - fResult = vpfnFDICopy(vhfdi, szCabFile, szCabDirectory, 0, pfnFdiNotify, NULL, static_cast(&ccs)); + fResult = vpfnFDICopy(vhfdi, szCabFile, pszCabDirectory, 0, pfnFdiNotify, NULL, static_cast(&ccs)); if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure { CabExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet); @@ -339,6 +337,7 @@ static HRESULT DAPI CabOperation( LExit: ReleaseStr(sczCabinet); + ReleaseStr(pszCabDirectory); v_pfnNetFx11Notify = NULL; return hr; @@ -493,14 +492,15 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE INT_PTR ipResult = 0; // result to return on success CAB_CALLBACK_STRUCT* pccs = static_cast(pFDINotify->pv); - LPCSTR sz; - WCHAR wz[MAX_PATH]; - FILETIME ft; + LPCSTR sz = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzPath = NULL; + FILETIME ft = { }; switch (iNotification) { case fdintCOPY_FILE: // begin extracting a resource from cabinet - CabExitOnNull(pFDINotify->psz1, hr, E_INVALIDARG, "No cabinet file ID given to convert"); + Assert(pccs && pFDINotify->psz1); CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); if (pccs->fStopExtracting) @@ -510,40 +510,37 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE // convert params to useful variables sz = static_cast(pFDINotify->psz1); - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) - { - CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); - } + CabExitOnNull(sz, hr, E_INVALIDARG, "No cabinet file ID given to convert"); + + hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP); + CabExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz); if (pccs->pfnProgress) { - hr = pccs->pfnProgress(TRUE, wz, pccs->pvContext); + hr = pccs->pfnProgress(TRUE, pwz, pccs->pvContext); if (S_OK != hr) { ExitFunction(); } } - if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, wz)) + if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, pwz)) { // get the created date for the resource in the cabinet FILETIME ftLocal; if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) { - CabExitWithLastError(hr, "failed to get time for resource: %ls", wz); + CabExitWithLastError(hr, "failed to get time for resource: %ls", pwz); } ::LocalFileTimeToFileTime(&ftLocal, &ft); - WCHAR wzPath[MAX_PATH]; - hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir); - CabExitOnFailure(hr, "failed to copy in extract directory: %ls for file: %ls", pccs->pwzExtractDir, wz); - hr = ::StringCchCatW(wzPath, countof(wzPath), wz); - CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); + hr = PathConcat(pccs->pwzExtractDir, pwz, &pwzPath); + CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", pccs->pwzExtractDir, pwz); - hFile = OpenFileWithRetry(wzPath, GENERIC_WRITE, CREATE_ALWAYS); + hFile = OpenFileWithRetry(pwzPath, GENERIC_WRITE, CREATE_ALWAYS); if (INVALID_HANDLE_VALUE == hFile) { - CabExitWithLastError(hr, "failed to create file: %ls", wzPath); + CabExitWithLastError(hr, "failed to create file: %ls", pwzPath); } ::SetFileTime(hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails) @@ -567,17 +564,15 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE break; case fdintCLOSE_FILE_INFO: // resource extraction complete - Assert(pFDINotify->hf && pFDINotify->psz1); + Assert(pFDINotify->hf && pccs && pFDINotify->psz1); CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); // convert params to useful variables sz = static_cast(pFDINotify->psz1); CabExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided"); - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) - { - CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); - } + hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP); + CabExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz); if (NULL != pFDINotify->hf) // just close the file { @@ -586,7 +581,7 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE if (pccs->pfnProgress) { - hr = pccs->pfnProgress(FALSE, wz, pccs->pvContext); + hr = pccs->pfnProgress(FALSE, pwz, pccs->pvContext); } if (S_OK == hr && L'*' == *pccs->pwzExtract) // if everything is okay and we're extracting all files, keep going @@ -613,5 +608,8 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE LExit: ReleaseFileHandle(hFile); + ReleaseStr(pwz); + ReleaseStr(pwzPath); + return (S_OK == hr) ? ipResult : -1; } diff --git a/src/libs/dutil/WixToolset.DUtil/dirutil.cpp b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp index 94eab9e7..2c02225d 100644 --- a/src/libs/dutil/WixToolset.DUtil/dirutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp @@ -59,34 +59,10 @@ LExit: *******************************************************************/ extern "C" HRESULT DAPI DirCreateTempPath( __in_z LPCWSTR wzPrefix, - __out_ecount_z(cchPath) LPWSTR wzPath, - __in DWORD cchPath + __out_opt LPWSTR* psczTempFile ) { - Assert(wzPrefix); - Assert(wzPath); - - HRESULT hr = S_OK; - - WCHAR wzDir[MAX_PATH]; - WCHAR wzFile[MAX_PATH]; - DWORD cch = 0; - - cch = ::GetTempPathW(countof(wzDir), wzDir); - if (!cch || cch >= countof(wzDir)) - { - DirExitWithLastError(hr, "Failed to GetTempPath."); - } - - if (!::GetTempFileNameW(wzDir, wzPrefix, 0, wzFile)) - { - DirExitWithLastError(hr, "Failed to GetTempFileName."); - } - - hr = ::StringCchCopyW(wzPath, cchPath, wzFile); - -LExit: - return hr; + return PathCreateTempFile(NULL, NULL, 0, wzPrefix, 0, psczTempFile, NULL); } @@ -192,18 +168,19 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( Assert(wzPath && *wzPath); HRESULT hr = S_OK; - DWORD er; + DWORD er = ERROR_SUCCESS; - DWORD dwAttrib; + DWORD dwAttrib = 0; HANDLE hFind = INVALID_HANDLE_VALUE; LPWSTR sczDelete = NULL; - WIN32_FIND_DATAW wfd; + WIN32_FIND_DATAW wfd = { }; BOOL fDeleteFiles = (DIR_DELETE_FILES == (dwFlags & DIR_DELETE_FILES)); BOOL fRecurse = (DIR_DELETE_RECURSE == (dwFlags & DIR_DELETE_RECURSE)); BOOL fScheduleDelete = (DIR_DELETE_SCHEDULE == (dwFlags & DIR_DELETE_SCHEDULE)); - WCHAR wzTempDirectory[MAX_PATH] = { }; - WCHAR wzTempPath[MAX_PATH] = { }; + WCHAR wzSafeFileName[MAX_PATH + 1] = { }; + LPWSTR sczTempDirectory = NULL; + LPWSTR sczTempPath = NULL; if (-1 == (dwAttrib = ::GetFileAttributesW(wzPath))) { @@ -231,10 +208,8 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( { if (fScheduleDelete) { - if (!::GetTempPathW(countof(wzTempDirectory), wzTempDirectory)) - { - DirExitWithLastError(hr, "Failed to get temp directory."); - } + hr = PathGetTempPath(&sczTempDirectory, NULL); + DirExitOnFailure(hr, "Failed to get temp directory."); } // Delete everything in this directory. @@ -256,10 +231,11 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( } // For extra safety and to silence OACR. - wfd.cFileName[MAX_PATH - 1] = L'\0'; + hr = ::StringCchCopyNExW(wzSafeFileName, countof(wzSafeFileName), wfd.cFileName, countof(wfd.cFileName), NULL, NULL, STRSAFE_FILL_BEHIND_NULL | STRSAFE_NULL_ON_FAILURE); + DirExitOnFailure(hr, "Failed to ensure file name was null terminated."); - hr = PathConcat(wzPath, wfd.cFileName, &sczDelete); - DirExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath); + hr = PathConcat(wzPath, wzSafeFileName, &sczDelete); + DirExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wzSafeFileName, wzPath); if (fRecurse && wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { @@ -288,16 +264,14 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( { if (fScheduleDelete) { - if (!::GetTempFileNameW(wzTempDirectory, L"DEL", 0, wzTempPath)) - { - DirExitWithLastError(hr, "Failed to get temp file to move to."); - } + hr = PathGetTempFileName(sczTempDirectory, L"DEL", 0, &sczTempPath); + DirExitOnFailure(hr, "Failed to get temp file to move to."); // Try to move the file to the temp directory then schedule for delete, // otherwise just schedule for delete. - if (::MoveFileExW(sczDelete, wzTempPath, MOVEFILE_REPLACE_EXISTING)) + if (::MoveFileExW(sczDelete, sczTempPath, MOVEFILE_REPLACE_EXISTING)) { - ::MoveFileExW(wzTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); + ::MoveFileExW(sczTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); } else { @@ -348,6 +322,8 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx( LExit: ReleaseFileFindHandle(hFind); ReleaseStr(sczDelete); + ReleaseStr(sczTempDirectory); + ReleaseStr(sczTempPath); return hr; } diff --git a/src/libs/dutil/WixToolset.DUtil/fileutil.cpp b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp index 9f68ee52..ac407916 100644 --- a/src/libs/dutil/WixToolset.DUtil/fileutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp @@ -1284,21 +1284,18 @@ extern "C" HRESULT DAPI FileCreateTempW( Assert(wzPrefix && *wzPrefix); HRESULT hr = E_FAIL; - WCHAR wzTempPath[MAX_PATH]; - DWORD cchTempPath = countof(wzTempPath); + LPWSTR pwzTempPath = NULL; LPWSTR pwzTempFile = NULL; HANDLE hTempFile = INVALID_HANDLE_VALUE; int i = 0; - if (!::GetTempPathW(cchTempPath, wzTempPath)) - { - FileExitOnLastError(hr, "failed to get temp path"); - } + hr = PathGetTempPath(&pwzTempPath, NULL); + FileExitOnFailure(hr, "failed to get temp path"); for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) { - hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", wzTempPath, wzPrefix, i, wzExtension); + hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", pwzTempPath, wzPrefix, i, wzExtension); FileExitOnFailure(hr, "failed to allocate memory for temp filename"); hTempFile = ::CreateFileW(pwzTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); @@ -1330,6 +1327,7 @@ extern "C" HRESULT DAPI FileCreateTempW( LExit: ReleaseFile(hTempFile); ReleaseStr(pwzTempFile); + ReleaseStr(pwzTempPath); return hr; } diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h index 42268a16..b8fc0431 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h @@ -20,8 +20,7 @@ BOOL DAPI DirExists( HRESULT DAPI DirCreateTempPath( __in_z LPCWSTR wzPrefix, - __out_ecount_z(cchPath) LPWSTR wzPath, - __in DWORD cchPath + __out_opt LPWSTR* psczTempFile ); HRESULT DAPI DirEnsureExists( diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h index 971ef887..de46b95d 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h @@ -159,18 +159,30 @@ DAPI_(HRESULT) PathRelativeToModule( /******************************************************************* PathCreateTempFile - Note: if wzDirectory is null, ::GetTempPath() will be used instead. + Note: if wzDirectory is null, ::GetTempPath2() will be used instead. if wzFileNameTemplate is null, GetTempFileName() will be used instead. *******************************************************************/ DAPI_(HRESULT) PathCreateTempFile( __in_opt LPCWSTR wzDirectory, __in_opt __format_string LPCWSTR wzFileNameTemplate, __in DWORD dwUniqueCount, + __in_z LPCWSTR wzPrefix, __in DWORD dwFileAttributes, __out_opt LPWSTR* psczTempFile, __out_opt HANDLE* phTempFile ); +/******************************************************************* + PathGetTempFileName - wrapper around ::GetTempFileName. + If the wzPathName is too long, it will use its own algorithm. +*******************************************************************/ +DAPI_(HRESULT) PathGetTempFileName( + __in LPCWSTR wzPathName, + __in LPCWSTR wzPrefixString, + __in UINT uUnique, + __out LPWSTR* psczTempFileName + ); + /******************************************************************* PathCreateTimeBasedTempFile - creates an empty temp file based on current system time @@ -187,7 +199,7 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile( /******************************************************************* PathCreateTempDirectory - Note: if wzDirectory is null, ::GetTempPath() will be used instead. + Note: if wzDirectory is null, ::GetTempPath2() will be used instead. *******************************************************************/ DAPI_(HRESULT) PathCreateTempDirectory( __in_opt LPCWSTR wzDirectory, @@ -201,7 +213,24 @@ DAPI_(HRESULT) PathCreateTempDirectory( that is backslash terminated. *******************************************************************/ DAPI_(HRESULT) PathGetTempPath( - __out_z LPWSTR* psczTempPath + __out_z LPWSTR* psczTempPath, + __out_opt SIZE_T* pcch + ); + +/******************************************************************* + PathGetSystemDirectory - returns the path to the system folder + that is backslash terminated. +*******************************************************************/ +DAPI_(HRESULT) PathGetSystemDirectory( + __out_z LPWSTR* psczSystemPath + ); + +/******************************************************************* + PathGetSystemWow64Directory - returns the path to the system WoW 64 folder + that is backslash terminated. +*******************************************************************/ +DAPI_(HRESULT) PathGetSystemWow64Directory( + __out_z LPWSTR* psczSystemPath ); /******************************************************************* @@ -223,12 +252,11 @@ DAPI_(HRESULT) PathGetSystemTempPaths( ); /******************************************************************* - PathGetKnownFolder - returns the path to a well-known shell folder - + PathGetVolumePathName - wrapper for ::GetVolumePathNameW. *******************************************************************/ -DAPI_(HRESULT) PathGetKnownFolder( - __in int csidl, - __out LPWSTR* psczKnownFolder +DAPI_(HRESULT) PathGetVolumePathName( + __in_z LPCWSTR wzFileName, + __out_z LPWSTR* psczVolumePathName ); /******************************************************************* @@ -340,6 +368,20 @@ DAPI_(HRESULT) PathGetHierarchyArray( __inout LPUINT pcPathArray ); +/******************************************************************** + Path2FunctionAllowFallback - allow functions only available in newer versions of Windows. + Typically used for unit testing. + +*********************************************************************/ +void DAPI Path2FunctionAllowFallback(); + +/******************************************************************** + Path2FunctionForceFallback - ignore functions only available in newer versions of Windows. + Typically used for unit testing. + +*********************************************************************/ +void DAPI Path2FunctionForceFallback(); + /******************************************************************* PathCanonicalizePath - wrapper around PathCanonicalizeW. *******************************************************************/ @@ -348,6 +390,15 @@ DAPI_(HRESULT) PathCanonicalizePath( __deref_out_z LPWSTR* psczCanonicalized ); +/******************************************************************* + PathAllocCanonicalizePath - wrapper around PathAllocCanonicalize. +*******************************************************************/ +DAPI_(HRESULT) PathAllocCanonicalizePath( + __in_z LPCWSTR wzPath, + __in DWORD dwFlags, + __deref_out_z LPWSTR* psczCanonicalized + ); + /******************************************************************* PathCanonicalizeForComparison - canonicalizes the path based on the given flags. . and .. directories are collapsed. diff --git a/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h b/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h index 0b9f539d..2ee7ce87 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h @@ -33,10 +33,25 @@ HRESULT DAPI ShelExecUnelevated( __in_z_opt LPCWSTR wzWorkingDirectory, __in int nShowCmd ); + +/******************************************************************** + ShelGetFolder() - translates the CSIDL into KNOWNFOLDERID and calls ShelGetKnownFolder. + If that returns E_NOTIMPL then falls back to ::SHGetFolderPathW. + The CSIDL_FLAG values are not supported, CSIDL_FLAG_CREATE is always used. + The path is backslash terminated. + +*******************************************************************/ HRESULT DAPI ShelGetFolder( __out_z LPWSTR* psczFolderPath, __in int csidlFolder ); + +/******************************************************************** + ShelGetKnownFolder() - gets a folder by KNOWNFOLDERID with ::SHGetKnownFolderPath. + The path is backslash terminated. + + Note: return E_NOTIMPL if called on pre-Vista operating systems. +*******************************************************************/ HRESULT DAPI ShelGetKnownFolder( __out_z LPWSTR* psczFolderPath, __in REFKNOWNFOLDERID rfidFolder diff --git a/src/libs/dutil/WixToolset.DUtil/logutil.cpp b/src/libs/dutil/WixToolset.DUtil/logutil.cpp index 88a90d8c..3a130b4e 100644 --- a/src/libs/dutil/WixToolset.DUtil/logutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/logutil.cpp @@ -684,9 +684,10 @@ LExit: extern "C" HRESULT DAPI LogHeader() { HRESULT hr = S_OK; - WCHAR wzComputerName[MAX_PATH]; + WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1] = { }; DWORD cchComputerName = countof(wzComputerName); - WCHAR wzPath[MAX_PATH]; + LPWSTR sczPath = NULL; + LPCWSTR wzPath = NULL; DWORD dwMajorVersion = 0; DWORD dwMinorVersion = 0; LPCSTR szLevel = LOGUTIL_UNKNOWN; @@ -695,12 +696,19 @@ extern "C" HRESULT DAPI LogHeader() // // get the interesting data // - if (!::GetModuleFileNameW(NULL, wzPath, countof(wzPath))) + + hr = PathForCurrentProcess(&sczPath, NULL); + if (FAILED(hr)) + { + wzPath = L""; + } + else { - memset(wzPath, 0, sizeof(wzPath)); + wzPath = sczPath; + + hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion); } - hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion); if (FAILED(hr)) { dwMajorVersion = 0; @@ -743,6 +751,7 @@ extern "C" HRESULT DAPI LogHeader() hr = S_OK; ReleaseStr(sczCurrentDateTime); + ReleaseStr(sczPath); return hr; } diff --git a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp index 3c4b2f88..862a743d 100644 --- a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp +++ b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp @@ -19,6 +19,65 @@ #define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__) +typedef HRESULT(WINAPI* PFN_PATH_ALLOC_CANONICALIZE)( + __in LPCWSTR wzSource, + __in DWORD dwFlags, + __out_z LPWSTR* psczPathOut + ); + +static BOOL vfInitialized = FALSE; +static HMODULE vhPathApiSet_1_1_0 = NULL; +static PFN_PATH_ALLOC_CANONICALIZE vpfnPathAllocCanonicalize = NULL; +static BOOL vfForceFallback = FALSE; + +// from PathCch.h +#ifndef PATHCCH_ALLOW_LONG_PATHS +#define PATHCCH_ALLOW_LONG_PATHS 0x01 +#endif + +static HRESULT Initialize() +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (vfInitialized) + { + ExitFunction(); + } + + hr = LoadSystemApiSet(L"api-ms-win-core-path-l1-1-0.dll", &vhPathApiSet_1_1_0); + if (E_MODNOTFOUND == hr) + { + hr = E_NOTIMPL; + } + PathExitOnFailure(hr, "Failed to load api-ms-win-core-path-l1-1-0.dll"); + + vpfnPathAllocCanonicalize = reinterpret_cast(::GetProcAddress(vhPathApiSet_1_1_0, "PathAllocCanonicalize")); + if (!vpfnPathAllocCanonicalize) + { + er = ::GetLastError(); + PathExitWithRootFailure(hr, ERROR_PROC_NOT_FOUND == er ? E_NOTIMPL : HRESULT_FROM_WIN32(er), "Failed to get address of PathAllocCanonicalize."); + } + + vfInitialized = TRUE; + +LExit: + return hr; +} + + +DAPI_(void) Path2FunctionAllowFallback() +{ + vfForceFallback = FALSE; +} + + +DAPI_(void) Path2FunctionForceFallback() +{ + vfForceFallback = TRUE; +} + + DAPI_(HRESULT) PathCanonicalizePath( __in_z LPCWSTR wzPath, __deref_out_z LPWSTR* psczCanonicalized @@ -43,6 +102,37 @@ LExit: return hr; } +DAPI_(HRESULT) PathAllocCanonicalizePath( + __in_z LPCWSTR wzPath, + __in DWORD dwFlags, + __deref_out_z LPWSTR* psczCanonicalized + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCanonicalizedPath = NULL; + + hr = Initialize(); + if (E_NOTIMPL == hr || SUCCEEDED(hr) && !vpfnPathAllocCanonicalize) + { + ExitFunction1(hr = E_NOTIMPL); + } + PathExitOnFailure(hr, "Failed to initialize path2utl."); + + hr = vpfnPathAllocCanonicalize(wzPath, dwFlags, &sczCanonicalizedPath); + PathExitOnFailure(hr, "Failed to canonicalize: %ls", wzPath); + + hr = StrAllocString(psczCanonicalized, sczCanonicalizedPath, 0); + PathExitOnFailure(hr, "Failed to copy the canonicalized path."); + +LExit: + if (sczCanonicalizedPath) + { + ::LocalFree(sczCanonicalizedPath); + } + + return hr; +} + DAPI_(HRESULT) PathCanonicalizeForComparison( __in_z LPCWSTR wzPath, __in DWORD dwCanonicalizeFlags, @@ -75,7 +165,19 @@ DAPI_(HRESULT) PathCanonicalizeForComparison( if (*wzNormalizedPath) { - hr = PathCanonicalizePath(wzNormalizedPath, psczCanonicalized); + if (!vfForceFallback) + { + hr = PathAllocCanonicalizePath(wzNormalizedPath, PATHCCH_ALLOW_LONG_PATHS, psczCanonicalized); + } + else + { + hr = E_NOTIMPL; + } + + if (E_NOTIMPL == hr) + { + hr = PathCanonicalizePath(wzNormalizedPath, psczCanonicalized); + } PathExitOnFailure(hr, "Failed to canonicalize: %ls", wzNormalizedPath); } else @@ -273,33 +375,52 @@ DAPI_(HRESULT) PathSystemWindowsSubdirectory( ) { HRESULT hr = S_OK; - WCHAR wzTempPath[MAX_PATH + 1] = { }; + LPWSTR sczWindowsPath = NULL; + DWORD cchBuffer = MAX_PATH + 1; DWORD cch = 0; - cch = ::GetSystemWindowsDirectoryW(wzTempPath, countof(wzTempPath)); - if (!cch) - { - PathExitWithLastError(hr, "Failed to get Windows directory path."); - } - else if (cch >= countof(wzTempPath)) + hr = StrAlloc(&sczWindowsPath, cchBuffer); + PathExitOnFailure(hr, "Failed to alloc Windows directory path."); + + cch = ::GetSystemWindowsDirectoryW(sczWindowsPath, cchBuffer); + PathExitOnNullWithLastError(cch, hr, "Failed to get Windows directory path with default size."); + + cch += 1; // add 1 for null terminator. + + if (cch > cchBuffer) { - PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "Windows directory path too long."); + hr = StrAlloc(&sczWindowsPath, cch); + PathExitOnFailure(hr, "Failed to realloc Windows directory path."); + + cchBuffer = cch; + + cch = ::GetSystemWindowsDirectoryW(sczWindowsPath, cchBuffer); + PathExitOnNullWithLastError(cch, hr, "Failed to get Windows directory path with returned size."); + + cch += 1; // add 1 for null terminator. + + if (cch > cchBuffer) + { + PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "Failed to get Windows directory path with returned size."); + } } if (wzSubdirectory) { - hr = PathConcatRelativeToBase(wzTempPath, wzSubdirectory, psczFullPath); + hr = PathConcatRelativeToBase(sczWindowsPath, wzSubdirectory, psczFullPath); PathExitOnFailure(hr, "Failed to concat subdirectory on Windows directory path."); } else { - hr = StrAllocString(psczFullPath, wzTempPath, 0); - PathExitOnFailure(hr, "Failed to copy Windows directory path."); + *psczFullPath = sczWindowsPath; + sczWindowsPath = NULL; } hr = PathBackslashTerminate(psczFullPath); PathExitOnFailure(hr, "Failed to terminate Windows directory path with backslash."); LExit: + ReleaseStr(sczWindowsPath); + return hr; } diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp index 0e2a5dec..dd5385fc 100644 --- a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp @@ -20,6 +20,11 @@ #define PATH_GOOD_ENOUGH 64 +typedef DWORD(APIENTRY* PFN_GETTEMPPATH2W)( + __in DWORD BufferLength, + __out LPWSTR Buffer + ); + static BOOL IsPathSeparatorChar( __in WCHAR wc ); @@ -527,28 +532,55 @@ DAPI_(HRESULT) PathForCurrentProcess( ) { HRESULT hr = S_OK; - DWORD cch = MAX_PATH; + WCHAR smallBuffer[1] = { }; + SIZE_T cchMax = 0; + DWORD cchBuffer = 0; + DWORD cch = 0; + DWORD dwMaxAttempts = 20; - do + // GetModuleFileNameW didn't originally set the last error when the buffer was too small. + ::SetLastError(ERROR_SUCCESS); + + cch = ::GetModuleFileNameW(hModule, smallBuffer, countof(smallBuffer)); + PathExitOnNullWithLastError(cch, hr, "Failed to get size of path for executing process."); + + if (*psczFullPath && ERROR_INSUFFICIENT_BUFFER == ::GetLastError()) { - hr = StrAlloc(psczFullPath, cch); - PathExitOnFailure(hr, "Failed to allocate string for module path."); + hr = StrMaxLength(*psczFullPath, &cchMax); + PathExitOnFailure(hr, "Failed to get max length of input buffer."); + + cchBuffer = (DWORD)min(DWORD_MAX, cchMax); + } + else + { + cchBuffer = MAX_PATH + 1; + + hr = StrAlloc(psczFullPath, cchBuffer); + PathExitOnFailure(hr, "Failed to allocate space for module path."); + } + + ::SetLastError(ERROR_SUCCESS); - DWORD cchRequired = ::GetModuleFileNameW(hModule, *psczFullPath, cch); - if (0 == cchRequired) + for (DWORD i = 0; i < dwMaxAttempts; ++i) + { + cch = ::GetModuleFileNameW(hModule, *psczFullPath, cchBuffer); + PathExitOnNullWithLastError(cch, hr, "Failed to get path for executing process."); + + if (ERROR_INSUFFICIENT_BUFFER != ::GetLastError()) { - PathExitWithLastError(hr, "Failed to get path for executing process."); + break; } - else if (cchRequired == cch) + + if ((dwMaxAttempts - 1) == i) { - cch = cchRequired + 1; - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + PathExitWithRootFailure(hr, E_FAIL, "Unexpected failure getting path for executing process."); } - else - { - hr = S_OK; - } - } while (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr); + + cchBuffer *= 2; + + hr = StrAlloc(psczFullPath, cchBuffer); + PathExitOnFailure(hr, "Failed to re-allocate more space for module path."); + } LExit: return hr; @@ -582,17 +614,18 @@ DAPI_(HRESULT) PathCreateTempFile( __in_opt LPCWSTR wzDirectory, __in_opt __format_string LPCWSTR wzFileNameTemplate, __in DWORD dwUniqueCount, + __in_z LPCWSTR wzPrefix, __in DWORD dwFileAttributes, __out_opt LPWSTR* psczTempFile, __out_opt HANDLE* phTempFile ) { - AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); + Assert(wzPrefix); + AssertSz(!wzFileNameTemplate || !*wzFileNameTemplate || 0 < dwUniqueCount, "Must specify a non-zero unique count."); HRESULT hr = S_OK; LPWSTR sczTempPath = NULL; - DWORD cchTempPath = MAX_PATH; HANDLE hTempFile = INVALID_HANDLE_VALUE; LPWSTR scz = NULL; @@ -605,13 +638,8 @@ DAPI_(HRESULT) PathCreateTempFile( } else { - hr = StrAlloc(&sczTempPath, cchTempPath); - PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); - - if (!::GetTempPathW(cchTempPath, sczTempPath)) - { - PathExitWithLastError(hr, "Failed to get temp path."); - } + hr = PathGetTempPath(&sczTempPath, NULL); + PathExitOnFailure(hr, "Failed to get temp path."); } if (wzFileNameTemplate && *wzFileNameTemplate) @@ -621,7 +649,7 @@ DAPI_(HRESULT) PathCreateTempFile( hr = StrAllocFormatted(&scz, wzFileNameTemplate, i); PathExitOnFailure(hr, "Failed to allocate memory for file template."); - hr = StrAllocFormatted(&sczTempFile, L"%s%s", sczTempPath, scz); + hr = StrAllocFormatted(&sczTempFile, L"%ls%ls", sczTempPath, scz); PathExitOnFailure(hr, "Failed to allocate temp file name."); hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, dwFileAttributes, NULL); @@ -642,13 +670,8 @@ DAPI_(HRESULT) PathCreateTempFile( // the system to provide us a temp file using its built-in mechanism. if (INVALID_HANDLE_VALUE == hTempFile) { - hr = StrAlloc(&sczTempFile, MAX_PATH); - PathExitOnFailure(hr, "Failed to allocate memory for the temp path"); - - if (!::GetTempFileNameW(sczTempPath, L"TMP", 0, sczTempFile)) - { - PathExitWithLastError(hr, "Failed to create new temp file name."); - } + hr = PathGetTempFileName(sczTempPath, wzPrefix, 0, &sczTempFile); + PathExitOnFailure(hr, "Failed to create new temp file name."); hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, dwFileAttributes, NULL); if (INVALID_HANDLE_VALUE == hTempFile) @@ -684,6 +707,82 @@ LExit: } +DAPI_(HRESULT) PathGetTempFileName( + __in LPCWSTR wzPathName, + __in LPCWSTR wzPrefixString, + __in UINT uUnique, + __out LPWSTR* psczTempFileName + ) +{ + HRESULT hr = S_OK; + size_t cchFullPath = 0; + WORD wValue = (WORD)(0xffff & uUnique); + LPWSTR scz = NULL; + LPWSTR sczTempFile = NULL; + HANDLE hTempFile = INVALID_HANDLE_VALUE; + + hr = ::StringCchLengthW(wzPathName, STRSAFE_MAX_CCH, &cchFullPath); + PathExitOnFailure(hr, "Failed to get length of path to prefix."); + + if (MAX_PATH - 14 >= cchFullPath) + { + hr = StrAlloc(psczTempFileName, MAX_PATH); + PathExitOnFailure(hr, "Failed to allocate buffer for GetTempFileNameW."); + + if (!::GetTempFileNameW(wzPathName, wzPrefixString, uUnique, *psczTempFileName)) + { + PathExitWithLastError(hr, "Failed to create new temp file name."); + } + + ExitFunction(); + } + + // TODO: when uUnique is 0, consider not always starting at 0 to avoid collisions if this is called repeatedly. + // Purposely let it wrap around. + for (WORD w = 0; w < WORD_MAX && INVALID_HANDLE_VALUE == hTempFile; ++wValue) + { + hr = StrAllocFormatted(&scz, L"%ls%x.TMP", wzPrefixString, w); + PathExitOnFailure(hr, "Failed to allocate memory for file template."); + + hr = PathConcat(wzPathName, scz, &sczTempFile); + PathExitOnFailure(hr, "Failed to allocate temp file name."); + + hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, 0, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + // if the file already exists, try next one. + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) + { + hr = S_OK; + } + PathExitOnFailure(hr, "Failed to create file: %ls", sczTempFile); + } + + ++w; + } + + if (INVALID_HANDLE_VALUE == hTempFile) + { + PathExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_FILE_EXISTS), "Failed to create temp file."); + } + + hr = StrAllocString(psczTempFileName, sczTempFile, 0); + PathExitOnFailure(hr, "Failed to copy temp file string."); + +LExit: + if (INVALID_HANDLE_VALUE != hTempFile) + { + ::CloseHandle(hTempFile); + } + + ReleaseStr(scz); + ReleaseStr(sczTempFile); + + return hr; +} + + DAPI_(HRESULT) PathCreateTimeBasedTempFile( __in_z_opt LPCWSTR wzDirectory, __in_z LPCWSTR wzPrefix, @@ -695,7 +794,7 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile( { HRESULT hr = S_OK; BOOL fRetry = FALSE; - WCHAR wzTempPath[MAX_PATH] = { }; + LPWSTR sczTempParentPath = NULL; LPWSTR sczPrefix = NULL; LPWSTR sczPrefixFolder = NULL; SYSTEMTIME time = { }; @@ -711,12 +810,10 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile( } else { - if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) - { - PathExitWithLastError(hr, "Failed to get temp folder."); - } + hr = PathGetTempPath(&sczTempParentPath, NULL); + PathExitOnFailure(hr, "Failed to get temp folder."); - hr = PathConcat(wzTempPath, wzPrefix, &sczPrefix); + hr = PathConcat(sczTempParentPath, wzPrefix, &sczPrefix); PathExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix."); } @@ -778,6 +875,7 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile( LExit: ReleaseFile(hTempFile); + ReleaseStr(sczTempParentPath); ReleaseStr(sczTempPath); ReleaseStr(sczPrefixFolder); ReleaseStr(sczPrefix); @@ -799,7 +897,6 @@ DAPI_(HRESULT) PathCreateTempDirectory( HRESULT hr = S_OK; LPWSTR sczTempPath = NULL; - DWORD cchTempPath = MAX_PATH; LPWSTR scz = NULL; @@ -813,13 +910,8 @@ DAPI_(HRESULT) PathCreateTempDirectory( } else { - hr = StrAlloc(&sczTempPath, cchTempPath); - PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); - - if (!::GetTempPathW(cchTempPath, sczTempPath)) - { - PathExitWithLastError(hr, "Failed to get temp path."); - } + hr = PathGetTempPath(&sczTempPath, NULL); + PathExitOnFailure(hr, "Failed to get temp path."); } for (DWORD i = 1; i <= dwUniqueCount; ++i) @@ -869,46 +961,230 @@ LExit: DAPI_(HRESULT) PathGetTempPath( - __out_z LPWSTR* psczTempPath + __out_z LPWSTR* psczTempPath, + __out_opt SIZE_T* pcch + ) +{ + + HRESULT hr = S_OK; + SIZE_T cchMax = 0; + DWORD cchBuffer = 0; + DWORD cch = 0; + DWORD dwAttempts = 0; + HMODULE hModule = NULL; + PFN_GETTEMPPATH2W pfnGetTempPath = NULL; + const DWORD dwMaxAttempts = 10; + + if (*psczTempPath) + { + hr = StrMaxLength(*psczTempPath, &cchMax); + PathExitOnFailure(hr, "Failed to get max length of input buffer."); + + cchBuffer = (DWORD)min(DWORD_MAX, cchMax); + } + else + { + cchBuffer = MAX_PATH + 1; + + hr = StrAlloc(psczTempPath, cchBuffer); + PathExitOnFailure(hr, "Failed to allocate space for temp path."); + } + + hr = LoadSystemLibrary(L"kernel32.dll", &hModule); + PathExitOnFailure(hr, "Failed to load kernel32.dll"); + + pfnGetTempPath = reinterpret_cast(::GetProcAddress(hModule, "GetTempPath2W")); + if (!pfnGetTempPath) + { + pfnGetTempPath = ::GetTempPathW; + } + + for (; dwAttempts < dwMaxAttempts; ++dwAttempts) + { + cch = pfnGetTempPath(cchBuffer, *psczTempPath); + PathExitOnNullWithLastError(cch, hr, "Failed to get temp path."); + + cch += 1; // add one for null terminator. + + if (cch <= cchBuffer) + { + break; + } + + hr = StrAlloc(psczTempPath, cch); + PathExitOnFailure(hr, "Failed to reallocate space for temp path."); + + cchBuffer = cch; + } + + if (dwMaxAttempts == dwAttempts) + { + PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "GetTempPathW results never converged."); + } + + if (pcch) + { + *pcch = cch - 1; // remove one for null terminator. + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathGetSystemDirectory( + __out_z LPWSTR* psczSystemPath + ) +{ + HRESULT hr = S_OK; + SIZE_T cchMax = 0; + DWORD cchBuffer = 0; + DWORD cch = 0; + + if (*psczSystemPath) + { + hr = StrMaxLength(*psczSystemPath, &cchMax); + PathExitOnFailure(hr, "Failed to get max length of input buffer."); + + cchBuffer = (DWORD)min(DWORD_MAX, cchMax); + } + else + { + cchBuffer = MAX_PATH + 1; + + hr = StrAlloc(psczSystemPath, cchBuffer); + PathExitOnFailure(hr, "Failed to allocate space for system directory."); + } + + cch = ::GetSystemDirectoryW(*psczSystemPath, cchBuffer); + PathExitOnNullWithLastError(cch, hr, "Failed to get system directory path with default size."); + + if (cch > cchBuffer) + { + hr = StrAlloc(psczSystemPath, cch); + PathExitOnFailure(hr, "Failed to realloc system directory path."); + + cchBuffer = cch; + + cch = ::GetSystemDirectoryW(*psczSystemPath, cchBuffer); + PathExitOnNullWithLastError(cch, hr, "Failed to get system directory path with returned size."); + + if (cch > cchBuffer) + { + PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "Failed to get system directory path with returned size."); + } + } + + hr = PathBackslashTerminate(psczSystemPath); + PathExitOnFailure(hr, "Failed to terminate system directory path with backslash."); + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathGetSystemWow64Directory( + __out_z LPWSTR* psczSystemPath ) { HRESULT hr = S_OK; - WCHAR wzTempPath[MAX_PATH + 1] = { }; + SIZE_T cchMax = 0; + DWORD cchBuffer = 0; DWORD cch = 0; - cch = ::GetTempPathW(countof(wzTempPath), wzTempPath); - if (!cch) + if (*psczSystemPath) { - PathExitWithLastError(hr, "Failed to GetTempPath."); + hr = StrMaxLength(*psczSystemPath, &cchMax); + PathExitOnFailure(hr, "Failed to get max length of input buffer."); + + cchBuffer = (DWORD)min(DWORD_MAX, cchMax); } - else if (cch >= countof(wzTempPath)) + else { - PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "TEMP directory path too long."); + cchBuffer = MAX_PATH + 1; + + hr = StrAlloc(psczSystemPath, cchBuffer); + PathExitOnFailure(hr, "Failed to allocate space for system wow64 directory."); } - hr = StrAllocString(psczTempPath, wzTempPath, cch); - PathExitOnFailure(hr, "Failed to copy TEMP directory path."); + cch = ::GetSystemWow64DirectoryW(*psczSystemPath, cchBuffer); + PathExitOnNullWithLastError(cch, hr, "Failed to get system wow64 directory path with default size."); + + cch += 1; // add one for the null terminator. + + if (cch > cchBuffer) + { + hr = StrAlloc(psczSystemPath, cch); + PathExitOnFailure(hr, "Failed to realloc system wow64 directory path."); + + cchBuffer = cch; + + cch = ::GetSystemWow64DirectoryW(*psczSystemPath, cchBuffer); + PathExitOnNullWithLastError(cch, hr, "Failed to get system wow64 directory path with returned size."); + + cch += 1; // add one for the null terminator. + + if (cch > cchBuffer) + { + PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "Failed to get system wow64 directory path with returned size."); + } + } + + hr = PathBackslashTerminate(psczSystemPath); + PathExitOnFailure(hr, "Failed to terminate system wow64 directory path with backslash."); LExit: return hr; } -DAPI_(HRESULT) PathGetKnownFolder( - __in int csidl, - __out LPWSTR* psczKnownFolder +DAPI_(HRESULT) PathGetVolumePathName( + __in_z LPCWSTR wzFileName, + __out_z LPWSTR* psczVolumePathName ) { HRESULT hr = S_OK; + DWORD cchBuffer = 0; + SIZE_T cchMax = 0; + const DWORD dwMaxAttempts = 20; - hr = StrAlloc(psczKnownFolder, MAX_PATH); - PathExitOnFailure(hr, "Failed to allocate memory for known folder."); + if (*psczVolumePathName) + { + hr = StrMaxLength(*psczVolumePathName, &cchMax); + PathExitOnFailure(hr, "Failed to get max length of input buffer."); - hr = ::SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, *psczKnownFolder); - PathExitOnFailure(hr, "Failed to get known folder path."); + cchBuffer = (DWORD)min(DWORD_MAX, cchMax); + } + else + { + cchBuffer = MAX_PATH + 1; + + hr = StrAlloc(psczVolumePathName, cchBuffer); + PathExitOnFailure(hr, "Failed to allocate space for volume path name."); + } + + for (DWORD i = 0; i < dwMaxAttempts; ++i) + { + if (::GetVolumePathNameW(wzFileName, *psczVolumePathName, cchBuffer)) + { + break; + } + + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) != hr && E_INSUFFICIENT_BUFFER != hr || + (dwMaxAttempts - 1) == i) + { + PathExitWithRootFailure(hr, hr, "Failed to get volume path name of: %ls", wzFileName); + } + + cchBuffer *= 2; + + hr = StrAlloc(psczVolumePathName, cchBuffer); + PathExitOnFailure(hr, "Failed to re-allocate more space for volume path name."); + } - hr = PathBackslashTerminate(psczKnownFolder); - PathExitOnFailure(hr, "Failed to ensure known folder path is backslash terminated."); + hr = PathBackslashTerminate(psczVolumePathName); + PathExitOnFailure(hr, "Failed to terminate volume path name with backslash."); LExit: return hr; diff --git a/src/libs/dutil/WixToolset.DUtil/rexutil.cpp b/src/libs/dutil/WixToolset.DUtil/rexutil.cpp index 155ca714..ce28beb3 100644 --- a/src/libs/dutil/WixToolset.DUtil/rexutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/rexutil.cpp @@ -28,7 +28,7 @@ static ERF verf; static FAKE_FILE vrgffFileTable[FILETABLESIZE]; static DWORD vcbRes; static LPCBYTE vpbRes; -static CHAR vszResource[MAX_PATH]; +static LPSTR vpszResource = NULL; static REX_CALLBACK_WRITE vpfnWrite = NULL; static HRESULT vhrLastError = S_OK; @@ -85,6 +85,8 @@ LExit: { ::FDIDestroy(vhfdi); vhfdi = NULL; + + ReleaseNullStr(vpszResource); } return hr; @@ -101,6 +103,8 @@ extern "C" void RexUninitialize() { ::FDIDestroy(vhfdi); vhfdi = NULL; + + ReleaseNullStr(vpszResource); } } @@ -124,12 +128,12 @@ extern "C" HRESULT RexExtract( { Assert(vhfdi); HRESULT hr = S_OK; - BOOL fResult; + BOOL fResult = FALSE; HRSRC hResInfo = NULL; HANDLE hRes = NULL; - REX_CALLBACK_STRUCT rcs; + REX_CALLBACK_STRUCT rcs = { }; // remember the write callback vpfnWrite = pfnWrite; @@ -158,7 +162,7 @@ extern "C" HRESULT RexExtract( // RexExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource); //} - hr = ::StringCchCopyA(vszResource, countof(vszResource), szResource); + hr = StrAnsiAllocStringAnsi(&vpszResource, szResource, 0); RexExitOnFailure(hr, "Failed to copy resource name to global."); // @@ -171,7 +175,7 @@ extern "C" HRESULT RexExtract( rcs.pfnProgress = pfnProgress; rcs.pvContext = pvContext; - fResult = ::FDICopy(vhfdi, vszResource, "", 0, RexCallback, NULL, static_cast(&rcs)); + fResult = ::FDICopy(vhfdi, vpszResource, "", 0, RexCallback, NULL, static_cast(&rcs)); if (!fResult && !rcs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure { hr = vhrLastError; // TODO: put verf info in trace message here @@ -227,7 +231,7 @@ static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int o RexExitOnFailure(hr, "File table exceeded"); } - if (0 == lstrcmpA(vszResource, pszFile)) + if (0 == lstrcmpA(vpszResource, pszFile)) { vrgffFileTable[i].fUsed = TRUE; vrgffFileTable[i].fftType = MEMORY_FILE; @@ -436,15 +440,16 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati HANDLE hFile = INVALID_HANDLE_VALUE; REX_CALLBACK_STRUCT* prcs = static_cast(pFDINotify->pv); - LPCSTR sz; - WCHAR wz[MAX_PATH]; - FILETIME ft; + LPCSTR sz = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzPath = NULL; + FILETIME ft = { }; int i = 0; switch (iNotification) { case fdintCOPY_FILE: // beGIN extracting a resource from cabinet - Assert(pFDINotify->psz1); + Assert(pFDINotify->psz1 && prcs); if (prcs->fStopExtracting) { @@ -453,55 +458,50 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati // convert params to useful variables sz = static_cast(pFDINotify->psz1); - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) - { - RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); - } + RexExitOnNull(sz, hr, E_INVALIDARG, "No cabinet file ID given to convert"); + + hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP); + RexExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz); if (prcs->pfnProgress) { - hr = prcs->pfnProgress(TRUE, wz, prcs->pvContext); + hr = prcs->pfnProgress(TRUE, pwz, prcs->pvContext); if (S_OK != hr) { ExitFunction(); } } - if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, wz)) + if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, pwz)) { // get the created date for the resource in the cabinet if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft)) { - RexExitWithLastError(hr, "failed to get time for resource: %ls", wz); + RexExitWithLastError(hr, "failed to get time for resource: %ls", pwz); } - WCHAR wzPath[MAX_PATH]; - - hr = ::StringCchCopyW(wzPath, countof(wzPath), prcs->pwzExtractDir); - RexExitOnFailure(hr, "failed to copy extract directory: %ls for file: %ls", prcs->pwzExtractDir, wz); - if (L'*' == *prcs->pwzExtract) { - hr = ::StringCchCatW(wzPath, countof(wzPath), wz); - RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); + hr = PathConcat(prcs->pwzExtractDir, pwz, &pwzPath); + RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", prcs->pwzExtractDir, pwz); } else { Assert(*prcs->pwzExtractName); - hr = ::StringCchCatW(wzPath, countof(wzPath), prcs->pwzExtractName); - RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName); + hr = PathConcat(prcs->pwzExtractDir, prcs->pwzExtractName, &pwzPath); + RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", prcs->pwzExtractDir, prcs->pwzExtractName); } // Quickly chop off the file name part of the path to ensure the path exists // then put the file name back on the path (by putting the first character // back over the null terminator). - LPWSTR wzFile = PathFile(wzPath); + LPWSTR wzFile = PathFile(pwzPath); WCHAR wzFileFirstChar = *wzFile; *wzFile = L'\0'; - hr = DirEnsureExists(wzPath, NULL); - RexExitOnFailure(hr, "failed to ensure directory: %ls", wzPath); + hr = DirEnsureExists(pwzPath, NULL); + RexExitOnFailure(hr, "failed to ensure directory: %ls", pwzPath); hr = S_OK; @@ -524,10 +524,10 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati } // open the file - hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + hFile = ::CreateFileW(pwzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { - RexExitWithLastError(hr, "failed to open file: %ls", wzPath); + RexExitWithLastError(hr, "failed to open file: %ls", pwzPath); } vrgffFileTable[i].fUsed = TRUE; @@ -554,20 +554,20 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati break; case fdintCLOSE_FILE_INFO: // resource extraction complete - Assert(pFDINotify->hf && pFDINotify->psz1); + Assert(pFDINotify->hf && prcs && pFDINotify->psz1); // convert params to useful variables sz = static_cast(pFDINotify->psz1); - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) - { - RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); - } + RexExitOnNull(sz, hr, E_INVALIDARG, "No cabinet file ID given to convert"); + + hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP); + RexExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz); RexClose(pFDINotify->hf); if (prcs->pfnProgress) { - hr = prcs->pfnProgress(FALSE, wz, prcs->pvContext); + hr = prcs->pfnProgress(FALSE, pwz, prcs->pvContext); } if (S_OK == hr && L'*' == *prcs->pwzExtract) // if everything is okay and we're extracting all files, keep going @@ -597,5 +597,8 @@ LExit: vhrLastError = hr; } + ReleaseStr(pwz); + ReleaseStr(pwzPath); + return (S_OK == hr) ? ipResult : -1; } diff --git a/src/libs/dutil/WixToolset.DUtil/sceutil.cpp b/src/libs/dutil/WixToolset.DUtil/sceutil.cpp index 4eede74f..590c937a 100644 --- a/src/libs/dutil/WixToolset.DUtil/sceutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/sceutil.cpp @@ -317,7 +317,7 @@ extern "C" HRESULT DAPI SceOpenDatabase( { HRESULT hr = S_OK; DWORD dwVersionFound = 0; - WCHAR wzTempDbFile[MAX_PATH]; + LPWSTR sczTempDbFile = NULL; LPCWSTR wzPathToOpen = NULL; LPWSTR sczSchemaType = NULL; SCE_DATABASE *pNewSceDatabase = NULL; @@ -343,16 +343,16 @@ extern "C" HRESULT DAPI SceOpenDatabase( // TODO: had trouble getting SQL CE to read a file read-only, so we're copying it to a temp path for now. if (fReadOnly) { - hr = DirCreateTempPath(PathFile(sczFile), (LPWSTR)wzTempDbFile, _countof(wzTempDbFile)); + hr = DirCreateTempPath(PathFile(sczFile), &sczTempDbFile); ExitOnFailure(hr, "Failed to get temp path"); - hr = FileEnsureCopy(sczFile, (LPCWSTR)wzTempDbFile, TRUE); + hr = FileEnsureCopy(sczFile, sczTempDbFile, TRUE); ExitOnFailure(hr, "Failed to copy file to temp path"); - hr = StrAllocString(&pNewSceDatabaseInternal->sczTempDbFile, (LPCWSTR)wzTempDbFile, 0); + hr = StrAllocString(&pNewSceDatabaseInternal->sczTempDbFile, sczTempDbFile, 0); ExitOnFailure(hr, "Failed to copy temp db file path"); - wzPathToOpen = (LPCWSTR)wzTempDbFile; + wzPathToOpen = sczTempDbFile; } else { @@ -424,6 +424,7 @@ extern "C" HRESULT DAPI SceOpenDatabase( LExit: ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal); ReleaseStr(sczSchemaType); + ReleaseStr(sczTempDbFile); ReleaseDatabase(pNewSceDatabase); return hr; diff --git a/src/libs/dutil/WixToolset.DUtil/shelutil.cpp b/src/libs/dutil/WixToolset.DUtil/shelutil.cpp index 2eb9a52a..72a0e5ce 100644 --- a/src/libs/dutil/WixToolset.DUtil/shelutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/shelutil.cpp @@ -9,6 +9,7 @@ #define ShelExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) #define ShelExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) #define ShelExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, e, s, __VA_ARGS__) #define ShelExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) #define ShelExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) #define ShelExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) @@ -19,6 +20,10 @@ static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW; +static HRESULT DAPI GetFolderFromCsidl( + __out_z LPWSTR* psczFolderPath, + __in int csidlFolder + ); static HRESULT GetDesktopShellView( __in REFIID riid, __out void **ppv @@ -57,7 +62,14 @@ extern "C" HRESULT DAPI ShelExec( ) { HRESULT hr = S_OK; - SHELLEXECUTEINFOW shExecInfo = {}; + SHELLEXECUTEINFOW shExecInfo = { }; + size_t cchWorkingDirectory = 0; + + // CreateProcessW has undocumented MAX_PATH restriction for lpCurrentDirectory even when long path support is enabled. + if (wzWorkingDirectory && FAILED(::StringCchLengthW(wzWorkingDirectory, MAX_PATH - 1, &cchWorkingDirectory))) + { + wzWorkingDirectory = NULL; + } shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; @@ -159,11 +171,7 @@ LExit: } -/******************************************************************** - ShelGetFolder() - gets a folder by CSIDL. - -*******************************************************************/ -extern "C" HRESULT DAPI ShelGetFolder( +static HRESULT DAPI GetFolderFromCsidl( __out_z LPWSTR* psczFolderPath, __in int csidlFolder ) @@ -185,19 +193,6 @@ LExit: } -/******************************************************************** - ShelGetKnownFolder() - gets a folder by KNOWNFOLDERID. - - Note: return E_NOTIMPL if called on pre-Vista operating systems. -*******************************************************************/ -#ifndef REFKNOWNFOLDERID -#define REFKNOWNFOLDERID REFGUID -#endif - -#ifndef KF_FLAG_CREATE -#define KF_FLAG_CREATE 0x00008000 // Make sure that the folder already exists or create it and apply security specified in folder definition -#endif - EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)( REFKNOWNFOLDERID rfid, DWORD dwFlags, @@ -249,6 +244,181 @@ LExit: return hr; } +extern "C" HRESULT DAPI ShelGetFolder( + __out_z LPWSTR* psczFolderPath, + __in int csidlFolder + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + KNOWNFOLDERID rfid = { }; + + csidlFolder &= ~CSIDL_FLAG_MASK; + + switch (csidlFolder) + { + case CSIDL_ADMINTOOLS: + rfid = FOLDERID_AdminTools; + break; + case CSIDL_APPDATA: + rfid = FOLDERID_RoamingAppData; + break; + case CSIDL_CDBURN_AREA: + rfid = FOLDERID_CDBurning; + break; + case CSIDL_COMMON_ADMINTOOLS: + rfid = FOLDERID_CommonAdminTools; + break; + case CSIDL_COMMON_APPDATA: + rfid = FOLDERID_ProgramData; + break; + case CSIDL_COMMON_DESKTOPDIRECTORY: + rfid = FOLDERID_PublicDesktop; + break; + case CSIDL_COMMON_DOCUMENTS: + rfid = FOLDERID_PublicDocuments; + break; + case CSIDL_COMMON_MUSIC: + rfid = FOLDERID_PublicMusic; + break; + case CSIDL_COMMON_OEM_LINKS: + rfid = FOLDERID_CommonOEMLinks; + break; + case CSIDL_COMMON_PICTURES: + rfid = FOLDERID_PublicPictures; + break; + case CSIDL_COMMON_PROGRAMS: + rfid = FOLDERID_CommonPrograms; + break; + case CSIDL_COMMON_STARTMENU: + rfid = FOLDERID_CommonStartMenu; + break; + case CSIDL_COMMON_STARTUP: __fallthrough; + case CSIDL_COMMON_ALTSTARTUP: + rfid = FOLDERID_CommonStartup; + break; + case CSIDL_COMMON_TEMPLATES: + rfid = FOLDERID_CommonTemplates; + break; + case CSIDL_COMMON_VIDEO: + rfid = FOLDERID_PublicVideos; + break; + case CSIDL_COOKIES: + rfid = FOLDERID_Cookies; + break; + case CSIDL_DESKTOP: + case CSIDL_DESKTOPDIRECTORY: + rfid = FOLDERID_Desktop; + break; + case CSIDL_FAVORITES: __fallthrough; + case CSIDL_COMMON_FAVORITES: + rfid = FOLDERID_Favorites; + break; + case CSIDL_FONTS: + rfid = FOLDERID_Fonts; + break; + case CSIDL_HISTORY: + rfid = FOLDERID_History; + break; + case CSIDL_INTERNET_CACHE: + rfid = FOLDERID_InternetCache; + break; + case CSIDL_LOCAL_APPDATA: + rfid = FOLDERID_LocalAppData; + break; + case CSIDL_MYMUSIC: + rfid = FOLDERID_Music; + break; + case CSIDL_MYPICTURES: + rfid = FOLDERID_Pictures; + break; + case CSIDL_MYVIDEO: + rfid = FOLDERID_Videos; + break; + case CSIDL_NETHOOD: + rfid = FOLDERID_NetHood; + break; + case CSIDL_PERSONAL: + rfid = FOLDERID_Documents; + break; + case CSIDL_PRINTHOOD: + rfid = FOLDERID_PrintHood; + break; + case CSIDL_PROFILE: + rfid = FOLDERID_Profile; + break; + case CSIDL_PROGRAM_FILES: + rfid = FOLDERID_ProgramFiles; + break; + case CSIDL_PROGRAM_FILESX86: + rfid = FOLDERID_ProgramFilesX86; + break; + case CSIDL_PROGRAM_FILES_COMMON: + rfid = FOLDERID_ProgramFilesCommon; + break; + case CSIDL_PROGRAM_FILES_COMMONX86: + rfid = FOLDERID_ProgramFilesCommonX86; + break; + case CSIDL_PROGRAMS: + rfid = FOLDERID_Programs; + break; + case CSIDL_RECENT: + rfid = FOLDERID_Recent; + break; + case CSIDL_RESOURCES: + rfid = FOLDERID_ResourceDir; + break; + case CSIDL_RESOURCES_LOCALIZED: + rfid = FOLDERID_LocalizedResourcesDir; + break; + case CSIDL_SENDTO: + rfid = FOLDERID_SendTo; + break; + case CSIDL_STARTMENU: + rfid = FOLDERID_StartMenu; + break; + case CSIDL_STARTUP: + case CSIDL_ALTSTARTUP: + rfid = FOLDERID_Startup; + break; + case CSIDL_SYSTEM: + rfid = FOLDERID_System; + break; + case CSIDL_SYSTEMX86: + rfid = FOLDERID_SystemX86; + break; + case CSIDL_TEMPLATES: + rfid = FOLDERID_Templates; + break; + case CSIDL_WINDOWS: + rfid = FOLDERID_Windows; + break; + default: + ShelExitWithRootFailure(hr, E_INVALIDARG, "Unknown csidl: %d", csidlFolder); + } + + hr = ShelGetKnownFolder(&sczPath, rfid); + if (E_NOTIMPL == hr) + { + hr = S_FALSE; + } + ShelExitOnFailure(hr, "Failed to get known folder."); + + if (S_FALSE == hr) + { + hr = GetFolderFromCsidl(&sczPath, csidlFolder); + ShelExitOnFailure(hr, "Failed to get csidl folder."); + } + + *psczFolderPath = sczPath; + sczPath = NULL; + +LExit: + ReleaseStr(sczPath); + + return hr; +} + // Internal functions. @@ -287,7 +457,7 @@ static HRESULT GetDesktopShellView( else if (S_FALSE == hr) { //Windows XP - hr = SHGetDesktopFolder(&psf); + hr = ::SHGetDesktopFolder(&psf); ShelExitOnFailure(hr, "Failed to get desktop folder."); hr = psf->CreateViewObject(NULL, IID_IShellView, ppv); diff --git a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp index e9ef1047..109c558c 100644 --- a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp +++ b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp @@ -7,6 +7,11 @@ using namespace System::IO; using namespace Xunit; using namespace WixBuildTools::TestSupport; +// from PathCch.h +#ifndef PATHCCH_ALLOW_LONG_PATHS +#define PATHCCH_ALLOW_LONG_PATHS 0x01 +#endif + namespace DutilTests { public ref class PathUtil @@ -101,8 +106,28 @@ namespace DutilTests } } + [Fact] + void PathCanonicalizeForComparisonFallbackTest() + { + Path2FunctionForceFallback(); + + try + { + PathCanonicalizeForComparisonTestCore(FALSE); + } + finally + { + Path2FunctionAllowFallback(); + } + } + [Fact] void PathCanonicalizeForComparisonTest() + { + PathCanonicalizeForComparisonTestCore(TRUE); + } + + void PathCanonicalizeForComparisonTestCore(BOOL fLongPathSupported) { HRESULT hr = S_OK; LPWSTR sczCanonicalized = NULL; @@ -110,10 +135,26 @@ namespace DutilTests try { hr = PathCanonicalizeForComparison(L"C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", 0, &sczCanonicalized); - Assert::Equal(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr); + if (!fLongPathSupported) + { + Assert::Equal(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr); + } + else + { + NativeAssert::Succeeded(hr, "Failed to canonicalize path"); + NativeAssert::StringEqual(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized); + } hr = PathCanonicalizeForComparison(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", 0, &sczCanonicalized); - Assert::Equal(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr); + if (!fLongPathSupported) + { + Assert::Equal(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr); + } + else + { + NativeAssert::Succeeded(hr, "Failed to canonicalize path"); + NativeAssert::StringEqual(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized); + } hr = PathCanonicalizeForComparison(L"\\\\server", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized); NativeAssert::Succeeded(hr, "Failed to canonicalize path"); @@ -237,6 +278,52 @@ namespace DutilTests } } + [Fact] + void PathAllocCanonicalizePathTest() + { + HRESULT hr = S_OK; + LPWSTR sczCanonicalized = NULL; + + try + { + hr = PathAllocCanonicalizePath(L"C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized); + NativeAssert::Succeeded(hr, "Failed to canonicalize path"); + NativeAssert::StringEqual(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized); + + hr = PathAllocCanonicalizePath(L"abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized); + NativeAssert::Succeeded(hr, "Failed to canonicalize path"); + NativeAssert::StringEqual(L"abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized); + + hr = PathAllocCanonicalizePath(L"\\\\server\\share\\..\\..\\otherdir\\unc.exe", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized); + NativeAssert::Succeeded(hr, "Failed to canonicalize path"); + NativeAssert::StringEqual(L"\\\\otherdir\\unc.exe", sczCanonicalized); // This is surprising. + + hr = PathAllocCanonicalizePath(L"C:\\dir\\subdir\\..\\..\\otherdir\\backslashes.exe", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized); + NativeAssert::Succeeded(hr, "Failed to canonicalize path"); + NativeAssert::StringEqual(L"C:\\otherdir\\backslashes.exe", sczCanonicalized); + + hr = PathAllocCanonicalizePath(L"C:/dir/subdir/../../otherdir/forwardslashes.exe", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized); + NativeAssert::Succeeded(hr, "Failed to canonicalize path"); + NativeAssert::StringEqual(L"C:/dir/subdir/../../otherdir/forwardslashes.exe", sczCanonicalized); + + hr = PathAllocCanonicalizePath(L"\\\\?\\C:\\test\\..\\validlongpath.exe", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized); + NativeAssert::Succeeded(hr, "Failed to canonicalize path"); + NativeAssert::StringEqual(L"C:\\validlongpath.exe", sczCanonicalized); + + hr = PathAllocCanonicalizePath(L"\\\\?\\test\\..\\invalidlongpath.exe", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized); + NativeAssert::Succeeded(hr, "Failed to canonicalize path"); + NativeAssert::StringEqual(L"\\\\?\\invalidlongpath.exe", sczCanonicalized); + + hr = PathAllocCanonicalizePath(L"C:\\.\\invalid:pathchars?.exe", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized); + NativeAssert::Succeeded(hr, "Failed to canonicalize path"); + NativeAssert::StringEqual(L"C:\\invalid:pathchars?.exe", sczCanonicalized); + } + finally + { + ReleaseStr(sczCanonicalized); + } + } + [Fact] void PathConcatTest() { @@ -544,6 +631,25 @@ namespace DutilTests } } + [Fact] + void PathForCurrentProcessTest() + { + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + + try + { + hr = PathForCurrentProcess(&sczPath, NULL); + NativeAssert::Succeeded(hr, "Failed to get current process path."); + + WixAssert::StringEqual(System::Diagnostics::Process::GetCurrentProcess()->MainModule->FileName, gcnew String(sczPath), false); + } + finally + { + ReleaseStr(sczPath); + } + } + [Fact] void PathGetDirectoryTest() { @@ -827,6 +933,78 @@ namespace DutilTests } } + [Fact] + void PathGetTempPathTest() + { + HRESULT hr = S_OK; + LPCWSTR wzEnvName = L"TMP"; + LPCWSTR wzEnvName2 = L"TEMP"; + LPCWSTR wzLongTempPath = L"C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\\cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\\"; + LPWSTR sczTempPath = NULL; + WCHAR wzOriginalTemp[MAX_PATH + 1] = { }; + WCHAR wzOriginalTemp2[MAX_PATH + 1] = { }; + DWORD cch = 0; + DWORD cch2 = 0; + SIZE_T cchTemp = 0; + size_t cchTemp2 = 0; + + try + { + cch = ::GetEnvironmentVariableW(wzEnvName, wzOriginalTemp, countof(wzOriginalTemp)); + Assert::NotEqual(0, cch); + + if (!::SetEnvironmentVariableW(wzEnvName, wzLongTempPath)) + { + Assert::Equal(0xFFFFFFFF, ::GetLastError()); + } + + cch2 = ::GetEnvironmentVariableW(wzEnvName2, wzOriginalTemp2, countof(wzOriginalTemp2)); + Assert::NotEqual(0, cch2); + + hr = PathGetTempPath(&sczTempPath, &cchTemp); + NativeAssert::Succeeded(hr, "Failed to get temp path."); + + PathFixedBackslashTerminate(wzOriginalTemp2, countof(wzOriginalTemp2)); + + hr = ::StringCchLengthW(wzOriginalTemp2, countof(wzOriginalTemp2), &cchTemp2); + NativeAssert::Succeeded(hr, "Failed to get temp path length."); + + NativeAssert::StringEqual(wzOriginalTemp2, sczTempPath); + Assert::Equal(cchTemp2, cchTemp); + } + finally + { + if (cch) + { + ::SetEnvironmentVariableW(wzEnvName, wzOriginalTemp); + } + + ReleaseStr(sczTempPath); + } + } + + [Fact] + void PathGetVolumePathNameTest() + { + HRESULT hr = S_OK; + LPWSTR sczVolumePathName = NULL; + + try + { + hr = StrAlloc(&sczVolumePathName, 1); + NativeAssert::Succeeded(hr, "Failed to alloc volume path name."); + + hr = PathGetVolumePathName(L"C:\\Windows", &sczVolumePathName); + NativeAssert::Succeeded(hr, "PathGetVolumePathName failed."); + + NativeAssert::StringEqual(L"C:\\", sczVolumePathName); + } + finally + { + ReleaseStr(sczVolumePathName); + } + } + [Fact] void PathNormalizeSlashesFixedTest() { -- cgit v1.2.3-55-g6feb