123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633 |
- #include <windows.h>
- #include <winstring.h> // For wil::unique_hstring
- #include <wil/common.h>
- #ifdef WIL_ENABLE_EXCEPTIONS
- #include <string>
- #endif
- // TODO: str_raw_ptr is not two-phase name lookup clean (https://github.com/Microsoft/wil/issues/8)
- namespace wil
- {
- PCWSTR str_raw_ptr(HSTRING);
- #ifdef WIL_ENABLE_EXCEPTIONS
- PCWSTR str_raw_ptr(const std::wstring&);
- #endif
- }
- #include <wil/filesystem.h>
- #ifdef WIL_ENABLE_EXCEPTIONS
- #include <wil/stl.h> // For std::wstring string_maker
- #endif
- #include "common.h"
- #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
- bool DirectoryExists(_In_ PCWSTR path)
- {
- DWORD dwAttrib = GetFileAttributesW(path);
- return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
- (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
- }
- bool FileExists(_In_ PCWSTR path)
- {
- DWORD dwAttrib = GetFileAttributesW(path);
- return (dwAttrib != INVALID_FILE_ATTRIBUTES);
- }
- TEST_CASE("FileSystemTests::CreateDirectory", "[filesystem]")
- {
- wchar_t basePath[MAX_PATH];
- REQUIRE(GetTempPathW(ARRAYSIZE(basePath), basePath));
- REQUIRE_SUCCEEDED(PathCchAppend(basePath, ARRAYSIZE(basePath), L"FileSystemTests"));
- REQUIRE_FALSE(DirectoryExists(basePath));
- REQUIRE(SUCCEEDED(wil::CreateDirectoryDeepNoThrow(basePath)));
- REQUIRE(DirectoryExists(basePath));
- auto scopeGuard = wil::scope_exit([&]
- {
- REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(basePath));
- });
- PCWSTR relativeTestPath = L"folder1\\folder2\\folder3\\folder4\\folder5\\folder6\\folder7\\folder8";
- wchar_t absoluteTestPath[MAX_PATH];
- REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath, ARRAYSIZE(absoluteTestPath), basePath));
- REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath, ARRAYSIZE(absoluteTestPath), relativeTestPath));
- REQUIRE_FALSE(DirectoryExists(absoluteTestPath));
- REQUIRE_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(absoluteTestPath));
- PCWSTR invalidCharsPath = L"Bad?Char|";
- wchar_t absoluteInvalidPath[MAX_PATH];
- REQUIRE_SUCCEEDED(StringCchCopyW(absoluteInvalidPath, ARRAYSIZE(absoluteInvalidPath), basePath));
- REQUIRE_SUCCEEDED(PathCchAppend(absoluteInvalidPath, ARRAYSIZE(absoluteInvalidPath), invalidCharsPath));
- REQUIRE_FALSE(DirectoryExists(absoluteInvalidPath));
- REQUIRE_FALSE(SUCCEEDED(wil::CreateDirectoryDeepNoThrow(absoluteInvalidPath)));
- PCWSTR testPath3 = L"folder1\\folder2\\folder3";
- wchar_t absoluteTestPath3[MAX_PATH];
- REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath3, ARRAYSIZE(absoluteTestPath3), basePath));
- REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath3, ARRAYSIZE(absoluteTestPath3), testPath3));
- REQUIRE(DirectoryExists(absoluteTestPath3));
- PCWSTR testPath4 = L"folder1\\folder2\\folder3\\folder4";
- wchar_t absoluteTestPath4[MAX_PATH];
- REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath4, ARRAYSIZE(absoluteTestPath4), basePath));
- REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath4, ARRAYSIZE(absoluteTestPath4), testPath4));
- REQUIRE(DirectoryExists(absoluteTestPath4));
- REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(absoluteTestPath3, wil::RemoveDirectoryOptions::KeepRootDirectory));
- REQUIRE(DirectoryExists(absoluteTestPath3));
- REQUIRE_FALSE(DirectoryExists(absoluteTestPath4));
- }
- TEST_CASE("FileSystemTests::VerifyRemoveDirectoryRecursiveDoesNotTraverseWithoutAHandle", "[filesystem]")
- {
- auto CreateRelativePath = [](PCWSTR root, PCWSTR name)
- {
- wil::unique_hlocal_string path;
- REQUIRE_SUCCEEDED(PathAllocCombine(root, name, PATHCCH_ALLOW_LONG_PATHS, &path));
- return path;
- };
- wil::unique_cotaskmem_string tempPath;
- REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(LR"(%TEMP%)", tempPath));
- const auto basePath = CreateRelativePath(tempPath.get(), L"FileSystemTests");
- REQUIRE_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(basePath.get()));
- auto scopeGuard = wil::scope_exit([&]
- {
- wil::RemoveDirectoryRecursiveNoThrow(basePath.get());
- });
- // Try to delete a directory whose handle is already taken.
- const auto folderToRecurse = CreateRelativePath(basePath.get(), L"folderToRecurse");
- REQUIRE(::CreateDirectoryW(folderToRecurse.get(), nullptr));
- const auto subfolderWithHandle = CreateRelativePath(folderToRecurse.get(), L"subfolderWithHandle");
- REQUIRE(::CreateDirectoryW(subfolderWithHandle.get(), nullptr));
- const auto childOfSubfolder = CreateRelativePath(subfolderWithHandle.get(), L"childOfSubfolder");
- REQUIRE(::CreateDirectoryW(childOfSubfolder.get(), nullptr));
- // Passing a 0 in share flags only allows metadata query on this file by other processes.
- // This should fail with a sharing violation error when any other action is taken.
- wil::unique_hfile subFolderHandle(::CreateFileW(subfolderWithHandle.get(), GENERIC_ALL,
- 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr));
- REQUIRE(subFolderHandle);
- REQUIRE(wil::RemoveDirectoryRecursiveNoThrow(folderToRecurse.get()) == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION));
- // Release the handle to allow cleanup.
- subFolderHandle.reset();
- }
- TEST_CASE("FileSystemTests::VerifyRemoveDirectoryRecursiveCanDeleteReadOnlyFiles", "[filesystem]")
- {
- auto CreateRelativePath = [](PCWSTR root, PCWSTR name)
- {
- wil::unique_hlocal_string path;
- REQUIRE_SUCCEEDED(PathAllocCombine(root, name, PATHCCH_ALLOW_LONG_PATHS, &path));
- return path;
- };
- auto CreateReadOnlyFile = [](PCWSTR path)
- {
- wil::unique_hfile fileHandle(CreateFileW(path, 0,
- 0, nullptr, CREATE_ALWAYS,
- FILE_ATTRIBUTE_READONLY, nullptr));
- REQUIRE(fileHandle);
- };
- wil::unique_cotaskmem_string tempPath;
- REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(LR"(%TEMP%)", tempPath));
- const auto basePath = CreateRelativePath(tempPath.get(), L"FileSystemTests");
- REQUIRE_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(basePath.get()));
- auto scopeGuard = wil::scope_exit([&]
- {
- wil::RemoveDirectoryRecursiveNoThrow(basePath.get(), wil::RemoveDirectoryOptions::RemoveReadOnly);
- });
- // Create a reparse point and a target folder that shouldn't get deleted
- auto folderToDelete = CreateRelativePath(basePath.get(), L"folderToDelete");
- REQUIRE(::CreateDirectoryW(folderToDelete.get(), nullptr));
- auto topLevelReadOnly = CreateRelativePath(folderToDelete.get(), L"topLevelReadOnly.txt");
- CreateReadOnlyFile(topLevelReadOnly.get());
- auto subLevel = CreateRelativePath(folderToDelete.get(), L"subLevel");
- REQUIRE(::CreateDirectoryW(subLevel.get(), nullptr));
- auto subLevelReadOnly = CreateRelativePath(subLevel.get(), L"subLevelReadOnly.txt");
- CreateReadOnlyFile(subLevelReadOnly.get());
- // Delete will fail without the RemoveReadOnlyFlag
- REQUIRE_FAILED(wil::RemoveDirectoryRecursiveNoThrow(folderToDelete.get()));
- REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(folderToDelete.get(), wil::RemoveDirectoryOptions::RemoveReadOnly));
- // Verify all files have been deleted
- REQUIRE_FALSE(FileExists(subLevelReadOnly.get()));
- REQUIRE_FALSE(DirectoryExists(subLevel.get()));
- REQUIRE_FALSE(FileExists(topLevelReadOnly.get()));
- REQUIRE_FALSE(DirectoryExists(folderToDelete.get()));
- }
- #ifdef WIL_ENABLE_EXCEPTIONS
- // Learn about the Win32 API normalization here: https://blogs.msdn.microsoft.com/jeremykuhne/2016/04/21/path-normalization/
- // This test verifies the ability of RemoveDirectoryRecursive to be able to delete files
- // that are in the non-normalized form.
- TEST_CASE("FileSystemTests::VerifyRemoveDirectoryRecursiveCanDeleteFoldersWithNonNormalizedNames", "[filesystem]")
- {
- // Extended length paths can access files with non-normalized names.
- // This function creates a path with that ability.
- auto CreatePathThatCanAccessNonNormalizedNames = [](PCWSTR root, PCWSTR name)
- {
- wil::unique_hlocal_string path;
- THROW_IF_FAILED(PathAllocCombine(root, name, PATHCCH_DO_NOT_NORMALIZE_SEGMENTS | PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH, &path));
- REQUIRE(wil::is_extended_length_path(path.get()));
- return path;
- };
- // Regular paths are normalized in the Win32 APIs thus can't address files in the non-normalized form.
- // This function creates a regular path form but preserves the non-normalized parts of the input (for testing)
- auto CreateRegularPath = [](PCWSTR root, PCWSTR name)
- {
- wil::unique_hlocal_string path;
- THROW_IF_FAILED(PathAllocCombine(root, name, PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &path));
- REQUIRE_FALSE(wil::is_extended_length_path(path.get()));
- return path;
- };
- struct TestCases
- {
- PCWSTR CreateWithName;
- PCWSTR DeleteWithName;
- wil::unique_hlocal_string (*CreatePathFunction)(PCWSTR root, PCWSTR name);
- HRESULT ExpectedResult;
- };
- PCWSTR NormalizedName = L"Foo";
- PCWSTR NonNormalizedName = L"Foo."; // The dot at the end is what makes this non-normalized.
- const auto PathNotFoundError = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
- TestCases tests[] =
- {
- { NormalizedName, NormalizedName, CreateRegularPath, S_OK },
- { NonNormalizedName, NormalizedName, CreateRegularPath, PathNotFoundError },
- { NormalizedName, NonNormalizedName, CreateRegularPath, S_OK },
- { NonNormalizedName, NonNormalizedName, CreateRegularPath, PathNotFoundError },
- { NormalizedName, NormalizedName, CreatePathThatCanAccessNonNormalizedNames, S_OK },
- { NonNormalizedName, NormalizedName, CreatePathThatCanAccessNonNormalizedNames, PathNotFoundError },
- { NormalizedName, NonNormalizedName, CreatePathThatCanAccessNonNormalizedNames, PathNotFoundError },
- { NonNormalizedName, NonNormalizedName, CreatePathThatCanAccessNonNormalizedNames, S_OK },
- };
- auto folderRoot = wil::ExpandEnvironmentStringsW(LR"(%TEMP%)");
- REQUIRE_FALSE(wil::is_extended_length_path(folderRoot.get()));
- auto EnsureFolderWithNonCanonicalNameAndContentsExists = [&](const TestCases& test)
- {
- const auto enableNonNormalized = PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS;
- wil::unique_hlocal_string targetFolder;
- // Create a folder for testing using the extended length form to enable
- // access to non-normalized forms of the path
- THROW_IF_FAILED(PathAllocCombine(folderRoot.get(), test.CreateWithName, enableNonNormalized, &targetFolder));
- // This ensures the folder is there and won't fail if it already exists (common when testing).
- wil::CreateDirectoryDeep(targetFolder.get());
- // Create a file in that folder with a non-normalized name (with the dot at the end).
- wil::unique_hlocal_string extendedFilePath;
- THROW_IF_FAILED(PathAllocCombine(targetFolder.get(), L"NonNormalized.", enableNonNormalized, &extendedFilePath));
- wil::unique_hfile fileHandle(CreateFileW(extendedFilePath.get(), FILE_WRITE_ATTRIBUTES,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
- CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
- THROW_LAST_ERROR_IF(!fileHandle);
- };
- for (auto const& test : tests)
- {
- // remove remnants from previous test that will cause failures
- wil::RemoveDirectoryRecursiveNoThrow(CreatePathThatCanAccessNonNormalizedNames(folderRoot.get(), NormalizedName).get());
- wil::RemoveDirectoryRecursiveNoThrow(CreatePathThatCanAccessNonNormalizedNames(folderRoot.get(), NonNormalizedName).get());
- EnsureFolderWithNonCanonicalNameAndContentsExists(test);
- auto deleteWithPath = test.CreatePathFunction(folderRoot.get(), test.DeleteWithName);
- const auto hr = wil::RemoveDirectoryRecursiveNoThrow(deleteWithPath.get());
- REQUIRE(test.ExpectedResult == hr);
- }
- }
- #endif
- // real paths to test
- const wchar_t c_variablePath[] = L"%systemdrive%\\Windows\\System32\\Windows.Storage.dll";
- const wchar_t c_expandedPath[] = L"c:\\Windows\\System32\\Windows.Storage.dll";
- // // paths that should not exist on the system
- const wchar_t c_missingVariable[] = L"%doesnotexist%\\doesnotexist.dll";
- const wchar_t c_missingPath[] = L"c:\\Windows\\System32\\doesnotexist.dll";
- const int c_stackBufferLimitTest = 5;
- #ifdef WIL_ENABLE_EXCEPTIONS
- TEST_CASE("FileSystemTests::VerifyGetCurrentDirectory", "[filesystem]")
- {
- auto pwd = wil::GetCurrentDirectoryW();
- REQUIRE(*pwd.get() != L'\0');
- }
- TEST_CASE("FileSystemTests::VerifyGetFullPathName", "[filesystem]")
- {
- PCWSTR fileName = L"ReadMe.txt";
- auto result = wil::GetFullPathNameW<wil::unique_cotaskmem_string>(fileName, nullptr);
- PCWSTR fileNameResult;
- result = wil::GetFullPathNameW<wil::unique_cotaskmem_string>(fileName, &fileNameResult);
- REQUIRE(wcscmp(fileName, fileNameResult) == 0);
- auto result2 = wil::GetFullPathNameW<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(fileName, &fileNameResult);
- REQUIRE(wcscmp(fileName, fileNameResult) == 0);
- REQUIRE(wcscmp(result.get(), result2.get()) == 0);
- // The only negative test case I've found is a path > 32k.
- std::wstring big(1024 * 32, L'a');
- wil::unique_hstring output;
- auto hr = wil::GetFullPathNameW(big.c_str(), output, nullptr);
- REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE));
- }
- TEST_CASE("FileSystemTests::VerifyGetFinalPathNameByHandle", "[filesystem]")
- {
- wil::unique_hfile fileHandle(CreateFileW(c_expandedPath, FILE_READ_ATTRIBUTES,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, nullptr));
- THROW_LAST_ERROR_IF(!fileHandle);
- auto name = wil::GetFinalPathNameByHandleW(fileHandle.get());
- auto name2 = wil::GetFinalPathNameByHandleW<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(fileHandle.get());
- REQUIRE(wcscmp(name.get(), name2.get()) == 0);
- std::wstring path;
- auto hr = wil::GetFinalPathNameByHandleW(nullptr, path);
- REQUIRE(hr == E_HANDLE); // should be a usage error so be a fail fast.
- // A more legitimate case is a non file handler like a drive volume.
- wil::unique_hfile volumeHandle(CreateFileW(LR"(\\?\C:)", FILE_READ_ATTRIBUTES,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, nullptr));
- THROW_LAST_ERROR_IF(!volumeHandle);
- const auto hr2 = wil::GetFinalPathNameByHandleW(volumeHandle.get(), path);
- REQUIRE(hr2 == HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION));
- }
- TEST_CASE("FileSystemTests::VerifyTrySearchPathW", "[filesystem]")
- {
- auto pathToTest = wil::TrySearchPathW(nullptr, c_expandedPath, nullptr);
- REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
- pathToTest = wil::TrySearchPathW(nullptr, c_missingPath, nullptr);
- REQUIRE(wil::string_get_not_null(pathToTest)[0] == L'\0');
- }
- #endif
- // Simple test to expand an environmental string
- TEST_CASE("FileSystemTests::VerifyExpandEnvironmentStringsW", "[filesystem]")
- {
- wil::unique_cotaskmem_string pathToTest;
- REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_variablePath, pathToTest));
- REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
- // This should effectively be a no-op
- REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_expandedPath, pathToTest));
- REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
- // Environment variable does not exist, but the call should still succeed
- REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_missingVariable, pathToTest));
- REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_missingVariable, -1, TRUE) == CSTR_EQUAL);
- }
- TEST_CASE("FileSystemTests::VerifySearchPathW", "[filesystem]")
- {
- wil::unique_cotaskmem_string pathToTest;
- REQUIRE_SUCCEEDED(wil::SearchPathW(nullptr, c_expandedPath, nullptr, pathToTest));
- REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
- REQUIRE(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == wil::SearchPathW(nullptr, c_missingPath, nullptr, pathToTest));
- }
- TEST_CASE("FileSystemTests::VerifyExpandEnvAndSearchPath", "[filesystem]")
- {
- wil::unique_cotaskmem_string pathToTest;
- REQUIRE_SUCCEEDED(wil::ExpandEnvAndSearchPath(c_variablePath, pathToTest));
- REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
- // This test will exercise the case where AdaptFixedSizeToAllocatedResult will need to
- // reallocate the initial buffer to fit the final string.
- // This test is sufficient to test both wil::ExpandEnvironmentStringsW and wil::SeachPathW
- REQUIRE_SUCCEEDED((wil::ExpandEnvAndSearchPath<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(c_variablePath, pathToTest)));
- REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
- pathToTest.reset();
- REQUIRE(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == wil::ExpandEnvAndSearchPath(c_missingVariable, pathToTest));
- REQUIRE(pathToTest.get() == nullptr);
- }
- TEST_CASE("FileSystemTests::VerifyGetSystemDirectoryW", "[filesystem]")
- {
- wil::unique_cotaskmem_string pathToTest;
- REQUIRE_SUCCEEDED(wil::GetSystemDirectoryW(pathToTest));
- // allocate based on the string that wil::GetSystemDirectoryW returned
- size_t length = wcslen(pathToTest.get()) + 1;
- auto trueSystemDir = wil::make_cotaskmem_string_nothrow(nullptr, length);
- REQUIRE(GetSystemDirectoryW(trueSystemDir.get(), static_cast<UINT>(length)) > 0);
- REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, trueSystemDir.get(), -1, TRUE) == CSTR_EQUAL);
- // Force AdaptFixed* to realloc. Test stack boundary with small initial buffer limit, c_stackBufferLimitTest
- REQUIRE_SUCCEEDED((wil::GetSystemDirectoryW<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(pathToTest)));
- // allocate based on the string that wil::GetSystemDirectoryW returned
- length = wcslen(pathToTest.get()) + 1;
- trueSystemDir = wil::make_cotaskmem_string_nothrow(nullptr, length);
- REQUIRE(GetSystemDirectoryW(trueSystemDir.get(), static_cast<UINT>(length)) > 0);
- REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, trueSystemDir.get(), -1, TRUE) == CSTR_EQUAL);
- }
- struct has_operator_pcwstr
- {
- PCWSTR value;
- operator PCWSTR() const
- {
- return value;
- }
- };
- struct has_operator_pwstr
- {
- PWSTR value;
- operator PWSTR() const
- {
- return value;
- }
- };
- #ifdef WIL_ENABLE_EXCEPTIONS
- struct has_operator_wstr_ref
- {
- std::wstring value;
- operator const std::wstring&() const
- {
- return value;
- }
- };
- // E.g. mimics something like std::filesystem::path
- struct has_operator_wstr
- {
- std::wstring value;
- operator std::wstring() const
- {
- return value;
- }
- };
- #endif
- TEST_CASE("FileSystemTests::VerifyStrConcat", "[filesystem]")
- {
- SECTION("Concat with multiple strings")
- {
- PCWSTR test1 = L"Test1";
- #ifdef WIL_ENABLE_EXCEPTIONS
- std::wstring test2 = L"Test2";
- #else
- PCWSTR test2 = L"Test2";
- #endif
- WCHAR test3[6] = L"Test3";
- wil::unique_cotaskmem_string test4 = wil::make_unique_string_nothrow<wil::unique_cotaskmem_string>(L"test4");
- wil::unique_hstring test5 = wil::make_unique_string_nothrow<wil::unique_hstring>(L"test5");
- has_operator_pcwstr test6{ L"Test6" };
- WCHAR test7Buffer[] = L"Test7";
- has_operator_pwstr test7{ test7Buffer };
- #ifdef WIL_ENABLE_EXCEPTIONS
- has_operator_wstr_ref test8{ L"Test8" };
- has_operator_wstr test9{ L"Test9" };
- #else
- PCWSTR test8 = L"Test8";
- PCWSTR test9 = L"Test9";
- #endif
- PCWSTR expectedStr = L"Test1Test2Test3Test4Test5Test6Test7Test8Test9";
- #ifdef WIL_ENABLE_EXCEPTIONS
- auto combinedString = wil::str_concat<wil::unique_cotaskmem_string>(test1, test2, test3, test4, test5, test6, test7, test8, test9);
- REQUIRE(CompareStringOrdinal(combinedString.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
- #endif
- wil::unique_cotaskmem_string combinedStringNT;
- REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test1, test2, test3, test4, test5, test6, test7, test8, test9));
- REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
- auto combinedStringFF = wil::str_concat_failfast<wil::unique_cotaskmem_string>(test1, test2, test3, test4, test5, test6, test7, test8, test9);
- REQUIRE(CompareStringOrdinal(combinedStringFF.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
- }
- SECTION("Concat with single string")
- {
- PCWSTR test1 = L"Test1";
- #ifdef WIL_ENABLE_EXCEPTIONS
- auto combinedString = wil::str_concat<wil::unique_cotaskmem_string>(test1);
- REQUIRE(CompareStringOrdinal(combinedString.get(), -1, test1, -1, TRUE) == CSTR_EQUAL);
- #endif
- wil::unique_cotaskmem_string combinedStringNT;
- REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test1));
- REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, test1, -1, TRUE) == CSTR_EQUAL);
- auto combinedStringFF = wil::str_concat_failfast<wil::unique_cotaskmem_string>(test1);
- REQUIRE(CompareStringOrdinal(combinedStringFF.get(), -1, test1, -1, TRUE) == CSTR_EQUAL);
- }
- SECTION("Concat with existing string")
- {
- std::wstring test2 = L"Test2";
- WCHAR test3[6] = L"Test3";
- PCWSTR expectedStr = L"Test1Test2Test3";
- wil::unique_cotaskmem_string combinedStringNT = wil::make_unique_string_nothrow<wil::unique_cotaskmem_string>(L"Test1");
- REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test2.c_str(), test3));
- REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
- }
- }
- TEST_CASE("FileSystemTests::VerifyStrPrintf", "[filesystem]")
- {
- #ifdef WIL_ENABLE_EXCEPTIONS
- auto formattedString = wil::str_printf<wil::unique_cotaskmem_string>(L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28);
- REQUIRE(CompareStringOrdinal(formattedString.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL);
- #endif
- wil::unique_cotaskmem_string formattedStringNT;
- REQUIRE_SUCCEEDED(wil::str_printf_nothrow(formattedStringNT, L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28));
- REQUIRE(CompareStringOrdinal(formattedStringNT.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL);
- auto formattedStringFF = wil::str_printf_failfast<wil::unique_cotaskmem_string>(L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28);
- REQUIRE(CompareStringOrdinal(formattedStringFF.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL);
- }
- TEST_CASE("FileSystemTests::VerifyGetModuleFileNameW", "[filesystem]")
- {
- wil::unique_cotaskmem_string path;
- REQUIRE_SUCCEEDED(wil::GetModuleFileNameW(nullptr, path));
- auto len = wcslen(path.get());
- REQUIRE(((len >= 4) && (wcscmp(path.get() + len - 4, L".exe") == 0)));
- // Call again, but force multiple retries through a small initial buffer
- wil::unique_cotaskmem_string path2;
- REQUIRE_SUCCEEDED((wil::GetModuleFileNameW<wil::unique_cotaskmem_string, 4>(nullptr, path2)));
- REQUIRE(wcscmp(path.get(), path2.get()) == 0);
- REQUIRE_FAILED(wil::GetModuleFileNameW((HMODULE)INVALID_HANDLE_VALUE, path));
- #ifdef WIL_ENABLE_EXCEPTIONS
- auto wstringPath = wil::GetModuleFileNameW<std::wstring, 15>(nullptr);
- REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
- #endif
- }
- #ifdef WIL_ENABLE_EXCEPTIONS
- wil::unique_cotaskmem_string NativeGetModuleFileNameWrap(HANDLE processHandle, HMODULE moduleHandle)
- {
- DWORD size = MAX_PATH * 4;
- auto path = wil::make_cotaskmem_string_nothrow(nullptr, size);
- DWORD copied = processHandle ?
- ::GetModuleFileNameExW(processHandle, moduleHandle, path.get(), size) :
- ::GetModuleFileNameW(moduleHandle, path.get(), size);
- REQUIRE(copied < size);
- return path;
- }
- #endif
- TEST_CASE("FileSystemTests::VerifyGetModuleFileNameExW", "[filesystem]")
- {
- wil::unique_cotaskmem_string path;
- REQUIRE_SUCCEEDED(wil::GetModuleFileNameExW(nullptr, nullptr, path));
- auto len = wcslen(path.get());
- REQUIRE(((len >= 4) && (wcscmp(path.get() + len - 4, L".exe") == 0)));
- // Call again, but force multiple retries through a small initial buffer
- wil::unique_cotaskmem_string path2;
- REQUIRE_SUCCEEDED((wil::GetModuleFileNameExW<wil::unique_cotaskmem_string, 4>(nullptr, nullptr, path2)));
- REQUIRE(wcscmp(path.get(), path2.get()) == 0);
- REQUIRE_FAILED(wil::GetModuleFileNameExW(nullptr, (HMODULE)INVALID_HANDLE_VALUE, path));
- #ifdef WIL_ENABLE_EXCEPTIONS
- auto wstringPath = wil::GetModuleFileNameExW<std::wstring, 15>(nullptr, nullptr);
- REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
- REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, nullptr).get());
- wstringPath = wil::GetModuleFileNameExW<std::wstring, 15>(GetCurrentProcess(), nullptr);
- REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
- REQUIRE(wstringPath == NativeGetModuleFileNameWrap(GetCurrentProcess(), nullptr).get());
- wstringPath = wil::GetModuleFileNameW<std::wstring, 15>(nullptr);
- REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
- REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, nullptr).get());
- HMODULE kernel32 = ::GetModuleHandleW(L"kernel32.dll");
- wstringPath = wil::GetModuleFileNameExW<std::wstring, 15>(nullptr, kernel32);
- REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
- REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, kernel32).get());
- wstringPath = wil::GetModuleFileNameExW<std::wstring, 15>(GetCurrentProcess(), kernel32);
- REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
- REQUIRE(wstringPath == NativeGetModuleFileNameWrap(GetCurrentProcess(), kernel32).get());
- wstringPath = wil::GetModuleFileNameW<std::wstring, 15>(kernel32);
- REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
- REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, kernel32).get());
- #endif
- }
- TEST_CASE("FileSystemTests::QueryFullProcessImageNameW and GetModuleFileNameW", "[filesystem]")
- {
- #ifdef WIL_ENABLE_EXCEPTIONS
- auto procName = wil::QueryFullProcessImageNameW<std::wstring>();
- auto moduleName = wil::GetModuleFileNameW<std::wstring>();
- REQUIRE(procName == moduleName);
- #endif
- }
- TEST_CASE("FileSystemTests::QueryFullProcessImageNameW", "[filesystem]")
- {
- WCHAR fullName[MAX_PATH * 4];
- DWORD fullNameSize = ARRAYSIZE(fullName);
- REQUIRE(::QueryFullProcessImageNameW(::GetCurrentProcess(), 0, fullName, &fullNameSize));
- wil::unique_cotaskmem_string path;
- REQUIRE_SUCCEEDED(wil::QueryFullProcessImageNameW(::GetCurrentProcess(), 0, path));
- REQUIRE(wcscmp(fullName, path.get()) == 0);
- wil::unique_cotaskmem nativePath;
- REQUIRE_SUCCEEDED((wil::QueryFullProcessImageNameW<wil::unique_cotaskmem_string, 15>(::GetCurrentProcess(), 0, path)));
- }
- #endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|