4 * Copyright 2000 Juergen Schmied
5 * Copyright 2002 Andriy Palamarchuk
6 * Copyright 2004 Dietrich Teickner (from Odin)
7 * Copyright 2004 Rolf Kalbermatter
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
40 #define NO_SHLWAPI_STREAM
42 #include "shell32_main.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
48 #define IsAttrib(x, y) ((INVALID_FILE_ATTRIBUTES != (x)) && ((x) & (y)))
49 #define IsAttribFile(x) (!((x) & FILE_ATTRIBUTE_DIRECTORY))
50 #define IsAttribDir(x) IsAttrib(x, FILE_ATTRIBUTE_DIRECTORY)
51 #define IsDotDir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
55 #define DE_SAMEFILE 0x71
56 #define DE_DESTSAMETREE 0x7D
58 static DWORD
SHNotifyCreateDirectoryA(LPCSTR path
, LPSECURITY_ATTRIBUTES sec
);
59 static DWORD
SHNotifyCreateDirectoryW(LPCWSTR path
, LPSECURITY_ATTRIBUTES sec
);
60 static DWORD
SHNotifyRemoveDirectoryA(LPCSTR path
);
61 static DWORD
SHNotifyRemoveDirectoryW(LPCWSTR path
);
62 static DWORD
SHNotifyDeleteFileA(LPCSTR path
);
63 static DWORD
SHNotifyDeleteFileW(LPCWSTR path
);
64 static DWORD
SHNotifyMoveFileW(LPCWSTR src
, LPCWSTR dest
);
65 static DWORD
SHNotifyCopyFileW(LPCWSTR src
, LPCWSTR dest
, BOOL bFailIfExists
);
66 static DWORD
SHFindAttrW(LPCWSTR pName
, BOOL fileOnly
);
76 /* Confirm dialogs with an optional "Yes To All" as used in file operations confirmations
78 struct confirm_msg_info
86 /* as some buttons may be hidden and the dialog height may change we may need
87 * to move the controls */
88 static void confirm_msg_move_button(HWND hDlg
, INT iId
, INT
*xPos
, INT yOffset
, BOOL bShow
)
90 HWND hButton
= GetDlgItem(hDlg
, iId
);
96 GetWindowRect(hButton
, &r
);
97 MapWindowPoints( 0, hDlg
, (POINT
*)&r
, 2 );
98 width
= r
.right
- r
.left
;
99 SetWindowPos(hButton
, 0, *xPos
- width
, r
.top
- yOffset
, 0, 0,
100 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOREDRAW
);
104 ShowWindow(hButton
, SW_HIDE
);
107 /* Note: we paint the text manually and don't use the static control to make
108 * sure the text has the same height as the one computed in WM_INITDIALOG
110 static INT_PTR
ConfirmMsgBox_Paint(HWND hDlg
)
117 BeginPaint(hDlg
, &ps
);
119 SetBkMode(hdc
, TRANSPARENT
);
121 GetClientRect(GetDlgItem(hDlg
, IDD_MESSAGE
), &r
);
122 /* this will remap the rect to dialog coords */
123 MapWindowPoints(GetDlgItem(hDlg
, IDD_MESSAGE
), hDlg
, (LPPOINT
)&r
, 2);
124 hOldFont
= SelectObject(hdc
, (HFONT
)SendDlgItemMessageW(hDlg
, IDD_MESSAGE
, WM_GETFONT
, 0, 0));
125 DrawTextW(hdc
, GetPropW(hDlg
, L
"WINE_CONFIRM"), -1, &r
, DT_NOPREFIX
| DT_PATH_ELLIPSIS
| DT_WORDBREAK
);
126 SelectObject(hdc
, hOldFont
);
131 static INT_PTR
ConfirmMsgBox_Init(HWND hDlg
, LPARAM lParam
)
133 struct confirm_msg_info
*info
= (struct confirm_msg_info
*)lParam
;
140 SetWindowTextW(hDlg
, info
->lpszCaption
);
141 ShowWindow(GetDlgItem(hDlg
, IDD_MESSAGE
), SW_HIDE
);
142 SetPropW(hDlg
, L
"WINE_CONFIRM", info
->lpszText
);
143 SendDlgItemMessageW(hDlg
, IDD_ICON
, STM_SETICON
, (WPARAM
)info
->hIcon
, 0);
145 /* compute the text height and resize the dialog */
146 GetClientRect(GetDlgItem(hDlg
, IDD_MESSAGE
), &r
);
149 hOldFont
= SelectObject(hdc
, (HFONT
)SendDlgItemMessageW(hDlg
, IDD_MESSAGE
, WM_GETFONT
, 0, 0));
150 DrawTextW(hdc
, info
->lpszText
, -1, &r
, DT_NOPREFIX
| DT_PATH_ELLIPSIS
| DT_WORDBREAK
| DT_CALCRECT
);
151 SelectObject(hdc
, hOldFont
);
153 yOffset
= min(yOffset
, 35); /* don't make the dialog too small */
154 ReleaseDC(hDlg
, hdc
);
156 GetClientRect(hDlg
, &r
);
158 GetWindowRect(hDlg
, &r
);
159 width
= r
.right
- r
.left
;
160 height
= r
.bottom
- r
.top
- yOffset
;
161 MoveWindow(hDlg
, (GetSystemMetrics(SM_CXSCREEN
) - width
)/2,
162 (GetSystemMetrics(SM_CYSCREEN
) - height
)/2, width
, height
, FALSE
);
164 confirm_msg_move_button(hDlg
, IDCANCEL
, &xPos
, yOffset
, info
->bYesToAll
);
165 confirm_msg_move_button(hDlg
, IDNO
, &xPos
, yOffset
, TRUE
);
166 confirm_msg_move_button(hDlg
, IDD_YESTOALL
, &xPos
, yOffset
, info
->bYesToAll
);
167 confirm_msg_move_button(hDlg
, IDYES
, &xPos
, yOffset
, TRUE
);
171 static INT_PTR CALLBACK
ConfirmMsgBoxProc(HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
176 return ConfirmMsgBox_Init(hDlg
, lParam
);
178 return ConfirmMsgBox_Paint(hDlg
);
180 EndDialog(hDlg
, wParam
);
183 EndDialog(hDlg
, IDCANCEL
);
189 static int SHELL_ConfirmMsgBox(HWND hWnd
, LPWSTR lpszText
, LPWSTR lpszCaption
, HICON hIcon
, BOOL bYesToAll
)
191 struct confirm_msg_info info
;
193 info
.lpszText
= lpszText
;
194 info
.lpszCaption
= lpszCaption
;
196 info
.bYesToAll
= bYesToAll
;
197 return DialogBoxParamW(shell32_hInstance
, L
"SHELL_YESTOALL_MSGBOX", hWnd
, ConfirmMsgBoxProc
, (LPARAM
)&info
);
200 /* confirmation dialogs content */
203 HINSTANCE hIconInstance
;
204 UINT icon_resource_id
;
205 UINT caption_resource_id
, text_resource_id
;
206 } SHELL_ConfirmIDstruc
;
208 static BOOL
SHELL_ConfirmIDs(int nKindOfDialog
, SHELL_ConfirmIDstruc
*ids
)
210 ids
->hIconInstance
= shell32_hInstance
;
211 switch (nKindOfDialog
) {
212 case ASK_DELETE_FILE
:
213 ids
->icon_resource_id
= IDI_SHELL_CONFIRM_DELETE
;
214 ids
->caption_resource_id
= IDS_DELETEITEM_CAPTION
;
215 ids
->text_resource_id
= IDS_DELETEITEM_TEXT
;
217 case ASK_DELETE_FOLDER
:
218 ids
->icon_resource_id
= IDI_SHELL_CONFIRM_DELETE
;
219 ids
->caption_resource_id
= IDS_DELETEFOLDER_CAPTION
;
220 ids
->text_resource_id
= IDS_DELETEITEM_TEXT
;
222 case ASK_DELETE_MULTIPLE_ITEM
:
223 ids
->icon_resource_id
= IDI_SHELL_CONFIRM_DELETE
;
224 ids
->caption_resource_id
= IDS_DELETEITEM_CAPTION
;
225 ids
->text_resource_id
= IDS_DELETEMULTIPLE_TEXT
;
228 ids
->icon_resource_id
= IDI_SHELL_TRASH_FILE
;
229 ids
->caption_resource_id
= IDS_DELETEITEM_CAPTION
;
230 ids
->text_resource_id
= IDS_TRASHITEM_TEXT
;
232 case ASK_TRASH_FOLDER
:
233 ids
->icon_resource_id
= IDI_SHELL_TRASH_FILE
;
234 ids
->caption_resource_id
= IDS_DELETEFOLDER_CAPTION
;
235 ids
->text_resource_id
= IDS_TRASHFOLDER_TEXT
;
237 case ASK_TRASH_MULTIPLE_ITEM
:
238 ids
->icon_resource_id
= IDI_SHELL_TRASH_FILE
;
239 ids
->caption_resource_id
= IDS_DELETEITEM_CAPTION
;
240 ids
->text_resource_id
= IDS_TRASHMULTIPLE_TEXT
;
242 case ASK_CANT_TRASH_ITEM
:
243 ids
->icon_resource_id
= IDI_SHELL_CONFIRM_DELETE
;
244 ids
->caption_resource_id
= IDS_DELETEITEM_CAPTION
;
245 ids
->text_resource_id
= IDS_CANTTRASH_TEXT
;
247 case ASK_DELETE_SELECTED
:
248 ids
->icon_resource_id
= IDI_SHELL_CONFIRM_DELETE
;
249 ids
->caption_resource_id
= IDS_DELETEITEM_CAPTION
;
250 ids
->text_resource_id
= IDS_DELETESELECTED_TEXT
;
252 case ASK_OVERWRITE_FILE
:
253 ids
->hIconInstance
= NULL
;
254 ids
->icon_resource_id
= IDI_WARNING
;
255 ids
->caption_resource_id
= IDS_OVERWRITEFILE_CAPTION
;
256 ids
->text_resource_id
= IDS_OVERWRITEFILE_TEXT
;
258 case ASK_OVERWRITE_FOLDER
:
259 ids
->hIconInstance
= NULL
;
260 ids
->icon_resource_id
= IDI_WARNING
;
261 ids
->caption_resource_id
= IDS_OVERWRITEFILE_CAPTION
;
262 ids
->text_resource_id
= IDS_OVERWRITEFOLDER_TEXT
;
265 FIXME(" Unhandled nKindOfDialog %d stub\n", nKindOfDialog
);
270 static BOOL
SHELL_ConfirmDialogW(HWND hWnd
, int nKindOfDialog
, LPCWSTR szDir
, FILE_OPERATION
*op
)
272 WCHAR szCaption
[255], szText
[255], szBuffer
[MAX_PATH
+ 256];
273 SHELL_ConfirmIDstruc ids
;
278 assert(nKindOfDialog
>= 0 && nKindOfDialog
< 32);
279 if (op
&& (op
->dwYesToAllMask
& (1 << nKindOfDialog
)))
282 if (!SHELL_ConfirmIDs(nKindOfDialog
, &ids
)) return FALSE
;
284 LoadStringW(shell32_hInstance
, ids
.caption_resource_id
, szCaption
, ARRAY_SIZE(szCaption
));
285 LoadStringW(shell32_hInstance
, ids
.text_resource_id
, szText
, ARRAY_SIZE(szText
));
287 args
[0] = (DWORD_PTR
)szDir
;
288 FormatMessageW(FORMAT_MESSAGE_FROM_STRING
|FORMAT_MESSAGE_ARGUMENT_ARRAY
,
289 szText
, 0, 0, szBuffer
, ARRAY_SIZE(szBuffer
), (va_list*)args
);
291 hIcon
= LoadIconW(ids
.hIconInstance
, (LPWSTR
)MAKEINTRESOURCE(ids
.icon_resource_id
));
293 ret
= SHELL_ConfirmMsgBox(hWnd
, szBuffer
, szCaption
, hIcon
, op
&& op
->bManyItems
);
295 if (ret
== IDD_YESTOALL
) {
296 op
->dwYesToAllMask
|= (1 << nKindOfDialog
);
300 op
->bCancelled
= TRUE
;
302 op
->req
->fAnyOperationsAborted
= TRUE
;
307 BOOL
SHELL_ConfirmYesNoW(HWND hWnd
, int nKindOfDialog
, LPCWSTR szDir
)
309 return SHELL_ConfirmDialogW(hWnd
, nKindOfDialog
, szDir
, NULL
);
312 static DWORD
SHELL32_AnsiToUnicodeBuf(LPCSTR aPath
, LPWSTR
*wPath
, DWORD minChars
)
314 DWORD len
= MultiByteToWideChar(CP_ACP
, 0, aPath
, -1, NULL
, 0);
319 *wPath
= heap_alloc(len
* sizeof(WCHAR
));
322 MultiByteToWideChar(CP_ACP
, 0, aPath
, -1, *wPath
, len
);
325 return E_OUTOFMEMORY
;
328 HRESULT WINAPI
SHIsFileAvailableOffline(LPCWSTR path
, LPDWORD status
)
330 FIXME("(%s, %p) stub\n", debugstr_w(path
), status
);
334 /**************************************************************************
335 * SHELL_DeleteDirectory() [internal]
337 * Asks for confirmation when bShowUI is true and deletes the directory and
338 * all its subdirectories and files if necessary.
340 static DWORD
SHELL_DeleteDirectoryW(HWND hwnd
, LPCWSTR pszDir
, BOOL bShowUI
)
344 WIN32_FIND_DATAW wfd
;
345 WCHAR szTemp
[MAX_PATH
];
347 PathCombineW(szTemp
, pszDir
, L
"*");
348 hFind
= FindFirstFileW(szTemp
, &wfd
);
350 if (hFind
!= INVALID_HANDLE_VALUE
) {
351 if (!bShowUI
|| SHELL_ConfirmDialogW(hwnd
, ASK_DELETE_FOLDER
, pszDir
, NULL
)) {
353 if (IsDotDir(wfd
.cFileName
))
355 PathCombineW(szTemp
, pszDir
, wfd
.cFileName
);
356 if (FILE_ATTRIBUTE_DIRECTORY
& wfd
.dwFileAttributes
)
357 ret
= SHELL_DeleteDirectoryW(hwnd
, szTemp
, FALSE
);
359 ret
= SHNotifyDeleteFileW(szTemp
);
360 } while (!ret
&& FindNextFileW(hFind
, &wfd
));
364 if (ret
== ERROR_SUCCESS
)
365 ret
= SHNotifyRemoveDirectoryW(pszDir
);
367 return ret
== ERROR_PATH_NOT_FOUND
?
368 0x7C: /* DE_INVALIDFILES (legacy Windows error) */
372 /**************************************************************************
373 * Win32CreateDirectory [SHELL32.93]
375 * Creates a directory. Also triggers a change notify if one exists.
378 * path [I] path to directory to create
381 * TRUE if successful, FALSE otherwise
384 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
385 * This is Unicode on NT/2000
387 static DWORD
SHNotifyCreateDirectoryA(LPCSTR path
, LPSECURITY_ATTRIBUTES sec
)
392 TRACE("(%s, %p)\n", debugstr_a(path
), sec
);
394 retCode
= SHELL32_AnsiToUnicodeBuf(path
, &wPath
, 0);
397 retCode
= SHNotifyCreateDirectoryW(wPath
, sec
);
403 /**********************************************************************/
405 static DWORD
SHNotifyCreateDirectoryW(LPCWSTR path
, LPSECURITY_ATTRIBUTES sec
)
407 TRACE("(%s, %p)\n", debugstr_w(path
), sec
);
409 if (CreateDirectoryW(path
, sec
))
411 SHChangeNotify(SHCNE_MKDIR
, SHCNF_PATHW
, path
, NULL
);
412 return ERROR_SUCCESS
;
414 return GetLastError();
417 /**********************************************************************/
419 BOOL WINAPI
Win32CreateDirectoryAW(LPCVOID path
, LPSECURITY_ATTRIBUTES sec
)
421 if (SHELL_OsIsUnicode())
422 return (SHNotifyCreateDirectoryW(path
, sec
) == ERROR_SUCCESS
);
423 return (SHNotifyCreateDirectoryA(path
, sec
) == ERROR_SUCCESS
);
426 /************************************************************************
427 * Win32RemoveDirectory [SHELL32.94]
429 * Deletes a directory. Also triggers a change notify if one exists.
432 * path [I] path to directory to delete
435 * TRUE if successful, FALSE otherwise
438 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
439 * This is Unicode on NT/2000
441 static DWORD
SHNotifyRemoveDirectoryA(LPCSTR path
)
446 TRACE("(%s)\n", debugstr_a(path
));
448 retCode
= SHELL32_AnsiToUnicodeBuf(path
, &wPath
, 0);
451 retCode
= SHNotifyRemoveDirectoryW(wPath
);
457 /***********************************************************************/
459 static DWORD
SHNotifyRemoveDirectoryW(LPCWSTR path
)
462 TRACE("(%s)\n", debugstr_w(path
));
464 ret
= RemoveDirectoryW(path
);
467 /* Directory may be write protected */
468 DWORD dwAttr
= GetFileAttributesW(path
);
469 if (IsAttrib(dwAttr
, FILE_ATTRIBUTE_READONLY
))
470 if (SetFileAttributesW(path
, dwAttr
& ~FILE_ATTRIBUTE_READONLY
))
471 ret
= RemoveDirectoryW(path
);
475 SHChangeNotify(SHCNE_RMDIR
, SHCNF_PATHW
, path
, NULL
);
476 return ERROR_SUCCESS
;
478 return GetLastError();
481 /***********************************************************************/
483 BOOL WINAPI
Win32RemoveDirectoryAW(LPCVOID path
)
485 if (SHELL_OsIsUnicode())
486 return (SHNotifyRemoveDirectoryW(path
) == ERROR_SUCCESS
);
487 return (SHNotifyRemoveDirectoryA(path
) == ERROR_SUCCESS
);
490 /************************************************************************
491 * Win32DeleteFile [SHELL32.164]
493 * Deletes a file. Also triggers a change notify if one exists.
496 * path [I] path to file to delete
499 * TRUE if successful, FALSE otherwise
502 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
503 * This is Unicode on NT/2000
505 static DWORD
SHNotifyDeleteFileA(LPCSTR path
)
510 TRACE("(%s)\n", debugstr_a(path
));
512 retCode
= SHELL32_AnsiToUnicodeBuf(path
, &wPath
, 0);
515 retCode
= SHNotifyDeleteFileW(wPath
);
521 /***********************************************************************/
523 static DWORD
SHNotifyDeleteFileW(LPCWSTR path
)
527 TRACE("(%s)\n", debugstr_w(path
));
529 ret
= DeleteFileW(path
);
532 /* File may be write protected or a system file */
533 DWORD dwAttr
= GetFileAttributesW(path
);
534 if (IsAttrib(dwAttr
, FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
))
535 if (SetFileAttributesW(path
, dwAttr
& ~(FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
)))
536 ret
= DeleteFileW(path
);
540 SHChangeNotify(SHCNE_DELETE
, SHCNF_PATHW
, path
, NULL
);
541 return ERROR_SUCCESS
;
543 return GetLastError();
546 /***********************************************************************/
548 DWORD WINAPI
Win32DeleteFileAW(LPCVOID path
)
550 if (SHELL_OsIsUnicode())
551 return (SHNotifyDeleteFileW(path
) == ERROR_SUCCESS
);
552 return (SHNotifyDeleteFileA(path
) == ERROR_SUCCESS
);
555 /************************************************************************
556 * SHNotifyMoveFile [internal]
558 * Moves a file. Also triggers a change notify if one exists.
561 * src [I] path to source file to move
562 * dest [I] path to target file to move to
565 * ERROR_SUCCESS if successful
567 static DWORD
SHNotifyMoveFileW(LPCWSTR src
, LPCWSTR dest
)
571 TRACE("(%s %s)\n", debugstr_w(src
), debugstr_w(dest
));
573 ret
= MoveFileExW(src
, dest
, MOVEFILE_REPLACE_EXISTING
);
575 /* MOVEFILE_REPLACE_EXISTING fails with dirs, so try MoveFile */
577 ret
= MoveFileW(src
, dest
);
583 dwAttr
= SHFindAttrW(dest
, FALSE
);
584 if (INVALID_FILE_ATTRIBUTES
== dwAttr
)
586 /* Source file may be write protected or a system file */
587 dwAttr
= GetFileAttributesW(src
);
588 if (IsAttrib(dwAttr
, FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
))
589 if (SetFileAttributesW(src
, dwAttr
& ~(FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
)))
590 ret
= MoveFileW(src
, dest
);
595 SHChangeNotify(SHCNE_RENAMEITEM
, SHCNF_PATHW
, src
, dest
);
596 return ERROR_SUCCESS
;
598 return GetLastError();
601 /************************************************************************
602 * SHNotifyCopyFile [internal]
604 * Copies a file. Also triggers a change notify if one exists.
607 * src [I] path to source file to move
608 * dest [I] path to target file to move to
609 * bFailIfExists [I] if TRUE, the target file will not be overwritten if
610 * a file with this name already exists
613 * ERROR_SUCCESS if successful
615 static DWORD
SHNotifyCopyFileW(LPCWSTR src
, LPCWSTR dest
, BOOL bFailIfExists
)
620 TRACE("(%s %s %s)\n", debugstr_w(src
), debugstr_w(dest
), bFailIfExists
? "failIfExists" : "");
622 /* Destination file may already exist with read only attribute */
623 attribs
= GetFileAttributesW(dest
);
624 if (IsAttrib(attribs
, FILE_ATTRIBUTE_READONLY
))
625 SetFileAttributesW(dest
, attribs
& ~FILE_ATTRIBUTE_READONLY
);
627 ret
= CopyFileW(src
, dest
, bFailIfExists
);
630 SHChangeNotify(SHCNE_CREATE
, SHCNF_PATHW
, dest
, NULL
);
631 return ERROR_SUCCESS
;
634 return GetLastError();
637 /*************************************************************************
638 * SHCreateDirectory [SHELL32.165]
640 * This function creates a file system folder whose fully qualified path is
641 * given by path. If one or more of the intermediate folders do not exist,
642 * they will be created as well.
646 * path [I] path of directory to create
649 * ERROR_SUCCESS or one of the following values:
650 * ERROR_BAD_PATHNAME if the path is relative
651 * ERROR_FILE_EXISTS when a file with that name exists
652 * ERROR_PATH_NOT_FOUND can't find the path, probably invalid
653 * ERROR_INVALID_NAME if the path contains invalid chars
654 * ERROR_ALREADY_EXISTS when the directory already exists
655 * ERROR_FILENAME_EXCED_RANGE if the filename was too long to process
658 * exported by ordinal
660 * WinNT/2000 exports Unicode
662 DWORD WINAPI
SHCreateDirectory(HWND hWnd
, LPCVOID path
)
664 if (SHELL_OsIsUnicode())
665 return SHCreateDirectoryExW(hWnd
, path
, NULL
);
666 return SHCreateDirectoryExA(hWnd
, path
, NULL
);
669 /*************************************************************************
670 * SHCreateDirectoryExA [SHELL32.@]
672 * This function creates a file system folder whose fully qualified path is
673 * given by path. If one or more of the intermediate folders do not exist,
674 * they will be created as well.
678 * path [I] path of directory to create
679 * sec [I] security attributes to use or NULL
682 * ERROR_SUCCESS or one of the following values:
683 * ERROR_BAD_PATHNAME or ERROR_PATH_NOT_FOUND if the path is relative
684 * ERROR_INVALID_NAME if the path contains invalid chars
685 * ERROR_FILE_EXISTS when a file with that name exists
686 * ERROR_ALREADY_EXISTS when the directory already exists
687 * ERROR_FILENAME_EXCED_RANGE if the filename was too long to process
689 * FIXME: Not implemented yet;
690 * SHCreateDirectoryEx also verifies that the files in the directory will be visible
691 * if the path is a network path to deal with network drivers which might have a limited
692 * but unknown maximum path length. If not:
694 * If hWnd is set to a valid window handle, a message box is displayed warning
695 * the user that the files may not be accessible. If the user chooses not to
696 * proceed, the function returns ERROR_CANCELLED.
698 * If hWnd is set to NULL, no user interface is displayed and the function
699 * returns ERROR_CANCELLED.
701 int WINAPI
SHCreateDirectoryExA(HWND hWnd
, LPCSTR path
, LPSECURITY_ATTRIBUTES sec
)
706 TRACE("(%s, %p)\n", debugstr_a(path
), sec
);
708 retCode
= SHELL32_AnsiToUnicodeBuf(path
, &wPath
, 0);
711 retCode
= SHCreateDirectoryExW(hWnd
, wPath
, sec
);
717 /*************************************************************************
718 * SHCreateDirectoryExW [SHELL32.@]
720 * See SHCreateDirectoryExA.
722 int WINAPI
SHCreateDirectoryExW(HWND hWnd
, LPCWSTR path
, LPSECURITY_ATTRIBUTES sec
)
724 int ret
= ERROR_BAD_PATHNAME
;
725 TRACE("(%p, %s, %p)\n", hWnd
, debugstr_w(path
), sec
);
727 if (PathIsRelativeW(path
))
733 ret
= SHNotifyCreateDirectoryW(path
, sec
);
734 /* Refuse to work on certain error codes before trying to create directories recursively */
735 if (ret
!= ERROR_SUCCESS
&&
736 ret
!= ERROR_FILE_EXISTS
&&
737 ret
!= ERROR_ALREADY_EXISTS
&&
738 ret
!= ERROR_FILENAME_EXCED_RANGE
)
740 WCHAR
*pEnd
, *pSlash
, szTemp
[MAX_PATH
+ 1]; /* extra for PathAddBackslash() */
742 lstrcpynW(szTemp
, path
, MAX_PATH
);
743 pEnd
= PathAddBackslashW(szTemp
);
748 while (*pSlash
&& *pSlash
!= '\\') pSlash
++;
751 *pSlash
= 0; /* terminate path at separator */
753 ret
= SHNotifyCreateDirectoryW(szTemp
, pSlash
+ 1 == pEnd
? sec
: NULL
);
755 *pSlash
++ = '\\'; /* put the separator back */
760 ret
!= ERROR_CANCELLED
&&
761 ret
!= ERROR_ALREADY_EXISTS
)
763 /* We failed and should show a dialog box */
764 FIXME("Show system error message, creating path %s, failed with error %d\n", debugstr_w(path
), ret
);
765 ret
= ERROR_CANCELLED
; /* Error has been already presented to user (not really yet!) */
771 /*************************************************************************
772 * SHFindAttrW [internal]
774 * Get the Attributes for a file or directory. The difference to GetAttributes()
775 * is that this function will also work for paths containing wildcard characters
779 * path [I] path of directory or file to check
780 * fileOnly [I] TRUE if only files should be found
783 * INVALID_FILE_ATTRIBUTES if the path does not exist, the actual attributes of
784 * the first file or directory found otherwise
786 static DWORD
SHFindAttrW(LPCWSTR pName
, BOOL fileOnly
)
788 WIN32_FIND_DATAW wfd
;
789 BOOL b_FileMask
= fileOnly
&& (NULL
!= StrPBrkW(pName
, L
"*?"));
790 DWORD dwAttr
= INVALID_FILE_ATTRIBUTES
;
791 HANDLE hFind
= FindFirstFileW(pName
, &wfd
);
793 TRACE("%s %d\n", debugstr_w(pName
), fileOnly
);
794 if (INVALID_HANDLE_VALUE
!= hFind
)
798 if (b_FileMask
&& IsAttribDir(wfd
.dwFileAttributes
))
800 dwAttr
= wfd
.dwFileAttributes
;
803 while (FindNextFileW(hFind
, &wfd
));
809 /*************************************************************************
811 * SHNameTranslate HelperFunction for SHFileOperationA
813 * Translates a list of 0 terminated ANSI strings into Unicode. If *wString
814 * is NULL, only the necessary size of the string is determined and returned,
815 * otherwise the ANSI strings are copied into it and the buffer is increased
816 * to point to the location after the final 0 termination char.
818 static DWORD
SHNameTranslate(LPWSTR
* wString
, LPCWSTR
* pWToFrom
, BOOL more
)
820 DWORD size
= 0, aSize
= 0;
821 LPCSTR aString
= (LPCSTR
)*pWToFrom
;
827 size
= lstrlenA(aString
) + 1;
830 } while ((size
!= 1) && more
);
831 /* The two sizes might be different in the case of multibyte chars */
832 size
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)*pWToFrom
, aSize
, *wString
, 0);
833 if (*wString
) /* only in the second loop */
835 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)*pWToFrom
, aSize
, *wString
, size
);
836 *pWToFrom
= *wString
;
842 /*************************************************************************
843 * SHFileOperationA [SHELL32.@]
845 * Function to copy, move, delete and create one or more files with optional
849 * lpFileOp [I/O] pointer to a structure containing all the necessary information
852 * Success: ERROR_SUCCESS.
853 * Failure: ERROR_CANCELLED.
858 int WINAPI
SHFileOperationA(LPSHFILEOPSTRUCTA lpFileOp
)
860 SHFILEOPSTRUCTW nFileOp
= *((LPSHFILEOPSTRUCTW
)lpFileOp
);
863 LPWSTR ForFree
= NULL
, /* we change wString in SHNameTranslate and can't use it for freeing */
864 wString
= NULL
; /* we change this in SHNameTranslate */
867 if (FO_DELETE
== (nFileOp
.wFunc
& FO_MASK
))
868 nFileOp
.pTo
= NULL
; /* we need a NULL or a valid pointer for translation */
869 if (!(nFileOp
.fFlags
& FOF_SIMPLEPROGRESS
))
870 nFileOp
.lpszProgressTitle
= NULL
; /* we need a NULL or a valid pointer for translation */
871 while (1) /* every loop calculate size, second translate also, if we have storage for this */
873 size
= SHNameTranslate(&wString
, &nFileOp
.lpszProgressTitle
, FALSE
); /* no loop */
874 size
+= SHNameTranslate(&wString
, &nFileOp
.pFrom
, TRUE
); /* internal loop */
875 size
+= SHNameTranslate(&wString
, &nFileOp
.pTo
, TRUE
); /* internal loop */
879 retCode
= SHFileOperationW(&nFileOp
);
880 /* Windows 95/98 returns S_OK for this case. */
881 if (retCode
== ERROR_ACCESS_DENIED
&& (GetVersion() & 0x80000000))
884 heap_free(ForFree
); /* we cannot use wString, it was changed */
889 wString
= ForFree
= heap_alloc(size
* sizeof(WCHAR
));
890 if (ForFree
) continue;
891 retCode
= ERROR_OUTOFMEMORY
;
892 nFileOp
.fAnyOperationsAborted
= TRUE
;
897 lpFileOp
->hNameMappings
= nFileOp
.hNameMappings
;
898 lpFileOp
->fAnyOperationsAborted
= nFileOp
.fAnyOperationsAborted
;
902 #define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026
920 BOOL bAnyFromWildcard
;
921 BOOL bAnyDirectories
;
926 static inline void grow_list(FILE_LIST
*list
)
928 FILE_ENTRY
*new = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, list
->feFiles
,
929 list
->num_alloc
* 2 * sizeof(*new) );
931 list
->num_alloc
*= 2;
934 /* adds a file to the FILE_ENTRY struct
936 static void add_file_to_entry(FILE_ENTRY
*feFile
, LPCWSTR szFile
)
938 DWORD dwLen
= lstrlenW(szFile
) + 1;
941 feFile
->szFullPath
= heap_alloc(dwLen
* sizeof(WCHAR
));
942 lstrcpyW(feFile
->szFullPath
, szFile
);
944 ptr
= StrRChrW(szFile
, NULL
, '\\');
947 dwLen
= ptr
- szFile
+ 1;
948 feFile
->szDirectory
= heap_alloc(dwLen
* sizeof(WCHAR
));
949 lstrcpynW(feFile
->szDirectory
, szFile
, dwLen
);
951 dwLen
= lstrlenW(feFile
->szFullPath
) - dwLen
+ 1;
952 feFile
->szFilename
= heap_alloc(dwLen
* sizeof(WCHAR
));
953 lstrcpyW(feFile
->szFilename
, ptr
+ 1); /* skip over backslash */
955 feFile
->bFromWildcard
= FALSE
;
958 static LPWSTR
wildcard_to_file(LPCWSTR szWildCard
, LPCWSTR szFileName
)
962 DWORD dwDirLen
, dwFullLen
;
964 ptr
= StrRChrW(szWildCard
, NULL
, '\\');
965 dwDirLen
= ptr
- szWildCard
+ 1;
967 dwFullLen
= dwDirLen
+ lstrlenW(szFileName
) + 1;
968 szFullPath
= heap_alloc(dwFullLen
* sizeof(WCHAR
));
970 lstrcpynW(szFullPath
, szWildCard
, dwDirLen
+ 1);
971 lstrcatW(szFullPath
, szFileName
);
976 static void parse_wildcard_files(FILE_LIST
*flList
, LPCWSTR szFile
, LPDWORD pdwListIndex
)
978 WIN32_FIND_DATAW wfd
;
979 HANDLE hFile
= FindFirstFileW(szFile
, &wfd
);
984 if (hFile
== INVALID_HANDLE_VALUE
) return;
986 for (res
= TRUE
; res
; res
= FindNextFileW(hFile
, &wfd
))
988 if (IsDotDir(wfd
.cFileName
)) continue;
989 if (*pdwListIndex
>= flList
->num_alloc
) grow_list( flList
);
990 szFullPath
= wildcard_to_file(szFile
, wfd
.cFileName
);
991 file
= &flList
->feFiles
[(*pdwListIndex
)++];
992 add_file_to_entry(file
, szFullPath
);
993 file
->bFromWildcard
= TRUE
;
994 file
->attributes
= wfd
.dwFileAttributes
;
995 if (IsAttribDir(file
->attributes
)) flList
->bAnyDirectories
= TRUE
;
996 heap_free(szFullPath
);
1002 /* takes the null-separated file list and fills out the FILE_LIST */
1003 static HRESULT
parse_file_list(FILE_LIST
*flList
, LPCWSTR szFiles
)
1005 LPCWSTR ptr
= szFiles
;
1006 WCHAR szCurFile
[MAX_PATH
];
1011 return ERROR_INVALID_PARAMETER
;
1013 flList
->bAnyFromWildcard
= FALSE
;
1014 flList
->bAnyDirectories
= FALSE
;
1015 flList
->bAnyDontExist
= FALSE
;
1016 flList
->num_alloc
= 32;
1017 flList
->dwNumFiles
= 0;
1021 return ERROR_ACCESS_DENIED
;
1023 flList
->feFiles
= heap_alloc_zero(flList
->num_alloc
* sizeof(FILE_ENTRY
));
1027 if (i
>= flList
->num_alloc
) grow_list( flList
);
1029 /* change relative to absolute path */
1030 if (PathIsRelativeW(ptr
))
1032 GetCurrentDirectoryW(MAX_PATH
, szCurFile
);
1033 PathCombineW(szCurFile
, szCurFile
, ptr
);
1034 flList
->feFiles
[i
].bFromRelative
= TRUE
;
1038 lstrcpyW(szCurFile
, ptr
);
1039 flList
->feFiles
[i
].bFromRelative
= FALSE
;
1042 for (p
= szCurFile
; *p
; p
++) if (*p
== '/') *p
= '\\';
1044 /* parse wildcard files if they are in the filename */
1045 if (StrPBrkW(szCurFile
, L
"*?"))
1047 parse_wildcard_files(flList
, szCurFile
, &i
);
1048 flList
->bAnyFromWildcard
= TRUE
;
1053 FILE_ENTRY
*file
= &flList
->feFiles
[i
];
1054 add_file_to_entry(file
, szCurFile
);
1055 file
->attributes
= GetFileAttributesW( file
->szFullPath
);
1056 file
->bExists
= (file
->attributes
!= INVALID_FILE_ATTRIBUTES
);
1057 if (!file
->bExists
) flList
->bAnyDontExist
= TRUE
;
1058 if (IsAttribDir(file
->attributes
)) flList
->bAnyDirectories
= TRUE
;
1061 /* advance to the next string */
1062 ptr
+= lstrlenW(ptr
) + 1;
1065 flList
->dwNumFiles
= i
;
1070 /* free the FILE_LIST */
1071 static void destroy_file_list(FILE_LIST
*flList
)
1075 if (!flList
|| !flList
->feFiles
)
1078 for (i
= 0; i
< flList
->dwNumFiles
; i
++)
1080 heap_free(flList
->feFiles
[i
].szDirectory
);
1081 heap_free(flList
->feFiles
[i
].szFilename
);
1082 heap_free(flList
->feFiles
[i
].szFullPath
);
1085 heap_free(flList
->feFiles
);
1088 static void copy_dir_to_dir(FILE_OPERATION
*op
, const FILE_ENTRY
*feFrom
, LPCWSTR szDestPath
)
1090 WCHAR szFrom
[MAX_PATH
], szTo
[MAX_PATH
];
1091 SHFILEOPSTRUCTW fileOp
;
1093 if (IsDotDir(feFrom
->szFilename
))
1096 if (PathFileExistsW(szDestPath
))
1097 PathCombineW(szTo
, szDestPath
, feFrom
->szFilename
);
1099 lstrcpyW(szTo
, szDestPath
);
1101 if (!(op
->req
->fFlags
& FOF_NOCONFIRMATION
) && PathFileExistsW(szTo
)) {
1102 if (!SHELL_ConfirmDialogW(op
->req
->hwnd
, ASK_OVERWRITE_FOLDER
, feFrom
->szFilename
, op
))
1104 /* Vista returns an ERROR_CANCELLED even if user pressed "No" */
1105 if (!op
->bManyItems
)
1106 op
->bCancelled
= TRUE
;
1111 szTo
[lstrlenW(szTo
) + 1] = '\0';
1112 SHNotifyCreateDirectoryW(szTo
, NULL
);
1114 PathCombineW(szFrom
, feFrom
->szFullPath
, L
"*.*");
1115 szFrom
[lstrlenW(szFrom
) + 1] = '\0';
1118 fileOp
.pFrom
= szFrom
;
1120 fileOp
.fFlags
&= ~FOF_MULTIDESTFILES
; /* we know we're copying to one dir */
1122 /* Don't ask the user about overwriting files when he accepted to overwrite the
1123 folder. FIXME: this is not exactly what Windows does - e.g. there would be
1124 an additional confirmation for a nested folder */
1125 fileOp
.fFlags
|= FOF_NOCONFIRMATION
;
1127 SHFileOperationW(&fileOp
);
1130 static BOOL
copy_file_to_file(FILE_OPERATION
*op
, const WCHAR
*szFrom
, const WCHAR
*szTo
)
1132 if (!(op
->req
->fFlags
& FOF_NOCONFIRMATION
) && PathFileExistsW(szTo
))
1134 if (!SHELL_ConfirmDialogW(op
->req
->hwnd
, ASK_OVERWRITE_FILE
, PathFindFileNameW(szTo
), op
))
1138 return SHNotifyCopyFileW(szFrom
, szTo
, FALSE
) == 0;
1141 /* copy a file or directory to another directory */
1142 static void copy_to_dir(FILE_OPERATION
*op
, const FILE_ENTRY
*feFrom
, const FILE_ENTRY
*feTo
)
1144 if (!PathFileExistsW(feTo
->szFullPath
))
1145 SHNotifyCreateDirectoryW(feTo
->szFullPath
, NULL
);
1147 if (IsAttribFile(feFrom
->attributes
))
1149 WCHAR szDestPath
[MAX_PATH
];
1151 PathCombineW(szDestPath
, feTo
->szFullPath
, feFrom
->szFilename
);
1152 copy_file_to_file(op
, feFrom
->szFullPath
, szDestPath
);
1154 else if (!(op
->req
->fFlags
& FOF_FILESONLY
&& feFrom
->bFromWildcard
))
1155 copy_dir_to_dir(op
, feFrom
, feTo
->szFullPath
);
1158 static void create_dest_dirs(LPCWSTR szDestDir
)
1160 WCHAR dir
[MAX_PATH
];
1161 LPCWSTR ptr
= StrChrW(szDestDir
, '\\');
1163 /* make sure all directories up to last one are created */
1164 while (ptr
&& (ptr
= StrChrW(ptr
+ 1, '\\')))
1166 lstrcpynW(dir
, szDestDir
, ptr
- szDestDir
+ 1);
1168 if (!PathFileExistsW(dir
))
1169 SHNotifyCreateDirectoryW(dir
, NULL
);
1172 /* create last directory */
1173 if (!PathFileExistsW(szDestDir
))
1174 SHNotifyCreateDirectoryW(szDestDir
, NULL
);
1177 /* the FO_COPY operation */
1178 static int copy_files(FILE_OPERATION
*op
, const FILE_LIST
*flFrom
, FILE_LIST
*flTo
)
1181 const FILE_ENTRY
*entryToCopy
;
1182 const FILE_ENTRY
*fileDest
= &flTo
->feFiles
[0];
1184 if (flFrom
->bAnyDontExist
)
1185 return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND
;
1187 if (flTo
->dwNumFiles
== 0)
1189 /* If the destination is empty, SHFileOperation should use the current directory */
1190 WCHAR curdir
[MAX_PATH
+1];
1192 GetCurrentDirectoryW(MAX_PATH
, curdir
);
1193 curdir
[lstrlenW(curdir
)+1] = 0;
1195 destroy_file_list(flTo
);
1196 ZeroMemory(flTo
, sizeof(FILE_LIST
));
1197 parse_file_list(flTo
, curdir
);
1198 fileDest
= &flTo
->feFiles
[0];
1201 if (op
->req
->fFlags
& FOF_MULTIDESTFILES
&& flTo
->dwNumFiles
> 1)
1203 if (flFrom
->bAnyFromWildcard
)
1204 return ERROR_CANCELLED
;
1206 if (flFrom
->dwNumFiles
!= flTo
->dwNumFiles
)
1208 if (flFrom
->dwNumFiles
!= 1 && !IsAttribDir(fileDest
->attributes
))
1209 return ERROR_CANCELLED
;
1211 /* Free all but the first entry. */
1212 for (i
= 1; i
< flTo
->dwNumFiles
; i
++)
1214 heap_free(flTo
->feFiles
[i
].szDirectory
);
1215 heap_free(flTo
->feFiles
[i
].szFilename
);
1216 heap_free(flTo
->feFiles
[i
].szFullPath
);
1219 flTo
->dwNumFiles
= 1;
1221 else if (IsAttribDir(fileDest
->attributes
))
1223 for (i
= 1; i
< flTo
->dwNumFiles
; i
++)
1224 if (!IsAttribDir(flTo
->feFiles
[i
].attributes
) ||
1225 !IsAttribDir(flFrom
->feFiles
[i
].attributes
))
1227 return ERROR_CANCELLED
;
1231 else if (flFrom
->dwNumFiles
!= 1)
1233 if (flTo
->dwNumFiles
!= 1 && !IsAttribDir(fileDest
->attributes
))
1234 return ERROR_CANCELLED
;
1236 if (PathFileExistsW(fileDest
->szFullPath
) &&
1237 IsAttribFile(fileDest
->attributes
))
1239 return ERROR_CANCELLED
;
1242 if (flTo
->dwNumFiles
== 1 && fileDest
->bFromRelative
&&
1243 !PathFileExistsW(fileDest
->szFullPath
))
1245 return ERROR_CANCELLED
;
1249 for (i
= 0; i
< flFrom
->dwNumFiles
; i
++)
1251 entryToCopy
= &flFrom
->feFiles
[i
];
1253 if ((op
->req
->fFlags
& FOF_MULTIDESTFILES
) &&
1254 flTo
->dwNumFiles
> 1)
1256 fileDest
= &flTo
->feFiles
[i
];
1259 if (IsAttribDir(entryToCopy
->attributes
) &&
1260 !lstrcmpiW(entryToCopy
->szFullPath
, fileDest
->szDirectory
))
1262 return ERROR_SUCCESS
;
1265 create_dest_dirs(fileDest
->szDirectory
);
1267 if (!lstrcmpiW(entryToCopy
->szFullPath
, fileDest
->szFullPath
))
1269 if (IsAttribFile(entryToCopy
->attributes
))
1270 return ERROR_NO_MORE_SEARCH_HANDLES
;
1272 return ERROR_SUCCESS
;
1275 if ((flFrom
->dwNumFiles
> 1 && flTo
->dwNumFiles
== 1) ||
1276 IsAttribDir(fileDest
->attributes
))
1278 copy_to_dir(op
, entryToCopy
, fileDest
);
1280 else if (IsAttribDir(entryToCopy
->attributes
))
1282 copy_dir_to_dir(op
, entryToCopy
, fileDest
->szFullPath
);
1286 if (!copy_file_to_file(op
, entryToCopy
->szFullPath
, fileDest
->szFullPath
))
1288 op
->req
->fAnyOperationsAborted
= TRUE
;
1289 return ERROR_CANCELLED
;
1293 /* Vista return code. XP would return e.g. ERROR_FILE_NOT_FOUND, ERROR_ALREADY_EXISTS */
1295 return ERROR_CANCELLED
;
1298 /* Vista return code. On XP if the used pressed "No" for the last item,
1299 * ERROR_ARENA_TRASHED would be returned */
1300 return ERROR_SUCCESS
;
1303 static BOOL
confirm_delete_list(HWND hWnd
, DWORD fFlags
, BOOL fTrash
, const FILE_LIST
*flFrom
)
1305 if (flFrom
->dwNumFiles
> 1)
1309 swprintf(tmp
, ARRAY_SIZE(tmp
), L
"%d", flFrom
->dwNumFiles
);
1310 return SHELL_ConfirmDialogW(hWnd
, (fTrash
?ASK_TRASH_MULTIPLE_ITEM
:ASK_DELETE_MULTIPLE_ITEM
), tmp
, NULL
);
1314 const FILE_ENTRY
*fileEntry
= &flFrom
->feFiles
[0];
1316 if (IsAttribFile(fileEntry
->attributes
))
1317 return SHELL_ConfirmDialogW(hWnd
, (fTrash
?ASK_TRASH_FILE
:ASK_DELETE_FILE
), fileEntry
->szFullPath
, NULL
);
1318 else if (!(fFlags
& FOF_FILESONLY
&& fileEntry
->bFromWildcard
))
1319 return SHELL_ConfirmDialogW(hWnd
, (fTrash
?ASK_TRASH_FOLDER
:ASK_DELETE_FOLDER
), fileEntry
->szFullPath
, NULL
);
1324 /* the FO_DELETE operation */
1325 static int delete_files(LPSHFILEOPSTRUCTW lpFileOp
, const FILE_LIST
*flFrom
)
1327 const FILE_ENTRY
*fileEntry
;
1332 if (!flFrom
->dwNumFiles
)
1333 return ERROR_SUCCESS
;
1335 /* Windows also checks only the first item */
1336 bTrash
= (lpFileOp
->fFlags
& FOF_ALLOWUNDO
) && is_trash_available();
1338 if (!(lpFileOp
->fFlags
& FOF_NOCONFIRMATION
) || (!bTrash
&& lpFileOp
->fFlags
& FOF_WANTNUKEWARNING
))
1339 if (!confirm_delete_list(lpFileOp
->hwnd
, lpFileOp
->fFlags
, bTrash
, flFrom
))
1341 lpFileOp
->fAnyOperationsAborted
= TRUE
;
1345 for (i
= 0; i
< flFrom
->dwNumFiles
; i
++)
1347 fileEntry
= &flFrom
->feFiles
[i
];
1349 if (!IsAttribFile(fileEntry
->attributes
) &&
1350 (lpFileOp
->fFlags
& FOF_FILESONLY
&& fileEntry
->bFromWildcard
))
1356 if (trash_file(fileEntry
->szFullPath
))
1359 /* Note: Windows silently deletes the file in such a situation, we show a dialog */
1360 if (!(lpFileOp
->fFlags
& FOF_NOCONFIRMATION
) || (lpFileOp
->fFlags
& FOF_WANTNUKEWARNING
))
1361 bDelete
= SHELL_ConfirmDialogW(lpFileOp
->hwnd
, ASK_CANT_TRASH_ITEM
, fileEntry
->szFullPath
, NULL
);
1367 lpFileOp
->fAnyOperationsAborted
= TRUE
;
1372 /* delete the file or directory */
1373 if (IsAttribFile(fileEntry
->attributes
))
1374 ret
= DeleteFileW(fileEntry
->szFullPath
) ?
1375 ERROR_SUCCESS
: GetLastError();
1377 ret
= SHELL_DeleteDirectoryW(lpFileOp
->hwnd
, fileEntry
->szFullPath
, FALSE
);
1383 return ERROR_SUCCESS
;
1386 /* moves a file or directory to another directory */
1387 static void move_to_dir(LPSHFILEOPSTRUCTW lpFileOp
, const FILE_ENTRY
*feFrom
, const FILE_ENTRY
*feTo
)
1389 WCHAR szDestPath
[MAX_PATH
];
1391 PathCombineW(szDestPath
, feTo
->szFullPath
, feFrom
->szFilename
);
1392 SHNotifyMoveFileW(feFrom
->szFullPath
, szDestPath
);
1395 /* the FO_MOVE operation */
1396 static int move_files(LPSHFILEOPSTRUCTW lpFileOp
, const FILE_LIST
*flFrom
, const FILE_LIST
*flTo
)
1400 const FILE_ENTRY
*entryToMove
;
1401 const FILE_ENTRY
*fileDest
;
1403 if (!flFrom
->dwNumFiles
)
1404 return ERROR_SUCCESS
;
1406 if (!flTo
->dwNumFiles
)
1407 return ERROR_FILE_NOT_FOUND
;
1409 if (!(lpFileOp
->fFlags
& FOF_MULTIDESTFILES
) &&
1410 flTo
->dwNumFiles
> 1 && flFrom
->dwNumFiles
> 1)
1412 return ERROR_CANCELLED
;
1415 if (!(lpFileOp
->fFlags
& FOF_MULTIDESTFILES
) &&
1416 !flFrom
->bAnyDirectories
&&
1417 flFrom
->dwNumFiles
> flTo
->dwNumFiles
)
1419 return ERROR_CANCELLED
;
1422 if (!PathFileExistsW(flTo
->feFiles
[0].szDirectory
))
1423 return ERROR_CANCELLED
;
1425 if (lpFileOp
->fFlags
& FOF_MULTIDESTFILES
)
1426 mismatched
= flFrom
->dwNumFiles
- flTo
->dwNumFiles
;
1428 fileDest
= &flTo
->feFiles
[0];
1429 for (i
= 0; i
< flFrom
->dwNumFiles
; i
++)
1431 entryToMove
= &flFrom
->feFiles
[i
];
1433 if (!PathFileExistsW(fileDest
->szDirectory
))
1434 return ERROR_CANCELLED
;
1436 if (lpFileOp
->fFlags
& FOF_MULTIDESTFILES
)
1438 if (i
>= flTo
->dwNumFiles
)
1440 fileDest
= &flTo
->feFiles
[i
];
1441 if (mismatched
&& !fileDest
->bExists
)
1443 create_dest_dirs(flTo
->feFiles
[i
].szFullPath
);
1444 flTo
->feFiles
[i
].bExists
= TRUE
;
1445 flTo
->feFiles
[i
].attributes
= FILE_ATTRIBUTE_DIRECTORY
;
1449 if (fileDest
->bExists
&& IsAttribDir(fileDest
->attributes
))
1450 move_to_dir(lpFileOp
, entryToMove
, fileDest
);
1452 SHNotifyMoveFileW(entryToMove
->szFullPath
, fileDest
->szFullPath
);
1457 if (flFrom
->bAnyDirectories
)
1458 return DE_DESTSAMETREE
;
1463 return ERROR_SUCCESS
;
1466 /* the FO_RENAME files */
1467 static int rename_files(LPSHFILEOPSTRUCTW lpFileOp
, const FILE_LIST
*flFrom
, const FILE_LIST
*flTo
)
1469 const FILE_ENTRY
*feFrom
;
1470 const FILE_ENTRY
*feTo
;
1472 if (flFrom
->dwNumFiles
!= 1)
1473 return ERROR_GEN_FAILURE
;
1475 if (flTo
->dwNumFiles
!= 1)
1476 return ERROR_CANCELLED
;
1478 feFrom
= &flFrom
->feFiles
[0];
1479 feTo
= &flTo
->feFiles
[0];
1481 /* fail if destination doesn't exist */
1482 if (!feFrom
->bExists
)
1483 return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND
;
1485 /* fail if destination already exists */
1487 return ERROR_ALREADY_EXISTS
;
1489 return SHNotifyMoveFileW(feFrom
->szFullPath
, feTo
->szFullPath
);
1492 /* alert the user if an unsupported flag is used */
1493 static void check_flags(FILEOP_FLAGS fFlags
)
1495 WORD wUnsupportedFlags
= FOF_NO_CONNECTED_ELEMENTS
|
1496 FOF_NOCOPYSECURITYATTRIBS
| FOF_NORECURSEREPARSE
|
1497 FOF_RENAMEONCOLLISION
| FOF_WANTMAPPINGHANDLE
;
1499 if (fFlags
& wUnsupportedFlags
)
1500 FIXME("Unsupported flags: %04x\n", fFlags
);
1503 /*************************************************************************
1504 * SHFileOperationW [SHELL32.@]
1506 * See SHFileOperationA
1508 int WINAPI
SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp
)
1511 FILE_LIST flFrom
, flTo
;
1515 return ERROR_INVALID_PARAMETER
;
1517 check_flags(lpFileOp
->fFlags
);
1519 ZeroMemory(&flFrom
, sizeof(FILE_LIST
));
1520 ZeroMemory(&flTo
, sizeof(FILE_LIST
));
1522 if ((ret
= parse_file_list(&flFrom
, lpFileOp
->pFrom
)))
1525 if (lpFileOp
->wFunc
!= FO_DELETE
)
1526 parse_file_list(&flTo
, lpFileOp
->pTo
);
1528 ZeroMemory(&op
, sizeof(op
));
1530 op
.bManyItems
= (flFrom
.dwNumFiles
> 1);
1531 lpFileOp
->fAnyOperationsAborted
= FALSE
;
1533 switch (lpFileOp
->wFunc
)
1536 ret
= copy_files(&op
, &flFrom
, &flTo
);
1539 ret
= delete_files(lpFileOp
, &flFrom
);
1542 ret
= move_files(lpFileOp
, &flFrom
, &flTo
);
1545 ret
= rename_files(lpFileOp
, &flFrom
, &flTo
);
1548 ret
= ERROR_INVALID_PARAMETER
;
1552 destroy_file_list(&flFrom
);
1554 if (lpFileOp
->wFunc
!= FO_DELETE
)
1555 destroy_file_list(&flTo
);
1557 if (ret
== ERROR_CANCELLED
)
1558 lpFileOp
->fAnyOperationsAborted
= TRUE
;
1560 SetLastError(ERROR_SUCCESS
);
1564 #define SHDSA_GetItemCount(hdsa) (*(int*)(hdsa))
1566 /*************************************************************************
1567 * SHFreeNameMappings [shell32.246]
1569 * Free the mapping handle returned by SHFileOperation if FOF_WANTSMAPPINGHANDLE
1573 * hNameMapping [I] handle to the name mappings used during renaming of files
1578 void WINAPI
SHFreeNameMappings(HANDLE hNameMapping
)
1582 int i
= SHDSA_GetItemCount((HDSA
)hNameMapping
) - 1;
1586 LPSHNAMEMAPPINGW lp
= DSA_GetItemPtr(hNameMapping
, i
);
1588 SHFree(lp
->pszOldPath
);
1589 SHFree(lp
->pszNewPath
);
1591 DSA_Destroy(hNameMapping
);
1595 /*************************************************************************
1596 * SheGetDirA [SHELL32.@]
1598 * drive = 0: returns the current directory path
1599 * drive > 0: returns the current directory path of the specified drive
1600 * drive=1 -> A: drive=2 -> B: ...
1601 * returns 0 if successful
1603 DWORD WINAPI
SheGetDirA(DWORD drive
, LPSTR buffer
)
1605 WCHAR org_path
[MAX_PATH
];
1609 /* change current directory to the specified drive */
1611 strcpy(drv_path
, "A:");
1612 drv_path
[0] += (char)drive
-1;
1614 GetCurrentDirectoryW(MAX_PATH
, org_path
);
1616 SetCurrentDirectoryA(drv_path
);
1619 /* query current directory path of the specified drive */
1620 ret
= GetCurrentDirectoryA(MAX_PATH
, buffer
);
1622 /* back to the original drive */
1624 SetCurrentDirectoryW(org_path
);
1627 return GetLastError();
1632 /*************************************************************************
1633 * SheGetDirW [SHELL32.@]
1635 * drive = 0: returns the current directory path
1636 * drive > 0: returns the current directory path of the specified drive
1637 * drive=1 -> A: drive=2 -> B: ...
1638 * returns 0 if successful
1640 DWORD WINAPI
SheGetDirW(DWORD drive
, LPWSTR buffer
)
1642 WCHAR org_path
[MAX_PATH
];
1646 /* change current directory to the specified drive */
1648 strcpy(drv_path
, "A:");
1649 drv_path
[0] += (char)drive
-1;
1651 GetCurrentDirectoryW(MAX_PATH
, org_path
);
1653 SetCurrentDirectoryA(drv_path
);
1656 /* query current directory path of the specified drive */
1657 ret
= GetCurrentDirectoryW(MAX_PATH
, buffer
);
1659 /* back to the original drive */
1661 SetCurrentDirectoryW(org_path
);
1664 return GetLastError();
1669 /*************************************************************************
1670 * SheChangeDirA [SHELL32.@]
1672 * changes the current directory to the specified path
1673 * and returns 0 if successful
1675 DWORD WINAPI
SheChangeDirA(LPSTR path
)
1677 if (SetCurrentDirectoryA(path
))
1680 return GetLastError();
1683 /*************************************************************************
1684 * SheChangeDirW [SHELL32.@]
1686 * changes the current directory to the specified path
1687 * and returns 0 if successful
1689 DWORD WINAPI
SheChangeDirW(LPWSTR path
)
1691 if (SetCurrentDirectoryW(path
))
1694 return GetLastError();
1697 /*************************************************************************
1698 * IsNetDrive [SHELL32.66]
1700 int WINAPI
IsNetDrive(int drive
)
1703 strcpy(root
, "A:\\");
1704 root
[0] += (char)drive
;
1705 return (GetDriveTypeA(root
) == DRIVE_REMOTE
);
1709 /*************************************************************************
1710 * RealDriveType [SHELL32.524]
1712 int WINAPI
RealDriveType(int drive
, BOOL bQueryNet
)
1714 char root
[] = "A:\\";
1715 root
[0] += (char)drive
;
1716 return GetDriveTypeA(root
);
1719 /***********************************************************************
1720 * SHPathPrepareForWriteA (SHELL32.@)
1722 HRESULT WINAPI
SHPathPrepareForWriteA(HWND hwnd
, IUnknown
*modless
, LPCSTR path
, DWORD flags
)
1724 WCHAR wpath
[MAX_PATH
];
1725 MultiByteToWideChar( CP_ACP
, 0, path
, -1, wpath
, MAX_PATH
);
1726 return SHPathPrepareForWriteW(hwnd
, modless
, wpath
, flags
);
1729 /***********************************************************************
1730 * SHPathPrepareForWriteW (SHELL32.@)
1732 HRESULT WINAPI
SHPathPrepareForWriteW(HWND hwnd
, IUnknown
*modless
, LPCWSTR path
, DWORD flags
)
1739 WCHAR
* temppath
=NULL
;
1741 TRACE("%p %p %s 0x%08x\n", hwnd
, modless
, debugstr_w(path
), flags
);
1743 if (flags
& ~(SHPPFW_DIRCREATE
|SHPPFW_ASKDIRCREATE
|SHPPFW_IGNOREFILENAME
))
1744 FIXME("unimplemented flags 0x%08x\n", flags
);
1746 /* cut off filename if necessary */
1747 if (flags
& SHPPFW_IGNOREFILENAME
)
1749 last_slash
= StrRChrW(path
, NULL
, '\\');
1750 if (last_slash
== NULL
)
1753 len
= last_slash
- path
+ 1;
1754 temppath
= heap_alloc(len
* sizeof(WCHAR
));
1756 return E_OUTOFMEMORY
;
1757 StrCpyNW(temppath
, path
, len
);
1758 realpath
= temppath
;
1765 /* try to create the directory if asked to */
1766 if (flags
& (SHPPFW_DIRCREATE
|SHPPFW_ASKDIRCREATE
))
1768 if (flags
& SHPPFW_ASKDIRCREATE
)
1769 FIXME("treating SHPPFW_ASKDIRCREATE as SHPPFW_DIRCREATE\n");
1771 SHCreateDirectoryExW(0, realpath
, NULL
);
1774 /* check if we can access the directory */
1775 res
= GetFileAttributesW(realpath
);
1777 heap_free(temppath
);
1779 if (res
== INVALID_FILE_ATTRIBUTES
)
1781 err
= GetLastError();
1782 if (err
== ERROR_FILE_NOT_FOUND
)
1783 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND
);
1784 return HRESULT_FROM_WIN32(err
);
1786 else if (res
& FILE_ATTRIBUTE_DIRECTORY
)
1789 return HRESULT_FROM_WIN32(ERROR_DIRECTORY
);
1792 /*************************************************************************
1793 * SHMultiFileProperties [SHELL32.@]
1796 HRESULT WINAPI
SHMultiFileProperties(IDataObject
*pdtobj
, DWORD flags
)
1798 FIXME("stub: %p %u\n", pdtobj
, flags
);
1802 struct file_operation
1804 IFileOperation IFileOperation_iface
;
1808 static inline struct file_operation
*impl_from_IFileOperation(IFileOperation
*iface
)
1810 return CONTAINING_RECORD(iface
, struct file_operation
, IFileOperation_iface
);
1813 static HRESULT WINAPI
file_operation_QueryInterface(IFileOperation
*iface
, REFIID riid
, void **out
)
1815 struct file_operation
*operation
= impl_from_IFileOperation(iface
);
1817 TRACE("(%p, %s, %p).\n", iface
, debugstr_guid(riid
), out
);
1819 if (IsEqualIID(&IID_IFileOperation
, riid
) ||
1820 IsEqualIID(&IID_IUnknown
, riid
))
1821 *out
= &operation
->IFileOperation_iface
;
1824 FIXME("not implemented for %s.\n", debugstr_guid(riid
));
1826 return E_NOINTERFACE
;
1829 IUnknown_AddRef((IUnknown
*)*out
);
1833 static ULONG WINAPI
file_operation_AddRef(IFileOperation
*iface
)
1835 struct file_operation
*operation
= impl_from_IFileOperation(iface
);
1836 ULONG ref
= InterlockedIncrement(&operation
->ref
);
1838 TRACE("(%p): ref=%u.\n", iface
, ref
);
1843 static ULONG WINAPI
file_operation_Release(IFileOperation
*iface
)
1845 struct file_operation
*operation
= impl_from_IFileOperation(iface
);
1846 ULONG ref
= InterlockedDecrement(&operation
->ref
);
1848 TRACE("(%p): ref=%u.\n", iface
, ref
);
1852 HeapFree(GetProcessHeap(), 0, operation
);
1858 static HRESULT WINAPI
file_operation_Advise(IFileOperation
*iface
, IFileOperationProgressSink
*sink
, DWORD
*cookie
)
1860 FIXME("(%p, %p, %p): stub.\n", iface
, sink
, cookie
);
1865 static HRESULT WINAPI
file_operation_Unadvise(IFileOperation
*iface
, DWORD cookie
)
1867 FIXME("(%p, %x): stub.\n", iface
, cookie
);
1872 static HRESULT WINAPI
file_operation_SetOperationFlags(IFileOperation
*iface
, DWORD flags
)
1874 FIXME("(%p, %x): stub.\n", iface
, flags
);
1879 static HRESULT WINAPI
file_operation_SetProgressMessage(IFileOperation
*iface
, LPCWSTR message
)
1881 FIXME("(%p, %s): stub.\n", iface
, debugstr_w(message
));
1886 static HRESULT WINAPI
file_operation_SetProgressDialog(IFileOperation
*iface
, IOperationsProgressDialog
*dialog
)
1888 FIXME("(%p, %p): stub.\n", iface
, dialog
);
1893 static HRESULT WINAPI
file_operation_SetProperties(IFileOperation
*iface
, IPropertyChangeArray
*array
)
1895 FIXME("(%p, %p): stub.\n", iface
, array
);
1900 static HRESULT WINAPI
file_operation_SetOwnerWindow(IFileOperation
*iface
, HWND owner
)
1902 FIXME("(%p, %p): stub.\n", iface
, owner
);
1907 static HRESULT WINAPI
file_operation_ApplyPropertiesToItem(IFileOperation
*iface
, IShellItem
*item
)
1909 FIXME("(%p, %p): stub.\n", iface
, item
);
1914 static HRESULT WINAPI
file_operation_ApplyPropertiesToItems(IFileOperation
*iface
, IUnknown
*items
)
1916 FIXME("(%p, %p): stub.\n", iface
, items
);
1921 static HRESULT WINAPI
file_operation_RenameItem(IFileOperation
*iface
, IShellItem
*item
, LPCWSTR name
,
1922 IFileOperationProgressSink
*sink
)
1924 FIXME("(%p, %p, %s, %p): stub.\n", iface
, item
, debugstr_w(name
), sink
);
1929 static HRESULT WINAPI
file_operation_RenameItems(IFileOperation
*iface
, IUnknown
*items
, LPCWSTR name
)
1931 FIXME("(%p, %p, %s): stub.\n", iface
, items
, debugstr_w(name
));
1936 static HRESULT WINAPI
file_operation_MoveItem(IFileOperation
*iface
, IShellItem
*item
, IShellItem
*folder
,
1937 LPCWSTR name
, IFileOperationProgressSink
*sink
)
1939 FIXME("(%p, %p, %p, %s, %p): stub.\n", iface
, item
, folder
, debugstr_w(name
), sink
);
1944 static HRESULT WINAPI
file_operation_MoveItems(IFileOperation
*iface
, IUnknown
*items
, IShellItem
*folder
)
1946 FIXME("(%p, %p, %p): stub.\n", iface
, items
, folder
);
1951 static HRESULT WINAPI
file_operation_CopyItem(IFileOperation
*iface
, IShellItem
*item
, IShellItem
*folder
,
1952 LPCWSTR name
, IFileOperationProgressSink
*sink
)
1954 FIXME("(%p, %p, %p, %s, %p): stub.\n", iface
, item
, folder
, debugstr_w(name
), sink
);
1959 static HRESULT WINAPI
file_operation_CopyItems(IFileOperation
*iface
, IUnknown
*items
, IShellItem
*folder
)
1961 FIXME("(%p, %p, %p): stub.\n", iface
, items
, folder
);
1966 static HRESULT WINAPI
file_operation_DeleteItem(IFileOperation
*iface
, IShellItem
*item
,
1967 IFileOperationProgressSink
*sink
)
1969 FIXME("(%p, %p, %p): stub.\n", iface
, item
, sink
);
1974 static HRESULT WINAPI
file_operation_DeleteItems(IFileOperation
*iface
, IUnknown
*items
)
1976 FIXME("(%p, %p): stub.\n", iface
, items
);
1981 static HRESULT WINAPI
file_operation_NewItem(IFileOperation
*iface
, IShellItem
*folder
, DWORD attributes
,
1982 LPCWSTR name
, LPCWSTR
template, IFileOperationProgressSink
*sink
)
1984 FIXME("(%p, %p, %x, %s, %s, %p): stub.\n", iface
, folder
, attributes
,
1985 debugstr_w(name
), debugstr_w(template), sink
);
1990 static HRESULT WINAPI
file_operation_PerformOperations(IFileOperation
*iface
)
1992 FIXME("(%p): stub.\n", iface
);
1997 static HRESULT WINAPI
file_operation_GetAnyOperationsAborted(IFileOperation
*iface
, BOOL
*aborted
)
1999 FIXME("(%p, %p): stub.\n", iface
, aborted
);
2004 static const IFileOperationVtbl file_operation_vtbl
=
2006 file_operation_QueryInterface
,
2007 file_operation_AddRef
,
2008 file_operation_Release
,
2009 file_operation_Advise
,
2010 file_operation_Unadvise
,
2011 file_operation_SetOperationFlags
,
2012 file_operation_SetProgressMessage
,
2013 file_operation_SetProgressDialog
,
2014 file_operation_SetProperties
,
2015 file_operation_SetOwnerWindow
,
2016 file_operation_ApplyPropertiesToItem
,
2017 file_operation_ApplyPropertiesToItems
,
2018 file_operation_RenameItem
,
2019 file_operation_RenameItems
,
2020 file_operation_MoveItem
,
2021 file_operation_MoveItems
,
2022 file_operation_CopyItem
,
2023 file_operation_CopyItems
,
2024 file_operation_DeleteItem
,
2025 file_operation_DeleteItems
,
2026 file_operation_NewItem
,
2027 file_operation_PerformOperations
,
2028 file_operation_GetAnyOperationsAborted
2031 HRESULT WINAPI
IFileOperation_Constructor(IUnknown
*outer
, REFIID riid
, void **out
)
2033 struct file_operation
*object
;
2036 object
= heap_alloc_zero(sizeof(*object
));
2038 return E_OUTOFMEMORY
;
2040 object
->IFileOperation_iface
.lpVtbl
= &file_operation_vtbl
;
2043 hr
= IFileOperation_QueryInterface(&object
->IFileOperation_iface
, riid
, out
);
2044 IFileOperation_Release(&object
->IFileOperation_iface
);