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
= malloc(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 free(ForFree
); /* we cannot use wString, it was changed */
889 wString
= ForFree
= malloc(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 = _recalloc(list
->feFiles
, list
->num_alloc
* 2, sizeof(*new));
930 list
->num_alloc
*= 2;
933 /* adds a file to the FILE_ENTRY struct
935 static void add_file_to_entry(FILE_ENTRY
*feFile
, LPCWSTR szFile
)
937 DWORD dwLen
= lstrlenW(szFile
) + 1;
940 feFile
->szFullPath
= malloc(dwLen
* sizeof(WCHAR
));
941 lstrcpyW(feFile
->szFullPath
, szFile
);
943 ptr
= StrRChrW(szFile
, NULL
, '\\');
946 dwLen
= ptr
- szFile
+ 1;
947 feFile
->szDirectory
= malloc(dwLen
* sizeof(WCHAR
));
948 lstrcpynW(feFile
->szDirectory
, szFile
, dwLen
);
950 dwLen
= lstrlenW(feFile
->szFullPath
) - dwLen
+ 1;
951 feFile
->szFilename
= malloc(dwLen
* sizeof(WCHAR
));
952 lstrcpyW(feFile
->szFilename
, ptr
+ 1); /* skip over backslash */
954 feFile
->bFromWildcard
= FALSE
;
957 static LPWSTR
wildcard_to_file(LPCWSTR szWildCard
, LPCWSTR szFileName
)
961 DWORD dwDirLen
, dwFullLen
;
963 ptr
= StrRChrW(szWildCard
, NULL
, '\\');
964 dwDirLen
= ptr
- szWildCard
+ 1;
966 dwFullLen
= dwDirLen
+ lstrlenW(szFileName
) + 1;
967 szFullPath
= malloc(dwFullLen
* sizeof(WCHAR
));
969 lstrcpynW(szFullPath
, szWildCard
, dwDirLen
+ 1);
970 lstrcatW(szFullPath
, szFileName
);
975 static void parse_wildcard_files(FILE_LIST
*flList
, LPCWSTR szFile
, LPDWORD pdwListIndex
)
977 WIN32_FIND_DATAW wfd
;
978 HANDLE hFile
= FindFirstFileW(szFile
, &wfd
);
983 if (hFile
== INVALID_HANDLE_VALUE
) return;
985 for (res
= TRUE
; res
; res
= FindNextFileW(hFile
, &wfd
))
987 if (IsDotDir(wfd
.cFileName
)) continue;
988 if (*pdwListIndex
>= flList
->num_alloc
) grow_list( flList
);
989 szFullPath
= wildcard_to_file(szFile
, wfd
.cFileName
);
990 file
= &flList
->feFiles
[(*pdwListIndex
)++];
991 add_file_to_entry(file
, szFullPath
);
992 file
->bFromWildcard
= TRUE
;
993 file
->attributes
= wfd
.dwFileAttributes
;
994 if (IsAttribDir(file
->attributes
)) flList
->bAnyDirectories
= TRUE
;
1001 /* takes the null-separated file list and fills out the FILE_LIST */
1002 static HRESULT
parse_file_list(FILE_LIST
*flList
, LPCWSTR szFiles
)
1004 LPCWSTR ptr
= szFiles
;
1005 WCHAR szCurFile
[MAX_PATH
];
1010 return ERROR_INVALID_PARAMETER
;
1012 flList
->bAnyFromWildcard
= FALSE
;
1013 flList
->bAnyDirectories
= FALSE
;
1014 flList
->bAnyDontExist
= FALSE
;
1015 flList
->num_alloc
= 32;
1016 flList
->dwNumFiles
= 0;
1020 return ERROR_ACCESS_DENIED
;
1022 flList
->feFiles
= calloc(flList
->num_alloc
, sizeof(FILE_ENTRY
));
1026 if (i
>= flList
->num_alloc
) grow_list( flList
);
1028 /* change relative to absolute path */
1029 if (PathIsRelativeW(ptr
))
1031 GetCurrentDirectoryW(MAX_PATH
, szCurFile
);
1032 PathCombineW(szCurFile
, szCurFile
, ptr
);
1033 flList
->feFiles
[i
].bFromRelative
= TRUE
;
1037 lstrcpyW(szCurFile
, ptr
);
1038 flList
->feFiles
[i
].bFromRelative
= FALSE
;
1041 for (p
= szCurFile
; *p
; p
++) if (*p
== '/') *p
= '\\';
1043 /* parse wildcard files if they are in the filename */
1044 if (StrPBrkW(szCurFile
, L
"*?"))
1046 parse_wildcard_files(flList
, szCurFile
, &i
);
1047 flList
->bAnyFromWildcard
= TRUE
;
1052 FILE_ENTRY
*file
= &flList
->feFiles
[i
];
1053 add_file_to_entry(file
, szCurFile
);
1054 file
->attributes
= GetFileAttributesW( file
->szFullPath
);
1055 file
->bExists
= (file
->attributes
!= INVALID_FILE_ATTRIBUTES
);
1056 if (!file
->bExists
) flList
->bAnyDontExist
= TRUE
;
1057 if (IsAttribDir(file
->attributes
)) flList
->bAnyDirectories
= TRUE
;
1060 /* advance to the next string */
1061 ptr
+= lstrlenW(ptr
) + 1;
1064 flList
->dwNumFiles
= i
;
1069 /* free the FILE_LIST */
1070 static void destroy_file_list(FILE_LIST
*flList
)
1074 if (!flList
|| !flList
->feFiles
)
1077 for (i
= 0; i
< flList
->dwNumFiles
; i
++)
1079 free(flList
->feFiles
[i
].szDirectory
);
1080 free(flList
->feFiles
[i
].szFilename
);
1081 free(flList
->feFiles
[i
].szFullPath
);
1084 free(flList
->feFiles
);
1087 static void copy_dir_to_dir(FILE_OPERATION
*op
, const FILE_ENTRY
*feFrom
, LPCWSTR szDestPath
)
1089 WCHAR szFrom
[MAX_PATH
], szTo
[MAX_PATH
];
1090 SHFILEOPSTRUCTW fileOp
;
1092 if (IsDotDir(feFrom
->szFilename
))
1095 if (PathFileExistsW(szDestPath
))
1096 PathCombineW(szTo
, szDestPath
, feFrom
->szFilename
);
1098 lstrcpyW(szTo
, szDestPath
);
1100 if (!(op
->req
->fFlags
& FOF_NOCONFIRMATION
) && PathFileExistsW(szTo
)) {
1101 if (!SHELL_ConfirmDialogW(op
->req
->hwnd
, ASK_OVERWRITE_FOLDER
, feFrom
->szFilename
, op
))
1103 /* Vista returns an ERROR_CANCELLED even if user pressed "No" */
1104 if (!op
->bManyItems
)
1105 op
->bCancelled
= TRUE
;
1110 szTo
[lstrlenW(szTo
) + 1] = '\0';
1111 SHNotifyCreateDirectoryW(szTo
, NULL
);
1113 PathCombineW(szFrom
, feFrom
->szFullPath
, L
"*.*");
1114 szFrom
[lstrlenW(szFrom
) + 1] = '\0';
1117 fileOp
.pFrom
= szFrom
;
1119 fileOp
.fFlags
&= ~FOF_MULTIDESTFILES
; /* we know we're copying to one dir */
1121 /* Don't ask the user about overwriting files when he accepted to overwrite the
1122 folder. FIXME: this is not exactly what Windows does - e.g. there would be
1123 an additional confirmation for a nested folder */
1124 fileOp
.fFlags
|= FOF_NOCONFIRMATION
;
1126 SHFileOperationW(&fileOp
);
1129 static BOOL
copy_file_to_file(FILE_OPERATION
*op
, const WCHAR
*szFrom
, const WCHAR
*szTo
)
1131 if (!(op
->req
->fFlags
& FOF_NOCONFIRMATION
) && PathFileExistsW(szTo
))
1133 if (!SHELL_ConfirmDialogW(op
->req
->hwnd
, ASK_OVERWRITE_FILE
, PathFindFileNameW(szTo
), op
))
1137 return SHNotifyCopyFileW(szFrom
, szTo
, FALSE
) == 0;
1140 /* copy a file or directory to another directory */
1141 static void copy_to_dir(FILE_OPERATION
*op
, const FILE_ENTRY
*feFrom
, const FILE_ENTRY
*feTo
)
1143 if (!PathFileExistsW(feTo
->szFullPath
))
1144 SHNotifyCreateDirectoryW(feTo
->szFullPath
, NULL
);
1146 if (IsAttribFile(feFrom
->attributes
))
1148 WCHAR szDestPath
[MAX_PATH
];
1150 PathCombineW(szDestPath
, feTo
->szFullPath
, feFrom
->szFilename
);
1151 copy_file_to_file(op
, feFrom
->szFullPath
, szDestPath
);
1153 else if (!(op
->req
->fFlags
& FOF_FILESONLY
&& feFrom
->bFromWildcard
))
1154 copy_dir_to_dir(op
, feFrom
, feTo
->szFullPath
);
1157 static void create_dest_dirs(LPCWSTR szDestDir
)
1159 WCHAR dir
[MAX_PATH
];
1160 LPCWSTR ptr
= StrChrW(szDestDir
, '\\');
1162 /* make sure all directories up to last one are created */
1163 while (ptr
&& (ptr
= StrChrW(ptr
+ 1, '\\')))
1165 lstrcpynW(dir
, szDestDir
, ptr
- szDestDir
+ 1);
1167 if (!PathFileExistsW(dir
))
1168 SHNotifyCreateDirectoryW(dir
, NULL
);
1171 /* create last directory */
1172 if (!PathFileExistsW(szDestDir
))
1173 SHNotifyCreateDirectoryW(szDestDir
, NULL
);
1176 /* the FO_COPY operation */
1177 static int copy_files(FILE_OPERATION
*op
, const FILE_LIST
*flFrom
, FILE_LIST
*flTo
)
1180 const FILE_ENTRY
*entryToCopy
;
1181 const FILE_ENTRY
*fileDest
= &flTo
->feFiles
[0];
1183 if (flFrom
->bAnyDontExist
)
1184 return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND
;
1186 if (flTo
->dwNumFiles
== 0)
1188 /* If the destination is empty, SHFileOperation should use the current directory */
1189 WCHAR curdir
[MAX_PATH
+1];
1191 GetCurrentDirectoryW(MAX_PATH
, curdir
);
1192 curdir
[lstrlenW(curdir
)+1] = 0;
1194 destroy_file_list(flTo
);
1195 ZeroMemory(flTo
, sizeof(FILE_LIST
));
1196 parse_file_list(flTo
, curdir
);
1197 fileDest
= &flTo
->feFiles
[0];
1200 if (op
->req
->fFlags
& FOF_MULTIDESTFILES
&& flTo
->dwNumFiles
> 1)
1202 if (flFrom
->bAnyFromWildcard
)
1203 return ERROR_CANCELLED
;
1205 if (flFrom
->dwNumFiles
!= flTo
->dwNumFiles
)
1207 if (flFrom
->dwNumFiles
!= 1 && !IsAttribDir(fileDest
->attributes
))
1208 return ERROR_CANCELLED
;
1210 /* Free all but the first entry. */
1211 for (i
= 1; i
< flTo
->dwNumFiles
; i
++)
1213 free(flTo
->feFiles
[i
].szDirectory
);
1214 free(flTo
->feFiles
[i
].szFilename
);
1215 free(flTo
->feFiles
[i
].szFullPath
);
1218 flTo
->dwNumFiles
= 1;
1220 else if (IsAttribDir(fileDest
->attributes
))
1222 for (i
= 1; i
< flTo
->dwNumFiles
; i
++)
1223 if (!IsAttribDir(flTo
->feFiles
[i
].attributes
) ||
1224 !IsAttribDir(flFrom
->feFiles
[i
].attributes
))
1226 return ERROR_CANCELLED
;
1230 else if (flFrom
->dwNumFiles
!= 1)
1232 if (flTo
->dwNumFiles
!= 1 && !IsAttribDir(fileDest
->attributes
))
1233 return ERROR_CANCELLED
;
1235 if (PathFileExistsW(fileDest
->szFullPath
) &&
1236 IsAttribFile(fileDest
->attributes
))
1238 return ERROR_CANCELLED
;
1241 if (flTo
->dwNumFiles
== 1 && fileDest
->bFromRelative
&&
1242 !PathFileExistsW(fileDest
->szFullPath
))
1244 return ERROR_CANCELLED
;
1248 for (i
= 0; i
< flFrom
->dwNumFiles
; i
++)
1250 entryToCopy
= &flFrom
->feFiles
[i
];
1252 if ((op
->req
->fFlags
& FOF_MULTIDESTFILES
) &&
1253 flTo
->dwNumFiles
> 1)
1255 fileDest
= &flTo
->feFiles
[i
];
1258 if (IsAttribDir(entryToCopy
->attributes
) &&
1259 !lstrcmpiW(entryToCopy
->szFullPath
, fileDest
->szDirectory
))
1261 return ERROR_SUCCESS
;
1264 create_dest_dirs(fileDest
->szDirectory
);
1266 if (!lstrcmpiW(entryToCopy
->szFullPath
, fileDest
->szFullPath
))
1268 if (IsAttribFile(entryToCopy
->attributes
))
1269 return ERROR_NO_MORE_SEARCH_HANDLES
;
1271 return ERROR_SUCCESS
;
1274 if ((flFrom
->dwNumFiles
> 1 && flTo
->dwNumFiles
== 1) ||
1275 IsAttribDir(fileDest
->attributes
))
1277 copy_to_dir(op
, entryToCopy
, fileDest
);
1279 else if (IsAttribDir(entryToCopy
->attributes
))
1281 copy_dir_to_dir(op
, entryToCopy
, fileDest
->szFullPath
);
1285 if (!copy_file_to_file(op
, entryToCopy
->szFullPath
, fileDest
->szFullPath
))
1287 op
->req
->fAnyOperationsAborted
= TRUE
;
1288 return ERROR_CANCELLED
;
1292 /* Vista return code. XP would return e.g. ERROR_FILE_NOT_FOUND, ERROR_ALREADY_EXISTS */
1294 return ERROR_CANCELLED
;
1297 /* Vista return code. On XP if the used pressed "No" for the last item,
1298 * ERROR_ARENA_TRASHED would be returned */
1299 return ERROR_SUCCESS
;
1302 static BOOL
confirm_delete_list(HWND hWnd
, DWORD fFlags
, BOOL fTrash
, const FILE_LIST
*flFrom
)
1304 if (flFrom
->dwNumFiles
> 1)
1308 swprintf(tmp
, ARRAY_SIZE(tmp
), L
"%d", flFrom
->dwNumFiles
);
1309 return SHELL_ConfirmDialogW(hWnd
, (fTrash
?ASK_TRASH_MULTIPLE_ITEM
:ASK_DELETE_MULTIPLE_ITEM
), tmp
, NULL
);
1313 const FILE_ENTRY
*fileEntry
= &flFrom
->feFiles
[0];
1315 if (IsAttribFile(fileEntry
->attributes
))
1316 return SHELL_ConfirmDialogW(hWnd
, (fTrash
?ASK_TRASH_FILE
:ASK_DELETE_FILE
), fileEntry
->szFullPath
, NULL
);
1317 else if (!(fFlags
& FOF_FILESONLY
&& fileEntry
->bFromWildcard
))
1318 return SHELL_ConfirmDialogW(hWnd
, (fTrash
?ASK_TRASH_FOLDER
:ASK_DELETE_FOLDER
), fileEntry
->szFullPath
, NULL
);
1323 /* the FO_DELETE operation */
1324 static int delete_files(LPSHFILEOPSTRUCTW lpFileOp
, const FILE_LIST
*flFrom
)
1326 const FILE_ENTRY
*fileEntry
;
1331 if (!flFrom
->dwNumFiles
)
1332 return ERROR_SUCCESS
;
1334 /* Windows also checks only the first item */
1335 bTrash
= (lpFileOp
->fFlags
& FOF_ALLOWUNDO
) && is_trash_available();
1337 if (!(lpFileOp
->fFlags
& FOF_NOCONFIRMATION
) || (!bTrash
&& lpFileOp
->fFlags
& FOF_WANTNUKEWARNING
))
1338 if (!confirm_delete_list(lpFileOp
->hwnd
, lpFileOp
->fFlags
, bTrash
, flFrom
))
1340 lpFileOp
->fAnyOperationsAborted
= TRUE
;
1344 for (i
= 0; i
< flFrom
->dwNumFiles
; i
++)
1346 fileEntry
= &flFrom
->feFiles
[i
];
1348 if (!IsAttribFile(fileEntry
->attributes
) &&
1349 (lpFileOp
->fFlags
& FOF_FILESONLY
&& fileEntry
->bFromWildcard
))
1355 if (trash_file(fileEntry
->szFullPath
))
1358 /* Note: Windows silently deletes the file in such a situation, we show a dialog */
1359 if (!(lpFileOp
->fFlags
& FOF_NOCONFIRMATION
) || (lpFileOp
->fFlags
& FOF_WANTNUKEWARNING
))
1360 bDelete
= SHELL_ConfirmDialogW(lpFileOp
->hwnd
, ASK_CANT_TRASH_ITEM
, fileEntry
->szFullPath
, NULL
);
1366 lpFileOp
->fAnyOperationsAborted
= TRUE
;
1371 /* delete the file or directory */
1372 if (IsAttribFile(fileEntry
->attributes
))
1373 ret
= DeleteFileW(fileEntry
->szFullPath
) ?
1374 ERROR_SUCCESS
: GetLastError();
1376 ret
= SHELL_DeleteDirectoryW(lpFileOp
->hwnd
, fileEntry
->szFullPath
, FALSE
);
1382 return ERROR_SUCCESS
;
1385 /* moves a file or directory to another directory */
1386 static void move_to_dir(LPSHFILEOPSTRUCTW lpFileOp
, const FILE_ENTRY
*feFrom
, const FILE_ENTRY
*feTo
)
1388 WCHAR szDestPath
[MAX_PATH
];
1390 PathCombineW(szDestPath
, feTo
->szFullPath
, feFrom
->szFilename
);
1391 SHNotifyMoveFileW(feFrom
->szFullPath
, szDestPath
);
1394 /* the FO_MOVE operation */
1395 static int move_files(LPSHFILEOPSTRUCTW lpFileOp
, const FILE_LIST
*flFrom
, const FILE_LIST
*flTo
)
1399 const FILE_ENTRY
*entryToMove
;
1400 const FILE_ENTRY
*fileDest
;
1402 if (!flFrom
->dwNumFiles
)
1403 return ERROR_SUCCESS
;
1405 if (!flTo
->dwNumFiles
)
1406 return ERROR_FILE_NOT_FOUND
;
1408 if (!(lpFileOp
->fFlags
& FOF_MULTIDESTFILES
) &&
1409 flTo
->dwNumFiles
> 1 && flFrom
->dwNumFiles
> 1)
1411 return ERROR_CANCELLED
;
1414 if (!(lpFileOp
->fFlags
& FOF_MULTIDESTFILES
) &&
1415 !flFrom
->bAnyDirectories
&&
1416 flFrom
->dwNumFiles
> flTo
->dwNumFiles
)
1418 return ERROR_CANCELLED
;
1421 if (!PathFileExistsW(flTo
->feFiles
[0].szDirectory
))
1422 return ERROR_CANCELLED
;
1424 if (lpFileOp
->fFlags
& FOF_MULTIDESTFILES
)
1425 mismatched
= flFrom
->dwNumFiles
- flTo
->dwNumFiles
;
1427 fileDest
= &flTo
->feFiles
[0];
1428 for (i
= 0; i
< flFrom
->dwNumFiles
; i
++)
1430 entryToMove
= &flFrom
->feFiles
[i
];
1432 if (!PathFileExistsW(fileDest
->szDirectory
))
1433 return ERROR_CANCELLED
;
1435 if (lpFileOp
->fFlags
& FOF_MULTIDESTFILES
)
1437 if (i
>= flTo
->dwNumFiles
)
1439 fileDest
= &flTo
->feFiles
[i
];
1440 if (mismatched
&& !fileDest
->bExists
)
1442 create_dest_dirs(flTo
->feFiles
[i
].szFullPath
);
1443 flTo
->feFiles
[i
].bExists
= TRUE
;
1444 flTo
->feFiles
[i
].attributes
= FILE_ATTRIBUTE_DIRECTORY
;
1448 if (fileDest
->bExists
&& IsAttribDir(fileDest
->attributes
))
1449 move_to_dir(lpFileOp
, entryToMove
, fileDest
);
1451 SHNotifyMoveFileW(entryToMove
->szFullPath
, fileDest
->szFullPath
);
1456 if (flFrom
->bAnyDirectories
)
1457 return DE_DESTSAMETREE
;
1462 return ERROR_SUCCESS
;
1465 /* the FO_RENAME files */
1466 static int rename_files(LPSHFILEOPSTRUCTW lpFileOp
, const FILE_LIST
*flFrom
, const FILE_LIST
*flTo
)
1468 const FILE_ENTRY
*feFrom
;
1469 const FILE_ENTRY
*feTo
;
1471 if (flFrom
->dwNumFiles
!= 1)
1472 return ERROR_GEN_FAILURE
;
1474 if (flTo
->dwNumFiles
!= 1)
1475 return ERROR_CANCELLED
;
1477 feFrom
= &flFrom
->feFiles
[0];
1478 feTo
= &flTo
->feFiles
[0];
1480 /* fail if destination doesn't exist */
1481 if (!feFrom
->bExists
)
1482 return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND
;
1484 /* fail if destination already exists */
1486 return ERROR_ALREADY_EXISTS
;
1488 return SHNotifyMoveFileW(feFrom
->szFullPath
, feTo
->szFullPath
);
1491 /* alert the user if an unsupported flag is used */
1492 static void check_flags(FILEOP_FLAGS fFlags
)
1494 WORD wUnsupportedFlags
= FOF_NO_CONNECTED_ELEMENTS
|
1495 FOF_NOCOPYSECURITYATTRIBS
| FOF_NORECURSEREPARSE
|
1496 FOF_RENAMEONCOLLISION
| FOF_WANTMAPPINGHANDLE
;
1498 if (fFlags
& wUnsupportedFlags
)
1499 FIXME("Unsupported flags: %04x\n", fFlags
);
1502 /*************************************************************************
1503 * SHFileOperationW [SHELL32.@]
1505 * See SHFileOperationA
1507 int WINAPI
SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp
)
1510 FILE_LIST flFrom
, flTo
;
1514 return ERROR_INVALID_PARAMETER
;
1516 check_flags(lpFileOp
->fFlags
);
1518 ZeroMemory(&flFrom
, sizeof(FILE_LIST
));
1519 ZeroMemory(&flTo
, sizeof(FILE_LIST
));
1521 if ((ret
= parse_file_list(&flFrom
, lpFileOp
->pFrom
)))
1524 if (lpFileOp
->wFunc
!= FO_DELETE
)
1525 parse_file_list(&flTo
, lpFileOp
->pTo
);
1527 ZeroMemory(&op
, sizeof(op
));
1529 op
.bManyItems
= (flFrom
.dwNumFiles
> 1);
1530 lpFileOp
->fAnyOperationsAborted
= FALSE
;
1532 switch (lpFileOp
->wFunc
)
1535 ret
= copy_files(&op
, &flFrom
, &flTo
);
1538 ret
= delete_files(lpFileOp
, &flFrom
);
1541 ret
= move_files(lpFileOp
, &flFrom
, &flTo
);
1544 ret
= rename_files(lpFileOp
, &flFrom
, &flTo
);
1547 ret
= ERROR_INVALID_PARAMETER
;
1551 destroy_file_list(&flFrom
);
1553 if (lpFileOp
->wFunc
!= FO_DELETE
)
1554 destroy_file_list(&flTo
);
1556 if (ret
== ERROR_CANCELLED
)
1557 lpFileOp
->fAnyOperationsAborted
= TRUE
;
1559 SetLastError(ERROR_SUCCESS
);
1563 #define SHDSA_GetItemCount(hdsa) (*(int*)(hdsa))
1565 /*************************************************************************
1566 * SHFreeNameMappings [shell32.246]
1568 * Free the mapping handle returned by SHFileOperation if FOF_WANTSMAPPINGHANDLE
1572 * hNameMapping [I] handle to the name mappings used during renaming of files
1577 void WINAPI
SHFreeNameMappings(HANDLE hNameMapping
)
1581 int i
= SHDSA_GetItemCount((HDSA
)hNameMapping
) - 1;
1585 LPSHNAMEMAPPINGW lp
= DSA_GetItemPtr(hNameMapping
, i
);
1587 SHFree(lp
->pszOldPath
);
1588 SHFree(lp
->pszNewPath
);
1590 DSA_Destroy(hNameMapping
);
1594 /*************************************************************************
1595 * SheGetDirA [SHELL32.@]
1597 * drive = 0: returns the current directory path
1598 * drive > 0: returns the current directory path of the specified drive
1599 * drive=1 -> A: drive=2 -> B: ...
1600 * returns 0 if successful
1602 DWORD WINAPI
SheGetDirA(DWORD drive
, LPSTR buffer
)
1604 WCHAR org_path
[MAX_PATH
];
1608 /* change current directory to the specified drive */
1610 strcpy(drv_path
, "A:");
1611 drv_path
[0] += (char)drive
-1;
1613 GetCurrentDirectoryW(MAX_PATH
, org_path
);
1615 SetCurrentDirectoryA(drv_path
);
1618 /* query current directory path of the specified drive */
1619 ret
= GetCurrentDirectoryA(MAX_PATH
, buffer
);
1621 /* back to the original drive */
1623 SetCurrentDirectoryW(org_path
);
1626 return GetLastError();
1631 /*************************************************************************
1632 * SheGetDirW [SHELL32.@]
1634 * drive = 0: returns the current directory path
1635 * drive > 0: returns the current directory path of the specified drive
1636 * drive=1 -> A: drive=2 -> B: ...
1637 * returns 0 if successful
1639 DWORD WINAPI
SheGetDirW(DWORD drive
, LPWSTR buffer
)
1641 WCHAR org_path
[MAX_PATH
];
1645 /* change current directory to the specified drive */
1647 strcpy(drv_path
, "A:");
1648 drv_path
[0] += (char)drive
-1;
1650 GetCurrentDirectoryW(MAX_PATH
, org_path
);
1652 SetCurrentDirectoryA(drv_path
);
1655 /* query current directory path of the specified drive */
1656 ret
= GetCurrentDirectoryW(MAX_PATH
, buffer
);
1658 /* back to the original drive */
1660 SetCurrentDirectoryW(org_path
);
1663 return GetLastError();
1668 /*************************************************************************
1669 * SheChangeDirA [SHELL32.@]
1671 * changes the current directory to the specified path
1672 * and returns 0 if successful
1674 DWORD WINAPI
SheChangeDirA(LPSTR path
)
1676 if (SetCurrentDirectoryA(path
))
1679 return GetLastError();
1682 /*************************************************************************
1683 * SheChangeDirW [SHELL32.@]
1685 * changes the current directory to the specified path
1686 * and returns 0 if successful
1688 DWORD WINAPI
SheChangeDirW(LPWSTR path
)
1690 if (SetCurrentDirectoryW(path
))
1693 return GetLastError();
1696 /*************************************************************************
1697 * IsNetDrive [SHELL32.66]
1699 int WINAPI
IsNetDrive(int drive
)
1702 strcpy(root
, "A:\\");
1703 root
[0] += (char)drive
;
1704 return (GetDriveTypeA(root
) == DRIVE_REMOTE
);
1708 /*************************************************************************
1709 * RealDriveType [SHELL32.524]
1711 int WINAPI
RealDriveType(int drive
, BOOL bQueryNet
)
1713 char root
[] = "A:\\";
1714 root
[0] += (char)drive
;
1715 return GetDriveTypeA(root
);
1718 /***********************************************************************
1719 * SHPathPrepareForWriteA (SHELL32.@)
1721 HRESULT WINAPI
SHPathPrepareForWriteA(HWND hwnd
, IUnknown
*modless
, LPCSTR path
, DWORD flags
)
1723 WCHAR wpath
[MAX_PATH
];
1724 MultiByteToWideChar( CP_ACP
, 0, path
, -1, wpath
, MAX_PATH
);
1725 return SHPathPrepareForWriteW(hwnd
, modless
, wpath
, flags
);
1728 /***********************************************************************
1729 * SHPathPrepareForWriteW (SHELL32.@)
1731 HRESULT WINAPI
SHPathPrepareForWriteW(HWND hwnd
, IUnknown
*modless
, LPCWSTR path
, DWORD flags
)
1738 WCHAR
* temppath
=NULL
;
1740 TRACE("%p %p %s 0x%08lx\n", hwnd
, modless
, debugstr_w(path
), flags
);
1742 if (flags
& ~(SHPPFW_DIRCREATE
|SHPPFW_ASKDIRCREATE
|SHPPFW_IGNOREFILENAME
))
1743 FIXME("unimplemented flags 0x%08lx\n", flags
);
1745 /* cut off filename if necessary */
1746 if (flags
& SHPPFW_IGNOREFILENAME
)
1748 last_slash
= StrRChrW(path
, NULL
, '\\');
1749 if (last_slash
== NULL
)
1752 len
= last_slash
- path
+ 1;
1753 temppath
= malloc(len
* sizeof(WCHAR
));
1755 return E_OUTOFMEMORY
;
1756 StrCpyNW(temppath
, path
, len
);
1757 realpath
= temppath
;
1764 /* try to create the directory if asked to */
1765 if (flags
& (SHPPFW_DIRCREATE
|SHPPFW_ASKDIRCREATE
))
1767 if (flags
& SHPPFW_ASKDIRCREATE
)
1768 FIXME("treating SHPPFW_ASKDIRCREATE as SHPPFW_DIRCREATE\n");
1770 SHCreateDirectoryExW(0, realpath
, NULL
);
1773 /* check if we can access the directory */
1774 res
= GetFileAttributesW(realpath
);
1778 if (res
== INVALID_FILE_ATTRIBUTES
)
1780 err
= GetLastError();
1781 if (err
== ERROR_FILE_NOT_FOUND
)
1782 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND
);
1783 return HRESULT_FROM_WIN32(err
);
1785 else if (res
& FILE_ATTRIBUTE_DIRECTORY
)
1788 return HRESULT_FROM_WIN32(ERROR_DIRECTORY
);
1791 /*************************************************************************
1792 * SHMultiFileProperties [SHELL32.@]
1795 HRESULT WINAPI
SHMultiFileProperties(IDataObject
*pdtobj
, DWORD flags
)
1797 FIXME("stub: %p %lu\n", pdtobj
, flags
);
1801 struct file_operation
1803 IFileOperation IFileOperation_iface
;
1807 static inline struct file_operation
*impl_from_IFileOperation(IFileOperation
*iface
)
1809 return CONTAINING_RECORD(iface
, struct file_operation
, IFileOperation_iface
);
1812 static HRESULT WINAPI
file_operation_QueryInterface(IFileOperation
*iface
, REFIID riid
, void **out
)
1814 struct file_operation
*operation
= impl_from_IFileOperation(iface
);
1816 TRACE("(%p, %s, %p).\n", iface
, debugstr_guid(riid
), out
);
1818 if (IsEqualIID(&IID_IFileOperation
, riid
) ||
1819 IsEqualIID(&IID_IUnknown
, riid
))
1820 *out
= &operation
->IFileOperation_iface
;
1823 FIXME("not implemented for %s.\n", debugstr_guid(riid
));
1825 return E_NOINTERFACE
;
1828 IUnknown_AddRef((IUnknown
*)*out
);
1832 static ULONG WINAPI
file_operation_AddRef(IFileOperation
*iface
)
1834 struct file_operation
*operation
= impl_from_IFileOperation(iface
);
1835 ULONG ref
= InterlockedIncrement(&operation
->ref
);
1837 TRACE("(%p): ref=%lu.\n", iface
, ref
);
1842 static ULONG WINAPI
file_operation_Release(IFileOperation
*iface
)
1844 struct file_operation
*operation
= impl_from_IFileOperation(iface
);
1845 ULONG ref
= InterlockedDecrement(&operation
->ref
);
1847 TRACE("(%p): ref=%lu.\n", iface
, ref
);
1857 static HRESULT WINAPI
file_operation_Advise(IFileOperation
*iface
, IFileOperationProgressSink
*sink
, DWORD
*cookie
)
1859 FIXME("(%p, %p, %p): stub.\n", iface
, sink
, cookie
);
1864 static HRESULT WINAPI
file_operation_Unadvise(IFileOperation
*iface
, DWORD cookie
)
1866 FIXME("(%p, %lx): stub.\n", iface
, cookie
);
1871 static HRESULT WINAPI
file_operation_SetOperationFlags(IFileOperation
*iface
, DWORD flags
)
1873 FIXME("(%p, %lx): stub.\n", iface
, flags
);
1878 static HRESULT WINAPI
file_operation_SetProgressMessage(IFileOperation
*iface
, LPCWSTR message
)
1880 FIXME("(%p, %s): stub.\n", iface
, debugstr_w(message
));
1885 static HRESULT WINAPI
file_operation_SetProgressDialog(IFileOperation
*iface
, IOperationsProgressDialog
*dialog
)
1887 FIXME("(%p, %p): stub.\n", iface
, dialog
);
1892 static HRESULT WINAPI
file_operation_SetProperties(IFileOperation
*iface
, IPropertyChangeArray
*array
)
1894 FIXME("(%p, %p): stub.\n", iface
, array
);
1899 static HRESULT WINAPI
file_operation_SetOwnerWindow(IFileOperation
*iface
, HWND owner
)
1901 FIXME("(%p, %p): stub.\n", iface
, owner
);
1906 static HRESULT WINAPI
file_operation_ApplyPropertiesToItem(IFileOperation
*iface
, IShellItem
*item
)
1908 FIXME("(%p, %p): stub.\n", iface
, item
);
1913 static HRESULT WINAPI
file_operation_ApplyPropertiesToItems(IFileOperation
*iface
, IUnknown
*items
)
1915 FIXME("(%p, %p): stub.\n", iface
, items
);
1920 static HRESULT WINAPI
file_operation_RenameItem(IFileOperation
*iface
, IShellItem
*item
, LPCWSTR name
,
1921 IFileOperationProgressSink
*sink
)
1923 FIXME("(%p, %p, %s, %p): stub.\n", iface
, item
, debugstr_w(name
), sink
);
1928 static HRESULT WINAPI
file_operation_RenameItems(IFileOperation
*iface
, IUnknown
*items
, LPCWSTR name
)
1930 FIXME("(%p, %p, %s): stub.\n", iface
, items
, debugstr_w(name
));
1935 static HRESULT WINAPI
file_operation_MoveItem(IFileOperation
*iface
, IShellItem
*item
, IShellItem
*folder
,
1936 LPCWSTR name
, IFileOperationProgressSink
*sink
)
1938 FIXME("(%p, %p, %p, %s, %p): stub.\n", iface
, item
, folder
, debugstr_w(name
), sink
);
1943 static HRESULT WINAPI
file_operation_MoveItems(IFileOperation
*iface
, IUnknown
*items
, IShellItem
*folder
)
1945 FIXME("(%p, %p, %p): stub.\n", iface
, items
, folder
);
1950 static HRESULT WINAPI
file_operation_CopyItem(IFileOperation
*iface
, IShellItem
*item
, IShellItem
*folder
,
1951 LPCWSTR name
, IFileOperationProgressSink
*sink
)
1953 FIXME("(%p, %p, %p, %s, %p): stub.\n", iface
, item
, folder
, debugstr_w(name
), sink
);
1958 static HRESULT WINAPI
file_operation_CopyItems(IFileOperation
*iface
, IUnknown
*items
, IShellItem
*folder
)
1960 FIXME("(%p, %p, %p): stub.\n", iface
, items
, folder
);
1965 static HRESULT WINAPI
file_operation_DeleteItem(IFileOperation
*iface
, IShellItem
*item
,
1966 IFileOperationProgressSink
*sink
)
1968 FIXME("(%p, %p, %p): stub.\n", iface
, item
, sink
);
1973 static HRESULT WINAPI
file_operation_DeleteItems(IFileOperation
*iface
, IUnknown
*items
)
1975 FIXME("(%p, %p): stub.\n", iface
, items
);
1980 static HRESULT WINAPI
file_operation_NewItem(IFileOperation
*iface
, IShellItem
*folder
, DWORD attributes
,
1981 LPCWSTR name
, LPCWSTR
template, IFileOperationProgressSink
*sink
)
1983 FIXME("(%p, %p, %lx, %s, %s, %p): stub.\n", iface
, folder
, attributes
,
1984 debugstr_w(name
), debugstr_w(template), sink
);
1989 static HRESULT WINAPI
file_operation_PerformOperations(IFileOperation
*iface
)
1991 FIXME("(%p): stub.\n", iface
);
1996 static HRESULT WINAPI
file_operation_GetAnyOperationsAborted(IFileOperation
*iface
, BOOL
*aborted
)
1998 FIXME("(%p, %p): stub.\n", iface
, aborted
);
2003 static const IFileOperationVtbl file_operation_vtbl
=
2005 file_operation_QueryInterface
,
2006 file_operation_AddRef
,
2007 file_operation_Release
,
2008 file_operation_Advise
,
2009 file_operation_Unadvise
,
2010 file_operation_SetOperationFlags
,
2011 file_operation_SetProgressMessage
,
2012 file_operation_SetProgressDialog
,
2013 file_operation_SetProperties
,
2014 file_operation_SetOwnerWindow
,
2015 file_operation_ApplyPropertiesToItem
,
2016 file_operation_ApplyPropertiesToItems
,
2017 file_operation_RenameItem
,
2018 file_operation_RenameItems
,
2019 file_operation_MoveItem
,
2020 file_operation_MoveItems
,
2021 file_operation_CopyItem
,
2022 file_operation_CopyItems
,
2023 file_operation_DeleteItem
,
2024 file_operation_DeleteItems
,
2025 file_operation_NewItem
,
2026 file_operation_PerformOperations
,
2027 file_operation_GetAnyOperationsAborted
2030 HRESULT WINAPI
IFileOperation_Constructor(IUnknown
*outer
, REFIID riid
, void **out
)
2032 struct file_operation
*object
;
2035 object
= calloc(1, sizeof(*object
));
2037 return E_OUTOFMEMORY
;
2039 object
->IFileOperation_iface
.lpVtbl
= &file_operation_vtbl
;
2042 hr
= IFileOperation_QueryInterface(&object
->IFileOperation_iface
, riid
, out
);
2043 IFileOperation_Release(&object
->IFileOperation_iface
);