Update shell32 tests
authorHervé Poussineau <hpoussin@reactos.org>
Mon, 27 Aug 2007 07:03:32 +0000 (07:03 +0000)
committerHervé Poussineau <hpoussin@reactos.org>
Mon, 27 Aug 2007 07:03:32 +0000 (07:03 +0000)
svn path=/trunk/; revision=28593

rostests/winetests/shell32/generated.c
rostests/winetests/shell32/shell32.rbuild
rostests/winetests/shell32/shell32_test.h
rostests/winetests/shell32/shelllink.c
rostests/winetests/shell32/shlexec.c
rostests/winetests/shell32/shlfileop.c
rostests/winetests/shell32/shlfolder.c
rostests/winetests/shell32/string.c
rostests/winetests/shell32/systray.c [new file with mode: 0644]
rostests/winetests/shell32/testlist.c

index dee7638..b424065 100755 (executable)
@@ -201,14 +201,16 @@ static void test_pack_COAUTHINFO(void)
     TEST_FIELD(COAUTHINFO, DWORD, dwCapabilities, 24, 4, 4);
 }
 
-static void test_pack_COSERVERINFO(void)
+static void test_pack_DATE(void)
 {
-    /* COSERVERINFO (pack 4) */
-    TEST_TYPE(COSERVERINFO, 16, 4);
-    TEST_FIELD(COSERVERINFO, DWORD, dwReserved1, 0, 4, 4);
-    TEST_FIELD(COSERVERINFO, LPWSTR, pwszName, 4, 4, 4);
-    TEST_FIELD(COSERVERINFO, COAUTHINFO *, pAuthInfo, 8, 4, 4);
-    TEST_FIELD(COSERVERINFO, DWORD, dwReserved2, 12, 4, 4);
+    /* DATE */
+    TEST_TYPE(DATE, 8, 8);
+}
+
+static void test_pack_DOUBLE(void)
+{
+    /* DOUBLE */
+    TEST_TYPE(DOUBLE, 8, 8);
 }
 
 static void test_pack_DWORD_SIZEDARR(void)
@@ -255,7 +257,6 @@ static void test_pack_LPBLOB(void)
 {
     /* LPBLOB */
     TEST_TYPE(LPBLOB, 4, 4);
-    TEST_TYPE_POINTER(LPBLOB, 8, 4);
 }
 
 static void test_pack_LPBSTR(void)
@@ -269,7 +270,6 @@ static void test_pack_LPBSTRBLOB(void)
 {
     /* LPBSTRBLOB */
     TEST_TYPE(LPBSTRBLOB, 4, 4);
-    TEST_TYPE_POINTER(LPBSTRBLOB, 8, 4);
 }
 
 static void test_pack_LPCOLESTR(void)
@@ -835,7 +835,6 @@ static void test_pack_LPITEMIDLIST(void)
 {
     /* LPITEMIDLIST */
     TEST_TYPE(LPITEMIDLIST, 4, 4);
-    TEST_TYPE_POINTER(LPITEMIDLIST, 3, 1);
 }
 
 static void test_pack_LPSHELLDETAILS(void)
@@ -848,7 +847,6 @@ static void test_pack_LPSHITEMID(void)
 {
     /* LPSHITEMID */
     TEST_TYPE(LPSHITEMID, 4, 4);
-    TEST_TYPE_POINTER(LPSHITEMID, 3, 1);
 }
 
 static void test_pack_LPSTRRET(void)
@@ -1011,26 +1009,6 @@ static void test_pack_FILEGROUPDESCRIPTORW(void)
     TEST_FIELD(FILEGROUPDESCRIPTORW, FILEDESCRIPTORW[1], fgd, 4, 592, 1);
 }
 
-static void test_pack_IFileSystemBindData(void)
-{
-    /* IFileSystemBindData */
-}
-
-static void test_pack_IFileSystemBindDataVtbl(void)
-{
-    /* IFileSystemBindDataVtbl */
-}
-
-static void test_pack_IShellChangeNotify(void)
-{
-    /* IShellChangeNotify */
-}
-
-static void test_pack_IShellIcon(void)
-{
-    /* IShellIcon */
-}
-
 static void test_pack_LPBROWSEINFOA(void)
 {
     /* LPBROWSEINFOA */
@@ -1282,8 +1260,9 @@ static void test_pack(void)
     test_pack_CLSID();
     test_pack_COAUTHIDENTITY();
     test_pack_COAUTHINFO();
-    test_pack_COSERVERINFO();
     test_pack_CSFV();
+    test_pack_DATE();
+    test_pack_DOUBLE();
     test_pack_DRAGINFOA();
     test_pack_DRAGINFOW();
     test_pack_DROPFILES();
@@ -1299,11 +1278,7 @@ static void test_pack(void)
     test_pack_GUID();
     test_pack_HMETAFILEPICT();
     test_pack_HYPER_SIZEDARR();
-    test_pack_IFileSystemBindData();
-    test_pack_IFileSystemBindDataVtbl();
     test_pack_IID();
-    test_pack_IShellChangeNotify();
-    test_pack_IShellIcon();
     test_pack_ITEMIDLIST();
     test_pack_LPBLOB();
     test_pack_LPBROWSEINFOA();
index a4c85e1..60962d3 100644 (file)
@@ -1,17 +1,27 @@
 <module name="shell32_winetest" type="win32cui" installbase="bin" installname="shell32_winetest.exe" allowwarnings="true">
-    <include base="shell32_winetest">.</include>
-    <define name="__USE_W32API" />
-    <library>ntdll</library>
-    <library>shell32</library>
-    <library>kernel32</library>
-    <library>advapi32</library>
-    <library>shlwapi</library>
-    <library>ole32</library>
-    <file>shelllink.c</file>
-    <file>shellpath.c</file>
-    <file>shlexec.c</file>
-    <file>shlfileop.c</file>
-    <file>shlfolder.c</file>
-    <file>string.c</file>
-    <file>testlist.c</file>
+       <include base="shell32_winetest">.</include>
+       <define name="__USE_W32API" />
+       <define name="_WIN32_IE">0x600</define>
+       <define name="_WIN32_WINNT">0x501</define>
+       <define name="WINVER">0x501</define>
+       <library>wine</library>
+       <library>shell32</library>
+       <library>ole32</library>
+       <library>oleaut32</library>
+       <library>shlwapi</library>
+       <library>user32</library>
+       <library>gdi32</library>
+       <library>advapi32</library>
+       <library>kernel32</library>
+       <library>uuid</library>
+       <library>ntdll</library>
+       <file>generated.c</file>
+       <file>shelllink.c</file>
+       <file>shellpath.c</file>
+       <file>shlexec.c</file>
+       <file>shlfileop.c</file>
+       <file>shlfolder.c</file>
+       <file>string.c</file>
+       <file>systray.c</file>
+       <file>testlist.c</file>
 </module>
index fc37789..64a2be1 100755 (executable)
@@ -15,7 +15,7 @@
  *
  * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 
index 88924e8..091522b 100755 (executable)
  *
  * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 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 filesytem path or a LPITEMIDLIST (shell
  * namespace) path for a given folder (CSIDL value).
  *
  */
 
-#define _WIN32_IE 0x0400
-
 #define COBJMACROS
 
-#include <stdarg.h>
-#include <stdio.h>
-#include "windef.h"
-#include "winbase.h"
-#include "basetyps.h"
+#include <windows.h>
 #include "shlguid.h"
-//#include "wine/shobjidl.h"
+#include "shobjidl.h"
 #include "shlobj.h"
 #include "wine/test.h"
 
 #include "shell32_test.h"
 
-extern BOOL WINAPI ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
-extern HRESULT WINAPI SHILCreateFromPath(LPCWSTR path, LPITEMIDLIST * ppidl, DWORD * attributes);
-extern void WINAPI ILFree(LPITEMIDLIST pidl);
+
+typedef void (WINAPI *fnILFree)(LPITEMIDLIST);
+typedef BOOL (WINAPI *fnILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST);
+typedef HRESULT (WINAPI *fnSHILCreateFromPath)(LPCWSTR, LPITEMIDLIST *,DWORD*);
+
+static fnILFree pILFree;
+static fnILIsEqual pILIsEqual;
+static fnSHILCreateFromPath pSHILCreateFromPath;
+
+static DWORD (WINAPI *pGetLongPathNameA)(LPCSTR, LPSTR, DWORD);
+
+static const GUID _IID_IShellLinkDataList = {
+    0x45e2b4ae, 0xb1c3, 0x11d0,
+    { 0xb9, 0x2f, 0x00, 0xa0, 0xc9, 0x03, 0x12, 0xe1 }
+};
 
 static const WCHAR lnkfile[]= { 'C',':','\\','t','e','s','t','.','l','n','k',0 };
 static const WCHAR notafile[]= { 'C',':','\\','n','o','n','e','x','i','s','t','e','n','t','\\','f','i','l','e',0 };
 
-#if 0 // FIXME: needed to build. Please update shell32 winetest.
-const GUID IID_IPersistFile = { 0x0000010b, 0x0000, 0x0000, { 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46 } };
-#endif
 
 /* For some reason SHILCreateFromPath does not work on Win98 and
  * SHSimpleIDListFromPathA does not work on NT4. But if we call both we
  * get what we want on all platforms.
  */
-static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathA)(LPCSTR)=NULL;
+static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathAW)(LPCVOID);
 
 static LPITEMIDLIST path_to_pidl(const char* path)
 {
     LPITEMIDLIST pidl;
 
-    if (!pSHSimpleIDListFromPathA)
+    if (!pSHSimpleIDListFromPathAW)
     {
-        HMODULE hdll=LoadLibraryA("shell32.dll");
-        pSHSimpleIDListFromPathA=(void*)GetProcAddress(hdll, (char*)162);
-        if (!pSHSimpleIDListFromPathA)
-            trace("SHSimpleIDListFromPathA not found in shell32.dll\n");
+        HMODULE hdll=GetModuleHandleA("shell32.dll");
+        pSHSimpleIDListFromPathAW=(void*)GetProcAddress(hdll, (char*)162);
+        if (!pSHSimpleIDListFromPathAW)
+            trace("SHSimpleIDListFromPathAW not found in shell32.dll\n");
     }
 
     pidl=NULL;
-    if (pSHSimpleIDListFromPathA)
-        pidl=pSHSimpleIDListFromPathA(path);
+    /* pSHSimpleIDListFromPathAW maps to A on non NT platforms */
+    if (pSHSimpleIDListFromPathAW && (GetVersion() & 0x80000000))
+        pidl=pSHSimpleIDListFromPathAW(path);
 
     if (!pidl)
     {
@@ -81,9 +85,9 @@ static LPITEMIDLIST path_to_pidl(const char* path)
         pathW=HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
         MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, len);
 
-        r=SHILCreateFromPath(pathW, &pidl, NULL);
+        r=pSHILCreateFromPath(pathW, &pidl, NULL);
         todo_wine {
-        ok(SUCCEEDED(r), "SHILCreateFromPath failed (0x%08lx)\n", r);
+        ok(SUCCEEDED(r), "SHILCreateFromPath failed (0x%08x)\n", r);
         }
         HeapFree(GetProcessHeap(), 0, pathW);
     }
@@ -108,71 +112,72 @@ static void test_get_set(void)
 
     r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
                          &IID_IShellLinkA, (LPVOID*)&sl);
-    ok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08x)\n", r);
     if (!SUCCEEDED(r))
         return;
 
     /* Test Getting / Setting the description */
     strcpy(buffer,"garbage");
     r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
-    ok(SUCCEEDED(r), "GetDescription failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetDescription failed (0x%08x)\n", r);
     ok(*buffer=='\0', "GetDescription returned '%s'\n", buffer);
 
     str="Some description";
     r = IShellLinkA_SetDescription(sl, str);
-    ok(SUCCEEDED(r), "SetDescription failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "SetDescription failed (0x%08x)\n", r);
 
     strcpy(buffer,"garbage");
     r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
-    ok(SUCCEEDED(r), "GetDescription failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetDescription failed (0x%08x)\n", r);
     ok(lstrcmp(buffer,str)==0, "GetDescription returned '%s'\n", buffer);
 
     /* Test Getting / Setting the work directory */
     strcpy(buffer,"garbage");
     r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
-    ok(SUCCEEDED(r), "GetWorkingDirectory failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetWorkingDirectory failed (0x%08x)\n", r);
     ok(*buffer=='\0', "GetWorkingDirectory returned '%s'\n", buffer);
 
     str="c:\\nonexistent\\directory";
     r = IShellLinkA_SetWorkingDirectory(sl, str);
-    ok(SUCCEEDED(r), "SetWorkingDirectory failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "SetWorkingDirectory failed (0x%08x)\n", r);
 
     strcpy(buffer,"garbage");
     r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
-    ok(SUCCEEDED(r), "GetWorkingDirectory failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetWorkingDirectory failed (0x%08x)\n", r);
     ok(lstrcmpi(buffer,str)==0, "GetWorkingDirectory returned '%s'\n", buffer);
 
     /* Test Getting / Setting the work directory */
     strcpy(buffer,"garbage");
     r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
-    ok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetPath failed (0x%08x)\n", r);
     ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
 
     r = IShellLinkA_SetPath(sl, "");
-    ok(r==S_OK, "SetPath failed (0x%08lx)\n", r);
+    ok(r==S_OK, "SetPath failed (0x%08x)\n", r);
 
     strcpy(buffer,"garbage");
     r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
-    ok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetPath failed (0x%08x)\n", r);
     ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
 
+    /* Win98 returns S_FALSE, but WinXP returns S_OK */
     str="c:\\nonexistent\\file";
     r = IShellLinkA_SetPath(sl, str);
-    ok(r==S_FALSE, "SetPath failed (0x%08lx)\n", r);
+    ok(r==S_FALSE || r==S_OK, "SetPath failed (0x%08x)\n", r);
 
     strcpy(buffer,"garbage");
     r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
-    ok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetPath failed (0x%08x)\n", r);
     ok(lstrcmpi(buffer,str)==0, "GetPath returned '%s'\n", buffer);
 
     /* Get some a real path to play with */
     r=GetModuleFileName(NULL, mypath, sizeof(mypath));
-    ok(r>=0 && r<sizeof(mypath), "GetModuleFileName failed (%ld)\n", r);
+    ok(r>=0 && r<sizeof(mypath), "GetModuleFileName failed (%d)\n", r);
 
     /* Test the interaction of SetPath and SetIDList */
     tmp_pidl=NULL;
     r = IShellLinkA_GetIDList(sl, &tmp_pidl);
-    ok(SUCCEEDED(r), "GetIDList failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetIDList failed (0x%08x)\n", r);
     if (SUCCEEDED(r))
     {
         strcpy(buffer,"garbage");
@@ -192,50 +197,73 @@ static void test_get_set(void)
     if (pidl)
     {
         r = IShellLinkA_SetIDList(sl, pidl);
-        ok(SUCCEEDED(r), "SetIDList failed (0x%08lx)\n", r);
+        ok(SUCCEEDED(r), "SetIDList failed (0x%08x)\n", r);
 
         tmp_pidl=NULL;
         r = IShellLinkA_GetIDList(sl, &tmp_pidl);
-        ok(SUCCEEDED(r), "GetIDList failed (0x%08lx)\n", r);
-        ok(tmp_pidl && ILIsEqual(pidl, tmp_pidl),
+        ok(SUCCEEDED(r), "GetIDList failed (0x%08x)\n", r);
+        ok(tmp_pidl && pILIsEqual(pidl, tmp_pidl),
            "GetIDList returned an incorrect pidl\n");
 
         /* tmp_pidl is owned by IShellLink so we don't free it */
-        ILFree(pidl);
+        pILFree(pidl);
 
         strcpy(buffer,"garbage");
         r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
-        ok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
+        ok(SUCCEEDED(r), "GetPath failed (0x%08x)\n", r);
         ok(lstrcmpi(buffer, mypath)==0, "GetPath returned '%s'\n", buffer);
     }
 
+    /* test path with quotes (Win98 IShellLinkA_SetPath returns S_FALSE, WinXP returns S_OK) */
+    r = IShellLinkA_SetPath(sl, "\"c:\\nonexistent\\file\"");
+    ok(r==S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
+
+    r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
+    ok(r==S_OK, "GetPath failed (0x%08x)\n", r);
+    ok(!lstrcmp(buffer, "C:\\nonexistent\\file"), "case doesn't match\n");
+
+    r = IShellLinkA_SetPath(sl, "\"c:\\foo");
+    ok(r==S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
+
+    r = IShellLinkA_SetPath(sl, "\"\"c:\\foo");
+    ok(r==S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
+
+    r = IShellLinkA_SetPath(sl, "c:\\foo\"");
+    ok(r==S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
+
+    r = IShellLinkA_SetPath(sl, "\"\"c:\\foo\"");
+    ok(r==S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
+
+    r = IShellLinkA_SetPath(sl, "\"\"c:\\foo\"\"");
+    ok(r==S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
+
     /* Test Getting / Setting the arguments */
     strcpy(buffer,"garbage");
     r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
-    ok(SUCCEEDED(r), "GetArguments failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetArguments failed (0x%08x)\n", r);
     ok(*buffer=='\0', "GetArguments returned '%s'\n", buffer);
 
     str="param1 \"spaced param2\"";
     r = IShellLinkA_SetArguments(sl, str);
-    ok(SUCCEEDED(r), "SetArguments failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "SetArguments failed (0x%08x)\n", r);
 
     strcpy(buffer,"garbage");
     r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
-    ok(SUCCEEDED(r), "GetArguments failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetArguments failed (0x%08x)\n", r);
     ok(lstrcmp(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
 
     /* Test Getting / Setting showcmd */
     i=0xdeadbeef;
     r = IShellLinkA_GetShowCmd(sl, &i);
-    ok(SUCCEEDED(r), "GetShowCmd failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetShowCmd failed (0x%08x)\n", r);
     ok(i==SW_SHOWNORMAL, "GetShowCmd returned %d\n", i);
 
     r = IShellLinkA_SetShowCmd(sl, SW_SHOWMAXIMIZED);
-    ok(SUCCEEDED(r), "SetShowCmd failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "SetShowCmd failed (0x%08x)\n", r);
 
     i=0xdeadbeef;
     r = IShellLinkA_GetShowCmd(sl, &i);
-    ok(SUCCEEDED(r), "GetShowCmd failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetShowCmd failed (0x%08x)\n", r);
     ok(i==SW_SHOWMAXIMIZED, "GetShowCmd returned %d'\n", i);
 
     /* Test Getting / Setting the icon */
@@ -243,33 +271,33 @@ static void test_get_set(void)
     strcpy(buffer,"garbage");
     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
     todo_wine {
-    ok(SUCCEEDED(r), "GetIconLocation failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetIconLocation failed (0x%08x)\n", r);
     }
     ok(*buffer=='\0', "GetIconLocation returned '%s'\n", buffer);
     ok(i==0, "GetIconLocation returned %d\n", i);
 
     str="c:\\nonexistent\\file";
     r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
-    ok(SUCCEEDED(r), "SetIconLocation failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "SetIconLocation failed (0x%08x)\n", r);
 
     i=0xdeadbeef;
     r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
-    ok(SUCCEEDED(r), "GetIconLocation failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetIconLocation failed (0x%08x)\n", r);
     ok(lstrcmpi(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
     ok(i==0xbabecafe, "GetIconLocation returned %d'\n", i);
 
     /* Test Getting / Setting the hot key */
     w=0xbeef;
     r = IShellLinkA_GetHotkey(sl, &w);
-    ok(SUCCEEDED(r), "GetHotkey failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetHotkey failed (0x%08x)\n", r);
     ok(w==0, "GetHotkey returned %d\n", w);
 
     r = IShellLinkA_SetHotkey(sl, 0x5678);
-    ok(SUCCEEDED(r), "SetHotkey failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "SetHotkey failed (0x%08x)\n", r);
 
     w=0xbeef;
     r = IShellLinkA_GetHotkey(sl, &w);
-    ok(SUCCEEDED(r), "GetHotkey failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "GetHotkey failed (0x%08x)\n", r);
     ok(w==0x5678, "GetHotkey returned %d'\n", w);
 
     IShellLinkA_Release(sl);
@@ -281,7 +309,13 @@ static void test_get_set(void)
  */
 
 #define lok                   ok_(__FILE__, line)
-#define check_lnk(a,b)        check_lnk_(__LINE__, (a), (b))
+#define lok_todo_4(todo_flag,a,b,c,d) \
+    if ((todo & todo_flag) == 0) lok((a), (b), (c), (d)); \
+    else todo_wine lok((a), (b), (c), (d));
+#define lok_todo_2(todo_flag,a,b) \
+    if ((todo & todo_flag) == 0) lok((a), (b)); \
+    else todo_wine lok((a), (b));
+#define check_lnk(a,b,c)        check_lnk_(__LINE__, (a), (b), (c))
 
 void create_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int save_fails)
 {
@@ -291,65 +325,65 @@ void create_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int save_fails)
 
     r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
                          &IID_IShellLinkA, (LPVOID*)&sl);
-    lok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08lx)\n", r);
+    lok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08x)\n", r);
     if (!SUCCEEDED(r))
         return;
 
     if (desc->description)
     {
         r = IShellLinkA_SetDescription(sl, desc->description);
-        lok(SUCCEEDED(r), "SetDescription failed (0x%08lx)\n", r);
+        lok(SUCCEEDED(r), "SetDescription failed (0x%08x)\n", r);
     }
     if (desc->workdir)
     {
         r = IShellLinkA_SetWorkingDirectory(sl, desc->workdir);
-        lok(SUCCEEDED(r), "SetWorkingDirectory failed (0x%08lx)\n", r);
+        lok(SUCCEEDED(r), "SetWorkingDirectory failed (0x%08x)\n", r);
     }
     if (desc->path)
     {
         r = IShellLinkA_SetPath(sl, desc->path);
-        lok(SUCCEEDED(r), "SetPath failed (0x%08lx)\n", r);
+        lok(SUCCEEDED(r), "SetPath failed (0x%08x)\n", r);
     }
     if (desc->pidl)
     {
         r = IShellLinkA_SetIDList(sl, desc->pidl);
-        lok(SUCCEEDED(r), "SetIDList failed (0x%08lx)\n", r);
+        lok(SUCCEEDED(r), "SetIDList failed (0x%08x)\n", r);
     }
     if (desc->arguments)
     {
         r = IShellLinkA_SetArguments(sl, desc->arguments);
-        lok(SUCCEEDED(r), "SetArguments failed (0x%08lx)\n", r);
+        lok(SUCCEEDED(r), "SetArguments failed (0x%08x)\n", r);
     }
     if (desc->showcmd)
     {
         r = IShellLinkA_SetShowCmd(sl, desc->showcmd);
-        lok(SUCCEEDED(r), "SetShowCmd failed (0x%08lx)\n", r);
+        lok(SUCCEEDED(r), "SetShowCmd failed (0x%08x)\n", r);
     }
     if (desc->icon)
     {
         r = IShellLinkA_SetIconLocation(sl, desc->icon, desc->icon_id);
-        lok(SUCCEEDED(r), "SetIconLocation failed (0x%08lx)\n", r);
+        lok(SUCCEEDED(r), "SetIconLocation failed (0x%08x)\n", r);
     }
     if (desc->hotkey)
     {
         r = IShellLinkA_SetHotkey(sl, desc->hotkey);
-        lok(SUCCEEDED(r), "SetHotkey failed (0x%08lx)\n", r);
+        lok(SUCCEEDED(r), "SetHotkey failed (0x%08x)\n", r);
     }
 
     r = IShellLinkW_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
-    lok(SUCCEEDED(r), "no IID_IPersistFile (0x%08lx)\n", r);
+    lok(SUCCEEDED(r), "no IID_IPersistFile (0x%08x)\n", r);
     if (SUCCEEDED(r))
     {
         r = IPersistFile_Save(pf, path, TRUE);
         if (save_fails)
         {
             todo_wine {
-            lok(SUCCEEDED(r), "save failed (0x%08lx)\n", r);
+            lok(SUCCEEDED(r), "save failed (0x%08x)\n", r);
             }
         }
         else
         {
-            lok(SUCCEEDED(r), "save failed (0x%08lx)\n", r);
+            lok(SUCCEEDED(r), "save failed (0x%08x)\n", r);
         }
         IPersistFile_Release(pf);
     }
@@ -357,7 +391,7 @@ void create_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int save_fails)
     IShellLinkA_Release(sl);
 }
 
-static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
+static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int todo)
 {
     HRESULT r;
     IShellLinkA *sl;
@@ -366,12 +400,12 @@ static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
 
     r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
                          &IID_IShellLinkA, (LPVOID*)&sl);
-    lok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08lx)\n", r);
+    lok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08x)\n", r);
     if (!SUCCEEDED(r))
         return;
 
     r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
-    lok(SUCCEEDED(r), "no IID_IPersistFile (0x%08lx)\n", r);
+    lok(SUCCEEDED(r), "no IID_IPersistFile (0x%08x)\n", r);
     if (!SUCCEEDED(r))
     {
         IShellLinkA_Release(sl);
@@ -379,7 +413,7 @@ static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
     }
 
     r = IPersistFile_Load(pf, path, STGM_READ);
-    lok(SUCCEEDED(r), "load failed (0x%08lx)\n", r);
+    lok(SUCCEEDED(r), "load failed (0x%08x)\n", r);
     IPersistFile_Release(pf);
     if (!SUCCEEDED(r))
     {
@@ -391,8 +425,8 @@ static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
     {
         strcpy(buffer,"garbage");
         r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
-        lok(SUCCEEDED(r), "GetDescription failed (0x%08lx)\n", r);
-        lok(lstrcmp(buffer, desc->description)==0,
+        lok(SUCCEEDED(r), "GetDescription failed (0x%08x)\n", r);
+        lok_todo_4(0x1, lstrcmp(buffer, desc->description)==0,
            "GetDescription returned '%s' instead of '%s'\n",
            buffer, desc->description);
     }
@@ -400,8 +434,8 @@ static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
     {
         strcpy(buffer,"garbage");
         r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
-        lok(SUCCEEDED(r), "GetWorkingDirectory failed (0x%08lx)\n", r);
-        lok(lstrcmpi(buffer, desc->workdir)==0,
+        lok(SUCCEEDED(r), "GetWorkingDirectory failed (0x%08x)\n", r);
+        lok_todo_4(0x2, lstrcmpi(buffer, desc->workdir)==0,
            "GetWorkingDirectory returned '%s' instead of '%s'\n",
            buffer, desc->workdir);
     }
@@ -409,8 +443,8 @@ static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
     {
         strcpy(buffer,"garbage");
         r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
-        lok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
-        lok(lstrcmpi(buffer, desc->path)==0,
+        lok(SUCCEEDED(r), "GetPath failed (0x%08x)\n", r);
+        lok_todo_4(0x4, lstrcmpi(buffer, desc->path)==0,
            "GetPath returned '%s' instead of '%s'\n",
            buffer, desc->path);
     }
@@ -418,16 +452,16 @@ static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
     {
         LPITEMIDLIST pidl=NULL;
         r = IShellLinkA_GetIDList(sl, &pidl);
-        lok(SUCCEEDED(r), "GetIDList failed (0x%08lx)\n", r);
-        lok(ILIsEqual(pidl, desc->pidl),
+        lok(SUCCEEDED(r), "GetIDList failed (0x%08x)\n", r);
+        lok_todo_2(0x8, pILIsEqual(pidl, desc->pidl),
            "GetIDList returned an incorrect pidl\n");
     }
     if (desc->showcmd)
     {
         int i=0xdeadbeef;
         r = IShellLinkA_GetShowCmd(sl, &i);
-        lok(SUCCEEDED(r), "GetShowCmd failed (0x%08lx)\n", r);
-        lok(i==desc->showcmd,
+        lok(SUCCEEDED(r), "GetShowCmd failed (0x%08x)\n", r);
+        lok_todo_4(0x10, i==desc->showcmd,
            "GetShowCmd returned 0x%0x instead of 0x%0x\n",
            i, desc->showcmd);
     }
@@ -436,11 +470,11 @@ static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
         int i=0xdeadbeef;
         strcpy(buffer,"garbage");
         r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
-        lok(SUCCEEDED(r), "GetIconLocation failed (0x%08lx)\n", r);
-        lok(lstrcmpi(buffer, desc->icon)==0,
+        lok(SUCCEEDED(r), "GetIconLocation failed (0x%08x)\n", r);
+        lok_todo_4(0x20, lstrcmpi(buffer, desc->icon)==0,
            "GetIconLocation returned '%s' instead of '%s'\n",
            buffer, desc->icon);
-        lok(i==desc->icon_id,
+        lok_todo_4(0x20, i==desc->icon_id,
            "GetIconLocation returned 0x%0x instead of 0x%0x\n",
            i, desc->icon_id);
     }
@@ -448,8 +482,8 @@ static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
     {
         WORD i=0xbeef;
         r = IShellLinkA_GetHotkey(sl, &i);
-        lok(SUCCEEDED(r), "GetHotkey failed (0x%08lx)\n", r);
-        lok(i==desc->hotkey,
+        lok(SUCCEEDED(r), "GetHotkey failed (0x%08x)\n", r);
+        lok_todo_4(0x40, i==desc->hotkey,
            "GetHotkey returned 0x%04x instead of 0x%04x\n",
            i, desc->hotkey);
     }
@@ -462,7 +496,9 @@ static void test_load_save(void)
     lnk_desc_t desc;
     char mypath[MAX_PATH];
     char mydir[MAX_PATH];
+    char realpath[MAX_PATH];
     char* p;
+    HANDLE hf;
     DWORD r;
 
     /* Save an empty .lnk file */
@@ -475,8 +511,7 @@ static void test_load_save(void)
     desc.path="";
     desc.arguments="";
     desc.icon="";
-    check_lnk(lnkfile, &desc);
-
+    check_lnk(lnkfile, &desc, 0x0);
 
     /* Point a .lnk file to nonexistent files */
     desc.description="";
@@ -489,16 +524,15 @@ static void test_load_save(void)
     desc.icon_id=1234;
     desc.hotkey=0;
     create_lnk(lnkfile, &desc, 0);
-    check_lnk(lnkfile, &desc);
+    check_lnk(lnkfile, &desc, 0x0);
 
     r=GetModuleFileName(NULL, mypath, sizeof(mypath));
-    ok(r>=0 && r<sizeof(mypath), "GetModuleFileName failed (%ld)\n", r);
+    ok(r>=0 && r<sizeof(mypath), "GetModuleFileName failed (%d)\n", r);
     strcpy(mydir, mypath);
     p=strrchr(mydir, '\\');
     if (p)
         *p='\0';
 
-
     /* Overwrite the existing lnk file and point it to existing files */
     desc.description="test 2";
     desc.workdir=mydir;
@@ -510,7 +544,54 @@ static void test_load_save(void)
     desc.icon_id=0;
     desc.hotkey=0x1234;
     create_lnk(lnkfile, &desc, 0);
-    check_lnk(lnkfile, &desc);
+    check_lnk(lnkfile, &desc, 0x0);
+
+    /* Overwrite the existing lnk file and test link to a command on the path */
+    desc.description="command on path";
+    desc.workdir=mypath;
+    desc.path="rundll32.exe";
+    desc.pidl=NULL;
+    desc.arguments="/option1 /option2 \"Some string\"";
+    desc.showcmd=SW_SHOWNORMAL;
+    desc.icon=mypath;
+    desc.icon_id=0;
+    desc.hotkey=0x1234;
+    create_lnk(lnkfile, &desc, 0);
+    /* Check that link is created to proper location */
+    SearchPathA( NULL, desc.path, NULL, MAX_PATH, realpath, NULL);
+    desc.path=realpath;
+    check_lnk(lnkfile, &desc, 0x0);
+
+    /* Create a temporary non-executable file */
+    r=GetTempPath(sizeof(mypath), mypath);
+    ok(r>=0 && r<sizeof(mypath), "GetTempPath failed (%d), err %d\n", r, GetLastError());
+    r=pGetLongPathNameA(mypath, mydir, sizeof(mydir));
+    ok(r>=0 && r<sizeof(mydir), "GetLongPathName failed (%d), err %d\n", r, GetLastError());
+    p=strrchr(mydir, '\\');
+    if (p)
+        *p='\0';
+
+    strcpy(mypath, mydir);
+    strcat(mypath, "\\test.txt");
+    hf = CreateFile(mypath, GENERIC_WRITE, 0, NULL,
+                    CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    CloseHandle(hf);
+
+    /* Overwrite the existing lnk file and test link to an existing non-executable file */
+    desc.description="non-executable file";
+    desc.workdir=mydir;
+    desc.path=mypath;
+    desc.pidl=NULL;
+    desc.arguments="";
+    desc.showcmd=SW_SHOWNORMAL;
+    desc.icon=mypath;
+    desc.icon_id=0;
+    desc.hotkey=0x1234;
+    create_lnk(lnkfile, &desc, 0);
+    check_lnk(lnkfile, &desc, 0x0);
+
+    r = DeleteFileA(mypath);
+    ok(r, "failed to delete file %s (%d)\n", mypath, GetLastError());
 
     /* FIXME: Also test saving a .lnk pointing to a pidl that cannot be
      * represented as a path.
@@ -518,20 +599,107 @@ static void test_load_save(void)
 
     /* DeleteFileW is not implemented on Win9x */
     r=DeleteFileA("c:\\test.lnk");
-    ok(r, "failed to delete link (%ld)\n", GetLastError());
+    ok(r, "failed to delete link (%d)\n", GetLastError());
+}
+
+static void test_datalink(void)
+{
+    static const WCHAR lnk[] = {
+      ':',':','{','9','d','b','1','1','8','6','f','-','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 };
+    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',']',
+      'j','I','{','j','f','(','=','1','&','L','[','-','8','1','-',']',0 };
+    IShellLinkDataList *dl = NULL;
+    IShellLinkW *sl = NULL;
+    HRESULT r;
+    DWORD flags = 0;
+    EXP_DARWIN_LINK *dar;
+
+    r = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+                            &IID_IShellLinkW, (LPVOID*)&sl );
+    ok( r == S_OK || r == E_NOINTERFACE, "CoCreateInstance failed (0x%08x)\n", r);
+    if (!sl)
+    {
+        skip("no shelllink\n");
+        return;
+    }
+
+    r = IShellLinkW_QueryInterface( sl, &_IID_IShellLinkDataList, (LPVOID*) &dl );
+    ok(r == S_OK, "IShellLinkW_QueryInterface failed (0x%08x)\n", r);
+
+    if (!dl)
+    {
+        skip("no datalink interface\n");
+        return;
+    }
+
+    flags = 0;
+    r = dl->lpVtbl->GetFlags( dl, &flags );
+    ok( r == S_OK, "GetFlags failed\n");
+    ok( flags == 0, "GetFlags returned wrong flags\n");
+
+    dar = (void*)-1;
+    r = dl->lpVtbl->CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
+    ok( r == E_FAIL, "CopyDataBlock failed\n");
+    ok( dar == NULL, "should be null\n");
+
+    r = IShellLinkW_SetPath(sl, lnk);
+    ok(r == S_OK, "set path failed\n");
+
+    /*
+     * The following crashes:
+     * r = dl->lpVtbl->GetFlags( dl, NULL );
+     */
+
+    flags = 0;
+    r = dl->lpVtbl->GetFlags( dl, &flags );
+    ok( r == S_OK, "GetFlags failed\n");
+    ok( flags == (SLDF_HAS_DARWINID|SLDF_HAS_LOGO3ID),
+        "GetFlags returned wrong flags\n");
+
+    dar = NULL;
+    r = dl->lpVtbl->CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
+    ok( r == S_OK, "CopyDataBlock failed\n");
+
+    ok( dar && ((DATABLOCK_HEADER*)dar)->dwSignature == EXP_DARWIN_ID_SIG, "signature wrong\n");
+    ok( dar && 0==lstrcmpW(dar->szwDarwinID, comp ), "signature wrong\n");
+
+    LocalFree( dar );
+
+    IUnknown_Release( dl );
+    IShellLinkW_Release( sl );
 }
 
 START_TEST(shelllink)
 {
     HRESULT r;
+    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);
+
+    pGetLongPathNameA = (void *)GetProcAddress(hkernel32, "GetLongPathNameA");
 
     r = CoInitialize(NULL);
-    ok(SUCCEEDED(r), "CoInitialize failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "CoInitialize failed (0x%08x)\n", r);
     if (!SUCCEEDED(r))
         return;
 
     test_get_set();
     test_load_save();
+    test_datalink();
 
     CoUninitialize();
 }
index 4d0750e..66b9406 100755 (executable)
@@ -15,7 +15,7 @@
  *
  * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 /* TODO:
 #include <stdio.h>
 #include <assert.h>
 
+/* Needed to get SEE_MASK_NOZONECHECKS with the PSDK */
+#define NTDDI_WINXPSP1 0x05010100
+#define NTDDI_VERSION NTDDI_WINXPSP1
+
 #include "wtypes.h"
 #include "winbase.h"
 #include "windef.h"
@@ -49,17 +53,24 @@ static char argv0[MAX_PATH];
 static int myARGC;
 static char** myARGV;
 static char tmpdir[MAX_PATH];
+static char child_file[MAX_PATH];
+static DLLVERSIONINFO dllver;
 
-static const char* testfiles[]=
-{
-    "%s\\test file.shlexec",
-    "%s\\test file.noassoc",
-    "%s\\test file.noassoc.shlexec",
-    "%s\\test file.shlexec.noassoc",
-    "%s\\test_shortcut_shlexec.lnk",
-    NULL
-};
 
+/***
+ *
+ * ShellExecute wrappers
+ *
+ ***/
+static void dump_child(void);
+
+static HANDLE hEvent;
+static void init_event(const char* child_file)
+{
+    char* event_name;
+    event_name=strrchr(child_file, '\\')+1;
+    hEvent=CreateEvent(NULL, FALSE, FALSE, event_name);
+}
 
 static void strcat_param(char* str, const char* param)
 {
@@ -78,6 +89,8 @@ static void strcat_param(char* str, const char* param)
 static char shell_call[2048]="";
 static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCSTR directory)
 {
+    int rc;
+
     strcpy(shell_call, "ShellExecute(");
     strcat_param(shell_call, operation);
     strcat(shell_call, ", ");
@@ -90,13 +103,30 @@ static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCST
     if (winetest_debug > 1)
         trace("%s\n", shell_call);
 
+    DeleteFile(child_file);
     SetLastError(0xcafebabe);
+
     /* FIXME: We cannot use ShellExecuteEx() here because if there is no
      * association it displays the 'Open With' dialog and I could not find
      * a flag to prevent this.
      */
-    return (int)ShellExecute(NULL, operation, file, parameters, directory,
-                             SW_SHOWNORMAL);
+    rc=(int)ShellExecute(NULL, operation, file, parameters, directory,
+                         SW_SHOWNORMAL);
+
+    if (rc > 32)
+    {
+        int wait_rc;
+        wait_rc=WaitForSingleObject(hEvent, 5000);
+        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
+     */
+    WritePrivateProfileStringA(NULL, NULL, NULL, child_file);
+    if (rc > 32)
+        dump_child();
+
+    return rc;
 }
 
 static int shell_execute_ex(DWORD mask, LPCSTR operation, LPCSTR file,
@@ -119,7 +149,7 @@ static int shell_execute_ex(DWORD mask, LPCSTR operation, LPCSTR file,
         trace("%s\n", shell_call);
 
     sei.cbSize=sizeof(sei);
-    sei.fMask=mask;
+    sei.fMask=SEE_MASK_NOCLOSEPROCESS | mask;
     sei.hwnd=NULL;
     sei.lpVerb=operation;
     sei.lpFile=file;
@@ -131,16 +161,45 @@ static int shell_execute_ex(DWORD mask, LPCSTR operation, LPCSTR file,
     sei.lpClass=NULL;
     sei.hkeyClass=NULL;
     sei.dwHotKey=0;
-    sei.hIcon=NULL;
+    U(sei).hIcon=NULL;
+    sei.hProcess=NULL; /* Out */
 
+    DeleteFile(child_file);
     SetLastError(0xcafebabe);
     success=ShellExecuteEx(&sei);
     rc=(int)sei.hInstApp;
-    ok((success && rc >= 32) || (!success && rc < 32),
+    ok((success && rc > 32) || (!success && rc <= 32),
        "%s rc=%d and hInstApp=%d is not allowed\n", shell_call, success, rc);
+
+    if (rc > 32)
+    {
+        int wait_rc;
+        if (sei.hProcess!=NULL)
+        {
+            wait_rc=WaitForSingleObject(sei.hProcess, 5000);
+            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);
+    }
+    /* The child process may have changed the result file, so let profile
+     * functions know about it
+     */
+    WritePrivateProfileStringA(NULL, NULL, NULL, child_file);
+    if (rc > 32)
+        dump_child();
+
     return rc;
 }
 
+
+
+/***
+ *
+ * Functions to create / delete associations wrappers
+ *
+ ***/
+
 static void create_test_association(const char* extension)
 {
     HKEY hkey, hkey_shell;
@@ -151,7 +210,7 @@ static void create_test_association(const char* extension)
     rc=RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, NULL, 0, KEY_SET_VALUE,
                       NULL, &hkey, NULL);
     assert(rc==ERROR_SUCCESS);
-    rc=RegSetValueEx(hkey, NULL, 0, REG_SZ, class, strlen(class)+1);
+    rc=RegSetValueEx(hkey, NULL, 0, REG_SZ, (LPBYTE) class, strlen(class)+1);
     assert(rc==ERROR_SUCCESS);
     CloseHandle(hkey);
 
@@ -174,7 +233,10 @@ static void delete_test_association(const char* extension)
     SHDeleteKey(HKEY_CLASSES_ROOT, extension);
 }
 
-static void create_test_verb(const char* extension, const char* verb)
+static void create_test_verb_dde(const char* extension, const char* verb,
+                                 int rawcmd, const char* cmdtail, const char *ddeexec,
+                                 const char *application, const char *topic,
+                                 const char *ifexec)
 {
     HKEY hkey_shell, hkey_verb, hkey_cmd;
     char shell[MAX_PATH];
@@ -192,56 +254,350 @@ static void create_test_verb(const char* extension, const char* verb)
                       NULL, &hkey_cmd, NULL);
     assert(rc==ERROR_SUCCESS);
 
-    cmd=malloc(strlen(argv0)+13+1);
-    sprintf(cmd,"%s shlexec \"%%1\"", argv0);
-    rc=RegSetValueEx(hkey_cmd, NULL, 0, REG_SZ, cmd, strlen(cmd)+1);
-    assert(rc==ERROR_SUCCESS);
+    if (rawcmd)
+    {
+        rc=RegSetValueEx(hkey_cmd, NULL, 0, REG_SZ, (LPBYTE)cmdtail, strlen(cmdtail)+1);
+    }
+    else
+    {
+        cmd=malloc(strlen(argv0)+10+strlen(child_file)+2+strlen(cmdtail)+1);
+        sprintf(cmd,"%s shlexec \"%s\" %s", argv0, child_file, cmdtail);
+        rc=RegSetValueEx(hkey_cmd, NULL, 0, REG_SZ, (LPBYTE)cmd, strlen(cmd)+1);
+        assert(rc==ERROR_SUCCESS);
+        free(cmd);
+    }
+
+    if (ddeexec)
+    {
+        HKEY hkey_ddeexec, hkey_application, hkey_topic, hkey_ifexec;
+
+        rc=RegCreateKeyEx(hkey_verb, "ddeexec", 0, NULL, 0, KEY_SET_VALUE |
+                          KEY_CREATE_SUB_KEY, NULL, &hkey_ddeexec, NULL);
+        assert(rc==ERROR_SUCCESS);
+        rc=RegSetValueEx(hkey_ddeexec, NULL, 0, REG_SZ, (LPBYTE)ddeexec,
+                         strlen(ddeexec)+1);
+        assert(rc==ERROR_SUCCESS);
+        if (application)
+        {
+            rc=RegCreateKeyEx(hkey_ddeexec, "application", 0, NULL, 0, KEY_SET_VALUE,
+                              NULL, &hkey_application, NULL);
+            assert(rc==ERROR_SUCCESS);
+            rc=RegSetValueEx(hkey_application, NULL, 0, REG_SZ, (LPBYTE)application,
+                             strlen(application)+1);
+            assert(rc==ERROR_SUCCESS);
+            CloseHandle(hkey_application);
+        }
+        if (topic)
+        {
+            rc=RegCreateKeyEx(hkey_ddeexec, "topic", 0, NULL, 0, KEY_SET_VALUE,
+                              NULL, &hkey_topic, NULL);
+            assert(rc==ERROR_SUCCESS);
+            rc=RegSetValueEx(hkey_topic, NULL, 0, REG_SZ, (LPBYTE)topic,
+                             strlen(topic)+1);
+            assert(rc==ERROR_SUCCESS);
+            CloseHandle(hkey_topic);
+        }
+        if (ifexec)
+        {
+            rc=RegCreateKeyEx(hkey_ddeexec, "ifexec", 0, NULL, 0, KEY_SET_VALUE,
+                              NULL, &hkey_ifexec, NULL);
+            assert(rc==ERROR_SUCCESS);
+            rc=RegSetValueEx(hkey_ifexec, NULL, 0, REG_SZ, (LPBYTE)ifexec,
+                             strlen(ifexec)+1);
+            assert(rc==ERROR_SUCCESS);
+            CloseHandle(hkey_ifexec);
+        }
+        CloseHandle(hkey_ddeexec);
+    }
 
-    free(cmd);
     CloseHandle(hkey_shell);
     CloseHandle(hkey_verb);
     CloseHandle(hkey_cmd);
 }
 
+static void create_test_verb(const char* extension, const char* verb,
+                             int rawcmd, const char* cmdtail)
+{
+    create_test_verb_dde(extension, verb, rawcmd, cmdtail, NULL, NULL,
+                         NULL, NULL);
+}
+
+/***
+ *
+ * Functions to check that the child process was started just right
+ * (borrowed from dlls/kernel32/tests/process.c)
+ *
+ ***/
+
+static const char* encodeA(const char* str)
+{
+    static char encoded[2*1024+1];
+    char*       ptr;
+    size_t      len,i;
+
+    if (!str) return "";
+    len = strlen(str) + 1;
+    if (len >= sizeof(encoded)/2)
+    {
+        fprintf(stderr, "string is too long!\n");
+        assert(0);
+    }
+    ptr = encoded;
+    for (i = 0; i < len; i++)
+        sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]);
+    ptr[2 * len] = '\0';
+    return ptr;
+}
+
+static unsigned decode_char(char c)
+{
+    if (c >= '0' && c <= '9') return c - '0';
+    if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+    assert(c >= 'A' && c <= 'F');
+    return c - 'A' + 10;
+}
+
+static char* decodeA(const char* str)
+{
+    static char decoded[1024];
+    char*       ptr;
+    size_t      len,i;
+
+    len = strlen(str) / 2;
+    if (!len--) return NULL;
+    if (len >= sizeof(decoded))
+    {
+        fprintf(stderr, "string is too long!\n");
+        assert(0);
+    }
+    ptr = decoded;
+    for (i = 0; i < len; i++)
+        ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]);
+    ptr[len] = '\0';
+    return ptr;
+}
+
+static void     childPrintf(HANDLE h, const char* fmt, ...)
+{
+    va_list     valist;
+    char        buffer[1024];
+    DWORD       w;
+
+    va_start(valist, fmt);
+    vsprintf(buffer, fmt, valist);
+    va_end(valist);
+    WriteFile(h, buffer, strlen(buffer), &w, NULL);
+}
+
+static void doChild(int argc, char** argv)
+{
+    char* filename;
+    HANDLE hFile;
+    int i;
+
+    filename=argv[2];
+    hFile=CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
+    if (hFile == INVALID_HANDLE_VALUE)
+        return;
+
+    /* Arguments */
+    childPrintf(hFile, "[Arguments]\r\n");
+    if (winetest_debug > 2)
+        trace("argcA=%d\n", argc);
+    childPrintf(hFile, "argcA=%d\r\n", argc);
+    for (i = 0; i < argc; i++)
+    {
+        if (winetest_debug > 2)
+            trace("argvA%d=%s\n", i, argv[i]);
+        childPrintf(hFile, "argvA%d=%s\r\n", i, encodeA(argv[i]));
+    }
+    CloseHandle(hFile);
+
+    init_event(filename);
+    SetEvent(hEvent);
+    CloseHandle(hEvent);
+}
+
+static char* getChildString(const char* sect, const char* key)
+{
+    char        buf[1024];
+    char*       ret;
+
+    GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), child_file);
+    if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL;
+    assert(!(strlen(buf) & 1));
+    ret = decodeA(buf);
+    return ret;
+}
+
+static void dump_child(void)
+{
+    if (winetest_debug > 1)
+    {
+        char key[18];
+        char* str;
+        int i, c;
+
+        c=GetPrivateProfileIntA("Arguments", "argcA", -1, child_file);
+        trace("argcA=%d\n",c);
+        for (i=0;i<c;i++)
+        {
+            sprintf(key, "argvA%d", i);
+            str=getChildString("Arguments", key);
+            trace("%s=%s\n", key, str);
+        }
+    }
+}
+
+static int StrCmpPath(const char* s1, const char* s2)
+{
+    if (!s1 && !s2) return 0;
+    if (!s2) return 1;
+    if (!s1) return -1;
+    while (*s1)
+    {
+        if (!*s2)
+        {
+            if (*s1=='.')
+                s1++;
+            return (*s1-*s2);
+        }
+        if ((*s1=='/' || *s1=='\\') && (*s2=='/' || *s2=='\\'))
+        {
+            while (*s1=='/' || *s1=='\\')
+                s1++;
+            while (*s2=='/' || *s2=='\\')
+                s2++;
+        }
+        else if (toupper(*s1)==toupper(*s2))
+        {
+            s1++;
+            s2++;
+        }
+        else
+        {
+            return (*s1-*s2);
+        }
+    }
+    if (*s2=='.')
+        s2++;
+    if (*s2)
+        return -1;
+    return 0;
+}
+
+static int _okChildString(const char* file, int line, const char* key, const char* expected)
+{
+    char* result;
+    result=getChildString("Arguments", key);
+    return ok_(file, line)(lstrcmpiA(result, expected) == 0,
+               "%s expected '%s', got '%s'\n", key, expected, result);
+}
+
+static int _okChildPath(const char* file, int line, const char* key, const char* expected)
+{
+    char* result;
+    result=getChildString("Arguments", key);
+    return ok_(file, line)(StrCmpPath(result, expected) == 0,
+               "%s expected '%s', got '%s'\n", key, expected, result);
+}
+
+static int _okChildInt(const char* file, int line, const char* key, int expected)
+{
+    INT result;
+    result=GetPrivateProfileIntA("Arguments", key, expected, child_file);
+    return ok_(file, line)(result == expected,
+               "%s expected %d, but got %d\n", key, expected, result);
+}
+
+#define okChildString(key, expected) _okChildString(__FILE__, __LINE__, (key), (expected))
+#define okChildPath(key, expected) _okChildPath(__FILE__, __LINE__, (key), (expected))
+#define okChildInt(key, expected)    _okChildInt(__FILE__, __LINE__, (key), (expected))
+
+
+
+/***
+ *
+ * Tests
+ *
+ ***/
+
+static const char* testfiles[]=
+{
+    "%s\\test file.shlexec",
+    "%s\\%%nasty%% $file.shlexec",
+    "%s\\test file.noassoc",
+    "%s\\test file.noassoc.shlexec",
+    "%s\\test file.shlexec.noassoc",
+    "%s\\test_shortcut_shlexec.lnk",
+    "%s\\test_shortcut_exe.lnk",
+    "%s\\test file.shl",
+    "%s\\test file.shlfoo",
+    "%s\\test file.sfe",
+    "%s\\masked file.shlexec",
+    "%s\\masked",
+    "%s\\test file.sde",
+    "%s\\test file.exe",
+    "%s\\test2.exe",
+    NULL
+};
 
 typedef struct
 {
-    char* basename;
-    int rc;
+    const char* verb;
+    const char* basename;
     int todo;
+    int rc;
 } filename_tests_t;
 
 static filename_tests_t filename_tests[]=
 {
     /* Test bad / nonexistent filenames */
-    {"%s\\nonexistent.shlexec", ERROR_FILE_NOT_FOUND, 1},
-    {"%s\\nonexistent.noassoc", ERROR_FILE_NOT_FOUND, 1},
+    {NULL,           "%s\\nonexistent.shlexec", 0x11, SE_ERR_FNF},
+    {NULL,           "%s\\nonexistent.noassoc", 0x11, SE_ERR_FNF},
 
     /* Standard tests */
-    {"%s\\test file.shlexec",   0, 0},
-    {"%s\\test file.shlexec.",  0, 0},
-    {"%s/test file.shlexec",    0, 0},
+    {NULL,           "%s\\test file.shlexec",   0x0, 33},
+    {NULL,           "%s\\test file.shlexec.",  0x0, 33},
+    {NULL,           "%s\\%%nasty%% $file.shlexec", 0x0, 33},
+    {NULL,           "%s/test file.shlexec",    0x0, 33},
 
     /* Test filenames with no association */
-    {"%s\\test file.noassoc",   SE_ERR_NOASSOC, 0},
+    {NULL,           "%s\\test file.noassoc",   0x0,  SE_ERR_NOASSOC},
 
     /* Test double extensions */
-    {"%s\\test file.noassoc.shlexec", 0, 0},
-    {"%s\\test file.shlexec.noassoc", SE_ERR_NOASSOC, 0},
+    {NULL,           "%s\\test file.noassoc.shlexec", 0x0, 33},
+    {NULL,           "%s\\test file.shlexec.noassoc", 0x0, SE_ERR_NOASSOC},
+
+    /* Test alternate verbs */
+    {"LowerL",       "%s\\nonexistent.shlexec", 0x11, SE_ERR_FNF},
+    {"LowerL",       "%s\\test file.noassoc",   0x0,  SE_ERR_NOASSOC},
+
+    {"QuotedLowerL", "%s\\test file.shlexec",   0x0, 33},
+    {"QuotedUpperL", "%s\\test file.shlexec",   0x0, 33},
 
-    /* Test shortcuts */
-    {"%s\\test_shortcut_shlexec.lnk", 0, 0},
+    /* Test file masked due to space */
+    {NULL,           "%s\\masked file.shlexec",   0x1, 33},
+    /* Test if quoting prevents the masking */
+    {NULL,           "%s\\masked file.shlexec",   0x40, 33},
 
-    {NULL, 0, 0}
+    {NULL, NULL, 0}
 };
 
-static void test_filename()
+static filename_tests_t noquotes_tests[]=
+{
+    /* Test unquoted '%1' thingies */
+    {"NoQuotes",     "%s\\test file.shlexec",   0xa, 33},
+    {"LowerL",       "%s\\test file.shlexec",   0xa, 33},
+    {"UpperL",       "%s\\test file.shlexec",   0xa, 33},
+
+    {NULL, NULL, 0}
+};
+
+static void test_filename(void)
 {
     char filename[MAX_PATH];
     const filename_tests_t* test;
-    HMODULE hdll;
-    DLLVERSIONINFO dllver;
-    HRESULT (WINAPI *pDllGetVersion)(DLLVERSIONINFO*);
     char* c;
     int rc;
 
@@ -259,50 +615,130 @@ static void test_filename()
                 c++;
             }
         }
-        rc=shell_execute(NULL, filename, NULL, NULL);
-        if (test->rc==0)
+        if ((test->todo & 0x40)==0)
+        {
+            rc=shell_execute(test->verb, filename, NULL, NULL);
+        }
+        else
+        {
+            char quoted[MAX_PATH + 2];
+            sprintf(quoted, "\"%s\"", filename);
+            rc=shell_execute(test->verb, quoted, 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,
+               rc, GetLastError());
+        }
+        else todo_wine
         {
-            if (test->todo)
+            ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
+               rc, GetLastError());
+        }
+        if (rc == 33)
+        {
+            const char* verb;
+            if ((test->todo & 0x2)==0)
             {
-                todo_wine
-                {
-                    ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call,
-                       rc, GetLastError());
-                }
+                okChildInt("argcA", 5);
             }
-            else
+            else todo_wine
+            {
+                okChildInt("argcA", 5);
+            }
+            verb=(test->verb ? test->verb : "Open");
+            if ((test->todo & 0x4)==0)
             {
-                ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call,
-                   rc, GetLastError());
+                okChildString("argvA3", verb);
+            }
+            else todo_wine
+            {
+                okChildString("argvA3", verb);
+            }
+            if ((test->todo & 0x8)==0)
+            {
+                okChildPath("argvA4", filename);
+            }
+            else todo_wine
+            {
+                okChildPath("argvA4", filename);
             }
         }
-        else
+        test++;
+    }
+
+    test=noquotes_tests;
+    while (test->basename)
+    {
+        sprintf(filename, test->basename, tmpdir);
+        rc=shell_execute(test->verb, filename, NULL, NULL);
+        if (rc > 32)
+            rc=33;
+        if ((test->todo & 0x1)==0)
+        {
+            ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
+               rc, GetLastError());
+        }
+        else todo_wine
+        {
+            ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
+               rc, GetLastError());
+        }
+        if (rc==0)
         {
-            if (test->todo)
+            int count;
+            const char* verb;
+            char* str;
+
+            verb=(test->verb ? test->verb : "Open");
+            if ((test->todo & 0x4)==0)
+            {
+                okChildString("argvA3", verb);
+            }
+            else todo_wine
+            {
+                okChildString("argvA3", verb);
+            }
+
+            count=4;
+            str=filename;
+            while (1)
             {
-                todo_wine
+                char attrib[18];
+                char* space;
+                space=strchr(str, ' ');
+                if (space)
+                    *space='\0';
+                sprintf(attrib, "argvA%d", count);
+                if ((test->todo & 0x8)==0)
+                {
+                    okChildPath(attrib, str);
+                }
+                else todo_wine
                 {
-                    ok(rc==test->rc, "%s returned %d\n", shell_call, rc);
+                    okChildPath(attrib, str);
                 }
+                count++;
+                if (!space)
+                    break;
+                str=space+1;
             }
-            else
+            if ((test->todo & 0x2)==0)
+            {
+                okChildInt("argcA", count);
+            }
+            else todo_wine
             {
-                ok(rc==test->rc, "%s returned %d\n", shell_call, rc);
+                okChildInt("argcA", count);
             }
         }
         test++;
     }
 
-    hdll=GetModuleHandleA("shell32.dll");
-    pDllGetVersion=(void*)GetProcAddress(hdll, "DllGetVersion");
-    if (pDllGetVersion)
+    if (dllver.dwMajorVersion != 0)
     {
-        dllver.cbSize=sizeof(dllver);
-        pDllGetVersion(&dllver);
-        trace("major=%ld minor=%ld build=%ld platform=%ld\n",
-              dllver.dwMajorVersion, dllver.dwMinorVersion,
-              dllver.dwBuildNumber, dllver.dwPlatformID);
-
         /* The more recent versions of shell32.dll accept quoted filenames
          * while older ones (e.g. 4.00) don't. Still we want to test this
          * because IE 6 depends on the new behavior.
@@ -311,15 +747,83 @@ static void test_filename()
          */
         sprintf(filename, "\"%s\\test file.shlexec\"", tmpdir);
         rc=shell_execute(NULL, filename, NULL, NULL);
-        ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call, rc,
+        ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc,
            GetLastError());
+        okChildInt("argcA", 5);
+        okChildString("argvA3", "Open");
+        sprintf(filename, "%s\\test file.shlexec", tmpdir);
+        okChildPath("argvA4", filename);
+    }
+}
+
+static void test_find_executable(void)
+{
+    char filename[MAX_PATH];
+    char command[MAX_PATH];
+    const filename_tests_t* test;
+    int rc;
+
+    create_test_association(".sfe");
+    create_test_verb(".sfe", "Open", 1, "%1");
+
+    /* Don't test FindExecutable(..., NULL), it always crashes */
+
+    strcpy(command, "your word");
+    rc=(int)FindExecutableA(NULL, NULL, command);
+    ok(rc == SE_ERR_FNF || rc > 32 /* nt4 */, "FindExecutable(NULL) returned %d\n", rc);
+    ok(strcmp(command, "your word") != 0, "FindExecutable(NULL) returned command=[%s]\n", command);
+
+    strcpy(command, "your word");
+    rc=(int)FindExecutableA(tmpdir, NULL, command);
+    ok(rc == SE_ERR_NOASSOC /* >= win2000 */ || rc > 32 /* win98, nt4 */, "FindExecutable(NULL) returned %d\n", rc);
+    ok(strcmp(command, "your word") != 0, "FindExecutable(NULL) returned command=[%s]\n", command);
+
+    sprintf(filename, "%s\\test file.sfe", tmpdir);
+    rc=(int)FindExecutableA(filename, NULL, command);
+    ok(rc > 32, "FindExecutable(%s) returned %d\n", filename, rc);
+    /* Depending on the platform, command could be '%1' or 'test file.sfe' */
+
+    rc=(int)FindExecutableA("test file.sfe", tmpdir, command);
+    ok(rc > 32, "FindExecutable(%s) returned %d\n", filename, rc);
 
-        if (dllver.dwMajorVersion>=6)
+    rc=(int)FindExecutableA("test file.sfe", NULL, command);
+    todo_wine ok(rc == SE_ERR_FNF, "FindExecutable(%s) returned %d\n", filename, rc);
+
+    delete_test_association(".sfe");
+
+    create_test_association(".shl");
+    create_test_verb(".shl", "Open", 0, "Open");
+
+    sprintf(filename, "%s\\test file.shl", tmpdir);
+    rc=(int)FindExecutableA(filename, NULL, command);
+    ok(rc == SE_ERR_FNF /* NT4 */ || rc > 32, "FindExecutable(%s) returned %d\n", filename, rc);
+
+    sprintf(filename, "%s\\test file.shlfoo", tmpdir);
+    rc=(int)FindExecutableA(filename, NULL, command);
+
+    delete_test_association(".shl");
+
+    if (rc > 32)
+    {
+        /* On Windows XP and 2003 FindExecutable() is completely broken.
+         * Probably what it does is convert the filename to 8.3 format,
+         * which as a side effect converts the '.shlfoo' extension to '.shl',
+         * and then tries to find an association for '.shl'. This means it
+         * will normally fail on most extensions with more than 3 characters,
+         * like '.mpeg', etc.
+         * Also it means we cannot do any other test.
+         */
+        trace("FindExecutable() is broken -> skipping 4+ character extension tests\n");
+        return;
+    }
+
+    test=filename_tests;
+    while (test->basename)
+    {
+        sprintf(filename, test->basename, tmpdir);
+        if (strchr(filename, '/'))
         {
-            /* Recent versions of shell32.dll accept '/'s in shortcut paths.
-             * Older versions don't or are quite buggy in this regard.
-             */
-            sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
+            char* c;
             c=filename;
             while (*c)
             {
@@ -327,48 +831,587 @@ static void test_filename()
                     *c='/';
                 c++;
             }
-            rc=shell_execute(NULL, filename, NULL, NULL);
-            todo_wine {
-            ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call, rc,
-               GetLastError());
+        }
+        /* Win98 does not '\0'-terminate command! */
+        memset(command, '\0', sizeof(command));
+        rc=(int)FindExecutableA(filename, NULL, command);
+        if (rc > 32)
+            rc=33;
+        if ((test->todo & 0x10)==0)
+        {
+            ok(rc==test->rc, "FindExecutable(%s) failed: rc=%d\n", filename, rc);
+        }
+        else todo_wine
+        {
+            ok(rc==test->rc, "FindExecutable(%s) failed: rc=%d\n", filename, rc);
+        }
+        if (rc > 32)
+        {
+            int equal;
+            equal=strcmp(command, argv0) == 0 ||
+                /* NT4 returns an extra 0x8 character! */
+                (strlen(command) == strlen(argv0)+1 && strncmp(command, argv0, strlen(argv0)) == 0);
+            if ((test->todo & 0x20)==0)
+            {
+                ok(equal, "FindExecutable(%s) returned command='%s' instead of '%s'\n",
+                   filename, command, argv0);
+            }
+            else todo_wine
+            {
+                ok(equal, "FindExecutable(%s) returned command='%s' instead of '%s'\n",
+                   filename, command, argv0);
+            }
+        }
+        test++;
+    }
+}
+
+
+static filename_tests_t lnk_tests[]=
+{
+    /* Pass bad / nonexistent filenames as a parameter */
+    {NULL, "%s\\nonexistent.shlexec",    0xa, 33},
+    {NULL, "%s\\nonexistent.noassoc",    0xa, 33},
+
+    /* Pass regular paths as a parameter */
+    {NULL, "%s\\test file.shlexec",      0xa, 33},
+    {NULL, "%s/%%nasty%% $file.shlexec", 0xa, 33},
+
+    /* Pass filenames with no association as a parameter */
+    {NULL, "%s\\test file.noassoc",      0xa, 33},
+
+    {NULL, NULL, 0}
+};
+
+static void test_lnks(void)
+{
+    char filename[MAX_PATH];
+    char params[MAX_PATH];
+    const filename_tests_t* test;
+    int rc;
+
+    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());
+    okChildInt("argcA", 5);
+    okChildString("argvA3", "Open");
+    sprintf(filename, "%s\\test file.shlexec", tmpdir);
+    okChildPath("argvA4", filename);
+
+    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());
+    okChildInt("argcA", 4);
+    okChildString("argvA3", "Lnk");
+
+    if (dllver.dwMajorVersion>=6)
+    {
+        char* c;
+       /* Recent versions of shell32.dll accept '/'s in shortcut paths.
+         * Older versions don't or are quite buggy in this regard.
+         */
+        sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
+        c=filename;
+        while (*c)
+        {
+            if (*c=='\\')
+                *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,
+           GetLastError());
+        okChildInt("argcA", 4);
+        okChildString("argvA3", "Lnk");
+    }
+
+    sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
+    test=lnk_tests;
+    while (test->basename)
+    {
+        params[0]='\"';
+        sprintf(params+1, test->basename, tmpdir);
+        strcat(params,"\"");
+        rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, params,
+                            NULL);
+        if (rc > 32)
+            rc=33;
+        if ((test->todo & 0x1)==0)
+        {
+            ok(rc==test->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,
+               rc, GetLastError());
+        }
+        if (rc==0)
+        {
+            if ((test->todo & 0x2)==0)
+            {
+                okChildInt("argcA", 5);
+            }
+            else 
+            {
+                okChildInt("argcA", 5);
+            }
+            if ((test->todo & 0x4)==0)
+            {
+                okChildString("argvA3", "Lnk");
+            }
+            else todo_wine
+            {
+                okChildString("argvA3", "Lnk");
+            }
+            sprintf(params, test->basename, tmpdir);
+            if ((test->todo & 0x8)==0)
+            {
+                okChildPath("argvA4", params);
+            }
+            else
+            {
+                okChildPath("argvA4", params);
             }
         }
+        test++;
     }
 }
 
 
-static void test_exes()
+static void test_exes(void)
+{
+    char filename[MAX_PATH];
+    char params[1024];
+    int 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);
+    okChildInt("argcA", 4);
+    okChildString("argvA3", "Exec");
+
+    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);
+        }
+    }
+}
+
+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, "shlexec -nop",
+    rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, params,
                         NULL);
-    ok(rc>=32, "%s returned %d\n", shell_call, rc);
+    ok(rc > 32, "%s returned %d\n", shell_call, rc);
+    okChildInt("argcA", 4);
+    okChildString("argvA3", longparam);
 
     sprintf(filename, "%s\\test file.noassoc", tmpdir);
     if (CopyFile(argv0, filename, FALSE))
     {
-        rc=shell_execute(NULL, filename, "shlexec -nop", NULL);
+        rc=shell_execute(NULL, filename, params, NULL);
         todo_wine {
         ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc);
         }
     }
 }
 
+typedef struct
+{
+    const char* command;
+    const char* ddeexec;
+    const char* application;
+    const char* topic;
+    const char* ifexec;
+    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},
+
+    /* 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},
+
+    /* Test unquoted %1 in command and ddeexec
+     * (test filename has space) */
+    {"%1", "[open(%1)]", "shlexec", "dde", NULL, 2, "[open(%s)]", 0x0, 33},
+
+    /* Test ifexec precedence over ddeexec */
+    {"", "[open(\"%1\")]", "shlexec", "dde", "[ifexec(\"%1\")]", FALSE, "[ifexec(\"%s\")]", 0x0, 33},
+
+    /* Test default DDE topic */
+    {"", "[open(\"%1\")]", "shlexec", NULL, NULL, FALSE, "[open(\"%s\")]", 0x0, 33},
+
+    /* Test default DDE application */
+    {"", "[open(\"%1\")]", NULL, "dde", NULL, FALSE, "[open(\"%s\")]", 0x0, 33},
+
+    {NULL, NULL, NULL, NULL, NULL, 0, 0x0, 0}
+};
+
+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)
+{
+    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))
+            {
+                if (denyNextConnection)
+                    denyNextConnection = FALSE;
+                else
+                {
+                    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);
+            return (HDDEDATA)DDE_FACK;
+
+        default:
+            return NULL;
+    }
+}
+
+typedef struct
+{
+    char *filename;
+    DWORD threadIdParent;
+} dde_thread_info_t;
 
-static void init_test()
+static DWORD CALLBACK ddeThread(LPVOID arg)
+{
+    dde_thread_info_t *info = (dde_thread_info_t *)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;
+
+    ddeInst = 0;
+    rc = DdeInitializeA(&ddeInst, ddeCb, CBF_SKIP_ALLNOTIFICATIONS | CBF_FAIL_ADVISES |
+                        CBF_FAIL_POKES | CBF_FAIL_REQUESTS, 0L);
+    assert(rc == DMLERR_NO_ERROR);
+
+    sprintf(filename, "%s\\test file.sde", tmpdir);
+
+    /* Default service is application name minus path and extension */
+    strcpy(defApplication, strrchr(argv0, '\\')+1);
+    *strchr(defApplication, '.') = 0;
+
+    test = dde_tests;
+    while (test->command)
+    {
+        create_test_association(".sde");
+        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;
+        ddeExec[0] = 0;
+
+        assert(CreateThread(NULL, 0, ddeThread, (LPVOID)&info, 0, &threadId));
+        while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg);
+        rc = msg.wParam > 32 ? 33 : msg.wParam;
+        if ((test->todo & 0x1)==0)
+        {
+            ok(rc==test->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,
+               rc, GetLastError());
+        }
+        if (rc == 33)
+        {
+            if ((test->todo & 0x2)==0)
+            {
+                okChildInt("argcA", test->expectedArgs + 3);
+            }
+            else todo_wine
+            {
+                okChildInt("argcA", test->expectedArgs + 3);
+            }
+            if (test->expectedArgs == 1)
+            {
+                if ((test->todo & 0x4) == 0)
+                {
+                    okChildPath("argvA3", filename);
+                }
+                else todo_wine
+                {
+                    okChildPath("argvA3", filename);
+                }
+            }
+            if ((test->todo & 0x8) == 0)
+            {
+                sprintf(params, test->expectedDdeExec, filename);
+                ok(StrCmpPath(params, ddeExec) == 0,
+                   "ddeexec expected '%s', got '%s'\n", params, ddeExec);
+            }
+            else todo_wine
+            {
+                sprintf(params, test->expectedDdeExec, filename);
+                ok(StrCmpPath(params, ddeExec) == 0,
+                   "ddeexec expected '%s', got '%s'\n", params, ddeExec);
+            }
+        }
+
+        assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_UNREGISTER));
+        assert(DdeFreeStringHandle(ddeInst, hszTopic));
+        assert(DdeFreeStringHandle(ddeInst, hszApplication));
+        delete_test_association(".sde");
+        test++;
+    }
+
+    assert(DdeUninitialize(ddeInst));
+}
+
+#define DDE_DEFAULT_APP_VARIANTS 2
+typedef struct
+{
+    const char* command;
+    const char* expectedDdeApplication[DDE_DEFAULT_APP_VARIANTS];
+    int todo;
+    int rc[DDE_DEFAULT_APP_VARIANTS];
+} dde_default_app_tests_t;
+
+static dde_default_app_tests_t dde_default_app_tests[] =
+{
+    /* Windows XP and 98 handle default DDE app names in different ways.
+     * The application name we see in the first test determines the pattern
+     * of application names and return codes we will look for. */
+
+    /* Test unquoted existing filename with a space */
+    {"%s\\test file.exe", {"test file", "test"}, 0x0, {33, 33}},
+    {"%s\\test file.exe param", {"test file", "test"}, 0x0, {33, 33}},
+
+    /* Test quoted existing filename with a space */
+    {"\"%s\\test file.exe\"", {"test file", "test file"}, 0x0, {33, 33}},
+    {"\"%s\\test file.exe\" param", {"test file", "test file"}, 0x0, {33, 33}},
+
+    /* Test unquoted filename with a space that doesn't exist, but
+     * test2.exe does */
+    {"%s\\test2 file.exe", {"test2", "test2"}, 0x0, {33, 33}},
+    {"%s\\test2 file.exe param", {"test2", "test2"}, 0x0, {33, 33}},
+
+    /* Test quoted filename with a space that does not exist */
+    {"\"%s\\test2 file.exe\"", {"", "test2 file"}, 0x0, {5, 33}},
+    {"\"%s\\test2 file.exe\" param", {"", "test2 file"}, 0x0, {5, 33}},
+
+    /* Test filename supplied without the extension */
+    {"%s\\test2", {"test2", "test2"}, 0x0, {33, 33}},
+    {"%s\\test2 param", {"test2", "test2"}, 0x0, {33, 33}},
+
+    /* Test an unquoted nonexistent filename */
+    {"%s\\notexist.exe", {"", "notexist"}, 0x0, {5, 33}},
+    {"%s\\notexist.exe param", {"", "notexist"}, 0x0, {5, 33}},
+
+    /* Test an application that will be found on the path */
+    {"cmd", {"cmd", "cmd"}, 0x0, {33, 33}},
+    {"cmd param", {"cmd", "cmd"}, 0x0, {33, 33}},
+
+    /* Test an application that will not be found on the path */
+    {"xyzwxyzwxyz", {"", "xyzwxyzwxyz"}, 0x0, {5, 33}},
+    {"xyzwxyzwxyz param", {"", "xyzwxyzwxyz"}, 0x0, {5, 33}},
+
+    {NULL, {NULL}, 0, {0}}
+};
+
+static void test_dde_default_app(void)
 {
+    char filename[MAX_PATH];
+    HSZ hszApplication;
+    dde_thread_info_t info = { filename, GetCurrentThreadId() };
+    const dde_default_app_tests_t* test;
+    char params[1024];
+    DWORD threadId;
+    MSG msg;
+    int rc, which = 0;
+
+    ddeInst = 0;
+    rc = DdeInitializeA(&ddeInst, ddeCb, CBF_SKIP_ALLNOTIFICATIONS | CBF_FAIL_ADVISES |
+                        CBF_FAIL_POKES | CBF_FAIL_REQUESTS, 0L);
+    assert(rc == DMLERR_NO_ERROR);
+
+    sprintf(filename, "%s\\test file.sde", tmpdir);
+
+    /* It is strictly not necessary to register an application name here, but wine's
+     * DdeNameService implementation complains if 0L is passed instead of
+     * hszApplication with DNS_FILTEROFF */
+    hszApplication = DdeCreateStringHandleA(ddeInst, "shlexec", CP_WINANSI);
+    hszTopic = DdeCreateStringHandleA(ddeInst, "shlexec", CP_WINANSI);
+    assert(hszApplication && hszTopic);
+    assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_REGISTER | DNS_FILTEROFF));
+
+    test = dde_default_app_tests;
+    while (test->command)
+    {
+        create_test_association(".sde");
+        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,
+         * so don't wait for it */
+        SetEvent(hEvent);
+
+        assert(CreateThread(NULL, 0, ddeThread, (LPVOID)&info, 0, &threadId));
+        while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg);
+        rc = msg.wParam > 32 ? 33 : msg.wParam;
+
+        /* First test, find which set of test data we expect to see */
+        if (test == dde_default_app_tests)
+        {
+            int i;
+            for (i=0; i<DDE_DEFAULT_APP_VARIANTS; i++)
+            {
+                if (!strcmp(ddeApplication, test->expectedDdeApplication[i]))
+                {
+                    which = i;
+                    break;
+                }
+            }
+            if (i == DDE_DEFAULT_APP_VARIANTS)
+                skip("Default DDE application test does not match any available results, using first expected data set.\n");
+        }
+
+        if ((test->todo & 0x1)==0)
+        {
+            ok(rc==test->rc[which], "%s failed: rc=%d err=%d\n", shell_call,
+               rc, GetLastError());
+        }
+        else todo_wine
+        {
+            ok(rc==test->rc[which], "%s failed: rc=%d err=%d\n", shell_call,
+               rc, GetLastError());
+        }
+        if (rc == 33)
+        {
+            if ((test->todo & 0x2)==0)
+            {
+                ok(!strcmp(ddeApplication, test->expectedDdeApplication[which]),
+                   "Expected application '%s', got '%s'\n",
+                   test->expectedDdeApplication[which], ddeApplication);
+            }
+            else todo_wine
+            {
+                ok(!strcmp(ddeApplication, test->expectedDdeApplication[which]),
+                   "Expected application '%s', got '%s'\n",
+                   test->expectedDdeApplication[which], ddeApplication);
+            }
+        }
+
+        delete_test_association(".sde");
+        test++;
+    }
+
+    assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_UNREGISTER));
+    assert(DdeFreeStringHandle(ddeInst, hszTopic));
+    assert(DdeFreeStringHandle(ddeInst, hszApplication));
+    assert(DdeUninitialize(ddeInst));
+}
+
+static void init_test(void)
+{
+    HMODULE hdll;
+    HRESULT (WINAPI *pDllGetVersion)(DLLVERSIONINFO*);
     char filename[MAX_PATH];
     WCHAR lnkfile[MAX_PATH];
+    char params[1024];
     const char* const * testfile;
     lnk_desc_t desc;
     DWORD rc;
     HRESULT r;
 
+    hdll=GetModuleHandleA("shell32.dll");
+    pDllGetVersion=(void*)GetProcAddress(hdll, "DllGetVersion");
+    if (pDllGetVersion)
+    {
+        dllver.cbSize=sizeof(dllver);
+        pDllGetVersion(&dllver);
+        trace("major=%d minor=%d build=%d platform=%d\n",
+              dllver.dwMajorVersion, dllver.dwMinorVersion,
+              dllver.dwBuildNumber, dllver.dwPlatformID);
+    }
+    else
+    {
+        memset(&dllver, 0, sizeof(dllver));
+    }
+
     r = CoInitialize(NULL);
-    ok(SUCCEEDED(r), "CoInitialize failed (0x%08lx)\n", r);
+    ok(SUCCEEDED(r), "CoInitialize failed (0x%08x)\n", r);
     if (!SUCCEEDED(r))
         exit(1);
 
@@ -382,6 +1425,8 @@ static void init_test()
     }
 
     GetTempPathA(sizeof(tmpdir)/sizeof(*tmpdir), tmpdir);
+    assert(GetTempFileNameA(tmpdir, "wt", 0, child_file)!=0);
+    init_event(child_file);
 
     /* Set up the test files */
     testfile=testfiles;
@@ -394,7 +1439,7 @@ static void init_test()
                      FILE_ATTRIBUTE_NORMAL, NULL);
         if (hfile==INVALID_HANDLE_VALUE)
         {
-            trace("unable to create '%s': err=%ld\n", filename, GetLastError());
+            trace("unable to create '%s': err=%d\n", filename, GetLastError());
             assert(0);
         }
         CloseHandle(hfile);
@@ -409,7 +1454,21 @@ static void init_test()
     sprintf(filename, "%s\\test file.shlexec", tmpdir);
     desc.path=filename;
     desc.pidl=NULL;
-    desc.arguments="";
+    desc.arguments="ignored";
+    desc.showcmd=0;
+    desc.icon=NULL;
+    desc.icon_id=0;
+    desc.hotkey=0;
+    create_lnk(lnkfile, &desc, 0);
+
+    sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
+    MultiByteToWideChar(CP_ACP, 0, filename, -1, lnkfile, sizeof(lnkfile)/sizeof(*lnkfile));
+    desc.description=NULL;
+    desc.workdir=NULL;
+    desc.path=argv0;
+    desc.pidl=NULL;
+    sprintf(params, "shlexec \"%s\" Lnk", child_file);
+    desc.arguments=params;
     desc.showcmd=0;
     desc.icon=NULL;
     desc.icon_id=0;
@@ -418,10 +1477,15 @@ static void init_test()
 
     /* Create a basic association suitable for most tests */
     create_test_association(".shlexec");
-    create_test_verb(".shlexec", "Open");
+    create_test_verb(".shlexec", "Open", 0, "Open \"%1\"");
+    create_test_verb(".shlexec", "NoQuotes", 0, "NoQuotes %1");
+    create_test_verb(".shlexec", "LowerL", 0, "LowerL %l");
+    create_test_verb(".shlexec", "QuotedLowerL", 0, "QuotedLowerL \"%l\"");
+    create_test_verb(".shlexec", "UpperL", 0, "UpperL %L");
+    create_test_verb(".shlexec", "QuotedUpperL", 0, "QuotedUpperL \"%L\"");
 }
 
-static void cleanup_test()
+static void cleanup_test(void)
 {
     char filename[MAX_PATH];
     const char* const * testfile;
@@ -434,10 +1498,13 @@ static void cleanup_test()
         DeleteFile(filename);
         testfile++;
     }
+    DeleteFile(child_file);
 
     /* Delete the test association */
     delete_test_association(".shlexec");
 
+    CloseHandle(hEvent);
+
     CoUninitialize();
 }
 
@@ -445,18 +1512,21 @@ START_TEST(shlexec)
 {
 
     myARGC = winetest_get_mainargs(&myARGV);
-    if (myARGC>=3)
+    if (myARGC >= 3)
     {
-        /* FIXME: We should dump the parameters we got
-         *        and have the parent verify them
-         */
+        doChild(myARGC, myARGV);
         exit(0);
     }
 
     init_test();
 
     test_filename();
+    test_find_executable();
+    test_lnks();
     test_exes();
+    test_exes_long();
+    test_dde();
+    test_dde_default_app();
 
     cleanup_test();
 }
index 03b197d..7c5354b 100644 (file)
  *
  * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include <stdarg.h>
 #include <stdio.h>
 
 #define WINE_NOWINSOCK
-#include "windef.h"
-#include "winbase.h"
-#include "wtypes.h"
+#include <windows.h>
 #include "shellapi.h"
 #include "shlobj.h"
 
 #include "wine/test.h"
 
-CHAR CURR_DIR[MAX_PATH];
+#ifndef FOF_NORECURSION
+#define FOF_NORECURSION 0x1000
+#endif
+
+static CHAR CURR_DIR[MAX_PATH];
+static const WCHAR UNICODE_PATH[] = {'c',':','\\',0x00c4,'\0','\0'};
+    /* "c:\Ä", or "c:\A" with diaeresis */
+    /* Double-null termination needed for pFrom field of SHFILEOPSTRUCT */
 
 static HMODULE hshell32;
 static int (WINAPI *pSHCreateDirectoryExA)(HWND, LPCSTR, LPSECURITY_ATTRIBUTES);
+static int (WINAPI *pSHCreateDirectoryExW)(HWND, LPCWSTR, LPSECURITY_ATTRIBUTES);
 
 static void InitFunctionPointers(void)
 {
     hshell32 = GetModuleHandleA("shell32.dll");
-
-    if(hshell32)
-       pSHCreateDirectoryExA = (void*)GetProcAddress(hshell32, "SHCreateDirectoryExA");
+    pSHCreateDirectoryExA = (void*)GetProcAddress(hshell32, "SHCreateDirectoryExA");
+    pSHCreateDirectoryExW = (void*)GetProcAddress(hshell32, "SHCreateDirectoryExW");
 }
 
 /* creates a file with the specified name for tests */
@@ -56,11 +61,40 @@ static void createTestFile(const CHAR *name)
     CloseHandle(file);
 }
 
+static void createTestFileW(const WCHAR *name)
+{
+    HANDLE file;
+
+    file = CreateFileW(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "Failure to open file\n");
+    CloseHandle(file);
+}
+
 static BOOL file_exists(const CHAR *name)
 {
     return GetFileAttributesA(name) != INVALID_FILE_ATTRIBUTES;
 }
 
+static BOOL file_existsW(LPCWSTR name)
+{
+  return GetFileAttributesW(name) != INVALID_FILE_ATTRIBUTES;
+}
+
+static BOOL file_has_content(const CHAR *name, const CHAR *content)
+{
+    CHAR buf[MAX_PATH];
+    HANDLE file;
+    DWORD read;
+
+    file = CreateFileA(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+    if (file == INVALID_HANDLE_VALUE)
+        return FALSE;
+    ReadFile(file, buf, MAX_PATH - 1, &read, NULL);
+    buf[read] = 0;
+    CloseHandle(file);
+    return strcmp(buf, content)==0;
+}
+
 /* initializes the tests */
 static void init_shfo_tests(void)
 {
@@ -72,31 +106,125 @@ static void init_shfo_tests(void)
     if(len && (CURR_DIR[len-1] == '\\'))
         CURR_DIR[len-1] = 0;
 
-    createTestFile(".\\test1.txt");
-    createTestFile(".\\test2.txt");
-    createTestFile(".\\test3.txt");
-    CreateDirectoryA(".\\test4.txt", NULL);
-    CreateDirectoryA(".\\testdir2", NULL);
+    createTestFile("test1.txt");
+    createTestFile("test2.txt");
+    createTestFile("test3.txt");
+    createTestFile("test_5.txt");
+    CreateDirectoryA("test4.txt", NULL);
+    CreateDirectoryA("testdir2", NULL);
+    CreateDirectoryA("testdir2\\nested", NULL);
+    createTestFile("testdir2\\one.txt");
+    createTestFile("testdir2\\nested\\two.txt");
 }
 
 /* cleans after tests */
 static void clean_after_shfo_tests(void)
 {
-    DeleteFileA(".\\test1.txt");
-    DeleteFileA(".\\test2.txt");
-    DeleteFileA(".\\test3.txt");
-    DeleteFileA(".\\test4.txt\\test1.txt");
-    DeleteFileA(".\\test4.txt\\test2.txt");
-    DeleteFileA(".\\test4.txt\\test3.txt");
-    RemoveDirectoryA(".\\test4.txt");
-    DeleteFileA(".\\testdir2\\test1.txt");
-    DeleteFileA(".\\testdir2\\test2.txt");
-    DeleteFileA(".\\testdir2\\test3.txt");
-    DeleteFileA(".\\testdir2\\test4.txt\\test1.txt");
-    RemoveDirectoryA(".\\testdir2\\test4.txt");
-    RemoveDirectoryA(".\\testdir2");
+    DeleteFileA("test1.txt");
+    DeleteFileA("test2.txt");
+    DeleteFileA("test3.txt");
+    DeleteFileA("test_5.txt");
+    DeleteFileA("one.txt");
+    DeleteFileA("test4.txt\\test1.txt");
+    DeleteFileA("test4.txt\\test2.txt");
+    DeleteFileA("test4.txt\\test3.txt");
+    RemoveDirectoryA("test4.txt");
+    DeleteFileA("testdir2\\one.txt");
+    DeleteFileA("testdir2\\test1.txt");
+    DeleteFileA("testdir2\\test2.txt");
+    DeleteFileA("testdir2\\test3.txt");
+    DeleteFileA("testdir2\\test4.txt\\test1.txt");
+    DeleteFileA("testdir2\\nested\\two.txt");
+    RemoveDirectoryA("testdir2\\test4.txt");
+    RemoveDirectoryA("testdir2\\nested");
+    RemoveDirectoryA("testdir2");
+    RemoveDirectoryA("c:\\testdir3");
+    DeleteFileA("nonexistent\\notreal\\test2.txt");
+    RemoveDirectoryA("nonexistent\\notreal");
+    RemoveDirectoryA("nonexistent");
 }
 
+
+static void test_get_file_info(void)
+{
+    DWORD rc, rc2;
+    SHFILEINFO shfi, shfi2;
+    char notepad[MAX_PATH];
+
+    /* Test some flag combinations that MSDN claims are not allowed,
+     * but which work anyway
+     */
+    shfi.dwAttributes=0xdeadbeef;
+    rc=SHGetFileInfoA("c:\\nonexistent", FILE_ATTRIBUTE_DIRECTORY,
+                      &shfi, sizeof(shfi),
+                      SHGFI_ATTRIBUTES | SHGFI_USEFILEATTRIBUTES);
+    todo_wine ok(rc, "SHGetFileInfoA(c:\\nonexistent | SHGFI_ATTRIBUTES) failed\n");
+    if (rc)
+        ok(shfi.dwAttributes != 0xdeadbeef, "dwFileAttributes is not set\n");
+
+    rc=SHGetFileInfoA("c:\\nonexistent", FILE_ATTRIBUTE_DIRECTORY,
+                      &shfi, sizeof(shfi),
+                      SHGFI_EXETYPE | SHGFI_USEFILEATTRIBUTES);
+    todo_wine ok(rc == 1, "SHGetFileInfoA(c:\\nonexistent | SHGFI_EXETYPE) returned %d\n", rc);
+
+    /* Test SHGFI_USEFILEATTRIBUTES support */
+    strcpy(shfi.szDisplayName, "dummy");
+    shfi.iIcon=0xdeadbeef;
+    rc=SHGetFileInfoA("c:\\nonexistent", FILE_ATTRIBUTE_DIRECTORY,
+                      &shfi, sizeof(shfi),
+                      SHGFI_ICONLOCATION | SHGFI_USEFILEATTRIBUTES);
+    ok(rc, "SHGetFileInfoA(c:\\nonexistent) failed\n");
+    if (rc)
+    {
+        ok(strcpy(shfi.szDisplayName, "dummy") != 0, "SHGetFileInfoA(c:\\nonexistent) displayname is not set\n");
+        ok(shfi.iIcon != 0xdeadbeef, "SHGetFileInfoA(c:\\nonexistent) iIcon is not set\n");
+    }
+
+    /* Wine does not have a default icon for text files, and Windows 98 fails
+     * if we give it an empty executable. So use notepad.exe as the test
+     */
+    if (SearchPath(NULL, "notepad.exe", NULL, sizeof(notepad), notepad, NULL))
+    {
+        strcpy(shfi.szDisplayName, "dummy");
+        shfi.iIcon=0xdeadbeef;
+        rc=SHGetFileInfoA(notepad, GetFileAttributes(notepad),
+                          &shfi, sizeof(shfi),
+                          SHGFI_ICONLOCATION | SHGFI_USEFILEATTRIBUTES);
+        ok(rc, "SHGetFileInfoA(%s, SHGFI_USEFILEATTRIBUTES) failed\n", notepad);
+        strcpy(shfi2.szDisplayName, "dummy");
+        shfi2.iIcon=0xdeadbeef;
+        rc2=SHGetFileInfoA(notepad, 0,
+                           &shfi2, sizeof(shfi2),
+                           SHGFI_ICONLOCATION);
+        ok(rc2, "SHGetFileInfoA(%s) failed\n", notepad);
+        if (rc && rc2)
+        {
+            ok(lstrcmpi(shfi2.szDisplayName, shfi.szDisplayName) == 0, "wrong display name %s != %s\n", shfi.szDisplayName, shfi2.szDisplayName);
+            ok(shfi2.iIcon == shfi.iIcon, "wrong icon index %d != %d\n", shfi.iIcon, shfi2.iIcon);
+        }
+    }
+
+    /* with a directory now */
+    strcpy(shfi.szDisplayName, "dummy");
+    shfi.iIcon=0xdeadbeef;
+    rc=SHGetFileInfoA("test4.txt", GetFileAttributes("test4.txt"),
+                      &shfi, sizeof(shfi),
+                      SHGFI_ICONLOCATION | SHGFI_USEFILEATTRIBUTES);
+    ok(rc, "SHGetFileInfoA(test4.txt/, SHGFI_USEFILEATTRIBUTES) failed\n");
+    strcpy(shfi2.szDisplayName, "dummy");
+    shfi2.iIcon=0xdeadbeef;
+    rc2=SHGetFileInfoA("test4.txt", 0,
+                      &shfi2, sizeof(shfi2),
+                      SHGFI_ICONLOCATION);
+    ok(rc2, "SHGetFileInfoA(test4.txt/) failed\n");
+    if (rc && rc2)
+    {
+        ok(lstrcmpi(shfi2.szDisplayName, shfi.szDisplayName) == 0, "wrong display name %s != %s\n", shfi.szDisplayName, shfi2.szDisplayName);
+        ok(shfi2.iIcon == shfi.iIcon, "wrong icon index %d != %d\n", shfi.iIcon, shfi2.iIcon);
+    }
+}
+
+
 /*
  puts into the specified buffer file names with current directory.
  files - string with file names, separated by null characters. Ends on a double
@@ -124,7 +252,7 @@ static void test_delete(void)
 {
     SHFILEOPSTRUCTA shfo;
     DWORD ret;
-    CHAR buf[MAX_PATH];
+    CHAR buf[sizeof(CURR_DIR)+sizeof("/test?.txt")+1];
 
     sprintf(buf, "%s\\%s", CURR_DIR, "test?.txt");
     buf[strlen(buf) + 1] = '\0';
@@ -137,43 +265,105 @@ static void test_delete(void)
     shfo.hNameMappings = NULL;
     shfo.lpszProgressTitle = NULL;
 
-    ok(!SHFileOperationA(&shfo), "Deletion was successful\n");
-    ok(file_exists(".\\test4.txt"), "Directory should not be removed\n");
-    ok(!file_exists(".\\test1.txt"), "File should be removed\n");
+    ok(!SHFileOperationA(&shfo), "Deletion was not successful\n");
+    ok(file_exists("test4.txt"), "Directory should not have been removed\n");
+    ok(!file_exists("test1.txt"), "File should have been removed\n");
 
     ret = SHFileOperationA(&shfo);
-    ok(!ret, "Directory exists, but is not removed, ret=%ld\n", ret);
-    ok(file_exists(".\\test4.txt"), "Directory should not be removed\n");
+    ok(!ret, "Directory exists, but is not removed, ret=%d\n", ret);
+    ok(file_exists("test4.txt"), "Directory should not have been removed\n");
 
     shfo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
 
-    ok(!SHFileOperationA(&shfo), "Directory removed\n");
-    ok(!file_exists(".\\test4.txt"), "Directory should be removed\n");
+    ok(!SHFileOperationA(&shfo), "Directory is not removed\n");
+    ok(!file_exists("test4.txt"), "Directory should have been removed\n");
 
     ret = SHFileOperationA(&shfo);
-    ok(!ret, "The requested file does not exist, ret=%ld\n", ret);
+    ok(!ret, "The requested file does not exist, ret=%d\n", ret);
 
     init_shfo_tests();
     sprintf(buf, "%s\\%s", CURR_DIR, "test4.txt");
     buf[strlen(buf) + 1] = '\0';
-    ok(MoveFileA(".\\test1.txt", ".\\test4.txt\\test1.txt"), "Fill the subdirectory\n");
-    ok(!SHFileOperationA(&shfo), "Directory removed\n");
-    ok(!file_exists(".\\test4.txt"), "Directory is removed\n");
+    ok(MoveFileA("test1.txt", "test4.txt\\test1.txt"), "Filling the subdirectory failed\n");
+    ok(!SHFileOperationA(&shfo), "Directory is not removed\n");
+    ok(!file_exists("test4.txt"), "Directory is not removed\n");
+
+    init_shfo_tests();
+    shfo.pFrom = "test1.txt\0test4.txt\0";
+    ok(!SHFileOperationA(&shfo), "Directory and a file are not removed\n");
+    ok(!file_exists("test1.txt"), "The file should have been removed\n");
+    ok(!file_exists("test4.txt"), "Directory should have been removed\n");
+    ok(file_exists("test2.txt"), "This file should not have been removed\n");
+
+    /* FOF_FILESONLY does not delete a dir matching a wildcard */
+    init_shfo_tests();
+    shfo.fFlags |= FOF_FILESONLY;
+    shfo.pFrom = "*.txt\0";
+    ok(!SHFileOperation(&shfo), "Failed to delete files\n");
+    ok(!file_exists("test1.txt"), "test1.txt should have been removed\n");
+    ok(!file_exists("test_5.txt"), "test_5.txt should have been removed\n");
+    ok(file_exists("test4.txt"), "test4.txt should not have been removed\n");
+
+    /* FOF_FILESONLY only deletes a dir if explicitly specified */
+    init_shfo_tests();
+    shfo.pFrom = "test_?.txt\0test4.txt\0";
+    ok(!SHFileOperation(&shfo), "Failed to delete files and directory\n");
+    ok(!file_exists("test4.txt"), "test4.txt should have been removed\n");
+    ok(!file_exists("test_5.txt"), "test_5.txt should have been removed\n");
+    ok(file_exists("test1.txt"), "test1.txt should not have been removed\n");
+
+    /* try to delete an invalid filename */
+    init_shfo_tests();
+    shfo.pFrom = "\0";
+    shfo.fFlags &= ~FOF_FILESONLY;
+    shfo.fAnyOperationsAborted = FALSE;
+    ret = SHFileOperation(&shfo);
+    ok(ret == ERROR_ACCESS_DENIED, "Expected ERROR_ACCESS_DENIED, got %d\n", ret);
+    ok(!shfo.fAnyOperationsAborted, "Expected no aborted operations\n");
+    ok(file_exists("test1.txt"), "Expected test1.txt to exist\n");
+
+    /* try an invalid function */
+    init_shfo_tests();
+    shfo.pFrom = "test1.txt\0";
+    shfo.wFunc = 0;
+    ret = SHFileOperation(&shfo);
+    ok(ret == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", ret);
+    ok(file_exists("test1.txt"), "Expected test1.txt to exist\n");
+
+    /* try an invalid list, only one null terminator */
+    init_shfo_tests();
+    shfo.pFrom = "";
+    shfo.wFunc = FO_DELETE;
+    ret = SHFileOperation(&shfo);
+    ok(ret == ERROR_ACCESS_DENIED, "Expected ERROR_ACCESS_DENIED, got %d\n", ret);
+    ok(file_exists("test1.txt"), "Expected test1.txt to exist\n");
+
+    /* delete a dir, and then a file inside the dir, same as
+    * deleting a nonexistent file
+    */
+    init_shfo_tests();
+    shfo.pFrom = "testdir2\0testdir2\\one.txt\0";
+    ret = SHFileOperation(&shfo);
+    ok(ret == ERROR_PATH_NOT_FOUND, "Expected ERROR_PATH_NOT_FOUND, got %d\n", ret);
+    ok(!file_exists("testdir2"), "Expected testdir2 to not exist\n");
+    ok(!file_exists("testdir2\\one.txt"), "Expected testdir2\\one.txt to not exist\n");
 
+    /* try the FOF_NORECURSION flag, continues deleting subdirs */
     init_shfo_tests();
-    shfo.pFrom = ".\\test1.txt\0.\\test4.txt\0";
-    ok(!SHFileOperationA(&shfo), "Directory and a file removed\n");
-    ok(!file_exists(".\\test1.txt"), "The file should be removed\n");
-    ok(!file_exists(".\\test4.txt"), "Directory should be removed\n");
-    ok(file_exists(".\\test2.txt"), "This file should not be removed\n");
+    shfo.pFrom = "testdir2\0";
+    shfo.fFlags |= FOF_NORECURSION;
+    ret = SHFileOperation(&shfo);
+    ok(ret == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", ret);
+    ok(!file_exists("testdir2\\one.txt"), "Expected testdir2\\one.txt to not exist\n");
+    ok(!file_exists("testdir2\\nested"), "Expected testdir2\\nested to exist\n");
 }
 
 /* tests the FO_RENAME action */
 static void test_rename(void)
 {
     SHFILEOPSTRUCTA shfo, shfo2;
-    CHAR from[MAX_PATH];
-    CHAR to[MAX_PATH];
+    CHAR from[5*MAX_PATH];
+    CHAR to[5*MAX_PATH];
     DWORD retval;
 
     shfo.hwnd = NULL;
@@ -188,19 +378,19 @@ static void test_rename(void)
     set_curr_dir_path(to, "test4.txt\0");
     ok(SHFileOperationA(&shfo), "File is not renamed moving to other directory "
        "when specifying directory name only\n");
-    ok(file_exists(".\\test1.txt"), "The file is removed\n");
+    ok(file_exists("test1.txt"), "The file is removed\n");
 
     set_curr_dir_path(from, "test3.txt\0");
     set_curr_dir_path(to, "test4.txt\\test1.txt\0");
     ok(!SHFileOperationA(&shfo), "File is renamed moving to other directory\n");
-    ok(file_exists(".\\test4.txt\\test1.txt"), "The file is not renamed\n");
+    ok(file_exists("test4.txt\\test1.txt"), "The file is not renamed\n");
 
     set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
     set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
     retval = SHFileOperationA(&shfo); /* W98 returns 0, W2K and newer returns ERROR_GEN_FAILURE, both do nothing */
     ok(!retval || retval == ERROR_GEN_FAILURE || retval == ERROR_INVALID_TARGET_HANDLE,
-       "Can't rename many files, retval = %ld\n", retval);
-    ok(file_exists(".\\test1.txt"), "The file is renamed - many files are specified\n");
+       "Can't rename many files, retval = %d\n", retval);
+    ok(file_exists("test1.txt"), "The file is renamed - many files are specified\n");
 
     memcpy(&shfo2, &shfo, sizeof(SHFILEOPSTRUCTA));
     shfo2.fFlags |= FOF_MULTIDESTFILES;
@@ -209,40 +399,80 @@ static void test_rename(void)
     set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
     retval = SHFileOperationA(&shfo2); /* W98 returns 0, W2K and newer returns ERROR_GEN_FAILURE, both do nothing */
     ok(!retval || retval == ERROR_GEN_FAILURE || retval == ERROR_INVALID_TARGET_HANDLE,
-       "Can't rename many files, retval = %ld\n", retval);
-    ok(file_exists(".\\test1.txt"), "The file is not renamed - many files are specified\n");
+       "Can't rename many files, retval = %d\n", retval);
+    ok(file_exists("test1.txt"), "The file is not renamed - many files are specified\n");
 
     set_curr_dir_path(from, "test1.txt\0");
     set_curr_dir_path(to, "test6.txt\0");
     retval = SHFileOperationA(&shfo);
-    ok(!retval, "Rename file failed, retval = %ld\n", retval);
-    ok(!file_exists(".\\test1.txt"), "The file is not renamed\n");
-    ok(file_exists(".\\test6.txt"), "The file is not renamed\n");
+    ok(!retval, "Rename file failed, retval = %d\n", retval);
+    ok(!file_exists("test1.txt"), "The file is not renamed\n");
+    ok(file_exists("test6.txt"), "The file is not renamed\n");
 
     set_curr_dir_path(from, "test6.txt\0");
     set_curr_dir_path(to, "test1.txt\0");
     retval = SHFileOperationA(&shfo);
-    ok(!retval, "Rename file back failed, retval = %ld\n", retval);
+    ok(!retval, "Rename file back failed, retval = %d\n", retval);
 
     set_curr_dir_path(from, "test4.txt\0");
     set_curr_dir_path(to, "test6.txt\0");
     retval = SHFileOperationA(&shfo);
-    ok(!retval, "Rename dir failed, retval = %ld\n", retval);
-    ok(!file_exists(".\\test4.txt"), "The dir is not renamed\n");
-    ok(file_exists(".\\test6.txt"), "The dir is not renamed\n");
+    ok(!retval, "Rename dir failed, retval = %d\n", retval);
+    ok(!file_exists("test4.txt"), "The dir is not renamed\n");
+    ok(file_exists("test6.txt"), "The dir is not renamed\n");
 
     set_curr_dir_path(from, "test6.txt\0");
     set_curr_dir_path(to, "test4.txt\0");
     retval = SHFileOperationA(&shfo);
-    ok(!retval, "Rename dir back failed, retval = %ld\n", retval);
+    ok(!retval, "Rename dir back failed, retval = %d\n", retval);
+
+    /* try to rename more than one file to a single file */
+    shfo.pFrom = "test1.txt\0test2.txt\0";
+    shfo.pTo = "a.txt\0";
+    retval = SHFileOperationA(&shfo);
+    ok(retval == ERROR_GEN_FAILURE, "Expected ERROR_GEN_FAILURE, got %d\n", retval);
+    ok(file_exists("test1.txt"), "Expected test1.txt to exist\n");
+    ok(file_exists("test2.txt"), "Expected test2.txt to exist\n");
+
+    /* pFrom doesn't exist */
+    shfo.pFrom = "idontexist\0";
+    shfo.pTo = "newfile\0";
+    retval = SHFileOperationA(&shfo);
+    ok(retval == 1026, "Expected 1026, got %d\n", retval);
+    ok(!file_exists("newfile"), "Expected newfile to not exist\n");
+
+    /* pTo already exist */
+    shfo.pFrom = "test1.txt\0";
+    shfo.pTo = "test2.txt\0";
+    retval = SHFileOperationA(&shfo);
+        ok(retval == ERROR_ALREADY_EXISTS, "Expected ERROR_ALREADY_EXISTS, got %d\n", retval);
+
+    /* pFrom is valid, but pTo is empty */
+    shfo.pFrom = "test1.txt\0";
+    shfo.pTo = "\0";
+    retval = SHFileOperationA(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+    ok(file_exists("test1.txt"), "Expected test1.txt to exist\n");
+
+    /* pFrom is empty */
+    shfo.pFrom = "\0";
+    retval = SHFileOperationA(&shfo);
+        ok(retval == ERROR_ACCESS_DENIED, "Expected ERROR_ACCESS_DENIED, got %d\n", retval);
+
+    /* pFrom is NULL, commented out because it crashes on nt 4.0 */
+#if 0
+    shfo.pFrom = NULL;
+    retval = SHFileOperationA(&shfo);
+    ok(retval == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", retval);
+#endif
 }
 
 /* tests the FO_COPY action */
 static void test_copy(void)
 {
     SHFILEOPSTRUCTA shfo, shfo2;
-    CHAR from[MAX_PATH];
-    CHAR to[MAX_PATH];
+    CHAR from[5*MAX_PATH];
+    CHAR to[5*MAX_PATH];
     FILEOP_FLAGS tmp_flags;
     DWORD retval;
 
@@ -257,7 +487,7 @@ static void test_copy(void)
     set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
     set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
     ok(SHFileOperationA(&shfo), "Can't copy many files\n");
-    ok(!file_exists(".\\test6.txt"), "The file is not copied - many files are "
+    ok(!file_exists("test6.txt"), "The file is not copied - many files are "
        "specified as a target\n");
 
     memcpy(&shfo2, &shfo, sizeof(SHFILEOPSTRUCTA));
@@ -266,50 +496,50 @@ static void test_copy(void)
     set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
     set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
     ok(!SHFileOperationA(&shfo2), "Can't copy many files\n");
-    ok(file_exists(".\\test6.txt"), "The file is copied - many files are "
+    ok(file_exists("test6.txt"), "The file is copied - many files are "
        "specified as a target\n");
-    DeleteFileA(".\\test6.txt");
-    DeleteFileA(".\\test7.txt");
-    RemoveDirectoryA(".\\test8.txt");
+    DeleteFileA("test6.txt");
+    DeleteFileA("test7.txt");
+    RemoveDirectoryA("test8.txt");
 
     /* number of sources do not correspond to number of targets */
     set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
     set_curr_dir_path(to, "test6.txt\0test7.txt\0");
     ok(SHFileOperationA(&shfo2), "Can't copy many files\n");
-    ok(!file_exists(".\\test6.txt"), "The file is not copied - many files are "
+    ok(!file_exists("test6.txt"), "The file is not copied - many files are "
        "specified as a target\n");
 
     set_curr_dir_path(from, "test1.txt\0");
     set_curr_dir_path(to, "test4.txt\0");
     ok(!SHFileOperationA(&shfo), "Prepare test to check how directories are copied recursively\n");
-    ok(file_exists(".\\test4.txt\\test1.txt"), "The file is copied\n");
+    ok(file_exists("test4.txt\\test1.txt"), "The file is copied\n");
 
     set_curr_dir_path(from, "test?.txt\0");
     set_curr_dir_path(to, "testdir2\0");
-    ok(!file_exists(".\\testdir2\\test1.txt"), "The file is not copied yet\n");
-    ok(!file_exists(".\\testdir2\\test4.txt"), "The directory is not copied yet\n");
+    ok(!file_exists("testdir2\\test1.txt"), "The file is not copied yet\n");
+    ok(!file_exists("testdir2\\test4.txt"), "The directory is not copied yet\n");
     ok(!SHFileOperationA(&shfo), "Files and directories are copied to directory\n");
-    ok(file_exists(".\\testdir2\\test1.txt"), "The file is copied\n");
-    ok(file_exists(".\\testdir2\\test4.txt"), "The directory is copied\n");
-    ok(file_exists(".\\testdir2\\test4.txt\\test1.txt"), "The file in subdirectory is copied\n");
+    ok(file_exists("testdir2\\test1.txt"), "The file is copied\n");
+    ok(file_exists("testdir2\\test4.txt"), "The directory is copied\n");
+    ok(file_exists("testdir2\\test4.txt\\test1.txt"), "The file in subdirectory is copied\n");
     clean_after_shfo_tests();
 
     init_shfo_tests();
     shfo.fFlags |= FOF_FILESONLY;
-    ok(!file_exists(".\\testdir2\\test1.txt"), "The file is not copied yet\n");
-    ok(!file_exists(".\\testdir2\\test4.txt"), "The directory is not copied yet\n");
+    ok(!file_exists("testdir2\\test1.txt"), "The file is not copied yet\n");
+    ok(!file_exists("testdir2\\test4.txt"), "The directory is not copied yet\n");
     ok(!SHFileOperationA(&shfo), "Files are copied to other directory\n");
-    ok(file_exists(".\\testdir2\\test1.txt"), "The file is copied\n");
-    ok(!file_exists(".\\testdir2\\test4.txt"), "The directory is copied\n");
+    ok(file_exists("testdir2\\test1.txt"), "The file is copied\n");
+    ok(!file_exists("testdir2\\test4.txt"), "The directory is copied\n");
     clean_after_shfo_tests();
 
     init_shfo_tests();
     set_curr_dir_path(from, "test1.txt\0test2.txt\0");
-    ok(!file_exists(".\\testdir2\\test1.txt"), "The file is not copied yet\n");
-    ok(!file_exists(".\\testdir2\\test2.txt"), "The file is not copied yet\n");
-    ok(!SHFileOperationA(&shfo), "Files are copied to other directory \n");
-    ok(file_exists(".\\testdir2\\test1.txt"), "The file is copied\n");
-    ok(file_exists(".\\testdir2\\test2.txt"), "The file is copied\n");
+    ok(!file_exists("testdir2\\test1.txt"), "The file is not copied yet\n");
+    ok(!file_exists("testdir2\\test2.txt"), "The file is not copied yet\n");
+    ok(!SHFileOperationA(&shfo), "Files are copied to other directory\n");
+    ok(file_exists("testdir2\\test1.txt"), "The file is copied\n");
+    ok(file_exists("testdir2\\test2.txt"), "The file is copied\n");
     clean_after_shfo_tests();
 
     /* Copying multiple files with one not existing as source, fails the
@@ -317,28 +547,301 @@ static void test_copy(void)
     init_shfo_tests();
     tmp_flags = shfo.fFlags;
     set_curr_dir_path(from, "test1.txt\0test10.txt\0test2.txt\0");
-    ok(!file_exists(".\\testdir2\\test1.txt"), "The file is not copied yet\n");
-    ok(!file_exists(".\\testdir2\\test2.txt"), "The file is not copied yet\n");
+    ok(!file_exists("testdir2\\test1.txt"), "The file is not copied yet\n");
+    ok(!file_exists("testdir2\\test2.txt"), "The file is not copied yet\n");
     retval = SHFileOperationA(&shfo);
     if (!retval)
-      /* Win 95/NT returns success but copies only the files up to the nonexistent source */
-      ok(file_exists(".\\testdir2\\test1.txt"), "The file is not copied\n");
+        /* Win 95/NT returns success but copies only the files up to the nonexistent source */
+        ok(file_exists("testdir2\\test1.txt"), "The file is not copied\n");
     else
     {
-      /* Win 98/ME/2K/XP fail the entire operation with return code 1026 if one source file does not exist */
-      ok(retval == 1026, "Files are copied to other directory\n");
-      ok(!file_exists(".\\testdir2\\test1.txt"), "The file is copied\n");
+        /* Win 98/ME/2K/XP fail the entire operation with return code 1026 if one source file does not exist */
+        ok(retval == 1026, "Files are copied to other directory\n");
+        ok(!file_exists("testdir2\\test1.txt"), "The file is copied\n");
     }
-    ok(!file_exists(".\\testdir2\\test2.txt"), "The file is copied\n");
+    ok(!file_exists("testdir2\\test2.txt"), "The file is copied\n");
     shfo.fFlags = tmp_flags;
+
+    /* copy into a nonexistent directory */
+    init_shfo_tests();
+    shfo.fFlags = FOF_NOCONFIRMMKDIR;
+    set_curr_dir_path(from, "test1.txt\0");
+    set_curr_dir_path(to, "nonexistent\\notreal\\test2.txt\0");
+    retval= SHFileOperation(&shfo);
+        ok(!retval, "Error copying into nonexistent directory\n");
+        ok(file_exists("nonexistent"), "nonexistent not created\n");
+        ok(file_exists("nonexistent\\notreal"), "nonexistent\\notreal not created\n");
+        ok(file_exists("nonexistent\\notreal\\test2.txt"), "Directory not created\n");
+    ok(!file_exists("nonexistent\\notreal\\test1.txt"), "test1.txt should not exist\n");
+
+    /* a relative dest directory is OK */
+    clean_after_shfo_tests();
+    init_shfo_tests();
+    shfo.pFrom = "test1.txt\0test2.txt\0test3.txt\0";
+    shfo.pTo = "testdir2\0";
+    retval = SHFileOperation(&shfo);
+    ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+    ok(file_exists("testdir2\\test1.txt"), "Expected testdir2\\test1 to exist\n");
+
+    /* try to copy files to a file */
+    clean_after_shfo_tests();
+    init_shfo_tests();
+    shfo.pFrom = from;
+    shfo.pTo = to;
+    set_curr_dir_path(from, "test1.txt\0test2.txt\0");
+    set_curr_dir_path(to, "test3.txt\0");
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+    ok(shfo.fAnyOperationsAborted, "Expected aborted operations\n");
+    ok(!file_exists("test3.txt\\test2.txt"), "Expected test3.txt\\test2.txt to not exist\n");
+
+    /* try to copy many files to nonexistent directory */
+    DeleteFile(to);
+    shfo.fAnyOperationsAborted = FALSE;
+    retval = SHFileOperation(&shfo);
+        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");
+        ok(RemoveDirectory(to), "Expected test3.txt to exist\n");
+
+    /* send in FOF_MULTIDESTFILES with too many destination files */
+    init_shfo_tests();
+    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;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+    ok(shfo.fAnyOperationsAborted, "Expected aborted operations\n");
+    ok(!file_exists("testdir2\\a.txt"), "Expected testdir2\\a.txt to not exist\n");
+
+    /* 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;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+    ok(shfo.fAnyOperationsAborted, "Expected aborted operations\n");
+    ok(!file_exists("e.txt"), "Expected e.txt to not exist\n");
+
+    /* 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;
+    retval = SHFileOperation(&shfo);
+    ok(retval == ERROR_SUCCESS, "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");
+
+    /* 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.fFlags &= ~FOF_MULTIDESTFILES;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+    ok(!file_exists("a.txt"), "Expected a.txt to not exist\n");
+
+    /* try a glob */
+    shfo.pFrom = "test?.txt\0";
+    shfo.pTo = "testdir2\0";
+    shfo.fFlags &= ~FOF_MULTIDESTFILES;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_SUCCESS, "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 */
+    clean_after_shfo_tests();
+    init_shfo_tests();
+    shfo.pFrom = "test?.txt\0";
+    shfo.fFlags |= FOF_FILESONLY;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+        ok(file_exists("testdir2\\test1.txt"), "Expected testdir2\\test1.txt to exist\n");
+    ok(!file_exists("testdir2\\test4.txt"), "Expected testdir2\\test4.txt to not exist\n");
+
+    /* try a glob with FOF_MULTIDESTFILES and the same number
+    * of dest files that we would expect
+    */
+    clean_after_shfo_tests();
+    init_shfo_tests();
+    shfo.pTo = "testdir2\\a.txt\0testdir2\\b.txt\0testdir2\\c.txt\0testdir2\\d.txt\0";
+    shfo.fFlags &= ~FOF_FILESONLY;
+    shfo.fFlags |= FOF_MULTIDESTFILES;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+    ok(shfo.fAnyOperationsAborted, "Expected aborted operations\n");
+    ok(!file_exists("testdir2\\a.txt"), "Expected testdir2\\test1.txt to not exist\n");
+    ok(!RemoveDirectory("b.txt"), "b.txt should not exist\n");
+
+    /* copy one file to two others, second is ignored */
+    clean_after_shfo_tests();
+    init_shfo_tests();
+    shfo.pFrom = "test1.txt\0";
+    shfo.pTo = "b.txt\0c.txt\0";
+    shfo.fAnyOperationsAborted = FALSE;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+        ok(DeleteFile("b.txt"), "Expected b.txt to exist\n");
+    ok(!DeleteFile("c.txt"), "Expected c.txt to not exist\n");
+
+    /* copy two file to three others, all fail */
+    shfo.pFrom = "test1.txt\0test2.txt\0";
+    shfo.pTo = "b.txt\0c.txt\0d.txt\0";
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+    ok(shfo.fAnyOperationsAborted, "Expected operations to be aborted\n");
+    ok(!DeleteFile("b.txt"), "Expected b.txt to not exist\n");
+
+    /* 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;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+    ok(shfo.fAnyOperationsAborted, "Expected operations to be aborted\n");
+    ok(!DeleteFile("b.txt"), "Expected b.txt to not exist\n");
+    ok(!DeleteFile("c.txt"), "Expected c.txt to not exist\n");
+
+    /* copy a directory with a file beneath it, plus some files */
+    createTestFile("test4.txt\\a.txt");
+    shfo.pFrom = "test4.txt\0test1.txt\0";
+    shfo.pTo = "testdir2\0";
+    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 newdir\\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");
+
+    /* copy a file in a directory first, and then the directory to a nonexistent dir */
+    shfo.pFrom = "test4.txt\\a.txt\0test4.txt\0";
+    shfo.pTo = "nonexistent\0";
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+    ok(shfo.fAnyOperationsAborted, "Expected operations to be aborted\n");
+    ok(!file_exists("nonexistent\\test4.txt"), "Expected nonexistent\\test4.txt to not exist\n");
+    DeleteFile("test4.txt\\a.txt");
+
+    /* destination is same as source file */
+    shfo.pFrom = "test1.txt\0test2.txt\0test3.txt\0";
+    shfo.pTo = "b.txt\0test2.txt\0c.txt\0";
+    shfo.fAnyOperationsAborted = FALSE;
+    shfo.fFlags = FOF_NOERRORUI | FOF_MULTIDESTFILES;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_NO_MORE_SEARCH_HANDLES,
+           "Expected ERROR_NO_MORE_SEARCH_HANDLES, got %d\n", retval);
+        ok(!shfo.fAnyOperationsAborted, "Expected no operations to be aborted\n");
+        ok(DeleteFile("b.txt"), "Expected b.txt to exist\n");
+    ok(!file_exists("c.txt"), "Expected c.txt to not exist\n");
+
+    /* destination is same as source directory */
+    shfo.pFrom = "test1.txt\0test4.txt\0test3.txt\0";
+    shfo.pTo = "b.txt\0test4.txt\0c.txt\0";
+    shfo.fAnyOperationsAborted = FALSE;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+        ok(DeleteFile("b.txt"), "Expected b.txt to exist\n");
+    ok(!file_exists("c.txt"), "Expected c.txt to not exist\n");
+
+    /* copy a directory into itself, error displayed in UI */
+    shfo.pFrom = "test4.txt\0";
+    shfo.pTo = "test4.txt\\newdir\0";
+    shfo.fFlags &= ~FOF_MULTIDESTFILES;
+    shfo.fAnyOperationsAborted = FALSE;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+    ok(!RemoveDirectory("test4.txt\\newdir"), "Expected test4.txt\\newdir to not exist\n");
+
+    /* copy a directory to itself, error displayed in UI */
+    shfo.pFrom = "test4.txt\0";
+    shfo.pTo = "test4.txt\0";
+    shfo.fAnyOperationsAborted = FALSE;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+
+    /* copy a file into a directory, and the directory into itself */
+    shfo.pFrom = "test1.txt\0test4.txt\0";
+    shfo.pTo = "test4.txt\0";
+    shfo.fAnyOperationsAborted = FALSE;
+    shfo.fFlags |= FOF_NOCONFIRMATION;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+    ok(DeleteFile("test4.txt\\test1.txt"), "Expected test4.txt\\test1.txt to exist\n");
+
+    /* copy a file to a file, and the directory into itself */
+    shfo.pFrom = "test1.txt\0test4.txt\0";
+    shfo.pTo = "test4.txt\\a.txt\0";
+    shfo.fAnyOperationsAborted = FALSE;
+    retval = SHFileOperation(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+    ok(!file_exists("test4.txt\\a.txt"), "Expected test4.txt\\a.txt to not exist\n");
+
+    /* copy a nonexistent file to a nonexistent directory */
+    shfo.pFrom = "e.txt\0";
+    shfo.pTo = "nonexistent\0";
+    shfo.fAnyOperationsAborted = FALSE;
+    retval = SHFileOperation(&shfo);
+    ok(retval == 1026, "Expected 1026, got %d\n", retval);
+    ok(!file_exists("nonexistent\\e.txt"), "Expected nonexistent\\e.txt to not exist\n");
+    ok(!file_exists("nonexistent"), "Expected nonexistent to not exist\n");
+
+    /* Overwrite tests */
+    clean_after_shfo_tests();
+    init_shfo_tests();
+    shfo.fFlags = FOF_NOCONFIRMATION;
+    shfo.pFrom = "test1.txt\0";
+    shfo.pTo = "test2.txt\0";
+    shfo.fAnyOperationsAborted = FALSE;
+    /* without FOF_NOCOFIRMATION the confirmation is Yes/No */
+    retval = SHFileOperation(&shfo);
+    ok(retval == 0, "Expected 0, got %d\n", retval);
+    ok(file_has_content("test2.txt", "test1.txt\n"), "The file was not copied\n");
+
+    shfo.pFrom = "test3.txt\0test1.txt\0";
+    shfo.pTo = "test2.txt\0one.txt\0";
+    shfo.fFlags = FOF_NOCONFIRMATION | FOF_MULTIDESTFILES;
+    /* without FOF_NOCOFIRMATION the confirmation is Yes/Yes to All/No/Cancel */
+    retval = SHFileOperation(&shfo);
+    ok(retval == 0, "Expected 0, got %d\n", retval);
+    ok(file_has_content("test2.txt", "test3.txt\n"), "The file was not copied\n");
+
+    shfo.pFrom = "one.txt\0";
+    shfo.pTo = "testdir2\0";
+    shfo.fFlags = FOF_NOCONFIRMATION;
+    /* without FOF_NOCOFIRMATION the confirmation is Yes/No */
+    retval = SHFileOperation(&shfo);
+    ok(retval == 0, "Expected 0, got %d\n", retval);
+    ok(file_has_content("testdir2\\one.txt", "test1.txt\n"), "The file was not copied\n");
+
+    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_NOCOFIRMATION 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");
 }
 
 /* tests the FO_MOVE action */
 static void test_move(void)
 {
     SHFILEOPSTRUCTA shfo, shfo2;
-    CHAR from[MAX_PATH];
-    CHAR to[MAX_PATH];
+    CHAR from[5*MAX_PATH];
+    CHAR to[5*MAX_PATH];
+    DWORD retval;
 
     shfo.hwnd = NULL;
     shfo.wFunc = FO_MOVE;
@@ -351,16 +854,17 @@ static void test_move(void)
     set_curr_dir_path(from, "test1.txt\0");
     set_curr_dir_path(to, "test4.txt\0");
     ok(!SHFileOperationA(&shfo), "Prepare test to check how directories are moved recursively\n");
-    ok(file_exists(".\\test4.txt\\test1.txt"), "The file is moved\n");
+    ok(!file_exists("test1.txt"), "test1.txt should not exist\n");
+    ok(file_exists("test4.txt\\test1.txt"), "The file is not moved\n");
 
     set_curr_dir_path(from, "test?.txt\0");
     set_curr_dir_path(to, "testdir2\0");
-    ok(!file_exists(".\\testdir2\\test2.txt"), "The file is not moved yet\n");
-    ok(!file_exists(".\\testdir2\\test4.txt"), "The directory is not moved yet\n");
+    ok(!file_exists("testdir2\\test2.txt"), "The file is not moved yet\n");
+    ok(!file_exists("testdir2\\test4.txt"), "The directory is not moved yet\n");
     ok(!SHFileOperationA(&shfo), "Files and directories are moved to directory\n");
-    ok(file_exists(".\\testdir2\\test2.txt"), "The file is moved\n");
-    ok(file_exists(".\\testdir2\\test4.txt"), "The directory is moved\n");
-    ok(file_exists(".\\testdir2\\test4.txt\\test1.txt"), "The file in subdirectory is moved\n");
+    ok(file_exists("testdir2\\test2.txt"), "The file is moved\n");
+    ok(file_exists("testdir2\\test4.txt"), "The directory is moved\n");
+    ok(file_exists("testdir2\\test4.txt\\test1.txt"), "The file in subdirectory is moved\n");
 
     clean_after_shfo_tests();
     init_shfo_tests();
@@ -371,11 +875,11 @@ static void test_move(void)
     set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
     set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
     ok(!SHFileOperationA(&shfo2), "Move many files\n");
-    ok(file_exists(".\\test6.txt"), "The file is moved - many files are "
+    ok(file_exists("test6.txt"), "The file is moved - many files are "
        "specified as a target\n");
-    DeleteFileA(".\\test6.txt");
-    DeleteFileA(".\\test7.txt");
-    RemoveDirectoryA(".\\test8.txt");
+    DeleteFileA("test6.txt");
+    DeleteFileA("test7.txt");
+    RemoveDirectoryA("test8.txt");
 
     init_shfo_tests();
 
@@ -383,7 +887,7 @@ static void test_move(void)
     set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
     set_curr_dir_path(to, "test6.txt\0test7.txt\0");
     ok(SHFileOperationA(&shfo2), "Can't move many files\n");
-    ok(!file_exists(".\\test6.txt"), "The file is not moved - many files are "
+    ok(!file_exists("test6.txt"), "The file is not moved - many files are "
        "specified as a target\n");
 
     init_shfo_tests();
@@ -391,19 +895,19 @@ static void test_move(void)
     set_curr_dir_path(from, "test3.txt\0");
     set_curr_dir_path(to, "test4.txt\\test1.txt\0");
     ok(!SHFileOperationA(&shfo), "File is moved moving to other directory\n");
-    ok(file_exists(".\\test4.txt\\test1.txt"), "The file is moved\n");
+    ok(file_exists("test4.txt\\test1.txt"), "The file is moved\n");
 
     set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
     set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
     ok(SHFileOperationA(&shfo), "Cannot move many files\n");
-    ok(file_exists(".\\test1.txt"), "The file is not moved. Many files are specified\n");
-    ok(file_exists(".\\test4.txt"), "The directory is not moved. Many files are specified\n");
+    ok(file_exists("test1.txt"), "The file is not moved. Many files are specified\n");
+    ok(file_exists("test4.txt"), "The directory is not moved. Many files are specified\n");
 
     set_curr_dir_path(from, "test1.txt\0");
     set_curr_dir_path(to, "test6.txt\0");
     ok(!SHFileOperationA(&shfo), "Move file\n");
-    ok(!file_exists(".\\test1.txt"), "The file is moved\n");
-    ok(file_exists(".\\test6.txt"), "The file is moved\n");
+    ok(!file_exists("test1.txt"), "The file is moved\n");
+    ok(file_exists("test6.txt"), "The file is moved\n");
     set_curr_dir_path(from, "test6.txt\0");
     set_curr_dir_path(to, "test1.txt\0");
     ok(!SHFileOperationA(&shfo), "Move file back\n");
@@ -411,11 +915,67 @@ static void test_move(void)
     set_curr_dir_path(from, "test4.txt\0");
     set_curr_dir_path(to, "test6.txt\0");
     ok(!SHFileOperationA(&shfo), "Move dir\n");
-    ok(!file_exists(".\\test4.txt"), "The dir is moved\n");
-    ok(file_exists(".\\test6.txt"), "The dir is moved\n");
+    ok(!file_exists("test4.txt"), "The dir is moved\n");
+    ok(file_exists("test6.txt"), "The dir is moved\n");
     set_curr_dir_path(from, "test6.txt\0");
     set_curr_dir_path(to, "test4.txt\0");
     ok(!SHFileOperationA(&shfo), "Move dir back\n");
+
+    /* move one file to two others */
+    init_shfo_tests();
+    shfo.pFrom = "test1.txt\0";
+    shfo.pTo = "a.txt\0b.txt\0";
+    retval = SHFileOperationA(&shfo);
+        ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+        ok(!file_exists("test1.txt"), "Expected test1.txt to not exist\n");
+        ok(DeleteFile("a.txt"), "Expected a.txt to exist\n");
+    ok(!file_exists("b.txt"), "Expected b.txt to not exist\n");
+
+    /* move two files to one other */
+    shfo.pFrom = "test2.txt\0test3.txt\0";
+    shfo.pTo = "test1.txt\0";
+    retval = SHFileOperationA(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+        ok(!file_exists("test1.txt"), "Expected test1.txt to not exist\n");
+    ok(file_exists("test2.txt"), "Expected test2.txt to exist\n");
+    ok(file_exists("test3.txt"), "Expected test3.txt to exist\n");
+
+    /* move a directory into itself */
+    shfo.pFrom = "test4.txt\0";
+    shfo.pTo = "test4.txt\\b.txt\0";
+    retval = SHFileOperationA(&shfo);
+        ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+    ok(!RemoveDirectory("test4.txt\\b.txt"), "Expected test4.txt\\b.txt to not exist\n");
+    ok(file_exists("test4.txt"), "Expected test4.txt to exist\n");
+
+    /* move many files without FOF_MULTIDESTFILES */
+    shfo.pFrom = "test2.txt\0test3.txt\0";
+    shfo.pTo = "d.txt\0e.txt\0";
+    retval = SHFileOperationA(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+    ok(!DeleteFile("d.txt"), "Expected d.txt to not exist\n");
+    ok(!DeleteFile("e.txt"), "Expected e.txt to not exist\n");
+
+    /* number of sources != number of targets */
+    shfo.pTo = "d.txt\0";
+    shfo.fFlags |= FOF_MULTIDESTFILES;
+    retval = SHFileOperationA(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+    ok(!DeleteFile("d.txt"), "Expected d.txt to not exist\n");
+
+    /* FO_MOVE does not create dest directories */
+    shfo.pFrom = "test2.txt\0";
+    shfo.pTo = "dir1\\dir2\\test2.txt\0";
+    retval = SHFileOperationA(&shfo);
+        ok(retval == ERROR_CANCELLED, "Expected ERROR_CANCELLED, got %d\n", retval);
+    ok(!file_exists("dir1"), "Expected dir1 to not exist\n");
+
+    /* try to overwrite an existing file */
+    shfo.pTo = "test3.txt\0";
+    retval = SHFileOperationA(&shfo);
+        ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+        ok(!file_exists("test2.txt"), "Expected test2.txt to not exist\n");
+    ok(file_exists("test3.txt"), "Expected test3.txt to exist\n");
 }
 
 static void test_sh_create_dir(void)
@@ -432,11 +992,81 @@ static void test_sh_create_dir(void)
     set_curr_dir_path(path, "testdir2\\test4.txt\0");
     ret = pSHCreateDirectoryExA(NULL, path, NULL);
     ok(ERROR_SUCCESS == ret, "SHCreateDirectoryEx failed to create directory recursively, ret = %d\n", ret);
-    ok(file_exists(".\\testdir2"), "The first directory is not created\n");
-    ok(file_exists(".\\testdir2\\test4.txt"), "The second directory is not created\n");
+    ok(file_exists("testdir2"), "The first directory is not created\n");
+    ok(file_exists("testdir2\\test4.txt"), "The second directory is not created\n");
 
     ret = pSHCreateDirectoryExA(NULL, path, NULL);
     ok(ERROR_ALREADY_EXISTS == ret, "SHCreateDirectoryEx should fail to create existing directory, ret = %d\n", ret);
+
+    ret = pSHCreateDirectoryExA(NULL, "c:\\testdir3", NULL);
+    ok(file_exists("c:\\testdir3"), "The directory is not created\n");
+}
+
+static void test_unicode(void)
+{
+    SHFILEOPSTRUCTW shfoW;
+    int ret;
+    HANDLE file;
+
+    shfoW.hwnd = NULL;
+    shfoW.wFunc = FO_DELETE;
+    shfoW.pFrom = UNICODE_PATH;
+    shfoW.pTo = '\0';
+    shfoW.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
+    shfoW.hNameMappings = NULL;
+    shfoW.lpszProgressTitle = NULL;
+
+    /* Clean up before start test */
+    DeleteFileW(UNICODE_PATH);
+    RemoveDirectoryW(UNICODE_PATH);
+
+    /* Make sure we are on a system that supports unicode */
+    SetLastError(0xdeadbeef);
+    file = CreateFileW(UNICODE_PATH, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+    if (GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+    {
+        skip("Unicode tests skipped on non-unicode system\n");
+        return;
+    }
+    CloseHandle(file);
+
+    /* Try to delete a file with unicode filename */
+    ok(file_existsW(UNICODE_PATH), "The file does not exist\n");
+    ret = SHFileOperationW(&shfoW);
+    ok(!ret, "File is not removed, ErrorCode: %d\n", ret);
+    ok(!file_existsW(UNICODE_PATH), "The file should have been removed\n");
+
+    /* Try to trash a file with unicode filename */
+    createTestFileW(UNICODE_PATH);
+    shfoW.fFlags |= FOF_ALLOWUNDO;
+    ok(file_existsW(UNICODE_PATH), "The file does not exist\n");
+    ret = SHFileOperationW(&shfoW);
+    ok(!ret, "File is not removed, ErrorCode: %d\n", ret);
+    ok(!file_existsW(UNICODE_PATH), "The file should have been removed\n");
+
+    if(!pSHCreateDirectoryExW)
+    {
+        skip("Skipping SHCreateDirectoryExW tests\n");
+        return;
+    }
+
+    /* Try to delete a directory with unicode filename */
+    ret = pSHCreateDirectoryExW(NULL, UNICODE_PATH, NULL);
+    ok(!ret, "SHCreateDirectoryExW returned %d\n", ret);
+    ok(file_existsW(UNICODE_PATH), "The directory is not created\n");
+    shfoW.fFlags &= ~FOF_ALLOWUNDO;
+    ret = SHFileOperationW(&shfoW);
+    ok(!ret, "Directory is not removed, ErrorCode: %d\n", ret);
+    ok(!file_existsW(UNICODE_PATH), "The directory should have been removed\n");
+
+    /* Try to trash a directory with unicode filename */
+    ret = pSHCreateDirectoryExW(NULL, UNICODE_PATH, NULL);
+    ok(!ret, "SHCreateDirectoryExW returned %d\n", ret);
+    ok(file_existsW(UNICODE_PATH), "The directory was not created\n");
+    shfoW.fFlags |= FOF_ALLOWUNDO;
+    ret = SHFileOperationW(&shfoW);
+    ok(!ret, "Directory is not removed, ErrorCode: %d\n", ret);
+    ok(!file_existsW(UNICODE_PATH), "The directory should have been removed\n");
 }
 
 START_TEST(shlfileop)
@@ -445,6 +1075,10 @@ START_TEST(shlfileop)
 
     clean_after_shfo_tests();
 
+    init_shfo_tests();
+    test_get_file_info();
+    clean_after_shfo_tests();
+
     init_shfo_tests();
     test_delete();
     clean_after_shfo_tests();
@@ -463,4 +1097,6 @@ START_TEST(shlfileop)
 
     test_sh_create_dir();
     clean_after_shfo_tests();
+
+    test_unicode();
 }
index 3566c7b..0cf398a 100644 (file)
  *
  * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include <stdarg.h>
 #include <stdio.h>
 
 #define COBJMACROS
+#define CONST_VTABLE
 
 #include "windef.h"
 #include "winbase.h"
 
 #include "shlguid.h"
 #include "shlobj.h"
-//#include "shobjidl.h"
+#include "shobjidl.h"
 #include "shlwapi.h"
+#include "ocidl.h"
+#include "oleauto.h"
 
-
-#include "wine/unicode.h"
 #include "wine/test.h"
 
 
@@ -43,20 +44,28 @@ static IMalloc *ppM;
 
 static HRESULT (WINAPI *pSHBindToParent)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*);
 static BOOL (WINAPI *pSHGetSpecialFolderPathW)(HWND, LPWSTR, int, BOOL);
+static HRESULT (WINAPI *pStrRetToBufW)(STRRET*,LPCITEMIDLIST,LPWSTR,UINT);
+static LPITEMIDLIST (WINAPI *pILFindLastID)(LPCITEMIDLIST);
+static void (WINAPI *pILFree)(LPITEMIDLIST);
+static BOOL (WINAPI *pILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST);
 
 static void init_function_pointers(void)
 {
-    HMODULE hmod = GetModuleHandleA("shell32.dll");
+    HMODULE hmod;
     HRESULT hr;
 
-    if(hmod)
-    {
-        pSHBindToParent = (void*)GetProcAddress(hmod, "SHBindToParent");
-        pSHGetSpecialFolderPathW = (void*)GetProcAddress(hmod, "SHGetSpecialFolderPathW");
-    }
+    hmod = GetModuleHandleA("shell32.dll");
+    pSHBindToParent = (void*)GetProcAddress(hmod, "SHBindToParent");
+    pSHGetSpecialFolderPathW = (void*)GetProcAddress(hmod, "SHGetSpecialFolderPathW");
+    pILFindLastID = (void *)GetProcAddress(hmod, (LPCSTR)16);
+    pILFree = (void*)GetProcAddress(hmod, (LPSTR)155);
+    pILIsEqual = (void*)GetProcAddress(hmod, (LPSTR)21);
+
+    hmod = GetModuleHandleA("shlwapi.dll");
+    pStrRetToBufW = (void*)GetProcAddress(hmod, "StrRetToBufW");
 
     hr = SHGetMalloc(&ppM);
-    ok(hr == S_OK, "SHGetMalloc failed %08lx\n", hr);
+    ok(hr == S_OK, "SHGetMalloc failed %08x\n", hr);
 }
 
 static void test_ParseDisplayName(void)
@@ -68,6 +77,7 @@ static void test_ParseDisplayName(void)
     DWORD res;
     WCHAR cTestDirW [MAX_PATH] = {0};
     ITEMIDLIST *newPIDL;
+    BOOL bRes;
 
     hr = SHGetDesktopFolder(&IDesktopFolder);
     if(hr != S_OK) return;
@@ -79,7 +89,7 @@ static void test_ParseDisplayName(void)
     hr = IShellFolder_ParseDisplayName(IDesktopFolder, 
         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
     ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL), 
-        "ParseDisplayName returned %08lx, expected 80070002 or E_FAIL\n", hr);
+        "ParseDisplayName returned %08x, expected 80070002 or E_FAIL\n", hr);
 
     res = GetFileAttributesA(cNonExistDir2A);
     if(res != INVALID_FILE_ATTRIBUTES) return;
@@ -87,7 +97,28 @@ static void test_ParseDisplayName(void)
     MultiByteToWideChar(CP_ACP, 0, cNonExistDir2A, -1, cTestDirW, MAX_PATH);
     hr = IShellFolder_ParseDisplayName(IDesktopFolder, 
         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
-    ok((hr == E_FAIL), "ParseDisplayName returned %08lx, expected E_FAIL\n", hr);
+    ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL) || (hr == E_INVALIDARG), 
+        "ParseDisplayName returned %08x, expected 80070002, E_FAIL or E_INVALIDARG\n", hr);
+
+    /* I thought that perhaps the DesktopFolder's ParseDisplayName would recognize the
+     * path corresponding to CSIDL_PERSONAL and return a CLSID_MyDocuments PIDL. Turns
+     * out it doesn't. The magic seems to happen in the file dialogs, then. */
+    if (!pSHGetSpecialFolderPathW || !pILFindLastID) goto finished;
+    
+    bRes = pSHGetSpecialFolderPathW(NULL, cTestDirW, CSIDL_PERSONAL, FALSE);
+    ok(bRes, "SHGetSpecialFolderPath(CSIDL_PERSONAL) failed! %u\n", GetLastError());
+    if (!bRes) goto finished;
+
+    hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
+    ok(SUCCEEDED(hr), "DesktopFolder->ParseDisplayName failed. hr = %08x.\n", hr);
+    if (FAILED(hr)) goto finished;
+
+    ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x31, "Last pidl should be of type "
+       "PT_FOLDER, but is: %02x\n", pILFindLastID(newPIDL)->mkid.abID[0]);
+    IMalloc_Free(ppM, newPIDL);
+    
+finished:
+    IShellFolder_Release(IDesktopFolder);
 }
 
 /* creates a file with the specified name for tests */
@@ -149,18 +180,19 @@ static void test_EnumObjects(IShellFolder *iFolder)
        { 1, 1, 1, 1, 0}
     };
 
-    /* Just test SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR for now */
+#define SFGAO_testfor SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR | SFGAO_CAPABILITYMASK
+    /* Don't test for SFGAO_HASSUBFOLDER since we return real state and native cached */
     static const ULONG attrs[5] =
     {
-        SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
-        SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
-        SFGAO_FILESYSTEM,
-        SFGAO_FILESYSTEM,
-        SFGAO_FILESYSTEM,
+        SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
+        SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
+        SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
+        SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
+        SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
     };
 
     hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList);
-    ok(hr == S_OK, "EnumObjects failed %08lx\n", hr);
+    ok(hr == S_OK, "EnumObjects failed %08x\n", hr);
 
     /* This is to show that, contrary to what is said on MSDN, on IEnumIDList::Next,
      * the filesystem shellfolders return S_OK even if less than 'celt' items are
@@ -171,7 +203,7 @@ static void test_EnumObjects(IShellFolder *iFolder)
     ok (i == 5, "i: %d\n", i);
 
     hr = IEnumIDList_Release(iEnumList);
-    ok(hr == S_OK, "IEnumIDList_Release failed %08lx\n", hr);
+    ok(hr == S_OK, "IEnumIDList_Release failed %08x\n", hr);
     
     /* Sort them first in case of wrong order from system */
     for (i=0;i<5;i++) for (j=0;j<5;j++)
@@ -185,18 +217,25 @@ static void test_EnumObjects(IShellFolder *iFolder)
     for (i=0;i<5;i++) for (j=0;j<5;j++)
     {
         hr = IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]);
-        ok(hr == iResults[i][j], "Got %lx expected [%d]-[%d]=%x\n", hr, i, j, iResults[i][j]);
+        ok(hr == iResults[i][j], "Got %x expected [%d]-[%d]=%x\n", hr, i, j, iResults[i][j]);
     }
 
 
     for (i = 0; i < 5; i++)
     {
         SFGAOF flags;
-        flags = SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR;
+        /* Native returns all flags no matter what we ask for */
+        flags = SFGAO_CANCOPY;
         hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
-        flags &= SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR;
-        ok(hr == S_OK, "GetAttributesOf returns %08lx\n", hr);
-        ok(flags == attrs[i], "GetAttributesOf gets attrs %08lx, expects %08lx\n", flags, attrs[i]);
+        flags &= SFGAO_testfor;
+        ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
+        ok(flags == (attrs[i]), "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
+
+        flags = SFGAO_testfor;
+        hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
+        flags &= SFGAO_testfor;
+        ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
+        ok(flags == attrs[i], "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
     }
 
     for (i=0;i<5;i++)
@@ -211,6 +250,7 @@ static void test_BindToObject(void)
     SHITEMID emptyitem = { 0, { 0 } };
     LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidlEmpty = (LPITEMIDLIST)&emptyitem;
     WCHAR wszSystemDir[MAX_PATH];
+    char szSystemDir[MAX_PATH];
     WCHAR wszMyComputer[] = { 
         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
@@ -219,61 +259,68 @@ static void test_BindToObject(void)
      * with an empty pidl. This is tested for Desktop, MyComputer and the FS ShellFolder
      */
     hr = SHGetDesktopFolder(&psfDesktop);
-    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
     if (FAILED(hr)) return;
     
     hr = IShellFolder_BindToObject(psfDesktop, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
-    ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr);
+    ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
 
     hr = IShellFolder_BindToObject(psfDesktop, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
-    ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr);
+    ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
 
     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
-    ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
     if (FAILED(hr)) {
         IShellFolder_Release(psfDesktop);
         return;
     }
     
     hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
-    ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
     IShellFolder_Release(psfDesktop);
     IMalloc_Free(ppM, pidlMyComputer);
     if (FAILED(hr)) return;
 
     hr = IShellFolder_BindToObject(psfMyComputer, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
-    ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr);
+    ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
 
+#if 0
+    /* this call segfaults on 98SE */
     hr = IShellFolder_BindToObject(psfMyComputer, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
-    ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr);
+    ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
+#endif
 
-    cChars = GetSystemDirectoryW(wszSystemDir, MAX_PATH);
-    ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryW failed! LastError: %08lx\n", GetLastError());
+    cChars = GetSystemDirectoryA(szSystemDir, MAX_PATH);
+    ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryA failed! LastError: %u\n", GetLastError());
     if (cChars == 0 || cChars >= MAX_PATH) {
         IShellFolder_Release(psfMyComputer);
         return;
     }
+    MultiByteToWideChar(CP_ACP, 0, szSystemDir, -1, wszSystemDir, MAX_PATH);
     
     hr = IShellFolder_ParseDisplayName(psfMyComputer, NULL, NULL, wszSystemDir, NULL, &pidlSystemDir, NULL);
-    ok (SUCCEEDED(hr), "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08x\n", hr);
     if (FAILED(hr)) {
         IShellFolder_Release(psfMyComputer);
         return;
     }
 
     hr = IShellFolder_BindToObject(psfMyComputer, pidlSystemDir, NULL, &IID_IShellFolder, (LPVOID*)&psfSystemDir);
-    ok (SUCCEEDED(hr), "MyComputer failed to bind to a FileSystem ShellFolder! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "MyComputer failed to bind to a FileSystem ShellFolder! hr = %08x\n", hr);
     IShellFolder_Release(psfMyComputer);
     IMalloc_Free(ppM, pidlSystemDir);
     if (FAILED(hr)) return;
 
     hr = IShellFolder_BindToObject(psfSystemDir, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
     ok (hr == E_INVALIDARG, 
-        "FileSystem ShellFolder's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr);
+        "FileSystem ShellFolder's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
     
+#if 0
+    /* this call segfaults on 98SE */
     hr = IShellFolder_BindToObject(psfSystemDir, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
     ok (hr == E_INVALIDARG, 
-        "FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr);
+        "FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
+#endif
 
     IShellFolder_Release(psfSystemDir);
 }
@@ -284,10 +331,13 @@ static void test_GetDisplayName(void)
     HRESULT hr;
     HANDLE hTestFile;
     WCHAR wszTestFile[MAX_PATH], wszTestFile2[MAX_PATH], wszTestDir[MAX_PATH];
+    char szTestFile[MAX_PATH], szTestDir[MAX_PATH];
+    DWORD attr;
     STRRET strret;
     LPSHELLFOLDER psfDesktop, psfPersonal;
     IUnknown *psfFile;
-    LPITEMIDLIST pidlTestFile;
+    SHITEMID emptyitem = { 0, { 0 } };
+    LPITEMIDLIST pidlTestFile, pidlEmpty = (LPITEMIDLIST)&emptyitem;
     LPCITEMIDLIST pidlLast;
     static const WCHAR wszFileName[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
     static const WCHAR wszDirName[] = { 'w','i','n','e','t','e','s','t',0 };
@@ -304,74 +354,137 @@ static void test_GetDisplayName(void)
 
     /* First creating a directory in MyDocuments and a file in this directory. */
     result = pSHGetSpecialFolderPathW(NULL, wszTestDir, CSIDL_PERSONAL, FALSE);
-    ok(result, "SHGetSpecialFolderPathW failed! Last error: %08lx\n", GetLastError());
+    ok(result, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
     if (!result) return;
 
     PathAddBackslashW(wszTestDir);
     lstrcatW(wszTestDir, wszDirName);
-    result = CreateDirectoryW(wszTestDir, NULL);
-    ok(result, "CreateDirectoryW failed! Last error: %08lx\n", GetLastError());
-    if (!result) return;
+    /* Use ANSI file functions so this works on Windows 9x */
+    WideCharToMultiByte(CP_ACP, 0, wszTestDir, -1, szTestDir, MAX_PATH, 0, 0);
+    CreateDirectoryA(szTestDir, NULL);
+    attr=GetFileAttributesA(szTestDir);
+    if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY))
+    {
+        ok(0, "unable to create the '%s' directory\n", szTestDir);
+        return;
+    }
 
     lstrcpyW(wszTestFile, wszTestDir);
     PathAddBackslashW(wszTestFile);
     lstrcatW(wszTestFile, wszFileName);
+    WideCharToMultiByte(CP_ACP, 0, wszTestFile, -1, szTestFile, MAX_PATH, 0, 0);
 
-    hTestFile = CreateFileW(wszTestFile, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
-    ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %08lx\n", GetLastError());
+    hTestFile = CreateFileA(szTestFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+    ok((hTestFile != INVALID_HANDLE_VALUE), "CreateFileA failed! Last error: %u\n", GetLastError());
     if (hTestFile == INVALID_HANDLE_VALUE) return;
     CloseHandle(hTestFile);
 
     /* Getting an itemidlist for the file. */
     hr = SHGetDesktopFolder(&psfDesktop);
-    ok(SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
+    ok(SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
     if (FAILED(hr)) return;
 
     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
-    ok(SUCCEEDED(hr), "Desktop->ParseDisplayName failed! hr = %08lx\n", hr);
+    ok(SUCCEEDED(hr), "Desktop->ParseDisplayName failed! hr = %08x\n", hr);
     if (FAILED(hr)) {
         IShellFolder_Release(psfDesktop);
         return;
     }
 
+    /* WinXP stores the filenames as both ANSI and UNICODE in the pidls */
+    pidlLast = pILFindLastID(pidlTestFile);
+    ok(pidlLast->mkid.cb >=76, "Expected pidl length of at least 76, got %d.\n", pidlLast->mkid.cb);
+    if (pidlLast->mkid.cb >= 76) {
+        ok(!lstrcmpW((WCHAR*)&pidlLast->mkid.abID[46], wszFileName),
+            "WinXP stores the filename as a wchar-string at this position!\n");
+    }
+    
     /* It seems as if we cannot bind to regular files on windows, but only directories. 
      */
     hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile);
-    todo_wine { ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hr = %08lx\n", hr); }
+    todo_wine { ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hr = %08x\n", hr); }
     if (SUCCEEDED(hr)) {
         IShellFolder_Release(psfFile);
     }
-    
+  
+    /* Some tests for IShellFolder::SetNameOf */
+    hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
+    ok(SUCCEEDED(hr), "SHBindToParent failed! hr = %08x\n", hr);
+    if (SUCCEEDED(hr)) {
+        /* It's ok to use this fixed path. Call will fail anyway. */
+        WCHAR wszAbsoluteFilename[] = { 'C',':','\\','w','i','n','e','t','e','s','t', 0 };
+        LPITEMIDLIST pidlNew;
+
+        /* The pidl returned through the last parameter of SetNameOf is a simple one. */
+        hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlLast, wszDirName, SHGDN_NORMAL, &pidlNew);
+        ok (SUCCEEDED(hr), "SetNameOf failed! hr = %08x\n", hr);
+        ok (((LPITEMIDLIST)((LPBYTE)pidlNew+pidlNew->mkid.cb))->mkid.cb == 0, 
+            "pidl returned from SetNameOf should be simple!\n");
+
+        /* Passing an absolute path to SetNameOf fails. The HRESULT code indicates that SetNameOf
+         * is implemented on top of SHFileOperation in WinXP. */
+        hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszAbsoluteFilename, 
+                SHGDN_FORPARSING, NULL);
+        ok (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED), "SetNameOf succeeded! hr = %08x\n", hr);
+
+        /* Rename the file back to its original name. SetNameOf ignores the fact, that the
+         * SHGDN flags specify an absolute path. */
+        hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszFileName, SHGDN_FORPARSING, NULL);
+        ok (SUCCEEDED(hr), "SetNameOf failed! hr = %08x\n", hr);
+
+        pILFree(pidlNew);
+        IShellFolder_Release(psfPersonal);
+    }
+
     /* Deleting the file and the directory */
-    DeleteFileW(wszTestFile);
-    RemoveDirectoryW(wszTestDir);
+    DeleteFileA(szTestFile);
+    RemoveDirectoryA(szTestDir);
 
     /* SHGetPathFromIDListW still works, although the file is not present anymore. */
     result = SHGetPathFromIDListW(pidlTestFile, wszTestFile2);
-    ok (result, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError());
+    ok (result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
     ok (!lstrcmpiW(wszTestFile, wszTestFile2), "SHGetPathFromIDListW returns incorrect path!\n");
 
     if(!pSHBindToParent) return;
 
+    /* SHBindToParent fails, if called with a NULL PIDL. */
+    hr = pSHBindToParent(NULL, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
+    ok (FAILED(hr), "SHBindToParent(NULL) should fail!\n");
+
+    /* But it succeeds with an empty PIDL. */
+    hr = pSHBindToParent(pidlEmpty, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
+    ok (SUCCEEDED(hr), "SHBindToParent(empty PIDL) should succeed! hr = %08x\n", hr);
+    ok (pidlLast == pidlEmpty, "The last element of an empty PIDL should be the PIDL itself!\n");
+    if (SUCCEEDED(hr)) 
+        IShellFolder_Release(psfPersonal);
+    
     /* Binding to the folder and querying the display name of the file also works. */
     hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast); 
-    ok (SUCCEEDED(hr), "SHBindToParent failed! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "SHBindToParent failed! hr = %08x\n", hr);
     if (FAILED(hr)) {
         IShellFolder_Release(psfDesktop);
         return;
     }
 
+    /* This test shows that Windows doesn't allocate a new pidlLast, but returns a pointer into 
+     * pidlTestFile (In accordance with MSDN). */
+    ok (pILFindLastID(pidlTestFile) == pidlLast, 
+                                "SHBindToParent doesn't return the last id of the pidl param!\n");
+    
     hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
-    ok (SUCCEEDED(hr), "Personal->GetDisplayNameOf failed! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "Personal->GetDisplayNameOf failed! hr = %08x\n", hr);
     if (FAILED(hr)) {
         IShellFolder_Release(psfDesktop);
         IShellFolder_Release(psfPersonal);
         return;
     }
-    
-    hr = StrRetToBufW(&strret, pidlLast, wszTestFile2, MAX_PATH);
-    ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08lx\n", hr);
-    ok (!lstrcmpiW(wszTestFile, wszTestFile2), "GetDisplayNameOf returns incorrect path!\n");
+
+    if (pStrRetToBufW)
+    {
+        hr = pStrRetToBufW(&strret, pidlLast, wszTestFile2, MAX_PATH);
+        ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
+        ok (!lstrcmpiW(wszTestFile, wszTestFile2), "GetDisplayNameOf returns incorrect path!\n");
+    }
     
     IShellFolder_Release(psfDesktop);
     IShellFolder_Release(psfPersonal);
@@ -407,13 +520,13 @@ static void test_CallForAttributes(void)
      * on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
      */
     hr = SHGetDesktopFolder(&psfDesktop);
-    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
     if (FAILED(hr)) return;
     
     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyDocuments, NULL, 
                                        &pidlMyDocuments, NULL);
     ok (SUCCEEDED(hr), 
-        "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08lx\n", hr);
+        "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08x\n", hr);
     if (FAILED(hr)) {
         IShellFolder_Release(psfDesktop);
         return;
@@ -422,11 +535,10 @@ static void test_CallForAttributes(void)
     dwAttributes = 0xffffffff;
     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
                                       (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
-    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
 
     /* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
-    todo_wine{ ok (dwAttributes & SFGAO_FILESYSTEM, 
-                   "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n"); }
+    ok (dwAttributes & SFGAO_FILESYSTEM, "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n");
     ok (!(dwAttributes & SFGAO_ISSLOW), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
     ok (!(dwAttributes & SFGAO_GHOSTED), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
 
@@ -434,7 +546,7 @@ static void test_CallForAttributes(void)
      * key. So the test will return at this point, if run on wine. 
      */
     lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMyDocumentsKey, 0, KEY_WRITE|KEY_READ, &hKey);
-    todo_wine { ok (lResult == ERROR_SUCCESS, "RegOpenKeyEx failed! result: %08lx\n", lResult); }
+    ok (lResult == ERROR_SUCCESS, "RegOpenKeyEx failed! result: %08x\n", lResult);
     if (lResult != ERROR_SUCCESS) {
         IMalloc_Free(ppM, pidlMyDocuments);
         IShellFolder_Release(psfDesktop);
@@ -444,7 +556,7 @@ static void test_CallForAttributes(void)
     /* Query MyDocuments' Attributes value, to be able to restore it later. */
     dwSize = sizeof(DWORD);
     lResult = RegQueryValueExW(hKey, wszAttributes, NULL, NULL, (LPBYTE)&dwOrigAttributes, &dwSize);
-    ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08lx\n", lResult);
+    ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
     if (lResult != ERROR_SUCCESS) {
         RegCloseKey(hKey);
         IMalloc_Free(ppM, pidlMyDocuments);
@@ -456,7 +568,7 @@ static void test_CallForAttributes(void)
     dwSize = sizeof(DWORD);
     lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL, 
                               (LPBYTE)&dwOrigCallForAttributes, &dwSize);
-    ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08lx\n", lResult);
+    ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
     if (lResult != ERROR_SUCCESS) {
         RegCloseKey(hKey);
         IMalloc_Free(ppM, pidlMyDocuments);
@@ -481,10 +593,10 @@ static void test_CallForAttributes(void)
     dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED|SFGAO_FILESYSTEM;
     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
                                       (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
-    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
     if (SUCCEEDED(hr)) 
         ok (dwAttributes == SFGAO_FILESYSTEM, 
-            "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08lx\n", 
+            "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08x\n", 
             dwAttributes);
 
     /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
@@ -504,37 +616,44 @@ static void test_GetAttributesOf(void)
     LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
     LPITEMIDLIST pidlMyComputer;
     DWORD dwFlags;
-    const static DWORD dwDesktopFlags = /* As observed on WinXP SP2 */
+    static const DWORD dwDesktopFlags = /* As observed on WinXP SP2 */
         SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR |
         SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER;
-    const static DWORD dwMyComputerFlags = /* As observed on WinXP SP2 */
+    static const DWORD dwMyComputerFlags = /* As observed on WinXP SP2 */
         SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET |
         SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
     WCHAR wszMyComputer[] = { 
         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
+    char  cCurrDirA [MAX_PATH] = {0};
+    WCHAR cCurrDirW [MAX_PATH];
+    static WCHAR cTestDirW[] = {'t','e','s','t','d','i','r',0};
+    static const WCHAR cBackSlash[] = {'\\',0};
+    IShellFolder *IDesktopFolder, *testIShellFolder;
+    ITEMIDLIST *newPIDL;
+    int len;
 
     hr = SHGetDesktopFolder(&psfDesktop);
-    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
     if (FAILED(hr)) return;
 
     /* The Desktop attributes can be queried with a single empty itemidlist, .. */
     dwFlags = 0xffffffff;
     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, &pidlEmpty, &dwFlags);
-    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(empty pidl) failed! hr = %08lx\n", hr);
-    ok (dwFlags == dwDesktopFlags, "Wrong Desktop attributes: %08lx, expected: %08lx\n", 
+    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(empty pidl) failed! hr = %08x\n", hr);
+    ok (dwFlags == dwDesktopFlags, "Wrong Desktop attributes: %08x, expected: %08x\n", 
         dwFlags, dwDesktopFlags);
 
     /* .. or with no itemidlist at all. */
     dwFlags = 0xffffffff;
     hr = IShellFolder_GetAttributesOf(psfDesktop, 0, NULL, &dwFlags);
-    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(NULL) failed! hr = %08lx\n", hr);
-    ok (dwFlags == dwDesktopFlags, "Wrong Desktop attributes: %08lx, expected: %08lx\n", 
+    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
+    ok (dwFlags == dwDesktopFlags, "Wrong Desktop attributes: %08x, expected: %08x\n", 
         dwFlags, dwDesktopFlags);
    
     /* Testing the attributes of the MyComputer shellfolder */
     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
-    ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
     if (FAILED(hr)) {
         IShellFolder_Release(psfDesktop);
         return;
@@ -549,26 +668,87 @@ static void test_GetAttributesOf(void)
      */
     dwFlags = 0xffffffff;
     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, (LPCITEMIDLIST*)&pidlMyComputer, &dwFlags);
-    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyComputer) failed! hr = %08lx\n", hr);
-    todo_wine { ok ((dwFlags & ~(DWORD)SFGAO_CANLINK) == dwMyComputerFlags, 
-                    "Wrong MyComputer attributes: %08lx, expected: %08lx\n", dwFlags, dwMyComputerFlags); }
+    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyComputer) failed! hr = %08x\n", hr);
+    ok ((dwFlags & ~(DWORD)SFGAO_CANLINK) == dwMyComputerFlags, 
+                    "Wrong MyComputer attributes: %08x, expected: %08x\n", dwFlags, dwMyComputerFlags);
 
     hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
-    ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
     IShellFolder_Release(psfDesktop);
     IMalloc_Free(ppM, pidlMyComputer);
     if (FAILED(hr)) return;
 
     hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
-    todo_wine {ok (hr == E_INVALIDARG, "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08lx\n", hr); }
+    todo_wine {ok (hr == E_INVALIDARG, "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08x\n", hr); }
 
     dwFlags = 0xffffffff;
     hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
-    ok (SUCCEEDED(hr), "MyComputer->GetAttributesOf(NULL) failed! hr = %08lx\n", hr); 
+    ok (SUCCEEDED(hr), "MyComputer->GetAttributesOf(NULL) failed! hr = %08x\n", hr); 
     todo_wine { ok (dwFlags == dwMyComputerFlags, 
-                    "Wrong MyComputer attributes: %08lx, expected: %08lx\n", dwFlags, dwMyComputerFlags); }
+                    "Wrong MyComputer attributes: %08x, expected: %08x\n", dwFlags, dwMyComputerFlags); }
 
     IShellFolder_Release(psfMyComputer);
+
+    /* create test directory */
+    CreateFilesFolders();
+
+    GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
+    len = lstrlenA(cCurrDirA);
+
+    if (len == 0) {
+       trace("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
+       return;
+    }
+    if(cCurrDirA[len-1] == '\\')
+       cCurrDirA[len-1] = 0;
+
+    MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
+    hr = SHGetDesktopFolder(&IDesktopFolder);
+    ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
+
+    hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
+    ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
+
+    hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
+    ok(hr == S_OK, "BindToObject failed %08x\n", hr);
+
+    IMalloc_Free(ppM, newPIDL);
+
+    /* get relative PIDL */
+    hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
+    ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
+
+    /* test the shell attributes of the test directory using the relative PIDL */
+    dwFlags = SFGAO_FOLDER;
+    hr = IShellFolder_GetAttributesOf(testIShellFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
+    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
+    ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for relative PIDL: %08x\n", dwFlags);
+
+    /* free memory */
+    IMalloc_Free(ppM, newPIDL);
+
+    /* append testdirectory name to path */
+    lstrcatW(cCurrDirW, cBackSlash);
+    lstrcatW(cCurrDirW, cTestDirW);
+
+    hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
+    ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
+
+    /* test the shell attributes of the test directory using the absolute PIDL */
+    dwFlags = SFGAO_FOLDER;
+    hr = IShellFolder_GetAttributesOf(IDesktopFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
+    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
+    ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for absolute PIDL: %08x\n", dwFlags);
+        
+    /* free memory */
+    IMalloc_Free(ppM, newPIDL);
+
+    IShellFolder_Release(testIShellFolder);
+
+    Cleanup();
+
+    IShellFolder_Release(IDesktopFolder);
 }    
 
 static void test_SHGetPathFromIDList(void)
@@ -583,35 +763,122 @@ static void test_SHGetPathFromIDList(void)
     WCHAR wszMyComputer[] = { 
         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
+    WCHAR wszFileName[MAX_PATH];
+    LPITEMIDLIST pidlTestFile;
+    HANDLE hTestFile;
+    STRRET strret;
+    static WCHAR wszTestFile[] = {
+        'w','i','n','e','t','e','s','t','.','f','o','o',0 };
+       HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
+       HMODULE hShell32;
+       LPITEMIDLIST pidlPrograms;
 
     if(!pSHGetSpecialFolderPathW) return;
 
+    /* Calling SHGetPathFromIDList with no pidl should return the empty string */
+    wszPath[0] = 'a';
+    wszPath[1] = '\0';
+    result = SHGetPathFromIDListW(NULL, wszPath);
+    ok(!result, "Expected failure\n");
+    ok(!wszPath[0], "Expected empty string\n");
+
     /* Calling SHGetPathFromIDList with an empty pidl should return the desktop folder's path. */
     result = pSHGetSpecialFolderPathW(NULL, wszDesktop, CSIDL_DESKTOP, FALSE);
-    ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %08lx\n", GetLastError());
+    ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %u\n", GetLastError());
     if (!result) return;
     
     result = SHGetPathFromIDListW(pidlEmpty, wszPath);
-    ok(result, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError());
+    ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
     if (!result) return;
     ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDList didn't return desktop path for empty pidl!\n");
 
     /* MyComputer does not map to a filesystem path. SHGetPathFromIDList should fail. */
     hr = SHGetDesktopFolder(&psfDesktop);
-    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
+    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
     if (FAILED(hr)) return;
 
     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
-    ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr);
-    IShellFolder_Release(psfDesktop);
-    if (FAILED(hr)) return;
+    ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
+    if (FAILED(hr)) {
+        IShellFolder_Release(psfDesktop);
+        return;
+    }
 
     SetLastError(0xdeadbeef);
+    wszPath[0] = 'a';
+    wszPath[1] = '\0';
     result = SHGetPathFromIDListW(pidlMyComputer, wszPath);
     ok (!result, "SHGetPathFromIDList succeeded where it shouldn't!\n");
-    ok (GetLastError()==0xdeadbeef, "SHGetPathFromIDList shouldn't set last error! Last error: %08lx\n", GetLastError());
+    ok (GetLastError()==0xdeadbeef, "SHGetPathFromIDList shouldn't set last error! Last error: %u\n", GetLastError());
+    ok (!wszPath[0], "Expected empty path\n");
+    if (result) {
+        IShellFolder_Release(psfDesktop);
+        return;
+    }
 
     IMalloc_Free(ppM, pidlMyComputer);
+
+    result = pSHGetSpecialFolderPathW(NULL, wszFileName, CSIDL_DESKTOPDIRECTORY, FALSE);
+    ok(result, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
+    if (!result) {
+        IShellFolder_Release(psfDesktop);
+        return;
+    }
+    PathAddBackslashW(wszFileName);
+    lstrcatW(wszFileName, wszTestFile);
+    hTestFile = CreateFileW(wszFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
+    ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %u\n", GetLastError());
+    if (hTestFile == INVALID_HANDLE_VALUE) {
+        IShellFolder_Release(psfDesktop);
+        return;
+    }
+    CloseHandle(hTestFile);
+
+    hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
+    ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse filename hr = %08x\n", hr);
+    if (FAILED(hr)) {
+        IShellFolder_Release(psfDesktop);
+        DeleteFileW(wszFileName);
+        IMalloc_Free(ppM, pidlTestFile);
+        return;
+    }
+
+    /* This test is to show that the Desktop shellfolder prepends the CSIDL_DESKTOPDIRECTORY
+     * path for files placed on the desktop, if called with SHGDN_FORPARSING. */
+    hr = IShellFolder_GetDisplayNameOf(psfDesktop, pidlTestFile, SHGDN_FORPARSING, &strret);
+    ok (SUCCEEDED(hr), "Desktop's GetDisplayNamfOf failed! hr = %08x\n", hr);
+    IShellFolder_Release(psfDesktop);
+    DeleteFileW(wszFileName);
+    if (FAILED(hr)) {
+        IMalloc_Free(ppM, pidlTestFile);
+        return;
+    }
+    if (pStrRetToBufW)
+    {
+        pStrRetToBufW(&strret, pidlTestFile, wszPath, MAX_PATH);
+        ok(0 == lstrcmpW(wszFileName, wszPath), 
+           "Desktop->GetDisplayNameOf(pidlTestFile, SHGDN_FORPARSING) "
+           "returned incorrect path for file placed on desktop\n");
+    }
+
+    result = SHGetPathFromIDListW(pidlTestFile, wszPath);
+    ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
+    IMalloc_Free(ppM, pidlTestFile);
+    if (!result) return;
+    ok(0 == lstrcmpW(wszFileName, wszPath), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n");
+
+
+       /* Test if we can get the path from the start menu "program files" PIDL. */
+    hShell32 = GetModuleHandleA("shell32");
+    pSHGetSpecialFolderLocation = (void *)GetProcAddress(hShell32, "SHGetSpecialFolderLocation");
+
+    hr = pSHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidlPrograms);
+    ok(SUCCEEDED(hr), "SHGetFolderLocation failed: 0x%08x\n", hr);
+
+    SetLastError(0xdeadbeef);
+    result = SHGetPathFromIDListW(pidlPrograms, wszPath);
+       IMalloc_Free(ppM, pidlPrograms);
+    ok(result, "SHGetPathFromIDList failed\n");
 }
 
 static void test_EnumObjects_and_CompareIDs(void)
@@ -635,27 +902,438 @@ static void test_EnumObjects_and_CompareIDs(void)
         cCurrDirA[len-1] = 0;
 
     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
-    strcatW(cCurrDirW, cTestDirW);
+    lstrcatW(cCurrDirW, cTestDirW);
 
     hr = SHGetDesktopFolder(&IDesktopFolder);
-    ok(hr == S_OK, "SHGetDesktopfolder failed %08lx\n", hr);
+    ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
 
     CreateFilesFolders();
 
     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
-    ok(hr == S_OK, "ParseDisplayName failed %08lx\n", hr);
+    ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
 
     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
-    ok(hr == S_OK, "BindToObject failed %08lx\n", hr);
+    ok(hr == S_OK, "BindToObject failed %08x\n", hr);
 
     test_EnumObjects(testIShellFolder);
 
-    hr = IShellFolder_Release(testIShellFolder);
-    ok(hr == S_OK, "IShellFolder_Release failed %08lx\n", hr);
+    IShellFolder_Release(testIShellFolder);
 
     Cleanup();
 
     IMalloc_Free(ppM, newPIDL);
+
+    IShellFolder_Release(IDesktopFolder);
+}
+
+/* A simple implementation of an IPropertyBag, which returns fixed values for
+ * 'Target' and 'Attributes' properties.
+ */
+static HRESULT WINAPI InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, REFIID riid,
+    void **ppvObject) 
+{
+    if (!ppvObject)
+        return E_INVALIDARG;
+
+    if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
+        *ppvObject = iface;
+    } else {
+        ok (FALSE, "InitPropertyBag asked for unknown interface!\n");
+        return E_NOINTERFACE;
+    }
+
+    IPropertyBag_AddRef(iface);
+    return S_OK;
+}
+
+static ULONG WINAPI InitPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
+    return 2;
+}
+
+static ULONG WINAPI InitPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
+    return 1;
+}
+
+static HRESULT WINAPI InitPropertyBag_IPropertyBag_Read(IPropertyBag *iface, LPCOLESTR pszPropName,
+    VARIANT *pVar, IErrorLog *pErrorLog)
+{
+    static const WCHAR wszTargetSpecialFolder[] = {
+        'T','a','r','g','e','t','S','p','e','c','i','a','l','F','o','l','d','e','r',0 };
+    static const WCHAR wszTarget[] = {
+        'T','a','r','g','e','t',0 };
+    static const WCHAR wszAttributes[] = {
+        'A','t','t','r','i','b','u','t','e','s',0 };
+    static const WCHAR wszResolveLinkFlags[] = {
+        'R','e','s','o','l','v','e','L','i','n','k','F','l','a','g','s',0 };
+       
+    if (!lstrcmpW(pszPropName, wszTargetSpecialFolder)) {
+        ok(V_VT(pVar) == VT_I4, "Wrong variant type for 'TargetSpecialFolder' property!\n");
+        return E_INVALIDARG;
+    }
+    
+    if (!lstrcmpW(pszPropName, wszResolveLinkFlags)) 
+    {
+        ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'ResolveLinkFlags' property!\n");
+        return E_INVALIDARG;
+    }
+
+    if (!lstrcmpW(pszPropName, wszTarget)) {
+        WCHAR wszPath[MAX_PATH];
+        BOOL result;
+        
+        ok(V_VT(pVar) == VT_BSTR, "Wrong variant type for 'Target' property!\n");
+        if (V_VT(pVar) != VT_BSTR) return E_INVALIDARG;
+
+        result = pSHGetSpecialFolderPathW(NULL, wszPath, CSIDL_DESKTOPDIRECTORY, FALSE);
+        ok(result, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! %u\n", GetLastError());
+        if (!result) return E_INVALIDARG;
+
+        V_BSTR(pVar) = SysAllocString(wszPath);
+        return S_OK;
+    }
+
+    if (!lstrcmpW(pszPropName, wszAttributes)) {
+        ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'Attributes' property!\n");
+        if (V_VT(pVar) != VT_UI4) return E_INVALIDARG;
+        V_UI4(pVar) = SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|
+                      SFGAO_CANRENAME|SFGAO_FILESYSTEM;
+        return S_OK;
+    }
+
+    ok(FALSE, "PropertyBag was asked for unknown property (vt=%d)!\n", V_VT(pVar));
+    return E_INVALIDARG;
+}
+
+static HRESULT WINAPI InitPropertyBag_IPropertyBag_Write(IPropertyBag *iface, LPCOLESTR pszPropName,
+    VARIANT *pVar)
+{
+    ok(FALSE, "Unexpected call to IPropertyBag_Write\n");
+    return E_NOTIMPL;
+}
+    
+static const IPropertyBagVtbl InitPropertyBag_IPropertyBagVtbl = {
+    InitPropertyBag_IPropertyBag_QueryInterface,
+    InitPropertyBag_IPropertyBag_AddRef,
+    InitPropertyBag_IPropertyBag_Release,
+    InitPropertyBag_IPropertyBag_Read,
+    InitPropertyBag_IPropertyBag_Write
+};
+
+static struct IPropertyBag InitPropertyBag = {
+    &InitPropertyBag_IPropertyBagVtbl
+};
+
+static void test_FolderShortcut(void) {
+    IPersistPropertyBag *pPersistPropertyBag;
+    IShellFolder *pShellFolder, *pDesktopFolder;
+    IPersistFolder3 *pPersistFolder3;
+    HRESULT hr;
+    STRRET strret;
+    WCHAR wszDesktopPath[MAX_PATH], wszBuffer[MAX_PATH];
+    BOOL result;
+    CLSID clsid;
+    LPITEMIDLIST pidlCurrentFolder, pidlWineTestFolder, pidlSubFolder;
+    HKEY hShellExtKey;
+    WCHAR wszWineTestFolder[] = {
+        ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-',
+        'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 };
+    WCHAR wszShellExtKey[] = { 'S','o','f','t','w','a','r','e','\\',
+        'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
+        'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+        'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
+        'N','a','m','e','S','p','a','c','e','\\',
+        '{','9','b','3','5','2','e','b','f','-','2','7','6','5','-','4','5','c','1','-',
+        'b','4','c','6','-','8','5','c','c','7','f','7','a','b','c','6','4','}',0 };
+    
+    WCHAR wszSomeSubFolder[] = { 'S','u','b','F','o','l','d','e','r', 0};
+    static const GUID CLSID_UnixDosFolder = 
+        {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
+
+    if (!pSHGetSpecialFolderPathW || !pStrRetToBufW) return;
+   
+    /* These tests basically show, that CLSID_FolderShortcuts are initialized
+     * via their IPersistPropertyBag interface. And that the target folder
+     * is taken from the IPropertyBag's 'Target' property.
+     */
+    hr = CoCreateInstance(&CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER, 
+                          &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag);
+    ok (SUCCEEDED(hr), "CoCreateInstance failed! hr = 0x%08x\n", hr);
+    if (FAILED(hr)) return;
+
+    hr = IPersistPropertyBag_Load(pPersistPropertyBag, &InitPropertyBag, NULL);
+    ok(SUCCEEDED(hr), "IPersistPropertyBag_Load failed! hr = %08x\n", hr);
+    if (FAILED(hr)) {
+        IPersistPropertyBag_Release(pPersistPropertyBag);
+        return;
+    }
+    
+    hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, &IID_IShellFolder, 
+                                            (LPVOID*)&pShellFolder);
+    IPersistPropertyBag_Release(pPersistPropertyBag);
+    ok(SUCCEEDED(hr), "IPersistPropertyBag_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
+    if (FAILED(hr)) return;
+
+    hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
+    ok(SUCCEEDED(hr), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
+    if (FAILED(hr)) {
+        IShellFolder_Release(pShellFolder);
+        return;
+    }
+
+    result = pSHGetSpecialFolderPathW(NULL, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE);
+    ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOPDIRECTORY) failed! %u\n", GetLastError());
+    if (!result) return;
+
+    pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
+    ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
+
+    hr = IShellFolder_QueryInterface(pShellFolder, &IID_IPersistFolder3, (LPVOID*)&pPersistFolder3);
+    IShellFolder_Release(pShellFolder);
+    ok(SUCCEEDED(hr), "IShellFolder_QueryInterface(IID_IPersistFolder3 failed! hr = 0x%08x\n", hr);
+    if (FAILED(hr)) return;
+
+    hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
+    ok(SUCCEEDED(hr), "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
+    ok(IsEqualCLSID(&clsid, &CLSID_FolderShortcut), "Unexpected CLSID!\n");
+
+    hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
+    ok(SUCCEEDED(hr), "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
+    ok(!pidlCurrentFolder, "IPersistFolder3_GetCurFolder should return a NULL pidl!\n");
+                    
+    /* For FolderShortcut objects, the Initialize method initialized the folder's position in the
+     * shell namespace. The target folder, read from the property bag above, remains untouched. 
+     * The following tests show this: The itemidlist for some imaginary shellfolder object
+     * is created and the FolderShortcut is initialized with it. GetCurFolder now returns this
+     * itemidlist, but GetDisplayNameOf still returns the path from above.
+     */
+    hr = SHGetDesktopFolder(&pDesktopFolder);
+    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
+    if (FAILED(hr)) return;
+
+    /* Temporarily register WineTestFolder as a shell namespace extension at the Desktop. 
+     * Otherwise ParseDisplayName fails on WinXP with E_INVALIDARG */
+    RegCreateKeyW(HKEY_CURRENT_USER, wszShellExtKey, &hShellExtKey);
+    RegCloseKey(hShellExtKey);
+    hr = IShellFolder_ParseDisplayName(pDesktopFolder, NULL, NULL, wszWineTestFolder, NULL,
+                                       &pidlWineTestFolder, NULL);
+    RegDeleteKeyW(HKEY_CURRENT_USER, wszShellExtKey);
+    IShellFolder_Release(pDesktopFolder);
+    ok (SUCCEEDED(hr), "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
+    if (FAILED(hr)) return;
+
+    hr = IPersistFolder3_Initialize(pPersistFolder3, pidlWineTestFolder);
+    ok (SUCCEEDED(hr), "IPersistFolder3::Initialize failed! hr = %08x\n", hr);
+    if (FAILED(hr)) {
+        IPersistFolder3_Release(pPersistFolder3);
+        pILFree(pidlWineTestFolder);
+        return;
+    }
+
+    hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
+    ok(SUCCEEDED(hr), "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
+    ok(pILIsEqual(pidlCurrentFolder, pidlWineTestFolder),
+        "IPersistFolder3_GetCurFolder should return pidlWineTestFolder!\n");
+    pILFree(pidlCurrentFolder);
+    pILFree(pidlWineTestFolder);
+
+    hr = IPersistFolder3_QueryInterface(pPersistFolder3, &IID_IShellFolder, (LPVOID*)&pShellFolder);
+    IPersistFolder3_Release(pPersistFolder3);
+    ok(SUCCEEDED(hr), "IPersistFolder3_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
+    if (FAILED(hr)) return;
+
+    hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
+    ok(SUCCEEDED(hr), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
+    if (FAILED(hr)) {
+        IShellFolder_Release(pShellFolder);
+        return;
+    }
+
+    pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
+    ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
+
+    /* Next few lines are meant to show that children of FolderShortcuts are not FolderShortcuts,
+     * but ShellFSFolders. */
+    PathAddBackslashW(wszDesktopPath);
+    lstrcatW(wszDesktopPath, wszSomeSubFolder);
+    if (!CreateDirectoryW(wszDesktopPath, NULL)) {
+        IShellFolder_Release(pShellFolder);
+        return;
+    }
+    
+    hr = IShellFolder_ParseDisplayName(pShellFolder, NULL, NULL, wszSomeSubFolder, NULL, 
+                                       &pidlSubFolder, NULL);
+    RemoveDirectoryW(wszDesktopPath);
+    ok (SUCCEEDED(hr), "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
+    if (FAILED(hr)) {
+        IShellFolder_Release(pShellFolder);
+        return;
+    }
+
+    hr = IShellFolder_BindToObject(pShellFolder, pidlSubFolder, NULL, &IID_IPersistFolder3,
+                                   (LPVOID*)&pPersistFolder3);
+    IShellFolder_Release(pShellFolder);
+    pILFree(pidlSubFolder);
+    ok (SUCCEEDED(hr), "IShellFolder::BindToObject failed! hr = %08x\n", hr);
+    if (FAILED(hr))
+        return;
+
+    /* On windows, we expect CLSID_ShellFSFolder. On wine we relax this constraint
+     * a little bit and also allow CLSID_UnixDosFolder. */
+    hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
+    ok(SUCCEEDED(hr), "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
+    ok(IsEqualCLSID(&clsid, &CLSID_ShellFSFolder) || IsEqualCLSID(&clsid, &CLSID_UnixDosFolder),
+        "IPersistFolder3::GetClassID returned unexpected CLSID!\n");
+
+    IPersistFolder3_Release(pPersistFolder3);
+}
+
+#include "pshpack1.h"
+struct FileStructA {
+    BYTE  type;
+    BYTE  dummy;
+    DWORD dwFileSize;
+    WORD  uFileDate;    /* In our current implementation this is */
+    WORD  uFileTime;    /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastWriteTime) */
+    WORD  uFileAttribs;
+    CHAR  szName[1];
+};
+
+struct FileStructW {
+    WORD  cbLen;        /* Length of this element. */
+    BYTE  abFooBar1[6]; /* Beyond any recognition. */
+    WORD  uDate;        /* FileTimeToDosDate(WIN32_FIND_DATA->ftCreationTime)? */
+    WORD  uTime;        /* (this is currently speculation) */
+    WORD  uDate2;       /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastAccessTime)? */
+    WORD  uTime2;       /* (this is currently speculation) */
+    BYTE  abFooBar2[4]; /* Beyond any recognition. */
+    WCHAR wszName[1];   /* The long filename in unicode. */
+    /* Just for documentation: Right after the unicode string: */
+    WORD  cbOffset;     /* FileStructW's offset from the beginning of the SHITMEID. 
+                         * SHITEMID->cb == uOffset + cbLen */
+};
+#include "poppack.h"
+
+static void test_ITEMIDLIST_format(void) {
+    WCHAR wszPersonal[MAX_PATH];
+    LPSHELLFOLDER psfDesktop, psfPersonal;
+    LPITEMIDLIST pidlPersonal, pidlFile;
+    HANDLE hFile;
+    HRESULT hr;
+    BOOL bResult;
+    WCHAR wszFile[3][17] = { { 'e','v','e','n','_',0 }, { 'o','d','d','_',0 }, 
+        { 'l','o','n','g','e','r','_','t','h','a','n','.','8','_','3',0 } };
+    int i;
+    
+    if(!pSHGetSpecialFolderPathW) return;
+
+    bResult = pSHGetSpecialFolderPathW(NULL, wszPersonal, CSIDL_PERSONAL, FALSE);
+    ok(bResult, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
+    if (!bResult) return;
+
+    bResult = SetCurrentDirectoryW(wszPersonal);
+    ok(bResult, "SetCurrentDirectory failed! Last error: %u\n", GetLastError());
+    if (!bResult) return;
+
+    hr = SHGetDesktopFolder(&psfDesktop);
+    ok(SUCCEEDED(hr), "SHGetDesktopFolder failed! hr: %08x\n", hr);
+    if (FAILED(hr)) return;
+
+    hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszPersonal, NULL, &pidlPersonal, NULL);
+    ok(SUCCEEDED(hr), "psfDesktop->ParseDisplayName failed! hr = %08x\n", hr);
+    if (FAILED(hr)) {
+        IShellFolder_Release(psfDesktop);
+        return;
+    }
+
+    hr = IShellFolder_BindToObject(psfDesktop, pidlPersonal, NULL, &IID_IShellFolder,
+        (LPVOID*)&psfPersonal);
+    IShellFolder_Release(psfDesktop);
+    pILFree(pidlPersonal);
+    ok(SUCCEEDED(hr), "psfDesktop->BindToObject failed! hr = %08x\n", hr);
+    if (FAILED(hr)) return;
+
+    for (i=0; i<3; i++) {
+        CHAR szFile[MAX_PATH];
+        struct FileStructA *pFileStructA;
+        WORD cbOffset;
+        
+        WideCharToMultiByte(CP_ACP, 0, wszFile[i], -1, szFile, MAX_PATH, NULL, NULL);
+        
+        hFile = CreateFileW(wszFile[i], GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_WRITE_THROUGH, NULL);
+        ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed! (%u)\n", GetLastError());
+        if (hFile == INVALID_HANDLE_VALUE) {
+            IShellFolder_Release(psfPersonal);
+            return;
+        }
+        CloseHandle(hFile);
+
+        hr = IShellFolder_ParseDisplayName(psfPersonal, NULL, NULL, wszFile[i], NULL, &pidlFile, NULL);
+        DeleteFileW(wszFile[i]);
+        ok(SUCCEEDED(hr), "psfPersonal->ParseDisplayName failed! hr: %08x\n", hr);
+        if (FAILED(hr)) {
+            IShellFolder_Release(psfPersonal);
+            return;
+        }
+
+        pFileStructA = (struct FileStructA *)pidlFile->mkid.abID;
+        ok(pFileStructA->type == 0x32, "PIDLTYPE should be 0x32!\n");
+        ok(pFileStructA->dummy == 0x00, "Dummy Byte should be 0x00!\n");
+        ok(pFileStructA->dwFileSize == 0, "Filesize should be zero!\n"); 
+
+        if (i < 2) /* First two file names are already in valid 8.3 format */ 
+            ok(!strcmp(szFile, (CHAR*)&pidlFile->mkid.abID[12]), "Wrong file name!\n");
+        else 
+            /* WinXP stores a derived 8.3 dos name (LONGER~1.8_3) here. We probably
+             * can't implement this correctly, since unix filesystems don't support
+             * this nasty short/long filename stuff. So we'll probably stay with our
+             * current habbit of storing the long filename here, which seems to work
+             * just fine. */
+            todo_wine { ok(pidlFile->mkid.abID[18] == '~', "Should be derived 8.3 name!\n"); }
+
+        if (i == 0) /* First file name has an even number of chars. No need for alignment. */
+            ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] != '\0', 
+                "Alignment byte, where there shouldn't be!\n"); 
+        
+        if (i == 1) /* Second file name has an uneven number of chars => alignment byte */
+            ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] == '\0', 
+                "There should be an alignment byte, but isn't!\n");
+
+        /* The offset of the FileStructW member is stored as a WORD at the end of the pidl. */
+        cbOffset = *(WORD*)(((LPBYTE)pidlFile)+pidlFile->mkid.cb-sizeof(WORD));
+        ok (cbOffset >= sizeof(struct FileStructA) &&
+            cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW),
+            "Wrong offset value (%d) stored at the end of the PIDL\n", cbOffset);
+
+        if (cbOffset >= sizeof(struct FileStructA) &&
+            cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW)) 
+        {
+            struct FileStructW *pFileStructW = (struct FileStructW *)(((LPBYTE)pidlFile)+cbOffset);
+
+            ok(pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen, 
+                "FileStructW's offset and length should add up to the PIDL's length!\n");
+
+            if (pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen) {
+                /* Since we just created the file, time of creation,
+                 * time of last access and time of last write access just be the same. 
+                 * These tests seem to fail sometimes (on WinXP), if the test is run again shortly 
+                 * after the first run. I do remember something with NTFS keeping the creation time
+                 * if a file is deleted and then created again within a couple of seconds or so.
+                 * Might be the reason. */
+                ok (pFileStructA->uFileDate == pFileStructW->uDate &&
+                    pFileStructA->uFileTime == pFileStructW->uTime,
+                    "Last write time should match creation time!\n");
+    
+                ok (pFileStructA->uFileDate == pFileStructW->uDate2 &&
+                    pFileStructA->uFileTime == pFileStructW->uTime2,
+                    "Last write time should match last access time!\n");
+
+                ok (!lstrcmpW(wszFile[i], pFileStructW->wszName),
+                    "The filename should be stored in unicode at this position!\n");
+            }
+        }
+
+        pILFree(pidlFile);
+    }
 }
 
 START_TEST(shlfolder)
@@ -672,6 +1350,8 @@ START_TEST(shlfolder)
     test_GetAttributesOf();
     test_SHGetPathFromIDList();
     test_CallForAttributes();
+    test_FolderShortcut();
+    test_ITEMIDLIST_format();
 
     OleUninitialize();
 }
index 0a3d592..c748f35 100755 (executable)
  *
  * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include <stdarg.h>
 #include <stdio.h>
 
-#define NONAMELESSUNION
-#define NONAMELESSSTRUCT
 #define WINE_NOWINSOCK
 #include "windef.h"
 #include "winbase.h"
@@ -67,21 +65,21 @@ static void test_StrRetToStringNW(void)
     trace("StrRetToStringNAW is Unicode\n");
 
     strret.uType = STRRET_WSTR;
-    strret.u.pOleStr = CoDupStrW("Test");
+    U(strret).pOleStr = CoDupStrW("Test");
     memset(buff, 0xff, sizeof(buff));
     ret = pStrRetToStrNAW(buff, sizeof(buff)/sizeof(WCHAR), &strret, NULL);
     ok(ret == TRUE && !strcmpW(buff, szTestW),
        "STRRET_WSTR: dup failed, ret=%d\n", ret);
 
     strret.uType = STRRET_CSTR;
-    lstrcpyA(strret.u.cStr, "Test");
+    lstrcpyA(U(strret).cStr, "Test");
     memset(buff, 0xff, sizeof(buff));
     ret = pStrRetToStrNAW(buff, sizeof(buff)/sizeof(WCHAR), &strret, NULL);
     ok(ret == TRUE && !strcmpW(buff, szTestW),
        "STRRET_CSTR: dup failed, ret=%d\n", ret);
 
     strret.uType = STRRET_OFFSET;
-    strret.u.uOffset = 1;
+    U(strret).uOffset = 1;
     strcpy((char*)&iidl, " Test");
     memset(buff, 0xff, sizeof(buff));
     ret = pStrRetToStrNAW(buff, sizeof(buff)/sizeof(WCHAR), &strret, iidl);
@@ -92,7 +90,7 @@ static void test_StrRetToStringNW(void)
 #if 0
     /* Invalid dest - should return FALSE, except NT4 does not, so we don't check. */
     strret.uType = STRRET_WSTR;
-    strret.u.pOleStr = CoDupStrW("Test");
+    U(strret).pOleStr = CoDupStrW("Test");
     pStrRetToStrNAW(NULL, sizeof(buff)/sizeof(WCHAR), &strret, NULL);
     trace("NULL dest: ret=%d\n", ret);
 #endif
@@ -102,9 +100,7 @@ START_TEST(string)
 {
     CoInitialize(0);
 
-    hShell32 = LoadLibraryA("shell32.dll");
-    if (!hShell32)
-        return;
+    hShell32 = GetModuleHandleA("shell32.dll");
 
     pStrRetToStrNAW = (void*)GetProcAddress(hShell32, (LPSTR)96);
     if (pStrRetToStrNAW)
diff --git a/rostests/winetests/shell32/systray.c b/rostests/winetests/shell32/systray.c
new file mode 100644 (file)
index 0000000..e6d72a9
--- /dev/null
@@ -0,0 +1,106 @@
+/* Unit tests for systray
+ *
+ * Copyright 2007 Mikolaj Zalewski
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#define _WIN32_IE 0x600
+#include <assert.h>
+#include <stdarg.h>
+
+#include <windows.h>
+
+#include "wine/test.h"
+
+
+static HWND hMainWnd;
+
+void test_cbsize()
+{
+    NOTIFYICONDATAW nidW;
+    NOTIFYICONDATAA nidA;
+
+    ZeroMemory(&nidW, sizeof(nidW));
+    nidW.cbSize = NOTIFYICONDATAW_V1_SIZE;
+    nidW.hWnd = hMainWnd;
+    nidW.uID = 1;
+    nidW.uFlags = NIF_ICON|NIF_MESSAGE;
+    nidW.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+    nidW.uCallbackMessage = WM_USER+17;
+    ok(Shell_NotifyIconW(NIM_ADD, &nidW), "NIM_ADD failed!\n");
+
+    /* using an invalid cbSize does work */
+    nidW.cbSize = 3;
+    nidW.hWnd = hMainWnd;
+    nidW.uID = 1;
+    ok(Shell_NotifyIconW(NIM_DELETE, &nidW), "NIM_DELETE failed!\n");
+    /* as icon doesn't exist anymore - now there will be an error */
+    nidW.cbSize = sizeof(nidW);
+    /* wine currently doesn't return error code put prints an ERR(...) */
+    todo_wine ok(!Shell_NotifyIconW(NIM_DELETE, &nidW), "The icon was not deleted\n");
+
+    /* same for Shell_NotifyIconA */
+    ZeroMemory(&nidA, sizeof(nidA));
+    nidA.cbSize = NOTIFYICONDATAA_V1_SIZE;
+    nidA.hWnd = hMainWnd;
+    nidA.uID = 1;
+    nidA.uFlags = NIF_ICON|NIF_MESSAGE;
+    nidA.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+    nidA.uCallbackMessage = WM_USER+17;
+    ok(Shell_NotifyIconA(NIM_ADD, &nidA), "NIM_ADD failed!\n");
+
+    /* using an invalid cbSize does work */
+    nidA.cbSize = 3;
+    nidA.hWnd = hMainWnd;
+    nidA.uID = 1;
+    ok(Shell_NotifyIconA(NIM_DELETE, &nidA), "NIM_DELETE failed!\n");
+    /* as icon doesn't exist anymore - now there will be an error */
+    nidA.cbSize = sizeof(nidA);
+    /* wine currently doesn't return error code put prints an ERR(...) */
+    todo_wine ok(!Shell_NotifyIconA(NIM_DELETE, &nidA), "The icon was not deleted\n");
+}
+
+START_TEST(systray)
+{
+    WNDCLASSA wc;
+    MSG msg;
+    RECT rc;
+
+    wc.style = CS_HREDRAW | CS_VREDRAW;
+    wc.cbClsExtra = 0;
+    wc.cbWndExtra = 0;
+    wc.hInstance = GetModuleHandleA(NULL);
+    wc.hIcon = NULL;
+    wc.hCursor = LoadCursorA(NULL, MAKEINTRESOURCEA(IDC_IBEAM));
+    wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
+    wc.lpszMenuName = NULL;
+    wc.lpszClassName = "MyTestWnd";
+    wc.lpfnWndProc = DefWindowProc;
+    RegisterClassA(&wc);
+
+    hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
+      CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, GetModuleHandleA(NULL), 0);
+    GetClientRect(hMainWnd, &rc);
+    ShowWindow(hMainWnd, SW_SHOW);
+
+    test_cbsize();
+
+    PostQuitMessage(0);
+    while(GetMessageA(&msg,0,0,0)) {
+        TranslateMessage(&msg);
+        DispatchMessageA(&msg);
+    }
+    DestroyWindow(hMainWnd);
+}
index 603044e..1eb0a0e 100644 (file)
@@ -13,15 +13,17 @@ extern void func_shlexec(void);
 extern void func_shlfileop(void);
 extern void func_shlfolder(void);
 extern void func_string(void);
+extern void func_systray(void);
 
 const struct test winetest_testlist[] =
 {
-//    { "generated", func_generated },
+    { "generated", func_generated },
     { "shelllink", func_shelllink },
     { "shellpath", func_shellpath },
     { "shlexec", func_shlexec },
     { "shlfileop", func_shlfileop },
     { "shlfolder", func_shlfolder },
     { "string", func_string },
+    { "systray", func_systray },
     { 0, 0 }
 };