diff --git a/dll/kernel32/fileapi.cpp b/dll/kernel32/fileapi.cpp index b65ce90..31b5083 100644 --- a/dll/kernel32/fileapi.cpp +++ b/dll/kernel32/fileapi.cpp @@ -1748,7 +1748,7 @@ UINT WINAPI GetTempFileNameA(LPCSTR lpPathName, LPCSTR lpPrefixString, UINT uUni HOST_CONTEXT_GUARD(); DEBUG_LOG("GetTempFileNameA(%s, %s, %u)\n", lpPathName ? lpPathName : "(null)", lpPrefixString ? lpPrefixString : "(null)", uUnique); - if (!lpPathName || !lpPrefixString || !lpTempFileName) { + if (!lpPathName || !lpTempFileName) { setLastError(ERROR_INVALID_PARAMETER); return 0; } @@ -1757,9 +1757,12 @@ UINT WINAPI GetTempFileNameA(LPCSTR lpPathName, LPCSTR lpPrefixString, UINT uUni return 0; } + // A null prefix is treated as an empty string, matching Windows/Wine. + const char *prefix = lpPrefixString ? lpPrefixString : ""; + auto makeTempFileName = [&](UINT unique) { char filename[12]; - snprintf(filename, sizeof(filename), "%.3s%04X.TMP", lpPrefixString, unique & 0xFFFF); + snprintf(filename, sizeof(filename), "%.3s%04X.TMP", prefix, unique & 0xFFFF); std::string path = lpPathName; if (!path.empty() && path.back() != '\\' && path.back() != '/') { @@ -1810,7 +1813,27 @@ UINT WINAPI GetTempFileNameA(LPCSTR lpPathName, LPCSTR lpPrefixString, UINT uUni lpTempFileName[MAX_PATH - 1] = '\0'; DEBUG_LOG(" -> %s\n", lpTempFileName); } - return uUnique; + return uUnique & 0xFFFF; +} + +UINT WINAPI GetTempFileNameW(LPCWSTR lpPathName, LPCWSTR lpPrefixString, UINT uUnique, LPWSTR lpTempFileName) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("GetTempFileNameW -> "); + if (!lpPathName || !lpTempFileName) { + setLastError(ERROR_INVALID_PARAMETER); + return 0; + } + std::string pathName = wideStringToString(lpPathName); + // A null prefix is treated as an empty string, matching Windows/Wine. + std::string prefixString = lpPrefixString ? wideStringToString(lpPrefixString) : std::string(); + char tempFileName[MAX_PATH]; + UINT result = GetTempFileNameA(pathName.c_str(), prefixString.c_str(), uUnique, tempFileName); + if (result == 0) { + return 0; + } + auto wide = stringToWideString(tempFileName); + wstrncpy(lpTempFileName, wide.data(), wstrlen(wide.data()) + 1); + return result; } DWORD WINAPI GetTempPathA(DWORD nBufferLength, LPSTR lpBuffer) { diff --git a/dll/kernel32/fileapi.h b/dll/kernel32/fileapi.h index 7531963..0e1d443 100644 --- a/dll/kernel32/fileapi.h +++ b/dll/kernel32/fileapi.h @@ -61,6 +61,7 @@ DWORD WINAPI GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lp DWORD WINAPI GetShortPathNameA(LPCSTR lpszLongPath, LPSTR lpszShortPath, DWORD cchBuffer); DWORD WINAPI GetShortPathNameW(LPCWSTR lpszLongPath, LPWSTR lpszShortPath, DWORD cchBuffer); UINT WINAPI GetTempFileNameA(LPCSTR lpPathName, LPCSTR lpPrefixString, UINT uUnique, LPSTR lpTempFileName); +UINT WINAPI GetTempFileNameW(LPCWSTR lpPathName, LPCWSTR lpPrefixString, UINT uUnique, LPWSTR lpTempFileName); DWORD WINAPI GetTempPathA(DWORD nBufferLength, LPSTR lpBuffer); HANDLE WINAPI FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData); HANDLE WINAPI FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData); diff --git a/test/test_gettempfilename.c b/test/test_gettempfilename.c index 776a4d6..85a1e25 100644 --- a/test/test_gettempfilename.c +++ b/test/test_gettempfilename.c @@ -2,6 +2,7 @@ #include #include +#include #include static const char *basename_of(const char *path) { @@ -9,15 +10,20 @@ static const char *basename_of(const char *path) { return slash ? slash + 1 : path; } +static const WCHAR *basename_of_w(const WCHAR *path) { + const WCHAR *slash = wcsrchr(path, L'\\'); + return slash ? slash + 1 : path; +} + static void expect_fixed_unique_name(const char *temp_path) { char temp_file[MAX_PATH]; UINT result = GetTempFileNameA(temp_path, "wboX", 0x12345, temp_file); - TEST_CHECK_EQ(0x12345, result); + TEST_CHECK_EQ(0x2345, result); DeleteFileA(temp_file); result = GetTempFileNameA(temp_path, "wboX", 0x12345, temp_file); - TEST_CHECK_EQ(0x12345, result); + TEST_CHECK_EQ(0x2345, result); TEST_CHECK_STR_EQ("wbo2345.TMP", basename_of(temp_file)); TEST_CHECK_EQ(INVALID_FILE_ATTRIBUTES, GetFileAttributesA(temp_file)); } @@ -39,6 +45,64 @@ static void expect_created_file_reopens_like_link(const char *temp_path) { TEST_CHECK(CloseHandle(handle)); } +static void expect_null_prefix_is_empty(const char *temp_path) { + char temp_file[MAX_PATH]; + + UINT result = GetTempFileNameA(temp_path, NULL, 0x12345, temp_file); + TEST_CHECK_EQ(0x2345, result); + TEST_CHECK_STR_EQ("2345.TMP", basename_of(temp_file)); +} + +static void expect_fixed_unique_name_w(const WCHAR *temp_path) { + WCHAR temp_file[MAX_PATH]; + + UINT result = GetTempFileNameW(temp_path, L"wboX", 0x12345, temp_file); + TEST_CHECK_EQ(0x2345, result); + DeleteFileW(temp_file); + + result = GetTempFileNameW(temp_path, L"wboX", 0x12345, temp_file); + TEST_CHECK_EQ(0x2345, result); + TEST_CHECK(wcscmp(L"wbo2345.TMP", basename_of_w(temp_file)) == 0); + TEST_CHECK_EQ(INVALID_FILE_ATTRIBUTES, GetFileAttributesW(temp_file)); +} + +static void expect_created_file_reopens_like_link_w(const WCHAR *temp_path) { + WCHAR temp_file[MAX_PATH]; + + UINT result = GetTempFileNameW(temp_path, L"GDI32", 0, temp_file); + TEST_CHECK(result != 0); + TEST_CHECK_EQ(11, wcslen(basename_of_w(temp_file))); + TEST_CHECK_EQ(0, wcsncmp(L"GDI", basename_of_w(temp_file), 3)); + TEST_CHECK(wcscmp(L".TMP", basename_of_w(temp_file) + 7) == 0); + TEST_CHECK(GetFileAttributesW(temp_file) != INVALID_FILE_ATTRIBUTES); + + HANDLE handle = CreateFileW(temp_file, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL); + TEST_CHECK_MSG(handle != INVALID_HANDLE_VALUE, "CreateFileW(temp, OPEN_EXISTING|DELETE_ON_CLOSE) failed: %lu", + GetLastError()); + TEST_CHECK(CloseHandle(handle)); +} + +static void expect_null_prefix_is_empty_w(const WCHAR *temp_path) { + WCHAR temp_file[MAX_PATH]; + + UINT result = GetTempFileNameW(temp_path, NULL, 0x12345, temp_file); + TEST_CHECK_EQ(0x2345, result); + TEST_CHECK(wcscmp(L"2345.TMP", basename_of_w(temp_file)) == 0); +} + +static void expect_invalid_parameters_w(const WCHAR *temp_path) { + WCHAR temp_file[MAX_PATH]; + + SetLastError(0); + TEST_CHECK_EQ(0, GetTempFileNameW(NULL, L"wbo", 0, temp_file)); + TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError()); + + SetLastError(0); + TEST_CHECK_EQ(0, GetTempFileNameW(temp_path, L"wbo", 0, NULL)); + TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError()); +} + int main(void) { char temp_path[MAX_PATH]; DWORD len = GetTempPathA(sizeof(temp_path), temp_path); @@ -46,6 +110,16 @@ int main(void) { expect_fixed_unique_name(temp_path); expect_created_file_reopens_like_link(temp_path); + expect_null_prefix_is_empty(temp_path); + + WCHAR temp_path_w[MAX_PATH]; + int wlen = MultiByteToWideChar(CP_ACP, 0, temp_path, -1, temp_path_w, MAX_PATH); + TEST_CHECK(wlen > 0); + + expect_fixed_unique_name_w(temp_path_w); + expect_created_file_reopens_like_link_w(temp_path_w); + expect_null_prefix_is_empty_w(temp_path_w); + expect_invalid_parameters_w(temp_path_w); return 0; }