[SHLWAPI][SHLWAPI_APITEST][SDK] INI file property bag (#5546)

- Add SHGetIniStringUTF7W and SHSetIniStringUTF7W functions.
- Add CIniPropertyBag class.
- Implement SHCreatePropertyBagOnProfileSection function.
CORE-9283
This commit is contained in:
Katayama Hirofumi MZ 2023-08-19 11:22:55 +09:00 committed by GitHub
parent c0e443058e
commit 56d95154ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 465 additions and 3 deletions

View file

@ -693,3 +693,305 @@ SHSetIniStringW(
return ret;
}
/**************************************************************************
* SHGetIniStringUTF7W (SHLWAPI.473)
*
* Retrieves a string value from an INI file.
*
* @param lpAppName The section name.
* @param lpKeyName The key name.
* If this string begins from '@', the value will be interpreted as UTF-7.
* @param lpReturnedString Receives a wide string value.
* @param nSize The number of characters in lpReturnedString.
* @param lpFileName The INI file.
* @return The number of characters copied to the buffer if succeeded.
*/
EXTERN_C DWORD WINAPI
SHGetIniStringUTF7W(
_In_opt_z_ LPCWSTR lpAppName,
_In_z_ LPCWSTR lpKeyName,
_Out_writes_to_(nSize, return + 1) _Post_z_ LPWSTR lpReturnedString,
_In_ DWORD nSize,
_In_z_ LPCWSTR lpFileName)
{
if (*lpKeyName == L'@') // UTF-7
return SHGetIniStringW(lpAppName, lpKeyName + 1, lpReturnedString, nSize, lpFileName);
return GetPrivateProfileStringW(lpAppName, lpKeyName, L"", lpReturnedString, nSize, lpFileName);
}
/**************************************************************************
* SHSetIniStringUTF7W (SHLWAPI.474)
*
* Sets a string value on an INI file.
*
* @param lpAppName The section name.
* @param lpKeyName The key name.
* If this begins from '@', the value will be stored as UTF-7.
* @param lpString The wide string value to be set.
* @param lpFileName The INI file.
* @return TRUE if successful. FALSE if failed.
*/
EXTERN_C BOOL WINAPI
SHSetIniStringUTF7W(
_In_z_ LPCWSTR lpAppName,
_In_z_ LPCWSTR lpKeyName,
_In_opt_z_ LPCWSTR lpString,
_In_z_ LPCWSTR lpFileName)
{
if (*lpKeyName == L'@') // UTF-7
return SHSetIniStringW(lpAppName, lpKeyName + 1, lpString, lpFileName);
return WritePrivateProfileStringW(lpAppName, lpKeyName, lpString, lpFileName);
}
class CIniPropertyBag : public CBasePropertyBag
{
protected:
LPWSTR m_pszFileName;
LPWSTR m_pszSection;
BOOL m_bAlternateStream; // ADS (Alternate Data Stream)
static BOOL LooksLikeAnAlternateStream(LPCWSTR pszStart)
{
LPCWSTR pch = StrRChrW(pszStart, NULL, L'\\');
if (!pch)
pch = pszStart;
return StrChrW(pch, L':') != NULL;
}
HRESULT
_GetSectionAndName(
LPCWSTR pszStart,
LPWSTR pszSection,
UINT cchSectionMax,
LPWSTR pszName,
UINT cchNameMax);
public:
CIniPropertyBag(DWORD dwMode)
: CBasePropertyBag(dwMode)
, m_pszFileName(NULL)
, m_pszSection(NULL)
, m_bAlternateStream(FALSE)
{
}
~CIniPropertyBag() override
{
::LocalFree(m_pszFileName);
::LocalFree(m_pszSection);
}
HRESULT Init(LPCWSTR pszIniFile, LPCWSTR pszSection);
STDMETHODIMP Read(
_In_z_ LPCWSTR pszPropName,
_Inout_ VARIANT *pvari,
_Inout_opt_ IErrorLog *pErrorLog) override;
STDMETHODIMP Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) override;
};
HRESULT CIniPropertyBag::Init(LPCWSTR pszIniFile, LPCWSTR pszSection)
{
m_pszFileName = StrDupW(pszIniFile);
if (!m_pszFileName)
return E_OUTOFMEMORY;
// Is it an ADS (Alternate Data Stream) pathname?
m_bAlternateStream = LooksLikeAnAlternateStream(m_pszFileName);
if (pszSection)
{
m_pszSection = StrDupW(pszSection);
if (!m_pszSection)
return E_OUTOFMEMORY;
}
return S_OK;
}
HRESULT
CIniPropertyBag::_GetSectionAndName(
LPCWSTR pszStart,
LPWSTR pszSection,
UINT cchSectionMax,
LPWSTR pszName,
UINT cchNameMax)
{
LPCWSTR pchSep = StrChrW(pszStart, L'\\');
if (pchSep)
{
UINT cchSep = (UINT)(pchSep - pszStart + 1);
StrCpyNW(pszSection, pszStart, min(cchSep, cchSectionMax));
StrCpyNW(pszName, pchSep + 1, cchNameMax);
return S_OK;
}
if (m_pszSection)
{
StrCpyNW(pszSection, m_pszSection, cchSectionMax);
StrCpyNW(pszName, pszStart, cchNameMax);
return S_OK;
}
ERR("%p: %s\n", this, debugstr_w(pszStart));
return E_INVALIDARG;
}
STDMETHODIMP
CIniPropertyBag::Read(
_In_z_ LPCWSTR pszPropName,
_Inout_ VARIANT *pvari,
_Inout_opt_ IErrorLog *pErrorLog)
{
UNREFERENCED_PARAMETER(pErrorLog);
TRACE("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari, pErrorLog);
VARTYPE vt = V_VT(pvari);
::VariantInit(pvari);
if ((m_dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) == STGM_WRITE)
{
ERR("%p: 0x%X\n", this, m_dwMode);
return E_ACCESSDENIED;
}
WCHAR szSection[64], szName[64];
HRESULT hr =
_GetSectionAndName(pszPropName, szSection, _countof(szSection), szName, _countof(szName));
if (FAILED(hr))
return hr;
const INT cchBuffMax = 4 * MAX_PATH; // UTF-7 needs 4 times length buffer.
CComHeapPtr<WCHAR> pszBuff;
if (!pszBuff.Allocate(cchBuffMax * sizeof(WCHAR)))
return E_OUTOFMEMORY;
if (!SHGetIniStringUTF7W(szSection, szName, pszBuff, cchBuffMax, m_pszFileName))
return E_FAIL;
BSTR bstr = ::SysAllocString(pszBuff);
V_BSTR(pvari) = bstr;
if (!bstr)
return E_OUTOFMEMORY;
V_VT(pvari) = VT_BSTR;
return ::VariantChangeTypeForRead(pvari, vt);
}
STDMETHODIMP
CIniPropertyBag::Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari)
{
TRACE("%p: %s %p\n", this, debugstr_w(pszPropName), pvari);
if ((m_dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) == STGM_READ)
{
ERR("%p: 0x%X\n", this, m_dwMode);
return E_ACCESSDENIED;
}
HRESULT hr;
BSTR bstr;
VARIANTARG vargTemp = { 0 };
switch (V_VT(pvari))
{
case VT_EMPTY:
bstr = NULL;
break;
case VT_BSTR:
bstr = V_BSTR(pvari);
break;
default:
hr = ::VariantChangeType(&vargTemp, pvari, 0, VT_BSTR);
if (FAILED(hr))
goto Quit;
bstr = V_BSTR(&vargTemp);
break;
}
WCHAR szSection[64], szName[64];
hr = _GetSectionAndName(pszPropName, szSection, _countof(szSection), szName, _countof(szName));
if (SUCCEEDED(hr))
{
if (SHSetIniStringUTF7W(szSection, szName, bstr, m_pszFileName))
{
if (!m_bAlternateStream)
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATHW, m_pszFileName, NULL);
}
else
{
hr = E_FAIL;
}
}
Quit:
::VariantClear(&vargTemp);
return hr;
}
/**************************************************************************
* SHCreatePropertyBagOnProfileSection (SHLWAPI.472)
*
* Creates a property bag object on INI file.
*
* @param lpFileName The INI filename.
* @param pszSection The optional section name.
* @param dwMode The combination of STGM_READ, STGM_WRITE, STGM_READWRITE, and STGM_CREATE.
* @param riid Specifies either IID_IUnknown, IID_IPropertyBag or IID_IPropertyBag2.
* @param ppvObj Receives an IPropertyBag pointer.
* @return An HRESULT value. S_OK on success, non-zero on failure.
* @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/createonprofilesection.htm
*/
EXTERN_C HRESULT WINAPI
SHCreatePropertyBagOnProfileSection(
_In_z_ LPCWSTR lpFileName,
_In_opt_z_ LPCWSTR pszSection,
_In_ DWORD dwMode,
_In_ REFIID riid,
_Out_ void **ppvObj)
{
HANDLE hFile;
PWCHAR pchFileTitle;
WCHAR szBuff[MAX_PATH];
if (dwMode & STGM_CREATE)
{
hFile = ::CreateFileW(lpFileName, 0, FILE_SHARE_DELETE, 0, CREATE_NEW,
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
pchFileTitle = PathFindFileNameW(lpFileName);
if (lstrcmpiW(pchFileTitle, L"desktop.ini") == 0)
{
StrCpyNW(szBuff, lpFileName, _countof(szBuff));
if (PathRemoveFileSpecW(szBuff))
PathMakeSystemFolderW(szBuff);
}
::CloseHandle(hFile);
}
}
*ppvObj = NULL;
if (!PathFileExistsW(lpFileName))
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
CComPtr<CIniPropertyBag> pIniPB(new CIniPropertyBag(dwMode));
HRESULT hr = pIniPB->Init(lpFileName, pszSection);
if (FAILED(hr))
{
ERR("0x%08X\n", hr);
return hr;
}
return pIniPB->QueryInterface(riid, ppvObj);
}

View file

@ -469,9 +469,9 @@
469 stub -noname RunRegCommand
470 stub -noname IUnknown_ProfferServiceOld
471 stdcall -noname SHCreatePropertyBagOnRegKey(ptr wstr long ptr ptr)
472 stub -noname SHCreatePropertyBagOnProfileSelection
473 stub -noname SHGetIniStringUTF7W
474 stub -noname SHSetIniStringUTF7W
472 stdcall -noname SHCreatePropertyBagOnProfileSection(wstr wstr long ptr ptr)
473 stdcall -noname SHGetIniStringUTF7W(wstr wstr ptr long wstr)
474 stdcall -noname SHSetIniStringUTF7W(wstr wstr wstr wstr)
475 stdcall -noname GetShellSecurityDescriptor(ptr long)
476 stdcall -noname SHGetObjectCompatFlags(ptr ptr)
477 stdcall -noname SHCreatePropertyBagOnMemory(long ptr ptr)

View file

@ -8,6 +8,7 @@
#include <apitest.h>
#include <shlwapi.h>
#include <shlobj.h>
#include <stdio.h>
#include <shlwapi_undoc.h>
#include <versionhelpers.h>
@ -687,6 +688,141 @@ static void SHPropertyBag_SHSetIniStringW(void)
DeleteFileW(szIniFile);
}
void SHPropertyBag_OnIniFile(void)
{
WCHAR szIniFile[MAX_PATH], szValue[MAX_PATH];
HRESULT hr;
IPropertyBag *pPropBag;
VARIANT vari;
DWORD dwRet;
ExpandEnvironmentStringsW(L"%TEMP%\\SHPropertyBag.ini", szIniFile, _countof(szIniFile));
DeleteFileW(szIniFile);
fclose(_wfopen(szIniFile, L"w"));
trace("%ls\n", szIniFile);
// read-write
hr = SHCreatePropertyBagOnProfileSection(
szIniFile,
L"TestSection",
STGM_READWRITE,
IID_IPropertyBag,
(void**)&pPropBag);
ok_long(hr, S_OK);
ok_int(PathFileExistsW(szIniFile), TRUE);
// Write UI4
VariantInit(&vari);
V_VT(&vari) = VT_UI4;
V_UI4(&vari) = 0xDEADFACE;
hr = pPropBag->Write(L"Name1", &vari);
ok_long(hr, S_OK);
VariantClear(&vari);
// Write BSTR
VariantInit(&vari);
V_VT(&vari) = VT_BSTR;
V_BSTR(&vari) = SysAllocString(L"StrValue");
hr = pPropBag->Write(L"Name2", &vari);
ok_long(hr, S_OK);
VariantClear(&vari);
// Write BSTR (dirty UTF-7)
VariantInit(&vari);
V_VT(&vari) = VT_BSTR;
V_BSTR(&vari) = SysAllocString(L"ABC\x3042\x3044\x3046\x2665");
hr = pPropBag->Write(L"@Name3", &vari);
ok_long(hr, S_OK);
VariantClear(&vari);
// Write BSTR (clean UTF-7)
VariantInit(&vari);
V_VT(&vari) = VT_BSTR;
V_BSTR(&vari) = SysAllocString(L"1234abc");
hr = pPropBag->Write(L"@Name4", &vari);
ok_long(hr, S_OK);
VariantClear(&vari);
pPropBag->Release();
// Flush
WritePrivateProfileStringW(NULL, NULL, NULL, szIniFile);
// Check INI file
dwRet = GetPrivateProfileStringW(L"TestSection", L"Name1", L"BAD", szValue, _countof(szValue), szIniFile);
ok_long(dwRet, 10);
ok_wstr(szValue, L"3735943886");
dwRet = GetPrivateProfileStringW(L"TestSection", L"Name2", L"BAD", szValue, _countof(szValue), szIniFile);
ok_long(dwRet, 8);
ok_wstr(szValue, L"StrValue");
GetPrivateProfileStringW(L"TestSection", L"Name3", L"NotFound", szValue, _countof(szValue), szIniFile);
ok_int(memcmp(szValue, L"ABC", 3 * sizeof(WCHAR)), 0);
GetPrivateProfileStringW(L"TestSection.A", L"Name3", L"NotFound", szValue, _countof(szValue), szIniFile);
ok_int(memcmp(szValue, L"ABC", 3 * sizeof(WCHAR)), 0);
GetPrivateProfileStringW(L"TestSection.W", L"Name3", L"NotFound", szValue, _countof(szValue), szIniFile);
ok_wstr(szValue, L"ABC+MEIwRDBGJmU-"); // UTF-7
GetPrivateProfileStringW(L"TestSection", L"Name4", L"NotFound", szValue, _countof(szValue), szIniFile);
ok_wstr(szValue, L"1234abc");
GetPrivateProfileStringW(L"TestSection.A", L"Name4", L"NotFound", szValue, _countof(szValue), szIniFile);
ok_wstr(szValue, L"NotFound");
GetPrivateProfileStringW(L"TestSection.W", L"Name4", L"NotFound", szValue, _countof(szValue), szIniFile);
ok_wstr(szValue, L"NotFound");
// read-only
hr = SHCreatePropertyBagOnProfileSection(
szIniFile,
NULL,
STGM_READ,
IID_IPropertyBag,
(void**)&pPropBag);
ok_long(hr, S_OK);
// Read UI4
VariantInit(&vari);
V_VT(&vari) = VT_UI4;
hr = pPropBag->Read(L"TestSection\\Name1", &vari, NULL);
ok_long(hr, S_OK);
ok_long(V_UI4(&vari), 0xDEADFACE);
VariantClear(&vari);
// Read BSTR
VariantInit(&vari);
V_VT(&vari) = VT_BSTR;
hr = pPropBag->Read(L"TestSection\\Name2", &vari, NULL);
ok_long(hr, S_OK);
ok_wstr(V_BSTR(&vari), L"StrValue");
VariantClear(&vari);
// Read BSTR (dirty UTF-7)
VariantInit(&vari);
V_VT(&vari) = VT_BSTR;
hr = pPropBag->Read(L"TestSection\\@Name3", &vari, NULL);
ok_long(hr, S_OK);
ok_wstr(V_BSTR(&vari), L"ABC\x3042\x3044\x3046\x2665");
VariantClear(&vari);
// Read BSTR (clean UTF-7)
VariantInit(&vari);
V_VT(&vari) = VT_BSTR;
hr = pPropBag->Read(L"TestSection\\@Name4", &vari, NULL);
ok_long(hr, S_OK);
ok_wstr(V_BSTR(&vari), L"1234abc");
VariantClear(&vari);
pPropBag->Release();
DeleteFileW(szIniFile);
}
START_TEST(SHPropertyBag)
{
SHPropertyBag_ReadTest();
@ -694,4 +830,5 @@ START_TEST(SHPropertyBag)
SHPropertyBag_OnMemory();
SHPropertyBag_OnRegKey();
SHPropertyBag_SHSetIniStringW();
SHPropertyBag_OnIniFile();
}

View file

@ -135,6 +135,14 @@ SHCreatePropertyBagOnRegKey(
_In_ REFIID riid,
_Out_ void **ppvObj);
HRESULT WINAPI
SHCreatePropertyBagOnProfileSection(
_In_z_ LPCWSTR lpFileName,
_In_opt_z_ LPCWSTR pszSection,
_In_ DWORD dwMode,
_In_ REFIID riid,
_Out_ void **ppvObj);
HWND WINAPI SHCreateWorkerWindowA(WNDPROC wndProc, HWND hWndParent, DWORD dwExStyle,
DWORD dwStyle, HMENU hMenu, LONG_PTR wnd_extra);
@ -194,6 +202,21 @@ SHSetIniStringW(
_In_opt_z_ LPCWSTR str,
_In_z_ LPCWSTR filename);
DWORD WINAPI
SHGetIniStringUTF7W(
_In_opt_z_ LPCWSTR lpAppName,
_In_z_ LPCWSTR lpKeyName,
_Out_writes_to_(nSize, return + 1) _Post_z_ LPWSTR lpReturnedString,
_In_ DWORD nSize,
_In_z_ LPCWSTR lpFileName);
BOOL WINAPI
SHSetIniStringUTF7W(
_In_z_ LPCWSTR lpAppName,
_In_z_ LPCWSTR lpKeyName,
_In_opt_z_ LPCWSTR lpString,
_In_z_ LPCWSTR lpFileName);
int
WINAPIV
ShellMessageBoxWrapW(