shell32: Move SHCreateLinks() flags to shlfolder.c.
[wine.git] / dlls / shell32 / shlfileop.c
blob87a445dd35c0f59abae30b497b65238d8c2fe3d2
1 /*
2 * SHFileOperation
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
24 #define COBJMACROS
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <assert.h>
32 #include "windef.h"
33 #include "winbase.h"
34 #include "winreg.h"
35 #include "shellapi.h"
36 #include "wingdi.h"
37 #include "winuser.h"
38 #include "shlobj.h"
39 #include "shresdef.h"
40 #define NO_SHLWAPI_STREAM
41 #include "shlwapi.h"
42 #include "shell32_main.h"
43 #include "shfldr.h"
44 #include "undocshell.h"
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(shell);
49 #define IsAttrib(x, y) ((INVALID_FILE_ATTRIBUTES != (x)) && ((x) & (y)))
50 #define IsAttribFile(x) (!((x) & FILE_ATTRIBUTE_DIRECTORY))
51 #define IsAttribDir(x) IsAttrib(x, FILE_ATTRIBUTE_DIRECTORY)
52 #define IsDotDir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
54 #define FO_MASK 0xF
56 #define DE_SAMEFILE 0x71
57 #define DE_DESTSAMETREE 0x7D
59 static DWORD SHNotifyCreateDirectoryA(LPCSTR path, LPSECURITY_ATTRIBUTES sec);
60 static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec);
61 static DWORD SHNotifyRemoveDirectoryA(LPCSTR path);
62 static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path);
63 static DWORD SHNotifyDeleteFileA(LPCSTR path);
64 static DWORD SHNotifyDeleteFileW(LPCWSTR path);
65 static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest);
66 static DWORD SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bFailIfExists);
67 static DWORD SHFindAttrW(LPCWSTR pName, BOOL fileOnly);
69 typedef struct
71 SHFILEOPSTRUCTW *req;
72 DWORD dwYesToAllMask;
73 BOOL bManyItems;
74 BOOL bCancelled;
75 } FILE_OPERATION;
77 /* Confirm dialogs with an optional "Yes To All" as used in file operations confirmations
79 struct confirm_msg_info
81 LPWSTR lpszText;
82 LPWSTR lpszCaption;
83 HICON hIcon;
84 BOOL bYesToAll;
87 /* as some buttons may be hidden and the dialog height may change we may need
88 * to move the controls */
89 static void confirm_msg_move_button(HWND hDlg, INT iId, INT *xPos, INT yOffset, BOOL bShow)
91 HWND hButton = GetDlgItem(hDlg, iId);
92 RECT r;
94 if (bShow) {
95 int width;
97 GetWindowRect(hButton, &r);
98 MapWindowPoints( 0, hDlg, (POINT *)&r, 2 );
99 width = r.right - r.left;
100 SetWindowPos(hButton, 0, *xPos - width, r.top - yOffset, 0, 0,
101 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW );
102 *xPos -= width + 5;
104 else
105 ShowWindow(hButton, SW_HIDE);
108 /* Note: we paint the text manually and don't use the static control to make
109 * sure the text has the same height as the one computed in WM_INITDIALOG
111 static INT_PTR ConfirmMsgBox_Paint(HWND hDlg)
113 PAINTSTRUCT ps;
114 HFONT hOldFont;
115 RECT r;
116 HDC hdc;
118 BeginPaint(hDlg, &ps);
119 hdc = ps.hdc;
120 SetBkMode(hdc, TRANSPARENT);
122 GetClientRect(GetDlgItem(hDlg, IDD_MESSAGE), &r);
123 /* this will remap the rect to dialog coords */
124 MapWindowPoints(GetDlgItem(hDlg, IDD_MESSAGE), hDlg, (LPPOINT)&r, 2);
125 hOldFont = SelectObject(hdc, (HFONT)SendDlgItemMessageW(hDlg, IDD_MESSAGE, WM_GETFONT, 0, 0));
126 DrawTextW(hdc, GetPropW(hDlg, L"WINE_CONFIRM"), -1, &r, DT_NOPREFIX | DT_PATH_ELLIPSIS | DT_WORDBREAK);
127 SelectObject(hdc, hOldFont);
128 EndPaint(hDlg, &ps);
129 return TRUE;
132 static INT_PTR ConfirmMsgBox_Init(HWND hDlg, LPARAM lParam)
134 struct confirm_msg_info *info = (struct confirm_msg_info *)lParam;
135 INT xPos, yOffset;
136 int width, height;
137 HFONT hOldFont;
138 HDC hdc;
139 RECT r;
141 SetWindowTextW(hDlg, info->lpszCaption);
142 ShowWindow(GetDlgItem(hDlg, IDD_MESSAGE), SW_HIDE);
143 SetPropW(hDlg, L"WINE_CONFIRM", info->lpszText);
144 SendDlgItemMessageW(hDlg, IDD_ICON, STM_SETICON, (WPARAM)info->hIcon, 0);
146 /* compute the text height and resize the dialog */
147 GetClientRect(GetDlgItem(hDlg, IDD_MESSAGE), &r);
148 hdc = GetDC(hDlg);
149 yOffset = r.bottom;
150 hOldFont = SelectObject(hdc, (HFONT)SendDlgItemMessageW(hDlg, IDD_MESSAGE, WM_GETFONT, 0, 0));
151 DrawTextW(hdc, info->lpszText, -1, &r, DT_NOPREFIX | DT_PATH_ELLIPSIS | DT_WORDBREAK | DT_CALCRECT);
152 SelectObject(hdc, hOldFont);
153 yOffset -= r.bottom;
154 yOffset = min(yOffset, 35); /* don't make the dialog too small */
155 ReleaseDC(hDlg, hdc);
157 GetClientRect(hDlg, &r);
158 xPos = r.right - 7;
159 GetWindowRect(hDlg, &r);
160 width = r.right - r.left;
161 height = r.bottom - r.top - yOffset;
162 MoveWindow(hDlg, (GetSystemMetrics(SM_CXSCREEN) - width)/2,
163 (GetSystemMetrics(SM_CYSCREEN) - height)/2, width, height, FALSE);
165 confirm_msg_move_button(hDlg, IDCANCEL, &xPos, yOffset, info->bYesToAll);
166 confirm_msg_move_button(hDlg, IDNO, &xPos, yOffset, TRUE);
167 confirm_msg_move_button(hDlg, IDD_YESTOALL, &xPos, yOffset, info->bYesToAll);
168 confirm_msg_move_button(hDlg, IDYES, &xPos, yOffset, TRUE);
169 return TRUE;
172 static INT_PTR CALLBACK ConfirmMsgBoxProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
174 switch (uMsg)
176 case WM_INITDIALOG:
177 return ConfirmMsgBox_Init(hDlg, lParam);
178 case WM_PAINT:
179 return ConfirmMsgBox_Paint(hDlg);
180 case WM_COMMAND:
181 EndDialog(hDlg, wParam);
182 break;
183 case WM_CLOSE:
184 EndDialog(hDlg, IDCANCEL);
185 break;
187 return FALSE;
190 static int SHELL_ConfirmMsgBox(HWND hWnd, LPWSTR lpszText, LPWSTR lpszCaption, HICON hIcon, BOOL bYesToAll)
192 struct confirm_msg_info info;
194 info.lpszText = lpszText;
195 info.lpszCaption = lpszCaption;
196 info.hIcon = hIcon;
197 info.bYesToAll = bYesToAll;
198 return DialogBoxParamW(shell32_hInstance, L"SHELL_YESTOALL_MSGBOX", hWnd, ConfirmMsgBoxProc, (LPARAM)&info);
201 /* confirmation dialogs content */
202 typedef struct
204 HINSTANCE hIconInstance;
205 UINT icon_resource_id;
206 UINT caption_resource_id, text_resource_id;
207 } SHELL_ConfirmIDstruc;
209 static BOOL SHELL_ConfirmIDs(int nKindOfDialog, SHELL_ConfirmIDstruc *ids)
211 ids->hIconInstance = shell32_hInstance;
212 switch (nKindOfDialog) {
213 case ASK_DELETE_FILE:
214 ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
215 ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
216 ids->text_resource_id = IDS_DELETEITEM_TEXT;
217 return TRUE;
218 case ASK_DELETE_FOLDER:
219 ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
220 ids->caption_resource_id = IDS_DELETEFOLDER_CAPTION;
221 ids->text_resource_id = IDS_DELETEITEM_TEXT;
222 return TRUE;
223 case ASK_DELETE_MULTIPLE_ITEM:
224 ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
225 ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
226 ids->text_resource_id = IDS_DELETEMULTIPLE_TEXT;
227 return TRUE;
228 case ASK_TRASH_FILE:
229 ids->icon_resource_id = IDI_SHELL_TRASH_FILE;
230 ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
231 ids->text_resource_id = IDS_TRASHITEM_TEXT;
232 return TRUE;
233 case ASK_TRASH_FOLDER:
234 ids->icon_resource_id = IDI_SHELL_TRASH_FILE;
235 ids->caption_resource_id = IDS_DELETEFOLDER_CAPTION;
236 ids->text_resource_id = IDS_TRASHFOLDER_TEXT;
237 return TRUE;
238 case ASK_TRASH_MULTIPLE_ITEM:
239 ids->icon_resource_id = IDI_SHELL_TRASH_FILE;
240 ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
241 ids->text_resource_id = IDS_TRASHMULTIPLE_TEXT;
242 return TRUE;
243 case ASK_CANT_TRASH_ITEM:
244 ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
245 ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
246 ids->text_resource_id = IDS_CANTTRASH_TEXT;
247 return TRUE;
248 case ASK_DELETE_SELECTED:
249 ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
250 ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
251 ids->text_resource_id = IDS_DELETESELECTED_TEXT;
252 return TRUE;
253 case ASK_OVERWRITE_FILE:
254 ids->hIconInstance = NULL;
255 ids->icon_resource_id = IDI_WARNING;
256 ids->caption_resource_id = IDS_OVERWRITEFILE_CAPTION;
257 ids->text_resource_id = IDS_OVERWRITEFILE_TEXT;
258 return TRUE;
259 case ASK_OVERWRITE_FOLDER:
260 ids->hIconInstance = NULL;
261 ids->icon_resource_id = IDI_WARNING;
262 ids->caption_resource_id = IDS_OVERWRITEFILE_CAPTION;
263 ids->text_resource_id = IDS_OVERWRITEFOLDER_TEXT;
264 return TRUE;
265 default:
266 FIXME(" Unhandled nKindOfDialog %d stub\n", nKindOfDialog);
268 return FALSE;
271 static BOOL SHELL_ConfirmDialogW(HWND hWnd, int nKindOfDialog, LPCWSTR szDir, FILE_OPERATION *op)
273 WCHAR szCaption[255], szText[255], szBuffer[MAX_PATH + 256];
274 SHELL_ConfirmIDstruc ids;
275 DWORD_PTR args[1];
276 HICON hIcon;
277 int ret;
279 assert(nKindOfDialog >= 0 && nKindOfDialog < 32);
280 if (op && (op->dwYesToAllMask & (1 << nKindOfDialog)))
281 return TRUE;
283 if (!SHELL_ConfirmIDs(nKindOfDialog, &ids)) return FALSE;
285 LoadStringW(shell32_hInstance, ids.caption_resource_id, szCaption, ARRAY_SIZE(szCaption));
286 LoadStringW(shell32_hInstance, ids.text_resource_id, szText, ARRAY_SIZE(szText));
288 args[0] = (DWORD_PTR)szDir;
289 FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
290 szText, 0, 0, szBuffer, ARRAY_SIZE(szBuffer), (va_list*)args);
292 hIcon = LoadIconW(ids.hIconInstance, (LPWSTR)MAKEINTRESOURCE(ids.icon_resource_id));
294 ret = SHELL_ConfirmMsgBox(hWnd, szBuffer, szCaption, hIcon, op && op->bManyItems);
295 if (op) {
296 if (ret == IDD_YESTOALL) {
297 op->dwYesToAllMask |= (1 << nKindOfDialog);
298 ret = IDYES;
300 if (ret == IDCANCEL)
301 op->bCancelled = TRUE;
302 if (ret != IDYES)
303 op->req->fAnyOperationsAborted = TRUE;
305 return ret == IDYES;
308 BOOL SHELL_ConfirmYesNoW(HWND hWnd, int nKindOfDialog, LPCWSTR szDir)
310 return SHELL_ConfirmDialogW(hWnd, nKindOfDialog, szDir, NULL);
313 static DWORD SHELL32_AnsiToUnicodeBuf(LPCSTR aPath, LPWSTR *wPath, DWORD minChars)
315 DWORD len = MultiByteToWideChar(CP_ACP, 0, aPath, -1, NULL, 0);
317 if (len < minChars)
318 len = minChars;
320 *wPath = heap_alloc(len * sizeof(WCHAR));
321 if (*wPath)
323 MultiByteToWideChar(CP_ACP, 0, aPath, -1, *wPath, len);
324 return NO_ERROR;
326 return E_OUTOFMEMORY;
329 HRESULT WINAPI SHIsFileAvailableOffline(LPCWSTR path, LPDWORD status)
331 FIXME("(%s, %p) stub\n", debugstr_w(path), status);
332 return E_FAIL;
335 /**************************************************************************
336 * SHELL_DeleteDirectory() [internal]
338 * Asks for confirmation when bShowUI is true and deletes the directory and
339 * all its subdirectories and files if necessary.
341 static DWORD SHELL_DeleteDirectoryW(HWND hwnd, LPCWSTR pszDir, BOOL bShowUI)
343 DWORD ret = 0;
344 HANDLE hFind;
345 WIN32_FIND_DATAW wfd;
346 WCHAR szTemp[MAX_PATH];
348 PathCombineW(szTemp, pszDir, L"*");
349 hFind = FindFirstFileW(szTemp, &wfd);
351 if (hFind != INVALID_HANDLE_VALUE) {
352 if (!bShowUI || SHELL_ConfirmDialogW(hwnd, ASK_DELETE_FOLDER, pszDir, NULL)) {
353 do {
354 if (IsDotDir(wfd.cFileName))
355 continue;
356 PathCombineW(szTemp, pszDir, wfd.cFileName);
357 if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
358 ret = SHELL_DeleteDirectoryW(hwnd, szTemp, FALSE);
359 else
360 ret = SHNotifyDeleteFileW(szTemp);
361 } while (!ret && FindNextFileW(hFind, &wfd));
363 FindClose(hFind);
365 if (ret == ERROR_SUCCESS)
366 ret = SHNotifyRemoveDirectoryW(pszDir);
368 return ret == ERROR_PATH_NOT_FOUND ?
369 0x7C: /* DE_INVALIDFILES (legacy Windows error) */
370 ret;
373 /**************************************************************************
374 * Win32CreateDirectory [SHELL32.93]
376 * Creates a directory. Also triggers a change notify if one exists.
378 * PARAMS
379 * path [I] path to directory to create
381 * RETURNS
382 * TRUE if successful, FALSE otherwise
384 * NOTES
385 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
386 * This is Unicode on NT/2000
388 static DWORD SHNotifyCreateDirectoryA(LPCSTR path, LPSECURITY_ATTRIBUTES sec)
390 LPWSTR wPath;
391 DWORD retCode;
393 TRACE("(%s, %p)\n", debugstr_a(path), sec);
395 retCode = SHELL32_AnsiToUnicodeBuf(path, &wPath, 0);
396 if (!retCode)
398 retCode = SHNotifyCreateDirectoryW(wPath, sec);
399 heap_free(wPath);
401 return retCode;
404 /**********************************************************************/
406 static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
408 TRACE("(%s, %p)\n", debugstr_w(path), sec);
410 if (CreateDirectoryW(path, sec))
412 SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW, path, NULL);
413 return ERROR_SUCCESS;
415 return GetLastError();
418 /**********************************************************************/
420 BOOL WINAPI Win32CreateDirectoryAW(LPCVOID path, LPSECURITY_ATTRIBUTES sec)
422 if (SHELL_OsIsUnicode())
423 return (SHNotifyCreateDirectoryW(path, sec) == ERROR_SUCCESS);
424 return (SHNotifyCreateDirectoryA(path, sec) == ERROR_SUCCESS);
427 /************************************************************************
428 * Win32RemoveDirectory [SHELL32.94]
430 * Deletes a directory. Also triggers a change notify if one exists.
432 * PARAMS
433 * path [I] path to directory to delete
435 * RETURNS
436 * TRUE if successful, FALSE otherwise
438 * NOTES
439 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
440 * This is Unicode on NT/2000
442 static DWORD SHNotifyRemoveDirectoryA(LPCSTR path)
444 LPWSTR wPath;
445 DWORD retCode;
447 TRACE("(%s)\n", debugstr_a(path));
449 retCode = SHELL32_AnsiToUnicodeBuf(path, &wPath, 0);
450 if (!retCode)
452 retCode = SHNotifyRemoveDirectoryW(wPath);
453 heap_free(wPath);
455 return retCode;
458 /***********************************************************************/
460 static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path)
462 BOOL ret;
463 TRACE("(%s)\n", debugstr_w(path));
465 ret = RemoveDirectoryW(path);
466 if (!ret)
468 /* Directory may be write protected */
469 DWORD dwAttr = GetFileAttributesW(path);
470 if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY))
471 if (SetFileAttributesW(path, dwAttr & ~FILE_ATTRIBUTE_READONLY))
472 ret = RemoveDirectoryW(path);
474 if (ret)
476 SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHW, path, NULL);
477 return ERROR_SUCCESS;
479 return GetLastError();
482 /***********************************************************************/
484 BOOL WINAPI Win32RemoveDirectoryAW(LPCVOID path)
486 if (SHELL_OsIsUnicode())
487 return (SHNotifyRemoveDirectoryW(path) == ERROR_SUCCESS);
488 return (SHNotifyRemoveDirectoryA(path) == ERROR_SUCCESS);
491 /************************************************************************
492 * Win32DeleteFile [SHELL32.164]
494 * Deletes a file. Also triggers a change notify if one exists.
496 * PARAMS
497 * path [I] path to file to delete
499 * RETURNS
500 * TRUE if successful, FALSE otherwise
502 * NOTES
503 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
504 * This is Unicode on NT/2000
506 static DWORD SHNotifyDeleteFileA(LPCSTR path)
508 LPWSTR wPath;
509 DWORD retCode;
511 TRACE("(%s)\n", debugstr_a(path));
513 retCode = SHELL32_AnsiToUnicodeBuf(path, &wPath, 0);
514 if (!retCode)
516 retCode = SHNotifyDeleteFileW(wPath);
517 heap_free(wPath);
519 return retCode;
522 /***********************************************************************/
524 static DWORD SHNotifyDeleteFileW(LPCWSTR path)
526 BOOL ret;
528 TRACE("(%s)\n", debugstr_w(path));
530 ret = DeleteFileW(path);
531 if (!ret)
533 /* File may be write protected or a system file */
534 DWORD dwAttr = GetFileAttributesW(path);
535 if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))
536 if (SetFileAttributesW(path, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
537 ret = DeleteFileW(path);
539 if (ret)
541 SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, path, NULL);
542 return ERROR_SUCCESS;
544 return GetLastError();
547 /***********************************************************************/
549 DWORD WINAPI Win32DeleteFileAW(LPCVOID path)
551 if (SHELL_OsIsUnicode())
552 return (SHNotifyDeleteFileW(path) == ERROR_SUCCESS);
553 return (SHNotifyDeleteFileA(path) == ERROR_SUCCESS);
556 /************************************************************************
557 * SHNotifyMoveFile [internal]
559 * Moves a file. Also triggers a change notify if one exists.
561 * PARAMS
562 * src [I] path to source file to move
563 * dest [I] path to target file to move to
565 * RETURNS
566 * ERROR_SUCCESS if successful
568 static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest)
570 BOOL ret;
572 TRACE("(%s %s)\n", debugstr_w(src), debugstr_w(dest));
574 ret = MoveFileExW(src, dest, MOVEFILE_REPLACE_EXISTING);
576 /* MOVEFILE_REPLACE_EXISTING fails with dirs, so try MoveFile */
577 if (!ret)
578 ret = MoveFileW(src, dest);
580 if (!ret)
582 DWORD dwAttr;
584 dwAttr = SHFindAttrW(dest, FALSE);
585 if (INVALID_FILE_ATTRIBUTES == dwAttr)
587 /* Source file may be write protected or a system file */
588 dwAttr = GetFileAttributesW(src);
589 if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))
590 if (SetFileAttributesW(src, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
591 ret = MoveFileW(src, dest);
594 if (ret)
596 SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_PATHW, src, dest);
597 return ERROR_SUCCESS;
599 return GetLastError();
602 /************************************************************************
603 * SHNotifyCopyFile [internal]
605 * Copies a file. Also triggers a change notify if one exists.
607 * PARAMS
608 * src [I] path to source file to move
609 * dest [I] path to target file to move to
610 * bFailIfExists [I] if TRUE, the target file will not be overwritten if
611 * a file with this name already exists
613 * RETURNS
614 * ERROR_SUCCESS if successful
616 static DWORD SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bFailIfExists)
618 BOOL ret;
619 DWORD attribs;
621 TRACE("(%s %s %s)\n", debugstr_w(src), debugstr_w(dest), bFailIfExists ? "failIfExists" : "");
623 /* Destination file may already exist with read only attribute */
624 attribs = GetFileAttributesW(dest);
625 if (IsAttrib(attribs, FILE_ATTRIBUTE_READONLY))
626 SetFileAttributesW(dest, attribs & ~FILE_ATTRIBUTE_READONLY);
628 ret = CopyFileW(src, dest, bFailIfExists);
629 if (ret)
631 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, dest, NULL);
632 return ERROR_SUCCESS;
635 return GetLastError();
638 /*************************************************************************
639 * SHCreateDirectory [SHELL32.165]
641 * This function creates a file system folder whose fully qualified path is
642 * given by path. If one or more of the intermediate folders do not exist,
643 * they will be created as well.
645 * PARAMS
646 * hWnd [I]
647 * path [I] path of directory to create
649 * RETURNS
650 * ERROR_SUCCESS or one of the following values:
651 * ERROR_BAD_PATHNAME if the path is relative
652 * ERROR_FILE_EXISTS when a file with that name exists
653 * ERROR_PATH_NOT_FOUND can't find the path, probably invalid
654 * ERROR_INVALID_NAME if the path contains invalid chars
655 * ERROR_ALREADY_EXISTS when the directory already exists
656 * ERROR_FILENAME_EXCED_RANGE if the filename was too long to process
658 * NOTES
659 * exported by ordinal
660 * Win9x exports ANSI
661 * WinNT/2000 exports Unicode
663 DWORD WINAPI SHCreateDirectory(HWND hWnd, LPCVOID path)
665 if (SHELL_OsIsUnicode())
666 return SHCreateDirectoryExW(hWnd, path, NULL);
667 return SHCreateDirectoryExA(hWnd, path, NULL);
670 /*************************************************************************
671 * SHCreateDirectoryExA [SHELL32.@]
673 * This function creates a file system folder whose fully qualified path is
674 * given by path. If one or more of the intermediate folders do not exist,
675 * they will be created as well.
677 * PARAMS
678 * hWnd [I]
679 * path [I] path of directory to create
680 * sec [I] security attributes to use or NULL
682 * RETURNS
683 * ERROR_SUCCESS or one of the following values:
684 * ERROR_BAD_PATHNAME or ERROR_PATH_NOT_FOUND if the path is relative
685 * ERROR_INVALID_NAME if the path contains invalid chars
686 * ERROR_FILE_EXISTS when a file with that name exists
687 * ERROR_ALREADY_EXISTS when the directory already exists
688 * ERROR_FILENAME_EXCED_RANGE if the filename was too long to process
690 * FIXME: Not implemented yet;
691 * SHCreateDirectoryEx also verifies that the files in the directory will be visible
692 * if the path is a network path to deal with network drivers which might have a limited
693 * but unknown maximum path length. If not:
695 * If hWnd is set to a valid window handle, a message box is displayed warning
696 * the user that the files may not be accessible. If the user chooses not to
697 * proceed, the function returns ERROR_CANCELLED.
699 * If hWnd is set to NULL, no user interface is displayed and the function
700 * returns ERROR_CANCELLED.
702 int WINAPI SHCreateDirectoryExA(HWND hWnd, LPCSTR path, LPSECURITY_ATTRIBUTES sec)
704 LPWSTR wPath;
705 DWORD retCode;
707 TRACE("(%s, %p)\n", debugstr_a(path), sec);
709 retCode = SHELL32_AnsiToUnicodeBuf(path, &wPath, 0);
710 if (!retCode)
712 retCode = SHCreateDirectoryExW(hWnd, wPath, sec);
713 heap_free(wPath);
715 return retCode;
718 /*************************************************************************
719 * SHCreateDirectoryExW [SHELL32.@]
721 * See SHCreateDirectoryExA.
723 int WINAPI SHCreateDirectoryExW(HWND hWnd, LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
725 int ret = ERROR_BAD_PATHNAME;
726 TRACE("(%p, %s, %p)\n", hWnd, debugstr_w(path), sec);
728 if (PathIsRelativeW(path))
730 SetLastError(ret);
732 else
734 ret = SHNotifyCreateDirectoryW(path, sec);
735 /* Refuse to work on certain error codes before trying to create directories recursively */
736 if (ret != ERROR_SUCCESS &&
737 ret != ERROR_FILE_EXISTS &&
738 ret != ERROR_ALREADY_EXISTS &&
739 ret != ERROR_FILENAME_EXCED_RANGE)
741 WCHAR *pEnd, *pSlash, szTemp[MAX_PATH + 1]; /* extra for PathAddBackslash() */
743 lstrcpynW(szTemp, path, MAX_PATH);
744 pEnd = PathAddBackslashW(szTemp);
745 pSlash = szTemp + 3;
747 while (*pSlash)
749 while (*pSlash && *pSlash != '\\') pSlash++;
750 if (*pSlash)
752 *pSlash = 0; /* terminate path at separator */
754 ret = SHNotifyCreateDirectoryW(szTemp, pSlash + 1 == pEnd ? sec : NULL);
756 *pSlash++ = '\\'; /* put the separator back */
760 if (ret && hWnd &&
761 ret != ERROR_CANCELLED &&
762 ret != ERROR_ALREADY_EXISTS)
764 /* We failed and should show a dialog box */
765 FIXME("Show system error message, creating path %s, failed with error %d\n", debugstr_w(path), ret);
766 ret = ERROR_CANCELLED; /* Error has been already presented to user (not really yet!) */
769 return ret;
772 /*************************************************************************
773 * SHFindAttrW [internal]
775 * Get the Attributes for a file or directory. The difference to GetAttributes()
776 * is that this function will also work for paths containing wildcard characters
777 * in its filename.
779 * PARAMS
780 * path [I] path of directory or file to check
781 * fileOnly [I] TRUE if only files should be found
783 * RETURNS
784 * INVALID_FILE_ATTRIBUTES if the path does not exist, the actual attributes of
785 * the first file or directory found otherwise
787 static DWORD SHFindAttrW(LPCWSTR pName, BOOL fileOnly)
789 WIN32_FIND_DATAW wfd;
790 BOOL b_FileMask = fileOnly && (NULL != StrPBrkW(pName, L"*?"));
791 DWORD dwAttr = INVALID_FILE_ATTRIBUTES;
792 HANDLE hFind = FindFirstFileW(pName, &wfd);
794 TRACE("%s %d\n", debugstr_w(pName), fileOnly);
795 if (INVALID_HANDLE_VALUE != hFind)
799 if (b_FileMask && IsAttribDir(wfd.dwFileAttributes))
800 continue;
801 dwAttr = wfd.dwFileAttributes;
802 break;
804 while (FindNextFileW(hFind, &wfd));
805 FindClose(hFind);
807 return dwAttr;
810 /*************************************************************************
812 * SHNameTranslate HelperFunction for SHFileOperationA
814 * Translates a list of 0 terminated ANSI strings into Unicode. If *wString
815 * is NULL, only the necessary size of the string is determined and returned,
816 * otherwise the ANSI strings are copied into it and the buffer is increased
817 * to point to the location after the final 0 termination char.
819 static DWORD SHNameTranslate(LPWSTR* wString, LPCWSTR* pWToFrom, BOOL more)
821 DWORD size = 0, aSize = 0;
822 LPCSTR aString = (LPCSTR)*pWToFrom;
824 if (aString)
828 size = lstrlenA(aString) + 1;
829 aSize += size;
830 aString += size;
831 } while ((size != 1) && more);
832 /* The two sizes might be different in the case of multibyte chars */
833 size = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*pWToFrom, aSize, *wString, 0);
834 if (*wString) /* only in the second loop */
836 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*pWToFrom, aSize, *wString, size);
837 *pWToFrom = *wString;
838 *wString += size;
841 return size;
843 /*************************************************************************
844 * SHFileOperationA [SHELL32.@]
846 * Function to copy, move, delete and create one or more files with optional
847 * user prompts.
849 * PARAMS
850 * lpFileOp [I/O] pointer to a structure containing all the necessary information
852 * RETURNS
853 * Success: ERROR_SUCCESS.
854 * Failure: ERROR_CANCELLED.
856 * NOTES
857 * exported by name
859 int WINAPI SHFileOperationA(LPSHFILEOPSTRUCTA lpFileOp)
861 SHFILEOPSTRUCTW nFileOp = *((LPSHFILEOPSTRUCTW)lpFileOp);
862 int retCode = 0;
863 DWORD size;
864 LPWSTR ForFree = NULL, /* we change wString in SHNameTranslate and can't use it for freeing */
865 wString = NULL; /* we change this in SHNameTranslate */
867 TRACE("\n");
868 if (FO_DELETE == (nFileOp.wFunc & FO_MASK))
869 nFileOp.pTo = NULL; /* we need a NULL or a valid pointer for translation */
870 if (!(nFileOp.fFlags & FOF_SIMPLEPROGRESS))
871 nFileOp.lpszProgressTitle = NULL; /* we need a NULL or a valid pointer for translation */
872 while (1) /* every loop calculate size, second translate also, if we have storage for this */
874 size = SHNameTranslate(&wString, &nFileOp.lpszProgressTitle, FALSE); /* no loop */
875 size += SHNameTranslate(&wString, &nFileOp.pFrom, TRUE); /* internal loop */
876 size += SHNameTranslate(&wString, &nFileOp.pTo, TRUE); /* internal loop */
878 if (ForFree)
880 retCode = SHFileOperationW(&nFileOp);
881 /* Windows 95/98 returns S_OK for this case. */
882 if (retCode == ERROR_ACCESS_DENIED && (GetVersion() & 0x80000000))
883 retCode = S_OK;
885 heap_free(ForFree); /* we cannot use wString, it was changed */
886 break;
888 else
890 wString = ForFree = heap_alloc(size * sizeof(WCHAR));
891 if (ForFree) continue;
892 retCode = ERROR_OUTOFMEMORY;
893 nFileOp.fAnyOperationsAborted = TRUE;
894 return retCode;
898 lpFileOp->hNameMappings = nFileOp.hNameMappings;
899 lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted;
900 return retCode;
903 #define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026
905 typedef struct
907 DWORD attributes;
908 LPWSTR szDirectory;
909 LPWSTR szFilename;
910 LPWSTR szFullPath;
911 BOOL bFromWildcard;
912 BOOL bFromRelative;
913 BOOL bExists;
914 } FILE_ENTRY;
916 typedef struct
918 FILE_ENTRY *feFiles;
919 DWORD num_alloc;
920 DWORD dwNumFiles;
921 BOOL bAnyFromWildcard;
922 BOOL bAnyDirectories;
923 BOOL bAnyDontExist;
924 } FILE_LIST;
927 static inline void grow_list(FILE_LIST *list)
929 FILE_ENTRY *new = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, list->feFiles,
930 list->num_alloc * 2 * sizeof(*new) );
931 list->feFiles = new;
932 list->num_alloc *= 2;
935 /* adds a file to the FILE_ENTRY struct
937 static void add_file_to_entry(FILE_ENTRY *feFile, LPCWSTR szFile)
939 DWORD dwLen = lstrlenW(szFile) + 1;
940 LPCWSTR ptr;
942 feFile->szFullPath = heap_alloc(dwLen * sizeof(WCHAR));
943 lstrcpyW(feFile->szFullPath, szFile);
945 ptr = StrRChrW(szFile, NULL, '\\');
946 if (ptr)
948 dwLen = ptr - szFile + 1;
949 feFile->szDirectory = heap_alloc(dwLen * sizeof(WCHAR));
950 lstrcpynW(feFile->szDirectory, szFile, dwLen);
952 dwLen = lstrlenW(feFile->szFullPath) - dwLen + 1;
953 feFile->szFilename = heap_alloc(dwLen * sizeof(WCHAR));
954 lstrcpyW(feFile->szFilename, ptr + 1); /* skip over backslash */
956 feFile->bFromWildcard = FALSE;
959 static LPWSTR wildcard_to_file(LPCWSTR szWildCard, LPCWSTR szFileName)
961 LPCWSTR ptr;
962 LPWSTR szFullPath;
963 DWORD dwDirLen, dwFullLen;
965 ptr = StrRChrW(szWildCard, NULL, '\\');
966 dwDirLen = ptr - szWildCard + 1;
968 dwFullLen = dwDirLen + lstrlenW(szFileName) + 1;
969 szFullPath = heap_alloc(dwFullLen * sizeof(WCHAR));
971 lstrcpynW(szFullPath, szWildCard, dwDirLen + 1);
972 lstrcatW(szFullPath, szFileName);
974 return szFullPath;
977 static void parse_wildcard_files(FILE_LIST *flList, LPCWSTR szFile, LPDWORD pdwListIndex)
979 WIN32_FIND_DATAW wfd;
980 HANDLE hFile = FindFirstFileW(szFile, &wfd);
981 FILE_ENTRY *file;
982 LPWSTR szFullPath;
983 BOOL res;
985 if (hFile == INVALID_HANDLE_VALUE) return;
987 for (res = TRUE; res; res = FindNextFileW(hFile, &wfd))
989 if (IsDotDir(wfd.cFileName)) continue;
990 if (*pdwListIndex >= flList->num_alloc) grow_list( flList );
991 szFullPath = wildcard_to_file(szFile, wfd.cFileName);
992 file = &flList->feFiles[(*pdwListIndex)++];
993 add_file_to_entry(file, szFullPath);
994 file->bFromWildcard = TRUE;
995 file->attributes = wfd.dwFileAttributes;
996 if (IsAttribDir(file->attributes)) flList->bAnyDirectories = TRUE;
997 heap_free(szFullPath);
1000 FindClose(hFile);
1003 /* takes the null-separated file list and fills out the FILE_LIST */
1004 static HRESULT parse_file_list(FILE_LIST *flList, LPCWSTR szFiles)
1006 LPCWSTR ptr = szFiles;
1007 WCHAR szCurFile[MAX_PATH];
1008 WCHAR *p;
1009 DWORD i = 0;
1011 if (!szFiles)
1012 return ERROR_INVALID_PARAMETER;
1014 flList->bAnyFromWildcard = FALSE;
1015 flList->bAnyDirectories = FALSE;
1016 flList->bAnyDontExist = FALSE;
1017 flList->num_alloc = 32;
1018 flList->dwNumFiles = 0;
1020 /* empty list */
1021 if (!szFiles[0])
1022 return ERROR_ACCESS_DENIED;
1024 flList->feFiles = heap_alloc_zero(flList->num_alloc * sizeof(FILE_ENTRY));
1026 while (*ptr)
1028 if (i >= flList->num_alloc) grow_list( flList );
1030 /* change relative to absolute path */
1031 if (PathIsRelativeW(ptr))
1033 GetCurrentDirectoryW(MAX_PATH, szCurFile);
1034 PathCombineW(szCurFile, szCurFile, ptr);
1035 flList->feFiles[i].bFromRelative = TRUE;
1037 else
1039 lstrcpyW(szCurFile, ptr);
1040 flList->feFiles[i].bFromRelative = FALSE;
1043 for (p = szCurFile; *p; p++) if (*p == '/') *p = '\\';
1045 /* parse wildcard files if they are in the filename */
1046 if (StrPBrkW(szCurFile, L"*?"))
1048 parse_wildcard_files(flList, szCurFile, &i);
1049 flList->bAnyFromWildcard = TRUE;
1050 i--;
1052 else
1054 FILE_ENTRY *file = &flList->feFiles[i];
1055 add_file_to_entry(file, szCurFile);
1056 file->attributes = GetFileAttributesW( file->szFullPath );
1057 file->bExists = (file->attributes != INVALID_FILE_ATTRIBUTES);
1058 if (!file->bExists) flList->bAnyDontExist = TRUE;
1059 if (IsAttribDir(file->attributes)) flList->bAnyDirectories = TRUE;
1062 /* advance to the next string */
1063 ptr += lstrlenW(ptr) + 1;
1064 i++;
1066 flList->dwNumFiles = i;
1068 return S_OK;
1071 /* free the FILE_LIST */
1072 static void destroy_file_list(FILE_LIST *flList)
1074 DWORD i;
1076 if (!flList || !flList->feFiles)
1077 return;
1079 for (i = 0; i < flList->dwNumFiles; i++)
1081 heap_free(flList->feFiles[i].szDirectory);
1082 heap_free(flList->feFiles[i].szFilename);
1083 heap_free(flList->feFiles[i].szFullPath);
1086 heap_free(flList->feFiles);
1089 static void copy_dir_to_dir(FILE_OPERATION *op, const FILE_ENTRY *feFrom, LPCWSTR szDestPath)
1091 WCHAR szFrom[MAX_PATH], szTo[MAX_PATH];
1092 SHFILEOPSTRUCTW fileOp;
1094 if (IsDotDir(feFrom->szFilename))
1095 return;
1097 if (PathFileExistsW(szDestPath))
1098 PathCombineW(szTo, szDestPath, feFrom->szFilename);
1099 else
1100 lstrcpyW(szTo, szDestPath);
1102 if (!(op->req->fFlags & FOF_NOCONFIRMATION) && PathFileExistsW(szTo)) {
1103 if (!SHELL_ConfirmDialogW(op->req->hwnd, ASK_OVERWRITE_FOLDER, feFrom->szFilename, op))
1105 /* Vista returns an ERROR_CANCELLED even if user pressed "No" */
1106 if (!op->bManyItems)
1107 op->bCancelled = TRUE;
1108 return;
1112 szTo[lstrlenW(szTo) + 1] = '\0';
1113 SHNotifyCreateDirectoryW(szTo, NULL);
1115 PathCombineW(szFrom, feFrom->szFullPath, L"*.*");
1116 szFrom[lstrlenW(szFrom) + 1] = '\0';
1118 fileOp = *op->req;
1119 fileOp.pFrom = szFrom;
1120 fileOp.pTo = szTo;
1121 fileOp.fFlags &= ~FOF_MULTIDESTFILES; /* we know we're copying to one dir */
1123 /* Don't ask the user about overwriting files when he accepted to overwrite the
1124 folder. FIXME: this is not exactly what Windows does - e.g. there would be
1125 an additional confirmation for a nested folder */
1126 fileOp.fFlags |= FOF_NOCONFIRMATION;
1128 SHFileOperationW(&fileOp);
1131 static BOOL copy_file_to_file(FILE_OPERATION *op, const WCHAR *szFrom, const WCHAR *szTo)
1133 if (!(op->req->fFlags & FOF_NOCONFIRMATION) && PathFileExistsW(szTo))
1135 if (!SHELL_ConfirmDialogW(op->req->hwnd, ASK_OVERWRITE_FILE, PathFindFileNameW(szTo), op))
1136 return FALSE;
1139 return SHNotifyCopyFileW(szFrom, szTo, FALSE) == 0;
1142 /* copy a file or directory to another directory */
1143 static void copy_to_dir(FILE_OPERATION *op, const FILE_ENTRY *feFrom, const FILE_ENTRY *feTo)
1145 if (!PathFileExistsW(feTo->szFullPath))
1146 SHNotifyCreateDirectoryW(feTo->szFullPath, NULL);
1148 if (IsAttribFile(feFrom->attributes))
1150 WCHAR szDestPath[MAX_PATH];
1152 PathCombineW(szDestPath, feTo->szFullPath, feFrom->szFilename);
1153 copy_file_to_file(op, feFrom->szFullPath, szDestPath);
1155 else if (!(op->req->fFlags & FOF_FILESONLY && feFrom->bFromWildcard))
1156 copy_dir_to_dir(op, feFrom, feTo->szFullPath);
1159 static void create_dest_dirs(LPCWSTR szDestDir)
1161 WCHAR dir[MAX_PATH];
1162 LPCWSTR ptr = StrChrW(szDestDir, '\\');
1164 /* make sure all directories up to last one are created */
1165 while (ptr && (ptr = StrChrW(ptr + 1, '\\')))
1167 lstrcpynW(dir, szDestDir, ptr - szDestDir + 1);
1169 if (!PathFileExistsW(dir))
1170 SHNotifyCreateDirectoryW(dir, NULL);
1173 /* create last directory */
1174 if (!PathFileExistsW(szDestDir))
1175 SHNotifyCreateDirectoryW(szDestDir, NULL);
1178 /* the FO_COPY operation */
1179 static int copy_files(FILE_OPERATION *op, const FILE_LIST *flFrom, FILE_LIST *flTo)
1181 DWORD i;
1182 const FILE_ENTRY *entryToCopy;
1183 const FILE_ENTRY *fileDest = &flTo->feFiles[0];
1185 if (flFrom->bAnyDontExist)
1186 return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
1188 if (flTo->dwNumFiles == 0)
1190 /* If the destination is empty, SHFileOperation should use the current directory */
1191 WCHAR curdir[MAX_PATH+1];
1193 GetCurrentDirectoryW(MAX_PATH, curdir);
1194 curdir[lstrlenW(curdir)+1] = 0;
1196 destroy_file_list(flTo);
1197 ZeroMemory(flTo, sizeof(FILE_LIST));
1198 parse_file_list(flTo, curdir);
1199 fileDest = &flTo->feFiles[0];
1202 if (op->req->fFlags & FOF_MULTIDESTFILES && flTo->dwNumFiles > 1)
1204 if (flFrom->bAnyFromWildcard)
1205 return ERROR_CANCELLED;
1207 if (flFrom->dwNumFiles != flTo->dwNumFiles)
1209 if (flFrom->dwNumFiles != 1 && !IsAttribDir(fileDest->attributes))
1210 return ERROR_CANCELLED;
1212 /* Free all but the first entry. */
1213 for (i = 1; i < flTo->dwNumFiles; i++)
1215 heap_free(flTo->feFiles[i].szDirectory);
1216 heap_free(flTo->feFiles[i].szFilename);
1217 heap_free(flTo->feFiles[i].szFullPath);
1220 flTo->dwNumFiles = 1;
1222 else if (IsAttribDir(fileDest->attributes))
1224 for (i = 1; i < flTo->dwNumFiles; i++)
1225 if (!IsAttribDir(flTo->feFiles[i].attributes) ||
1226 !IsAttribDir(flFrom->feFiles[i].attributes))
1228 return ERROR_CANCELLED;
1232 else if (flFrom->dwNumFiles != 1)
1234 if (flTo->dwNumFiles != 1 && !IsAttribDir(fileDest->attributes))
1235 return ERROR_CANCELLED;
1237 if (PathFileExistsW(fileDest->szFullPath) &&
1238 IsAttribFile(fileDest->attributes))
1240 return ERROR_CANCELLED;
1243 if (flTo->dwNumFiles == 1 && fileDest->bFromRelative &&
1244 !PathFileExistsW(fileDest->szFullPath))
1246 return ERROR_CANCELLED;
1250 for (i = 0; i < flFrom->dwNumFiles; i++)
1252 entryToCopy = &flFrom->feFiles[i];
1254 if ((op->req->fFlags & FOF_MULTIDESTFILES) &&
1255 flTo->dwNumFiles > 1)
1257 fileDest = &flTo->feFiles[i];
1260 if (IsAttribDir(entryToCopy->attributes) &&
1261 !lstrcmpiW(entryToCopy->szFullPath, fileDest->szDirectory))
1263 return ERROR_SUCCESS;
1266 create_dest_dirs(fileDest->szDirectory);
1268 if (!lstrcmpiW(entryToCopy->szFullPath, fileDest->szFullPath))
1270 if (IsAttribFile(entryToCopy->attributes))
1271 return ERROR_NO_MORE_SEARCH_HANDLES;
1272 else
1273 return ERROR_SUCCESS;
1276 if ((flFrom->dwNumFiles > 1 && flTo->dwNumFiles == 1) ||
1277 IsAttribDir(fileDest->attributes))
1279 copy_to_dir(op, entryToCopy, fileDest);
1281 else if (IsAttribDir(entryToCopy->attributes))
1283 copy_dir_to_dir(op, entryToCopy, fileDest->szFullPath);
1285 else
1287 if (!copy_file_to_file(op, entryToCopy->szFullPath, fileDest->szFullPath))
1289 op->req->fAnyOperationsAborted = TRUE;
1290 return ERROR_CANCELLED;
1294 /* Vista return code. XP would return e.g. ERROR_FILE_NOT_FOUND, ERROR_ALREADY_EXISTS */
1295 if (op->bCancelled)
1296 return ERROR_CANCELLED;
1299 /* Vista return code. On XP if the used pressed "No" for the last item,
1300 * ERROR_ARENA_TRASHED would be returned */
1301 return ERROR_SUCCESS;
1304 static BOOL confirm_delete_list(HWND hWnd, DWORD fFlags, BOOL fTrash, const FILE_LIST *flFrom)
1306 if (flFrom->dwNumFiles > 1)
1308 WCHAR tmp[12];
1310 swprintf(tmp, ARRAY_SIZE(tmp), L"%d", flFrom->dwNumFiles);
1311 return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_MULTIPLE_ITEM:ASK_DELETE_MULTIPLE_ITEM), tmp, NULL);
1313 else
1315 const FILE_ENTRY *fileEntry = &flFrom->feFiles[0];
1317 if (IsAttribFile(fileEntry->attributes))
1318 return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_FILE:ASK_DELETE_FILE), fileEntry->szFullPath, NULL);
1319 else if (!(fFlags & FOF_FILESONLY && fileEntry->bFromWildcard))
1320 return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_FOLDER:ASK_DELETE_FOLDER), fileEntry->szFullPath, NULL);
1322 return TRUE;
1325 /* the FO_DELETE operation */
1326 static int delete_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom)
1328 const FILE_ENTRY *fileEntry;
1329 DWORD i;
1330 int ret;
1331 BOOL bTrash;
1333 if (!flFrom->dwNumFiles)
1334 return ERROR_SUCCESS;
1336 /* Windows also checks only the first item */
1337 bTrash = (lpFileOp->fFlags & FOF_ALLOWUNDO) && is_trash_available();
1339 if (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) || (!bTrash && lpFileOp->fFlags & FOF_WANTNUKEWARNING))
1340 if (!confirm_delete_list(lpFileOp->hwnd, lpFileOp->fFlags, bTrash, flFrom))
1342 lpFileOp->fAnyOperationsAborted = TRUE;
1343 return 0;
1346 for (i = 0; i < flFrom->dwNumFiles; i++)
1348 fileEntry = &flFrom->feFiles[i];
1350 if (!IsAttribFile(fileEntry->attributes) &&
1351 (lpFileOp->fFlags & FOF_FILESONLY && fileEntry->bFromWildcard))
1352 continue;
1354 if (bTrash)
1356 BOOL bDelete;
1357 if (trash_file(fileEntry->szFullPath))
1358 continue;
1360 /* Note: Windows silently deletes the file in such a situation, we show a dialog */
1361 if (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) || (lpFileOp->fFlags & FOF_WANTNUKEWARNING))
1362 bDelete = SHELL_ConfirmDialogW(lpFileOp->hwnd, ASK_CANT_TRASH_ITEM, fileEntry->szFullPath, NULL);
1363 else
1364 bDelete = TRUE;
1366 if (!bDelete)
1368 lpFileOp->fAnyOperationsAborted = TRUE;
1369 break;
1373 /* delete the file or directory */
1374 if (IsAttribFile(fileEntry->attributes))
1375 ret = DeleteFileW(fileEntry->szFullPath) ?
1376 ERROR_SUCCESS : GetLastError();
1377 else
1378 ret = SHELL_DeleteDirectoryW(lpFileOp->hwnd, fileEntry->szFullPath, FALSE);
1380 if (ret)
1381 return ret;
1384 return ERROR_SUCCESS;
1387 /* moves a file or directory to another directory */
1388 static void move_to_dir(LPSHFILEOPSTRUCTW lpFileOp, const FILE_ENTRY *feFrom, const FILE_ENTRY *feTo)
1390 WCHAR szDestPath[MAX_PATH];
1392 PathCombineW(szDestPath, feTo->szFullPath, feFrom->szFilename);
1393 SHNotifyMoveFileW(feFrom->szFullPath, szDestPath);
1396 /* the FO_MOVE operation */
1397 static int move_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, const FILE_LIST *flTo)
1399 DWORD i;
1400 INT mismatched = 0;
1401 const FILE_ENTRY *entryToMove;
1402 const FILE_ENTRY *fileDest;
1404 if (!flFrom->dwNumFiles)
1405 return ERROR_SUCCESS;
1407 if (!flTo->dwNumFiles)
1408 return ERROR_FILE_NOT_FOUND;
1410 if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
1411 flTo->dwNumFiles > 1 && flFrom->dwNumFiles > 1)
1413 return ERROR_CANCELLED;
1416 if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
1417 !flFrom->bAnyDirectories &&
1418 flFrom->dwNumFiles > flTo->dwNumFiles)
1420 return ERROR_CANCELLED;
1423 if (!PathFileExistsW(flTo->feFiles[0].szDirectory))
1424 return ERROR_CANCELLED;
1426 if (lpFileOp->fFlags & FOF_MULTIDESTFILES)
1427 mismatched = flFrom->dwNumFiles - flTo->dwNumFiles;
1429 fileDest = &flTo->feFiles[0];
1430 for (i = 0; i < flFrom->dwNumFiles; i++)
1432 entryToMove = &flFrom->feFiles[i];
1434 if (!PathFileExistsW(fileDest->szDirectory))
1435 return ERROR_CANCELLED;
1437 if (lpFileOp->fFlags & FOF_MULTIDESTFILES)
1439 if (i >= flTo->dwNumFiles)
1440 break;
1441 fileDest = &flTo->feFiles[i];
1442 if (mismatched && !fileDest->bExists)
1444 create_dest_dirs(flTo->feFiles[i].szFullPath);
1445 flTo->feFiles[i].bExists = TRUE;
1446 flTo->feFiles[i].attributes = FILE_ATTRIBUTE_DIRECTORY;
1450 if (fileDest->bExists && IsAttribDir(fileDest->attributes))
1451 move_to_dir(lpFileOp, entryToMove, fileDest);
1452 else
1453 SHNotifyMoveFileW(entryToMove->szFullPath, fileDest->szFullPath);
1456 if (mismatched > 0)
1458 if (flFrom->bAnyDirectories)
1459 return DE_DESTSAMETREE;
1460 else
1461 return DE_SAMEFILE;
1464 return ERROR_SUCCESS;
1467 /* the FO_RENAME files */
1468 static int rename_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, const FILE_LIST *flTo)
1470 const FILE_ENTRY *feFrom;
1471 const FILE_ENTRY *feTo;
1473 if (flFrom->dwNumFiles != 1)
1474 return ERROR_GEN_FAILURE;
1476 if (flTo->dwNumFiles != 1)
1477 return ERROR_CANCELLED;
1479 feFrom = &flFrom->feFiles[0];
1480 feTo= &flTo->feFiles[0];
1482 /* fail if destination doesn't exist */
1483 if (!feFrom->bExists)
1484 return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
1486 /* fail if destination already exists */
1487 if (feTo->bExists)
1488 return ERROR_ALREADY_EXISTS;
1490 return SHNotifyMoveFileW(feFrom->szFullPath, feTo->szFullPath);
1493 /* alert the user if an unsupported flag is used */
1494 static void check_flags(FILEOP_FLAGS fFlags)
1496 WORD wUnsupportedFlags = FOF_NO_CONNECTED_ELEMENTS |
1497 FOF_NOCOPYSECURITYATTRIBS | FOF_NORECURSEREPARSE |
1498 FOF_RENAMEONCOLLISION | FOF_WANTMAPPINGHANDLE;
1500 if (fFlags & wUnsupportedFlags)
1501 FIXME("Unsupported flags: %04x\n", fFlags);
1504 /*************************************************************************
1505 * SHFileOperationW [SHELL32.@]
1507 * See SHFileOperationA
1509 int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
1511 FILE_OPERATION op;
1512 FILE_LIST flFrom, flTo;
1513 int ret = 0;
1515 if (!lpFileOp)
1516 return ERROR_INVALID_PARAMETER;
1518 check_flags(lpFileOp->fFlags);
1520 ZeroMemory(&flFrom, sizeof(FILE_LIST));
1521 ZeroMemory(&flTo, sizeof(FILE_LIST));
1523 if ((ret = parse_file_list(&flFrom, lpFileOp->pFrom)))
1524 return ret;
1526 if (lpFileOp->wFunc != FO_DELETE)
1527 parse_file_list(&flTo, lpFileOp->pTo);
1529 ZeroMemory(&op, sizeof(op));
1530 op.req = lpFileOp;
1531 op.bManyItems = (flFrom.dwNumFiles > 1);
1532 lpFileOp->fAnyOperationsAborted = FALSE;
1534 switch (lpFileOp->wFunc)
1536 case FO_COPY:
1537 ret = copy_files(&op, &flFrom, &flTo);
1538 break;
1539 case FO_DELETE:
1540 ret = delete_files(lpFileOp, &flFrom);
1541 break;
1542 case FO_MOVE:
1543 ret = move_files(lpFileOp, &flFrom, &flTo);
1544 break;
1545 case FO_RENAME:
1546 ret = rename_files(lpFileOp, &flFrom, &flTo);
1547 break;
1548 default:
1549 ret = ERROR_INVALID_PARAMETER;
1550 break;
1553 destroy_file_list(&flFrom);
1555 if (lpFileOp->wFunc != FO_DELETE)
1556 destroy_file_list(&flTo);
1558 if (ret == ERROR_CANCELLED)
1559 lpFileOp->fAnyOperationsAborted = TRUE;
1561 SetLastError(ERROR_SUCCESS);
1562 return ret;
1565 #define SHDSA_GetItemCount(hdsa) (*(int*)(hdsa))
1567 /*************************************************************************
1568 * SHFreeNameMappings [shell32.246]
1570 * Free the mapping handle returned by SHFileOperation if FOF_WANTSMAPPINGHANDLE
1571 * was specified.
1573 * PARAMS
1574 * hNameMapping [I] handle to the name mappings used during renaming of files
1576 * RETURNS
1577 * Nothing
1579 void WINAPI SHFreeNameMappings(HANDLE hNameMapping)
1581 if (hNameMapping)
1583 int i = SHDSA_GetItemCount((HDSA)hNameMapping) - 1;
1585 for (; i>= 0; i--)
1587 LPSHNAMEMAPPINGW lp = DSA_GetItemPtr(hNameMapping, i);
1589 SHFree(lp->pszOldPath);
1590 SHFree(lp->pszNewPath);
1592 DSA_Destroy(hNameMapping);
1596 /*************************************************************************
1597 * SheGetDirA [SHELL32.@]
1599 * drive = 0: returns the current directory path
1600 * drive > 0: returns the current directory path of the specified drive
1601 * drive=1 -> A: drive=2 -> B: ...
1602 * returns 0 if successful
1604 DWORD WINAPI SheGetDirA(DWORD drive, LPSTR buffer)
1606 WCHAR org_path[MAX_PATH];
1607 DWORD ret;
1608 char drv_path[3];
1610 /* change current directory to the specified drive */
1611 if (drive) {
1612 strcpy(drv_path, "A:");
1613 drv_path[0] += (char)drive-1;
1615 GetCurrentDirectoryW(MAX_PATH, org_path);
1617 SetCurrentDirectoryA(drv_path);
1620 /* query current directory path of the specified drive */
1621 ret = GetCurrentDirectoryA(MAX_PATH, buffer);
1623 /* back to the original drive */
1624 if (drive)
1625 SetCurrentDirectoryW(org_path);
1627 if (!ret)
1628 return GetLastError();
1630 return 0;
1633 /*************************************************************************
1634 * SheGetDirW [SHELL32.@]
1636 * drive = 0: returns the current directory path
1637 * drive > 0: returns the current directory path of the specified drive
1638 * drive=1 -> A: drive=2 -> B: ...
1639 * returns 0 if successful
1641 DWORD WINAPI SheGetDirW(DWORD drive, LPWSTR buffer)
1643 WCHAR org_path[MAX_PATH];
1644 DWORD ret;
1645 char drv_path[3];
1647 /* change current directory to the specified drive */
1648 if (drive) {
1649 strcpy(drv_path, "A:");
1650 drv_path[0] += (char)drive-1;
1652 GetCurrentDirectoryW(MAX_PATH, org_path);
1654 SetCurrentDirectoryA(drv_path);
1657 /* query current directory path of the specified drive */
1658 ret = GetCurrentDirectoryW(MAX_PATH, buffer);
1660 /* back to the original drive */
1661 if (drive)
1662 SetCurrentDirectoryW(org_path);
1664 if (!ret)
1665 return GetLastError();
1667 return 0;
1670 /*************************************************************************
1671 * SheChangeDirA [SHELL32.@]
1673 * changes the current directory to the specified path
1674 * and returns 0 if successful
1676 DWORD WINAPI SheChangeDirA(LPSTR path)
1678 if (SetCurrentDirectoryA(path))
1679 return 0;
1680 else
1681 return GetLastError();
1684 /*************************************************************************
1685 * SheChangeDirW [SHELL32.@]
1687 * changes the current directory to the specified path
1688 * and returns 0 if successful
1690 DWORD WINAPI SheChangeDirW(LPWSTR path)
1692 if (SetCurrentDirectoryW(path))
1693 return 0;
1694 else
1695 return GetLastError();
1698 /*************************************************************************
1699 * IsNetDrive [SHELL32.66]
1701 int WINAPI IsNetDrive(int drive)
1703 char root[4];
1704 strcpy(root, "A:\\");
1705 root[0] += (char)drive;
1706 return (GetDriveTypeA(root) == DRIVE_REMOTE);
1710 /*************************************************************************
1711 * RealDriveType [SHELL32.524]
1713 int WINAPI RealDriveType(int drive, BOOL bQueryNet)
1715 char root[] = "A:\\";
1716 root[0] += (char)drive;
1717 return GetDriveTypeA(root);
1720 /***********************************************************************
1721 * SHPathPrepareForWriteA (SHELL32.@)
1723 HRESULT WINAPI SHPathPrepareForWriteA(HWND hwnd, IUnknown *modless, LPCSTR path, DWORD flags)
1725 WCHAR wpath[MAX_PATH];
1726 MultiByteToWideChar( CP_ACP, 0, path, -1, wpath, MAX_PATH);
1727 return SHPathPrepareForWriteW(hwnd, modless, wpath, flags);
1730 /***********************************************************************
1731 * SHPathPrepareForWriteW (SHELL32.@)
1733 HRESULT WINAPI SHPathPrepareForWriteW(HWND hwnd, IUnknown *modless, LPCWSTR path, DWORD flags)
1735 DWORD res;
1736 DWORD err;
1737 LPCWSTR realpath;
1738 int len;
1739 WCHAR* last_slash;
1740 WCHAR* temppath=NULL;
1742 TRACE("%p %p %s 0x%08x\n", hwnd, modless, debugstr_w(path), flags);
1744 if (flags & ~(SHPPFW_DIRCREATE|SHPPFW_ASKDIRCREATE|SHPPFW_IGNOREFILENAME))
1745 FIXME("unimplemented flags 0x%08x\n", flags);
1747 /* cut off filename if necessary */
1748 if (flags & SHPPFW_IGNOREFILENAME)
1750 last_slash = StrRChrW(path, NULL, '\\');
1751 if (last_slash == NULL)
1752 len = 1;
1753 else
1754 len = last_slash - path + 1;
1755 temppath = heap_alloc(len * sizeof(WCHAR));
1756 if (!temppath)
1757 return E_OUTOFMEMORY;
1758 StrCpyNW(temppath, path, len);
1759 realpath = temppath;
1761 else
1763 realpath = path;
1766 /* try to create the directory if asked to */
1767 if (flags & (SHPPFW_DIRCREATE|SHPPFW_ASKDIRCREATE))
1769 if (flags & SHPPFW_ASKDIRCREATE)
1770 FIXME("treating SHPPFW_ASKDIRCREATE as SHPPFW_DIRCREATE\n");
1772 SHCreateDirectoryExW(0, realpath, NULL);
1775 /* check if we can access the directory */
1776 res = GetFileAttributesW(realpath);
1778 heap_free(temppath);
1780 if (res == INVALID_FILE_ATTRIBUTES)
1782 err = GetLastError();
1783 if (err == ERROR_FILE_NOT_FOUND)
1784 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
1785 return HRESULT_FROM_WIN32(err);
1787 else if (res & FILE_ATTRIBUTE_DIRECTORY)
1788 return S_OK;
1789 else
1790 return HRESULT_FROM_WIN32(ERROR_DIRECTORY);
1793 /*************************************************************************
1794 * SHMultiFileProperties [SHELL32.@]
1797 HRESULT WINAPI SHMultiFileProperties(IDataObject *pdtobj, DWORD flags)
1799 FIXME("stub: %p %u\n", pdtobj, flags);
1800 return E_NOTIMPL;
1803 struct file_operation
1805 IFileOperation IFileOperation_iface;
1806 LONG ref;
1809 static inline struct file_operation *impl_from_IFileOperation(IFileOperation *iface)
1811 return CONTAINING_RECORD(iface, struct file_operation, IFileOperation_iface);
1814 static HRESULT WINAPI file_operation_QueryInterface(IFileOperation *iface, REFIID riid, void **out)
1816 struct file_operation *operation = impl_from_IFileOperation(iface);
1818 TRACE("(%p, %s, %p).\n", iface, debugstr_guid(riid), out);
1820 if (IsEqualIID(&IID_IFileOperation, riid) ||
1821 IsEqualIID(&IID_IUnknown, riid))
1822 *out = &operation->IFileOperation_iface;
1823 else
1825 FIXME("not implemented for %s.\n", debugstr_guid(riid));
1826 *out = NULL;
1827 return E_NOINTERFACE;
1830 IUnknown_AddRef((IUnknown *)*out);
1831 return S_OK;
1834 static ULONG WINAPI file_operation_AddRef(IFileOperation *iface)
1836 struct file_operation *operation = impl_from_IFileOperation(iface);
1837 ULONG ref = InterlockedIncrement(&operation->ref);
1839 TRACE("(%p): ref=%u.\n", iface, ref);
1841 return ref;
1844 static ULONG WINAPI file_operation_Release(IFileOperation *iface)
1846 struct file_operation *operation = impl_from_IFileOperation(iface);
1847 ULONG ref = InterlockedDecrement(&operation->ref);
1849 TRACE("(%p): ref=%u.\n", iface, ref);
1851 if (!ref)
1853 HeapFree(GetProcessHeap(), 0, operation);
1856 return ref;
1859 static HRESULT WINAPI file_operation_Advise(IFileOperation *iface, IFileOperationProgressSink *sink, DWORD *cookie)
1861 FIXME("(%p, %p, %p): stub.\n", iface, sink, cookie);
1863 return E_NOTIMPL;
1866 static HRESULT WINAPI file_operation_Unadvise(IFileOperation *iface, DWORD cookie)
1868 FIXME("(%p, %x): stub.\n", iface, cookie);
1870 return E_NOTIMPL;
1873 static HRESULT WINAPI file_operation_SetOperationFlags(IFileOperation *iface, DWORD flags)
1875 FIXME("(%p, %x): stub.\n", iface, flags);
1877 return E_NOTIMPL;
1880 static HRESULT WINAPI file_operation_SetProgressMessage(IFileOperation *iface, LPCWSTR message)
1882 FIXME("(%p, %s): stub.\n", iface, debugstr_w(message));
1884 return E_NOTIMPL;
1887 static HRESULT WINAPI file_operation_SetProgressDialog(IFileOperation *iface, IOperationsProgressDialog *dialog)
1889 FIXME("(%p, %p): stub.\n", iface, dialog);
1891 return E_NOTIMPL;
1894 static HRESULT WINAPI file_operation_SetProperties(IFileOperation *iface, IPropertyChangeArray *array)
1896 FIXME("(%p, %p): stub.\n", iface, array);
1898 return E_NOTIMPL;
1901 static HRESULT WINAPI file_operation_SetOwnerWindow(IFileOperation *iface, HWND owner)
1903 FIXME("(%p, %p): stub.\n", iface, owner);
1905 return E_NOTIMPL;
1908 static HRESULT WINAPI file_operation_ApplyPropertiesToItem(IFileOperation *iface, IShellItem *item)
1910 FIXME("(%p, %p): stub.\n", iface, item);
1912 return E_NOTIMPL;
1915 static HRESULT WINAPI file_operation_ApplyPropertiesToItems(IFileOperation *iface, IUnknown *items)
1917 FIXME("(%p, %p): stub.\n", iface, items);
1919 return E_NOTIMPL;
1922 static HRESULT WINAPI file_operation_RenameItem(IFileOperation *iface, IShellItem *item, LPCWSTR name,
1923 IFileOperationProgressSink *sink)
1925 FIXME("(%p, %p, %s, %p): stub.\n", iface, item, debugstr_w(name), sink);
1927 return E_NOTIMPL;
1930 static HRESULT WINAPI file_operation_RenameItems(IFileOperation *iface, IUnknown *items, LPCWSTR name)
1932 FIXME("(%p, %p, %s): stub.\n", iface, items, debugstr_w(name));
1934 return E_NOTIMPL;
1937 static HRESULT WINAPI file_operation_MoveItem(IFileOperation *iface, IShellItem *item, IShellItem *folder,
1938 LPCWSTR name, IFileOperationProgressSink *sink)
1940 FIXME("(%p, %p, %p, %s, %p): stub.\n", iface, item, folder, debugstr_w(name), sink);
1942 return E_NOTIMPL;
1945 static HRESULT WINAPI file_operation_MoveItems(IFileOperation *iface, IUnknown *items, IShellItem *folder)
1947 FIXME("(%p, %p, %p): stub.\n", iface, items, folder);
1949 return E_NOTIMPL;
1952 static HRESULT WINAPI file_operation_CopyItem(IFileOperation *iface, IShellItem *item, IShellItem *folder,
1953 LPCWSTR name, IFileOperationProgressSink *sink)
1955 FIXME("(%p, %p, %p, %s, %p): stub.\n", iface, item, folder, debugstr_w(name), sink);
1957 return E_NOTIMPL;
1960 static HRESULT WINAPI file_operation_CopyItems(IFileOperation *iface, IUnknown *items, IShellItem *folder)
1962 FIXME("(%p, %p, %p): stub.\n", iface, items, folder);
1964 return E_NOTIMPL;
1967 static HRESULT WINAPI file_operation_DeleteItem(IFileOperation *iface, IShellItem *item,
1968 IFileOperationProgressSink *sink)
1970 FIXME("(%p, %p, %p): stub.\n", iface, item, sink);
1972 return E_NOTIMPL;
1975 static HRESULT WINAPI file_operation_DeleteItems(IFileOperation *iface, IUnknown *items)
1977 FIXME("(%p, %p): stub.\n", iface, items);
1979 return E_NOTIMPL;
1982 static HRESULT WINAPI file_operation_NewItem(IFileOperation *iface, IShellItem *folder, DWORD attributes,
1983 LPCWSTR name, LPCWSTR template, IFileOperationProgressSink *sink)
1985 FIXME("(%p, %p, %x, %s, %s, %p): stub.\n", iface, folder, attributes,
1986 debugstr_w(name), debugstr_w(template), sink);
1988 return E_NOTIMPL;
1991 static HRESULT WINAPI file_operation_PerformOperations(IFileOperation *iface)
1993 FIXME("(%p): stub.\n", iface);
1995 return E_NOTIMPL;
1998 static HRESULT WINAPI file_operation_GetAnyOperationsAborted(IFileOperation *iface, BOOL *aborted)
2000 FIXME("(%p, %p): stub.\n", iface, aborted);
2002 return E_NOTIMPL;
2005 static const IFileOperationVtbl file_operation_vtbl =
2007 file_operation_QueryInterface,
2008 file_operation_AddRef,
2009 file_operation_Release,
2010 file_operation_Advise,
2011 file_operation_Unadvise,
2012 file_operation_SetOperationFlags,
2013 file_operation_SetProgressMessage,
2014 file_operation_SetProgressDialog,
2015 file_operation_SetProperties,
2016 file_operation_SetOwnerWindow,
2017 file_operation_ApplyPropertiesToItem,
2018 file_operation_ApplyPropertiesToItems,
2019 file_operation_RenameItem,
2020 file_operation_RenameItems,
2021 file_operation_MoveItem,
2022 file_operation_MoveItems,
2023 file_operation_CopyItem,
2024 file_operation_CopyItems,
2025 file_operation_DeleteItem,
2026 file_operation_DeleteItems,
2027 file_operation_NewItem,
2028 file_operation_PerformOperations,
2029 file_operation_GetAnyOperationsAborted
2032 HRESULT WINAPI IFileOperation_Constructor(IUnknown *outer, REFIID riid, void **out)
2034 struct file_operation *object;
2035 HRESULT hr;
2037 object = heap_alloc_zero(sizeof(*object));
2038 if (!object)
2039 return E_OUTOFMEMORY;
2041 object->IFileOperation_iface.lpVtbl = &file_operation_vtbl;
2042 object->ref = 1;
2044 hr = IFileOperation_QueryInterface(&object->IFileOperation_iface, riid, out);
2045 IFileOperation_Release(&object->IFileOperation_iface);
2047 return hr;