sync shell32 winetest to wine 1.1.32
authorChristoph von Wittich <christoph_vw@reactos.org>
Wed, 11 Nov 2009 07:36:49 +0000 (07:36 +0000)
committerChristoph von Wittich <christoph_vw@reactos.org>
Wed, 11 Nov 2009 07:36:49 +0000 (07:36 +0000)
svn path=/trunk/; revision=44098

rostests/winetests/shell32/appbar.c
rostests/winetests/shell32/shelllink.c
rostests/winetests/shell32/shellpath.c
rostests/winetests/shell32/shlexec.c
rostests/winetests/shell32/shlfileop.c

index 73dc0b2..5e5dfaf 100644 (file)
@@ -205,6 +205,7 @@ static void test_setpos(void)
     RECT rc;
     int screen_width, screen_height;
     BOOL ret;
+    int org_bottom1;
 
     screen_width = GetSystemMetrics(SM_CXSCREEN);
     screen_height = GetSystemMetrics(SM_CYSCREEN);
@@ -336,6 +337,9 @@ static void test_setpos(void)
 
     /* removing windows[0] will cause windows[1] to move down into its space */
     expected_bottom = max(windows[0].allocated_rect.bottom, windows[1].allocated_rect.bottom);
+    org_bottom1 = windows[1].allocated_rect.bottom;
+    ok(windows[0].allocated_rect.bottom > windows[1].allocated_rect.bottom,
+        "Expected windows[0] to be lower than windows[1]\n");
 
     abd.hWnd = windows[0].hwnd;
     windows[0].to_be_deleted = TRUE;
@@ -346,7 +350,12 @@ static void test_setpos(void)
 
     do_events_until(got_expected_bottom);
 
-    ok(windows[1].allocated_rect.bottom == expected_bottom, "windows[1]'s bottom is %i, expected %i\n", windows[1].allocated_rect.bottom, expected_bottom);
+    if (windows[1].allocated_rect.bottom == org_bottom1)
+        win_skip("Some broken Vista boxes don't move the higher appbar down\n");
+    else
+        ok(windows[1].allocated_rect.bottom == expected_bottom,
+            "windows[1]'s bottom is %i, expected %i\n",
+            windows[1].allocated_rect.bottom, expected_bottom);
 
     test_window_rects(1, 2);
 
index 15a93db..2f20200 100755 (executable)
@@ -107,6 +107,7 @@ static void test_get_set(void)
 {
     HRESULT r;
     IShellLinkA *sl;
+    IShellLinkW *slW = NULL;
     char mypath[MAX_PATH];
     char buffer[INFOTIPSIZE];
     LPITEMIDLIST pidl, tmp_pidl;
@@ -156,6 +157,19 @@ static void test_get_set(void)
     ok(SUCCEEDED(r), "GetPath failed (0x%08x)\n", r);
     ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
 
+    CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+                     &IID_IShellLinkW, (LPVOID*)&slW);
+    if (!slW)
+        skip("SetPath with NULL parameter crashes on Win9x\n");
+    else
+    {
+        IShellLinkW_Release(slW);
+        r = IShellLinkA_SetPath(sl, NULL);
+        ok(r==E_INVALIDARG ||
+           broken(r==S_OK), /* Some Win95 and NT4 */
+           "SetPath failed (0x%08x)\n", r);
+    }
+
     r = IShellLinkA_SetPath(sl, "");
     ok(r==S_OK, "SetPath failed (0x%08x)\n", r);
 
@@ -651,16 +665,12 @@ static void test_load_save(void)
 static void test_datalink(void)
 {
     static const WCHAR lnk[] = {
-      ':',':','{','9','d','b','1','1','8','6','f','-','4','0','d','f','-','1',
+      ':',':','{','9','d','b','1','1','8','6','e','-','4','0','d','f','-','1',
       '1','d','1','-','a','a','8','c','-','0','0','c','0','4','f','b','6','7',
-      '8','6','3','}',':','{','0','0','0','1','0','4','0','9','-','7','8','E',
-      '1','-','1','1','D','2','-','B','6','0','F','-','0','0','6','0','9','7',
-      'C','9','9','8','E','7','}',':',':','{','9','d','b','1','1','8','6','e',
-      '-','4','0','d','f','-','1','1','d','1','-','a','a','8','c','-','0','0',
-      'c','0','4','f','b','6','7','8','6','3','}',':','2','6',',','!','!','g',
-      'x','s','f','(','N','g',']','q','F','`','H','{','L','s','A','C','C','E',
-      'S','S','F','i','l','e','s','>','p','l','T',']','j','I','{','j','f','(',
-      '=','1','&','L','[','-','8','1','-',']',':',':',0 };
+      '8','6','3','}',':','2','6',',','!','!','g','x','s','f','(','N','g',']',
+      'q','F','`','H','{','L','s','A','C','C','E','S','S','F','i','l','e','s',
+      '>','p','l','T',']','j','I','{','j','f','(','=','1','&','L','[','-','8',
+      '1','-',']',':',':',0 };
     static const WCHAR comp[] = {
       '2','6',',','!','!','g','x','s','f','(','N','g',']','q','F','`','H','{',
       'L','s','A','C','C','E','S','S','F','i','l','e','s','>','p','l','T',']',
@@ -704,6 +714,9 @@ static void test_datalink(void)
     ok( r == E_FAIL, "CopyDataBlock failed\n");
     ok( dar == NULL, "should be null\n");
 
+    r = IShellLinkW_SetPath(sl, NULL);
+    ok(r == E_INVALIDARG, "set path failed\n");
+
     r = IShellLinkW_SetPath(sl, lnk);
     ok(r == S_OK, "set path failed\n");
 
@@ -715,7 +728,8 @@ static void test_datalink(void)
     flags = 0;
     r = IShellLinkDataList_GetFlags( dl, &flags );
     ok( r == S_OK, "GetFlags failed\n");
-    ok( flags == (SLDF_HAS_DARWINID|SLDF_HAS_LOGO3ID),
+    /* SLDF_HAS_LOGO3ID is no longer supported on Vista+, filter it out */
+    ok( (flags & (~ SLDF_HAS_LOGO3ID)) == SLDF_HAS_DARWINID,
         "GetFlags returned wrong flags\n");
 
     dar = NULL;
index b5b1967..f9f59e1 100644 (file)
@@ -43,6 +43,9 @@
 #endif
 
 /* from pidl.h, not included here: */
+#ifndef PT_CPL             /* Guess, Win7 uses this for CSIDL_CONTROLS */
+#define PT_CPL        0x01 /* no path */
+#endif
 #ifndef PT_GUID
 #define PT_GUID       0x1f /* no path */
 #endif
@@ -93,7 +96,7 @@ static UINT (WINAPI *pGetSystemWow64DirectoryA)(LPSTR,UINT);
 static DLLVERSIONINFO shellVersion = { 0 };
 static LPMALLOC pMalloc;
 static const BYTE guidType[] = { PT_GUID };
-static const BYTE controlPanelType[] = { PT_SHELLEXT, PT_GUID };
+static const BYTE controlPanelType[] = { PT_SHELLEXT, PT_GUID, PT_CPL };
 static const BYTE folderType[] = { PT_FOLDER, PT_FOLDERW };
 static const BYTE favoritesType[] = { PT_FOLDER, PT_FOLDERW, 0, PT_IESPECIAL2 /* Win98 */ };
 static const BYTE folderOrSpecialType[] = { PT_FOLDER, PT_IESPECIAL2 };
index 7ddeee3..5e97f61 100755 (executable)
@@ -56,6 +56,7 @@ static char** myARGV;
 static char tmpdir[MAX_PATH];
 static char child_file[MAX_PATH];
 static DLLVERSIONINFO dllver;
+static BOOL skip_noassoc_tests = FALSE;
 
 
 /***
@@ -117,7 +118,18 @@ static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCST
     {
         int wait_rc;
         wait_rc=WaitForSingleObject(hEvent, 5000);
-        ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject returned %d\n", wait_rc);
+        if (wait_rc == WAIT_TIMEOUT)
+        {
+            HWND wnd = FindWindowA("#32770", "Windows");
+            if (wnd != NULL)
+            {
+                SendMessage(wnd, WM_CLOSE, 0, 0);
+                win_skip("Skipping shellexecute of file with unassociated extension\n");
+                skip_noassoc_tests = TRUE;
+                rc = SE_ERR_NOASSOC;
+            }
+        }
+        ok(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
@@ -462,11 +474,67 @@ static void     childPrintf(HANDLE h, const char* fmt, ...)
     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 = DdeQueryString(ddeInst, hsz2, ddeApplication, MAX_PATH, CP_WINANSI);
+                assert(size < MAX_PATH);
+                return (HDDEDATA)TRUE;
+            }
+            return (HDDEDATA)FALSE;
+
+        case XTYP_EXECUTE:
+            size = DdeGetData(hData, (LPBYTE)ddeExec, MAX_PATH, 0L);
+            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;
-    HANDLE hFile;
+    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);
@@ -484,6 +552,51 @@ static void doChild(int argc, char** argv)
             trace("argvA%d=%s\n", i, argv[i]);
         childPrintf(hFile, "argvA%d=%s\r\n", i, encodeA(argv[i]));
     }
+
+    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, 0L);
+            assert(rc == DMLERR_NO_ERROR);
+            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, 0L, DNS_REGISTER | DNS_FILTEROFF));
+
+            timer = SetTimer(NULL, 0, 2500, childTimeout);
+
+            dde_ready = CreateEvent(NULL, FALSE, FALSE, "winetest_shlexec_dde_ready");
+            SetEvent(dde_ready);
+            CloseHandle(dde_ready);
+
+            while (GetMessage(&msg, NULL, 0, 0))
+                DispatchMessage(&msg);
+
+            KillTimer(NULL, timer);
+            assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_UNREGISTER));
+            assert(DdeFreeStringHandle(ddeInst, hszTopic));
+            assert(DdeFreeStringHandle(ddeInst, hszApplication));
+            assert(DdeUninitialize(ddeInst));
+        }
+        else
+        {
+            dde_ready = CreateEvent(NULL, FALSE, 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);
@@ -751,6 +864,13 @@ static void test_filename(void)
     {
         BOOL quotedfile = FALSE;
 
+        if (skip_noassoc_tests && test->rc == SE_ERR_NOASSOC)
+        {
+            win_skip("Skipping shellexecute of file with unassociated extension\n");
+            test++;
+            continue;
+        }
+
         sprintf(filename, test->basename, tmpdir);
         if (strchr(filename, '/'))
         {
@@ -1117,7 +1237,7 @@ static void test_lnks(void)
             {
                 okChildInt("argcA", 5);
             }
-            else 
+            else
             {
                 okChildInt("argcA", 5);
             }
@@ -1159,14 +1279,21 @@ static void test_exes(void)
     okChildInt("argcA", 4);
     okChildString("argvA3", "Exec");
 
-    sprintf(filename, "%s\\test file.noassoc", tmpdir);
-    if (CopyFile(argv0, filename, FALSE))
+    if (! skip_noassoc_tests)
     {
-        rc=shell_execute(NULL, filename, params, NULL);
-        todo_wine {
-        ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc);
+        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);
+            }
         }
     }
+    else
+    {
+        win_skip("Skipping shellexecute of file with unassociated extension\n");
+    }
 }
 
 static void test_exes_long(void)
@@ -1190,14 +1317,21 @@ static void test_exes_long(void)
     okChildInt("argcA", 4);
     okChildString("argvA3", longparam);
 
-    sprintf(filename, "%s\\test file.noassoc", tmpdir);
-    if (CopyFile(argv0, filename, FALSE))
+    if (! skip_noassoc_tests)
     {
-        rc=shell_execute(NULL, filename, params, NULL);
-        todo_wine {
-        ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc);
+        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);
+            }
         }
     }
+    else
+    {
+        win_skip("Skipping shellexecute of file with unassociated extension\n");
+    }
 }
 
 typedef struct
@@ -1210,115 +1344,123 @@ typedef struct
     int expectedArgs;
     const char* expectedDdeExec;
     int todo;
-    int rc;
 } dde_tests_t;
 
 static dde_tests_t dde_tests[] =
 {
     /* Test passing and not passing command-line
      * argument, no DDE */
-    {"", NULL, NULL, NULL, NULL, FALSE, "", 0x0, 33},
-    {"\"%1\"", NULL, NULL, NULL, NULL, TRUE, "", 0x0, 33},
+    {"", NULL, NULL, NULL, NULL, FALSE, "", 0x0},
+    {"\"%1\"", NULL, NULL, NULL, NULL, TRUE, "", 0x0},
 
     /* Test passing and not passing command-line
      * argument, with DDE */
-    {"", "[open(\"%1\")]", "shlexec", "dde", NULL, FALSE, "[open(\"%s\")]", 0x0, 33},
-    {"\"%1\"", "[open(\"%1\")]", "shlexec", "dde", NULL, TRUE, "[open(\"%s\")]", 0x0, 33},
+    {"", "[open(\"%1\")]", "shlexec", "dde", NULL, FALSE, "[open(\"%s\")]", 0x0},
+    {"\"%1\"", "[open(\"%1\")]", "shlexec", "dde", NULL, TRUE, "[open(\"%s\")]", 0x0},
 
     /* Test unquoted %1 in command and ddeexec
      * (test filename has space) */
-    {"%1", "[open(%1)]", "shlexec", "dde", NULL, 2, "[open(%s)]", 0x0, 33},
+    {"%1", "[open(%1)]", "shlexec", "dde", NULL, 2, "[open(%s)]", 0x0},
 
     /* Test ifexec precedence over ddeexec */
-    {"", "[open(\"%1\")]", "shlexec", "dde", "[ifexec(\"%1\")]", FALSE, "[ifexec(\"%s\")]", 0x0, 33},
+    {"", "[open(\"%1\")]", "shlexec", "dde", "[ifexec(\"%1\")]", FALSE, "[ifexec(\"%s\")]", 0x0},
 
     /* Test default DDE topic */
-    {"", "[open(\"%1\")]", "shlexec", NULL, NULL, FALSE, "[open(\"%s\")]", 0x0, 33},
+    {"", "[open(\"%1\")]", "shlexec", NULL, NULL, FALSE, "[open(\"%s\")]", 0x0},
 
     /* Test default DDE application */
-    {"", "[open(\"%1\")]", NULL, "dde", NULL, FALSE, "[open(\"%s\")]", 0x0, 33},
+    {"", "[open(\"%1\")]", NULL, "dde", NULL, FALSE, "[open(\"%s\")]", 0x0},
 
-    {NULL, NULL, NULL, NULL, NULL, 0, 0x0, 0}
+    {NULL, NULL, NULL, NULL, NULL, 0, 0x0}
 };
 
-static DWORD ddeInst;
-static HSZ hszTopic;
-static char ddeExec[MAX_PATH], ddeApplication[MAX_PATH];
-static BOOL denyNextConnection;
-
-static HDDEDATA CALLBACK ddeCb(UINT uType, UINT uFmt, HCONV hConv,
-                                HSZ hsz1, HSZ hsz2, HDDEDATA hData,
-                                ULONG_PTR dwData1, ULONG_PTR dwData2)
+static DWORD WINAPI hooked_WaitForInputIdle(HANDLE process, DWORD timeout)
 {
-    DWORD size = 0;
+    HANDLE dde_ready;
+    DWORD wait_result;
 
-    if (winetest_debug > 2)
-        trace("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n",
-              uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2);
+    dde_ready = CreateEventA(NULL, FALSE, FALSE, "winetest_shlexec_dde_ready");
+    wait_result = WaitForSingleObject(dde_ready, timeout);
+    CloseHandle(dde_ready);
 
-    switch (uType)
+    return wait_result;
+}
+
+/*
+ * WaitForInputIdle() will normally return immediately for console apps. That's
+ * a problem for us because ShellExecute will assume that an app is ready to
+ * receive DDE messages after it has called WaitForInputIdle() on that app.
+ * To work around that we install our own version of WaitForInputIdle() that
+ * will wait for the child to explicitly tell us that it is ready. We do that
+ * by changing the entry for WaitForInputIdle() in the shell32 import address
+ * table.
+ */
+static void hook_WaitForInputIdle(void *new_func)
+{
+    char *base;
+    PIMAGE_NT_HEADERS nt_headers;
+    DWORD import_directory_rva;
+    PIMAGE_IMPORT_DESCRIPTOR import_descriptor;
+
+    base = (char *) GetModuleHandleA("shell32.dll");
+    nt_headers = (PIMAGE_NT_HEADERS)(base + ((PIMAGE_DOS_HEADER) base)->e_lfanew);
+    import_directory_rva = nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+
+    /* Search for the correct imported module by walking the import descriptors */
+    import_descriptor = (PIMAGE_IMPORT_DESCRIPTOR)(base + import_directory_rva);
+    while (U(*import_descriptor).OriginalFirstThunk != 0)
     {
-        case XTYP_CONNECT:
-            if (!DdeCmpStringHandles(hsz1, hszTopic))
+        char *import_module_name;
+
+        import_module_name = base + import_descriptor->Name;
+        if (lstrcmpiA(import_module_name, "user32.dll") == 0 ||
+            lstrcmpiA(import_module_name, "user32") == 0)
+        {
+            PIMAGE_THUNK_DATA int_entry;
+            PIMAGE_THUNK_DATA iat_entry;
+
+            /* The import name table and import address table are two parallel
+             * arrays. We need the import name table to find the imported
+             * routine and the import address table to patch the address, so
+             * walk them side by side */
+            int_entry = (PIMAGE_THUNK_DATA)(base + U(*import_descriptor).OriginalFirstThunk);
+            iat_entry = (PIMAGE_THUNK_DATA)(base + import_descriptor->FirstThunk);
+            while (int_entry->u1.Ordinal != 0)
             {
-                if (denyNextConnection)
-                    denyNextConnection = FALSE;
-                else
+                if (! IMAGE_SNAP_BY_ORDINAL(int_entry->u1.Ordinal))
                 {
-                    size = DdeQueryString(ddeInst, hsz2, ddeApplication, MAX_PATH, CP_WINANSI);
-                    assert(size < MAX_PATH);
-                    return (HDDEDATA)TRUE;
+                    PIMAGE_IMPORT_BY_NAME import_by_name;
+                    import_by_name = (PIMAGE_IMPORT_BY_NAME)(base + int_entry->u1.AddressOfData);
+                    if (lstrcmpA((char *) import_by_name->Name, "WaitForInputIdle") == 0)
+                    {
+                        /* Found the correct routine in the correct imported module. Patch it. */
+                        DWORD old_prot;
+                        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);
+                        break;
+                    }
                 }
+                int_entry++;
+                iat_entry++;
             }
-            return (HDDEDATA)FALSE;
-
-        case XTYP_EXECUTE:
-            size = DdeGetData(hData, (LPBYTE)ddeExec, MAX_PATH, 0L);
-            assert(size < MAX_PATH);
-            DdeFreeDataHandle(hData);
-            return (HDDEDATA)DDE_FACK;
+            break;
+        }
 
-        default:
-            return NULL;
+        import_descriptor++;
     }
 }
 
-typedef struct
-{
-    char *filename;
-    DWORD threadIdParent;
-} dde_thread_info_t;
-
-static DWORD CALLBACK ddeThread(LPVOID arg)
-{
-    dde_thread_info_t *info = 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),
-                      0L);
-    ExitThread(0);
-}
-
-/* ShellExecute won't successfully send DDE commands to console applications after starting them,
- * so we run a DDE server in this application, deny the first connection request to make
- * ShellExecute start the application, and then process the next DDE connection in this application
- * to see the execute command that is sent. */
 static void test_dde(void)
 {
     char filename[MAX_PATH], defApplication[MAX_PATH];
-    HSZ hszApplication;
-    dde_thread_info_t info = { filename, GetCurrentThreadId() };
     const dde_tests_t* test;
     char params[1024];
-    DWORD threadId;
-    MSG msg;
     int rc;
+    HANDLE map;
+    char *shared_block;
 
-    ddeInst = 0;
-    rc = DdeInitializeA(&ddeInst, ddeCb, CBF_SKIP_ALLNOTIFICATIONS | CBF_FAIL_ADVISES |
-                        CBF_FAIL_POKES | CBF_FAIL_REQUESTS, 0L);
-    assert(rc == DMLERR_NO_ERROR);
+    hook_WaitForInputIdle((void *) hooked_WaitForInputIdle);
 
     sprintf(filename, "%s\\test file.sde", tmpdir);
 
@@ -1326,6 +1468,10 @@ static void test_dde(void)
     strcpy(defApplication, strrchr(argv0, '\\')+1);
     *strchr(defApplication, '.') = 0;
 
+    map = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
+                             4096, "winetest_shlexec_dde_map");
+    shared_block = MapViewOfFile(map, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 4096);
+
     test = dde_tests;
     while (test->command)
     {
@@ -1336,29 +1482,31 @@ static void test_dde(void)
         }
         create_test_verb_dde(".sde", "Open", 0, test->command, test->ddeexec,
                              test->application, test->topic, test->ifexec);
-        hszApplication = DdeCreateStringHandleA(ddeInst, test->application ?
-                                                test->application : defApplication, CP_WINANSI);
-        hszTopic = DdeCreateStringHandleA(ddeInst, test->topic ? test->topic : SZDDESYS_TOPIC,
-                                          CP_WINANSI);
-        assert(hszApplication && hszTopic);
-        assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_REGISTER));
-        denyNextConnection = TRUE;
+
+        if (test->application != NULL || test->topic != NULL)
+        {
+            strcpy(shared_block, test->application ? test->application : defApplication);
+            strcpy(shared_block + strlen(shared_block) + 1, test->topic ? test->topic : SZDDESYS_TOPIC);
+        }
+        else
+        {
+            shared_block[0] = '\0';
+            shared_block[1] = '\0';
+        }
         ddeExec[0] = 0;
 
-        assert(CreateThread(NULL, 0, ddeThread, &info, 0, &threadId));
-        while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg);
-        rc = msg.wParam > 32 ? 33 : msg.wParam;
+        rc = shell_execute_ex(SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI, NULL, filename, NULL, NULL);
         if ((test->todo & 0x1)==0)
         {
-            ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
+            ok(32 < rc, "%s failed: rc=%d err=%d\n", shell_call,
                rc, GetLastError());
         }
         else todo_wine
         {
-            ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
+            ok(32 < rc, "%s failed: rc=%d err=%d\n", shell_call,
                rc, GetLastError());
         }
-        if (rc == 33)
+        if (32 < rc)
         {
             if ((test->todo & 0x2)==0)
             {
@@ -1382,25 +1530,22 @@ static void test_dde(void)
             if ((test->todo & 0x8) == 0)
             {
                 sprintf(params, test->expectedDdeExec, filename);
-                ok(StrCmpPath(params, ddeExec) == 0,
-                   "ddeexec expected '%s', got '%s'\n", params, ddeExec);
+                okChildPath("ddeExec", params);
             }
             else todo_wine
             {
                 sprintf(params, test->expectedDdeExec, filename);
-                ok(StrCmpPath(params, ddeExec) == 0,
-                   "ddeexec expected '%s', got '%s'\n", params, ddeExec);
+                okChildPath("ddeExec", params);
             }
         }
 
-        assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_UNREGISTER));
-        assert(DdeFreeStringHandle(ddeInst, hszTopic));
-        assert(DdeFreeStringHandle(ddeInst, hszApplication));
         delete_test_association(".sde");
         test++;
     }
 
-    assert(DdeUninitialize(ddeInst));
+    UnmapViewOfFile(shared_block);
+    CloseHandle(map);
+    hook_WaitForInputIdle((void *) WaitForInputIdle);
 }
 
 #define DDE_DEFAULT_APP_VARIANTS 2
@@ -1454,6 +1599,23 @@ static dde_default_app_tests_t dde_default_app_tests[] =
     {NULL, {NULL}, 0, {0}}
 };
 
+typedef struct
+{
+    char *filename;
+    DWORD threadIdParent;
+} dde_thread_info_t;
+
+static DWORD CALLBACK ddeThread(LPVOID arg)
+{
+    dde_thread_info_t *info = 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),
+                      0L);
+    ExitThread(0);
+}
+
 static void test_dde_default_app(void)
 {
     char filename[MAX_PATH];
@@ -1465,6 +1627,7 @@ static void test_dde_default_app(void)
     MSG msg;
     int rc, which = 0;
 
+    post_quit_on_execute = FALSE;
     ddeInst = 0;
     rc = DdeInitializeA(&ddeInst, ddeCb, CBF_SKIP_ALLNOTIFICATIONS | CBF_FAIL_ADVISES |
                         CBF_FAIL_POKES | CBF_FAIL_REQUESTS, 0L);
@@ -1491,7 +1654,6 @@ static void test_dde_default_app(void)
         sprintf(params, test->command, tmpdir);
         create_test_verb_dde(".sde", "Open", 1, params, "[test]", NULL,
                              "shlexec", NULL);
-        denyNextConnection = FALSE;
         ddeApplication[0] = 0;
 
         /* No application will be run as we will respond to the first DDE event,
index 3c126ed..33d1a94 100644 (file)
@@ -48,8 +48,8 @@
        "Expected %d, got %d\n", ret, retval)
 
 static CHAR CURR_DIR[MAX_PATH];
-static const WCHAR UNICODE_PATH[] = {'c',':','\\',0x00c4,'\0','\0'};
-    /* "c:\Ä", or "c:\A" with diaeresis */
+static const WCHAR UNICODE_PATH[] = {'c',':','\\',0x00ae,'\0','\0'};
+    /* "c:\®" can be used in all codepages */
     /* Double-null termination needed for pFrom field of SHFILEOPSTRUCT */
 
 static HMODULE hshell32;
@@ -207,11 +207,11 @@ static void test_get_file_info(void)
         memset(&shfiw, 0xcf, sizeof(shfiw));
         memset(&unset_icon, 0xcf, sizeof(unset_icon));
         rc=pSHGetFileInfoW(NULL, 0, &shfiw, sizeof(shfiw), 0);
-        todo_wine ok(!rc, "SHGetFileInfoW(NULL | 0) should fail\n");
+        ok(!rc, "SHGetFileInfoW(NULL | 0) should fail\n");
         ok(shfiw.hIcon == unset_icon, "SHGetFileInfoW(NULL | 0) should not clear hIcon\n");
-        todo_wine ok(shfiw.szDisplayName[0] == 0xcfcf, "SHGetFileInfoW(NULL | 0) should not clear szDisplayName[0]\n");
-        todo_wine ok(shfiw.szTypeName[0] == 0xcfcf, "SHGetFileInfoW(NULL | 0) should not clear szTypeName[0]\n");
-        todo_wine ok(shfiw.iIcon == 0xcfcfcfcf, "SHGetFileInfoW(NULL | 0) should not clear iIcon\n");
+        ok(shfiw.szDisplayName[0] == 0xcfcf, "SHGetFileInfoW(NULL | 0) should not clear szDisplayName[0]\n");
+        ok(shfiw.szTypeName[0] == 0xcfcf, "SHGetFileInfoW(NULL | 0) should not clear szTypeName[0]\n");
+        ok(shfiw.iIcon == 0xcfcfcfcf, "SHGetFileInfoW(NULL | 0) should not clear iIcon\n");
         ok(shfiw.dwAttributes == 0xcfcfcfcf, "SHGetFileInfoW(NULL | 0) should not clear dwAttributes\n");
     }
     else
@@ -1042,10 +1042,13 @@ static void test_copy(void)
     shfo.pTo = "testdir2\\a.txt\0testdir2\\b.txt\0testdir2\\c.txt\0";
     shfo.fAnyOperationsAborted = FALSE;
     retval = SHFileOperation(&shfo);
-    ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+    ok(retval == ERROR_SUCCESS ||
+       broken(retval == 0x100a1), /* WinMe */
+       "Expected ERROR_SUCCESS, got %d\n", retval);
     ok(DeleteFile("testdir2\\a.txt"), "Expected testdir2\\a.txt to exist\n");
     ok(DeleteFile("testdir2\\b.txt"), "Expected testdir2\\b.txt to exist\n");
-    ok(RemoveDirectory("testdir2\\c.txt"), "Expected testdir2\\c.txt to exist\n");
+    if (retval == ERROR_SUCCESS)
+        ok(RemoveDirectory("testdir2\\c.txt"), "Expected testdir2\\c.txt to exist\n");
 
     /* try many dest files without FOF_MULTIDESTFILES flag */
     shfo.pFrom = "test1.txt\0test2.txt\0test3.txt\0";
@@ -1073,7 +1076,9 @@ static void test_copy(void)
     shfo.pTo = "testdir2\0";
     shfo.fFlags &= ~FOF_MULTIDESTFILES;
     retval = SHFileOperation(&shfo);
-    ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+    ok(retval == ERROR_SUCCESS ||
+       broken(retval == 0x100a1), /* WinMe */
+       "Expected ERROR_SUCCESS, got %d\n", retval);
     ok(file_exists("testdir2\\test1.txt"), "Expected testdir2\\test1.txt to exist\n");
 
     /* try a glob with FOF_FILESONLY */
@@ -1190,18 +1195,28 @@ static void test_copy(void)
     shfo.fFlags &= ~FOF_MULTIDESTFILES;
     shfo.fAnyOperationsAborted = FALSE;
     retval = SHFileOperation(&shfo);
-    ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
-    ok(DeleteFile("testdir2\\test1.txt"), "Expected testdir2\\test1.txt to exist\n");
-    ok(DeleteFile("testdir2\\test4.txt\\a.txt"), "Expected a.txt to exist\n");
-    ok(RemoveDirectory("testdir2\\test4.txt"), "Expected testdir2\\test4.txt to exist\n");
+    ok(retval == ERROR_SUCCESS ||
+       broken(retval == 0x100a1), /* WinMe */
+       "Expected ERROR_SUCCESS, got %d\n", retval);
+    if (retval == ERROR_SUCCESS)
+    {
+        ok(DeleteFile("testdir2\\test1.txt"), "Expected testdir2\\test1.txt to exist\n");
+        ok(DeleteFile("testdir2\\test4.txt\\a.txt"), "Expected a.txt to exist\n");
+        ok(RemoveDirectory("testdir2\\test4.txt"), "Expected testdir2\\test4.txt to exist\n");
+    }
 
     /* copy one directory and a file in that dir to another dir */
     shfo.pFrom = "test4.txt\0test4.txt\\a.txt\0";
     shfo.pTo = "testdir2\0";
     retval = SHFileOperation(&shfo);
-    ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
-    ok(DeleteFile("testdir2\\test4.txt\\a.txt"), "Expected a.txt to exist\n");
-    ok(DeleteFile("testdir2\\a.txt"), "Expected testdir2\\a.txt to exist\n");
+    ok(retval == ERROR_SUCCESS ||
+       broken(retval == 0x100a1), /* WinMe */
+       "Expected ERROR_SUCCESS, got %d\n", retval);
+    if (retval == ERROR_SUCCESS)
+    {
+        ok(DeleteFile("testdir2\\test4.txt\\a.txt"), "Expected a.txt to exist\n");
+        ok(DeleteFile("testdir2\\a.txt"), "Expected testdir2\\a.txt to exist\n");
+    }
 
     /* copy a file in a directory first, and then the directory to a nonexistent dir */
     shfo.pFrom = "test4.txt\\a.txt\0test4.txt\0";
@@ -1362,13 +1377,21 @@ static void test_copy(void)
     createTestFile("test4.txt\\test1.txt");
     shfo.pFrom = "test4.txt\0";
     shfo.pTo = "testdir2\0";
-    shfo.fFlags = FOF_NOCONFIRMATION;
-    ok(!SHFileOperation(&shfo), "First SHFileOperation failed\n");
-    createTestFile("test4.txt\\.\\test1.txt"); /* modify the content of the file */
-    /* without FOF_NOCONFIRMATION the confirmation is "This folder already contains a folder named ..." */
+    /* WinMe needs FOF_NOERRORUI */
+    shfo.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI;
     retval = SHFileOperation(&shfo);
-    ok(retval == 0, "Expected 0, got %d\n", retval);
-    ok(file_has_content("testdir2\\test4.txt\\test1.txt", "test4.txt\\.\\test1.txt\n"), "The file was not copied\n");
+    ok(retval == ERROR_SUCCESS ||
+       broken(retval == 0x100a1), /* WinMe */
+       "Expected ERROR_SUCCESS, got %d\n", retval);
+    shfo.fFlags = FOF_NOCONFIRMATION;
+    if (ERROR_SUCCESS)
+    {
+        createTestFile("test4.txt\\.\\test1.txt"); /* modify the content of the file */
+        /* without FOF_NOCONFIRMATION the confirmation is "This folder already contains a folder named ..." */
+        retval = SHFileOperation(&shfo);
+        ok(retval == 0, "Expected 0, got %d\n", retval);
+        ok(file_has_content("testdir2\\test4.txt\\test1.txt", "test4.txt\\.\\test1.txt\n"), "The file was not copied\n");
+    }
 
     createTestFile("one.txt");
 
@@ -1955,6 +1978,7 @@ static void test_sh_path_prepare(void)
 {
     HRESULT res;
     CHAR path[MAX_PATH];
+    CHAR UNICODE_PATH_A[MAX_PATH];
 
     if(!pSHPathPrepareForWriteA)
     {
@@ -1987,12 +2011,14 @@ static void test_sh_path_prepare(void)
     set_curr_dir_path(path, "test1.txt\0");
     res = pSHPathPrepareForWriteA(0, 0, path, SHPPFW_NONE);
     ok(res == HRESULT_FROM_WIN32(ERROR_DIRECTORY) ||
+       res == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) || /* WinMe */
        res == HRESULT_FROM_WIN32(ERROR_INVALID_NAME), /* Vista */
        "Unexpected result : 0x%08x\n", res);
 
     /* file exists, SHPPFW_DIRCREATE */
     res = pSHPathPrepareForWriteA(0, 0, path, SHPPFW_DIRCREATE);
     ok(res == HRESULT_FROM_WIN32(ERROR_DIRECTORY) ||
+       res == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) || /* WinMe */
        res == HRESULT_FROM_WIN32(ERROR_INVALID_NAME), /* Vista */
        "Unexpected result : 0x%08x\n", res);
 
@@ -2000,6 +2026,7 @@ static void test_sh_path_prepare(void)
     set_curr_dir_path(path, "test1.txt\\\0");
     res = pSHPathPrepareForWriteA(0, 0, path, SHPPFW_NONE);
     ok(res == HRESULT_FROM_WIN32(ERROR_DIRECTORY) ||
+       res == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) || /* WinMe */
        res == HRESULT_FROM_WIN32(ERROR_INVALID_NAME), /* Vista */
        "Unexpected result : 0x%08x\n", res);
 
@@ -2040,19 +2067,22 @@ static void test_sh_path_prepare(void)
 
     if(!pSHPathPrepareForWriteW)
     {
-        skip("Skipping SHPathPrepareForWriteW tests\n");
+        win_skip("Skipping SHPathPrepareForWriteW tests\n");
         return;
     }
+    WideCharToMultiByte(CP_ACP, 0, UNICODE_PATH, -1, UNICODE_PATH_A, sizeof(UNICODE_PATH_A), NULL, NULL);
+
     /* unicode directory doesn't exist, SHPPFW_NONE */
+    RemoveDirectoryA(UNICODE_PATH_A);
     res = pSHPathPrepareForWriteW(0, 0, UNICODE_PATH, SHPPFW_NONE);
     ok(res == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), "res == %08x, expected HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)\n", res);
-    ok(!file_existsW(UNICODE_PATH), "unicode path was created but shouldn't be\n");
-    RemoveDirectoryW(UNICODE_PATH);
+    ok(!file_exists(UNICODE_PATH_A), "unicode path was created but shouldn't be\n");
+    RemoveDirectoryA(UNICODE_PATH_A);
 
     /* unicode directory doesn't exist, SHPPFW_DIRCREATE */
     res = pSHPathPrepareForWriteW(0, 0, UNICODE_PATH, SHPPFW_DIRCREATE);
     ok(res == S_OK, "res == %08x, expected S_OK\n", res);
-    ok(file_existsW(UNICODE_PATH), "unicode path should've been created\n");
+    ok(file_exists(UNICODE_PATH_A), "unicode path should've been created\n");
 
     /* unicode directory exists, SHPPFW_NONE */
     res = pSHPathPrepareForWriteW(0, 0, UNICODE_PATH, SHPPFW_NONE);
@@ -2061,7 +2091,7 @@ static void test_sh_path_prepare(void)
     /* unicode directory exists, SHPPFW_DIRCREATE */
     res = pSHPathPrepareForWriteW(0, 0, UNICODE_PATH, SHPPFW_DIRCREATE);
     ok(res == S_OK, "ret == %08x, expected S_OK\n", res);
-    RemoveDirectoryW(UNICODE_PATH);
+    RemoveDirectoryA(UNICODE_PATH_A);
 }
 
 static void test_sh_new_link_info(void)