added shell32 tests. Not enabled just yet
authorSteven Edwards <winehacker@gmail.com>
Wed, 10 Aug 2005 20:35:15 +0000 (20:35 +0000)
committerSteven Edwards <winehacker@gmail.com>
Wed, 10 Aug 2005 20:35:15 +0000 (20:35 +0000)
svn path=/trunk/; revision=17268

reactos/regtests/winetests/shell32/generated.c [new file with mode: 0755]
reactos/regtests/winetests/shell32/shell32.xml [new file with mode: 0644]
reactos/regtests/winetests/shell32/shell32_test.h [new file with mode: 0755]
reactos/regtests/winetests/shell32/shelllink.c [new file with mode: 0755]
reactos/regtests/winetests/shell32/shellpath.c [new file with mode: 0644]
reactos/regtests/winetests/shell32/shlexec.c [new file with mode: 0755]
reactos/regtests/winetests/shell32/shlfileop.c [new file with mode: 0644]
reactos/regtests/winetests/shell32/shlfolder.c [new file with mode: 0644]
reactos/regtests/winetests/shell32/string.c [new file with mode: 0755]
reactos/regtests/winetests/shell32/testlist.c [new file with mode: 0644]

diff --git a/reactos/regtests/winetests/shell32/generated.c b/reactos/regtests/winetests/shell32/generated.c
new file mode 100755 (executable)
index 0000000..dee7638
--- /dev/null
@@ -0,0 +1,1414 @@
+/* File generated automatically from tools/winapi/test.dat; do not edit! */
+/* This file can be copied, modified and distributed without restriction. */
+
+/*
+ * Unit tests for data structure packing
+ */
+
+#define WINVER 0x0501
+#define _WIN32_IE 0x0501
+#define _WIN32_WINNT 0x0501
+
+#define WINE_NOWINSOCK
+
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "wtypes.h"
+#include "shellapi.h"
+#include "winuser.h"
+#include "wingdi.h"
+#include "shlobj.h"
+
+#include "wine/test.h"
+
+/***********************************************************************
+ * Compability macros
+ */
+
+#define DWORD_PTR UINT_PTR
+#define LONG_PTR INT_PTR
+#define ULONG_PTR UINT_PTR
+
+/***********************************************************************
+ * Windows API extension
+ */
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300) && defined(__cplusplus)
+# define FIELD_ALIGNMENT(type, field) __alignof(((type*)0)->field)
+#elif defined(__GNUC__)
+# define FIELD_ALIGNMENT(type, field) __alignof__(((type*)0)->field)
+#else
+/* FIXME: Not sure if is possible to do without compiler extension */
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300) && defined(__cplusplus)
+# define _TYPE_ALIGNMENT(type) __alignof(type)
+#elif defined(__GNUC__)
+# define _TYPE_ALIGNMENT(type) __alignof__(type)
+#else
+/*
+ * FIXME: Not sure if is possible to do without compiler extension
+ *        (if type is not just a name that is, if so the normal)
+ *         TYPE_ALIGNMENT can be used)
+ */
+#endif
+
+#if defined(TYPE_ALIGNMENT) && defined(_MSC_VER) && _MSC_VER >= 800 && !defined(__cplusplus)
+#pragma warning(disable:4116)
+#endif
+
+#if !defined(TYPE_ALIGNMENT) && defined(_TYPE_ALIGNMENT)
+# define TYPE_ALIGNMENT _TYPE_ALIGNMENT
+#endif
+
+/***********************************************************************
+ * Test helper macros
+ */
+
+#ifdef FIELD_ALIGNMENT
+# define TEST_FIELD_ALIGNMENT(type, field, align) \
+   ok(FIELD_ALIGNMENT(type, field) == align, \
+       "FIELD_ALIGNMENT(" #type ", " #field ") == %d (expected " #align ")\n", \
+           (int)FIELD_ALIGNMENT(type, field))
+#else
+# define TEST_FIELD_ALIGNMENT(type, field, align) do { } while (0)
+#endif
+
+#define TEST_FIELD_OFFSET(type, field, offset) \
+    ok(FIELD_OFFSET(type, field) == offset, \
+        "FIELD_OFFSET(" #type ", " #field ") == %ld (expected " #offset ")\n", \
+             (long int)FIELD_OFFSET(type, field))
+
+#ifdef _TYPE_ALIGNMENT
+#define TEST__TYPE_ALIGNMENT(type, align) \
+    ok(_TYPE_ALIGNMENT(type) == align, "TYPE_ALIGNMENT(" #type ") == %d (expected " #align ")\n", (int)_TYPE_ALIGNMENT(type))
+#else
+# define TEST__TYPE_ALIGNMENT(type, align) do { } while (0)
+#endif
+
+#ifdef TYPE_ALIGNMENT
+#define TEST_TYPE_ALIGNMENT(type, align) \
+    ok(TYPE_ALIGNMENT(type) == align, "TYPE_ALIGNMENT(" #type ") == %d (expected " #align ")\n", (int)TYPE_ALIGNMENT(type))
+#else
+# define TEST_TYPE_ALIGNMENT(type, align) do { } while (0)
+#endif
+
+#define TEST_TYPE_SIZE(type, size) \
+    ok(sizeof(type) == size, "sizeof(" #type ") == %d (expected " #size ")\n", ((int) sizeof(type)))
+
+/***********************************************************************
+ * Test macros
+ */
+
+#define TEST_FIELD(type, field_type, field_name, field_offset, field_size, field_align) \
+  TEST_TYPE_SIZE(field_type, field_size); \
+  TEST_FIELD_ALIGNMENT(type, field_name, field_align); \
+  TEST_FIELD_OFFSET(type, field_name, field_offset); \
+
+#define TEST_TYPE(type, size, align) \
+  TEST_TYPE_ALIGNMENT(type, align); \
+  TEST_TYPE_SIZE(type, size)
+
+#define TEST_TYPE_POINTER(type, size, align) \
+    TEST__TYPE_ALIGNMENT(*(type)0, align); \
+    TEST_TYPE_SIZE(*(type)0, size)
+
+#define TEST_TYPE_SIGNED(type) \
+    ok((type) -1 < 0, "(" #type ") -1 < 0\n");
+
+#define TEST_TYPE_UNSIGNED(type) \
+     ok((type) -1 > 0, "(" #type ") -1 > 0\n");
+
+static void test_pack_BLOB(void)
+{
+    /* BLOB (pack 4) */
+    TEST_TYPE(BLOB, 8, 4);
+    TEST_FIELD(BLOB, ULONG, cbSize, 0, 4, 4);
+    TEST_FIELD(BLOB, BYTE *, pBlobData, 4, 4, 4);
+}
+
+static void test_pack_BSTR(void)
+{
+    /* BSTR */
+    TEST_TYPE(BSTR, 4, 4);
+    TEST_TYPE_POINTER(BSTR, 2, 2);
+}
+
+static void test_pack_BSTRBLOB(void)
+{
+    /* BSTRBLOB (pack 4) */
+    TEST_TYPE(BSTRBLOB, 8, 4);
+    TEST_FIELD(BSTRBLOB, ULONG, cbSize, 0, 4, 4);
+    TEST_FIELD(BSTRBLOB, BYTE *, pData, 4, 4, 4);
+}
+
+static void test_pack_BYTE_BLOB(void)
+{
+    /* BYTE_BLOB (pack 4) */
+    TEST_TYPE(BYTE_BLOB, 8, 4);
+    TEST_FIELD(BYTE_BLOB, unsigned long, clSize, 0, 4, 4);
+    TEST_FIELD(BYTE_BLOB, byte[1], abData, 4, 1, 1);
+}
+
+static void test_pack_BYTE_SIZEDARR(void)
+{
+    /* BYTE_SIZEDARR (pack 4) */
+    TEST_TYPE(BYTE_SIZEDARR, 8, 4);
+    TEST_FIELD(BYTE_SIZEDARR, unsigned long, clSize, 0, 4, 4);
+    TEST_FIELD(BYTE_SIZEDARR, byte *, pData, 4, 4, 4);
+}
+
+static void test_pack_CLIPDATA(void)
+{
+    /* CLIPDATA (pack 4) */
+    TEST_TYPE(CLIPDATA, 12, 4);
+    TEST_FIELD(CLIPDATA, ULONG, cbSize, 0, 4, 4);
+    TEST_FIELD(CLIPDATA, long, ulClipFmt, 4, 4, 4);
+    TEST_FIELD(CLIPDATA, BYTE *, pClipData, 8, 4, 4);
+}
+
+static void test_pack_CLIPFORMAT(void)
+{
+    /* CLIPFORMAT */
+    TEST_TYPE(CLIPFORMAT, 2, 2);
+    TEST_TYPE_UNSIGNED(CLIPFORMAT);
+}
+
+static void test_pack_COAUTHIDENTITY(void)
+{
+    /* COAUTHIDENTITY (pack 4) */
+    TEST_TYPE(COAUTHIDENTITY, 28, 4);
+    TEST_FIELD(COAUTHIDENTITY, USHORT *, User, 0, 4, 4);
+    TEST_FIELD(COAUTHIDENTITY, ULONG, UserLength, 4, 4, 4);
+    TEST_FIELD(COAUTHIDENTITY, USHORT *, Domain, 8, 4, 4);
+    TEST_FIELD(COAUTHIDENTITY, ULONG, DomainLength, 12, 4, 4);
+    TEST_FIELD(COAUTHIDENTITY, USHORT *, Password, 16, 4, 4);
+    TEST_FIELD(COAUTHIDENTITY, ULONG, PasswordLength, 20, 4, 4);
+    TEST_FIELD(COAUTHIDENTITY, ULONG, Flags, 24, 4, 4);
+}
+
+static void test_pack_COAUTHINFO(void)
+{
+    /* COAUTHINFO (pack 4) */
+    TEST_TYPE(COAUTHINFO, 28, 4);
+    TEST_FIELD(COAUTHINFO, DWORD, dwAuthnSvc, 0, 4, 4);
+    TEST_FIELD(COAUTHINFO, DWORD, dwAuthzSvc, 4, 4, 4);
+    TEST_FIELD(COAUTHINFO, LPWSTR, pwszServerPrincName, 8, 4, 4);
+    TEST_FIELD(COAUTHINFO, DWORD, dwAuthnLevel, 12, 4, 4);
+    TEST_FIELD(COAUTHINFO, DWORD, dwImpersonationLevel, 16, 4, 4);
+    TEST_FIELD(COAUTHINFO, COAUTHIDENTITY *, pAuthIdentityData, 20, 4, 4);
+    TEST_FIELD(COAUTHINFO, DWORD, dwCapabilities, 24, 4, 4);
+}
+
+static void test_pack_COSERVERINFO(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);
+}
+
+static void test_pack_DWORD_SIZEDARR(void)
+{
+    /* DWORD_SIZEDARR (pack 4) */
+    TEST_TYPE(DWORD_SIZEDARR, 8, 4);
+    TEST_FIELD(DWORD_SIZEDARR, unsigned long, clSize, 0, 4, 4);
+    TEST_FIELD(DWORD_SIZEDARR, unsigned long *, pData, 4, 4, 4);
+}
+
+static void test_pack_FLAGGED_BYTE_BLOB(void)
+{
+    /* FLAGGED_BYTE_BLOB (pack 4) */
+    TEST_TYPE(FLAGGED_BYTE_BLOB, 12, 4);
+    TEST_FIELD(FLAGGED_BYTE_BLOB, unsigned long, fFlags, 0, 4, 4);
+    TEST_FIELD(FLAGGED_BYTE_BLOB, unsigned long, clSize, 4, 4, 4);
+    TEST_FIELD(FLAGGED_BYTE_BLOB, byte[1], abData, 8, 1, 1);
+}
+
+static void test_pack_FLAGGED_WORD_BLOB(void)
+{
+    /* FLAGGED_WORD_BLOB (pack 4) */
+    TEST_TYPE(FLAGGED_WORD_BLOB, 12, 4);
+    TEST_FIELD(FLAGGED_WORD_BLOB, unsigned long, fFlags, 0, 4, 4);
+    TEST_FIELD(FLAGGED_WORD_BLOB, unsigned long, clSize, 4, 4, 4);
+    TEST_FIELD(FLAGGED_WORD_BLOB, unsigned short[1], asData, 8, 2, 2);
+}
+
+static void test_pack_HMETAFILEPICT(void)
+{
+    /* HMETAFILEPICT */
+    TEST_TYPE(HMETAFILEPICT, 4, 4);
+}
+
+static void test_pack_HYPER_SIZEDARR(void)
+{
+    /* HYPER_SIZEDARR (pack 4) */
+    TEST_TYPE(HYPER_SIZEDARR, 8, 4);
+    TEST_FIELD(HYPER_SIZEDARR, unsigned long, clSize, 0, 4, 4);
+    TEST_FIELD(HYPER_SIZEDARR, hyper *, pData, 4, 4, 4);
+}
+
+static void test_pack_LPBLOB(void)
+{
+    /* LPBLOB */
+    TEST_TYPE(LPBLOB, 4, 4);
+    TEST_TYPE_POINTER(LPBLOB, 8, 4);
+}
+
+static void test_pack_LPBSTR(void)
+{
+    /* LPBSTR */
+    TEST_TYPE(LPBSTR, 4, 4);
+    TEST_TYPE_POINTER(LPBSTR, 4, 4);
+}
+
+static void test_pack_LPBSTRBLOB(void)
+{
+    /* LPBSTRBLOB */
+    TEST_TYPE(LPBSTRBLOB, 4, 4);
+    TEST_TYPE_POINTER(LPBSTRBLOB, 8, 4);
+}
+
+static void test_pack_LPCOLESTR(void)
+{
+    /* LPCOLESTR */
+    TEST_TYPE(LPCOLESTR, 4, 4);
+    TEST_TYPE_POINTER(LPCOLESTR, 2, 2);
+}
+
+static void test_pack_LPCY(void)
+{
+    /* LPCY */
+    TEST_TYPE(LPCY, 4, 4);
+}
+
+static void test_pack_LPDECIMAL(void)
+{
+    /* LPDECIMAL */
+    TEST_TYPE(LPDECIMAL, 4, 4);
+}
+
+static void test_pack_LPOLESTR(void)
+{
+    /* LPOLESTR */
+    TEST_TYPE(LPOLESTR, 4, 4);
+    TEST_TYPE_POINTER(LPOLESTR, 2, 2);
+}
+
+static void test_pack_OLECHAR(void)
+{
+    /* OLECHAR */
+    TEST_TYPE(OLECHAR, 2, 2);
+}
+
+static void test_pack_PROPID(void)
+{
+    /* PROPID */
+    TEST_TYPE(PROPID, 4, 4);
+}
+
+static void test_pack_RemHBITMAP(void)
+{
+    /* RemHBITMAP (pack 4) */
+    TEST_TYPE(RemHBITMAP, 8, 4);
+    TEST_FIELD(RemHBITMAP, unsigned long, cbData, 0, 4, 4);
+    TEST_FIELD(RemHBITMAP, byte[1], data, 4, 1, 1);
+}
+
+static void test_pack_RemHENHMETAFILE(void)
+{
+    /* RemHENHMETAFILE (pack 4) */
+    TEST_TYPE(RemHENHMETAFILE, 8, 4);
+    TEST_FIELD(RemHENHMETAFILE, unsigned long, cbData, 0, 4, 4);
+    TEST_FIELD(RemHENHMETAFILE, byte[1], data, 4, 1, 1);
+}
+
+static void test_pack_RemHGLOBAL(void)
+{
+    /* RemHGLOBAL (pack 4) */
+    TEST_TYPE(RemHGLOBAL, 12, 4);
+    TEST_FIELD(RemHGLOBAL, long, fNullHGlobal, 0, 4, 4);
+    TEST_FIELD(RemHGLOBAL, unsigned long, cbData, 4, 4, 4);
+    TEST_FIELD(RemHGLOBAL, byte[1], data, 8, 1, 1);
+}
+
+static void test_pack_RemHMETAFILEPICT(void)
+{
+    /* RemHMETAFILEPICT (pack 4) */
+    TEST_TYPE(RemHMETAFILEPICT, 20, 4);
+    TEST_FIELD(RemHMETAFILEPICT, long, mm, 0, 4, 4);
+    TEST_FIELD(RemHMETAFILEPICT, long, xExt, 4, 4, 4);
+    TEST_FIELD(RemHMETAFILEPICT, long, yExt, 8, 4, 4);
+    TEST_FIELD(RemHMETAFILEPICT, unsigned long, cbData, 12, 4, 4);
+    TEST_FIELD(RemHMETAFILEPICT, byte[1], data, 16, 1, 1);
+}
+
+static void test_pack_RemHPALETTE(void)
+{
+    /* RemHPALETTE (pack 4) */
+    TEST_TYPE(RemHPALETTE, 8, 4);
+    TEST_FIELD(RemHPALETTE, unsigned long, cbData, 0, 4, 4);
+    TEST_FIELD(RemHPALETTE, byte[1], data, 4, 1, 1);
+}
+
+static void test_pack_SCODE(void)
+{
+    /* SCODE */
+    TEST_TYPE(SCODE, 4, 4);
+}
+
+static void test_pack_UP_BYTE_BLOB(void)
+{
+    /* UP_BYTE_BLOB */
+    TEST_TYPE(UP_BYTE_BLOB, 4, 4);
+    TEST_TYPE_POINTER(UP_BYTE_BLOB, 8, 4);
+}
+
+static void test_pack_UP_FLAGGED_BYTE_BLOB(void)
+{
+    /* UP_FLAGGED_BYTE_BLOB */
+    TEST_TYPE(UP_FLAGGED_BYTE_BLOB, 4, 4);
+    TEST_TYPE_POINTER(UP_FLAGGED_BYTE_BLOB, 12, 4);
+}
+
+static void test_pack_UP_FLAGGED_WORD_BLOB(void)
+{
+    /* UP_FLAGGED_WORD_BLOB */
+    TEST_TYPE(UP_FLAGGED_WORD_BLOB, 4, 4);
+    TEST_TYPE_POINTER(UP_FLAGGED_WORD_BLOB, 12, 4);
+}
+
+static void test_pack_VARIANT_BOOL(void)
+{
+    /* VARIANT_BOOL */
+    TEST_TYPE(VARIANT_BOOL, 2, 2);
+    TEST_TYPE_SIGNED(VARIANT_BOOL);
+}
+
+static void test_pack_VARTYPE(void)
+{
+    /* VARTYPE */
+    TEST_TYPE(VARTYPE, 2, 2);
+    TEST_TYPE_UNSIGNED(VARTYPE);
+}
+
+static void test_pack_WORD_SIZEDARR(void)
+{
+    /* WORD_SIZEDARR (pack 4) */
+    TEST_TYPE(WORD_SIZEDARR, 8, 4);
+    TEST_FIELD(WORD_SIZEDARR, unsigned long, clSize, 0, 4, 4);
+    TEST_FIELD(WORD_SIZEDARR, unsigned short *, pData, 4, 4, 4);
+}
+
+static void test_pack_remoteMETAFILEPICT(void)
+{
+    /* remoteMETAFILEPICT (pack 4) */
+    TEST_TYPE(remoteMETAFILEPICT, 16, 4);
+    TEST_FIELD(remoteMETAFILEPICT, long, mm, 0, 4, 4);
+    TEST_FIELD(remoteMETAFILEPICT, long, xExt, 4, 4, 4);
+    TEST_FIELD(remoteMETAFILEPICT, long, yExt, 8, 4, 4);
+    TEST_FIELD(remoteMETAFILEPICT, userHMETAFILE *, hMF, 12, 4, 4);
+}
+
+static void test_pack_userBITMAP(void)
+{
+    /* userBITMAP (pack 4) */
+    TEST_TYPE(userBITMAP, 28, 4);
+    TEST_FIELD(userBITMAP, LONG, bmType, 0, 4, 4);
+    TEST_FIELD(userBITMAP, LONG, bmWidth, 4, 4, 4);
+    TEST_FIELD(userBITMAP, LONG, bmHeight, 8, 4, 4);
+    TEST_FIELD(userBITMAP, LONG, bmWidthBytes, 12, 4, 4);
+    TEST_FIELD(userBITMAP, WORD, bmPlanes, 16, 2, 2);
+    TEST_FIELD(userBITMAP, WORD, bmBitsPixel, 18, 2, 2);
+    TEST_FIELD(userBITMAP, ULONG, cbSize, 20, 4, 4);
+    TEST_FIELD(userBITMAP, byte[1], pBuffer, 24, 1, 1);
+}
+
+static void test_pack_userCLIPFORMAT(void)
+{
+    /* userCLIPFORMAT (pack 4) */
+    TEST_FIELD(userCLIPFORMAT, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_userHBITMAP(void)
+{
+    /* userHBITMAP (pack 4) */
+    TEST_FIELD(userHBITMAP, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_userHENHMETAFILE(void)
+{
+    /* userHENHMETAFILE (pack 4) */
+    TEST_FIELD(userHENHMETAFILE, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_userHGLOBAL(void)
+{
+    /* userHGLOBAL (pack 4) */
+    TEST_FIELD(userHGLOBAL, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_userHMETAFILE(void)
+{
+    /* userHMETAFILE (pack 4) */
+    TEST_FIELD(userHMETAFILE, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_userHMETAFILEPICT(void)
+{
+    /* userHMETAFILEPICT (pack 4) */
+    TEST_FIELD(userHMETAFILEPICT, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_userHPALETTE(void)
+{
+    /* userHPALETTE (pack 4) */
+    TEST_FIELD(userHPALETTE, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_wireBSTR(void)
+{
+    /* wireBSTR */
+    TEST_TYPE(wireBSTR, 4, 4);
+    TEST_TYPE_POINTER(wireBSTR, 12, 4);
+}
+
+static void test_pack_wireCLIPFORMAT(void)
+{
+    /* wireCLIPFORMAT */
+    TEST_TYPE(wireCLIPFORMAT, 4, 4);
+}
+
+static void test_pack_wireHBITMAP(void)
+{
+    /* wireHBITMAP */
+    TEST_TYPE(wireHBITMAP, 4, 4);
+}
+
+static void test_pack_wireHENHMETAFILE(void)
+{
+    /* wireHENHMETAFILE */
+    TEST_TYPE(wireHENHMETAFILE, 4, 4);
+}
+
+static void test_pack_wireHGLOBAL(void)
+{
+    /* wireHGLOBAL */
+    TEST_TYPE(wireHGLOBAL, 4, 4);
+}
+
+static void test_pack_wireHMETAFILE(void)
+{
+    /* wireHMETAFILE */
+    TEST_TYPE(wireHMETAFILE, 4, 4);
+}
+
+static void test_pack_wireHMETAFILEPICT(void)
+{
+    /* wireHMETAFILEPICT */
+    TEST_TYPE(wireHMETAFILEPICT, 4, 4);
+}
+
+static void test_pack_wireHPALETTE(void)
+{
+    /* wireHPALETTE */
+    TEST_TYPE(wireHPALETTE, 4, 4);
+}
+
+static void test_pack_CLSID(void)
+{
+    /* CLSID */
+    TEST_TYPE(CLSID, 16, 4);
+}
+
+static void test_pack_FMTID(void)
+{
+    /* FMTID */
+    TEST_TYPE(FMTID, 16, 4);
+}
+
+static void test_pack_GUID(void)
+{
+    /* GUID (pack 4) */
+    TEST_TYPE(GUID, 16, 4);
+    TEST_FIELD(GUID, unsigned long, Data1, 0, 4, 4);
+    TEST_FIELD(GUID, unsigned short, Data2, 4, 2, 2);
+    TEST_FIELD(GUID, unsigned short, Data3, 6, 2, 2);
+    TEST_FIELD(GUID, unsigned char[ 8 ], Data4, 8, 8, 1);
+}
+
+static void test_pack_IID(void)
+{
+    /* IID */
+    TEST_TYPE(IID, 16, 4);
+}
+
+static void test_pack_LPGUID(void)
+{
+    /* LPGUID */
+    TEST_TYPE(LPGUID, 4, 4);
+    TEST_TYPE_POINTER(LPGUID, 16, 4);
+}
+
+static void test_pack_APPBARDATA(void)
+{
+    /* APPBARDATA (pack 1) */
+    TEST_TYPE(APPBARDATA, 36, 1);
+    TEST_FIELD(APPBARDATA, DWORD, cbSize, 0, 4, 1);
+    TEST_FIELD(APPBARDATA, HWND, hWnd, 4, 4, 1);
+    TEST_FIELD(APPBARDATA, UINT, uCallbackMessage, 8, 4, 1);
+    TEST_FIELD(APPBARDATA, UINT, uEdge, 12, 4, 1);
+    TEST_FIELD(APPBARDATA, RECT, rc, 16, 16, 1);
+    TEST_FIELD(APPBARDATA, LPARAM, lParam, 32, 4, 1);
+}
+
+static void test_pack_DRAGINFOA(void)
+{
+    /* DRAGINFOA (pack 1) */
+    TEST_TYPE(DRAGINFOA, 24, 1);
+    TEST_FIELD(DRAGINFOA, UINT, uSize, 0, 4, 1);
+    TEST_FIELD(DRAGINFOA, POINT, pt, 4, 8, 1);
+    TEST_FIELD(DRAGINFOA, BOOL, fNC, 12, 4, 1);
+    TEST_FIELD(DRAGINFOA, LPSTR, lpFileList, 16, 4, 1);
+    TEST_FIELD(DRAGINFOA, DWORD, grfKeyState, 20, 4, 1);
+}
+
+static void test_pack_DRAGINFOW(void)
+{
+    /* DRAGINFOW (pack 1) */
+    TEST_TYPE(DRAGINFOW, 24, 1);
+    TEST_FIELD(DRAGINFOW, UINT, uSize, 0, 4, 1);
+    TEST_FIELD(DRAGINFOW, POINT, pt, 4, 8, 1);
+    TEST_FIELD(DRAGINFOW, BOOL, fNC, 12, 4, 1);
+    TEST_FIELD(DRAGINFOW, LPWSTR, lpFileList, 16, 4, 1);
+    TEST_FIELD(DRAGINFOW, DWORD, grfKeyState, 20, 4, 1);
+}
+
+static void test_pack_FILEOP_FLAGS(void)
+{
+    /* FILEOP_FLAGS */
+    TEST_TYPE(FILEOP_FLAGS, 2, 2);
+    TEST_TYPE_UNSIGNED(FILEOP_FLAGS);
+}
+
+static void test_pack_LPDRAGINFOA(void)
+{
+    /* LPDRAGINFOA */
+    TEST_TYPE(LPDRAGINFOA, 4, 4);
+    TEST_TYPE_POINTER(LPDRAGINFOA, 24, 1);
+}
+
+static void test_pack_LPDRAGINFOW(void)
+{
+    /* LPDRAGINFOW */
+    TEST_TYPE(LPDRAGINFOW, 4, 4);
+    TEST_TYPE_POINTER(LPDRAGINFOW, 24, 1);
+}
+
+static void test_pack_LPSHELLEXECUTEINFOA(void)
+{
+    /* LPSHELLEXECUTEINFOA */
+    TEST_TYPE(LPSHELLEXECUTEINFOA, 4, 4);
+}
+
+static void test_pack_LPSHELLEXECUTEINFOW(void)
+{
+    /* LPSHELLEXECUTEINFOW */
+    TEST_TYPE(LPSHELLEXECUTEINFOW, 4, 4);
+}
+
+static void test_pack_LPSHFILEOPSTRUCTA(void)
+{
+    /* LPSHFILEOPSTRUCTA */
+    TEST_TYPE(LPSHFILEOPSTRUCTA, 4, 4);
+    TEST_TYPE_POINTER(LPSHFILEOPSTRUCTA, 30, 1);
+}
+
+static void test_pack_LPSHFILEOPSTRUCTW(void)
+{
+    /* LPSHFILEOPSTRUCTW */
+    TEST_TYPE(LPSHFILEOPSTRUCTW, 4, 4);
+    TEST_TYPE_POINTER(LPSHFILEOPSTRUCTW, 30, 1);
+}
+
+static void test_pack_LPSHNAMEMAPPINGA(void)
+{
+    /* LPSHNAMEMAPPINGA */
+    TEST_TYPE(LPSHNAMEMAPPINGA, 4, 4);
+    TEST_TYPE_POINTER(LPSHNAMEMAPPINGA, 16, 1);
+}
+
+static void test_pack_LPSHNAMEMAPPINGW(void)
+{
+    /* LPSHNAMEMAPPINGW */
+    TEST_TYPE(LPSHNAMEMAPPINGW, 4, 4);
+    TEST_TYPE_POINTER(LPSHNAMEMAPPINGW, 16, 1);
+}
+
+static void test_pack_NOTIFYICONDATAA(void)
+{
+    /* NOTIFYICONDATAA (pack 1) */
+    TEST_FIELD(NOTIFYICONDATAA, DWORD, cbSize, 0, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAA, HWND, hWnd, 4, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAA, UINT, uID, 8, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAA, UINT, uFlags, 12, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAA, UINT, uCallbackMessage, 16, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAA, HICON, hIcon, 20, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAA, CHAR[128], szTip, 24, 128, 1);
+    TEST_FIELD(NOTIFYICONDATAA, DWORD, dwState, 152, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAA, DWORD, dwStateMask, 156, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAA, CHAR[256], szInfo, 160, 256, 1);
+}
+
+static void test_pack_NOTIFYICONDATAW(void)
+{
+    /* NOTIFYICONDATAW (pack 1) */
+    TEST_FIELD(NOTIFYICONDATAW, DWORD, cbSize, 0, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAW, HWND, hWnd, 4, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAW, UINT, uID, 8, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAW, UINT, uFlags, 12, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAW, UINT, uCallbackMessage, 16, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAW, HICON, hIcon, 20, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAW, WCHAR[128], szTip, 24, 256, 1);
+    TEST_FIELD(NOTIFYICONDATAW, DWORD, dwState, 280, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAW, DWORD, dwStateMask, 284, 4, 1);
+    TEST_FIELD(NOTIFYICONDATAW, WCHAR[256], szInfo, 288, 512, 1);
+}
+
+static void test_pack_PAPPBARDATA(void)
+{
+    /* PAPPBARDATA */
+    TEST_TYPE(PAPPBARDATA, 4, 4);
+    TEST_TYPE_POINTER(PAPPBARDATA, 36, 1);
+}
+
+static void test_pack_PNOTIFYICONDATAA(void)
+{
+    /* PNOTIFYICONDATAA */
+    TEST_TYPE(PNOTIFYICONDATAA, 4, 4);
+}
+
+static void test_pack_PNOTIFYICONDATAW(void)
+{
+    /* PNOTIFYICONDATAW */
+    TEST_TYPE(PNOTIFYICONDATAW, 4, 4);
+}
+
+static void test_pack_PRINTEROP_FLAGS(void)
+{
+    /* PRINTEROP_FLAGS */
+    TEST_TYPE(PRINTEROP_FLAGS, 2, 2);
+    TEST_TYPE_UNSIGNED(PRINTEROP_FLAGS);
+}
+
+static void test_pack_SHELLEXECUTEINFOA(void)
+{
+    /* SHELLEXECUTEINFOA (pack 1) */
+    TEST_FIELD(SHELLEXECUTEINFOA, DWORD, cbSize, 0, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOA, ULONG, fMask, 4, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOA, HWND, hwnd, 8, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOA, LPCSTR, lpVerb, 12, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOA, LPCSTR, lpFile, 16, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOA, LPCSTR, lpParameters, 20, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOA, LPCSTR, lpDirectory, 24, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOA, INT, nShow, 28, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOA, HINSTANCE, hInstApp, 32, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOA, LPVOID, lpIDList, 36, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOA, LPCSTR, lpClass, 40, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOA, HKEY, hkeyClass, 44, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOA, DWORD, dwHotKey, 48, 4, 1);
+}
+
+static void test_pack_SHELLEXECUTEINFOW(void)
+{
+    /* SHELLEXECUTEINFOW (pack 1) */
+    TEST_FIELD(SHELLEXECUTEINFOW, DWORD, cbSize, 0, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOW, ULONG, fMask, 4, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOW, HWND, hwnd, 8, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOW, LPCWSTR, lpVerb, 12, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOW, LPCWSTR, lpFile, 16, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOW, LPCWSTR, lpParameters, 20, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOW, LPCWSTR, lpDirectory, 24, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOW, INT, nShow, 28, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOW, HINSTANCE, hInstApp, 32, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOW, LPVOID, lpIDList, 36, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOW, LPCWSTR, lpClass, 40, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOW, HKEY, hkeyClass, 44, 4, 1);
+    TEST_FIELD(SHELLEXECUTEINFOW, DWORD, dwHotKey, 48, 4, 1);
+}
+
+static void test_pack_SHFILEINFOA(void)
+{
+    /* SHFILEINFOA (pack 1) */
+    TEST_TYPE(SHFILEINFOA, 352, 1);
+    TEST_FIELD(SHFILEINFOA, HICON, hIcon, 0, 4, 1);
+    TEST_FIELD(SHFILEINFOA, int, iIcon, 4, 4, 1);
+    TEST_FIELD(SHFILEINFOA, DWORD, dwAttributes, 8, 4, 1);
+    TEST_FIELD(SHFILEINFOA, CHAR[MAX_PATH], szDisplayName, 12, 260, 1);
+    TEST_FIELD(SHFILEINFOA, CHAR[80], szTypeName, 272, 80, 1);
+}
+
+static void test_pack_SHFILEINFOW(void)
+{
+    /* SHFILEINFOW (pack 1) */
+    TEST_TYPE(SHFILEINFOW, 692, 1);
+    TEST_FIELD(SHFILEINFOW, HICON, hIcon, 0, 4, 1);
+    TEST_FIELD(SHFILEINFOW, int, iIcon, 4, 4, 1);
+    TEST_FIELD(SHFILEINFOW, DWORD, dwAttributes, 8, 4, 1);
+    TEST_FIELD(SHFILEINFOW, WCHAR[MAX_PATH], szDisplayName, 12, 520, 1);
+    TEST_FIELD(SHFILEINFOW, WCHAR[80], szTypeName, 532, 160, 1);
+}
+
+static void test_pack_SHFILEOPSTRUCTA(void)
+{
+    /* SHFILEOPSTRUCTA (pack 1) */
+    TEST_TYPE(SHFILEOPSTRUCTA, 30, 1);
+    TEST_FIELD(SHFILEOPSTRUCTA, HWND, hwnd, 0, 4, 1);
+    TEST_FIELD(SHFILEOPSTRUCTA, UINT, wFunc, 4, 4, 1);
+    TEST_FIELD(SHFILEOPSTRUCTA, LPCSTR, pFrom, 8, 4, 1);
+    TEST_FIELD(SHFILEOPSTRUCTA, LPCSTR, pTo, 12, 4, 1);
+    TEST_FIELD(SHFILEOPSTRUCTA, FILEOP_FLAGS, fFlags, 16, 2, 1);
+    TEST_FIELD(SHFILEOPSTRUCTA, BOOL, fAnyOperationsAborted, 18, 4, 1);
+    TEST_FIELD(SHFILEOPSTRUCTA, LPVOID, hNameMappings, 22, 4, 1);
+    TEST_FIELD(SHFILEOPSTRUCTA, LPCSTR, lpszProgressTitle, 26, 4, 1);
+}
+
+static void test_pack_SHFILEOPSTRUCTW(void)
+{
+    /* SHFILEOPSTRUCTW (pack 1) */
+    TEST_TYPE(SHFILEOPSTRUCTW, 30, 1);
+    TEST_FIELD(SHFILEOPSTRUCTW, HWND, hwnd, 0, 4, 1);
+    TEST_FIELD(SHFILEOPSTRUCTW, UINT, wFunc, 4, 4, 1);
+    TEST_FIELD(SHFILEOPSTRUCTW, LPCWSTR, pFrom, 8, 4, 1);
+    TEST_FIELD(SHFILEOPSTRUCTW, LPCWSTR, pTo, 12, 4, 1);
+    TEST_FIELD(SHFILEOPSTRUCTW, FILEOP_FLAGS, fFlags, 16, 2, 1);
+    TEST_FIELD(SHFILEOPSTRUCTW, BOOL, fAnyOperationsAborted, 18, 4, 1);
+    TEST_FIELD(SHFILEOPSTRUCTW, LPVOID, hNameMappings, 22, 4, 1);
+    TEST_FIELD(SHFILEOPSTRUCTW, LPCWSTR, lpszProgressTitle, 26, 4, 1);
+}
+
+static void test_pack_SHNAMEMAPPINGA(void)
+{
+    /* SHNAMEMAPPINGA (pack 1) */
+    TEST_TYPE(SHNAMEMAPPINGA, 16, 1);
+    TEST_FIELD(SHNAMEMAPPINGA, LPSTR, pszOldPath, 0, 4, 1);
+    TEST_FIELD(SHNAMEMAPPINGA, LPSTR, pszNewPath, 4, 4, 1);
+    TEST_FIELD(SHNAMEMAPPINGA, int, cchOldPath, 8, 4, 1);
+    TEST_FIELD(SHNAMEMAPPINGA, int, cchNewPath, 12, 4, 1);
+}
+
+static void test_pack_SHNAMEMAPPINGW(void)
+{
+    /* SHNAMEMAPPINGW (pack 1) */
+    TEST_TYPE(SHNAMEMAPPINGW, 16, 1);
+    TEST_FIELD(SHNAMEMAPPINGW, LPWSTR, pszOldPath, 0, 4, 1);
+    TEST_FIELD(SHNAMEMAPPINGW, LPWSTR, pszNewPath, 4, 4, 1);
+    TEST_FIELD(SHNAMEMAPPINGW, int, cchOldPath, 8, 4, 1);
+    TEST_FIELD(SHNAMEMAPPINGW, int, cchNewPath, 12, 4, 1);
+}
+
+static void test_pack_ITEMIDLIST(void)
+{
+    /* ITEMIDLIST (pack 1) */
+    TEST_TYPE(ITEMIDLIST, 3, 1);
+    TEST_FIELD(ITEMIDLIST, SHITEMID, mkid, 0, 3, 1);
+}
+
+static void test_pack_LPCITEMIDLIST(void)
+{
+    /* LPCITEMIDLIST */
+    TEST_TYPE(LPCITEMIDLIST, 4, 4);
+    TEST_TYPE_POINTER(LPCITEMIDLIST, 3, 1);
+}
+
+static void test_pack_LPCSHITEMID(void)
+{
+    /* LPCSHITEMID */
+    TEST_TYPE(LPCSHITEMID, 4, 4);
+    TEST_TYPE_POINTER(LPCSHITEMID, 3, 1);
+}
+
+static void test_pack_LPITEMIDLIST(void)
+{
+    /* LPITEMIDLIST */
+    TEST_TYPE(LPITEMIDLIST, 4, 4);
+    TEST_TYPE_POINTER(LPITEMIDLIST, 3, 1);
+}
+
+static void test_pack_LPSHELLDETAILS(void)
+{
+    /* LPSHELLDETAILS */
+    TEST_TYPE(LPSHELLDETAILS, 4, 4);
+}
+
+static void test_pack_LPSHITEMID(void)
+{
+    /* LPSHITEMID */
+    TEST_TYPE(LPSHITEMID, 4, 4);
+    TEST_TYPE_POINTER(LPSHITEMID, 3, 1);
+}
+
+static void test_pack_LPSTRRET(void)
+{
+    /* LPSTRRET */
+    TEST_TYPE(LPSTRRET, 4, 4);
+}
+
+static void test_pack_SHELLDETAILS(void)
+{
+    /* SHELLDETAILS (pack 1) */
+    TEST_FIELD(SHELLDETAILS, int, fmt, 0, 4, 1);
+    TEST_FIELD(SHELLDETAILS, int, cxChar, 4, 4, 1);
+}
+
+static void test_pack_SHITEMID(void)
+{
+    /* SHITEMID (pack 1) */
+    TEST_TYPE(SHITEMID, 3, 1);
+    TEST_FIELD(SHITEMID, WORD, cb, 0, 2, 1);
+    TEST_FIELD(SHITEMID, BYTE[1], abID, 2, 1, 1);
+}
+
+static void test_pack_STRRET(void)
+{
+    /* STRRET (pack 4) */
+    TEST_FIELD(STRRET, UINT, uType, 0, 4, 4);
+}
+
+static void test_pack_AUTO_SCROLL_DATA(void)
+{
+    /* AUTO_SCROLL_DATA (pack 1) */
+    TEST_TYPE(AUTO_SCROLL_DATA, 48, 1);
+    TEST_FIELD(AUTO_SCROLL_DATA, int, iNextSample, 0, 4, 1);
+    TEST_FIELD(AUTO_SCROLL_DATA, DWORD, dwLastScroll, 4, 4, 1);
+    TEST_FIELD(AUTO_SCROLL_DATA, BOOL, bFull, 8, 4, 1);
+    TEST_FIELD(AUTO_SCROLL_DATA, POINT[NUM_POINTS], pts, 12, 24, 1);
+    TEST_FIELD(AUTO_SCROLL_DATA, DWORD[NUM_POINTS], dwTimes, 36, 12, 1);
+}
+
+static void test_pack_BFFCALLBACK(void)
+{
+    /* BFFCALLBACK */
+    TEST_TYPE(BFFCALLBACK, 4, 4);
+}
+
+static void test_pack_BROWSEINFOA(void)
+{
+    /* BROWSEINFOA (pack 8) */
+    TEST_TYPE(BROWSEINFOA, 32, 4);
+    TEST_FIELD(BROWSEINFOA, HWND, hwndOwner, 0, 4, 4);
+    TEST_FIELD(BROWSEINFOA, LPCITEMIDLIST, pidlRoot, 4, 4, 4);
+    TEST_FIELD(BROWSEINFOA, LPSTR, pszDisplayName, 8, 4, 4);
+    TEST_FIELD(BROWSEINFOA, LPCSTR, lpszTitle, 12, 4, 4);
+    TEST_FIELD(BROWSEINFOA, UINT, ulFlags, 16, 4, 4);
+    TEST_FIELD(BROWSEINFOA, BFFCALLBACK, lpfn, 20, 4, 4);
+    TEST_FIELD(BROWSEINFOA, LPARAM, lParam, 24, 4, 4);
+    TEST_FIELD(BROWSEINFOA, INT, iImage, 28, 4, 4);
+}
+
+static void test_pack_BROWSEINFOW(void)
+{
+    /* BROWSEINFOW (pack 8) */
+    TEST_TYPE(BROWSEINFOW, 32, 4);
+    TEST_FIELD(BROWSEINFOW, HWND, hwndOwner, 0, 4, 4);
+    TEST_FIELD(BROWSEINFOW, LPCITEMIDLIST, pidlRoot, 4, 4, 4);
+    TEST_FIELD(BROWSEINFOW, LPWSTR, pszDisplayName, 8, 4, 4);
+    TEST_FIELD(BROWSEINFOW, LPCWSTR, lpszTitle, 12, 4, 4);
+    TEST_FIELD(BROWSEINFOW, UINT, ulFlags, 16, 4, 4);
+    TEST_FIELD(BROWSEINFOW, BFFCALLBACK, lpfn, 20, 4, 4);
+    TEST_FIELD(BROWSEINFOW, LPARAM, lParam, 24, 4, 4);
+    TEST_FIELD(BROWSEINFOW, INT, iImage, 28, 4, 4);
+}
+
+static void test_pack_CABINETSTATE(void)
+{
+    /* CABINETSTATE (pack 1) */
+    TEST_TYPE(CABINETSTATE, 12, 1);
+    TEST_FIELD(CABINETSTATE, WORD, cLength, 0, 2, 1);
+    TEST_FIELD(CABINETSTATE, WORD, nVersion, 2, 2, 1);
+    TEST_FIELD(CABINETSTATE, UINT, fMenuEnumFilter, 8, 4, 1);
+}
+
+static void test_pack_CIDA(void)
+{
+    /* CIDA (pack 1) */
+    TEST_TYPE(CIDA, 8, 1);
+    TEST_FIELD(CIDA, UINT, cidl, 0, 4, 1);
+    TEST_FIELD(CIDA, UINT[1], aoffset, 4, 4, 1);
+}
+
+static void test_pack_CSFV(void)
+{
+    /* CSFV (pack 1) */
+    TEST_FIELD(CSFV, UINT, cbSize, 0, 4, 1);
+    TEST_FIELD(CSFV, IShellFolder*, pshf, 4, 4, 1);
+    TEST_FIELD(CSFV, IShellView*, psvOuter, 8, 4, 1);
+    TEST_FIELD(CSFV, LPCITEMIDLIST, pidl, 12, 4, 1);
+    TEST_FIELD(CSFV, LONG, lEvents, 16, 4, 1);
+    TEST_FIELD(CSFV, LPFNVIEWCALLBACK, pfnCallback, 20, 4, 1);
+}
+
+static void test_pack_DROPFILES(void)
+{
+    /* DROPFILES (pack 1) */
+    TEST_TYPE(DROPFILES, 20, 1);
+    TEST_FIELD(DROPFILES, DWORD, pFiles, 0, 4, 1);
+    TEST_FIELD(DROPFILES, POINT, pt, 4, 8, 1);
+    TEST_FIELD(DROPFILES, BOOL, fNC, 12, 4, 1);
+    TEST_FIELD(DROPFILES, BOOL, fWide, 16, 4, 1);
+}
+
+static void test_pack_FILEDESCRIPTORA(void)
+{
+    /* FILEDESCRIPTORA (pack 1) */
+    TEST_TYPE(FILEDESCRIPTORA, 332, 1);
+    TEST_FIELD(FILEDESCRIPTORA, DWORD, dwFlags, 0, 4, 1);
+    TEST_FIELD(FILEDESCRIPTORA, CLSID, clsid, 4, 16, 1);
+    TEST_FIELD(FILEDESCRIPTORA, SIZEL, sizel, 20, 8, 1);
+    TEST_FIELD(FILEDESCRIPTORA, POINTL, pointl, 28, 8, 1);
+    TEST_FIELD(FILEDESCRIPTORA, DWORD, dwFileAttributes, 36, 4, 1);
+    TEST_FIELD(FILEDESCRIPTORA, FILETIME, ftCreationTime, 40, 8, 1);
+    TEST_FIELD(FILEDESCRIPTORA, FILETIME, ftLastAccessTime, 48, 8, 1);
+    TEST_FIELD(FILEDESCRIPTORA, FILETIME, ftLastWriteTime, 56, 8, 1);
+    TEST_FIELD(FILEDESCRIPTORA, DWORD, nFileSizeHigh, 64, 4, 1);
+    TEST_FIELD(FILEDESCRIPTORA, DWORD, nFileSizeLow, 68, 4, 1);
+    TEST_FIELD(FILEDESCRIPTORA, CHAR[MAX_PATH], cFileName, 72, 260, 1);
+}
+
+static void test_pack_FILEDESCRIPTORW(void)
+{
+    /* FILEDESCRIPTORW (pack 1) */
+    TEST_TYPE(FILEDESCRIPTORW, 592, 1);
+    TEST_FIELD(FILEDESCRIPTORW, DWORD, dwFlags, 0, 4, 1);
+    TEST_FIELD(FILEDESCRIPTORW, CLSID, clsid, 4, 16, 1);
+    TEST_FIELD(FILEDESCRIPTORW, SIZEL, sizel, 20, 8, 1);
+    TEST_FIELD(FILEDESCRIPTORW, POINTL, pointl, 28, 8, 1);
+    TEST_FIELD(FILEDESCRIPTORW, DWORD, dwFileAttributes, 36, 4, 1);
+    TEST_FIELD(FILEDESCRIPTORW, FILETIME, ftCreationTime, 40, 8, 1);
+    TEST_FIELD(FILEDESCRIPTORW, FILETIME, ftLastAccessTime, 48, 8, 1);
+    TEST_FIELD(FILEDESCRIPTORW, FILETIME, ftLastWriteTime, 56, 8, 1);
+    TEST_FIELD(FILEDESCRIPTORW, DWORD, nFileSizeHigh, 64, 4, 1);
+    TEST_FIELD(FILEDESCRIPTORW, DWORD, nFileSizeLow, 68, 4, 1);
+    TEST_FIELD(FILEDESCRIPTORW, WCHAR[MAX_PATH], cFileName, 72, 520, 1);
+}
+
+static void test_pack_FILEGROUPDESCRIPTORA(void)
+{
+    /* FILEGROUPDESCRIPTORA (pack 1) */
+    TEST_TYPE(FILEGROUPDESCRIPTORA, 336, 1);
+    TEST_FIELD(FILEGROUPDESCRIPTORA, UINT, cItems, 0, 4, 1);
+    TEST_FIELD(FILEGROUPDESCRIPTORA, FILEDESCRIPTORA[1], fgd, 4, 332, 1);
+}
+
+static void test_pack_FILEGROUPDESCRIPTORW(void)
+{
+    /* FILEGROUPDESCRIPTORW (pack 1) */
+    TEST_TYPE(FILEGROUPDESCRIPTORW, 596, 1);
+    TEST_FIELD(FILEGROUPDESCRIPTORW, UINT, cItems, 0, 4, 1);
+    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 */
+    TEST_TYPE(LPBROWSEINFOA, 4, 4);
+    TEST_TYPE_POINTER(LPBROWSEINFOA, 32, 4);
+}
+
+static void test_pack_LPBROWSEINFOW(void)
+{
+    /* LPBROWSEINFOW */
+    TEST_TYPE(LPBROWSEINFOW, 4, 4);
+    TEST_TYPE_POINTER(LPBROWSEINFOW, 32, 4);
+}
+
+static void test_pack_LPCABINETSTATE(void)
+{
+    /* LPCABINETSTATE */
+    TEST_TYPE(LPCABINETSTATE, 4, 4);
+    TEST_TYPE_POINTER(LPCABINETSTATE, 12, 1);
+}
+
+static void test_pack_LPCSFV(void)
+{
+    /* LPCSFV */
+    TEST_TYPE(LPCSFV, 4, 4);
+}
+
+static void test_pack_LPDROPFILES(void)
+{
+    /* LPDROPFILES */
+    TEST_TYPE(LPDROPFILES, 4, 4);
+    TEST_TYPE_POINTER(LPDROPFILES, 20, 1);
+}
+
+static void test_pack_LPFILEDESCRIPTORA(void)
+{
+    /* LPFILEDESCRIPTORA */
+    TEST_TYPE(LPFILEDESCRIPTORA, 4, 4);
+    TEST_TYPE_POINTER(LPFILEDESCRIPTORA, 332, 1);
+}
+
+static void test_pack_LPFILEDESCRIPTORW(void)
+{
+    /* LPFILEDESCRIPTORW */
+    TEST_TYPE(LPFILEDESCRIPTORW, 4, 4);
+    TEST_TYPE_POINTER(LPFILEDESCRIPTORW, 592, 1);
+}
+
+static void test_pack_LPFILEGROUPDESCRIPTORA(void)
+{
+    /* LPFILEGROUPDESCRIPTORA */
+    TEST_TYPE(LPFILEGROUPDESCRIPTORA, 4, 4);
+    TEST_TYPE_POINTER(LPFILEGROUPDESCRIPTORA, 336, 1);
+}
+
+static void test_pack_LPFILEGROUPDESCRIPTORW(void)
+{
+    /* LPFILEGROUPDESCRIPTORW */
+    TEST_TYPE(LPFILEGROUPDESCRIPTORW, 4, 4);
+    TEST_TYPE_POINTER(LPFILEGROUPDESCRIPTORW, 596, 1);
+}
+
+static void test_pack_LPFNVIEWCALLBACK(void)
+{
+    /* LPFNVIEWCALLBACK */
+    TEST_TYPE(LPFNVIEWCALLBACK, 4, 4);
+}
+
+static void test_pack_LPIDA(void)
+{
+    /* LPIDA */
+    TEST_TYPE(LPIDA, 4, 4);
+    TEST_TYPE_POINTER(LPIDA, 8, 1);
+}
+
+static void test_pack_LPQCMINFO(void)
+{
+    /* LPQCMINFO */
+    TEST_TYPE(LPQCMINFO, 4, 4);
+    TEST_TYPE_POINTER(LPQCMINFO, 20, 4);
+}
+
+static void test_pack_LPSHChangeDWORDAsIDList(void)
+{
+    /* LPSHChangeDWORDAsIDList */
+    TEST_TYPE(LPSHChangeDWORDAsIDList, 4, 4);
+    TEST_TYPE_POINTER(LPSHChangeDWORDAsIDList, 12, 1);
+}
+
+static void test_pack_LPSHChangeProductKeyAsIDList(void)
+{
+    /* LPSHChangeProductKeyAsIDList */
+    TEST_TYPE(LPSHChangeProductKeyAsIDList, 4, 4);
+    TEST_TYPE_POINTER(LPSHChangeProductKeyAsIDList, 82, 1);
+}
+
+static void test_pack_LPSHDESCRIPTIONID(void)
+{
+    /* LPSHDESCRIPTIONID */
+    TEST_TYPE(LPSHDESCRIPTIONID, 4, 4);
+    TEST_TYPE_POINTER(LPSHDESCRIPTIONID, 20, 4);
+}
+
+static void test_pack_LPSHELLFLAGSTATE(void)
+{
+    /* LPSHELLFLAGSTATE */
+    TEST_TYPE(LPSHELLFLAGSTATE, 4, 4);
+    TEST_TYPE_POINTER(LPSHELLFLAGSTATE, 4, 1);
+}
+
+static void test_pack_LPSHELLSTATE(void)
+{
+    /* LPSHELLSTATE */
+    TEST_TYPE(LPSHELLSTATE, 4, 4);
+    TEST_TYPE_POINTER(LPSHELLSTATE, 32, 1);
+}
+
+static void test_pack_LPTBINFO(void)
+{
+    /* LPTBINFO */
+    TEST_TYPE(LPTBINFO, 4, 4);
+    TEST_TYPE_POINTER(LPTBINFO, 8, 4);
+}
+
+static void test_pack_PBROWSEINFOA(void)
+{
+    /* PBROWSEINFOA */
+    TEST_TYPE(PBROWSEINFOA, 4, 4);
+    TEST_TYPE_POINTER(PBROWSEINFOA, 32, 4);
+}
+
+static void test_pack_PBROWSEINFOW(void)
+{
+    /* PBROWSEINFOW */
+    TEST_TYPE(PBROWSEINFOW, 4, 4);
+    TEST_TYPE_POINTER(PBROWSEINFOW, 32, 4);
+}
+
+static void test_pack_QCMINFO(void)
+{
+    /* QCMINFO (pack 8) */
+    TEST_TYPE(QCMINFO, 20, 4);
+    TEST_FIELD(QCMINFO, HMENU, hmenu, 0, 4, 4);
+    TEST_FIELD(QCMINFO, UINT, indexMenu, 4, 4, 4);
+    TEST_FIELD(QCMINFO, UINT, idCmdFirst, 8, 4, 4);
+    TEST_FIELD(QCMINFO, UINT, idCmdLast, 12, 4, 4);
+    TEST_FIELD(QCMINFO, QCMINFO_IDMAP const*, pIdMap, 16, 4, 4);
+}
+
+static void test_pack_QCMINFO_IDMAP(void)
+{
+    /* QCMINFO_IDMAP (pack 8) */
+    TEST_TYPE(QCMINFO_IDMAP, 12, 4);
+    TEST_FIELD(QCMINFO_IDMAP, UINT, nMaxIds, 0, 4, 4);
+    TEST_FIELD(QCMINFO_IDMAP, QCMINFO_IDMAP_PLACEMENT[1], pIdList, 4, 8, 4);
+}
+
+static void test_pack_QCMINFO_IDMAP_PLACEMENT(void)
+{
+    /* QCMINFO_IDMAP_PLACEMENT (pack 8) */
+    TEST_TYPE(QCMINFO_IDMAP_PLACEMENT, 8, 4);
+    TEST_FIELD(QCMINFO_IDMAP_PLACEMENT, UINT, id, 0, 4, 4);
+    TEST_FIELD(QCMINFO_IDMAP_PLACEMENT, UINT, fFlags, 4, 4, 4);
+}
+
+static void test_pack_SHChangeDWORDAsIDList(void)
+{
+    /* SHChangeDWORDAsIDList (pack 1) */
+    TEST_TYPE(SHChangeDWORDAsIDList, 12, 1);
+    TEST_FIELD(SHChangeDWORDAsIDList, USHORT, cb, 0, 2, 1);
+    TEST_FIELD(SHChangeDWORDAsIDList, DWORD, dwItem1, 2, 4, 1);
+    TEST_FIELD(SHChangeDWORDAsIDList, DWORD, dwItem2, 6, 4, 1);
+    TEST_FIELD(SHChangeDWORDAsIDList, USHORT, cbZero, 10, 2, 1);
+}
+
+static void test_pack_SHChangeNotifyEntry(void)
+{
+    /* SHChangeNotifyEntry (pack 1) */
+    TEST_TYPE(SHChangeNotifyEntry, 8, 1);
+    TEST_FIELD(SHChangeNotifyEntry, LPCITEMIDLIST, pidl, 0, 4, 1);
+    TEST_FIELD(SHChangeNotifyEntry, BOOL, fRecursive, 4, 4, 1);
+}
+
+static void test_pack_SHChangeProductKeyAsIDList(void)
+{
+    /* SHChangeProductKeyAsIDList (pack 1) */
+    TEST_TYPE(SHChangeProductKeyAsIDList, 82, 1);
+    TEST_FIELD(SHChangeProductKeyAsIDList, USHORT, cb, 0, 2, 1);
+    TEST_FIELD(SHChangeProductKeyAsIDList, WCHAR[39], wszProductKey, 2, 78, 1);
+    TEST_FIELD(SHChangeProductKeyAsIDList, USHORT, cbZero, 80, 2, 1);
+}
+
+static void test_pack_SHDESCRIPTIONID(void)
+{
+    /* SHDESCRIPTIONID (pack 8) */
+    TEST_TYPE(SHDESCRIPTIONID, 20, 4);
+    TEST_FIELD(SHDESCRIPTIONID, DWORD, dwDescriptionId, 0, 4, 4);
+    TEST_FIELD(SHDESCRIPTIONID, CLSID, clsid, 4, 16, 4);
+}
+
+static void test_pack_SHELLFLAGSTATE(void)
+{
+    /* SHELLFLAGSTATE (pack 1) */
+    TEST_TYPE(SHELLFLAGSTATE, 4, 1);
+}
+
+static void test_pack_SHELLSTATE(void)
+{
+    /* SHELLSTATE (pack 1) */
+    TEST_TYPE(SHELLSTATE, 32, 1);
+    TEST_FIELD(SHELLSTATE, DWORD, dwWin95Unused, 4, 4, 1);
+    TEST_FIELD(SHELLSTATE, UINT, uWin95Unused, 8, 4, 1);
+    TEST_FIELD(SHELLSTATE, LONG, lParamSort, 12, 4, 1);
+    TEST_FIELD(SHELLSTATE, int, iSortDirection, 16, 4, 1);
+    TEST_FIELD(SHELLSTATE, UINT, version, 20, 4, 1);
+    TEST_FIELD(SHELLSTATE, UINT, uNotUsed, 24, 4, 1);
+}
+
+static void test_pack_SHELLVIEWID(void)
+{
+    /* SHELLVIEWID */
+    TEST_TYPE(SHELLVIEWID, 16, 4);
+}
+
+static void test_pack_TBINFO(void)
+{
+    /* TBINFO (pack 8) */
+    TEST_TYPE(TBINFO, 8, 4);
+    TEST_FIELD(TBINFO, UINT, cbuttons, 0, 4, 4);
+    TEST_FIELD(TBINFO, UINT, uFlags, 4, 4, 4);
+}
+
+static void test_pack(void)
+{
+    test_pack_APPBARDATA();
+    test_pack_AUTO_SCROLL_DATA();
+    test_pack_BFFCALLBACK();
+    test_pack_BLOB();
+    test_pack_BROWSEINFOA();
+    test_pack_BROWSEINFOW();
+    test_pack_BSTR();
+    test_pack_BSTRBLOB();
+    test_pack_BYTE_BLOB();
+    test_pack_BYTE_SIZEDARR();
+    test_pack_CABINETSTATE();
+    test_pack_CIDA();
+    test_pack_CLIPDATA();
+    test_pack_CLIPFORMAT();
+    test_pack_CLSID();
+    test_pack_COAUTHIDENTITY();
+    test_pack_COAUTHINFO();
+    test_pack_COSERVERINFO();
+    test_pack_CSFV();
+    test_pack_DRAGINFOA();
+    test_pack_DRAGINFOW();
+    test_pack_DROPFILES();
+    test_pack_DWORD_SIZEDARR();
+    test_pack_FILEDESCRIPTORA();
+    test_pack_FILEDESCRIPTORW();
+    test_pack_FILEGROUPDESCRIPTORA();
+    test_pack_FILEGROUPDESCRIPTORW();
+    test_pack_FILEOP_FLAGS();
+    test_pack_FLAGGED_BYTE_BLOB();
+    test_pack_FLAGGED_WORD_BLOB();
+    test_pack_FMTID();
+    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();
+    test_pack_LPBROWSEINFOW();
+    test_pack_LPBSTR();
+    test_pack_LPBSTRBLOB();
+    test_pack_LPCABINETSTATE();
+    test_pack_LPCITEMIDLIST();
+    test_pack_LPCOLESTR();
+    test_pack_LPCSFV();
+    test_pack_LPCSHITEMID();
+    test_pack_LPCY();
+    test_pack_LPDECIMAL();
+    test_pack_LPDRAGINFOA();
+    test_pack_LPDRAGINFOW();
+    test_pack_LPDROPFILES();
+    test_pack_LPFILEDESCRIPTORA();
+    test_pack_LPFILEDESCRIPTORW();
+    test_pack_LPFILEGROUPDESCRIPTORA();
+    test_pack_LPFILEGROUPDESCRIPTORW();
+    test_pack_LPFNVIEWCALLBACK();
+    test_pack_LPGUID();
+    test_pack_LPIDA();
+    test_pack_LPITEMIDLIST();
+    test_pack_LPOLESTR();
+    test_pack_LPQCMINFO();
+    test_pack_LPSHChangeDWORDAsIDList();
+    test_pack_LPSHChangeProductKeyAsIDList();
+    test_pack_LPSHDESCRIPTIONID();
+    test_pack_LPSHELLDETAILS();
+    test_pack_LPSHELLEXECUTEINFOA();
+    test_pack_LPSHELLEXECUTEINFOW();
+    test_pack_LPSHELLFLAGSTATE();
+    test_pack_LPSHELLSTATE();
+    test_pack_LPSHFILEOPSTRUCTA();
+    test_pack_LPSHFILEOPSTRUCTW();
+    test_pack_LPSHITEMID();
+    test_pack_LPSHNAMEMAPPINGA();
+    test_pack_LPSHNAMEMAPPINGW();
+    test_pack_LPSTRRET();
+    test_pack_LPTBINFO();
+    test_pack_NOTIFYICONDATAA();
+    test_pack_NOTIFYICONDATAW();
+    test_pack_OLECHAR();
+    test_pack_PAPPBARDATA();
+    test_pack_PBROWSEINFOA();
+    test_pack_PBROWSEINFOW();
+    test_pack_PNOTIFYICONDATAA();
+    test_pack_PNOTIFYICONDATAW();
+    test_pack_PRINTEROP_FLAGS();
+    test_pack_PROPID();
+    test_pack_QCMINFO();
+    test_pack_QCMINFO_IDMAP();
+    test_pack_QCMINFO_IDMAP_PLACEMENT();
+    test_pack_RemHBITMAP();
+    test_pack_RemHENHMETAFILE();
+    test_pack_RemHGLOBAL();
+    test_pack_RemHMETAFILEPICT();
+    test_pack_RemHPALETTE();
+    test_pack_SCODE();
+    test_pack_SHChangeDWORDAsIDList();
+    test_pack_SHChangeNotifyEntry();
+    test_pack_SHChangeProductKeyAsIDList();
+    test_pack_SHDESCRIPTIONID();
+    test_pack_SHELLDETAILS();
+    test_pack_SHELLEXECUTEINFOA();
+    test_pack_SHELLEXECUTEINFOW();
+    test_pack_SHELLFLAGSTATE();
+    test_pack_SHELLSTATE();
+    test_pack_SHELLVIEWID();
+    test_pack_SHFILEINFOA();
+    test_pack_SHFILEINFOW();
+    test_pack_SHFILEOPSTRUCTA();
+    test_pack_SHFILEOPSTRUCTW();
+    test_pack_SHITEMID();
+    test_pack_SHNAMEMAPPINGA();
+    test_pack_SHNAMEMAPPINGW();
+    test_pack_STRRET();
+    test_pack_TBINFO();
+    test_pack_UP_BYTE_BLOB();
+    test_pack_UP_FLAGGED_BYTE_BLOB();
+    test_pack_UP_FLAGGED_WORD_BLOB();
+    test_pack_VARIANT_BOOL();
+    test_pack_VARTYPE();
+    test_pack_WORD_SIZEDARR();
+    test_pack_remoteMETAFILEPICT();
+    test_pack_userBITMAP();
+    test_pack_userCLIPFORMAT();
+    test_pack_userHBITMAP();
+    test_pack_userHENHMETAFILE();
+    test_pack_userHGLOBAL();
+    test_pack_userHMETAFILE();
+    test_pack_userHMETAFILEPICT();
+    test_pack_userHPALETTE();
+    test_pack_wireBSTR();
+    test_pack_wireCLIPFORMAT();
+    test_pack_wireHBITMAP();
+    test_pack_wireHENHMETAFILE();
+    test_pack_wireHGLOBAL();
+    test_pack_wireHMETAFILE();
+    test_pack_wireHMETAFILEPICT();
+    test_pack_wireHPALETTE();
+}
+
+START_TEST(generated)
+{
+    test_pack();
+}
diff --git a/reactos/regtests/winetests/shell32/shell32.xml b/reactos/regtests/winetests/shell32/shell32.xml
new file mode 100644 (file)
index 0000000..c30ba05
--- /dev/null
@@ -0,0 +1,16 @@
+<module name="shell32_winetest" type="win32cui" installbase="bin" installname="shell32_winetest.exe" warnings="true">
+    <include base="shell32_winetest">.</include>
+    <define name="__USE_W32API" />
+    <library>ntdll</library>
+    <library>shell32</library>
+    <library>shlwapi</library>
+    <library>ole32</library>
+    <library>uuid</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>
+</module>
diff --git a/reactos/regtests/winetests/shell32/shell32_test.h b/reactos/regtests/winetests/shell32/shell32_test.h
new file mode 100755 (executable)
index 0000000..fc37789
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Unit test suite for shell32 functions
+ *
+ * Copyright 2005 Francois Gougett for CodeWeavers
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+/* Helper function for creating .lnk files */
+typedef struct
+{
+    const char* description;
+    const char* workdir;
+    const char* path;
+    LPITEMIDLIST pidl;
+    const char* arguments;
+    int   showcmd;
+    const char* icon;
+    int   icon_id;
+    WORD  hotkey;
+} lnk_desc_t;
+
+#define create_lnk(a,b,c)     create_lnk_(__LINE__, (a), (b), (c))
+void create_lnk_(int,const WCHAR*,lnk_desc_t*,int);
diff --git a/reactos/regtests/winetests/shell32/shelllink.c b/reactos/regtests/winetests/shell32/shelllink.c
new file mode 100755 (executable)
index 0000000..0ff121d
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+ * Unit tests for shelllinks
+ *
+ * Copyright 2004 Mike McCormack
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "shlguid.h"
+//#include "wine/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);
+
+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 };
+
+
+/* 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 path_to_pidl(const char* path)
+{
+    LPITEMIDLIST pidl;
+
+    if (!pSHSimpleIDListFromPathA)
+    {
+        HMODULE hdll=LoadLibraryA("shell32.dll");
+        pSHSimpleIDListFromPathA=(void*)GetProcAddress(hdll, (char*)162);
+        if (!pSHSimpleIDListFromPathA)
+            trace("SHSimpleIDListFromPathA not found in shell32.dll\n");
+    }
+
+    pidl=NULL;
+    if (pSHSimpleIDListFromPathA)
+        pidl=pSHSimpleIDListFromPathA(path);
+
+    if (!pidl)
+    {
+        WCHAR* pathW;
+        HRESULT r;
+        int len;
+
+        len=MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
+        pathW=HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
+        MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, len);
+
+        r=SHILCreateFromPath(pathW, &pidl, NULL);
+        todo_wine {
+        ok(SUCCEEDED(r), "SHILCreateFromPath failed (0x%08lx)\n", r);
+        }
+        HeapFree(GetProcessHeap(), 0, pathW);
+    }
+    return pidl;
+}
+
+
+/*
+ * Test manipulation of an IShellLink's properties.
+ */
+
+static void test_get_set(void)
+{
+    HRESULT r;
+    IShellLinkA *sl;
+    char mypath[MAX_PATH];
+    char buffer[INFOTIPSIZE];
+    LPITEMIDLIST pidl, tmp_pidl;
+    const char * str;
+    int i;
+    WORD w;
+
+    r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+                         &IID_IShellLinkA, (LPVOID*)&sl);
+    ok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08lx)\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(*buffer=='\0', "GetDescription returned '%s'\n", buffer);
+
+    str="Some description";
+    r = IShellLinkA_SetDescription(sl, str);
+    ok(SUCCEEDED(r), "SetDescription failed (0x%08lx)\n", r);
+
+    strcpy(buffer,"garbage");
+    r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
+    ok(SUCCEEDED(r), "GetDescription failed (0x%08lx)\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(*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);
+
+    strcpy(buffer,"garbage");
+    r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
+    ok(SUCCEEDED(r), "GetWorkingDirectory failed (0x%08lx)\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(*buffer=='\0', "GetPath returned '%s'\n", buffer);
+
+    r = IShellLinkA_SetPath(sl, "");
+    ok(r==S_OK, "SetPath failed (0x%08lx)\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(*buffer=='\0', "GetPath returned '%s'\n", buffer);
+
+    str="c:\\nonexistent\\file";
+    r = IShellLinkA_SetPath(sl, str);
+    ok(r==S_FALSE, "SetPath failed (0x%08lx)\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(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);
+
+    /* 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);
+    if (SUCCEEDED(r))
+    {
+        strcpy(buffer,"garbage");
+        r=SHGetPathFromIDListA(tmp_pidl, buffer);
+        todo_wine {
+        ok(r, "SHGetPathFromIDListA failed\n");
+        }
+        if (r)
+            ok(lstrcmpi(buffer,str)==0, "GetIDList returned '%s'\n", buffer);
+    }
+
+    pidl=path_to_pidl(mypath);
+    todo_wine {
+    ok(pidl!=NULL, "path_to_pidl returned a NULL pidl\n");
+    }
+
+    if (pidl)
+    {
+        r = IShellLinkA_SetIDList(sl, pidl);
+        ok(SUCCEEDED(r), "SetIDList failed (0x%08lx)\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),
+           "GetIDList returned an incorrect pidl\n");
+
+        /* tmp_pidl is owned by IShellLink so we don't free it */
+        ILFree(pidl);
+
+        strcpy(buffer,"garbage");
+        r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
+        ok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
+        ok(lstrcmpi(buffer, mypath)==0, "GetPath returned '%s'\n", buffer);
+    }
+
+    /* 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(*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);
+
+    strcpy(buffer,"garbage");
+    r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
+    ok(SUCCEEDED(r), "GetArguments failed (0x%08lx)\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(i==SW_SHOWNORMAL, "GetShowCmd returned %d\n", i);
+
+    r = IShellLinkA_SetShowCmd(sl, SW_SHOWMAXIMIZED);
+    ok(SUCCEEDED(r), "SetShowCmd failed (0x%08lx)\n", r);
+
+    i=0xdeadbeef;
+    r = IShellLinkA_GetShowCmd(sl, &i);
+    ok(SUCCEEDED(r), "GetShowCmd failed (0x%08lx)\n", r);
+    ok(i==SW_SHOWMAXIMIZED, "GetShowCmd returned %d'\n", i);
+
+    /* Test Getting / Setting the icon */
+    i=0xdeadbeef;
+    strcpy(buffer,"garbage");
+    r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
+    todo_wine {
+    ok(SUCCEEDED(r), "GetIconLocation failed (0x%08lx)\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);
+
+    i=0xdeadbeef;
+    r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
+    ok(SUCCEEDED(r), "GetIconLocation failed (0x%08lx)\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(w==0, "GetHotkey returned %d\n", w);
+
+    r = IShellLinkA_SetHotkey(sl, 0x5678);
+    ok(SUCCEEDED(r), "SetHotkey failed (0x%08lx)\n", r);
+
+    w=0xbeef;
+    r = IShellLinkA_GetHotkey(sl, &w);
+    ok(SUCCEEDED(r), "GetHotkey failed (0x%08lx)\n", r);
+    ok(w==0x5678, "GetHotkey returned %d'\n", w);
+
+    IShellLinkA_Release(sl);
+}
+
+
+/*
+ * Test saving and loading .lnk files
+ */
+
+#define lok                   ok_(__FILE__, line)
+#define check_lnk(a,b)        check_lnk_(__LINE__, (a), (b))
+
+void create_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int save_fails)
+{
+    HRESULT r;
+    IShellLinkA *sl;
+    IPersistFile *pf;
+
+    r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+                         &IID_IShellLinkA, (LPVOID*)&sl);
+    lok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08lx)\n", r);
+    if (!SUCCEEDED(r))
+        return;
+
+    if (desc->description)
+    {
+        r = IShellLinkA_SetDescription(sl, desc->description);
+        lok(SUCCEEDED(r), "SetDescription failed (0x%08lx)\n", r);
+    }
+    if (desc->workdir)
+    {
+        r = IShellLinkA_SetWorkingDirectory(sl, desc->workdir);
+        lok(SUCCEEDED(r), "SetWorkingDirectory failed (0x%08lx)\n", r);
+    }
+    if (desc->path)
+    {
+        r = IShellLinkA_SetPath(sl, desc->path);
+        lok(SUCCEEDED(r), "SetPath failed (0x%08lx)\n", r);
+    }
+    if (desc->pidl)
+    {
+        r = IShellLinkA_SetIDList(sl, desc->pidl);
+        lok(SUCCEEDED(r), "SetIDList failed (0x%08lx)\n", r);
+    }
+    if (desc->arguments)
+    {
+        r = IShellLinkA_SetArguments(sl, desc->arguments);
+        lok(SUCCEEDED(r), "SetArguments failed (0x%08lx)\n", r);
+    }
+    if (desc->showcmd)
+    {
+        r = IShellLinkA_SetShowCmd(sl, desc->showcmd);
+        lok(SUCCEEDED(r), "SetShowCmd failed (0x%08lx)\n", r);
+    }
+    if (desc->icon)
+    {
+        r = IShellLinkA_SetIconLocation(sl, desc->icon, desc->icon_id);
+        lok(SUCCEEDED(r), "SetIconLocation failed (0x%08lx)\n", r);
+    }
+    if (desc->hotkey)
+    {
+        r = IShellLinkA_SetHotkey(sl, desc->hotkey);
+        lok(SUCCEEDED(r), "SetHotkey failed (0x%08lx)\n", r);
+    }
+
+    r = IShellLinkW_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
+    lok(SUCCEEDED(r), "no IID_IPersistFile (0x%08lx)\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);
+            }
+        }
+        else
+        {
+            lok(SUCCEEDED(r), "save failed (0x%08lx)\n", r);
+        }
+        IPersistFile_Release(pf);
+    }
+
+    IShellLinkA_Release(sl);
+}
+
+static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
+{
+    HRESULT r;
+    IShellLinkA *sl;
+    IPersistFile *pf;
+    char buffer[INFOTIPSIZE];
+
+    r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+                         &IID_IShellLinkA, (LPVOID*)&sl);
+    lok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08lx)\n", r);
+    if (!SUCCEEDED(r))
+        return;
+
+    r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
+    lok(SUCCEEDED(r), "no IID_IPersistFile (0x%08lx)\n", r);
+    if (!SUCCEEDED(r))
+    {
+        IShellLinkA_Release(sl);
+        return;
+    }
+
+    r = IPersistFile_Load(pf, path, STGM_READ);
+    lok(SUCCEEDED(r), "load failed (0x%08lx)\n", r);
+    IPersistFile_Release(pf);
+    if (!SUCCEEDED(r))
+    {
+        IShellLinkA_Release(sl);
+        return;
+    }
+
+    if (desc->description)
+    {
+        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,
+           "GetDescription returned '%s' instead of '%s'\n",
+           buffer, desc->description);
+    }
+    if (desc->workdir)
+    {
+        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,
+           "GetWorkingDirectory returned '%s' instead of '%s'\n",
+           buffer, desc->workdir);
+    }
+    if (desc->path)
+    {
+        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,
+           "GetPath returned '%s' instead of '%s'\n",
+           buffer, desc->path);
+    }
+    if (desc->pidl)
+    {
+        LPITEMIDLIST pidl=NULL;
+        r = IShellLinkA_GetIDList(sl, &pidl);
+        lok(SUCCEEDED(r), "GetIDList failed (0x%08lx)\n", r);
+        lok(ILIsEqual(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,
+           "GetShowCmd returned 0x%0x instead of 0x%0x\n",
+           i, desc->showcmd);
+    }
+    if (desc->icon)
+    {
+        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,
+           "GetIconLocation returned '%s' instead of '%s'\n",
+           buffer, desc->icon);
+        lok(i==desc->icon_id,
+           "GetIconLocation returned 0x%0x instead of 0x%0x\n",
+           i, desc->icon_id);
+    }
+    if (desc->hotkey)
+    {
+        WORD i=0xbeef;
+        r = IShellLinkA_GetHotkey(sl, &i);
+        lok(SUCCEEDED(r), "GetHotkey failed (0x%08lx)\n", r);
+        lok(i==desc->hotkey,
+           "GetHotkey returned 0x%04x instead of 0x%04x\n",
+           i, desc->hotkey);
+    }
+
+    IShellLinkA_Release(sl);
+}
+
+static void test_load_save(void)
+{
+    lnk_desc_t desc;
+    char mypath[MAX_PATH];
+    char mydir[MAX_PATH];
+    char* p;
+    DWORD r;
+
+    /* Save an empty .lnk file */
+    memset(&desc, 0, sizeof(desc));
+    create_lnk(lnkfile, &desc, 0);
+
+    /* It should come back as a bunch of empty strings */
+    desc.description="";
+    desc.workdir="";
+    desc.path="";
+    desc.arguments="";
+    desc.icon="";
+    check_lnk(lnkfile, &desc);
+
+
+    /* Point a .lnk file to nonexistent files */
+    desc.description="";
+    desc.workdir="c:\\Nonexitent\\work\\directory";
+    desc.path="c:\\nonexistent\\path";
+    desc.pidl=NULL;
+    desc.arguments="";
+    desc.showcmd=0;
+    desc.icon="c:\\nonexistent\\icon\\file";
+    desc.icon_id=1234;
+    desc.hotkey=0;
+    create_lnk(lnkfile, &desc, 0);
+    check_lnk(lnkfile, &desc);
+
+    r=GetModuleFileName(NULL, mypath, sizeof(mypath));
+    ok(r>=0 && r<sizeof(mypath), "GetModuleFileName failed (%ld)\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;
+    desc.path=mypath;
+    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_lnk(lnkfile, &desc);
+
+    /* FIXME: Also test saving a .lnk pointing to a pidl that cannot be
+     * represented as a path.
+     */
+
+    /* DeleteFileW is not implemented on Win9x */
+    r=DeleteFileA("c:\\test.lnk");
+    ok(r, "failed to delete link (%ld)\n", GetLastError());
+}
+
+START_TEST(shelllink)
+{
+    HRESULT r;
+
+    r = CoInitialize(NULL);
+    ok(SUCCEEDED(r), "CoInitialize failed (0x%08lx)\n", r);
+    if (!SUCCEEDED(r))
+        return;
+
+    test_get_set();
+    test_load_save();
+
+    CoUninitialize();
+}
diff --git a/reactos/regtests/winetests/shell32/shellpath.c b/reactos/regtests/winetests/shell32/shellpath.c
new file mode 100644 (file)
index 0000000..4e96f5e
--- /dev/null
@@ -0,0 +1,906 @@
+/*
+ * Unit tests for shell32 SHGet{Special}Folder{Path|Location} functions.
+ *
+ * Copyright 2004 Juan Lang
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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).
+ *
+ * FIXME:
+ * - Need to verify on more systems.
+ */
+
+#define COBJMACROS
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "windef.h"
+#include "winbase.h"
+#include "initguid.h"
+#include "shlguid.h"
+#include "shlobj.h"
+#include "shlwapi.h"
+#include "wine/test.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) ( sizeof(x) / sizeof((x)[0]) )
+#endif
+
+/* from pidl.h, not included here: */
+#ifndef PT_GUID
+#define PT_GUID       0x1f /* no path */
+#endif
+#ifndef PT_DRIVE 
+#define PT_DRIVE      0x23 /* has path */
+#endif
+#ifndef PT_DRIVE2
+#define PT_DRIVE2     0x25 /* has path */
+#endif
+#ifndef PT_SHELLEXT
+#define PT_SHELLEXT   0x2e /* no path */
+#endif
+#ifndef PT_FOLDER
+#define PT_FOLDER     0x31 /* has path */
+#endif
+#ifndef PT_WORKGRP
+#define PT_WORKGRP    0x41 /* no path */
+#endif
+#ifndef PT_YAGUID
+#define PT_YAGUID     0x70 /* no path */
+#endif
+/* FIXME: this is used for history/favorites folders; what's a better name? */
+#ifndef PT_IESPECIAL2
+#define PT_IESPECIAL2 0xb1 /* has path */
+#endif
+
+static GUID CLSID_CommonDocuments = { 0x0000000c, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x1a } };
+
+struct shellExpectedValues {
+    int  folder;
+    BYTE pidlType;
+};
+
+static HMODULE hShell32;
+static HRESULT (WINAPI *pSHGetFolderPathA)(HWND, int, HANDLE, DWORD, LPSTR);
+static HRESULT (WINAPI *pSHGetFolderLocation)(HWND, int, HANDLE, DWORD,
+ LPITEMIDLIST *);
+static BOOL    (WINAPI *pSHGetSpecialFolderPathA)(HWND, LPSTR, int, BOOL);
+static HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
+static LPITEMIDLIST (WINAPI *pILFindLastID)(LPCITEMIDLIST);
+static int (WINAPI *pSHFileOperationA)(LPSHFILEOPSTRUCTA);
+static HRESULT (WINAPI *pSHGetMalloc)(LPMALLOC *);
+static DLLVERSIONINFO shellVersion = { 0 };
+static LPMALLOC pMalloc;
+static const struct shellExpectedValues requiredShellValues[] = {
+ { CSIDL_BITBUCKET, PT_GUID },
+ { CSIDL_CONTROLS, PT_SHELLEXT },
+ { CSIDL_COOKIES, PT_FOLDER },
+ { CSIDL_DESKTOPDIRECTORY, PT_FOLDER },
+ { CSIDL_DRIVES, PT_GUID },
+ { CSIDL_FAVORITES, PT_FOLDER },
+ { CSIDL_FONTS, PT_FOLDER },
+/* FIXME: the following fails in Wine, returns type PT_FOLDER
+ { CSIDL_HISTORY, PT_IESPECIAL2 },
+ */
+ { CSIDL_INTERNET, PT_GUID },
+ { CSIDL_NETHOOD, PT_FOLDER },
+ { CSIDL_NETWORK, PT_GUID },
+ { CSIDL_PRINTERS, PT_YAGUID },
+ { CSIDL_PRINTHOOD, PT_FOLDER },
+ { CSIDL_PROGRAMS, PT_FOLDER },
+ { CSIDL_RECENT, PT_FOLDER },
+ { CSIDL_SENDTO, PT_FOLDER },
+ { CSIDL_STARTMENU, PT_FOLDER },
+ { CSIDL_STARTUP, PT_FOLDER },
+ { CSIDL_TEMPLATES, PT_FOLDER },
+};
+static const struct shellExpectedValues optionalShellValues[] = {
+/* FIXME: the following only semi-succeed; they return NULL PIDLs on XP.. hmm.
+ { CSIDL_ALTSTARTUP, PT_FOLDER },
+ { CSIDL_COMMON_ALTSTARTUP, PT_FOLDER },
+ { CSIDL_COMMON_OEM_LINKS, PT_FOLDER },
+ */
+/* Windows NT-only: */
+ { CSIDL_COMMON_DESKTOPDIRECTORY, PT_FOLDER },
+ { CSIDL_COMMON_DOCUMENTS, PT_SHELLEXT },
+ { CSIDL_COMMON_FAVORITES, PT_FOLDER },
+ { CSIDL_COMMON_PROGRAMS, PT_FOLDER },
+ { CSIDL_COMMON_STARTMENU, PT_FOLDER },
+ { CSIDL_COMMON_STARTUP, PT_FOLDER },
+ { CSIDL_COMMON_TEMPLATES, PT_FOLDER },
+/* first appearing in shell32 version 4.71: */
+ { CSIDL_APPDATA, PT_FOLDER },
+/* first appearing in shell32 version 4.72: */
+ { CSIDL_INTERNET_CACHE, PT_IESPECIAL2 },
+/* first appearing in shell32 version 5.0: */
+ { CSIDL_ADMINTOOLS, PT_FOLDER },
+ { CSIDL_COMMON_APPDATA, PT_FOLDER },
+ { CSIDL_LOCAL_APPDATA, PT_FOLDER },
+ { CSIDL_MYDOCUMENTS, PT_FOLDER },
+ { CSIDL_MYMUSIC, PT_FOLDER },
+ { CSIDL_MYPICTURES, PT_FOLDER },
+ { CSIDL_MYVIDEO, PT_FOLDER },
+ { CSIDL_PROFILE, PT_FOLDER },
+ { CSIDL_PROGRAM_FILES, PT_FOLDER },
+ { CSIDL_PROGRAM_FILESX86, PT_FOLDER },
+ { CSIDL_PROGRAM_FILES_COMMON, PT_FOLDER },
+ { CSIDL_PROGRAM_FILES_COMMONX86, PT_FOLDER },
+ { CSIDL_SYSTEM, PT_FOLDER },
+ { CSIDL_WINDOWS, PT_FOLDER },
+/* first appearing in shell32 6.0: */
+ { CSIDL_CDBURN_AREA, PT_FOLDER },
+ { CSIDL_COMMON_MUSIC, PT_FOLDER },
+ { CSIDL_COMMON_PICTURES, PT_FOLDER },
+ { CSIDL_COMMON_VIDEO, PT_FOLDER },
+ { CSIDL_COMPUTERSNEARME, PT_WORKGRP },
+ { CSIDL_RESOURCES, PT_FOLDER },
+ { CSIDL_RESOURCES_LOCALIZED, PT_FOLDER },
+};
+
+static void loadShell32(void)
+{
+    hShell32 = LoadLibraryA("shell32");
+    if (hShell32)
+    {
+        HRESULT (WINAPI *pDllGetVersion)(DLLVERSIONINFO *);
+
+        pSHGetFolderPathA = (void *)GetProcAddress(hShell32,
+         "SHGetFolderPathA");
+        pSHGetFolderLocation = (void *)GetProcAddress(hShell32,
+         "SHGetFolderLocation");
+        pSHGetSpecialFolderPathA = (void *)GetProcAddress(hShell32,
+         "SHGetSpecialFolderPathA");
+        pSHGetSpecialFolderLocation = (void *)GetProcAddress(hShell32,
+         "SHGetSpecialFolderLocation");
+        pDllGetVersion = (void *)GetProcAddress(hShell32, "DllGetVersion");
+        pILFindLastID = (void *)GetProcAddress(hShell32, "ILFindLastID");
+        if (!pILFindLastID)
+            pILFindLastID = (void *)GetProcAddress(hShell32, (LPCSTR)16);
+        pSHFileOperationA = (void *)GetProcAddress(hShell32,
+         "SHFileOperationA");
+        pSHGetMalloc = (void *)GetProcAddress(hShell32, "SHGetMalloc");
+
+        ok(pSHGetMalloc != NULL, "shell32 is missing SHGetMalloc\n");
+        if (pSHGetMalloc)
+        {
+            HRESULT hr = pSHGetMalloc(&pMalloc);
+
+            ok(SUCCEEDED(hr), "SHGetMalloc failed: 0x%08lx\n", hr);
+            ok(pMalloc != NULL, "SHGetMalloc returned a NULL IMalloc\n");
+        }
+
+        if (pDllGetVersion)
+        {
+            shellVersion.cbSize = sizeof(shellVersion);
+            pDllGetVersion(&shellVersion);
+            if (winetest_interactive)
+                printf("shell32 version is %ld.%ld\n",
+                 shellVersion.dwMajorVersion, shellVersion.dwMinorVersion);
+        }
+    }
+}
+
+#ifndef CSIDL_PROFILES
+#define CSIDL_PROFILES         0x003e
+#endif
+
+/* A couple utility printing functions */
+static const char *getFolderName(int folder)
+{
+    static char unknown[17];
+
+#define CSIDL_TO_STR(x) case x: return#x;
+    switch (folder)
+    {
+    CSIDL_TO_STR(CSIDL_DESKTOP);
+    CSIDL_TO_STR(CSIDL_INTERNET);
+    CSIDL_TO_STR(CSIDL_PROGRAMS);
+    CSIDL_TO_STR(CSIDL_CONTROLS);
+    CSIDL_TO_STR(CSIDL_PRINTERS);
+    CSIDL_TO_STR(CSIDL_PERSONAL);
+    CSIDL_TO_STR(CSIDL_FAVORITES);
+    CSIDL_TO_STR(CSIDL_STARTUP);
+    CSIDL_TO_STR(CSIDL_RECENT);
+    CSIDL_TO_STR(CSIDL_SENDTO);
+    CSIDL_TO_STR(CSIDL_BITBUCKET);
+    CSIDL_TO_STR(CSIDL_STARTMENU);
+    CSIDL_TO_STR(CSIDL_MYDOCUMENTS);
+    CSIDL_TO_STR(CSIDL_MYMUSIC);
+    CSIDL_TO_STR(CSIDL_MYVIDEO);
+    CSIDL_TO_STR(CSIDL_DESKTOPDIRECTORY);
+    CSIDL_TO_STR(CSIDL_DRIVES);
+    CSIDL_TO_STR(CSIDL_NETWORK);
+    CSIDL_TO_STR(CSIDL_NETHOOD);
+    CSIDL_TO_STR(CSIDL_FONTS);
+    CSIDL_TO_STR(CSIDL_TEMPLATES);
+    CSIDL_TO_STR(CSIDL_COMMON_STARTMENU);
+    CSIDL_TO_STR(CSIDL_COMMON_PROGRAMS);
+    CSIDL_TO_STR(CSIDL_COMMON_STARTUP);
+    CSIDL_TO_STR(CSIDL_COMMON_DESKTOPDIRECTORY);
+    CSIDL_TO_STR(CSIDL_APPDATA);
+    CSIDL_TO_STR(CSIDL_PRINTHOOD);
+    CSIDL_TO_STR(CSIDL_LOCAL_APPDATA);
+    CSIDL_TO_STR(CSIDL_ALTSTARTUP);
+    CSIDL_TO_STR(CSIDL_COMMON_ALTSTARTUP);
+    CSIDL_TO_STR(CSIDL_COMMON_FAVORITES);
+    CSIDL_TO_STR(CSIDL_INTERNET_CACHE);
+    CSIDL_TO_STR(CSIDL_COOKIES);
+    CSIDL_TO_STR(CSIDL_HISTORY);
+    CSIDL_TO_STR(CSIDL_COMMON_APPDATA);
+    CSIDL_TO_STR(CSIDL_WINDOWS);
+    CSIDL_TO_STR(CSIDL_SYSTEM);
+    CSIDL_TO_STR(CSIDL_PROGRAM_FILES);
+    CSIDL_TO_STR(CSIDL_MYPICTURES);
+    CSIDL_TO_STR(CSIDL_PROFILE);
+    CSIDL_TO_STR(CSIDL_SYSTEMX86);
+    CSIDL_TO_STR(CSIDL_PROGRAM_FILESX86);
+    CSIDL_TO_STR(CSIDL_PROGRAM_FILES_COMMON);
+    CSIDL_TO_STR(CSIDL_PROGRAM_FILES_COMMONX86);
+    CSIDL_TO_STR(CSIDL_COMMON_TEMPLATES);
+    CSIDL_TO_STR(CSIDL_COMMON_DOCUMENTS);
+    CSIDL_TO_STR(CSIDL_COMMON_ADMINTOOLS);
+    CSIDL_TO_STR(CSIDL_ADMINTOOLS);
+    CSIDL_TO_STR(CSIDL_CONNECTIONS);
+    CSIDL_TO_STR(CSIDL_PROFILES);
+    CSIDL_TO_STR(CSIDL_COMMON_MUSIC);
+    CSIDL_TO_STR(CSIDL_COMMON_PICTURES);
+    CSIDL_TO_STR(CSIDL_COMMON_VIDEO);
+    CSIDL_TO_STR(CSIDL_RESOURCES);
+    CSIDL_TO_STR(CSIDL_RESOURCES_LOCALIZED);
+    CSIDL_TO_STR(CSIDL_COMMON_OEM_LINKS);
+    CSIDL_TO_STR(CSIDL_CDBURN_AREA);
+    CSIDL_TO_STR(CSIDL_COMPUTERSNEARME);
+#undef CSIDL_TO_STR
+    default:
+        wnsprintfA(unknown, sizeof(unknown), "unknown (0x%04x)", folder);
+        return unknown;
+    }
+}
+
+static const char *printGUID(const GUID *guid)
+{
+    static char guidSTR[39];
+
+    if (!guid) return NULL;
+
+    wnsprintfA(guidSTR, sizeof(guidSTR),
+     "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+     guid->Data1, guid->Data2, guid->Data3,
+     guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
+     guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+    return guidSTR;
+}
+
+static void testSHGetFolderLocationInvalidArgs(void)
+{
+    LPITEMIDLIST pidl;
+    HRESULT hr;
+
+    if (!pSHGetFolderLocation) return;
+
+    /* check a bogus CSIDL: */
+    pidl = NULL;
+    hr = pSHGetFolderLocation(NULL, 0xeeee, NULL, 0, &pidl);
+    ok(hr == E_INVALIDARG,
+     "SHGetFolderLocation(NULL, 0xeeee, NULL, 0, &pidl)\n"
+     "returned 0x%08lx, expected E_INVALIDARG\n", hr);
+    if (SUCCEEDED(hr))
+        IMalloc_Free(pMalloc, pidl);
+    /* check a bogus user token: */
+    pidl = NULL;
+    hr = pSHGetFolderLocation(NULL, CSIDL_FAVORITES, (HANDLE)2, 0, &pidl);
+    ok(hr == E_FAIL,
+     "SHGetFolderLocation(NULL, CSIDL_FAVORITES, 2, 0, &pidl)\n"
+     "returned 0x%08lx, expected E_FAIL\n", hr);
+    if (SUCCEEDED(hr))
+        IMalloc_Free(pMalloc, pidl);
+    /* check reserved is not zero: */
+    pidl = NULL;
+    hr = pSHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, 1, &pidl);
+    ok(hr == E_INVALIDARG,
+     "SHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, 1, &pidl)\n"
+     "returned 0x%08lx, expected E_INVALIDARG\n", hr);
+    if (SUCCEEDED(hr))
+        IMalloc_Free(pMalloc, pidl);
+    /* a NULL pidl pointer crashes, so don't test it */
+}
+
+static void testSHGetSpecialFolderLocationInvalidArgs(void)
+{
+    LPITEMIDLIST pidl = NULL;
+    HRESULT hr;
+
+    if (!pSHGetSpecialFolderLocation) return;
+
+    /* SHGetSpecialFolderLocation(NULL, 0, NULL) crashes */
+    hr = pSHGetSpecialFolderLocation(NULL, 0xeeee, &pidl);
+    ok(hr == E_INVALIDARG,
+     "SHGetSpecialFolderLocation(NULL, 0xeeee, &pidl) returned 0x%08lx, "
+     "expected E_INVALIDARG\n", hr);
+}
+
+static void testSHGetFolderPathInvalidArgs(void)
+{
+    char path[MAX_PATH];
+    HRESULT hr;
+
+    if (!pSHGetFolderPathA) return;
+
+    /* expect 2's a bogus handle, especially since we didn't open it */
+    hr = pSHGetFolderPathA(NULL, CSIDL_DESKTOP, (HANDLE)2,
+     SHGFP_TYPE_DEFAULT, path);
+    ok(hr == E_FAIL,
+     "SHGetFolderPathA(NULL, CSIDL_DESKTOP, 2, SHGFP_TYPE_DEFAULT, path)\n"
+     "returned 0x%08lx, expected E_FAIL\n", hr);
+    hr = pSHGetFolderPathA(NULL, 0xeeee, NULL, SHGFP_TYPE_DEFAULT, path);
+    ok(hr == E_INVALIDARG,
+     "SHGetFolderPathA(NULL, 0xeeee, NULL, SHGFP_TYPE_DEFAULT, path)\n"
+     "returned 0x%08lx, expected E_INVALIDARG\n", hr);
+}
+
+static void testSHGetSpecialFolderPathInvalidArgs(void)
+{
+    char path[MAX_PATH];
+    BOOL ret;
+
+    if (!pSHGetSpecialFolderPathA) return;
+
+    ret = pSHGetSpecialFolderPathA(NULL, NULL, CSIDL_BITBUCKET, FALSE);
+    ok(!ret,
+     "SHGetSpecialFolderPathA(NULL, NULL, CSIDL_BITBUCKET, FALSE)\n"
+     "returned TRUE, expected FALSE\n");
+    /* odd but true: calling with a NULL path still succeeds if it's a real
+     * dir
+     */
+    ret = pSHGetSpecialFolderPathA(NULL, NULL, CSIDL_PROGRAMS, FALSE);
+    ok(ret,
+     "SHGetSpecialFolderPathA(NULL, NULL, CSIDL_PROGRAMS, FALSE)\n"
+     "returned FALSE, expected TRUE\n");
+    ret = pSHGetSpecialFolderPathA(NULL, path, 0xeeee, FALSE);
+    ok(!ret,
+     "SHGetSpecialFolderPathA(NULL, path, 0xeeee, FALSE)\n"
+     "returned TRUE, expected FALSE\n");
+}
+
+static void testApiParameters(void)
+{
+    testSHGetFolderLocationInvalidArgs();
+    testSHGetSpecialFolderLocationInvalidArgs();
+    testSHGetFolderPathInvalidArgs();
+    testSHGetSpecialFolderPathInvalidArgs();
+}
+
+/* Returns the folder's PIDL type, or 0xff if one can't be found. */
+static BYTE testSHGetFolderLocation(BOOL optional, int folder)
+{
+    LPITEMIDLIST pidl;
+    HRESULT hr;
+    BYTE ret = 0xff;
+
+    /* treat absence of function as success */
+    if (!pSHGetFolderLocation) return TRUE;
+
+    pidl = NULL;
+    hr = pSHGetFolderLocation(NULL, folder, NULL, 0, &pidl);
+    ok(SUCCEEDED(hr) || optional,
+     "SHGetFolderLocation(NULL, %s, NULL, 0, &pidl)\n"
+     "failed: 0x%08lx\n", getFolderName(folder), hr);
+    if (SUCCEEDED(hr))
+    {
+        ok(pidl != NULL,
+         "SHGetFolderLocation(NULL, %s, NULL, 0, &pidl)\n"
+         "succeeded, but returned pidl is NULL\n", getFolderName(folder));
+        if (pidl)
+        {
+            LPITEMIDLIST pidlLast = pILFindLastID(pidl);
+
+            ok(pidlLast != NULL, "%s: ILFindLastID failed\n",
+             getFolderName(folder));
+            if (pidlLast)
+                ret = pidlLast->mkid.abID[0];
+            IMalloc_Free(pMalloc, pidl);
+        }
+    }
+    return ret;
+}
+
+/* Returns the folder's PIDL type, or 0xff if one can't be found. */
+static BYTE testSHGetSpecialFolderLocation(BOOL optional, int folder)
+{
+    LPITEMIDLIST pidl;
+    HRESULT hr;
+    BYTE ret = 0xff;
+
+    /* treat absence of function as success */
+    if (!pSHGetSpecialFolderLocation) return TRUE;
+
+    pidl = NULL;
+    hr = pSHGetSpecialFolderLocation(NULL, folder, &pidl);
+    ok(SUCCEEDED(hr) || optional,
+     "SHGetSpecialFolderLocation(NULL, %s, &pidl)\n"
+     "failed: 0x%08lx\n", getFolderName(folder), hr);
+    if (SUCCEEDED(hr))
+    {
+        ok(pidl != NULL,
+         "SHGetSpecialFolderLocation(NULL, %s, &pidl)\n"
+         "succeeded, but returned pidl is NULL\n", getFolderName(folder));
+        if (pidl)
+        {
+            LPITEMIDLIST pidlLast = pILFindLastID(pidl);
+
+            ok(pidlLast != NULL,
+                "%s: ILFindLastID failed\n", getFolderName(folder));
+            if (pidlLast)
+                ret = pidlLast->mkid.abID[0];
+            IMalloc_Free(pMalloc, pidl);
+        }
+    }
+    return ret;
+}
+
+static void testSHGetFolderPath(BOOL optional, int folder)
+{
+    char path[MAX_PATH];
+    HRESULT hr;
+
+    if (!pSHGetFolderPathA) return;
+
+    hr = pSHGetFolderPathA(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path);
+    ok(SUCCEEDED(hr) || optional,
+     "SHGetFolderPathA(NULL, %s, NULL, SHGFP_TYPE_CURRENT, path)\n"
+     "failed: 0x%08lx\n", getFolderName(folder), hr);
+}
+
+static void testSHGetSpecialFolderPath(BOOL optional, int folder)
+{
+    char path[MAX_PATH];
+    BOOL ret;
+
+    if (!pSHGetSpecialFolderPathA) return;
+
+    ret = pSHGetSpecialFolderPathA(NULL, path, folder, FALSE);
+    if (ret && winetest_interactive)
+        printf("%s: %s\n", getFolderName(folder), path);
+    ok(ret || optional,
+     "SHGetSpecialFolderPathA(NULL, path, %s, FALSE) failed\n",
+     getFolderName(folder));
+}
+
+static void testShellValues(const struct shellExpectedValues testEntries[],
+ int numEntries, BOOL optional)
+{
+    int i;
+
+    for (i = 0; i < numEntries; i++)
+    {
+        BYTE type;
+
+        type = testSHGetFolderLocation(optional, testEntries[i].folder);
+        ok(type == testEntries[i].pidlType || optional,
+         "%s has type %d (0x%02x), expected %d (0x%02x)\n",
+         getFolderName(testEntries[i].folder), type, type,
+         testEntries[i].pidlType, testEntries[i].pidlType);
+        type = testSHGetSpecialFolderLocation(optional, testEntries[i].folder);
+        ok(type == testEntries[i].pidlType || optional,
+         "%s has type %d (0x%02x), expected %d (0x%02x)\n",
+         getFolderName(testEntries[i].folder), type, type,
+         testEntries[i].pidlType, testEntries[i].pidlType);
+        switch (type)
+        {
+            case PT_FOLDER:
+            case PT_DRIVE:
+            case PT_DRIVE2:
+            case PT_IESPECIAL2:
+                testSHGetFolderPath(optional, testEntries[i].folder);
+                testSHGetSpecialFolderPath(optional, testEntries[i].folder);
+                break;
+        }
+    }
+}
+
+/* Attempts to verify that the folder path corresponding to the folder CSIDL
+ * value has the same value as the environment variable with name envVar.
+ * Doesn't mind if SHGetSpecialFolderPath fails for folder or if envVar isn't
+ * set in this environment; different OS and shell version behave differently.
+ * However, if both are present, fails if envVar's value is not the same
+ * (byte-for-byte) as what SHGetSpecialFolderPath returns.
+ */
+static void matchSpecialFolderPathToEnv(int folder, const char *envVar)
+{
+    char path[MAX_PATH];
+
+    if (!pSHGetSpecialFolderPathA) return;
+
+    if (pSHGetSpecialFolderPathA(NULL, path, folder, FALSE))
+    {
+        char *envVal = getenv(envVar);
+
+        ok(!envVal || !lstrcmpiA(envVal, path),
+         "%%%s%% does not match SHGetSpecialFolderPath:\n"
+         "%%%s%% is %s\nSHGetSpecialFolderPath returns %s\n",
+         envVar, envVar, envVal, path);
+    }
+}
+
+/* Attempts to match the GUID returned by SHGetFolderLocation for folder with
+ * GUID.  Assumes the type of the returned PIDL is in fact a GUID, but doesn't
+ * fail if it isn't--that check should already have been done.
+ * Fails if the returned PIDL is a GUID whose value does not match guid.
+ */
+static void matchGUID(int folder, const GUID *guid)
+{
+    LPITEMIDLIST pidl;
+    HRESULT hr;
+
+    if (!pSHGetFolderLocation) return;
+    if (!guid) return;
+
+    pidl = NULL;
+    hr = pSHGetFolderLocation(NULL, folder, NULL, 0, &pidl);
+    if (SUCCEEDED(hr))
+    {
+        LPITEMIDLIST pidlLast = pILFindLastID(pidl);
+
+        if (pidlLast && (pidlLast->mkid.abID[0] == PT_SHELLEXT ||
+         pidlLast->mkid.abID[0] == PT_GUID))
+        {
+            GUID *shellGuid = (GUID *)(pidlLast->mkid.abID + 2);
+
+            ok(IsEqualIID(shellGuid, guid),
+             "%s: got GUID %s, expected %s\n", getFolderName(folder),
+             printGUID(shellGuid), printGUID(guid));
+        }
+        IMalloc_Free(pMalloc, pidl);
+    }
+}
+
+static void testDesktop(void)
+{
+    testSHGetFolderPath(FALSE, CSIDL_DESKTOP);
+    testSHGetSpecialFolderPath(FALSE, CSIDL_DESKTOP);
+    /* Test the desktop; even though SHITEMID should always contain abID of at
+     * least one type, when cb is 0 its value is undefined.  So don't check
+     * what the returned type is, just make sure it exists.
+     */
+    testSHGetFolderLocation(FALSE, CSIDL_DESKTOP);
+    testSHGetSpecialFolderLocation(FALSE, CSIDL_DESKTOP);
+}
+
+static void testPersonal(void)
+{
+    BYTE type;
+
+    /* The pidl may be a real folder, or a virtual directory, or a drive if the
+     * home directory is set to the root directory of a drive.
+     */
+    type = testSHGetFolderLocation(FALSE, CSIDL_PERSONAL);
+    ok(type == PT_FOLDER || type == PT_GUID || type == PT_DRIVE,
+     "CSIDL_PERSONAL returned invalid type 0x%02x, "
+     "expected PT_FOLDER or PT_GUID\n", type);
+    if (type == PT_FOLDER)
+        testSHGetFolderPath(FALSE, CSIDL_PERSONAL);
+    type = testSHGetSpecialFolderLocation(FALSE, CSIDL_PERSONAL);
+    ok(type == PT_FOLDER || type == PT_GUID || type == PT_DRIVE,
+     "CSIDL_PERSONAL returned invalid type 0x%02x, "
+     "expected PT_FOLDER or PT_GUID\n", type);
+    if (type == PT_FOLDER)
+        testSHGetSpecialFolderPath(FALSE, CSIDL_PERSONAL);
+}
+
+/* Checks the PIDL type of all the known values. */
+static void testPidlTypes(void)
+{
+    testDesktop();
+    testPersonal();
+    testShellValues(requiredShellValues, ARRAY_SIZE(requiredShellValues),
+     FALSE);
+    testShellValues(optionalShellValues, ARRAY_SIZE(optionalShellValues),
+     TRUE);
+}
+
+/* Verifies various shell virtual folders have the correct well-known GUIDs. */
+static void testGUIDs(void)
+{
+    matchGUID(CSIDL_BITBUCKET, &CLSID_RecycleBin);
+    matchGUID(CSIDL_CONTROLS, &CLSID_ControlPanel);
+    matchGUID(CSIDL_DRIVES, &CLSID_MyComputer);
+    matchGUID(CSIDL_INTERNET, &CLSID_Internet);
+    matchGUID(CSIDL_NETWORK, &CLSID_NetworkPlaces);
+    matchGUID(CSIDL_PERSONAL, &CLSID_MyDocuments);
+    matchGUID(CSIDL_COMMON_DOCUMENTS, &CLSID_CommonDocuments);
+}
+
+/* Verifies various shell paths match the environment variables to which they
+ * correspond.
+ */
+static void testEnvVars(void)
+{
+    matchSpecialFolderPathToEnv(CSIDL_PROGRAM_FILES, "ProgramFiles");
+    matchSpecialFolderPathToEnv(CSIDL_APPDATA, "APPDATA");
+    matchSpecialFolderPathToEnv(CSIDL_PROFILE, "USERPROFILE");
+    matchSpecialFolderPathToEnv(CSIDL_WINDOWS, "SystemRoot");
+    matchSpecialFolderPathToEnv(CSIDL_WINDOWS, "windir");
+    matchSpecialFolderPathToEnv(CSIDL_PROGRAM_FILES_COMMON,
+     "CommonProgramFiles");
+    /* this is only set on Wine, but can't hurt to verify it: */
+    matchSpecialFolderPathToEnv(CSIDL_SYSTEM, "winsysdir");
+}
+
+/* Verifies the shell path for CSIDL_WINDOWS matches the return from
+ * GetWindowsDirectory.  If SHGetSpecialFolderPath fails, no harm, no foul--not
+ * every shell32 version supports CSIDL_WINDOWS.
+ */
+static void testWinDir(void)
+{
+    char windowsShellPath[MAX_PATH], windowsDir[MAX_PATH] = { 0 };
+
+    if (!pSHGetSpecialFolderPathA) return;
+
+    if (pSHGetSpecialFolderPathA(NULL, windowsShellPath, CSIDL_WINDOWS, FALSE))
+    {
+        PathRemoveBackslashA(windowsShellPath);
+        GetWindowsDirectoryA(windowsDir, sizeof(windowsDir));
+        PathRemoveBackslashA(windowsDir);
+        ok(!lstrcmpiA(windowsDir, windowsShellPath),
+         "GetWindowsDirectory does not match SHGetSpecialFolderPath:\n"
+         "GetWindowsDirectory returns %s\nSHGetSpecialFolderPath returns %s\n",
+         windowsDir, windowsShellPath);
+    }
+}
+
+/* Verifies the shell path for CSIDL_SYSTEM and CSIDL_SYSTEMX86 matches the
+ * return from GetSystemDirectory.  If SHGetSpecialFolderPath fails, no harm,
+ * no foul--not every shell32 version supports CSIDL_SYSTEM.
+ */
+static void testSystemDir(void)
+{
+    char systemShellPath[MAX_PATH], systemDir[MAX_PATH] = { 0 };
+
+    if (!pSHGetSpecialFolderPathA) return;
+
+    GetSystemDirectoryA(systemDir, sizeof(systemDir));
+    PathRemoveBackslashA(systemDir);
+    if (pSHGetSpecialFolderPathA(NULL, systemShellPath, CSIDL_SYSTEM, FALSE))
+    {
+        PathRemoveBackslashA(systemShellPath);
+        ok(!lstrcmpiA(systemDir, systemShellPath),
+         "GetSystemDirectory does not match SHGetSpecialFolderPath:\n"
+         "GetSystemDirectory returns %s\nSHGetSpecialFolderPath returns %s\n",
+         systemDir, systemShellPath);
+    }
+    /* check CSIDL_SYSTEMX86; note that this isn't always present, so don't
+     * worry if it fails
+     */
+    if (pSHGetSpecialFolderPathA(NULL, systemShellPath, CSIDL_SYSTEMX86, FALSE))
+    {
+        PathRemoveBackslashA(systemShellPath);
+        ok(!lstrcmpiA(systemDir, systemShellPath),
+         "GetSystemDirectory does not match SHGetSpecialFolderPath:\n"
+         "GetSystemDirectory returns %s\nSHGetSpecialFolderPath returns %s\n",
+         systemDir, systemShellPath);
+    }
+}
+
+/* Globals used by subprocesses */
+static int    myARGC;
+static char **myARGV;
+static char   base[MAX_PATH];
+static char   selfname[MAX_PATH];
+
+static int init(void)
+{
+    myARGC = winetest_get_mainargs(&myARGV);
+    if (!GetCurrentDirectoryA(sizeof(base), base)) return 0;
+    strcpy(selfname, myARGV[0]);
+    return 1;
+}
+
+/* Subprocess helper 1: test what happens when CSIDL_FAVORITES is set to a
+ * nonexistent directory.
+ */
+static void testNonExistentPath1(void)
+{
+    HRESULT hr;
+    LPITEMIDLIST pidl;
+    char path[MAX_PATH];
+
+    /* test some failure cases first: */
+    hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES, NULL,
+     SHGFP_TYPE_CURRENT, NULL);
+    ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+     "SHGetFolderPath returned 0x%08lx, expected 0x80070002\n", hr);
+    pidl = NULL;
+    hr = pSHGetFolderLocation(NULL, CSIDL_FAVORITES, NULL, 0,
+     &pidl);
+    ok(hr == E_FAIL,
+     "SHGetFolderLocation returned 0x%08lx, expected E_FAIL\n", hr);
+    if (SUCCEEDED(hr) && pidl)
+        IMalloc_Free(pMalloc, pidl);
+    ok(!pSHGetSpecialFolderPathA(NULL, path, CSIDL_FAVORITES, FALSE),
+     "SHGetSpecialFolderPath succeeded, expected failure\n");
+    pidl = NULL;
+    hr = pSHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl);
+    ok(hr == E_FAIL, "SHGetFolderLocation returned 0x%08lx, expected E_FAIL\n",
+     hr);
+    if (SUCCEEDED(hr) && pidl)
+        IMalloc_Free(pMalloc, pidl);
+    /* now test success: */
+    hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, NULL,
+     SHGFP_TYPE_CURRENT, path);
+    if (SUCCEEDED(hr))
+    {
+        BOOL ret;
+
+        if (winetest_interactive)
+            printf("CSIDL_FAVORITES was changed to %s\n", path);
+        ret = CreateDirectoryA(path, NULL);
+        ok(!ret,
+         "CreateDirectoryA succeeded but should have failed "
+         "with ERROR_ALREADY_EXISTS\n");
+        if (!ret)
+            ok(GetLastError() == ERROR_ALREADY_EXISTS,
+             "CreateDirectoryA failed with %ld, "
+             "expected ERROR_ALREADY_EXISTS\n",
+             GetLastError());
+    }
+    ok(SUCCEEDED(hr),
+     "SHGetFolderPath(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, "
+     "NULL, SHGFP_TYPE_CURRENT, path)\nfailed: 0x%08lx\n", hr);
+}
+
+/* Subprocess helper 2: make sure SHGetFolderPath still succeeds when the
+ * original value of CSIDL_FAVORITES is restored.
+ */
+static void testNonExistentPath2(void)
+{
+    HRESULT hr;
+
+    hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, NULL,
+     SHGFP_TYPE_CURRENT, NULL);
+    ok(SUCCEEDED(hr), "SHGetFolderPath failed: 0x%08lx\n", hr);
+}
+
+static void doChild(const char *arg)
+{
+    if (arg[0] == '1')
+        testNonExistentPath1();
+    else if (arg[0] == '2')
+        testNonExistentPath2();
+}
+
+/* Tests the return values from the various shell functions both with and
+ * without the use of the CSIDL_FLAG_CREATE flag.  This flag only appeared in
+ * version 5 of the shell, so don't test unless it's at least version 5.
+ * The test reads a value from the registry, modifies it, calls
+ * SHGetFolderPath once with the CSIDL_FLAG_CREATE flag, and immediately
+ * afterward without it.  Then it restores the registry and deletes the folder
+ * that was created.
+ * One oddity with respect to restoration: shell32 caches somehow, so it needs
+ * to be reloaded in order to see the correct (restored) value.
+ * Some APIs unrelated to the ones under test may fail, but I expect they're
+ * covered by other unit tests; I just print out something about failure to
+ * help trace what's going on.
+ */
+static void testNonExistentPath(void)
+{
+    static const char userShellFolders[] = 
+     "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders";
+    char originalPath[MAX_PATH], modifiedPath[MAX_PATH];
+    HKEY key;
+
+    if (!pSHGetFolderPathA) return;
+    if (!pSHGetFolderLocation) return;
+    if (!pSHGetSpecialFolderPathA) return;
+    if (!pSHGetSpecialFolderLocation) return;
+    if (!pSHFileOperationA) return;
+    if (shellVersion.dwMajorVersion < 5) return;
+
+    if (!RegOpenKeyExA(HKEY_CURRENT_USER, userShellFolders, 0, KEY_ALL_ACCESS,
+     &key))
+    {
+        DWORD len, type;
+
+        len = sizeof(originalPath);
+        if (!RegQueryValueExA(key, "Favorites", NULL, &type,
+         (LPBYTE)&originalPath, &len))
+        {
+            size_t len = strlen(originalPath);
+
+            memcpy(modifiedPath, originalPath, len);
+            modifiedPath[len++] = '2';
+            modifiedPath[len++] = '\0';
+            if (winetest_interactive)
+                printf("Changing CSIDL_FAVORITES to %s\n", modifiedPath);
+            if (!RegSetValueExA(key, "Favorites", 0, type, (LPBYTE) modifiedPath, len))
+            {
+                char buffer[MAX_PATH];
+                STARTUPINFOA startup;
+                PROCESS_INFORMATION info;
+                HRESULT hr;
+                BOOL ret;
+
+                wnsprintfA(buffer, sizeof(buffer), "%s tests/shellpath.c 1",
+                 selfname);
+                memset(&startup, 0, sizeof(startup));
+                startup.cb = sizeof(startup);
+                startup.dwFlags = STARTF_USESHOWWINDOW;
+                startup.dwFlags = SW_SHOWNORMAL;
+                CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL,
+                 &startup, &info);
+                ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0,
+                 "child process termination\n");
+
+                /* Query the path to be able to delete it below */
+                hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES, NULL,
+                 SHGFP_TYPE_CURRENT, modifiedPath);
+                ok(SUCCEEDED(hr), "SHGetFolderPathA failed: 0x%08lx\n", hr);
+
+                /* restore original values: */
+                if (winetest_interactive)
+                    printf("Restoring CSIDL_FAVORITES to %s\n", originalPath);
+                RegSetValueExA(key, "Favorites", 0, type, (LPBYTE) originalPath,
+                 strlen(originalPath) + 1);
+                RegFlushKey(key);
+
+                wnsprintfA(buffer, sizeof(buffer), "%s tests/shellpath.c 2",
+                 selfname);
+                memset(&startup, 0, sizeof(startup));
+                startup.cb = sizeof(startup);
+                startup.dwFlags = STARTF_USESHOWWINDOW;
+                startup.dwFlags = SW_SHOWNORMAL;
+                CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL,
+                 &startup, &info);
+                ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0,
+                 "child process termination\n");
+
+                ret = RemoveDirectoryA(modifiedPath);
+                ok( ret, "RemoveDirectoryA failed: %ld\n", GetLastError());
+            }
+        }
+        else if (winetest_interactive)
+            printf("RegQueryValueExA(key, Favorites, ...) failed\n");
+        if (key)
+            RegCloseKey(key);
+    }
+    else if (winetest_interactive)
+        printf("RegOpenKeyExA(HKEY_CURRENT_USER, %s, ...) failed\n",
+         userShellFolders);
+}
+
+START_TEST(shellpath)
+{
+    if (!init()) return;
+
+    loadShell32();
+    if (!hShell32) return;
+
+    if (myARGC >= 3)
+        doChild(myARGV[2]);
+    else
+    {
+        /* first test various combinations of parameters: */
+        testApiParameters();
+
+        /* check known values: */
+        testPidlTypes();
+        testGUIDs();
+        testEnvVars();
+        testWinDir();
+        testSystemDir();
+        testNonExistentPath();
+    }
+}
diff --git a/reactos/regtests/winetests/shell32/shlexec.c b/reactos/regtests/winetests/shell32/shlexec.c
new file mode 100755 (executable)
index 0000000..4d0750e
--- /dev/null
@@ -0,0 +1,462 @@
+/*
+ * Unit test of the ShellExecute function.
+ *
+ * Copyright 2005 Francois Gouget for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* TODO:
+ * - test the default verb selection
+ * - test selection of an alternate class
+ * - try running executables in more ways
+ * - try passing arguments to executables
+ * - ShellExecute("foo.shlexec") with no path should work if foo.shlexec is
+ *   in the PATH
+ * - test associations that use %l, %L or "%1" instead of %1
+ * - we may want to test ShellExecuteEx() instead of ShellExecute()
+ *   and then we could also check its return value
+ * - ShellExecuteEx() also calls SetLastError() with meaningful values which
+ *   we could check
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "wtypes.h"
+#include "winbase.h"
+#include "windef.h"
+#include "shellapi.h"
+#include "shlwapi.h"
+#include "wine/test.h"
+
+#include "shell32_test.h"
+
+
+static char argv0[MAX_PATH];
+static int myARGC;
+static char** myARGV;
+static char tmpdir[MAX_PATH];
+
+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
+};
+
+
+static void strcat_param(char* str, const char* param)
+{
+    if (param!=NULL)
+    {
+        strcat(str, "\"");
+        strcat(str, param);
+        strcat(str, "\"");
+    }
+    else
+    {
+        strcat(str, "null");
+    }
+}
+
+static char shell_call[2048]="";
+static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCSTR directory)
+{
+    strcpy(shell_call, "ShellExecute(");
+    strcat_param(shell_call, operation);
+    strcat(shell_call, ", ");
+    strcat_param(shell_call, file);
+    strcat(shell_call, ", ");
+    strcat_param(shell_call, parameters);
+    strcat(shell_call, ", ");
+    strcat_param(shell_call, directory);
+    strcat(shell_call, ")");
+    if (winetest_debug > 1)
+        trace("%s\n", shell_call);
+
+    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);
+}
+
+static int shell_execute_ex(DWORD mask, LPCSTR operation, LPCSTR file,
+                            LPCSTR parameters, LPCSTR directory)
+{
+    SHELLEXECUTEINFO sei;
+    BOOL success;
+    int rc;
+
+    strcpy(shell_call, "ShellExecuteEx(");
+    strcat_param(shell_call, operation);
+    strcat(shell_call, ", ");
+    strcat_param(shell_call, file);
+    strcat(shell_call, ", ");
+    strcat_param(shell_call, parameters);
+    strcat(shell_call, ", ");
+    strcat_param(shell_call, directory);
+    strcat(shell_call, ")");
+    if (winetest_debug > 1)
+        trace("%s\n", shell_call);
+
+    sei.cbSize=sizeof(sei);
+    sei.fMask=mask;
+    sei.hwnd=NULL;
+    sei.lpVerb=operation;
+    sei.lpFile=file;
+    sei.lpParameters=parameters;
+    sei.lpDirectory=directory;
+    sei.nShow=SW_SHOWNORMAL;
+    sei.hInstApp=NULL; /* Out */
+    sei.lpIDList=NULL;
+    sei.lpClass=NULL;
+    sei.hkeyClass=NULL;
+    sei.dwHotKey=0;
+    sei.hIcon=NULL;
+
+    SetLastError(0xcafebabe);
+    success=ShellExecuteEx(&sei);
+    rc=(int)sei.hInstApp;
+    ok((success && rc >= 32) || (!success && rc < 32),
+       "%s rc=%d and hInstApp=%d is not allowed\n", shell_call, success, rc);
+    return rc;
+}
+
+static void create_test_association(const char* extension)
+{
+    HKEY hkey, hkey_shell;
+    char class[MAX_PATH];
+    LONG rc;
+
+    sprintf(class, "shlexec%s", 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);
+    assert(rc==ERROR_SUCCESS);
+    CloseHandle(hkey);
+
+    rc=RegCreateKeyEx(HKEY_CLASSES_ROOT, class, 0, NULL, 0,
+                      KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS, NULL, &hkey, NULL);
+    assert(rc==ERROR_SUCCESS);
+    rc=RegCreateKeyEx(hkey, "shell", 0, NULL, 0,
+                      KEY_CREATE_SUB_KEY, NULL, &hkey_shell, NULL);
+    assert(rc==ERROR_SUCCESS);
+    CloseHandle(hkey);
+    CloseHandle(hkey_shell);
+}
+
+static void delete_test_association(const char* extension)
+{
+    char class[MAX_PATH];
+
+    sprintf(class, "shlexec%s", extension);
+    SHDeleteKey(HKEY_CLASSES_ROOT, class);
+    SHDeleteKey(HKEY_CLASSES_ROOT, extension);
+}
+
+static void create_test_verb(const char* extension, const char* verb)
+{
+    HKEY hkey_shell, hkey_verb, hkey_cmd;
+    char shell[MAX_PATH];
+    char* cmd;
+    LONG rc;
+
+    sprintf(shell, "shlexec%s\\shell", extension);
+    rc=RegOpenKeyEx(HKEY_CLASSES_ROOT, shell, 0,
+                    KEY_CREATE_SUB_KEY, &hkey_shell);
+    assert(rc==ERROR_SUCCESS);
+    rc=RegCreateKeyEx(hkey_shell, verb, 0, NULL, 0, KEY_CREATE_SUB_KEY,
+                      NULL, &hkey_verb, NULL);
+    assert(rc==ERROR_SUCCESS);
+    rc=RegCreateKeyEx(hkey_verb, "command", 0, NULL, 0, KEY_SET_VALUE,
+                      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);
+
+    free(cmd);
+    CloseHandle(hkey_shell);
+    CloseHandle(hkey_verb);
+    CloseHandle(hkey_cmd);
+}
+
+
+typedef struct
+{
+    char* basename;
+    int rc;
+    int todo;
+} 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},
+
+    /* Standard tests */
+    {"%s\\test file.shlexec",   0, 0},
+    {"%s\\test file.shlexec.",  0, 0},
+    {"%s/test file.shlexec",    0, 0},
+
+    /* Test filenames with no association */
+    {"%s\\test file.noassoc",   SE_ERR_NOASSOC, 0},
+
+    /* Test double extensions */
+    {"%s\\test file.noassoc.shlexec", 0, 0},
+    {"%s\\test file.shlexec.noassoc", SE_ERR_NOASSOC, 0},
+
+    /* Test shortcuts */
+    {"%s\\test_shortcut_shlexec.lnk", 0, 0},
+
+    {NULL, 0, 0}
+};
+
+static void test_filename()
+{
+    char filename[MAX_PATH];
+    const filename_tests_t* test;
+    HMODULE hdll;
+    DLLVERSIONINFO dllver;
+    HRESULT (WINAPI *pDllGetVersion)(DLLVERSIONINFO*);
+    char* c;
+    int rc;
+
+    test=filename_tests;
+    while (test->basename)
+    {
+        sprintf(filename, test->basename, tmpdir);
+        if (strchr(filename, '/'))
+        {
+            c=filename;
+            while (*c)
+            {
+                if (*c=='\\')
+                    *c='/';
+                c++;
+            }
+        }
+        rc=shell_execute(NULL, filename, NULL, NULL);
+        if (test->rc==0)
+        {
+            if (test->todo)
+            {
+                todo_wine
+                {
+                    ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call,
+                       rc, GetLastError());
+                }
+            }
+            else
+            {
+                ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call,
+                   rc, GetLastError());
+            }
+        }
+        else
+        {
+            if (test->todo)
+            {
+                todo_wine
+                {
+                    ok(rc==test->rc, "%s returned %d\n", shell_call, rc);
+                }
+            }
+            else
+            {
+                ok(rc==test->rc, "%s returned %d\n", shell_call, rc);
+            }
+        }
+        test++;
+    }
+
+    hdll=GetModuleHandleA("shell32.dll");
+    pDllGetVersion=(void*)GetProcAddress(hdll, "DllGetVersion");
+    if (pDllGetVersion)
+    {
+        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.
+         * One day we may need to check the exact version of the dll but for
+         * now making sure DllGetVersion() is present is sufficient.
+         */
+        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,
+           GetLastError());
+
+        if (dllver.dwMajorVersion>=6)
+        {
+            /* 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);
+            c=filename;
+            while (*c)
+            {
+                if (*c=='\\')
+                    *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());
+            }
+        }
+    }
+}
+
+
+static void test_exes()
+{
+    char filename[MAX_PATH];
+    int rc;
+
+    /* We need NOZONECHECKS on Win2003 to block a dialog */
+    rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, "shlexec -nop",
+                        NULL);
+    ok(rc>=32, "%s returned %d\n", shell_call, rc);
+
+    sprintf(filename, "%s\\test file.noassoc", tmpdir);
+    if (CopyFile(argv0, filename, FALSE))
+    {
+        rc=shell_execute(NULL, filename, "shlexec -nop", NULL);
+        todo_wine {
+        ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc);
+        }
+    }
+}
+
+
+static void init_test()
+{
+    char filename[MAX_PATH];
+    WCHAR lnkfile[MAX_PATH];
+    const char* const * testfile;
+    lnk_desc_t desc;
+    DWORD rc;
+    HRESULT r;
+
+    r = CoInitialize(NULL);
+    ok(SUCCEEDED(r), "CoInitialize failed (0x%08lx)\n", r);
+    if (!SUCCEEDED(r))
+        exit(1);
+
+    rc=GetModuleFileName(NULL, argv0, sizeof(argv0));
+    assert(rc!=0 && rc<sizeof(argv0));
+    if (GetFileAttributes(argv0)==INVALID_FILE_ATTRIBUTES)
+    {
+        strcat(argv0, ".so");
+        ok(GetFileAttributes(argv0)!=INVALID_FILE_ATTRIBUTES,
+           "unable to find argv0!\n");
+    }
+
+    GetTempPathA(sizeof(tmpdir)/sizeof(*tmpdir), tmpdir);
+
+    /* Set up the test files */
+    testfile=testfiles;
+    while (*testfile)
+    {
+        HANDLE hfile;
+
+        sprintf(filename, *testfile, tmpdir);
+        hfile=CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+                     FILE_ATTRIBUTE_NORMAL, NULL);
+        if (hfile==INVALID_HANDLE_VALUE)
+        {
+            trace("unable to create '%s': err=%ld\n", filename, GetLastError());
+            assert(0);
+        }
+        CloseHandle(hfile);
+        testfile++;
+    }
+
+    /* Setup the test shortcuts */
+    sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
+    MultiByteToWideChar(CP_ACP, 0, filename, -1, lnkfile, sizeof(lnkfile)/sizeof(*lnkfile));
+    desc.description=NULL;
+    desc.workdir=NULL;
+    sprintf(filename, "%s\\test file.shlexec", tmpdir);
+    desc.path=filename;
+    desc.pidl=NULL;
+    desc.arguments="";
+    desc.showcmd=0;
+    desc.icon=NULL;
+    desc.icon_id=0;
+    desc.hotkey=0;
+    create_lnk(lnkfile, &desc, 0);
+
+    /* Create a basic association suitable for most tests */
+    create_test_association(".shlexec");
+    create_test_verb(".shlexec", "Open");
+}
+
+static void cleanup_test()
+{
+    char filename[MAX_PATH];
+    const char* const * testfile;
+
+    /* Delete the test files */
+    testfile=testfiles;
+    while (*testfile)
+    {
+        sprintf(filename, *testfile, tmpdir);
+        DeleteFile(filename);
+        testfile++;
+    }
+
+    /* Delete the test association */
+    delete_test_association(".shlexec");
+
+    CoUninitialize();
+}
+
+START_TEST(shlexec)
+{
+
+    myARGC = winetest_get_mainargs(&myARGV);
+    if (myARGC>=3)
+    {
+        /* FIXME: We should dump the parameters we got
+         *        and have the parent verify them
+         */
+        exit(0);
+    }
+
+    init_test();
+
+    test_filename();
+    test_exes();
+
+    cleanup_test();
+}
diff --git a/reactos/regtests/winetests/shell32/shlfileop.c b/reactos/regtests/winetests/shell32/shlfileop.c
new file mode 100644 (file)
index 0000000..03b197d
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * Unit test of the SHFileOperation function.
+ *
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#define WINE_NOWINSOCK
+#include "windef.h"
+#include "winbase.h"
+#include "wtypes.h"
+#include "shellapi.h"
+#include "shlobj.h"
+
+#include "wine/test.h"
+
+CHAR CURR_DIR[MAX_PATH];
+
+static HMODULE hshell32;
+static int (WINAPI *pSHCreateDirectoryExA)(HWND, LPCSTR, LPSECURITY_ATTRIBUTES);
+
+static void InitFunctionPointers(void)
+{
+    hshell32 = GetModuleHandleA("shell32.dll");
+
+    if(hshell32)
+       pSHCreateDirectoryExA = (void*)GetProcAddress(hshell32, "SHCreateDirectoryExA");
+}
+
+/* creates a file with the specified name for tests */
+static void createTestFile(const CHAR *name)
+{
+    HANDLE file;
+    DWORD written;
+
+    file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
+    WriteFile(file, name, strlen(name), &written, NULL);
+    WriteFile(file, "\n", strlen("\n"), &written, NULL);
+    CloseHandle(file);
+}
+
+static BOOL file_exists(const CHAR *name)
+{
+    return GetFileAttributesA(name) != INVALID_FILE_ATTRIBUTES;
+}
+
+/* initializes the tests */
+static void init_shfo_tests(void)
+{
+    int len;
+
+    GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
+    len = lstrlenA(CURR_DIR);
+
+    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);
+}
+
+/* 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");
+}
+
+/*
+ puts into the specified buffer file names with current directory.
+ files - string with file names, separated by null characters. Ends on a double
+ null characters
+*/
+static void set_curr_dir_path(CHAR *buf, const CHAR* files)
+{
+    buf[0] = 0;
+    while (files[0])
+    {
+        strcpy(buf, CURR_DIR);
+        buf += strlen(buf);
+        buf[0] = '\\';
+        buf++;
+        strcpy(buf, files);
+        buf += strlen(buf) + 1;
+        files += strlen(files) + 1;
+    }
+    buf[0] = 0;
+}
+
+
+/* tests the FO_DELETE action */
+static void test_delete(void)
+{
+    SHFILEOPSTRUCTA shfo;
+    DWORD ret;
+    CHAR buf[MAX_PATH];
+
+    sprintf(buf, "%s\\%s", CURR_DIR, "test?.txt");
+    buf[strlen(buf) + 1] = '\0';
+
+    shfo.hwnd = NULL;
+    shfo.wFunc = FO_DELETE;
+    shfo.pFrom = buf;
+    shfo.pTo = "\0";
+    shfo.fFlags = FOF_FILESONLY | FOF_NOCONFIRMATION | FOF_SILENT;
+    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");
+
+    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");
+
+    shfo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
+
+    ok(!SHFileOperationA(&shfo), "Directory removed\n");
+    ok(!file_exists(".\\test4.txt"), "Directory should be removed\n");
+
+    ret = SHFileOperationA(&shfo);
+    ok(!ret, "The requested file does not exist, ret=%ld\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");
+
+    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");
+}
+
+/* tests the FO_RENAME action */
+static void test_rename(void)
+{
+    SHFILEOPSTRUCTA shfo, shfo2;
+    CHAR from[MAX_PATH];
+    CHAR to[MAX_PATH];
+    DWORD retval;
+
+    shfo.hwnd = NULL;
+    shfo.wFunc = FO_RENAME;
+    shfo.pFrom = from;
+    shfo.pTo = to;
+    shfo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
+    shfo.hNameMappings = NULL;
+    shfo.lpszProgressTitle = NULL;
+
+    set_curr_dir_path(from, "test1.txt\0");
+    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");
+
+    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");
+
+    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");
+
+    memcpy(&shfo2, &shfo, sizeof(SHFILEOPSTRUCTA));
+    shfo2.fFlags |= FOF_MULTIDESTFILES;
+
+    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(&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");
+
+    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");
+
+    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);
+
+    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");
+
+    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);
+}
+
+/* tests the FO_COPY action */
+static void test_copy(void)
+{
+    SHFILEOPSTRUCTA shfo, shfo2;
+    CHAR from[MAX_PATH];
+    CHAR to[MAX_PATH];
+    FILEOP_FLAGS tmp_flags;
+    DWORD retval;
+
+    shfo.hwnd = NULL;
+    shfo.wFunc = FO_COPY;
+    shfo.pFrom = from;
+    shfo.pTo = to;
+    shfo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
+    shfo.hNameMappings = NULL;
+    shfo.lpszProgressTitle = NULL;
+
+    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 "
+       "specified as a target\n");
+
+    memcpy(&shfo2, &shfo, sizeof(SHFILEOPSTRUCTA));
+    shfo2.fFlags |= FOF_MULTIDESTFILES;
+
+    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 "
+       "specified as a target\n");
+    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 "
+       "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");
+
+    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(!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");
+    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(!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");
+    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");
+    clean_after_shfo_tests();
+
+    /* Copying multiple files with one not existing as source, fails the
+       entire operation in Win98/ME/2K/XP, but not in 95/NT */
+    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");
+    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");
+    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");
+    }
+    ok(!file_exists(".\\testdir2\\test2.txt"), "The file is copied\n");
+    shfo.fFlags = tmp_flags;
+}
+
+/* tests the FO_MOVE action */
+static void test_move(void)
+{
+    SHFILEOPSTRUCTA shfo, shfo2;
+    CHAR from[MAX_PATH];
+    CHAR to[MAX_PATH];
+
+    shfo.hwnd = NULL;
+    shfo.wFunc = FO_MOVE;
+    shfo.pFrom = from;
+    shfo.pTo = to;
+    shfo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
+    shfo.hNameMappings = NULL;
+    shfo.lpszProgressTitle = NULL;
+
+    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");
+
+    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(!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");
+
+    clean_after_shfo_tests();
+    init_shfo_tests();
+
+    memcpy(&shfo2, &shfo, sizeof(SHFILEOPSTRUCTA));
+    shfo2.fFlags |= FOF_MULTIDESTFILES;
+
+    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 "
+       "specified as a target\n");
+    DeleteFileA(".\\test6.txt");
+    DeleteFileA(".\\test7.txt");
+    RemoveDirectoryA(".\\test8.txt");
+
+    init_shfo_tests();
+
+    /* 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 move many files\n");
+    ok(!file_exists(".\\test6.txt"), "The file is not moved - many files are "
+       "specified as a target\n");
+
+    init_shfo_tests();
+
+    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");
+
+    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");
+
+    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");
+    set_curr_dir_path(from, "test6.txt\0");
+    set_curr_dir_path(to, "test1.txt\0");
+    ok(!SHFileOperationA(&shfo), "Move file back\n");
+
+    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");
+    set_curr_dir_path(from, "test6.txt\0");
+    set_curr_dir_path(to, "test4.txt\0");
+    ok(!SHFileOperationA(&shfo), "Move dir back\n");
+}
+
+static void test_sh_create_dir(void)
+{
+    CHAR path[MAX_PATH];
+    int ret;
+
+    if(!pSHCreateDirectoryExA)
+    {
+       trace("skipping SHCreateDirectoryExA tests\n");
+       return;
+    }
+
+    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");
+
+    ret = pSHCreateDirectoryExA(NULL, path, NULL);
+    ok(ERROR_ALREADY_EXISTS == ret, "SHCreateDirectoryEx should fail to create existing directory, ret = %d\n", ret);
+}
+
+START_TEST(shlfileop)
+{
+    InitFunctionPointers();
+
+    clean_after_shfo_tests();
+
+    init_shfo_tests();
+    test_delete();
+    clean_after_shfo_tests();
+
+    init_shfo_tests();
+    test_rename();
+    clean_after_shfo_tests();
+
+    init_shfo_tests();
+    test_copy();
+    clean_after_shfo_tests();
+
+    init_shfo_tests();
+    test_move();
+    clean_after_shfo_tests();
+
+    test_sh_create_dir();
+    clean_after_shfo_tests();
+}
diff --git a/reactos/regtests/winetests/shell32/shlfolder.c b/reactos/regtests/winetests/shell32/shlfolder.c
new file mode 100644 (file)
index 0000000..3566c7b
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ * Unit test of the IShellFolder functions.
+ *
+ * Copyright 2004 Vitaliy Margolen
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "wtypes.h"
+#include "shellapi.h"
+
+
+#include "shlguid.h"
+#include "shlobj.h"
+//#include "shobjidl.h"
+#include "shlwapi.h"
+
+
+#include "wine/unicode.h"
+#include "wine/test.h"
+
+
+static IMalloc *ppM;
+
+static HRESULT (WINAPI *pSHBindToParent)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*);
+static BOOL (WINAPI *pSHGetSpecialFolderPathW)(HWND, LPWSTR, int, BOOL);
+
+static void init_function_pointers(void)
+{
+    HMODULE hmod = GetModuleHandleA("shell32.dll");
+    HRESULT hr;
+
+    if(hmod)
+    {
+        pSHBindToParent = (void*)GetProcAddress(hmod, "SHBindToParent");
+        pSHGetSpecialFolderPathW = (void*)GetProcAddress(hmod, "SHGetSpecialFolderPathW");
+    }
+
+    hr = SHGetMalloc(&ppM);
+    ok(hr == S_OK, "SHGetMalloc failed %08lx\n", hr);
+}
+
+static void test_ParseDisplayName(void)
+{
+    HRESULT hr;
+    IShellFolder *IDesktopFolder;
+    static const char *cNonExistDir1A = "c:\\nonexist_subdir";
+    static const char *cNonExistDir2A = "c:\\\\nonexist_subdir";
+    DWORD res;
+    WCHAR cTestDirW [MAX_PATH] = {0};
+    ITEMIDLIST *newPIDL;
+
+    hr = SHGetDesktopFolder(&IDesktopFolder);
+    if(hr != S_OK) return;
+
+    res = GetFileAttributesA(cNonExistDir1A);
+    if(res != INVALID_FILE_ATTRIBUTES) return;
+
+    MultiByteToWideChar(CP_ACP, 0, cNonExistDir1A, -1, cTestDirW, MAX_PATH);
+    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);
+
+    res = GetFileAttributesA(cNonExistDir2A);
+    if(res != INVALID_FILE_ATTRIBUTES) return;
+
+    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);
+}
+
+/* creates a file with the specified name for tests */
+static void CreateTestFile(const CHAR *name)
+{
+    HANDLE file;
+    DWORD written;
+
+    file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+    if (file != INVALID_HANDLE_VALUE)
+    {
+       WriteFile(file, name, strlen(name), &written, NULL);
+       WriteFile(file, "\n", strlen("\n"), &written, NULL);
+       CloseHandle(file);
+    }
+}
+
+
+/* initializes the tests */
+static void CreateFilesFolders(void)
+{
+    CreateDirectoryA(".\\testdir", NULL);
+    CreateDirectoryA(".\\testdir\\test.txt", NULL);
+    CreateTestFile  (".\\testdir\\test1.txt ");
+    CreateTestFile  (".\\testdir\\test2.txt ");
+    CreateTestFile  (".\\testdir\\test3.txt ");
+    CreateDirectoryA(".\\testdir\\testdir2 ", NULL);
+    CreateDirectoryA(".\\testdir\\testdir2\\subdir", NULL);
+}
+
+/* cleans after tests */
+static void Cleanup(void)
+{
+    DeleteFileA(".\\testdir\\test1.txt");
+    DeleteFileA(".\\testdir\\test2.txt");
+    DeleteFileA(".\\testdir\\test3.txt");
+    RemoveDirectoryA(".\\testdir\\test.txt");
+    RemoveDirectoryA(".\\testdir\\testdir2\\subdir");
+    RemoveDirectoryA(".\\testdir\\testdir2");
+    RemoveDirectoryA(".\\testdir");
+}
+
+
+/* perform test */
+static void test_EnumObjects(IShellFolder *iFolder)
+{
+    IEnumIDList *iEnumList;
+    LPITEMIDLIST newPIDL, idlArr[10];
+    ULONG NumPIDLs;
+    int i=0, j;
+    HRESULT hr;
+
+    static const WORD iResults [5][5] =
+    {
+       { 0,-1,-1,-1,-1},
+       { 1, 0,-1,-1,-1},
+       { 1, 1, 0,-1,-1},
+       { 1, 1, 1, 0,-1},
+       { 1, 1, 1, 1, 0}
+    };
+
+    /* Just test SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR for now */
+    static const ULONG attrs[5] =
+    {
+        SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
+        SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
+        SFGAO_FILESYSTEM,
+        SFGAO_FILESYSTEM,
+        SFGAO_FILESYSTEM,
+    };
+
+    hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList);
+    ok(hr == S_OK, "EnumObjects failed %08lx\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
+     * returned (in contrast to S_FALSE). We have to do it in a loop since WinXP
+     * only ever returns a single entry per call. */
+    while (IEnumIDList_Next(iEnumList, 10-i, &idlArr[i], &NumPIDLs) == S_OK) 
+        i += NumPIDLs;
+    ok (i == 5, "i: %d\n", i);
+
+    hr = IEnumIDList_Release(iEnumList);
+    ok(hr == S_OK, "IEnumIDList_Release failed %08lx\n", hr);
+    
+    /* Sort them first in case of wrong order from system */
+    for (i=0;i<5;i++) for (j=0;j<5;j++)
+        if ((SHORT)IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]) < 0)
+       {
+            newPIDL = idlArr[i];
+            idlArr[i] = idlArr[j];
+            idlArr[j] = newPIDL;
+        }
+           
+    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]);
+    }
+
+
+    for (i = 0; i < 5; i++)
+    {
+        SFGAOF flags;
+        flags = SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR;
+        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]);
+    }
+
+    for (i=0;i<5;i++)
+        IMalloc_Free(ppM, idlArr[i]);
+}
+
+static void test_BindToObject(void)
+{
+    HRESULT hr;
+    UINT cChars;
+    IShellFolder *psfDesktop, *psfChild, *psfMyComputer, *psfSystemDir;
+    SHITEMID emptyitem = { 0, { 0 } };
+    LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidlEmpty = (LPITEMIDLIST)&emptyitem;
+    WCHAR wszSystemDir[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 };
+
+    /* The following tests shows that BindToObject should fail with E_INVALIDARG if called
+     * 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);
+    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);
+
+    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);
+
+    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);
+    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);
+    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);
+
+    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);
+
+    cChars = GetSystemDirectoryW(wszSystemDir, MAX_PATH);
+    ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryW failed! LastError: %08lx\n", GetLastError());
+    if (cChars == 0 || cChars >= MAX_PATH) {
+        IShellFolder_Release(psfMyComputer);
+        return;
+    }
+    
+    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);
+    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);
+    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);
+    
+    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);
+
+    IShellFolder_Release(psfSystemDir);
+}
+  
+static void test_GetDisplayName(void)
+{
+    BOOL result;
+    HRESULT hr;
+    HANDLE hTestFile;
+    WCHAR wszTestFile[MAX_PATH], wszTestFile2[MAX_PATH], wszTestDir[MAX_PATH];
+    STRRET strret;
+    LPSHELLFOLDER psfDesktop, psfPersonal;
+    IUnknown *psfFile;
+    LPITEMIDLIST pidlTestFile;
+    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 };
+
+    /* I'm trying to figure if there is a functional difference between calling
+     * SHGetPathFromIDList and calling GetDisplayNameOf(SHGDN_FORPARSING) after
+     * binding to the shellfolder. One thing I thought of was that perhaps 
+     * SHGetPathFromIDList would be able to get the path to a file, which does
+     * not exist anymore, while the other method would'nt. It turns out there's
+     * no functional difference in this respect.
+     */
+
+    if(!pSHGetSpecialFolderPathW) return;
+
+    /* 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());
+    if (!result) return;
+
+    PathAddBackslashW(wszTestDir);
+    lstrcatW(wszTestDir, wszDirName);
+    result = CreateDirectoryW(wszTestDir, NULL);
+    ok(result, "CreateDirectoryW failed! Last error: %08lx\n", GetLastError());
+    if (!result) return;
+
+    lstrcpyW(wszTestFile, wszTestDir);
+    PathAddBackslashW(wszTestFile);
+    lstrcatW(wszTestFile, wszFileName);
+
+    hTestFile = CreateFileW(wszTestFile, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
+    ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %08lx\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);
+    if (FAILED(hr)) return;
+
+    hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
+    ok(SUCCEEDED(hr), "Desktop->ParseDisplayName failed! hr = %08lx\n", hr);
+    if (FAILED(hr)) {
+        IShellFolder_Release(psfDesktop);
+        return;
+    }
+
+    /* 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); }
+    if (SUCCEEDED(hr)) {
+        IShellFolder_Release(psfFile);
+    }
+    
+    /* Deleting the file and the directory */
+    DeleteFileW(wszTestFile);
+    RemoveDirectoryW(wszTestDir);
+
+    /* SHGetPathFromIDListW still works, although the file is not present anymore. */
+    result = SHGetPathFromIDListW(pidlTestFile, wszTestFile2);
+    ok (result, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError());
+    ok (!lstrcmpiW(wszTestFile, wszTestFile2), "SHGetPathFromIDListW returns incorrect path!\n");
+
+    if(!pSHBindToParent) return;
+
+    /* 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);
+    if (FAILED(hr)) {
+        IShellFolder_Release(psfDesktop);
+        return;
+    }
+
+    hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
+    ok (SUCCEEDED(hr), "Personal->GetDisplayNameOf failed! hr = %08lx\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");
+    
+    IShellFolder_Release(psfDesktop);
+    IShellFolder_Release(psfPersonal);
+}
+
+static void test_CallForAttributes(void)
+{
+    HKEY hKey;
+    LONG lResult;
+    HRESULT hr;
+    DWORD dwSize;
+    LPSHELLFOLDER psfDesktop;
+    LPITEMIDLIST pidlMyDocuments;
+    DWORD dwAttributes, dwCallForAttributes, dwOrigAttributes, dwOrigCallForAttributes;
+    static const WCHAR wszAttributes[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
+    static const WCHAR wszCallForAttributes[] = { 
+        'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
+    static const WCHAR wszMyDocumentsKey[] = {
+        'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
+        '1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
+        '\\','S','h','e','l','l','F','o','l','d','e','r',0 };
+    WCHAR wszMyDocuments[] = {
+        ':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
+        '9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
+    
+    /* For the root of a namespace extension, the attributes are not queried by binding
+     * to the object and calling GetAttributesOf. Instead, the attributes are read from 
+     * the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
+     *
+     * The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
+     * value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
+     * the shell does bind to the folder object and calls GetAttributesOf. This is not documented
+     * 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);
+    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);
+    if (FAILED(hr)) {
+        IShellFolder_Release(psfDesktop);
+        return;
+    }
+
+    dwAttributes = 0xffffffff;
+    hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
+                                      (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
+    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08lx\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_ISSLOW), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
+    ok (!(dwAttributes & SFGAO_GHOSTED), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
+
+    /* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
+     * 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); }
+    if (lResult != ERROR_SUCCESS) {
+        IMalloc_Free(ppM, pidlMyDocuments);
+        IShellFolder_Release(psfDesktop);
+        return;
+    }
+    
+    /* 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);
+    if (lResult != ERROR_SUCCESS) {
+        RegCloseKey(hKey);
+        IMalloc_Free(ppM, pidlMyDocuments);
+        IShellFolder_Release(psfDesktop);
+        return;
+    }
+
+    /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
+    dwSize = sizeof(DWORD);
+    lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL, 
+                              (LPBYTE)&dwOrigCallForAttributes, &dwSize);
+    ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08lx\n", lResult);
+    if (lResult != ERROR_SUCCESS) {
+        RegCloseKey(hKey);
+        IMalloc_Free(ppM, pidlMyDocuments);
+        IShellFolder_Release(psfDesktop);
+        return;
+    }
+    
+    /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and 
+     * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
+     * SFGAO_FILESYSTEM attributes. */
+    dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED;
+    RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwAttributes, sizeof(DWORD));
+    dwCallForAttributes = SFGAO_ISSLOW|SFGAO_FILESYSTEM;
+    RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
+                   (LPBYTE)&dwCallForAttributes, sizeof(DWORD));
+
+    /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by 
+     * GetAttributesOf. It seems that once there is a single attribute queried, for which
+     * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
+     * the flags in Attributes are ignored. 
+     */
+    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);
+    if (SUCCEEDED(hr)) 
+        ok (dwAttributes == SFGAO_FILESYSTEM, 
+            "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08lx\n", 
+            dwAttributes);
+
+    /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
+    RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwOrigAttributes, sizeof(DWORD));
+    RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
+                   (LPBYTE)&dwOrigCallForAttributes, sizeof(DWORD));
+    RegCloseKey(hKey);
+    IMalloc_Free(ppM, pidlMyDocuments);
+    IShellFolder_Release(psfDesktop);
+}
+
+static void test_GetAttributesOf(void) 
+{
+    HRESULT hr;
+    LPSHELLFOLDER psfDesktop, psfMyComputer;
+    SHITEMID emptyitem = { 0, { 0 } };
+    LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
+    LPITEMIDLIST pidlMyComputer;
+    DWORD dwFlags;
+    const static 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 */
+        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 };
+
+    hr = SHGetDesktopFolder(&psfDesktop);
+    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\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", 
+        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", 
+        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);
+    if (FAILED(hr)) {
+        IShellFolder_Release(psfDesktop);
+        return;
+    }
+
+    /* WinXP SP2 sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop 
+     * folder object. It doesn't do this, if MyComputer is queried directly (see below).
+     * SFGAO_CANLINK is the same as DROPEFFECT_LINK, which MSDN says means: "Drag source
+     * should create a link to the original data". You can't create links on MyComputer on
+     * Windows, so this flag shouldn't be set. Seems like a bug in Windows. As long as nobody
+     * depends on this bug, we probably shouldn't imitate it.
+     */
+    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); }
+
+    hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
+    ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08lx\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); }
+
+    dwFlags = 0xffffffff;
+    hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
+    ok (SUCCEEDED(hr), "MyComputer->GetAttributesOf(NULL) failed! hr = %08lx\n", hr); 
+    todo_wine { ok (dwFlags == dwMyComputerFlags, 
+                    "Wrong MyComputer attributes: %08lx, expected: %08lx\n", dwFlags, dwMyComputerFlags); }
+
+    IShellFolder_Release(psfMyComputer);
+}    
+
+static void test_SHGetPathFromIDList(void)
+{
+    SHITEMID emptyitem = { 0, { 0 } };
+    LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
+    LPITEMIDLIST pidlMyComputer;
+    WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
+    BOOL result;
+    HRESULT hr;
+    LPSHELLFOLDER psfDesktop;
+    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 };
+
+    if(!pSHGetSpecialFolderPathW) return;
+
+    /* 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());
+    if (!result) return;
+    
+    result = SHGetPathFromIDListW(pidlEmpty, wszPath);
+    ok(result, "SHGetPathFromIDListW failed! Last error: %08lx\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);
+    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;
+
+    SetLastError(0xdeadbeef);
+    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());
+
+    IMalloc_Free(ppM, pidlMyComputer);
+}
+
+static void test_EnumObjects_and_CompareIDs(void)
+{
+    ITEMIDLIST *newPIDL;
+    IShellFolder *IDesktopFolder, *testIShellFolder;
+    char  cCurrDirA [MAX_PATH] = {0};
+    WCHAR cCurrDirW [MAX_PATH];
+    static const WCHAR cTestDirW[] = {'\\','t','e','s','t','d','i','r',0};
+    int len;
+    HRESULT hr;
+
+    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);
+    strcatW(cCurrDirW, cTestDirW);
+
+    hr = SHGetDesktopFolder(&IDesktopFolder);
+    ok(hr == S_OK, "SHGetDesktopfolder failed %08lx\n", hr);
+
+    CreateFilesFolders();
+
+    hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
+    ok(hr == S_OK, "ParseDisplayName failed %08lx\n", hr);
+
+    hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
+    ok(hr == S_OK, "BindToObject failed %08lx\n", hr);
+
+    test_EnumObjects(testIShellFolder);
+
+    hr = IShellFolder_Release(testIShellFolder);
+    ok(hr == S_OK, "IShellFolder_Release failed %08lx\n", hr);
+
+    Cleanup();
+
+    IMalloc_Free(ppM, newPIDL);
+}
+
+START_TEST(shlfolder)
+{
+    init_function_pointers();
+    /* if OleInitialize doesn't get called, ParseDisplayName returns
+       CO_E_NOTINITIALIZED for malformed directory names on win2k. */
+    OleInitialize(NULL);
+
+    test_ParseDisplayName();
+    test_BindToObject();
+    test_EnumObjects_and_CompareIDs();
+    test_GetDisplayName();
+    test_GetAttributesOf();
+    test_SHGetPathFromIDList();
+    test_CallForAttributes();
+
+    OleUninitialize();
+}
diff --git a/reactos/regtests/winetests/shell32/string.c b/reactos/regtests/winetests/shell32/string.c
new file mode 100755 (executable)
index 0000000..0a3d592
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Unit tests for shell32 string operations
+ *
+ * Copyright 2004 Jon Griffiths
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#define WINE_NOWINSOCK
+#include "windef.h"
+#include "winbase.h"
+#include "wtypes.h"
+#include "shellapi.h"
+#include "shtypes.h"
+#include "objbase.h"
+
+#include "wine/test.h"
+
+static HMODULE hShell32;
+static HRESULT (WINAPI *pStrRetToStrNAW)(LPVOID,DWORD,LPSTRRET,const ITEMIDLIST *);
+
+static WCHAR *CoDupStrW(const char* src)
+{
+  INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
+  WCHAR* szTemp = (WCHAR*)CoTaskMemAlloc(len * sizeof(WCHAR));
+  MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
+  return szTemp;
+}
+
+static inline int strcmpW(const WCHAR *str1, const WCHAR *str2)
+{
+    while (*str1 && (*str1 == *str2)) { str1++; str2++; }
+    return *str1 - *str2;
+}
+
+static void test_StrRetToStringNA(void)
+{
+    trace("StrRetToStringNAW is Ascii\n");
+    /* FIXME */
+}
+
+static void test_StrRetToStringNW(void)
+{
+    static const WCHAR szTestW[] = { 'T','e','s','t','\0' };
+    ITEMIDLIST iidl[10];
+    WCHAR buff[128];
+    STRRET strret;
+    BOOL ret;
+
+    trace("StrRetToStringNAW is Unicode\n");
+
+    strret.uType = STRRET_WSTR;
+    strret.u.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");
+    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;
+    strcpy((char*)&iidl, " Test");
+    memset(buff, 0xff, sizeof(buff));
+    ret = pStrRetToStrNAW(buff, sizeof(buff)/sizeof(WCHAR), &strret, iidl);
+    ok(ret == TRUE && !strcmpW(buff, szTestW),
+       "STRRET_OFFSET: dup failed, ret=%d\n", ret);
+
+    /* The next test crashes on W2K, WinXP and W2K3, so we don't test. */
+#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");
+    pStrRetToStrNAW(NULL, sizeof(buff)/sizeof(WCHAR), &strret, NULL);
+    trace("NULL dest: ret=%d\n", ret);
+#endif
+}
+
+START_TEST(string)
+{
+    CoInitialize(0);
+
+    hShell32 = LoadLibraryA("shell32.dll");
+    if (!hShell32)
+        return;
+
+    pStrRetToStrNAW = (void*)GetProcAddress(hShell32, (LPSTR)96);
+    if (pStrRetToStrNAW)
+    {
+        if (!(GetVersion() & 0x80000000))
+            test_StrRetToStringNW();
+        else
+            test_StrRetToStringNA();
+    }
+}
diff --git a/reactos/regtests/winetests/shell32/testlist.c b/reactos/regtests/winetests/shell32/testlist.c
new file mode 100644 (file)
index 0000000..f4fcfe9
--- /dev/null
@@ -0,0 +1,36 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+/* stdarg.h is needed for Winelib */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "windef.h"
+#include "winbase.h"
+
+extern void func_shelllink(void);
+extern void func_shellpath(void);
+extern void func_shlexec(void);
+extern void func_shlfileop(void);
+extern void func_shlfolder(void);
+extern void func_string(void);
+
+struct test
+{
+    const char *name;
+    void (*func)(void);
+};
+
+
+const struct test winetest_testlist[] =
+{
+    { "shelllink", func_shelllink },
+    { "shellpath", func_shellpath },
+    { "shlexec", func_shlexec },
+    { "shlfileop", func_shlfileop },
+    { "shlfolder", func_shlfolder },
+    { "string", func_string },
+    { 0, 0 }
+};
+
+#define WINETEST_WANT_MAIN
+#include "wine/test.h"