added shell32 tests. Not enabled just yet

svn path=/trunk/; revision=17268
This commit is contained in:
Steven Edwards 2005-08-10 20:35:15 +00:00
parent be6f8b5112
commit 877dc5eaf7
10 changed files with 4664 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,16 @@
<module name="shell32_winetest" type="win32cui" installbase="bin" installname="shell32_winetest.exe" warnings="true">
<include base="shell32_winetest">.</include>
<define name="__USE_W32API" />
<library>ntdll</library>
<library>shell32</library>
<library>shlwapi</library>
<library>ole32</library>
<library>uuid</library>
<file>shelllink.c</file>
<file>shellpath.c</file>
<file>shlexec.c</file>
<file>shlfileop.c</file>
<file>shlfolder.c</file>
<file>string.c</file>
<file>testlist.c</file>
</module>

View file

@ -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);

View file

@ -0,0 +1,533 @@
/*
* Unit tests for shelllinks
*
* Copyright 2004 Mike McCormack
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* This is a test program for the SHGet{Special}Folder{Path|Location} functions
* of shell32, that get either a filesytem path or a LPITEMIDLIST (shell
* namespace) path for a given folder (CSIDL value).
*
*/
#define _WIN32_IE 0x0400
#define COBJMACROS
#include <stdarg.h>
#include <stdio.h>
#include "windef.h"
#include "winbase.h"
#include "shlguid.h"
//#include "wine/shobjidl.h"
#include "shlobj.h"
#include "wine/test.h"
#include "shell32_test.h"
extern BOOL WINAPI ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
extern HRESULT WINAPI SHILCreateFromPath(LPCWSTR path, LPITEMIDLIST * ppidl, DWORD * attributes);
extern void WINAPI ILFree(LPITEMIDLIST pidl);
static const WCHAR lnkfile[]= { 'C',':','\\','t','e','s','t','.','l','n','k',0 };
static const WCHAR notafile[]= { 'C',':','\\','n','o','n','e','x','i','s','t','e','n','t','\\','f','i','l','e',0 };
/* For some reason SHILCreateFromPath does not work on Win98 and
* SHSimpleIDListFromPathA does not work on NT4. But if we call both we
* get what we want on all platforms.
*/
static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathA)(LPCSTR)=NULL;
static LPITEMIDLIST path_to_pidl(const char* path)
{
LPITEMIDLIST pidl;
if (!pSHSimpleIDListFromPathA)
{
HMODULE hdll=LoadLibraryA("shell32.dll");
pSHSimpleIDListFromPathA=(void*)GetProcAddress(hdll, (char*)162);
if (!pSHSimpleIDListFromPathA)
trace("SHSimpleIDListFromPathA not found in shell32.dll\n");
}
pidl=NULL;
if (pSHSimpleIDListFromPathA)
pidl=pSHSimpleIDListFromPathA(path);
if (!pidl)
{
WCHAR* pathW;
HRESULT r;
int len;
len=MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
pathW=HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, len);
r=SHILCreateFromPath(pathW, &pidl, NULL);
todo_wine {
ok(SUCCEEDED(r), "SHILCreateFromPath failed (0x%08lx)\n", r);
}
HeapFree(GetProcessHeap(), 0, pathW);
}
return pidl;
}
/*
* Test manipulation of an IShellLink's properties.
*/
static void test_get_set(void)
{
HRESULT r;
IShellLinkA *sl;
char mypath[MAX_PATH];
char buffer[INFOTIPSIZE];
LPITEMIDLIST pidl, tmp_pidl;
const char * str;
int i;
WORD w;
r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
&IID_IShellLinkA, (LPVOID*)&sl);
ok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08lx)\n", r);
if (!SUCCEEDED(r))
return;
/* Test Getting / Setting the description */
strcpy(buffer,"garbage");
r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
ok(SUCCEEDED(r), "GetDescription failed (0x%08lx)\n", r);
ok(*buffer=='\0', "GetDescription returned '%s'\n", buffer);
str="Some description";
r = IShellLinkA_SetDescription(sl, str);
ok(SUCCEEDED(r), "SetDescription failed (0x%08lx)\n", r);
strcpy(buffer,"garbage");
r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
ok(SUCCEEDED(r), "GetDescription failed (0x%08lx)\n", r);
ok(lstrcmp(buffer,str)==0, "GetDescription returned '%s'\n", buffer);
/* Test Getting / Setting the work directory */
strcpy(buffer,"garbage");
r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
ok(SUCCEEDED(r), "GetWorkingDirectory failed (0x%08lx)\n", r);
ok(*buffer=='\0', "GetWorkingDirectory returned '%s'\n", buffer);
str="c:\\nonexistent\\directory";
r = IShellLinkA_SetWorkingDirectory(sl, str);
ok(SUCCEEDED(r), "SetWorkingDirectory failed (0x%08lx)\n", r);
strcpy(buffer,"garbage");
r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
ok(SUCCEEDED(r), "GetWorkingDirectory failed (0x%08lx)\n", r);
ok(lstrcmpi(buffer,str)==0, "GetWorkingDirectory returned '%s'\n", buffer);
/* Test Getting / Setting the work directory */
strcpy(buffer,"garbage");
r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
ok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
r = IShellLinkA_SetPath(sl, "");
ok(r==S_OK, "SetPath failed (0x%08lx)\n", r);
strcpy(buffer,"garbage");
r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
ok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
str="c:\\nonexistent\\file";
r = IShellLinkA_SetPath(sl, str);
ok(r==S_FALSE, "SetPath failed (0x%08lx)\n", r);
strcpy(buffer,"garbage");
r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
ok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
ok(lstrcmpi(buffer,str)==0, "GetPath returned '%s'\n", buffer);
/* Get some a real path to play with */
r=GetModuleFileName(NULL, mypath, sizeof(mypath));
ok(r>=0 && r<sizeof(mypath), "GetModuleFileName failed (%ld)\n", r);
/* Test the interaction of SetPath and SetIDList */
tmp_pidl=NULL;
r = IShellLinkA_GetIDList(sl, &tmp_pidl);
ok(SUCCEEDED(r), "GetIDList failed (0x%08lx)\n", r);
if (SUCCEEDED(r))
{
strcpy(buffer,"garbage");
r=SHGetPathFromIDListA(tmp_pidl, buffer);
todo_wine {
ok(r, "SHGetPathFromIDListA failed\n");
}
if (r)
ok(lstrcmpi(buffer,str)==0, "GetIDList returned '%s'\n", buffer);
}
pidl=path_to_pidl(mypath);
todo_wine {
ok(pidl!=NULL, "path_to_pidl returned a NULL pidl\n");
}
if (pidl)
{
r = IShellLinkA_SetIDList(sl, pidl);
ok(SUCCEEDED(r), "SetIDList failed (0x%08lx)\n", r);
tmp_pidl=NULL;
r = IShellLinkA_GetIDList(sl, &tmp_pidl);
ok(SUCCEEDED(r), "GetIDList failed (0x%08lx)\n", r);
ok(tmp_pidl && ILIsEqual(pidl, tmp_pidl),
"GetIDList returned an incorrect pidl\n");
/* tmp_pidl is owned by IShellLink so we don't free it */
ILFree(pidl);
strcpy(buffer,"garbage");
r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
ok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
ok(lstrcmpi(buffer, mypath)==0, "GetPath returned '%s'\n", buffer);
}
/* Test Getting / Setting the arguments */
strcpy(buffer,"garbage");
r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
ok(SUCCEEDED(r), "GetArguments failed (0x%08lx)\n", r);
ok(*buffer=='\0', "GetArguments returned '%s'\n", buffer);
str="param1 \"spaced param2\"";
r = IShellLinkA_SetArguments(sl, str);
ok(SUCCEEDED(r), "SetArguments failed (0x%08lx)\n", r);
strcpy(buffer,"garbage");
r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
ok(SUCCEEDED(r), "GetArguments failed (0x%08lx)\n", r);
ok(lstrcmp(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
/* Test Getting / Setting showcmd */
i=0xdeadbeef;
r = IShellLinkA_GetShowCmd(sl, &i);
ok(SUCCEEDED(r), "GetShowCmd failed (0x%08lx)\n", r);
ok(i==SW_SHOWNORMAL, "GetShowCmd returned %d\n", i);
r = IShellLinkA_SetShowCmd(sl, SW_SHOWMAXIMIZED);
ok(SUCCEEDED(r), "SetShowCmd failed (0x%08lx)\n", r);
i=0xdeadbeef;
r = IShellLinkA_GetShowCmd(sl, &i);
ok(SUCCEEDED(r), "GetShowCmd failed (0x%08lx)\n", r);
ok(i==SW_SHOWMAXIMIZED, "GetShowCmd returned %d'\n", i);
/* Test Getting / Setting the icon */
i=0xdeadbeef;
strcpy(buffer,"garbage");
r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
todo_wine {
ok(SUCCEEDED(r), "GetIconLocation failed (0x%08lx)\n", r);
}
ok(*buffer=='\0', "GetIconLocation returned '%s'\n", buffer);
ok(i==0, "GetIconLocation returned %d\n", i);
str="c:\\nonexistent\\file";
r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
ok(SUCCEEDED(r), "SetIconLocation failed (0x%08lx)\n", r);
i=0xdeadbeef;
r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
ok(SUCCEEDED(r), "GetIconLocation failed (0x%08lx)\n", r);
ok(lstrcmpi(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
ok(i==0xbabecafe, "GetIconLocation returned %d'\n", i);
/* Test Getting / Setting the hot key */
w=0xbeef;
r = IShellLinkA_GetHotkey(sl, &w);
ok(SUCCEEDED(r), "GetHotkey failed (0x%08lx)\n", r);
ok(w==0, "GetHotkey returned %d\n", w);
r = IShellLinkA_SetHotkey(sl, 0x5678);
ok(SUCCEEDED(r), "SetHotkey failed (0x%08lx)\n", r);
w=0xbeef;
r = IShellLinkA_GetHotkey(sl, &w);
ok(SUCCEEDED(r), "GetHotkey failed (0x%08lx)\n", r);
ok(w==0x5678, "GetHotkey returned %d'\n", w);
IShellLinkA_Release(sl);
}
/*
* Test saving and loading .lnk files
*/
#define lok ok_(__FILE__, line)
#define check_lnk(a,b) check_lnk_(__LINE__, (a), (b))
void create_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int save_fails)
{
HRESULT r;
IShellLinkA *sl;
IPersistFile *pf;
r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
&IID_IShellLinkA, (LPVOID*)&sl);
lok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08lx)\n", r);
if (!SUCCEEDED(r))
return;
if (desc->description)
{
r = IShellLinkA_SetDescription(sl, desc->description);
lok(SUCCEEDED(r), "SetDescription failed (0x%08lx)\n", r);
}
if (desc->workdir)
{
r = IShellLinkA_SetWorkingDirectory(sl, desc->workdir);
lok(SUCCEEDED(r), "SetWorkingDirectory failed (0x%08lx)\n", r);
}
if (desc->path)
{
r = IShellLinkA_SetPath(sl, desc->path);
lok(SUCCEEDED(r), "SetPath failed (0x%08lx)\n", r);
}
if (desc->pidl)
{
r = IShellLinkA_SetIDList(sl, desc->pidl);
lok(SUCCEEDED(r), "SetIDList failed (0x%08lx)\n", r);
}
if (desc->arguments)
{
r = IShellLinkA_SetArguments(sl, desc->arguments);
lok(SUCCEEDED(r), "SetArguments failed (0x%08lx)\n", r);
}
if (desc->showcmd)
{
r = IShellLinkA_SetShowCmd(sl, desc->showcmd);
lok(SUCCEEDED(r), "SetShowCmd failed (0x%08lx)\n", r);
}
if (desc->icon)
{
r = IShellLinkA_SetIconLocation(sl, desc->icon, desc->icon_id);
lok(SUCCEEDED(r), "SetIconLocation failed (0x%08lx)\n", r);
}
if (desc->hotkey)
{
r = IShellLinkA_SetHotkey(sl, desc->hotkey);
lok(SUCCEEDED(r), "SetHotkey failed (0x%08lx)\n", r);
}
r = IShellLinkW_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
lok(SUCCEEDED(r), "no IID_IPersistFile (0x%08lx)\n", r);
if (SUCCEEDED(r))
{
r = IPersistFile_Save(pf, path, TRUE);
if (save_fails)
{
todo_wine {
lok(SUCCEEDED(r), "save failed (0x%08lx)\n", r);
}
}
else
{
lok(SUCCEEDED(r), "save failed (0x%08lx)\n", r);
}
IPersistFile_Release(pf);
}
IShellLinkA_Release(sl);
}
static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
{
HRESULT r;
IShellLinkA *sl;
IPersistFile *pf;
char buffer[INFOTIPSIZE];
r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
&IID_IShellLinkA, (LPVOID*)&sl);
lok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08lx)\n", r);
if (!SUCCEEDED(r))
return;
r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
lok(SUCCEEDED(r), "no IID_IPersistFile (0x%08lx)\n", r);
if (!SUCCEEDED(r))
{
IShellLinkA_Release(sl);
return;
}
r = IPersistFile_Load(pf, path, STGM_READ);
lok(SUCCEEDED(r), "load failed (0x%08lx)\n", r);
IPersistFile_Release(pf);
if (!SUCCEEDED(r))
{
IShellLinkA_Release(sl);
return;
}
if (desc->description)
{
strcpy(buffer,"garbage");
r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
lok(SUCCEEDED(r), "GetDescription failed (0x%08lx)\n", r);
lok(lstrcmp(buffer, desc->description)==0,
"GetDescription returned '%s' instead of '%s'\n",
buffer, desc->description);
}
if (desc->workdir)
{
strcpy(buffer,"garbage");
r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
lok(SUCCEEDED(r), "GetWorkingDirectory failed (0x%08lx)\n", r);
lok(lstrcmpi(buffer, desc->workdir)==0,
"GetWorkingDirectory returned '%s' instead of '%s'\n",
buffer, desc->workdir);
}
if (desc->path)
{
strcpy(buffer,"garbage");
r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
lok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
lok(lstrcmpi(buffer, desc->path)==0,
"GetPath returned '%s' instead of '%s'\n",
buffer, desc->path);
}
if (desc->pidl)
{
LPITEMIDLIST pidl=NULL;
r = IShellLinkA_GetIDList(sl, &pidl);
lok(SUCCEEDED(r), "GetIDList failed (0x%08lx)\n", r);
lok(ILIsEqual(pidl, desc->pidl),
"GetIDList returned an incorrect pidl\n");
}
if (desc->showcmd)
{
int i=0xdeadbeef;
r = IShellLinkA_GetShowCmd(sl, &i);
lok(SUCCEEDED(r), "GetShowCmd failed (0x%08lx)\n", r);
lok(i==desc->showcmd,
"GetShowCmd returned 0x%0x instead of 0x%0x\n",
i, desc->showcmd);
}
if (desc->icon)
{
int i=0xdeadbeef;
strcpy(buffer,"garbage");
r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
lok(SUCCEEDED(r), "GetIconLocation failed (0x%08lx)\n", r);
lok(lstrcmpi(buffer, desc->icon)==0,
"GetIconLocation returned '%s' instead of '%s'\n",
buffer, desc->icon);
lok(i==desc->icon_id,
"GetIconLocation returned 0x%0x instead of 0x%0x\n",
i, desc->icon_id);
}
if (desc->hotkey)
{
WORD i=0xbeef;
r = IShellLinkA_GetHotkey(sl, &i);
lok(SUCCEEDED(r), "GetHotkey failed (0x%08lx)\n", r);
lok(i==desc->hotkey,
"GetHotkey returned 0x%04x instead of 0x%04x\n",
i, desc->hotkey);
}
IShellLinkA_Release(sl);
}
static void test_load_save(void)
{
lnk_desc_t desc;
char mypath[MAX_PATH];
char mydir[MAX_PATH];
char* p;
DWORD r;
/* Save an empty .lnk file */
memset(&desc, 0, sizeof(desc));
create_lnk(lnkfile, &desc, 0);
/* It should come back as a bunch of empty strings */
desc.description="";
desc.workdir="";
desc.path="";
desc.arguments="";
desc.icon="";
check_lnk(lnkfile, &desc);
/* Point a .lnk file to nonexistent files */
desc.description="";
desc.workdir="c:\\Nonexitent\\work\\directory";
desc.path="c:\\nonexistent\\path";
desc.pidl=NULL;
desc.arguments="";
desc.showcmd=0;
desc.icon="c:\\nonexistent\\icon\\file";
desc.icon_id=1234;
desc.hotkey=0;
create_lnk(lnkfile, &desc, 0);
check_lnk(lnkfile, &desc);
r=GetModuleFileName(NULL, mypath, sizeof(mypath));
ok(r>=0 && r<sizeof(mypath), "GetModuleFileName failed (%ld)\n", r);
strcpy(mydir, mypath);
p=strrchr(mydir, '\\');
if (p)
*p='\0';
/* Overwrite the existing lnk file and point it to existing files */
desc.description="test 2";
desc.workdir=mydir;
desc.path=mypath;
desc.pidl=NULL;
desc.arguments="/option1 /option2 \"Some string\"";
desc.showcmd=SW_SHOWNORMAL;
desc.icon=mypath;
desc.icon_id=0;
desc.hotkey=0x1234;
create_lnk(lnkfile, &desc, 0);
check_lnk(lnkfile, &desc);
/* FIXME: Also test saving a .lnk pointing to a pidl that cannot be
* represented as a path.
*/
/* DeleteFileW is not implemented on Win9x */
r=DeleteFileA("c:\\test.lnk");
ok(r, "failed to delete link (%ld)\n", GetLastError());
}
START_TEST(shelllink)
{
HRESULT r;
r = CoInitialize(NULL);
ok(SUCCEEDED(r), "CoInitialize failed (0x%08lx)\n", r);
if (!SUCCEEDED(r))
return;
test_get_set();
test_load_save();
CoUninitialize();
}

View file

@ -0,0 +1,906 @@
/*
* Unit tests for shell32 SHGet{Special}Folder{Path|Location} functions.
*
* Copyright 2004 Juan Lang
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* This is a test program for the SHGet{Special}Folder{Path|Location} functions
* of shell32, that get either a filesytem path or a LPITEMIDLIST (shell
* namespace) path for a given folder (CSIDL value).
*
* FIXME:
* - Need to verify on more systems.
*/
#define COBJMACROS
#include <stdarg.h>
#include <stdio.h>
#include "windef.h"
#include "winbase.h"
#include "initguid.h"
#include "shlguid.h"
#include "shlobj.h"
#include "shlwapi.h"
#include "wine/test.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) ( sizeof(x) / sizeof((x)[0]) )
#endif
/* from pidl.h, not included here: */
#ifndef PT_GUID
#define PT_GUID 0x1f /* no path */
#endif
#ifndef PT_DRIVE
#define PT_DRIVE 0x23 /* has path */
#endif
#ifndef PT_DRIVE2
#define PT_DRIVE2 0x25 /* has path */
#endif
#ifndef PT_SHELLEXT
#define PT_SHELLEXT 0x2e /* no path */
#endif
#ifndef PT_FOLDER
#define PT_FOLDER 0x31 /* has path */
#endif
#ifndef PT_WORKGRP
#define PT_WORKGRP 0x41 /* no path */
#endif
#ifndef PT_YAGUID
#define PT_YAGUID 0x70 /* no path */
#endif
/* FIXME: this is used for history/favorites folders; what's a better name? */
#ifndef PT_IESPECIAL2
#define PT_IESPECIAL2 0xb1 /* has path */
#endif
static GUID CLSID_CommonDocuments = { 0x0000000c, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x1a } };
struct shellExpectedValues {
int folder;
BYTE pidlType;
};
static HMODULE hShell32;
static HRESULT (WINAPI *pSHGetFolderPathA)(HWND, int, HANDLE, DWORD, LPSTR);
static HRESULT (WINAPI *pSHGetFolderLocation)(HWND, int, HANDLE, DWORD,
LPITEMIDLIST *);
static BOOL (WINAPI *pSHGetSpecialFolderPathA)(HWND, LPSTR, int, BOOL);
static HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
static LPITEMIDLIST (WINAPI *pILFindLastID)(LPCITEMIDLIST);
static int (WINAPI *pSHFileOperationA)(LPSHFILEOPSTRUCTA);
static HRESULT (WINAPI *pSHGetMalloc)(LPMALLOC *);
static DLLVERSIONINFO shellVersion = { 0 };
static LPMALLOC pMalloc;
static const struct shellExpectedValues requiredShellValues[] = {
{ CSIDL_BITBUCKET, PT_GUID },
{ CSIDL_CONTROLS, PT_SHELLEXT },
{ CSIDL_COOKIES, PT_FOLDER },
{ CSIDL_DESKTOPDIRECTORY, PT_FOLDER },
{ CSIDL_DRIVES, PT_GUID },
{ CSIDL_FAVORITES, PT_FOLDER },
{ CSIDL_FONTS, PT_FOLDER },
/* FIXME: the following fails in Wine, returns type PT_FOLDER
{ CSIDL_HISTORY, PT_IESPECIAL2 },
*/
{ CSIDL_INTERNET, PT_GUID },
{ CSIDL_NETHOOD, PT_FOLDER },
{ CSIDL_NETWORK, PT_GUID },
{ CSIDL_PRINTERS, PT_YAGUID },
{ CSIDL_PRINTHOOD, PT_FOLDER },
{ CSIDL_PROGRAMS, PT_FOLDER },
{ CSIDL_RECENT, PT_FOLDER },
{ CSIDL_SENDTO, PT_FOLDER },
{ CSIDL_STARTMENU, PT_FOLDER },
{ CSIDL_STARTUP, PT_FOLDER },
{ CSIDL_TEMPLATES, PT_FOLDER },
};
static const struct shellExpectedValues optionalShellValues[] = {
/* FIXME: the following only semi-succeed; they return NULL PIDLs on XP.. hmm.
{ CSIDL_ALTSTARTUP, PT_FOLDER },
{ CSIDL_COMMON_ALTSTARTUP, PT_FOLDER },
{ CSIDL_COMMON_OEM_LINKS, PT_FOLDER },
*/
/* Windows NT-only: */
{ CSIDL_COMMON_DESKTOPDIRECTORY, PT_FOLDER },
{ CSIDL_COMMON_DOCUMENTS, PT_SHELLEXT },
{ CSIDL_COMMON_FAVORITES, PT_FOLDER },
{ CSIDL_COMMON_PROGRAMS, PT_FOLDER },
{ CSIDL_COMMON_STARTMENU, PT_FOLDER },
{ CSIDL_COMMON_STARTUP, PT_FOLDER },
{ CSIDL_COMMON_TEMPLATES, PT_FOLDER },
/* first appearing in shell32 version 4.71: */
{ CSIDL_APPDATA, PT_FOLDER },
/* first appearing in shell32 version 4.72: */
{ CSIDL_INTERNET_CACHE, PT_IESPECIAL2 },
/* first appearing in shell32 version 5.0: */
{ CSIDL_ADMINTOOLS, PT_FOLDER },
{ CSIDL_COMMON_APPDATA, PT_FOLDER },
{ CSIDL_LOCAL_APPDATA, PT_FOLDER },
{ CSIDL_MYDOCUMENTS, PT_FOLDER },
{ CSIDL_MYMUSIC, PT_FOLDER },
{ CSIDL_MYPICTURES, PT_FOLDER },
{ CSIDL_MYVIDEO, PT_FOLDER },
{ CSIDL_PROFILE, PT_FOLDER },
{ CSIDL_PROGRAM_FILES, PT_FOLDER },
{ CSIDL_PROGRAM_FILESX86, PT_FOLDER },
{ CSIDL_PROGRAM_FILES_COMMON, PT_FOLDER },
{ CSIDL_PROGRAM_FILES_COMMONX86, PT_FOLDER },
{ CSIDL_SYSTEM, PT_FOLDER },
{ CSIDL_WINDOWS, PT_FOLDER },
/* first appearing in shell32 6.0: */
{ CSIDL_CDBURN_AREA, PT_FOLDER },
{ CSIDL_COMMON_MUSIC, PT_FOLDER },
{ CSIDL_COMMON_PICTURES, PT_FOLDER },
{ CSIDL_COMMON_VIDEO, PT_FOLDER },
{ CSIDL_COMPUTERSNEARME, PT_WORKGRP },
{ CSIDL_RESOURCES, PT_FOLDER },
{ CSIDL_RESOURCES_LOCALIZED, PT_FOLDER },
};
static void loadShell32(void)
{
hShell32 = LoadLibraryA("shell32");
if (hShell32)
{
HRESULT (WINAPI *pDllGetVersion)(DLLVERSIONINFO *);
pSHGetFolderPathA = (void *)GetProcAddress(hShell32,
"SHGetFolderPathA");
pSHGetFolderLocation = (void *)GetProcAddress(hShell32,
"SHGetFolderLocation");
pSHGetSpecialFolderPathA = (void *)GetProcAddress(hShell32,
"SHGetSpecialFolderPathA");
pSHGetSpecialFolderLocation = (void *)GetProcAddress(hShell32,
"SHGetSpecialFolderLocation");
pDllGetVersion = (void *)GetProcAddress(hShell32, "DllGetVersion");
pILFindLastID = (void *)GetProcAddress(hShell32, "ILFindLastID");
if (!pILFindLastID)
pILFindLastID = (void *)GetProcAddress(hShell32, (LPCSTR)16);
pSHFileOperationA = (void *)GetProcAddress(hShell32,
"SHFileOperationA");
pSHGetMalloc = (void *)GetProcAddress(hShell32, "SHGetMalloc");
ok(pSHGetMalloc != NULL, "shell32 is missing SHGetMalloc\n");
if (pSHGetMalloc)
{
HRESULT hr = pSHGetMalloc(&pMalloc);
ok(SUCCEEDED(hr), "SHGetMalloc failed: 0x%08lx\n", hr);
ok(pMalloc != NULL, "SHGetMalloc returned a NULL IMalloc\n");
}
if (pDllGetVersion)
{
shellVersion.cbSize = sizeof(shellVersion);
pDllGetVersion(&shellVersion);
if (winetest_interactive)
printf("shell32 version is %ld.%ld\n",
shellVersion.dwMajorVersion, shellVersion.dwMinorVersion);
}
}
}
#ifndef CSIDL_PROFILES
#define CSIDL_PROFILES 0x003e
#endif
/* A couple utility printing functions */
static const char *getFolderName(int folder)
{
static char unknown[17];
#define CSIDL_TO_STR(x) case x: return#x;
switch (folder)
{
CSIDL_TO_STR(CSIDL_DESKTOP);
CSIDL_TO_STR(CSIDL_INTERNET);
CSIDL_TO_STR(CSIDL_PROGRAMS);
CSIDL_TO_STR(CSIDL_CONTROLS);
CSIDL_TO_STR(CSIDL_PRINTERS);
CSIDL_TO_STR(CSIDL_PERSONAL);
CSIDL_TO_STR(CSIDL_FAVORITES);
CSIDL_TO_STR(CSIDL_STARTUP);
CSIDL_TO_STR(CSIDL_RECENT);
CSIDL_TO_STR(CSIDL_SENDTO);
CSIDL_TO_STR(CSIDL_BITBUCKET);
CSIDL_TO_STR(CSIDL_STARTMENU);
CSIDL_TO_STR(CSIDL_MYDOCUMENTS);
CSIDL_TO_STR(CSIDL_MYMUSIC);
CSIDL_TO_STR(CSIDL_MYVIDEO);
CSIDL_TO_STR(CSIDL_DESKTOPDIRECTORY);
CSIDL_TO_STR(CSIDL_DRIVES);
CSIDL_TO_STR(CSIDL_NETWORK);
CSIDL_TO_STR(CSIDL_NETHOOD);
CSIDL_TO_STR(CSIDL_FONTS);
CSIDL_TO_STR(CSIDL_TEMPLATES);
CSIDL_TO_STR(CSIDL_COMMON_STARTMENU);
CSIDL_TO_STR(CSIDL_COMMON_PROGRAMS);
CSIDL_TO_STR(CSIDL_COMMON_STARTUP);
CSIDL_TO_STR(CSIDL_COMMON_DESKTOPDIRECTORY);
CSIDL_TO_STR(CSIDL_APPDATA);
CSIDL_TO_STR(CSIDL_PRINTHOOD);
CSIDL_TO_STR(CSIDL_LOCAL_APPDATA);
CSIDL_TO_STR(CSIDL_ALTSTARTUP);
CSIDL_TO_STR(CSIDL_COMMON_ALTSTARTUP);
CSIDL_TO_STR(CSIDL_COMMON_FAVORITES);
CSIDL_TO_STR(CSIDL_INTERNET_CACHE);
CSIDL_TO_STR(CSIDL_COOKIES);
CSIDL_TO_STR(CSIDL_HISTORY);
CSIDL_TO_STR(CSIDL_COMMON_APPDATA);
CSIDL_TO_STR(CSIDL_WINDOWS);
CSIDL_TO_STR(CSIDL_SYSTEM);
CSIDL_TO_STR(CSIDL_PROGRAM_FILES);
CSIDL_TO_STR(CSIDL_MYPICTURES);
CSIDL_TO_STR(CSIDL_PROFILE);
CSIDL_TO_STR(CSIDL_SYSTEMX86);
CSIDL_TO_STR(CSIDL_PROGRAM_FILESX86);
CSIDL_TO_STR(CSIDL_PROGRAM_FILES_COMMON);
CSIDL_TO_STR(CSIDL_PROGRAM_FILES_COMMONX86);
CSIDL_TO_STR(CSIDL_COMMON_TEMPLATES);
CSIDL_TO_STR(CSIDL_COMMON_DOCUMENTS);
CSIDL_TO_STR(CSIDL_COMMON_ADMINTOOLS);
CSIDL_TO_STR(CSIDL_ADMINTOOLS);
CSIDL_TO_STR(CSIDL_CONNECTIONS);
CSIDL_TO_STR(CSIDL_PROFILES);
CSIDL_TO_STR(CSIDL_COMMON_MUSIC);
CSIDL_TO_STR(CSIDL_COMMON_PICTURES);
CSIDL_TO_STR(CSIDL_COMMON_VIDEO);
CSIDL_TO_STR(CSIDL_RESOURCES);
CSIDL_TO_STR(CSIDL_RESOURCES_LOCALIZED);
CSIDL_TO_STR(CSIDL_COMMON_OEM_LINKS);
CSIDL_TO_STR(CSIDL_CDBURN_AREA);
CSIDL_TO_STR(CSIDL_COMPUTERSNEARME);
#undef CSIDL_TO_STR
default:
wnsprintfA(unknown, sizeof(unknown), "unknown (0x%04x)", folder);
return unknown;
}
}
static const char *printGUID(const GUID *guid)
{
static char guidSTR[39];
if (!guid) return NULL;
wnsprintfA(guidSTR, sizeof(guidSTR),
"{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
guid->Data1, guid->Data2, guid->Data3,
guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
return guidSTR;
}
static void testSHGetFolderLocationInvalidArgs(void)
{
LPITEMIDLIST pidl;
HRESULT hr;
if (!pSHGetFolderLocation) return;
/* check a bogus CSIDL: */
pidl = NULL;
hr = pSHGetFolderLocation(NULL, 0xeeee, NULL, 0, &pidl);
ok(hr == E_INVALIDARG,
"SHGetFolderLocation(NULL, 0xeeee, NULL, 0, &pidl)\n"
"returned 0x%08lx, expected E_INVALIDARG\n", hr);
if (SUCCEEDED(hr))
IMalloc_Free(pMalloc, pidl);
/* check a bogus user token: */
pidl = NULL;
hr = pSHGetFolderLocation(NULL, CSIDL_FAVORITES, (HANDLE)2, 0, &pidl);
ok(hr == E_FAIL,
"SHGetFolderLocation(NULL, CSIDL_FAVORITES, 2, 0, &pidl)\n"
"returned 0x%08lx, expected E_FAIL\n", hr);
if (SUCCEEDED(hr))
IMalloc_Free(pMalloc, pidl);
/* check reserved is not zero: */
pidl = NULL;
hr = pSHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, 1, &pidl);
ok(hr == E_INVALIDARG,
"SHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, 1, &pidl)\n"
"returned 0x%08lx, expected E_INVALIDARG\n", hr);
if (SUCCEEDED(hr))
IMalloc_Free(pMalloc, pidl);
/* a NULL pidl pointer crashes, so don't test it */
}
static void testSHGetSpecialFolderLocationInvalidArgs(void)
{
LPITEMIDLIST pidl = NULL;
HRESULT hr;
if (!pSHGetSpecialFolderLocation) return;
/* SHGetSpecialFolderLocation(NULL, 0, NULL) crashes */
hr = pSHGetSpecialFolderLocation(NULL, 0xeeee, &pidl);
ok(hr == E_INVALIDARG,
"SHGetSpecialFolderLocation(NULL, 0xeeee, &pidl) returned 0x%08lx, "
"expected E_INVALIDARG\n", hr);
}
static void testSHGetFolderPathInvalidArgs(void)
{
char path[MAX_PATH];
HRESULT hr;
if (!pSHGetFolderPathA) return;
/* expect 2's a bogus handle, especially since we didn't open it */
hr = pSHGetFolderPathA(NULL, CSIDL_DESKTOP, (HANDLE)2,
SHGFP_TYPE_DEFAULT, path);
ok(hr == E_FAIL,
"SHGetFolderPathA(NULL, CSIDL_DESKTOP, 2, SHGFP_TYPE_DEFAULT, path)\n"
"returned 0x%08lx, expected E_FAIL\n", hr);
hr = pSHGetFolderPathA(NULL, 0xeeee, NULL, SHGFP_TYPE_DEFAULT, path);
ok(hr == E_INVALIDARG,
"SHGetFolderPathA(NULL, 0xeeee, NULL, SHGFP_TYPE_DEFAULT, path)\n"
"returned 0x%08lx, expected E_INVALIDARG\n", hr);
}
static void testSHGetSpecialFolderPathInvalidArgs(void)
{
char path[MAX_PATH];
BOOL ret;
if (!pSHGetSpecialFolderPathA) return;
ret = pSHGetSpecialFolderPathA(NULL, NULL, CSIDL_BITBUCKET, FALSE);
ok(!ret,
"SHGetSpecialFolderPathA(NULL, NULL, CSIDL_BITBUCKET, FALSE)\n"
"returned TRUE, expected FALSE\n");
/* odd but true: calling with a NULL path still succeeds if it's a real
* dir
*/
ret = pSHGetSpecialFolderPathA(NULL, NULL, CSIDL_PROGRAMS, FALSE);
ok(ret,
"SHGetSpecialFolderPathA(NULL, NULL, CSIDL_PROGRAMS, FALSE)\n"
"returned FALSE, expected TRUE\n");
ret = pSHGetSpecialFolderPathA(NULL, path, 0xeeee, FALSE);
ok(!ret,
"SHGetSpecialFolderPathA(NULL, path, 0xeeee, FALSE)\n"
"returned TRUE, expected FALSE\n");
}
static void testApiParameters(void)
{
testSHGetFolderLocationInvalidArgs();
testSHGetSpecialFolderLocationInvalidArgs();
testSHGetFolderPathInvalidArgs();
testSHGetSpecialFolderPathInvalidArgs();
}
/* Returns the folder's PIDL type, or 0xff if one can't be found. */
static BYTE testSHGetFolderLocation(BOOL optional, int folder)
{
LPITEMIDLIST pidl;
HRESULT hr;
BYTE ret = 0xff;
/* treat absence of function as success */
if (!pSHGetFolderLocation) return TRUE;
pidl = NULL;
hr = pSHGetFolderLocation(NULL, folder, NULL, 0, &pidl);
ok(SUCCEEDED(hr) || optional,
"SHGetFolderLocation(NULL, %s, NULL, 0, &pidl)\n"
"failed: 0x%08lx\n", getFolderName(folder), hr);
if (SUCCEEDED(hr))
{
ok(pidl != NULL,
"SHGetFolderLocation(NULL, %s, NULL, 0, &pidl)\n"
"succeeded, but returned pidl is NULL\n", getFolderName(folder));
if (pidl)
{
LPITEMIDLIST pidlLast = pILFindLastID(pidl);
ok(pidlLast != NULL, "%s: ILFindLastID failed\n",
getFolderName(folder));
if (pidlLast)
ret = pidlLast->mkid.abID[0];
IMalloc_Free(pMalloc, pidl);
}
}
return ret;
}
/* Returns the folder's PIDL type, or 0xff if one can't be found. */
static BYTE testSHGetSpecialFolderLocation(BOOL optional, int folder)
{
LPITEMIDLIST pidl;
HRESULT hr;
BYTE ret = 0xff;
/* treat absence of function as success */
if (!pSHGetSpecialFolderLocation) return TRUE;
pidl = NULL;
hr = pSHGetSpecialFolderLocation(NULL, folder, &pidl);
ok(SUCCEEDED(hr) || optional,
"SHGetSpecialFolderLocation(NULL, %s, &pidl)\n"
"failed: 0x%08lx\n", getFolderName(folder), hr);
if (SUCCEEDED(hr))
{
ok(pidl != NULL,
"SHGetSpecialFolderLocation(NULL, %s, &pidl)\n"
"succeeded, but returned pidl is NULL\n", getFolderName(folder));
if (pidl)
{
LPITEMIDLIST pidlLast = pILFindLastID(pidl);
ok(pidlLast != NULL,
"%s: ILFindLastID failed\n", getFolderName(folder));
if (pidlLast)
ret = pidlLast->mkid.abID[0];
IMalloc_Free(pMalloc, pidl);
}
}
return ret;
}
static void testSHGetFolderPath(BOOL optional, int folder)
{
char path[MAX_PATH];
HRESULT hr;
if (!pSHGetFolderPathA) return;
hr = pSHGetFolderPathA(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path);
ok(SUCCEEDED(hr) || optional,
"SHGetFolderPathA(NULL, %s, NULL, SHGFP_TYPE_CURRENT, path)\n"
"failed: 0x%08lx\n", getFolderName(folder), hr);
}
static void testSHGetSpecialFolderPath(BOOL optional, int folder)
{
char path[MAX_PATH];
BOOL ret;
if (!pSHGetSpecialFolderPathA) return;
ret = pSHGetSpecialFolderPathA(NULL, path, folder, FALSE);
if (ret && winetest_interactive)
printf("%s: %s\n", getFolderName(folder), path);
ok(ret || optional,
"SHGetSpecialFolderPathA(NULL, path, %s, FALSE) failed\n",
getFolderName(folder));
}
static void testShellValues(const struct shellExpectedValues testEntries[],
int numEntries, BOOL optional)
{
int i;
for (i = 0; i < numEntries; i++)
{
BYTE type;
type = testSHGetFolderLocation(optional, testEntries[i].folder);
ok(type == testEntries[i].pidlType || optional,
"%s has type %d (0x%02x), expected %d (0x%02x)\n",
getFolderName(testEntries[i].folder), type, type,
testEntries[i].pidlType, testEntries[i].pidlType);
type = testSHGetSpecialFolderLocation(optional, testEntries[i].folder);
ok(type == testEntries[i].pidlType || optional,
"%s has type %d (0x%02x), expected %d (0x%02x)\n",
getFolderName(testEntries[i].folder), type, type,
testEntries[i].pidlType, testEntries[i].pidlType);
switch (type)
{
case PT_FOLDER:
case PT_DRIVE:
case PT_DRIVE2:
case PT_IESPECIAL2:
testSHGetFolderPath(optional, testEntries[i].folder);
testSHGetSpecialFolderPath(optional, testEntries[i].folder);
break;
}
}
}
/* Attempts to verify that the folder path corresponding to the folder CSIDL
* value has the same value as the environment variable with name envVar.
* Doesn't mind if SHGetSpecialFolderPath fails for folder or if envVar isn't
* set in this environment; different OS and shell version behave differently.
* However, if both are present, fails if envVar's value is not the same
* (byte-for-byte) as what SHGetSpecialFolderPath returns.
*/
static void matchSpecialFolderPathToEnv(int folder, const char *envVar)
{
char path[MAX_PATH];
if (!pSHGetSpecialFolderPathA) return;
if (pSHGetSpecialFolderPathA(NULL, path, folder, FALSE))
{
char *envVal = getenv(envVar);
ok(!envVal || !lstrcmpiA(envVal, path),
"%%%s%% does not match SHGetSpecialFolderPath:\n"
"%%%s%% is %s\nSHGetSpecialFolderPath returns %s\n",
envVar, envVar, envVal, path);
}
}
/* Attempts to match the GUID returned by SHGetFolderLocation for folder with
* GUID. Assumes the type of the returned PIDL is in fact a GUID, but doesn't
* fail if it isn't--that check should already have been done.
* Fails if the returned PIDL is a GUID whose value does not match guid.
*/
static void matchGUID(int folder, const GUID *guid)
{
LPITEMIDLIST pidl;
HRESULT hr;
if (!pSHGetFolderLocation) return;
if (!guid) return;
pidl = NULL;
hr = pSHGetFolderLocation(NULL, folder, NULL, 0, &pidl);
if (SUCCEEDED(hr))
{
LPITEMIDLIST pidlLast = pILFindLastID(pidl);
if (pidlLast && (pidlLast->mkid.abID[0] == PT_SHELLEXT ||
pidlLast->mkid.abID[0] == PT_GUID))
{
GUID *shellGuid = (GUID *)(pidlLast->mkid.abID + 2);
ok(IsEqualIID(shellGuid, guid),
"%s: got GUID %s, expected %s\n", getFolderName(folder),
printGUID(shellGuid), printGUID(guid));
}
IMalloc_Free(pMalloc, pidl);
}
}
static void testDesktop(void)
{
testSHGetFolderPath(FALSE, CSIDL_DESKTOP);
testSHGetSpecialFolderPath(FALSE, CSIDL_DESKTOP);
/* Test the desktop; even though SHITEMID should always contain abID of at
* least one type, when cb is 0 its value is undefined. So don't check
* what the returned type is, just make sure it exists.
*/
testSHGetFolderLocation(FALSE, CSIDL_DESKTOP);
testSHGetSpecialFolderLocation(FALSE, CSIDL_DESKTOP);
}
static void testPersonal(void)
{
BYTE type;
/* The pidl may be a real folder, or a virtual directory, or a drive if the
* home directory is set to the root directory of a drive.
*/
type = testSHGetFolderLocation(FALSE, CSIDL_PERSONAL);
ok(type == PT_FOLDER || type == PT_GUID || type == PT_DRIVE,
"CSIDL_PERSONAL returned invalid type 0x%02x, "
"expected PT_FOLDER or PT_GUID\n", type);
if (type == PT_FOLDER)
testSHGetFolderPath(FALSE, CSIDL_PERSONAL);
type = testSHGetSpecialFolderLocation(FALSE, CSIDL_PERSONAL);
ok(type == PT_FOLDER || type == PT_GUID || type == PT_DRIVE,
"CSIDL_PERSONAL returned invalid type 0x%02x, "
"expected PT_FOLDER or PT_GUID\n", type);
if (type == PT_FOLDER)
testSHGetSpecialFolderPath(FALSE, CSIDL_PERSONAL);
}
/* Checks the PIDL type of all the known values. */
static void testPidlTypes(void)
{
testDesktop();
testPersonal();
testShellValues(requiredShellValues, ARRAY_SIZE(requiredShellValues),
FALSE);
testShellValues(optionalShellValues, ARRAY_SIZE(optionalShellValues),
TRUE);
}
/* Verifies various shell virtual folders have the correct well-known GUIDs. */
static void testGUIDs(void)
{
matchGUID(CSIDL_BITBUCKET, &CLSID_RecycleBin);
matchGUID(CSIDL_CONTROLS, &CLSID_ControlPanel);
matchGUID(CSIDL_DRIVES, &CLSID_MyComputer);
matchGUID(CSIDL_INTERNET, &CLSID_Internet);
matchGUID(CSIDL_NETWORK, &CLSID_NetworkPlaces);
matchGUID(CSIDL_PERSONAL, &CLSID_MyDocuments);
matchGUID(CSIDL_COMMON_DOCUMENTS, &CLSID_CommonDocuments);
}
/* Verifies various shell paths match the environment variables to which they
* correspond.
*/
static void testEnvVars(void)
{
matchSpecialFolderPathToEnv(CSIDL_PROGRAM_FILES, "ProgramFiles");
matchSpecialFolderPathToEnv(CSIDL_APPDATA, "APPDATA");
matchSpecialFolderPathToEnv(CSIDL_PROFILE, "USERPROFILE");
matchSpecialFolderPathToEnv(CSIDL_WINDOWS, "SystemRoot");
matchSpecialFolderPathToEnv(CSIDL_WINDOWS, "windir");
matchSpecialFolderPathToEnv(CSIDL_PROGRAM_FILES_COMMON,
"CommonProgramFiles");
/* this is only set on Wine, but can't hurt to verify it: */
matchSpecialFolderPathToEnv(CSIDL_SYSTEM, "winsysdir");
}
/* Verifies the shell path for CSIDL_WINDOWS matches the return from
* GetWindowsDirectory. If SHGetSpecialFolderPath fails, no harm, no foul--not
* every shell32 version supports CSIDL_WINDOWS.
*/
static void testWinDir(void)
{
char windowsShellPath[MAX_PATH], windowsDir[MAX_PATH] = { 0 };
if (!pSHGetSpecialFolderPathA) return;
if (pSHGetSpecialFolderPathA(NULL, windowsShellPath, CSIDL_WINDOWS, FALSE))
{
PathRemoveBackslashA(windowsShellPath);
GetWindowsDirectoryA(windowsDir, sizeof(windowsDir));
PathRemoveBackslashA(windowsDir);
ok(!lstrcmpiA(windowsDir, windowsShellPath),
"GetWindowsDirectory does not match SHGetSpecialFolderPath:\n"
"GetWindowsDirectory returns %s\nSHGetSpecialFolderPath returns %s\n",
windowsDir, windowsShellPath);
}
}
/* Verifies the shell path for CSIDL_SYSTEM and CSIDL_SYSTEMX86 matches the
* return from GetSystemDirectory. If SHGetSpecialFolderPath fails, no harm,
* no foul--not every shell32 version supports CSIDL_SYSTEM.
*/
static void testSystemDir(void)
{
char systemShellPath[MAX_PATH], systemDir[MAX_PATH] = { 0 };
if (!pSHGetSpecialFolderPathA) return;
GetSystemDirectoryA(systemDir, sizeof(systemDir));
PathRemoveBackslashA(systemDir);
if (pSHGetSpecialFolderPathA(NULL, systemShellPath, CSIDL_SYSTEM, FALSE))
{
PathRemoveBackslashA(systemShellPath);
ok(!lstrcmpiA(systemDir, systemShellPath),
"GetSystemDirectory does not match SHGetSpecialFolderPath:\n"
"GetSystemDirectory returns %s\nSHGetSpecialFolderPath returns %s\n",
systemDir, systemShellPath);
}
/* check CSIDL_SYSTEMX86; note that this isn't always present, so don't
* worry if it fails
*/
if (pSHGetSpecialFolderPathA(NULL, systemShellPath, CSIDL_SYSTEMX86, FALSE))
{
PathRemoveBackslashA(systemShellPath);
ok(!lstrcmpiA(systemDir, systemShellPath),
"GetSystemDirectory does not match SHGetSpecialFolderPath:\n"
"GetSystemDirectory returns %s\nSHGetSpecialFolderPath returns %s\n",
systemDir, systemShellPath);
}
}
/* Globals used by subprocesses */
static int myARGC;
static char **myARGV;
static char base[MAX_PATH];
static char selfname[MAX_PATH];
static int init(void)
{
myARGC = winetest_get_mainargs(&myARGV);
if (!GetCurrentDirectoryA(sizeof(base), base)) return 0;
strcpy(selfname, myARGV[0]);
return 1;
}
/* Subprocess helper 1: test what happens when CSIDL_FAVORITES is set to a
* nonexistent directory.
*/
static void testNonExistentPath1(void)
{
HRESULT hr;
LPITEMIDLIST pidl;
char path[MAX_PATH];
/* test some failure cases first: */
hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES, NULL,
SHGFP_TYPE_CURRENT, NULL);
ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
"SHGetFolderPath returned 0x%08lx, expected 0x80070002\n", hr);
pidl = NULL;
hr = pSHGetFolderLocation(NULL, CSIDL_FAVORITES, NULL, 0,
&pidl);
ok(hr == E_FAIL,
"SHGetFolderLocation returned 0x%08lx, expected E_FAIL\n", hr);
if (SUCCEEDED(hr) && pidl)
IMalloc_Free(pMalloc, pidl);
ok(!pSHGetSpecialFolderPathA(NULL, path, CSIDL_FAVORITES, FALSE),
"SHGetSpecialFolderPath succeeded, expected failure\n");
pidl = NULL;
hr = pSHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl);
ok(hr == E_FAIL, "SHGetFolderLocation returned 0x%08lx, expected E_FAIL\n",
hr);
if (SUCCEEDED(hr) && pidl)
IMalloc_Free(pMalloc, pidl);
/* now test success: */
hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, NULL,
SHGFP_TYPE_CURRENT, path);
if (SUCCEEDED(hr))
{
BOOL ret;
if (winetest_interactive)
printf("CSIDL_FAVORITES was changed to %s\n", path);
ret = CreateDirectoryA(path, NULL);
ok(!ret,
"CreateDirectoryA succeeded but should have failed "
"with ERROR_ALREADY_EXISTS\n");
if (!ret)
ok(GetLastError() == ERROR_ALREADY_EXISTS,
"CreateDirectoryA failed with %ld, "
"expected ERROR_ALREADY_EXISTS\n",
GetLastError());
}
ok(SUCCEEDED(hr),
"SHGetFolderPath(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, "
"NULL, SHGFP_TYPE_CURRENT, path)\nfailed: 0x%08lx\n", hr);
}
/* Subprocess helper 2: make sure SHGetFolderPath still succeeds when the
* original value of CSIDL_FAVORITES is restored.
*/
static void testNonExistentPath2(void)
{
HRESULT hr;
hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, NULL,
SHGFP_TYPE_CURRENT, NULL);
ok(SUCCEEDED(hr), "SHGetFolderPath failed: 0x%08lx\n", hr);
}
static void doChild(const char *arg)
{
if (arg[0] == '1')
testNonExistentPath1();
else if (arg[0] == '2')
testNonExistentPath2();
}
/* Tests the return values from the various shell functions both with and
* without the use of the CSIDL_FLAG_CREATE flag. This flag only appeared in
* version 5 of the shell, so don't test unless it's at least version 5.
* The test reads a value from the registry, modifies it, calls
* SHGetFolderPath once with the CSIDL_FLAG_CREATE flag, and immediately
* afterward without it. Then it restores the registry and deletes the folder
* that was created.
* One oddity with respect to restoration: shell32 caches somehow, so it needs
* to be reloaded in order to see the correct (restored) value.
* Some APIs unrelated to the ones under test may fail, but I expect they're
* covered by other unit tests; I just print out something about failure to
* help trace what's going on.
*/
static void testNonExistentPath(void)
{
static const char userShellFolders[] =
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders";
char originalPath[MAX_PATH], modifiedPath[MAX_PATH];
HKEY key;
if (!pSHGetFolderPathA) return;
if (!pSHGetFolderLocation) return;
if (!pSHGetSpecialFolderPathA) return;
if (!pSHGetSpecialFolderLocation) return;
if (!pSHFileOperationA) return;
if (shellVersion.dwMajorVersion < 5) return;
if (!RegOpenKeyExA(HKEY_CURRENT_USER, userShellFolders, 0, KEY_ALL_ACCESS,
&key))
{
DWORD len, type;
len = sizeof(originalPath);
if (!RegQueryValueExA(key, "Favorites", NULL, &type,
(LPBYTE)&originalPath, &len))
{
size_t len = strlen(originalPath);
memcpy(modifiedPath, originalPath, len);
modifiedPath[len++] = '2';
modifiedPath[len++] = '\0';
if (winetest_interactive)
printf("Changing CSIDL_FAVORITES to %s\n", modifiedPath);
if (!RegSetValueExA(key, "Favorites", 0, type, (LPBYTE) modifiedPath, len))
{
char buffer[MAX_PATH];
STARTUPINFOA startup;
PROCESS_INFORMATION info;
HRESULT hr;
BOOL ret;
wnsprintfA(buffer, sizeof(buffer), "%s tests/shellpath.c 1",
selfname);
memset(&startup, 0, sizeof(startup));
startup.cb = sizeof(startup);
startup.dwFlags = STARTF_USESHOWWINDOW;
startup.dwFlags = SW_SHOWNORMAL;
CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL,
&startup, &info);
ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0,
"child process termination\n");
/* Query the path to be able to delete it below */
hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES, NULL,
SHGFP_TYPE_CURRENT, modifiedPath);
ok(SUCCEEDED(hr), "SHGetFolderPathA failed: 0x%08lx\n", hr);
/* restore original values: */
if (winetest_interactive)
printf("Restoring CSIDL_FAVORITES to %s\n", originalPath);
RegSetValueExA(key, "Favorites", 0, type, (LPBYTE) originalPath,
strlen(originalPath) + 1);
RegFlushKey(key);
wnsprintfA(buffer, sizeof(buffer), "%s tests/shellpath.c 2",
selfname);
memset(&startup, 0, sizeof(startup));
startup.cb = sizeof(startup);
startup.dwFlags = STARTF_USESHOWWINDOW;
startup.dwFlags = SW_SHOWNORMAL;
CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL,
&startup, &info);
ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0,
"child process termination\n");
ret = RemoveDirectoryA(modifiedPath);
ok( ret, "RemoveDirectoryA failed: %ld\n", GetLastError());
}
}
else if (winetest_interactive)
printf("RegQueryValueExA(key, Favorites, ...) failed\n");
if (key)
RegCloseKey(key);
}
else if (winetest_interactive)
printf("RegOpenKeyExA(HKEY_CURRENT_USER, %s, ...) failed\n",
userShellFolders);
}
START_TEST(shellpath)
{
if (!init()) return;
loadShell32();
if (!hShell32) return;
if (myARGC >= 3)
doChild(myARGV[2]);
else
{
/* first test various combinations of parameters: */
testApiParameters();
/* check known values: */
testPidlTypes();
testGUIDs();
testEnvVars();
testWinDir();
testSystemDir();
testNonExistentPath();
}
}

View file

@ -0,0 +1,462 @@
/*
* Unit test of the ShellExecute function.
*
* Copyright 2005 Francois Gouget for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* TODO:
* - test the default verb selection
* - test selection of an alternate class
* - try running executables in more ways
* - try passing arguments to executables
* - ShellExecute("foo.shlexec") with no path should work if foo.shlexec is
* in the PATH
* - test associations that use %l, %L or "%1" instead of %1
* - we may want to test ShellExecuteEx() instead of ShellExecute()
* and then we could also check its return value
* - ShellExecuteEx() also calls SetLastError() with meaningful values which
* we could check
*/
#include <stdio.h>
#include <assert.h>
#include "wtypes.h"
#include "winbase.h"
#include "windef.h"
#include "shellapi.h"
#include "shlwapi.h"
#include "wine/test.h"
#include "shell32_test.h"
static char argv0[MAX_PATH];
static int myARGC;
static char** myARGV;
static char tmpdir[MAX_PATH];
static const char* testfiles[]=
{
"%s\\test file.shlexec",
"%s\\test file.noassoc",
"%s\\test file.noassoc.shlexec",
"%s\\test file.shlexec.noassoc",
"%s\\test_shortcut_shlexec.lnk",
NULL
};
static void strcat_param(char* str, const char* param)
{
if (param!=NULL)
{
strcat(str, "\"");
strcat(str, param);
strcat(str, "\"");
}
else
{
strcat(str, "null");
}
}
static char shell_call[2048]="";
static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCSTR directory)
{
strcpy(shell_call, "ShellExecute(");
strcat_param(shell_call, operation);
strcat(shell_call, ", ");
strcat_param(shell_call, file);
strcat(shell_call, ", ");
strcat_param(shell_call, parameters);
strcat(shell_call, ", ");
strcat_param(shell_call, directory);
strcat(shell_call, ")");
if (winetest_debug > 1)
trace("%s\n", shell_call);
SetLastError(0xcafebabe);
/* FIXME: We cannot use ShellExecuteEx() here because if there is no
* association it displays the 'Open With' dialog and I could not find
* a flag to prevent this.
*/
return (int)ShellExecute(NULL, operation, file, parameters, directory,
SW_SHOWNORMAL);
}
static int shell_execute_ex(DWORD mask, LPCSTR operation, LPCSTR file,
LPCSTR parameters, LPCSTR directory)
{
SHELLEXECUTEINFO sei;
BOOL success;
int rc;
strcpy(shell_call, "ShellExecuteEx(");
strcat_param(shell_call, operation);
strcat(shell_call, ", ");
strcat_param(shell_call, file);
strcat(shell_call, ", ");
strcat_param(shell_call, parameters);
strcat(shell_call, ", ");
strcat_param(shell_call, directory);
strcat(shell_call, ")");
if (winetest_debug > 1)
trace("%s\n", shell_call);
sei.cbSize=sizeof(sei);
sei.fMask=mask;
sei.hwnd=NULL;
sei.lpVerb=operation;
sei.lpFile=file;
sei.lpParameters=parameters;
sei.lpDirectory=directory;
sei.nShow=SW_SHOWNORMAL;
sei.hInstApp=NULL; /* Out */
sei.lpIDList=NULL;
sei.lpClass=NULL;
sei.hkeyClass=NULL;
sei.dwHotKey=0;
sei.hIcon=NULL;
SetLastError(0xcafebabe);
success=ShellExecuteEx(&sei);
rc=(int)sei.hInstApp;
ok((success && rc >= 32) || (!success && rc < 32),
"%s rc=%d and hInstApp=%d is not allowed\n", shell_call, success, rc);
return rc;
}
static void create_test_association(const char* extension)
{
HKEY hkey, hkey_shell;
char class[MAX_PATH];
LONG rc;
sprintf(class, "shlexec%s", extension);
rc=RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, NULL, 0, KEY_SET_VALUE,
NULL, &hkey, NULL);
assert(rc==ERROR_SUCCESS);
rc=RegSetValueEx(hkey, NULL, 0, REG_SZ, class, strlen(class)+1);
assert(rc==ERROR_SUCCESS);
CloseHandle(hkey);
rc=RegCreateKeyEx(HKEY_CLASSES_ROOT, class, 0, NULL, 0,
KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS, NULL, &hkey, NULL);
assert(rc==ERROR_SUCCESS);
rc=RegCreateKeyEx(hkey, "shell", 0, NULL, 0,
KEY_CREATE_SUB_KEY, NULL, &hkey_shell, NULL);
assert(rc==ERROR_SUCCESS);
CloseHandle(hkey);
CloseHandle(hkey_shell);
}
static void delete_test_association(const char* extension)
{
char class[MAX_PATH];
sprintf(class, "shlexec%s", extension);
SHDeleteKey(HKEY_CLASSES_ROOT, class);
SHDeleteKey(HKEY_CLASSES_ROOT, extension);
}
static void create_test_verb(const char* extension, const char* verb)
{
HKEY hkey_shell, hkey_verb, hkey_cmd;
char shell[MAX_PATH];
char* cmd;
LONG rc;
sprintf(shell, "shlexec%s\\shell", extension);
rc=RegOpenKeyEx(HKEY_CLASSES_ROOT, shell, 0,
KEY_CREATE_SUB_KEY, &hkey_shell);
assert(rc==ERROR_SUCCESS);
rc=RegCreateKeyEx(hkey_shell, verb, 0, NULL, 0, KEY_CREATE_SUB_KEY,
NULL, &hkey_verb, NULL);
assert(rc==ERROR_SUCCESS);
rc=RegCreateKeyEx(hkey_verb, "command", 0, NULL, 0, KEY_SET_VALUE,
NULL, &hkey_cmd, NULL);
assert(rc==ERROR_SUCCESS);
cmd=malloc(strlen(argv0)+13+1);
sprintf(cmd,"%s shlexec \"%%1\"", argv0);
rc=RegSetValueEx(hkey_cmd, NULL, 0, REG_SZ, cmd, strlen(cmd)+1);
assert(rc==ERROR_SUCCESS);
free(cmd);
CloseHandle(hkey_shell);
CloseHandle(hkey_verb);
CloseHandle(hkey_cmd);
}
typedef struct
{
char* basename;
int rc;
int todo;
} filename_tests_t;
static filename_tests_t filename_tests[]=
{
/* Test bad / nonexistent filenames */
{"%s\\nonexistent.shlexec", ERROR_FILE_NOT_FOUND, 1},
{"%s\\nonexistent.noassoc", ERROR_FILE_NOT_FOUND, 1},
/* Standard tests */
{"%s\\test file.shlexec", 0, 0},
{"%s\\test file.shlexec.", 0, 0},
{"%s/test file.shlexec", 0, 0},
/* Test filenames with no association */
{"%s\\test file.noassoc", SE_ERR_NOASSOC, 0},
/* Test double extensions */
{"%s\\test file.noassoc.shlexec", 0, 0},
{"%s\\test file.shlexec.noassoc", SE_ERR_NOASSOC, 0},
/* Test shortcuts */
{"%s\\test_shortcut_shlexec.lnk", 0, 0},
{NULL, 0, 0}
};
static void test_filename()
{
char filename[MAX_PATH];
const filename_tests_t* test;
HMODULE hdll;
DLLVERSIONINFO dllver;
HRESULT (WINAPI *pDllGetVersion)(DLLVERSIONINFO*);
char* c;
int rc;
test=filename_tests;
while (test->basename)
{
sprintf(filename, test->basename, tmpdir);
if (strchr(filename, '/'))
{
c=filename;
while (*c)
{
if (*c=='\\')
*c='/';
c++;
}
}
rc=shell_execute(NULL, filename, NULL, NULL);
if (test->rc==0)
{
if (test->todo)
{
todo_wine
{
ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call,
rc, GetLastError());
}
}
else
{
ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call,
rc, GetLastError());
}
}
else
{
if (test->todo)
{
todo_wine
{
ok(rc==test->rc, "%s returned %d\n", shell_call, rc);
}
}
else
{
ok(rc==test->rc, "%s returned %d\n", shell_call, rc);
}
}
test++;
}
hdll=GetModuleHandleA("shell32.dll");
pDllGetVersion=(void*)GetProcAddress(hdll, "DllGetVersion");
if (pDllGetVersion)
{
dllver.cbSize=sizeof(dllver);
pDllGetVersion(&dllver);
trace("major=%ld minor=%ld build=%ld platform=%ld\n",
dllver.dwMajorVersion, dllver.dwMinorVersion,
dllver.dwBuildNumber, dllver.dwPlatformID);
/* The more recent versions of shell32.dll accept quoted filenames
* while older ones (e.g. 4.00) don't. Still we want to test this
* because IE 6 depends on the new behavior.
* One day we may need to check the exact version of the dll but for
* now making sure DllGetVersion() is present is sufficient.
*/
sprintf(filename, "\"%s\\test file.shlexec\"", tmpdir);
rc=shell_execute(NULL, filename, NULL, NULL);
ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call, rc,
GetLastError());
if (dllver.dwMajorVersion>=6)
{
/* Recent versions of shell32.dll accept '/'s in shortcut paths.
* Older versions don't or are quite buggy in this regard.
*/
sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
c=filename;
while (*c)
{
if (*c=='\\')
*c='/';
c++;
}
rc=shell_execute(NULL, filename, NULL, NULL);
todo_wine {
ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call, rc,
GetLastError());
}
}
}
}
static void test_exes()
{
char filename[MAX_PATH];
int rc;
/* We need NOZONECHECKS on Win2003 to block a dialog */
rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, "shlexec -nop",
NULL);
ok(rc>=32, "%s returned %d\n", shell_call, rc);
sprintf(filename, "%s\\test file.noassoc", tmpdir);
if (CopyFile(argv0, filename, FALSE))
{
rc=shell_execute(NULL, filename, "shlexec -nop", NULL);
todo_wine {
ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc);
}
}
}
static void init_test()
{
char filename[MAX_PATH];
WCHAR lnkfile[MAX_PATH];
const char* const * testfile;
lnk_desc_t desc;
DWORD rc;
HRESULT r;
r = CoInitialize(NULL);
ok(SUCCEEDED(r), "CoInitialize failed (0x%08lx)\n", r);
if (!SUCCEEDED(r))
exit(1);
rc=GetModuleFileName(NULL, argv0, sizeof(argv0));
assert(rc!=0 && rc<sizeof(argv0));
if (GetFileAttributes(argv0)==INVALID_FILE_ATTRIBUTES)
{
strcat(argv0, ".so");
ok(GetFileAttributes(argv0)!=INVALID_FILE_ATTRIBUTES,
"unable to find argv0!\n");
}
GetTempPathA(sizeof(tmpdir)/sizeof(*tmpdir), tmpdir);
/* Set up the test files */
testfile=testfiles;
while (*testfile)
{
HANDLE hfile;
sprintf(filename, *testfile, tmpdir);
hfile=CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hfile==INVALID_HANDLE_VALUE)
{
trace("unable to create '%s': err=%ld\n", filename, GetLastError());
assert(0);
}
CloseHandle(hfile);
testfile++;
}
/* Setup the test shortcuts */
sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
MultiByteToWideChar(CP_ACP, 0, filename, -1, lnkfile, sizeof(lnkfile)/sizeof(*lnkfile));
desc.description=NULL;
desc.workdir=NULL;
sprintf(filename, "%s\\test file.shlexec", tmpdir);
desc.path=filename;
desc.pidl=NULL;
desc.arguments="";
desc.showcmd=0;
desc.icon=NULL;
desc.icon_id=0;
desc.hotkey=0;
create_lnk(lnkfile, &desc, 0);
/* Create a basic association suitable for most tests */
create_test_association(".shlexec");
create_test_verb(".shlexec", "Open");
}
static void cleanup_test()
{
char filename[MAX_PATH];
const char* const * testfile;
/* Delete the test files */
testfile=testfiles;
while (*testfile)
{
sprintf(filename, *testfile, tmpdir);
DeleteFile(filename);
testfile++;
}
/* Delete the test association */
delete_test_association(".shlexec");
CoUninitialize();
}
START_TEST(shlexec)
{
myARGC = winetest_get_mainargs(&myARGV);
if (myARGC>=3)
{
/* FIXME: We should dump the parameters we got
* and have the parent verify them
*/
exit(0);
}
init_test();
test_filename();
test_exes();
cleanup_test();
}

View file

@ -0,0 +1,466 @@
/*
* Unit test of the SHFileOperation function.
*
* Copyright 2002 Andriy Palamarchuk
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdarg.h>
#include <stdio.h>
#define WINE_NOWINSOCK
#include "windef.h"
#include "winbase.h"
#include "wtypes.h"
#include "shellapi.h"
#include "shlobj.h"
#include "wine/test.h"
CHAR CURR_DIR[MAX_PATH];
static HMODULE hshell32;
static int (WINAPI *pSHCreateDirectoryExA)(HWND, LPCSTR, LPSECURITY_ATTRIBUTES);
static void InitFunctionPointers(void)
{
hshell32 = GetModuleHandleA("shell32.dll");
if(hshell32)
pSHCreateDirectoryExA = (void*)GetProcAddress(hshell32, "SHCreateDirectoryExA");
}
/* creates a file with the specified name for tests */
static void createTestFile(const CHAR *name)
{
HANDLE file;
DWORD written;
file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
WriteFile(file, name, strlen(name), &written, NULL);
WriteFile(file, "\n", strlen("\n"), &written, NULL);
CloseHandle(file);
}
static BOOL file_exists(const CHAR *name)
{
return GetFileAttributesA(name) != INVALID_FILE_ATTRIBUTES;
}
/* initializes the tests */
static void init_shfo_tests(void)
{
int len;
GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
len = lstrlenA(CURR_DIR);
if(len && (CURR_DIR[len-1] == '\\'))
CURR_DIR[len-1] = 0;
createTestFile(".\\test1.txt");
createTestFile(".\\test2.txt");
createTestFile(".\\test3.txt");
CreateDirectoryA(".\\test4.txt", NULL);
CreateDirectoryA(".\\testdir2", NULL);
}
/* cleans after tests */
static void clean_after_shfo_tests(void)
{
DeleteFileA(".\\test1.txt");
DeleteFileA(".\\test2.txt");
DeleteFileA(".\\test3.txt");
DeleteFileA(".\\test4.txt\\test1.txt");
DeleteFileA(".\\test4.txt\\test2.txt");
DeleteFileA(".\\test4.txt\\test3.txt");
RemoveDirectoryA(".\\test4.txt");
DeleteFileA(".\\testdir2\\test1.txt");
DeleteFileA(".\\testdir2\\test2.txt");
DeleteFileA(".\\testdir2\\test3.txt");
DeleteFileA(".\\testdir2\\test4.txt\\test1.txt");
RemoveDirectoryA(".\\testdir2\\test4.txt");
RemoveDirectoryA(".\\testdir2");
}
/*
puts into the specified buffer file names with current directory.
files - string with file names, separated by null characters. Ends on a double
null characters
*/
static void set_curr_dir_path(CHAR *buf, const CHAR* files)
{
buf[0] = 0;
while (files[0])
{
strcpy(buf, CURR_DIR);
buf += strlen(buf);
buf[0] = '\\';
buf++;
strcpy(buf, files);
buf += strlen(buf) + 1;
files += strlen(files) + 1;
}
buf[0] = 0;
}
/* tests the FO_DELETE action */
static void test_delete(void)
{
SHFILEOPSTRUCTA shfo;
DWORD ret;
CHAR buf[MAX_PATH];
sprintf(buf, "%s\\%s", CURR_DIR, "test?.txt");
buf[strlen(buf) + 1] = '\0';
shfo.hwnd = NULL;
shfo.wFunc = FO_DELETE;
shfo.pFrom = buf;
shfo.pTo = "\0";
shfo.fFlags = FOF_FILESONLY | FOF_NOCONFIRMATION | FOF_SILENT;
shfo.hNameMappings = NULL;
shfo.lpszProgressTitle = NULL;
ok(!SHFileOperationA(&shfo), "Deletion was successful\n");
ok(file_exists(".\\test4.txt"), "Directory should not be removed\n");
ok(!file_exists(".\\test1.txt"), "File should be removed\n");
ret = SHFileOperationA(&shfo);
ok(!ret, "Directory exists, but is not removed, ret=%ld\n", ret);
ok(file_exists(".\\test4.txt"), "Directory should not be removed\n");
shfo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
ok(!SHFileOperationA(&shfo), "Directory removed\n");
ok(!file_exists(".\\test4.txt"), "Directory should be removed\n");
ret = SHFileOperationA(&shfo);
ok(!ret, "The requested file does not exist, ret=%ld\n", ret);
init_shfo_tests();
sprintf(buf, "%s\\%s", CURR_DIR, "test4.txt");
buf[strlen(buf) + 1] = '\0';
ok(MoveFileA(".\\test1.txt", ".\\test4.txt\\test1.txt"), "Fill the subdirectory\n");
ok(!SHFileOperationA(&shfo), "Directory removed\n");
ok(!file_exists(".\\test4.txt"), "Directory is removed\n");
init_shfo_tests();
shfo.pFrom = ".\\test1.txt\0.\\test4.txt\0";
ok(!SHFileOperationA(&shfo), "Directory and a file removed\n");
ok(!file_exists(".\\test1.txt"), "The file should be removed\n");
ok(!file_exists(".\\test4.txt"), "Directory should be removed\n");
ok(file_exists(".\\test2.txt"), "This file should not be removed\n");
}
/* tests the FO_RENAME action */
static void test_rename(void)
{
SHFILEOPSTRUCTA shfo, shfo2;
CHAR from[MAX_PATH];
CHAR to[MAX_PATH];
DWORD retval;
shfo.hwnd = NULL;
shfo.wFunc = FO_RENAME;
shfo.pFrom = from;
shfo.pTo = to;
shfo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
shfo.hNameMappings = NULL;
shfo.lpszProgressTitle = NULL;
set_curr_dir_path(from, "test1.txt\0");
set_curr_dir_path(to, "test4.txt\0");
ok(SHFileOperationA(&shfo), "File is not renamed moving to other directory "
"when specifying directory name only\n");
ok(file_exists(".\\test1.txt"), "The file is removed\n");
set_curr_dir_path(from, "test3.txt\0");
set_curr_dir_path(to, "test4.txt\\test1.txt\0");
ok(!SHFileOperationA(&shfo), "File is renamed moving to other directory\n");
ok(file_exists(".\\test4.txt\\test1.txt"), "The file is not renamed\n");
set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
retval = SHFileOperationA(&shfo); /* W98 returns 0, W2K and newer returns ERROR_GEN_FAILURE, both do nothing */
ok(!retval || retval == ERROR_GEN_FAILURE || retval == ERROR_INVALID_TARGET_HANDLE,
"Can't rename many files, retval = %ld\n", retval);
ok(file_exists(".\\test1.txt"), "The file is renamed - many files are specified\n");
memcpy(&shfo2, &shfo, sizeof(SHFILEOPSTRUCTA));
shfo2.fFlags |= FOF_MULTIDESTFILES;
set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
retval = SHFileOperationA(&shfo2); /* W98 returns 0, W2K and newer returns ERROR_GEN_FAILURE, both do nothing */
ok(!retval || retval == ERROR_GEN_FAILURE || retval == ERROR_INVALID_TARGET_HANDLE,
"Can't rename many files, retval = %ld\n", retval);
ok(file_exists(".\\test1.txt"), "The file is not renamed - many files are specified\n");
set_curr_dir_path(from, "test1.txt\0");
set_curr_dir_path(to, "test6.txt\0");
retval = SHFileOperationA(&shfo);
ok(!retval, "Rename file failed, retval = %ld\n", retval);
ok(!file_exists(".\\test1.txt"), "The file is not renamed\n");
ok(file_exists(".\\test6.txt"), "The file is not renamed\n");
set_curr_dir_path(from, "test6.txt\0");
set_curr_dir_path(to, "test1.txt\0");
retval = SHFileOperationA(&shfo);
ok(!retval, "Rename file back failed, retval = %ld\n", retval);
set_curr_dir_path(from, "test4.txt\0");
set_curr_dir_path(to, "test6.txt\0");
retval = SHFileOperationA(&shfo);
ok(!retval, "Rename dir failed, retval = %ld\n", retval);
ok(!file_exists(".\\test4.txt"), "The dir is not renamed\n");
ok(file_exists(".\\test6.txt"), "The dir is not renamed\n");
set_curr_dir_path(from, "test6.txt\0");
set_curr_dir_path(to, "test4.txt\0");
retval = SHFileOperationA(&shfo);
ok(!retval, "Rename dir back failed, retval = %ld\n", retval);
}
/* tests the FO_COPY action */
static void test_copy(void)
{
SHFILEOPSTRUCTA shfo, shfo2;
CHAR from[MAX_PATH];
CHAR to[MAX_PATH];
FILEOP_FLAGS tmp_flags;
DWORD retval;
shfo.hwnd = NULL;
shfo.wFunc = FO_COPY;
shfo.pFrom = from;
shfo.pTo = to;
shfo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
shfo.hNameMappings = NULL;
shfo.lpszProgressTitle = NULL;
set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
ok(SHFileOperationA(&shfo), "Can't copy many files\n");
ok(!file_exists(".\\test6.txt"), "The file is not copied - many files are "
"specified as a target\n");
memcpy(&shfo2, &shfo, sizeof(SHFILEOPSTRUCTA));
shfo2.fFlags |= FOF_MULTIDESTFILES;
set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
ok(!SHFileOperationA(&shfo2), "Can't copy many files\n");
ok(file_exists(".\\test6.txt"), "The file is copied - many files are "
"specified as a target\n");
DeleteFileA(".\\test6.txt");
DeleteFileA(".\\test7.txt");
RemoveDirectoryA(".\\test8.txt");
/* number of sources do not correspond to number of targets */
set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
set_curr_dir_path(to, "test6.txt\0test7.txt\0");
ok(SHFileOperationA(&shfo2), "Can't copy many files\n");
ok(!file_exists(".\\test6.txt"), "The file is not copied - many files are "
"specified as a target\n");
set_curr_dir_path(from, "test1.txt\0");
set_curr_dir_path(to, "test4.txt\0");
ok(!SHFileOperationA(&shfo), "Prepare test to check how directories are copied recursively\n");
ok(file_exists(".\\test4.txt\\test1.txt"), "The file is copied\n");
set_curr_dir_path(from, "test?.txt\0");
set_curr_dir_path(to, "testdir2\0");
ok(!file_exists(".\\testdir2\\test1.txt"), "The file is not copied yet\n");
ok(!file_exists(".\\testdir2\\test4.txt"), "The directory is not copied yet\n");
ok(!SHFileOperationA(&shfo), "Files and directories are copied to directory\n");
ok(file_exists(".\\testdir2\\test1.txt"), "The file is copied\n");
ok(file_exists(".\\testdir2\\test4.txt"), "The directory is copied\n");
ok(file_exists(".\\testdir2\\test4.txt\\test1.txt"), "The file in subdirectory is copied\n");
clean_after_shfo_tests();
init_shfo_tests();
shfo.fFlags |= FOF_FILESONLY;
ok(!file_exists(".\\testdir2\\test1.txt"), "The file is not copied yet\n");
ok(!file_exists(".\\testdir2\\test4.txt"), "The directory is not copied yet\n");
ok(!SHFileOperationA(&shfo), "Files are copied to other directory\n");
ok(file_exists(".\\testdir2\\test1.txt"), "The file is copied\n");
ok(!file_exists(".\\testdir2\\test4.txt"), "The directory is copied\n");
clean_after_shfo_tests();
init_shfo_tests();
set_curr_dir_path(from, "test1.txt\0test2.txt\0");
ok(!file_exists(".\\testdir2\\test1.txt"), "The file is not copied yet\n");
ok(!file_exists(".\\testdir2\\test2.txt"), "The file is not copied yet\n");
ok(!SHFileOperationA(&shfo), "Files are copied to other directory \n");
ok(file_exists(".\\testdir2\\test1.txt"), "The file is copied\n");
ok(file_exists(".\\testdir2\\test2.txt"), "The file is copied\n");
clean_after_shfo_tests();
/* Copying multiple files with one not existing as source, fails the
entire operation in Win98/ME/2K/XP, but not in 95/NT */
init_shfo_tests();
tmp_flags = shfo.fFlags;
set_curr_dir_path(from, "test1.txt\0test10.txt\0test2.txt\0");
ok(!file_exists(".\\testdir2\\test1.txt"), "The file is not copied yet\n");
ok(!file_exists(".\\testdir2\\test2.txt"), "The file is not copied yet\n");
retval = SHFileOperationA(&shfo);
if (!retval)
/* Win 95/NT returns success but copies only the files up to the nonexistent source */
ok(file_exists(".\\testdir2\\test1.txt"), "The file is not copied\n");
else
{
/* Win 98/ME/2K/XP fail the entire operation with return code 1026 if one source file does not exist */
ok(retval == 1026, "Files are copied to other directory\n");
ok(!file_exists(".\\testdir2\\test1.txt"), "The file is copied\n");
}
ok(!file_exists(".\\testdir2\\test2.txt"), "The file is copied\n");
shfo.fFlags = tmp_flags;
}
/* tests the FO_MOVE action */
static void test_move(void)
{
SHFILEOPSTRUCTA shfo, shfo2;
CHAR from[MAX_PATH];
CHAR to[MAX_PATH];
shfo.hwnd = NULL;
shfo.wFunc = FO_MOVE;
shfo.pFrom = from;
shfo.pTo = to;
shfo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
shfo.hNameMappings = NULL;
shfo.lpszProgressTitle = NULL;
set_curr_dir_path(from, "test1.txt\0");
set_curr_dir_path(to, "test4.txt\0");
ok(!SHFileOperationA(&shfo), "Prepare test to check how directories are moved recursively\n");
ok(file_exists(".\\test4.txt\\test1.txt"), "The file is moved\n");
set_curr_dir_path(from, "test?.txt\0");
set_curr_dir_path(to, "testdir2\0");
ok(!file_exists(".\\testdir2\\test2.txt"), "The file is not moved yet\n");
ok(!file_exists(".\\testdir2\\test4.txt"), "The directory is not moved yet\n");
ok(!SHFileOperationA(&shfo), "Files and directories are moved to directory\n");
ok(file_exists(".\\testdir2\\test2.txt"), "The file is moved\n");
ok(file_exists(".\\testdir2\\test4.txt"), "The directory is moved\n");
ok(file_exists(".\\testdir2\\test4.txt\\test1.txt"), "The file in subdirectory is moved\n");
clean_after_shfo_tests();
init_shfo_tests();
memcpy(&shfo2, &shfo, sizeof(SHFILEOPSTRUCTA));
shfo2.fFlags |= FOF_MULTIDESTFILES;
set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
ok(!SHFileOperationA(&shfo2), "Move many files\n");
ok(file_exists(".\\test6.txt"), "The file is moved - many files are "
"specified as a target\n");
DeleteFileA(".\\test6.txt");
DeleteFileA(".\\test7.txt");
RemoveDirectoryA(".\\test8.txt");
init_shfo_tests();
/* number of sources do not correspond to number of targets */
set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
set_curr_dir_path(to, "test6.txt\0test7.txt\0");
ok(SHFileOperationA(&shfo2), "Can't move many files\n");
ok(!file_exists(".\\test6.txt"), "The file is not moved - many files are "
"specified as a target\n");
init_shfo_tests();
set_curr_dir_path(from, "test3.txt\0");
set_curr_dir_path(to, "test4.txt\\test1.txt\0");
ok(!SHFileOperationA(&shfo), "File is moved moving to other directory\n");
ok(file_exists(".\\test4.txt\\test1.txt"), "The file is moved\n");
set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
ok(SHFileOperationA(&shfo), "Cannot move many files\n");
ok(file_exists(".\\test1.txt"), "The file is not moved. Many files are specified\n");
ok(file_exists(".\\test4.txt"), "The directory is not moved. Many files are specified\n");
set_curr_dir_path(from, "test1.txt\0");
set_curr_dir_path(to, "test6.txt\0");
ok(!SHFileOperationA(&shfo), "Move file\n");
ok(!file_exists(".\\test1.txt"), "The file is moved\n");
ok(file_exists(".\\test6.txt"), "The file is moved\n");
set_curr_dir_path(from, "test6.txt\0");
set_curr_dir_path(to, "test1.txt\0");
ok(!SHFileOperationA(&shfo), "Move file back\n");
set_curr_dir_path(from, "test4.txt\0");
set_curr_dir_path(to, "test6.txt\0");
ok(!SHFileOperationA(&shfo), "Move dir\n");
ok(!file_exists(".\\test4.txt"), "The dir is moved\n");
ok(file_exists(".\\test6.txt"), "The dir is moved\n");
set_curr_dir_path(from, "test6.txt\0");
set_curr_dir_path(to, "test4.txt\0");
ok(!SHFileOperationA(&shfo), "Move dir back\n");
}
static void test_sh_create_dir(void)
{
CHAR path[MAX_PATH];
int ret;
if(!pSHCreateDirectoryExA)
{
trace("skipping SHCreateDirectoryExA tests\n");
return;
}
set_curr_dir_path(path, "testdir2\\test4.txt\0");
ret = pSHCreateDirectoryExA(NULL, path, NULL);
ok(ERROR_SUCCESS == ret, "SHCreateDirectoryEx failed to create directory recursively, ret = %d\n", ret);
ok(file_exists(".\\testdir2"), "The first directory is not created\n");
ok(file_exists(".\\testdir2\\test4.txt"), "The second directory is not created\n");
ret = pSHCreateDirectoryExA(NULL, path, NULL);
ok(ERROR_ALREADY_EXISTS == ret, "SHCreateDirectoryEx should fail to create existing directory, ret = %d\n", ret);
}
START_TEST(shlfileop)
{
InitFunctionPointers();
clean_after_shfo_tests();
init_shfo_tests();
test_delete();
clean_after_shfo_tests();
init_shfo_tests();
test_rename();
clean_after_shfo_tests();
init_shfo_tests();
test_copy();
clean_after_shfo_tests();
init_shfo_tests();
test_move();
clean_after_shfo_tests();
test_sh_create_dir();
clean_after_shfo_tests();
}

View file

@ -0,0 +1,677 @@
/*
* Unit test of the IShellFolder functions.
*
* Copyright 2004 Vitaliy Margolen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdarg.h>
#include <stdio.h>
#define COBJMACROS
#include "windef.h"
#include "winbase.h"
#include "wtypes.h"
#include "shellapi.h"
#include "shlguid.h"
#include "shlobj.h"
//#include "shobjidl.h"
#include "shlwapi.h"
#include "wine/unicode.h"
#include "wine/test.h"
static IMalloc *ppM;
static HRESULT (WINAPI *pSHBindToParent)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*);
static BOOL (WINAPI *pSHGetSpecialFolderPathW)(HWND, LPWSTR, int, BOOL);
static void init_function_pointers(void)
{
HMODULE hmod = GetModuleHandleA("shell32.dll");
HRESULT hr;
if(hmod)
{
pSHBindToParent = (void*)GetProcAddress(hmod, "SHBindToParent");
pSHGetSpecialFolderPathW = (void*)GetProcAddress(hmod, "SHGetSpecialFolderPathW");
}
hr = SHGetMalloc(&ppM);
ok(hr == S_OK, "SHGetMalloc failed %08lx\n", hr);
}
static void test_ParseDisplayName(void)
{
HRESULT hr;
IShellFolder *IDesktopFolder;
static const char *cNonExistDir1A = "c:\\nonexist_subdir";
static const char *cNonExistDir2A = "c:\\\\nonexist_subdir";
DWORD res;
WCHAR cTestDirW [MAX_PATH] = {0};
ITEMIDLIST *newPIDL;
hr = SHGetDesktopFolder(&IDesktopFolder);
if(hr != S_OK) return;
res = GetFileAttributesA(cNonExistDir1A);
if(res != INVALID_FILE_ATTRIBUTES) return;
MultiByteToWideChar(CP_ACP, 0, cNonExistDir1A, -1, cTestDirW, MAX_PATH);
hr = IShellFolder_ParseDisplayName(IDesktopFolder,
NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL),
"ParseDisplayName returned %08lx, expected 80070002 or E_FAIL\n", hr);
res = GetFileAttributesA(cNonExistDir2A);
if(res != INVALID_FILE_ATTRIBUTES) return;
MultiByteToWideChar(CP_ACP, 0, cNonExistDir2A, -1, cTestDirW, MAX_PATH);
hr = IShellFolder_ParseDisplayName(IDesktopFolder,
NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
ok((hr == E_FAIL), "ParseDisplayName returned %08lx, expected E_FAIL\n", hr);
}
/* creates a file with the specified name for tests */
static void CreateTestFile(const CHAR *name)
{
HANDLE file;
DWORD written;
file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
if (file != INVALID_HANDLE_VALUE)
{
WriteFile(file, name, strlen(name), &written, NULL);
WriteFile(file, "\n", strlen("\n"), &written, NULL);
CloseHandle(file);
}
}
/* initializes the tests */
static void CreateFilesFolders(void)
{
CreateDirectoryA(".\\testdir", NULL);
CreateDirectoryA(".\\testdir\\test.txt", NULL);
CreateTestFile (".\\testdir\\test1.txt ");
CreateTestFile (".\\testdir\\test2.txt ");
CreateTestFile (".\\testdir\\test3.txt ");
CreateDirectoryA(".\\testdir\\testdir2 ", NULL);
CreateDirectoryA(".\\testdir\\testdir2\\subdir", NULL);
}
/* cleans after tests */
static void Cleanup(void)
{
DeleteFileA(".\\testdir\\test1.txt");
DeleteFileA(".\\testdir\\test2.txt");
DeleteFileA(".\\testdir\\test3.txt");
RemoveDirectoryA(".\\testdir\\test.txt");
RemoveDirectoryA(".\\testdir\\testdir2\\subdir");
RemoveDirectoryA(".\\testdir\\testdir2");
RemoveDirectoryA(".\\testdir");
}
/* perform test */
static void test_EnumObjects(IShellFolder *iFolder)
{
IEnumIDList *iEnumList;
LPITEMIDLIST newPIDL, idlArr[10];
ULONG NumPIDLs;
int i=0, j;
HRESULT hr;
static const WORD iResults [5][5] =
{
{ 0,-1,-1,-1,-1},
{ 1, 0,-1,-1,-1},
{ 1, 1, 0,-1,-1},
{ 1, 1, 1, 0,-1},
{ 1, 1, 1, 1, 0}
};
/* Just test SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR for now */
static const ULONG attrs[5] =
{
SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
SFGAO_FILESYSTEM,
SFGAO_FILESYSTEM,
SFGAO_FILESYSTEM,
};
hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList);
ok(hr == S_OK, "EnumObjects failed %08lx\n", hr);
/* This is to show that, contrary to what is said on MSDN, on IEnumIDList::Next,
* the filesystem shellfolders return S_OK even if less than 'celt' items are
* returned (in contrast to S_FALSE). We have to do it in a loop since WinXP
* only ever returns a single entry per call. */
while (IEnumIDList_Next(iEnumList, 10-i, &idlArr[i], &NumPIDLs) == S_OK)
i += NumPIDLs;
ok (i == 5, "i: %d\n", i);
hr = IEnumIDList_Release(iEnumList);
ok(hr == S_OK, "IEnumIDList_Release failed %08lx\n", hr);
/* Sort them first in case of wrong order from system */
for (i=0;i<5;i++) for (j=0;j<5;j++)
if ((SHORT)IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]) < 0)
{
newPIDL = idlArr[i];
idlArr[i] = idlArr[j];
idlArr[j] = newPIDL;
}
for (i=0;i<5;i++) for (j=0;j<5;j++)
{
hr = IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]);
ok(hr == iResults[i][j], "Got %lx expected [%d]-[%d]=%x\n", hr, i, j, iResults[i][j]);
}
for (i = 0; i < 5; i++)
{
SFGAOF flags;
flags = SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR;
hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
flags &= SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR;
ok(hr == S_OK, "GetAttributesOf returns %08lx\n", hr);
ok(flags == attrs[i], "GetAttributesOf gets attrs %08lx, expects %08lx\n", flags, attrs[i]);
}
for (i=0;i<5;i++)
IMalloc_Free(ppM, idlArr[i]);
}
static void test_BindToObject(void)
{
HRESULT hr;
UINT cChars;
IShellFolder *psfDesktop, *psfChild, *psfMyComputer, *psfSystemDir;
SHITEMID emptyitem = { 0, { 0 } };
LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidlEmpty = (LPITEMIDLIST)&emptyitem;
WCHAR wszSystemDir[MAX_PATH];
WCHAR wszMyComputer[] = {
':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
/* The following tests shows that BindToObject should fail with E_INVALIDARG if called
* with an empty pidl. This is tested for Desktop, MyComputer and the FS ShellFolder
*/
hr = SHGetDesktopFolder(&psfDesktop);
ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
if (FAILED(hr)) return;
hr = IShellFolder_BindToObject(psfDesktop, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr);
hr = IShellFolder_BindToObject(psfDesktop, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr);
hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr);
if (FAILED(hr)) {
IShellFolder_Release(psfDesktop);
return;
}
hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08lx\n", hr);
IShellFolder_Release(psfDesktop);
IMalloc_Free(ppM, pidlMyComputer);
if (FAILED(hr)) return;
hr = IShellFolder_BindToObject(psfMyComputer, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr);
hr = IShellFolder_BindToObject(psfMyComputer, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr);
cChars = GetSystemDirectoryW(wszSystemDir, MAX_PATH);
ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryW failed! LastError: %08lx\n", GetLastError());
if (cChars == 0 || cChars >= MAX_PATH) {
IShellFolder_Release(psfMyComputer);
return;
}
hr = IShellFolder_ParseDisplayName(psfMyComputer, NULL, NULL, wszSystemDir, NULL, &pidlSystemDir, NULL);
ok (SUCCEEDED(hr), "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08lx\n", hr);
if (FAILED(hr)) {
IShellFolder_Release(psfMyComputer);
return;
}
hr = IShellFolder_BindToObject(psfMyComputer, pidlSystemDir, NULL, &IID_IShellFolder, (LPVOID*)&psfSystemDir);
ok (SUCCEEDED(hr), "MyComputer failed to bind to a FileSystem ShellFolder! hr = %08lx\n", hr);
IShellFolder_Release(psfMyComputer);
IMalloc_Free(ppM, pidlSystemDir);
if (FAILED(hr)) return;
hr = IShellFolder_BindToObject(psfSystemDir, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
ok (hr == E_INVALIDARG,
"FileSystem ShellFolder's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr);
hr = IShellFolder_BindToObject(psfSystemDir, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
ok (hr == E_INVALIDARG,
"FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr);
IShellFolder_Release(psfSystemDir);
}
static void test_GetDisplayName(void)
{
BOOL result;
HRESULT hr;
HANDLE hTestFile;
WCHAR wszTestFile[MAX_PATH], wszTestFile2[MAX_PATH], wszTestDir[MAX_PATH];
STRRET strret;
LPSHELLFOLDER psfDesktop, psfPersonal;
IUnknown *psfFile;
LPITEMIDLIST pidlTestFile;
LPCITEMIDLIST pidlLast;
static const WCHAR wszFileName[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
static const WCHAR wszDirName[] = { 'w','i','n','e','t','e','s','t',0 };
/* I'm trying to figure if there is a functional difference between calling
* SHGetPathFromIDList and calling GetDisplayNameOf(SHGDN_FORPARSING) after
* binding to the shellfolder. One thing I thought of was that perhaps
* SHGetPathFromIDList would be able to get the path to a file, which does
* not exist anymore, while the other method would'nt. It turns out there's
* no functional difference in this respect.
*/
if(!pSHGetSpecialFolderPathW) return;
/* First creating a directory in MyDocuments and a file in this directory. */
result = pSHGetSpecialFolderPathW(NULL, wszTestDir, CSIDL_PERSONAL, FALSE);
ok(result, "SHGetSpecialFolderPathW failed! Last error: %08lx\n", GetLastError());
if (!result) return;
PathAddBackslashW(wszTestDir);
lstrcatW(wszTestDir, wszDirName);
result = CreateDirectoryW(wszTestDir, NULL);
ok(result, "CreateDirectoryW failed! Last error: %08lx\n", GetLastError());
if (!result) return;
lstrcpyW(wszTestFile, wszTestDir);
PathAddBackslashW(wszTestFile);
lstrcatW(wszTestFile, wszFileName);
hTestFile = CreateFileW(wszTestFile, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %08lx\n", GetLastError());
if (hTestFile == INVALID_HANDLE_VALUE) return;
CloseHandle(hTestFile);
/* Getting an itemidlist for the file. */
hr = SHGetDesktopFolder(&psfDesktop);
ok(SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
if (FAILED(hr)) return;
hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
ok(SUCCEEDED(hr), "Desktop->ParseDisplayName failed! hr = %08lx\n", hr);
if (FAILED(hr)) {
IShellFolder_Release(psfDesktop);
return;
}
/* It seems as if we cannot bind to regular files on windows, but only directories.
*/
hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile);
todo_wine { ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hr = %08lx\n", hr); }
if (SUCCEEDED(hr)) {
IShellFolder_Release(psfFile);
}
/* Deleting the file and the directory */
DeleteFileW(wszTestFile);
RemoveDirectoryW(wszTestDir);
/* SHGetPathFromIDListW still works, although the file is not present anymore. */
result = SHGetPathFromIDListW(pidlTestFile, wszTestFile2);
ok (result, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError());
ok (!lstrcmpiW(wszTestFile, wszTestFile2), "SHGetPathFromIDListW returns incorrect path!\n");
if(!pSHBindToParent) return;
/* Binding to the folder and querying the display name of the file also works. */
hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
ok (SUCCEEDED(hr), "SHBindToParent failed! hr = %08lx\n", hr);
if (FAILED(hr)) {
IShellFolder_Release(psfDesktop);
return;
}
hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
ok (SUCCEEDED(hr), "Personal->GetDisplayNameOf failed! hr = %08lx\n", hr);
if (FAILED(hr)) {
IShellFolder_Release(psfDesktop);
IShellFolder_Release(psfPersonal);
return;
}
hr = StrRetToBufW(&strret, pidlLast, wszTestFile2, MAX_PATH);
ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08lx\n", hr);
ok (!lstrcmpiW(wszTestFile, wszTestFile2), "GetDisplayNameOf returns incorrect path!\n");
IShellFolder_Release(psfDesktop);
IShellFolder_Release(psfPersonal);
}
static void test_CallForAttributes(void)
{
HKEY hKey;
LONG lResult;
HRESULT hr;
DWORD dwSize;
LPSHELLFOLDER psfDesktop;
LPITEMIDLIST pidlMyDocuments;
DWORD dwAttributes, dwCallForAttributes, dwOrigAttributes, dwOrigCallForAttributes;
static const WCHAR wszAttributes[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
static const WCHAR wszCallForAttributes[] = {
'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
static const WCHAR wszMyDocumentsKey[] = {
'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
'1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
'\\','S','h','e','l','l','F','o','l','d','e','r',0 };
WCHAR wszMyDocuments[] = {
':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
'9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
/* For the root of a namespace extension, the attributes are not queried by binding
* to the object and calling GetAttributesOf. Instead, the attributes are read from
* the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
*
* The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
* value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
* the shell does bind to the folder object and calls GetAttributesOf. This is not documented
* on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
*/
hr = SHGetDesktopFolder(&psfDesktop);
ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
if (FAILED(hr)) return;
hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyDocuments, NULL,
&pidlMyDocuments, NULL);
ok (SUCCEEDED(hr),
"Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08lx\n", hr);
if (FAILED(hr)) {
IShellFolder_Release(psfDesktop);
return;
}
dwAttributes = 0xffffffff;
hr = IShellFolder_GetAttributesOf(psfDesktop, 1,
(LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08lx\n", hr);
/* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
todo_wine{ ok (dwAttributes & SFGAO_FILESYSTEM,
"SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n"); }
ok (!(dwAttributes & SFGAO_ISSLOW), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
ok (!(dwAttributes & SFGAO_GHOSTED), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
/* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
* key. So the test will return at this point, if run on wine.
*/
lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMyDocumentsKey, 0, KEY_WRITE|KEY_READ, &hKey);
todo_wine { ok (lResult == ERROR_SUCCESS, "RegOpenKeyEx failed! result: %08lx\n", lResult); }
if (lResult != ERROR_SUCCESS) {
IMalloc_Free(ppM, pidlMyDocuments);
IShellFolder_Release(psfDesktop);
return;
}
/* Query MyDocuments' Attributes value, to be able to restore it later. */
dwSize = sizeof(DWORD);
lResult = RegQueryValueExW(hKey, wszAttributes, NULL, NULL, (LPBYTE)&dwOrigAttributes, &dwSize);
ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08lx\n", lResult);
if (lResult != ERROR_SUCCESS) {
RegCloseKey(hKey);
IMalloc_Free(ppM, pidlMyDocuments);
IShellFolder_Release(psfDesktop);
return;
}
/* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
dwSize = sizeof(DWORD);
lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL,
(LPBYTE)&dwOrigCallForAttributes, &dwSize);
ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08lx\n", lResult);
if (lResult != ERROR_SUCCESS) {
RegCloseKey(hKey);
IMalloc_Free(ppM, pidlMyDocuments);
IShellFolder_Release(psfDesktop);
return;
}
/* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and
* SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
* SFGAO_FILESYSTEM attributes. */
dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED;
RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwAttributes, sizeof(DWORD));
dwCallForAttributes = SFGAO_ISSLOW|SFGAO_FILESYSTEM;
RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD,
(LPBYTE)&dwCallForAttributes, sizeof(DWORD));
/* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by
* GetAttributesOf. It seems that once there is a single attribute queried, for which
* CallForAttributes is set, all flags are taken from the GetAttributesOf call and
* the flags in Attributes are ignored.
*/
dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED|SFGAO_FILESYSTEM;
hr = IShellFolder_GetAttributesOf(psfDesktop, 1,
(LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08lx\n", hr);
if (SUCCEEDED(hr))
ok (dwAttributes == SFGAO_FILESYSTEM,
"Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08lx\n",
dwAttributes);
/* Restore MyDocuments' original Attributes and CallForAttributes registry values */
RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwOrigAttributes, sizeof(DWORD));
RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD,
(LPBYTE)&dwOrigCallForAttributes, sizeof(DWORD));
RegCloseKey(hKey);
IMalloc_Free(ppM, pidlMyDocuments);
IShellFolder_Release(psfDesktop);
}
static void test_GetAttributesOf(void)
{
HRESULT hr;
LPSHELLFOLDER psfDesktop, psfMyComputer;
SHITEMID emptyitem = { 0, { 0 } };
LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
LPITEMIDLIST pidlMyComputer;
DWORD dwFlags;
const static DWORD dwDesktopFlags = /* As observed on WinXP SP2 */
SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR |
SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER;
const static DWORD dwMyComputerFlags = /* As observed on WinXP SP2 */
SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET |
SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
WCHAR wszMyComputer[] = {
':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
hr = SHGetDesktopFolder(&psfDesktop);
ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
if (FAILED(hr)) return;
/* The Desktop attributes can be queried with a single empty itemidlist, .. */
dwFlags = 0xffffffff;
hr = IShellFolder_GetAttributesOf(psfDesktop, 1, &pidlEmpty, &dwFlags);
ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(empty pidl) failed! hr = %08lx\n", hr);
ok (dwFlags == dwDesktopFlags, "Wrong Desktop attributes: %08lx, expected: %08lx\n",
dwFlags, dwDesktopFlags);
/* .. or with no itemidlist at all. */
dwFlags = 0xffffffff;
hr = IShellFolder_GetAttributesOf(psfDesktop, 0, NULL, &dwFlags);
ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(NULL) failed! hr = %08lx\n", hr);
ok (dwFlags == dwDesktopFlags, "Wrong Desktop attributes: %08lx, expected: %08lx\n",
dwFlags, dwDesktopFlags);
/* Testing the attributes of the MyComputer shellfolder */
hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr);
if (FAILED(hr)) {
IShellFolder_Release(psfDesktop);
return;
}
/* WinXP SP2 sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop
* folder object. It doesn't do this, if MyComputer is queried directly (see below).
* SFGAO_CANLINK is the same as DROPEFFECT_LINK, which MSDN says means: "Drag source
* should create a link to the original data". You can't create links on MyComputer on
* Windows, so this flag shouldn't be set. Seems like a bug in Windows. As long as nobody
* depends on this bug, we probably shouldn't imitate it.
*/
dwFlags = 0xffffffff;
hr = IShellFolder_GetAttributesOf(psfDesktop, 1, (LPCITEMIDLIST*)&pidlMyComputer, &dwFlags);
ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyComputer) failed! hr = %08lx\n", hr);
todo_wine { ok ((dwFlags & ~(DWORD)SFGAO_CANLINK) == dwMyComputerFlags,
"Wrong MyComputer attributes: %08lx, expected: %08lx\n", dwFlags, dwMyComputerFlags); }
hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08lx\n", hr);
IShellFolder_Release(psfDesktop);
IMalloc_Free(ppM, pidlMyComputer);
if (FAILED(hr)) return;
hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
todo_wine {ok (hr == E_INVALIDARG, "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08lx\n", hr); }
dwFlags = 0xffffffff;
hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
ok (SUCCEEDED(hr), "MyComputer->GetAttributesOf(NULL) failed! hr = %08lx\n", hr);
todo_wine { ok (dwFlags == dwMyComputerFlags,
"Wrong MyComputer attributes: %08lx, expected: %08lx\n", dwFlags, dwMyComputerFlags); }
IShellFolder_Release(psfMyComputer);
}
static void test_SHGetPathFromIDList(void)
{
SHITEMID emptyitem = { 0, { 0 } };
LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
LPITEMIDLIST pidlMyComputer;
WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
BOOL result;
HRESULT hr;
LPSHELLFOLDER psfDesktop;
WCHAR wszMyComputer[] = {
':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
if(!pSHGetSpecialFolderPathW) return;
/* Calling SHGetPathFromIDList with an empty pidl should return the desktop folder's path. */
result = pSHGetSpecialFolderPathW(NULL, wszDesktop, CSIDL_DESKTOP, FALSE);
ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %08lx\n", GetLastError());
if (!result) return;
result = SHGetPathFromIDListW(pidlEmpty, wszPath);
ok(result, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError());
if (!result) return;
ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDList didn't return desktop path for empty pidl!\n");
/* MyComputer does not map to a filesystem path. SHGetPathFromIDList should fail. */
hr = SHGetDesktopFolder(&psfDesktop);
ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
if (FAILED(hr)) return;
hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr);
IShellFolder_Release(psfDesktop);
if (FAILED(hr)) return;
SetLastError(0xdeadbeef);
result = SHGetPathFromIDListW(pidlMyComputer, wszPath);
ok (!result, "SHGetPathFromIDList succeeded where it shouldn't!\n");
ok (GetLastError()==0xdeadbeef, "SHGetPathFromIDList shouldn't set last error! Last error: %08lx\n", GetLastError());
IMalloc_Free(ppM, pidlMyComputer);
}
static void test_EnumObjects_and_CompareIDs(void)
{
ITEMIDLIST *newPIDL;
IShellFolder *IDesktopFolder, *testIShellFolder;
char cCurrDirA [MAX_PATH] = {0};
WCHAR cCurrDirW [MAX_PATH];
static const WCHAR cTestDirW[] = {'\\','t','e','s','t','d','i','r',0};
int len;
HRESULT hr;
GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
len = lstrlenA(cCurrDirA);
if(len == 0) {
trace("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
return;
}
if(cCurrDirA[len-1] == '\\')
cCurrDirA[len-1] = 0;
MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
strcatW(cCurrDirW, cTestDirW);
hr = SHGetDesktopFolder(&IDesktopFolder);
ok(hr == S_OK, "SHGetDesktopfolder failed %08lx\n", hr);
CreateFilesFolders();
hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
ok(hr == S_OK, "ParseDisplayName failed %08lx\n", hr);
hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
ok(hr == S_OK, "BindToObject failed %08lx\n", hr);
test_EnumObjects(testIShellFolder);
hr = IShellFolder_Release(testIShellFolder);
ok(hr == S_OK, "IShellFolder_Release failed %08lx\n", hr);
Cleanup();
IMalloc_Free(ppM, newPIDL);
}
START_TEST(shlfolder)
{
init_function_pointers();
/* if OleInitialize doesn't get called, ParseDisplayName returns
CO_E_NOTINITIALIZED for malformed directory names on win2k. */
OleInitialize(NULL);
test_ParseDisplayName();
test_BindToObject();
test_EnumObjects_and_CompareIDs();
test_GetDisplayName();
test_GetAttributesOf();
test_SHGetPathFromIDList();
test_CallForAttributes();
OleUninitialize();
}

View file

@ -0,0 +1,117 @@
/*
* Unit tests for shell32 string operations
*
* Copyright 2004 Jon Griffiths
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdarg.h>
#include <stdio.h>
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#define WINE_NOWINSOCK
#include "windef.h"
#include "winbase.h"
#include "wtypes.h"
#include "shellapi.h"
#include "shtypes.h"
#include "objbase.h"
#include "wine/test.h"
static HMODULE hShell32;
static HRESULT (WINAPI *pStrRetToStrNAW)(LPVOID,DWORD,LPSTRRET,const ITEMIDLIST *);
static WCHAR *CoDupStrW(const char* src)
{
INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
WCHAR* szTemp = (WCHAR*)CoTaskMemAlloc(len * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
return szTemp;
}
static inline int strcmpW(const WCHAR *str1, const WCHAR *str2)
{
while (*str1 && (*str1 == *str2)) { str1++; str2++; }
return *str1 - *str2;
}
static void test_StrRetToStringNA(void)
{
trace("StrRetToStringNAW is Ascii\n");
/* FIXME */
}
static void test_StrRetToStringNW(void)
{
static const WCHAR szTestW[] = { 'T','e','s','t','\0' };
ITEMIDLIST iidl[10];
WCHAR buff[128];
STRRET strret;
BOOL ret;
trace("StrRetToStringNAW is Unicode\n");
strret.uType = STRRET_WSTR;
strret.u.pOleStr = CoDupStrW("Test");
memset(buff, 0xff, sizeof(buff));
ret = pStrRetToStrNAW(buff, sizeof(buff)/sizeof(WCHAR), &strret, NULL);
ok(ret == TRUE && !strcmpW(buff, szTestW),
"STRRET_WSTR: dup failed, ret=%d\n", ret);
strret.uType = STRRET_CSTR;
lstrcpyA(strret.u.cStr, "Test");
memset(buff, 0xff, sizeof(buff));
ret = pStrRetToStrNAW(buff, sizeof(buff)/sizeof(WCHAR), &strret, NULL);
ok(ret == TRUE && !strcmpW(buff, szTestW),
"STRRET_CSTR: dup failed, ret=%d\n", ret);
strret.uType = STRRET_OFFSET;
strret.u.uOffset = 1;
strcpy((char*)&iidl, " Test");
memset(buff, 0xff, sizeof(buff));
ret = pStrRetToStrNAW(buff, sizeof(buff)/sizeof(WCHAR), &strret, iidl);
ok(ret == TRUE && !strcmpW(buff, szTestW),
"STRRET_OFFSET: dup failed, ret=%d\n", ret);
/* The next test crashes on W2K, WinXP and W2K3, so we don't test. */
#if 0
/* Invalid dest - should return FALSE, except NT4 does not, so we don't check. */
strret.uType = STRRET_WSTR;
strret.u.pOleStr = CoDupStrW("Test");
pStrRetToStrNAW(NULL, sizeof(buff)/sizeof(WCHAR), &strret, NULL);
trace("NULL dest: ret=%d\n", ret);
#endif
}
START_TEST(string)
{
CoInitialize(0);
hShell32 = LoadLibraryA("shell32.dll");
if (!hShell32)
return;
pStrRetToStrNAW = (void*)GetProcAddress(hShell32, (LPSTR)96);
if (pStrRetToStrNAW)
{
if (!(GetVersion() & 0x80000000))
test_StrRetToStringNW();
else
test_StrRetToStringNA();
}
}

View file

@ -0,0 +1,36 @@
/* Automatically generated file; DO NOT EDIT!! */
/* stdarg.h is needed for Winelib */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "windef.h"
#include "winbase.h"
extern void func_shelllink(void);
extern void func_shellpath(void);
extern void func_shlexec(void);
extern void func_shlfileop(void);
extern void func_shlfolder(void);
extern void func_string(void);
struct test
{
const char *name;
void (*func)(void);
};
const struct test winetest_testlist[] =
{
{ "shelllink", func_shelllink },
{ "shellpath", func_shellpath },
{ "shlexec", func_shlexec },
{ "shlfileop", func_shlfileop },
{ "shlfolder", func_shlfolder },
{ "string", func_string },
{ 0, 0 }
};
#define WINETEST_WANT_MAIN
#include "wine/test.h"