diff --git a/dll/win32/shell32/shell32.spec b/dll/win32/shell32/shell32.spec index 86a376f49e6..2784c946de8 100644 --- a/dll/win32/shell32/shell32.spec +++ b/dll/win32/shell32/shell32.spec @@ -460,7 +460,7 @@ 749 stdcall -noname -version=0x501-0x502 SHGetShellStyleHInstance() 750 stdcall -noname SHGetAttributesFromDataObject(ptr long ptr ptr) 751 stub -noname SHSimulateDropOnClsid -752 stdcall -noname SHGetComputerDisplayNameW(long long long long) +752 stdcall -noname SHGetComputerDisplayNameW(wstr long ptr long) 753 stdcall -noname CheckStagingArea() 754 stub -noname SHLimitInputEditWithFlags 755 stdcall -noname PathIsEqualOrSubFolder(wstr wstr) diff --git a/dll/win32/shell32/stubs.cpp b/dll/win32/shell32/stubs.cpp index 3034c25d9fd..efda8e0864a 100644 --- a/dll/win32/shell32/stubs.cpp +++ b/dll/win32/shell32/stubs.cpp @@ -820,10 +820,3 @@ DWORD WINAPI CheckStagingArea(VOID) /* Called by native explorer */ return 0; } - -EXTERN_C -DWORD WINAPI SHGetComputerDisplayNameW(DWORD param1, DWORD param2, DWORD param3, DWORD param4) -{ - FIXME("SHGetComputerDisplayNameW() stub\n"); - return E_FAIL; -} diff --git a/dll/win32/shell32/utils.cpp b/dll/win32/shell32/utils.cpp index 693a8619aff..6a7b139f191 100644 --- a/dll/win32/shell32/utils.cpp +++ b/dll/win32/shell32/utils.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include WINE_DEFAULT_DEBUG_CHANNEL(shell); @@ -1893,3 +1894,149 @@ SHGetUserDisplayName( return hr; } + +// Skip leading backslashes +static PCWSTR +SHELL_SkipServerSlashes( + _In_ PCWSTR pszPath) +{ + PCWSTR pch; + for (pch = pszPath; *pch == L'\\'; ++pch) + ; + return pch; +} + +// The registry key for server computer descriptions cache +#define COMPUTER_DESCRIPTIONS_KEY \ + L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComputerDescriptions" + +// Get server computer description from cache +static HRESULT +SHELL_GetCachedComputerDescription( + _Out_writes_z_(cchDescMax) PWSTR pszDesc, + _In_ DWORD cchDescMax, + _In_ PCWSTR pszServerName) +{ + cchDescMax *= sizeof(WCHAR); + DWORD error = SHGetValueW(HKEY_CURRENT_USER, COMPUTER_DESCRIPTIONS_KEY, + SHELL_SkipServerSlashes(pszServerName), NULL, pszDesc, &cchDescMax); + return HRESULT_FROM_WIN32(error); +} + +// Do cache a server computer description +static VOID +SHELL_CacheComputerDescription( + _In_ PCWSTR pszServerName, + _In_ PCWSTR pszDesc) +{ + if (!pszDesc) + return; + + SIZE_T cbDesc = (wcslen(pszDesc) + 1) * sizeof(WCHAR); + SHSetValueW(HKEY_CURRENT_USER, COMPUTER_DESCRIPTIONS_KEY, + SHELL_SkipServerSlashes(pszServerName), REG_SZ, pszDesc, (DWORD)cbDesc); +} + +// Get real server computer description +static HRESULT +SHELL_GetComputerDescription( + _Out_writes_z_(cchDescMax) PWSTR pszDesc, + _In_ SIZE_T cchDescMax, + _In_ PWSTR pszServerName) +{ + PSERVER_INFO_101 bufptr; + NET_API_STATUS error = NetServerGetInfo(pszServerName, 101, (PBYTE*)&bufptr); + HRESULT hr = (error > 0) ? HRESULT_FROM_WIN32(error) : error; + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + PCWSTR comment = bufptr->sv101_comment; + if (comment && comment[0]) + StringCchCopyW(pszDesc, cchDescMax, comment); + else + hr = E_FAIL; + + NetApiBufferFree(bufptr); + return hr; +} + +// Build computer display name +static HRESULT +SHELL_BuildDisplayMachineName( + _Out_writes_z_(cchNameMax) PWSTR pszName, + _In_ DWORD cchNameMax, + _In_ PCWSTR pszServerName, + _In_ PCWSTR pszDescription) +{ + if (!pszDescription || !*pszDescription) + return E_FAIL; + + PCWSTR pszFormat = (SHRestricted(REST_ALLOWCOMMENTTOGGLE) ? L"%2 (%1)" : L"%1 (%2)"); + PCWSTR args[] = { pszDescription , SHELL_SkipServerSlashes(pszServerName) }; + return (FormatMessageW(FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_STRING, + pszFormat, 0, 0, pszName, cchNameMax, (va_list *)args) ? S_OK : E_FAIL); +} + +/************************************************************************* + * SHGetComputerDisplayNameW [SHELL32.752] + */ +EXTERN_C +HRESULT WINAPI +SHGetComputerDisplayNameW( + _In_opt_ PWSTR pszServerName, + _In_ DWORD dwFlags, + _Out_writes_z_(cchNameMax) PWSTR pszName, + _In_ DWORD cchNameMax) +{ + WCHAR szDesc[256], szCompName[MAX_COMPUTERNAME_LENGTH + 1]; + + // If no server name is specified, retrieve the local computer name + if (!pszServerName) + { + // Use computer name as server name + DWORD cchCompName = _countof(szCompName); + if (!GetComputerNameW(szCompName, &cchCompName)) + return E_FAIL; + pszServerName = szCompName; + + // Don't use the cache for the local machine + dwFlags |= SHGCDN_NOCACHE; + } + + // Get computer description from cache if necessary + HRESULT hr = E_FAIL; + if (!(dwFlags & SHGCDN_NOCACHE)) + hr = SHELL_GetCachedComputerDescription(szDesc, _countof(szDesc), pszServerName); + + // Actually retrieve the computer description if it is not in the cache + if (FAILED(hr)) + { + hr = SHELL_GetComputerDescription(szDesc, _countof(szDesc), pszServerName); + if (FAILED(hr)) + szDesc[0] = UNICODE_NULL; + + // Cache the description if necessary + if (!(dwFlags & SHGCDN_NOCACHE)) + SHELL_CacheComputerDescription(pszServerName, szDesc); + } + + // If getting the computer description failed, store the server name only + if (FAILED(hr) || !szDesc[0]) + { + if (dwFlags & SHGCDN_NOSERVERNAME) + return hr; // Bail out if no server name is requested + + StringCchCopyW(pszName, cchNameMax, SHELL_SkipServerSlashes(pszServerName)); + return S_OK; + } + + // If no server name is requested, store the description only + if (dwFlags & SHGCDN_NOSERVERNAME) + { + StringCchCopyW(pszName, cchNameMax, szDesc); + return S_OK; + } + + // Build a string like "Description (SERVERNAME)" + return SHELL_BuildDisplayMachineName(pszName, cchNameMax, pszServerName, szDesc); +} diff --git a/modules/rostests/apitests/shell32/CMakeLists.txt b/modules/rostests/apitests/shell32/CMakeLists.txt index 6f4dfdb00f3..5389915fd87 100644 --- a/modules/rostests/apitests/shell32/CMakeLists.txt +++ b/modules/rostests/apitests/shell32/CMakeLists.txt @@ -31,6 +31,7 @@ list(APPEND SOURCE SHCreateDataObject.cpp SHCreateFileDataObject.cpp SHCreateFileExtractIconW.cpp + SHGetComputerDisplayNameW.cpp SHGetUnreadMailCountW.cpp SHIsBadInterfacePtr.cpp SHParseDisplayName.cpp diff --git a/modules/rostests/apitests/shell32/SHGetComputerDisplayNameW.cpp b/modules/rostests/apitests/shell32/SHGetComputerDisplayNameW.cpp new file mode 100644 index 00000000000..e4d6f49f1b8 --- /dev/null +++ b/modules/rostests/apitests/shell32/SHGetComputerDisplayNameW.cpp @@ -0,0 +1,159 @@ +/* + * PROJECT: ReactOS API Tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Tests for SHGetComputerDisplayNameW + * COPYRIGHT: Copyright 2025 Katayama Hirofumi MZ + */ + +#include "shelltest.h" +#include +#include +#include +#include + +typedef HRESULT (WINAPI *FN_SHGetComputerDisplayNameW)(PWSTR, DWORD, PWSTR, DWORD); +typedef NET_API_STATUS (WINAPI *FN_NetServerGetInfo)(LPWSTR, DWORD, PBYTE*); +typedef NET_API_STATUS (WINAPI *FN_NetApiBufferFree)(PVOID); + +static FN_SHGetComputerDisplayNameW s_pSHGetComputerDisplayNameW = NULL; +static FN_NetServerGetInfo s_pNetServerGetInfo = NULL; +static FN_NetApiBufferFree s_pNetApiBufferFree = NULL; + +#define COMPUTER_DESCRIPTIONS_KEY \ + L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComputerDescriptions" + +static PCWSTR +SHELL_SkipServerSlashes( + _In_ PCWSTR pszPath) +{ + PCWSTR pch; + for (pch = pszPath; *pch == L'\\'; ++pch) + ; + return pch; +} + +static VOID +SHELL_CacheComputerDescription( + _In_ PCWSTR pszServerName, + _In_ PCWSTR pszDesc) +{ + if (!pszDesc) + return; + + SIZE_T cbDesc = (wcslen(pszDesc) + 1) * sizeof(WCHAR); + SHSetValueW(HKEY_CURRENT_USER, COMPUTER_DESCRIPTIONS_KEY, + SHELL_SkipServerSlashes(pszServerName), REG_SZ, pszDesc, (DWORD)cbDesc); +} + +static HRESULT +SHELL_GetCachedComputerDescription( + _Out_writes_z_(cchDescMax) PWSTR pszDesc, + _In_ DWORD cchDescMax, + _In_ PCWSTR pszServerName) +{ + cchDescMax *= sizeof(WCHAR); + DWORD error = SHGetValueW(HKEY_CURRENT_USER, COMPUTER_DESCRIPTIONS_KEY, + SHELL_SkipServerSlashes(pszServerName), NULL, pszDesc, &cchDescMax); + return HRESULT_FROM_WIN32(error); +} + +static HRESULT +SHELL_BuildDisplayMachineName( + _Out_writes_z_(cchNameMax) PWSTR pszName, + _In_ DWORD cchNameMax, + _In_ PCWSTR pszServerName, + _In_ PCWSTR pszDescription) +{ + if (!pszDescription || !*pszDescription) + return E_FAIL; + + PCWSTR pszFormat = (SHRestricted(REST_ALLOWCOMMENTTOGGLE) ? L"%2 (%1)" : L"%1 (%2)"); + PCWSTR args[] = { pszDescription , SHELL_SkipServerSlashes(pszServerName) }; + return (FormatMessageW(FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_STRING, + pszFormat, 0, 0, pszName, cchNameMax, (va_list *)args) ? S_OK : E_FAIL); +} + +static VOID +TEST_SHGetComputerDisplayNameW(VOID) +{ + WCHAR szCompName[MAX_COMPUTERNAME_LENGTH + 1], szDesc[256], szDisplayName[MAX_PATH]; + WCHAR szName[MAX_PATH], szServerName[] = L"DummyServerName"; + + DWORD cchCompName = _countof(szCompName); + BOOL ret = GetComputerNameW(szCompName, &cchCompName); + ok_int(ret, TRUE); + trace("%s\n", wine_dbgstr_w(szCompName)); + + SHELL_CacheComputerDescription(szServerName, L"DummyDescription"); + + HRESULT hr = SHELL_GetCachedComputerDescription(szDesc, _countof(szDesc), szServerName); + if (FAILED(hr)) + szDesc[0] = UNICODE_NULL; + trace("%s\n", wine_dbgstr_w(szDesc)); + + StringCchCopyW(szDisplayName, _countof(szDisplayName), L"@"); + hr = s_pSHGetComputerDisplayNameW(NULL, SHGCDN_NOCACHE, szDisplayName, _countof(szDisplayName)); + ok_hex(hr, S_OK); + trace("%s\n", wine_dbgstr_w(szDisplayName)); + ok_wstr(szDisplayName, szCompName); + + StringCchCopyW(szDisplayName, _countof(szDisplayName), L"@"); + hr = s_pSHGetComputerDisplayNameW(szServerName, 0, szDisplayName, _countof(szDisplayName)); + ok_hex(hr, S_OK); + trace("%s\n", wine_dbgstr_w(szServerName)); + ok_wstr(szServerName, L"DummyServerName"); + + hr = SHELL_BuildDisplayMachineName(szName, _countof(szName), szServerName, szDesc); + ok_hex(hr, S_OK); + + trace("%s\n", wine_dbgstr_w(szDisplayName)); + trace("%s\n", wine_dbgstr_w(szName)); + ok_wstr(szDisplayName, szName); + + // Delete registry value + HKEY hKey; + LSTATUS error = RegOpenKeyExW(HKEY_CURRENT_USER, COMPUTER_DESCRIPTIONS_KEY, 0, KEY_WRITE, &hKey); + if (error == ERROR_SUCCESS) + { + RegDeleteValueW(hKey, L"DummyServerName"); + RegCloseKey(hKey); + } +} + +START_TEST(SHGetComputerDisplayNameW) +{ + if (IsWindowsVistaOrGreater()) + { + skip("Tests on Vista+ will cause exception\n"); + return; + } + + HINSTANCE hShell32 = GetModuleHandleW(L"shell32.dll"); + s_pSHGetComputerDisplayNameW = + (FN_SHGetComputerDisplayNameW)GetProcAddress(hShell32, MAKEINTRESOURCEA(752)); + if (!s_pSHGetComputerDisplayNameW) + { + skip("SHGetComputerDisplayNameW not found\n"); + return; + } + + HINSTANCE hNetApi32 = LoadLibraryW(L"netapi32.dll"); + if (!hNetApi32) + { + skip("netapi32.dll not found\n"); + return; + } + + s_pNetServerGetInfo = (FN_NetServerGetInfo)GetProcAddress(hNetApi32, "NetServerGetInfo"); + s_pNetApiBufferFree = (FN_NetApiBufferFree)GetProcAddress(hNetApi32, "NetApiBufferFree"); + if (!s_pNetServerGetInfo || !s_pNetApiBufferFree) + { + skip("NetServerGetInfo or NetApiBufferFree not found\n"); + FreeLibrary(hNetApi32); + return; + } + + TEST_SHGetComputerDisplayNameW(); + + FreeLibrary(hNetApi32); +} diff --git a/modules/rostests/apitests/shell32/testlist.c b/modules/rostests/apitests/shell32/testlist.c index 91078fb8cc1..1c964c36be4 100644 --- a/modules/rostests/apitests/shell32/testlist.c +++ b/modules/rostests/apitests/shell32/testlist.c @@ -43,6 +43,7 @@ extern void func_ShellExecuteW(void); extern void func_ShellHook(void); extern void func_ShellState(void); extern void func_SHGetAttributesFromDataObject(void); +extern void func_SHGetComputerDisplayNameW(void); extern void func_SHGetFileInfo(void); extern void func_SHGetUnreadMailCountW(void); extern void func_SHGetUserDisplayName(void); @@ -97,6 +98,7 @@ const struct test winetest_testlist[] = { "ShellHook", func_ShellHook }, { "ShellState", func_ShellState }, { "SHGetAttributesFromDataObject", func_SHGetAttributesFromDataObject }, + { "SHGetComputerDisplayNameW", func_SHGetComputerDisplayNameW }, { "SHGetFileInfo", func_SHGetFileInfo }, { "SHGetUnreadMailCountW", func_SHGetUnreadMailCountW }, { "SHGetUserDisplayName", func_SHGetUserDisplayName }, diff --git a/sdk/include/reactos/undocshell.h b/sdk/include/reactos/undocshell.h index 574f4851e1a..187a74f4d44 100644 --- a/sdk/include/reactos/undocshell.h +++ b/sdk/include/reactos/undocshell.h @@ -966,6 +966,17 @@ CopyStreamUI( _Inout_opt_ IProgressDialog *pProgress, _In_opt_ DWORDLONG dwlSize); +// Flags for SHGetComputerDisplayNameW +#define SHGCDN_NOCACHE 0x1 +#define SHGCDN_NOSERVERNAME 0x10000 + +HRESULT WINAPI +SHGetComputerDisplayNameW( + _In_opt_ LPWSTR pszServerName, + _In_ DWORD dwFlags, + _Out_writes_z_(cchNameMax) LPWSTR pszName, + _In_ DWORD cchNameMax); + /***************************************************************************** * INVALID_FILETITLE_CHARACTERS */