Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions dll/kernel32/fileapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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() != '/') {
Expand Down Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions dll/kernel32/fileapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
78 changes: 76 additions & 2 deletions test/test_gettempfilename.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,28 @@

#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <windows.h>

static const char *basename_of(const char *path) {
const char *slash = strrchr(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));
}
Expand All @@ -39,13 +45,81 @@ 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);
TEST_CHECK(len > 0 && len < sizeof(temp_path));

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;
}
Loading