diff --git a/rostests/winetests/shell32/CMakeLists.txt b/rostests/winetests/shell32/CMakeLists.txt index bab651a262b..a4632114fc1 100644 --- a/rostests/winetests/shell32/CMakeLists.txt +++ b/rostests/winetests/shell32/CMakeLists.txt @@ -1,9 +1,7 @@ remove_definitions(-DWINVER=0x502 -D_WIN32_IE=0x600 -D_WIN32_WINNT=0x502) -add_definitions( - -D__ROS_LONG64__ - -D_DLL -D__USE_CRTIMP) +add_definitions(-D__ROS_LONG64__) list(APPEND SOURCE appbar.c @@ -12,7 +10,7 @@ list(APPEND SOURCE brsfolder.c ebrowser.c generated.c - #progman_dde.c FIXME: bug 7233 + #progman_dde.c FIXME: CORE-6559 recyclebin.c shelldispatch.c shelllink.c diff --git a/rostests/winetests/shell32/assoc.c b/rostests/winetests/shell32/assoc.c index 488f8e9649b..29524dc601a 100644 --- a/rostests/winetests/shell32/assoc.c +++ b/rostests/winetests/shell32/assoc.c @@ -43,19 +43,19 @@ static void test_IQueryAssociations_QueryInterface(void) return; } - hr = IUnknown_QueryInterface(qa, &IID_IQueryAssociations, (void**)&qa2); + hr = IQueryAssociations_QueryInterface(qa, &IID_IQueryAssociations, (void**)&qa2); ok(hr == S_OK, "QueryInterface (IQueryAssociations) returned 0x%x\n", hr); if (SUCCEEDED(hr)) { - IUnknown_Release(qa2); + IQueryAssociations_Release(qa2); } - hr = IUnknown_QueryInterface(qa, &IID_IUnknown, (void**)&unk); + hr = IQueryAssociations_QueryInterface(qa, &IID_IUnknown, (void**)&unk); ok(hr == S_OK, "QueryInterface (IUnknown) returned 0x%x\n", hr); if (SUCCEEDED(hr)) { IUnknown_Release(unk); } - hr = IUnknown_QueryInterface(qa, &IID_IUnknown, NULL); + hr = IQueryAssociations_QueryInterface(qa, &IID_IUnknown, NULL); ok(hr == E_POINTER, "got 0x%x (expected E_POINTER)\n", hr); IQueryAssociations_Release(qa); @@ -78,19 +78,20 @@ static void test_IApplicationAssociationRegistration_QueryInterface(void) return; } - hr = IUnknown_QueryInterface(appreg, &IID_IApplicationAssociationRegistration, (void**)&appreg2); + hr = IApplicationAssociationRegistration_QueryInterface(appreg, &IID_IApplicationAssociationRegistration, + (void**)&appreg2); ok(hr == S_OK, "QueryInterface (IApplicationAssociationRegistration) returned 0x%x\n", hr); if (SUCCEEDED(hr)) { - IUnknown_Release(appreg2); + IApplicationAssociationRegistration_Release(appreg2); } - hr = IUnknown_QueryInterface(appreg, &IID_IUnknown, (void**)&unk); + hr = IApplicationAssociationRegistration_QueryInterface(appreg, &IID_IUnknown, (void**)&unk); ok(hr == S_OK, "QueryInterface (IUnknown) returned 0x%x\n", hr); if (SUCCEEDED(hr)) { IUnknown_Release(unk); } - hr = IUnknown_QueryInterface(appreg, &IID_IUnknown, NULL); + hr = IApplicationAssociationRegistration_QueryInterface(appreg, &IID_IUnknown, NULL); ok(hr == E_POINTER, "got 0x%x (expected E_POINTER)\n", hr); IApplicationAssociationRegistration_Release(appreg); diff --git a/rostests/winetests/shell32/shelldispatch.c b/rostests/winetests/shell32/shelldispatch.c index 307e0fc4d59..0c496747454 100644 --- a/rostests/winetests/shell32/shelldispatch.c +++ b/rostests/winetests/shell32/shelldispatch.c @@ -140,7 +140,7 @@ static void test_namespace(void) p = path + lstrlenW(path); while (path < p && *(p - 1) != '\\') p--; - ok(!lstrcmpW(title, p), "expected %s, got %s\n", + ok(!lstrcmpiW(title, p), "expected %s, got %s\n", wine_dbgstr_w(p), wine_dbgstr_w(title)); } else skip("skipping Folder::get_Title test\n"); @@ -157,7 +157,7 @@ static void test_namespace(void) r = FolderItem_get_Path(item, &item_path); ok(r == S_OK, "FolderItem::get_Path failed: %08x\n", r); if (pSHGetFolderPathW) - ok(!lstrcmpW(item_path, path), "expected %s, got %s\n", + ok(!lstrcmpiW(item_path, path), "expected %s, got %s\n", wine_dbgstr_w(path), wine_dbgstr_w(item_path)); SysFreeString(item_path); FolderItem_Release(item); @@ -360,7 +360,7 @@ static void test_service(void) ok(V_BOOL(&v) == VARIANT_FALSE, "got %d\n", V_BOOL(&v)); SysFreeString(name); - IShellDispatch_Release(sd); + IShellDispatch2_Release(sd); } START_TEST(shelldispatch) diff --git a/rostests/winetests/shell32/shelllink.c b/rostests/winetests/shell32/shelllink.c index d28cff0ac27..b22079c7e85 100755 --- a/rostests/winetests/shell32/shelllink.c +++ b/rostests/winetests/shell32/shelllink.c @@ -16,9 +16,6 @@ * 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 - * This is a test program for the SHGet{Special}Folder{Path|Location} functions - * of shell32, that get either a filesystem path or a LPITEMIDLIST (shell - * namespace) path for a given folder (CSIDL value). * */ @@ -37,15 +34,10 @@ # define SLDF_HAS_LOGO3ID 0x00000800 /* not available in the Vista SDK */ #endif -typedef void (WINAPI *fnILFree)(LPITEMIDLIST); -typedef BOOL (WINAPI *fnILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST); -typedef HRESULT (WINAPI *fnSHILCreateFromPath)(LPCWSTR, LPITEMIDLIST *,DWORD*); -typedef HRESULT (WINAPI *fnSHDefExtractIconA)(LPCSTR, int, UINT, HICON*, HICON*, UINT); - -static fnILFree pILFree; -static fnILIsEqual pILIsEqual; -static fnSHILCreateFromPath pSHILCreateFromPath; -static fnSHDefExtractIconA pSHDefExtractIconA; +static void (WINAPI *pILFree)(LPITEMIDLIST); +static BOOL (WINAPI *pILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST); +static HRESULT (WINAPI *pSHILCreateFromPath)(LPCWSTR, LPITEMIDLIST *,DWORD*); +static HRESULT (WINAPI *pSHDefExtractIconA)(LPCSTR, int, UINT, HICON*, HICON*, UINT); static DWORD (WINAPI *pGetLongPathNameA)(LPCSTR, LPSTR, DWORD); static DWORD (WINAPI *pGetShortPathNameA)(LPCSTR, LPSTR, DWORD); @@ -422,7 +414,7 @@ void create_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int save_fails) lok(r == S_OK, "SetHotkey failed (0x%08x)\n", r); } - r = IShellLinkW_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf); + r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (void**)&pf); lok(r == S_OK, "no IID_IPersistFile (0x%08x)\n", r); if (r == S_OK) { @@ -907,7 +899,7 @@ if (0) LocalFree( dar ); - IUnknown_Release( dl ); + IShellLinkDataList_Release( dl ); IShellLinkW_Release( sl ); } @@ -1005,10 +997,10 @@ START_TEST(shelllink) HMODULE hmod = GetModuleHandleA("shell32.dll"); HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); - pILFree = (fnILFree) GetProcAddress(hmod, (LPSTR)155); - pILIsEqual = (fnILIsEqual) GetProcAddress(hmod, (LPSTR)21); - pSHILCreateFromPath = (fnSHILCreateFromPath) GetProcAddress(hmod, (LPSTR)28); - pSHDefExtractIconA = (fnSHDefExtractIconA) GetProcAddress(hmod, "SHDefExtractIconA"); + pILFree = (void *)GetProcAddress(hmod, (LPSTR)155); + pILIsEqual = (void *)GetProcAddress(hmod, (LPSTR)21); + pSHILCreateFromPath = (void *)GetProcAddress(hmod, (LPSTR)28); + pSHDefExtractIconA = (void *)GetProcAddress(hmod, "SHDefExtractIconA"); pGetLongPathNameA = (void *)GetProcAddress(hkernel32, "GetLongPathNameA"); pGetShortPathNameA = (void *)GetProcAddress(hkernel32, "GetShortPathNameA"); diff --git a/rostests/winetests/shell32/shellpath.c b/rostests/winetests/shell32/shellpath.c index 21cfa1ca1b9..729a341edc7 100644 --- a/rostests/winetests/shell32/shellpath.c +++ b/rostests/winetests/shell32/shellpath.c @@ -30,10 +30,11 @@ #include "shlguid.h" #include "shlobj.h" #include "shlwapi.h" -#include "initguid.h" #include "knownfolders.h" #include "wine/test.h" +#include "initguid.h" + /* CSIDL_MYDOCUMENTS is now the same as CSIDL_PERSONAL, but what we want * here is its original value. */ @@ -2494,6 +2495,157 @@ static void test_knownFolders(void) CoUninitialize(); } + +static void test_DoEnvironmentSubst(void) +{ + WCHAR expectedW[MAX_PATH]; + WCHAR bufferW[MAX_PATH]; + CHAR expectedA[MAX_PATH]; + CHAR bufferA[MAX_PATH]; + DWORD res; + DWORD res2; + DWORD len; + INT i; + static const WCHAR does_not_existW[] = {'%','D','O','E','S','_','N','O','T','_','E','X','I','S','T','%',0}; + static const CHAR does_not_existA[] = "%DOES_NOT_EXIST%"; + static const CHAR *names[] = { + /* interactive apps and services (works on all windows versions) */ + "%ALLUSERSPROFILE%", "%APPDATA%", "%LOCALAPPDATA%", + "%NUMBER_OF_PROCESSORS%", "%OS%", "%PROCESSOR_ARCHITECTURE%", + "%PROCESSOR_IDENTIFIER%", "%PROCESSOR_LEVEL%", "%PROCESSOR_REVISION%", + "%ProgramFiles%", "%SystemDrive%", + "%SystemRoot%", "%USERPROFILE%", "%windir%", + /* todo_wine: "%COMPUTERNAME%", "%ProgramData%", "%PUBLIC%", */ + + /* replace more than one var is allowed */ + "%HOMEDRIVE%%HOMEPATH%", + "%OS% %windir%"}; /* always the last entry in the table */ + + for (i = 0; i < (sizeof(names)/sizeof(LPSTR)); i++) + { + memset(bufferA, '#', MAX_PATH - 1); + bufferA[MAX_PATH - 1] = 0; + lstrcpyA(bufferA, names[i]); + MultiByteToWideChar(CP_ACP, 0, bufferA, MAX_PATH, bufferW, sizeof(bufferW)/sizeof(WCHAR)); + + res2 = ExpandEnvironmentStringsA(names[i], expectedA, MAX_PATH); + res = DoEnvironmentSubstA(bufferA, MAX_PATH); + + /* is the space for the terminating 0 included? */ + if (!i && HIWORD(res) && (LOWORD(res) == (lstrlenA(bufferA)))) + { + win_skip("DoEnvironmentSubstA/W are broken on NT 4\n"); + return; + } + ok(HIWORD(res) && (LOWORD(res) == res2), + "%d: got %d/%d (expected TRUE/%d)\n", i, HIWORD(res), LOWORD(res), res2); + ok(!lstrcmpA(bufferA, expectedA), + "%d: got %s (expected %s)\n", i, bufferA, expectedA); + + res2 = ExpandEnvironmentStringsW(bufferW, expectedW, MAX_PATH); + res = DoEnvironmentSubstW(bufferW, MAX_PATH); + ok(HIWORD(res) && (LOWORD(res) == res2), + "%d: got %d/%d (expected TRUE/%d)\n", i, HIWORD(res), LOWORD(res), res2); + ok(!lstrcmpW(bufferW, expectedW), + "%d: got %s (expected %s)\n", i, wine_dbgstr_w(bufferW), wine_dbgstr_w(expectedW)); + } + + i--; /* reuse data in the last table entry */ + len = LOWORD(res); /* needed length */ + + /* one character extra is fine */ + memset(bufferA, '#', MAX_PATH - 1); + bufferA[len + 2] = 0; + lstrcpyA(bufferA, names[i]); + MultiByteToWideChar(CP_ACP, 0, bufferA, MAX_PATH, bufferW, sizeof(bufferW)/sizeof(WCHAR)); + + res2 = ExpandEnvironmentStringsA(bufferA, expectedA, MAX_PATH); + res = DoEnvironmentSubstA(bufferA, len + 1); + ok(HIWORD(res) && (LOWORD(res) == res2), + "+1: got %d/%d (expected TRUE/%d)\n", HIWORD(res), LOWORD(res), res2); + ok(!lstrcmpA(bufferA, expectedA), + "+1: got %s (expected %s)\n", bufferA, expectedA); + + res2 = ExpandEnvironmentStringsW(bufferW, expectedW, MAX_PATH); + res = DoEnvironmentSubstW(bufferW, len + 1); + ok(HIWORD(res) && (LOWORD(res) == res2), + "+1: got %d/%d (expected TRUE/%d)\n", HIWORD(res), LOWORD(res), res2); + ok(!lstrcmpW(bufferW, expectedW), + "+1: got %s (expected %s)\n", wine_dbgstr_w(bufferW), wine_dbgstr_w(expectedW)); + + + /* minimal buffer length (result string and terminating 0) */ + memset(bufferA, '#', MAX_PATH - 1); + bufferA[len + 2] = 0; + lstrcpyA(bufferA, names[i]); + MultiByteToWideChar(CP_ACP, 0, bufferA, MAX_PATH, bufferW, sizeof(bufferW)/sizeof(WCHAR)); + + /* ANSI version failed without an extra byte, as documented on msdn */ + res = DoEnvironmentSubstA(bufferA, len); + ok(!HIWORD(res) && (LOWORD(res) == len), + " 0: got %d/%d (expected FALSE/%d)\n", HIWORD(res), LOWORD(res), len); + ok(!lstrcmpA(bufferA, names[i]), + " 0: got %s (expected %s)\n", bufferA, names[i]); + + /* DoEnvironmentSubstW works as expected */ + res2 = ExpandEnvironmentStringsW(bufferW, expectedW, MAX_PATH); + res = DoEnvironmentSubstW(bufferW, len); + ok(HIWORD(res) && (LOWORD(res) == res2), + " 0: got %d/%d (expected TRUE/%d)\n", HIWORD(res), LOWORD(res), res2); + ok(!lstrcmpW(bufferW, expectedW), + " 0: got %s (expected %s)\n", wine_dbgstr_w(bufferW), wine_dbgstr_w(expectedW)); + + + /* buffer to small */ + /* result: FALSE / provided buffer length / the buffer is untouched */ + memset(bufferA, '#', MAX_PATH - 1); + bufferA[len + 2] = 0; + lstrcpyA(bufferA, names[i]); + MultiByteToWideChar(CP_ACP, 0, bufferA, MAX_PATH, bufferW, sizeof(bufferW)/sizeof(WCHAR)); + + res = DoEnvironmentSubstA(bufferA, len - 1); + ok(!HIWORD(res) && (LOWORD(res) == (len - 1)), + "-1: got %d/%d (expected FALSE/%d)\n", HIWORD(res), LOWORD(res), len - 1); + ok(!lstrcmpA(bufferA, names[i]), + "-1: got %s (expected %s)\n", bufferA, names[i]); + + lstrcpyW(expectedW, bufferW); + res = DoEnvironmentSubstW(bufferW, len - 1); + ok(!HIWORD(res) && (LOWORD(res) == (len - 1)), + "-1: got %d/%d (expected FALSE/%d)\n", HIWORD(res), LOWORD(res), len - 1); + ok(!lstrcmpW(bufferW, expectedW), + "-1: got %s (expected %s)\n", wine_dbgstr_w(bufferW), wine_dbgstr_w(expectedW)); + + + /* unknown variable */ + /* result: TRUE / string length including terminating 0 / the buffer is untouched */ + memset(bufferA, '#', MAX_PATH - 1); + bufferA[MAX_PATH - 1] = 0; + lstrcpyA(bufferA, does_not_existA); + MultiByteToWideChar(CP_ACP, 0, bufferA, MAX_PATH, bufferW, sizeof(bufferW)/sizeof(WCHAR)); + + res2 = lstrlenA(does_not_existA) + 1; + res = DoEnvironmentSubstA(bufferA, MAX_PATH); + ok(HIWORD(res) && (LOWORD(res) == res2), + "%d: got %d/%d (expected TRUE/%d)\n", i, HIWORD(res), LOWORD(res), res2); + ok(!lstrcmpA(bufferA, does_not_existA), + "%d: got %s (expected %s)\n", i, bufferA, does_not_existA); + + res = DoEnvironmentSubstW(bufferW, MAX_PATH); + ok(HIWORD(res) && (LOWORD(res) == res2), + "%d: got %d/%d (expected TRUE/%d)\n", i, HIWORD(res), LOWORD(res), res2); + ok(!lstrcmpW(bufferW, does_not_existW), + "%d: got %s (expected %s)\n", i, wine_dbgstr_w(bufferW), wine_dbgstr_w(does_not_existW)); + + + if (0) + { + /* NULL crashes on windows */ + res = DoEnvironmentSubstA(NULL, MAX_PATH); + res = DoEnvironmentSubstW(NULL, MAX_PATH); + } +} + START_TEST(shellpath) { if (!init()) return; @@ -2521,5 +2673,6 @@ START_TEST(shellpath) test_NonExistentPath(); test_SHGetFolderPathEx(); test_knownFolders(); + test_DoEnvironmentSubst(); } } diff --git a/rostests/winetests/shell32/shlexec.c b/rostests/winetests/shell32/shlexec.c index 86cd30784c1..3d363cdd016 100755 --- a/rostests/winetests/shell32/shlexec.c +++ b/rostests/winetests/shell32/shlexec.c @@ -57,7 +57,7 @@ static char tmpdir[MAX_PATH]; static char child_file[MAX_PATH]; static DLLVERSIONINFO dllver; static BOOL skip_noassoc_tests = FALSE; -//static HANDLE dde_ready_event; FIXME: bug 7233 +//static HANDLE dde_ready_event; FIXME: CORE-6559 /*** @@ -75,36 +75,36 @@ static void init_event(const char* child_file) hEvent=CreateEvent(NULL, FALSE, FALSE, event_name); } -static void strcat_param(char* str, const char* param) +static void strcat_param(char* str, const char* name, const char* param) { - if (param!=NULL) + if (param) { - strcat(str, "\""); + if (str[strlen(str)-1] == '"') + strcat(str, ", "); + strcat(str, name); + strcat(str, "=\""); strcat(str, param); strcat(str, "\""); } - else - { - strcat(str, "null"); - } } +static int _todo_wait = 0; +#define todo_wait for (_todo_wait = 1; _todo_wait; _todo_wait = 0) + static char shell_call[2048]=""; -static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCSTR directory) +static int bad_shellexecute = 0; +static INT_PTR shell_execute(LPCSTR verb, LPCSTR file, LPCSTR parameters, LPCSTR directory) { INT_PTR rc, rcEmpty = 0; - if(!operation) + if(!verb) rcEmpty = shell_execute("", file, parameters, directory); strcpy(shell_call, "ShellExecute("); - strcat_param(shell_call, operation); - strcat(shell_call, ", "); - strcat_param(shell_call, file); - strcat(shell_call, ", "); - strcat_param(shell_call, parameters); - strcat(shell_call, ", "); - strcat_param(shell_call, directory); + strcat_param(shell_call, "verb", verb); + strcat_param(shell_call, "file", file); + strcat_param(shell_call, "params", parameters); + strcat_param(shell_call, "dir", directory); strcat(shell_call, ")"); if (winetest_debug > 1) trace("%s\n", shell_call); @@ -116,7 +116,7 @@ static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCST * association it displays the 'Open With' dialog and I could not find * a flag to prevent this. */ - rc=(INT_PTR)ShellExecute(NULL, operation, file, parameters, directory, SW_SHOWNORMAL); + rc=(INT_PTR)ShellExecute(NULL, verb, file, parameters, directory, SW_SHOWNORMAL); if (rc > 32) { @@ -133,7 +133,10 @@ static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCST rc = SE_ERR_NOASSOC; } } - ok(wait_rc==WAIT_OBJECT_0 || rc <= 32, "WaitForSingleObject returned %d\n", wait_rc); + 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); } /* The child process may have changed the result file, so let profile * functions know about it @@ -142,28 +145,37 @@ static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCST if (rc > 32) dump_child(); - if(!operation) - ok(rc == rcEmpty || broken(rc > 32 && rcEmpty == SE_ERR_NOASSOC) /* NT4 */, - "Got different return value with empty string: %lu %lu\n", rc, rcEmpty); + 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); + } return rc; } -static int shell_execute_ex(DWORD mask, LPCSTR operation, LPCSTR file, - LPCSTR parameters, LPCSTR directory) +static INT_PTR shell_execute_ex(DWORD mask, LPCSTR verb, LPCSTR file, + LPCSTR parameters, LPCSTR directory, + LPCSTR class) { SHELLEXECUTEINFO sei; BOOL success; INT_PTR rc; strcpy(shell_call, "ShellExecuteEx("); - strcat_param(shell_call, operation); - strcat(shell_call, ", "); - strcat_param(shell_call, file); - strcat(shell_call, ", "); - strcat_param(shell_call, parameters); - strcat(shell_call, ", "); - strcat_param(shell_call, directory); + if (mask) + { + char smask[11]; + 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, "params", parameters); + strcat_param(shell_call, "dir", directory); + strcat_param(shell_call, "class", class); strcat(shell_call, ")"); if (winetest_debug > 1) trace("%s\n", shell_call); @@ -171,14 +183,14 @@ static int shell_execute_ex(DWORD mask, LPCSTR operation, LPCSTR file, sei.cbSize=sizeof(sei); sei.fMask=SEE_MASK_NOCLOSEPROCESS | mask; sei.hwnd=NULL; - sei.lpVerb=operation; + sei.lpVerb=verb; sei.lpFile=file; sei.lpParameters=parameters; sei.lpDirectory=directory; sei.nShow=SW_SHOWNORMAL; sei.hInstApp=NULL; /* Out */ sei.lpIDList=NULL; - sei.lpClass=NULL; + sei.lpClass=class; sei.hkeyClass=NULL; sei.dwHotKey=0; U(sei).hIcon=NULL; @@ -200,7 +212,10 @@ static int shell_execute_ex(DWORD mask, LPCSTR operation, LPCSTR file, ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject(hProcess) returned %d\n", wait_rc); } wait_rc=WaitForSingleObject(hEvent, 5000); - ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject returned %d\n", wait_rc); + 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); } /* The child process may have changed the result file, so let profile * functions know about it @@ -552,12 +567,16 @@ static void doChild(int argc, char** argv) /* 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]); + 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); @@ -635,13 +654,15 @@ static void dump_child(void) 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;i32, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); - } + todo_wine ok(rc > 32, "%s failed: rc=%lu\n", shell_call, rc); /* if quoted, existing "drawback_file.noassoc" not prevents finding "drawback_file.noassoc foo.shlexec" on wine */ - testfile = "\"%s\\drawback_file.noassoc foo.shlexec\""; - sprintf(fileA, testfile, tmpdir); + sprintf(fileA, "\"%s\\drawback_file.noassoc foo.shlexec\"", tmpdir); rc=shell_execute(NULL, fileA, NULL, NULL); - ok(rc>32 || broken(rc == 2) /* Win95/NT4 */, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); + ok(rc > 32 || broken(rc == SE_ERR_FNF) /* Win95/NT4 */, + "%s failed: rc=%lu\n", shell_call, rc); - /* error should be 2, not 31 */ - testfile = "\"%s\\drawback_file.noassoc\" foo.shlexec"; - sprintf(fileA, testfile, tmpdir); + /* 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==2, - "expected failure (2), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); + ok(rc == SE_ERR_FNF, "%s succeeded: rc=%lu\n", shell_call, rc); /* ""command"" not works on wine (and real win9x and w2k) */ - testfile = "\"\"%s\\simple.shlexec\"\""; - sprintf(fileA, testfile, tmpdir); + sprintf(fileA, "\"\"%s\\simple.shlexec\"\"", tmpdir); rc=shell_execute(NULL, fileA, NULL, NULL); - todo_wine { - ok(rc>32 || broken(rc == 2) /* Win9x/2000 */, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); - } + todo_wine ok(rc > 32 || broken(rc == SE_ERR_FNF) /* Win9x/2000 */, + "%s failed: rc=%lu\n", shell_call, rc); /* nonexisting "drawback_nonexist.noassoc" not prevents finding "drawback_nonexist.noassoc foo.shlexec" on wine */ - testfile = "%s\\drawback_nonexist.noassoc foo.shlexec"; - sprintf(fileA, testfile, tmpdir); + sprintf(fileA, "%s\\drawback_nonexist.noassoc foo.shlexec", tmpdir); rc=shell_execute(NULL, fileA, NULL, NULL); - ok(rc>32, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); + ok(rc > 32, "%s failed: rc=%lu\n", shell_call, rc); /* is SEE_MASK_DOENVSUBST default flag? Should only be when XP emulates 9x (XP bug or real 95 or ME behavior ?) */ - testfile = "%%TEMP%%\\%s\\simple.shlexec"; - sprintf(fileA, testfile, shorttmpdir); - rc=shell_execute(NULL, fileA, NULL, NULL); - todo_wine { - ok(rc==2, - "expected failure (2), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); - } + rc=shell_execute(NULL, "%TMPDIR%\\simple.shlexec", NULL, NULL); + todo_wine ok(rc == SE_ERR_FNF, "%s succeeded: rc=%lu\n", shell_call, rc); /* quoted */ - testfile = "\"%%TEMP%%\\%s\\simple.shlexec\""; - sprintf(fileA, testfile, shorttmpdir); - rc=shell_execute(NULL, fileA, NULL, NULL); - todo_wine { - ok(rc==2, - "expected failure (2), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); - } + rc=shell_execute(NULL, "\"%TMPDIR%\\simple.shlexec\"", NULL, NULL); + todo_wine ok(rc == SE_ERR_FNF, "%s succeeded: rc=%lu\n", shell_call, rc); /* test SEE_MASK_DOENVSUBST works */ - testfile = "%%TEMP%%\\%s\\simple.shlexec"; - sprintf(fileA, testfile, shorttmpdir); - rc=shell_execute_ex(SEE_MASK_DOENVSUBST | SEE_MASK_FLAG_NO_UI, NULL, fileA, NULL, NULL); - ok(rc>32, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); - - /* quoted lpFile not works only on real win95 and nt4 */ - testfile = "\"%%TEMP%%\\%s\\simple.shlexec\""; - sprintf(fileA, testfile, shorttmpdir); - rc=shell_execute_ex(SEE_MASK_DOENVSUBST | SEE_MASK_FLAG_NO_UI, NULL, fileA, NULL, NULL); - ok(rc>32 || broken(rc == 2) /* Win95/NT4 */, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); + 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); + /* 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); } +typedef struct +{ + const char* cmd; + const char* args[11]; + int todo; +} cmdline_tests_t; + +static const cmdline_tests_t cmdline_tests[] = +{ + {"exe", + {"exe", NULL}, 0}, + + {"exe arg1 arg2 \"arg three\" 'four five` six\\ $even)", + {"exe", "arg1", "arg2", "arg three", "'four", "five`", "six\\", "$even)", NULL}, 0}, + + {"exe arg=1 arg-2 three\tfour\rfour\nfour ", + {"exe", "arg=1", "arg-2", "three", "four\rfour\nfour", NULL}, 0}, + + {"exe arg\"one\" \"second\"arg thirdarg ", + {"exe", "argone", "secondarg", "thirdarg", NULL}, 0}, + + /* Don't lose unclosed quoted arguments */ + {"exe arg1 \"unclosed", + {"exe", "arg1", "unclosed", NULL}, 0}, + + {"exe arg1 \"", + {"exe", "arg1", "", NULL}, 0}, + + /* cmd's metacharacters have no special meaning */ + {"exe \"one^\" \"arg\"&two three|four", + {"exe", "one^", "arg&two", "three|four", NULL}, 0}, + + /* Environment variables are not interpreted either */ + {"exe %TMPDIR% %2", + {"exe", "%TMPDIR%", "%2", NULL}, 0}, + + /* If not followed by a quote, backslashes go through as is */ + {"exe o\\ne t\\\\wo t\\\\\\ree f\\\\\\\\our ", + {"exe", "o\\ne", "t\\\\wo", "t\\\\\\ree", "f\\\\\\\\our", NULL}, 0}, + + {"exe \"o\\ne\" \"t\\\\wo\" \"t\\\\\\ree\" \"f\\\\\\\\our\" ", + {"exe", "o\\ne", "t\\\\wo", "t\\\\\\ree", "f\\\\\\\\our", NULL}, 0}, + + /* When followed by a quote their number is halved and the remainder + * escapes the quote + */ + {"exe \\\"one \\\\\"two\" \\\\\\\"three \\\\\\\\\"four\" end", + {"exe", "\"one", "\\two", "\\\"three", "\\\\four", "end", NULL}, 0}, + + {"exe \"one\\\" still\" \"two\\\\\" \"three\\\\\\\" still\" \"four\\\\\\\\\" end", + {"exe", "one\" still", "two\\", "three\\\" still", "four\\\\", "end", NULL}, 0}, + + /* One can put a quote in an unquoted string by tripling it, that is in + * effect quoting it like so """ -> ". The general rule is as follows: + * 3n quotes -> n quotes + * 3n+1 quotes -> n quotes plus start of a quoted string + * 3n+2 quotes -> n quotes (plus an empty string from the remaining pair) + * Nicely, when n is 0 we get the standard rules back. + */ + {"exe two\"\"quotes next", + {"exe", "twoquotes", "next", NULL}, 0}, + + {"exe three\"\"\"quotes next", + {"exe", "three\"quotes", "next", NULL}, 0}, + + {"exe four\"\"\"\" quotes\" next 4%3=1", + {"exe", "four\" quotes", "next", "4%3=1", NULL}, 0}, + + {"exe five\"\"\"\"\"quotes next", + {"exe", "five\"quotes", "next", NULL}, 0}, + + {"exe six\"\"\"\"\"\"quotes next", + {"exe", "six\"\"quotes", "next", NULL}, 0}, + + {"exe seven\"\"\"\"\"\"\" quotes\" next 7%3=1", + {"exe", "seven\"\" quotes", "next", "7%3=1", NULL}, 0}, + + {"exe twelve\"\"\"\"\"\"\"\"\"\"\"\"quotes next", + {"exe", "twelve\"\"\"\"quotes", "next", NULL}, 0}, + + {"exe thirteen\"\"\"\"\"\"\"\"\"\"\"\"\" quotes\" next 13%3=1", + {"exe", "thirteen\"\"\"\" quotes", "next", "13%3=1", NULL}, 0}, + + /* Inside a quoted string the opening quote is added to the set of + * consecutive quotes to get the effective quotes count. This gives: + * 1+3n quotes -> n quotes + * 1+3n+1 quotes -> n quotes plus closes the quoted string + * 1+3n+2 quotes -> n+1 quotes plus closes the quoted string + */ + {"exe \"two\"\"quotes next", + {"exe", "two\"quotes", "next", NULL}, 0}, + + {"exe \"two\"\" next", + {"exe", "two\"", "next", NULL}, 0}, + + {"exe \"three\"\"\" quotes\" next 4%3=1", + {"exe", "three\" quotes", "next", "4%3=1", NULL}, 0}, + + {"exe \"four\"\"\"\"quotes next", + {"exe", "four\"quotes", "next", NULL}, 0}, + + {"exe \"five\"\"\"\"\"quotes next", + {"exe", "five\"\"quotes", "next", NULL}, 0}, + + {"exe \"six\"\"\"\"\"\" quotes\" next 7%3=1", + {"exe", "six\"\" quotes", "next", "7%3=1", NULL}, 0}, + + {"exe \"eleven\"\"\"\"\"\"\"\"\"\"\"quotes next", + {"exe", "eleven\"\"\"\"quotes", "next", NULL}, 0}, + + {"exe \"twelve\"\"\"\"\"\"\"\"\"\"\"\" quotes\" next 13%3=1", + {"exe", "twelve\"\"\"\" quotes", "next", "13%3=1", NULL}, 0}, + + /* Escaped consecutive quotes are fun */ + {"exe \"the crazy \\\\\"\"\"\\\\\" quotes", + {"exe", "the crazy \\\"\\", "quotes", NULL}, 0}, + + /* The executable path has its own rules!!! + * - Backslashes have no special meaning. + * - If the first character is a quote, then the second quote ends the + * executable path. + * - The previous rule holds even if the next character is not a space! + * - If the first character is not a quote, then quotes have no special + * meaning either and the executable path stops at the first space. + * - The consecutive quotes rules don't apply either. + * - Even if there is no space between the executable path and the first + * argument, the latter is parsed using the regular rules. + */ + {"exe\"file\"path arg1", + {"exe\"file\"path", "arg1", NULL}, 0}, + + {"exe\"file\"path\targ1", + {"exe\"file\"path", "arg1", NULL}, 0}, + + {"exe\"path\\ arg1", + {"exe\"path\\", "arg1", NULL}, 0}, + + {"\\\"exe \"arg one\"", + {"\\\"exe", "arg one", NULL}, 0}, + + {"\"spaced exe\" \"next arg\"", + {"spaced exe", "next arg", NULL}, 0}, + + {"\"spaced exe\"\t\"next arg\"", + {"spaced exe", "next arg", NULL}, 0}, + + {"\"exe\"arg\" one\" argtwo", + {"exe", "arg one", "argtwo", NULL}, 0}, + + {"\"spaced exe\\\"arg1 arg2", + {"spaced exe\\", "arg1", "arg2", NULL}, 0}, + + {"\"two\"\" arg1 ", + {"two", " arg1 ", NULL}, 0}, + + {"\"three\"\"\" arg2", + {"three", "", "arg2", NULL}, 0}, + + {"\"four\"\"\"\"arg1", + {"four", "\"arg1", NULL}, 0}, + + /* If the first character is a space then the executable path is empty */ + {" \"arg\"one argtwo", + {"", "argone", "argtwo", NULL}, 0}, + + {NULL, {NULL}, 0} +}; + +static BOOL test_one_cmdline(const cmdline_tests_t* test) +{ + WCHAR cmdW[MAX_PATH], argW[MAX_PATH]; + LPWSTR *cl2a; + int cl2a_count; + LPWSTR *argsW; + int i, count; + + /* trace("----- cmd='%s'\n", test->cmd); */ + MultiByteToWideChar(CP_ACP, 0, test->cmd, -1, cmdW, sizeof(cmdW)/sizeof(*cmdW)); + argsW = cl2a = CommandLineToArgvW(cmdW, &cl2a_count); + if (argsW == NULL && cl2a_count == -1) + { + win_skip("CommandLineToArgvW not implemented, skipping\n"); + return FALSE; + } + + 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 + ok(cl2a_count == count, "%s: expected %d arguments, but got %d\n", test->cmd, count, cl2a_count); + + for (i = 0; i < cl2a_count; i++) + { + 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 + 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 + ok(0, "%s: got extra arg[%d]=%s\n", test->cmd, i, wine_dbgstr_w(*argsW)); + argsW++; + } + LocalFree(cl2a); + return TRUE; +} + +static void test_commandline2argv(void) +{ + static const WCHAR exeW[] = {'e','x','e',0}; + const cmdline_tests_t* test; + WCHAR strW[MAX_PATH]; + LPWSTR *args; + int numargs; + DWORD le; + + test = cmdline_tests; + while (test->cmd) + { + if (!test_one_cmdline(test)) + return; + test++; + } + + SetLastError(0xdeadbeef); + args = CommandLineToArgvW(exeW, NULL); + le = GetLastError(); + ok(args == NULL && le == ERROR_INVALID_PARAMETER, "expected NULL with ERROR_INVALID_PARAMETER got %p with %u\n", args, le); + + SetLastError(0xdeadbeef); + args = CommandLineToArgvW(NULL, NULL); + le = GetLastError(); + ok(args == NULL && le == ERROR_INVALID_PARAMETER, "expected NULL with ERROR_INVALID_PARAMETER got %p with %u\n", args, le); + + *strW = 0; + args = CommandLineToArgvW(strW, &numargs); + ok(numargs == 1, "expected 1 args, got %d\n", numargs); + if (numargs == 1) + { + GetModuleFileNameW(NULL, strW, sizeof(strW)/sizeof(*strW)); + ok(!lstrcmpW(args[0], strW), "wrong path to the current executable: %s instead of %s\n", wine_dbgstr_w(args[0]), wine_dbgstr_w(strW)); + } + if (args) LocalFree(args); +} + +/* The goal here is to analyze how ShellExecute() builds the command that + * will be run. The tricky part is that there are three transformation + * steps between the 'parameters' string we pass to ShellExecute() and the + * argument list we observe in the child process: + * - The parsing of 'parameters' string into individual arguments. The tests + * show this is done differently from both CreateProcess() and + * CommandLineToArgv()! + * - The way the command 'formatting directives' such as %1, %2, etc are + * handled. + * - And the way the resulting command line is then parsed to yield the + * argument list we check. + */ +typedef struct +{ + const char* verb; + const char* params; + int todo; + cmdline_tests_t cmd; + cmdline_tests_t broken; +} argify_tests_t; + +static const argify_tests_t argify_tests[] = +{ + /* Start with three simple parameters. Notice that one can reorder and + * duplicate the parameters. Also notice how %* take the raw input + * parameters string, including the trailing spaces, no matter what + * arguments have already been used. + */ + {"Params232S", "p2 p3 p4 ", 0xc2, + {" p2 p3 \"p2\" \"p2 p3 p4 \"", + {"", "p2", "p3", "p2", "p2 p3 p4 ", NULL}, 0}}, + + /* Unquoted argument references like %2 don't automatically quote their + * argument. Similarly, when they are quoted they don't escape the quotes + * that their argument may contain. + */ + {"Params232S", "\"p two\" p3 p4 ", 0x3f3, + {" p two p3 \"p two\" \"\"p two\" p3 p4 \"", + {"", "p", "two", "p3", "p two", "p", "two p3 p4 ", NULL}, 0}}, + + /* Only single digits are supported so only %1 to %9. Shown here with %20 + * because %10 is a pain. + */ + {"Params20", "p", 0, + {" \"p0\"", + {"", "p0", NULL}, 0}}, + + /* Only (double-)quotes have a special meaning. */ + {"Params23456", "'p2 p3` p4\\ $even", 0x40, + {" \"'p2\" \"p3`\" \"p4\\\" \"$even\" \"\"", + {"", "'p2", "p3`", "p4\" $even \"", NULL}, 0}}, + + {"Params23456", "p=2 p-3 p4\tp4\rp4\np4", 0x1c2, + {" \"p=2\" \"p-3\" \"p4\tp4\rp4\np4\" \"\" \"\"", + {"", "p=2", "p-3", "p4\tp4\rp4\np4", "", "", NULL}, 0}}, + + /* In unquoted strings, quotes are treated are a parameter separator just + * like spaces! However they can be doubled to get a literal quote. + * Specifically: + * 2n quotes -> n quotes + * 2n+1 quotes -> n quotes and a parameter separator + */ + {"Params23456789", "one\"quote \"p four\" one\"quote p7", 0xff3, + {" \"one\" \"quote\" \"p four\" \"one\" \"quote\" \"p7\" \"\" \"\"", + {"", "one", "quote", "p four", "one", "quote", "p7", "", "", NULL}, 0}}, + + {"Params23456789", "two\"\"quotes \"p three\" two\"\"quotes p5", 0xf2, + {" \"two\"quotes\" \"p three\" \"two\"quotes\" \"p5\" \"\" \"\" \"\" \"\"", + {"", "twoquotes p", "three twoquotes", "p5", "", "", "", "", NULL}, 0}}, + + {"Params23456789", "three\"\"\"quotes \"p four\" three\"\"\"quotes p6", 0xff3, + {" \"three\"\" \"quotes\" \"p four\" \"three\"\" \"quotes\" \"p6\" \"\" \"\"", + {"", "three\"", "quotes", "p four", "three\"", "quotes", "p6", "", "", NULL}, 0}}, + + {"Params23456789", "four\"\"\"\"quotes \"p three\" four\"\"\"\"quotes p5", 0xf3, + {" \"four\"\"quotes\" \"p three\" \"four\"\"quotes\" \"p5\" \"\" \"\" \"\" \"\"", + {"", "four\"quotes p", "three fourquotes p5 \"", "", "", "", NULL}, 0}}, + + /* Quoted strings cannot be continued by tacking on a non space character + * either. + */ + {"Params23456", "\"p two\"p3 \"p four\"p5 p6", 0x1f3, + {" \"p two\" \"p3\" \"p four\" \"p5\" \"p6\"", + {"", "p two", "p3", "p four", "p5", "p6", NULL}, 0}}, + + /* In quoted strings, the quotes are halved and an odd number closes the + * string. Specifically: + * 2n quotes -> n quotes + * 2n+1 quotes -> n quotes and closes the string and hence the parameter + */ + {"Params23456789", "\"one q\"uote \"p four\" \"one q\"uote p7", 0xff3, + {" \"one q\" \"uote\" \"p four\" \"one q\" \"uote\" \"p7\" \"\" \"\"", + {"", "one q", "uote", "p four", "one q", "uote", "p7", "", "", NULL}, 0}}, + + {"Params23456789", "\"two \"\" quotes\" \"p three\" \"two \"\" quotes\" p5", 0x1ff3, + {" \"two \" quotes\" \"p three\" \"two \" quotes\" \"p5\" \"\" \"\" \"\" \"\"", + {"", "two ", "quotes p", "three two", " quotes", "p5", "", "", "", "", NULL}, 0}}, + + {"Params23456789", "\"three q\"\"\"uotes \"p four\" \"three q\"\"\"uotes p7", 0xff3, + {" \"three q\"\" \"uotes\" \"p four\" \"three q\"\" \"uotes\" \"p7\" \"\" \"\"", + {"", "three q\"", "uotes", "p four", "three q\"", "uotes", "p7", "", "", NULL}, 0}}, + + {"Params23456789", "\"four \"\"\"\" quotes\" \"p three\" \"four \"\"\"\" quotes\" p5", 0xff3, + {" \"four \"\" quotes\" \"p three\" \"four \"\" quotes\" \"p5\" \"\" \"\" \"\" \"\"", + {"", "four \"", "quotes p", "three four", "", "quotes p5 \"", "", "", "", NULL}, 0}}, + + /* The quoted string rules also apply to consecutive quotes at the start + * of a parameter but don't count the opening quote! + */ + {"Params23456789", "\"\"twoquotes \"p four\" \"\"twoquotes p7", 0xbf3, + {" \"\" \"twoquotes\" \"p four\" \"\" \"twoquotes\" \"p7\" \"\" \"\"", + {"", "", "twoquotes", "p four", "", "twoquotes", "p7", "", "", NULL}, 0}}, + + {"Params23456789", "\"\"\"three quotes\" \"p three\" \"\"\"three quotes\" p5", 0x6f3, + {" \"\"three quotes\" \"p three\" \"\"three quotes\" \"p5\" \"\" \"\" \"\" \"\"", + {"", "three", "quotes p", "three \"three", "quotes p5 \"", "", "", "", NULL}, 0}}, + + {"Params23456789", "\"\"\"\"fourquotes \"p four\" \"\"\"\"fourquotes p7", 0xbf3, + {" \"\"\" \"fourquotes\" \"p four\" \"\"\" \"fourquotes\" \"p7\" \"\" \"\"", + {"", "\"", "fourquotes", "p four", "\"", "fourquotes", "p7", "", "", NULL}, 0}}, + + /* An unclosed quoted string gets lost! */ + {"Params23456", "p2 \"p3\" \"p4 is lost", 0x1c3, + {" \"p2\" \"p3\" \"\" \"\" \"\"", + {"", "p2", "p3", "", "", "", NULL}, 0}, + {" \"p2\" \"p3\" \"p3\" \"\" \"\"", + {"", "p2", "p3", "p3", "", "", NULL}, 0}}, + + /* Backslashes have no special meaning even when preceding quotes. All + * they do is start an unquoted string. + */ + {"Params23456", "\\\"p\\three \"pfour\\\" pfive", 0x73, + {" \"\\\" \"p\\three\" \"pfour\\\" \"pfive\" \"\"", + {"", "\" p\\three pfour\"", "pfive", "", NULL}, 0}}, + + /* Environment variables are left untouched. */ + {"Params23456", "%TMPDIR% %t %c", 0, + {" \"%TMPDIR%\" \"%t\" \"%c\" \"\" \"\"", + {"", "%TMPDIR%", "%t", "%c", "", "", NULL}, 0}}, + + /* %~2 is equivalent to %*. However %~3 and higher include the spaces + * before the parameter! + * (but not the previous parameter's closing quote fortunately) + */ + {"Params2345Etc", "p2 p3 \"p4\" p5 p6 ", 0x3f3, + {" ~2=\"p2 p3 \"p4\" p5 p6 \" ~3=\" p3 \"p4\" p5 p6 \" ~4=\" \"p4\" p5 p6 \" ~5= p5 p6 ", + {"", "~2=p2 p3 p4 p5 p6 ", "~3= p3 p4 p5 p6 ", "~4= p4 p5 p6 ", "~5=", "p5", "p6", NULL}, 0}}, + + /* %~n works even if there is no nth parameter. */ + {"Params9Etc", "p2 p3 p4 p5 p6 p7 p8 ", 0x12, + {" ~9=\" \"", + {"", "~9= ", NULL}, 0}}, + + {"Params9Etc", "p2 p3 p4 p5 p6 p7 ", 0x12, + {" ~9=\"\"", + {"", "~9=", NULL}, 0}}, + + /* The %~n directives also transmit the tenth parameter and beyond. */ + {"Params9Etc", "p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 and beyond!", 0x12, + {" ~9=\" p9 p10 p11 and beyond!\"", + {"", "~9= p9 p10 p11 and beyond!", NULL}, 0}}, + + /* Bad formatting directives lose their % sign, except those followed by + * a tilde! Environment variables are not expanded but lose their % sign. + */ + {"ParamsBad", "p2 p3 p4 p5", 0x12, + {" \"% - %~ %~0 %~1 %~a %~* a b c TMPDIR\"", + {"", "% - %~ %~0 %~1 %~a %~* a b c TMPDIR", NULL}, 0}}, + + {NULL, NULL, 0, {NULL, {NULL}, 0}} +}; + static void test_argify(void) { - char fileA[MAX_PATH]; + BOOL has_cl2a = TRUE; + char fileA[MAX_PATH], params[2*MAX_PATH+12]; + INT_PTR rc; + const argify_tests_t* test; + const cmdline_tests_t *bad; + const char* cmd; + unsigned i, count; - int rc; + create_test_verb(".shlexec", "Params232S", 0, "Params232S %2 %3 \"%2\" \"%*\""); + create_test_verb(".shlexec", "Params23456", 0, "Params23456 \"%2\" \"%3\" \"%4\" \"%5\" \"%6\""); + create_test_verb(".shlexec", "Params23456789", 0, "Params23456789 \"%2\" \"%3\" \"%4\" \"%5\" \"%6\" \"%7\" \"%8\" \"%9\""); + create_test_verb(".shlexec", "Params2345Etc", 0, "Params2345Etc ~2=\"%~2\" ~3=\"%~3\" ~4=\"%~4\" ~5=%~5"); + create_test_verb(".shlexec", "Params9Etc", 0, "Params9Etc ~9=\"%~9\""); + create_test_verb(".shlexec", "Params20", 0, "Params20 \"%20\""); + create_test_verb(".shlexec", "ParamsBad", 0, "ParamsBad \"%% %- %~ %~0 %~1 %~a %~* %a %b %c %TMPDIR%\""); sprintf(fileA, "%s\\test file.shlexec", tmpdir); - /* %2 */ - rc=shell_execute("NoQuotesParam2", fileA, "a b", NULL); - ok(rc>32, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); - if (rc>32) + test = argify_tests; + while (test->params) { - okChildInt("argcA", 5); - okChildString("argvA4", "a"); - } + bad = test->broken.cmd ? &test->broken : &test->cmd; - /* %2 */ - /* '"a"""' -> 'a"' */ - rc=shell_execute("NoQuotesParam2", fileA, "\"a:\"\"some string\"\"\"", NULL); - ok(rc>32, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); - if (rc>32) - { - okChildInt("argcA", 5); - todo_wine { - okChildString("argvA4", "a:some string"); + /* 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); + + 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 + okChildInt("argcA", 4 + count - 1); + + cmd = getChildString("Arguments", "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); + + 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 + okChildStringBroken(argname, test->cmd.args[i+1], bad->args[i+1]); } + + if (has_cl2a) + has_cl2a = test_one_cmdline(&(test->cmd)); + test++; } - /* %2 */ - /* backslash isn't escape char - * '"a\""' -> '"a\""' */ - rc=shell_execute("NoQuotesParam2", fileA, "\"a:\\\"some string\\\"\"", NULL); - ok(rc>32, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); - if (rc>32) - { - okChildInt("argcA", 5); - todo_wine { - okChildString("argvA4", "a:\\"); - } - } - - /* "%2" */ - /* \t isn't whitespace */ - rc=shell_execute("QuotedParam2", fileA, "a\tb c", NULL); - ok(rc>32, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); - if (rc>32) - { - okChildInt("argcA", 5); - todo_wine { - okChildString("argvA4", "a\tb"); - } - } - - /* %* */ - rc=shell_execute("NoQuotesAllParams", fileA, "a b c d e f g h", NULL); - ok(rc>32, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); - if (rc>32) - { - todo_wine { - okChildInt("argcA", 12); - okChildString("argvA4", "a"); - okChildString("argvA11", "h"); - } - } - - /* %* can sometimes contain only whitespaces and no args */ - rc=shell_execute("QuotedAllParams", fileA, " ", NULL); - ok(rc>32, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); - if (rc>32) - { - todo_wine { - okChildInt("argcA", 5); - okChildString("argvA4", " "); - } - } - - /* %~3 */ - rc=shell_execute("NoQuotesParams345etc", fileA, "a b c d e f g h", NULL); - ok(rc>32, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); - if (rc>32) - { - todo_wine { - okChildInt("argcA", 11); - okChildString("argvA4", "b"); - okChildString("argvA10", "h"); - } - } - - /* %~3 is rest of command line starting with whitespaces after 2nd arg */ - rc=shell_execute("QuotedParams345etc", fileA, "a ", NULL); - ok(rc>32, - "expected success (33), got %s (%d), lpFile: %s\n", - rc > 32 ? "success" : "failure", rc, fileA - ); - if (rc>32) - { - okChildInt("argcA", 5); - todo_wine { - okChildString("argvA4", " "); - } - } + /* 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) @@ -1135,7 +1462,7 @@ static void test_filename(void) char filename[MAX_PATH]; const filename_tests_t* test; char* c; - int rc; + INT_PTR rc; test=filename_tests; while (test->basename) @@ -1177,13 +1504,13 @@ static void test_filename(void) if ((test->todo & 0x1)==0) { ok(rc==test->rc || - broken(quotedfile && rc == 2), /* NT4 */ - "%s failed: rc=%d err=%d\n", shell_call, + broken(quotedfile && rc == SE_ERR_FNF), /* NT4 */ + "%s failed: rc=%ld err=%u\n", shell_call, rc, GetLastError()); } else todo_wine { - ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call, + ok(rc==test->rc, "%s failed: rc=%ld err=%u\n", shell_call, rc, GetLastError()); } if (rc == 33) @@ -1227,12 +1554,12 @@ static void test_filename(void) rc=33; if ((test->todo & 0x1)==0) { - ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call, + 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=%d err=%d\n", shell_call, + ok(rc==test->rc, "%s failed: rc=%ld err=%u\n", shell_call, rc, GetLastError()); } if (rc==0) @@ -1296,7 +1623,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=%d err=%d\n", shell_call, rc, + ok(rc > 32, "%s failed: rc=%ld err=%u\n", shell_call, rc, GetLastError()); okChildInt("argcA", 5); okChildString("argvA3", "Open"); @@ -1305,8 +1632,144 @@ static void test_filename(void) } } +typedef struct +{ + const char* urlprefix; + const char* basename; + int flags; + int todo; +} fileurl_tests_t; + +#define URL_SUCCESS 0x1 +#define USE_COLON 0x2 +#define USE_BSLASH 0x4 + +static fileurl_tests_t fileurl_tests[]= +{ + /* How many slashes does it take... */ + {"file:", "%s\\test file.shlexec", URL_SUCCESS, 0}, + {"file:/", "%s\\test file.shlexec", URL_SUCCESS, 0}, + {"file://", "%s\\test file.shlexec", URL_SUCCESS, 0}, + {"file:///", "%s\\test file.shlexec", URL_SUCCESS, 0}, + {"File:///", "%s\\test file.shlexec", URL_SUCCESS, 0}, + {"file:////", "%s\\test file.shlexec", URL_SUCCESS, 0}, + {"file://///", "%s\\test file.shlexec", 0, 0}, + + /* Test with Windows-style paths */ + {"file:///", "%s\\test file.shlexec", URL_SUCCESS | USE_COLON, 0}, + {"file:///", "%s\\test file.shlexec", URL_SUCCESS | USE_BSLASH, 0}, + + /* Check handling of hostnames */ + {"file://localhost/", "%s\\test file.shlexec", URL_SUCCESS, 0}, + {"file://localhost:80/", "%s\\test file.shlexec", 0, 0}, + {"file://LocalHost/", "%s\\test file.shlexec", URL_SUCCESS, 0}, + {"file://127.0.0.1/", "%s\\test file.shlexec", 0, 0}, + {"file://::1/", "%s\\test file.shlexec", 0, 0}, + {"file://notahost/", "%s\\test file.shlexec", 0, 0}, + + /* Environment variables are not expanded in URLs */ + {"%urlprefix%", "%s\\test file.shlexec", 0, 0x1}, + {"file:///", "%%TMPDIR%%\\test file.shlexec", 0, 0}, + + /* Test shortcuts vs. URLs */ + {"file://///", "%s\\test_shortcut_shlexec.lnk", 0, 0x1d}, + + {NULL, NULL, 0, 0} +}; + +static void test_fileurls(void) +{ + char filename[MAX_PATH], fileurl[MAX_PATH], longtmpdir[MAX_PATH]; + char command[MAX_PATH]; + const fileurl_tests_t* test; + char *s; + INT_PTR rc; + + rc = (INT_PTR)ShellExecute(NULL, NULL, "file:///nosuchfile.shlexec", NULL, NULL, SW_SHOWNORMAL); + if (rc > 32) + { + win_skip("shell32 is too old (likely < 4.72). Skipping the file URL tests\n"); + return; + } + + get_long_path_name(tmpdir, longtmpdir, sizeof(longtmpdir)/sizeof(*longtmpdir)); + SetEnvironmentVariable("urlprefix", "file:///"); + + test=fileurl_tests; + while (test->basename) + { + /* Build the file URL */ + sprintf(filename, test->basename, longtmpdir); + strcpy(fileurl, test->urlprefix); + strcat(fileurl, filename); + s = fileurl + strlen(test->urlprefix); + while (*s) + { + if (!(test->flags & USE_COLON) && *s == ':') + *s = '|'; + else if (!(test->flags & USE_BSLASH) && *s == '\\') + *s = '/'; + s++; + } + + /* Test it first with FindExecutable() */ + rc = (INT_PTR)FindExecutableA(fileurl, NULL, command); + ok(rc == SE_ERR_FNF, "FindExecutable(%s) failed: bad rc=%lu\n", fileurl, rc); + + /* Then ShellExecute() */ + if ((test->todo & 0x10) == 0) + rc = shell_execute(NULL, fileurl, NULL, NULL); + else todo_wait + rc = shell_execute(NULL, fileurl, NULL, NULL); + if (bad_shellexecute) + { + win_skip("shell32 is too old (likely 4.72). Skipping the file URL tests\n"); + break; + } + 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); + } + 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); + } + if (rc == 33) + { + if ((test->todo & 0x2) == 0) + okChildInt("argcA", 5); + else todo_wine + okChildInt("argcA", 5); + + if ((test->todo & 0x4) == 0) + okChildString("argvA3", "Open"); + else todo_wine + okChildString("argvA3", "Open"); + + if ((test->todo & 0x8) == 0) + okChildPath("argvA4", filename); + else todo_wine + okChildPath("argvA4", filename); + } + test++; + } + + SetEnvironmentVariable("urlprefix", NULL); +} + static void test_find_executable(void) { + char notepad_path[MAX_PATH]; char filename[MAX_PATH]; char command[MAX_PATH]; const filename_tests_t* test; @@ -1329,6 +1792,21 @@ static void test_find_executable(void) ok(strcmp(command, "your word") != 0, "FindExecutable(NULL) returned command=[%s]\n", command); } + GetSystemDirectoryA( notepad_path, MAX_PATH ); + strcat( notepad_path, "\\notepad.exe" ); + + /* Search for something that should be in the system-wide search path (no default directory) */ + strcpy(command, "your word"); + rc=(INT_PTR)FindExecutableA("notepad.exe", NULL, command); + ok(rc > 32, "FindExecutable(%s) returned %ld\n", "notepad.exe", rc); + ok(strcasecmp(command, notepad_path) == 0, "FindExecutable(%s) returned command=[%s]\n", "notepad.exe", command); + + /* Search for something that should be in the system-wide search path (with default directory) */ + strcpy(command, "your word"); + rc=(INT_PTR)FindExecutableA("notepad.exe", tmpdir, command); + ok(rc > 32, "FindExecutable(%s) returned %ld\n", "notepad.exe", rc); + ok(strcasecmp(command, notepad_path) == 0, "FindExecutable(%s) returned command=[%s]\n", "notepad.exe", command); + strcpy(command, "your word"); rc=(INT_PTR)FindExecutableA(tmpdir, NULL, command); ok(rc == SE_ERR_NOASSOC /* >= win2000 */ || rc > 32 /* win98, nt4 */, "FindExecutable(NULL) returned %ld\n", rc); @@ -1448,22 +1926,36 @@ static void test_lnks(void) char filename[MAX_PATH]; char params[MAX_PATH]; const filename_tests_t* test; - int rc; + INT_PTR rc; + /* Should open through our association */ sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir); - rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL); - ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc, - GetLastError()); + 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()); okChildInt("argcA", 5); okChildString("argvA3", "Open"); sprintf(params, "%s\\test file.shlexec", tmpdir); get_long_path_name(params, filename, sizeof(filename)); 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()); + okChildInt("argcA", 5); + todo_wine okChildString("argvA3", "Open"); + sprintf(params, "%s\\test file.shlexec", tmpdir); + get_long_path_name(params, filename, sizeof(filename)); + todo_wine okChildPath("argvA4", filename); + + /* Should just run our executable */ sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir); - rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL); - ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc, - GetLastError()); + 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()); + 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"); @@ -1481,8 +1973,8 @@ static void test_lnks(void) *c='/'; c++; } - rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL); - ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc, + 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()); okChildInt("argcA", 4); okChildString("argvA3", "Lnk"); @@ -1496,17 +1988,17 @@ static void test_lnks(void) sprintf(params+1, test->basename, tmpdir); strcat(params,"\""); rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, params, - NULL); + NULL, NULL); if (rc > 32) rc=33; if ((test->todo & 0x1)==0) { - ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call, + 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=%d err=%d\n", shell_call, + ok(rc==test->rc, "%s failed: rc=%lu err=%u\n", shell_call, rc, GetLastError()); } if (rc==0) @@ -1546,14 +2038,14 @@ static void test_exes(void) { char filename[MAX_PATH]; char params[1024]; - int rc; + INT_PTR rc; sprintf(params, "shlexec \"%s\" Exec", child_file); /* We need NOZONECHECKS on Win2003 to block a dialog */ rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, params, - NULL); - ok(rc > 32, "%s returned %d\n", shell_call, rc); + NULL, NULL); + ok(rc > 32, "%s returned %lu\n", shell_call, rc); okChildInt("argcA", 4); okChildString("argvA3", "Exec"); @@ -1564,45 +2056,7 @@ static void test_exes(void) { rc=shell_execute(NULL, filename, params, NULL); todo_wine { - ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc); - } - } - } - else - { - win_skip("Skipping shellexecute of file with unassociated extension\n"); - } -} - -static void test_exes_long(void) -{ - char filename[MAX_PATH]; - char params[2024]; - char longparam[MAX_PATH]; - int rc; - - for (rc = 0; rc < MAX_PATH; rc++) - longparam[rc]='a'+rc%26; - longparam[MAX_PATH-1]=0; - - - sprintf(params, "shlexec \"%s\" %s", child_file,longparam); - - /* We need NOZONECHECKS on Win2003 to block a dialog */ - rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, params, - NULL); - ok(rc > 32, "%s returned %d\n", shell_call, rc); - okChildInt("argcA", 4); - okChildString("argvA3", longparam); - - if (! skip_noassoc_tests) - { - sprintf(filename, "%s\\test file.noassoc", tmpdir); - if (CopyFile(argv0, filename, FALSE)) - { - rc=shell_execute(NULL, filename, params, NULL); - todo_wine { - ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc); + ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%lu\n", shell_call, rc); } } } @@ -1624,7 +2078,7 @@ typedef struct int todo; } dde_tests_t; -#if BUG_7233_IS_FIXED +#if CORE_6559_IS_FIXED static dde_tests_t dde_tests[] = { /* Test passing and not passing command-line @@ -1667,7 +2121,7 @@ static DWORD WINAPI hooked_WaitForInputIdle(HANDLE process, DWORD timeout) * by changing the entry for WaitForInputIdle() in the shell32 import address * table. */ -static void hook_WaitForInputIdle(void *new_func) +static void hook_WaitForInputIdle(DWORD (WINAPI *new_func)(HANDLE, DWORD)) { char *base; PIMAGE_NT_HEADERS nt_headers; @@ -1728,11 +2182,11 @@ static void test_dde(void) char filename[MAX_PATH], defApplication[MAX_PATH]; const dde_tests_t* test; char params[1024]; - int rc; + INT_PTR rc; HANDLE map; char *shared_block; - hook_WaitForInputIdle((void *) hooked_WaitForInputIdle); + hook_WaitForInputIdle(hooked_WaitForInputIdle); sprintf(filename, "%s\\test file.sde", tmpdir); @@ -1768,16 +2222,16 @@ 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); + rc = shell_execute_ex(SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI, NULL, filename, NULL, NULL, NULL); CloseHandle(dde_ready_event); if ((test->todo & 0x1)==0) { - ok(32 < rc, "%s failed: rc=%d err=%d\n", shell_call, + ok(32 < rc, "%s failed: rc=%lu err=%u\n", shell_call, rc, GetLastError()); } else todo_wine { - ok(32 < rc, "%s failed: rc=%d err=%d\n", shell_call, + ok(32 < rc, "%s failed: rc=%lu err=%u\n", shell_call, rc, GetLastError()); } if (32 < rc) @@ -1832,7 +2286,7 @@ typedef struct int rc[DDE_DEFAULT_APP_VARIANTS]; } dde_default_app_tests_t; -#if BUG_7233_IS_FIXED +#if CORE_6559_IS_FIXED static dde_default_app_tests_t dde_default_app_tests[] = { /* Windows XP and 98 handle default DDE app names in different ways. @@ -1887,7 +2341,7 @@ static DWORD CALLBACK ddeThread(LPVOID arg) assert(info && info->filename); PostThreadMessage(info->threadIdParent, WM_QUIT, - shell_execute_ex(SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI, NULL, info->filename, NULL, NULL), + shell_execute_ex(SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI, NULL, info->filename, NULL, NULL, NULL), 0L); ExitThread(0); } @@ -1901,7 +2355,8 @@ static void test_dde_default_app(void) char params[1024]; DWORD threadId; MSG msg; - int rc, which = 0; + INT_PTR rc; + int which = 0; post_quit_on_execute = FALSE; ddeInst = 0; @@ -1958,12 +2413,12 @@ static void test_dde_default_app(void) if ((test->todo & 0x1)==0) { - ok(rc==test->rc[which], "%s failed: rc=%d err=%d\n", shell_call, + 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=%d err=%d\n", shell_call, + ok(rc==test->rc[which], "%s failed: rc=%lu err=%u\n", shell_call, rc, GetLastError()); } if (rc == 33) @@ -2039,6 +2494,9 @@ static void init_test(void) DeleteFileA( tmpdir ); rc = CreateDirectoryA( tmpdir, NULL ); ok( rc, "failed to create %s err %u\n", tmpdir, GetLastError() ); + /* Set %TMPDIR% for the tests */ + SetEnvironmentVariableA("TMPDIR", tmpdir); + rc = GetTempFileNameA(tmpdir, "wt", 0, child_file); assert(rc != 0); init_event(child_file); @@ -2054,7 +2512,7 @@ static void init_test(void) FILE_ATTRIBUTE_NORMAL, NULL); if (hfile==INVALID_HANDLE_VALUE) { - trace("unable to create '%s': err=%d\n", filename, GetLastError()); + trace("unable to create '%s': err=%u\n", filename, GetLastError()); assert(0); } CloseHandle(hfile); @@ -2102,15 +2560,6 @@ 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\""); - - create_test_verb(".shlexec", "NoQuotesParam2", 0, "NoQuotesParam2 %2"); - create_test_verb(".shlexec", "QuotedParam2", 0, "QuotedParam2 \"%2\""); - - create_test_verb(".shlexec", "NoQuotesAllParams", 0, "NoQuotesAllParams %*"); - create_test_verb(".shlexec", "QuotedAllParams", 0, "QuotedAllParams \"%*\""); - - create_test_verb(".shlexec", "NoQuotesParams345etc", 0, "NoQuotesParams345etc %~3"); - create_test_verb(".shlexec", "QuotedParams345etc", 0, "QuotedParams345etc \"%~3\""); } static void cleanup_test(void) @@ -2139,128 +2588,57 @@ static void cleanup_test(void) CoUninitialize(); } -static void test_commandline(void) -{ - static const WCHAR one[] = {'o','n','e',0}; - static const WCHAR two[] = {'t','w','o',0}; - static const WCHAR three[] = {'t','h','r','e','e',0}; - static const WCHAR four[] = {'f','o','u','r',0}; - - static const WCHAR fmt1[] = {'%','s',' ','%','s',' ','%','s',' ','%','s',0}; - static const WCHAR fmt2[] = {' ','%','s',' ','%','s',' ','%','s',' ','%','s',0}; - static const WCHAR fmt3[] = {'%','s','=','%','s',' ','%','s','=','\"','%','s','\"',0}; - static const WCHAR fmt4[] = {'\"','%','s','\"',' ','\"','%','s',' ','%','s','\"',' ','%','s',0}; - static const WCHAR fmt5[] = {'\\','\"','%','s','\"',' ','%','s','=','\"','%','s','\\','\"',' ','\"','%','s','\\','\"',0}; - static const WCHAR fmt6[] = {0}; - - static const WCHAR chkfmt1[] = {'%','s','=','%','s',0}; - static const WCHAR chkfmt2[] = {'%','s',' ','%','s',0}; - static const WCHAR chkfmt3[] = {'\\','\"','%','s','\"',0}; - static const WCHAR chkfmt4[] = {'%','s','=','%','s','\"',' ','%','s','\"',0}; - WCHAR cmdline[255]; - LPWSTR *args = (LPWSTR*)0xdeadcafe, pbuf; - INT numargs = -1; - size_t buflen; - DWORD lerror; - - wsprintfW(cmdline,fmt1,one,two,three,four); - args=CommandLineToArgvW(cmdline,&numargs); - if (args == NULL && numargs == -1) - { - win_skip("CommandLineToArgvW not implemented, skipping\n"); - return; - } - ok(numargs == 4, "expected 4 args, got %i\n",numargs); - ok(lstrcmpW(args[0],one)==0,"arg0 is not as expected\n"); - ok(lstrcmpW(args[1],two)==0,"arg1 is not as expected\n"); - ok(lstrcmpW(args[2],three)==0,"arg2 is not as expected\n"); - ok(lstrcmpW(args[3],four)==0,"arg3 is not as expected\n"); - - SetLastError(0xdeadbeef); - args=CommandLineToArgvW(cmdline,NULL); - lerror=GetLastError(); - ok(args == NULL && lerror == ERROR_INVALID_PARAMETER, "expected NULL with ERROR_INVALID_PARAMETER got %p with %d\n",args,lerror); - SetLastError(0xdeadbeef); - args=CommandLineToArgvW(NULL,NULL); - lerror=GetLastError(); - ok(args == NULL && lerror == ERROR_INVALID_PARAMETER, "expected NULL with ERROR_INVALID_PARAMETER got %p with %d\n",args,lerror); - - wsprintfW(cmdline,fmt2,one,two,three,four); - args=CommandLineToArgvW(cmdline,&numargs); - ok(numargs == 5, "expected 5 args, got %i\n",numargs); - ok(args[0][0]==0,"arg0 is not as expected\n"); - ok(lstrcmpW(args[1],one)==0,"arg1 is not as expected\n"); - ok(lstrcmpW(args[2],two)==0,"arg2 is not as expected\n"); - ok(lstrcmpW(args[3],three)==0,"arg3 is not as expected\n"); - ok(lstrcmpW(args[4],four)==0,"arg4 is not as expected\n"); - - wsprintfW(cmdline,fmt3,one,two,three,four); - args=CommandLineToArgvW(cmdline,&numargs); - ok(numargs == 2, "expected 2 args, got %i\n",numargs); - wsprintfW(cmdline,chkfmt1,one,two); - ok(lstrcmpW(args[0],cmdline)==0,"arg0 is not as expected\n"); - wsprintfW(cmdline,chkfmt1,three,four); - ok(lstrcmpW(args[1],cmdline)==0,"arg1 is not as expected\n"); - - wsprintfW(cmdline,fmt4,one,two,three,four); - args=CommandLineToArgvW(cmdline,&numargs); - ok(numargs == 3, "expected 3 args, got %i\n",numargs); - ok(lstrcmpW(args[0],one)==0,"arg0 is not as expected\n"); - wsprintfW(cmdline,chkfmt2,two,three); - ok(lstrcmpW(args[1],cmdline)==0,"arg1 is not as expected\n"); - ok(lstrcmpW(args[2],four)==0,"arg2 is not as expected\n"); - - wsprintfW(cmdline,fmt5,one,two,three,four); - args=CommandLineToArgvW(cmdline,&numargs); - ok(numargs == 2, "expected 2 args, got %i\n",numargs); - wsprintfW(cmdline,chkfmt3,one); - todo_wine ok(lstrcmpW(args[0],cmdline)==0,"arg0 is not as expected\n"); - wsprintfW(cmdline,chkfmt4,two,three,four); - todo_wine ok(lstrcmpW(args[1],cmdline)==0,"arg1 is not as expected\n"); - - wsprintfW(cmdline,fmt6); - args=CommandLineToArgvW(cmdline,&numargs); - ok(numargs == 1, "expected 1 args, got %i\n",numargs); - if (numargs == 1) { - buflen = max(lstrlenW(args[0])+1,256); - pbuf = HeapAlloc(GetProcessHeap(), 0, buflen*sizeof(pbuf[0])); - GetModuleFileNameW(NULL, pbuf, buflen); - pbuf[buflen-1] = 0; - /* check args[0] is module file name */ - ok(lstrcmpW(args[0],pbuf)==0, "wrong path to the current executable\n"); - HeapFree(GetProcessHeap(), 0, pbuf); - } -} - static void test_directory(void) { - char path[MAX_PATH], newdir[MAX_PATH]; - char params[1024]; - int rc; + char path[MAX_PATH], curdir[MAX_PATH]; + char params[1024], dirpath[1024]; + INT_PTR rc; - /* copy this executable to a new folder and cd to it */ - sprintf(newdir, "%s\\newfolder", tmpdir); - rc = CreateDirectoryA( newdir, NULL ); - ok( rc, "failed to create %s err %u\n", newdir, GetLastError() ); - sprintf(path, "%s\\%s", newdir, path_find_file_name(argv0)); + sprintf(path, "%s\\test2.exe", tmpdir); CopyFileA(argv0, path, FALSE); - SetCurrentDirectory(tmpdir); sprintf(params, "shlexec \"%s\" Exec", child_file); + /* Test with the current directory */ + GetCurrentDirectoryA(sizeof(curdir), curdir); + SetCurrentDirectoryA(tmpdir); rc=shell_execute_ex(SEE_MASK_NOZONECHECKS|SEE_MASK_FLAG_NO_UI, - NULL, path_find_file_name(argv0), params, NULL); - todo_wine ok(rc == SE_ERR_FNF, "%s returned %d\n", shell_call, rc); + NULL, "test2.exe", params, NULL, NULL); + ok(rc > 32, "%s returned %lu\n", shell_call, rc); + okChildInt("argcA", 4); + okChildString("argvA3", "Exec"); + todo_wine okChildPath("longPath", path); + SetCurrentDirectoryA(curdir); rc=shell_execute_ex(SEE_MASK_NOZONECHECKS|SEE_MASK_FLAG_NO_UI, - NULL, path_find_file_name(argv0), params, newdir); - ok(rc > 32, "%s returned %d\n", shell_call, rc); + NULL, "test2.exe", params, NULL, NULL); + ok(rc == SE_ERR_FNF, "%s returned %lu\n", shell_call, 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); okChildInt("argcA", 4); okChildString("argvA3", "Exec"); todo_wine okChildPath("longPath", path); - DeleteFile(path); - RemoveDirectoryA(newdir); + /* 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); + + 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); + okChildInt("argcA", 4); + okChildString("argvA3", "Exec"); + todo_wine okChildPath("longPath", path); + + /* Not a colon-separated directory list */ + 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); } START_TEST(shlexec) @@ -2275,20 +2653,20 @@ START_TEST(shlexec) init_test(); + test_commandline2argv(); test_argify(); test_lpFile_parsed(); test_filename(); + test_fileurls(); test_find_executable(); test_lnks(); test_exes(); - test_exes_long(); -#if BUG_7233_IS_FIXED +#if CORE_6559_IS_FIXED test_dde(); test_dde_default_app(); #endif - win_skip("Skipping test_dde() until we have a sane DDE implementation. Bug 7233.\n"); - win_skip("Skipping test_dde_default_app() until we have a sane DDE implementation. Bug 7233.\n"); - test_commandline(); + win_skip("Skipping test_dde() until we have a sane DDE implementation. CORE-6559.\n"); + win_skip("Skipping test_dde_default_app() until we have a sane DDE implementation. CORE-6559.\n"); test_directory(); cleanup_test(); diff --git a/rostests/winetests/shell32/shlfileop.c b/rostests/winetests/shell32/shlfileop.c index f1e2a3ed18e..9bf1e73279b 100644 --- a/rostests/winetests/shell32/shlfileop.c +++ b/rostests/winetests/shell32/shlfileop.c @@ -995,7 +995,11 @@ static void test_copy(void) shfo.fFlags |= FOF_NOERRORUI; set_curr_dir_path(from, "test1.txt\0test2.txt\0"); set_curr_dir_path(to, "test3.txt\0"); + shfo.fAnyOperationsAborted = 0xdeadbeef; retval = SHFileOperation(&shfo); + ok(shfo.fAnyOperationsAborted != 0xdeadbeef || + broken(shfo.fAnyOperationsAborted == 0xdeadbeef), /* NT4 */ + "Expected TRUE/FALSE fAnyOperationsAborted not 0xdeadbeef\n"); if (retval == DE_FLDDESTISFILE || /* Vista and W2K8 */ retval == DE_INVALIDFILES) /* Win7 */ { @@ -1012,8 +1016,11 @@ static void test_copy(void) /* try to copy many files to nonexistent directory */ DeleteFile(to); shfo.fFlags &= ~FOF_NOERRORUI; - shfo.fAnyOperationsAborted = FALSE; + shfo.fAnyOperationsAborted = 0xdeadbeef; retval = SHFileOperation(&shfo); + ok(!shfo.fAnyOperationsAborted || + broken(shfo.fAnyOperationsAborted == 0xdeadbeef), /* NT4 */ + "Didn't expect aborted operations\n"); ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval); ok(DeleteFile("test3.txt\\test1.txt"), "Expected test3.txt\\test1.txt to exist\n"); ok(DeleteFile("test3.txt\\test2.txt"), "Expected test3.txt\\test1.txt to exist\n"); @@ -1024,11 +1031,16 @@ static void test_copy(void) shfo.pFrom = "test1.txt\0test2.txt\0test3.txt\0"; shfo.pTo = "testdir2\\a.txt\0testdir2\\b.txt\0testdir2\\c.txt\0testdir2\\d.txt\0"; shfo.fFlags |= FOF_NOERRORUI | FOF_MULTIDESTFILES; + shfo.fAnyOperationsAborted = 0xdeadbeef; retval = SHFileOperation(&shfo); + ok(shfo.fAnyOperationsAborted != 0xdeadbeef || + broken(shfo.fAnyOperationsAborted == 0xdeadbeef), /* NT4 */ + "Expected TRUE/FALSE fAnyOperationsAborted not 0xdeadbeef\n"); if (dir_exists("testdir2\\a.txt")) { /* Vista and W2K8 (broken or new behavior ?) */ ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval); + ok(!shfo.fAnyOperationsAborted, "Didn't expect aborted operations\n"); ok(DeleteFile("testdir2\\a.txt\\test1.txt"), "Expected testdir2\\a.txt\\test1.txt to exist\n"); RemoveDirectory("testdir2\\a.txt"); ok(DeleteFile("testdir2\\b.txt\\test2.txt"), "Expected testdir2\\b.txt\\test2.txt to exist\n"); @@ -1049,11 +1061,15 @@ static void test_copy(void) /* send in FOF_MULTIDESTFILES with too many destination files */ shfo.pFrom = "test1.txt\0test2.txt\0test3.txt\0"; shfo.pTo = "e.txt\0f.txt\0"; - shfo.fAnyOperationsAborted = FALSE; + shfo.fAnyOperationsAborted = 0xdeadbeef; retval = SHFileOperation(&shfo); + ok(shfo.fAnyOperationsAborted != 0xdeadbeef || + broken(shfo.fAnyOperationsAborted == 0xdeadbeef), /* NT4 */ + "Expected TRUE/FALSE fAnyOperationsAborted not 0xdeadbeef\n"); if (dir_exists("e.txt")) { /* Vista and W2K8 (broken or new behavior ?) */ + ok(!shfo.fAnyOperationsAborted, "Didn't expect aborted operations\n"); ok(retval == DE_SAMEFILE, "Expected DE_SAMEFILE, got %d\n", retval); ok(DeleteFile("e.txt\\test1.txt"), "Expected e.txt\\test1.txt to exist\n"); RemoveDirectory("e.txt"); @@ -1072,8 +1088,11 @@ static void test_copy(void) /* use FOF_MULTIDESTFILES with files and a source directory */ shfo.pFrom = "test1.txt\0test2.txt\0test4.txt\0"; shfo.pTo = "testdir2\\a.txt\0testdir2\\b.txt\0testdir2\\c.txt\0"; - shfo.fAnyOperationsAborted = FALSE; + shfo.fAnyOperationsAborted = 0xdeadbeef; retval = SHFileOperation(&shfo); + ok(!shfo.fAnyOperationsAborted || + broken(shfo.fAnyOperationsAborted == 0xdeadbeef), /* NT4 */ + "Didn't expect aborted operations\n"); ok(retval == ERROR_SUCCESS || broken(retval == 0x100a1), /* WinMe */ "Expected ERROR_SUCCESS, got %d\n", retval); @@ -1085,13 +1104,17 @@ static void test_copy(void) /* try many dest files without FOF_MULTIDESTFILES flag */ shfo.pFrom = "test1.txt\0test2.txt\0test3.txt\0"; shfo.pTo = "a.txt\0b.txt\0c.txt\0"; - shfo.fAnyOperationsAborted = FALSE; + shfo.fAnyOperationsAborted = 0xdeadbeef; shfo.fFlags &= ~FOF_MULTIDESTFILES; retval = SHFileOperation(&shfo); + ok(shfo.fAnyOperationsAborted != 0xdeadbeef || + broken(shfo.fAnyOperationsAborted == 0xdeadbeef), /* NT4 */ + "Expected TRUE/FALSE fAnyOperationsAborted not 0xdeadbeef\n"); if (dir_exists("a.txt")) { /* Vista and W2K8 (broken or new behavior ?) */ ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval); + ok(!shfo.fAnyOperationsAborted, "Didn't expect aborted operations\n"); ok(DeleteFile("a.txt\\test1.txt"), "Expected a.txt\\test1.txt to exist\n"); ok(DeleteFile("a.txt\\test2.txt"), "Expected a.txt\\test2.txt to exist\n"); ok(DeleteFile("a.txt\\test3.txt"), "Expected a.txt\\test3.txt to exist\n"); @@ -1099,7 +1122,11 @@ static void test_copy(void) } else { + expect_retval(ERROR_CANCELLED, DE_OPCANCELLED /* Win9x, NT4 */); + ok(shfo.fAnyOperationsAborted || + broken(!shfo.fAnyOperationsAborted), /* NT4 */ + "Expected aborted operations\n"); ok(!file_exists("a.txt"), "Expected a.txt to not exist\n"); } @@ -1157,8 +1184,11 @@ static void test_copy(void) init_shfo_tests(); shfo.pFrom = "test1.txt\0"; shfo.pTo = "b.txt\0c.txt\0"; - shfo.fAnyOperationsAborted = FALSE; + shfo.fAnyOperationsAborted = 0xdeadbeef; retval = SHFileOperation(&shfo); + ok(!shfo.fAnyOperationsAborted || + broken(shfo.fAnyOperationsAborted == 0xdeadbeef), /* NT4 */ + "Didn't expect aborted operations\n"); if (retval == DE_OPCANCELLED) { /* NT4 fails and doesn't copy any files */ @@ -1177,11 +1207,13 @@ static void test_copy(void) /* copy two file to three others, all fail */ shfo.pFrom = "test1.txt\0test2.txt\0"; shfo.pTo = "b.txt\0c.txt\0d.txt\0"; + shfo.fAnyOperationsAborted = 0xdeadbeef; retval = SHFileOperation(&shfo); if (dir_exists("b.txt")) { /* Vista and W2K8 (broken or new behavior ?) */ ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval); + ok(!shfo.fAnyOperationsAborted, "Didn't expect aborted operations\n"); ok(DeleteFile("b.txt\\test1.txt"), "Expected b.txt\\test1.txt to exist\n"); RemoveDirectory("b.txt"); ok(DeleteFile("c.txt\\test2.txt"), "Expected c.txt\\test2.txt to exist\n"); @@ -1191,7 +1223,7 @@ static void test_copy(void) { expect_retval(ERROR_CANCELLED, DE_OPCANCELLED /* Win9x, NT4 */); ok(shfo.fAnyOperationsAborted || - broken(!shfo.fAnyOperationsAborted), /* NT4 */ + broken(shfo.fAnyOperationsAborted == 0xdeadbeef), /* NT4 */ "Expected aborted operations\n"); ok(!DeleteFile("b.txt"), "Expected b.txt to not exist\n"); } @@ -1199,12 +1231,13 @@ static void test_copy(void) /* copy one file and one directory to three others */ shfo.pFrom = "test1.txt\0test4.txt\0"; shfo.pTo = "b.txt\0c.txt\0d.txt\0"; - shfo.fAnyOperationsAborted = FALSE; + shfo.fAnyOperationsAborted = 0xdeadbeef; retval = SHFileOperation(&shfo); if (dir_exists("b.txt")) { /* Vista and W2K8 (broken or new behavior ?) */ ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval); + ok(!shfo.fAnyOperationsAborted, "Didn't expect aborted operations\n"); ok(DeleteFile("b.txt\\test1.txt"), "Expected b.txt\\test1.txt to exist\n"); RemoveDirectory("b.txt"); ok(RemoveDirectory("c.txt\\test4.txt"), "Expected c.txt\\test4.txt to exist\n"); @@ -1214,7 +1247,7 @@ static void test_copy(void) { expect_retval(ERROR_CANCELLED, DE_OPCANCELLED /* Win9x, NT4 */); ok(shfo.fAnyOperationsAborted || - broken(!shfo.fAnyOperationsAborted), /* NT4 */ + broken(shfo.fAnyOperationsAborted == 0xdeadbeef), /* NT4 */ "Expected aborted operations\n"); ok(!DeleteFile("b.txt"), "Expected b.txt to not exist\n"); ok(!DeleteFile("c.txt"), "Expected c.txt to not exist\n"); @@ -1383,10 +1416,11 @@ static void test_copy(void) shfo.fFlags = FOF_NOCONFIRMATION; shfo.pFrom = "test1.txt\0"; shfo.pTo = "test2.txt\0"; - shfo.fAnyOperationsAborted = FALSE; + shfo.fAnyOperationsAborted = 0xdeadbeef; /* without FOF_NOCONFIRMATION the confirmation is Yes/No */ retval = SHFileOperation(&shfo); ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval); + ok(!shfo.fAnyOperationsAborted, "Didn't expect aborted operations\n"); ok(file_has_content("test2.txt", "test1.txt\n"), "The file was not copied\n"); shfo.pFrom = "test3.txt\0test1.txt\0"; diff --git a/rostests/winetests/shell32/shlfolder.c b/rostests/winetests/shell32/shlfolder.c index bee6cb5a13a..ef8aae9cb6b 100644 --- a/rostests/winetests/shell32/shlfolder.c +++ b/rostests/winetests/shell32/shlfolder.c @@ -73,6 +73,18 @@ static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); static UINT (WINAPI *pGetSystemWow64DirectoryW)(LPWSTR, UINT); static HRESULT (WINAPI *pSHCreateDefaultContextMenu)(const DEFCONTEXTMENU*,REFIID,void**); +static const char *debugstr_guid(REFIID riid) +{ + static char buf[50]; + + sprintf(buf, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + riid->Data1, riid->Data2, riid->Data3, riid->Data4[0], + riid->Data4[1], riid->Data4[2], riid->Data4[3], riid->Data4[4], + riid->Data4[5], riid->Data4[6], riid->Data4[7]); + + return buf; +} + static WCHAR *make_wstr(const char *str) { WCHAR *ret; @@ -566,7 +578,9 @@ if (0) CLSID id; hr = IPersist_GetClassID(pp, &id); ok(hr == S_OK, "Got 0x%08x\n", hr); - ok(IsEqualIID(&id, &CLSID_ShellDocObjView), "Unexpected classid\n"); + /* CLSID_ShellFSFolder on some w2k systems */ + ok(IsEqualIID(&id, &CLSID_ShellDocObjView) || broken(IsEqualIID(&id, &CLSID_ShellFSFolder)), + "Unexpected classid %s\n", debugstr_guid(&id)); IPersist_Release(pp); } @@ -782,7 +796,7 @@ static void test_GetDisplayName(void) broken(hr == S_OK), /* Win9x, W2K */ "hr = %08x\n", hr); if (hr == S_OK) { - IShellFolder_Release(psfFile); + IUnknown_Release(psfFile); } if (!pSHBindToParent) diff --git a/rostests/winetests/shell32/shlview.c b/rostests/winetests/shell32/shlview.c index 6e974992159..554e0869cc0 100644 --- a/rostests/winetests/shell32/shlview.c +++ b/rostests/winetests/shell32/shlview.c @@ -170,7 +170,7 @@ static HRESULT WINAPI IDataObjectImpl_QueryInterface(IDataObject *iface, REFIID if(*ppvObj) { - IUnknown_AddRef(iface); + IDataObject_AddRef(iface); return S_OK; } @@ -302,7 +302,7 @@ static HRESULT WINAPI IShellBrowserImpl_QueryInterface(IShellBrowser *iface, if(*ppvObj) { - IUnknown_AddRef(iface); + IShellBrowser_AddRef(iface); return S_OK; }