https://git.reactos.org/?p=reactos.git;a=commitdiff;h=6c55a3aa2ad4afb06a931ec648055abf63e42651
commit 6c55a3aa2ad4afb06a931ec648055abf63e42651 Author: Katayama Hirofumi MZ <katayama.hirofumi...@gmail.com> AuthorDate: Thu Sep 21 09:14:40 2023 +0900 Commit: GitHub <nore...@github.com> CommitDate: Thu Sep 21 09:14:40 2023 +0900 [SHELL32][SHELL32_APITEST][SDK] Implement Int64ToString (#5706) - Implement Int64ToString and LargeIntegerToString functions. - Add Int64ToString and LargeIntegerToString prototypes to <shellundoc.h>. - Add Int64ToString testcase. - I found a bug in GetNumberFormat.LeadingZero. http://undoc.airesoft.co.uk/shell32.dll/Int64ToString.php http://undoc.airesoft.co.uk/shell32.dll/LargeIntegerToString.php --- dll/win32/shell32/stubs.cpp | 32 ---- dll/win32/shell32/utils.cpp | 156 +++++++++++++++++ modules/rostests/apitests/shell32/CMakeLists.txt | 1 + .../rostests/apitests/shell32/Int64ToString.cpp | 194 +++++++++++++++++++++ modules/rostests/apitests/shell32/testlist.c | 2 + sdk/include/reactos/undocshell.h | 26 +++ 6 files changed, 379 insertions(+), 32 deletions(-) diff --git a/dll/win32/shell32/stubs.cpp b/dll/win32/shell32/stubs.cpp index 4c019e9b031..a49a506928e 100644 --- a/dll/win32/shell32/stubs.cpp +++ b/dll/win32/shell32/stubs.cpp @@ -949,38 +949,6 @@ Printers_GetPidl(LPCITEMIDLIST pidl, LPCWSTR lpName, DWORD dwUnknown1, DWORD dwU return NULL; } -/* - * Unimplemented - */ -EXTERN_C INT -WINAPI -Int64ToString(LONGLONG llInt64, - LPWSTR lpOut, - UINT uSize, - BOOL bUseFormat, - NUMBERFMT *pNumberFormat, - DWORD dwNumberFlags) -{ - FIXME("Int64ToString() stub\n"); - return 0; -} - -/* - * Unimplemented - */ -EXTERN_C INT -WINAPI -LargeIntegerToString(LARGE_INTEGER *pLargeInt, - LPWSTR lpOut, - UINT uSize, - BOOL bUseFormat, - NUMBERFMT *pNumberFormat, - DWORD dwNumberFlags) -{ - FIXME("LargeIntegerToString() stub\n"); - return 0; -} - /* * Unimplemented */ diff --git a/dll/win32/shell32/utils.cpp b/dll/win32/shell32/utils.cpp index c21e679a07b..4140fcaa274 100644 --- a/dll/win32/shell32/utils.cpp +++ b/dll/win32/shell32/utils.cpp @@ -99,3 +99,159 @@ SHFindComputer(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlSavedSearch) return SUCCEEDED(hr); } + +static HRESULT +Int64ToStr( + _In_ LONGLONG llValue, + _Out_writes_z_(cchValue) LPWSTR pszValue, + _In_ UINT cchValue) +{ + WCHAR szBuff[40]; + UINT ich = 0, ichValue; +#if (WINVER >= _WIN32_WINNT_VISTA) + BOOL bMinus = (llValue < 0); + + if (bMinus) + llValue = -llValue; +#endif + + if (cchValue <= 0) + return E_FAIL; + + do + { + szBuff[ich++] = (WCHAR)(L'0' + (llValue % 10)); + llValue /= 10; + } while (llValue != 0 && ich < _countof(szBuff) - 1); + +#if (WINVER >= _WIN32_WINNT_VISTA) + if (bMinus && ich < _countof(szBuff)) + szBuff[ich++] = '-'; +#endif + + for (ichValue = 0; ich > 0 && ichValue < cchValue; ++ichValue) + { + --ich; + pszValue[ichValue] = szBuff[ich]; + } + + if (ichValue >= cchValue) + { + pszValue[cchValue - 1] = UNICODE_NULL; + return E_FAIL; + } + + pszValue[ichValue] = UNICODE_NULL; + return S_OK; +} + +static VOID +Int64GetNumFormat( + _Out_ NUMBERFMTW *pDest, + _In_opt_ const NUMBERFMTW *pSrc, + _In_ DWORD dwNumberFlags, + _Out_writes_z_(cchDecimal) LPWSTR pszDecimal, + _In_ INT cchDecimal, + _Out_writes_z_(cchThousand) LPWSTR pszThousand, + _In_ INT cchThousand) +{ + WCHAR szBuff[20]; + + if (pSrc) + *pDest = *pSrc; + else + dwNumberFlags = 0; + + if (!(dwNumberFlags & FMT_USE_NUMDIGITS)) + { + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, szBuff, _countof(szBuff)); + pDest->NumDigits = StrToIntW(szBuff); + } + + if (!(dwNumberFlags & FMT_USE_LEADZERO)) + { + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, szBuff, _countof(szBuff)); + pDest->LeadingZero = StrToIntW(szBuff); + } + + if (!(dwNumberFlags & FMT_USE_GROUPING)) + { + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szBuff, _countof(szBuff)); + pDest->Grouping = StrToIntW(szBuff); + } + + if (!(dwNumberFlags & FMT_USE_DECIMAL)) + { + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, pszDecimal, cchDecimal); + pDest->lpDecimalSep = pszDecimal; + } + + if (!(dwNumberFlags & FMT_USE_THOUSAND)) + { + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, pszThousand, cchThousand); + pDest->lpThousandSep = pszThousand; + } + + if (!(dwNumberFlags & FMT_USE_NEGNUMBER)) + { + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szBuff, _countof(szBuff)); + pDest->NegativeOrder = StrToIntW(szBuff); + } +} + +/************************************************************************* + * Int64ToString [SHELL32.209] + * + * @see http://undoc.airesoft.co.uk/shell32.dll/Int64ToString.php + */ +EXTERN_C +INT WINAPI +Int64ToString( + _In_ LONGLONG llValue, + _Out_writes_z_(cchOut) LPWSTR pszOut, + _In_ UINT cchOut, + _In_ BOOL bUseFormat, + _In_opt_ const NUMBERFMTW *pNumberFormat, + _In_ DWORD dwNumberFlags) +{ + INT ret; + NUMBERFMTW NumFormat; + WCHAR szValue[80], szDecimalSep[6], szThousandSep[6]; + + Int64ToStr(llValue, szValue, _countof(szValue)); + + if (bUseFormat) + { + Int64GetNumFormat(&NumFormat, pNumberFormat, dwNumberFlags, + szDecimalSep, _countof(szDecimalSep), + szThousandSep, _countof(szThousandSep)); + ret = GetNumberFormatW(LOCALE_USER_DEFAULT, 0, szValue, &NumFormat, pszOut, cchOut); + if (ret) + --ret; + return ret; + } + + if (FAILED(StringCchCopyW(pszOut, cchOut, szValue))) + return 0; + + return lstrlenW(pszOut); +} + +/************************************************************************* + * LargeIntegerToString [SHELL32.210] + * + * @see http://undoc.airesoft.co.uk/shell32.dll/LargeIntegerToString.php + */ +EXTERN_C +INT WINAPI +LargeIntegerToString( + _In_ const LARGE_INTEGER *pLargeInt, + _Out_writes_z_(cchOut) LPWSTR pszOut, + _In_ UINT cchOut, + _In_ BOOL bUseFormat, + _In_opt_ const NUMBERFMTW *pNumberFormat, + _In_ DWORD dwNumberFlags) +{ + return Int64ToString(pLargeInt->QuadPart, pszOut, cchOut, bUseFormat, + pNumberFormat, dwNumberFlags); +} diff --git a/modules/rostests/apitests/shell32/CMakeLists.txt b/modules/rostests/apitests/shell32/CMakeLists.txt index 602e25ad199..07ba4a96ca6 100644 --- a/modules/rostests/apitests/shell32/CMakeLists.txt +++ b/modules/rostests/apitests/shell32/CMakeLists.txt @@ -16,6 +16,7 @@ list(APPEND SOURCE ExtractIconEx.cpp FindExecutable.cpp GetDisplayNameOf.cpp + Int64ToString.cpp IShellFolderViewCB.cpp OpenAs_RunDLL.cpp PathResolve.cpp diff --git a/modules/rostests/apitests/shell32/Int64ToString.cpp b/modules/rostests/apitests/shell32/Int64ToString.cpp new file mode 100644 index 00000000000..19ff028db6f --- /dev/null +++ b/modules/rostests/apitests/shell32/Int64ToString.cpp @@ -0,0 +1,194 @@ +/* + * PROJECT: ReactOS API Tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Tests for Int64ToString + * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi...@gmail.com> + */ + +#include "shelltest.h" +#include <undocshell.h> +#include <versionhelpers.h> + +typedef struct tagTEST_RETURN +{ + INT ret; + LPCWSTR text; +} TEST_RETURN, *PTEST_RETURN; + +typedef struct tagTEST_ENTRY +{ + INT lineno; + LONGLONG value; + + UINT NumDigits; + UINT LeadingZero; + UINT Grouping; + LPCWSTR pszDecimalSep; + LPCWSTR pszThousandSep; + UINT NegativeOrder; + + TEST_RETURN NonVista; + TEST_RETURN Vista; +} TEST_ENTRY, *PTEST_ENTRY; + +#define DATA0 2, FALSE, 3, L".", L",", 0 +#define DATA1 3, TRUE, 3, L"<>", L":", 1 + +static const TEST_ENTRY s_Entries[] = +{ + { __LINE__, 0, DATA0, { 3, L".00" }, { 3, L".00" } }, + { __LINE__, 0, DATA1, { 6, L"0<>000" }, { 6, L"0<>000" } }, + { __LINE__, 1, DATA0, { 4, L"1.00" }, { 4, L"1.00" } }, + { __LINE__, 1, DATA1, { 6, L"1<>000" }, { 6, L"1<>000" } }, + { __LINE__, -999, DATA0, { 0, L"#####" }, { 8, L"(999.00)" } }, + { __LINE__, -999, DATA1, { 0, L"#####" }, { 9, L"-999<>000" } }, + { __LINE__, 100000, DATA0, { 10, L"100,000.00" }, { 10, L"100,000.00" } }, + { __LINE__, 100000, DATA1, { 12, L"100:000<>000" }, { 12, L"100:000<>000" } }, + { __LINE__, 0xE8D4A51000LL, DATA0, { 20, L"1,000,000,000,000.00" }, { 20, L"1,000,000,000,000.00" } }, + { __LINE__, 0xE8D4A51000LL, DATA1, { 22, L"1:000:000:000:000<>000" }, { 22, L"1:000:000:000:000<>000" } }, + { __LINE__, 0x7FFFFFFFFFFFFFFFLL, DATA0, { 28, L"9,223,372,036,854,775,807.00" }, { 28, L"9,223,372,036,854,775,807.00" } }, + { __LINE__, 0x7FFFFFFFFFFFFFFFLL, DATA1, { 30, L"9:223:372:036:854:775:807<>000" }, { 30, L"9:223:372:036:854:775:807<>000" } }, +}; +static const SIZE_T s_cEntries = _countof(s_Entries); + +static void DoTestEntry(BOOL bVista, const TEST_ENTRY *pEntry) +{ + INT lineno = pEntry->lineno; + + WCHAR szDecimalSep[10], szThousandSep[10]; + lstrcpynW(szDecimalSep, pEntry->pszDecimalSep, _countof(szDecimalSep)); + lstrcpynW(szThousandSep, pEntry->pszThousandSep, _countof(szThousandSep)); + + NUMBERFMTW format = + { + pEntry->NumDigits, pEntry->LeadingZero, pEntry->Grouping, + szDecimalSep, szThousandSep, pEntry->NegativeOrder + }; + + WCHAR szBuff[64]; + lstrcpynW(szBuff, L"#####", _countof(szBuff)); + + INT ret = Int64ToString(pEntry->value, szBuff, _countof(szBuff), TRUE, &format, -1); + if (bVista) + { + ok(pEntry->Vista.ret == ret, "Line %d: %d vs %d\n", lineno, pEntry->Vista.ret, ret); + ok(lstrcmpW(pEntry->Vista.text, szBuff) == 0, "Line %d: %ls vs %ls\n", + lineno, pEntry->Vista.text, szBuff); + } + else + { + ok(pEntry->NonVista.ret == ret, "Line %d: %d vs %d\n", lineno, pEntry->NonVista.ret, ret); + ok(lstrcmpW(pEntry->NonVista.text, szBuff) == 0, "Line %d: %ls vs %ls\n", + lineno, pEntry->NonVista.text, szBuff); + } + + lstrcpynW(szBuff, L"#####", _countof(szBuff)); + + LARGE_INTEGER LInt; + LInt.QuadPart = pEntry->value; + ret = LargeIntegerToString(&LInt, szBuff, _countof(szBuff), TRUE, &format, -1); + if (bVista) + { + ok(pEntry->Vista.ret == ret, "Line %d: %d vs %d\n", lineno, pEntry->Vista.ret, ret); + ok(lstrcmpW(pEntry->Vista.text, szBuff) == 0, "Line %d: %ls vs %ls\n", + lineno, pEntry->Vista.text, szBuff); + } + else + { + ok(pEntry->NonVista.ret == ret, "Line %d: %d vs %d\n", lineno, pEntry->NonVista.ret, ret); + ok(lstrcmpW(pEntry->NonVista.text, szBuff) == 0, "Line %d: %ls vs %ls\n", + lineno, pEntry->NonVista.text, szBuff); + } +} + +static void Test_EntryTest(BOOL bVista) +{ + for (SIZE_T i = 0; i < s_cEntries; ++i) + { + DoTestEntry(bVista, &s_Entries[i]); + } +} + +static void Test_Int64ToString(BOOL bVista) +{ + WCHAR szBuff[64]; + INT ret; + + ret = Int64ToString(0, szBuff, _countof(szBuff), FALSE, NULL, 0); + ok_int(ret, 1); + ok_wstr(szBuff, L"0"); + + ret = Int64ToString(1, szBuff, _countof(szBuff), FALSE, NULL, 0); + ok_int(ret, 1); + ok_wstr(szBuff, L"1"); + + ret = Int64ToString(-9999, szBuff, _countof(szBuff), FALSE, NULL, 0); + if (bVista) + { + ok_int(ret, 5); + ok_wstr(szBuff, L"-9999"); + } + else + { + ok_int(ret, 4); + ok_wstr(szBuff, L"''''"); + } + + ret = Int64ToString(10000, szBuff, _countof(szBuff), FALSE, NULL, 0); + ok_int(ret, 5); + ok_wstr(szBuff, L"10000"); + + ret = Int64ToString(0xE8D4A51000LL, szBuff, _countof(szBuff), FALSE, NULL, 0); + ok_int(ret, 13); + ok_wstr(szBuff, L"1000000000000"); +} + +static void Test_LargeIntegerToString(BOOL bVista) +{ + LARGE_INTEGER LInt; + WCHAR szBuff[64]; + INT ret; + + LInt.QuadPart = 0; + ret = LargeIntegerToString(&LInt, szBuff, _countof(szBuff), FALSE, NULL, 0); + ok_int(ret, 1); + ok_wstr(szBuff, L"0"); + + LInt.QuadPart = 1; + ret = LargeIntegerToString(&LInt, szBuff, _countof(szBuff), FALSE, NULL, 0); + ok_int(ret, 1); + ok_wstr(szBuff, L"1"); + + LInt.QuadPart = -9999; + ret = LargeIntegerToString(&LInt, szBuff, _countof(szBuff), FALSE, NULL, 0); + if (bVista) + { + ok_int(ret, 5); + ok_wstr(szBuff, L"-9999"); + } + else + { + ok_int(ret, 4); + ok_wstr(szBuff, L"''''"); + } + + LInt.QuadPart = 10000; + ret = LargeIntegerToString(&LInt, szBuff, _countof(szBuff), FALSE, NULL, 0); + ok_int(ret, 5); + ok_wstr(szBuff, L"10000"); + + LInt.QuadPart = 0xE8D4A51000LL; + ret = LargeIntegerToString(&LInt, szBuff, _countof(szBuff), FALSE, NULL, 0); + ok_int(ret, 13); + ok_wstr(szBuff, L"1000000000000"); +} + +START_TEST(Int64ToString) +{ + BOOL bVista = IsWindowsVistaOrGreater(); + trace("bVista: %d\n", bVista); + + Test_EntryTest(bVista); + Test_Int64ToString(bVista); + Test_LargeIntegerToString(bVista); +} diff --git a/modules/rostests/apitests/shell32/testlist.c b/modules/rostests/apitests/shell32/testlist.c index 2d364f19770..424e878d3b9 100644 --- a/modules/rostests/apitests/shell32/testlist.c +++ b/modules/rostests/apitests/shell32/testlist.c @@ -17,6 +17,7 @@ extern void func_DragDrop(void); extern void func_ExtractIconEx(void); extern void func_FindExecutable(void); extern void func_GetDisplayNameOf(void); +extern void func_Int64ToString(void); extern void func_IShellFolderViewCB(void); extern void func_menu(void); extern void func_OpenAs_RunDLL(void); @@ -52,6 +53,7 @@ const struct test winetest_testlist[] = { "ExtractIconEx", func_ExtractIconEx }, { "FindExecutable", func_FindExecutable }, { "GetDisplayNameOf", func_GetDisplayNameOf }, + { "Int64ToString", func_Int64ToString }, { "IShellFolderViewCB", func_IShellFolderViewCB }, { "menu", func_menu }, { "OpenAs_RunDLL", func_OpenAs_RunDLL }, diff --git a/sdk/include/reactos/undocshell.h b/sdk/include/reactos/undocshell.h index 370fef7377e..93fc4201d8c 100644 --- a/sdk/include/reactos/undocshell.h +++ b/sdk/include/reactos/undocshell.h @@ -658,6 +658,32 @@ BOOL WINAPI GUIDFromStringW( LPSTR WINAPI SheRemoveQuotesA(LPSTR psz); LPWSTR WINAPI SheRemoveQuotesW(LPWSTR psz); +/* Flags for Int64ToString and LargeIntegerToString */ +#define FMT_USE_NUMDIGITS 0x01 +#define FMT_USE_LEADZERO 0x02 +#define FMT_USE_GROUPING 0x04 +#define FMT_USE_DECIMAL 0x08 +#define FMT_USE_THOUSAND 0x10 +#define FMT_USE_NEGNUMBER 0x20 + +INT WINAPI +Int64ToString( + _In_ LONGLONG llValue, + _Out_writes_z_(cchOut) LPWSTR pszOut, + _In_ UINT cchOut, + _In_ BOOL bUseFormat, + _In_opt_ const NUMBERFMTW *pNumberFormat, + _In_ DWORD dwNumberFlags); + +INT WINAPI +LargeIntegerToString( + _In_ const LARGE_INTEGER *pLargeInt, + _Out_writes_z_(cchOut) LPWSTR pszOut, + _In_ UINT cchOut, + _In_ BOOL bUseFormat, + _In_opt_ const NUMBERFMTW *pNumberFormat, + _In_ DWORD dwNumberFlags); + /***************************************************************************** * Shell32 resources */