mirror of
https://github.com/reactos/wine.git
synced 2024-11-28 22:20:26 +00:00
2916 lines
95 KiB
C
2916 lines
95 KiB
C
/* Unit test suite for SHLWAPI ordinal functions
|
|
*
|
|
* Copyright 2004 Jon Griffiths
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#define COBJMACROS
|
|
#define CONST_VTABLE
|
|
#include "wine/test.h"
|
|
#include "winbase.h"
|
|
#include "winerror.h"
|
|
#include "winuser.h"
|
|
#include "ole2.h"
|
|
#include "oaidl.h"
|
|
#include "ocidl.h"
|
|
#include "mlang.h"
|
|
#include "shlwapi.h"
|
|
#include "docobj.h"
|
|
#include "shobjidl.h"
|
|
#include "shlobj.h"
|
|
|
|
/* Function ptrs for ordinal calls */
|
|
static HMODULE hShlwapi;
|
|
static BOOL is_win2k_and_lower;
|
|
static BOOL is_win9x;
|
|
|
|
static int (WINAPI *pSHSearchMapInt)(const int*,const int*,int,int);
|
|
static HRESULT (WINAPI *pGetAcceptLanguagesA)(LPSTR,LPDWORD);
|
|
|
|
static HANDLE (WINAPI *pSHAllocShared)(LPCVOID,DWORD,DWORD);
|
|
static LPVOID (WINAPI *pSHLockShared)(HANDLE,DWORD);
|
|
static BOOL (WINAPI *pSHUnlockShared)(LPVOID);
|
|
static BOOL (WINAPI *pSHFreeShared)(HANDLE,DWORD);
|
|
static HRESULT(WINAPIV *pSHPackDispParams)(DISPPARAMS*,VARIANTARG*,UINT,...);
|
|
static HRESULT(WINAPI *pIConnectionPoint_SimpleInvoke)(IConnectionPoint*,DISPID,DISPPARAMS*);
|
|
static HRESULT(WINAPI *pIConnectionPoint_InvokeWithCancel)(IConnectionPoint*,DISPID,DISPPARAMS*,DWORD,DWORD);
|
|
static HRESULT(WINAPI *pConnectToConnectionPoint)(IUnknown*,REFIID,BOOL,IUnknown*, LPDWORD,IConnectionPoint **);
|
|
static HRESULT(WINAPI *pSHPropertyBag_ReadLONG)(IPropertyBag *,LPCWSTR,LPLONG);
|
|
static LONG (WINAPI *pSHSetWindowBits)(HWND, INT, UINT, UINT);
|
|
static INT (WINAPI *pSHFormatDateTimeA)(const FILETIME UNALIGNED*, DWORD*, LPSTR, UINT);
|
|
static INT (WINAPI *pSHFormatDateTimeW)(const FILETIME UNALIGNED*, DWORD*, LPWSTR, UINT);
|
|
static DWORD (WINAPI *pSHGetObjectCompatFlags)(IUnknown*, const CLSID*);
|
|
static BOOL (WINAPI *pGUIDFromStringA)(LPSTR, CLSID *);
|
|
static HRESULT (WINAPI *pIUnknown_QueryServiceExec)(IUnknown*, REFIID, const GUID*, DWORD, DWORD, VARIANT*, VARIANT*);
|
|
static HRESULT (WINAPI *pIUnknown_ProfferService)(IUnknown*, REFGUID, IServiceProvider*, DWORD*);
|
|
static HWND (WINAPI *pSHCreateWorkerWindowA)(LONG, HWND, DWORD, DWORD, HMENU, LONG_PTR);
|
|
static HRESULT (WINAPI *pSHIShellFolder_EnumObjects)(LPSHELLFOLDER, HWND, SHCONTF, IEnumIDList**);
|
|
static DWORD (WINAPI *pSHGetIniStringW)(LPCWSTR, LPCWSTR, LPWSTR, DWORD, LPCWSTR);
|
|
static BOOL (WINAPI *pSHSetIniStringW)(LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR);
|
|
static HKEY (WINAPI *pSHGetShellKey)(DWORD, LPCWSTR, BOOL);
|
|
static HRESULT (WINAPI *pSKGetValueW)(DWORD, LPCWSTR, LPCWSTR, DWORD*, void*, DWORD*);
|
|
static HRESULT (WINAPI *pSKSetValueW)(DWORD, LPCWSTR, LPCWSTR, DWORD, void*, DWORD);
|
|
static HRESULT (WINAPI *pSKDeleteValueW)(DWORD, LPCWSTR, LPCWSTR);
|
|
static HRESULT (WINAPI *pSKAllocValueW)(DWORD, LPCWSTR, LPCWSTR, DWORD*, void**, DWORD*);
|
|
|
|
static HMODULE hmlang;
|
|
static HRESULT (WINAPI *pLcidToRfc1766A)(LCID, LPSTR, INT);
|
|
|
|
static HMODULE hshell32;
|
|
static HRESULT (WINAPI *pSHGetDesktopFolder)(IShellFolder**);
|
|
|
|
static const CHAR ie_international[] = {
|
|
'S','o','f','t','w','a','r','e','\\',
|
|
'M','i','c','r','o','s','o','f','t','\\',
|
|
'I','n','t','e','r','n','e','t',' ','E','x','p','l','o','r','e','r','\\',
|
|
'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
|
|
static const CHAR acceptlanguage[] = {
|
|
'A','c','c','e','p','t','L','a','n','g','u','a','g','e',0};
|
|
|
|
static int strcmp_wa(LPCWSTR strw, const char *stra)
|
|
{
|
|
CHAR buf[512];
|
|
WideCharToMultiByte(CP_ACP, 0, strw, -1, buf, sizeof(buf), NULL, NULL);
|
|
return lstrcmpA(stra, buf);
|
|
}
|
|
|
|
typedef struct {
|
|
int id;
|
|
const void *args[5];
|
|
} call_entry_t;
|
|
|
|
typedef struct {
|
|
call_entry_t *calls;
|
|
int count;
|
|
int alloc;
|
|
} call_trace_t;
|
|
|
|
static void init_call_trace(call_trace_t *ctrace)
|
|
{
|
|
ctrace->alloc = 10;
|
|
ctrace->count = 0;
|
|
ctrace->calls = HeapAlloc(GetProcessHeap(), 0, sizeof(call_entry_t) * ctrace->alloc);
|
|
}
|
|
|
|
static void free_call_trace(const call_trace_t *ctrace)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, ctrace->calls);
|
|
}
|
|
|
|
static void add_call(call_trace_t *ctrace, int id, const void *arg0,
|
|
const void *arg1, const void *arg2, const void *arg3, const void *arg4)
|
|
{
|
|
call_entry_t call;
|
|
|
|
call.id = id;
|
|
call.args[0] = arg0;
|
|
call.args[1] = arg1;
|
|
call.args[2] = arg2;
|
|
call.args[3] = arg3;
|
|
call.args[4] = arg4;
|
|
|
|
if (ctrace->count == ctrace->alloc)
|
|
{
|
|
ctrace->alloc *= 2;
|
|
ctrace->calls = HeapReAlloc(GetProcessHeap(),0, ctrace->calls, ctrace->alloc*sizeof(call_entry_t));
|
|
}
|
|
|
|
ctrace->calls[ctrace->count++] = call;
|
|
}
|
|
|
|
static void ok_trace_(call_trace_t *texpected, call_trace_t *tgot, int line)
|
|
{
|
|
if (texpected->count == tgot->count)
|
|
{
|
|
INT i;
|
|
/* compare */
|
|
for (i = 0; i < texpected->count; i++)
|
|
{
|
|
call_entry_t *expected = &texpected->calls[i];
|
|
call_entry_t *got = &tgot->calls[i];
|
|
INT j;
|
|
|
|
ok_(__FILE__, line)(expected->id == got->id, "got different ids %d: %d, %d\n", i+1, expected->id, got->id);
|
|
|
|
for (j = 0; j < 5; j++)
|
|
{
|
|
ok_(__FILE__, line)(expected->args[j] == got->args[j], "got different args[%d] for %d: %p, %p\n", j, i+1,
|
|
expected->args[j], got->args[j]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ok_(__FILE__, line)(0, "traces length mismatch\n");
|
|
}
|
|
|
|
#define ok_trace(a, b) ok_trace_(a, b, __LINE__)
|
|
|
|
/* trace of actually made calls */
|
|
static call_trace_t trace_got;
|
|
|
|
static void test_GetAcceptLanguagesA(void)
|
|
{
|
|
static LPCSTR table[] = {"de,en-gb;q=0.7,en;q=0.3",
|
|
"de,en;q=0.3,en-gb;q=0.7", /* sorting is ignored */
|
|
"winetest", /* content is ignored */
|
|
"de-de,de;q=0.5",
|
|
"de",
|
|
NULL};
|
|
|
|
DWORD exactsize;
|
|
char original[512];
|
|
char language[32];
|
|
char buffer[64];
|
|
HKEY hroot = NULL;
|
|
LONG res_query = ERROR_SUCCESS;
|
|
LONG lres;
|
|
HRESULT hr;
|
|
DWORD maxlen = sizeof(buffer) - 2;
|
|
DWORD len;
|
|
LCID lcid;
|
|
LPCSTR entry;
|
|
INT i = 0;
|
|
|
|
if (!pGetAcceptLanguagesA) {
|
|
win_skip("GetAcceptLanguagesA is not available\n");
|
|
return;
|
|
}
|
|
|
|
lcid = GetUserDefaultLCID();
|
|
|
|
/* Get the original Value */
|
|
lres = RegOpenKeyA(HKEY_CURRENT_USER, ie_international, &hroot);
|
|
if (lres) {
|
|
skip("RegOpenKey(%s) failed: %d\n", ie_international, lres);
|
|
return;
|
|
}
|
|
len = sizeof(original);
|
|
original[0] = 0;
|
|
res_query = RegQueryValueExA(hroot, acceptlanguage, 0, NULL, (PBYTE)original, &len);
|
|
|
|
RegDeleteValue(hroot, acceptlanguage);
|
|
|
|
/* Some windows versions use "lang-COUNTRY" as default */
|
|
memset(language, 0, sizeof(language));
|
|
len = GetLocaleInfoA(lcid, LOCALE_SISO639LANGNAME, language, sizeof(language));
|
|
|
|
if (len) {
|
|
lstrcat(language, "-");
|
|
memset(buffer, 0, sizeof(buffer));
|
|
len = GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, buffer, sizeof(buffer) - len - 1);
|
|
lstrcat(language, buffer);
|
|
}
|
|
else
|
|
{
|
|
/* LOCALE_SNAME has additional parts in some languages. Try only as last chance */
|
|
memset(language, 0, sizeof(language));
|
|
len = GetLocaleInfoA(lcid, LOCALE_SNAME, language, sizeof(language));
|
|
}
|
|
|
|
/* get the default value */
|
|
len = maxlen;
|
|
memset(buffer, '#', maxlen);
|
|
buffer[maxlen] = 0;
|
|
hr = pGetAcceptLanguagesA( buffer, &len);
|
|
|
|
if (hr != S_OK) {
|
|
win_skip("GetAcceptLanguagesA failed with 0x%x\n", hr);
|
|
goto restore_original;
|
|
}
|
|
|
|
if (lstrcmpA(buffer, language)) {
|
|
/* some windows versions use "lang" or "lang-country" as default */
|
|
language[0] = 0;
|
|
if (pLcidToRfc1766A) {
|
|
hr = pLcidToRfc1766A(lcid, language, sizeof(language));
|
|
ok(hr == S_OK, "LcidToRfc1766A returned 0x%x and %s\n", hr, language);
|
|
}
|
|
}
|
|
|
|
ok(!lstrcmpA(buffer, language),
|
|
"have '%s' (searching for '%s')\n", language, buffer);
|
|
|
|
if (lstrcmpA(buffer, language)) {
|
|
win_skip("no more ideas, how to build the default language '%s'\n", buffer);
|
|
goto restore_original;
|
|
}
|
|
|
|
trace("detected default: %s\n", language);
|
|
while ((entry = table[i])) {
|
|
|
|
exactsize = lstrlenA(entry);
|
|
|
|
lres = RegSetValueExA(hroot, acceptlanguage, 0, REG_SZ, (const BYTE *) entry, exactsize + 1);
|
|
ok(!lres, "got %d for RegSetValueExA: %s\n", lres, entry);
|
|
|
|
/* len includes space for the terminating 0 before vista/w2k8 */
|
|
len = exactsize + 2;
|
|
memset(buffer, '#', maxlen);
|
|
buffer[maxlen] = 0;
|
|
hr = pGetAcceptLanguagesA( buffer, &len);
|
|
ok(((hr == E_INVALIDARG) && (len == 0)) ||
|
|
(SUCCEEDED(hr) &&
|
|
((len == exactsize) || (len == exactsize+1)) &&
|
|
!lstrcmpA(buffer, entry)),
|
|
"+2_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
|
|
|
|
len = exactsize + 1;
|
|
memset(buffer, '#', maxlen);
|
|
buffer[maxlen] = 0;
|
|
hr = pGetAcceptLanguagesA( buffer, &len);
|
|
ok(((hr == E_INVALIDARG) && (len == 0)) ||
|
|
(SUCCEEDED(hr) &&
|
|
((len == exactsize) || (len == exactsize+1)) &&
|
|
!lstrcmpA(buffer, entry)),
|
|
"+1_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
|
|
|
|
len = exactsize;
|
|
memset(buffer, '#', maxlen);
|
|
buffer[maxlen] = 0;
|
|
hr = pGetAcceptLanguagesA( buffer, &len);
|
|
|
|
/* There is no space for the string in the registry.
|
|
When the buffer is large enough, the default language is returned
|
|
|
|
When the buffer is too small for that fallback, win7_32 and w2k8_64
|
|
and above fail with HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), but
|
|
recent os succeed and return a partial result while
|
|
older os succeed and overflow the buffer */
|
|
|
|
ok(((hr == E_INVALIDARG) && (len == 0)) ||
|
|
(((hr == S_OK) && !lstrcmpA(buffer, language) && (len == lstrlenA(language))) ||
|
|
((hr == S_OK) && !memcmp(buffer, language, len)) ||
|
|
((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len)),
|
|
"==_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
|
|
|
|
if (exactsize > 1) {
|
|
len = exactsize - 1;
|
|
memset(buffer, '#', maxlen);
|
|
buffer[maxlen] = 0;
|
|
hr = pGetAcceptLanguagesA( buffer, &len);
|
|
ok(((hr == E_INVALIDARG) && (len == 0)) ||
|
|
(((hr == S_OK) && !lstrcmpA(buffer, language) && (len == lstrlenA(language))) ||
|
|
((hr == S_OK) && !memcmp(buffer, language, len)) ||
|
|
((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len)),
|
|
"-1_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
|
|
}
|
|
|
|
len = 1;
|
|
memset(buffer, '#', maxlen);
|
|
buffer[maxlen] = 0;
|
|
hr = pGetAcceptLanguagesA( buffer, &len);
|
|
ok(((hr == E_INVALIDARG) && (len == 0)) ||
|
|
(((hr == S_OK) && !lstrcmpA(buffer, language) && (len == lstrlenA(language))) ||
|
|
((hr == S_OK) && !memcmp(buffer, language, len)) ||
|
|
((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len)),
|
|
"=1_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
|
|
|
|
len = maxlen;
|
|
hr = pGetAcceptLanguagesA( NULL, &len);
|
|
|
|
/* w2k3 and below: E_FAIL and untouched len,
|
|
since w2k8: S_OK and needed size (excluding 0) */
|
|
ok( ((hr == S_OK) && (len == exactsize)) ||
|
|
((hr == E_FAIL) && (len == maxlen)),
|
|
"NULL,max #%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
|
|
|
|
i++;
|
|
}
|
|
|
|
/* without a value in the registry, a default language is returned */
|
|
RegDeleteValue(hroot, acceptlanguage);
|
|
|
|
len = maxlen;
|
|
memset(buffer, '#', maxlen);
|
|
buffer[maxlen] = 0;
|
|
hr = pGetAcceptLanguagesA( buffer, &len);
|
|
ok( ((hr == S_OK) && (len == lstrlenA(language))),
|
|
"max: got 0x%x with %d and %s (expected S_OK with %d and '%s'\n",
|
|
hr, len, buffer, lstrlenA(language), language);
|
|
|
|
len = 2;
|
|
memset(buffer, '#', maxlen);
|
|
buffer[maxlen] = 0;
|
|
hr = pGetAcceptLanguagesA( buffer, &len);
|
|
ok( (((hr == S_OK) || (hr == E_INVALIDARG)) && !memcmp(buffer, language, len)) ||
|
|
((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len),
|
|
"=2: got 0x%x with %d and %s\n", hr, len, buffer);
|
|
|
|
len = 1;
|
|
memset(buffer, '#', maxlen);
|
|
buffer[maxlen] = 0;
|
|
hr = pGetAcceptLanguagesA( buffer, &len);
|
|
/* When the buffer is too small, win7_32 and w2k8_64 and above fail with
|
|
HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), other versions suceed
|
|
and return a partial 0 terminated result while other versions
|
|
fail with E_INVALIDARG and return a partial unterminated result */
|
|
ok( (((hr == S_OK) || (hr == E_INVALIDARG)) && !memcmp(buffer, language, len)) ||
|
|
((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len),
|
|
"=1: got 0x%x with %d and %s\n", hr, len, buffer);
|
|
|
|
len = 0;
|
|
memset(buffer, '#', maxlen);
|
|
buffer[maxlen] = 0;
|
|
hr = pGetAcceptLanguagesA( buffer, &len);
|
|
/* w2k3 and below: E_FAIL, since w2k8: E_INVALIDARG */
|
|
ok((hr == E_FAIL) || (hr == E_INVALIDARG),
|
|
"got 0x%x (expected E_FAIL or E_INVALIDARG)\n", hr);
|
|
|
|
memset(buffer, '#', maxlen);
|
|
buffer[maxlen] = 0;
|
|
hr = pGetAcceptLanguagesA( buffer, NULL);
|
|
/* w2k3 and below: E_FAIL, since w2k8: E_INVALIDARG */
|
|
ok((hr == E_FAIL) || (hr == E_INVALIDARG),
|
|
"got 0x%x (expected E_FAIL or E_INVALIDARG)\n", hr);
|
|
|
|
|
|
hr = pGetAcceptLanguagesA( NULL, NULL);
|
|
/* w2k3 and below: E_FAIL, since w2k8: E_INVALIDARG */
|
|
ok((hr == E_FAIL) || (hr == E_INVALIDARG),
|
|
"got 0x%x (expected E_FAIL or E_INVALIDARG)\n", hr);
|
|
|
|
restore_original:
|
|
if (!res_query) {
|
|
len = lstrlenA(original);
|
|
lres = RegSetValueExA(hroot, acceptlanguage, 0, REG_SZ, (const BYTE *) original, len ? len + 1: 0);
|
|
ok(!lres, "RegSetValueEx(%s) failed: %d\n", original, lres);
|
|
}
|
|
else
|
|
{
|
|
RegDeleteValue(hroot, acceptlanguage);
|
|
}
|
|
RegCloseKey(hroot);
|
|
}
|
|
|
|
static void test_SHSearchMapInt(void)
|
|
{
|
|
int keys[8], values[8];
|
|
int i = 0;
|
|
|
|
if (!pSHSearchMapInt)
|
|
return;
|
|
|
|
memset(keys, 0, sizeof(keys));
|
|
memset(values, 0, sizeof(values));
|
|
keys[0] = 99; values[0] = 101;
|
|
|
|
/* NULL key/value lists crash native, so skip testing them */
|
|
|
|
/* 1 element */
|
|
i = pSHSearchMapInt(keys, values, 1, keys[0]);
|
|
ok(i == values[0], "Len 1, expected %d, got %d\n", values[0], i);
|
|
|
|
/* Key doesn't exist */
|
|
i = pSHSearchMapInt(keys, values, 1, 100);
|
|
ok(i == -1, "Len 1 - bad key, expected -1, got %d\n", i);
|
|
|
|
/* Len = 0 => not found */
|
|
i = pSHSearchMapInt(keys, values, 0, keys[0]);
|
|
ok(i == -1, "Len 1 - passed len 0, expected -1, got %d\n", i);
|
|
|
|
/* 2 elements, len = 1 */
|
|
keys[1] = 98; values[1] = 102;
|
|
i = pSHSearchMapInt(keys, values, 1, keys[1]);
|
|
ok(i == -1, "Len 1 - array len 2, expected -1, got %d\n", i);
|
|
|
|
/* 2 elements, len = 2 */
|
|
i = pSHSearchMapInt(keys, values, 2, keys[1]);
|
|
ok(i == values[1], "Len 2, expected %d, got %d\n", values[1], i);
|
|
|
|
/* Searches forward */
|
|
keys[2] = 99; values[2] = 103;
|
|
i = pSHSearchMapInt(keys, values, 3, keys[0]);
|
|
ok(i == values[0], "Len 3, expected %d, got %d\n", values[0], i);
|
|
}
|
|
|
|
static void test_alloc_shared(void)
|
|
{
|
|
DWORD procid;
|
|
HANDLE hmem;
|
|
int val;
|
|
int* p;
|
|
BOOL ret;
|
|
|
|
procid=GetCurrentProcessId();
|
|
hmem=pSHAllocShared(NULL,10,procid);
|
|
ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %u\n", GetLastError());
|
|
ret = pSHFreeShared(hmem, procid);
|
|
ok( ret, "SHFreeShared failed: %u\n", GetLastError());
|
|
|
|
val=0x12345678;
|
|
hmem=pSHAllocShared(&val,4,procid);
|
|
ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %u\n", GetLastError());
|
|
|
|
p=pSHLockShared(hmem,procid);
|
|
ok(p!=NULL,"SHLockShared failed: %u\n", GetLastError());
|
|
if (p!=NULL)
|
|
ok(*p==val,"Wrong value in shared memory: %d instead of %d\n",*p,val);
|
|
ret = pSHUnlockShared(p);
|
|
ok( ret, "SHUnlockShared failed: %u\n", GetLastError());
|
|
|
|
ret = pSHFreeShared(hmem, procid);
|
|
ok( ret, "SHFreeShared failed: %u\n", GetLastError());
|
|
}
|
|
|
|
static void test_fdsa(void)
|
|
{
|
|
typedef struct
|
|
{
|
|
DWORD num_items; /* Number of elements inserted */
|
|
void *mem; /* Ptr to array */
|
|
DWORD blocks_alloced; /* Number of elements allocated */
|
|
BYTE inc; /* Number of elements to grow by when we need to expand */
|
|
BYTE block_size; /* Size in bytes of an element */
|
|
BYTE flags; /* Flags */
|
|
} FDSA_info;
|
|
|
|
BOOL (WINAPI *pFDSA_Initialize)(DWORD block_size, DWORD inc, FDSA_info *info, void *mem,
|
|
DWORD init_blocks);
|
|
BOOL (WINAPI *pFDSA_Destroy)(FDSA_info *info);
|
|
DWORD (WINAPI *pFDSA_InsertItem)(FDSA_info *info, DWORD where, const void *block);
|
|
BOOL (WINAPI *pFDSA_DeleteItem)(FDSA_info *info, DWORD where);
|
|
|
|
FDSA_info info;
|
|
int block_size = 10, init_blocks = 4, inc = 2;
|
|
DWORD ret;
|
|
char *mem;
|
|
|
|
pFDSA_Initialize = (void *)GetProcAddress(hShlwapi, (LPSTR)208);
|
|
pFDSA_Destroy = (void *)GetProcAddress(hShlwapi, (LPSTR)209);
|
|
pFDSA_InsertItem = (void *)GetProcAddress(hShlwapi, (LPSTR)210);
|
|
pFDSA_DeleteItem = (void *)GetProcAddress(hShlwapi, (LPSTR)211);
|
|
|
|
mem = HeapAlloc(GetProcessHeap(), 0, block_size * init_blocks);
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
ok(pFDSA_Initialize(block_size, inc, &info, mem, init_blocks), "FDSA_Initialize rets FALSE\n");
|
|
ok(info.num_items == 0, "num_items = %d\n", info.num_items);
|
|
ok(info.mem == mem, "mem = %p\n", info.mem);
|
|
ok(info.blocks_alloced == init_blocks, "blocks_alloced = %d\n", info.blocks_alloced);
|
|
ok(info.inc == inc, "inc = %d\n", info.inc);
|
|
ok(info.block_size == block_size, "block_size = %d\n", info.block_size);
|
|
ok(info.flags == 0, "flags = %d\n", info.flags);
|
|
|
|
ret = pFDSA_InsertItem(&info, 1234, "1234567890");
|
|
ok(ret == 0, "ret = %d\n", ret);
|
|
ok(info.num_items == 1, "num_items = %d\n", info.num_items);
|
|
ok(info.mem == mem, "mem = %p\n", info.mem);
|
|
ok(info.blocks_alloced == init_blocks, "blocks_alloced = %d\n", info.blocks_alloced);
|
|
ok(info.inc == inc, "inc = %d\n", info.inc);
|
|
ok(info.block_size == block_size, "block_size = %d\n", info.block_size);
|
|
ok(info.flags == 0, "flags = %d\n", info.flags);
|
|
|
|
ret = pFDSA_InsertItem(&info, 1234, "abcdefghij");
|
|
ok(ret == 1, "ret = %d\n", ret);
|
|
|
|
ret = pFDSA_InsertItem(&info, 1, "klmnopqrst");
|
|
ok(ret == 1, "ret = %d\n", ret);
|
|
|
|
ret = pFDSA_InsertItem(&info, 0, "uvwxyzABCD");
|
|
ok(ret == 0, "ret = %d\n", ret);
|
|
ok(info.mem == mem, "mem = %p\n", info.mem);
|
|
ok(info.flags == 0, "flags = %d\n", info.flags);
|
|
|
|
/* This next InsertItem will cause shlwapi to allocate its own mem buffer */
|
|
ret = pFDSA_InsertItem(&info, 0, "EFGHIJKLMN");
|
|
ok(ret == 0, "ret = %d\n", ret);
|
|
ok(info.mem != mem, "mem = %p\n", info.mem);
|
|
ok(info.blocks_alloced == init_blocks + inc, "blocks_alloced = %d\n", info.blocks_alloced);
|
|
ok(info.flags == 0x1, "flags = %d\n", info.flags);
|
|
|
|
ok(!memcmp(info.mem, "EFGHIJKLMNuvwxyzABCD1234567890klmnopqrstabcdefghij", 50), "mem %s\n", (char*)info.mem);
|
|
|
|
ok(pFDSA_DeleteItem(&info, 2), "rets FALSE\n");
|
|
ok(info.mem != mem, "mem = %p\n", info.mem);
|
|
ok(info.blocks_alloced == init_blocks + inc, "blocks_alloced = %d\n", info.blocks_alloced);
|
|
ok(info.flags == 0x1, "flags = %d\n", info.flags);
|
|
|
|
ok(!memcmp(info.mem, "EFGHIJKLMNuvwxyzABCDklmnopqrstabcdefghij", 40), "mem %s\n", (char*)info.mem);
|
|
|
|
ok(pFDSA_DeleteItem(&info, 3), "rets FALSE\n");
|
|
ok(info.mem != mem, "mem = %p\n", info.mem);
|
|
ok(info.blocks_alloced == init_blocks + inc, "blocks_alloced = %d\n", info.blocks_alloced);
|
|
ok(info.flags == 0x1, "flags = %d\n", info.flags);
|
|
|
|
ok(!memcmp(info.mem, "EFGHIJKLMNuvwxyzABCDklmnopqrst", 30), "mem %s\n", (char*)info.mem);
|
|
|
|
ok(!pFDSA_DeleteItem(&info, 4), "does not ret FALSE\n");
|
|
|
|
/* As shlwapi has allocated memory internally, Destroy will ret FALSE */
|
|
ok(!pFDSA_Destroy(&info), "FDSA_Destroy does not ret FALSE\n");
|
|
|
|
|
|
/* When Initialize is called with inc = 0, set it to 1 */
|
|
ok(pFDSA_Initialize(block_size, 0, &info, mem, init_blocks), "FDSA_Initialize rets FALSE\n");
|
|
ok(info.inc == 1, "inc = %d\n", info.inc);
|
|
|
|
/* This time, because shlwapi hasn't had to allocate memory
|
|
internally, Destroy rets non-zero */
|
|
ok(pFDSA_Destroy(&info), "FDSA_Destroy rets FALSE\n");
|
|
|
|
|
|
HeapFree(GetProcessHeap(), 0, mem);
|
|
}
|
|
|
|
|
|
typedef struct SHELL_USER_SID {
|
|
SID_IDENTIFIER_AUTHORITY sidAuthority;
|
|
DWORD dwUserGroupID;
|
|
DWORD dwUserID;
|
|
} SHELL_USER_SID, *PSHELL_USER_SID;
|
|
typedef struct SHELL_USER_PERMISSION {
|
|
SHELL_USER_SID susID;
|
|
DWORD dwAccessType;
|
|
BOOL fInherit;
|
|
DWORD dwAccessMask;
|
|
DWORD dwInheritMask;
|
|
DWORD dwInheritAccessMask;
|
|
} SHELL_USER_PERMISSION, *PSHELL_USER_PERMISSION;
|
|
static void test_GetShellSecurityDescriptor(void)
|
|
{
|
|
SHELL_USER_PERMISSION supCurrentUserFull = {
|
|
{ {SECURITY_NULL_SID_AUTHORITY}, 0, 0 },
|
|
ACCESS_ALLOWED_ACE_TYPE, FALSE,
|
|
GENERIC_ALL, 0, 0 };
|
|
#define MY_INHERITANCE 0xBE /* invalid value to proof behavior */
|
|
SHELL_USER_PERMISSION supEveryoneDenied = {
|
|
{ {SECURITY_WORLD_SID_AUTHORITY}, SECURITY_WORLD_RID, 0 },
|
|
ACCESS_DENIED_ACE_TYPE, TRUE,
|
|
GENERIC_WRITE, MY_INHERITANCE | 0xDEADBA00, GENERIC_READ };
|
|
PSHELL_USER_PERMISSION rgsup[2] = {
|
|
&supCurrentUserFull, &supEveryoneDenied,
|
|
};
|
|
SECURITY_DESCRIPTOR* psd;
|
|
SECURITY_DESCRIPTOR* (WINAPI*pGetShellSecurityDescriptor)(PSHELL_USER_PERMISSION*,int);
|
|
void *pChrCmpIW = GetProcAddress(hShlwapi, "ChrCmpIW");
|
|
|
|
pGetShellSecurityDescriptor=(void*)GetProcAddress(hShlwapi,(char*)475);
|
|
|
|
if(!pGetShellSecurityDescriptor)
|
|
{
|
|
win_skip("GetShellSecurityDescriptor not available\n");
|
|
return;
|
|
}
|
|
|
|
if(pChrCmpIW && pChrCmpIW == pGetShellSecurityDescriptor) /* win2k */
|
|
{
|
|
win_skip("Skipping for GetShellSecurityDescriptor, same ordinal used for ChrCmpIW\n");
|
|
return;
|
|
}
|
|
|
|
psd = pGetShellSecurityDescriptor(NULL, 2);
|
|
ok(psd==NULL ||
|
|
broken(psd==INVALID_HANDLE_VALUE), /* IE5 */
|
|
"GetShellSecurityDescriptor should fail\n");
|
|
psd = pGetShellSecurityDescriptor(rgsup, 0);
|
|
ok(psd==NULL, "GetShellSecurityDescriptor should fail, got %p\n", psd);
|
|
|
|
SetLastError(0xdeadbeef);
|
|
psd = pGetShellSecurityDescriptor(rgsup, 2);
|
|
if (psd == NULL && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
|
|
{
|
|
/* The previous calls to GetShellSecurityDescriptor don't set the last error */
|
|
win_skip("GetShellSecurityDescriptor is not implemented\n");
|
|
return;
|
|
}
|
|
if (psd == INVALID_HANDLE_VALUE)
|
|
{
|
|
win_skip("GetShellSecurityDescriptor is broken on IE5\n");
|
|
return;
|
|
}
|
|
ok(psd!=NULL, "GetShellSecurityDescriptor failed\n");
|
|
if (psd!=NULL)
|
|
{
|
|
BOOL bHasDacl = FALSE, bDefaulted;
|
|
PACL pAcl;
|
|
DWORD dwRev;
|
|
SECURITY_DESCRIPTOR_CONTROL control;
|
|
|
|
ok(IsValidSecurityDescriptor(psd), "returned value is not valid SD\n");
|
|
|
|
ok(GetSecurityDescriptorControl(psd, &control, &dwRev),
|
|
"GetSecurityDescriptorControl failed with error %u\n", GetLastError());
|
|
ok(0 == (control & SE_SELF_RELATIVE), "SD should be absolute\n");
|
|
|
|
ok(GetSecurityDescriptorDacl(psd, &bHasDacl, &pAcl, &bDefaulted),
|
|
"GetSecurityDescriptorDacl failed with error %u\n", GetLastError());
|
|
|
|
ok(bHasDacl, "SD has no DACL\n");
|
|
if (bHasDacl)
|
|
{
|
|
ok(!bDefaulted, "DACL should not be defaulted\n");
|
|
|
|
ok(pAcl != NULL, "NULL DACL!\n");
|
|
if (pAcl != NULL)
|
|
{
|
|
ACL_SIZE_INFORMATION asiSize;
|
|
|
|
ok(IsValidAcl(pAcl), "DACL is not valid\n");
|
|
|
|
ok(GetAclInformation(pAcl, &asiSize, sizeof(asiSize), AclSizeInformation),
|
|
"GetAclInformation failed with error %u\n", GetLastError());
|
|
|
|
ok(asiSize.AceCount == 3, "Incorrect number of ACEs: %d entries\n", asiSize.AceCount);
|
|
if (asiSize.AceCount == 3)
|
|
{
|
|
ACCESS_ALLOWED_ACE *paaa; /* will use for DENIED too */
|
|
|
|
ok(GetAce(pAcl, 0, (LPVOID*)&paaa), "GetAce failed with error %u\n", GetLastError());
|
|
ok(paaa->Header.AceType == ACCESS_ALLOWED_ACE_TYPE,
|
|
"Invalid ACE type %d\n", paaa->Header.AceType);
|
|
ok(paaa->Header.AceFlags == 0, "Invalid ACE flags %x\n", paaa->Header.AceFlags);
|
|
ok(paaa->Mask == GENERIC_ALL, "Invalid ACE mask %x\n", paaa->Mask);
|
|
|
|
ok(GetAce(pAcl, 1, (LPVOID*)&paaa), "GetAce failed with error %u\n", GetLastError());
|
|
ok(paaa->Header.AceType == ACCESS_DENIED_ACE_TYPE,
|
|
"Invalid ACE type %d\n", paaa->Header.AceType);
|
|
/* first one of two ACEs generated from inheritable entry - without inheritance */
|
|
ok(paaa->Header.AceFlags == 0, "Invalid ACE flags %x\n", paaa->Header.AceFlags);
|
|
ok(paaa->Mask == GENERIC_WRITE, "Invalid ACE mask %x\n", paaa->Mask);
|
|
|
|
ok(GetAce(pAcl, 2, (LPVOID*)&paaa), "GetAce failed with error %u\n", GetLastError());
|
|
ok(paaa->Header.AceType == ACCESS_DENIED_ACE_TYPE,
|
|
"Invalid ACE type %d\n", paaa->Header.AceType);
|
|
/* second ACE - with inheritance */
|
|
ok(paaa->Header.AceFlags == MY_INHERITANCE,
|
|
"Invalid ACE flags %x\n", paaa->Header.AceFlags);
|
|
ok(paaa->Mask == GENERIC_READ, "Invalid ACE mask %x\n", paaa->Mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFree(psd);
|
|
}
|
|
}
|
|
|
|
static void test_SHPackDispParams(void)
|
|
{
|
|
DISPPARAMS params;
|
|
VARIANT vars[10];
|
|
HRESULT hres;
|
|
|
|
if(!pSHPackDispParams)
|
|
win_skip("SHPackSidpParams not available\n");
|
|
|
|
memset(¶ms, 0xc0, sizeof(params));
|
|
memset(vars, 0xc0, sizeof(vars));
|
|
hres = pSHPackDispParams(¶ms, vars, 1, VT_I4, 0xdeadbeef);
|
|
ok(hres == S_OK, "SHPackDispParams failed: %08x\n", hres);
|
|
ok(params.cArgs == 1, "params.cArgs = %d\n", params.cArgs);
|
|
ok(params.cNamedArgs == 0, "params.cNamedArgs = %d\n", params.cArgs);
|
|
ok(params.rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", params.rgdispidNamedArgs);
|
|
ok(params.rgvarg == vars, "params.rgvarg = %p\n", params.rgvarg);
|
|
ok(V_VT(vars) == VT_I4, "V_VT(var) = %d\n", V_VT(vars));
|
|
ok(V_I4(vars) == 0xdeadbeef, "failed %x\n", V_I4(vars));
|
|
|
|
memset(¶ms, 0xc0, sizeof(params));
|
|
hres = pSHPackDispParams(¶ms, NULL, 0, 0);
|
|
ok(hres == S_OK, "SHPackDispParams failed: %08x\n", hres);
|
|
ok(params.cArgs == 0, "params.cArgs = %d\n", params.cArgs);
|
|
ok(params.cNamedArgs == 0, "params.cNamedArgs = %d\n", params.cArgs);
|
|
ok(params.rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", params.rgdispidNamedArgs);
|
|
ok(params.rgvarg == NULL, "params.rgvarg = %p\n", params.rgvarg);
|
|
|
|
memset(vars, 0xc0, sizeof(vars));
|
|
memset(¶ms, 0xc0, sizeof(params));
|
|
hres = pSHPackDispParams(¶ms, vars, 4, VT_BSTR, (void*)0xdeadbeef, VT_EMPTY, 10,
|
|
VT_I4, 100, VT_DISPATCH, (void*)0xdeadbeef);
|
|
ok(hres == S_OK, "SHPackDispParams failed: %08x\n", hres);
|
|
ok(params.cArgs == 4, "params.cArgs = %d\n", params.cArgs);
|
|
ok(params.cNamedArgs == 0, "params.cNamedArgs = %d\n", params.cArgs);
|
|
ok(params.rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", params.rgdispidNamedArgs);
|
|
ok(params.rgvarg == vars, "params.rgvarg = %p\n", params.rgvarg);
|
|
ok(V_VT(vars) == VT_DISPATCH, "V_VT(vars[0]) = %x\n", V_VT(vars));
|
|
ok(V_I4(vars) == 0xdeadbeef, "V_I4(vars[0]) = %x\n", V_I4(vars));
|
|
ok(V_VT(vars+1) == VT_I4, "V_VT(vars[1]) = %d\n", V_VT(vars+1));
|
|
ok(V_I4(vars+1) == 100, "V_I4(vars[1]) = %x\n", V_I4(vars+1));
|
|
ok(V_VT(vars+2) == VT_I4, "V_VT(vars[2]) = %d\n", V_VT(vars+2));
|
|
ok(V_I4(vars+2) == 10, "V_I4(vars[2]) = %x\n", V_I4(vars+2));
|
|
ok(V_VT(vars+3) == VT_BSTR, "V_VT(vars[3]) = %d\n", V_VT(vars+3));
|
|
ok(V_BSTR(vars+3) == (void*)0xdeadbeef, "V_BSTR(vars[3]) = %p\n", V_BSTR(vars+3));
|
|
}
|
|
|
|
typedef struct _disp
|
|
{
|
|
IDispatch IDispatch_iface;
|
|
LONG refCount;
|
|
} Disp;
|
|
|
|
static inline Disp *impl_from_IDispatch(IDispatch *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, Disp, IDispatch_iface);
|
|
}
|
|
|
|
typedef struct _contain
|
|
{
|
|
IConnectionPointContainer IConnectionPointContainer_iface;
|
|
LONG refCount;
|
|
|
|
UINT ptCount;
|
|
IConnectionPoint **pt;
|
|
} Contain;
|
|
|
|
static inline Contain *impl_from_IConnectionPointContainer(IConnectionPointContainer *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, Contain, IConnectionPointContainer_iface);
|
|
}
|
|
|
|
typedef struct _cntptn
|
|
{
|
|
IConnectionPoint IConnectionPoint_iface;
|
|
LONG refCount;
|
|
|
|
Contain *container;
|
|
GUID id;
|
|
UINT sinkCount;
|
|
IUnknown **sink;
|
|
} ConPt;
|
|
|
|
static inline ConPt *impl_from_IConnectionPoint(IConnectionPoint *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, ConPt, IConnectionPoint_iface);
|
|
}
|
|
|
|
typedef struct _enum
|
|
{
|
|
IEnumConnections IEnumConnections_iface;
|
|
LONG refCount;
|
|
|
|
UINT idx;
|
|
ConPt *pt;
|
|
} EnumCon;
|
|
|
|
static inline EnumCon *impl_from_IEnumConnections(IEnumConnections *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, EnumCon, IEnumConnections_iface);
|
|
}
|
|
|
|
typedef struct _enumpt
|
|
{
|
|
IEnumConnectionPoints IEnumConnectionPoints_iface;
|
|
LONG refCount;
|
|
|
|
int idx;
|
|
Contain *container;
|
|
} EnumPt;
|
|
|
|
static inline EnumPt *impl_from_IEnumConnectionPoints(IEnumConnectionPoints *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, EnumPt, IEnumConnectionPoints_iface);
|
|
}
|
|
|
|
|
|
static HRESULT WINAPI Disp_QueryInterface(
|
|
IDispatch* This,
|
|
REFIID riid,
|
|
void **ppvObject)
|
|
{
|
|
*ppvObject = NULL;
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch))
|
|
{
|
|
*ppvObject = This;
|
|
}
|
|
|
|
if (*ppvObject)
|
|
{
|
|
IUnknown_AddRef(This);
|
|
return S_OK;
|
|
}
|
|
|
|
trace("no interface\n");
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI Disp_AddRef(IDispatch* This)
|
|
{
|
|
Disp *iface = impl_from_IDispatch(This);
|
|
return InterlockedIncrement(&iface->refCount);
|
|
}
|
|
|
|
static ULONG WINAPI Disp_Release(IDispatch* This)
|
|
{
|
|
Disp *iface = impl_from_IDispatch(This);
|
|
ULONG ret;
|
|
|
|
ret = InterlockedDecrement(&iface->refCount);
|
|
if (ret == 0)
|
|
HeapFree(GetProcessHeap(),0,This);
|
|
return ret;
|
|
}
|
|
|
|
static HRESULT WINAPI Disp_GetTypeInfoCount(
|
|
IDispatch* This,
|
|
UINT *pctinfo)
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static HRESULT WINAPI Disp_GetTypeInfo(
|
|
IDispatch* This,
|
|
UINT iTInfo,
|
|
LCID lcid,
|
|
ITypeInfo **ppTInfo)
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static HRESULT WINAPI Disp_GetIDsOfNames(
|
|
IDispatch* This,
|
|
REFIID riid,
|
|
LPOLESTR *rgszNames,
|
|
UINT cNames,
|
|
LCID lcid,
|
|
DISPID *rgDispId)
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static HRESULT WINAPI Disp_Invoke(
|
|
IDispatch* This,
|
|
DISPID dispIdMember,
|
|
REFIID riid,
|
|
LCID lcid,
|
|
WORD wFlags,
|
|
DISPPARAMS *pDispParams,
|
|
VARIANT *pVarResult,
|
|
EXCEPINFO *pExcepInfo,
|
|
UINT *puArgErr)
|
|
{
|
|
trace("%p %x %p %x %x %p %p %p %p\n",This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
|
|
|
|
ok(dispIdMember == 0xa0 || dispIdMember == 0xa1, "Unknown dispIdMember\n");
|
|
ok(pDispParams != NULL, "Invoked with NULL pDispParams\n");
|
|
ok(wFlags == DISPATCH_METHOD, "Wrong flags %x\n",wFlags);
|
|
ok(lcid == 0,"Wrong lcid %x\n",lcid);
|
|
if (dispIdMember == 0xa0)
|
|
{
|
|
ok(pDispParams->cArgs == 0, "params.cArgs = %d\n", pDispParams->cArgs);
|
|
ok(pDispParams->cNamedArgs == 0, "params.cNamedArgs = %d\n", pDispParams->cArgs);
|
|
ok(pDispParams->rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", pDispParams->rgdispidNamedArgs);
|
|
ok(pDispParams->rgvarg == NULL, "params.rgvarg = %p\n", pDispParams->rgvarg);
|
|
}
|
|
else if (dispIdMember == 0xa1)
|
|
{
|
|
ok(pDispParams->cArgs == 2, "params.cArgs = %d\n", pDispParams->cArgs);
|
|
ok(pDispParams->cNamedArgs == 0, "params.cNamedArgs = %d\n", pDispParams->cArgs);
|
|
ok(pDispParams->rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", pDispParams->rgdispidNamedArgs);
|
|
ok(V_VT(pDispParams->rgvarg) == VT_BSTR, "V_VT(var) = %d\n", V_VT(pDispParams->rgvarg));
|
|
ok(V_I4(pDispParams->rgvarg) == 0xdeadcafe , "failed %p\n", V_BSTR(pDispParams->rgvarg));
|
|
ok(V_VT(pDispParams->rgvarg+1) == VT_I4, "V_VT(var) = %d\n", V_VT(pDispParams->rgvarg+1));
|
|
ok(V_I4(pDispParams->rgvarg+1) == 0xdeadbeef, "failed %x\n", V_I4(pDispParams->rgvarg+1));
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static const IDispatchVtbl disp_vtbl = {
|
|
Disp_QueryInterface,
|
|
Disp_AddRef,
|
|
Disp_Release,
|
|
|
|
Disp_GetTypeInfoCount,
|
|
Disp_GetTypeInfo,
|
|
Disp_GetIDsOfNames,
|
|
Disp_Invoke
|
|
};
|
|
|
|
static HRESULT WINAPI Enum_QueryInterface(
|
|
IEnumConnections* This,
|
|
REFIID riid,
|
|
void **ppvObject)
|
|
{
|
|
*ppvObject = NULL;
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IEnumConnections))
|
|
{
|
|
*ppvObject = This;
|
|
}
|
|
|
|
if (*ppvObject)
|
|
{
|
|
IUnknown_AddRef(This);
|
|
return S_OK;
|
|
}
|
|
|
|
trace("no interface\n");
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI Enum_AddRef(IEnumConnections* This)
|
|
{
|
|
EnumCon *iface = impl_from_IEnumConnections(This);
|
|
return InterlockedIncrement(&iface->refCount);
|
|
}
|
|
|
|
static ULONG WINAPI Enum_Release(IEnumConnections* This)
|
|
{
|
|
EnumCon *iface = impl_from_IEnumConnections(This);
|
|
ULONG ret;
|
|
|
|
ret = InterlockedDecrement(&iface->refCount);
|
|
if (ret == 0)
|
|
HeapFree(GetProcessHeap(),0,This);
|
|
return ret;
|
|
}
|
|
|
|
static HRESULT WINAPI Enum_Next(
|
|
IEnumConnections* This,
|
|
ULONG cConnections,
|
|
LPCONNECTDATA rgcd,
|
|
ULONG *pcFetched)
|
|
{
|
|
EnumCon *iface = impl_from_IEnumConnections(This);
|
|
|
|
if (cConnections > 0 && iface->idx < iface->pt->sinkCount)
|
|
{
|
|
rgcd->pUnk = iface->pt->sink[iface->idx];
|
|
IUnknown_AddRef(iface->pt->sink[iface->idx]);
|
|
rgcd->dwCookie=0xff;
|
|
if (pcFetched)
|
|
*pcFetched = 1;
|
|
iface->idx++;
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
static HRESULT WINAPI Enum_Skip(
|
|
IEnumConnections* This,
|
|
ULONG cConnections)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
static HRESULT WINAPI Enum_Reset(
|
|
IEnumConnections* This)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
static HRESULT WINAPI Enum_Clone(
|
|
IEnumConnections* This,
|
|
IEnumConnections **ppEnum)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
static const IEnumConnectionsVtbl enum_vtbl = {
|
|
|
|
Enum_QueryInterface,
|
|
Enum_AddRef,
|
|
Enum_Release,
|
|
Enum_Next,
|
|
Enum_Skip,
|
|
Enum_Reset,
|
|
Enum_Clone
|
|
};
|
|
|
|
static HRESULT WINAPI ConPt_QueryInterface(
|
|
IConnectionPoint* This,
|
|
REFIID riid,
|
|
void **ppvObject)
|
|
{
|
|
*ppvObject = NULL;
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IConnectionPoint))
|
|
{
|
|
*ppvObject = This;
|
|
}
|
|
|
|
if (*ppvObject)
|
|
{
|
|
IUnknown_AddRef(This);
|
|
return S_OK;
|
|
}
|
|
|
|
trace("no interface\n");
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI ConPt_AddRef(
|
|
IConnectionPoint* This)
|
|
{
|
|
ConPt *iface = impl_from_IConnectionPoint(This);
|
|
return InterlockedIncrement(&iface->refCount);
|
|
}
|
|
|
|
static ULONG WINAPI ConPt_Release(
|
|
IConnectionPoint* This)
|
|
{
|
|
ConPt *iface = impl_from_IConnectionPoint(This);
|
|
ULONG ret;
|
|
|
|
ret = InterlockedDecrement(&iface->refCount);
|
|
if (ret == 0)
|
|
{
|
|
if (iface->sinkCount > 0)
|
|
{
|
|
int i;
|
|
for (i = 0; i < iface->sinkCount; i++)
|
|
{
|
|
if (iface->sink[i])
|
|
IUnknown_Release(iface->sink[i]);
|
|
}
|
|
HeapFree(GetProcessHeap(),0,iface->sink);
|
|
}
|
|
HeapFree(GetProcessHeap(),0,This);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static HRESULT WINAPI ConPt_GetConnectionInterface(
|
|
IConnectionPoint* This,
|
|
IID *pIID)
|
|
{
|
|
static int i = 0;
|
|
ConPt *iface = impl_from_IConnectionPoint(This);
|
|
if (i==0)
|
|
{
|
|
i++;
|
|
return E_FAIL;
|
|
}
|
|
else
|
|
memcpy(pIID,&iface->id,sizeof(GUID));
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI ConPt_GetConnectionPointContainer(
|
|
IConnectionPoint* This,
|
|
IConnectionPointContainer **ppCPC)
|
|
{
|
|
ConPt *iface = impl_from_IConnectionPoint(This);
|
|
|
|
*ppCPC = &iface->container->IConnectionPointContainer_iface;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI ConPt_Advise(
|
|
IConnectionPoint* This,
|
|
IUnknown *pUnkSink,
|
|
DWORD *pdwCookie)
|
|
{
|
|
ConPt *iface = impl_from_IConnectionPoint(This);
|
|
|
|
if (iface->sinkCount == 0)
|
|
iface->sink = HeapAlloc(GetProcessHeap(),0,sizeof(IUnknown*));
|
|
else
|
|
iface->sink = HeapReAlloc(GetProcessHeap(),0,iface->sink,sizeof(IUnknown*)*(iface->sinkCount+1));
|
|
iface->sink[iface->sinkCount] = pUnkSink;
|
|
IUnknown_AddRef(pUnkSink);
|
|
iface->sinkCount++;
|
|
*pdwCookie = iface->sinkCount;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI ConPt_Unadvise(
|
|
IConnectionPoint* This,
|
|
DWORD dwCookie)
|
|
{
|
|
ConPt *iface = impl_from_IConnectionPoint(This);
|
|
|
|
if (dwCookie > iface->sinkCount)
|
|
return E_FAIL;
|
|
else
|
|
{
|
|
IUnknown_Release(iface->sink[dwCookie-1]);
|
|
iface->sink[dwCookie-1] = NULL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI ConPt_EnumConnections(
|
|
IConnectionPoint* This,
|
|
IEnumConnections **ppEnum)
|
|
{
|
|
EnumCon *ec;
|
|
|
|
ec = HeapAlloc(GetProcessHeap(),0,sizeof(EnumCon));
|
|
ec->IEnumConnections_iface.lpVtbl = &enum_vtbl;
|
|
ec->refCount = 1;
|
|
ec->pt = impl_from_IConnectionPoint(This);
|
|
ec->idx = 0;
|
|
*ppEnum = &ec->IEnumConnections_iface;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static const IConnectionPointVtbl point_vtbl = {
|
|
ConPt_QueryInterface,
|
|
ConPt_AddRef,
|
|
ConPt_Release,
|
|
|
|
ConPt_GetConnectionInterface,
|
|
ConPt_GetConnectionPointContainer,
|
|
ConPt_Advise,
|
|
ConPt_Unadvise,
|
|
ConPt_EnumConnections
|
|
};
|
|
|
|
static HRESULT WINAPI EnumPt_QueryInterface(
|
|
IEnumConnectionPoints* This,
|
|
REFIID riid,
|
|
void **ppvObject)
|
|
{
|
|
*ppvObject = NULL;
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IEnumConnectionPoints))
|
|
{
|
|
*ppvObject = This;
|
|
}
|
|
|
|
if (*ppvObject)
|
|
{
|
|
IUnknown_AddRef(This);
|
|
return S_OK;
|
|
}
|
|
|
|
trace("no interface\n");
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI EnumPt_AddRef(IEnumConnectionPoints* This)
|
|
{
|
|
EnumPt *iface = impl_from_IEnumConnectionPoints(This);
|
|
return InterlockedIncrement(&iface->refCount);
|
|
}
|
|
|
|
static ULONG WINAPI EnumPt_Release(IEnumConnectionPoints* This)
|
|
{
|
|
EnumPt *iface = impl_from_IEnumConnectionPoints(This);
|
|
ULONG ret;
|
|
|
|
ret = InterlockedDecrement(&iface->refCount);
|
|
if (ret == 0)
|
|
HeapFree(GetProcessHeap(),0,This);
|
|
return ret;
|
|
}
|
|
|
|
static HRESULT WINAPI EnumPt_Next(
|
|
IEnumConnectionPoints* This,
|
|
ULONG cConnections,
|
|
IConnectionPoint **rgcd,
|
|
ULONG *pcFetched)
|
|
{
|
|
EnumPt *iface = impl_from_IEnumConnectionPoints(This);
|
|
|
|
if (cConnections > 0 && iface->idx < iface->container->ptCount)
|
|
{
|
|
*rgcd = iface->container->pt[iface->idx];
|
|
IUnknown_AddRef(iface->container->pt[iface->idx]);
|
|
if (pcFetched)
|
|
*pcFetched = 1;
|
|
iface->idx++;
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
static HRESULT WINAPI EnumPt_Skip(
|
|
IEnumConnectionPoints* This,
|
|
ULONG cConnections)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
static HRESULT WINAPI EnumPt_Reset(
|
|
IEnumConnectionPoints* This)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
static HRESULT WINAPI EnumPt_Clone(
|
|
IEnumConnectionPoints* This,
|
|
IEnumConnectionPoints **ppEnumPt)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
static const IEnumConnectionPointsVtbl enumpt_vtbl = {
|
|
|
|
EnumPt_QueryInterface,
|
|
EnumPt_AddRef,
|
|
EnumPt_Release,
|
|
EnumPt_Next,
|
|
EnumPt_Skip,
|
|
EnumPt_Reset,
|
|
EnumPt_Clone
|
|
};
|
|
|
|
static HRESULT WINAPI Contain_QueryInterface(
|
|
IConnectionPointContainer* This,
|
|
REFIID riid,
|
|
void **ppvObject)
|
|
{
|
|
*ppvObject = NULL;
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IConnectionPointContainer))
|
|
{
|
|
*ppvObject = This;
|
|
}
|
|
|
|
if (*ppvObject)
|
|
{
|
|
IUnknown_AddRef(This);
|
|
return S_OK;
|
|
}
|
|
|
|
trace("no interface\n");
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI Contain_AddRef(
|
|
IConnectionPointContainer* This)
|
|
{
|
|
Contain *iface = impl_from_IConnectionPointContainer(This);
|
|
return InterlockedIncrement(&iface->refCount);
|
|
}
|
|
|
|
static ULONG WINAPI Contain_Release(
|
|
IConnectionPointContainer* This)
|
|
{
|
|
Contain *iface = impl_from_IConnectionPointContainer(This);
|
|
ULONG ret;
|
|
|
|
ret = InterlockedDecrement(&iface->refCount);
|
|
if (ret == 0)
|
|
{
|
|
if (iface->ptCount > 0)
|
|
{
|
|
int i;
|
|
for (i = 0; i < iface->ptCount; i++)
|
|
IUnknown_Release(iface->pt[i]);
|
|
HeapFree(GetProcessHeap(),0,iface->pt);
|
|
}
|
|
HeapFree(GetProcessHeap(),0,This);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static HRESULT WINAPI Contain_EnumConnectionPoints(
|
|
IConnectionPointContainer* This,
|
|
IEnumConnectionPoints **ppEnum)
|
|
{
|
|
EnumPt *ec;
|
|
|
|
ec = HeapAlloc(GetProcessHeap(),0,sizeof(EnumPt));
|
|
ec->IEnumConnectionPoints_iface.lpVtbl = &enumpt_vtbl;
|
|
ec->refCount = 1;
|
|
ec->idx= 0;
|
|
ec->container = impl_from_IConnectionPointContainer(This);
|
|
*ppEnum = &ec->IEnumConnectionPoints_iface;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI Contain_FindConnectionPoint(
|
|
IConnectionPointContainer* This,
|
|
REFIID riid,
|
|
IConnectionPoint **ppCP)
|
|
{
|
|
Contain *iface = impl_from_IConnectionPointContainer(This);
|
|
ConPt *pt;
|
|
|
|
if (!IsEqualIID(riid, &IID_NULL) || iface->ptCount ==0)
|
|
{
|
|
pt = HeapAlloc(GetProcessHeap(),0,sizeof(ConPt));
|
|
pt->IConnectionPoint_iface.lpVtbl = &point_vtbl;
|
|
pt->refCount = 1;
|
|
pt->sinkCount = 0;
|
|
pt->sink = NULL;
|
|
pt->container = iface;
|
|
pt->id = IID_IDispatch;
|
|
|
|
if (iface->ptCount == 0)
|
|
iface->pt =HeapAlloc(GetProcessHeap(),0,sizeof(IUnknown*));
|
|
else
|
|
iface->pt = HeapReAlloc(GetProcessHeap(),0,iface->pt,sizeof(IUnknown*)*(iface->ptCount+1));
|
|
iface->pt[iface->ptCount] = &pt->IConnectionPoint_iface;
|
|
iface->ptCount++;
|
|
|
|
*ppCP = &pt->IConnectionPoint_iface;
|
|
}
|
|
else
|
|
{
|
|
*ppCP = iface->pt[0];
|
|
IUnknown_AddRef((IUnknown*)*ppCP);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static const IConnectionPointContainerVtbl contain_vtbl = {
|
|
Contain_QueryInterface,
|
|
Contain_AddRef,
|
|
Contain_Release,
|
|
|
|
Contain_EnumConnectionPoints,
|
|
Contain_FindConnectionPoint
|
|
};
|
|
|
|
static void test_IConnectionPoint(void)
|
|
{
|
|
HRESULT rc;
|
|
ULONG ref;
|
|
IConnectionPoint *point;
|
|
Contain *container;
|
|
Disp *dispatch;
|
|
DWORD cookie = 0xffffffff;
|
|
DISPPARAMS params;
|
|
VARIANT vars[10];
|
|
|
|
if (!pIConnectionPoint_SimpleInvoke || !pConnectToConnectionPoint)
|
|
{
|
|
win_skip("IConnectionPoint Apis not present\n");
|
|
return;
|
|
}
|
|
|
|
container = HeapAlloc(GetProcessHeap(),0,sizeof(Contain));
|
|
container->IConnectionPointContainer_iface.lpVtbl = &contain_vtbl;
|
|
container->refCount = 1;
|
|
container->ptCount = 0;
|
|
container->pt = NULL;
|
|
|
|
dispatch = HeapAlloc(GetProcessHeap(),0,sizeof(Disp));
|
|
dispatch->IDispatch_iface.lpVtbl = &disp_vtbl;
|
|
dispatch->refCount = 1;
|
|
|
|
rc = pConnectToConnectionPoint((IUnknown*)dispatch, &IID_NULL, TRUE, (IUnknown*)container, &cookie, &point);
|
|
ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
|
|
ok(point != NULL, "returned ConnectionPoint is NULL\n");
|
|
ok(cookie != 0xffffffff, "invalid cookie returned\n");
|
|
|
|
rc = pIConnectionPoint_SimpleInvoke(point,0xa0,NULL);
|
|
ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
|
|
|
|
if (pSHPackDispParams)
|
|
{
|
|
memset(¶ms, 0xc0, sizeof(params));
|
|
memset(vars, 0xc0, sizeof(vars));
|
|
rc = pSHPackDispParams(¶ms, vars, 2, VT_I4, 0xdeadbeef, VT_BSTR, 0xdeadcafe);
|
|
ok(rc == S_OK, "SHPackDispParams failed: %08x\n", rc);
|
|
|
|
rc = pIConnectionPoint_SimpleInvoke(point,0xa1,¶ms);
|
|
ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
|
|
}
|
|
else
|
|
win_skip("pSHPackDispParams not present\n");
|
|
|
|
rc = pConnectToConnectionPoint(NULL, &IID_NULL, FALSE, (IUnknown*)container, &cookie, NULL);
|
|
ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
|
|
|
|
/* MSDN says this should be required but it crashs on XP
|
|
IUnknown_Release(point);
|
|
*/
|
|
ref = IUnknown_Release((IUnknown*)container);
|
|
ok(ref == 0, "leftover IConnectionPointContainer reference %i\n",ref);
|
|
ref = IUnknown_Release((IUnknown*)dispatch);
|
|
ok(ref == 0, "leftover IDispatch reference %i\n",ref);
|
|
}
|
|
|
|
typedef struct _propbag
|
|
{
|
|
IPropertyBag IPropertyBag_iface;
|
|
LONG refCount;
|
|
|
|
} PropBag;
|
|
|
|
static inline PropBag *impl_from_IPropertyBag(IPropertyBag *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, PropBag, IPropertyBag_iface);
|
|
}
|
|
|
|
|
|
static HRESULT WINAPI Prop_QueryInterface(
|
|
IPropertyBag* This,
|
|
REFIID riid,
|
|
void **ppvObject)
|
|
{
|
|
*ppvObject = NULL;
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPropertyBag))
|
|
{
|
|
*ppvObject = This;
|
|
}
|
|
|
|
if (*ppvObject)
|
|
{
|
|
IUnknown_AddRef(This);
|
|
return S_OK;
|
|
}
|
|
|
|
trace("no interface\n");
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI Prop_AddRef(
|
|
IPropertyBag* This)
|
|
{
|
|
PropBag *iface = impl_from_IPropertyBag(This);
|
|
return InterlockedIncrement(&iface->refCount);
|
|
}
|
|
|
|
static ULONG WINAPI Prop_Release(
|
|
IPropertyBag* This)
|
|
{
|
|
PropBag *iface = impl_from_IPropertyBag(This);
|
|
ULONG ret;
|
|
|
|
ret = InterlockedDecrement(&iface->refCount);
|
|
if (ret == 0)
|
|
HeapFree(GetProcessHeap(),0,This);
|
|
return ret;
|
|
}
|
|
|
|
static HRESULT WINAPI Prop_Read(
|
|
IPropertyBag* This,
|
|
LPCOLESTR pszPropName,
|
|
VARIANT *pVar,
|
|
IErrorLog *pErrorLog)
|
|
{
|
|
V_VT(pVar) = VT_BLOB|VT_BYREF;
|
|
V_BYREF(pVar) = (LPVOID)0xdeadcafe;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI Prop_Write(
|
|
IPropertyBag* This,
|
|
LPCOLESTR pszPropName,
|
|
VARIANT *pVar)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
static const IPropertyBagVtbl prop_vtbl = {
|
|
Prop_QueryInterface,
|
|
Prop_AddRef,
|
|
Prop_Release,
|
|
|
|
Prop_Read,
|
|
Prop_Write
|
|
};
|
|
|
|
static void test_SHPropertyBag_ReadLONG(void)
|
|
{
|
|
PropBag *pb;
|
|
HRESULT rc;
|
|
LONG out;
|
|
static const WCHAR szName1[] = {'n','a','m','e','1',0};
|
|
|
|
if (!pSHPropertyBag_ReadLONG)
|
|
{
|
|
win_skip("SHPropertyBag_ReadLONG not present\n");
|
|
return;
|
|
}
|
|
|
|
pb = HeapAlloc(GetProcessHeap(),0,sizeof(PropBag));
|
|
pb->refCount = 1;
|
|
pb->IPropertyBag_iface.lpVtbl = &prop_vtbl;
|
|
|
|
out = 0xfeedface;
|
|
rc = pSHPropertyBag_ReadLONG(NULL, szName1, &out);
|
|
ok(rc == E_INVALIDARG || broken(rc == 0), "incorrect return %x\n",rc);
|
|
ok(out == 0xfeedface, "value should not have changed\n");
|
|
rc = pSHPropertyBag_ReadLONG(&pb->IPropertyBag_iface, NULL, &out);
|
|
ok(rc == E_INVALIDARG || broken(rc == 0) || broken(rc == 1), "incorrect return %x\n",rc);
|
|
ok(out == 0xfeedface, "value should not have changed\n");
|
|
rc = pSHPropertyBag_ReadLONG(&pb->IPropertyBag_iface, szName1, NULL);
|
|
ok(rc == E_INVALIDARG || broken(rc == 0) || broken(rc == 1), "incorrect return %x\n",rc);
|
|
ok(out == 0xfeedface, "value should not have changed\n");
|
|
rc = pSHPropertyBag_ReadLONG(&pb->IPropertyBag_iface, szName1, &out);
|
|
ok(rc == DISP_E_BADVARTYPE || broken(rc == 0) || broken(rc == 1), "incorrect return %x\n",rc);
|
|
ok(out == 0xfeedface || broken(out == 0xfeedfa00), "value should not have changed %x\n",out);
|
|
IUnknown_Release((IUnknown*)pb);
|
|
}
|
|
|
|
|
|
|
|
static void test_SHSetWindowBits(void)
|
|
{
|
|
HWND hwnd;
|
|
DWORD style, styleold;
|
|
WNDCLASSA clsA;
|
|
|
|
if(!pSHSetWindowBits)
|
|
{
|
|
win_skip("SHSetWindowBits is not available\n");
|
|
return;
|
|
}
|
|
|
|
clsA.style = 0;
|
|
clsA.lpfnWndProc = DefWindowProcA;
|
|
clsA.cbClsExtra = 0;
|
|
clsA.cbWndExtra = 0;
|
|
clsA.hInstance = GetModuleHandleA(NULL);
|
|
clsA.hIcon = 0;
|
|
clsA.hCursor = LoadCursorA(0, IDC_ARROW);
|
|
clsA.hbrBackground = NULL;
|
|
clsA.lpszMenuName = NULL;
|
|
clsA.lpszClassName = "Shlwapi test class";
|
|
RegisterClassA(&clsA);
|
|
|
|
hwnd = CreateWindowA("Shlwapi test class", "Test", WS_VISIBLE, 0, 0, 100, 100,
|
|
NULL, NULL, GetModuleHandle(NULL), 0);
|
|
ok(IsWindow(hwnd), "failed to create window\n");
|
|
|
|
/* null window */
|
|
SetLastError(0xdeadbeef);
|
|
style = pSHSetWindowBits(NULL, GWL_STYLE, 0, 0);
|
|
ok(style == 0, "expected 0 retval, got %d\n", style);
|
|
ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE ||
|
|
broken(GetLastError() == 0xdeadbeef), /* Win9x/WinMe */
|
|
"expected ERROR_INVALID_WINDOW_HANDLE, got %d\n", GetLastError());
|
|
|
|
/* zero mask, zero flags */
|
|
styleold = GetWindowLongA(hwnd, GWL_STYLE);
|
|
style = pSHSetWindowBits(hwnd, GWL_STYLE, 0, 0);
|
|
ok(styleold == style, "expected old style\n");
|
|
ok(styleold == GetWindowLongA(hwnd, GWL_STYLE), "expected to keep old style\n");
|
|
|
|
/* test mask */
|
|
styleold = GetWindowLongA(hwnd, GWL_STYLE);
|
|
ok(styleold & WS_VISIBLE, "expected WS_VISIBLE\n");
|
|
style = pSHSetWindowBits(hwnd, GWL_STYLE, WS_VISIBLE, 0);
|
|
|
|
ok(style == styleold, "expected previous style, got %x\n", style);
|
|
ok((GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE) == 0, "expected updated style\n");
|
|
|
|
/* test mask, unset style bit used */
|
|
styleold = GetWindowLongA(hwnd, GWL_STYLE);
|
|
style = pSHSetWindowBits(hwnd, GWL_STYLE, WS_VISIBLE, 0);
|
|
ok(style == styleold, "expected previous style, got %x\n", style);
|
|
ok(styleold == GetWindowLongA(hwnd, GWL_STYLE), "expected to keep old style\n");
|
|
|
|
/* set back with flags */
|
|
styleold = GetWindowLongA(hwnd, GWL_STYLE);
|
|
style = pSHSetWindowBits(hwnd, GWL_STYLE, WS_VISIBLE, WS_VISIBLE);
|
|
ok(style == styleold, "expected previous style, got %x\n", style);
|
|
ok(GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE, "expected updated style\n");
|
|
|
|
/* reset and try to set without a mask */
|
|
pSHSetWindowBits(hwnd, GWL_STYLE, WS_VISIBLE, 0);
|
|
ok((GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE) == 0, "expected updated style\n");
|
|
styleold = GetWindowLongA(hwnd, GWL_STYLE);
|
|
style = pSHSetWindowBits(hwnd, GWL_STYLE, 0, WS_VISIBLE);
|
|
ok(style == styleold, "expected previous style, got %x\n", style);
|
|
ok((GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE) == 0, "expected updated style\n");
|
|
|
|
DestroyWindow(hwnd);
|
|
|
|
UnregisterClassA("Shlwapi test class", GetModuleHandleA(NULL));
|
|
}
|
|
|
|
static void test_SHFormatDateTimeA(void)
|
|
{
|
|
FILETIME UNALIGNED filetime;
|
|
CHAR buff[100], buff2[100], buff3[100];
|
|
SYSTEMTIME st;
|
|
DWORD flags;
|
|
INT ret;
|
|
|
|
if(!pSHFormatDateTimeA)
|
|
{
|
|
win_skip("pSHFormatDateTimeA isn't available\n");
|
|
return;
|
|
}
|
|
|
|
if (0)
|
|
{
|
|
/* crashes on native */
|
|
ret = pSHFormatDateTimeA(NULL, NULL, NULL, 0);
|
|
}
|
|
|
|
GetLocalTime(&st);
|
|
SystemTimeToFileTime(&st, &filetime);
|
|
/* SHFormatDateTime expects input as utc */
|
|
LocalFileTimeToFileTime(&filetime, &filetime);
|
|
|
|
/* no way to get required buffer length here */
|
|
SetLastError(0xdeadbeef);
|
|
ret = pSHFormatDateTimeA(&filetime, NULL, NULL, 0);
|
|
ok(ret == 0, "got %d\n", ret);
|
|
ok(GetLastError() == 0xdeadbeef || broken(GetLastError() == ERROR_SUCCESS /* Win7 */),
|
|
"expected 0xdeadbeef, got %d\n", GetLastError());
|
|
|
|
SetLastError(0xdeadbeef);
|
|
buff[0] = 'a'; buff[1] = 0;
|
|
ret = pSHFormatDateTimeA(&filetime, NULL, buff, 0);
|
|
ok(ret == 0, "got %d\n", ret);
|
|
ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
|
|
ok(buff[0] == 'a', "expected same string, got %s\n", buff);
|
|
|
|
/* flags needs to have FDTF_NOAUTOREADINGORDER for these tests to succeed on Vista+ */
|
|
|
|
/* all combinations documented as invalid succeeded */
|
|
flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTTIME | FDTF_LONGTIME;
|
|
SetLastError(0xdeadbeef);
|
|
ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
|
|
ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
|
|
ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
|
|
|
|
flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTDATE | FDTF_LONGDATE;
|
|
SetLastError(0xdeadbeef);
|
|
ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
|
|
ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
|
|
ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
|
|
|
|
flags = FDTF_SHORTDATE | FDTF_LTRDATE | FDTF_RTLDATE;
|
|
SetLastError(0xdeadbeef);
|
|
ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
|
|
ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
|
|
ok(GetLastError() == 0xdeadbeef ||
|
|
broken(GetLastError() == ERROR_INVALID_FLAGS), /* Win9x/WinMe */
|
|
"expected 0xdeadbeef, got %d\n", GetLastError());
|
|
|
|
/* now check returned strings */
|
|
flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTTIME;
|
|
ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
|
|
ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
|
|
ret = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff2, sizeof(buff2));
|
|
ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
|
|
ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
|
|
|
|
flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGTIME;
|
|
ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
|
|
ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
|
|
ret = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buff2, sizeof(buff2));
|
|
ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
|
|
ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
|
|
|
|
/* both time flags */
|
|
flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGTIME | FDTF_SHORTTIME;
|
|
ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
|
|
ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
|
|
ret = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buff2, sizeof(buff2));
|
|
ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
|
|
ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
|
|
|
|
flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTDATE;
|
|
ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
|
|
ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
|
|
ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2));
|
|
ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
|
|
ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
|
|
|
|
flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGDATE;
|
|
ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
|
|
ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
|
|
ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2));
|
|
ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
|
|
ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
|
|
|
|
/* both date flags */
|
|
flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGDATE | FDTF_SHORTDATE;
|
|
ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
|
|
ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
|
|
ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2));
|
|
ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
|
|
ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
|
|
|
|
/* various combinations of date/time flags */
|
|
flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGDATE | FDTF_SHORTTIME;
|
|
ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
|
|
ok(ret == lstrlenA(buff)+1, "got %d, length %d\n", ret, lstrlenA(buff)+1);
|
|
ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2));
|
|
ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
|
|
strcat(buff2, ", ");
|
|
ret = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3));
|
|
ok(ret == lstrlenA(buff3)+1, "got %d\n", ret);
|
|
strcat(buff2, buff3);
|
|
ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
|
|
|
|
flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGDATE | FDTF_LONGTIME;
|
|
ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
|
|
ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
|
|
ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2));
|
|
ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
|
|
strcat(buff2, ", ");
|
|
ret = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3));
|
|
ok(ret == lstrlenA(buff3)+1, "got %d\n", ret);
|
|
strcat(buff2, buff3);
|
|
ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
|
|
|
|
flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTDATE | FDTF_SHORTTIME;
|
|
ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
|
|
ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
|
|
ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2));
|
|
ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
|
|
strcat(buff2, " ");
|
|
ret = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3));
|
|
ok(ret == lstrlenA(buff3)+1, "got %d\n", ret);
|
|
strcat(buff2, buff3);
|
|
ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
|
|
|
|
flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTDATE | FDTF_LONGTIME;
|
|
ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
|
|
ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
|
|
ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2));
|
|
ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
|
|
strcat(buff2, " ");
|
|
ret = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3));
|
|
ok(ret == lstrlenA(buff3)+1, "got %d\n", ret);
|
|
strcat(buff2, buff3);
|
|
ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
|
|
}
|
|
|
|
static void test_SHFormatDateTimeW(void)
|
|
{
|
|
FILETIME UNALIGNED filetime;
|
|
WCHAR buff[100], buff2[100], buff3[100];
|
|
SYSTEMTIME st;
|
|
DWORD flags;
|
|
INT ret;
|
|
static const WCHAR spaceW[] = {' ',0};
|
|
static const WCHAR commaW[] = {',',' ',0};
|
|
|
|
if(!pSHFormatDateTimeW)
|
|
{
|
|
win_skip("pSHFormatDateTimeW isn't available\n");
|
|
return;
|
|
}
|
|
|
|
if (0)
|
|
{
|
|
/* crashes on native */
|
|
ret = pSHFormatDateTimeW(NULL, NULL, NULL, 0);
|
|
}
|
|
|
|
GetLocalTime(&st);
|
|
SystemTimeToFileTime(&st, &filetime);
|
|
/* SHFormatDateTime expects input as utc */
|
|
LocalFileTimeToFileTime(&filetime, &filetime);
|
|
|
|
/* no way to get required buffer length here */
|
|
SetLastError(0xdeadbeef);
|
|
ret = pSHFormatDateTimeW(&filetime, NULL, NULL, 0);
|
|
ok(ret == 0, "got %d\n", ret);
|
|
ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
|
|
|
|
SetLastError(0xdeadbeef);
|
|
buff[0] = 'a'; buff[1] = 0;
|
|
ret = pSHFormatDateTimeW(&filetime, NULL, buff, 0);
|
|
ok(ret == 0, "got %d\n", ret);
|
|
ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
|
|
ok(buff[0] == 'a', "expected same string\n");
|
|
|
|
/* all combinations documented as invalid succeeded */
|
|
flags = FDTF_SHORTTIME | FDTF_LONGTIME;
|
|
SetLastError(0xdeadbeef);
|
|
ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff)+1, "got %d\n", ret);
|
|
ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
|
|
|
|
flags = FDTF_SHORTDATE | FDTF_LONGDATE;
|
|
SetLastError(0xdeadbeef);
|
|
ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff)+1, "got %d\n", ret);
|
|
ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
|
|
|
|
flags = FDTF_SHORTDATE | FDTF_LTRDATE | FDTF_RTLDATE;
|
|
SetLastError(0xdeadbeef);
|
|
buff[0] = 0; /* NT4 doesn't clear the buffer on failure */
|
|
ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff)+1, "got %d\n", ret);
|
|
ok(GetLastError() == 0xdeadbeef ||
|
|
broken(GetLastError() == ERROR_INVALID_FLAGS), /* Win9x/WinMe/NT4 */
|
|
"expected 0xdeadbeef, got %d\n", GetLastError());
|
|
|
|
/* now check returned strings */
|
|
flags = FDTF_SHORTTIME;
|
|
ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff)+1, "got %d\n", ret);
|
|
SetLastError(0xdeadbeef);
|
|
ret = GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
|
|
if (ret == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
|
|
{
|
|
win_skip("Needed W-functions are not implemented\n");
|
|
return;
|
|
}
|
|
ok(ret == lstrlenW(buff2)+1, "got %d\n", ret);
|
|
ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
|
|
|
|
flags = FDTF_LONGTIME;
|
|
ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff)+1, "got %d\n", ret);
|
|
ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff2)+1, "got %d\n", ret);
|
|
ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
|
|
|
|
/* both time flags */
|
|
flags = FDTF_LONGTIME | FDTF_SHORTTIME;
|
|
ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff)+1, "got %d\n", ret);
|
|
ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff2)+1, "got %d\n", ret);
|
|
ok(lstrcmpW(buff, buff2) == 0, "expected equal string\n");
|
|
|
|
flags = FDTF_SHORTDATE;
|
|
ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff)+1, "got %d\n", ret);
|
|
ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff2)+1, "got %d\n", ret);
|
|
ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
|
|
|
|
flags = FDTF_LONGDATE;
|
|
ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff)+1, "got %d\n", ret);
|
|
ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff2)+1, "got %d\n", ret);
|
|
ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
|
|
|
|
/* both date flags */
|
|
flags = FDTF_LONGDATE | FDTF_SHORTDATE;
|
|
ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff)+1, "got %d\n", ret);
|
|
ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff2)+1, "got %d\n", ret);
|
|
ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
|
|
|
|
/* various combinations of date/time flags */
|
|
flags = FDTF_LONGDATE | FDTF_SHORTTIME;
|
|
ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff)+1, "got %d, length %d\n", ret, lstrlenW(buff)+1);
|
|
ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff2)+1, "got %d\n", ret);
|
|
lstrcatW(buff2, commaW);
|
|
ret = GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff3)+1, "got %d\n", ret);
|
|
lstrcatW(buff2, buff3);
|
|
ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
|
|
|
|
flags = FDTF_LONGDATE | FDTF_LONGTIME;
|
|
ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff)+1, "got %d\n", ret);
|
|
ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff2)+1, "got %d\n", ret);
|
|
lstrcatW(buff2, commaW);
|
|
ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff3)+1, "got %d\n", ret);
|
|
lstrcatW(buff2, buff3);
|
|
ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
|
|
|
|
flags = FDTF_SHORTDATE | FDTF_SHORTTIME;
|
|
ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff)+1, "got %d\n", ret);
|
|
ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff2)+1, "got %d\n", ret);
|
|
lstrcatW(buff2, spaceW);
|
|
ret = GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff3)+1, "got %d\n", ret);
|
|
lstrcatW(buff2, buff3);
|
|
ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
|
|
|
|
flags = FDTF_SHORTDATE | FDTF_LONGTIME;
|
|
ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff)+1, "got %d\n", ret);
|
|
ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff2)+1, "got %d\n", ret);
|
|
lstrcatW(buff2, spaceW);
|
|
ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR));
|
|
ok(ret == lstrlenW(buff3)+1, "got %d\n", ret);
|
|
lstrcatW(buff2, buff3);
|
|
ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
|
|
}
|
|
|
|
static void test_SHGetObjectCompatFlags(void)
|
|
{
|
|
struct compat_value {
|
|
CHAR nameA[30];
|
|
DWORD value;
|
|
};
|
|
|
|
struct compat_value values[] = {
|
|
{ "OTNEEDSSFCACHE", 0x1 },
|
|
{ "NO_WEBVIEW", 0x2 },
|
|
{ "UNBINDABLE", 0x4 },
|
|
{ "PINDLL", 0x8 },
|
|
{ "NEEDSFILESYSANCESTOR", 0x10 },
|
|
{ "NOTAFILESYSTEM", 0x20 },
|
|
{ "CTXMENU_NOVERBS", 0x40 },
|
|
{ "CTXMENU_LIMITEDQI", 0x80 },
|
|
{ "COCREATESHELLFOLDERONLY", 0x100 },
|
|
{ "NEEDSSTORAGEANCESTOR", 0x200 },
|
|
{ "NOLEGACYWEBVIEW", 0x400 },
|
|
{ "CTXMENU_XPQCMFLAGS", 0x1000 },
|
|
{ "NOIPROPERTYSTORE", 0x2000 }
|
|
};
|
|
|
|
static const char compat_path[] = "Software\\Microsoft\\Windows\\CurrentVersion\\ShellCompatibility\\Objects";
|
|
void *pColorAdjustLuma = GetProcAddress(hShlwapi, "ColorAdjustLuma");
|
|
CHAR keyA[39]; /* {CLSID} */
|
|
HKEY root;
|
|
DWORD ret;
|
|
int i;
|
|
|
|
if (!pSHGetObjectCompatFlags)
|
|
{
|
|
win_skip("SHGetObjectCompatFlags isn't available\n");
|
|
return;
|
|
}
|
|
|
|
if (pColorAdjustLuma && pColorAdjustLuma == pSHGetObjectCompatFlags) /* win2k */
|
|
{
|
|
win_skip("Skipping SHGetObjectCompatFlags, same ordinal used for ColorAdjustLuma\n");
|
|
return;
|
|
}
|
|
|
|
/* null args */
|
|
ret = pSHGetObjectCompatFlags(NULL, NULL);
|
|
ok(ret == 0, "got %d\n", ret);
|
|
|
|
ret = RegOpenKeyA(HKEY_LOCAL_MACHINE, compat_path, &root);
|
|
if (ret != ERROR_SUCCESS)
|
|
{
|
|
skip("No compatibility class data found\n");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; RegEnumKeyA(root, i, keyA, sizeof(keyA)) == ERROR_SUCCESS; i++)
|
|
{
|
|
HKEY clsid_key;
|
|
|
|
if (RegOpenKeyA(root, keyA, &clsid_key) == ERROR_SUCCESS)
|
|
{
|
|
CHAR valueA[30];
|
|
DWORD expected = 0, got, length = sizeof(valueA);
|
|
CLSID clsid;
|
|
int v;
|
|
|
|
for (v = 0; RegEnumValueA(clsid_key, v, valueA, &length, NULL, NULL, NULL, NULL) == ERROR_SUCCESS; v++)
|
|
{
|
|
int j;
|
|
|
|
for (j = 0; j < sizeof(values)/sizeof(struct compat_value); j++)
|
|
if (lstrcmpA(values[j].nameA, valueA) == 0)
|
|
{
|
|
expected |= values[j].value;
|
|
break;
|
|
}
|
|
|
|
length = sizeof(valueA);
|
|
}
|
|
|
|
pGUIDFromStringA(keyA, &clsid);
|
|
got = pSHGetObjectCompatFlags(NULL, &clsid);
|
|
ok(got == expected, "got 0x%08x, expected 0x%08x. Key %s\n", got, expected, keyA);
|
|
|
|
RegCloseKey(clsid_key);
|
|
}
|
|
}
|
|
|
|
RegCloseKey(root);
|
|
}
|
|
|
|
typedef struct {
|
|
const IOleCommandTargetVtbl *lpVtbl;
|
|
LONG ref;
|
|
} IOleCommandTargetImpl;
|
|
|
|
static const IOleCommandTargetVtbl IOleCommandTargetImpl_Vtbl;
|
|
|
|
static IOleCommandTarget* IOleCommandTargetImpl_Construct(void)
|
|
{
|
|
IOleCommandTargetImpl *obj;
|
|
|
|
obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj));
|
|
obj->lpVtbl = &IOleCommandTargetImpl_Vtbl;
|
|
obj->ref = 1;
|
|
|
|
return (IOleCommandTarget*)obj;
|
|
}
|
|
|
|
static HRESULT WINAPI IOleCommandTargetImpl_QueryInterface(IOleCommandTarget *iface, REFIID riid, void **ppvObj)
|
|
{
|
|
IOleCommandTargetImpl *This = (IOleCommandTargetImpl *)iface;
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown) ||
|
|
IsEqualIID(riid, &IID_IOleCommandTarget))
|
|
{
|
|
*ppvObj = This;
|
|
}
|
|
|
|
if(*ppvObj)
|
|
{
|
|
IUnknown_AddRef(iface);
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI IOleCommandTargetImpl_AddRef(IOleCommandTarget *iface)
|
|
{
|
|
IOleCommandTargetImpl *This = (IOleCommandTargetImpl *)iface;
|
|
return InterlockedIncrement(&This->ref);
|
|
}
|
|
|
|
static ULONG WINAPI IOleCommandTargetImpl_Release(IOleCommandTarget *iface)
|
|
{
|
|
IOleCommandTargetImpl *This = (IOleCommandTargetImpl *)iface;
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
|
|
|
if (!ref)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
return 0;
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
static HRESULT WINAPI IOleCommandTargetImpl_QueryStatus(
|
|
IOleCommandTarget *iface, const GUID *group, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI IOleCommandTargetImpl_Exec(
|
|
IOleCommandTarget *iface,
|
|
const GUID *CmdGroup,
|
|
DWORD nCmdID,
|
|
DWORD nCmdexecopt,
|
|
VARIANT *pvaIn,
|
|
VARIANT *pvaOut)
|
|
{
|
|
add_call(&trace_got, 3, CmdGroup, (void*)(DWORD_PTR)nCmdID, (void*)(DWORD_PTR)nCmdexecopt, pvaIn, pvaOut);
|
|
return S_OK;
|
|
}
|
|
|
|
static const IOleCommandTargetVtbl IOleCommandTargetImpl_Vtbl =
|
|
{
|
|
IOleCommandTargetImpl_QueryInterface,
|
|
IOleCommandTargetImpl_AddRef,
|
|
IOleCommandTargetImpl_Release,
|
|
IOleCommandTargetImpl_QueryStatus,
|
|
IOleCommandTargetImpl_Exec
|
|
};
|
|
|
|
typedef struct {
|
|
const IServiceProviderVtbl *lpVtbl;
|
|
LONG ref;
|
|
} IServiceProviderImpl;
|
|
|
|
typedef struct {
|
|
const IProfferServiceVtbl *lpVtbl;
|
|
LONG ref;
|
|
} IProfferServiceImpl;
|
|
|
|
|
|
static const IServiceProviderVtbl IServiceProviderImpl_Vtbl;
|
|
static const IProfferServiceVtbl IProfferServiceImpl_Vtbl;
|
|
|
|
static IServiceProvider* IServiceProviderImpl_Construct(void)
|
|
{
|
|
IServiceProviderImpl *obj;
|
|
|
|
obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj));
|
|
obj->lpVtbl = &IServiceProviderImpl_Vtbl;
|
|
obj->ref = 1;
|
|
|
|
return (IServiceProvider*)obj;
|
|
}
|
|
|
|
static IProfferService* IProfferServiceImpl_Construct(void)
|
|
{
|
|
IProfferServiceImpl *obj;
|
|
|
|
obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj));
|
|
obj->lpVtbl = &IProfferServiceImpl_Vtbl;
|
|
obj->ref = 1;
|
|
|
|
return (IProfferService*)obj;
|
|
}
|
|
|
|
static HRESULT WINAPI IServiceProviderImpl_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppvObj)
|
|
{
|
|
IServiceProviderImpl *This = (IServiceProviderImpl *)iface;
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown) ||
|
|
IsEqualIID(riid, &IID_IServiceProvider))
|
|
{
|
|
*ppvObj = This;
|
|
}
|
|
|
|
if(*ppvObj)
|
|
{
|
|
IUnknown_AddRef(iface);
|
|
/* native uses redefined IID_IServiceProvider symbol, so we can't compare pointers */
|
|
if (IsEqualIID(riid, &IID_IServiceProvider))
|
|
add_call(&trace_got, 1, iface, &IID_IServiceProvider, 0, 0, 0);
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI IServiceProviderImpl_AddRef(IServiceProvider *iface)
|
|
{
|
|
IServiceProviderImpl *This = (IServiceProviderImpl *)iface;
|
|
return InterlockedIncrement(&This->ref);
|
|
}
|
|
|
|
static ULONG WINAPI IServiceProviderImpl_Release(IServiceProvider *iface)
|
|
{
|
|
IServiceProviderImpl *This = (IServiceProviderImpl *)iface;
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
|
|
|
if (!ref)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
return 0;
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
static HRESULT WINAPI IServiceProviderImpl_QueryService(
|
|
IServiceProvider *iface, REFGUID service, REFIID riid, void **ppv)
|
|
{
|
|
/* native uses redefined pointer for IID_IOleCommandTarget, not one from uuid.lib */
|
|
if (IsEqualIID(riid, &IID_IOleCommandTarget))
|
|
{
|
|
add_call(&trace_got, 2, iface, service, &IID_IOleCommandTarget, 0, 0);
|
|
*ppv = IOleCommandTargetImpl_Construct();
|
|
}
|
|
if (IsEqualIID(riid, &IID_IProfferService))
|
|
{
|
|
if (IsEqualIID(service, &IID_IProfferService))
|
|
add_call(&trace_got, 2, &IID_IProfferService, &IID_IProfferService, 0, 0, 0);
|
|
*ppv = IProfferServiceImpl_Construct();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
static const IServiceProviderVtbl IServiceProviderImpl_Vtbl =
|
|
{
|
|
IServiceProviderImpl_QueryInterface,
|
|
IServiceProviderImpl_AddRef,
|
|
IServiceProviderImpl_Release,
|
|
IServiceProviderImpl_QueryService
|
|
};
|
|
|
|
static void test_IUnknown_QueryServiceExec(void)
|
|
{
|
|
IServiceProvider *provider = IServiceProviderImpl_Construct();
|
|
static const GUID dummy_serviceid = { 0xdeadbeef };
|
|
static const GUID dummy_groupid = { 0xbeefbeef };
|
|
call_trace_t trace_expected;
|
|
HRESULT hr;
|
|
|
|
/* on <=W2K platforms same ordinal used for another export with different
|
|
prototype, so skipping using this indirect condition */
|
|
if (is_win2k_and_lower)
|
|
{
|
|
win_skip("IUnknown_QueryServiceExec is not available\n");
|
|
return;
|
|
}
|
|
|
|
/* null source pointer */
|
|
hr = pIUnknown_QueryServiceExec(NULL, &dummy_serviceid, &dummy_groupid, 0, 0, 0, 0);
|
|
ok(hr == E_FAIL, "got 0x%08x\n", hr);
|
|
|
|
/* expected trace:
|
|
IUnknown_QueryServiceExec( ptr1, serviceid, groupid, arg1, arg2, arg3, arg4);
|
|
-> IUnknown_QueryInterface( ptr1, &IID_IServiceProvider, &prov );
|
|
-> IServiceProvider_QueryService( prov, serviceid, &IID_IOleCommandTarget, &obj );
|
|
-> IOleCommandTarget_Exec( obj, groupid, arg1, arg2, arg3, arg4 );
|
|
*/
|
|
init_call_trace(&trace_expected);
|
|
|
|
add_call(&trace_expected, 1, provider, &IID_IServiceProvider, 0, 0, 0);
|
|
add_call(&trace_expected, 2, provider, &dummy_serviceid, &IID_IOleCommandTarget, 0, 0);
|
|
add_call(&trace_expected, 3, &dummy_groupid, (void*)0x1, (void*)0x2, (void*)0x3, (void*)0x4);
|
|
|
|
init_call_trace(&trace_got);
|
|
hr = pIUnknown_QueryServiceExec((IUnknown*)provider, &dummy_serviceid, &dummy_groupid, 0x1, 0x2, (void*)0x3, (void*)0x4);
|
|
ok(hr == S_OK, "got 0x%08x\n", hr);
|
|
|
|
ok_trace(&trace_expected, &trace_got);
|
|
|
|
free_call_trace(&trace_expected);
|
|
free_call_trace(&trace_got);
|
|
|
|
IServiceProvider_Release(provider);
|
|
}
|
|
|
|
|
|
static HRESULT WINAPI IProfferServiceImpl_QueryInterface(IProfferService *iface, REFIID riid, void **ppvObj)
|
|
{
|
|
IProfferServiceImpl *This = (IProfferServiceImpl *)iface;
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown) ||
|
|
IsEqualIID(riid, &IID_IProfferService))
|
|
{
|
|
*ppvObj = This;
|
|
}
|
|
else if (IsEqualIID(riid, &IID_IServiceProvider))
|
|
{
|
|
*ppvObj = IServiceProviderImpl_Construct();
|
|
add_call(&trace_got, 1, iface, &IID_IServiceProvider, 0, 0, 0);
|
|
return S_OK;
|
|
}
|
|
|
|
if(*ppvObj)
|
|
{
|
|
IUnknown_AddRef(iface);
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI IProfferServiceImpl_AddRef(IProfferService *iface)
|
|
{
|
|
IProfferServiceImpl *This = (IProfferServiceImpl *)iface;
|
|
return InterlockedIncrement(&This->ref);
|
|
}
|
|
|
|
static ULONG WINAPI IProfferServiceImpl_Release(IProfferService *iface)
|
|
{
|
|
IProfferServiceImpl *This = (IProfferServiceImpl *)iface;
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
|
|
|
if (!ref)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
return 0;
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
static HRESULT WINAPI IProfferServiceImpl_ProfferService(IProfferService *iface,
|
|
REFGUID service, IServiceProvider *pService, DWORD *pCookie)
|
|
{
|
|
add_call(&trace_got, 3, service, pService, pCookie, 0, 0);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IProfferServiceImpl_RevokeService(IProfferService *iface, DWORD cookie)
|
|
{
|
|
add_call(&trace_got, 4, (void*)(DWORD_PTR)cookie, 0, 0, 0, 0);
|
|
return S_OK;
|
|
}
|
|
|
|
static const IProfferServiceVtbl IProfferServiceImpl_Vtbl =
|
|
{
|
|
IProfferServiceImpl_QueryInterface,
|
|
IProfferServiceImpl_AddRef,
|
|
IProfferServiceImpl_Release,
|
|
IProfferServiceImpl_ProfferService,
|
|
IProfferServiceImpl_RevokeService
|
|
};
|
|
|
|
static void test_IUnknown_ProfferService(void)
|
|
{
|
|
IServiceProvider *provider = IServiceProviderImpl_Construct();
|
|
IProfferService *proff = IProfferServiceImpl_Construct();
|
|
static const GUID dummy_serviceid = { 0xdeadbeef };
|
|
call_trace_t trace_expected;
|
|
HRESULT hr;
|
|
DWORD cookie;
|
|
|
|
/* on <=W2K platforms same ordinal used for another export with different
|
|
prototype, so skipping using this indirect condition */
|
|
if (is_win2k_and_lower)
|
|
{
|
|
win_skip("IUnknown_ProfferService is not available\n");
|
|
return;
|
|
}
|
|
|
|
/* null source pointer */
|
|
hr = pIUnknown_ProfferService(NULL, &dummy_serviceid, 0, 0);
|
|
ok(hr == E_FAIL, "got 0x%08x\n", hr);
|
|
|
|
/* expected trace:
|
|
IUnknown_ProfferService( ptr1, serviceid, arg1, arg2);
|
|
-> IUnknown_QueryInterface( ptr1, &IID_IServiceProvider, &provider );
|
|
-> IServiceProvider_QueryService( provider, &IID_IProfferService, &IID_IProfferService, &proffer );
|
|
|
|
if (service pointer not null):
|
|
-> IProfferService_ProfferService( proffer, serviceid, arg1, arg2 );
|
|
else
|
|
-> IProfferService_RevokeService( proffer, *arg2 );
|
|
*/
|
|
init_call_trace(&trace_expected);
|
|
|
|
add_call(&trace_expected, 1, proff, &IID_IServiceProvider, 0, 0, 0);
|
|
add_call(&trace_expected, 2, &IID_IProfferService, &IID_IProfferService, 0, 0, 0);
|
|
add_call(&trace_expected, 3, &dummy_serviceid, provider, &cookie, 0, 0);
|
|
|
|
init_call_trace(&trace_got);
|
|
hr = pIUnknown_ProfferService((IUnknown*)proff, &dummy_serviceid, provider, &cookie);
|
|
ok(hr == S_OK, "got 0x%08x\n", hr);
|
|
|
|
ok_trace(&trace_expected, &trace_got);
|
|
free_call_trace(&trace_got);
|
|
free_call_trace(&trace_expected);
|
|
|
|
/* same with ::Revoke path */
|
|
init_call_trace(&trace_expected);
|
|
|
|
add_call(&trace_expected, 1, proff, &IID_IServiceProvider, 0, 0, 0);
|
|
add_call(&trace_expected, 2, &IID_IProfferService, &IID_IProfferService, 0, 0, 0);
|
|
add_call(&trace_expected, 4, (void*)(DWORD_PTR)cookie, 0, 0, 0, 0);
|
|
|
|
init_call_trace(&trace_got);
|
|
hr = pIUnknown_ProfferService((IUnknown*)proff, &dummy_serviceid, 0, &cookie);
|
|
ok(hr == S_OK, "got 0x%08x\n", hr);
|
|
ok_trace(&trace_expected, &trace_got);
|
|
free_call_trace(&trace_got);
|
|
free_call_trace(&trace_expected);
|
|
|
|
IServiceProvider_Release(provider);
|
|
IProfferService_Release(proff);
|
|
}
|
|
|
|
static void test_SHCreateWorkerWindowA(void)
|
|
{
|
|
WNDCLASSA cliA;
|
|
char classA[20];
|
|
HWND hwnd;
|
|
LONG_PTR ret;
|
|
BOOL res;
|
|
|
|
if (is_win2k_and_lower)
|
|
{
|
|
win_skip("SHCreateWorkerWindowA not available\n");
|
|
return;
|
|
}
|
|
|
|
hwnd = pSHCreateWorkerWindowA(0, NULL, 0, 0, 0, 0);
|
|
ok(hwnd != 0, "expected window\n");
|
|
|
|
GetClassName(hwnd, classA, 20);
|
|
ok(lstrcmpA(classA, "WorkerA") == 0, "expected WorkerA class, got %s\n", classA);
|
|
|
|
ret = GetWindowLongPtrA(hwnd, 0);
|
|
ok(ret == 0, "got %ld\n", ret);
|
|
|
|
/* class info */
|
|
memset(&cliA, 0, sizeof(cliA));
|
|
res = GetClassInfoA(GetModuleHandle("shlwapi.dll"), "WorkerA", &cliA);
|
|
ok(res, "failed to get class info\n");
|
|
ok(cliA.style == 0, "got 0x%08x\n", cliA.style);
|
|
ok(cliA.cbClsExtra == 0, "got %d\n", cliA.cbClsExtra);
|
|
ok(cliA.cbWndExtra == sizeof(LONG_PTR), "got %d\n", cliA.cbWndExtra);
|
|
ok(cliA.lpszMenuName == 0, "got %s\n", cliA.lpszMenuName);
|
|
|
|
DestroyWindow(hwnd);
|
|
|
|
/* set extra bytes */
|
|
hwnd = pSHCreateWorkerWindowA(0, NULL, 0, 0, 0, 0xdeadbeef);
|
|
ok(hwnd != 0, "expected window\n");
|
|
|
|
GetClassName(hwnd, classA, 20);
|
|
ok(lstrcmpA(classA, "WorkerA") == 0, "expected WorkerA class, got %s\n", classA);
|
|
|
|
ret = GetWindowLongPtrA(hwnd, 0);
|
|
ok(ret == 0xdeadbeef, "got %ld\n", ret);
|
|
|
|
/* test exstyle */
|
|
ret = GetWindowLongA(hwnd, GWL_EXSTYLE);
|
|
ok(ret == WS_EX_WINDOWEDGE ||
|
|
ret == (WS_EX_WINDOWEDGE|WS_EX_LAYOUTRTL) /* systems with RTL locale */, "0x%08lx\n", ret);
|
|
|
|
DestroyWindow(hwnd);
|
|
|
|
hwnd = pSHCreateWorkerWindowA(0, NULL, WS_EX_TOOLWINDOW, 0, 0, 0);
|
|
ret = GetWindowLongA(hwnd, GWL_EXSTYLE);
|
|
ok(ret == (WS_EX_WINDOWEDGE|WS_EX_TOOLWINDOW) ||
|
|
ret == (WS_EX_WINDOWEDGE|WS_EX_TOOLWINDOW|WS_EX_LAYOUTRTL) /* systems with RTL locale */, "0x%08lx\n", ret);
|
|
DestroyWindow(hwnd);
|
|
}
|
|
|
|
static HRESULT WINAPI SF_QueryInterface(IShellFolder *iface,
|
|
REFIID riid, void **ppv)
|
|
{
|
|
/* SHIShellFolder_EnumObjects doesn't QI the object for IShellFolder */
|
|
ok(!IsEqualGUID(&IID_IShellFolder, riid),
|
|
"Unexpected QI for IShellFolder\n");
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI SF_AddRef(IShellFolder *iface)
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
static ULONG WINAPI SF_Release(IShellFolder *iface)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static HRESULT WINAPI SF_ParseDisplayName(IShellFolder *iface,
|
|
HWND owner, LPBC reserved, LPOLESTR displayName, ULONG *eaten,
|
|
LPITEMIDLIST *idl, ULONG *attr)
|
|
{
|
|
ok(0, "Didn't expect ParseDisplayName\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI SF_EnumObjects(IShellFolder *iface,
|
|
HWND owner, SHCONTF flags, IEnumIDList **enm)
|
|
{
|
|
*enm = (IEnumIDList*)0xcafebabe;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI SF_BindToObject(IShellFolder *iface,
|
|
LPCITEMIDLIST idl, LPBC reserved, REFIID riid, void **obj)
|
|
{
|
|
ok(0, "Didn't expect BindToObject\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI SF_BindToStorage(IShellFolder *iface,
|
|
LPCITEMIDLIST idl, LPBC reserved, REFIID riid, void **obj)
|
|
{
|
|
ok(0, "Didn't expect BindToStorage\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI SF_CompareIDs(IShellFolder *iface,
|
|
LPARAM lparam, LPCITEMIDLIST idl1, LPCITEMIDLIST idl2)
|
|
{
|
|
ok(0, "Didn't expect CompareIDs\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI SF_CreateViewObject(IShellFolder *iface,
|
|
HWND owner, REFIID riid, void **out)
|
|
{
|
|
ok(0, "Didn't expect CreateViewObject\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI SF_GetAttributesOf(IShellFolder *iface,
|
|
UINT cidl, LPCITEMIDLIST *idl, SFGAOF *inOut)
|
|
{
|
|
ok(0, "Didn't expect GetAttributesOf\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI SF_GetUIObjectOf(IShellFolder *iface,
|
|
HWND owner, UINT cidl, LPCITEMIDLIST *idls, REFIID riid, UINT *inOut,
|
|
void **out)
|
|
{
|
|
ok(0, "Didn't expect GetUIObjectOf\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI SF_GetDisplayNameOf(IShellFolder *iface,
|
|
LPCITEMIDLIST idl, SHGDNF flags, STRRET *name)
|
|
{
|
|
ok(0, "Didn't expect GetDisplayNameOf\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI SF_SetNameOf(IShellFolder *iface,
|
|
HWND hwnd, LPCITEMIDLIST idl, LPCOLESTR name, SHGDNF flags,
|
|
LPITEMIDLIST *idlOut)
|
|
{
|
|
ok(0, "Didn't expect SetNameOf\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static IShellFolderVtbl ShellFolderVtbl = {
|
|
SF_QueryInterface,
|
|
SF_AddRef,
|
|
SF_Release,
|
|
SF_ParseDisplayName,
|
|
SF_EnumObjects,
|
|
SF_BindToObject,
|
|
SF_BindToStorage,
|
|
SF_CompareIDs,
|
|
SF_CreateViewObject,
|
|
SF_GetAttributesOf,
|
|
SF_GetUIObjectOf,
|
|
SF_GetDisplayNameOf,
|
|
SF_SetNameOf
|
|
};
|
|
|
|
static IShellFolder ShellFolder = { &ShellFolderVtbl };
|
|
|
|
static void test_SHIShellFolder_EnumObjects(void)
|
|
{
|
|
IEnumIDList *enm;
|
|
HRESULT hres;
|
|
IShellFolder *folder;
|
|
|
|
if(!pSHIShellFolder_EnumObjects || is_win2k_and_lower){
|
|
win_skip("SHIShellFolder_EnumObjects not available\n");
|
|
return;
|
|
}
|
|
|
|
if(0){
|
|
/* NULL object crashes on Windows */
|
|
hres = pSHIShellFolder_EnumObjects(NULL, NULL, 0, NULL);
|
|
}
|
|
|
|
/* SHIShellFolder_EnumObjects doesn't QI the object for IShellFolder */
|
|
enm = (IEnumIDList*)0xdeadbeef;
|
|
hres = pSHIShellFolder_EnumObjects(&ShellFolder, NULL, 0, &enm);
|
|
ok(hres == S_OK, "SHIShellFolder_EnumObjects failed: 0x%08x\n", hres);
|
|
ok(enm == (IEnumIDList*)0xcafebabe, "Didn't get expected enumerator location, instead: %p\n", enm);
|
|
|
|
/* SHIShellFolder_EnumObjects isn't strict about the IShellFolder object */
|
|
hres = pSHGetDesktopFolder(&folder);
|
|
ok(hres == S_OK, "SHGetDesktopFolder failed: 0x%08x\n", hres);
|
|
|
|
enm = NULL;
|
|
hres = pSHIShellFolder_EnumObjects(folder, NULL, 0, &enm);
|
|
ok(hres == S_OK, "SHIShellFolder_EnumObjects failed: 0x%08x\n", hres);
|
|
ok(enm != NULL, "Didn't get an enumerator\n");
|
|
if(enm)
|
|
IEnumIDList_Release(enm);
|
|
|
|
IShellFolder_Release(folder);
|
|
}
|
|
|
|
static void write_inifile(LPCWSTR filename)
|
|
{
|
|
DWORD written;
|
|
HANDLE file;
|
|
|
|
static const char data[] =
|
|
"[TestApp]\r\n"
|
|
"AKey=1\r\n"
|
|
"AnotherKey=asdf\r\n";
|
|
|
|
file = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
|
|
if(file == INVALID_HANDLE_VALUE)
|
|
return;
|
|
|
|
WriteFile(file, data, sizeof(data), &written, NULL);
|
|
|
|
CloseHandle(file);
|
|
}
|
|
|
|
#define verify_inifile(f, e) r_verify_inifile(__LINE__, f, e)
|
|
static void r_verify_inifile(unsigned l, LPCWSTR filename, LPCSTR exp)
|
|
{
|
|
HANDLE file;
|
|
CHAR buf[1024];
|
|
DWORD read;
|
|
|
|
file = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
|
|
if(file == INVALID_HANDLE_VALUE)
|
|
return;
|
|
|
|
ReadFile(file, buf, sizeof(buf) * sizeof(CHAR), &read, NULL);
|
|
buf[read] = '\0';
|
|
|
|
CloseHandle(file);
|
|
|
|
ok_(__FILE__,l)(!strcmp(buf, exp), "Expected:\n%s\nGot:\n%s\n", exp,
|
|
buf);
|
|
}
|
|
|
|
static void test_SHGetIniString(void)
|
|
{
|
|
DWORD ret;
|
|
WCHAR out[64] = {0};
|
|
|
|
static const WCHAR TestAppW[] = {'T','e','s','t','A','p','p',0};
|
|
static const WCHAR TestIniW[] = {'C',':','\\','t','e','s','t','.','i','n','i',0};
|
|
static const WCHAR AKeyW[] = {'A','K','e','y',0};
|
|
static const WCHAR AnotherKeyW[] = {'A','n','o','t','h','e','r','K','e','y',0};
|
|
static const WCHAR JunkKeyW[] = {'J','u','n','k','K','e','y',0};
|
|
|
|
if(!pSHGetIniStringW || is_win2k_and_lower){
|
|
win_skip("SHGetIniStringW is not available\n");
|
|
return;
|
|
}
|
|
|
|
write_inifile(TestIniW);
|
|
|
|
if(0){
|
|
/* these crash on Windows */
|
|
ret = pSHGetIniStringW(NULL, NULL, NULL, 0, NULL);
|
|
ret = pSHGetIniStringW(NULL, AKeyW, out, sizeof(out), TestIniW);
|
|
ret = pSHGetIniStringW(TestAppW, AKeyW, NULL, sizeof(out), TestIniW);
|
|
}
|
|
|
|
ret = pSHGetIniStringW(TestAppW, AKeyW, out, 0, TestIniW);
|
|
ok(ret == 0, "SHGetIniStringW should have given 0, instead: %d\n", ret);
|
|
|
|
/* valid arguments */
|
|
ret = pSHGetIniStringW(TestAppW, NULL, out, sizeof(out), TestIniW);
|
|
ok(broken(ret == 0) || /* win 98 */
|
|
ret == 4, "SHGetIniStringW should have given 4, instead: %d\n", ret);
|
|
ok(!lstrcmpW(out, AKeyW), "Expected %s, got: %s\n",
|
|
wine_dbgstr_w(AKeyW), wine_dbgstr_w(out));
|
|
|
|
ret = pSHGetIniStringW(TestAppW, AKeyW, out, sizeof(out), TestIniW);
|
|
ok(broken(ret == 0) || /* win 98 */
|
|
ret == 1, "SHGetIniStringW should have given 1, instead: %d\n", ret);
|
|
ok(broken(*out == 0) || /*win 98 */
|
|
!strcmp_wa(out, "1"), "Expected L\"1\", got: %s\n", wine_dbgstr_w(out));
|
|
|
|
ret = pSHGetIniStringW(TestAppW, AnotherKeyW, out, sizeof(out), TestIniW);
|
|
ok(broken(ret == 0) || /* win 98 */
|
|
ret == 4, "SHGetIniStringW should have given 4, instead: %d\n", ret);
|
|
ok(broken(*out == 0) || /* win 98 */
|
|
!strcmp_wa(out, "asdf"), "Expected L\"asdf\", got: %s\n", wine_dbgstr_w(out));
|
|
|
|
ret = pSHGetIniStringW(TestAppW, JunkKeyW, out, sizeof(out), TestIniW);
|
|
ok(ret == 0, "SHGetIniStringW should have given 0, instead: %d\n", ret);
|
|
ok(*out == 0, "Expected L\"\", got: %s\n", wine_dbgstr_w(out));
|
|
|
|
DeleteFileW(TestIniW);
|
|
}
|
|
|
|
static void test_SHSetIniString(void)
|
|
{
|
|
BOOL ret;
|
|
|
|
static const WCHAR TestAppW[] = {'T','e','s','t','A','p','p',0};
|
|
static const WCHAR AnotherAppW[] = {'A','n','o','t','h','e','r','A','p','p',0};
|
|
static const WCHAR TestIniW[] = {'C',':','\\','t','e','s','t','.','i','n','i',0};
|
|
static const WCHAR AKeyW[] = {'A','K','e','y',0};
|
|
static const WCHAR NewKeyW[] = {'N','e','w','K','e','y',0};
|
|
static const WCHAR AValueW[] = {'A','V','a','l','u','e',0};
|
|
|
|
if(!pSHSetIniStringW || is_win2k_and_lower){
|
|
win_skip("SHSetIniStringW is not available\n");
|
|
return;
|
|
}
|
|
|
|
write_inifile(TestIniW);
|
|
|
|
ret = pSHSetIniStringW(TestAppW, AKeyW, AValueW, TestIniW);
|
|
ok(ret == TRUE, "SHSetIniStringW should not have failed\n");
|
|
todo_wine /* wine sticks an extra \r\n at the end of the file */
|
|
verify_inifile(TestIniW, "[TestApp]\r\nAKey=AValue\r\nAnotherKey=asdf\r\n");
|
|
|
|
ret = pSHSetIniStringW(TestAppW, AKeyW, NULL, TestIniW);
|
|
ok(ret == TRUE, "SHSetIniStringW should not have failed\n");
|
|
verify_inifile(TestIniW, "[TestApp]\r\nAnotherKey=asdf\r\n");
|
|
|
|
ret = pSHSetIniStringW(AnotherAppW, NewKeyW, AValueW, TestIniW);
|
|
ok(ret == TRUE, "SHSetIniStringW should not have failed\n");
|
|
verify_inifile(TestIniW, "[TestApp]\r\nAnotherKey=asdf\r\n[AnotherApp]\r\nNewKey=AValue\r\n");
|
|
|
|
ret = pSHSetIniStringW(TestAppW, NULL, AValueW, TestIniW);
|
|
ok(ret == TRUE, "SHSetIniStringW should not have failed\n");
|
|
verify_inifile(TestIniW, "[AnotherApp]\r\nNewKey=AValue\r\n");
|
|
|
|
DeleteFileW(TestIniW);
|
|
}
|
|
|
|
enum _shellkey_flags {
|
|
SHKEY_Root_HKCU = 0x1,
|
|
SHKEY_Root_HKLM = 0x2,
|
|
SHKEY_Key_Explorer = 0x00,
|
|
SHKEY_Key_Shell = 0x10,
|
|
SHKEY_Key_ShellNoRoam = 0x20,
|
|
SHKEY_Key_Classes = 0x30,
|
|
SHKEY_Subkey_Default = 0x0000,
|
|
SHKEY_Subkey_ResourceName = 0x1000,
|
|
SHKEY_Subkey_Handlers = 0x2000,
|
|
SHKEY_Subkey_Associations = 0x3000,
|
|
SHKEY_Subkey_Volatile = 0x4000,
|
|
SHKEY_Subkey_MUICache = 0x5000,
|
|
SHKEY_Subkey_FileExts = 0x6000
|
|
};
|
|
|
|
static void test_SHGetShellKey(void)
|
|
{
|
|
static const WCHAR ShellFoldersW[] = { 'S','h','e','l','l',' ','F','o','l','d','e','r','s',0 };
|
|
static const WCHAR WineTestW[] = { 'W','i','n','e','T','e','s','t',0 };
|
|
|
|
void *pPathBuildRootW = GetProcAddress(hShlwapi, "PathBuildRootW");
|
|
DWORD *alloc_data, data, size;
|
|
HKEY hkey;
|
|
HRESULT hres;
|
|
|
|
if (!pSHGetShellKey)
|
|
{
|
|
win_skip("SHGetShellKey(ordinal 491) isn't available\n");
|
|
return;
|
|
}
|
|
|
|
/* some win2k */
|
|
if (pPathBuildRootW && pPathBuildRootW == pSHGetShellKey)
|
|
{
|
|
win_skip("SHGetShellKey(ordinal 491) used for PathBuildRootW\n");
|
|
return;
|
|
}
|
|
|
|
if (is_win9x || is_win2k_and_lower)
|
|
{
|
|
win_skip("Ordinal 491 used for another call, skipping SHGetShellKey tests\n");
|
|
return;
|
|
}
|
|
|
|
/* Vista+ limits SHKEY enumeration values */
|
|
SetLastError(0xdeadbeef);
|
|
hkey = pSHGetShellKey(SHKEY_Key_Explorer, ShellFoldersW, FALSE);
|
|
if (hkey)
|
|
{
|
|
/* Tests not working on Vista+ */
|
|
RegCloseKey(hkey);
|
|
|
|
hkey = pSHGetShellKey(SHKEY_Root_HKLM|SHKEY_Key_Classes, NULL, FALSE);
|
|
ok(hkey != NULL, "hkey = NULL\n");
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
hkey = pSHGetShellKey(SHKEY_Root_HKCU|SHKEY_Key_Explorer, ShellFoldersW, FALSE);
|
|
ok(hkey != NULL, "hkey = NULL\n");
|
|
RegCloseKey(hkey);
|
|
|
|
hkey = pSHGetShellKey(SHKEY_Root_HKLM|SHKEY_Key_Explorer, ShellFoldersW, FALSE);
|
|
ok(hkey != NULL, "hkey = NULL\n");
|
|
RegCloseKey(hkey);
|
|
|
|
hkey = pSHGetShellKey(SHKEY_Root_HKLM, WineTestW, FALSE);
|
|
ok(hkey == NULL, "hkey != NULL\n");
|
|
|
|
hkey = pSHGetShellKey(SHKEY_Root_HKLM, WineTestW, TRUE);
|
|
ok(hkey != NULL, "Can't create key\n");
|
|
RegCloseKey(hkey);
|
|
|
|
hkey = pSHGetShellKey(SHKEY_Root_HKLM, NULL, FALSE);
|
|
ok(hkey != NULL, "Can't create key\n");
|
|
ok(SUCCEEDED(RegDeleteKeyW(hkey, WineTestW)), "Can't delte key\n");
|
|
RegCloseKey(hkey);
|
|
|
|
if (!pSKGetValueW || !pSKSetValueW || !pSKDeleteValueW || !pSKAllocValueW)
|
|
{
|
|
win_skip("SKGetValueW, SKSetValueW, SKDeleteValueW or SKAllocValueW not available\n");
|
|
return;
|
|
}
|
|
|
|
hres = pSKGetValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, &data, &size);
|
|
ok(hres == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hres = %x\n", hres);
|
|
|
|
data = 1234;
|
|
hres = pSKSetValueW(SHKEY_Root_HKLM, WineTestW, NULL, REG_DWORD, &data, sizeof(DWORD));
|
|
ok(hres == S_OK, "hres = %x\n", hres);
|
|
|
|
size = 1;
|
|
hres = pSKGetValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, NULL, &size);
|
|
ok(hres == S_OK, "hres = %x\n", hres);
|
|
ok(size == sizeof(DWORD), "size = %d\n", size);
|
|
|
|
data = 0xdeadbeef;
|
|
hres = pSKGetValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, &data, &size);
|
|
ok(hres == S_OK, "hres = %x\n", hres);
|
|
ok(size == sizeof(DWORD), "size = %d\n", size);
|
|
ok(data == 1234, "data = %d\n", data);
|
|
|
|
hres = pSKAllocValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, (void**)&alloc_data, &size);
|
|
ok(hres == S_OK, "hres= %x\n", hres);
|
|
ok(size == sizeof(DWORD), "size = %d\n", size);
|
|
ok(*alloc_data == 1234, "*alloc_data = %d\n", *alloc_data);
|
|
LocalFree(alloc_data);
|
|
|
|
hres = pSKDeleteValueW(SHKEY_Root_HKLM, WineTestW, NULL);
|
|
ok(hres == S_OK, "hres = %x\n", hres);
|
|
|
|
hres = pSKDeleteValueW(SHKEY_Root_HKLM, WineTestW, NULL);
|
|
ok(hres == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hres = %x\n", hres);
|
|
|
|
hres = pSKGetValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, &data, &size);
|
|
ok(hres == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hres = %x\n", hres);
|
|
|
|
hkey = pSHGetShellKey(SHKEY_Root_HKLM, NULL, FALSE);
|
|
ok(hkey != NULL, "Can't create key\n");
|
|
ok(SUCCEEDED(RegDeleteKeyW(hkey, WineTestW)), "Can't delte key\n");
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
static void init_pointers(void)
|
|
{
|
|
#define MAKEFUNC(f, ord) (p##f = (void*)GetProcAddress(hShlwapi, (LPSTR)(ord)))
|
|
MAKEFUNC(SHAllocShared, 7);
|
|
MAKEFUNC(SHLockShared, 8);
|
|
MAKEFUNC(SHUnlockShared, 9);
|
|
MAKEFUNC(SHFreeShared, 10);
|
|
MAKEFUNC(GetAcceptLanguagesA, 14);
|
|
MAKEFUNC(SHSetWindowBits, 165);
|
|
MAKEFUNC(ConnectToConnectionPoint, 168);
|
|
MAKEFUNC(SHSearchMapInt, 198);
|
|
MAKEFUNC(SHCreateWorkerWindowA, 257);
|
|
MAKEFUNC(GUIDFromStringA, 269);
|
|
MAKEFUNC(SHPackDispParams, 282);
|
|
MAKEFUNC(IConnectionPoint_InvokeWithCancel, 283);
|
|
MAKEFUNC(IConnectionPoint_SimpleInvoke, 284);
|
|
MAKEFUNC(SHGetIniStringW, 294);
|
|
MAKEFUNC(SHSetIniStringW, 295);
|
|
MAKEFUNC(SHFormatDateTimeA, 353);
|
|
MAKEFUNC(SHFormatDateTimeW, 354);
|
|
MAKEFUNC(SHIShellFolder_EnumObjects, 404);
|
|
MAKEFUNC(SHGetObjectCompatFlags, 476);
|
|
MAKEFUNC(IUnknown_QueryServiceExec, 484);
|
|
MAKEFUNC(SHGetShellKey, 491);
|
|
MAKEFUNC(SHPropertyBag_ReadLONG, 496);
|
|
MAKEFUNC(IUnknown_ProfferService, 514);
|
|
MAKEFUNC(SKGetValueW, 516);
|
|
MAKEFUNC(SKSetValueW, 517);
|
|
MAKEFUNC(SKDeleteValueW, 518);
|
|
MAKEFUNC(SKAllocValueW, 519);
|
|
#undef MAKEFUNC
|
|
}
|
|
|
|
START_TEST(ordinal)
|
|
{
|
|
hShlwapi = GetModuleHandleA("shlwapi.dll");
|
|
is_win2k_and_lower = GetProcAddress(hShlwapi, "StrChrNW") == 0;
|
|
is_win9x = GetProcAddress(hShlwapi, (LPSTR)99) == 0; /* StrCpyNXA */
|
|
|
|
/* SHCreateStreamOnFileEx was introduced in shlwapi v6.0 */
|
|
if(!GetProcAddress(hShlwapi, "SHCreateStreamOnFileEx")){
|
|
win_skip("Too old shlwapi version\n");
|
|
return;
|
|
}
|
|
|
|
init_pointers();
|
|
|
|
hmlang = LoadLibraryA("mlang.dll");
|
|
pLcidToRfc1766A = (void *)GetProcAddress(hmlang, "LcidToRfc1766A");
|
|
|
|
hshell32 = LoadLibraryA("shell32.dll");
|
|
pSHGetDesktopFolder = (void *)GetProcAddress(hshell32, "SHGetDesktopFolder");
|
|
|
|
test_GetAcceptLanguagesA();
|
|
test_SHSearchMapInt();
|
|
test_alloc_shared();
|
|
test_fdsa();
|
|
test_GetShellSecurityDescriptor();
|
|
test_SHPackDispParams();
|
|
test_IConnectionPoint();
|
|
test_SHPropertyBag_ReadLONG();
|
|
test_SHSetWindowBits();
|
|
test_SHFormatDateTimeA();
|
|
test_SHFormatDateTimeW();
|
|
test_SHGetObjectCompatFlags();
|
|
test_IUnknown_QueryServiceExec();
|
|
test_IUnknown_ProfferService();
|
|
test_SHCreateWorkerWindowA();
|
|
test_SHIShellFolder_EnumObjects();
|
|
test_SHGetIniString();
|
|
test_SHSetIniString();
|
|
test_SHGetShellKey();
|
|
|
|
FreeLibrary(hshell32);
|
|
FreeLibrary(hmlang);
|
|
}
|