diff --git a/dll/win32/shlwapi/ordinal.c b/dll/win32/shlwapi/ordinal.c index 9053c33b956..f43408d9479 100644 --- a/dll/win32/shlwapi/ordinal.c +++ b/dll/win32/shlwapi/ordinal.c @@ -5068,6 +5068,7 @@ free_sids: return psd; } +#ifndef __REACTOS__ /* See propbag.cpp */ /*********************************************************************** * SHCreatePropertyBagOnRegKey [SHLWAPI.471] * @@ -5093,6 +5094,7 @@ HRESULT WINAPI SHCreatePropertyBagOnRegKey (HKEY hKey, LPCWSTR subkey, return E_NOTIMPL; } +#endif /*********************************************************************** * SHGetViewStatePropertyBag [SHLWAPI.515] diff --git a/dll/win32/shlwapi/precomp.h b/dll/win32/shlwapi/precomp.h index 86f3ea401fd..2113a5dd967 100644 --- a/dll/win32/shlwapi/precomp.h +++ b/dll/win32/shlwapi/precomp.h @@ -20,7 +20,6 @@ #include #include #include -#define NO_SHLWAPI_STREAM #define NO_SHLWAPI_USER #include #include diff --git a/dll/win32/shlwapi/propbag.cpp b/dll/win32/shlwapi/propbag.cpp index 27434091fc3..f9951324d0c 100644 --- a/dll/win32/shlwapi/propbag.cpp +++ b/dll/win32/shlwapi/propbag.cpp @@ -51,6 +51,8 @@ public: *ppvObject = static_cast(this); return S_OK; } + + ERR("%p: %s: E_NOTIMPL\n", this, debugstr_guid(&riid)); return E_NOTIMPL; } STDMETHODIMP_(ULONG) AddRef() override @@ -245,9 +247,336 @@ SHCreatePropertyBagOnMemory(_In_ DWORD dwMode, _In_ REFIID riid, _Out_ void **pp CComPtr pMemBag(new CMemPropertyBag(dwMode)); - HRESULT hr = pMemBag->QueryInterface(riid, ppvObj); - if (FAILED(hr)) - ERR("0x%08X %s\n", hr, debugstr_guid(&riid)); + return pMemBag->QueryInterface(riid, ppvObj); +} + +class CRegPropertyBag : public CBasePropertyBag +{ +protected: + HKEY m_hKey; + + HRESULT _ReadDword(LPCWSTR pszPropName, VARIANT *pvari); + HRESULT _ReadString(LPCWSTR pszPropName, VARIANTARG *pvarg, UINT len); + HRESULT _ReadBinary(LPCWSTR pszPropName, VARIANT *pvari, VARTYPE vt, DWORD uBytes); + HRESULT _ReadStream(VARIANT *pvari, BYTE *pInit, UINT cbInit); + HRESULT _CopyStreamIntoBuff(IStream *pStream, void *pv, ULONG cb); + HRESULT _GetStreamSize(IStream *pStream, LPDWORD pcbSize); + HRESULT _WriteStream(LPCWSTR pszPropName, IStream *pStream); + +public: + CRegPropertyBag(DWORD dwMode) + : CBasePropertyBag(dwMode) + , m_hKey(NULL) + { + } + + ~CRegPropertyBag() override + { + if (m_hKey) + ::RegCloseKey(m_hKey); + } + + HRESULT Init(HKEY hKey, LPCWSTR lpSubKey); + + 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 CRegPropertyBag::Init(HKEY hKey, LPCWSTR lpSubKey) +{ + REGSAM nAccess = 0; + if ((m_dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_WRITE) + nAccess |= KEY_READ; + if ((m_dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_READ) + nAccess |= KEY_WRITE; + + LONG error; + if (m_dwMode & STGM_CREATE) + error = ::RegCreateKeyExW(hKey, lpSubKey, 0, NULL, 0, nAccess, NULL, &m_hKey, NULL); + else + error = ::RegOpenKeyExW(hKey, lpSubKey, 0, nAccess, &m_hKey); + + if (error != ERROR_SUCCESS) + { + ERR("%p %s 0x%08X\n", hKey, debugstr_w(lpSubKey), error); + return HRESULT_FROM_WIN32(error); + } + + return S_OK; +} + +HRESULT CRegPropertyBag::_ReadDword(LPCWSTR pszPropName, VARIANT *pvari) +{ + DWORD cbData = sizeof(DWORD); + LONG error = SHGetValueW(m_hKey, NULL, pszPropName, NULL, &V_UI4(pvari), &cbData); + if (error) + return E_FAIL; + + V_VT(pvari) = VT_UI4; + return S_OK; +} + +HRESULT CRegPropertyBag::_ReadString(LPCWSTR pszPropName, VARIANTARG *pvarg, UINT len) +{ + BSTR bstr = ::SysAllocStringByteLen(NULL, len); + V_BSTR(pvarg) = bstr; + if (!bstr) + return E_OUTOFMEMORY; + + V_VT(pvarg) = VT_BSTR; + LONG error = SHGetValueW(m_hKey, NULL, pszPropName, NULL, bstr, (LPDWORD)&len); + if (error) + { + ::VariantClear(pvarg); + return E_FAIL; + } + + return S_OK; +} + +HRESULT CRegPropertyBag::_ReadStream(VARIANT *pvari, BYTE *pInit, UINT cbInit) +{ + IStream *pStream = SHCreateMemStream(pInit, cbInit); + V_UNKNOWN(pvari) = pStream; + if (!pStream) + return E_OUTOFMEMORY; + V_VT(pvari) = VT_UNKNOWN; + return S_OK; +} + +HRESULT +CRegPropertyBag::_ReadBinary( + LPCWSTR pszPropName, + VARIANT *pvari, + VARTYPE vt, + DWORD uBytes) +{ + HRESULT hr = E_FAIL; + if (vt != VT_UNKNOWN || uBytes < sizeof(GUID)) + return hr; + + LPBYTE pbData = (LPBYTE)::LocalAlloc(LMEM_ZEROINIT, uBytes); + if (!pbData) + return hr; + + if (!SHGetValueW(m_hKey, NULL, pszPropName, NULL, pbData, &uBytes) && + memcmp(&GUID_NULL, pbData, sizeof(GUID)) == 0) + { + hr = _ReadStream(pvari, pbData + sizeof(GUID), uBytes - sizeof(GUID)); + } + + ::LocalFree(pbData); return hr; } + +HRESULT CRegPropertyBag::_CopyStreamIntoBuff(IStream *pStream, void *pv, ULONG cb) +{ + LARGE_INTEGER li; + li.QuadPart = 0; + HRESULT hr = pStream->Seek(li, 0, NULL); + if (FAILED(hr)) + return hr; + return pStream->Read(pv, cb, NULL); +} + +HRESULT CRegPropertyBag::_GetStreamSize(IStream *pStream, LPDWORD pcbSize) +{ + *pcbSize = 0; + + ULARGE_INTEGER ui; + HRESULT hr = IStream_Size(pStream, &ui); + if (FAILED(hr)) + return hr; + + if (ui.DUMMYSTRUCTNAME.HighPart) + return E_FAIL; /* 64-bit value is not supported */ + + *pcbSize = ui.DUMMYSTRUCTNAME.LowPart; + return hr; +} + +STDMETHODIMP +CRegPropertyBag::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); + + if ((m_dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) == STGM_WRITE) + { + ERR("%p: 0x%X\n", this, m_dwMode); + ::VariantInit(pvari); + return E_ACCESSDENIED; + } + + VARTYPE vt = V_VT(pvari); + VariantInit(pvari); + + HRESULT hr; + DWORD dwType, cbValue; + LONG error = SHGetValueW(m_hKey, NULL, pszPropName, &dwType, NULL, &cbValue); + if (error != ERROR_SUCCESS) + hr = E_FAIL; + else if (dwType == REG_SZ) + hr = _ReadString(pszPropName, pvari, cbValue); + else if (dwType == REG_BINARY) + hr = _ReadBinary(pszPropName, pvari, vt, cbValue); + else if (dwType == REG_DWORD) + hr = _ReadDword(pszPropName, pvari); + else + hr = E_FAIL; + + if (FAILED(hr)) + { + ERR("%p: 0x%08X %ld: %s %p\n", this, hr, dwType, debugstr_w(pszPropName), pvari); + ::VariantInit(pvari); + return hr; + } + + hr = ::VariantChangeTypeForRead(pvari, vt); + if (FAILED(hr)) + { + ERR("%p: 0x%08X %ld: %s %p\n", this, hr, dwType, debugstr_w(pszPropName), pvari); + ::VariantInit(pvari); + } + + return hr; +} + +HRESULT +CRegPropertyBag::_WriteStream(LPCWSTR pszPropName, IStream *pStream) +{ + DWORD cbData; + HRESULT hr = _GetStreamSize(pStream, &cbData); + if (FAILED(hr) || !cbData) + return hr; + + DWORD cbBinary = cbData + sizeof(GUID); + LPBYTE pbBinary = (LPBYTE)::LocalAlloc(LMEM_ZEROINIT, cbBinary); + if (!pbBinary) + return E_OUTOFMEMORY; + + hr = _CopyStreamIntoBuff(pStream, pbBinary + sizeof(GUID), cbData); + if (SUCCEEDED(hr)) + { + if (SHSetValueW(m_hKey, NULL, pszPropName, REG_BINARY, pbBinary, cbBinary)) + hr = E_FAIL; + } + + ::LocalFree(pbBinary); + return hr; +} + +STDMETHODIMP +CRegPropertyBag::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; + LONG error; + VARIANTARG vargTemp = { 0 }; + switch (V_VT(pvari)) + { + case VT_EMPTY: + SHDeleteValueW(m_hKey, NULL, pszPropName); + hr = S_OK; + break; + + case VT_BOOL: + case VT_I1: + case VT_I2: + case VT_I4: + case VT_UI1: + case VT_UI2: + case VT_UI4: + case VT_INT: + case VT_UINT: + { + hr = ::VariantChangeType(&vargTemp, pvari, 0, VT_UI4); + if (FAILED(hr)) + return hr; + + error = SHSetValueW(m_hKey, NULL, pszPropName, REG_DWORD, &V_UI4(&vargTemp), sizeof(DWORD)); + if (error) + hr = E_FAIL; + + ::VariantClear(&vargTemp); + break; + } + + case VT_UNKNOWN: + { + CComPtr pStream; + hr = V_UNKNOWN(pvari)->QueryInterface(IID_IStream, (void **)&pStream); + if (FAILED(hr)) + return hr; + + hr = _WriteStream(pszPropName, pStream); + break; + } + + default: + { + hr = ::VariantChangeType(&vargTemp, pvari, 0, VT_BSTR); + if (FAILED(hr)) + return hr; + + int cch = lstrlenW(V_BSTR(&vargTemp)); + DWORD cb = (cch + 1) * sizeof(WCHAR); + error = SHSetValueW(m_hKey, NULL, pszPropName, REG_SZ, V_BSTR(&vargTemp), cb); + if (error) + hr = E_FAIL; + + ::VariantClear(&vargTemp); + break; + } + } + + return hr; +} + +/************************************************************************** + * SHCreatePropertyBagOnRegKey (SHLWAPI.471) + * + * Creates a property bag object on registry key. + * + * @param hKey The registry key. + * @param pszSubKey The path of the sub-key. + * @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://source.winehq.org/WineAPI/SHCreatePropertyBagOnRegKey.html + */ +EXTERN_C HRESULT WINAPI +SHCreatePropertyBagOnRegKey( + _In_ HKEY hKey, + _In_z_ LPCWSTR pszSubKey, + _In_ DWORD dwMode, + _In_ REFIID riid, + _Out_ void **ppvObj) +{ + TRACE("%p, %s, 0x%08X, %s, %p\n", hKey, debugstr_w(pszSubKey), dwMode, + debugstr_guid(&riid), ppvObj); + + *ppvObj = NULL; + + CComPtr pRegBag(new CRegPropertyBag(dwMode)); + + HRESULT hr = pRegBag->Init(hKey, pszSubKey); + if (FAILED(hr)) + return hr; + + return pRegBag->QueryInterface(riid, ppvObj); +} diff --git a/dll/win32/shlwapi/shlwapi.spec b/dll/win32/shlwapi/shlwapi.spec index 0844af03d5d..13cac9b8c7a 100644 --- a/dll/win32/shlwapi/shlwapi.spec +++ b/dll/win32/shlwapi/shlwapi.spec @@ -468,7 +468,7 @@ 468 stub -noname RunIndirectRegCommand 469 stub -noname RunRegCommand 470 stub -noname IUnknown_ProfferServiceOld -471 stdcall -noname SHCreatePropertyBagOnRegKey(long wstr long ptr ptr) +471 stdcall -noname SHCreatePropertyBagOnRegKey(ptr wstr long ptr ptr) 472 stub -noname SHCreatePropertyBagOnProfileSelection 473 stub -noname SHGetIniStringUTF7W 474 stub -noname SHSetIniStringUTF7W diff --git a/modules/rostests/apitests/shlwapi/SHPropertyBag.cpp b/modules/rostests/apitests/shlwapi/SHPropertyBag.cpp index 2114ceff231..365c4478f19 100644 --- a/modules/rostests/apitests/shlwapi/SHPropertyBag.cpp +++ b/modules/rostests/apitests/shlwapi/SHPropertyBag.cpp @@ -96,7 +96,7 @@ public: if (lstrcmpiW(pszPropName, L"GUID1") == 0) { V_VT(pvari) = (VT_UI1 | VT_ARRAY); - V_ARRAY(pvari) = CreateByteArray(&IID_IShellLink, sizeof(IID)); + V_ARRAY(pvari) = CreateByteArray(&IID_IShellLinkW, sizeof(IID)); return S_OK; } @@ -225,7 +225,7 @@ static void SHPropertyBag_ReadTest(void) ok_long(hr, S_OK); ok_int(s_cRead, 1); ok_int(s_cWrite, 0); - ok_int(IsEqualGUID(guid, IID_IShellLink), TRUE); + ok_int(IsEqualGUID(guid, IID_IShellLinkW), TRUE); ResetTest(VT_EMPTY, L"GUID2"); hr = SHPropertyBag_ReadGUID(&dummy, L"GUID2", &guid); @@ -456,9 +456,192 @@ static void SHPropertyBag_OnMemory(void) } } +static void SHPropertyBag_OnRegKey(void) +{ + HKEY hKey, hSubKey; + LONG error; + VARIANT vari; + WCHAR szText[MAX_PATH]; + IStream *pStream; + GUID guid; + BYTE guid_and_extra[sizeof(GUID) + sizeof(GUID)]; + + // Create HKCU\Software\ReactOS registry key + error = RegCreateKeyW(HKEY_CURRENT_USER, L"Software\\ReactOS", &hKey); + if (error) + { + skip("FAILED to create HKCU\\Software\\ReactOS\n"); + return; + } + + IPropertyBag *pPropBag; + HRESULT hr; + + // Try to create new registry key + RegDeleteKeyW(hKey, L"PropBagTest"); + hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", 0, + IID_IPropertyBag, (void **)&pPropBag); + ok_long(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + + // Try to create new registry key + RegDeleteKeyW(hKey, L"PropBagTest"); + hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", STGM_READWRITE, + IID_IPropertyBag, (void **)&pPropBag); + ok_long(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + + // Create new registry key + RegDeleteKeyW(hKey, L"PropBagTest"); + hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", STGM_CREATE | STGM_READWRITE, + IID_IPropertyBag, (void **)&pPropBag); + if (FAILED(hr)) + { + skip("SHCreatePropertyBagOnRegKey FAILED\n"); + RegCloseKey(hKey); + return; + } + + // 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); + + // Read UI4 + VariantInit(&vari); + hr = pPropBag->Read(L"Name1", &vari, NULL); + ok_long(hr, S_OK); + ok_long(V_VT(&vari), VT_UI4); + ok_long(V_UI4(&vari), 0xDEADFACE); + 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); + + // Read BSTR + VariantInit(&vari); + V_VT(&vari) = VT_BSTR; + hr = pPropBag->Read(L"Name2", &vari, NULL); + ok_long(hr, S_OK); + ok_long(V_VT(&vari), VT_BSTR); + ok_wstr(V_BSTR(&vari), L"StrValue"); + VariantClear(&vari); + + // Write GUID + VariantInit(&vari); + V_VT(&vari) = VT_UNKNOWN; + V_UNKNOWN(&vari) = SHCreateMemStream((BYTE*)&IID_IShellLinkW, sizeof(IID_IShellLinkW)); + hr = pPropBag->Write(L"Name4", &vari); + ok_long(hr, S_OK); + VariantClear(&vari); + + // Read GUID + VariantInit(&vari); + V_VT(&vari) = VT_EMPTY; + hr = pPropBag->Read(L"Name4", &vari, NULL); + if (IsWindowsVistaOrGreater()) + { + ok_long(hr, S_OK); + ok_long(V_VT(&vari), VT_UNKNOWN); + pStream = (IStream*)V_UNKNOWN(&vari); + FillMemory(&guid, sizeof(guid), 0xEE); + hr = pStream->Read(&guid, sizeof(guid), NULL); + ok_long(hr, S_OK); + ok_int(::IsEqualGUID(guid, IID_IShellLinkW), TRUE); + } + else // XP/2k3 Read is buggy + { + ok_long(hr, E_FAIL); + ok_long(V_VT(&vari), VT_EMPTY); + } + VariantClear(&vari); + + pPropBag->Release(); + + // Check registry + error = RegOpenKeyExW(hKey, L"PropBagTest", 0, KEY_READ, &hSubKey); + ok_long(error, ERROR_SUCCESS); + DWORD dwType, dwValue, cbValue = sizeof(dwValue); + error = RegQueryValueExW(hSubKey, L"Name1", NULL, &dwType, (BYTE*)&dwValue, &cbValue); + ok_long(error, ERROR_SUCCESS); + ok_long(dwType, REG_DWORD); + ok_long(dwValue, 0xDEADFACE); + ok_long(cbValue, sizeof(DWORD)); + cbValue = sizeof(szText); + error = RegQueryValueExW(hSubKey, L"Name2", NULL, &dwType, (BYTE*)szText, &cbValue); + ok_long(error, ERROR_SUCCESS); + ok_long(dwType, REG_SZ); + ok_wstr(szText, L"StrValue"); + cbValue = sizeof(guid_and_extra); + error = RegQueryValueExW(hSubKey, L"Name4", NULL, &dwType, (BYTE*)&guid_and_extra, &cbValue); + ok_long(error, ERROR_SUCCESS); + ok_long(dwType, REG_BINARY); + ok_int(memcmp(&guid_and_extra, &GUID_NULL, sizeof(GUID)), 0); + ok_int(memcmp(&guid_and_extra[sizeof(GUID)], &IID_IShellLinkW, sizeof(GUID)), 0); + RegCloseKey(hSubKey); + + // Create as read-only + hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", STGM_READ, + IID_IPropertyBag, (void **)&pPropBag); + ok_long(hr, S_OK); + + // Read UI4 + VariantInit(&vari); + hr = pPropBag->Read(L"Name1", &vari, NULL); + ok_long(hr, S_OK); + ok_long(V_VT(&vari), VT_UI4); + ok_long(V_UI4(&vari), 0xDEADFACE); + VariantClear(&vari); + + // Write UI4 + VariantInit(&vari); + V_VT(&vari) = VT_UI4; + V_UI4(&vari) = 0xDEADFACE; + hr = pPropBag->Write(L"Name1", &vari); + ok_long(hr, E_ACCESSDENIED); + VariantClear(&vari); + + pPropBag->Release(); + + // Create as write-only IPropertyBag2 + hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", STGM_WRITE, + IID_IPropertyBag2, (void **)&pPropBag); + ok_long(hr, S_OK); + + // Write UI4 + VariantInit(&vari); + V_VT(&vari) = VT_UI4; + V_UI4(&vari) = 0xDEADFACE; + hr = pPropBag->Write(L"Name3", &vari); + ok_long(hr, E_NOTIMPL); + VariantClear(&vari); + + // Read UI4 + VariantInit(&vari); + V_UI4(&vari) = 0xFEEDF00D; + hr = pPropBag->Read(L"Name3", &vari, NULL); + ok_long(hr, E_NOTIMPL); + ok_int(V_VT(&vari), VT_EMPTY); + ok_long(V_UI4(&vari), 0xFEEDF00D); + VariantClear(&vari); + + pPropBag->Release(); + + // Clean up + RegDeleteKeyW(hKey, L"PropBagTest"); + RegCloseKey(hKey); +} + START_TEST(SHPropertyBag) { SHPropertyBag_ReadTest(); SHPropertyBag_WriteTest(); SHPropertyBag_OnMemory(); + SHPropertyBag_OnRegKey(); } diff --git a/sdk/include/reactos/shlwapi_undoc.h b/sdk/include/reactos/shlwapi_undoc.h index 9ed45b2c8d5..29aae083047 100644 --- a/sdk/include/reactos/shlwapi_undoc.h +++ b/sdk/include/reactos/shlwapi_undoc.h @@ -91,6 +91,7 @@ BOOL WINAPI SHExpandEnvironmentStringsForUserW(HANDLE, LPCWSTR, LPWSTR, DWORD); BOOL WINAPI SHIsEmptyStream(IStream*); +HRESULT WINAPI IStream_Size(IStream *lpStream, ULARGE_INTEGER* lpulSize); HRESULT WINAPI SHInvokeDefaultCommand(HWND,IShellFolder*,LPCITEMIDLIST); HRESULT WINAPI SHPropertyBag_ReadType(IPropertyBag *ppb, LPCWSTR pszPropName, VARIANTARG *pvarg, VARTYPE vt); HRESULT WINAPI SHPropertyBag_ReadBOOL(IPropertyBag *ppb, LPCWSTR pszPropName, BOOL *pbValue); @@ -126,6 +127,14 @@ HRESULT WINAPI SHPropertyBag_WriteRECTL(IPropertyBag *ppb, LPCWSTR pszPropName, HRESULT WINAPI SHCreatePropertyBagOnMemory(_In_ DWORD dwMode, _In_ REFIID riid, _Out_ void **ppvObj); +HRESULT WINAPI +SHCreatePropertyBagOnRegKey( + _In_ HKEY hKey, + _In_z_ LPCWSTR pszSubKey, + _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);