[SHELL32]
authorAmine Khaldi <amine.khaldi@reactos.org>
Fri, 9 Sep 2011 10:55:09 +0000 (10:55 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Fri, 9 Sep 2011 10:55:09 +0000 (10:55 +0000)
* Reintegrate the c++ shell32 branch. Exemplary team-work.. kudos !
* Better code quality, more tests run with less failures... and more.
* Dedicated to everyone who helped ;)

svn path=/trunk/; revision=53653

145 files changed:
1  2 
reactos/dll/win32/shell32/CMakeLists.txt
reactos/dll/win32/shell32/GlueCode.cpp
reactos/dll/win32/shell32/GlueCode.h
reactos/dll/win32/shell32/authors.cpp
reactos/dll/win32/shell32/autocomplete.cpp
reactos/dll/win32/shell32/autocomplete.h
reactos/dll/win32/shell32/basebar.cpp
reactos/dll/win32/shell32/basebar.h
reactos/dll/win32/shell32/brsfolder.cpp
reactos/dll/win32/shell32/changenotify.cpp
reactos/dll/win32/shell32/classes.cpp
reactos/dll/win32/shell32/clipboard.cpp
reactos/dll/win32/shell32/control.cpp
reactos/dll/win32/shell32/dataobject.cpp
reactos/dll/win32/shell32/dde.cpp
reactos/dll/win32/shell32/debughlp.cpp
reactos/dll/win32/shell32/desktop.cpp
reactos/dll/win32/shell32/dialogs.cpp
reactos/dll/win32/shell32/dragdrophelper.cpp
reactos/dll/win32/shell32/dragdrophelper.h
reactos/dll/win32/shell32/drive.cpp
reactos/dll/win32/shell32/enumidlist.cpp
reactos/dll/win32/shell32/enumidlist.h
reactos/dll/win32/shell32/extracticon.cpp
reactos/dll/win32/shell32/folder_options.cpp
reactos/dll/win32/shell32/folders.cpp
reactos/dll/win32/shell32/fprop.cpp
reactos/dll/win32/shell32/icon_res.rc
reactos/dll/win32/shell32/iconcache.cpp
reactos/dll/win32/shell32/lang/de-DE.rc
reactos/dll/win32/shell32/menuband.cpp
reactos/dll/win32/shell32/menuband.h
reactos/dll/win32/shell32/menubandsite.cpp
reactos/dll/win32/shell32/menubandsite.h
reactos/dll/win32/shell32/menudeskbar.cpp
reactos/dll/win32/shell32/menudeskbar.h
reactos/dll/win32/shell32/pidl.cpp
reactos/dll/win32/shell32/pidl.h
reactos/dll/win32/shell32/precomp.h
reactos/dll/win32/shell32/regsvr.cpp
reactos/dll/win32/shell32/res/Copy of rgs/adminfoldershortcut.rgs
reactos/dll/win32/shell32/res/Copy of rgs/autocomplete.rgs
reactos/dll/win32/shell32/res/Copy of rgs/controlpanel.rgs
reactos/dll/win32/shell32/res/Copy of rgs/dragdrophelper.rgs
reactos/dll/win32/shell32/res/Copy of rgs/folderoptions.rgs
reactos/dll/win32/shell32/res/Copy of rgs/foldershortcut.rgs
reactos/dll/win32/shell32/res/Copy of rgs/fontsfoldershortcut.rgs
reactos/dll/win32/shell32/res/Copy of rgs/menubandsite.rgs
reactos/dll/win32/shell32/res/Copy of rgs/mycomputer.rgs
reactos/dll/win32/shell32/res/Copy of rgs/mydocuments.rgs
reactos/dll/win32/shell32/res/Copy of rgs/networkplaces.rgs
reactos/dll/win32/shell32/res/Copy of rgs/newmenu.rgs
reactos/dll/win32/shell32/res/Copy of rgs/printers.rgs
reactos/dll/win32/shell32/res/Copy of rgs/recyclebin.rgs
reactos/dll/win32/shell32/res/Copy of rgs/shelldesktop.rgs
reactos/dll/win32/shell32/res/Copy of rgs/shellfsfolder.rgs
reactos/dll/win32/shell32/res/Copy of rgs/shelllink.rgs
reactos/dll/win32/shell32/res/Copy of rgs/startmenu.rgs
reactos/dll/win32/shell32/res/rgs/adminfoldershortcut.rgs
reactos/dll/win32/shell32/res/rgs/autocomplete.rgs
reactos/dll/win32/shell32/res/rgs/controlpanel.rgs
reactos/dll/win32/shell32/res/rgs/dragdrophelper.rgs
reactos/dll/win32/shell32/res/rgs/folderoptions.rgs
reactos/dll/win32/shell32/res/rgs/foldershortcut.rgs
reactos/dll/win32/shell32/res/rgs/fontsfoldershortcut.rgs
reactos/dll/win32/shell32/res/rgs/menubandsite.rgs
reactos/dll/win32/shell32/res/rgs/mycomputer.rgs
reactos/dll/win32/shell32/res/rgs/mydocuments.rgs
reactos/dll/win32/shell32/res/rgs/networkplaces.rgs
reactos/dll/win32/shell32/res/rgs/newmenu.rgs
reactos/dll/win32/shell32/res/rgs/printers.rgs
reactos/dll/win32/shell32/res/rgs/recyclebin.rgs
reactos/dll/win32/shell32/res/rgs/shelldesktop.rgs
reactos/dll/win32/shell32/res/rgs/shellfsfolder.rgs
reactos/dll/win32/shell32/res/rgs/shelllink.rgs
reactos/dll/win32/shell32/res/rgs/startmenu.rgs
reactos/dll/win32/shell32/rgs_res.rc
reactos/dll/win32/shell32/ros-systray.cpp
reactos/dll/win32/shell32/she_ocmenu.cpp
reactos/dll/win32/shell32/she_ocmenu.h
reactos/dll/win32/shell32/shell.cpp
reactos/dll/win32/shell32/shell32.rbuild
reactos/dll/win32/shell32/shell32.rbuild.bak
reactos/dll/win32/shell32/shell32.rc
reactos/dll/win32/shell32/shell32_main.cpp
reactos/dll/win32/shell32/shell32_main.h
reactos/dll/win32/shell32/shellitem.cpp
reactos/dll/win32/shell32/shellitem.h
reactos/dll/win32/shell32/shelllink.cpp
reactos/dll/win32/shell32/shelllink.h
reactos/dll/win32/shell32/shellole.cpp
reactos/dll/win32/shell32/shellord.cpp
reactos/dll/win32/shell32/shellpath.cpp
reactos/dll/win32/shell32/shellreg.cpp
reactos/dll/win32/shell32/shellstring.cpp
reactos/dll/win32/shell32/shfldr.h
reactos/dll/win32/shell32/shfldr_admintools.cpp
reactos/dll/win32/shell32/shfldr_admintools.h
reactos/dll/win32/shell32/shfldr_cpanel.cpp
reactos/dll/win32/shell32/shfldr_cpanel.h
reactos/dll/win32/shell32/shfldr_desktop.cpp
reactos/dll/win32/shell32/shfldr_desktop.h
reactos/dll/win32/shell32/shfldr_fonts.cpp
reactos/dll/win32/shell32/shfldr_fonts.h
reactos/dll/win32/shell32/shfldr_fs.cpp
reactos/dll/win32/shell32/shfldr_fs.h
reactos/dll/win32/shell32/shfldr_mycomp.cpp
reactos/dll/win32/shell32/shfldr_mycomp.h
reactos/dll/win32/shell32/shfldr_mydocuments.cpp
reactos/dll/win32/shell32/shfldr_mydocuments.h
reactos/dll/win32/shell32/shfldr_netplaces.cpp
reactos/dll/win32/shell32/shfldr_netplaces.h
reactos/dll/win32/shell32/shfldr_printers.cpp
reactos/dll/win32/shell32/shfldr_printers.h
reactos/dll/win32/shell32/shfldr_recyclebin.cpp
reactos/dll/win32/shell32/shfldr_recyclebin.h
reactos/dll/win32/shell32/shfldr_unixfs.cpp
reactos/dll/win32/shell32/shlexec.cpp
reactos/dll/win32/shell32/shlfileop.cpp
reactos/dll/win32/shell32/shlfolder.cpp
reactos/dll/win32/shell32/shlfsbind.cpp
reactos/dll/win32/shell32/shlmenu.cpp
reactos/dll/win32/shell32/shlview.cpp
reactos/dll/win32/shell32/shpolicy.cpp
reactos/dll/win32/shell32/shresdef.h
reactos/dll/win32/shell32/shv_def_cmenu.cpp
reactos/dll/win32/shell32/shv_item_new.cpp
reactos/dll/win32/shell32/shv_item_new.h
reactos/dll/win32/shell32/startmenu.cpp
reactos/dll/win32/shell32/startmenu.h
reactos/dll/win32/shell32/stubs.cpp
reactos/dll/win32/shell32/systray.cpp
reactos/dll/win32/shell32/toolband.cpp
reactos/dll/win32/shell32/toolband.h
reactos/dll/win32/shell32/undocshell.h
reactos/include/ndk/rtlfuncs.h
reactos/include/psdk/appmgmt.h
reactos/include/psdk/shlguid.h
reactos/include/psdk/shlguid_undoc.h
reactos/include/psdk/shlobj.h
reactos/include/psdk/shlobj_undoc.h
reactos/include/psdk/shlwapi.h
reactos/include/psdk/shlwapi_undoc.h
reactos/include/reactos/wine/commctrl.h
reactos/lib/atl/atlbase.h

index bf68b6b,0000000..c915f75
mode 100644,000000..100644
--- /dev/null
@@@ -1,96 -1,0 +1,105 @@@
-     authors.c
-     autocomplete.c
-     brsfolder.c
-     changenotify.c
-     classes.c
-     clipboard.c
-     control.c
-     dataobject.c
-     dde.c
-     debughlp.c
-     desktop.c
-     dialogs.c
-     dragdrophelper.c
-     enumidlist.c
-     extracticon.c
-     folders.c
-     iconcache.c
-     pidl.c
-     regsvr.c
-     shell32_main.c
-     shellitem.c
-     shelllink.c
-     shellole.c
-     shellord.c
-     shellpath.c
-     shellreg.c
-     shellstring.c
-     shfldr_desktop.c
-     shfldr_fs.c
-     shfldr_mycomp.c
-     shfldr_mydocuments.c
-     shfldr_printers.c
-     shfldr_admintools.c
-     shfldr_netplaces.c
-     shfldr_fonts.c
-     shfldr_cpanel.c
-     shfldr_recyclebin.c
-     shlexec.c
-     shlfileop.c
-     shlfolder.c
-     shlfsbind.c
-     shlmenu.c
-     shlview.c
-     shpolicy.c
-     shv_def_cmenu.c
-     startmenu.c
-     stubs.c
-     ros-systray.c
-     fprop.c
-     drive.c
-     she_ocmenu.c
-     shv_item_new.c
-     folder_options.c
++set_cpp()
 +
 +remove_definitions(-D_WIN32_WINNT=0x502)
 +add_definitions(-D_WIN32_WINNT=0x600)
 +
 +add_definitions(
 +    -D_SHELL32_
 +    -DCOM_NO_WINDOWS_H
 +    -D_WINE)
 +
 +include_directories(
 +    ${REACTOS_SOURCE_DIR}/include/reactos/wine
 +    ${REACTOS_SOURCE_DIR}/lib/recyclebin
++    ${REACTOS_SOURCE_DIR}/lib/atl
 +    ${REACTOS_SOURCE_DIR})
 +
 +spec2def(shell32.dll shell32.spec)
 +
 +list(APPEND SOURCE
- set_module_type(shell32 win32dll)
- target_link_libraries(shell32 wine uuid recyclebin)
++    authors.cpp
++    autocomplete.cpp
++    brsfolder.cpp
++    changenotify.cpp
++    classes.cpp
++    clipboard.cpp
++    control.cpp
++    dataobject.cpp
++    dde.cpp
++    debughlp.cpp
++    desktop.cpp
++    dialogs.cpp
++    dragdrophelper.cpp
++    enumidlist.cpp
++    extracticon.cpp
++    folders.cpp
++    iconcache.cpp
++    pidl.cpp
++    shell32_main.cpp
++    shellitem.cpp
++    shelllink.cpp
++    shellole.cpp
++    shellord.cpp
++    shellpath.cpp
++    shellreg.cpp
++    shellstring.cpp
++    shfldr_desktop.cpp
++    shfldr_fs.cpp
++    shfldr_mycomp.cpp
++    shfldr_mydocuments.cpp
++    shfldr_printers.cpp
++    shfldr_admintools.cpp
++    shfldr_netplaces.cpp
++    shfldr_fonts.cpp
++    shfldr_cpanel.cpp
++    shfldr_recyclebin.cpp
++    shlexec.cpp
++    shlfileop.cpp
++    shlfolder.cpp
++    shlfsbind.cpp
++    shlmenu.cpp
++    shlview.cpp
++    shpolicy.cpp
++    shv_def_cmenu.cpp
++    startmenu.cpp
++    stubs.cpp
++    ros-systray.cpp
++    fprop.cpp
++    drive.cpp
++    she_ocmenu.cpp
++    shv_item_new.cpp
++    folder_options.cpp
 +    shell32.rc
 +    ${CMAKE_CURRENT_BINARY_DIR}/shell32_stubs.c
 +    ${CMAKE_CURRENT_BINARY_DIR}/shell32.def)
 +
 +add_library(shell32 SHARED ${SOURCE})
++
++set_module_type(shell32 win32dll UNICODE)
++
++target_link_libraries(shell32
++    atlnew
++    wine
++    uuid
++    recyclebin)
++
 +add_delay_importlibs(shell32 ole32 version)
 +
 +add_importlibs(shell32
 +    advapi32
 +    gdi32
 +    user32
 +    comctl32
 +    comdlg32
 +    shlwapi
 +    devmgr
 +    winspool
 +    winmm
 +    msvcrt
 +    kernel32
 +    ntdll)
 +
 +add_pch(shell32 precomp.h)
++
 +add_cd_file(TARGET shell32 DESTINATION reactos/system32 FOR all)
 +add_importlib_target(shell32.spec)
index 0000000,055ca47..055ca47
mode 000000,100644..100644
--- /dev/null
index 0000000,4803de2..4803de2
mode 000000,100644..100644
--- /dev/null
index 0000000,36243b6..36243b6
mode 000000,100644..100644
--- /dev/null
index 0000000,e69de29..e69de29
mode 000000,100644..100644
--- /dev/null
index 0000000,0000000..e69de29
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000,2d66bdd..2d66bdd
mode 000000,100644..100644
--- /dev/null
index 0000000,fe69656..fe69656
mode 000000,100644..100644
--- /dev/null
index 0000000,b4a6943..b4a6943
mode 000000,100644..100644
--- /dev/null
index 0000000,811258e..811258e
mode 000000,100644..100644
--- /dev/null
index 0000000,96139bb..96139bb
mode 000000,100644..100644
--- /dev/null
index 0000000,a64b6f7..a64b6f7
mode 000000,100644..100644
--- /dev/null
index 0000000,c5a6ec6..c5a6ec6
mode 000000,100644..100644
--- /dev/null
index 0000000,89e4064..89e4064
mode 000000,100644..100644
--- /dev/null
index 0000000,3863c4b..3863c4b
mode 000000,100644..100644
--- /dev/null
index 0000000,dc08d49..dc08d49
mode 000000,100644..100644
--- /dev/null
index 0000000,7eee167..7eee167
mode 000000,100644..100644
--- /dev/null
Simple merge
index 0000000,b4f0076..b4f0076
mode 000000,100644..100644
--- /dev/null
index 0000000,0000000..e69de29
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000,0000000..e69de29
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000,0000000..e69de29
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000,0000000..e69de29
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000,0000000..e69de29
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000,0000000..e69de29
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000,d28dbce..d28dbce
mode 000000,100644..100644
--- /dev/null
Simple merge
index a4fca38,0000000..1d2b495
mode 100644,000000..100644
--- /dev/null
@@@ -1,81 -1,0 +1,100 @@@
- #define NONAMELESSUNION
- #define NONAMELESSSTRUCT
 +#ifndef _PRECOMP_H__
 +#define _PRECOMP_H__
 +
 +#include "config.h"
 +
 +#include <stdarg.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include <stdio.h>
 +#include <assert.h>
 +#include <ctype.h>
 +#include <malloc.h>
 +
 +#define COBJMACROS
- #define UNICODE
- #define _UNICODE
 +#define WIN32_NO_STATUS
 +#define NTOS_MODE_USER
- #include <ndk/umtypes.h>
 +
 +#include <windows.h>
 +
 +#include <shlguid.h>
 +#include <shlguid_undoc.h>
 +#include <shlwapi.h>
 +#include <shlobj.h>
 +#include <shlobj_undoc.h>
 +#include <shldisp.h>
 +#include <commdlg.h>
 +#include <commctrl.h>
 +#include <cpl.h>
 +#include <objbase.h>
 +#include <ole2.h>
 +#include <ocidl.h>
 +#include <docobj.h>
 +#include <prsht.h>
 +//#include <initguid.h>
 +//#include <devguid.h>
 +#include <shobjidl.h>
 +#include <shellapi.h>
 +#include <msi.h>
 +#include <appmgmt.h>
 +#include <ntquery.h>
 +#include <recyclebin.h>
 +#include <shtypes.h>
++#include <ndk/rtlfuncs.h>
 +#include <fmifs/fmifs.h>
 +#include <largeint.h>
 +#include <sddl.h>
 +
++#include <tchar.h>
++#include <atlbase.h>
++#include <atlcom.h>
++#include <atlwin.h>
++
 +#include "base/shell/explorer-new/todo.h"
 +#include "dlgs.h"
 +#include "pidl.h"
 +#include "debughlp.h"
 +#include "undocshell.h"
 +#include "shell32_main.h"
 +#include "shresdef.h"
 +#include "cpanel.h"
 +#include "enumidlist.h"
 +#include "shfldr.h"
 +#include "version.h"
 +#include "shellfolder.h"
 +#include "xdg.h"
 +#include "shellapi.h"
 +
++#include "shfldr_fs.h"
++#include "shfldr_mycomp.h"
++#include "shfldr_desktop.h"
++#include "shellitem.h"
++#include "shelllink.h"
++#include "dragdrophelper.h"
++#include "shfldr_cpanel.h"
++#include "autocomplete.h"
++#include "shfldr_mydocuments.h"
++#include "shfldr_netplaces.h"
++#include "shfldr_fonts.h"
++#include "shfldr_printers.h"
++#include "shfldr_admintools.h"
++#include "shfldr_recyclebin.h"
++#include "she_ocmenu.h"
++#include "shv_item_new.h"
++#include "startmenu.h"
++
 +#include "wine/debug.h"
 +#include "wine/unicode.h"
 +
 +extern const GUID CLSID_AdminFolderShortcut;
 +extern const GUID CLSID_FontsFolderShortcut;
 +extern const GUID CLSID_StartMenu;
 +extern const GUID CLSID_MenuBandSite;
 +extern const GUID CLSID_OpenWith;
 +extern const GUID CLSID_UnixFolder;
 +extern const GUID CLSID_UnixDosFolder;
 +extern const GUID SHELL32_AdvtShortcutProduct;
 +extern const GUID SHELL32_AdvtShortcutComponent;
 +
 +
 +#endif
index 0000000,4567aff..4567aff
mode 000000,100644..100644
--- /dev/null
index 0000000,6a37542..6a37542
mode 000000,100644..100644
--- /dev/null
index 0000000,f4cf23b..f4cf23b
mode 000000,100644..100644
--- /dev/null
index 0000000,2813943..2813943
mode 000000,100644..100644
--- /dev/null
index 5276af0,0000000..844a30c
mode 100644,000000..100644
--- /dev/null
@@@ -1,86 -1,0 +1,89 @@@
- <module name="shell32" type="win32dll" baseaddress="${BASEADDRESS_SHELL32}" installbase="system32" installname="shell32.dll" crt="msvcrt">
 +<group>
-       <include base="shell32">.</include>
++<module name="shell32" type="win32dll" baseaddress="${BASEADDRESS_SHELL32}" installbase="system32" installname="shell32.dll" allowwarnings="true" crt="msvcrt">
 +      <autoregister infsection="OleControlDlls" type="Both" />
 +      <importlibrary definition="shell32.spec" />
-       <define name="_SHELL32_" />
 +      <include base="recyclebin">.</include>
 +      <include base="ReactOS">include/reactos/wine</include>
-       <file>authors.c</file>
-       <file>autocomplete.c</file>
-       <file>brsfolder.c</file>
-       <file>changenotify.c</file>
-       <file>classes.c</file>
-       <file>clipboard.c</file>
-       <file>control.c</file>
-       <file>dataobject.c</file>
-       <file>dde.c</file>
-       <file>debughlp.c</file>
-       <file>desktop.c</file>
-       <file>dialogs.c</file>
-       <file>dragdrophelper.c</file>
-       <file>enumidlist.c</file>
-       <file>extracticon.c</file>
-       <file>folders.c</file>
-       <file>iconcache.c</file>
-       <file>pidl.c</file>
-       <file>regsvr.c</file>
-       <file>shell32_main.c</file>
-       <file>shellitem.c</file>
-       <file>shelllink.c</file>
-       <file>shellole.c</file>
-       <file>shellord.c</file>
-       <file>shellpath.c</file>
-       <file>shellreg.c</file>
-       <file>shellstring.c</file>
-       <file>shfldr_desktop.c</file>
-       <file>shfldr_fs.c</file>
-       <file>shfldr_mycomp.c</file>
-       <file>shfldr_mydocuments.c</file>
-       <file>shfldr_printers.c</file>
-       <file>shfldr_admintools.c</file>
-       <file>shfldr_netplaces.c</file>
-       <file>shfldr_fonts.c</file>
-       <file>shfldr_cpanel.c</file>
-       <file>shfldr_recyclebin.c</file>
-       <file>shlexec.c</file>
-       <file>shlfileop.c</file>
-       <file>shlfolder.c</file>
-       <file>shlfsbind.c</file>
-       <file>shlmenu.c</file>
-       <file>shlview.c</file>
-       <file>shpolicy.c</file>
-       <file>shv_def_cmenu.c</file>
-       <file>startmenu.c</file>
-       <file>stubs.c</file>
-       <file>ros-systray.c</file>
-       <file>fprop.c</file>
-       <file>drive.c</file>
-       <file>she_ocmenu.c</file>
-       <file>shv_item_new.c</file>
-       <file>folder_options.c</file>
++  <include base="atlnew">.</include>
++  <define name="_SHELL32_" />
 +      <define name="COM_NO_WINDOWS_H" />
 +      <define name="_WINE" />
++      <define name="UNICODE" />
++      <define name="_UNICODE" />
 +      <redefine name="_WIN32_WINNT">0x600</redefine>
 +      <library>wine</library>
 +      <library>uuid</library>
 +      <library>recyclebin</library>
 +      <library>ntdll</library>
 +      <library>advapi32</library>
 +      <library>gdi32</library>
 +      <library>user32</library>
 +      <library>comctl32</library>
 +      <library>comdlg32</library>
 +      <library>shlwapi</library>
 +      <library>ole32</library>
 +      <library>version</library>
 +      <library>devmgr</library>
 +      <library>winspool</library>
 +      <library>winmm</library>
++      <library>msvcrt</library>
++      <library>atlnew</library>
 +      <pch>precomp.h</pch>
++      <file>authors.cpp</file>
++      <file>autocomplete.cpp</file>
++      <file>brsfolder.cpp</file>
++      <file>changenotify.cpp</file>
++      <file>classes.cpp</file>
++      <file>clipboard.cpp</file>
++      <file>control.cpp</file>
++      <file>dataobject.cpp</file>
++      <file>dde.cpp</file>
++      <file>debughlp.cpp</file>
++      <file>desktop.cpp</file>
++      <file>dialogs.cpp</file>
++      <file>dragdrophelper.cpp</file>
++      <file>enumidlist.cpp</file>
++      <file>extracticon.cpp</file>
++      <file>folders.cpp</file>
++      <file>iconcache.cpp</file>
++      <file>pidl.cpp</file>
++      <file>shell32_main.cpp</file>
++      <file>shellitem.cpp</file>
++      <file>shelllink.cpp</file>
++      <file>shellole.cpp</file>
++      <file>shellord.cpp</file>
++      <file>shellpath.cpp</file>
++      <file>shellreg.cpp</file>
++      <file>shellstring.cpp</file>
++      <file>shfldr_desktop.cpp</file>
++      <file>shfldr_fs.cpp</file>
++      <file>shfldr_mycomp.cpp</file>
++      <file>shfldr_mydocuments.cpp</file>
++      <file>shfldr_printers.cpp</file>
++      <file>shfldr_admintools.cpp</file>
++      <file>shfldr_netplaces.cpp</file>
++      <file>shfldr_fonts.cpp</file>
++      <file>shfldr_cpanel.cpp</file>
++      <file>shfldr_recyclebin.cpp</file>
++      <file>shlexec.cpp</file>
++      <file>shlfileop.cpp</file>
++      <file>shlfolder.cpp</file>
++      <file>shlfsbind.cpp</file>
++      <file>shlmenu.cpp</file>
++      <file>shlview.cpp</file>
++      <file>shpolicy.cpp</file>
++      <file>shv_def_cmenu.cpp</file>
++      <file>startmenu.cpp</file>
++      <file>stubs.cpp</file>
++      <file>ros-systray.cpp</file>
++      <file>fprop.cpp</file>
++      <file>drive.cpp</file>
++      <file>she_ocmenu.cpp</file>
++      <file>shv_item_new.cpp</file>
++      <file>folder_options.cpp</file>
 +      <file>shell32.rc</file>
 +</module>
 +<module name="shobjidl_local_interface" type="idlinterface">
 +      <file>shobjidl_local.idl</file>
 +</module>
 +</group>
Simple merge
index 0000000,995d3b2..995d3b2
mode 000000,100644..100644
--- /dev/null
index 0000000,546fb74..546fb74
mode 000000,100644..100644
--- /dev/null
index 0000000,fa0a251..fa0a251
mode 000000,100644..100644
--- /dev/null
index 0000000,2005235..2005235
mode 000000,100644..100644
--- /dev/null
index 0000000,6d1023f..6d1023f
mode 000000,100644..100644
--- /dev/null
index 0000000,44fa887..44fa887
mode 000000,100644..100644
--- /dev/null
index 0000000,34e2a18..34e2a18
mode 000000,100644..100644
--- /dev/null
index 0000000,b63eabc..b63eabc
mode 000000,100644..100644
--- /dev/null
Simple merge
index 0000000,0000000..d55d325
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1276 @@@
++/*
++ *    Virtual Desktop Folder
++ *
++ *    Copyright 1997                Marcus Meissner
++ *    Copyright 1998, 1999, 2002    Juergen Schmied
++ *    Copyright 2009                Andrew Hill
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
++ */
++
++#include <precomp.h>
++
++WINE_DEFAULT_DEBUG_CHANNEL(shell);
++
++/*
++CDesktopFolder should create two file system folders internally, one representing the
++user's desktop folder, and the other representing the common desktop folder. It should
++also create a CRegFolder to represent the virtual items that exist only in the registry.
++The CRegFolder is aggregated by the CDesktopFolder, and queries for the CLSID_IShellFolder,
++CLSID_IShellFolder2, or CLSID_IShellIconOverlay interfaces prefer the CRegFolder
++implementation.
++The CDesktopFolderEnum class should create two enumerators, one for each of the file
++system folders, and enumerate the contents of each folder. Since the CRegFolder
++implementation of IShellFolder::EnumObjects enumerates the virtual items, the
++CDesktopFolderEnum is only responsible for returning the physical items.
++CDesktopFolderEnum is incorrect where it filters My Computer from the enumeration
++if the new start menu is used. The CDesktopViewCallback is responsible for filtering
++it from the view by handling the IncludeObject query to return S_FALSE. The enumerator
++always shows My Computer.
++*/
++
++/***********************************************************************
++*     Desktopfolder implementation
++*/
++
++class CDesktopFolder;
++
++class CDesktopFolderEnum :
++    public IEnumIDListImpl
++{
++private:
++//    CComPtr                                fDesktopEnumerator;
++//    CComPtr                                fCommonDesktopEnumerator;
++public:
++    CDesktopFolderEnum();
++    ~CDesktopFolderEnum();
++    HRESULT WINAPI Initialize(CDesktopFolder *desktopFolder, HWND hwndOwner, DWORD dwFlags);
++
++BEGIN_COM_MAP(CDesktopFolderEnum)
++    COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
++END_COM_MAP()
++};
++
++WCHAR *build_paths_list(LPCWSTR wszBasePath, int cidl, LPCITEMIDLIST *pidls);
++int SHELL_ConfirmMsgBox(HWND hWnd, LPWSTR lpszText, LPWSTR lpszCaption, HICON hIcon, BOOL bYesToAll);
++
++static const shvheader DesktopSFHeader[] = {
++    {IDS_SHV_COLUMN1, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
++    {IDS_SHV_COLUMN2, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
++    {IDS_SHV_COLUMN3, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
++    {IDS_SHV_COLUMN4, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 12},
++    {IDS_SHV_COLUMN5, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 5}
++};
++
++#define DESKTOPSHELLVIEWCOLUMNS 5
++
++CDesktopFolderEnum::CDesktopFolderEnum()
++{
++}
++
++CDesktopFolderEnum::~CDesktopFolderEnum()
++{
++}
++
++static const WCHAR ClassicStartMenuW[] =  {'S','O','F','T','W','A','R','E','\\',
++ 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
++ 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r',
++ '\\','H','i','d','e','D','e','s','k','t','o','p','I','c','o','n','s','\\',
++ 'C','l','a','s','s','i','c','S','t','a','r','t','M','e','n','u','\0' };
++
++INT
++IsNamespaceExtensionHidden(WCHAR *iid)
++{
++    DWORD Result, dwResult;
++    dwResult = sizeof(DWORD);
++
++    if (RegGetValueW(HKEY_CURRENT_USER, /* FIXME use NewStartPanel when activated */
++                     ClassicStartMenuW,
++                     iid,
++                     RRF_RT_DWORD,
++                     NULL,
++                     &Result,
++                     &dwResult) != ERROR_SUCCESS)
++    {
++        return -1;     
++    }
++    
++    return Result;
++}
++
++static
++VOID
++SetNamespaceExtensionVisibleStatus(const WCHAR * iid, DWORD dwStatus)
++{
++    HKEY hKey;
++
++    if (RegOpenKeyExW(HKEY_CURRENT_USER, ClassicStartMenuW, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
++    {
++        RegSetValueExW(hKey, iid, 0, REG_DWORD, (LPBYTE)&dwStatus, sizeof(DWORD));
++        RegCloseKey(hKey);
++    }
++}
++
++/**************************************************************************
++ *  CreateDesktopEnumList()
++ */
++static const WCHAR Desktop_NameSpaceW[] = { 'S','O','F','T','W','A','R','E',
++ '\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
++ 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l',
++ 'o','r','e','r','\\','D','e','s','k','t','o','p','\\','N','a','m','e','s','p',
++ 'a','c','e','\0' };
++
++HRESULT WINAPI CDesktopFolderEnum::Initialize(CDesktopFolder *desktopFolder, HWND hwndOwner, DWORD dwFlags)
++{
++    BOOL ret = TRUE;
++    WCHAR szPath[MAX_PATH];
++
++    static WCHAR MyDocumentsClassString[] = L"{450D8FBA-AD25-11D0-98A8-0800361B1103}";
++
++    TRACE("(%p)->(flags=0x%08x)\n", this, dwFlags);
++
++    /* enumerate the root folders */
++    if (dwFlags & SHCONTF_FOLDERS)
++    {
++        HKEY hkey;
++        UINT i;
++        DWORD dwResult;
++
++        /* create the pidl for This item */
++        if (IsNamespaceExtensionHidden(MyDocumentsClassString) < 1)
++        {
++            ret = AddToEnumList(_ILCreateMyDocuments());
++        }
++        ret = AddToEnumList(_ILCreateMyComputer());
++
++        for (i = 0; i < 2; i++)
++        {
++            if (i == 0)
++                dwResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, Desktop_NameSpaceW, 0, KEY_READ, &hkey);
++            else 
++                dwResult = RegOpenKeyExW(HKEY_CURRENT_USER, Desktop_NameSpaceW, 0, KEY_READ, &hkey);
++
++            if (dwResult == ERROR_SUCCESS)
++            {
++                WCHAR iid[50];
++                LPITEMIDLIST pidl;
++                int i=0;
++
++                while (ret)
++                {
++                    DWORD size;
++                    LONG r;
++
++                    size = sizeof (iid) / sizeof (iid[0]);
++                    r = RegEnumKeyExW(hkey, i, iid, &size, 0, NULL, NULL, NULL);
++                    if (ERROR_SUCCESS == r)
++                    {
++                        if (IsNamespaceExtensionHidden(iid) < 1)
++                        {
++                           pidl = _ILCreateGuidFromStrW(iid);
++                            if (pidl != NULL)
++                            {
++                               if (!HasItemWithCLSID(pidl))
++                               {
++                                   ret = AddToEnumList(pidl);
++                               }
++                               else
++                               {
++                                    SHFree(pidl);
++                              }
++                            }
++                       }
++                    }
++                    else if (ERROR_NO_MORE_ITEMS == r)
++                        break;
++                    else
++                        ret = FALSE;
++                    i++;
++                }
++                RegCloseKey(hkey);
++            }
++        }
++        for (i = 0; i < 2; i++)
++        {
++            if (i == 0)
++                dwResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, ClassicStartMenuW, 0, KEY_READ, &hkey);
++            else 
++                dwResult = RegOpenKeyExW(HKEY_CURRENT_USER, ClassicStartMenuW, 0, KEY_READ, &hkey);
++
++            if (dwResult == ERROR_SUCCESS)
++            {
++                DWORD j = 0, dwVal, Val, dwType, dwIID;
++                LONG r;
++                WCHAR iid[50];
++
++                while(ret)
++                {
++                    dwVal = sizeof(Val);
++                    dwIID = sizeof(iid) / sizeof(WCHAR);
++
++                    r = RegEnumValueW(hkey, j++, iid, &dwIID, NULL, &dwType, (LPBYTE)&Val, &dwVal);
++                    if (r == ERROR_SUCCESS)
++                    {
++                        if (Val == 0 && dwType == REG_DWORD)
++                        {
++                            LPITEMIDLIST pidl = _ILCreateGuidFromStrW(iid);
++                            if (pidl != NULL)
++                            {
++                                if (!HasItemWithCLSID(pidl))
++                                {
++                                   AddToEnumList(pidl);
++                                }
++                                else
++                                {
++                                    SHFree(pidl);
++                                }
++                            }
++                        }
++                    }
++                    else if (ERROR_NO_MORE_ITEMS == r)
++                        break;
++                    else
++                        ret = FALSE;
++                }
++                RegCloseKey(hkey);
++            }
++
++        }
++    }
++
++    /* enumerate the elements in %windir%\desktop */
++    ret = ret && SHGetSpecialFolderPathW(0, szPath, CSIDL_DESKTOPDIRECTORY, FALSE);
++    ret = ret && CreateFolderEnumList(szPath, dwFlags);
++
++    ret = ret && SHGetSpecialFolderPathW(0, szPath, CSIDL_COMMON_DESKTOPDIRECTORY, FALSE);
++    ret = ret && CreateFolderEnumList(szPath, dwFlags);
++
++    return ret ? S_OK : E_FAIL;
++}
++
++CDesktopFolder::CDesktopFolder()
++{
++    pidlRoot = NULL;
++    sPathTarget = NULL;
++}
++
++CDesktopFolder::~CDesktopFolder()
++{
++}
++
++HRESULT WINAPI CDesktopFolder::FinalConstruct()
++{
++    WCHAR                                szMyPath[MAX_PATH];
++
++    if (!SHGetSpecialFolderPathW( 0, szMyPath, CSIDL_DESKTOPDIRECTORY, TRUE ))
++        return E_UNEXPECTED;
++
++    pidlRoot = _ILCreateDesktop();    /* my qualified pidl */
++    sPathTarget = (LPWSTR)SHAlloc((wcslen(szMyPath) + 1) * sizeof(WCHAR));
++    wcscpy(sPathTarget, szMyPath);
++    return S_OK;
++}
++
++/**************************************************************************
++ *    ISF_Desktop_fnParseDisplayName
++ *
++ * NOTES
++ *    "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" and "" binds
++ *    to MyComputer
++ */
++HRESULT WINAPI CDesktopFolder::ParseDisplayName (HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
++                DWORD *pchEaten, LPITEMIDLIST *ppidl, DWORD *pdwAttributes)
++{
++    WCHAR szElement[MAX_PATH];
++    LPCWSTR szNext = NULL;
++    LPITEMIDLIST pidlTemp = NULL;
++    HRESULT hr = S_OK;
++    CLSID clsid;
++
++    TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
++           this, hwndOwner, pbc, lpszDisplayName, debugstr_w(lpszDisplayName),
++           pchEaten, ppidl, pdwAttributes);
++
++    if (!ppidl)
++        return E_INVALIDARG;
++
++    if (!lpszDisplayName)
++    {
++        *ppidl = NULL;
++        return E_INVALIDARG;
++    }
++
++    *ppidl = NULL;
++
++    if (pchEaten)
++        *pchEaten = 0;        /* strange but like the original */
++
++    if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':')
++    {
++        szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH);
++        TRACE ("-- element: %s\n", debugstr_w (szElement));
++        CLSIDFromString (szElement + 2, &clsid);
++        pidlTemp = _ILCreateGuid (PT_GUID, clsid);
++    }
++    else if (PathGetDriveNumberW (lpszDisplayName) >= 0)
++    {
++        /* it's a filesystem path with a drive. Let MyComputer/UnixDosFolder parse it */
++        pidlTemp = _ILCreateMyComputer ();
++        szNext = lpszDisplayName;
++    }
++    else if (PathIsUNCW(lpszDisplayName))
++    {
++        pidlTemp = _ILCreateNetwork();
++        szNext = lpszDisplayName;
++    }
++    else if( (pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, lpszDisplayName)) )
++    {
++        *ppidl = pidlTemp;
++        return S_OK;
++    }
++    else
++    {
++        /* it's a filesystem path on the desktop. Let a FSFolder parse it */
++
++        if (*lpszDisplayName)
++        {
++            WCHAR szPath[MAX_PATH];
++            LPWSTR pathPtr;
++
++            /* build a complete path to create a simple pidl */
++            lstrcpynW(szPath, sPathTarget, MAX_PATH);
++            pathPtr = PathAddBackslashW(szPath);
++            if (pathPtr)
++            {
++                lstrcpynW(pathPtr, lpszDisplayName, MAX_PATH - (pathPtr - szPath));
++                hr = _ILCreateFromPathW(szPath, &pidlTemp);
++            }
++            else
++            {
++                /* should never reach here, but for completeness */
++                hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
++            }
++        }
++        else
++            pidlTemp = _ILCreateMyComputer();
++
++        szNext = NULL;
++    }
++
++    if (SUCCEEDED(hr) && pidlTemp)
++    {
++        if (szNext && *szNext)
++        {
++            hr = SHELL32_ParseNextElement(this, hwndOwner, pbc,
++                    &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes);
++        }
++        else
++        {
++            if (pdwAttributes && *pdwAttributes)
++                hr = SHELL32_GetItemAttributes((IShellFolder *)this,
++                                               pidlTemp, pdwAttributes);
++        }
++    }
++
++    if (SUCCEEDED(hr))
++        *ppidl = pidlTemp;
++    else
++        *ppidl = NULL;
++
++    TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr);
++
++    return hr;
++}
++
++/**************************************************************************
++ *        ISF_Desktop_fnEnumObjects
++ */
++HRESULT WINAPI CDesktopFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
++{
++    CComObject<CDesktopFolderEnum>            *theEnumerator;
++    CComPtr<IEnumIDList>                    result;
++    HRESULT                                    hResult;
++
++    TRACE ("(%p)->(HWND=%p flags=0x%08x pplist=%p)\n", this, hwndOwner, dwFlags, ppEnumIDList);
++
++    if (ppEnumIDList == NULL)
++        return E_POINTER;
++    *ppEnumIDList = NULL;
++    
++    ATLTRY (theEnumerator = new CComObject<CDesktopFolderEnum>);
++    
++    if (theEnumerator == NULL)
++        return E_OUTOFMEMORY;
++    
++    hResult = theEnumerator->QueryInterface (IID_IEnumIDList, (void **)&result);
++    if (FAILED (hResult))
++    {
++        delete theEnumerator;
++        return hResult;
++    }
++    
++    hResult = theEnumerator->Initialize (this, hwndOwner, dwFlags);
++    if (FAILED (hResult))
++        return hResult;
++    *ppEnumIDList = result.Detach ();
++
++    TRACE ("-- (%p)->(new ID List: %p)\n", this, *ppEnumIDList);
++
++    return S_OK;
++}
++
++/**************************************************************************
++ *        ISF_Desktop_fnBindToObject
++ */
++HRESULT WINAPI CDesktopFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
++{
++    TRACE ("(%p)->(pidl=%p,%p,%s,%p)\n",
++           this, pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
++
++    return SHELL32_BindToChild( pidlRoot, sPathTarget, pidl, riid, ppvOut );
++}
++
++/**************************************************************************
++ *    ISF_Desktop_fnBindToStorage
++ */
++HRESULT WINAPI CDesktopFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
++{
++    FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n",
++           this, pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
++
++    *ppvOut = NULL;
++    return E_NOTIMPL;
++}
++
++/**************************************************************************
++ *     ISF_Desktop_fnCompareIDs
++ */
++HRESULT WINAPI CDesktopFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
++{
++    int nReturn;
++
++    TRACE ("(%p)->(0x%08lx,pidl1=%p,pidl2=%p)\n", this, lParam, pidl1, pidl2);
++    nReturn = SHELL32_CompareIDs ((IShellFolder *)this, lParam, pidl1, pidl2);
++    TRACE ("-- %i\n", nReturn);
++    return nReturn;
++}
++
++/**************************************************************************
++ *    ISF_Desktop_fnCreateViewObject
++ */
++HRESULT WINAPI CDesktopFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut)
++{
++    CComPtr<IShellView>                    pShellView;
++    HRESULT hr = E_INVALIDARG;
++
++    TRACE ("(%p)->(hwnd=%p,%s,%p)\n",
++           this, hwndOwner, shdebugstr_guid (&riid), ppvOut);
++
++    if (!ppvOut)
++        return hr;
++
++    *ppvOut = NULL;
++
++    if (IsEqualIID (riid, IID_IDropTarget))
++    {
++        WARN ("IDropTarget not implemented\n");
++        hr = E_NOTIMPL;
++    }
++    else if (IsEqualIID (riid, IID_IContextMenu))
++    {
++        WARN ("IContextMenu not implemented\n");
++        hr = E_NOTIMPL;
++    }
++    else if (IsEqualIID (riid, IID_IShellView))
++    {
++        hr = IShellView_Constructor((IShellFolder *)this, &pShellView);
++        if (pShellView)
++            hr = pShellView->QueryInterface(riid, ppvOut);
++    }
++    TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
++    return hr;
++}
++
++/**************************************************************************
++ *  ISF_Desktop_fnGetAttributesOf
++ */
++HRESULT WINAPI CDesktopFolder::GetAttributesOf (UINT cidl, LPCITEMIDLIST *apidl, DWORD *rgfInOut)
++{
++    HRESULT hr = S_OK;
++    static const DWORD dwDesktopAttributes =
++        SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR |
++        SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER;
++    static const DWORD dwMyComputerAttributes =
++        SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET |
++        SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
++
++    TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
++           this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
++
++    if (!rgfInOut)
++        return E_INVALIDARG;
++    if (cidl && !apidl)
++        return E_INVALIDARG;
++
++    if (*rgfInOut == 0)
++        *rgfInOut = ~0;
++
++    if(cidl == 0) {
++        *rgfInOut &= dwDesktopAttributes;
++    } else {
++        while (cidl > 0 && *apidl) {
++            pdump (*apidl);
++            if (_ILIsDesktop(*apidl)) {
++                *rgfInOut &= dwDesktopAttributes;
++            } else if (_ILIsMyComputer(*apidl)) {
++                *rgfInOut &= dwMyComputerAttributes;
++            } else {
++                SHELL32_GetItemAttributes ((IShellFolder *)this, *apidl, rgfInOut);
++            }
++            apidl++;
++            cidl--;
++        }
++    }
++    /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
++    *rgfInOut &= ~SFGAO_VALIDATE;
++
++    TRACE ("-- result=0x%08x\n", *rgfInOut);
++
++    return hr;
++}
++
++/**************************************************************************
++ *    ISF_Desktop_fnGetUIObjectOf
++ *
++ * PARAMETERS
++ *  HWND           hwndOwner, //[in ] Parent window for any output
++ *  UINT           cidl,      //[in ] array size
++ *  LPCITEMIDLIST* apidl,     //[in ] simple pidl array
++ *  REFIID         riid,      //[in ] Requested Interface
++ *  UINT*          prgfInOut, //[   ] reserved
++ *  LPVOID*        ppvObject) //[out] Resulting Interface
++ *
++ */
++HRESULT WINAPI CDesktopFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST *apidl,
++                REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
++{
++    LPITEMIDLIST pidl;
++    IUnknown *pObj = NULL;
++    HRESULT hr = E_INVALIDARG;
++
++    TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
++       this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
++
++    if (!ppvOut)
++        return hr;
++
++    *ppvOut = NULL;
++
++    if (IsEqualIID (riid, IID_IContextMenu))
++    {
++        hr = CDefFolderMenu_Create2(pidlRoot, hwndOwner, cidl, apidl, (IShellFolder *)this, NULL, 0, NULL, (IContextMenu **)&pObj);
++    }
++    else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
++    {
++        hr = IDataObject_Constructor( hwndOwner, pidlRoot, apidl, cidl, (IDataObject **)&pObj);
++    }
++    else if (IsEqualIID (riid, IID_IExtractIconA) && (cidl == 1))
++    {
++        pidl = ILCombine (pidlRoot, apidl[0]);
++        pObj = (LPUNKNOWN) IExtractIconA_Constructor (pidl);
++        SHFree (pidl);
++        hr = S_OK;
++    }
++    else if (IsEqualIID (riid, IID_IExtractIconW) && (cidl == 1))
++    {
++        pidl = ILCombine (pidlRoot, apidl[0]);
++        pObj = (LPUNKNOWN) IExtractIconW_Constructor (pidl);
++        SHFree (pidl);
++        hr = S_OK;
++    }
++    else if (IsEqualIID (riid, IID_IDropTarget) && (cidl >= 1))
++    {
++        hr = this->QueryInterface (IID_IDropTarget, (LPVOID *)&pObj);
++    }
++    else if ((IsEqualIID(riid, IID_IShellLinkW) ||
++              IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
++    {
++        pidl = ILCombine (pidlRoot, apidl[0]);
++        hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj);
++        SHFree (pidl);
++    }
++    else
++        hr = E_NOINTERFACE;
++
++    if (SUCCEEDED(hr) && !pObj)
++        hr = E_OUTOFMEMORY;
++
++    *ppvOut = pObj;
++    TRACE ("(%p)->hr=0x%08x\n", this, hr);
++    return hr;
++}
++
++/**************************************************************************
++ *    ISF_Desktop_fnGetDisplayNameOf
++ *
++ * NOTES
++ *    special case: pidl = null gives desktop-name back
++ */
++HRESULT WINAPI CDesktopFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet)
++{
++    HRESULT hr = S_OK;
++    LPWSTR pszPath;
++
++    TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
++    pdump (pidl);
++
++    if (!strRet)
++        return E_INVALIDARG;
++
++    pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH +1) * sizeof(WCHAR));
++    if (!pszPath)
++        return E_OUTOFMEMORY;
++
++    if (_ILIsDesktop (pidl))
++    {
++        if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) &&
++            (GET_SHGDN_FOR (dwFlags) & SHGDN_FORPARSING))
++            wcscpy(pszPath, sPathTarget);
++        else
++            HCR_GetClassNameW(CLSID_ShellDesktop, pszPath, MAX_PATH);
++    }
++    else if (_ILIsPidlSimple (pidl))
++    {
++        GUID const *clsid;
++
++        if ((clsid = _ILGetGUIDPointer (pidl)))
++        {
++            if (GET_SHGDN_FOR (dwFlags) & SHGDN_FORPARSING)
++            {
++                int bWantsForParsing;
++
++                /*
++                 * We can only get a filesystem path from a shellfolder if the
++                 *  value WantsFORPARSING in CLSID\\{...}\\shellfolder exists.
++                 *
++                 * Exception: The MyComputer folder doesn't have this key,
++                 *   but any other filesystem backed folder it needs it.
++                 */
++                if (IsEqualIID (*clsid, CLSID_MyComputer))
++                {
++                    bWantsForParsing = TRUE;
++                }
++                else
++                {
++                    /* get the "WantsFORPARSING" flag from the registry */
++                    static const WCHAR clsidW[] =
++                     { 'C','L','S','I','D','\\',0 };
++                    static const WCHAR shellfolderW[] =
++                     { '\\','s','h','e','l','l','f','o','l','d','e','r',0 };
++                    static const WCHAR wantsForParsingW[] =
++                     { 'W','a','n','t','s','F','o','r','P','a','r','s','i','n',
++                     'g',0 };
++                    WCHAR szRegPath[100];
++                    LONG r;
++
++                    wcscpy (szRegPath, clsidW);
++                    SHELL32_GUIDToStringW (*clsid, &szRegPath[6]);
++                    wcscat (szRegPath, shellfolderW);
++                    r = SHGetValueW(HKEY_CLASSES_ROOT, szRegPath,
++                                    wantsForParsingW, NULL, NULL, NULL);
++                    if (r == ERROR_SUCCESS)
++                        bWantsForParsing = TRUE;
++                    else
++                        bWantsForParsing = FALSE;
++                }
++
++                if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) &&
++                     bWantsForParsing)
++                {
++                    /*
++                     * we need the filesystem path to the destination folder.
++                     * Only the folder itself can know it
++                     */
++                    hr = SHELL32_GetDisplayNameOfChild (this, pidl, dwFlags,
++                                                        pszPath,
++                                                        MAX_PATH);
++                }
++                else
++                {
++                    /* parsing name like ::{...} */
++                    pszPath[0] = ':';
++                    pszPath[1] = ':';
++                    SHELL32_GUIDToStringW (*clsid, &pszPath[2]);
++                }
++            }
++            else
++            {
++                /* user friendly name */
++                HCR_GetClassNameW (*clsid, pszPath, MAX_PATH);
++            }
++        }
++        else
++        {
++            int cLen = 0;
++
++            /* file system folder or file rooted at the desktop */
++            if ((GET_SHGDN_FOR(dwFlags) == SHGDN_FORPARSING) &&
++                (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER))
++            {
++                lstrcpynW(pszPath, sPathTarget, MAX_PATH - 1);
++                PathAddBackslashW(pszPath);
++                cLen = wcslen(pszPath);
++            }
++
++            _ILSimpleGetTextW(pidl, pszPath + cLen, MAX_PATH - cLen);
++            if (!_ILIsFolder(pidl))
++                SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
++
++            if (GetFileAttributes(pszPath) == INVALID_FILE_ATTRIBUTES)
++            {
++                /* file system folder or file rooted at the AllUsers desktop */
++                if ((GET_SHGDN_FOR(dwFlags) == SHGDN_FORPARSING) &&
++                    (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER))
++                {
++                    SHGetSpecialFolderPathW(0, pszPath, CSIDL_COMMON_DESKTOPDIRECTORY, FALSE);
++                    PathAddBackslashW(pszPath);
++                    cLen = wcslen(pszPath);
++                }
++
++                _ILSimpleGetTextW(pidl, pszPath + cLen, MAX_PATH - cLen);
++                if (!_ILIsFolder(pidl))
++                    SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
++            }        }
++    }
++    else
++    {
++        /* a complex pidl, let the subfolder do the work */
++        hr = SHELL32_GetDisplayNameOfChild (this, pidl, dwFlags,
++                                            pszPath, MAX_PATH);
++    }
++
++    if (SUCCEEDED(hr))
++    {
++        /* Win9x always returns ANSI strings, NT always returns Unicode strings */
++        if (GetVersion() & 0x80000000)
++        {
++            strRet->uType = STRRET_CSTR;
++            if (!WideCharToMultiByte(CP_ACP, 0, pszPath, -1, strRet->cStr, MAX_PATH,
++                                     NULL, NULL))
++                strRet->cStr[0] = '\0';
++            CoTaskMemFree(pszPath);
++        }
++        else
++        {
++            strRet->uType = STRRET_WSTR;
++            strRet->pOleStr = pszPath;
++        }
++    }
++    else
++        CoTaskMemFree(pszPath);
++
++    TRACE ("-- (%p)->(%s,0x%08x)\n", this,
++     strRet->uType == STRRET_CSTR ? strRet->cStr :
++     debugstr_w(strRet->pOleStr), hr);
++    return hr;
++}
++
++/**************************************************************************
++ *  ISF_Desktop_fnSetNameOf
++ *  Changes the name of a file object or subfolder, possibly changing its item
++ *  identifier in the process.
++ *
++ * PARAMETERS
++ *  HWND          hwndOwner,  //[in ] Owner window for output
++ *  LPCITEMIDLIST pidl,       //[in ] simple pidl of item to change
++ *  LPCOLESTR     lpszName,   //[in ] the items new display name
++ *  DWORD         dwFlags,    //[in ] SHGNO formatting flags
++ *  LPITEMIDLIST* ppidlOut)   //[out] simple pidl returned
++ */
++HRESULT WINAPI CDesktopFolder::SetNameOf(HWND hwndOwner, LPCITEMIDLIST pidl,    /* simple pidl */
++                LPCOLESTR lpName, DWORD dwFlags, LPITEMIDLIST *pPidlOut)
++{
++    CComPtr<IShellFolder2>                psf;
++    HRESULT hr;
++    WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
++    LPWSTR ptr;
++    BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
++
++    TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
++           debugstr_w (lpName), dwFlags, pPidlOut);
++
++    if (_ILGetGUIDPointer(pidl))
++    {
++        if (SUCCEEDED(BindToObject(pidl, NULL, IID_IShellFolder2, (LPVOID *)&psf)))
++        {
++            hr = psf->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
++            return hr;
++        }
++    }
++
++    /* build source path */
++    lstrcpynW(szSrc, sPathTarget, MAX_PATH);
++    ptr = PathAddBackslashW (szSrc);
++    if (ptr)
++        _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc));
++
++    /* build destination path */
++    if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) {
++        lstrcpynW(szDest, sPathTarget, MAX_PATH);
++        ptr = PathAddBackslashW (szDest);
++        if (ptr)
++            lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest));
++    } else
++        lstrcpynW(szDest, lpName, MAX_PATH);
++
++    if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
++        WCHAR *ext = PathFindExtensionW(szSrc);
++        if(*ext != '\0') {
++            INT len = wcslen(szDest);
++            lstrcpynW(szDest + len, ext, MAX_PATH - len);
++        }
++    }
++
++    if (!memcmp(szSrc, szDest, (wcslen(szDest)+1) * sizeof(WCHAR)))
++    {
++        /* src and destination is the same */
++        hr = S_OK;
++        if (pPidlOut)
++            hr = _ILCreateFromPathW(szDest, pPidlOut);
++
++        return hr;
++    }
++
++    TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
++    if (MoveFileW (szSrc, szDest))
++    {
++        hr = S_OK;
++
++        if (pPidlOut)
++            hr = _ILCreateFromPathW(szDest, pPidlOut);
++
++        SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
++         SHCNF_PATHW, szSrc, szDest);
++
++        return hr;
++    }
++    return E_FAIL;
++}
++
++HRESULT WINAPI CDesktopFolder::GetDefaultSearchGUID(GUID *pguid)
++{
++    FIXME ("(%p)\n", this);
++    return E_NOTIMPL;
++}
++
++HRESULT WINAPI CDesktopFolder::EnumSearches(IEnumExtraSearch **ppenum)
++{
++    FIXME ("(%p)\n", this);
++    return E_NOTIMPL;
++}
++
++HRESULT WINAPI CDesktopFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
++{
++    TRACE ("(%p)\n", this);
++
++    if (pSort)
++        *pSort = 0;
++    if (pDisplay)
++        *pDisplay = 0;
++
++    return S_OK;
++}
++
++HRESULT WINAPI CDesktopFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
++{
++    TRACE ("(%p)\n", this);
++
++    if (!pcsFlags || iColumn >= DESKTOPSHELLVIEWCOLUMNS)
++    return E_INVALIDARG;
++
++    *pcsFlags = DesktopSFHeader[iColumn].pcsFlags;
++
++    return S_OK;
++}
++
++HRESULT WINAPI CDesktopFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
++{
++    FIXME ("(%p)\n", this);
++
++    return E_NOTIMPL;
++}
++
++HRESULT WINAPI CDesktopFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *psd)
++{
++    HRESULT hr = S_OK;
++
++    TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
++
++    if (!psd || iColumn >= DESKTOPSHELLVIEWCOLUMNS)
++        return E_INVALIDARG;
++
++    if (!pidl)
++    {
++        psd->fmt = DesktopSFHeader[iColumn].fmt;
++        psd->cxChar = DesktopSFHeader[iColumn].cxChar;
++        psd->str.uType = STRRET_CSTR;
++        LoadStringA (shell32_hInstance, DesktopSFHeader[iColumn].colnameid,
++                     psd->str.cStr, MAX_PATH);
++        return S_OK;
++    }
++
++    /* the data from the pidl */
++    psd->str.uType = STRRET_CSTR;
++    switch (iColumn)
++    {
++    case 0:        /* name */
++        hr = GetDisplayNameOf(pidl,
++                   SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
++        break;
++    case 1:        /* size */
++        _ILGetFileSize (pidl, psd->str.cStr, MAX_PATH);
++        break;
++    case 2:        /* type */
++        _ILGetFileType (pidl, psd->str.cStr, MAX_PATH);
++        break;
++    case 3:        /* date */
++        _ILGetFileDate (pidl, psd->str.cStr, MAX_PATH);
++        break;
++    case 4:        /* attributes */
++        _ILGetFileAttributes (pidl, psd->str.cStr, MAX_PATH);
++        break;
++    }
++
++    return hr;
++}
++
++HRESULT WINAPI CDesktopFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
++{
++    FIXME ("(%p)\n", this);
++    return E_NOTIMPL;
++}
++
++HRESULT WINAPI CDesktopFolder::GetClassID(CLSID *lpClassId)
++{
++    TRACE ("(%p)\n", this);
++
++    if (!lpClassId)
++        return E_POINTER;
++
++    *lpClassId = CLSID_ShellDesktop;
++
++    return S_OK;
++}
++
++HRESULT WINAPI CDesktopFolder::Initialize(LPCITEMIDLIST pidl)
++{
++    TRACE ("(%p)->(%p)\n", this, pidl);
++
++    return E_NOTIMPL;
++}
++
++HRESULT WINAPI CDesktopFolder::GetCurFolder(LPITEMIDLIST * pidl)
++{
++    TRACE ("(%p)->(%p)\n", this, pidl);
++
++    if (!pidl) return E_POINTER;
++    *pidl = ILClone (pidlRoot);
++    return S_OK;
++}
++
++HRESULT WINAPI CDesktopFolder::GetUniqueName(LPWSTR pwszName, UINT uLen)
++{
++    CComPtr<IEnumIDList>                penum;
++    HRESULT hr;
++    WCHAR wszText[MAX_PATH];
++    WCHAR wszNewFolder[25];
++    const WCHAR wszFormat[] = {'%','s',' ','%','d',0 };
++
++    LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder,  sizeof(wszNewFolder)/sizeof(WCHAR));
++
++    TRACE ("(%p)(%p %u)\n", this, pwszName, uLen);
++
++    if (uLen < sizeof(wszNewFolder)/sizeof(WCHAR) + 3)
++        return E_POINTER;
++
++    lstrcpynW (pwszName, wszNewFolder, uLen);
++
++    hr = EnumObjects(0,
++     SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penum);
++    if (penum) {
++        LPITEMIDLIST pidl;
++        DWORD dwFetched;
++        int i = 1;
++
++next:
++        penum->Reset ();
++        while (S_OK == penum->Next(1, &pidl, &dwFetched) &&
++         dwFetched) {
++            _ILSimpleGetTextW (pidl, wszText, MAX_PATH);
++            if (0 == lstrcmpiW (wszText, pwszName)) {
++                _snwprintf (pwszName, uLen, wszFormat, wszNewFolder, i++);
++                if (i > 99) {
++                    hr = E_FAIL;
++                    break;
++                }
++                goto next;
++            }
++        }
++
++    }
++    return hr;
++}
++
++HRESULT WINAPI CDesktopFolder::AddFolder(HWND hwnd, LPCWSTR pwszName, LPITEMIDLIST *ppidlOut)
++{
++    WCHAR wszNewDir[MAX_PATH];
++    DWORD bRes;
++    HRESULT hres = E_FAIL;
++
++    TRACE ("(%p)(%s %p)\n", this, debugstr_w(pwszName), ppidlOut);
++
++    wszNewDir[0] = 0;
++    if (sPathTarget)
++        lstrcpynW(wszNewDir, sPathTarget, MAX_PATH);
++    PathAppendW(wszNewDir, pwszName);
++    bRes = CreateDirectoryW (wszNewDir, NULL);
++    if (bRes) 
++    {
++        SHChangeNotify (SHCNE_MKDIR, SHCNF_PATHW, wszNewDir, NULL);
++        hres = S_OK;
++        if (ppidlOut)
++                hres = _ILCreateFromPathW(wszNewDir, ppidlOut);
++    }
++
++    return hres;
++}
++
++HRESULT WINAPI CDesktopFolder::DeleteItems(UINT cidl, LPCITEMIDLIST *apidl)
++{
++    UINT i;
++    SHFILEOPSTRUCTW op;
++    WCHAR wszPath[MAX_PATH];
++    WCHAR wszCaption[50];
++    WCHAR *wszPathsList;
++    HRESULT ret;
++    WCHAR *wszCurrentPath;
++    UINT bRestoreWithDeskCpl = FALSE;
++    int res;
++
++    TRACE ("(%p)(%u %p)\n", this, cidl, apidl);
++    if (cidl==0) return S_OK;
++
++    for(i = 0; i < cidl; i++)
++    {
++        if (_ILIsMyComputer(apidl[i]))
++            bRestoreWithDeskCpl++;
++        else if (_ILIsNetHood(apidl[i]))
++            bRestoreWithDeskCpl++;
++        else if (_ILIsMyDocuments(apidl[i]))
++            bRestoreWithDeskCpl++;
++    }
++
++    if (bRestoreWithDeskCpl)
++    {
++        /* FIXME use FormatMessage
++         * use a similar message resource as in windows
++         */
++        LoadStringW(shell32_hInstance, IDS_DELETEMULTIPLE_TEXT, wszPath, sizeof(wszPath)/sizeof(WCHAR));
++        wszPath[(sizeof(wszPath)/sizeof(WCHAR))-1] = 0;
++
++        LoadStringW(shell32_hInstance, IDS_DELETEITEM_CAPTION, wszCaption, sizeof(wszCaption)/sizeof(WCHAR));
++        wszCaption[(sizeof(wszCaption)/sizeof(WCHAR))-1] = 0;
++
++        res = SHELL_ConfirmMsgBox(GetActiveWindow(), wszPath, wszCaption, NULL, cidl > 1);
++        if (res == IDD_YESTOALL || res == IDYES)
++        {
++            for(i = 0; i < cidl; i++)
++            {
++                if (_ILIsMyComputer(apidl[i]))
++                    SetNamespaceExtensionVisibleStatus(L"{20D04FE0-3AEA-1069-A2D8-08002B30309D}", 0x1);
++                else if (_ILIsNetHood(apidl[i]))
++                    SetNamespaceExtensionVisibleStatus(L"{208D2C60-3AEA-1069-A2D7-08002B30309D}", 0x1);
++                else if (_ILIsMyDocuments(apidl[i]))
++                    SetNamespaceExtensionVisibleStatus(L"{450D8FBA-AD25-11D0-98A8-0800361B1103}", 0x1);
++            }
++        }
++    }
++    if (sPathTarget)
++        lstrcpynW(wszPath, sPathTarget, MAX_PATH);
++    else
++        wszPath[0] = '\0';
++
++    PathAddBackslashW(wszPath);
++    wszPathsList = build_paths_list(wszPath, cidl, apidl);
++
++    ZeroMemory(&op, sizeof(op));
++    op.hwnd = GetActiveWindow();
++    op.wFunc = FO_DELETE;
++    op.pFrom = wszPathsList;
++    op.fFlags = FOF_ALLOWUNDO;
++    if (SHFileOperationW(&op))
++    {
++        WARN("SHFileOperation failed\n");
++        ret = E_FAIL;
++    }
++    else
++        ret = S_OK;
++
++    /* we currently need to manually send the notifies */
++    wszCurrentPath = wszPathsList;
++    for (i = 0; i < cidl; i++)
++    {
++        LONG wEventId;
++
++        if (_ILIsFolder(apidl[i]))
++            wEventId = SHCNE_RMDIR;
++        else if (_ILIsValue(apidl[i]))
++            wEventId = SHCNE_DELETE;
++        else
++            continue;
++
++        /* check if file exists */
++        if (GetFileAttributesW(wszCurrentPath) == INVALID_FILE_ATTRIBUTES)
++        {
++            LPITEMIDLIST pidl = ILCombine(pidlRoot, apidl[i]);
++            SHChangeNotify(wEventId, SHCNF_IDLIST, pidl, NULL);
++            SHFree(pidl);
++        }
++
++        wszCurrentPath += wcslen(wszCurrentPath)+1;
++    }
++    HeapFree(GetProcessHeap(), 0, wszPathsList);
++    return ret;
++}
++
++HRESULT WINAPI CDesktopFolder::CopyItems(IShellFolder *pSFFrom, UINT cidl, LPCITEMIDLIST *apidl)
++{
++    CComPtr<IPersistFolder2>            ppf2;
++    WCHAR szSrcPath[MAX_PATH];
++    WCHAR szTargetPath[MAX_PATH];
++    SHFILEOPSTRUCTW op;
++    LPITEMIDLIST pidl;
++    LPWSTR pszSrc, pszTarget, pszSrcList, pszTargetList, pszFileName;
++    int res, length;
++    STRRET strRet;
++
++    TRACE ("(%p)->(%p,%u,%p)\n", this, pSFFrom, cidl, apidl);
++
++    pSFFrom->QueryInterface(IID_IPersistFolder2, (LPVOID *)&ppf2);
++    if (ppf2) 
++    {
++        if (FAILED(ppf2->GetCurFolder(&pidl)))
++            return E_FAIL;
++
++        if (FAILED(pSFFrom->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strRet)))
++        {
++            SHFree (pidl);
++            return E_FAIL;
++        }
++
++        if (FAILED(StrRetToBufW(&strRet, pidl, szSrcPath, MAX_PATH)))
++        {
++            SHFree (pidl);
++            return E_FAIL;
++        }
++        SHFree (pidl);
++
++        pszSrc = PathAddBackslashW (szSrcPath);
++
++        wcscpy(szTargetPath, sPathTarget);
++        pszTarget = PathAddBackslashW (szTargetPath);
++
++        pszSrcList = build_paths_list(szSrcPath, cidl, apidl);
++        pszTargetList = build_paths_list(szTargetPath, cidl, apidl);
++
++        if (!pszSrcList || !pszTargetList)
++        {
++            if (pszSrcList)
++                HeapFree(GetProcessHeap(), 0, pszSrcList);
++
++            if (pszTargetList)
++                HeapFree(GetProcessHeap(), 0, pszTargetList);
++
++            SHFree (pidl);
++            return E_OUTOFMEMORY;
++        }
++        ZeroMemory(&op, sizeof(op));
++        if (!pszSrcList[0])
++        {
++            /* remove trailing backslash */
++            pszSrc--;
++            pszSrc[0] = L'\0';
++            op.pFrom = szSrcPath;
++        }
++        else
++        {
++            op.pFrom = pszSrcList;
++        }
++
++        if (!pszTargetList[0])
++        {
++            /* remove trailing backslash */
++            if (pszTarget - szTargetPath > 3)
++            {
++                pszTarget--;
++                pszTarget[0] = L'\0';
++            }
++            else
++            {
++                pszTarget[1] = L'\0';
++            }
++
++            op.pTo = szTargetPath;
++        }
++        else
++        {
++            op.pTo = pszTargetList;
++        }
++        op.hwnd = GetActiveWindow();
++        op.wFunc = FO_COPY;
++        op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR;
++
++        res = SHFileOperationW(&op);
++
++        if (res == DE_SAMEFILE)
++        {
++            length = wcslen(szTargetPath);
++
++
++            pszFileName = wcsrchr(pszSrcList, '\\');
++            pszFileName++;
++
++            if (LoadStringW(shell32_hInstance, IDS_COPY_OF, pszTarget, MAX_PATH - length))
++            {
++                wcscat(szTargetPath, L" ");
++            }
++
++            wcscat(szTargetPath, pszFileName);
++            op.pTo = szTargetPath;
++
++            res = SHFileOperationW(&op);
++        }
++
++
++        HeapFree(GetProcessHeap(), 0, pszSrcList);
++        HeapFree(GetProcessHeap(), 0, pszTargetList);
++
++        if (res)
++            return E_FAIL;
++        else
++            return S_OK;
++    }
++    return E_FAIL;
++}
index 0000000,4151ced..4151ced
mode 000000,100644..100644
--- /dev/null
index 0000000,6523659..6523659
mode 000000,100644..100644
--- /dev/null
index 0000000,0000000..58f7607
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,876 @@@
++/*
++ *    Virtual Workplace folder
++ *
++ *    Copyright 1997                Marcus Meissner
++ *    Copyright 1998, 1999, 2002    Juergen Schmied
++ *    Copyright 2009                Andrew Hill
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
++ */
++
++#include <precomp.h>
++
++WINE_DEFAULT_DEBUG_CHANNEL (shell);
++
++/*
++CDrivesFolder should create a CRegFolder to represent the virtual items that exist only in
++the registry. The CRegFolder is aggregated by the CDrivesFolder.
++The CDrivesFolderEnum class should enumerate only drives on the system. Since the CRegFolder
++implementation of IShellFolder::EnumObjects enumerates the virtual items, the
++CDrivesFolderEnum is only responsible for returning the physical items.
++
++2. At least on my XP system, the drive pidls returned are of type PT_DRIVE1, not PT_DRIVE
++3. The parsing name returned for my computer is incorrect. It should be "My Computer"
++*/
++
++/***********************************************************************
++*   IShellFolder implementation
++*/
++
++class CDrivesFolderEnum :
++    public IEnumIDListImpl
++{
++private:
++public:
++    CDrivesFolderEnum();
++    ~CDrivesFolderEnum();
++    HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags);
++    BOOL CreateMyCompEnumList(DWORD dwFlags);
++
++BEGIN_COM_MAP(CDrivesFolderEnum)
++    COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
++END_COM_MAP()
++};
++
++/***********************************************************************
++*   IShellFolder [MyComputer] implementation
++*/
++
++static const shvheader MyComputerSFHeader[] = {
++    {IDS_SHV_COLUMN1, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
++    {IDS_SHV_COLUMN3, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
++    {IDS_SHV_COLUMN6, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
++    {IDS_SHV_COLUMN7, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
++};
++
++#define MYCOMPUTERSHELLVIEWCOLUMNS 4
++
++CDrivesFolderEnum::CDrivesFolderEnum()
++{
++}
++
++CDrivesFolderEnum::~CDrivesFolderEnum()
++{
++}
++
++HRESULT WINAPI CDrivesFolderEnum::Initialize(HWND hwndOwner, DWORD dwFlags)
++{
++    if (CreateMyCompEnumList(dwFlags) == FALSE)
++        return E_FAIL;
++    return S_OK;
++}
++
++/**************************************************************************
++ *  CreateMyCompEnumList()
++ */
++static const WCHAR MyComputer_NameSpaceW[] = { 'S','O','F','T','W','A','R','E',
++ '\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
++ 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l',
++ 'o','r','e','r','\\','M','y','C','o','m','p','u','t','e','r','\\','N','a','m',
++ 'e','s','p','a','c','e','\0' };
++
++BOOL CDrivesFolderEnum::CreateMyCompEnumList(DWORD dwFlags)
++{
++    BOOL ret = TRUE;
++
++    TRACE("(%p)->(flags=0x%08x)\n", this, dwFlags);
++
++    /* enumerate the folders */
++    if (dwFlags & SHCONTF_FOLDERS)
++    {
++        WCHAR wszDriveName[] = {'A', ':', '\\', '\0'};
++        DWORD dwDrivemap = GetLogicalDrives();
++        HKEY hkey;
++        UINT i;
++
++        while (ret && wszDriveName[0]<='Z')
++        {
++            if(dwDrivemap & 0x00000001L)
++                ret = AddToEnumList(_ILCreateDrive(wszDriveName));
++            wszDriveName[0]++;
++            dwDrivemap = dwDrivemap >> 1;
++        }
++
++        TRACE("-- (%p)-> enumerate (mycomputer shell extensions)\n", this);
++        for (i=0; i<2; i++)
++        {
++            if (ret && !RegOpenKeyExW(i == 0 ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
++                                      MyComputer_NameSpaceW, 0, KEY_READ, &hkey))
++            {
++                WCHAR iid[50];
++                int i=0;
++
++                while (ret)
++                {
++                    DWORD size;
++                    LONG r;
++
++                    size = sizeof(iid) / sizeof(iid[0]);
++                    r = RegEnumKeyExW(hkey, i, iid, &size, 0, NULL, NULL, NULL);
++                    if (ERROR_SUCCESS == r)
++                    {
++                        /* FIXME: shell extensions, shouldn't the type be
++                         * PT_SHELLEXT? */
++                        LPITEMIDLIST pidl = _ILCreateGuidFromStrW(iid);
++                        if (pidl != NULL)
++                            ret = AddToEnumList(pidl);
++                        i++;
++                    }
++                    else if (ERROR_NO_MORE_ITEMS == r)
++                        break;
++                    else
++                        ret = FALSE;
++                }
++                RegCloseKey(hkey);
++            }
++        }
++    }
++    return ret;
++}
++
++CDrivesFolder::CDrivesFolder()
++{
++    pidlRoot = NULL;
++    sName = NULL;
++}
++
++CDrivesFolder::~CDrivesFolder()
++{
++    TRACE ("-- destroying IShellFolder(%p)\n", this);
++    SHFree(pidlRoot);
++}
++
++HRESULT WINAPI CDrivesFolder::FinalConstruct()
++{
++    DWORD dwSize;
++    WCHAR szName[MAX_PATH];
++
++    pidlRoot = _ILCreateMyComputer();    /* my qualified pidl */
++    if (pidlRoot == NULL)
++        return E_OUTOFMEMORY;
++
++    dwSize = sizeof(szName);
++    if (RegGetValueW(HKEY_CURRENT_USER,
++                     L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
++                     NULL, RRF_RT_REG_SZ, NULL, szName, &dwSize) == ERROR_SUCCESS)
++    {
++        szName[MAX_PATH - 1] = 0;
++        sName = (LPWSTR)SHAlloc((wcslen(szName) + 1) * sizeof(WCHAR));
++        if (sName)
++        {
++            wcscpy(sName, szName);
++        }
++        TRACE("sName %s\n", debugstr_w(sName));
++    }
++    return S_OK;
++}
++
++/**************************************************************************
++*    ISF_MyComputer_fnParseDisplayName
++*/
++HRESULT WINAPI CDrivesFolder::ParseDisplayName (HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
++               DWORD * pchEaten, LPITEMIDLIST * ppidl, DWORD * pdwAttributes)
++{
++    HRESULT hr = E_INVALIDARG;
++    LPCWSTR szNext = NULL;
++    WCHAR szElement[MAX_PATH];
++    LPITEMIDLIST pidlTemp = NULL;
++    CLSID clsid;
++
++    TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this,
++          hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
++          pchEaten, ppidl, pdwAttributes);
++
++    *ppidl = 0;
++    if (pchEaten)
++        *pchEaten = 0;        /* strange but like the original */
++
++    /* handle CLSID paths */
++    if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':')
++    {
++        szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH);
++        TRACE ("-- element: %s\n", debugstr_w (szElement));
++        CLSIDFromString (szElement + 2, &clsid);
++        pidlTemp = _ILCreateGuid (PT_GUID, clsid);
++    }
++    /* do we have an absolute path name ? */
++    else if (PathGetDriveNumberW (lpszDisplayName) >= 0 &&
++              lpszDisplayName[2] == (WCHAR) '\\')
++    {
++        szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH);
++        /* make drive letter uppercase to enable PIDL comparison */
++        szElement[0] = toupper(szElement[0]);
++        pidlTemp = _ILCreateDrive (szElement);
++    }
++
++    if (szNext && *szNext)
++    {
++        hr = SHELL32_ParseNextElement (this, hwndOwner, pbc, &pidlTemp,
++                              (LPOLESTR) szNext, pchEaten, pdwAttributes);
++    }
++    else
++    {
++        if (pdwAttributes && *pdwAttributes)
++            SHELL32_GetItemAttributes (this,
++                                       pidlTemp, pdwAttributes);
++        hr = S_OK;
++    }
++
++    *ppidl = pidlTemp;
++
++    TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr);
++
++    return hr;
++}
++
++/**************************************************************************
++*        ISF_MyComputer_fnEnumObjects
++*/
++HRESULT WINAPI CDrivesFolder::EnumObjects (HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
++{
++    CComObject<CDrivesFolderEnum>            *theEnumerator;
++    CComPtr<IEnumIDList>                    result;
++    HRESULT                                    hResult;
++
++    TRACE ("(%p)->(HWND=%p flags=0x%08x pplist=%p)\n", this, hwndOwner, dwFlags, ppEnumIDList);
++
++    if (ppEnumIDList == NULL)
++        return E_POINTER;
++    
++    *ppEnumIDList = NULL;
++    ATLTRY (theEnumerator = new CComObject<CDrivesFolderEnum>);
++    
++    if (theEnumerator == NULL)
++        return E_OUTOFMEMORY;
++    
++    hResult = theEnumerator->QueryInterface (IID_IEnumIDList, (void **)&result);
++    if (FAILED (hResult))
++    {
++        delete theEnumerator;
++        return hResult;
++    }
++
++    hResult = theEnumerator->Initialize (hwndOwner, dwFlags);
++    if (FAILED (hResult))
++        return hResult;
++    *ppEnumIDList = result.Detach ();
++
++    TRACE ("-- (%p)->(new ID List: %p)\n", this, *ppEnumIDList);
++
++    return S_OK;
++}
++
++/**************************************************************************
++*        ISF_MyComputer_fnBindToObject
++*/
++HRESULT WINAPI CDrivesFolder::BindToObject (LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
++{
++    TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this,
++          pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
++
++    return SHELL32_BindToChild (pidlRoot, NULL, pidl, riid, ppvOut);
++}
++
++/**************************************************************************
++*    ISF_MyComputer_fnBindToStorage
++*/
++HRESULT WINAPI CDrivesFolder::BindToStorage (LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
++{
++    FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this,
++          pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
++
++    *ppvOut = NULL;
++    return E_NOTIMPL;
++}
++
++/**************************************************************************
++*     ISF_MyComputer_fnCompareIDs
++*/
++
++HRESULT WINAPI CDrivesFolder::CompareIDs (LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
++{
++    int nReturn;
++
++    TRACE ("(%p)->(0x%08lx,pidl1=%p,pidl2=%p)\n", this, lParam, pidl1, pidl2);
++    nReturn = SHELL32_CompareIDs (this, lParam, pidl1, pidl2);
++    TRACE ("-- %i\n", nReturn);
++    return nReturn;
++}
++
++/**************************************************************************
++*    ISF_MyComputer_fnCreateViewObject
++*/
++HRESULT WINAPI CDrivesFolder::CreateViewObject (HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
++{
++    LPSHELLVIEW pShellView;
++    HRESULT hr = E_INVALIDARG;
++
++    TRACE("(%p)->(hwnd=%p,%s,%p)\n", this,
++          hwndOwner, shdebugstr_guid (&riid), ppvOut);
++
++    if (!ppvOut)
++        return hr;
++
++    *ppvOut = NULL;
++
++    if (IsEqualIID (riid, IID_IDropTarget))
++    {
++        WARN ("IDropTarget not implemented\n");
++        hr = E_NOTIMPL;
++    }
++    else if (IsEqualIID (riid, IID_IContextMenu))
++    {
++        WARN ("IContextMenu not implemented\n");
++        hr = E_NOTIMPL;
++    }
++    else if (IsEqualIID (riid, IID_IShellView))
++    {
++        hr = IShellView_Constructor ((IShellFolder *)this, &pShellView);
++        if (pShellView)
++        {
++            hr = pShellView->QueryInterface(riid, ppvOut);
++            pShellView->Release();
++        }
++    }
++    TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
++    return hr;
++}
++
++/**************************************************************************
++*  ISF_MyComputer_fnGetAttributesOf
++*/
++HRESULT WINAPI CDrivesFolder::GetAttributesOf (UINT cidl, LPCITEMIDLIST * apidl, DWORD * rgfInOut)
++{
++    HRESULT hr = S_OK;
++    static const DWORD dwComputerAttributes =
++        SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR | SFGAO_CANCOPY |
++        SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER | SFGAO_CANRENAME | SFGAO_CANDELETE;
++
++    TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
++           this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
++
++    if (!rgfInOut)
++        return E_INVALIDARG;
++    if (cidl && !apidl)
++        return E_INVALIDARG;
++
++    if (*rgfInOut == 0)
++        *rgfInOut = ~0;
++
++    if(cidl == 0) {
++        *rgfInOut &= dwComputerAttributes;
++    } else {
++        while (cidl > 0 && *apidl) {
++            pdump (*apidl);
++            SHELL32_GetItemAttributes (this, *apidl, rgfInOut);
++            apidl++;
++            cidl--;
++        }
++    }
++    /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
++    *rgfInOut &= ~SFGAO_VALIDATE;
++
++    TRACE ("-- result=0x%08x\n", *rgfInOut);
++    return hr;
++}
++
++/**************************************************************************
++*    ISF_MyComputer_fnGetUIObjectOf
++*
++* PARAMETERS
++*  hwndOwner [in]  Parent window for any output
++*  cidl      [in]  array size
++*  apidl     [in]  simple pidl array
++*  riid      [in]  Requested Interface
++*  prgfInOut [   ] reserved
++*  ppvObject [out] Resulting Interface
++*
++*/
++HRESULT WINAPI CDrivesFolder::GetUIObjectOf (HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid,
++                UINT * prgfInOut, LPVOID * ppvOut)
++{
++    LPITEMIDLIST pidl;
++    IUnknown *pObj = NULL;
++    HRESULT hr = E_INVALIDARG;
++
++    TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
++          hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
++
++    if (!ppvOut)
++        return hr;
++
++    *ppvOut = NULL;
++
++    if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1))
++    {
++        hr = CDefFolderMenu_Create2(pidlRoot, hwndOwner, cidl, apidl, (IShellFolder*)this, NULL, 0, NULL, (IContextMenu**)&pObj);
++    }
++    else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
++    {
++        hr = IDataObject_Constructor (hwndOwner,
++                                              pidlRoot, apidl, cidl, (IDataObject **)&pObj);
++    }
++    else if (IsEqualIID (riid, IID_IExtractIconA) && (cidl == 1))
++    {
++        pidl = ILCombine (pidlRoot, apidl[0]);
++        pObj = (LPUNKNOWN) IExtractIconA_Constructor (pidl);
++        SHFree (pidl);
++        hr = S_OK;
++    }
++    else if (IsEqualIID (riid, IID_IExtractIconW) && (cidl == 1))
++    {
++        pidl = ILCombine (pidlRoot, apidl[0]);
++        pObj = (LPUNKNOWN) IExtractIconW_Constructor (pidl);
++        SHFree (pidl);
++        hr = S_OK;
++    }
++    else if (IsEqualIID (riid, IID_IDropTarget) && (cidl >= 1))
++    {
++        hr = this->QueryInterface(IID_IDropTarget,
++                                          (LPVOID *) &pObj);
++    }
++    else if ((IsEqualIID(riid,IID_IShellLinkW) ||
++              IsEqualIID(riid,IID_IShellLinkA)) && (cidl == 1))
++    {
++        pidl = ILCombine (pidlRoot, apidl[0]);
++        hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*) &pObj);
++        SHFree (pidl);
++    }
++    else
++        hr = E_NOINTERFACE;
++
++    if (SUCCEEDED(hr) && !pObj)
++        hr = E_OUTOFMEMORY;
++
++    *ppvOut = pObj;
++    TRACE ("(%p)->hr=0x%08x\n", this, hr);
++    return hr;
++}
++
++/**************************************************************************
++*    ISF_MyComputer_fnGetDisplayNameOf
++*/
++HRESULT WINAPI CDrivesFolder::GetDisplayNameOf (LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet)
++{
++    LPWSTR pszPath;
++    HRESULT hr = S_OK;
++
++    TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
++    pdump (pidl);
++
++    if (!strRet)
++        return E_INVALIDARG;
++
++    pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH +1) * sizeof(WCHAR));
++    if (!pszPath)
++        return E_OUTOFMEMORY;
++
++    pszPath[0] = 0;
++
++    if (!pidl->mkid.cb)
++    {
++        /* parsing name like ::{...} */
++        pszPath[0] = ':';
++        pszPath[1] = ':';
++        SHELL32_GUIDToStringW(CLSID_MyComputer, &pszPath[2]);
++    }
++    else if (_ILIsPidlSimple(pidl))
++    {
++        /* take names of special folders only if its only this folder */
++        if (_ILIsSpecialFolder(pidl))
++        {
++            GUID const *clsid;
++
++            clsid = _ILGetGUIDPointer (pidl);
++            if (clsid)
++            {
++                if (GET_SHGDN_FOR (dwFlags) & SHGDN_FORPARSING)
++                {
++                    static const WCHAR clsidW[] =
++                     { 'C','L','S','I','D','\\',0 };
++                    static const WCHAR shellfolderW[] =
++                     { '\\','s','h','e','l','l','f','o','l','d','e','r',0 };
++                    static const WCHAR wantsForParsingW[] =
++                     { 'W','a','n','t','s','F','o','r','P','a','r','s','i','n',
++                     'g',0 };
++                    int bWantsForParsing = FALSE;
++                    WCHAR szRegPath[100];
++                    LONG r;
++
++                    /*
++                     * We can only get a filesystem path from a shellfolder
++                     * if the value WantsFORPARSING exists in
++                     *      CLSID\\{...}\\shellfolder
++                     * exception: the MyComputer folder has this keys not
++                     *            but like any filesystem backed
++                     *            folder it needs these behaviour
++                     *
++                     * Get the "WantsFORPARSING" flag from the registry
++                     */
++
++                    wcscpy (szRegPath, clsidW);
++                    SHELL32_GUIDToStringW (*clsid, &szRegPath[6]);
++                    wcscat (szRegPath, shellfolderW);
++                    r = SHGetValueW (HKEY_CLASSES_ROOT, szRegPath,
++                                     wantsForParsingW, NULL, NULL, NULL);
++                    if (r == ERROR_SUCCESS)
++                        bWantsForParsing = TRUE;
++
++                    if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) &&
++                        bWantsForParsing)
++                    {
++                        /*
++                         * We need the filesystem path to the destination folder
++                         * Only the folder itself can know it
++                         */
++                        hr = SHELL32_GetDisplayNameOfChild (this, pidl,
++                                                dwFlags, pszPath, MAX_PATH);
++                    }
++                    else
++                    {
++                        LPWSTR p = pszPath;
++
++                        /* parsing name like ::{...} */
++                        p[0] = ':';
++                        p[1] = ':';
++                        p += 2;
++                        p += SHELL32_GUIDToStringW(CLSID_MyComputer, p);
++
++                        /* \:: */
++                        p[0] = '\\';
++                        p[1] = ':';
++                        p[2] = ':';
++                        p += 3;
++                        SHELL32_GUIDToStringW(*clsid, p);
++                    }
++                }
++                else
++                {
++                    /* user friendly name */
++
++                    if (_ILIsMyComputer(pidl) && sName)
++                        wcscpy(pszPath, sName);
++                    else
++                        HCR_GetClassNameW (*clsid, pszPath, MAX_PATH);
++
++                    TRACE("pszPath %s\n", debugstr_w(pszPath));
++                }
++            }
++            else
++            {
++                /* append my own path */
++                _ILSimpleGetTextW (pidl, pszPath, MAX_PATH);
++            }
++        }
++        else if (_ILIsDrive(pidl))
++        {
++
++            _ILSimpleGetTextW (pidl, pszPath, MAX_PATH);    /* append my own path */
++            /* long view "lw_name (C:)" */
++            if (!(dwFlags & SHGDN_FORPARSING))
++            {
++                WCHAR wszDrive[18] = {0};
++                DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags;
++                static const WCHAR wszOpenBracket[] = {' ','(',0};
++                static const WCHAR wszCloseBracket[] = {')',0};
++
++                lstrcpynW(wszDrive, pszPath, 4);
++                pszPath[0] = L'\0';
++                GetVolumeInformationW (wszDrive, pszPath,
++                           MAX_PATH - 7,
++                           &dwVolumeSerialNumber,
++                           &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0);
++                pszPath[MAX_PATH-1] = L'\0';
++                if (!wcslen(pszPath))
++                {
++                    UINT DriveType, ResourceId;
++                    DriveType = GetDriveTypeW(wszDrive);
++                    switch(DriveType)
++                    {
++                        case DRIVE_FIXED:
++                            ResourceId = IDS_DRIVE_FIXED;
++                            break;
++                        case DRIVE_REMOTE:
++                            ResourceId = IDS_DRIVE_NETWORK;
++                            break;
++                        case DRIVE_CDROM:
++                            ResourceId = IDS_DRIVE_CDROM;
++                            break;
++                        default:
++                            ResourceId = 0;
++                    }
++                    if (ResourceId)
++                    {
++                        dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH);
++                        if (dwFileSystemFlags > MAX_PATH - 7)
++                            pszPath[MAX_PATH-7] = L'\0';
++                    }
++                }
++                wcscat (pszPath, wszOpenBracket);
++                wszDrive[2] = L'\0';
++                wcscat (pszPath, wszDrive);
++                wcscat (pszPath, wszCloseBracket);
++            }
++        }
++        else
++        {
++            /* Neither a shell namespace extension nor a drive letter. */
++            ERR("Wrong pidl type\n");
++            CoTaskMemFree(pszPath);
++            return E_INVALIDARG;
++        }
++    }
++    else
++    {
++        /* Complex pidl. Let the child folder do the work */
++        hr = SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, pszPath, MAX_PATH);
++    }
++
++    if (SUCCEEDED (hr))
++    {
++        strRet->uType = STRRET_WSTR;
++        strRet->pOleStr = pszPath;
++    }
++    else
++        CoTaskMemFree(pszPath);
++
++    TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
++    return hr;
++}
++
++/**************************************************************************
++*  ISF_MyComputer_fnSetNameOf
++*  Changes the name of a file object or subfolder, possibly changing its item
++*  identifier in the process.
++*
++* PARAMETERS
++*  hwndOwner  [in]   Owner window for output
++*  pidl       [in]   simple pidl of item to change
++*  lpszName   [in]   the items new display name
++*  dwFlags    [in]   SHGNO formatting flags
++*  ppidlOut   [out]  simple pidl returned
++*/
++HRESULT WINAPI CDrivesFolder::SetNameOf(HWND hwndOwner, LPCITEMIDLIST pidl,
++               LPCOLESTR lpName, DWORD dwFlags, LPITEMIDLIST * pPidlOut)
++{
++    LPWSTR sName;
++    HKEY hKey;
++    UINT length;
++    WCHAR szName[30];
++
++    TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this,
++           hwndOwner, pidl, debugstr_w (lpName), dwFlags, pPidlOut);
++
++    if (_ILIsDrive(pidl))
++    {
++       if (_ILSimpleGetTextW(pidl, szName, sizeof(szName)/sizeof(WCHAR)))
++       {
++           SetVolumeLabelW(szName, lpName);
++       }
++       if (pPidlOut)
++           *pPidlOut = _ILCreateDrive(szName);
++       return S_OK;
++    }
++
++
++    if (pPidlOut != NULL)
++    {
++        *pPidlOut = _ILCreateMyComputer();
++    }
++
++    length = (wcslen(lpName) + 1) * sizeof(WCHAR);
++    sName = (LPWSTR)SHAlloc(length);
++
++    if (!sName)
++    {
++        return E_OUTOFMEMORY;
++    }
++    
++    if (RegOpenKeyExW(HKEY_CURRENT_USER,
++                      L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
++                      0,
++                      KEY_WRITE,
++                      &hKey) != ERROR_SUCCESS)
++    {
++        WARN("Error: failed to open registry key\n");
++    }
++    else
++    {
++        RegSetValueExW(hKey, NULL, 0, REG_SZ, (const LPBYTE)lpName, length);
++        RegCloseKey(hKey);
++    }
++
++    wcscpy(sName, lpName);
++    SHFree(sName);
++    sName = sName;
++    TRACE("result %s\n", debugstr_w(sName));
++    return S_OK;
++}
++
++HRESULT WINAPI CDrivesFolder::GetDefaultSearchGUID(GUID * pguid)
++{
++    FIXME ("(%p)\n", this);
++    return E_NOTIMPL;
++}
++
++HRESULT WINAPI CDrivesFolder::EnumSearches(IEnumExtraSearch ** ppenum)
++{
++    FIXME ("(%p)\n", this);
++    return E_NOTIMPL;
++}
++
++HRESULT WINAPI CDrivesFolder::GetDefaultColumn (DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
++{
++    TRACE ("(%p)\n", this);
++
++    if (pSort)
++         *pSort = 0;
++    if (pDisplay)
++        *pDisplay = 0;
++    return S_OK;
++}
++
++HRESULT WINAPI CDrivesFolder::GetDefaultColumnState(UINT iColumn, DWORD * pcsFlags)
++{
++    TRACE ("(%p)\n", this);
++
++    if (!pcsFlags || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
++        return E_INVALIDARG;
++    *pcsFlags = MyComputerSFHeader[iColumn].pcsFlags;
++    return S_OK;
++}
++
++HRESULT WINAPI CDrivesFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID * pscid, VARIANT * pv)
++{
++    FIXME ("(%p)\n", this);
++    return E_NOTIMPL;
++}
++
++/* FIXME: drive size >4GB is rolling over */
++HRESULT WINAPI CDrivesFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS * psd)
++{
++    HRESULT hr;
++
++    TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
++
++    if (!psd || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
++        return E_INVALIDARG;
++
++    if (!pidl)
++    {
++        psd->fmt = MyComputerSFHeader[iColumn].fmt;
++        psd->cxChar = MyComputerSFHeader[iColumn].cxChar;
++        psd->str.uType = STRRET_CSTR;
++        LoadStringA (shell32_hInstance, MyComputerSFHeader[iColumn].colnameid,
++                     psd->str.cStr, MAX_PATH);
++        return S_OK;
++    }
++    else
++    {
++        char szPath[MAX_PATH];
++        ULARGE_INTEGER ulBytes;
++
++        psd->str.cStr[0] = 0x00;
++        psd->str.uType = STRRET_CSTR;
++        switch (iColumn)
++        {
++        case 0:        /* name */
++            hr = GetDisplayNameOf(pidl,
++                       SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
++            break;
++        case 1:        /* type */
++            _ILGetFileType (pidl, psd->str.cStr, MAX_PATH);
++            break;
++        case 2:        /* total size */
++            if (_ILIsDrive (pidl))
++            {
++                _ILSimpleGetText (pidl, szPath, MAX_PATH);
++                GetDiskFreeSpaceExA (szPath, NULL, &ulBytes, NULL);
++                StrFormatByteSizeA (ulBytes.LowPart, psd->str.cStr, MAX_PATH);
++            }
++            break;
++        case 3:        /* free size */
++            if (_ILIsDrive (pidl))
++            {
++                _ILSimpleGetText (pidl, szPath, MAX_PATH);
++                GetDiskFreeSpaceExA (szPath, &ulBytes, NULL, NULL);
++                StrFormatByteSizeA (ulBytes.LowPart, psd->str.cStr, MAX_PATH);
++            }
++            break;
++        }
++        hr = S_OK;
++    }
++
++    return hr;
++}
++
++HRESULT WINAPI CDrivesFolder::MapColumnToSCID(UINT column, SHCOLUMNID * pscid)
++{
++    FIXME ("(%p)\n", this);
++    return E_NOTIMPL;
++}
++
++/************************************************************************
++ *    IMCFldr_PersistFolder2_GetClassID
++ */
++HRESULT WINAPI CDrivesFolder::GetClassID(CLSID *lpClassId)
++{
++    TRACE ("(%p)\n", this);
++
++    if (!lpClassId)
++    return E_POINTER;
++    *lpClassId = CLSID_MyComputer;
++
++    return S_OK;
++}
++
++/************************************************************************
++ *    IMCFldr_PersistFolder2_Initialize
++ *
++ * NOTES: it makes no sense to change the pidl
++ */
++HRESULT WINAPI CDrivesFolder::Initialize(LPCITEMIDLIST pidl)
++{
++    TRACE ("(%p)->(%p)\n", this, pidl);
++
++    if (pidlRoot)
++        SHFree((LPVOID)pidlRoot);
++
++    pidlRoot = ILClone(pidl);
++    return S_OK;
++}
++
++/**************************************************************************
++ *    IPersistFolder2_fnGetCurFolder
++ */
++HRESULT WINAPI CDrivesFolder::GetCurFolder(LPITEMIDLIST *pidl)
++{
++    TRACE ("(%p)->(%p)\n", this, pidl);
++
++    if (!pidl)
++        return E_POINTER;
++    *pidl = ILClone (pidlRoot);
++    return S_OK;
++}
index 0000000,0000000..95e4def
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++/*
++ * UNIXFS - Shell namespace extension for the unix filesystem
++ *
++ * Copyright (C) 2005 Michael Jung
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
++ */
++
++/* Placeholder in ReactOS, we don't need this */
index 0000000,0000000..d4b30aa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2185 @@@
++/*
++ *          Shell Library Functions
++ *
++ * Copyright 1998 Marcus Meissner
++ * Copyright 2002 Eric Pouech
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
++ */
++
++#include <precomp.h>
++#include <windef.h>
++
++WINE_DEFAULT_DEBUG_CHANNEL(exec);
++
++static const WCHAR wszOpen[] = {'o','p','e','n',0};
++static const WCHAR wszExe[] = {'.','e','x','e',0};
++static const WCHAR wszILPtr[] = {':','%','p',0};
++static const WCHAR wszShell[] = {'\\','s','h','e','l','l','\\',0};
++static const WCHAR wszFolder[] = {'F','o','l','d','e','r',0};
++static const WCHAR wszEmpty[] = {0};
++
++#define SEE_MASK_CLASSALL (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY)
++
++static void ParseNoTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum)
++{
++    bool firstCharQuote = false;
++    bool quotes_opened = false;
++    bool backslash_encountered = false;
++
++    for (int curArg=0; curArg<=argNum && *args; ++curArg)
++    {
++        firstCharQuote = false;
++        if (*args == '"')
++        {
++            quotes_opened = true;
++            firstCharQuote = true;
++            args++;
++        }
++
++        while(*args)
++        {
++            if (*args == '\\')
++            {
++                // if we found a backslash then flip the variable
++                backslash_encountered = !backslash_encountered;
++            }
++            else if (*args == '"')
++            {
++                if (quotes_opened)
++                {
++                    if (*(args+1) != '"')
++                    {
++                        quotes_opened = false;
++                        args++;
++                        break;
++                    }
++                    else
++                    {
++                       args++;
++                    }
++                }
++                else
++                {
++                    quotes_opened = true;
++                }
++
++                backslash_encountered = false;
++            }
++            else
++            {
++                backslash_encountered = false;
++                if (*args == ' ' && !firstCharQuote)
++                    break;
++            }
++
++            if (curArg == argNum)
++            {
++                used++;
++                if (used < len)
++                    *res++ = *args;
++            }
++
++            args++;
++        }
++
++        while(*args == ' ')
++            ++args;
++    }
++}
++
++static void ParseTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum)
++{
++    bool quotes_opened = false;
++    bool backslash_encountered = false;
++
++    for (int curArg=0; curArg<=argNum && *args; ++curArg)
++    {
++        while(*args)
++        {
++            if (*args == '\\')
++            {
++                // if we found a backslash then flip the variable
++                backslash_encountered = !backslash_encountered;
++            }
++            else if (*args == '"')
++            {
++                if (quotes_opened)
++                {
++                    if (*(args+1) != '"')
++                    {
++                        quotes_opened = false;
++                    }
++                    else
++                    {
++                       args++;
++                    }
++                }
++                else
++                {
++                    quotes_opened = true;
++                }
++
++                backslash_encountered = false;
++            }
++            else
++            {
++                backslash_encountered = false;
++                if (*args == ' ' && !quotes_opened && curArg!=argNum)
++                    break;
++            }
++
++            if (curArg == argNum)
++            {
++                used++;
++                if (used < len)
++                    *res++ = *args;
++            }
++
++            args++;
++        }
++    }
++}
++
++/***********************************************************************
++ * SHELL_ArgifyW [Internal]
++ *
++ * this function is supposed to expand the escape sequences found in the registry
++ * some diving reported that the following were used:
++ * + %1, %2...  seem to report to parameter of index N in ShellExecute pmts
++ * %1 file
++ * %2 printer
++ * %3 driver
++ * %4 port
++ * %I address of a global item ID (explorer switch /idlist)
++ * %L seems to be %1 as long filename followed by the 8+3 variation
++ * %S ???
++ * %* all following parameters (see batfile)
++ *
++ * The way we parse the command line arguments was determined through extensive
++ * testing and can be summed up by the following rules"
++ *
++ * - %2
++ *     - if first letter is " break on first non literal " and include any white spaces
++ *     - if first letter is NOT " break on first " or white space
++ *     - if " is opened any pair of consecutive " results in ONE literal "
++ *
++ * - %~2
++ *     - use rules from here http://www.autohotkey.net/~deleyd/parameters/parameters.htm
++ */
++
++static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR* lpFile, LPITEMIDLIST pidl, LPCWSTR args, DWORD* out_len)
++{
++    WCHAR   xlpFile[1024];
++    BOOL    done = FALSE;
++    BOOL    found_p1 = FALSE;
++    PWSTR   res = out;
++    PCWSTR  cmd;
++    DWORD   used = 0;
++    bool    tildeEffect = false;
++
++    TRACE("Before parsing: %p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt),
++          debugstr_w(lpFile), pidl, args);
++
++    while (*fmt)
++    {
++        if (*fmt == '%')
++        {
++            switch (*++fmt)
++            {
++                case '\0':
++                case '%':
++                {
++                    used++;
++                    if (used < len)
++                        *res++ = '%';
++                }; break;
++
++                case '*':
++                {
++                    if (args)
++                    {
++                        if (*fmt == '*')
++                        {
++                            used++;
++                            while(*args)
++                            {
++                                used++;
++                                if (used < len)
++                                    *res++ = *args++;
++                                else
++                                    args++;
++                            }
++                            used++;
++                            break;
++                        }
++                    }
++                }; break;
++
++                case '~':
++
++                case '2':
++                case '3':
++                case '4':
++                case '5':
++                case '6':
++                case '7':
++                case '8':
++                case '9':
++                //case '0':
++                {
++                    if (*fmt == '~')
++                    {
++                        fmt++;
++                        tildeEffect = true;
++                    }
++
++                    if (args)
++                    {
++                        if (tildeEffect)
++                        {
++                            ParseTildeEffect(res, args, len, used, *fmt - '2');
++                            tildeEffect = false;
++                        }
++                        else
++                        {
++                            ParseNoTildeEffect(res, args, len, used, *fmt - '2');
++                        }
++                    }
++                }; break;
++
++                case '1':
++                    if (!done || (*fmt == '1'))
++                    {
++                        /*FIXME Is the call to SearchPathW() really needed? We already have separated out the parameter string in args. */
++                        if (SearchPathW(NULL, lpFile, wszExe, sizeof(xlpFile)/sizeof(WCHAR), xlpFile, NULL))
++                            cmd = xlpFile;
++                        else
++                            cmd = lpFile;
++
++                        used += wcslen(cmd);
++                        if (used < len)
++                        {
++                            wcscpy(res, cmd);
++                            res += wcslen(cmd);
++                        }
++                    }
++                    found_p1 = TRUE;
++                    break;
++
++                /*
++                 * IE uses this a lot for activating things such as windows media
++                 * player. This is not verified to be fully correct but it appears
++                 * to work just fine.
++                 */
++                case 'l':
++                case 'L':
++                    if (lpFile)
++                    {
++                        used += wcslen(lpFile);
++                        if (used < len)
++                        {
++                            wcscpy(res, lpFile);
++                            res += wcslen(lpFile);
++                        }
++                    }
++                    found_p1 = TRUE;
++                    break;
++
++                case 'i':
++                case 'I':
++                    if (pidl)
++                    {
++                        DWORD chars = 0;
++                        /* %p should not exceed 8, maybe 16 when looking forward to 64bit.
++                            * allowing a buffer of 100 should more than exceed all needs */
++                        WCHAR buf[100];
++                        LPVOID  pv;
++                        HGLOBAL hmem = SHAllocShared(pidl, ILGetSize(pidl), 0);
++                        pv = SHLockShared(hmem, 0);
++                        chars = swprintf(buf, wszILPtr, pv);
++
++                        if (chars >= sizeof(buf)/sizeof(WCHAR))
++                            ERR("pidl format buffer too small!\n");
++
++                        used += chars;
++
++                        if (used < len)
++                        {
++                            wcscpy(res,buf);
++                            res += chars;
++                        }
++                        SHUnlockShared(pv);
++                    }
++                    found_p1 = TRUE;
++                    break;
++
++                default:
++                    /*
++                     * Check if this is an env-variable here...
++                     */
++
++                    /* Make sure that we have at least one more %.*/
++                    if (strchrW(fmt, '%'))
++                    {
++                        WCHAR   tmpBuffer[1024];
++                        PWSTR   tmpB = tmpBuffer;
++                        WCHAR   tmpEnvBuff[MAX_PATH];
++                        DWORD   envRet;
++
++                        while (*fmt != '%')
++                            *tmpB++ = *fmt++;
++                        *tmpB++ = 0;
++
++                        TRACE("Checking %s to be an env-var\n", debugstr_w(tmpBuffer));
++
++                        envRet = GetEnvironmentVariableW(tmpBuffer, tmpEnvBuff, MAX_PATH);
++                        if (envRet == 0 || envRet > MAX_PATH)
++                        {
++                            used += wcslen(tmpBuffer);
++                            if (used < len)
++                            {
++                                wcscpy( res, tmpBuffer );
++                                res += wcslen(tmpBuffer);
++                            }
++                        }
++                        else
++                        {
++                            used += wcslen(tmpEnvBuff);
++                            if (used < len)
++                            {
++                                wcscpy( res, tmpEnvBuff );
++                                res += wcslen(tmpEnvBuff);
++                            }
++                        }
++                    }
++                    done = TRUE;
++                    break;
++            }
++            /* Don't skip past terminator (catch a single '%' at the end) */
++            if (*fmt != '\0')
++            {
++                fmt++;
++            }
++        }
++        else
++        {
++            used ++;
++            if (used < len)
++                *res++ = *fmt++;
++            else
++                fmt++;
++        }
++    }
++
++    *res = '\0';
++    TRACE("used %i of %i space\n",used,len);
++    if (out_len)
++        *out_len = used;
++
++    TRACE("After parsing: %p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt),
++          debugstr_w(lpFile), pidl, args);
++
++    return found_p1;
++}
++
++static HRESULT SHELL_GetPathFromIDListForExecuteW(LPCITEMIDLIST pidl, LPWSTR pszPath, UINT uOutSize)
++{
++    STRRET strret;
++    IShellFolder* desktop;
++
++    HRESULT hr = SHGetDesktopFolder(&desktop);
++
++    if (SUCCEEDED(hr))
++    {
++        hr = desktop->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strret);
++
++        if (SUCCEEDED(hr))
++            StrRetToStrNW(pszPath, uOutSize, &strret, pidl);
++
++        desktop->Release();
++    }
++
++    return hr;
++}
++
++/*************************************************************************
++ *    SHELL_ExecuteW [Internal]
++ *
++ */
++static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
++                const SHELLEXECUTEINFOW *psei, LPSHELLEXECUTEINFOW psei_out)
++{
++    STARTUPINFOW  startup;
++    PROCESS_INFORMATION info;
++    UINT_PTR retval = SE_ERR_NOASSOC;
++    UINT gcdret = 0;
++    WCHAR curdir[MAX_PATH];
++    DWORD dwCreationFlags;
++    const WCHAR *lpDirectory = NULL;
++
++    TRACE("Execute %s from directory %s\n", debugstr_w(lpCmd), debugstr_w(psei->lpDirectory));
++
++    /* make sure we don't fail the CreateProcess if the calling app passes in
++     * a bad working directory */
++    if (psei->lpDirectory && psei->lpDirectory[0])
++    {
++        DWORD attr = GetFileAttributesW(psei->lpDirectory);
++        if (attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_DIRECTORY)
++            lpDirectory = psei->lpDirectory;
++    }
++
++    /* ShellExecute specifies the command from psei->lpDirectory
++     * if present. Not from the current dir as CreateProcess does */
++    if ( lpDirectory )
++        if ( ( gcdret = GetCurrentDirectoryW( MAX_PATH, curdir)))
++            if ( !SetCurrentDirectoryW( lpDirectory))
++                ERR("cannot set directory %s\n", debugstr_w(lpDirectory));
++
++    ZeroMemory(&startup,sizeof(STARTUPINFOW));
++    startup.cb = sizeof(STARTUPINFOW);
++    startup.dwFlags = STARTF_USESHOWWINDOW;
++    startup.wShowWindow = psei->nShow;
++    dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
++
++    if (psei->fMask & SEE_MASK_NO_CONSOLE)
++        dwCreationFlags |= CREATE_NEW_CONSOLE;
++
++    if (CreateProcessW(NULL, (LPWSTR)lpCmd, NULL, NULL, FALSE, dwCreationFlags, env,
++                       lpDirectory, &startup, &info))
++    {
++        /* Give 30 seconds to the app to come up, if desired. Probably only needed
++           when starting app immediately before making a DDE connection. */
++        if (shWait)
++            if (WaitForInputIdle( info.hProcess, 30000 ) == WAIT_FAILED)
++                WARN("WaitForInputIdle failed: Error %d\n", GetLastError() );
++        retval = 33;
++
++        if (psei->fMask & SEE_MASK_NOCLOSEPROCESS)
++            psei_out->hProcess = info.hProcess;
++        else
++            CloseHandle( info.hProcess );
++        CloseHandle( info.hThread );
++    }
++    else if ((retval = GetLastError()) >= 32)
++    {
++        TRACE("CreateProcess returned error %ld\n", retval);
++        retval = ERROR_BAD_FORMAT;
++    }
++
++    TRACE("returning %lu\n", retval);
++
++    psei_out->hInstApp = (HINSTANCE)retval;
++
++    if( gcdret )
++        if( !SetCurrentDirectoryW( curdir))
++            ERR("cannot return to directory %s\n", debugstr_w(curdir));
++
++    return retval;
++}
++
++
++/***********************************************************************
++ *           SHELL_BuildEnvW    [Internal]
++ *
++ * Build the environment for the new process, adding the specified
++ * path to the PATH variable. Returned pointer must be freed by caller.
++ */
++static LPWSTR SHELL_BuildEnvW( const WCHAR *path )
++{
++    static const WCHAR wPath[] = {'P','A','T','H','=',0};
++    WCHAR *strings, *new_env;
++    WCHAR *p, *p2;
++    int total = wcslen(path) + 1;
++    BOOL got_path = FALSE;
++
++    if (!(strings = GetEnvironmentStringsW())) return NULL;
++    p = strings;
++    while (*p)
++    {
++        int len = wcslen(p) + 1;
++        if (!_wcsnicmp( p, wPath, 5 )) got_path = TRUE;
++        total += len;
++        p += len;
++    }
++    if (!got_path) total += 5;  /* we need to create PATH */
++    total++;  /* terminating null */
++
++    if (!(new_env = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, total * sizeof(WCHAR) )))
++    {
++        FreeEnvironmentStringsW( strings );
++        return NULL;
++    }
++    p = strings;
++    p2 = new_env;
++    while (*p)
++    {
++        int len = wcslen(p) + 1;
++        memcpy( p2, p, len * sizeof(WCHAR) );
++        if (!_wcsnicmp( p, wPath, 5 ))
++        {
++            p2[len - 1] = ';';
++            wcscpy( p2 + len, path );
++            p2 += wcslen(path) + 1;
++        }
++        p += len;
++        p2 += len;
++    }
++    if (!got_path)
++    {
++        wcscpy( p2, wPath );
++        wcscat( p2, path );
++        p2 += wcslen(p2) + 1;
++    }
++    *p2 = 0;
++    FreeEnvironmentStringsW( strings );
++    return new_env;
++}
++
++
++/***********************************************************************
++ *           SHELL_TryAppPathW    [Internal]
++ *
++ * Helper function for SHELL_FindExecutable
++ * @param lpResult - pointer to a buffer of size MAX_PATH
++ * On entry: szName is a filename (probably without path separators).
++ * On exit: if szName found in "App Path", place full path in lpResult, and return true
++ */
++static BOOL SHELL_TryAppPathW( LPCWSTR szName, LPWSTR lpResult, WCHAR **env)
++{
++    static const WCHAR wszKeyAppPaths[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s',
++    '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','A','p','p',' ','P','a','t','h','s','\\',0};
++    static const WCHAR wPath[] = {'P','a','t','h',0};
++    HKEY hkApp = 0;
++    WCHAR buffer[1024];
++    LONG len;
++    LONG res;
++    BOOL found = FALSE;
++
++    if (env) *env = NULL;
++    wcscpy(buffer, wszKeyAppPaths);
++    wcscat(buffer, szName);
++    res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hkApp);
++    if (res) goto end;
++
++    len = MAX_PATH*sizeof(WCHAR);
++    res = RegQueryValueW(hkApp, NULL, lpResult, &len);
++    if (res) goto end;
++    found = TRUE;
++
++    if (env)
++    {
++        DWORD count = sizeof(buffer);
++        if (!RegQueryValueExW(hkApp, wPath, NULL, NULL, (LPBYTE)buffer, &count) && buffer[0])
++            *env = SHELL_BuildEnvW( buffer );
++    }
++
++end:
++    if (hkApp) RegCloseKey(hkApp);
++    return found;
++}
++
++static UINT SHELL_FindExecutableByOperation(LPCWSTR lpOperation, LPWSTR key, LPWSTR filetype, LPWSTR command, LONG commandlen)
++{
++    static const WCHAR wCommand[] = {'\\','c','o','m','m','a','n','d',0};
++    HKEY hkeyClass;
++    WCHAR verb[MAX_PATH];
++
++    if (RegOpenKeyExW(HKEY_CLASSES_ROOT, filetype, 0, 0x02000000, &hkeyClass))
++        return SE_ERR_NOASSOC;
++    if (!HCR_GetDefaultVerbW(hkeyClass, lpOperation, verb, sizeof(verb)/sizeof(verb[0])))
++        return SE_ERR_NOASSOC;
++    RegCloseKey(hkeyClass);
++
++    /* Looking for ...buffer\shell\<verb>\command */
++    wcscat(filetype, wszShell);
++    wcscat(filetype, verb);
++    wcscat(filetype, wCommand);
++
++    if (RegQueryValueW(HKEY_CLASSES_ROOT, filetype, command,
++                       &commandlen) == ERROR_SUCCESS)
++    {
++        commandlen /= sizeof(WCHAR);
++        if (key) wcscpy(key, filetype);
++#if 0
++        LPWSTR tmp;
++        WCHAR param[256];
++        LONG paramlen = sizeof(param);
++        static const WCHAR wSpace[] = {' ',0};
++
++        /* FIXME: it seems all Windows version don't behave the same here.
++         * the doc states that this ddeexec information can be found after
++         * the exec names.
++         * on Win98, it doesn't appear, but I think it does on Win2k
++         */
++        /* Get the parameters needed by the application
++           from the associated ddeexec key */
++        tmp = strstrW(filetype, wCommand);
++        tmp[0] = '\0';
++        wcscat(filetype, wDdeexec);
++        if (RegQueryValueW(HKEY_CLASSES_ROOT, filetype, param,
++                           &paramlen) == ERROR_SUCCESS)
++        {
++            paramlen /= sizeof(WCHAR);
++            wcscat(command, wSpace);
++            wcscat(command, param);
++            commandlen += paramlen;
++        }
++#endif
++
++        command[commandlen] = '\0';
++
++        return 33; /* FIXME see SHELL_FindExecutable() */
++    }
++
++    return SE_ERR_NOASSOC;
++}
++
++/*************************************************************************
++ *    SHELL_FindExecutable [Internal]
++ *
++ * Utility for code sharing between FindExecutable and ShellExecute
++ * in:
++ *      lpFile the name of a file
++ *      lpOperation the operation on it (open)
++ * out:
++ *      lpResult a buffer, big enough :-(, to store the command to do the
++ *              operation on the file
++ *      key a buffer, big enough, to get the key name to do actually the
++ *              command (it'll be used afterwards for more information
++ *              on the operation)
++ */
++static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOperation,
++                                 LPWSTR lpResult, DWORD resultLen, LPWSTR key, WCHAR **env,LPITEMIDLIST pidl, LPCWSTR args)
++{
++    static const WCHAR wWindows[] = {'w','i','n','d','o','w','s',0};
++    static const WCHAR wPrograms[] = {'p','r','o','g','r','a','m','s',0};
++    static const WCHAR wExtensions[] = {'e','x','e',' ','p','i','f',' ','b','a','t',' ','c','m','d',' ','c','o','m',0};
++    WCHAR *extension = NULL; /* pointer to file extension */
++    WCHAR filetype[256];     /* registry name for this filetype */
++    LONG  filetypelen = sizeof(filetype); /* length of above */
++    WCHAR command[1024];     /* command from registry */
++    WCHAR wBuffer[256];      /* Used to GetProfileString */
++    UINT  retval = SE_ERR_NOASSOC;
++    WCHAR *tok;              /* token pointer */
++    WCHAR xlpFile[256];      /* result of SearchPath */
++    DWORD attribs;           /* file attributes */
++
++    TRACE("%s\n", debugstr_w(lpFile));
++
++    if (!lpResult)
++        return ERROR_INVALID_PARAMETER;
++
++    xlpFile[0] = '\0';
++    lpResult[0] = '\0'; /* Start off with an empty return string */
++    if (key) *key = '\0';
++
++    /* trap NULL parameters on entry */
++    if (!lpFile)
++    {
++        WARN("(lpFile=%s,lpResult=%s): NULL parameter\n",
++             debugstr_w(lpFile), debugstr_w(lpResult));
++        return ERROR_FILE_NOT_FOUND; /* File not found. Close enough, I guess. */
++    }
++
++    if (SHELL_TryAppPathW( lpFile, lpResult, env ))
++    {
++        TRACE("found %s via App Paths\n", debugstr_w(lpResult));
++        return 33;
++    }
++
++    if (SearchPathW(lpPath, lpFile, wszExe, sizeof(xlpFile)/sizeof(WCHAR), xlpFile, NULL))
++    {
++        TRACE("SearchPathW returned non-zero\n");
++        lpFile = xlpFile;
++        /* Hey, isn't this value ignored?  Why make this call?  Shouldn't we return here?  --dank*/
++    }
++
++    attribs = GetFileAttributesW(lpFile);
++    if (attribs!=INVALID_FILE_ATTRIBUTES && (attribs&FILE_ATTRIBUTE_DIRECTORY))
++    {
++       wcscpy(filetype, wszFolder);
++       filetypelen = 6;    /* strlen("Folder") */
++    }
++    else
++    {
++        /* Did we get something? Anything? */
++        if (xlpFile[0]==0)
++        {
++            TRACE("Returning SE_ERR_FNF\n");
++            return SE_ERR_FNF;
++        }
++        /* First thing we need is the file's extension */
++        extension = wcsrchr(xlpFile, '.'); /* Assume last "." is the one; */
++        /* File->Run in progman uses */
++        /* .\FILE.EXE :( */
++        TRACE("xlpFile=%s,extension=%s\n", debugstr_w(xlpFile), debugstr_w(extension));
++
++        if (extension == NULL || extension[1]==0)
++        {
++            WARN("Returning SE_ERR_NOASSOC\n");
++            return SE_ERR_NOASSOC;
++        }
++
++        /* Three places to check: */
++        /* 1. win.ini, [windows], programs (NB no leading '.') */
++        /* 2. Registry, HKEY_CLASS_ROOT\<filetype>\shell\open\command */
++        /* 3. win.ini, [extensions], extension (NB no leading '.' */
++        /* All I know of the order is that registry is checked before */
++        /* extensions; however, it'd make sense to check the programs */
++        /* section first, so that's what happens here. */
++
++        /* See if it's a program - if GetProfileString fails, we skip this
++         * section. Actually, if GetProfileString fails, we've probably
++         * got a lot more to worry about than running a program... */
++        if (GetProfileStringW(wWindows, wPrograms, wExtensions, wBuffer, sizeof(wBuffer)/sizeof(WCHAR)) > 0)
++        {
++            CharLowerW(wBuffer);
++            tok = wBuffer;
++            while (*tok)
++            {
++                WCHAR *p = tok;
++                while (*p && *p != ' ' && *p != '\t') p++;
++                if (*p)
++                {
++                    *p++ = 0;
++                    while (*p == ' ' || *p == '\t') p++;
++                }
++
++                if (wcsicmp(tok, &extension[1]) == 0) /* have to skip the leading "." */
++                {
++                    wcscpy(lpResult, xlpFile);
++                    /* Need to perhaps check that the file has a path
++                     * attached */
++                    TRACE("found %s\n", debugstr_w(lpResult));
++                    return 33;
++                    /* Greater than 32 to indicate success */
++                }
++                tok = p;
++            }
++        }
++
++        /* Check registry */
++        if (RegQueryValueW(HKEY_CLASSES_ROOT, extension, filetype,
++                           &filetypelen) == ERROR_SUCCESS)
++        {
++            filetypelen /= sizeof(WCHAR);
++            if (filetypelen == sizeof(filetype)/sizeof(WCHAR))
++                filetypelen--;
++
++            filetype[filetypelen] = '\0';
++            TRACE("File type: %s\n", debugstr_w(filetype));
++        }
++        else
++        {
++            *filetype = '\0';
++            filetypelen = 0;
++        }
++    }
++
++    if (*filetype)
++    {
++        /* pass the operation string to SHELL_FindExecutableByOperation() */
++        filetype[filetypelen] = '\0';
++        retval = SHELL_FindExecutableByOperation(lpOperation, key, filetype, command, sizeof(command));
++
++        if (retval > 32)
++        {
++            DWORD finishedLen;
++            SHELL_ArgifyW(lpResult, resultLen, command, xlpFile, pidl, args, &finishedLen);
++            if (finishedLen > resultLen)
++                ERR("Argify buffer not large enough.. truncated\n");
++            /* Remove double quotation marks and command line arguments */
++            if (*lpResult == '"')
++            {
++                WCHAR *p = lpResult;
++                while (*(p + 1) != '"')
++                {
++                    *p = *(p + 1);
++                    p++;
++                }
++                *p = '\0';
++            }
++            else
++            {
++                /* Truncate on first space */
++                WCHAR *p = lpResult;
++                while (*p != ' ' && *p != '\0')
++                    p++;
++                *p='\0';
++            }
++        }
++    }
++    else /* Check win.ini */
++    {
++        static const WCHAR wExtensions[] = {'e','x','t','e','n','s','i','o','n','s',0};
++
++        /* Toss the leading dot */
++        extension++;
++        if (GetProfileStringW(wExtensions, extension, wszEmpty, command, sizeof(command)/sizeof(WCHAR)) > 0)
++        {
++            if (wcslen(command) != 0)
++            {
++                wcscpy(lpResult, command);
++                tok = wcschr(lpResult, '^'); /* should be ^.extension? */
++                if (tok != NULL)
++                {
++                    tok[0] = '\0';
++                    wcscat(lpResult, xlpFile); /* what if no dir in xlpFile? */
++                    tok = wcschr(command, '^'); /* see above */
++                    if ((tok != NULL) && (wcslen(tok)>5))
++                    {
++                        wcscat(lpResult, &tok[5]);
++                    }
++                }
++                retval = 33; /* FIXME - see above */
++            }
++        }
++    }
++
++    TRACE("returning %s\n", debugstr_w(lpResult));
++    return retval;
++}
++
++/******************************************************************
++ *        dde_cb
++ *
++ * callback for the DDE connection. not really useful
++ */
++static HDDEDATA CALLBACK dde_cb(UINT uType, UINT uFmt, HCONV hConv,
++                                HSZ hsz1, HSZ hsz2, HDDEDATA hData,
++                                ULONG_PTR dwData1, ULONG_PTR dwData2)
++{
++    TRACE("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n",
++           uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2);
++    return NULL;
++}
++
++/******************************************************************
++ *        dde_connect
++ *
++ * ShellExecute helper. Used to do an operation with a DDE connection
++ *
++ * Handles both the direct connection (try #1), and if it fails,
++ * launching an application and trying (#2) to connect to it
++ *
++ */
++static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec,
++                            const WCHAR* lpFile, WCHAR *env,
++                            LPCWSTR szCommandline, LPITEMIDLIST pidl, SHELL_ExecuteW32 execfunc,
++                            const SHELLEXECUTEINFOW *psei, LPSHELLEXECUTEINFOW psei_out)
++{
++    static const WCHAR wApplication[] = {'\\','a','p','p','l','i','c','a','t','i','o','n',0};
++    static const WCHAR wTopic[] = {'\\','t','o','p','i','c',0};
++    WCHAR       regkey[256];
++    WCHAR *     endkey = regkey + wcslen(key);
++    WCHAR       app[256], topic[256], ifexec[256], res[256];
++    LONG        applen, topiclen, ifexeclen;
++    WCHAR *     exec;
++    DWORD       ddeInst = 0;
++    DWORD       tid;
++    DWORD       resultLen;
++    HSZ         hszApp, hszTopic;
++    HCONV       hConv;
++    HDDEDATA    hDdeData;
++    unsigned    ret = SE_ERR_NOASSOC;
++    BOOL unicode = !(GetVersion() & 0x80000000);
++
++    wcscpy(regkey, key);
++    wcscpy(endkey, wApplication);
++    applen = sizeof(app);
++    if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, app, &applen) != ERROR_SUCCESS)
++    {
++        WCHAR command[1024], fullpath[MAX_PATH];
++        static const WCHAR wSo[] = { '.','s','o',0 };
++        DWORD sizeSo = sizeof(wSo)/sizeof(WCHAR);
++        LPWSTR ptr = NULL;
++        DWORD ret = 0;
++
++        /* Get application command from start string and find filename of application */
++        if (*start == '"')
++        {
++            wcscpy(command, start+1);
++            if ((ptr = wcschr(command, '"')))
++                *ptr = 0;
++            ret = SearchPathW(NULL, command, wszExe, sizeof(fullpath)/sizeof(WCHAR), fullpath, &ptr);
++        }
++        else
++        {
++            LPWSTR p,space;
++            for (p=(LPWSTR)start; (space= const_cast<LPWSTR>(strchrW(p, ' '))); p=space+1)
++            {
++                int idx = space-start;
++                memcpy(command, start, idx*sizeof(WCHAR));
++                command[idx] = '\0';
++                if ((ret = SearchPathW(NULL, command, wszExe, sizeof(fullpath)/sizeof(WCHAR), fullpath, &ptr)))
++                    break;
++            }
++            if (!ret)
++                ret = SearchPathW(NULL, start, wszExe, sizeof(fullpath)/sizeof(WCHAR), fullpath, &ptr);
++        }
++
++        if (!ret)
++        {
++            ERR("Unable to find application path for command %s\n", debugstr_w(start));
++            return ERROR_ACCESS_DENIED;
++        }
++        wcscpy(app, ptr);
++
++        /* Remove extensions (including .so) */
++        ptr = app + wcslen(app) - (sizeSo-1);
++        if (wcslen(app) >= sizeSo &&
++            !wcscmp(ptr, wSo))
++            *ptr = 0;
++
++        ptr = const_cast<LPWSTR>(strrchrW(app, '.'));
++        assert(ptr);
++        *ptr = 0;
++    }
++
++    wcscpy(endkey, wTopic);
++    topiclen = sizeof(topic);
++    if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, topic, &topiclen) != ERROR_SUCCESS)
++    {
++        static const WCHAR wSystem[] = {'S','y','s','t','e','m',0};
++        wcscpy(topic, wSystem);
++    }
++
++    if (unicode)
++    {
++        if (DdeInitializeW(&ddeInst, dde_cb, APPCMD_CLIENTONLY, 0L) != DMLERR_NO_ERROR)
++            return 2;
++    }
++    else
++    {
++        if (DdeInitializeA(&ddeInst, dde_cb, APPCMD_CLIENTONLY, 0L) != DMLERR_NO_ERROR)
++            return 2;
++    }
++
++    hszApp = DdeCreateStringHandleW(ddeInst, app, CP_WINUNICODE);
++    hszTopic = DdeCreateStringHandleW(ddeInst, topic, CP_WINUNICODE);
++
++    hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL);
++    exec = ddeexec;
++    if (!hConv)
++    {
++        static const WCHAR wIfexec[] = {'\\','i','f','e','x','e','c',0};
++        TRACE("Launching %s\n", debugstr_w(start));
++        ret = execfunc(start, env, TRUE, psei, psei_out);
++        if (ret <= 32)
++        {
++            TRACE("Couldn't launch\n");
++            goto error;
++        }
++        hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL);
++        if (!hConv)
++        {
++            TRACE("Couldn't connect. ret=%d\n", ret);
++            DdeUninitialize(ddeInst);
++            SetLastError(ERROR_DDE_FAIL);
++            return 30; /* whatever */
++        }
++        strcpyW(endkey, wIfexec);
++        ifexeclen = sizeof(ifexec);
++        if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, ifexec, &ifexeclen) == ERROR_SUCCESS)
++        {
++            exec = ifexec;
++        }
++    }
++
++    SHELL_ArgifyW(res, sizeof(res)/sizeof(WCHAR), exec, lpFile, pidl, szCommandline, &resultLen);
++    if (resultLen > sizeof(res)/sizeof(WCHAR))
++        ERR("Argify buffer not large enough, truncated\n");
++    TRACE("%s %s => %s\n", debugstr_w(exec), debugstr_w(lpFile), debugstr_w(res));
++
++    /* It's documented in the KB 330337 that IE has a bug and returns
++     * error DMLERR_NOTPROCESSED on XTYP_EXECUTE request.
++     */
++    if (unicode)
++        hDdeData = DdeClientTransaction((LPBYTE)res, (strlenW(res) + 1) * sizeof(WCHAR), hConv, 0L, 0, XTYP_EXECUTE, 30000, &tid);
++    else
++    {
++        DWORD lenA = WideCharToMultiByte(CP_ACP, 0, res, -1, NULL, 0, NULL, NULL);
++        char *resA = (LPSTR)HeapAlloc(GetProcessHeap(), 0, lenA);
++        WideCharToMultiByte(CP_ACP, 0, res, -1, resA, lenA, NULL, NULL);
++        hDdeData = DdeClientTransaction( (LPBYTE)resA, lenA, hConv, 0L, 0,
++                                         XTYP_EXECUTE, 10000, &tid );
++        HeapFree(GetProcessHeap(), 0, resA);
++    }
++    if (hDdeData)
++        DdeFreeDataHandle(hDdeData);
++    else
++        WARN("DdeClientTransaction failed with error %04x\n", DdeGetLastError(ddeInst));
++    ret = 33;
++
++    DdeDisconnect(hConv);
++
++ error:
++    DdeUninitialize(ddeInst);
++
++    return ret;
++}
++
++/*************************************************************************
++ *    execute_from_key [Internal]
++ */
++static UINT_PTR execute_from_key(LPCWSTR key, LPCWSTR lpFile, WCHAR *env,
++                                 LPCWSTR szCommandline, LPCWSTR executable_name,
++                                 SHELL_ExecuteW32 execfunc,
++                                 LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out)
++{
++    static const WCHAR wCommand[] = {'c','o','m','m','a','n','d',0};
++    static const WCHAR wDdeexec[] = {'d','d','e','e','x','e','c',0};
++    WCHAR cmd[256], param[1024], ddeexec[256];
++    DWORD cmdlen = sizeof(cmd), ddeexeclen = sizeof(ddeexec);
++    UINT_PTR retval = SE_ERR_NOASSOC;
++    DWORD resultLen;
++    LPWSTR tmp;
++
++    TRACE("%s %s %s %s %s\n", debugstr_w(key), debugstr_w(lpFile), debugstr_w(env),
++           debugstr_w(szCommandline), debugstr_w(executable_name));
++
++    cmd[0] = '\0';
++    param[0] = '\0';
++
++    /* Get the application from the registry */
++    if (RegQueryValueW(HKEY_CLASSES_ROOT, key, cmd, (LONG *)&cmdlen) == ERROR_SUCCESS)
++    {
++        TRACE("got cmd: %s\n", debugstr_w(cmd));
++
++        /* Is there a replace() function anywhere? */
++        cmdlen /= sizeof(WCHAR);
++    if (cmdlen >= sizeof(cmd)/sizeof(WCHAR))
++        cmdlen = sizeof(cmd)/sizeof(WCHAR)-1;
++        cmd[cmdlen] = '\0';
++        SHELL_ArgifyW(param, sizeof(param)/sizeof(WCHAR), cmd, lpFile, (LPITEMIDLIST)psei->lpIDList, szCommandline, &resultLen);
++        if (resultLen > sizeof(param)/sizeof(WCHAR))
++            ERR("Argify buffer not large enough, truncating\n");
++    }
++
++    /* Get the parameters needed by the application
++       from the associated ddeexec key */
++    tmp = const_cast<LPWSTR>(strstrW(key, wCommand));
++    assert(tmp);
++    wcscpy(tmp, wDdeexec);
++
++    if (RegQueryValueW(HKEY_CLASSES_ROOT, key, ddeexec, (LONG *)&ddeexeclen) == ERROR_SUCCESS)
++    {
++        TRACE("Got ddeexec %s => %s\n", debugstr_w(key), debugstr_w(ddeexec));
++        if (!param[0]) strcpyW(param, executable_name);
++        retval = dde_connect(key, param, ddeexec, lpFile, env, szCommandline, (LPITEMIDLIST)psei->lpIDList, execfunc, psei, psei_out);
++    }
++    else if (param[0])
++    {
++        TRACE("executing: %s\n", debugstr_w(param));
++        retval = execfunc(param, env, FALSE, psei, psei_out);
++    }
++    else
++        WARN("Nothing appropriate found for %s\n", debugstr_w(key));
++
++    return retval;
++}
++
++/*************************************************************************
++ * FindExecutableA            [SHELL32.@]
++ */
++HINSTANCE WINAPI FindExecutableA(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult)
++{
++    HINSTANCE retval;
++    WCHAR *wFile = NULL, *wDirectory = NULL;
++    WCHAR wResult[MAX_PATH];
++
++    if (lpFile) __SHCloneStrAtoW(&wFile, lpFile);
++    if (lpDirectory) __SHCloneStrAtoW(&wDirectory, lpDirectory);
++
++    retval = FindExecutableW(wFile, wDirectory, wResult);
++    WideCharToMultiByte(CP_ACP, 0, wResult, -1, lpResult, MAX_PATH, NULL, NULL);
++    SHFree( wFile );
++    SHFree( wDirectory );
++
++    TRACE("returning %s\n", lpResult);
++    return retval;
++}
++
++/*************************************************************************
++ * FindExecutableW            [SHELL32.@]
++ *
++ * This function returns the executable associated with the specified file
++ * for the default verb.
++ *
++ * PARAMS
++ *  lpFile   [I] The file to find the association for. This must refer to
++ *               an existing file otherwise FindExecutable fails and returns
++ *               SE_ERR_FNF.
++ *  lpResult [O] Points to a buffer into which the executable path is
++ *               copied. This parameter must not be NULL otherwise
++ *               FindExecutable() segfaults. The buffer must be of size at
++ *               least MAX_PATH characters.
++ *
++ * RETURNS
++ *  A value greater than 32 on success, less than or equal to 32 otherwise.
++ *  See the SE_ERR_* constants.
++ *
++ * NOTES
++ *  On Windows XP and 2003, FindExecutable() seems to first convert the
++ *  filename into 8.3 format, thus taking into account only the first three
++ *  characters of the extension, and expects to find an association for those.
++ *  However other Windows versions behave sanely.
++ */
++HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpResult)
++{
++    UINT_PTR retval = SE_ERR_NOASSOC;
++    WCHAR old_dir[1024];
++
++    TRACE("File %s, Dir %s\n", debugstr_w(lpFile), debugstr_w(lpDirectory));
++
++    lpResult[0] = '\0'; /* Start off with an empty return string */
++    if (lpFile == NULL)
++        return (HINSTANCE)SE_ERR_FNF;
++
++    if (lpDirectory)
++    {
++        GetCurrentDirectoryW(sizeof(old_dir)/sizeof(WCHAR), old_dir);
++        SetCurrentDirectoryW(lpDirectory);
++    }
++
++    retval = SHELL_FindExecutable(lpDirectory, lpFile, wszOpen, lpResult, MAX_PATH, NULL, NULL, NULL, NULL);
++
++    TRACE("returning %s\n", debugstr_w(lpResult));
++    if (lpDirectory)
++        SetCurrentDirectoryW(old_dir);
++    return (HINSTANCE)retval;
++}
++
++/* FIXME: is this already implemented somewhere else? */
++static HKEY ShellExecute_GetClassKey( const SHELLEXECUTEINFOW *sei )
++{
++    LPCWSTR ext = NULL, lpClass = NULL;
++    LPWSTR cls = NULL;
++    DWORD type = 0, sz = 0;
++    HKEY hkey = 0;
++    LONG r;
++
++    if (sei->fMask & SEE_MASK_CLASSALL)
++        return sei->hkeyClass;
++
++    if (sei->fMask & SEE_MASK_CLASSNAME)
++        lpClass = sei->lpClass;
++    else
++    {
++        ext = PathFindExtensionW( sei->lpFile );
++        TRACE("ext = %s\n", debugstr_w( ext ) );
++        if (!ext)
++            return hkey;
++
++        r = RegOpenKeyW( HKEY_CLASSES_ROOT, ext, &hkey );
++        if (r != ERROR_SUCCESS )
++            return hkey;
++
++        r = RegQueryValueExW( hkey, NULL, 0, &type, NULL, &sz );
++        if ( r == ERROR_SUCCESS && type == REG_SZ )
++        {
++            sz += sizeof (WCHAR);
++            cls = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, sz );
++            cls[0] = 0;
++            RegQueryValueExW( hkey, NULL, 0, &type, (LPBYTE) cls, &sz );
++        }
++
++        RegCloseKey( hkey );
++        lpClass = cls;
++    }
++
++    TRACE("class = %s\n", debugstr_w(lpClass) );
++
++    hkey = 0;
++    if ( lpClass )
++        RegOpenKeyW( HKEY_CLASSES_ROOT, lpClass, &hkey );
++
++    HeapFree( GetProcessHeap(), 0, cls );
++
++    return hkey;
++}
++
++static IDataObject *shellex_get_dataobj( LPSHELLEXECUTEINFOW sei )
++{
++    LPCITEMIDLIST pidllast = NULL;
++    IDataObject *dataobj = NULL;
++    IShellFolder *shf = NULL;
++    LPITEMIDLIST pidl = NULL;
++    HRESULT r;
++
++    if (sei->fMask & SEE_MASK_CLASSALL)
++        pidl = (LPITEMIDLIST)sei->lpIDList;
++    else
++    {
++        WCHAR fullpath[MAX_PATH];
++        BOOL ret;
++
++        fullpath[0] = 0;
++        ret = GetFullPathNameW( sei->lpFile, MAX_PATH, fullpath, NULL );
++        if (!ret)
++            goto end;
++
++        pidl = ILCreateFromPathW( fullpath );
++    }
++
++    r = SHBindToParent( pidl, IID_IShellFolder, (LPVOID*)&shf, &pidllast );
++    if ( FAILED( r ) )
++        goto end;
++
++    shf->GetUIObjectOf(NULL, 1, &pidllast,
++                                IID_IDataObject, NULL, (LPVOID*) &dataobj );
++
++end:
++    if ( pidl != sei->lpIDList )
++        ILFree( pidl );
++    if ( shf )
++        shf->Release();
++    return dataobj;
++}
++
++static HRESULT shellex_run_context_menu_default( IShellExtInit *obj,
++                                                 LPSHELLEXECUTEINFOW sei )
++{
++    IContextMenu *cm = NULL;
++    CMINVOKECOMMANDINFOEX ici;
++    MENUITEMINFOW info;
++    WCHAR string[0x80];
++    INT i, n, def = -1;
++    HMENU hmenu = 0;
++    HRESULT r;
++
++    TRACE("%p %p\n", obj, sei );
++
++    r = obj->QueryInterface(IID_IContextMenu, (LPVOID*) &cm );
++    if ( FAILED( r ) )
++        return r;
++
++    hmenu = CreateMenu();
++    if ( !hmenu )
++        goto end;
++
++    /* the number of the last menu added is returned in r */
++    r = cm->QueryContextMenu(hmenu, 0, 0x20, 0x7fff, CMF_DEFAULTONLY );
++    if ( FAILED( r ) )
++        goto end;
++
++    n = GetMenuItemCount( hmenu );
++    for ( i = 0; i < n; i++ )
++    {
++        memset( &info, 0, sizeof info );
++        info.cbSize = sizeof info;
++        info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID;
++        info.dwTypeData = string;
++        info.cch = sizeof string;
++        string[0] = 0;
++        GetMenuItemInfoW( hmenu, i, TRUE, &info );
++
++        TRACE("menu %d %s %08x %08lx %08x %08x\n", i, debugstr_w(string),
++            info.fState, info.dwItemData, info.fType, info.wID );
++        if ( ( !sei->lpVerb && (info.fState & MFS_DEFAULT) ) ||
++             ( sei->lpVerb && !lstrcmpiW( sei->lpVerb, string ) ) )
++        {
++            def = i;
++            break;
++        }
++    }
++
++    r = E_FAIL;
++    if ( def == -1 )
++        goto end;
++
++    memset( &ici, 0, sizeof ici );
++    ici.cbSize = sizeof ici;
++    ici.fMask = CMIC_MASK_UNICODE | (sei->fMask & (SEE_MASK_NOASYNC|SEE_MASK_ASYNCOK|SEE_MASK_FLAG_NO_UI));
++    ici.nShow = sei->nShow;
++    ici.lpVerb = MAKEINTRESOURCEA( def );
++    ici.hwnd = sei->hwnd;
++    ici.lpParametersW = sei->lpParameters;
++
++    r = cm->InvokeCommand((LPCMINVOKECOMMANDINFO) &ici );
++
++    TRACE("invoke command returned %08x\n", r );
++
++end:
++    if ( hmenu )
++        DestroyMenu( hmenu );
++    if ( cm )
++        cm->Release();
++    return r;
++}
++
++static HRESULT shellex_load_object_and_run( HKEY hkey, LPCGUID guid, LPSHELLEXECUTEINFOW sei )
++{
++    IDataObject *dataobj = NULL;
++    IObjectWithSite *ows = NULL;
++    IShellExtInit *obj = NULL;
++    HRESULT r;
++
++    TRACE("%p %s %p\n", hkey, debugstr_guid( guid ), sei );
++
++    r = CoInitialize( NULL );
++    if ( FAILED( r ) )
++        goto end;
++
++    r = CoCreateInstance(*guid, NULL, CLSCTX_INPROC_SERVER,
++                           IID_IShellExtInit, (LPVOID*)&obj );
++    if ( FAILED( r ) )
++    {
++        ERR("failed %08x\n", r );
++        goto end;
++    }
++
++    dataobj = shellex_get_dataobj( sei );
++    if ( !dataobj )
++    {
++        ERR("failed to get data object\n");
++        goto end;
++    }
++
++    r = obj->Initialize(NULL, dataobj, hkey );
++    if ( FAILED( r ) )
++        goto end;
++
++    r = obj->QueryInterface(IID_IObjectWithSite, (LPVOID*) &ows );
++    if ( FAILED( r ) )
++        goto end;
++
++    ows->SetSite(NULL );
++
++    r = shellex_run_context_menu_default( obj, sei );
++
++end:
++    if ( ows )
++        ows->Release();
++    if ( dataobj )
++        dataobj->Release();
++    if ( obj )
++        obj->Release();
++    CoUninitialize();
++    return r;
++}
++
++
++/*************************************************************************
++ *    ShellExecute_FromContextMenu [Internal]
++ */
++static LONG ShellExecute_FromContextMenu( LPSHELLEXECUTEINFOW sei )
++{
++    static const WCHAR szcm[] = { 's','h','e','l','l','e','x','\\',
++        'C','o','n','t','e','x','t','M','e','n','u','H','a','n','d','l','e','r','s',0 };
++    HKEY hkey, hkeycm = 0;
++    WCHAR szguid[39];
++    HRESULT hr;
++    GUID guid;
++    DWORD i;
++    LONG r;
++
++    TRACE("%s\n", debugstr_w(sei->lpFile) );
++
++    hkey = ShellExecute_GetClassKey( sei );
++    if ( !hkey )
++        return ERROR_FUNCTION_FAILED;
++
++    r = RegOpenKeyW( hkey, szcm, &hkeycm );
++    if ( r == ERROR_SUCCESS )
++    {
++        i = 0;
++        while ( 1 )
++        {
++            r = RegEnumKeyW( hkeycm, i++, szguid, sizeof(szguid)/sizeof(szguid[0]) );
++            if ( r != ERROR_SUCCESS )
++                break;
++
++            hr = CLSIDFromString( szguid, &guid );
++            if (SUCCEEDED(hr))
++            {
++                /* stop at the first one that succeeds in running */
++                hr = shellex_load_object_and_run( hkey, &guid, sei );
++                if ( SUCCEEDED( hr ) )
++                    break;
++            }
++        }
++        RegCloseKey( hkeycm );
++    }
++
++    if ( hkey != sei->hkeyClass )
++        RegCloseKey( hkey );
++    return r;
++}
++
++static UINT_PTR SHELL_execute_class( LPCWSTR wszApplicationName, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc )
++{
++    static const WCHAR wSpace[] = {' ',0};
++    WCHAR execCmd[1024], wcmd[1024];
++    /* launch a document by fileclass like 'WordPad.Document.1' */
++    /* the Commandline contains 'c:\Path\wordpad.exe "%1"' */
++    /* FIXME: wcmd should not be of a fixed size. Fixed to 1024, MAX_PATH is way too short! */
++    ULONG cmask=(psei->fMask & SEE_MASK_CLASSALL);
++    DWORD resultLen;
++    BOOL done;
++
++    HCR_GetExecuteCommandW((cmask == SEE_MASK_CLASSKEY) ? psei->hkeyClass : NULL,
++                           (cmask == SEE_MASK_CLASSNAME) ? psei->lpClass: NULL,
++                           psei->lpVerb,
++                           execCmd, sizeof(execCmd));
++
++    /* FIXME: get the extension of lpFile, check if it fits to the lpClass */
++    TRACE("SEE_MASK_CLASSNAME->%s, doc->%s\n", debugstr_w(execCmd), debugstr_w(wszApplicationName));
++
++    wcmd[0] = '\0';
++    done = SHELL_ArgifyW(wcmd, sizeof(wcmd)/sizeof(WCHAR), execCmd, wszApplicationName, (LPITEMIDLIST)psei->lpIDList, NULL, &resultLen);
++    if (!done && wszApplicationName[0])
++    {
++        strcatW(wcmd, wSpace);
++        strcatW(wcmd, wszApplicationName);
++    }
++    if (resultLen > sizeof(wcmd)/sizeof(WCHAR))
++        ERR("Argify buffer not large enough... truncating\n");
++    return execfunc(wcmd, NULL, FALSE, psei, psei_out);
++}
++
++static BOOL SHELL_translate_idlist( LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters, DWORD parametersLen, LPWSTR wszApplicationName, DWORD dwApplicationNameLen )
++{
++    static const WCHAR wExplorer[] = {'e','x','p','l','o','r','e','r','.','e','x','e',0};
++    WCHAR buffer[MAX_PATH];
++    BOOL appKnownSingular = FALSE;
++
++    /* last chance to translate IDList: now also allow CLSID paths */
++    if (SUCCEEDED(SHELL_GetPathFromIDListForExecuteW((LPCITEMIDLIST)sei->lpIDList, buffer, sizeof(buffer)))) {
++        if (buffer[0]==':' && buffer[1]==':') {
++            /* open shell folder for the specified class GUID */
++            if (strlenW(buffer) + 1 > parametersLen)
++                ERR("parameters len exceeds buffer size (%i > %i), truncating\n",
++                    lstrlenW(buffer) + 1, parametersLen);
++            lstrcpynW(wszParameters, buffer, parametersLen);
++            if (strlenW(wExplorer) > dwApplicationNameLen)
++                ERR("application len exceeds buffer size (%i > %i), truncating\n",
++                    lstrlenW(wExplorer) + 1, dwApplicationNameLen);
++            lstrcpynW(wszApplicationName, wExplorer, dwApplicationNameLen);
++            appKnownSingular = TRUE;
++
++            sei->fMask &= ~SEE_MASK_INVOKEIDLIST;
++        } else {
++            WCHAR target[MAX_PATH];
++            DWORD attribs;
++            DWORD resultLen;
++            /* Check if we're executing a directory and if so use the
++               handler for the Folder class */
++            strcpyW(target, buffer);
++            attribs = GetFileAttributesW(buffer);
++            if (attribs != INVALID_FILE_ATTRIBUTES &&
++                (attribs & FILE_ATTRIBUTE_DIRECTORY) &&
++                HCR_GetExecuteCommandW(0, wszFolder,
++                                       sei->lpVerb,
++                                       buffer, sizeof(buffer))) {
++                SHELL_ArgifyW(wszApplicationName, dwApplicationNameLen,
++                              buffer, target, (LPITEMIDLIST)sei->lpIDList, NULL, &resultLen);
++                if (resultLen > dwApplicationNameLen)
++                    ERR("Argify buffer not large enough... truncating\n");
++                appKnownSingular = FALSE;
++            }
++            sei->fMask &= ~SEE_MASK_INVOKEIDLIST;
++        }
++    }
++    return appKnownSingular;
++}
++
++static UINT_PTR SHELL_quote_and_execute( LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR lpstrProtocol, LPCWSTR wszApplicationName, LPWSTR env, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc )
++{
++    static const WCHAR wQuote[] = {'"',0};
++    static const WCHAR wSpace[] = {' ',0};
++    UINT_PTR retval;
++    DWORD len;
++    WCHAR *wszQuotedCmd;
++
++    /* Length of quotes plus length of command plus NULL terminator */
++    len = 2 + lstrlenW(wcmd) + 1;
++    if (wszParameters[0])
++    {
++        /* Length of space plus length of parameters */
++        len += 1 + lstrlenW(wszParameters);
++    }
++    wszQuotedCmd = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
++    /* Must quote to handle case where cmd contains spaces,
++     * else security hole if malicious user creates executable file "C:\\Program"
++     */
++    strcpyW(wszQuotedCmd, wQuote);
++    strcatW(wszQuotedCmd, wcmd);
++    strcatW(wszQuotedCmd, wQuote);
++    if (wszParameters[0])
++    {
++        strcatW(wszQuotedCmd, wSpace);
++        strcatW(wszQuotedCmd, wszParameters);
++    }
++
++    TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(psei->lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(lpstrProtocol));
++
++    if (*lpstrProtocol)
++        retval = execute_from_key(lpstrProtocol, wszApplicationName, env, psei->lpParameters, wcmd, execfunc, psei, psei_out);
++    else
++        retval = execfunc(wszQuotedCmd, env, FALSE, psei, psei_out);
++    HeapFree(GetProcessHeap(), 0, wszQuotedCmd);
++    return retval;
++}
++
++static UINT_PTR SHELL_execute_url( LPCWSTR lpFile, LPCWSTR wFile, LPCWSTR wcmd, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc )
++{
++    static const WCHAR wShell[] = {'\\','s','h','e','l','l','\\',0};
++    static const WCHAR wCommand[] = {'\\','c','o','m','m','a','n','d',0};
++    UINT_PTR retval;
++    WCHAR *lpstrProtocol;
++    LPCWSTR lpstrRes;
++    INT iSize;
++    DWORD len;
++
++    lpstrRes = strchrW(lpFile, ':');
++    if (lpstrRes)
++        iSize = lpstrRes - lpFile;
++    else
++        iSize = strlenW(lpFile);
++
++    TRACE("Got URL: %s\n", debugstr_w(lpFile));
++    /* Looking for ...protocol\shell\lpOperation\command */
++    len = iSize + lstrlenW(wShell) + lstrlenW(wCommand) + 1;
++    if (psei->lpVerb)
++        len += lstrlenW(psei->lpVerb);
++    else
++        len += lstrlenW(wszOpen);
++    lpstrProtocol = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
++    memcpy(lpstrProtocol, lpFile, iSize*sizeof(WCHAR));
++    lpstrProtocol[iSize] = '\0';
++    strcatW(lpstrProtocol, wShell);
++    strcatW(lpstrProtocol, psei->lpVerb? psei->lpVerb: wszOpen);
++    strcatW(lpstrProtocol, wCommand);
++
++    /* Remove File Protocol from lpFile */
++    /* In the case file://path/file     */
++    if (!strncmpiW(lpFile, wFile, iSize))
++    {
++        lpFile += iSize;
++        while (*lpFile == ':') lpFile++;
++    }
++    retval = execute_from_key(lpstrProtocol, lpFile, NULL, psei->lpParameters,
++                              wcmd, execfunc, psei, psei_out);
++    HeapFree(GetProcessHeap(), 0, lpstrProtocol);
++    return retval;
++}
++
++void do_error_dialog( UINT_PTR retval, HWND hwnd, WCHAR* filename)
++{
++    WCHAR msg[2048];
++    DWORD_PTR msgArguments[3]  = { (DWORD_PTR)filename, 0, 0 };
++    DWORD error_code;
++
++    error_code = GetLastError();
++
++    if (retval == SE_ERR_NOASSOC)
++        LoadStringW(shell32_hInstance, IDS_SHLEXEC_NOASSOC, msg, sizeof(msg)/sizeof(WCHAR));
++    else
++        FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
++                       NULL,
++                       error_code,
++                       LANG_USER_DEFAULT,
++                       msg,
++                       sizeof(msg)/sizeof(WCHAR),
++                       (va_list*)msgArguments);
++
++    MessageBoxW(hwnd, msg, NULL, MB_ICONERROR);
++}
++
++/*************************************************************************
++ *    SHELL_execute [Internal]
++ */
++BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc )
++{
++    static const WCHAR wSpace[] = {' ',0};
++    static const WCHAR wWww[] = {'w','w','w',0};
++    static const WCHAR wFile[] = {'f','i','l','e',0};
++    static const WCHAR wHttp[] = {'h','t','t','p',':','/','/',0};
++    static const DWORD unsupportedFlags =
++        SEE_MASK_INVOKEIDLIST  | SEE_MASK_ICON         | SEE_MASK_HOTKEY |
++        SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT |
++        SEE_MASK_UNICODE       | SEE_MASK_ASYNCOK      | SEE_MASK_HMONITOR;
++
++    WCHAR parametersBuffer[1024], dirBuffer[MAX_PATH], wcmdBuffer[1024];
++    WCHAR *wszApplicationName, *wszParameters, *wszDir, *wcmd;
++    DWORD dwApplicationNameLen = MAX_PATH+2;
++    DWORD parametersLen = sizeof(parametersBuffer) / sizeof(WCHAR);
++    DWORD dirLen = sizeof(dirBuffer) / sizeof(WCHAR);
++    DWORD wcmdLen = sizeof(wcmdBuffer) / sizeof(WCHAR);
++    DWORD len;
++    SHELLEXECUTEINFOW sei_tmp;    /* modifiable copy of SHELLEXECUTEINFO struct */
++    WCHAR wfileName[MAX_PATH];
++    WCHAR *env;
++    WCHAR lpstrProtocol[256];
++    LPCWSTR lpFile;
++    UINT_PTR retval = SE_ERR_NOASSOC;
++    BOOL appKnownSingular = FALSE;
++
++    /* make a local copy of the LPSHELLEXECUTEINFO structure and work with this from now on */
++    sei_tmp = *sei;
++
++    TRACE("mask=0x%08x hwnd=%p verb=%s file=%s parm=%s dir=%s show=0x%08x class=%s\n",
++            sei_tmp.fMask, sei_tmp.hwnd, debugstr_w(sei_tmp.lpVerb),
++            debugstr_w(sei_tmp.lpFile), debugstr_w(sei_tmp.lpParameters),
++            debugstr_w(sei_tmp.lpDirectory), sei_tmp.nShow,
++            ((sei_tmp.fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME) ?
++                debugstr_w(sei_tmp.lpClass) : "not used");
++
++    sei->hProcess = NULL;
++
++    /* make copies of all path/command strings */
++    if (!sei_tmp.lpFile)
++    {
++        wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR));
++        *wszApplicationName = '\0';
++    }
++    else if (*sei_tmp.lpFile == '\"')
++    {
++        DWORD l = strlenW(sei_tmp.lpFile+1);
++        if(l >= dwApplicationNameLen)
++            dwApplicationNameLen = l+1;
++
++        wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR));
++        memcpy(wszApplicationName, sei_tmp.lpFile+1, (l+1)*sizeof(WCHAR));
++
++        if (wszApplicationName[l-1] == '\"')
++            wszApplicationName[l-1] = '\0';
++        appKnownSingular = TRUE;
++
++        TRACE("wszApplicationName=%s\n",debugstr_w(wszApplicationName));
++    }
++    else
++    {
++        DWORD l = strlenW(sei_tmp.lpFile)+1;
++        if(l > dwApplicationNameLen) dwApplicationNameLen = l+1;
++        wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR));
++        memcpy(wszApplicationName, sei_tmp.lpFile, l*sizeof(WCHAR));
++    }
++
++    wszParameters = parametersBuffer;
++    if (sei_tmp.lpParameters)
++    {
++        len = lstrlenW(sei_tmp.lpParameters) + 1;
++        if (len > parametersLen)
++        {
++            wszParameters = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
++            parametersLen = len;
++        }
++        strcpyW(wszParameters, sei_tmp.lpParameters);
++    }
++    else
++        *wszParameters = '\0';
++
++    wszDir = dirBuffer;
++    if (sei_tmp.lpDirectory)
++    {
++        len = lstrlenW(sei_tmp.lpDirectory) + 1;
++        if (len > dirLen)
++        {
++            wszDir = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
++            dirLen = len;
++        }
++        strcpyW(wszDir, sei_tmp.lpDirectory);
++    }
++    else
++        *wszDir = '\0';
++
++    /* adjust string pointers to point to the new buffers */
++    sei_tmp.lpFile = wszApplicationName;
++    sei_tmp.lpParameters = wszParameters;
++    sei_tmp.lpDirectory = wszDir;
++
++    if (sei_tmp.fMask & unsupportedFlags)
++    {
++        FIXME("flags ignored: 0x%08x\n", sei_tmp.fMask & unsupportedFlags);
++    }
++
++    /* process the IDList */
++    if (sei_tmp.fMask & SEE_MASK_IDLIST)
++    {
++        IShellExecuteHookW* pSEH;
++
++        HRESULT hr = SHBindToParent((LPCITEMIDLIST)sei_tmp.lpIDList, IID_IShellExecuteHookW, (LPVOID*)&pSEH, NULL);
++
++        if (SUCCEEDED(hr))
++        {
++            hr = pSEH->Execute(&sei_tmp);
++
++            pSEH->Release();
++
++            if (hr == S_OK)
++            {
++                HeapFree(GetProcessHeap(), 0, wszApplicationName);
++                if (wszParameters != parametersBuffer)
++                    HeapFree(GetProcessHeap(), 0, wszParameters);
++                if (wszDir != dirBuffer)
++                    HeapFree(GetProcessHeap(), 0, wszDir);
++                return TRUE;
++            }
++        }
++
++        SHGetPathFromIDListW((LPCITEMIDLIST)sei_tmp.lpIDList, wszApplicationName);
++        appKnownSingular = TRUE;
++        TRACE("-- idlist=%p (%s)\n", sei_tmp.lpIDList, debugstr_w(wszApplicationName));
++    }
++
++    if ( ERROR_SUCCESS == ShellExecute_FromContextMenu( &sei_tmp ) )
++    {
++        sei->hInstApp = (HINSTANCE) 33;
++        HeapFree(GetProcessHeap(), 0, wszApplicationName);
++        if (wszParameters != parametersBuffer)
++            HeapFree(GetProcessHeap(), 0, wszParameters);
++        if (wszDir != dirBuffer)
++            HeapFree(GetProcessHeap(), 0, wszDir);
++        return TRUE;
++    }
++
++    if (sei_tmp.fMask & SEE_MASK_CLASSALL)
++    {
++        retval = SHELL_execute_class( wszApplicationName, &sei_tmp, sei,
++                                      execfunc );
++        if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI))
++        {
++            OPENASINFO Info;
++
++            //FIXME
++            // need full path
++
++            Info.pcszFile = wszApplicationName;
++            Info.pcszClass = NULL;
++            Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC;
++
++            //if (SHOpenWithDialog(sei_tmp.hwnd, &Info) != S_OK)
++               do_error_dialog(retval, sei_tmp.hwnd, wszApplicationName);
++        }
++        HeapFree(GetProcessHeap(), 0, wszApplicationName);
++        if (wszParameters != parametersBuffer)
++            HeapFree(GetProcessHeap(), 0, wszParameters);
++        if (wszDir != dirBuffer)
++            HeapFree(GetProcessHeap(), 0, wszDir);
++        return retval > 32;
++    }
++
++    /* Has the IDList not yet been translated? */
++    if (sei_tmp.fMask & SEE_MASK_IDLIST)
++    {
++        appKnownSingular = SHELL_translate_idlist( &sei_tmp, wszParameters,
++                                                   parametersLen,
++                                                   wszApplicationName,
++                                                   dwApplicationNameLen );
++    }
++
++    /* expand environment strings */
++    len = ExpandEnvironmentStringsW(sei_tmp.lpFile, NULL, 0);
++    if (len>0)
++    {
++        LPWSTR buf;
++        buf = (LPWSTR)HeapAlloc(GetProcessHeap(),0,(len+1)*sizeof(WCHAR));
++
++        ExpandEnvironmentStringsW(sei_tmp.lpFile, buf, len+1);
++        HeapFree(GetProcessHeap(), 0, wszApplicationName);
++        dwApplicationNameLen = len+1;
++        wszApplicationName = buf;
++        /* appKnownSingular unmodified */
++
++        sei_tmp.lpFile = wszApplicationName;
++    }
++
++    if (*sei_tmp.lpParameters)
++    {
++        len = ExpandEnvironmentStringsW(sei_tmp.lpParameters, NULL, 0);
++        if (len > 0)
++        {
++            LPWSTR buf;
++            len++;
++            buf = (LPWSTR)HeapAlloc(GetProcessHeap(),0,len*sizeof(WCHAR));
++            ExpandEnvironmentStringsW(sei_tmp.lpParameters, buf, len);
++            if (wszParameters != parametersBuffer)
++                HeapFree(GetProcessHeap(), 0, wszParameters);
++            wszParameters = buf;
++            parametersLen = len;
++            sei_tmp.lpParameters = wszParameters;
++        }
++    }
++
++    if (*sei_tmp.lpDirectory)
++    {
++        len = ExpandEnvironmentStringsW(sei_tmp.lpDirectory, NULL, 0);
++        if (len > 0)
++        {
++            LPWSTR buf;
++            len++;
++            buf = (LPWSTR)HeapAlloc(GetProcessHeap(),0,len*sizeof(WCHAR));
++            ExpandEnvironmentStringsW(sei_tmp.lpDirectory, buf, len);
++            if (wszDir != dirBuffer)
++                HeapFree(GetProcessHeap(), 0, wszDir);
++            wszDir = buf;
++            sei_tmp.lpDirectory = wszDir;
++        }
++    }
++
++    /* Else, try to execute the filename */
++    TRACE("execute:%s,%s,%s\n", debugstr_w(wszApplicationName), debugstr_w(wszParameters), debugstr_w(wszDir));
++
++    /* separate out command line arguments from executable file name */
++    if (!*sei_tmp.lpParameters && !appKnownSingular)
++    {
++        /* If the executable path is quoted, handle the rest of the command line as parameters. */
++        if (sei_tmp.lpFile[0] == '"')
++        {
++            LPWSTR src = wszApplicationName/*sei_tmp.lpFile*/ + 1;
++            LPWSTR dst = wfileName;
++            LPWSTR end;
++
++            /* copy the unquoted executable path to 'wfileName' */
++            while(*src && *src!='"')
++            *dst++ = *src++;
++
++            *dst = '\0';
++
++            if (*src == '"')
++            {
++                end = ++src;
++
++                while(isspace(*src))
++                    ++src;
++            }
++            else
++                end = src;
++
++            /* copy the parameter string to 'wszParameters' */
++            strcpyW(wszParameters, src);
++
++            /* terminate previous command string after the quote character */
++            *end = '\0';
++        }
++        else
++        {
++            /* If the executable name is not quoted, we have to use this search loop here,
++               that in CreateProcess() is not sufficient because it does not handle shell links. */
++            WCHAR buffer[MAX_PATH], xlpFile[MAX_PATH];
++            LPWSTR space, s;
++
++            LPWSTR beg = wszApplicationName/*sei_tmp.lpFile*/;
++            for(s=beg; (space= const_cast<LPWSTR>(strchrW(s, ' '))); s=space+1)
++            {
++                int idx = space-sei_tmp.lpFile;
++                memcpy(buffer, sei_tmp.lpFile, idx * sizeof(WCHAR));
++                buffer[idx] = '\0';
++
++                /*FIXME This finds directory paths if the targeted file name contains spaces. */
++                if (SearchPathW(*sei_tmp.lpDirectory? sei_tmp.lpDirectory: NULL, buffer, wszExe, sizeof(xlpFile)/sizeof(xlpFile[0]), xlpFile, NULL))
++                {
++                    /* separate out command from parameter string */
++                    LPCWSTR p = space + 1;
++
++                    while(isspaceW(*p))
++                        ++p;
++
++                    strcpyW(wszParameters, p);
++                    *space = '\0';
++
++                    break;
++                }
++            }
++
++               lstrcpynW(wfileName, sei_tmp.lpFile,sizeof(wfileName)/sizeof(WCHAR));
++        }
++    }
++    else
++       lstrcpynW(wfileName, sei_tmp.lpFile,sizeof(wfileName)/sizeof(WCHAR));
++
++    lpFile = wfileName;
++
++    wcmd = wcmdBuffer;
++    len = lstrlenW(wszApplicationName) + 1;
++    if (sei_tmp.lpParameters[0])
++        len += 1 + lstrlenW(wszParameters);
++    if (len > wcmdLen)
++    {
++        wcmd = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
++        wcmdLen = len;
++    }
++    strcpyW(wcmd, wszApplicationName);
++    if (sei_tmp.lpParameters[0])
++    {
++        strcatW(wcmd, wSpace);
++        strcatW(wcmd, wszParameters);
++    }
++
++    retval = execfunc(wcmd, NULL, FALSE, &sei_tmp, sei);
++    if (retval > 32)
++    {
++        HeapFree(GetProcessHeap(), 0, wszApplicationName);
++        if (wszParameters != parametersBuffer)
++            HeapFree(GetProcessHeap(), 0, wszParameters);
++        if (wszDir != dirBuffer)
++            HeapFree(GetProcessHeap(), 0, wszDir);
++        if (wcmd != wcmdBuffer)
++            HeapFree(GetProcessHeap(), 0, wcmd);
++        return TRUE;
++    }
++
++    /* Else, try to find the executable */
++    wcmd[0] = '\0';
++    retval = SHELL_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, wcmdLen, lpstrProtocol, &env, (LPITEMIDLIST)sei_tmp.lpIDList, sei_tmp.lpParameters);
++    if (retval > 32)  /* Found */
++    {
++        retval = SHELL_quote_and_execute( wcmd, wszParameters, lpstrProtocol,
++                                          wszApplicationName, env, &sei_tmp,
++                                          sei, execfunc );
++        HeapFree( GetProcessHeap(), 0, env );
++    }
++    else if (PathIsDirectoryW(lpFile))
++    {
++        static const WCHAR wExplorer[] = {'e','x','p','l','o','r','e','r',0};
++        static const WCHAR wQuote[] = {'"',0};
++        WCHAR wExec[MAX_PATH];
++        WCHAR * lpQuotedFile = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR) * (strlenW(lpFile) + 3) );
++
++        if (lpQuotedFile)
++        {
++            retval = SHELL_FindExecutable( sei_tmp.lpDirectory, wExplorer,
++                                           wszOpen, wExec, MAX_PATH,
++                                           NULL, &env, NULL, NULL );
++            if (retval > 32)
++            {
++                strcpyW(lpQuotedFile, wQuote);
++                strcatW(lpQuotedFile, lpFile);
++                strcatW(lpQuotedFile, wQuote);
++                retval = SHELL_quote_and_execute( wExec, lpQuotedFile,
++                                                  lpstrProtocol,
++                                                  wszApplicationName, env,
++                                                  &sei_tmp, sei, execfunc );
++                HeapFree( GetProcessHeap(), 0, env );
++            }
++            HeapFree( GetProcessHeap(), 0, lpQuotedFile );
++        }
++        else
++            retval = 0; /* Out of memory */
++    }
++    else if (PathIsURLW(lpFile))    /* File not found, check for URL */
++    {
++        retval = SHELL_execute_url( lpFile, wFile, wcmd, &sei_tmp, sei, execfunc );
++    }
++    /* Check if file specified is in the form www.??????.*** */
++    else if (!strncmpiW(lpFile, wWww, 3))
++    {
++        /* if so, append lpFile http:// and call ShellExecute */
++        WCHAR lpstrTmpFile[256];
++        strcpyW(lpstrTmpFile, wHttp);
++        strcatW(lpstrTmpFile, lpFile);
++        retval = (UINT_PTR)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0);
++    }
++
++    TRACE("retval %lu\n", retval);
++
++    if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI))
++    {
++        OPENASINFO Info;
++
++        //FIXME
++        // need full path
++
++        Info.pcszFile = wszApplicationName;
++        Info.pcszClass = NULL;
++        Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC;
++
++        //if (SHOpenWithDialog(sei_tmp.hwnd, &Info) != S_OK)
++            do_error_dialog(retval, sei_tmp.hwnd, wszApplicationName);
++    }
++
++    HeapFree(GetProcessHeap(), 0, wszApplicationName);
++    if (wszParameters != parametersBuffer)
++        HeapFree(GetProcessHeap(), 0, wszParameters);
++    if (wszDir != dirBuffer)
++        HeapFree(GetProcessHeap(), 0, wszDir);
++    if (wcmd != wcmdBuffer)
++        HeapFree(GetProcessHeap(), 0, wcmd);
++
++    sei->hInstApp = (HINSTANCE)(retval > 32 ? 33 : retval);
++
++    return retval > 32;
++}
++
++/*************************************************************************
++ * ShellExecuteA            [SHELL32.290]
++ */
++HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpOperation,LPCSTR lpFile,
++                               LPCSTR lpParameters,LPCSTR lpDirectory, INT iShowCmd)
++{
++    SHELLEXECUTEINFOA sei;
++
++    TRACE("%p,%s,%s,%s,%s,%d\n",
++          hWnd, debugstr_a(lpOperation), debugstr_a(lpFile),
++          debugstr_a(lpParameters), debugstr_a(lpDirectory), iShowCmd);
++
++    sei.cbSize = sizeof(sei);
++    sei.fMask = SEE_MASK_FLAG_NO_UI;
++    sei.hwnd = hWnd;
++    sei.lpVerb = lpOperation;
++    sei.lpFile = lpFile;
++    sei.lpParameters = lpParameters;
++    sei.lpDirectory = lpDirectory;
++    sei.nShow = iShowCmd;
++    sei.lpIDList = 0;
++    sei.lpClass = 0;
++    sei.hkeyClass = 0;
++    sei.dwHotKey = 0;
++    sei.hProcess = 0;
++
++    ShellExecuteExA (&sei);
++    return sei.hInstApp;
++}
++
++/*************************************************************************
++ * ShellExecuteExA                [SHELL32.292]
++ *
++ */
++BOOL WINAPI ShellExecuteExA (LPSHELLEXECUTEINFOA sei)
++{
++    SHELLEXECUTEINFOW seiW;
++    BOOL ret;
++    WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL, *wClass = NULL;
++
++    TRACE("%p\n", sei);
++
++    memcpy(&seiW, sei, sizeof(SHELLEXECUTEINFOW));
++
++    if (sei->lpVerb)
++        seiW.lpVerb = __SHCloneStrAtoW(&wVerb, sei->lpVerb);
++
++    if (sei->lpFile)
++        seiW.lpFile = __SHCloneStrAtoW(&wFile, sei->lpFile);
++
++    if (sei->lpParameters)
++        seiW.lpParameters = __SHCloneStrAtoW(&wParameters, sei->lpParameters);
++
++    if (sei->lpDirectory)
++        seiW.lpDirectory = __SHCloneStrAtoW(&wDirectory, sei->lpDirectory);
++
++    if ((sei->fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME && sei->lpClass)
++        seiW.lpClass = __SHCloneStrAtoW(&wClass, sei->lpClass);
++    else
++        seiW.lpClass = NULL;
++
++    ret = SHELL_execute( &seiW, SHELL_ExecuteW );
++
++    sei->hInstApp = seiW.hInstApp;
++
++    if (sei->fMask & SEE_MASK_NOCLOSEPROCESS)
++        sei->hProcess = seiW.hProcess;
++
++    SHFree(wVerb);
++    SHFree(wFile);
++    SHFree(wParameters);
++    SHFree(wDirectory);
++    SHFree(wClass);
++
++    return ret;
++}
++
++/*************************************************************************
++ * ShellExecuteExW                [SHELL32.293]
++ *
++ */
++BOOL WINAPI ShellExecuteExW (LPSHELLEXECUTEINFOW sei)
++{
++    return SHELL_execute( sei, SHELL_ExecuteW );
++}
++
++/*************************************************************************
++ * ShellExecuteW            [SHELL32.294]
++ * from shellapi.h
++ * WINSHELLAPI HINSTANCE APIENTRY ShellExecuteW(HWND hwnd, LPCWSTR lpOperation,
++ * LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd);
++ */
++HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile,
++                               LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd)
++{
++    SHELLEXECUTEINFOW sei;
++
++    TRACE("\n");
++    sei.cbSize = sizeof(sei);
++    sei.fMask = SEE_MASK_FLAG_NO_UI;
++    sei.hwnd = hwnd;
++    sei.lpVerb = lpOperation;
++    sei.lpFile = lpFile;
++    sei.lpParameters = lpParameters;
++    sei.lpDirectory = lpDirectory;
++    sei.nShow = nShowCmd;
++    sei.lpIDList = 0;
++    sei.lpClass = 0;
++    sei.hkeyClass = 0;
++    sei.dwHotKey = 0;
++    sei.hProcess = 0;
++
++    SHELL_execute( &sei, SHELL_ExecuteW );
++    return sei.hInstApp;
++}
++
++/*************************************************************************
++ * WOWShellExecute            [SHELL32.@]
++ *
++ * FIXME: the callback function most likely doesn't work the same way on Windows.
++ */
++EXTERN_C HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpOperation,LPCSTR lpFile,
++                                 LPCSTR lpParameters,LPCSTR lpDirectory, INT iShowCmd, void *callback)
++{
++    SHELLEXECUTEINFOW seiW;
++    WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL;
++    HANDLE hProcess = 0;
++
++    seiW.lpVerb = lpOperation ? __SHCloneStrAtoW(&wVerb, lpOperation) : NULL;
++    seiW.lpFile = lpFile ? __SHCloneStrAtoW(&wFile, lpFile) : NULL;
++    seiW.lpParameters = lpParameters ? __SHCloneStrAtoW(&wParameters, lpParameters) : NULL;
++    seiW.lpDirectory = lpDirectory ? __SHCloneStrAtoW(&wDirectory, lpDirectory) : NULL;
++
++    seiW.cbSize = sizeof(seiW);
++    seiW.fMask = 0;
++    seiW.hwnd = hWnd;
++    seiW.nShow = iShowCmd;
++    seiW.lpIDList = 0;
++    seiW.lpClass = 0;
++    seiW.hkeyClass = 0;
++    seiW.dwHotKey = 0;
++    seiW.hProcess = hProcess;
++
++    SHELL_execute( &seiW, (SHELL_ExecuteW32)callback );
++
++    SHFree(wVerb);
++    SHFree(wFile);
++    SHFree(wParameters);
++    SHFree(wDirectory);
++    return seiW.hInstApp;
++}
++
++/*************************************************************************
++ * OpenAs_RunDLLA          [SHELL32.@]
++ */
++EXTERN_C void WINAPI OpenAs_RunDLLA(HWND hwnd, HINSTANCE hinst, LPCSTR cmdline, int cmdshow)
++{
++    FIXME("%p, %p, %s, %d\n", hwnd, hinst, debugstr_a(cmdline), cmdshow);
++}
++
++/*************************************************************************
++ * OpenAs_RunDLLW          [SHELL32.@]
++ */
++EXTERN_C void WINAPI OpenAs_RunDLLW(HWND hwnd, HINSTANCE hinst, LPCWSTR cmdline, int cmdshow)
++{
++    FIXME("%p, %p, %s, %d\n", hwnd, hinst, debugstr_w(cmdline), cmdshow);
++}
index 0000000,0000000..42e22c7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1931 @@@
++/*
++ * SHFileOperation
++ *
++ * Copyright 2000 Juergen Schmied
++ * Copyright 2002 Andriy Palamarchuk
++ * Copyright 2004 Dietrich Teickner (from Odin)
++ * Copyright 2004 Rolf Kalbermatter
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
++ */
++
++//#define NO_SHLWAPI_STREAM
++#include <precomp.h>
++
++WINE_DEFAULT_DEBUG_CHANNEL(shell);
++
++#define IsAttrib(x, y)  ((INVALID_FILE_ATTRIBUTES != (x)) && ((x) & (y)))
++#define IsAttribFile(x) (!((x) & FILE_ATTRIBUTE_DIRECTORY))
++#define IsAttribDir(x)  IsAttrib(x, FILE_ATTRIBUTE_DIRECTORY)
++#define IsDotDir(x)     ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
++
++#define FO_MASK         0xF
++#define WM_FILE       (WM_USER + 1)
++#define TIMER_ID      (100)
++
++static const WCHAR wWildcardFile[] = {'*',0};
++static const WCHAR wWildcardChars[] = {'*','?',0};
++
++static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec);
++static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path);
++static DWORD SHNotifyDeleteFileW(LPCWSTR path);
++static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest);
++static DWORD SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bFailIfExists);
++static DWORD SHFindAttrW(LPCWSTR pName, BOOL fileOnly);
++
++typedef struct
++{
++    SHFILEOPSTRUCTW *req;
++    DWORD dwYesToAllMask;
++    BOOL bManyItems;
++    BOOL bCancelled;
++} FILE_OPERATION;
++
++#define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026
++
++typedef struct
++{
++    DWORD attributes;
++    LPWSTR szDirectory;
++    LPWSTR szFilename;
++    LPWSTR szFullPath;
++    BOOL bFromWildcard;
++    BOOL bFromRelative;
++    BOOL bExists;
++} FILE_ENTRY;
++
++typedef struct
++{
++    FILE_ENTRY *feFiles;
++    DWORD num_alloc;
++    DWORD dwNumFiles;
++    BOOL bAnyFromWildcard;
++    BOOL bAnyDirectories;
++    BOOL bAnyDontExist;
++} FILE_LIST;
++
++typedef struct
++{
++    FILE_LIST * from;
++    FILE_LIST * to;
++    FILE_OPERATION * op;
++    DWORD Index;
++    HWND hDlgCtrl;
++    HWND hwndDlg;
++}FILE_OPERATION_CONTEXT;
++
++
++/* Confirm dialogs with an optional "Yes To All" as used in file operations confirmations
++ */
++static const WCHAR CONFIRM_MSG_PROP[] = {'W','I','N','E','_','C','O','N','F','I','R','M',0};
++
++struct confirm_msg_info
++{
++    LPWSTR lpszText;
++    LPWSTR lpszCaption;
++    HICON hIcon;
++    BOOL bYesToAll;
++};
++
++/* as some buttons may be hidden and the dialog height may change we may need
++ * to move the controls */
++static void confirm_msg_move_button(HWND hDlg, INT iId, INT *xPos, INT yOffset, BOOL bShow)
++{
++    HWND hButton = GetDlgItem(hDlg, iId);
++    RECT r;
++
++    if (bShow)
++    {
++        POINT pt;
++        int width;
++
++        GetWindowRect(hButton, &r);
++        width = r.right - r.left;
++        pt.x = r.left;
++        pt.y = r.top;
++        ScreenToClient(hDlg, &pt);
++        MoveWindow(hButton, *xPos - width, pt.y - yOffset, width, r.bottom - r.top, FALSE);
++        *xPos -= width + 5;
++    }
++    else
++        ShowWindow(hButton, SW_HIDE);
++}
++
++/* Note: we paint the text manually and don't use the static control to make
++ * sure the text has the same height as the one computed in WM_INITDIALOG
++ */
++static INT_PTR ConfirmMsgBox_Paint(HWND hDlg)
++{
++    PAINTSTRUCT ps;
++    HFONT hOldFont;
++    RECT r;
++    HDC hdc;
++
++    BeginPaint(hDlg, &ps);
++    hdc = ps.hdc;
++
++    GetClientRect(GetDlgItem(hDlg, IDD_MESSAGE), &r);
++    /* this will remap the rect to dialog coords */
++    MapWindowPoints(GetDlgItem(hDlg, IDD_MESSAGE), hDlg, (LPPOINT)&r, 2);
++    hOldFont = (HFONT)SelectObject(hdc, (HFONT)SendDlgItemMessageW(hDlg, IDD_MESSAGE, WM_GETFONT, 0, 0));
++    DrawTextW(hdc, (LPWSTR)GetPropW(hDlg, CONFIRM_MSG_PROP), -1, &r, DT_NOPREFIX | DT_PATH_ELLIPSIS | DT_WORDBREAK);
++    SelectObject(hdc, hOldFont);
++    EndPaint(hDlg, &ps);
++
++    return TRUE;
++}
++
++static INT_PTR ConfirmMsgBox_Init(HWND hDlg, LPARAM lParam)
++{
++    struct confirm_msg_info *info = (struct confirm_msg_info *)lParam;
++    INT xPos, yOffset;
++    int width, height;
++    HFONT hOldFont;
++    HDC hdc;
++    RECT r;
++
++    SetWindowTextW(hDlg, info->lpszCaption);
++    ShowWindow(GetDlgItem(hDlg, IDD_MESSAGE), SW_HIDE);
++    SetPropW(hDlg, CONFIRM_MSG_PROP, info->lpszText);
++    SendDlgItemMessageW(hDlg, IDD_ICON, STM_SETICON, (WPARAM)info->hIcon, 0);
++
++    /* compute the text height and resize the dialog */
++    GetClientRect(GetDlgItem(hDlg, IDD_MESSAGE), &r);
++    hdc = GetDC(hDlg);
++    yOffset = r.bottom;
++    hOldFont = (HFONT)SelectObject(hdc, (HFONT)SendDlgItemMessageW(hDlg, IDD_MESSAGE, WM_GETFONT, 0, 0));
++    DrawTextW(hdc, info->lpszText, -1, &r, DT_NOPREFIX | DT_PATH_ELLIPSIS | DT_WORDBREAK | DT_CALCRECT);
++    SelectObject(hdc, hOldFont);
++    yOffset -= r.bottom;
++    yOffset = min(yOffset, 35);  /* don't make the dialog too small */
++    ReleaseDC(hDlg, hdc);
++
++    GetClientRect(hDlg, &r);
++    xPos = r.right - 7;
++    GetWindowRect(hDlg, &r);
++    width = r.right - r.left;
++    height = r.bottom - r.top - yOffset;
++    MoveWindow(hDlg, (GetSystemMetrics(SM_CXSCREEN) - width)/2,
++        (GetSystemMetrics(SM_CYSCREEN) - height)/2, width, height, FALSE);
++
++    confirm_msg_move_button(hDlg, IDCANCEL,     &xPos, yOffset, info->bYesToAll);
++    confirm_msg_move_button(hDlg, IDNO,         &xPos, yOffset, TRUE);
++    confirm_msg_move_button(hDlg, IDD_YESTOALL, &xPos, yOffset, info->bYesToAll);
++    confirm_msg_move_button(hDlg, IDYES,        &xPos, yOffset, TRUE);
++
++    return TRUE;
++}
++
++static INT_PTR CALLBACK ConfirmMsgBoxProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
++{
++    switch (uMsg)
++    {
++        case WM_INITDIALOG:
++            return ConfirmMsgBox_Init(hDlg, lParam);
++        case WM_PAINT:
++            return ConfirmMsgBox_Paint(hDlg);
++        case WM_COMMAND:
++            EndDialog(hDlg, wParam);
++            break;
++        case WM_CLOSE:
++            EndDialog(hDlg, IDCANCEL);
++            break;
++    }
++    return FALSE;
++}
++
++int SHELL_ConfirmMsgBox(HWND hWnd, LPWSTR lpszText, LPWSTR lpszCaption, HICON hIcon, BOOL bYesToAll)
++{
++    static const WCHAR wszTemplate[] = {'S','H','E','L','L','_','Y','E','S','T','O','A','L','L','_','M','S','G','B','O','X',0};
++    struct confirm_msg_info info;
++
++    info.lpszText = lpszText;
++    info.lpszCaption = lpszCaption;
++    info.hIcon = hIcon;
++    info.bYesToAll = bYesToAll;
++    return DialogBoxParamW(shell32_hInstance, wszTemplate, hWnd, ConfirmMsgBoxProc, (LPARAM)&info);
++}
++
++/* confirmation dialogs content */
++typedef struct
++{
++    HINSTANCE hIconInstance;
++    UINT icon_resource_id;
++    UINT caption_resource_id, text_resource_id;
++} SHELL_ConfirmIDstruc;
++
++static BOOL SHELL_ConfirmIDs(int nKindOfDialog, SHELL_ConfirmIDstruc *ids)
++{
++    ids->hIconInstance = shell32_hInstance;
++    switch (nKindOfDialog)
++    {
++        case ASK_DELETE_FILE:
++            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
++            ids->caption_resource_id  = IDS_DELETEITEM_CAPTION;
++            ids->text_resource_id  = IDS_DELETEITEM_TEXT;
++            return TRUE;
++
++        case ASK_DELETE_FOLDER:
++            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
++            ids->caption_resource_id  = IDS_DELETEFOLDER_CAPTION;
++            ids->text_resource_id  = IDS_DELETEITEM_TEXT;
++            return TRUE;
++
++        case ASK_DELETE_MULTIPLE_ITEM:
++            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
++            ids->caption_resource_id  = IDS_DELETEITEM_CAPTION;
++            ids->text_resource_id  = IDS_DELETEMULTIPLE_TEXT;
++            return TRUE;
++
++        case ASK_TRASH_FILE:
++            ids->icon_resource_id = IDI_SHELL_TRASH_FILE;
++            ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
++            ids->text_resource_id = IDS_TRASHITEM_TEXT;
++            return TRUE;
++
++        case ASK_TRASH_FOLDER:
++            ids->icon_resource_id = IDI_SHELL_TRASH_FILE;
++            ids->caption_resource_id = IDS_DELETEFOLDER_CAPTION;
++            ids->text_resource_id = IDS_TRASHFOLDER_TEXT;
++            return TRUE;
++
++        case ASK_TRASH_MULTIPLE_ITEM:
++            ids->icon_resource_id = IDI_SHELL_TRASH_FILE;
++            ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
++            ids->text_resource_id = IDS_TRASHMULTIPLE_TEXT;
++            return TRUE;
++
++        case ASK_CANT_TRASH_ITEM:
++            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
++            ids->caption_resource_id  = IDS_DELETEITEM_CAPTION;
++            ids->text_resource_id  = IDS_CANTTRASH_TEXT;
++            return TRUE;
++
++        case ASK_DELETE_SELECTED:
++            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
++            ids->caption_resource_id  = IDS_DELETEITEM_CAPTION;
++            ids->text_resource_id  = IDS_DELETESELECTED_TEXT;
++            return TRUE;
++
++      case ASK_OVERWRITE_FILE:
++            ids->hIconInstance = NULL;
++            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
++            ids->caption_resource_id  = IDS_OVERWRITEFILE_CAPTION;
++            ids->text_resource_id  = IDS_OVERWRITEFILE_TEXT;
++            return TRUE;
++
++      case ASK_OVERWRITE_FOLDER:
++            ids->hIconInstance = NULL;
++            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
++            ids->caption_resource_id  = IDS_OVERWRITEFILE_CAPTION;
++            ids->text_resource_id  = IDS_OVERWRITEFOLDER_TEXT;
++            return TRUE;
++
++        default:
++            FIXME(" Unhandled nKindOfDialog %d stub\n", nKindOfDialog);
++    }
++    return FALSE;
++}
++
++static BOOL SHELL_ConfirmDialogW(HWND hWnd, int nKindOfDialog, LPCWSTR szDir, FILE_OPERATION *op)
++{
++    WCHAR szCaption[255], szText[255], szBuffer[MAX_PATH + 256];
++    SHELL_ConfirmIDstruc ids;
++    DWORD_PTR args[1];
++    HICON hIcon;
++    int ret;
++
++    assert(nKindOfDialog >= 0 && nKindOfDialog < 32);
++    if (op && (op->dwYesToAllMask & (1 << nKindOfDialog)))
++        return TRUE;
++
++    if (!SHELL_ConfirmIDs(nKindOfDialog, &ids)) return FALSE;
++
++    LoadStringW(shell32_hInstance, ids.caption_resource_id, szCaption, sizeof(szCaption)/sizeof(WCHAR));
++    LoadStringW(shell32_hInstance, ids.text_resource_id, szText, sizeof(szText)/sizeof(WCHAR));
++
++    args[0] = (DWORD_PTR)szDir;
++    FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
++                   szText, 0, 0, szBuffer, sizeof(szBuffer), (va_list*)args);
++    hIcon = LoadIconW(ids.hIconInstance, (LPWSTR)MAKEINTRESOURCE(ids.icon_resource_id));
++
++    ret = SHELL_ConfirmMsgBox(hWnd, szBuffer, szCaption, hIcon, op && op->bManyItems);
++    if (op)
++    {
++        if (ret == IDD_YESTOALL)
++        {
++            op->dwYesToAllMask |= (1 << nKindOfDialog);
++            ret = IDYES;
++        }
++        if (ret == IDCANCEL)
++            op->bCancelled = TRUE;
++        if (ret != IDYES)
++            op->req->fAnyOperationsAborted = TRUE;
++    }
++    return ret == IDYES;
++}
++
++BOOL SHELL_ConfirmYesNoW(HWND hWnd, int nKindOfDialog, LPCWSTR szDir)
++{
++    return SHELL_ConfirmDialogW(hWnd, nKindOfDialog, szDir, NULL);
++}
++
++static DWORD SHELL32_AnsiToUnicodeBuf(LPCSTR aPath, LPWSTR *wPath, DWORD minChars)
++{
++    DWORD len = MultiByteToWideChar(CP_ACP, 0, aPath, -1, NULL, 0);
++
++    if (len < minChars)
++      len = minChars;
++
++    *wPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
++    if (*wPath)
++    {
++      MultiByteToWideChar(CP_ACP, 0, aPath, -1, *wPath, len);
++      return NO_ERROR;
++    }
++    return E_OUTOFMEMORY;
++}
++
++static void SHELL32_FreeUnicodeBuf(LPWSTR wPath)
++{
++    HeapFree(GetProcessHeap(), 0, wPath);
++}
++
++EXTERN_C HRESULT WINAPI SHIsFileAvailableOffline(LPCWSTR path, LPDWORD status)
++{
++    FIXME("(%s, %p) stub\n", debugstr_w(path), status);
++    return E_FAIL;
++}
++
++/**************************************************************************
++ * SHELL_DeleteDirectory()  [internal]
++ *
++ * Asks for confirmation when bShowUI is true and deletes the directory and
++ * all its subdirectories and files if necessary.
++ */
++BOOL SHELL_DeleteDirectoryW(HWND hwnd, LPCWSTR pszDir, BOOL bShowUI)
++{
++    BOOL    ret = TRUE;
++    HANDLE  hFind;
++    WIN32_FIND_DATAW wfd;
++    WCHAR   szTemp[MAX_PATH];
++
++    /* Make sure the directory exists before eventually prompting the user */
++    PathCombineW(szTemp, pszDir, wWildcardFile);
++    hFind = FindFirstFileW(szTemp, &wfd);
++    if (hFind == INVALID_HANDLE_VALUE)
++      return FALSE;
++
++    if (!bShowUI || (ret = SHELL_ConfirmDialogW(hwnd, ASK_DELETE_FOLDER, pszDir, NULL)))
++    {
++        do
++        {
++            if (IsDotDir(wfd.cFileName))
++                continue;
++            PathCombineW(szTemp, pszDir, wfd.cFileName);
++            if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
++                ret = SHELL_DeleteDirectoryW(hwnd, szTemp, FALSE);
++            else
++                ret = (SHNotifyDeleteFileW(szTemp) == ERROR_SUCCESS);
++        } while (ret && FindNextFileW(hFind, &wfd));
++    }
++    FindClose(hFind);
++    if (ret)
++        ret = (SHNotifyRemoveDirectoryW(pszDir) == ERROR_SUCCESS);
++    return ret;
++}
++
++/**************************************************************************
++ * Win32CreateDirectory      [SHELL32.93]
++ *
++ * Creates a directory. Also triggers a change notify if one exists.
++ *
++ * PARAMS
++ *  path       [I]   path to directory to create
++ *
++ * RETURNS
++ *  TRUE if successful, FALSE otherwise
++ */
++
++static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
++{
++    TRACE("(%s, %p)\n", debugstr_w(path), sec);
++
++    if (CreateDirectoryW(path, sec))
++    {
++      SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW, path, NULL);
++      return ERROR_SUCCESS;
++    }
++    return GetLastError();
++}
++
++/**********************************************************************/
++
++EXTERN_C BOOL WINAPI Win32CreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
++{
++    return (SHNotifyCreateDirectoryW(path, sec) == ERROR_SUCCESS);
++}
++
++/************************************************************************
++ * Win32RemoveDirectory      [SHELL32.94]
++ *
++ * Deletes a directory. Also triggers a change notify if one exists.
++ *
++ * PARAMS
++ *  path       [I]   path to directory to delete
++ *
++ * RETURNS
++ *  TRUE if successful, FALSE otherwise
++ */
++static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path)
++{
++    BOOL ret;
++    TRACE("(%s)\n", debugstr_w(path));
++
++    ret = RemoveDirectoryW(path);
++    if (!ret)
++    {
++      /* Directory may be write protected */
++      DWORD dwAttr = GetFileAttributesW(path);
++      if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY))
++        if (SetFileAttributesW(path, dwAttr & ~FILE_ATTRIBUTE_READONLY))
++          ret = RemoveDirectoryW(path);
++    }
++    if (ret)
++    {
++      SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHW, path, NULL);
++      return ERROR_SUCCESS;
++    }
++    return GetLastError();
++}
++
++/***********************************************************************/
++
++EXTERN_C BOOL WINAPI Win32RemoveDirectoryW(LPCWSTR path)
++{
++    return (SHNotifyRemoveDirectoryW(path) == ERROR_SUCCESS);
++}
++
++/************************************************************************
++ * Win32DeleteFile           [SHELL32.164]
++ *
++ * Deletes a file. Also triggers a change notify if one exists.
++ *
++ * PARAMS
++ *  path       [I]   path to file to delete
++ *
++ * RETURNS
++ *  TRUE if successful, FALSE otherwise
++ */
++static DWORD SHNotifyDeleteFileW(LPCWSTR path)
++{
++    BOOL ret;
++
++    TRACE("(%s)\n", debugstr_w(path));
++
++    ret = DeleteFileW(path);
++    if (!ret)
++    {
++      /* File may be write protected or a system file */
++      DWORD dwAttr = GetFileAttributesW(path);
++      if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))
++        if (SetFileAttributesW(path, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
++            ret = DeleteFileW(path);
++    }
++    if (ret)
++    {
++        SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, path, NULL);
++        return ERROR_SUCCESS;
++    }
++    return GetLastError();
++}
++
++/***********************************************************************/
++
++EXTERN_C DWORD WINAPI Win32DeleteFileW(LPCWSTR path)
++{
++    return (SHNotifyDeleteFileW(path) == ERROR_SUCCESS);
++}
++
++/************************************************************************
++ * SHNotifyMoveFile          [internal]
++ *
++ * Moves a file. Also triggers a change notify if one exists.
++ *
++ * PARAMS
++ *  src        [I]   path to source file to move
++ *  dest       [I]   path to target file to move to
++ *
++ * RETURNS
++ *  ERORR_SUCCESS if successful
++ */
++static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest)
++{
++    BOOL ret;
++
++    TRACE("(%s %s)\n", debugstr_w(src), debugstr_w(dest));
++
++        ret = MoveFileExW(src, dest, MOVEFILE_REPLACE_EXISTING);
++
++        /* MOVEFILE_REPLACE_EXISTING fails with dirs, so try MoveFile */
++        if (!ret)
++            ret = MoveFileW(src, dest);
++
++    if (!ret)
++    {
++      DWORD dwAttr;
++
++      dwAttr = SHFindAttrW(dest, FALSE);
++      if (INVALID_FILE_ATTRIBUTES == dwAttr)
++      {
++        /* Source file may be write protected or a system file */
++        dwAttr = GetFileAttributesW(src);
++        if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))
++            if (SetFileAttributesW(src, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
++                ret = MoveFileW(src, dest);
++      }
++    }
++    if (ret)
++    {
++        SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_PATHW, src, dest);
++        return ERROR_SUCCESS;
++    }
++    return GetLastError();
++}
++
++static DWORD WINAPI SHOperationProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData)
++{
++    FILE_OPERATION_CONTEXT * Context;
++    LARGE_INTEGER Progress;
++
++    /* get context */
++    Context = (FILE_OPERATION_CONTEXT*)lpData;
++
++    if (TotalBytesTransferred.QuadPart)
++    {
++        Progress.QuadPart = (TotalBytesTransferred.QuadPart * 100) / TotalFileSize.QuadPart;
++    }
++    else
++    {
++        Progress.QuadPart = 1;
++    }
++
++    /* update progress bar */
++    SendMessageW(Context->hDlgCtrl, PBM_SETPOS, (WPARAM)Progress.u.LowPart, 0);
++
++    if (TotalBytesTransferred.QuadPart == TotalFileSize.QuadPart)
++    {
++        /* file was copied */
++        Context->Index++;
++        PostMessageW(Context->hwndDlg, WM_FILE, 0, 0);
++    }
++
++    return PROGRESS_CONTINUE;
++}
++
++BOOL
++QueueFile(
++    FILE_OPERATION_CONTEXT * Context)
++{
++    FILE_ENTRY * from, *to = NULL;
++    BOOL bRet = FALSE;
++
++    if (Context->Index >= Context->from->dwNumFiles)
++        return FALSE;
++
++    /* get current file */
++    from = &Context->from->feFiles[Context->Index];
++
++    if (Context->op->req->wFunc != FO_DELETE)
++        to = &Context->to->feFiles[Context->Index];
++
++    /* update status */
++    SendDlgItemMessageW(Context->hwndDlg, 14001, WM_SETTEXT, 0, (LPARAM)from->szFullPath);
++
++    if (Context->op->req->wFunc == FO_COPY)
++    {
++        bRet = CopyFileExW(from->szFullPath, to->szFullPath, SHOperationProgressRoutine, (LPVOID)Context, &Context->op->bCancelled, 0);
++    }
++    else if (Context->op->req->wFunc == FO_MOVE)
++    {
++        //bRet = MoveFileWithProgressW(from->szFullPath, to->szFullPath, SHOperationProgressRoutine, (LPVOID)Context, MOVEFILE_COPY_ALLOWED);
++    }
++    else if (Context->op->req->wFunc == FO_DELETE)
++    {
++        bRet = DeleteFile(from->szFullPath);
++    }
++
++    return bRet;
++}
++
++static INT_PTR CALLBACK SHOperationDialog(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
++{
++    FILE_OPERATION_CONTEXT * Context;
++
++    Context = (FILE_OPERATION_CONTEXT*) GetWindowLongPtr(hwndDlg, DWLP_USER);
++
++    switch(uMsg)
++    {
++        case WM_INITDIALOG:
++            SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG)lParam);
++
++            /* get context */
++            Context = (FILE_OPERATION_CONTEXT*)lParam;
++
++            /* store progress bar handle */
++            Context->hDlgCtrl = GetDlgItem(hwndDlg, 14000);
++
++            /* store window handle */
++            Context->hwndDlg = hwndDlg;
++
++            /* set progress bar range */
++            (void)SendMessageW(Context->hDlgCtrl, (UINT) PBM_SETRANGE, 0, MAKELPARAM(0, 100));
++
++            /* start file queueing */
++            SetTimer(hwndDlg, TIMER_ID, 1000, NULL);
++            //QueueFile(Context);
++
++            return TRUE;
++
++        case WM_CLOSE:
++            Context->op->bCancelled = TRUE;
++            EndDialog(hwndDlg, Context->op->bCancelled);
++            return TRUE;
++
++        case WM_COMMAND:
++            if (LOWORD(wParam) == 14002)
++            {
++                Context->op->bCancelled = TRUE;
++                EndDialog(hwndDlg, Context->op->bCancelled);
++                return TRUE;
++            }; break;
++
++        case WM_TIMER:
++            if (wParam == TIMER_ID)
++            {
++                QueueFile(Context);
++                KillTimer(hwndDlg, TIMER_ID);
++            }; break;
++
++        case WM_FILE:
++            if (!QueueFile(Context))
++                EndDialog(hwndDlg, Context->op->bCancelled);
++        default:
++            break;
++    }
++    return FALSE;
++}
++
++HRESULT
++SHShowFileOperationDialog(FILE_OPERATION *op, FILE_LIST *flFrom, FILE_LIST *flTo)
++{
++    HWND hwnd;
++    BOOL bRet;
++    MSG msg;
++    FILE_OPERATION_CONTEXT Context;
++
++    Context.from = flFrom;
++    Context.to = flTo;
++    Context.op = op;
++    Context.Index = 0;
++    Context.op->bCancelled = FALSE;
++
++    hwnd = CreateDialogParam(shell32_hInstance, MAKEINTRESOURCE(IDD_SH_FILE_COPY), NULL, SHOperationDialog, (LPARAM)&Context);
++    if (hwnd == NULL)
++    {
++        ERR("Failed to create dialog\n");
++        return E_FAIL;
++    }
++    ShowWindow(hwnd, SW_SHOWNORMAL);
++
++    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
++    {
++        if (!IsWindow(hwnd) || !IsDialogMessage(hwnd, &msg))
++        {
++            TranslateMessage(&msg);
++            DispatchMessage(&msg);
++        }
++    }
++
++    return NOERROR;
++}
++
++
++/************************************************************************
++ * SHNotifyCopyFile          [internal]
++ *
++ * Copies a file. Also triggers a change notify if one exists.
++ *
++ * PARAMS
++ *  src           [I]   path to source file to move
++ *  dest          [I]   path to target file to move to
++ *  bFailIfExists [I]   if TRUE, the target file will not be overwritten if
++ *                      a file with this name already exists
++ *
++ * RETURNS
++ *  ERROR_SUCCESS if successful
++ */
++static DWORD SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bFailIfExists)
++{
++    BOOL ret;
++    DWORD attribs;
++
++    TRACE("(%s %s %s)\n", debugstr_w(src), debugstr_w(dest), bFailIfExists ? "failIfExists" : "");
++
++    /* Destination file may already exist with read only attribute */
++    attribs = GetFileAttributesW(dest);
++    if (IsAttrib(attribs, FILE_ATTRIBUTE_READONLY))
++        SetFileAttributesW(dest, attribs & ~FILE_ATTRIBUTE_READONLY);
++
++    if (GetFileAttributesW(dest) & FILE_ATTRIBUTE_READONLY)
++    {
++        SetFileAttributesW(dest, attribs & ~FILE_ATTRIBUTE_READONLY);
++        if (GetFileAttributesW(dest) & FILE_ATTRIBUTE_READONLY)
++        {
++            TRACE("[shell32, SHNotifyCopyFileW] STILL SHIT\n");
++        }
++    }
++
++    ret = CopyFileW(src, dest, bFailIfExists);
++    if (ret)
++    {
++        SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, dest, NULL);
++        return ERROR_SUCCESS;
++    }
++
++    return GetLastError();
++}
++
++/*************************************************************************
++ * SHCreateDirectory         [SHELL32.165]
++ *
++ * This function creates a file system folder whose fully qualified path is
++ * given by path. If one or more of the intermediate folders do not exist,
++ * they will be created as well.
++ *
++ * PARAMS
++ *  hWnd       [I]
++ *  path       [I]   path of directory to create
++ *
++ * RETURNS
++ *  ERROR_SUCCESS or one of the following values:
++ *  ERROR_BAD_PATHNAME if the path is relative
++ *  ERROR_FILE_EXISTS when a file with that name exists
++ *  ERROR_PATH_NOT_FOUND can't find the path, probably invalid
++ *  ERROR_INVALID_NAME if the path contains invalid chars
++ *  ERROR_ALREADY_EXISTS when the directory already exists
++ *  ERROR_FILENAME_EXCED_RANGE if the filename was to long to process
++ *
++ * NOTES
++ *  exported by ordinal
++ *  Win9x exports ANSI
++ *  WinNT/2000 exports Unicode
++ */
++int WINAPI SHCreateDirectory(HWND hWnd, LPCWSTR path)
++{
++     return SHCreateDirectoryExW(hWnd, path, NULL);
++}
++
++/*************************************************************************
++ * SHCreateDirectoryExA      [SHELL32.@]
++ *
++ * This function creates a file system folder whose fully qualified path is
++ * given by path. If one or more of the intermediate folders do not exist,
++ * they will be created as well.
++ *
++ * PARAMS
++ *  hWnd       [I]
++ *  path       [I]   path of directory to create
++ *  sec        [I]   security attributes to use or NULL
++ *
++ * RETURNS
++ *  ERROR_SUCCESS or one of the following values:
++ *  ERROR_BAD_PATHNAME or ERROR_PATH_NOT_FOUND if the path is relative
++ *  ERROR_INVALID_NAME if the path contains invalid chars
++ *  ERROR_FILE_EXISTS when a file with that name exists
++ *  ERROR_ALREADY_EXISTS when the directory already exists
++ *  ERROR_FILENAME_EXCED_RANGE if the filename was to long to process
++ *
++ *  FIXME: Not implemented yet;
++ *  SHCreateDirectoryEx also verifies that the files in the directory will be visible
++ *  if the path is a network path to deal with network drivers which might have a limited
++ *  but unknown maximum path length. If not:
++ *
++ *  If hWnd is set to a valid window handle, a message box is displayed warning
++ *  the user that the files may not be accessible. If the user chooses not to
++ *  proceed, the function returns ERROR_CANCELLED.
++ *
++ *  If hWnd is set to NULL, no user interface is displayed and the function
++ *  returns ERROR_CANCELLED.
++ */
++int WINAPI SHCreateDirectoryExA(HWND hWnd, LPCSTR path, LPSECURITY_ATTRIBUTES sec)
++{
++    LPWSTR wPath;
++    DWORD retCode;
++
++    TRACE("(%s, %p)\n", debugstr_a(path), sec);
++
++    retCode = SHELL32_AnsiToUnicodeBuf(path, &wPath, 0);
++    if (!retCode)
++    {
++        retCode = SHCreateDirectoryExW(hWnd, wPath, sec);
++        SHELL32_FreeUnicodeBuf(wPath);
++    }
++    return retCode;
++}
++
++/*************************************************************************
++ * SHCreateDirectoryExW      [SHELL32.@]
++ *
++ * See SHCreateDirectoryExA.
++ */
++int WINAPI SHCreateDirectoryExW(HWND hWnd, LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
++{
++    int ret = ERROR_BAD_PATHNAME;
++    TRACE("(%p, %s, %p)\n", hWnd, debugstr_w(path), sec);
++
++    if (PathIsRelativeW(path))
++    {
++      SetLastError(ret);
++    }
++    else
++    {
++        ret = SHNotifyCreateDirectoryW(path, sec);
++        /* Refuse to work on certain error codes before trying to create directories recursively */
++        if (ret != ERROR_SUCCESS &&
++          ret != ERROR_FILE_EXISTS &&
++          ret != ERROR_ALREADY_EXISTS &&
++          ret != ERROR_FILENAME_EXCED_RANGE)
++        {
++            WCHAR *pEnd, *pSlash, szTemp[MAX_PATH + 1];  /* extra for PathAddBackslash() */
++
++            lstrcpynW(szTemp, path, MAX_PATH);
++            pEnd = PathAddBackslashW(szTemp);
++            pSlash = szTemp + 3;
++
++            while (*pSlash)
++            {
++                while (*pSlash && *pSlash != '\\') pSlash++;
++                if (*pSlash)
++                {
++                    *pSlash = 0;    /* terminate path at separator */
++
++                    ret = SHNotifyCreateDirectoryW(szTemp, pSlash + 1 == pEnd ? sec : NULL);
++                }
++                *pSlash++ = '\\'; /* put the separator back */
++            }
++        }
++
++        if (ret && hWnd && (ERROR_CANCELLED != ret))
++        {
++            /* We failed and should show a dialog box */
++            FIXME("Show system error message, creating path %s, failed with error %d\n", debugstr_w(path), ret);
++            ret = ERROR_CANCELLED; /* Error has been already presented to user (not really yet!) */
++        }
++    }
++
++    return ret;
++}
++
++/*************************************************************************
++ * SHFindAttrW      [internal]
++ *
++ * Get the Attributes for a file or directory. The difference to GetAttributes()
++ * is that this function will also work for paths containing wildcard characters
++ * in its filename.
++
++ * PARAMS
++ *  path       [I]   path of directory or file to check
++ *  fileOnly   [I]   TRUE if only files should be found
++ *
++ * RETURNS
++ *  INVALID_FILE_ATTRIBUTES if the path does not exist, the actual attributes of
++ *  the first file or directory found otherwise
++ */
++static DWORD SHFindAttrW(LPCWSTR pName, BOOL fileOnly)
++{
++    WIN32_FIND_DATAW wfd;
++    BOOL b_FileMask = fileOnly && (NULL != StrPBrkW(pName, wWildcardChars));
++    DWORD dwAttr = INVALID_FILE_ATTRIBUTES;
++    HANDLE hFind = FindFirstFileW(pName, &wfd);
++
++    TRACE("%s %d\n", debugstr_w(pName), fileOnly);
++    if (INVALID_HANDLE_VALUE != hFind)
++    {
++        do
++        {
++            if (b_FileMask && IsAttribDir(wfd.dwFileAttributes))
++               continue;
++            dwAttr = wfd.dwFileAttributes;
++            break;
++        } while (FindNextFileW(hFind, &wfd));
++
++      FindClose(hFind);
++    }
++    return dwAttr;
++}
++
++/*************************************************************************
++ *
++ * SHNameTranslate HelperFunction for SHFileOperationA
++ *
++ * Translates a list of 0 terminated ASCII strings into Unicode. If *wString
++ * is NULL, only the necessary size of the string is determined and returned,
++ * otherwise the ASCII strings are copied into it and the buffer is increased
++ * to point to the location after the final 0 termination char.
++ */
++static DWORD SHNameTranslate(LPWSTR* wString, LPCWSTR* pWToFrom, BOOL more)
++{
++    DWORD size = 0, aSize = 0;
++    LPCSTR aString = (LPCSTR)*pWToFrom;
++
++    if (aString)
++    {
++        do
++        {
++            size = lstrlenA(aString) + 1;
++            aSize += size;
++            aString += size;
++        } while ((size != 1) && more);
++
++        /* The two sizes might be different in the case of multibyte chars */
++        size = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*pWToFrom, aSize, *wString, 0);
++        if (*wString) /* only in the second loop */
++        {
++            MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*pWToFrom, aSize, *wString, size);
++            *pWToFrom = *wString;
++            *wString += size;
++        }
++    }
++    return size;
++}
++/*************************************************************************
++ * SHFileOperationA          [SHELL32.@]
++ *
++ * Function to copy, move, delete and create one or more files with optional
++ * user prompts.
++ *
++ * PARAMS
++ *  lpFileOp   [I/O] pointer to a structure containing all the necessary information
++ *
++ * RETURNS
++ *  Success: ERROR_SUCCESS.
++ *  Failure: ERROR_CANCELLED.
++ *
++ * NOTES
++ *  exported by name
++ */
++int WINAPI SHFileOperationA(LPSHFILEOPSTRUCTA lpFileOp)
++{
++    SHFILEOPSTRUCTW nFileOp = *((LPSHFILEOPSTRUCTW)lpFileOp);
++    int retCode = 0;
++    DWORD size;
++    LPWSTR ForFree = NULL, /* we change wString in SHNameTranslate and can't use it for freeing */
++           wString = NULL; /* we change this in SHNameTranslate */
++
++    TRACE("\n");
++    if (FO_DELETE == (nFileOp.wFunc & FO_MASK))
++        nFileOp.pTo = NULL; /* we need a NULL or a valid pointer for translation */
++    if (!(nFileOp.fFlags & FOF_SIMPLEPROGRESS))
++        nFileOp.lpszProgressTitle = NULL; /* we need a NULL or a valid pointer for translation */
++    while (1) /* every loop calculate size, second translate also, if we have storage for this */
++    {
++        size = SHNameTranslate(&wString, &nFileOp.lpszProgressTitle, FALSE); /* no loop */
++        size += SHNameTranslate(&wString, &nFileOp.pFrom, TRUE); /* internal loop */
++        size += SHNameTranslate(&wString, &nFileOp.pTo, TRUE); /* internal loop */
++
++        if (ForFree)
++        {
++            retCode = SHFileOperationW(&nFileOp);
++            HeapFree(GetProcessHeap(), 0, ForFree); /* we cannot use wString, it was changed */
++            break;
++        }
++        else
++        {
++            wString = ForFree = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
++            if (ForFree) continue;
++            retCode = ERROR_OUTOFMEMORY;
++            nFileOp.fAnyOperationsAborted = TRUE;
++            SetLastError(retCode);
++            return retCode;
++        }
++    }
++
++    lpFileOp->hNameMappings = nFileOp.hNameMappings;
++    lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted;
++    return retCode;
++}
++
++static void __inline grow_list(FILE_LIST *list)
++{
++    FILE_ENTRY *newx = (FILE_ENTRY *)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, list->feFiles,
++                                  list->num_alloc * 2 * sizeof(*newx) );
++    list->feFiles = newx;
++    list->num_alloc *= 2;
++}
++
++/* adds a file to the FILE_ENTRY struct
++ */
++static void add_file_to_entry(FILE_ENTRY *feFile, LPCWSTR szFile)
++{
++    DWORD dwLen = lstrlenW(szFile) + 1;
++    LPCWSTR ptr;
++
++    feFile->szFullPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
++    lstrcpyW(feFile->szFullPath, szFile);
++
++    ptr = StrRChrW(szFile, NULL, '\\');
++    if (ptr)
++    {
++        dwLen = ptr - szFile + 1;
++        feFile->szDirectory = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
++        lstrcpynW(feFile->szDirectory, szFile, dwLen);
++
++        dwLen = lstrlenW(feFile->szFullPath) - dwLen + 1;
++        feFile->szFilename = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
++        lstrcpyW(feFile->szFilename, ptr + 1); /* skip over backslash */
++    }
++    feFile->bFromWildcard = FALSE;
++}
++
++static LPWSTR wildcard_to_file(LPCWSTR szWildCard, LPCWSTR szFileName)
++{
++    LPCWSTR ptr;
++    LPWSTR szFullPath;
++    DWORD dwDirLen, dwFullLen;
++
++    ptr = StrRChrW(szWildCard, NULL, '\\');
++    dwDirLen = ptr - szWildCard + 1;
++
++    dwFullLen = dwDirLen + lstrlenW(szFileName) + 1;
++    szFullPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwFullLen * sizeof(WCHAR));
++
++    lstrcpynW(szFullPath, szWildCard, dwDirLen + 1);
++    lstrcatW(szFullPath, szFileName);
++
++    return szFullPath;
++}
++
++static void parse_wildcard_files(FILE_LIST *flList, LPCWSTR szFile, LPDWORD pdwListIndex)
++{
++    WIN32_FIND_DATAW wfd;
++    HANDLE hFile = FindFirstFileW(szFile, &wfd);
++    FILE_ENTRY *file;
++    LPWSTR szFullPath;
++    BOOL res;
++
++    if (hFile == INVALID_HANDLE_VALUE) return;
++
++    for (res = TRUE; res; res = FindNextFileW(hFile, &wfd))
++    {
++        if (IsDotDir(wfd.cFileName))
++            continue;
++
++        if (*pdwListIndex >= flList->num_alloc)
++            grow_list( flList );
++
++        szFullPath = wildcard_to_file(szFile, wfd.cFileName);
++        file = &flList->feFiles[(*pdwListIndex)++];
++        add_file_to_entry(file, szFullPath);
++        file->bFromWildcard = TRUE;
++        file->attributes = wfd.dwFileAttributes;
++
++        if (IsAttribDir(file->attributes))
++            flList->bAnyDirectories = TRUE;
++
++        HeapFree(GetProcessHeap(), 0, szFullPath);
++    }
++
++    FindClose(hFile);
++}
++
++/* takes the null-separated file list and fills out the FILE_LIST */
++static HRESULT parse_file_list(FILE_LIST *flList, LPCWSTR szFiles)
++{
++    LPCWSTR ptr = szFiles;
++    WCHAR szCurFile[MAX_PATH];
++    DWORD i = 0;
++
++    if (!szFiles)
++        return ERROR_INVALID_PARAMETER;
++
++    flList->bAnyFromWildcard = FALSE;
++    flList->bAnyDirectories = FALSE;
++    flList->bAnyDontExist = FALSE;
++    flList->num_alloc = 32;
++    flList->dwNumFiles = 0;
++
++    /* empty list */
++    if (!szFiles[0])
++        return ERROR_ACCESS_DENIED;
++
++    flList->feFiles = (FILE_ENTRY *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
++                                flList->num_alloc * sizeof(FILE_ENTRY));
++
++    while (*ptr)
++    {
++        if (i >= flList->num_alloc) grow_list( flList );
++
++        /* change relative to absolute path */
++        if (PathIsRelativeW(ptr))
++        {
++            GetCurrentDirectoryW(MAX_PATH, szCurFile);
++            PathCombineW(szCurFile, szCurFile, ptr);
++            flList->feFiles[i].bFromRelative = TRUE;
++        }
++        else
++        {
++            lstrcpyW(szCurFile, ptr);
++            flList->feFiles[i].bFromRelative = FALSE;
++        }
++
++        /* parse wildcard files if they are in the filename */
++        if (StrPBrkW(szCurFile, wWildcardChars))
++        {
++            parse_wildcard_files(flList, szCurFile, &i);
++            flList->bAnyFromWildcard = TRUE;
++            i--;
++        }
++        else
++        {
++            FILE_ENTRY *file = &flList->feFiles[i];
++            add_file_to_entry(file, szCurFile);
++            file->attributes = GetFileAttributesW( file->szFullPath );
++            file->bExists = (file->attributes != INVALID_FILE_ATTRIBUTES);
++
++            if (!file->bExists)
++                flList->bAnyDontExist = TRUE;
++
++            if (IsAttribDir(file->attributes))
++                flList->bAnyDirectories = TRUE;
++        }
++
++        /* advance to the next string */
++        ptr += lstrlenW(ptr) + 1;
++        i++;
++    }
++    flList->dwNumFiles = i;
++
++    return S_OK;
++}
++
++/* free the FILE_LIST */
++static void destroy_file_list(FILE_LIST *flList)
++{
++    DWORD i;
++
++    if (!flList || !flList->feFiles)
++        return;
++
++    for (i = 0; i < flList->dwNumFiles; i++)
++    {
++        HeapFree(GetProcessHeap(), 0, flList->feFiles[i].szDirectory);
++        HeapFree(GetProcessHeap(), 0, flList->feFiles[i].szFilename);
++        HeapFree(GetProcessHeap(), 0, flList->feFiles[i].szFullPath);
++    }
++
++    HeapFree(GetProcessHeap(), 0, flList->feFiles);
++}
++
++static void copy_dir_to_dir(FILE_OPERATION *op, const FILE_ENTRY *feFrom, LPCWSTR szDestPath)
++{
++    WCHAR szFrom[MAX_PATH], szTo[MAX_PATH];
++    SHFILEOPSTRUCTW fileOp;
++
++    static const WCHAR wildCardFiles[] = {'*','.','*',0};
++
++    if (IsDotDir(feFrom->szFilename))
++        return;
++
++    if (PathFileExistsW(szDestPath))
++        PathCombineW(szTo, szDestPath, feFrom->szFilename);
++    else
++        lstrcpyW(szTo, szDestPath);
++
++    if (!(op->req->fFlags & FOF_NOCONFIRMATION) && PathFileExistsW(szTo))
++    {
++        if (!SHELL_ConfirmDialogW(op->req->hwnd, ASK_OVERWRITE_FOLDER, feFrom->szFilename, op))
++        {
++            /* Vista returns an ERROR_CANCELLED even if user pressed "No" */
++            if (!op->bManyItems)
++                op->bCancelled = TRUE;
++            return;
++        }
++    }
++
++    szTo[lstrlenW(szTo) + 1] = '\0';
++    SHNotifyCreateDirectoryW(szTo, NULL);
++
++    PathCombineW(szFrom, feFrom->szFullPath, wildCardFiles);
++    szFrom[lstrlenW(szFrom) + 1] = '\0';
++
++    fileOp = *op->req;
++    fileOp.pFrom = szFrom;
++    fileOp.pTo = szTo;
++    fileOp.fFlags &= ~FOF_MULTIDESTFILES; /* we know we're copying to one dir */
++
++    /* Don't ask the user about overwriting files when he accepted to overwrite the
++       folder. FIXME: this is not exactly what Windows does - e.g. there would be
++       an additional confirmation for a nested folder */
++    fileOp.fFlags |= FOF_NOCONFIRMATION;
++
++    SHFileOperationW(&fileOp);
++}
++
++static BOOL copy_file_to_file(FILE_OPERATION *op, const WCHAR *szFrom, const WCHAR *szTo)
++{
++    if (!(op->req->fFlags & FOF_NOCONFIRMATION) && PathFileExistsW(szTo))
++    {
++        if (!SHELL_ConfirmDialogW(op->req->hwnd, ASK_OVERWRITE_FILE, PathFindFileNameW(szTo), op))
++            return 0;
++    }
++
++    return SHNotifyCopyFileW(szFrom, szTo, FALSE) == 0;
++}
++
++/* copy a file or directory to another directory */
++static void copy_to_dir(FILE_OPERATION *op, const FILE_ENTRY *feFrom, const FILE_ENTRY *feTo)
++{
++    if (!PathFileExistsW(feTo->szFullPath))
++        SHNotifyCreateDirectoryW(feTo->szFullPath, NULL);
++
++    if (IsAttribFile(feFrom->attributes))
++    {
++        WCHAR szDestPath[MAX_PATH];
++
++        PathCombineW(szDestPath, feTo->szFullPath, feFrom->szFilename);
++        copy_file_to_file(op, feFrom->szFullPath, szDestPath);
++    }
++    else if (!(op->req->fFlags & FOF_FILESONLY && feFrom->bFromWildcard))
++        copy_dir_to_dir(op, feFrom, feTo->szFullPath);
++}
++
++static void create_dest_dirs(LPCWSTR szDestDir)
++{
++    WCHAR dir[MAX_PATH];
++    LPCWSTR ptr = StrChrW(szDestDir, '\\');
++
++    /* make sure all directories up to last one are created */
++    while (ptr && (ptr = StrChrW(ptr + 1, '\\')))
++    {
++        lstrcpynW(dir, szDestDir, ptr - szDestDir + 1);
++
++        if (!PathFileExistsW(dir))
++            SHNotifyCreateDirectoryW(dir, NULL);
++    }
++
++    /* create last directory */
++    if (!PathFileExistsW(szDestDir))
++        SHNotifyCreateDirectoryW(szDestDir, NULL);
++}
++
++/* the FO_COPY operation */
++static HRESULT copy_files(FILE_OPERATION *op, const FILE_LIST *flFrom, FILE_LIST *flTo)
++{
++    DWORD i;
++    const FILE_ENTRY *entryToCopy;
++    const FILE_ENTRY *fileDest = &flTo->feFiles[0];
++
++    if (flFrom->bAnyDontExist)
++        return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
++
++    if (flTo->dwNumFiles == 0)
++    {
++        /* If the destination is empty, SHFileOperation should use the current directory */
++        WCHAR curdir[MAX_PATH+1];
++
++        GetCurrentDirectoryW(MAX_PATH, curdir);
++        curdir[lstrlenW(curdir)+1] = 0;
++
++        destroy_file_list(flTo);
++        ZeroMemory(flTo, sizeof(FILE_LIST));
++        parse_file_list(flTo, curdir);
++        fileDest = &flTo->feFiles[0];
++    }
++
++    if (op->req->fFlags & FOF_MULTIDESTFILES)
++    {
++        if (flFrom->bAnyFromWildcard)
++            return ERROR_CANCELLED;
++
++        if (flFrom->dwNumFiles != flTo->dwNumFiles)
++        {
++            if (flFrom->dwNumFiles != 1 && !IsAttribDir(fileDest->attributes))
++                return ERROR_CANCELLED;
++
++            /* Free all but the first entry. */
++            for (i = 1; i < flTo->dwNumFiles; i++)
++            {
++                HeapFree(GetProcessHeap(), 0, flTo->feFiles[i].szDirectory);
++                HeapFree(GetProcessHeap(), 0, flTo->feFiles[i].szFilename);
++                HeapFree(GetProcessHeap(), 0, flTo->feFiles[i].szFullPath);
++            }
++
++            flTo->dwNumFiles = 1;
++        }
++        else if (IsAttribDir(fileDest->attributes))
++        {
++            for (i = 1; i < flTo->dwNumFiles; i++)
++                if (!IsAttribDir(flTo->feFiles[i].attributes) ||
++                    !IsAttribDir(flFrom->feFiles[i].attributes))
++                {
++                    return ERROR_CANCELLED;
++                }
++        }
++    }
++    else if (flFrom->dwNumFiles != 1)
++    {
++        if (flTo->dwNumFiles != 1 && !IsAttribDir(fileDest->attributes))
++            return ERROR_CANCELLED;
++
++        if (PathFileExistsW(fileDest->szFullPath) &&
++            IsAttribFile(fileDest->attributes))
++        {
++            return ERROR_CANCELLED;
++        }
++
++        if (flTo->dwNumFiles == 1 && fileDest->bFromRelative &&
++            !PathFileExistsW(fileDest->szFullPath))
++        {
++            return ERROR_CANCELLED;
++        }
++    }
++
++    for (i = 0; i < flFrom->dwNumFiles; i++)
++    {
++        entryToCopy = &flFrom->feFiles[i];
++
++        if ((op->req->fFlags & FOF_MULTIDESTFILES) &&
++            flTo->dwNumFiles > 1)
++        {
++            fileDest = &flTo->feFiles[i];
++        }
++
++        if (IsAttribDir(entryToCopy->attributes) &&
++            !lstrcmpiW(entryToCopy->szFullPath, fileDest->szDirectory))
++        {
++            return ERROR_SUCCESS;
++        }
++
++        create_dest_dirs(fileDest->szDirectory);
++
++        if (!lstrcmpiW(entryToCopy->szFullPath, fileDest->szFullPath))
++        {
++            if (IsAttribFile(entryToCopy->attributes))
++                return ERROR_NO_MORE_SEARCH_HANDLES;
++            else
++                return ERROR_SUCCESS;
++        }
++
++        if ((flFrom->dwNumFiles > 1 && flTo->dwNumFiles == 1) ||
++            IsAttribDir(fileDest->attributes))
++        {
++            copy_to_dir(op, entryToCopy, fileDest);
++        }
++        else if (IsAttribDir(entryToCopy->attributes))
++        {
++            copy_dir_to_dir(op, entryToCopy, fileDest->szFullPath);
++        }
++        else
++        {
++            if (!copy_file_to_file(op, entryToCopy->szFullPath, fileDest->szFullPath))
++            {
++                op->req->fAnyOperationsAborted = TRUE;
++                return ERROR_CANCELLED;
++            }
++        }
++
++        /* Vista return code. XP would return e.g. ERROR_FILE_NOT_FOUND, ERROR_ALREADY_EXISTS */
++        if (op->bCancelled)
++            return ERROR_CANCELLED;
++    }
++
++    /* Vista return code. On XP if the used pressed "No" for the last item,
++     * ERROR_ARENA_TRASHED would be returned */
++    return ERROR_SUCCESS;
++}
++
++static BOOL confirm_delete_list(HWND hWnd, DWORD fFlags, BOOL fTrash, const FILE_LIST *flFrom)
++{
++    if (flFrom->dwNumFiles > 1)
++    {
++        WCHAR tmp[8];
++        const WCHAR format[] = {'%','d',0};
++
++        wnsprintfW(tmp, sizeof(tmp)/sizeof(tmp[0]), format, flFrom->dwNumFiles);
++        return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_MULTIPLE_ITEM:ASK_DELETE_MULTIPLE_ITEM), tmp, NULL);
++    }
++    else
++    {
++        const FILE_ENTRY *fileEntry = &flFrom->feFiles[0];
++
++        if (IsAttribFile(fileEntry->attributes))
++            return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_FILE:ASK_DELETE_FILE), fileEntry->szFullPath, NULL);
++        else if (!(fFlags & FOF_FILESONLY && fileEntry->bFromWildcard))
++            return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_FOLDER:ASK_DELETE_FOLDER), fileEntry->szFullPath, NULL);
++    }
++    return TRUE;
++}
++
++/* the FO_DELETE operation */
++static HRESULT delete_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom)
++{
++    const FILE_ENTRY *fileEntry;
++    DWORD i;
++    BOOL bPathExists;
++    BOOL bTrash;
++
++    if (!flFrom->dwNumFiles)
++        return ERROR_SUCCESS;
++
++    /* Windows also checks only the first item */
++    bTrash = (lpFileOp->fFlags & FOF_ALLOWUNDO)
++        && TRASH_CanTrashFile(flFrom->feFiles[0].szFullPath);
++
++    if (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) || (!bTrash && lpFileOp->fFlags & FOF_WANTNUKEWARNING))
++        if (!confirm_delete_list(lpFileOp->hwnd, lpFileOp->fFlags, bTrash, flFrom))
++        {
++            lpFileOp->fAnyOperationsAborted = TRUE;
++            return 0;
++        }
++
++    for (i = 0; i < flFrom->dwNumFiles; i++)
++    {
++        bPathExists = TRUE;
++        fileEntry = &flFrom->feFiles[i];
++
++        if (!IsAttribFile(fileEntry->attributes) &&
++            (lpFileOp->fFlags & FOF_FILESONLY && fileEntry->bFromWildcard))
++            continue;
++
++        if (bTrash)
++        {
++            BOOL bDelete;
++            if (TRASH_TrashFile(fileEntry->szFullPath))
++                continue;
++
++            /* Note: Windows silently deletes the file in such a situation, we show a dialog */
++            if (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) || (lpFileOp->fFlags & FOF_WANTNUKEWARNING))
++                bDelete = SHELL_ConfirmDialogW(lpFileOp->hwnd, ASK_CANT_TRASH_ITEM, fileEntry->szFullPath, NULL);
++            else
++                bDelete = TRUE;
++
++            if (!bDelete)
++            {
++                lpFileOp->fAnyOperationsAborted = TRUE;
++                break;
++            }
++        }
++
++        /* delete the file or directory */
++        if (IsAttribFile(fileEntry->attributes))
++            bPathExists = DeleteFileW(fileEntry->szFullPath);
++        else
++            bPathExists = SHELL_DeleteDirectoryW(lpFileOp->hwnd, fileEntry->szFullPath, FALSE);
++
++        if (!bPathExists)
++        {
++            DWORD err = GetLastError();
++
++            if (ERROR_FILE_NOT_FOUND == err)
++            {
++                // This is a windows 2003 server specific value which ahs been removed.
++                // Later versions of windows return ERROR_FILE_NOT_FOUND.
++                return 1026;
++            }
++            else
++            {
++                return err;
++            }
++        }
++    }
++
++    return ERROR_SUCCESS;
++}
++
++static void move_dir_to_dir(LPSHFILEOPSTRUCTW lpFileOp, const FILE_ENTRY *feFrom, LPCWSTR szDestPath)
++{
++    WCHAR szFrom[MAX_PATH], szTo[MAX_PATH];
++    SHFILEOPSTRUCTW fileOp;
++
++    static const WCHAR wildCardFiles[] = {'*','.','*',0};
++
++    if (IsDotDir(feFrom->szFilename))
++        return;
++
++    SHNotifyCreateDirectoryW(szDestPath, NULL);
++
++    PathCombineW(szFrom, feFrom->szFullPath, wildCardFiles);
++    szFrom[lstrlenW(szFrom) + 1] = '\0';
++
++    lstrcpyW(szTo, szDestPath);
++    szTo[lstrlenW(szDestPath) + 1] = '\0';
++
++    fileOp = *lpFileOp;
++    fileOp.pFrom = szFrom;
++    fileOp.pTo = szTo;
++
++    SHFileOperationW(&fileOp);
++}
++
++/* moves a file or directory to another directory */
++static void move_to_dir(LPSHFILEOPSTRUCTW lpFileOp, const FILE_ENTRY *feFrom, const FILE_ENTRY *feTo)
++{
++    WCHAR szDestPath[MAX_PATH];
++
++    PathCombineW(szDestPath, feTo->szFullPath, feFrom->szFilename);
++
++    if (IsAttribFile(feFrom->attributes))
++        SHNotifyMoveFileW(feFrom->szFullPath, szDestPath);
++    else if (!(lpFileOp->fFlags & FOF_FILESONLY && feFrom->bFromWildcard))
++        move_dir_to_dir(lpFileOp, feFrom, szDestPath);
++}
++
++/* the FO_MOVE operation */
++static HRESULT move_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, const FILE_LIST *flTo)
++{
++    DWORD i;
++    const FILE_ENTRY *entryToMove;
++    const FILE_ENTRY *fileDest;
++
++    if (!flFrom->dwNumFiles || !flTo->dwNumFiles)
++        return ERROR_CANCELLED;
++
++    if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
++        flTo->dwNumFiles > 1 && flFrom->dwNumFiles > 1)
++    {
++        return ERROR_CANCELLED;
++    }
++
++    if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
++        !flFrom->bAnyDirectories &&
++        flFrom->dwNumFiles > flTo->dwNumFiles)
++    {
++        return ERROR_CANCELLED;
++    }
++
++    if (!PathFileExistsW(flTo->feFiles[0].szDirectory))
++        return ERROR_CANCELLED;
++
++    if ((lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
++        flFrom->dwNumFiles != flTo->dwNumFiles)
++    {
++        return ERROR_CANCELLED;
++    }
++
++    fileDest = &flTo->feFiles[0];
++    for (i = 0; i < flFrom->dwNumFiles; i++)
++    {
++        entryToMove = &flFrom->feFiles[i];
++
++        if (lpFileOp->fFlags & FOF_MULTIDESTFILES)
++            fileDest = &flTo->feFiles[i];
++
++        if (!PathFileExistsW(fileDest->szDirectory))
++            return ERROR_CANCELLED;
++
++        if (fileDest->bExists && IsAttribDir(fileDest->attributes))
++            move_to_dir(lpFileOp, entryToMove, fileDest);
++        else
++            SHNotifyMoveFileW(entryToMove->szFullPath, fileDest->szFullPath);
++    }
++
++    return ERROR_SUCCESS;
++}
++
++/* the FO_RENAME files */
++static HRESULT rename_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, const FILE_LIST *flTo)
++{
++    const FILE_ENTRY *feFrom;
++    const FILE_ENTRY *feTo;
++
++    if (flFrom->dwNumFiles != 1)
++        return ERROR_GEN_FAILURE;
++
++    if (flTo->dwNumFiles != 1)
++        return ERROR_CANCELLED;
++
++    feFrom = &flFrom->feFiles[0];
++    feTo= &flTo->feFiles[0];
++
++    /* fail if destination doesn't exist */
++    if (!feFrom->bExists)
++        return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
++
++    /* fail if destination already exists */
++    if (feTo->bExists)
++        return ERROR_ALREADY_EXISTS;
++
++    return SHNotifyMoveFileW(feFrom->szFullPath, feTo->szFullPath);
++}
++
++/* alert the user if an unsupported flag is used */
++static void check_flags(FILEOP_FLAGS fFlags)
++{
++    WORD wUnsupportedFlags = FOF_NO_CONNECTED_ELEMENTS |
++        FOF_NOCOPYSECURITYATTRIBS | FOF_NORECURSEREPARSE |
++        FOF_RENAMEONCOLLISION | FOF_WANTMAPPINGHANDLE;
++
++    if (fFlags & wUnsupportedFlags)
++        FIXME("Unsupported flags: %04x\n", fFlags);
++}
++
++/*************************************************************************
++ * SHFileOperationW          [SHELL32.@]
++ *
++ * See SHFileOperationA
++ */
++int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
++{
++    FILE_OPERATION op;
++    FILE_LIST flFrom, flTo;
++    int ret = 0;
++
++    if (!lpFileOp)
++        return ERROR_INVALID_PARAMETER;
++
++    check_flags(lpFileOp->fFlags);
++
++    ZeroMemory(&flFrom, sizeof(FILE_LIST));
++    ZeroMemory(&flTo, sizeof(FILE_LIST));
++
++    if ((ret = parse_file_list(&flFrom, lpFileOp->pFrom)))
++        return ret;
++
++    if (lpFileOp->wFunc != FO_DELETE)
++        parse_file_list(&flTo, lpFileOp->pTo);
++
++    ZeroMemory(&op, sizeof(op));
++    op.req = lpFileOp;
++    op.bManyItems = (flFrom.dwNumFiles > 1);
++
++    switch (lpFileOp->wFunc)
++    {
++        case FO_COPY:
++            ret = copy_files(&op, &flFrom, &flTo);
++            break;
++        case FO_DELETE:
++            ret = delete_files(lpFileOp, &flFrom);
++            break;
++        case FO_MOVE:
++            ret = move_files(lpFileOp, &flFrom, &flTo);
++            break;
++        case FO_RENAME:
++            ret = rename_files(lpFileOp, &flFrom, &flTo);
++            break;
++        default:
++            ret = ERROR_INVALID_PARAMETER;
++            break;
++    }
++
++    destroy_file_list(&flFrom);
++
++    if (lpFileOp->wFunc != FO_DELETE)
++        destroy_file_list(&flTo);
++
++    if (ret == ERROR_CANCELLED)
++        lpFileOp->fAnyOperationsAborted = TRUE;
++
++    return ret;
++}
++
++#define SHDSA_GetItemCount(hdsa) (*(int*)(hdsa))
++
++/*************************************************************************
++ * SHFreeNameMappings      [shell32.246]
++ *
++ * Free the mapping handle returned by SHFileOperation if FOF_WANTSMAPPINGHANDLE
++ * was specified.
++ *
++ * PARAMS
++ *  hNameMapping [I] handle to the name mappings used during renaming of files
++ *
++ * RETURNS
++ *  Nothing
++ */
++void WINAPI SHFreeNameMappings(HANDLE hNameMapping)
++{
++    if (hNameMapping)
++    {
++        int i = SHDSA_GetItemCount((HDSA)hNameMapping) - 1;
++
++        for (; i>= 0; i--)
++        {
++            LPSHNAMEMAPPINGW lp = (SHNAMEMAPPINGW *)DSA_GetItemPtr((HDSA)hNameMapping, i);
++
++            SHFree(lp->pszOldPath);
++            SHFree(lp->pszNewPath);
++        }
++        DSA_Destroy((HDSA)hNameMapping);
++    }
++}
++
++/*************************************************************************
++ * SheGetDirA [SHELL32.@]
++ *
++ * drive = 0: returns the current directory path
++ * drive > 0: returns the current directory path of the specified drive
++ *            drive=1 -> A:  drive=2 -> B:  ...
++ * returns 0 if successful
++*/
++EXTERN_C DWORD WINAPI SheGetDirA(DWORD drive, LPSTR buffer)
++{
++    WCHAR org_path[MAX_PATH];
++    DWORD ret;
++    char drv_path[3];
++
++    /* change current directory to the specified drive */
++    if (drive) {
++        strcpy(drv_path, "A:");
++        drv_path[0] += (char)drive-1;
++
++        GetCurrentDirectoryW(MAX_PATH, org_path);
++
++        SetCurrentDirectoryA(drv_path);
++    }
++
++    /* query current directory path of the specified drive */
++    ret = GetCurrentDirectoryA(MAX_PATH, buffer);
++
++    /* back to the original drive */
++    if (drive)
++        SetCurrentDirectoryW(org_path);
++
++    if (!ret)
++        return GetLastError();
++
++    return 0;
++}
++
++/*************************************************************************
++ * SheGetDirW [SHELL32.@]
++ *
++ * drive = 0: returns the current directory path
++ * drive > 0: returns the current directory path of the specified drive
++ *            drive=1 -> A:  drive=2 -> B:  ...
++ * returns 0 if successful
++ */
++EXTERN_C DWORD WINAPI SheGetDirW(DWORD drive, LPWSTR buffer)
++{
++    WCHAR org_path[MAX_PATH];
++    DWORD ret;
++    char drv_path[3];
++
++    /* change current directory to the specified drive */
++    if (drive)
++    {
++        strcpy(drv_path, "A:");
++        drv_path[0] += (char)drive-1;
++
++        GetCurrentDirectoryW(MAX_PATH, org_path);
++
++        SetCurrentDirectoryA(drv_path);
++    }
++
++    /* query current directory path of the specified drive */
++    ret = GetCurrentDirectoryW(MAX_PATH, buffer);
++
++    /* back to the original drive */
++    if (drive)
++        SetCurrentDirectoryW(org_path);
++
++    if (!ret)
++        return GetLastError();
++
++    return 0;
++}
++
++/*************************************************************************
++ * SheChangeDirA [SHELL32.@]
++ *
++ * changes the current directory to the specified path
++ * and returns 0 if successful
++ */
++EXTERN_C DWORD WINAPI SheChangeDirA(LPSTR path)
++{
++    if (SetCurrentDirectoryA(path))
++        return 0;
++    else
++        return GetLastError();
++}
++
++/*************************************************************************
++ * SheChangeDirW [SHELL32.@]
++ *
++ * changes the current directory to the specified path
++ * and returns 0 if successful
++ */
++EXTERN_C DWORD WINAPI SheChangeDirW(LPWSTR path)
++{
++    if (SetCurrentDirectoryW(path))
++        return 0;
++    else
++        return GetLastError();
++}
++
++/*************************************************************************
++ * IsNetDrive            [SHELL32.66]
++ */
++EXTERN_C int WINAPI IsNetDrive(int drive)
++{
++    char root[4];
++    strcpy(root, "A:\\");
++    root[0] += (char)drive;
++    return (GetDriveTypeA(root) == DRIVE_REMOTE);
++}
++
++
++/*************************************************************************
++ * RealDriveType                [SHELL32.524]
++ */
++EXTERN_C INT WINAPI RealDriveType(INT drive, BOOL bQueryNet)
++{
++    char root[] = "A:\\";
++    root[0]&