From 182359adfdf282427d0bde8ddc70a7c027eee87a Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Wed, 9 Mar 2016 08:43:20 +0000 Subject: [PATCH] [SHELL32_WINETEST] Sync with Wine Staging 1.9.4. CORE-10912 svn path=/trunk/; revision=70977 --- rostests/winetests/shell32/progman_dde.c | 8 +- rostests/winetests/shell32/shellole.c | 33 + rostests/winetests/shell32/shlexec.c | 1337 ++++++++++++---------- rostests/winetests/shell32/shlfolder.c | 79 +- 4 files changed, 824 insertions(+), 633 deletions(-) diff --git a/rostests/winetests/shell32/progman_dde.c b/rostests/winetests/shell32/progman_dde.c index a3faa837a26..4c9116e69e0 100644 --- a/rostests/winetests/shell32/progman_dde.c +++ b/rostests/winetests/shell32/progman_dde.c @@ -372,7 +372,13 @@ static HWND CheckWindowCreated(const char *winName, BOOL closeWindow, int testPa for (i = 0; window == NULL && i < PDDE_POLL_NUM; i++) { Sleep(PDDE_POLL_TIME); - window = FindWindowA(NULL, winName); + /* Specify the window class name to make sure what we find is really an + * Explorer window. Explorer used two different window classes so try + * both. + */ + window = FindWindowA("ExplorerWClass", winName); + if (!window) + window = FindWindowA("CabinetWClass", winName); } ok (window != NULL, "Window \"%s\" was not created in %i seconds - assumed failure.%s\n", winName, PDDE_POLL_NUM*PDDE_POLL_TIME/1000, GetStringFromTestParams(testParams)); diff --git a/rostests/winetests/shell32/shellole.c b/rostests/winetests/shell32/shellole.c index 5335385d2d3..cdc65098435 100644 --- a/rostests/winetests/shell32/shellole.c +++ b/rostests/winetests/shell32/shellole.c @@ -74,6 +74,7 @@ static HRESULT (WINAPI *pSHPropStgReadMultiple)(IPropertyStorage*, UINT, static HRESULT (WINAPI *pSHPropStgWriteMultiple)(IPropertyStorage*, UINT*, ULONG, const PROPSPEC*, PROPVARIANT*, PROPID); static HRESULT (WINAPI *pSHCreateQueryCancelAutoPlayMoniker)(IMoniker**); +static HRESULT (WINAPI *pSHCreateSessionKey)(REGSAM, HKEY*); static void init(void) { @@ -83,6 +84,7 @@ static void init(void) pSHPropStgReadMultiple = (void*)GetProcAddress(hmod, "SHPropStgReadMultiple"); pSHPropStgWriteMultiple = (void*)GetProcAddress(hmod, "SHPropStgWriteMultiple"); pSHCreateQueryCancelAutoPlayMoniker = (void*)GetProcAddress(hmod, "SHCreateQueryCancelAutoPlayMoniker"); + pSHCreateSessionKey = (void*)GetProcAddress(hmod, (char*)723); } static HRESULT WINAPI PropertyStorage_QueryInterface(IPropertyStorage *This, @@ -857,6 +859,36 @@ static void test_DragQueryFile(void) } #undef DROPTEST_FILENAME +static void test_SHCreateSessionKey(void) +{ + HKEY hkey, hkey2; + HRESULT hr; + + if (!pSHCreateSessionKey) + { + skip("SHCreateSessionKey is not implemented\n"); + return; + } + +if (0) /* crashes on native */ + hr = pSHCreateSessionKey(KEY_READ, NULL); + + hkey = (HKEY)0xdeadbeef; + hr = pSHCreateSessionKey(0, &hkey); + todo_wine ok(hr == E_ACCESSDENIED, "got 0x%08x\n", hr); + todo_wine ok(hkey == NULL, "got %p\n", hkey); + + hr = pSHCreateSessionKey(KEY_READ, &hkey); + ok(hr == S_OK, "got 0x%08x\n", hr); + + hr = pSHCreateSessionKey(KEY_READ, &hkey2); + ok(hr == S_OK, "got 0x%08x\n", hr); + ok(hkey != hkey2, "got %p, %p\n", hkey, hkey2); + + RegCloseKey(hkey); + RegCloseKey(hkey2); +} + START_TEST(shellole) { init(); @@ -864,4 +896,5 @@ START_TEST(shellole) test_SHPropStg_functions(); test_SHCreateQueryCancelAutoPlayMoniker(); test_DragQueryFile(); + test_SHCreateSessionKey(); } diff --git a/rostests/winetests/shell32/shlexec.c b/rostests/winetests/shell32/shlexec.c index 3d95b4148e5..e003356a1a4 100755 --- a/rostests/winetests/shell32/shlexec.c +++ b/rostests/winetests/shell32/shlexec.c @@ -1,7 +1,7 @@ /* * Unit test of the ShellExecute function. * - * Copyright 2005 Francois Gouget for CodeWeavers + * Copyright 2005, 2016 Francois Gouget for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -26,8 +26,6 @@ * - ShellExecute("foo.shlexec") with no path should work if foo.shlexec is * in the PATH * - test associations that use %l, %L or "%1" instead of %1 - * - we may want to test ShellExecuteEx() instead of ShellExecute() - * and then we could also check its return value * - ShellExecuteEx() also calls SetLastError() with meaningful values which * we could check */ @@ -64,10 +62,132 @@ static HANDLE dde_ready_event; /*** * - * ShellExecute wrappers + * Helpers to read from / write to the child process results file. + * (borrowed from dlls/kernel32/tests/process.c) * ***/ -static void dump_child(void); + +static const char* encodeA(const char* str) +{ + static char encoded[2*1024+1]; + char* ptr; + size_t len,i; + + if (!str) return ""; + len = strlen(str) + 1; + if (len >= sizeof(encoded)/2) + { + fprintf(stderr, "string is too long!\n"); + assert(0); + } + ptr = encoded; + for (i = 0; i < len; i++) + sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]); + ptr[2 * len] = '\0'; + return ptr; +} + +static unsigned decode_char(char c) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + assert(c >= 'A' && c <= 'F'); + return c - 'A' + 10; +} + +static char* decodeA(const char* str) +{ + static char decoded[1024]; + char* ptr; + size_t len,i; + + len = strlen(str) / 2; + if (!len--) return NULL; + if (len >= sizeof(decoded)) + { + fprintf(stderr, "string is too long!\n"); + assert(0); + } + ptr = decoded; + for (i = 0; i < len; i++) + ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]); + ptr[len] = '\0'; + return ptr; +} + +static void WINETEST_PRINTF_ATTR(2,3) childPrintf(HANDLE h, const char* fmt, ...) +{ + va_list valist; + char buffer[1024]; + DWORD w; + + va_start(valist, fmt); + vsprintf(buffer, fmt, valist); + va_end(valist); + WriteFile(h, buffer, strlen(buffer), &w, NULL); +} + +static char* getChildString(const char* sect, const char* key) +{ + char buf[1024]; + char* ret; + + GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), child_file); + if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL; + assert(!(strlen(buf) & 1)); + ret = decodeA(buf); + return ret; +} + + +/*** + * + * Child code + * + ***/ + +#define CHILD_DDE_TIMEOUT 2500 +static DWORD ddeInst; +static HSZ hszTopic; +static char ddeExec[MAX_PATH], ddeApplication[MAX_PATH]; +static BOOL post_quit_on_execute; + +/* Handle DDE for doChild() and test_dde_default_app() */ +static HDDEDATA CALLBACK ddeCb(UINT uType, UINT uFmt, HCONV hConv, + HSZ hsz1, HSZ hsz2, HDDEDATA hData, + ULONG_PTR dwData1, ULONG_PTR dwData2) +{ + DWORD size = 0; + + if (winetest_debug > 2) + trace("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n", + uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2); + + switch (uType) + { + case XTYP_CONNECT: + if (!DdeCmpStringHandles(hsz1, hszTopic)) + { + size = DdeQueryStringA(ddeInst, hsz2, ddeApplication, MAX_PATH, CP_WINANSI); + ok(size < MAX_PATH, "got size %d\n", size); + assert(size < MAX_PATH); + return (HDDEDATA)TRUE; + } + return (HDDEDATA)FALSE; + + case XTYP_EXECUTE: + size = DdeGetData(hData, (LPBYTE)ddeExec, MAX_PATH, 0); + ok(size < MAX_PATH, "got size %d\n", size); + assert(size < MAX_PATH); + DdeFreeDataHandle(hData); + if (post_quit_on_execute) + PostQuitMessage(0); + return (HDDEDATA)DDE_FACK; + + default: + return NULL; + } +} static HANDLE hEvent; static void init_event(const char* child_file) @@ -77,6 +197,287 @@ static void init_event(const char* child_file) hEvent=CreateEventA(NULL, FALSE, FALSE, event_name); } +/* + * This is just to make sure the child won't run forever stuck in a + * GetMessage() loop when DDE fails for some reason. + */ +static void CALLBACK childTimeout(HWND wnd, UINT msg, UINT_PTR timer, DWORD time) +{ + trace("childTimeout called\n"); + + PostQuitMessage(0); +} + +static void doChild(int argc, char** argv) +{ + char *filename, buffer[MAX_PATH]; + HANDLE hFile, map; + int i; + UINT_PTR timer; + + filename=argv[2]; + hFile=CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + if (hFile == INVALID_HANDLE_VALUE) + return; + + /* Arguments */ + childPrintf(hFile, "[Child]\r\n"); + if (winetest_debug > 2) + { + trace("cmdlineA='%s'\n", GetCommandLineA()); + trace("argcA=%d\n", argc); + } + childPrintf(hFile, "cmdlineA=%s\r\n", encodeA(GetCommandLineA())); + childPrintf(hFile, "argcA=%d\r\n", argc); + for (i = 0; i < argc; i++) + { + if (winetest_debug > 2) + trace("argvA%d='%s'\n", i, argv[i]); + childPrintf(hFile, "argvA%d=%s\r\n", i, encodeA(argv[i])); + } + GetModuleFileNameA(GetModuleHandleA(NULL), buffer, sizeof(buffer)); + childPrintf(hFile, "longPath=%s\r\n", encodeA(buffer)); + + /* Check environment variable inheritance */ + *buffer = '\0'; + SetLastError(0); + GetEnvironmentVariableA("ShlexecVar", buffer, sizeof(buffer)); + childPrintf(hFile, "ShlexecVarLE=%d\r\n", GetLastError()); + childPrintf(hFile, "ShlexecVar=%s\r\n", encodeA(buffer)); + + map = OpenFileMappingA(FILE_MAP_READ, FALSE, "winetest_shlexec_dde_map"); + if (map != NULL) + { + HANDLE dde_ready; + char *shared_block = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 4096); + CloseHandle(map); + if (shared_block[0] != '\0' || shared_block[1] != '\0') + { + HDDEDATA hdde; + HSZ hszApplication; + MSG msg; + UINT rc; + + post_quit_on_execute = TRUE; + ddeInst = 0; + rc = DdeInitializeA(&ddeInst, ddeCb, CBF_SKIP_ALLNOTIFICATIONS | CBF_FAIL_ADVISES | + CBF_FAIL_POKES | CBF_FAIL_REQUESTS, 0); + ok(rc == DMLERR_NO_ERROR, "DdeInitializeA() returned %d\n", rc); + hszApplication = DdeCreateStringHandleA(ddeInst, shared_block, CP_WINANSI); + ok(hszApplication != NULL, "DdeCreateStringHandleA(%s) = NULL\n", shared_block); + shared_block += strlen(shared_block) + 1; + hszTopic = DdeCreateStringHandleA(ddeInst, shared_block, CP_WINANSI); + ok(hszTopic != NULL, "DdeCreateStringHandleA(%s) = NULL\n", shared_block); + hdde = DdeNameService(ddeInst, hszApplication, 0, DNS_REGISTER | DNS_FILTEROFF); + ok(hdde != NULL, "DdeNameService() failed le=%u\n", GetLastError()); + + timer = SetTimer(NULL, 0, CHILD_DDE_TIMEOUT, childTimeout); + + dde_ready = OpenEventA(EVENT_MODIFY_STATE, FALSE, "winetest_shlexec_dde_ready"); + SetEvent(dde_ready); + CloseHandle(dde_ready); + + while (GetMessageA(&msg, NULL, 0, 0)) + { + if (winetest_debug > 2) + trace("msg %d lParam=%ld wParam=%lu\n", msg.message, msg.lParam, msg.wParam); + DispatchMessageA(&msg); + } + + Sleep(500); + KillTimer(NULL, timer); + hdde = DdeNameService(ddeInst, hszApplication, 0, DNS_UNREGISTER); + ok(hdde != NULL, "DdeNameService() failed le=%u\n", GetLastError()); + ok(DdeFreeStringHandle(ddeInst, hszTopic), "DdeFreeStringHandle(topic)\n"); + ok(DdeFreeStringHandle(ddeInst, hszApplication), "DdeFreeStringHandle(application)\n"); + ok(DdeUninitialize(ddeInst), "DdeUninitialize() failed\n"); + } + else + { + dde_ready = OpenEventA(EVENT_MODIFY_STATE, FALSE, "winetest_shlexec_dde_ready"); + SetEvent(dde_ready); + CloseHandle(dde_ready); + } + + UnmapViewOfFile(shared_block); + + childPrintf(hFile, "ddeExec=%s\r\n", encodeA(ddeExec)); + } + + childPrintf(hFile, "Failures=%d\r\n", winetest_get_failures()); + CloseHandle(hFile); + + init_event(filename); + SetEvent(hEvent); + CloseHandle(hEvent); +} + +static void dump_child_(const char* file, int line) +{ + if (winetest_debug > 1) + { + char key[18]; + char* str; + int i, c; + + str=getChildString("Child", "cmdlineA"); + trace_(file, line)("cmdlineA='%s'\n", str); + c=GetPrivateProfileIntA("Child", "argcA", -1, child_file); + trace_(file, line)("argcA=%d\n",c); + for (i=0;i 1) - trace("%s\n", shell_call); + trace_(file, line)("Called %s\n", shell_call); DeleteFileA(child_file); SetLastError(0xcafebabe); @@ -118,7 +520,7 @@ static INT_PTR shell_execute(LPCSTR verb, LPCSTR file, LPCSTR parameters, LPCSTR * association it displays the 'Open With' dialog and I could not find * a flag to prevent this. */ - rc=(INT_PTR)ShellExecuteA(NULL, verb, file, parameters, directory, SW_HIDE); + rc=(INT_PTR)ShellExecuteA(NULL, verb, filename, parameters, directory, SW_HIDE); if (rc > 32) { @@ -137,58 +539,70 @@ static INT_PTR shell_execute(LPCSTR verb, LPCSTR file, LPCSTR parameters, LPCSTR rc = SE_ERR_NOASSOC; } } - if (!_todo_wait) - ok(wait_rc==WAIT_OBJECT_0 || rc <= 32, "%s WaitForSingleObject returned %d\n", shell_call, wait_rc); - else todo_wine - ok(wait_rc==WAIT_OBJECT_0 || rc <= 32, "%s WaitForSingleObject returned %d\n", shell_call, wait_rc); + todo_wine_if(_todo_wait) + okShell_(file, line)(wait_rc==WAIT_OBJECT_0 || rc <= 32, + "WaitForSingleObject returned %d\n", wait_rc); } /* The child process may have changed the result file, so let profile * functions know about it */ WritePrivateProfileStringA(NULL, NULL, NULL, child_file); - if (rc > 32) - dump_child(); + if (GetFileAttributesA(child_file) != INVALID_FILE_ATTRIBUTES) + { + int c; + dump_child_(file, line); + c = GetPrivateProfileIntA("Child", "Failures", -1, child_file); + if (c > 0) + winetest_add_failures(c); + okChildInt_(file, line, "ShlexecVarLE", 0); + okChildString_(file, line, "ShlexecVar", "Present", "Present"); + } if(!verb) { if (rc != rcEmpty && rcEmpty == SE_ERR_NOASSOC) /* NT4 */ bad_shellexecute = 1; - ok(rc == rcEmpty || broken(rc != rcEmpty && rcEmpty == SE_ERR_NOASSOC) /* NT4 */, - "%s Got different return value with empty string: %lu %lu\n", shell_call, rc, rcEmpty); + okShell_(file, line)(rc == rcEmpty || + broken(rc != rcEmpty && rcEmpty == SE_ERR_NOASSOC) /* NT4 */, + "Got different return value with empty string: %lu %lu\n", rc, rcEmpty); } return rc; } +#define shell_execute(verb, filename, parameters, directory) \ + shell_execute_(__FILE__, __LINE__, verb, filename, parameters, directory) -static INT_PTR shell_execute_ex(DWORD mask, LPCSTR verb, LPCSTR file, - LPCSTR parameters, LPCSTR directory, - LPCSTR class) +static INT_PTR shell_execute_ex_(const char* file, int line, + DWORD mask, LPCSTR verb, LPCSTR filename, + LPCSTR parameters, LPCSTR directory, + LPCSTR class) { + char smask[11]; SHELLEXECUTEINFOA sei; BOOL success; INT_PTR rc; + /* Add some flags so we can wait for the child process */ + mask |= SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE; + strcpy(shell_call, "ShellExecuteEx("); - if (mask) - { - char smask[11]; - sprintf(smask, "0x%x", mask); - strcat_param(shell_call, "mask", smask); - } + sprintf(smask, "0x%x", mask); + strcat_param(shell_call, "mask", smask); strcat_param(shell_call, "verb", verb); - strcat_param(shell_call, "file", file); + strcat_param(shell_call, "file", filename); strcat_param(shell_call, "params", parameters); strcat_param(shell_call, "dir", directory); strcat_param(shell_call, "class", class); strcat(shell_call, ")"); + strcat(shell_call, assoc_desc); if (winetest_debug > 1) - trace("%s\n", shell_call); + trace_(file, line)("Called %s\n", shell_call); sei.cbSize=sizeof(sei); - sei.fMask=SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE | mask; + sei.fMask=mask; sei.hwnd=NULL; sei.lpVerb=verb; - sei.lpFile=file; + sei.lpFile=filename; sei.lpParameters=parameters; sei.lpDirectory=directory; sei.nShow=SW_SHOWNORMAL; @@ -198,39 +612,70 @@ static INT_PTR shell_execute_ex(DWORD mask, LPCSTR verb, LPCSTR file, sei.hkeyClass=NULL; sei.dwHotKey=0; U(sei).hIcon=NULL; - sei.hProcess=NULL; /* Out */ + sei.hProcess=(HANDLE)0xdeadbeef; /* Out */ DeleteFileA(child_file); SetLastError(0xcafebabe); success=ShellExecuteExA(&sei); rc=(INT_PTR)sei.hInstApp; - ok((success && rc > 32) || (!success && rc <= 32), - "%s rc=%d and hInstApp=%ld is not allowed\n", shell_call, success, rc); + okShell_(file, line)((success && rc > 32) || (!success && rc <= 32), + "rc=%d and hInstApp=%ld is not allowed\n", + success, rc); if (rc > 32) { - int wait_rc; + DWORD wait_rc, rc; if (sei.hProcess!=NULL) { wait_rc=WaitForSingleObject(sei.hProcess, 5000); - ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject(hProcess) returned %d\n", wait_rc); + okShell_(file, line)(wait_rc==WAIT_OBJECT_0, + "WaitForSingleObject(hProcess) returned %d\n", + wait_rc); + wait_rc = GetExitCodeProcess(sei.hProcess, &rc); + okShell_(file, line)(wait_rc, "GetExitCodeProcess() failed le=%u\n", GetLastError()); + todo_wine_if(_todo_wait) + okShell_(file, line)(rc == 0, "child returned %u\n", rc); + CloseHandle(sei.hProcess); } wait_rc=WaitForSingleObject(hEvent, 5000); - if (!_todo_wait) - ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject returned %d\n", wait_rc); - else todo_wine - ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject returned %d\n", wait_rc); + todo_wine_if(_todo_wait) + okShell_(file, line)(wait_rc==WAIT_OBJECT_0, + "WaitForSingleObject returned %d\n", wait_rc); } + else + okShell_(file, line)(sei.hProcess==NULL, + "returned a process handle %p\n", sei.hProcess); + /* The child process may have changed the result file, so let profile * functions know about it */ WritePrivateProfileStringA(NULL, NULL, NULL, child_file); - if (rc > 32) - dump_child(); + if (GetFileAttributesA(child_file) != INVALID_FILE_ATTRIBUTES) + { + int c; + dump_child_(file, line); + c = GetPrivateProfileIntA("Child", "Failures", -1, child_file); + if (c > 0) + winetest_add_failures(c); + /* When NOZONECHECKS is specified the environment variables are not + * inherited if the process does not have elevated privileges. + */ + if ((mask & SEE_MASK_NOZONECHECKS) && skip_shlexec_tests) + { + okChildInt_(file, line, "ShlexecVarLE", 203); + okChildString_(file, line, "ShlexecVar", "", ""); + } + else + { + okChildInt_(file, line, "ShlexecVarLE", 0); + okChildString_(file, line, "ShlexecVar", "Present", "Present"); + } + } return rc; } - +#define shell_execute_ex(mask, verb, filename, parameters, directory, class) \ + shell_execute_ex_(__FILE__, __LINE__, mask, verb, filename, parameters, directory, class) /*** @@ -248,6 +693,8 @@ static BOOL create_test_association(const char* extension) sprintf(class, "shlexec%s", extension); rc=RegCreateKeyExA(HKEY_CLASSES_ROOT, extension, 0, NULL, 0, KEY_SET_VALUE, NULL, &hkey, NULL); + ok(rc == ERROR_SUCCESS || rc == ERROR_ACCESS_DENIED, + "could not create association %s (rc=%d)\n", class, rc); if (rc != ERROR_SUCCESS) return FALSE; @@ -355,6 +802,17 @@ static void create_test_verb_dde(const char* extension, const char* verb, char* cmd; LONG rc; + strcpy(assoc_desc, " Assoc "); + strcat_param(assoc_desc, "ext", extension); + strcat_param(assoc_desc, "verb", verb); + sprintf(shell, "%d", rawcmd); + strcat_param(assoc_desc, "rawcmd", shell); + strcat_param(assoc_desc, "cmdtail", cmdtail); + strcat_param(assoc_desc, "ddeexec", ddeexec); + strcat_param(assoc_desc, "app", application); + strcat_param(assoc_desc, "topic", topic); + strcat_param(assoc_desc, "ifexec", ifexec); + sprintf(shell, "shlexec%s\\shell", extension); rc=RegOpenKeyExA(HKEY_CLASSES_ROOT, shell, 0, KEY_CREATE_SUB_KEY, &hkey_shell); @@ -431,337 +889,18 @@ static void create_test_verb_dde(const char* extension, const char* verb, CloseHandle(hkey_cmd); } +/* Creates a class' non-DDE test verb. + * This function is meant to be used to create long term test verbs and thus + * does not trace them. + */ static void create_test_verb(const char* extension, const char* verb, int rawcmd, const char* cmdtail) { create_test_verb_dde(extension, verb, rawcmd, cmdtail, NULL, NULL, NULL, NULL); + reset_association_description(); } -/*** - * - * Functions to check that the child process was started just right - * (borrowed from dlls/kernel32/tests/process.c) - * - ***/ - -static const char* encodeA(const char* str) -{ - static char encoded[2*1024+1]; - char* ptr; - size_t len,i; - - if (!str) return ""; - len = strlen(str) + 1; - if (len >= sizeof(encoded)/2) - { - fprintf(stderr, "string is too long!\n"); - assert(0); - } - ptr = encoded; - for (i = 0; i < len; i++) - sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]); - ptr[2 * len] = '\0'; - return ptr; -} - -static unsigned decode_char(char c) -{ - if (c >= '0' && c <= '9') return c - '0'; - if (c >= 'a' && c <= 'f') return c - 'a' + 10; - assert(c >= 'A' && c <= 'F'); - return c - 'A' + 10; -} - -static char* decodeA(const char* str) -{ - static char decoded[1024]; - char* ptr; - size_t len,i; - - len = strlen(str) / 2; - if (!len--) return NULL; - if (len >= sizeof(decoded)) - { - fprintf(stderr, "string is too long!\n"); - assert(0); - } - ptr = decoded; - for (i = 0; i < len; i++) - ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]); - ptr[len] = '\0'; - return ptr; -} - -static void childPrintf(HANDLE h, const char* fmt, ...) -{ - va_list valist; - char buffer[1024]; - DWORD w; - - va_start(valist, fmt); - vsprintf(buffer, fmt, valist); - va_end(valist); - WriteFile(h, buffer, strlen(buffer), &w, NULL); -} - -static DWORD ddeInst; -static HSZ hszTopic; -static char ddeExec[MAX_PATH], ddeApplication[MAX_PATH]; -static BOOL post_quit_on_execute; - -static HDDEDATA CALLBACK ddeCb(UINT uType, UINT uFmt, HCONV hConv, - HSZ hsz1, HSZ hsz2, HDDEDATA hData, - ULONG_PTR dwData1, ULONG_PTR dwData2) -{ - DWORD size = 0; - - if (winetest_debug > 2) - trace("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n", - uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2); - - switch (uType) - { - case XTYP_CONNECT: - if (!DdeCmpStringHandles(hsz1, hszTopic)) - { - size = DdeQueryStringA(ddeInst, hsz2, ddeApplication, MAX_PATH, CP_WINANSI); - ok(size < MAX_PATH, "got size %d\n", size); - assert(size < MAX_PATH); - return (HDDEDATA)TRUE; - } - return (HDDEDATA)FALSE; - - case XTYP_EXECUTE: - size = DdeGetData(hData, (LPBYTE)ddeExec, MAX_PATH, 0); - ok(size < MAX_PATH, "got size %d\n", size); - assert(size < MAX_PATH); - DdeFreeDataHandle(hData); - if (post_quit_on_execute) - PostQuitMessage(0); - return (HDDEDATA)DDE_FACK; - - default: - return NULL; - } -} - -/* - * This is just to make sure the child won't run forever stuck in a GetMessage() - * loop when DDE fails for some reason. - */ -static void CALLBACK childTimeout(HWND wnd, UINT msg, UINT_PTR timer, DWORD time) -{ - trace("childTimeout called\n"); - - PostQuitMessage(0); -} - -static void doChild(int argc, char** argv) -{ - char *filename, longpath[MAX_PATH] = ""; - HANDLE hFile, map; - int i; - int rc; - HSZ hszApplication; - UINT_PTR timer; - HANDLE dde_ready; - MSG msg; - char *shared_block; - - filename=argv[2]; - hFile=CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); - if (hFile == INVALID_HANDLE_VALUE) - return; - - /* Arguments */ - childPrintf(hFile, "[Arguments]\r\n"); - if (winetest_debug > 2) - { - trace("cmdlineA='%s'\n", GetCommandLineA()); - trace("argcA=%d\n", argc); - } - childPrintf(hFile, "cmdlineA=%s\r\n", encodeA(GetCommandLineA())); - childPrintf(hFile, "argcA=%d\r\n", argc); - for (i = 0; i < argc; i++) - { - if (winetest_debug > 2) - trace("argvA%d='%s'\n", i, argv[i]); - childPrintf(hFile, "argvA%d=%s\r\n", i, encodeA(argv[i])); - } - GetModuleFileNameA(GetModuleHandleA(NULL), longpath, MAX_PATH); - childPrintf(hFile, "longPath=%s\r\n", encodeA(longpath)); - - map = OpenFileMappingA(FILE_MAP_READ, FALSE, "winetest_shlexec_dde_map"); - if (map != NULL) - { - shared_block = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 4096); - CloseHandle(map); - if (shared_block[0] != '\0' || shared_block[1] != '\0') - { - post_quit_on_execute = TRUE; - ddeInst = 0; - rc = DdeInitializeA(&ddeInst, ddeCb, CBF_SKIP_ALLNOTIFICATIONS | CBF_FAIL_ADVISES | - CBF_FAIL_POKES | CBF_FAIL_REQUESTS, 0); - ok(rc == DMLERR_NO_ERROR, "got %d\n", rc); - hszApplication = DdeCreateStringHandleA(ddeInst, shared_block, CP_WINANSI); - hszTopic = DdeCreateStringHandleA(ddeInst, shared_block + strlen(shared_block) + 1, CP_WINANSI); - assert(hszApplication && hszTopic); - assert(DdeNameService(ddeInst, hszApplication, 0, DNS_REGISTER | DNS_FILTEROFF)); - - timer = SetTimer(NULL, 0, 2500, childTimeout); - - dde_ready = OpenEventA(EVENT_MODIFY_STATE, FALSE, "winetest_shlexec_dde_ready"); - SetEvent(dde_ready); - CloseHandle(dde_ready); - - while (GetMessageA(&msg, NULL, 0, 0)) - DispatchMessageA(&msg); - - Sleep(500); - KillTimer(NULL, timer); - assert(DdeNameService(ddeInst, hszApplication, 0, DNS_UNREGISTER)); - assert(DdeFreeStringHandle(ddeInst, hszTopic)); - assert(DdeFreeStringHandle(ddeInst, hszApplication)); - assert(DdeUninitialize(ddeInst)); - } - else - { - dde_ready = OpenEventA(EVENT_MODIFY_STATE, FALSE, "winetest_shlexec_dde_ready"); - SetEvent(dde_ready); - CloseHandle(dde_ready); - } - - UnmapViewOfFile(shared_block); - - childPrintf(hFile, "ddeExec=%s\r\n", encodeA(ddeExec)); - } - - CloseHandle(hFile); - - init_event(filename); - SetEvent(hEvent); - CloseHandle(hEvent); -} - -static char* getChildString(const char* sect, const char* key) -{ - char buf[1024]; - char* ret; - - GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), child_file); - if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL; - assert(!(strlen(buf) & 1)); - ret = decodeA(buf); - return ret; -} - -static void dump_child(void) -{ - if (winetest_debug > 1) - { - char key[18]; - char* str; - int i, c; - - str=getChildString("Arguments", "cmdlineA"); - trace("cmdlineA='%s'\n", str); - c=GetPrivateProfileIntA("Arguments", "argcA", -1, child_file); - trace("argcA=%d\n",c); - for (i=0;i 32, "%s failed: rc=%lu\n", shell_call, rc); + okShell(rc > 32, "failed: rc=%lu\n", rc); /* if quoted, existing "drawback_file.noassoc" not prevents finding "drawback_file.noassoc foo.shlexec" on wine */ sprintf(fileA, "\"%s\\drawback_file.noassoc foo.shlexec\"", tmpdir); rc=shell_execute(NULL, fileA, NULL, NULL); - ok(rc > 32 || broken(rc == SE_ERR_FNF) /* Win95/NT4 */, - "%s failed: rc=%lu\n", shell_call, rc); + okShell(rc > 32 || broken(rc == SE_ERR_FNF) /* Win95/NT4 */, + "failed: rc=%lu\n", rc); /* error should be SE_ERR_FNF, not SE_ERR_NOASSOC */ sprintf(fileA, "\"%s\\drawback_file.noassoc\" foo.shlexec", tmpdir); rc=shell_execute(NULL, fileA, NULL, NULL); - ok(rc == SE_ERR_FNF, "%s succeeded: rc=%lu\n", shell_call, rc); + okShell(rc == SE_ERR_FNF, "returned %lu\n", rc); /* ""command"" not works on wine (and real win9x and w2k) */ sprintf(fileA, "\"\"%s\\simple.shlexec\"\"", tmpdir); rc=shell_execute(NULL, fileA, NULL, NULL); - todo_wine ok(rc > 32 || broken(rc == SE_ERR_FNF) /* Win9x/2000 */, - "%s failed: rc=%lu\n", shell_call, rc); + todo_wine okShell(rc > 32 || broken(rc == SE_ERR_FNF) /* Win9x/2000 */, + "failed: rc=%lu\n", rc); /* nonexisting "drawback_nonexist.noassoc" not prevents finding "drawback_nonexist.noassoc foo.shlexec" on wine */ sprintf(fileA, "%s\\drawback_nonexist.noassoc foo.shlexec", tmpdir); rc=shell_execute(NULL, fileA, NULL, NULL); - ok(rc > 32, "%s failed: rc=%lu\n", shell_call, rc); + okShell(rc > 32, "failed: rc=%lu\n", rc); /* is SEE_MASK_DOENVSUBST default flag? Should only be when XP emulates 9x (XP bug or real 95 or ME behavior ?) */ rc=shell_execute(NULL, "%TMPDIR%\\simple.shlexec", NULL, NULL); - todo_wine ok(rc == SE_ERR_FNF, "%s succeeded: rc=%lu\n", shell_call, rc); + todo_wine okShell(rc == SE_ERR_FNF, "returned %lu\n", rc); /* quoted */ rc=shell_execute(NULL, "\"%TMPDIR%\\simple.shlexec\"", NULL, NULL); - todo_wine ok(rc == SE_ERR_FNF, "%s succeeded: rc=%lu\n", shell_call, rc); + todo_wine okShell(rc == SE_ERR_FNF, "returned %lu\n", rc); /* test SEE_MASK_DOENVSUBST works */ rc=shell_execute_ex(SEE_MASK_DOENVSUBST | SEE_MASK_FLAG_NO_UI, NULL, "%TMPDIR%\\simple.shlexec", NULL, NULL, NULL); - ok(rc > 32, "%s failed: rc=%lu\n", shell_call, rc); + okShell(rc > 32, "failed: rc=%lu\n", rc); /* quoted lpFile does not work on real win95 and nt4 */ rc=shell_execute_ex(SEE_MASK_DOENVSUBST | SEE_MASK_FLAG_NO_UI, NULL, "\"%TMPDIR%\\simple.shlexec\"", NULL, NULL, NULL); - ok(rc > 32 || broken(rc == SE_ERR_FNF) /* Win95/NT4 */, - "%s failed: rc=%lu\n", shell_call, rc); + okShell(rc > 32 || broken(rc == SE_ERR_FNF) /* Win95/NT4 */, + "failed: rc=%lu\n", rc); } typedef struct @@ -1169,9 +1311,7 @@ static BOOL test_one_cmdline(const cmdline_tests_t* test) count = 0; while (test->args[count]) count++; - if ((test->todo & 0x1) == 0) - ok(cl2a_count == count, "%s: expected %d arguments, but got %d\n", test->cmd, count, cl2a_count); - else todo_wine + todo_wine_if(test->todo & 0x1) ok(cl2a_count == count, "%s: expected %d arguments, but got %d\n", test->cmd, count, cl2a_count); for (i = 0; i < cl2a_count; i++) @@ -1179,14 +1319,10 @@ static BOOL test_one_cmdline(const cmdline_tests_t* test) if (i < count) { MultiByteToWideChar(CP_ACP, 0, test->args[i], -1, argW, sizeof(argW)/sizeof(*argW)); - if ((test->todo & (1 << (i+4))) == 0) - ok(!lstrcmpW(*argsW, argW), "%s: arg[%d] expected %s but got %s\n", test->cmd, i, wine_dbgstr_w(argW), wine_dbgstr_w(*argsW)); - else todo_wine + todo_wine_if(test->todo & (1 << (i+4))) ok(!lstrcmpW(*argsW, argW), "%s: arg[%d] expected %s but got %s\n", test->cmd, i, wine_dbgstr_w(argW), wine_dbgstr_w(*argsW)); } - else if ((test->todo & 0x1) == 0) - ok(0, "%s: got extra arg[%d]=%s\n", test->cmd, i, wine_dbgstr_w(*argsW)); - else todo_wine + else todo_wine_if(test->todo & 0x1) ok(0, "%s: got extra arg[%d]=%s\n", test->cmd, i, wine_dbgstr_w(*argsW)); argsW++; } @@ -1416,6 +1552,18 @@ static void test_argify(void) const char* cmd; unsigned i, count; + /* Test with a long parameter */ + for (rc = 0; rc < MAX_PATH; rc++) + fileA[rc] = 'a' + rc % 26; + fileA[MAX_PATH-1] = '\0'; + sprintf(params, "shlexec \"%s\" %s", child_file, fileA); + + /* We need NOZONECHECKS on Win2003 to block a dialog */ + rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, params, NULL, NULL); + okShell(rc > 32, "failed: rc=%lu\n", rc); + okChildInt("argcA", 4); + okChildPath("argvA3", fileA); + if (skip_shlexec_tests) { skip("No argify tests due to lack of .shlexec association\n"); @@ -1439,40 +1587,33 @@ static void test_argify(void) /* trace("***** verb='%s' params='%s'\n", test->verb, test->params); */ rc = shell_execute_ex(SEE_MASK_DOENVSUBST, test->verb, fileA, test->params, NULL, NULL); - ok(rc > 32, "%s failed: rc=%lu\n", shell_call, rc); + okShell(rc > 32, "failed: rc=%lu\n", rc); count = 0; while (test->cmd.args[count]) count++; - if ((test->todo & 0x1) == 0) - /* +4 for the shlexec arguments, -1 because of the added "" - * argument for the CommandLineToArgvW() tests. - */ - okChildInt("argcA", 4 + count - 1); - else todo_wine + /* +4 for the shlexec arguments, -1 because of the added "" + * argument for the CommandLineToArgvW() tests. + */ + todo_wine_if(test->todo & 0x1) okChildInt("argcA", 4 + count - 1); - cmd = getChildString("Arguments", "cmdlineA"); + cmd = getChildString("Child", "cmdlineA"); /* Our commands are such that the verb immediately precedes the * part we are interested in. */ if (cmd) cmd = strstr(cmd, test->verb); if (cmd) cmd += strlen(test->verb); if (!cmd) cmd = "(null)"; - if ((test->todo & 0x2) == 0) - ok(!strcmp(cmd, test->cmd.cmd) || broken(!strcmp(cmd, bad->cmd)), - "%s: the cmdline is '%s' instead of '%s'\n", shell_call, cmd, test->cmd.cmd); - else todo_wine - ok(!strcmp(cmd, test->cmd.cmd) || broken(!strcmp(cmd, bad->cmd)), - "%s: the cmdline is '%s' instead of '%s'\n", shell_call, cmd, test->cmd.cmd); + todo_wine_if(test->todo & 0x2) + okShell(!strcmp(cmd, test->cmd.cmd) || broken(!strcmp(cmd, bad->cmd)), + "the cmdline is '%s' instead of '%s'\n", cmd, test->cmd.cmd); for (i = 0; i < count - 1; i++) { char argname[18]; sprintf(argname, "argvA%d", 4 + i); - if ((test->todo & (1 << (i+4))) == 0) - okChildStringBroken(argname, test->cmd.args[i+1], bad->args[i+1]); - else todo_wine + todo_wine_if(test->todo & (1 << (i+4))) okChildStringBroken(argname, test->cmd.args[i+1], bad->args[i+1]); } @@ -1480,18 +1621,6 @@ static void test_argify(void) has_cl2a = test_one_cmdline(&(test->cmd)); test++; } - - /* Test with a long parameter */ - for (rc = 0; rc < MAX_PATH; rc++) - fileA[rc] = 'a' + rc % 26; - fileA[MAX_PATH-1] = '\0'; - sprintf(params, "shlexec \"%s\" %s", child_file, fileA); - - /* We need NOZONECHECKS on Win2003 to block a dialog */ - rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, params, NULL, NULL); - ok(rc > 32, "%s failed: rc=%lu\n", shell_call, rc); - okChildInt("argcA", 4); - okChildString("argvA3", fileA); } static void test_filename(void) @@ -1544,38 +1673,19 @@ static void test_filename(void) } if (rc > 32) rc=33; - ok(rc==test->rc || - broken(quotedfile && rc == SE_ERR_FNF), /* NT4 */ - "%s failed: rc=%ld err=%u\n", shell_call, - rc, GetLastError()); + okShell(rc==test->rc || + broken(quotedfile && rc == SE_ERR_FNF), /* NT4 */ + "failed: rc=%ld err=%u\n", rc, GetLastError()); if (rc == 33) { const char* verb; - if ((test->todo & 0x2)==0) - { + todo_wine_if(test->todo & 0x2) okChildInt("argcA", 5); - } - else todo_wine - { - okChildInt("argcA", 5); - } verb=(test->verb ? test->verb : "Open"); - if ((test->todo & 0x4)==0) - { + todo_wine_if(test->todo & 0x4) okChildString("argvA3", verb); - } - else todo_wine - { - okChildString("argvA3", verb); - } - if ((test->todo & 0x8)==0) - { + todo_wine_if(test->todo & 0x8) okChildPath("argvA4", filename); - } - else todo_wine - { - okChildPath("argvA4", filename); - } } test++; } @@ -1587,16 +1697,8 @@ static void test_filename(void) rc=shell_execute(test->verb, filename, NULL, NULL); if (rc > 32) rc=33; - if ((test->todo & 0x1)==0) - { - ok(rc==test->rc, "%s failed: rc=%ld err=%u\n", shell_call, - rc, GetLastError()); - } - else todo_wine - { - ok(rc==test->rc, "%s failed: rc=%ld err=%u\n", shell_call, - rc, GetLastError()); - } + todo_wine_if(test->todo & 0x1) + okShell(rc==test->rc, "failed: rc=%ld err=%u\n", rc, GetLastError()); if (rc==0) { int count; @@ -1604,14 +1706,8 @@ static void test_filename(void) char* str; verb=(test->verb ? test->verb : "Open"); - if ((test->todo & 0x4)==0) - { + todo_wine_if(test->todo & 0x4) okChildString("argvA3", verb); - } - else todo_wine - { - okChildString("argvA3", verb); - } count=4; str=filename; @@ -1623,27 +1719,15 @@ static void test_filename(void) if (space) *space='\0'; sprintf(attrib, "argvA%d", count); - if ((test->todo & 0x8)==0) - { + todo_wine_if(test->todo & 0x8) okChildPath(attrib, str); - } - else todo_wine - { - okChildPath(attrib, str); - } count++; if (!space) break; str=space+1; } - if ((test->todo & 0x2)==0) - { + todo_wine_if(test->todo & 0x2) okChildInt("argcA", count); - } - else todo_wine - { - okChildInt("argcA", count); - } } test++; } @@ -1658,8 +1742,7 @@ static void test_filename(void) */ sprintf(filename, "\"%s\\test file.shlexec\"", tmpdir); rc=shell_execute(NULL, filename, NULL, NULL); - ok(rc > 32, "%s failed: rc=%ld err=%u\n", shell_call, rc, - GetLastError()); + okShell(rc > 32, "failed: rc=%ld err=%u\n", rc, GetLastError()); okChildInt("argcA", 5); okChildString("argvA3", "Open"); sprintf(filename, "%s\\test file.shlexec", tmpdir); @@ -1726,7 +1809,8 @@ static void test_fileurls(void) return; } - rc = (INT_PTR)ShellExecuteA(NULL, NULL, "file:///nosuchfile.shlexec", NULL, NULL, SW_SHOWNORMAL); + rc = shell_execute_ex(SEE_MASK_FLAG_NO_UI, NULL, + "file:///nosuchfile.shlexec", NULL, NULL, NULL); if (rc > 32) { win_skip("shell32 is too old (likely < 4.72). Skipping the file URL tests\n"); @@ -1769,37 +1853,23 @@ static void test_fileurls(void) } if (test->flags & URL_SUCCESS) { - if ((test->todo & 0x1) == 0) - ok(rc > 32, "%s failed: bad rc=%lu\n", shell_call, rc); - else todo_wine - ok(rc > 32, "%s failed: bad rc=%lu\n", shell_call, rc); + todo_wine_if(test->todo & 0x1) + okShell(rc > 32, "failed: bad rc=%lu\n", rc); } else { - if ((test->todo & 0x1) == 0) - ok(rc == SE_ERR_FNF || rc == SE_ERR_PNF || - broken(rc == SE_ERR_ACCESSDENIED) /* win2000 */, - "%s failed: bad rc=%lu\n", shell_call, rc); - else todo_wine - ok(rc == SE_ERR_FNF || rc == SE_ERR_PNF || - broken(rc == SE_ERR_ACCESSDENIED) /* win2000 */, - "%s failed: bad rc=%lu\n", shell_call, rc); + todo_wine_if(test->todo & 0x1) + okShell(rc == SE_ERR_FNF || rc == SE_ERR_PNF || + broken(rc == SE_ERR_ACCESSDENIED) /* win2000 */, + "failed: bad rc=%lu\n", rc); } if (rc == 33) { - if ((test->todo & 0x2) == 0) + todo_wine_if(test->todo & 0x2) okChildInt("argcA", 5); - else todo_wine - okChildInt("argcA", 5); - - if ((test->todo & 0x4) == 0) + todo_wine_if(test->todo & 0x4) okChildString("argvA3", "Open"); - else todo_wine - okChildString("argvA3", "Open"); - - if ((test->todo & 0x8) == 0) - okChildPath("argvA4", filename); - else todo_wine + todo_wine_if(test->todo & 0x8) okChildPath("argvA4", filename); } test++; @@ -1922,30 +1992,17 @@ static void test_find_executable(void) rc=(INT_PTR)FindExecutableA(filename, NULL, command); if (rc > 32) rc=33; - if ((test->todo & 0x10)==0) - { + todo_wine_if(test->todo & 0x10) ok(rc==test->rc, "FindExecutable(%s) failed: rc=%ld\n", filename, rc); - } - else todo_wine - { - ok(rc==test->rc, "FindExecutable(%s) failed: rc=%ld\n", filename, rc); - } if (rc > 32) { BOOL equal; equal=strcmp(command, argv0) == 0 || /* NT4 returns an extra 0x8 character! */ (strlen(command) == strlen(argv0)+1 && strncmp(command, argv0, strlen(argv0)) == 0); - if ((test->todo & 0x20)==0) - { + todo_wine_if(test->todo & 0x20) ok(equal, "FindExecutable(%s) returned command='%s' instead of '%s'\n", filename, command, argv0); - } - else todo_wine - { - ok(equal, "FindExecutable(%s) returned command='%s' instead of '%s'\n", - filename, command, argv0); - } } test++; } @@ -1982,7 +2039,7 @@ static void test_lnks(void) /* Should open through our association */ sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir); rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL, NULL); - ok(rc > 32, "%s failed: rc=%lu err=%u\n", shell_call, rc, GetLastError()); + okShell(rc > 32, "failed: rc=%lu err=%u\n", rc, GetLastError()); okChildInt("argcA", 5); okChildString("argvA3", "Open"); sprintf(params, "%s\\test file.shlexec", tmpdir); @@ -1990,7 +2047,7 @@ static void test_lnks(void) okChildPath("argvA4", filename); todo_wait rc=shell_execute_ex(SEE_MASK_NOZONECHECKS|SEE_MASK_DOENVSUBST, NULL, "%TMPDIR%\\test_shortcut_shlexec.lnk", NULL, NULL, NULL); - ok(rc > 32, "%s failed: rc=%lu err=%u\n", shell_call, rc, GetLastError()); + okShell(rc > 32, "failed: rc=%lu err=%u\n", rc, GetLastError()); okChildInt("argcA", 5); todo_wine okChildString("argvA3", "Open"); sprintf(params, "%s\\test file.shlexec", tmpdir); @@ -2001,15 +2058,19 @@ static void test_lnks(void) /* Should just run our executable */ sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir); rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL, NULL); - ok(rc > 32, "%s failed: rc=%lu err=%u\n", shell_call, rc, GetLastError()); + okShell(rc > 32, "failed: rc=%lu err=%u\n", rc, GetLastError()); okChildInt("argcA", 4); okChildString("argvA3", "Lnk"); - /* Lnk's ContextMenuHandler has priority over an explicit class */ - rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL, "shlexec.shlexec"); - ok(rc > 32, "%s failed: rc=%lu err=%u\n", shell_call, rc, GetLastError()); - okChildInt("argcA", 4); - okChildString("argvA3", "Lnk"); + if (!skip_shlexec_tests) + { + /* An explicit class overrides lnk's ContextMenuHandler */ + rc=shell_execute_ex(SEE_MASK_CLASSNAME | SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL, "shlexec.shlexec"); + okShell(rc > 32, "failed: rc=%lu err=%u\n", rc, GetLastError()); + okChildInt("argcA", 5); + okChildString("argvA3", "Open"); + okChildPath("argvA4", filename); + } if (dllver.dwMajorVersion>=6) { @@ -2026,8 +2087,7 @@ static void test_lnks(void) c++; } rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL, NULL); - ok(rc > 32, "%s failed: rc=%lu err=%u\n", shell_call, rc, - GetLastError()); + okShell(rc > 32, "failed: rc=%lu err=%u\n", rc, GetLastError()); okChildInt("argcA", 4); okChildString("argvA3", "Lnk"); } @@ -2043,43 +2103,16 @@ static void test_lnks(void) NULL, NULL); if (rc > 32) rc=33; - if ((test->todo & 0x1)==0) - { - ok(rc==test->rc, "%s failed: rc=%lu err=%u\n", shell_call, - rc, GetLastError()); - } - else todo_wine - { - ok(rc==test->rc, "%s failed: rc=%lu err=%u\n", shell_call, - rc, GetLastError()); - } + todo_wine_if(test->todo & 0x1) + okShell(rc==test->rc, "failed: rc=%lu err=%u\n", rc, GetLastError()); if (rc==0) { - if ((test->todo & 0x2)==0) - { + todo_wine_if(test->todo & 0x2) okChildInt("argcA", 5); - } - else - { - okChildInt("argcA", 5); - } - if ((test->todo & 0x4)==0) - { + todo_wine_if(test->todo & 0x4) okChildString("argvA3", "Lnk"); - } - else todo_wine - { - okChildString("argvA3", "Lnk"); - } sprintf(params, test->basename, tmpdir); - if ((test->todo & 0x8)==0) - { - okChildPath("argvA4", params); - } - else - { - okChildPath("argvA4", params); - } + okChildPath("argvA4", params); } test++; } @@ -2097,7 +2130,7 @@ static void test_exes(void) /* We need NOZONECHECKS on Win2003 to block a dialog */ rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, params, NULL, NULL); - ok(rc > 32, "%s returned %lu\n", shell_call, rc); + okShell(rc > 32, "returned %lu\n", rc); okChildInt("argcA", 4); okChildString("argvA3", "Exec"); @@ -2108,7 +2141,7 @@ static void test_exes(void) { rc=shell_execute(NULL, filename, params, NULL); todo_wine { - ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%lu\n", shell_call, rc); + okShell(rc==SE_ERR_NOASSOC, "returned %lu\n", rc); } } } @@ -2120,11 +2153,28 @@ static void test_exes(void) /* test combining executable and parameters */ sprintf(filename, "%s shlexec \"%s\" Exec", argv0, child_file); rc = shell_execute(NULL, filename, NULL, NULL); - ok(rc == SE_ERR_FNF, "%s returned %lu\n", shell_call, rc); + okShell(rc == SE_ERR_FNF, "returned %lu\n", rc); sprintf(filename, "\"%s\" shlexec \"%s\" Exec", argv0, child_file); rc = shell_execute(NULL, filename, NULL, NULL); - ok(rc == SE_ERR_FNF, "%s returned %lu\n", shell_call, rc); + okShell(rc == SE_ERR_FNF, "returned %lu\n", rc); + + /* A verb, even if invalid, overrides the normal handling of executables */ + todo_wait rc = shell_execute_ex(SEE_MASK_FLAG_NO_UI, + "notaverb", argv0, NULL, NULL, NULL); + todo_wine okShell(rc == SE_ERR_NOASSOC, "returned %lu\n", rc); + + if (!skip_shlexec_tests) + { + /* A class overrides the normal handling of executables too */ + /* FIXME SEE_MASK_FLAG_NO_UI is only needed due to Wine's bug */ + rc = shell_execute_ex(SEE_MASK_CLASSNAME | SEE_MASK_FLAG_NO_UI, + NULL, argv0, NULL, NULL, ".shlexec"); + todo_wine okShell(rc > 32, "returned %lu\n", rc); + okChildInt("argcA", 5); + todo_wine okChildString("argvA3", "Open"); + todo_wine okChildPath("argvA4", argv0); + } } typedef struct @@ -2143,32 +2193,37 @@ static dde_tests_t dde_tests[] = { /* Test passing and not passing command-line * argument, no DDE */ - {"", NULL, NULL, NULL, NULL, FALSE, ""}, - {"\"%1\"", NULL, NULL, NULL, NULL, TRUE, ""}, + {"", NULL, NULL, NULL, NULL, 0, ""}, + {"\"%1\"", NULL, NULL, NULL, NULL, 1, ""}, /* Test passing and not passing command-line * argument, with DDE */ - {"", "[open(\"%1\")]", "shlexec", "dde", NULL, FALSE, "[open(\"%s\")]"}, - {"\"%1\"", "[open(\"%1\")]", "shlexec", "dde", NULL, TRUE, "[open(\"%s\")]"}, + {"", "[open(\"%1\")]", "shlexec", "dde", NULL, 0, "[open(\"%s\")]"}, + {"\"%1\"", "[open(\"%1\")]", "shlexec", "dde", NULL, 1, "[open(\"%s\")]"}, /* Test unquoted %1 in command and ddeexec * (test filename has space) */ {"%1", "[open(%1)]", "shlexec", "dde", NULL, 2, "[open(%s)]", TRUE /* before vista */}, /* Test ifexec precedence over ddeexec */ - {"", "[open(\"%1\")]", "shlexec", "dde", "[ifexec(\"%1\")]", FALSE, "[ifexec(\"%s\")]"}, + {"", "[open(\"%1\")]", "shlexec", "dde", "[ifexec(\"%1\")]", 0, "[ifexec(\"%s\")]"}, /* Test default DDE topic */ - {"", "[open(\"%1\")]", "shlexec", NULL, NULL, FALSE, "[open(\"%s\")]"}, + {"", "[open(\"%1\")]", "shlexec", NULL, NULL, 0, "[open(\"%s\")]"}, /* Test default DDE application */ - {"", "[open(\"%1\")]", NULL, "dde", NULL, FALSE, "[open(\"%s\")]"}, + {"", "[open(\"%1\")]", NULL, "dde", NULL, 0, "[open(\"%s\")]"}, {NULL} }; +static int waitforinputidle_count; static DWORD WINAPI hooked_WaitForInputIdle(HANDLE process, DWORD timeout) { + waitforinputidle_count++; + if (winetest_debug > 1) + trace("WaitForInputIdle() waiting for dde event timeout=min(%u,5s)\n", timeout); + timeout = timeout < 5000 ? timeout : 5000; return WaitForSingleObject(dde_ready_event, timeout); } @@ -2187,6 +2242,7 @@ static void hook_WaitForInputIdle(DWORD (WINAPI *new_func)(HANDLE, DWORD)) PIMAGE_NT_HEADERS nt_headers; DWORD import_directory_rva; PIMAGE_IMPORT_DESCRIPTOR import_descriptor; + int hook_count = 0; base = (char *) GetModuleHandleA("shell32.dll"); nt_headers = (PIMAGE_NT_HEADERS)(base + ((PIMAGE_DOS_HEADER) base)->e_lfanew); @@ -2224,6 +2280,9 @@ static void hook_WaitForInputIdle(DWORD (WINAPI *new_func)(HANDLE, DWORD)) VirtualProtect(&iat_entry->u1.Function, sizeof(ULONG_PTR), PAGE_READWRITE, &old_prot); iat_entry->u1.Function = (ULONG_PTR) new_func; VirtualProtect(&iat_entry->u1.Function, sizeof(ULONG_PTR), old_prot, &old_prot); + if (winetest_debug > 1) + trace("Hooked %s.WaitForInputIdle\n", import_module_name); + hook_count++; break; } } @@ -2235,6 +2294,7 @@ static void hook_WaitForInputIdle(DWORD (WINAPI *new_func)(HANDLE, DWORD)) import_descriptor++; } + ok(hook_count, "Could not hook WaitForInputIdle()\n"); } static void test_dde(void) @@ -2245,6 +2305,7 @@ static void test_dde(void) INT_PTR rc; HANDLE map; char *shared_block; + DWORD ddeflags; hook_WaitForInputIdle(hooked_WaitForInputIdle); @@ -2258,6 +2319,7 @@ static void test_dde(void) 4096, "winetest_shlexec_dde_map"); shared_block = MapViewOfFile(map, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 4096); + ddeflags = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI; test = dde_tests; while (test->command) { @@ -2281,10 +2343,33 @@ static void test_dde(void) } ddeExec[0] = 0; - dde_ready_event = CreateEventA(NULL, FALSE, FALSE, "winetest_shlexec_dde_ready"); - rc = shell_execute_ex(SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI, NULL, filename, NULL, NULL, NULL); + waitforinputidle_count = 0; + dde_ready_event = CreateEventA(NULL, TRUE, FALSE, "winetest_shlexec_dde_ready"); + rc = shell_execute_ex(ddeflags, NULL, filename, NULL, NULL, NULL); CloseHandle(dde_ready_event); - ok(32 < rc, "%s failed: rc=%lu err=%u\n", shell_call, rc, GetLastError()); + if (!(ddeflags & SEE_MASK_WAITFORINPUTIDLE) && rc == SE_ERR_DDEFAIL && + GetLastError() == ERROR_FILE_NOT_FOUND && + strcmp(winetest_platform, "windows") == 0) + { + /* Windows 10 does not call WaitForInputIdle() for DDE which creates + * a race condition as the DDE server may not have time to start up. + * When that happens the test fails with the above results and we + * compensate by forcing the WaitForInputIdle() call. + */ + trace("Adding SEE_MASK_WAITFORINPUTIDLE for Windows 10\n"); + ddeflags |= SEE_MASK_WAITFORINPUTIDLE; + delete_test_association(".sde"); + Sleep(CHILD_DDE_TIMEOUT); + continue; + } + okShell(32 < rc, "failed: rc=%lu err=%u\n", rc, GetLastError()); + if (test->ddeexec) + okShell(waitforinputidle_count == 1 || + broken(waitforinputidle_count == 0) /* Win10 race */, + "WaitForInputIdle() was called %u times\n", + waitforinputidle_count); + else + okShell(waitforinputidle_count == 0, "WaitForInputIdle() was called %u times for a non-DDE case\n", waitforinputidle_count); if (32 < rc) { @@ -2298,6 +2383,7 @@ static void test_dde(void) sprintf(params, test->expectedDdeExec, filename); okChildPath("ddeExec", params); } + reset_association_description(); delete_test_association(".sde"); test++; @@ -2308,7 +2394,7 @@ static void test_dde(void) hook_WaitForInputIdle((void *) WaitForInputIdle); } -#define DDE_DEFAULT_APP_VARIANTS 2 +#define DDE_DEFAULT_APP_VARIANTS 3 typedef struct { const char* command; @@ -2319,42 +2405,44 @@ typedef struct static dde_default_app_tests_t dde_default_app_tests[] = { - /* Windows XP and 98 handle default DDE app names in different ways. - * The application name we see in the first test determines the pattern - * of application names and return codes we will look for. */ + /* There are three possible sets of results: Windows <= 2000, XP SP1 and + * >= XP SP2. Use the first two tests to determine which results to expect. + */ /* Test unquoted existing filename with a space */ - {"%s\\test file.exe", {"test file", "test"}, 0x0, {33, 33}}, - {"%s\\test file.exe param", {"test file", "test"}, 0x0, {33, 33}}, + {"%s\\test file.exe", {"test file", "test file", "test"}, 0x0, {33, 33, 33}}, + {"%s\\test2 file.exe", {"test2", "", "test2"}, 0x0, {33, 5, 33}}, + + /* Test unquoted existing filename with a space */ + {"%s\\test file.exe param", {"test file", "test file", "test"}, 0x0, {33, 33, 33}}, /* Test quoted existing filename with a space */ - {"\"%s\\test file.exe\"", {"test file", "test file"}, 0x0, {33, 33}}, - {"\"%s\\test file.exe\" param", {"test file", "test file"}, 0x0, {33, 33}}, + {"\"%s\\test file.exe\"", {"test file", "test file", "test file"}, 0x0, {33, 33, 33}}, + {"\"%s\\test file.exe\" param", {"test file", "test file", "test file"}, 0x0, {33, 33, 33}}, /* Test unquoted filename with a space that doesn't exist, but * test2.exe does */ - {"%s\\test2 file.exe", {"test2", "test2"}, 0x0, {33, 33}}, - {"%s\\test2 file.exe param", {"test2", "test2"}, 0x0, {33, 33}}, + {"%s\\test2 file.exe param", {"test2", "", "test2"}, 0x0, {33, 5, 33}}, /* Test quoted filename with a space that does not exist */ - {"\"%s\\test2 file.exe\"", {"", "test2 file"}, 0x0, {5, 33}}, - {"\"%s\\test2 file.exe\" param", {"", "test2 file"}, 0x0, {5, 33}}, + {"\"%s\\test2 file.exe\"", {"", "", "test2 file"}, 0x0, {5, 2, 33}}, + {"\"%s\\test2 file.exe\" param", {"", "", "test2 file"}, 0x0, {5, 2, 33}}, /* Test filename supplied without the extension */ - {"%s\\test2", {"test2", "test2"}, 0x0, {33, 33}}, - {"%s\\test2 param", {"test2", "test2"}, 0x0, {33, 33}}, + {"%s\\test2", {"test2", "", "test2"}, 0x0, {33, 5, 33}}, + {"%s\\test2 param", {"test2", "", "test2"}, 0x0, {33, 5, 33}}, /* Test an unquoted nonexistent filename */ - {"%s\\notexist.exe", {"", "notexist"}, 0x0, {5, 33}}, - {"%s\\notexist.exe param", {"", "notexist"}, 0x0, {5, 33}}, + {"%s\\notexist.exe", {"", "", "notexist"}, 0x0, {5, 2, 33}}, + {"%s\\notexist.exe param", {"", "", "notexist"}, 0x0, {5, 2, 33}}, /* Test an application that will be found on the path */ - {"cmd", {"cmd", "cmd"}, 0x0, {33, 33}}, - {"cmd param", {"cmd", "cmd"}, 0x0, {33, 33}}, + {"cmd", {"cmd", "cmd", "cmd"}, 0x0, {33, 33, 33}}, + {"cmd param", {"cmd", "cmd", "cmd"}, 0x0, {33, 33, 33}}, /* Test an application that will not be found on the path */ - {"xyzwxyzwxyz", {"", "xyzwxyzwxyz"}, 0x0, {5, 33}}, - {"xyzwxyzwxyz param", {"", "xyzwxyzwxyz"}, 0x0, {5, 33}}, + {"xyzwxyzwxyz", {"", "", "xyzwxyzwxyz"}, 0x0, {5, 2, 33}}, + {"xyzwxyzwxyz param", {"", "", "xyzwxyzwxyz"}, 0x0, {5, 2, 33}}, {NULL, {NULL}, 0, {0}} }; @@ -2431,47 +2519,36 @@ static void test_dde_default_app(void) while (GetMessageA(&msg, NULL, 0, 0)) DispatchMessageA(&msg); rc = msg.wParam > 32 ? 33 : msg.wParam; - /* First test, find which set of test data we expect to see */ - if (test == dde_default_app_tests) + /* The first two tests determine which set of results to expect. + * First check the platform as only the first set of results is + * acceptable for Wine. + */ + if (strcmp(winetest_platform, "wine")) { - int i; - for (i=0; iexpectedDdeApplication[i])) - { - which = i; - break; - } + if (strcmp(ddeApplication, test->expectedDdeApplication[0])) + which = 2; + } + else if (test == dde_default_app_tests + 1) + { + if (which == 0 && rc == test->rc[1]) + which = 1; + trace("DDE result variant %d\n", which); } - if (i == DDE_DEFAULT_APP_VARIANTS) - skip("Default DDE application test does not match any available results, using first expected data set.\n"); } - if ((test->todo & 0x1)==0) - { - ok(rc==test->rc[which], "%s failed: rc=%lu err=%u\n", shell_call, - rc, GetLastError()); - } - else todo_wine - { - ok(rc==test->rc[which], "%s failed: rc=%lu err=%u\n", shell_call, - rc, GetLastError()); - } + todo_wine_if(test->todo & 0x1) + okShell(rc==test->rc[which], "failed: rc=%lu err=%u\n", + rc, GetLastError()); if (rc == 33) { - if ((test->todo & 0x2)==0) - { + todo_wine_if(test->todo & 0x2) ok(!strcmp(ddeApplication, test->expectedDdeApplication[which]), "Expected application '%s', got '%s'\n", test->expectedDdeApplication[which], ddeApplication); - } - else todo_wine - { - ok(!strcmp(ddeApplication, test->expectedDdeApplication[which]), - "Expected application '%s', got '%s'\n", - test->expectedDdeApplication[which], ddeApplication); - } } + reset_association_description(); delete_test_association(".sde"); test++; @@ -2606,6 +2683,9 @@ static void init_test(void) create_test_verb(".shlexec", "QuotedLowerL", 0, "QuotedLowerL \"%l\""); create_test_verb(".shlexec", "UpperL", 0, "UpperL %L"); create_test_verb(".shlexec", "QuotedUpperL", 0, "QuotedUpperL \"%L\""); + + /* Set an environment variable to see if it is inherited */ + SetEnvironmentVariableA("ShlexecVar", "Present"); } static void cleanup_test(void) @@ -2650,7 +2730,7 @@ static void test_directory(void) SetCurrentDirectoryA(tmpdir); rc=shell_execute_ex(SEE_MASK_NOZONECHECKS|SEE_MASK_FLAG_NO_UI, NULL, "test2.exe", params, NULL, NULL); - ok(rc > 32, "%s returned %lu\n", shell_call, rc); + okShell(rc > 32, "returned %lu\n", rc); okChildInt("argcA", 4); okChildString("argvA3", "Exec"); todo_wine okChildPath("longPath", path); @@ -2658,12 +2738,12 @@ static void test_directory(void) rc=shell_execute_ex(SEE_MASK_NOZONECHECKS|SEE_MASK_FLAG_NO_UI, NULL, "test2.exe", params, NULL, NULL); - ok(rc == SE_ERR_FNF, "%s returned %lu\n", shell_call, rc); + okShell(rc == SE_ERR_FNF, "returned %lu\n", rc); /* Explicitly specify the directory to use */ rc=shell_execute_ex(SEE_MASK_NOZONECHECKS|SEE_MASK_FLAG_NO_UI, NULL, "test2.exe", params, tmpdir, NULL); - ok(rc > 32, "%s returned %lu\n", shell_call, rc); + okShell(rc > 32, "returned %lu\n", rc); okChildInt("argcA", 4); okChildString("argvA3", "Exec"); todo_wine okChildPath("longPath", path); @@ -2671,11 +2751,11 @@ static void test_directory(void) /* Specify it through an environment variable */ rc=shell_execute_ex(SEE_MASK_NOZONECHECKS|SEE_MASK_FLAG_NO_UI, NULL, "test2.exe", params, "%TMPDIR%", NULL); - todo_wine ok(rc == SE_ERR_FNF, "%s returned %lu\n", shell_call, rc); + todo_wine okShell(rc == SE_ERR_FNF, "returned %lu\n", rc); rc=shell_execute_ex(SEE_MASK_DOENVSUBST|SEE_MASK_NOZONECHECKS|SEE_MASK_FLAG_NO_UI, NULL, "test2.exe", params, "%TMPDIR%", NULL); - ok(rc > 32, "%s returned %lu\n", shell_call, rc); + okShell(rc > 32, "returned %lu\n", rc); okChildInt("argcA", 4); okChildString("argvA3", "Exec"); todo_wine okChildPath("longPath", path); @@ -2684,7 +2764,7 @@ static void test_directory(void) sprintf(dirpath, "%s:%s", curdir, tmpdir); rc=shell_execute_ex(SEE_MASK_NOZONECHECKS|SEE_MASK_FLAG_NO_UI, NULL, "test2.exe", params, dirpath, NULL); - ok(rc == SE_ERR_FNF, "%s returned %lu\n", shell_call, rc); + okShell(rc == SE_ERR_FNF, "returned %lu\n", rc); } START_TEST(shlexec) @@ -2694,7 +2774,8 @@ START_TEST(shlexec) if (myARGC >= 3) { doChild(myARGC, myARGV); - exit(0); + /* Skip the tests/failures trace for child processes */ + ExitProcess(winetest_get_failures()); } init_test(); diff --git a/rostests/winetests/shell32/shlfolder.c b/rostests/winetests/shell32/shlfolder.c index 975e73d83d7..e59bfde2a63 100644 --- a/rostests/winetests/shell32/shlfolder.c +++ b/rostests/winetests/shell32/shlfolder.c @@ -388,6 +388,14 @@ static void test_EnumObjects(IShellFolder *iFolder) SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM, SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM, }; + static const ULONG full_attrs[5] = + { + SFGAO_CAPABILITYMASK | SFGAO_STORAGE | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR, + SFGAO_CAPABILITYMASK | SFGAO_STORAGE | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR, + SFGAO_CAPABILITYMASK | SFGAO_STREAM | SFGAO_FILESYSTEM, + SFGAO_CAPABILITYMASK | SFGAO_STREAM | SFGAO_FILESYSTEM, + SFGAO_CAPABILITYMASK | SFGAO_STREAM | SFGAO_FILESYSTEM, + }; hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList); ok(hr == S_OK, "EnumObjects failed %08x\n", hr); @@ -440,6 +448,11 @@ static void test_EnumObjects(IShellFolder *iFolder) ok(flags == attrs[i] || flags == (attrs[i] & ~SFGAO_FILESYSANCESTOR), /* Win9x, NT4 */ "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]); + + flags = ~0u; + hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags); + ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr); + ok((flags & ~SFGAO_HASSUBFOLDER) == full_attrs[i], "%d: got %08x expected %08x\n", i, flags, full_attrs[i]); } for (i=0;i<5;i++) @@ -1889,14 +1902,14 @@ static void test_SHGetFolderPathA(void) if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; hr = pSHGetFolderPathA( 0, CSIDL_PROGRAM_FILES, 0, SHGFP_TYPE_CURRENT, path ); - ok( !hr, "SHGetFolderPathA failed %x\n", hr ); + ok( hr == S_OK, "SHGetFolderPathA failed %x\n", hr ); hr = pSHGetFolderPathA( 0, CSIDL_PROGRAM_FILESX86, 0, SHGFP_TYPE_CURRENT, path_x86 ); if (hr == E_FAIL) { win_skip( "Program Files (x86) not supported\n" ); return; } - ok( !hr, "SHGetFolderPathA failed %x\n", hr ); + ok( hr == S_OK, "SHGetFolderPathA failed %x\n", hr ); if (is_win64) { ok( lstrcmpiA( path, path_x86 ), "paths are identical '%s'\n", path ); @@ -1924,14 +1937,14 @@ static void test_SHGetFolderPathA(void) } hr = pSHGetFolderPathA( 0, CSIDL_PROGRAM_FILES_COMMON, 0, SHGFP_TYPE_CURRENT, path ); - ok( !hr, "SHGetFolderPathA failed %x\n", hr ); + ok( hr == S_OK, "SHGetFolderPathA failed %x\n", hr ); hr = pSHGetFolderPathA( 0, CSIDL_PROGRAM_FILES_COMMONX86, 0, SHGFP_TYPE_CURRENT, path_x86 ); if (hr == E_FAIL) { win_skip( "Common Files (x86) not supported\n" ); return; } - ok( !hr, "SHGetFolderPathA failed %x\n", hr ); + ok( hr == S_OK, "SHGetFolderPathA failed %x\n", hr ); if (is_win64) { ok( lstrcmpiA( path, path_x86 ), "paths are identical '%s'\n", path ); @@ -5256,6 +5269,63 @@ static void test_SHCreateShellFolderViewEx(void) IShellFolder_Release(desktop); } +static void test_DataObject(void) +{ + IShellFolder *desktop; + IDataObject *data_obj; + HRESULT hres; + IEnumIDList *peidl; + LPITEMIDLIST apidl; + FORMATETC fmt; + DWORD cf_shellidlist; + STGMEDIUM medium; + + SHGetDesktopFolder(&desktop); + + hres = IShellFolder_EnumObjects(desktop, NULL, + SHCONTF_NONFOLDERS|SHCONTF_FOLDERS|SHCONTF_INCLUDEHIDDEN, &peidl); + ok(hres == S_OK, "got %x\n", hres); + + if(IEnumIDList_Next(peidl, 1, &apidl, NULL) != S_OK) { + skip("no files on desktop - skipping GetDataObject tests\n"); + IEnumIDList_Release(peidl); + IShellFolder_Release(desktop); + return; + } + IEnumIDList_Release(peidl); + + hres = IShellFolder_GetUIObjectOf(desktop, NULL, 1, (LPCITEMIDLIST*)&apidl, + &IID_IDataObject, NULL, (void**)&data_obj); + ok(hres == S_OK, "got %x\n", hres); + pILFree(apidl); + IShellFolder_Release(desktop); + + cf_shellidlist = RegisterClipboardFormatW(CFSTR_SHELLIDLISTW); + fmt.cfFormat = cf_shellidlist; + fmt.ptd = NULL; + fmt.dwAspect = DVASPECT_CONTENT; + fmt.lindex = -1; + fmt.tymed = TYMED_HGLOBAL; + hres = IDataObject_QueryGetData(data_obj, &fmt); + ok(hres == S_OK, "got %x\n", hres); + + fmt.tymed = TYMED_HGLOBAL | TYMED_ISTREAM; + hres = IDataObject_QueryGetData(data_obj, &fmt); + ok(hres == S_OK, "got %x\n", hres); + + fmt.tymed = TYMED_ISTREAM; + hres = IDataObject_QueryGetData(data_obj, &fmt); + todo_wine ok(hres == S_FALSE, "got %x\n", hres); + + fmt.tymed = TYMED_HGLOBAL | TYMED_ISTREAM; + hres = IDataObject_GetData(data_obj, &fmt, &medium); + ok(hres == S_OK, "got %x\n", hres); + ok(medium.tymed == TYMED_HGLOBAL, "medium.tymed = %x\n", medium.tymed); + ReleaseStgMedium(&medium); + + IDataObject_Release(data_obj); +} + START_TEST(shlfolder) { init_function_pointers(); @@ -5296,6 +5366,7 @@ START_TEST(shlfolder) test_SHCreateDefaultContextMenu(); test_SHCreateShellFolderView(); test_SHCreateShellFolderViewEx(); + test_DataObject(); OleUninitialize(); }