/* 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 #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, ret; PACL pAcl; DWORD dwRev; SECURITY_DESCRIPTOR_CONTROL control; ok(IsValidSecurityDescriptor(psd), "returned value is not valid SD\n"); ret = GetSecurityDescriptorControl(psd, &control, &dwRev); ok(ret, "GetSecurityDescriptorControl failed with error %u\n", GetLastError()); ok(0 == (control & SE_SELF_RELATIVE), "SD should be absolute\n"); ret = GetSecurityDescriptorDacl(psd, &bHasDacl, &pAcl, &bDefaulted); ok(ret, "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"); ret = GetAclInformation(pAcl, &asiSize, sizeof(asiSize), AclSizeInformation); ok(ret, "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 */ ret = GetAce(pAcl, 0, (LPVOID*)&paaa); ok(ret, "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); ret = GetAce(pAcl, 1, (LPVOID*)&paaa); ok(ret, "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); ret = GetAce(pAcl, 2, (LPVOID*)&paaa); ok(ret, "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 */ 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 = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3)); ok(ret == lstrlenA(buff3)+1, "got %d\n", ret); ok(lstrcmpA(buff3, buff + lstrlenA(buff) - lstrlenA(buff3)) == 0, "expected (%s), got (%s) for time part\n", buff3, buff + lstrlenA(buff) - lstrlenA(buff3)); ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)); ok(ret == lstrlenA(buff2)+1, "got %d\n", ret); buff[lstrlenA(buff2)] = '\0'; ok(lstrcmpA(buff2, buff) == 0, "expected (%s) got (%s) for date part\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 = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3)); ok(ret == lstrlenA(buff3)+1, "got %d\n", ret); ok(lstrcmpA(buff3, buff + lstrlenA(buff) - lstrlenA(buff3)) == 0, "expected (%s), got (%s) for time part\n", buff3, buff + lstrlenA(buff) - lstrlenA(buff3)); ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)); ok(ret == lstrlenA(buff2)+1, "got %d\n", ret); buff[lstrlenA(buff2)] = '\0'; ok(lstrcmpA(buff2, buff) == 0, "expected (%s) got (%s) for date part\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], *p1, *p2; SYSTEMTIME st; DWORD flags; INT ret; static const WCHAR spaceW[] = {' ',0}; #define UNICODE_LTR_MARK 0x200e if(!pSHFormatDateTimeW) { win_skip("pSHFormatDateTimeW isn't available\n"); return; } if (0) { /* crashes on native */ 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, "expected 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, "expected 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 || ret == lstrlenW(buff), "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), 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 || ret == lstrlenW(buff), "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), 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 || ret == lstrlenW(buff), "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), 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 || ret == lstrlenW(buff), "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), 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, "expected %d, got %d\n", lstrlenW(buff2)+1, 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 || ret == lstrlenW(buff), "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret); ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR)); ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, 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 || ret == lstrlenW(buff), "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret); ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR)); ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, 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 || ret == lstrlenW(buff), "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret); ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR)); ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, 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 || ret == lstrlenW(buff), "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret); ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR)); ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, 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 || ret == lstrlenW(buff), "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret); ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR)); ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, 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 || ret == lstrlenW(buff), "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret); ret = GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR)); ok(ret == lstrlenW(buff3)+1, "expected %d, got %d\n", lstrlenW(buff3)+1, ret); ok(lstrcmpW(buff3, buff + lstrlenW(buff) - lstrlenW(buff3)) == 0, "expected (%s), got (%s) for time part\n", wine_dbgstr_w(buff3), wine_dbgstr_w(buff + lstrlenW(buff) - lstrlenW(buff3))); ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR)); ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret); p1 = buff; p2 = buff2; while (*p2 != '\0') { while (*p1 == UNICODE_LTR_MARK) p1++; while (*p2 == UNICODE_LTR_MARK) p2++; p1++; p2++; } *p1 = '\0'; ok(lstrcmpW(buff2, buff) == 0, "expected (%s) got (%s) for date part\n", wine_dbgstr_w(buff2), wine_dbgstr_w(buff)); flags = FDTF_LONGDATE | FDTF_LONGTIME; ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR)); ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff), "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret); ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR)); ok(ret == lstrlenW(buff3)+1, "expected %d, got %d\n", lstrlenW(buff3)+1, ret); ok(lstrcmpW(buff3, buff + lstrlenW(buff) - lstrlenW(buff3)) == 0, "expected (%s), got (%s) for time part\n", wine_dbgstr_w(buff3), wine_dbgstr_w(buff + lstrlenW(buff) - lstrlenW(buff3))); ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR)); ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret); p1 = buff; p2 = buff2; while (*p2 != '\0') { while (*p1 == UNICODE_LTR_MARK) p1++; while (*p2 == UNICODE_LTR_MARK) p2++; p1++; p2++; } *p1 = '\0'; ok(lstrcmpW(buff2, buff) == 0, "expected (%s) got (%s) for date part\n", wine_dbgstr_w(buff2), wine_dbgstr_w(buff)); flags = FDTF_SHORTDATE | FDTF_SHORTTIME; ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR)); ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff), "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret); ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR)); ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret); lstrcatW(buff2, spaceW); ret = GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR)); ok(ret == lstrlenW(buff3)+1, "expected %d, got %d\n", lstrlenW(buff3)+1, 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 || ret == lstrlenW(buff), "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret); ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR)); ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret); lstrcatW(buff2, spaceW); ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR)); ok(ret == lstrlenW(buff3)+1, "expected %d, got %d\n", lstrlenW(buff3)+1, 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 { IOleCommandTarget IOleCommandTarget_iface; LONG ref; } IOleCommandTargetImpl; static inline IOleCommandTargetImpl *impl_from_IOleCommandTarget(IOleCommandTarget *iface) { return CONTAINING_RECORD(iface, IOleCommandTargetImpl, IOleCommandTarget_iface); } static const IOleCommandTargetVtbl IOleCommandTargetImpl_Vtbl; static IOleCommandTarget* IOleCommandTargetImpl_Construct(void) { IOleCommandTargetImpl *obj; obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj)); obj->IOleCommandTarget_iface.lpVtbl = &IOleCommandTargetImpl_Vtbl; obj->ref = 1; return &obj->IOleCommandTarget_iface; } static HRESULT WINAPI IOleCommandTargetImpl_QueryInterface(IOleCommandTarget *iface, REFIID riid, void **ppvObj) { IOleCommandTargetImpl *This = impl_from_IOleCommandTarget(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 = impl_from_IOleCommandTarget(iface); return InterlockedIncrement(&This->ref); } static ULONG WINAPI IOleCommandTargetImpl_Release(IOleCommandTarget *iface) { IOleCommandTargetImpl *This = impl_from_IOleCommandTarget(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 { IServiceProvider IServiceProvider_iface; LONG ref; } IServiceProviderImpl; static inline IServiceProviderImpl *impl_from_IServiceProvider(IServiceProvider *iface) { return CONTAINING_RECORD(iface, IServiceProviderImpl, IServiceProvider_iface); } typedef struct { IProfferService IProfferService_iface; LONG ref; } IProfferServiceImpl; static inline IProfferServiceImpl *impl_from_IProfferService(IProfferService *iface) { return CONTAINING_RECORD(iface, IProfferServiceImpl, IProfferService_iface); } static const IServiceProviderVtbl IServiceProviderImpl_Vtbl; static const IProfferServiceVtbl IProfferServiceImpl_Vtbl; static IServiceProvider* IServiceProviderImpl_Construct(void) { IServiceProviderImpl *obj; obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj)); obj->IServiceProvider_iface.lpVtbl = &IServiceProviderImpl_Vtbl; obj->ref = 1; return &obj->IServiceProvider_iface; } static IProfferService* IProfferServiceImpl_Construct(void) { IProfferServiceImpl *obj; obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj)); obj->IProfferService_iface.lpVtbl = &IProfferServiceImpl_Vtbl; obj->ref = 1; return &obj->IProfferService_iface; } static HRESULT WINAPI IServiceProviderImpl_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppvObj) { IServiceProviderImpl *This = impl_from_IServiceProvider(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 = impl_from_IServiceProvider(iface); return InterlockedIncrement(&This->ref); } static ULONG WINAPI IServiceProviderImpl_Release(IServiceProvider *iface) { IServiceProviderImpl *This = impl_from_IServiceProvider(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 = impl_from_IProfferService(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 = impl_from_IProfferService(iface); return InterlockedIncrement(&This->ref); } static ULONG WINAPI IProfferServiceImpl_Release(IProfferService *iface) { IProfferServiceImpl *This = impl_from_IProfferService(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) { *pCookie = 0xdeadbeef; 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); cookie = 0; hr = pIUnknown_ProfferService((IUnknown*)proff, &dummy_serviceid, provider, &cookie); ok(hr == S_OK, "got 0x%08x\n", hr); ok(cookie == 0xdeadbeef, "got %x\n", cookie); 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); ok(cookie != 0, "got %x\n", cookie); hr = pIUnknown_ProfferService((IUnknown*)proff, &dummy_serviceid, 0, &cookie); ok(hr == S_OK, "got 0x%08x\n", hr); ok(cookie == 0, "got %x\n", cookie); 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 */ 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 */ pSHGetIniStringW(NULL, NULL, NULL, 0, NULL); pSHGetIniStringW(NULL, AKeyW, out, sizeof(out), TestIniW); 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); }