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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "wine/port.h"
39 #define NO_SHLWAPI_STREAM
41 #include "shell32_main.h"
42 #include "undocshell.h"
43 #include "wine/unicode.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 static const WCHAR wWildcardFile
[] = {'*',0};
56 static const WCHAR wWildcardChars
[] = {'*','?',0};
57 static const WCHAR wBackslash
[] = {'\\',0};
59 static BOOL
SHELL_DeleteDirectoryW(LPCWSTR path
, BOOL bShowUI
);
60 static DWORD
SHNotifyCreateDirectoryA(LPCSTR path
, LPSECURITY_ATTRIBUTES sec
);
61 static DWORD
SHNotifyCreateDirectoryW(LPCWSTR path
, LPSECURITY_ATTRIBUTES sec
);
62 static DWORD
SHNotifyRemoveDirectoryA(LPCSTR path
);
63 static DWORD
SHNotifyRemoveDirectoryW(LPCWSTR path
);
64 static DWORD
SHNotifyDeleteFileA(LPCSTR path
);
65 static DWORD
SHNotifyDeleteFileW(LPCWSTR path
);
66 static DWORD
SHNotifyMoveFileW(LPCWSTR src
, LPCWSTR dest
);
67 static DWORD
SHNotifyCopyFileW(LPCWSTR src
, LPCWSTR dest
, BOOL bFailIfExists
);
68 static DWORD
SHFindAttrW(LPCWSTR pName
, BOOL fileOnly
);
72 UINT caption_resource_id
, text_resource_id
;
73 } SHELL_ConfirmIDstruc
;
75 static BOOL
SHELL_ConfirmIDs(int nKindOfDialog
, SHELL_ConfirmIDstruc
*ids
)
77 switch (nKindOfDialog
) {
79 ids
->caption_resource_id
= IDS_DELETEITEM_CAPTION
;
80 ids
->text_resource_id
= IDS_DELETEITEM_TEXT
;
82 case ASK_DELETE_FOLDER
:
83 ids
->caption_resource_id
= IDS_DELETEFOLDER_CAPTION
;
84 ids
->text_resource_id
= IDS_DELETEITEM_TEXT
;
86 case ASK_DELETE_MULTIPLE_ITEM
:
87 ids
->caption_resource_id
= IDS_DELETEITEM_CAPTION
;
88 ids
->text_resource_id
= IDS_DELETEMULTIPLE_TEXT
;
90 case ASK_OVERWRITE_FILE
:
91 ids
->caption_resource_id
= IDS_OVERWRITEFILE_CAPTION
;
92 ids
->text_resource_id
= IDS_OVERWRITEFILE_TEXT
;
95 FIXME(" Unhandled nKindOfDialog %d stub\n", nKindOfDialog
);
100 BOOL
SHELL_ConfirmDialog(int nKindOfDialog
, LPCSTR szDir
)
102 CHAR szCaption
[255], szText
[255], szBuffer
[MAX_PATH
+ 256];
103 SHELL_ConfirmIDstruc ids
;
105 if (!SHELL_ConfirmIDs(nKindOfDialog
, &ids
))
108 LoadStringA(shell32_hInstance
, ids
.caption_resource_id
, szCaption
, sizeof(szCaption
));
109 LoadStringA(shell32_hInstance
, ids
.text_resource_id
, szText
, sizeof(szText
));
111 FormatMessageA(FORMAT_MESSAGE_FROM_STRING
|FORMAT_MESSAGE_ARGUMENT_ARRAY
,
112 szText
, 0, 0, szBuffer
, sizeof(szBuffer
), (va_list*)&szDir
);
114 return (IDOK
== MessageBoxA(GetActiveWindow(), szBuffer
, szCaption
, MB_OKCANCEL
| MB_ICONEXCLAMATION
));
117 BOOL
SHELL_ConfirmDialogW(int nKindOfDialog
, LPCWSTR szDir
)
119 WCHAR szCaption
[255], szText
[255], szBuffer
[MAX_PATH
+ 256];
120 SHELL_ConfirmIDstruc ids
;
122 if (!SHELL_ConfirmIDs(nKindOfDialog
, &ids
))
125 LoadStringW(shell32_hInstance
, ids
.caption_resource_id
, szCaption
, sizeof(szCaption
));
126 LoadStringW(shell32_hInstance
, ids
.text_resource_id
, szText
, sizeof(szText
));
128 FormatMessageW(FORMAT_MESSAGE_FROM_STRING
|FORMAT_MESSAGE_ARGUMENT_ARRAY
,
129 szText
, 0, 0, szBuffer
, sizeof(szBuffer
), (va_list*)&szDir
);
131 return (IDOK
== MessageBoxW(GetActiveWindow(), szBuffer
, szCaption
, MB_OKCANCEL
| MB_ICONEXCLAMATION
));
134 static DWORD
SHELL32_AnsiToUnicodeBuf(LPCSTR aPath
, LPWSTR
*wPath
, DWORD minChars
)
136 DWORD len
= MultiByteToWideChar(CP_ACP
, 0, aPath
, -1, NULL
, 0);
141 *wPath
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
144 MultiByteToWideChar(CP_ACP
, 0, aPath
, -1, *wPath
, len
);
147 return E_OUTOFMEMORY
;
150 static void SHELL32_FreeUnicodeBuf(LPWSTR wPath
)
152 HeapFree(GetProcessHeap(), 0, wPath
);
155 /**************************************************************************
156 * SHELL_DeleteDirectory() [internal]
158 * Asks for confirmation when bShowUI is true and deletes the directory and
159 * all its subdirectories and files if necessary.
161 BOOL
SHELL_DeleteDirectoryA(LPCSTR pszDir
, BOOL bShowUI
)
166 if (!SHELL32_AnsiToUnicodeBuf(pszDir
, &wPath
, 0))
168 ret
= SHELL_DeleteDirectoryW(wPath
, bShowUI
);
169 SHELL32_FreeUnicodeBuf(wPath
);
174 static BOOL
SHELL_DeleteDirectoryW(LPCWSTR pszDir
, BOOL bShowUI
)
178 WIN32_FIND_DATAW wfd
;
179 WCHAR szTemp
[MAX_PATH
];
181 /* Make sure the directory exists before eventually prompting the user */
182 PathCombineW(szTemp
, pszDir
, wWildcardFile
);
183 hFind
= FindFirstFileW(szTemp
, &wfd
);
184 if (hFind
== INVALID_HANDLE_VALUE
)
187 if (!bShowUI
|| (ret
= SHELL_ConfirmDialogW(ASK_DELETE_FOLDER
, pszDir
)))
191 LPWSTR lp
= wfd
.cAlternateFileName
;
196 PathCombineW(szTemp
, pszDir
, lp
);
197 if (FILE_ATTRIBUTE_DIRECTORY
& wfd
.dwFileAttributes
)
198 ret
= SHELL_DeleteDirectoryW(szTemp
, FALSE
);
200 ret
= (SHNotifyDeleteFileW(szTemp
) == ERROR_SUCCESS
);
201 } while (ret
&& FindNextFileW(hFind
, &wfd
));
205 ret
= (SHNotifyRemoveDirectoryW(pszDir
) == ERROR_SUCCESS
);
209 /**************************************************************************
210 * SHELL_DeleteFileA() [internal]
212 BOOL
SHELL_DeleteFileA(LPCSTR pszFile
, BOOL bShowUI
)
214 if (bShowUI
&& !SHELL_ConfirmDialog(ASK_DELETE_FILE
, pszFile
))
217 return (SHNotifyDeleteFileA(pszFile
) == ERROR_SUCCESS
);
220 BOOL
SHELL_DeleteFileW(LPCWSTR pszFile
, BOOL bShowUI
)
222 if (bShowUI
&& !SHELL_ConfirmDialogW(ASK_DELETE_FILE
, pszFile
))
225 return (SHNotifyDeleteFileW(pszFile
) == ERROR_SUCCESS
);
228 /**************************************************************************
229 * Win32CreateDirectory [SHELL32.93]
231 * Creates a directory. Also triggers a change notify if one exists.
234 * path [I] path to directory to create
237 * TRUE if successful, FALSE otherwise
240 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
241 * This is Unicode on NT/2000
243 static DWORD
SHNotifyCreateDirectoryA(LPCSTR path
, LPSECURITY_ATTRIBUTES sec
)
248 TRACE("(%s, %p)\n", debugstr_a(path
), sec
);
250 retCode
= SHELL32_AnsiToUnicodeBuf(path
, &wPath
, 0);
253 retCode
= SHNotifyCreateDirectoryW(wPath
, sec
);
254 SHELL32_FreeUnicodeBuf(wPath
);
259 /**********************************************************************/
261 static DWORD
SHNotifyCreateDirectoryW(LPCWSTR path
, LPSECURITY_ATTRIBUTES sec
)
263 TRACE("(%s, %p)\n", debugstr_w(path
), sec
);
265 if (CreateDirectoryW(path
, sec
))
267 SHChangeNotify(SHCNE_MKDIR
, SHCNF_PATHW
, path
, NULL
);
268 return ERROR_SUCCESS
;
270 return GetLastError();
273 /**********************************************************************/
275 BOOL WINAPI
Win32CreateDirectoryAW(LPCVOID path
, LPSECURITY_ATTRIBUTES sec
)
277 if (SHELL_OsIsUnicode())
278 return (SHNotifyCreateDirectoryW(path
, sec
) == ERROR_SUCCESS
);
279 return (SHNotifyCreateDirectoryA(path
, sec
) == ERROR_SUCCESS
);
282 /************************************************************************
283 * Win32RemoveDirectory [SHELL32.94]
285 * Deletes a directory. Also triggers a change notify if one exists.
288 * path [I] path to directory to delete
291 * TRUE if successful, FALSE otherwise
294 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
295 * This is Unicode on NT/2000
297 static DWORD
SHNotifyRemoveDirectoryA(LPCSTR path
)
302 TRACE("(%s)\n", debugstr_a(path
));
304 retCode
= SHELL32_AnsiToUnicodeBuf(path
, &wPath
, 0);
307 retCode
= SHNotifyRemoveDirectoryW(wPath
);
308 SHELL32_FreeUnicodeBuf(wPath
);
313 /***********************************************************************/
315 static DWORD
SHNotifyRemoveDirectoryW(LPCWSTR path
)
318 TRACE("(%s)\n", debugstr_w(path
));
320 ret
= RemoveDirectoryW(path
);
323 /* Directory may be write protected */
324 DWORD dwAttr
= GetFileAttributesW(path
);
325 if (IsAttrib(dwAttr
, FILE_ATTRIBUTE_READONLY
))
326 if (SetFileAttributesW(path
, dwAttr
& ~FILE_ATTRIBUTE_READONLY
))
327 ret
= RemoveDirectoryW(path
);
331 SHChangeNotify(SHCNE_RMDIR
, SHCNF_PATHW
, path
, NULL
);
332 return ERROR_SUCCESS
;
334 return GetLastError();
337 /***********************************************************************/
339 BOOL WINAPI
Win32RemoveDirectoryAW(LPCVOID path
)
341 if (SHELL_OsIsUnicode())
342 return (SHNotifyRemoveDirectoryW(path
) == ERROR_SUCCESS
);
343 return (SHNotifyRemoveDirectoryA(path
) == ERROR_SUCCESS
);
346 /************************************************************************
347 * Win32DeleteFile [SHELL32.164]
349 * Deletes a file. Also triggers a change notify if one exists.
352 * path [I] path to file to delete
355 * TRUE if successful, FALSE otherwise
358 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
359 * This is Unicode on NT/2000
361 static DWORD
SHNotifyDeleteFileA(LPCSTR path
)
366 TRACE("(%s)\n", debugstr_a(path
));
368 retCode
= SHELL32_AnsiToUnicodeBuf(path
, &wPath
, 0);
371 retCode
= SHNotifyDeleteFileW(wPath
);
372 SHELL32_FreeUnicodeBuf(wPath
);
377 /***********************************************************************/
379 static DWORD
SHNotifyDeleteFileW(LPCWSTR path
)
383 TRACE("(%s)\n", debugstr_w(path
));
385 ret
= DeleteFileW(path
);
388 /* File may be write protected or a system file */
389 DWORD dwAttr
= GetFileAttributesW(path
);
390 if (IsAttrib(dwAttr
, FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
))
391 if (SetFileAttributesW(path
, dwAttr
& ~(FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
)))
392 ret
= DeleteFileW(path
);
396 SHChangeNotify(SHCNE_DELETE
, SHCNF_PATHW
, path
, NULL
);
397 return ERROR_SUCCESS
;
399 return GetLastError();
402 /***********************************************************************/
404 DWORD WINAPI
Win32DeleteFileAW(LPCVOID path
)
406 if (SHELL_OsIsUnicode())
407 return (SHNotifyDeleteFileW(path
) == ERROR_SUCCESS
);
408 return (SHNotifyDeleteFileA(path
) == ERROR_SUCCESS
);
411 /************************************************************************
412 * SHNotifyMoveFile [internal]
414 * Moves a file. Also triggers a change notify if one exists.
417 * src [I] path to source file to move
418 * dest [I] path to target file to move to
421 * ERORR_SUCCESS if successful
423 static DWORD
SHNotifyMoveFileW(LPCWSTR src
, LPCWSTR dest
)
427 TRACE("(%s %s)\n", debugstr_w(src
), debugstr_w(dest
));
429 ret
= MoveFileW(src
, dest
);
434 dwAttr
= SHFindAttrW(dest
, FALSE
);
435 if (INVALID_FILE_ATTRIBUTES
== dwAttr
)
437 /* Source file may be write protected or a system file */
438 dwAttr
= GetFileAttributesW(src
);
439 if (IsAttrib(dwAttr
, FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
))
440 if (SetFileAttributesW(src
, dwAttr
& ~(FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
)))
441 ret
= MoveFileW(src
, dest
);
446 SHChangeNotify(SHCNE_RENAMEITEM
, SHCNF_PATHW
, src
, dest
);
447 return ERROR_SUCCESS
;
449 return GetLastError();
452 /************************************************************************
453 * SHNotifyCopyFile [internal]
455 * Copies a file. Also triggers a change notify if one exists.
458 * src [I] path to source file to move
459 * dest [I] path to target file to move to
460 * bFailIfExists [I] if TRUE, the target file will not be overwritten if
461 * a file with this name already exists
464 * ERROR_SUCCESS if successful
466 static DWORD
SHNotifyCopyFileW(LPCWSTR src
, LPCWSTR dest
, BOOL bFailIfExists
)
470 TRACE("(%s %s %s)\n", debugstr_w(src
), debugstr_w(dest
), bFailIfExists
? "failIfExists" : "");
472 ret
= CopyFileW(src
, dest
, bFailIfExists
);
475 SHChangeNotify(SHCNE_CREATE
, SHCNF_PATHW
, dest
, NULL
);
476 return ERROR_SUCCESS
;
478 return GetLastError();
481 /*************************************************************************
482 * SHCreateDirectory [SHELL32.165]
484 * This function creates a file system folder whose fully qualified path is
485 * given by path. If one or more of the intermediate folders do not exist,
486 * they will be created as well.
490 * path [I] path of directory to create
493 * ERRROR_SUCCESS or one of the following values:
494 * ERROR_BAD_PATHNAME if the path is relative
495 * ERROR_FILE_EXISTS when a file with that name exists
496 * ERROR_PATH_NOT_FOUND can't find the path, probably invalid
497 * ERROR_INVLID_NAME if the path contains invalid chars
498 * ERROR_ALREADY_EXISTS when the directory already exists
499 * ERROR_FILENAME_EXCED_RANGE if the filename was to long to process
502 * exported by ordinal
504 * WinNT/2000 exports Unicode
506 DWORD WINAPI
SHCreateDirectory(HWND hWnd
, LPCVOID path
)
508 if (SHELL_OsIsUnicode())
509 return SHCreateDirectoryExW(hWnd
, path
, NULL
);
510 return SHCreateDirectoryExA(hWnd
, path
, NULL
);
513 /*************************************************************************
514 * SHCreateDirectoryExA [SHELL32.@]
516 * This function creates a file system folder whose fully qualified path is
517 * given by path. If one or more of the intermediate folders do not exist,
518 * they will be created as well.
522 * path [I] path of directory to create
523 * sec [I] security attributes to use or NULL
526 * ERRROR_SUCCESS or one of the following values:
527 * ERROR_BAD_PATHNAME or ERROR_PATH_NOT_FOUND if the path is relative
528 * ERROR_INVLID_NAME if the path contains invalid chars
529 * ERROR_FILE_EXISTS when a file with that name exists
530 * ERROR_ALREADY_EXISTS when the directory already exists
531 * ERROR_FILENAME_EXCED_RANGE if the filename was to long to process
533 * FIXME: Not implemented yet;
534 * SHCreateDirectoryEx also verifies that the files in the directory will be visible
535 * if the path is a network path to deal with network drivers which might have a limited
536 * but unknown maximum path length. If not:
538 * If hWnd is set to a valid window handle, a message box is displayed warning
539 * the user that the files may not be accessible. If the user chooses not to
540 * proceed, the function returns ERROR_CANCELLED.
542 * If hWnd is set to NULL, no user interface is displayed and the function
543 * returns ERROR_CANCELLED.
545 int WINAPI
SHCreateDirectoryExA(HWND hWnd
, LPCSTR path
, LPSECURITY_ATTRIBUTES sec
)
550 TRACE("(%s, %p)\n", debugstr_a(path
), sec
);
552 retCode
= SHELL32_AnsiToUnicodeBuf(path
, &wPath
, 0);
555 retCode
= SHCreateDirectoryExW(hWnd
, wPath
, sec
);
556 SHELL32_FreeUnicodeBuf(wPath
);
561 /*************************************************************************
562 * SHCreateDirectoryExW [SHELL32.@]
564 * See SHCreateDirectoryExA.
566 int WINAPI
SHCreateDirectoryExW(HWND hWnd
, LPCWSTR path
, LPSECURITY_ATTRIBUTES sec
)
568 int ret
= ERROR_BAD_PATHNAME
;
569 TRACE("(%p, %s, %p)\n", hWnd
, debugstr_w(path
), sec
);
571 if (PathIsRelativeW(path
))
577 ret
= SHNotifyCreateDirectoryW(path
, sec
);
578 /* Refuse to work on certain error codes before trying to create directories recursively */
579 if (ret
!= ERROR_FILE_EXISTS
&&
580 ret
!= ERROR_ALREADY_EXISTS
&&
581 ret
!= ERROR_FILENAME_EXCED_RANGE
)
583 WCHAR
*pEnd
, *pSlash
, szTemp
[MAX_PATH
+ 1]; /* extra for PathAddBackslash() */
585 lstrcpynW(szTemp
, path
, MAX_PATH
);
586 pEnd
= PathAddBackslashW(szTemp
);
591 while (*pSlash
&& *pSlash
!= '\\')
592 pSlash
= CharNextW(pSlash
);
596 *pSlash
= 0; /* terminate path at separator */
598 ret
= SHNotifyCreateDirectoryW(szTemp
, pSlash
+ 1 == pEnd
? sec
: NULL
);
600 *pSlash
++ = '\\'; /* put the separator back */
604 if (ret
&& hWnd
&& (ERROR_CANCELLED
!= ret
))
606 /* We failed and should show a dialog box */
607 FIXME("Show system error message, creating path %s, failed with error %d\n", debugstr_w(path
), ret
);
608 ret
= ERROR_CANCELLED
; /* Error has been already presented to user (not really yet!) */
615 /*************************************************************************
616 * SHFindAttrW [internal]
618 * Get the Attributes for a file or directory. The difference to GetAttributes()
619 * is that this function will also work for paths containing wildcard characters
623 * path [I] path of directory or file to check
624 * fileOnly [I] TRUE if only files should be found
627 * INVALID_FILE_ATTRIBUTES if the path does not exist, the actual attributes of
628 * the first file or directory found otherwise
630 static DWORD
SHFindAttrW(LPCWSTR pName
, BOOL fileOnly
)
632 WIN32_FIND_DATAW wfd
;
633 BOOL b_FileMask
= fileOnly
&& (NULL
!= StrPBrkW(pName
, wWildcardChars
));
634 DWORD dwAttr
= INVALID_FILE_ATTRIBUTES
;
635 HANDLE hFind
= FindFirstFileW(pName
, &wfd
);
637 TRACE("%s %d\n", debugstr_w(pName
), fileOnly
);
638 if (INVALID_HANDLE_VALUE
!= hFind
)
642 if (b_FileMask
&& IsAttribDir(wfd
.dwFileAttributes
))
644 dwAttr
= wfd
.dwFileAttributes
;
647 while (FindNextFileW(hFind
, &wfd
));
653 /*************************************************************************
655 * SHFileStrICmp HelperFunction for SHFileOperationW
658 BOOL
SHFileStrICmpW(LPWSTR p1
, LPWSTR p2
, LPWSTR p1End
, LPWSTR p2End
)
663 int i_len1
= lstrlenW(p1
);
664 int i_len2
= lstrlenW(p2
);
666 if (p1End
&& (&p1
[i_len1
] >= p1End
) && ('\\' == p1End
[0]))
670 i_len1
= lstrlenW(p1
);
674 if ((&p2
[i_len2
] >= p2End
) && ('\\' == p2End
[0]))
683 if ((i_len1
<= i_len2
) && ('\\' == p2
[i_len1
]))
690 i_len2
= lstrlenW(p2
);
691 if (i_len1
== i_len2
)
692 i_Temp
= lstrcmpiW(p1
,p2
);
700 /*************************************************************************
702 * SHFileStrCpyCat HelperFunction for SHFileOperationW
705 LPWSTR
SHFileStrCpyCatW(LPWSTR pTo
, LPCWSTR pFrom
, LPCWSTR pCatStr
)
707 LPWSTR pToFile
= NULL
;
712 lstrcpyW(pTo
, pFrom
);
715 i_len
= lstrlenW(pTo
);
716 if ((i_len
) && (pTo
[--i_len
] != '\\'))
719 if (pCatStr
[0] == '\\')
721 lstrcpyW(&pTo
[i_len
+1], pCatStr
);
723 pToFile
= StrRChrW(pTo
,NULL
,'\\');
726 /* termination of the new string-group */
727 pTo
[(lstrlenW(pTo
)) + 1] = '\0';
732 /**************************************************************************
733 * SHELL_FileNamesMatch()
735 * Accepts two \0 delimited lists of the file names. Checks whether number of
736 * files in both lists is the same, and checks also if source-name exists.
738 BOOL
SHELL_FileNamesMatch(LPCWSTR pszFiles1
, LPCWSTR pszFiles2
, BOOL bOnlySrc
)
740 while ((pszFiles1
[0] != '\0') &&
741 (bOnlySrc
|| (pszFiles2
[0] != '\0')))
743 if (NULL
== StrPBrkW(pszFiles1
, wWildcardChars
))
745 if (INVALID_FILE_ATTRIBUTES
== GetFileAttributesW(pszFiles1
))
748 pszFiles1
+= lstrlenW(pszFiles1
) + 1;
750 pszFiles2
+= lstrlenW(pszFiles2
) + 1;
752 return ((pszFiles1
[0] == '\0') && (bOnlySrc
|| (pszFiles2
[0] == '\0')));
755 /*************************************************************************
757 * SHNameTranslate HelperFunction for SHFileOperationA
759 * Translates a list of 0 terminated ASCII strings into Unicode. If *wString
760 * is NULL, only the necessary size of the string is determined and returned,
761 * otherwise the ASCII strings are copied into it and the buffer is increased
762 * to point to the location after the final 0 termination char.
764 DWORD
SHNameTranslate(LPWSTR
* wString
, LPCWSTR
* pWToFrom
, BOOL more
)
766 DWORD size
= 0, aSize
= 0;
767 LPCSTR aString
= (LPCSTR
)*pWToFrom
;
773 size
= lstrlenA(aString
) + 1;
776 } while ((size
!= 1) && more
);
777 /* The two sizes might be different in the case of multibyte chars */
778 size
= MultiByteToWideChar(CP_ACP
, 0, aString
, aSize
, *wString
, 0);
779 if (*wString
) /* only in the second loop */
781 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)*pWToFrom
, aSize
, *wString
, size
);
782 *pWToFrom
= *wString
;
788 /*************************************************************************
789 * SHFileOperationA [SHELL32.@]
791 * Function to copy, move, delete and create one or more files with optional
795 * lpFileOp [I/O] pointer to a structure containing all the necessary information
804 int WINAPI
SHFileOperationA(LPSHFILEOPSTRUCTA lpFileOp
)
806 SHFILEOPSTRUCTW nFileOp
= *((LPSHFILEOPSTRUCTW
)lpFileOp
);
809 LPWSTR ForFree
= NULL
, /* we change wString in SHNameTranslate and can't use it for freeing */
810 wString
= NULL
; /* we change this in SHNameTranslate */
813 if (FO_DELETE
== (nFileOp
.wFunc
& FO_MASK
))
814 nFileOp
.pTo
= NULL
; /* we need a NULL or a valid pointer for translation */
815 if (!(nFileOp
.fFlags
& FOF_SIMPLEPROGRESS
))
816 nFileOp
.lpszProgressTitle
= NULL
; /* we need a NULL or a valid pointer for translation */
817 while (1) /* every loop calculate size, second translate also, if we have storage for this */
819 size
= SHNameTranslate(&wString
, &nFileOp
.lpszProgressTitle
, FALSE
); /* no loop */
820 size
+= SHNameTranslate(&wString
, &nFileOp
.pFrom
, TRUE
); /* internal loop */
821 size
+= SHNameTranslate(&wString
, &nFileOp
.pTo
, TRUE
); /* internal loop */
825 retCode
= SHFileOperationW(&nFileOp
);
826 HeapFree(GetProcessHeap(), 0, ForFree
); /* we cannot use wString, it was changed */
831 wString
= ForFree
= HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
));
832 if (ForFree
) continue;
833 retCode
= ERROR_OUTOFMEMORY
;
834 nFileOp
.fAnyOperationsAborted
= TRUE
;
835 SetLastError(retCode
);
840 lpFileOp
->hNameMappings
= nFileOp
.hNameMappings
;
841 lpFileOp
->fAnyOperationsAborted
= nFileOp
.fAnyOperationsAborted
;
845 static const char * debug_shfileops_flags( DWORD fFlags
)
847 return wine_dbg_sprintf( "%s%s%s%s%s%s%s%s%s%s%s%s%s",
848 fFlags
& FOF_MULTIDESTFILES
? "FOF_MULTIDESTFILES " : "",
849 fFlags
& FOF_CONFIRMMOUSE
? "FOF_CONFIRMMOUSE " : "",
850 fFlags
& FOF_SILENT
? "FOF_SILENT " : "",
851 fFlags
& FOF_RENAMEONCOLLISION
? "FOF_RENAMEONCOLLISION " : "",
852 fFlags
& FOF_NOCONFIRMATION
? "FOF_NOCONFIRMATION " : "",
853 fFlags
& FOF_WANTMAPPINGHANDLE
? "FOF_WANTMAPPINGHANDLE " : "",
854 fFlags
& FOF_ALLOWUNDO
? "FOF_ALLOWUNDO " : "",
855 fFlags
& FOF_FILESONLY
? "FOF_FILESONLY " : "",
856 fFlags
& FOF_SIMPLEPROGRESS
? "FOF_SIMPLEPROGRESS " : "",
857 fFlags
& FOF_NOCONFIRMMKDIR
? "FOF_NOCONFIRMMKDIR " : "",
858 fFlags
& FOF_NOERRORUI
? "FOF_NOERRORUI " : "",
859 fFlags
& FOF_NOCOPYSECURITYATTRIBS
? "FOF_NOCOPYSECURITYATTRIBS" : "",
860 fFlags
& 0xf000 ? "MORE-UNKNOWN-Flags" : "");
863 static const char * debug_shfileops_action( DWORD op
)
865 LPCSTR cFO_Name
[] = {"FO_????","FO_MOVE","FO_COPY","FO_DELETE","FO_RENAME"};
866 return wine_dbg_sprintf("%s", cFO_Name
[ op
]);
869 #define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026
870 #define HIGH_ADR (LPWSTR)0xffffffff
872 /* handle the complete deletion of `pTempFrom` */
873 static int shfileops_delete(WIN32_FIND_DATAW
*wfd
,SHFILEOPSTRUCTW nFileOp
, LPWSTR pFromFile
,LPWSTR pTempFrom
,HANDLE
*hFind
)
877 BOOL b_Mask
= (NULL
!= StrPBrkW(&pFromFile
[1], wWildcardChars
));
881 lpFileName
= wfd
->cAlternateFileName
;
883 lpFileName
= wfd
->cFileName
;
884 if (IsDotDir(lpFileName
) ||
885 ((b_Mask
) && IsAttribDir(wfd
->dwFileAttributes
) && (nFileOp
.fFlags
& FOF_FILESONLY
)))
887 SHFileStrCpyCatW(&pFromFile
[1], lpFileName
, NULL
);
888 /* TODO: Check the SHELL_DeleteFileOrDirectoryW() function in shell32.dll */
889 if (IsAttribFile(wfd
->dwFileAttributes
))
891 if(SHNotifyDeleteFileW(pTempFrom
) != ERROR_SUCCESS
)
893 nFileOp
.fAnyOperationsAborted
= TRUE
;
894 retCode
= 0x78; /* value unknown */
897 else if(!SHELL_DeleteDirectoryW(pTempFrom
, (!(nFileOp
.fFlags
& FOF_NOCONFIRMATION
))))
899 nFileOp
.fAnyOperationsAborted
= TRUE
;
900 retCode
= 0x79; /* value unknown */
903 while (!nFileOp
.fAnyOperationsAborted
&& FindNextFileW(*hFind
,wfd
));
905 *hFind
= INVALID_HANDLE_VALUE
;
913 * FOF_MULTIDESTFILES, FOF_NOCONFIRMATION, FOF_FILESONLY
915 * unimplememented and ignored flags:
916 * FOF_CONFIRMMOUSE, FOF_SILENT, FOF_NOCONFIRMMKDIR,
917 * FOF_SIMPLEPROGRESS, FOF_NOCOPYSECURITYATTRIBS
919 * partially implemented, breaks if file exists:
920 * FOF_RENAMEONCOLLISION
922 * unimplemented and break if any other flag set:
923 * FOF_ALLOWUNDO, FOF_WANTMAPPINGHANDLE
926 static int shfileops_check_flags(SHFILEOPSTRUCTW nFileOp
)
928 FILEOP_FLAGS OFl
= ((FILEOP_FLAGS
)nFileOp
.fFlags
& 0xfff);
929 long FuncSwitch
= (nFileOp
.wFunc
& FO_MASK
);
930 long level
= nFileOp
.wFunc
>> 4;
932 TRACE("%s level=%ld nFileOp.fFlags=0x%x\n",
933 debug_shfileops_action(FuncSwitch
), level
, nFileOp
.fFlags
);
934 /* OFl &= (-1 - (FOF_MULTIDESTFILES | FOF_FILESONLY)); */
935 /* OFl ^= (FOF_SILENT | FOF_NOCONFIRMATION | FOF_SIMPLEPROGRESS | FOF_NOCONFIRMMKDIR); */
936 OFl
&= (~(FOF_MULTIDESTFILES
| FOF_NOCONFIRMATION
| FOF_FILESONLY
)); /* implemented */
937 OFl
^= (FOF_SILENT
| FOF_NOCONFIRMMKDIR
| FOF_NOERRORUI
| FOF_NOCOPYSECURITYATTRIBS
); /* ignored, if one */
938 OFl
&= (~FOF_SIMPLEPROGRESS
); /* ignored, only with FOF_SILENT */
941 if (OFl
& (~(FOF_CONFIRMMOUSE
| FOF_SILENT
| FOF_RENAMEONCOLLISION
|
942 FOF_NOCONFIRMMKDIR
| FOF_NOERRORUI
| FOF_NOCOPYSECURITYATTRIBS
| FOF_ALLOWUNDO
)))
944 FIXME("%s level=%ld lpFileOp->fFlags=0x%x not implemented, Aborted=TRUE, stub\n",
945 debug_shfileops_action(FuncSwitch
), level
, OFl
);
946 return 0x403; /* 1027, we need an extension to shlfileop */
950 TRACE("%s level=%ld lpFileOp->fFlags=0x%x not fully implemented, stub\n",
951 debug_shfileops_action(FuncSwitch
), level
, OFl
);
957 static int shfileops_do_operation(WIN32_FIND_DATAW wfd
,SHFILEOPSTRUCTW
*nFileOp
, LPWSTR pToFile
, LPWSTR pFromFile
)
959 LPWSTR lpFileName
= wfd
.cAlternateFileName
;
961 lpFileName
= wfd
.cFileName
;
962 if (IsDotDir(lpFileName
) ||
963 (IsAttribDir(wfd
.dwFileAttributes
) && (nFileOp
->fFlags
& FOF_FILESONLY
)))
964 return 0; /* next name in pTempFrom(dir) */
965 SHFileStrCpyCatW(&pToFile
[1], lpFileName
, NULL
);
966 SHFileStrCpyCatW(&pFromFile
[1], lpFileName
, NULL
);
967 return SHFileOperationW (nFileOp
);
970 /* get attributes of the parent dir of pTemp and create the directory if it does not exists */
971 static DWORD
shfileops_get_parent_attr2(LPWSTR pFile
,LPWSTR pTemp
,int flag
,int *retCode
)
975 PathAttr
= GetFileAttributesW(pTemp
);
976 if ((PathAttr
== INVALID_FILE_ATTRIBUTES
) && flag
)
978 /* create dir must be here, sample target D:\y\ *.* create with RC=10003 */
979 if (SHNotifyCreateDirectoryW(pTemp
, NULL
))
981 *retCode
= 0x73;/* value unknown */
982 /*goto shfileop_end;*/
985 PathAttr
= GetFileAttributesW(pTemp
);
991 /* get attributes of the parent dir of pTemp without creating the directory if it does not exists */
992 static DWORD
shfileops_get_parent_attr(LPWSTR pFile
,LPWSTR pTemp
)
995 return shfileops_get_parent_attr2(pFile,pTemp,0,NULL);
999 PathAttr
= GetFileAttributesW(pTemp
);
1004 /*************************************************************************
1005 * SHFileOperationW [SHELL32.@]
1007 * See SHFileOperationA.
1009 int WINAPI
SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp
)
1011 SHFILEOPSTRUCTW nFileOp
= *(lpFileOp
);
1013 LPCWSTR pNextFrom
= nFileOp
.pFrom
;
1014 LPCWSTR pNextTo
= nFileOp
.pTo
;
1015 LPCWSTR pFrom
= pNextFrom
;
1017 HANDLE hFind
= INVALID_HANDLE_VALUE
;
1018 WIN32_FIND_DATAW wfd
;
1019 LPWSTR pTempFrom
= NULL
;
1020 LPWSTR pTempTo
= NULL
;
1022 LPWSTR pToFile
= NULL
;
1027 BOOL b_Multi
= (nFileOp
.fFlags
& FOF_MULTIDESTFILES
);
1029 BOOL b_MultiTo
= (FO_DELETE
!= (lpFileOp
->wFunc
& FO_MASK
));
1030 BOOL b_MultiPaired
= (!b_MultiTo
);
1031 BOOL b_MultiFrom
= FALSE
;
1035 BOOL b_SameTailName
;
1036 BOOL b_ToInvalidTail
= FALSE
;
1037 BOOL b_ToValid
; /* for W98-Bug for FO_MOVE with source and target in same rootdrive */
1039 BOOL b_ToTailSlash
= FALSE
;
1041 long FuncSwitch
= (nFileOp
.wFunc
& FO_MASK
);
1042 long level
= nFileOp
.wFunc
>>4;
1044 /* default no error */
1045 nFileOp
.fAnyOperationsAborted
= FALSE
;
1047 if ((FuncSwitch
< FO_MOVE
) || (FuncSwitch
> FO_RENAME
))
1048 goto shfileop_end
; /* no valid FunctionCode */
1051 TRACE("%s: flags (0x%04x) : %s\n",
1052 debug_shfileops_action(FuncSwitch
), nFileOp
.fFlags
,
1053 debug_shfileops_flags(nFileOp
.fFlags
) );
1055 /* establish when pTo is interpreted as the name of the destination file
1056 * or the directory where the Fromfile should be copied to.
1058 * (1) pTo points to the name of an existing directory;
1059 * (2) the flag FOF_MULTIDESTFILES is present;
1060 * (3) whether pFrom point to multiple filenames.
1064 * destisdir 1 1 1 1 0 0 0 0
1065 * FOF_MULTIDESTFILES 1 1 0 0 1 1 0 0
1066 * multiple from filenames 1 0 1 0 1 0 1 0
1068 * copy files to dir 1 0 1 1 0 0 1 0
1069 * create dir 0 0 0 0 0 0 1 0
1072 retCode
= shfileops_check_flags(nFileOp
);
1076 if ((pNextFrom
) && (!(b_MultiTo
) || (pNextTo
)))
1078 nFileOp
.pFrom
= pTempFrom
= HeapAlloc(GetProcessHeap(), 0, ((1 + 2 * (b_MultiTo
)) * MAX_PATH
+ 6) * sizeof(WCHAR
));
1081 retCode
= ERROR_OUTOFMEMORY
;
1082 SetLastError(retCode
);
1086 pTempTo
= &pTempFrom
[MAX_PATH
+ 4];
1087 nFileOp
.pTo
= pTempTo
;
1088 ask_overwrite
= (!(nFileOp
.fFlags
& FOF_NOCONFIRMATION
) && !(nFileOp
.fFlags
& FOF_RENAMEONCOLLISION
));
1089 not_overwrite
= (!(nFileOp
.fFlags
& FOF_NOCONFIRMATION
) || (nFileOp
.fFlags
& FOF_RENAMEONCOLLISION
));
1093 retCode
= ERROR_SHELL_INTERNAL_FILE_NOT_FOUND
;
1096 /* need break at error before change sourcepointer */
1097 while(!nFileOp
.fAnyOperationsAborted
&& (pNextFrom
[0]))
1099 nFileOp
.wFunc
= ((level
+ 1) << 4) + FuncSwitch
;
1100 nFileOp
.fFlags
= lpFileOp
->fFlags
;
1105 pNextTo
= &pNextTo
[lstrlenW(pTo
)+1];
1106 b_MultiTo
= (b_Multi
&& pNextTo
[0]);
1110 pNextFrom
= &pNextFrom
[lstrlenW(pNextFrom
)+1];
1111 if (!b_MultiFrom
&& !b_MultiTo
)
1112 b_MultiFrom
= (pNextFrom
[0]);
1114 pFromFile
= SHFileStrCpyCatW(pTempFrom
, pFrom
, NULL
);
1118 pToFile
= SHFileStrCpyCatW(pTempTo
, pTo
, NULL
);
1123 SHELL_FileNamesMatch(lpFileOp
->pFrom
, lpFileOp
->pTo
, (!b_Multi
|| b_MultiFrom
));
1125 if (!(b_MultiPaired
) || !(pFromFile
) || !(pFromFile
[1]) || ((pTo
) && !(pToFile
)))
1127 retCode
= ERROR_SHELL_INTERNAL_FILE_NOT_FOUND
;
1132 b_ToTailSlash
= (!pToFile
[1]);
1136 if (StrChrW(pTempTo
,'\\'))
1138 pToFile
= SHFileStrCpyCatW(pTempTo
, NULL
, NULL
);
1141 b_ToInvalidTail
= (NULL
!= StrPBrkW(&pToFile
[1], wWildcardChars
));
1145 b_Mask
= (NULL
!= StrPBrkW(&pFromFile
[1], wWildcardChars
));
1146 if (FO_RENAME
== FuncSwitch
)
1148 /* temporary only for FO_RENAME */
1149 if (b_MultiTo
|| b_MultiFrom
|| (b_Mask
&& !b_ToInvalidTail
))
1151 #ifndef W98_FO_FUNCTION
1152 retCode
= ERROR_GEN_FAILURE
; /* W2K ERROR_GEN_FAILURE, W98 returns no error */
1158 hFind
= FindFirstFileW(pFrom
, &wfd
);
1159 if (INVALID_HANDLE_VALUE
== hFind
)
1161 if ((FO_DELETE
== FuncSwitch
) && (b_Mask
) && IsAttribDir(shfileops_get_parent_attr(pFromFile
,pTempFrom
)))
1163 /* FO_DELETE with mask and without found is valid */
1166 /* root (without mask) is also not allowed as source, tested in W98 */
1167 retCode
= ERROR_SHELL_INTERNAL_FILE_NOT_FOUND
;
1173 /* ??? b_Mask = (!SHFileStrICmpA(&pFromFile[1], &wfd.cFileName[0], HIGH_ADR, HIGH_ADR)); */
1174 if (!pTo
) /* FO_DELETE */
1176 retCode
= shfileops_delete(&wfd
,nFileOp
,pFromFile
,pTempFrom
,&hFind
);
1177 /* if ret is not 0, nFileOp.fAnyOperationsAborted is TRUE and the loop will end */
1179 } /* FO_DELETE ends, pTo must be always valid from here */
1181 b_SameRoot
= (toupperW(pTempFrom
[0]) == toupperW(pTempTo
[0]));
1182 b_SameTailName
= SHFileStrICmpW(pToFile
, pFromFile
, NULL
, NULL
);
1184 ToPathAttr
= ToAttr
= GetFileAttributesW(pTempTo
);
1185 if (!b_Mask
&& (ToAttr
== INVALID_FILE_ATTRIBUTES
) && (pToFile
))
1187 ToPathAttr
= shfileops_get_parent_attr(pToFile
,pTempTo
);
1190 if (FO_RENAME
== FuncSwitch
)
1192 if (!b_SameRoot
|| b_Mask
/* FO_RENAME works not with Mask */
1193 || !SHFileStrICmpW(pTempFrom
, pTempTo
, pFromFile
, NULL
)
1194 || (SHFileStrICmpW(pTempFrom
, pTempTo
, pFromFile
, HIGH_ADR
) && !b_ToTailSlash
))
1199 if (b_ToInvalidTail
)
1204 if (INVALID_FILE_ATTRIBUTES
== ToPathAttr
)
1209 if (IsAttribDir(wfd
.dwFileAttributes
) && IsAttribDir(ToAttr
))
1211 retCode
= (b_ToTailSlash
) ? 0xb7 : 0x7b;
1214 /* we use SHNotifyMoveFile() instead MoveFileW */
1215 if (SHNotifyMoveFileW(pTempFrom
, pTempTo
) != ERROR_SUCCESS
)
1217 /* we need still the value for the returncode, we use the mostly assumed */
1224 /* W98 Bug with FO_MOVE different from FO_COPY, better the same as FO_COPY */
1225 b_ToValid
= ((b_SameTailName
&& b_SameRoot
&& (FO_COPY
== FuncSwitch
)) ||
1226 (b_SameTailName
&& !b_SameRoot
) || (b_ToInvalidTail
));
1228 /* handle mask in source */
1231 if (!IsAttribDir(ToAttr
))
1233 retCode
= (b_ToInvalidTail
&&/* b_SameTailName &&*/ (FO_MOVE
== FuncSwitch
)) \
1237 pToFile
= SHFileStrCpyCatW(pTempTo
, NULL
, wBackslash
);
1238 nFileOp
.fFlags
= (nFileOp
.fFlags
| FOF_MULTIDESTFILES
);
1241 retCode
= shfileops_do_operation(wfd
,&nFileOp
,pToFile
,pFromFile
);
1242 } while(!nFileOp
.fAnyOperationsAborted
&& FindNextFileW(hFind
, &wfd
));
1245 hFind
= INVALID_HANDLE_VALUE
;
1246 /* FO_COPY/FO_MOVE with mask, FO_DELETE and FO_RENAME are solved */
1250 /* only FO_COPY/FO_MOVE without mask, all others are (must be) solved */
1251 if (IsAttribDir(wfd
.dwFileAttributes
) && (ToAttr
== INVALID_FILE_ATTRIBUTES
))
1255 ToPathAttr
= shfileops_get_parent_attr2(pToFile
,pTempTo
,b_ToValid
,&retCode
);
1258 if (b_ToInvalidTail
)
1266 /* trailing BackSlash is ever removed and pToFile points to BackSlash before */
1267 if (!b_MultiTo
&& (b_MultiFrom
|| (!(b_Multi
) && IsAttribDir(ToAttr
))))
1269 if ((FO_MOVE
== FuncSwitch
) && IsAttribDir(ToAttr
) && IsAttribDir(wfd
.dwFileAttributes
))
1273 retCode
= 0x73; /* !b_Multi = 0x8 ?? */
1277 pToFile
= SHFileStrCpyCatW(pTempTo
, NULL
, wfd
.cFileName
);
1278 ToAttr
= GetFileAttributesW(pTempTo
);
1281 if (IsAttribDir(ToAttr
))
1283 if (IsAttribFile(wfd
.dwFileAttributes
))
1285 retCode
= (FO_COPY
== FuncSwitch
) ? 0x75 : 0xb7;
1291 ToPathAttr
= shfileops_get_parent_attr(pToFile
,pTempTo
);
1292 if (IsAttribFile(ToPathAttr
))
1294 /* error, is this tested ? */
1300 /* singlesource + no mask */
1301 if (INVALID_FILE_ATTRIBUTES
== (ToAttr
& ToPathAttr
))
1303 /* Target-dir does not exist, and cannot be created */
1312 if ((ToAttr
== INVALID_FILE_ATTRIBUTES
) && SHFileStrICmpW(pTempFrom
, pTempTo
, pFromFile
, NULL
))
1314 nFileOp
.wFunc
= ((level
+1)<<4) + FO_RENAME
;
1318 if (b_SameRoot
&& IsAttribDir(ToAttr
) && IsAttribDir(wfd
.dwFileAttributes
))
1320 /* we need pToFile for FO_DELETE after FO_MOVE contence */
1321 pToFile
= SHFileStrCpyCatW(pTempFrom
, NULL
, wWildcardFile
);
1325 nFileOp
.wFunc
= ((level
+1)<<4) + FO_COPY
;
1328 retCode
= SHFileOperationW(&nFileOp
);
1330 ((DWORD
*)pToFile
)[0] = '\0';
1331 if (!nFileOp
.fAnyOperationsAborted
&& (FO_RENAME
!= (nFileOp
.wFunc
& 0xf)))
1333 nFileOp
.wFunc
= ((level
+1)<<4) + FO_DELETE
;
1334 retCode
= SHFileOperationW(&nFileOp
);
1338 if (SHFileStrICmpW(pTempFrom
, pTempTo
, NULL
, NULL
))
1339 { /* target is the same as source ? */
1340 /* we still need the value for the returncode, we assume 0x71 */
1344 if (IsAttribDir((ToAttr
& wfd
.dwFileAttributes
)))
1346 if (IsAttribDir(ToAttr
) || !SHNotifyCreateDirectoryW(pTempTo
, NULL
))
1348 /* ??? nFileOp.fFlags = (nFileOp.fFlags | FOF_MULTIDESTFILES); */
1349 SHFileStrCpyCatW(pTempFrom
, NULL
, wWildcardFile
);
1350 retCode
= SHFileOperationW(&nFileOp
);
1354 retCode
= 0x750;/* value unknown */
1360 if (!(ask_overwrite
&& SHELL_ConfirmDialogW(ASK_OVERWRITE_FILE
, pTempTo
))
1363 /* we still need the value for the returncode, we use the mostly assumed */
1367 if (SHNotifyCopyFileW(pTempFrom
, pTempTo
, TRUE
) != ERROR_SUCCESS
)
1369 retCode
= 0x77; /* value unknown */
1377 if (hFind
!= INVALID_HANDLE_VALUE
)
1379 hFind
= INVALID_HANDLE_VALUE
;
1380 HeapFree(GetProcessHeap(), 0, pTempFrom
);
1382 nFileOp
.fAnyOperationsAborted
= TRUE
;
1383 TRACE("%s level=%ld AnyOpsAborted=%s ret=0x%x, with %s %s%s\n",
1384 debug_shfileops_action(FuncSwitch
), level
,
1385 nFileOp
.fAnyOperationsAborted
? "TRUE":"FALSE",
1386 retCode
, debugstr_w(pFrom
), pTo
? "-> ":"", debugstr_w(pTo
));
1388 lpFileOp
->fAnyOperationsAborted
= nFileOp
.fAnyOperationsAborted
;
1392 #define SHDSA_GetItemCount(hdsa) (*(int*)(hdsa))
1394 /*************************************************************************
1395 * SHFreeNameMappings [shell32.246]
1397 * Free the mapping handle returned by SHFileoperation if FOF_WANTSMAPPINGHANDLE
1401 * hNameMapping [I] handle to the name mappings used during renaming of files
1406 void WINAPI
SHFreeNameMappings(HANDLE hNameMapping
)
1410 int i
= SHDSA_GetItemCount((HDSA
)hNameMapping
) - 1;
1414 LPSHNAMEMAPPINGW lp
= DSA_GetItemPtr((HDSA
)hNameMapping
, i
);
1416 SHFree(lp
->pszOldPath
);
1417 SHFree(lp
->pszNewPath
);
1419 DSA_Destroy((HDSA
)hNameMapping
);
1423 /*************************************************************************
1424 * SheGetDirA [SHELL32.@]
1427 HRESULT WINAPI
SheGetDirA(LPSTR u
, LPSTR v
)
1428 { FIXME("%p %p stub\n",u
,v
);
1432 /*************************************************************************
1433 * SheGetDirW [SHELL32.@]
1436 HRESULT WINAPI
SheGetDirW(LPWSTR u
, LPWSTR v
)
1437 { FIXME("%p %p stub\n",u
,v
);
1441 /*************************************************************************
1442 * SheChangeDirA [SHELL32.@]
1445 HRESULT WINAPI
SheChangeDirA(LPSTR u
)
1446 { FIXME("(%s),stub\n",debugstr_a(u
));
1450 /*************************************************************************
1451 * SheChangeDirW [SHELL32.@]
1454 HRESULT WINAPI
SheChangeDirW(LPWSTR u
)
1455 { FIXME("(%s),stub\n",debugstr_w(u
));
1459 /*************************************************************************
1460 * IsNetDrive [SHELL32.66]
1462 BOOL WINAPI
IsNetDrive(DWORD drive
)
1465 strcpy(root
, "A:\\");
1466 root
[0] += (char)drive
;
1467 return (GetDriveTypeA(root
) == DRIVE_REMOTE
);
1471 /*************************************************************************
1472 * RealDriveType [SHELL32.524]
1474 INT WINAPI
RealDriveType(INT drive
, BOOL bQueryNet
)
1476 char root
[] = "A:\\";
1477 root
[0] += (char)drive
;
1478 return GetDriveTypeA(root
);