[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
This commit is contained in:
Katayama Hirofumi MZ 2023-09-21 09:14:40 +09:00 committed by GitHub
parent 4651faeaa2
commit 6c55a3aa2a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 379 additions and 32 deletions

View file

@ -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
*/

View file

@ -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);
}

View file

@ -16,6 +16,7 @@ list(APPEND SOURCE
ExtractIconEx.cpp
FindExecutable.cpp
GetDisplayNameOf.cpp
Int64ToString.cpp
IShellFolderViewCB.cpp
OpenAs_RunDLL.cpp
PathResolve.cpp

View file

@ -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.mz@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);
}

View file

@ -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 },

View file

@ -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
*/