From 84607161b4701bc7eb89e443eff6c6f484247184 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Mon, 20 Jan 2025 09:51:27 +0900 Subject: [PATCH] [SHELL32][SHELL32_APITEST][SDK] Implement SHGetUnreadMailCountW (#7622) Implementing missing features... JIRA issue: CORE-19278 - Modify shell32.spec. - Move function definition from stubs.cpp into utils.cpp. - Add SHELL_ReadSingleUnreadMailCount helper function. - Add prototype to . --- dll/win32/shell32/shell32.spec | 2 +- dll/win32/shell32/stubs.cpp | 16 --- dll/win32/shell32/utils.cpp | 117 ++++++++++++++++++ .../rostests/apitests/shell32/CMakeLists.txt | 1 + .../shell32/SHGetUnreadMailCountW.cpp | 81 ++++++++++++ modules/rostests/apitests/shell32/testlist.c | 2 + sdk/include/psdk/shellapi.h | 9 ++ 7 files changed, 211 insertions(+), 17 deletions(-) create mode 100644 modules/rostests/apitests/shell32/SHGetUnreadMailCountW.cpp diff --git a/dll/win32/shell32/shell32.spec b/dll/win32/shell32/shell32.spec index 9a4f8b07c43..86a376f49e6 100644 --- a/dll/win32/shell32/shell32.spec +++ b/dll/win32/shell32/shell32.spec @@ -317,7 +317,7 @@ 317 stdcall SHGetSpecialFolderLocation(long long ptr) 318 stdcall SHGetSpecialFolderPathA(long ptr long long) 319 stdcall SHGetSpecialFolderPathW(long ptr long long) -320 stdcall SHGetUnreadMailCountW (long wstr long ptr wstr long) +320 stdcall SHGetUnreadMailCountW(ptr wstr ptr ptr ptr long) 321 stdcall SHHelpShortcuts_RunDLL(long long long long) SHHelpShortcuts_RunDLLA 322 stdcall SHHelpShortcuts_RunDLLA(long long long long) 323 stdcall SHHelpShortcuts_RunDLLW(long long long long) diff --git a/dll/win32/shell32/stubs.cpp b/dll/win32/shell32/stubs.cpp index 5698b7dc0c8..f9ef73a2d7e 100644 --- a/dll/win32/shell32/stubs.cpp +++ b/dll/win32/shell32/stubs.cpp @@ -14,22 +14,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell); -/* - * Unimplemented - */ -EXTERN_C HRESULT -WINAPI -SHGetUnreadMailCountW(HKEY hKeyUser, - LPCWSTR pszMailAddress, - DWORD *pdwCount, - FILETIME *pFileTime, - LPWSTR pszShellExecuteCommand, - int cchShellExecuteCommand) -{ - FIXME("SHGetUnreadMailCountW() stub\n"); - return E_FAIL; -} - /* * Unimplemented */ diff --git a/dll/win32/shell32/utils.cpp b/dll/win32/shell32/utils.cpp index 831d52d3c1e..4e0949eb2bb 100644 --- a/dll/win32/shell32/utils.cpp +++ b/dll/win32/shell32/utils.cpp @@ -878,6 +878,123 @@ SHCreatePropertyBag(_In_ REFIID riid, _Out_ void **ppvObj) return SHCreatePropertyBagOnMemory(STGM_READWRITE, riid, ppvObj); } +// The helper function for SHGetUnreadMailCountW +static DWORD +SHELL_ReadSingleUnreadMailCount( + _In_ HKEY hKey, + _Out_opt_ PDWORD pdwCount, + _Out_opt_ PFILETIME pFileTime, + _Out_writes_opt_(cchShellExecuteCommand) LPWSTR pszShellExecuteCommand, + _In_ INT cchShellExecuteCommand) +{ + DWORD dwType, dwCount, cbSize = sizeof(dwCount); + DWORD error = SHQueryValueExW(hKey, L"MessageCount", 0, &dwType, &dwCount, &cbSize); + if (error) + return error; + if (pdwCount && dwType == REG_DWORD) + *pdwCount = dwCount; + + FILETIME FileTime; + cbSize = sizeof(FileTime); + error = SHQueryValueExW(hKey, L"TimeStamp", 0, &dwType, &FileTime, &cbSize); + if (error) + return error; + if (pFileTime && dwType == REG_BINARY) + *pFileTime = FileTime; + + WCHAR szName[2 * MAX_PATH]; + cbSize = sizeof(szName); + error = SHQueryValueExW(hKey, L"Application", 0, &dwType, szName, &cbSize); + if (error) + return error; + + if (pszShellExecuteCommand && dwType == REG_SZ && + FAILED(StringCchCopyW(pszShellExecuteCommand, cchShellExecuteCommand, szName))) + { + return ERROR_INSUFFICIENT_BUFFER; + } + + return ERROR_SUCCESS; +} + +/************************************************************************* + * SHGetUnreadMailCountW [SHELL32.320] + * + * @see https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetunreadmailcountw + */ +EXTERN_C +HRESULT WINAPI +SHGetUnreadMailCountW( + _In_opt_ HKEY hKeyUser, + _In_opt_ LPCWSTR pszMailAddress, + _Out_opt_ PDWORD pdwCount, + _Inout_opt_ PFILETIME pFileTime, + _Out_writes_opt_(cchShellExecuteCommand) LPWSTR pszShellExecuteCommand, + _In_ INT cchShellExecuteCommand) +{ + LSTATUS error; + HKEY hKey; + + if (!hKeyUser) + hKeyUser = HKEY_CURRENT_USER; + + if (pszMailAddress) + { + CStringW strKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail"; + strKey += L'\\'; + strKey += pszMailAddress; + + error = RegOpenKeyExW(hKeyUser, strKey, 0, KEY_QUERY_VALUE, &hKey); + if (error) + return HRESULT_FROM_WIN32(error); + + error = SHELL_ReadSingleUnreadMailCount(hKey, pdwCount, pFileTime, + pszShellExecuteCommand, cchShellExecuteCommand); + } + else + { + if (pszShellExecuteCommand || cchShellExecuteCommand) + return E_INVALIDARG; + + *pdwCount = 0; + + error = RegOpenKeyExW(hKeyUser, L"Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail", + 0, KEY_ENUMERATE_SUB_KEYS, &hKey); + if (error) + return HRESULT_FROM_WIN32(error); + + for (DWORD dwIndex = 0; !error; ++dwIndex) + { + WCHAR Name[2 * MAX_PATH]; + DWORD cchName = _countof(Name); + FILETIME LastWritten; + error = RegEnumKeyExW(hKey, dwIndex, Name, &cchName, NULL, NULL, NULL, &LastWritten); + if (error) + break; + + HKEY hSubKey; + error = RegOpenKeyExW(hKey, Name, 0, KEY_QUERY_VALUE, &hSubKey); + if (error) + break; + + FILETIME FileTime; + DWORD dwCount; + error = SHELL_ReadSingleUnreadMailCount(hSubKey, &dwCount, &FileTime, NULL, 0); + if (!error && (!pFileTime || CompareFileTime(&FileTime, pFileTime) >= 0)) + *pdwCount += dwCount; + + RegCloseKey(hSubKey); + } + + if (error == ERROR_NO_MORE_ITEMS) + error = ERROR_SUCCESS; + } + + RegCloseKey(hKey); + + return error ? HRESULT_FROM_WIN32(error) : S_OK; +} + /************************************************************************* * SHSetUnreadMailCountW [SHELL32.336] * diff --git a/modules/rostests/apitests/shell32/CMakeLists.txt b/modules/rostests/apitests/shell32/CMakeLists.txt index cb6c11bcf8e..3c9a2e73dcf 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 + SHGetUnreadMailCountW.cpp SHParseDisplayName.cpp SHRestricted.cpp SHShouldShowWizards.cpp diff --git a/modules/rostests/apitests/shell32/SHGetUnreadMailCountW.cpp b/modules/rostests/apitests/shell32/SHGetUnreadMailCountW.cpp new file mode 100644 index 00000000000..a669cbe1269 --- /dev/null +++ b/modules/rostests/apitests/shell32/SHGetUnreadMailCountW.cpp @@ -0,0 +1,81 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Test for SHGetUnreadMailCountW + * COPYRIGHT: Copyright 2025 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + */ + +#include "shelltest.h" + +static VOID SetUnreadMailInfo(PDWORD pdwDisposition) +{ + HKEY hKey; + LSTATUS error = RegCreateKeyExW( + HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail\\example.com", + 0, NULL, 0, KEY_WRITE, NULL, &hKey, pdwDisposition); + ok_long(error, ERROR_SUCCESS); + + DWORD dwCount = 1; + error = SHSetValueW(hKey, NULL, L"MessageCount", REG_DWORD, &dwCount, sizeof(dwCount)); + ok_long(error, ERROR_SUCCESS); + + FILETIME FileTime; + GetSystemTimeAsFileTime(&FileTime); + error = SHSetValueW(hKey, NULL, L"TimeStamp", REG_BINARY, &FileTime, sizeof(FileTime)); + ok_long(error, ERROR_SUCCESS); + + LPCWSTR pszApp = L"MyMailerApp"; + DWORD cbValue = (lstrlenW(pszApp) + 1) * sizeof(WCHAR); + error = SHSetValueW(hKey, NULL, L"Application", REG_SZ, pszApp, cbValue); + ok_long(error, ERROR_SUCCESS); + + RegCloseKey(hKey); +} + +START_TEST(SHGetUnreadMailCountW) +{ + HRESULT hr; + + DWORD dwDisposition; + SetUnreadMailInfo(&dwDisposition); + + hr = SHGetUnreadMailCountW(NULL, L"example.com", NULL, NULL, NULL, 0); + ok_hex(hr, S_OK); + + FILETIME FileTime; + ZeroMemory(&FileTime, sizeof(FileTime)); + hr = SHGetUnreadMailCountW(HKEY_CURRENT_USER, L"example.com", NULL, &FileTime, NULL, 0); + ok_hex(hr, S_OK); + ok(FileTime.dwHighDateTime != 0, "FileTime.dwHighDateTime was zero\n"); + + DWORD dwCount = 0; + ZeroMemory(&FileTime, sizeof(FileTime)); + hr = SHGetUnreadMailCountW(NULL, NULL, &dwCount, &FileTime, NULL, 0); + ok_hex(hr, S_OK); + ok_long(dwCount, 1); + ok_long(FileTime.dwHighDateTime, 0); + + dwCount = 0; + hr = SHGetUnreadMailCountW(NULL, L"example.com", &dwCount, NULL, NULL, 0); + ok_hex(hr, S_OK); + ok_long(dwCount, 1); + + hr = SHGetUnreadMailCountW(NULL, NULL, &dwCount, NULL, NULL, 0); + ok_hex(hr, S_OK); + + WCHAR szAppName[MAX_PATH]; + dwCount = 0; + hr = SHGetUnreadMailCountW(NULL, NULL, &dwCount, NULL, szAppName, _countof(szAppName)); + ok_hex(hr, E_INVALIDARG); + ok_long(dwCount, 0); + + hr = SHGetUnreadMailCountW(NULL, L"example.com", NULL, NULL, szAppName, _countof(szAppName)); + ok_hex(hr, S_OK); + ok_wstr(szAppName, L"MyMailerApp"); + + if (dwDisposition == REG_CREATED_NEW_KEY) + { + RegDeleteKeyW(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail\\example.com"); + } +} diff --git a/modules/rostests/apitests/shell32/testlist.c b/modules/rostests/apitests/shell32/testlist.c index fc879889ebe..3c1cbd7e045 100644 --- a/modules/rostests/apitests/shell32/testlist.c +++ b/modules/rostests/apitests/shell32/testlist.c @@ -44,6 +44,7 @@ extern void func_ShellHook(void); extern void func_ShellState(void); extern void func_SHGetAttributesFromDataObject(void); extern void func_SHGetFileInfo(void); +extern void func_SHGetUnreadMailCountW(void); extern void func_SHGetUserDisplayName(void); extern void func_SHLimitInputEdit(void); extern void func_SHParseDisplayName(void); @@ -96,6 +97,7 @@ const struct test winetest_testlist[] = { "ShellState", func_ShellState }, { "SHGetAttributesFromDataObject", func_SHGetAttributesFromDataObject }, { "SHGetFileInfo", func_SHGetFileInfo }, + { "SHGetUnreadMailCountW", func_SHGetUnreadMailCountW }, { "SHGetUserDisplayName", func_SHGetUserDisplayName }, { "SHLimitInputEdit", func_SHLimitInputEdit }, { "SHParseDisplayName", func_SHParseDisplayName }, diff --git a/sdk/include/psdk/shellapi.h b/sdk/include/psdk/shellapi.h index b707d6d41df..f343928aa62 100644 --- a/sdk/include/psdk/shellapi.h +++ b/sdk/include/psdk/shellapi.h @@ -659,6 +659,15 @@ SHEnumerateUnreadMailAccountsW( _Out_writes_(cchMailAddress) PWSTR pszMailAddress, _In_ INT cchMailAddress); +HRESULT WINAPI +SHGetUnreadMailCountW( + _In_opt_ HKEY hKeyUser, + _In_opt_ LPCWSTR pszMailAddress, + _Out_opt_ PDWORD pdwCount, + _Inout_opt_ PFILETIME pFileTime, + _Out_writes_opt_(cchShellExecuteCommand) LPWSTR pszShellExecuteCommand, + _In_ INT cchShellExecuteCommand); + #ifdef UNICODE #define NOTIFYICONDATA_V1_SIZE NOTIFYICONDATAW_V1_SIZE #define NOTIFYICONDATA_V2_SIZE NOTIFYICONDATAW_V2_SIZE