From 877dc5eaf73423f04ea752832c8124126c5975cd Mon Sep 17 00:00:00 2001 From: Steven Edwards Date: Wed, 10 Aug 2005 20:35:15 +0000 Subject: [PATCH] added shell32 tests. Not enabled just yet svn path=/trunk/; revision=17268 --- .../regtests/winetests/shell32/generated.c | 1414 +++++++++++++++++ .../regtests/winetests/shell32/shell32.xml | 16 + .../regtests/winetests/shell32/shell32_test.h | 37 + .../regtests/winetests/shell32/shelllink.c | 533 +++++++ .../regtests/winetests/shell32/shellpath.c | 906 +++++++++++ reactos/regtests/winetests/shell32/shlexec.c | 462 ++++++ .../regtests/winetests/shell32/shlfileop.c | 466 ++++++ .../regtests/winetests/shell32/shlfolder.c | 677 ++++++++ reactos/regtests/winetests/shell32/string.c | 117 ++ reactos/regtests/winetests/shell32/testlist.c | 36 + 10 files changed, 4664 insertions(+) create mode 100755 reactos/regtests/winetests/shell32/generated.c create mode 100644 reactos/regtests/winetests/shell32/shell32.xml create mode 100755 reactos/regtests/winetests/shell32/shell32_test.h create mode 100755 reactos/regtests/winetests/shell32/shelllink.c create mode 100644 reactos/regtests/winetests/shell32/shellpath.c create mode 100755 reactos/regtests/winetests/shell32/shlexec.c create mode 100644 reactos/regtests/winetests/shell32/shlfileop.c create mode 100644 reactos/regtests/winetests/shell32/shlfolder.c create mode 100755 reactos/regtests/winetests/shell32/string.c create mode 100644 reactos/regtests/winetests/shell32/testlist.c diff --git a/reactos/regtests/winetests/shell32/generated.c b/reactos/regtests/winetests/shell32/generated.c new file mode 100755 index 00000000000..dee76384554 --- /dev/null +++ b/reactos/regtests/winetests/shell32/generated.c @@ -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 +#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 index 00000000000..c30ba05c9dd --- /dev/null +++ b/reactos/regtests/winetests/shell32/shell32.xml @@ -0,0 +1,16 @@ + + . + + ntdll + shell32 + shlwapi + ole32 + uuid + shelllink.c + shellpath.c + shlexec.c + shlfileop.c + shlfolder.c + string.c + testlist.c + diff --git a/reactos/regtests/winetests/shell32/shell32_test.h b/reactos/regtests/winetests/shell32/shell32_test.h new file mode 100755 index 00000000000..fc377896189 --- /dev/null +++ b/reactos/regtests/winetests/shell32/shell32_test.h @@ -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 index 00000000000..0ff121d5e42 --- /dev/null +++ b/reactos/regtests/winetests/shell32/shelllink.c @@ -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 +#include +#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 && rdescription) + { + 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 +#include +#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 index 00000000000..4d0750e27c2 --- /dev/null +++ b/reactos/regtests/winetests/shell32/shlexec.c @@ -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 +#include + +#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=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 index 00000000000..03b197d3233 --- /dev/null +++ b/reactos/regtests/winetests/shell32/shlfileop.c @@ -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 +#include + +#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 index 00000000000..3566c7ba217 --- /dev/null +++ b/reactos/regtests/winetests/shell32/shlfolder.c @@ -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 +#include + +#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 index 00000000000..0a3d5925c07 --- /dev/null +++ b/reactos/regtests/winetests/shell32/string.c @@ -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 +#include + +#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 index 00000000000..f4fcfe9be89 --- /dev/null +++ b/reactos/regtests/winetests/shell32/testlist.c @@ -0,0 +1,36 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +/* stdarg.h is needed for Winelib */ +#include +#include +#include +#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"