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 int WINAPI
SHCreateDirectoryExW(HWND hWnd
, LPCWSTR path
, LPSECURITY_ATTRIBUTES sec
)
566 int ret
= ERROR_BAD_PATHNAME
;
567 TRACE("(%p, %s, %p)\n", hWnd
, debugstr_w(path
), sec
);
569 if (PathIsRelativeW(path
))
575 ret
= SHNotifyCreateDirectoryW(path
, sec
);
576 /* Refuse to work on certain error codes before trying to create directories recursively */
577 if (ret
!= ERROR_FILE_EXISTS
&&
578 ret
!= ERROR_ALREADY_EXISTS
&&
579 ret
!= ERROR_FILENAME_EXCED_RANGE
)
581 WCHAR
*pEnd
, *pSlash
, szTemp
[MAX_PATH
+ 1]; /* extra for PathAddBackslash() */
583 lstrcpynW(szTemp
, path
, MAX_PATH
);
584 pEnd
= PathAddBackslashW(szTemp
);
589 while (*pSlash
&& *pSlash
!= '\\')
590 pSlash
= CharNextW(pSlash
);
594 *pSlash
= 0; /* terminate path at separator */
596 ret
= SHNotifyCreateDirectoryW(szTemp
, pSlash
+ 1 == pEnd
? sec
: NULL
);
598 *pSlash
++ = '\\'; /* put the separator back */
602 if (ret
&& hWnd
&& (ERROR_CANCELLED
!= ret
))
604 /* We failed and should show a dialog box */
605 FIXME("Show system error message, creating path %s, failed with error %d\n", debugstr_w(path
), ret
);
606 ret
= ERROR_CANCELLED
; /* Error has been already presented to user (not really yet!) */
613 /*************************************************************************
614 * SHFindAttrW [internal]
616 * Get the Attributes for a file or directory. The difference to GetAttributes()
617 * is that this function will also work for paths containing wildcard characters
621 * path [I] path of directory or file to check
622 * fileOnly [I] TRUE if only files should be found
625 * INVALID_FILE_ATTRIBUTES if the path does not exist, the actual attributes of
626 * the first file or directory found otherwise
628 static DWORD
SHFindAttrW(LPCWSTR pName
, BOOL fileOnly
)
630 WIN32_FIND_DATAW wfd
;
631 BOOL b_FileMask
= fileOnly
&& (NULL
!= StrPBrkW(pName
, wWildcardChars
));
632 DWORD dwAttr
= INVALID_FILE_ATTRIBUTES
;
633 HANDLE hFind
= FindFirstFileW(pName
, &wfd
);
635 TRACE("%s %d\n", debugstr_w(pName
), fileOnly
);
636 if (INVALID_HANDLE_VALUE
!= hFind
)
640 if (b_FileMask
&& IsAttribDir(wfd
.dwFileAttributes
))
642 dwAttr
= wfd
.dwFileAttributes
;
645 while (FindNextFileW(hFind
, &wfd
));
651 /*************************************************************************
653 * SHFileStrICmp HelperFunction for SHFileOperationW
656 BOOL
SHFileStrICmpW(LPWSTR p1
, LPWSTR p2
, LPWSTR p1End
, LPWSTR p2End
)
661 int i_len1
= lstrlenW(p1
);
662 int i_len2
= lstrlenW(p2
);
664 if (p1End
&& (&p1
[i_len1
] >= p1End
) && ('\\' == p1End
[0]))
668 i_len1
= lstrlenW(p1
);
672 if ((&p2
[i_len2
] >= p2End
) && ('\\' == p2End
[0]))
681 if ((i_len1
<= i_len2
) && ('\\' == p2
[i_len1
]))
688 i_len2
= lstrlenW(p2
);
689 if (i_len1
== i_len2
)
690 i_Temp
= lstrcmpiW(p1
,p2
);
698 /*************************************************************************
700 * SHFileStrCpyCat HelperFunction for SHFileOperationW
703 LPWSTR
SHFileStrCpyCatW(LPWSTR pTo
, LPCWSTR pFrom
, LPCWSTR pCatStr
)
705 LPWSTR pToFile
= NULL
;
710 lstrcpyW(pTo
, pFrom
);
713 i_len
= lstrlenW(pTo
);
714 if ((i_len
) && (pTo
[--i_len
] != '\\'))
717 if (pCatStr
[0] == '\\')
719 lstrcpyW(&pTo
[i_len
+1], pCatStr
);
721 pToFile
= StrRChrW(pTo
,NULL
,'\\');
724 /* termination of the new string-group */
725 pTo
[(lstrlenW(pTo
)) + 1] = '\0';
730 /**************************************************************************
731 * SHELL_FileNamesMatch()
733 * Accepts two \0 delimited lists of the file names. Checks whether number of
734 * files in both lists is the same, and checks also if source-name exists.
736 BOOL
SHELL_FileNamesMatch(LPCWSTR pszFiles1
, LPCWSTR pszFiles2
, BOOL bOnlySrc
)
738 while ((pszFiles1
[0] != '\0') &&
739 (bOnlySrc
|| (pszFiles2
[0] != '\0')))
741 if (NULL
== StrPBrkW(pszFiles1
, wWildcardChars
))
743 if (INVALID_FILE_ATTRIBUTES
== GetFileAttributesW(pszFiles1
))
746 pszFiles1
+= lstrlenW(pszFiles1
) + 1;
748 pszFiles2
+= lstrlenW(pszFiles2
) + 1;
750 return ((pszFiles1
[0] == '\0') && (bOnlySrc
|| (pszFiles2
[0] == '\0')));
753 /*************************************************************************
755 * SHNameTranslate HelperFunction for SHFileOperationA
757 * Translates a list of 0 terminated ASCII strings into Unicode. If *wString
758 * is NULL, only the necessary size of the string is determined and returned,
759 * otherwise the ASCII strings are copied into it and the buffer is increased
760 * to point to the location after the final 0 termination char.
762 DWORD
SHNameTranslate(LPWSTR
* wString
, LPCWSTR
* pWToFrom
, BOOL more
)
764 DWORD size
= 0, aSize
= 0;
765 LPCSTR aString
= (LPCSTR
)*pWToFrom
;
771 size
= lstrlenA(aString
) + 1;
774 } while ((size
!= 1) && more
);
775 /* The two sizes might be different in the case of multibyte chars */
776 size
= MultiByteToWideChar(CP_ACP
, 0, aString
, aSize
, *wString
, 0);
777 if (*wString
) /* only in the second loop */
779 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)*pWToFrom
, aSize
, *wString
, size
);
780 *pWToFrom
= *wString
;
786 /*************************************************************************
787 * SHFileOperationA [SHELL32.@]
789 * Function to copy, move, delete and create one or more files with optional
793 * lpFileOp [I/O] pointer to a structure containing all the necessary information
798 int WINAPI
SHFileOperationA(LPSHFILEOPSTRUCTA lpFileOp
)
800 SHFILEOPSTRUCTW nFileOp
= *((LPSHFILEOPSTRUCTW
)lpFileOp
);
803 LPWSTR ForFree
= NULL
, /* we change wString in SHNameTranslate and can't use it for freeing */
804 wString
= NULL
; /* we change this in SHNameTranslate */
807 if (FO_DELETE
== (nFileOp
.wFunc
& FO_MASK
))
808 nFileOp
.pTo
= NULL
; /* we need a NULL or a valid pointer for translation */
809 if (!(nFileOp
.fFlags
& FOF_SIMPLEPROGRESS
))
810 nFileOp
.lpszProgressTitle
= NULL
; /* we need a NULL or a valid pointer for translation */
811 while (1) /* every loop calculate size, second translate also, if we have storage for this */
813 size
= SHNameTranslate(&wString
, &nFileOp
.lpszProgressTitle
, FALSE
); /* no loop */
814 size
+= SHNameTranslate(&wString
, &nFileOp
.pFrom
, TRUE
); /* internal loop */
815 size
+= SHNameTranslate(&wString
, &nFileOp
.pTo
, TRUE
); /* internal loop */
819 retCode
= SHFileOperationW(&nFileOp
);
820 HeapFree(GetProcessHeap(), 0, ForFree
); /* we cannot use wString, it was changed */
825 wString
= ForFree
= HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
));
826 if (ForFree
) continue;
827 retCode
= ERROR_OUTOFMEMORY
;
828 nFileOp
.fAnyOperationsAborted
= TRUE
;
829 SetLastError(retCode
);
834 lpFileOp
->hNameMappings
= nFileOp
.hNameMappings
;
835 lpFileOp
->fAnyOperationsAborted
= nFileOp
.fAnyOperationsAborted
;
839 static const char * debug_shfileops_flags( DWORD fFlags
)
841 return wine_dbg_sprintf( "%s%s%s%s%s%s%s%s%s%s%s%s%s",
842 fFlags
& FOF_MULTIDESTFILES
? "FOF_MULTIDESTFILES " : "",
843 fFlags
& FOF_CONFIRMMOUSE
? "FOF_CONFIRMMOUSE " : "",
844 fFlags
& FOF_SILENT
? "FOF_SILENT " : "",
845 fFlags
& FOF_RENAMEONCOLLISION
? "FOF_RENAMEONCOLLISION " : "",
846 fFlags
& FOF_NOCONFIRMATION
? "FOF_NOCONFIRMATION " : "",
847 fFlags
& FOF_WANTMAPPINGHANDLE
? "FOF_WANTMAPPINGHANDLE " : "",
848 fFlags
& FOF_ALLOWUNDO
? "FOF_ALLOWUNDO " : "",
849 fFlags
& FOF_FILESONLY
? "FOF_FILESONLY " : "",
850 fFlags
& FOF_SIMPLEPROGRESS
? "FOF_SIMPLEPROGRESS " : "",
851 fFlags
& FOF_NOCONFIRMMKDIR
? "FOF_NOCONFIRMMKDIR " : "",
852 fFlags
& FOF_NOERRORUI
? "FOF_NOERRORUI " : "",
853 fFlags
& FOF_NOCOPYSECURITYATTRIBS
? "FOF_NOCOPYSECURITYATTRIBS" : "",
854 fFlags
& 0xf000 ? "MORE-UNKNOWN-Flags" : "");
857 static const char * debug_shfileops_action( DWORD op
)
859 LPCSTR cFO_Name
[] = {"FO_????","FO_MOVE","FO_COPY","FO_DELETE","FO_RENAME"};
860 return wine_dbg_sprintf("%s", cFO_Name
[ op
]);
863 #define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026
864 #define HIGH_ADR (LPWSTR)0xffffffff
866 /* handle the complete deletion of `pTempFrom` */
867 static int shfileops_delete(WIN32_FIND_DATAW
*wfd
,SHFILEOPSTRUCTW nFileOp
, LPWSTR pFromFile
,LPWSTR pTempFrom
,HANDLE
*hFind
)
871 BOOL b_Mask
= (NULL
!= StrPBrkW(&pFromFile
[1], wWildcardChars
));
875 lpFileName
= wfd
->cAlternateFileName
;
877 lpFileName
= wfd
->cFileName
;
878 if (IsDotDir(lpFileName
) ||
879 ((b_Mask
) && IsAttribDir(wfd
->dwFileAttributes
) && (nFileOp
.fFlags
& FOF_FILESONLY
)))
881 SHFileStrCpyCatW(&pFromFile
[1], lpFileName
, NULL
);
882 /* TODO: Check the SHELL_DeleteFileOrDirectoryW() function in shell32.dll */
883 if (IsAttribFile(wfd
->dwFileAttributes
))
885 if(SHNotifyDeleteFileW(pTempFrom
) != ERROR_SUCCESS
)
887 nFileOp
.fAnyOperationsAborted
= TRUE
;
888 retCode
= 0x78; /* value unknown */
891 else if(!SHELL_DeleteDirectoryW(pTempFrom
, (!(nFileOp
.fFlags
& FOF_NOCONFIRMATION
))))
893 nFileOp
.fAnyOperationsAborted
= TRUE
;
894 retCode
= 0x79; /* value unknown */
897 while (!nFileOp
.fAnyOperationsAborted
&& FindNextFileW(*hFind
,wfd
));
899 *hFind
= INVALID_HANDLE_VALUE
;
907 * FOF_MULTIDESTFILES, FOF_NOCONFIRMATION, FOF_FILESONLY
909 * unimplememented and ignored flags:
910 * FOF_CONFIRMMOUSE, FOF_SILENT, FOF_NOCONFIRMMKDIR,
911 * FOF_SIMPLEPROGRESS, FOF_NOCOPYSECURITYATTRIBS
913 * partially implemented, breaks if file exists:
914 * FOF_RENAMEONCOLLISION
916 * unimplemented and break if any other flag set:
917 * FOF_ALLOWUNDO, FOF_WANTMAPPINGHANDLE
920 static int shfileops_check_flags(SHFILEOPSTRUCTW nFileOp
)
922 FILEOP_FLAGS OFl
= ((FILEOP_FLAGS
)nFileOp
.fFlags
& 0xfff);
923 long FuncSwitch
= (nFileOp
.wFunc
& FO_MASK
);
924 long level
= nFileOp
.wFunc
>> 4;
926 TRACE("%s level=%ld nFileOp.fFlags=0x%x\n",
927 debug_shfileops_action(FuncSwitch
), level
, nFileOp
.fFlags
);
928 /* OFl &= (-1 - (FOF_MULTIDESTFILES | FOF_FILESONLY)); */
929 /* OFl ^= (FOF_SILENT | FOF_NOCONFIRMATION | FOF_SIMPLEPROGRESS | FOF_NOCONFIRMMKDIR); */
930 OFl
&= (~(FOF_MULTIDESTFILES
| FOF_NOCONFIRMATION
| FOF_FILESONLY
)); /* implemented */
931 OFl
^= (FOF_SILENT
| FOF_NOCONFIRMMKDIR
| FOF_NOERRORUI
| FOF_NOCOPYSECURITYATTRIBS
); /* ignored, if one */
932 OFl
&= (~FOF_SIMPLEPROGRESS
); /* ignored, only with FOF_SILENT */
935 if (OFl
& (~(FOF_CONFIRMMOUSE
| FOF_SILENT
| FOF_RENAMEONCOLLISION
|
936 FOF_NOCONFIRMMKDIR
| FOF_NOERRORUI
| FOF_NOCOPYSECURITYATTRIBS
| FOF_ALLOWUNDO
)))
938 FIXME("%s level=%ld lpFileOp->fFlags=0x%x not implemented, Aborted=TRUE, stub\n",
939 debug_shfileops_action(FuncSwitch
), level
, OFl
);
940 return 0x403; /* 1027, we need an extension to shlfileop */
944 TRACE("%s level=%ld lpFileOp->fFlags=0x%x not fully implemented, stub\n",
945 debug_shfileops_action(FuncSwitch
), level
, OFl
);
951 static int shfileops_do_operation(WIN32_FIND_DATAW wfd
,SHFILEOPSTRUCTW
*nFileOp
, LPWSTR pToFile
, LPWSTR pFromFile
)
953 LPWSTR lpFileName
= wfd
.cAlternateFileName
;
955 lpFileName
= wfd
.cFileName
;
956 if (IsDotDir(lpFileName
) ||
957 (IsAttribDir(wfd
.dwFileAttributes
) && (nFileOp
->fFlags
& FOF_FILESONLY
)))
958 return 0; /* next name in pTempFrom(dir) */
959 SHFileStrCpyCatW(&pToFile
[1], lpFileName
, NULL
);
960 SHFileStrCpyCatW(&pFromFile
[1], lpFileName
, NULL
);
961 return SHFileOperationW (nFileOp
);
964 /* get attributes of the parent dir of pTemp and create the directory if it does not exists */
965 static DWORD
shfileops_get_parent_attr2(LPWSTR pFile
,LPWSTR pTemp
,int flag
,int *retCode
)
969 PathAttr
= GetFileAttributesW(pTemp
);
970 if ((PathAttr
== INVALID_FILE_ATTRIBUTES
) && flag
)
972 /* create dir must be here, sample target D:\y\ *.* create with RC=10003 */
973 if (SHNotifyCreateDirectoryW(pTemp
, NULL
))
975 *retCode
= 0x73;/* value unknown */
976 /*goto shfileop_end;*/
979 PathAttr
= GetFileAttributesW(pTemp
);
985 /* get attributes of the parent dir of pTemp without creating the directory if it does not exists */
986 static DWORD
shfileops_get_parent_attr(LPWSTR pFile
,LPWSTR pTemp
)
989 return shfileops_get_parent_attr2(pFile,pTemp,0,NULL);
993 PathAttr
= GetFileAttributesW(pTemp
);
998 /*************************************************************************
999 * SHFileOperationW [SHELL32.@]
1001 * See SHFileOperationA
1003 int WINAPI
SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp
)
1005 SHFILEOPSTRUCTW nFileOp
= *(lpFileOp
);
1007 LPCWSTR pNextFrom
= nFileOp
.pFrom
;
1008 LPCWSTR pNextTo
= nFileOp
.pTo
;
1009 LPCWSTR pFrom
= pNextFrom
;
1011 HANDLE hFind
= INVALID_HANDLE_VALUE
;
1012 WIN32_FIND_DATAW wfd
;
1013 LPWSTR pTempFrom
= NULL
;
1014 LPWSTR pTempTo
= NULL
;
1016 LPWSTR pToFile
= NULL
;
1021 BOOL b_Multi
= (nFileOp
.fFlags
& FOF_MULTIDESTFILES
);
1023 BOOL b_MultiTo
= (FO_DELETE
!= (lpFileOp
->wFunc
& FO_MASK
));
1024 BOOL b_MultiPaired
= (!b_MultiTo
);
1025 BOOL b_MultiFrom
= FALSE
;
1029 BOOL b_SameTailName
;
1030 BOOL b_ToInvalidTail
= FALSE
;
1031 BOOL b_ToValid
; /* for W98-Bug for FO_MOVE with source and target in same rootdrive */
1033 BOOL b_ToTailSlash
= FALSE
;
1035 long FuncSwitch
= (nFileOp
.wFunc
& FO_MASK
);
1036 long level
= nFileOp
.wFunc
>>4;
1038 /* default no error */
1039 nFileOp
.fAnyOperationsAborted
= FALSE
;
1041 if ((FuncSwitch
< FO_MOVE
) || (FuncSwitch
> FO_RENAME
))
1042 goto shfileop_end
; /* no valid FunctionCode */
1045 TRACE("%s: flags (0x%04x) : %s\n",
1046 debug_shfileops_action(FuncSwitch
), nFileOp
.fFlags
,
1047 debug_shfileops_flags(nFileOp
.fFlags
) );
1049 /* establish when pTo is interpreted as the name of the destination file
1050 * or the directory where the Fromfile should be copied to.
1052 * (1) pTo points to the name of an existing directory;
1053 * (2) the flag FOF_MULTIDESTFILES is present;
1054 * (3) whether pFrom point to multiple filenames.
1058 * destisdir 1 1 1 1 0 0 0 0
1059 * FOF_MULTIDESTFILES 1 1 0 0 1 1 0 0
1060 * multiple from filenames 1 0 1 0 1 0 1 0
1062 * copy files to dir 1 0 1 1 0 0 1 0
1063 * create dir 0 0 0 0 0 0 1 0
1066 retCode
= shfileops_check_flags(nFileOp
);
1070 if ((pNextFrom
) && (!(b_MultiTo
) || (pNextTo
)))
1072 nFileOp
.pFrom
= pTempFrom
= HeapAlloc(GetProcessHeap(), 0, ((1 + 2 * (b_MultiTo
)) * MAX_PATH
+ 6) * sizeof(WCHAR
));
1075 retCode
= ERROR_OUTOFMEMORY
;
1076 SetLastError(retCode
);
1080 pTempTo
= &pTempFrom
[MAX_PATH
+ 4];
1081 nFileOp
.pTo
= pTempTo
;
1082 ask_overwrite
= (!(nFileOp
.fFlags
& FOF_NOCONFIRMATION
) && !(nFileOp
.fFlags
& FOF_RENAMEONCOLLISION
));
1083 not_overwrite
= (!(nFileOp
.fFlags
& FOF_NOCONFIRMATION
) || (nFileOp
.fFlags
& FOF_RENAMEONCOLLISION
));
1087 retCode
= ERROR_SHELL_INTERNAL_FILE_NOT_FOUND
;
1090 /* need break at error before change sourcepointer */
1091 while(!nFileOp
.fAnyOperationsAborted
&& (pNextFrom
[0]))
1093 nFileOp
.wFunc
= ((level
+ 1) << 4) + FuncSwitch
;
1094 nFileOp
.fFlags
= lpFileOp
->fFlags
;
1099 pNextTo
= &pNextTo
[lstrlenW(pTo
)+1];
1100 b_MultiTo
= (b_Multi
&& pNextTo
[0]);
1104 pNextFrom
= &pNextFrom
[lstrlenW(pNextFrom
)+1];
1105 if (!b_MultiFrom
&& !b_MultiTo
)
1106 b_MultiFrom
= (pNextFrom
[0]);
1108 pFromFile
= SHFileStrCpyCatW(pTempFrom
, pFrom
, NULL
);
1112 pToFile
= SHFileStrCpyCatW(pTempTo
, pTo
, NULL
);
1117 SHELL_FileNamesMatch(lpFileOp
->pFrom
, lpFileOp
->pTo
, (!b_Multi
|| b_MultiFrom
));
1119 if (!(b_MultiPaired
) || !(pFromFile
) || !(pFromFile
[1]) || ((pTo
) && !(pToFile
)))
1121 retCode
= ERROR_SHELL_INTERNAL_FILE_NOT_FOUND
;
1126 b_ToTailSlash
= (!pToFile
[1]);
1130 if (StrChrW(pTempTo
,'\\'))
1132 pToFile
= SHFileStrCpyCatW(pTempTo
, NULL
, NULL
);
1135 b_ToInvalidTail
= (NULL
!= StrPBrkW(&pToFile
[1], wWildcardChars
));
1139 b_Mask
= (NULL
!= StrPBrkW(&pFromFile
[1], wWildcardChars
));
1140 if (FO_RENAME
== FuncSwitch
)
1142 /* temporary only for FO_RENAME */
1143 if (b_MultiTo
|| b_MultiFrom
|| (b_Mask
&& !b_ToInvalidTail
))
1145 #ifndef W98_FO_FUNCTION
1146 retCode
= ERROR_GEN_FAILURE
; /* W2K ERROR_GEN_FAILURE, W98 returns no error */
1152 hFind
= FindFirstFileW(pFrom
, &wfd
);
1153 if (INVALID_HANDLE_VALUE
== hFind
)
1155 if ((FO_DELETE
== FuncSwitch
) && (b_Mask
) && IsAttribDir(shfileops_get_parent_attr(pFromFile
,pTempFrom
)))
1157 /* FO_DELETE with mask and without found is valid */
1160 /* root (without mask) is also not allowed as source, tested in W98 */
1161 retCode
= ERROR_SHELL_INTERNAL_FILE_NOT_FOUND
;
1167 /* ??? b_Mask = (!SHFileStrICmpA(&pFromFile[1], &wfd.cFileName[0], HIGH_ADR, HIGH_ADR)); */
1168 if (!pTo
) /* FO_DELETE */
1170 retCode
= shfileops_delete(&wfd
,nFileOp
,pFromFile
,pTempFrom
,&hFind
);
1171 /* if ret is not 0, nFileOp.fAnyOperationsAborted is TRUE and the loop will end */
1173 } /* FO_DELETE ends, pTo must be always valid from here */
1175 b_SameRoot
= (toupperW(pTempFrom
[0]) == toupperW(pTempTo
[0]));
1176 b_SameTailName
= SHFileStrICmpW(pToFile
, pFromFile
, NULL
, NULL
);
1178 ToPathAttr
= ToAttr
= GetFileAttributesW(pTempTo
);
1179 if (!b_Mask
&& (ToAttr
== INVALID_FILE_ATTRIBUTES
) && (pToFile
))
1181 ToPathAttr
= shfileops_get_parent_attr(pToFile
,pTempTo
);
1184 if (FO_RENAME
== FuncSwitch
)
1186 if (!b_SameRoot
|| b_Mask
/* FO_RENAME works not with Mask */
1187 || !SHFileStrICmpW(pTempFrom
, pTempTo
, pFromFile
, NULL
)
1188 || (SHFileStrICmpW(pTempFrom
, pTempTo
, pFromFile
, HIGH_ADR
) && !b_ToTailSlash
))
1193 if (b_ToInvalidTail
)
1198 if (INVALID_FILE_ATTRIBUTES
== ToPathAttr
)
1203 if (IsAttribDir(wfd
.dwFileAttributes
) && IsAttribDir(ToAttr
))
1205 retCode
= (b_ToTailSlash
) ? 0xb7 : 0x7b;
1208 /* we use SHNotifyMoveFile() instead MoveFileW */
1209 if (SHNotifyMoveFileW(pTempFrom
, pTempTo
) != ERROR_SUCCESS
)
1211 /* we need still the value for the returncode, we use the mostly assumed */
1218 /* W98 Bug with FO_MOVE different from FO_COPY, better the same as FO_COPY */
1219 b_ToValid
= ((b_SameTailName
&& b_SameRoot
&& (FO_COPY
== FuncSwitch
)) ||
1220 (b_SameTailName
&& !b_SameRoot
) || (b_ToInvalidTail
));
1222 /* handle mask in source */
1225 if (!IsAttribDir(ToAttr
))
1227 retCode
= (b_ToInvalidTail
&&/* b_SameTailName &&*/ (FO_MOVE
== FuncSwitch
)) \
1231 pToFile
= SHFileStrCpyCatW(pTempTo
, NULL
, wBackslash
);
1232 nFileOp
.fFlags
= (nFileOp
.fFlags
| FOF_MULTIDESTFILES
);
1235 retCode
= shfileops_do_operation(wfd
,&nFileOp
,pToFile
,pFromFile
);
1236 } while(!nFileOp
.fAnyOperationsAborted
&& FindNextFileW(hFind
, &wfd
));
1239 hFind
= INVALID_HANDLE_VALUE
;
1240 /* FO_COPY/FO_MOVE with mask, FO_DELETE and FO_RENAME are solved */
1244 /* only FO_COPY/FO_MOVE without mask, all others are (must be) solved */
1245 if (IsAttribDir(wfd
.dwFileAttributes
) && (ToAttr
== INVALID_FILE_ATTRIBUTES
))
1249 ToPathAttr
= shfileops_get_parent_attr2(pToFile
,pTempTo
,b_ToValid
,&retCode
);
1252 if (b_ToInvalidTail
)
1260 /* trailing BackSlash is ever removed and pToFile points to BackSlash before */
1261 if (!b_MultiTo
&& (b_MultiFrom
|| (!(b_Multi
) && IsAttribDir(ToAttr
))))
1263 if ((FO_MOVE
== FuncSwitch
) && IsAttribDir(ToAttr
) && IsAttribDir(wfd
.dwFileAttributes
))
1267 retCode
= 0x73; /* !b_Multi = 0x8 ?? */
1271 pToFile
= SHFileStrCpyCatW(pTempTo
, NULL
, wfd
.cFileName
);
1272 ToAttr
= GetFileAttributesW(pTempTo
);
1275 if (IsAttribDir(ToAttr
))
1277 if (IsAttribFile(wfd
.dwFileAttributes
))
1279 retCode
= (FO_COPY
== FuncSwitch
) ? 0x75 : 0xb7;
1285 ToPathAttr
= shfileops_get_parent_attr(pToFile
,pTempTo
);
1286 if (IsAttribFile(ToPathAttr
))
1288 /* error, is this tested ? */
1294 /* singlesource + no mask */
1295 if (INVALID_FILE_ATTRIBUTES
== (ToAttr
& ToPathAttr
))
1297 /* Target-dir does not exist, and cannot be created */
1306 if ((ToAttr
== INVALID_FILE_ATTRIBUTES
) && SHFileStrICmpW(pTempFrom
, pTempTo
, pFromFile
, NULL
))
1308 nFileOp
.wFunc
= ((level
+1)<<4) + FO_RENAME
;
1312 if (b_SameRoot
&& IsAttribDir(ToAttr
) && IsAttribDir(wfd
.dwFileAttributes
))
1314 /* we need pToFile for FO_DELETE after FO_MOVE contence */
1315 pToFile
= SHFileStrCpyCatW(pTempFrom
, NULL
, wWildcardFile
);
1319 nFileOp
.wFunc
= ((level
+1)<<4) + FO_COPY
;
1322 retCode
= SHFileOperationW(&nFileOp
);
1324 ((DWORD
*)pToFile
)[0] = '\0';
1325 if (!nFileOp
.fAnyOperationsAborted
&& (FO_RENAME
!= (nFileOp
.wFunc
& 0xf)))
1327 nFileOp
.wFunc
= ((level
+1)<<4) + FO_DELETE
;
1328 retCode
= SHFileOperationW(&nFileOp
);
1332 if (SHFileStrICmpW(pTempFrom
, pTempTo
, NULL
, NULL
))
1333 { /* target is the same as source ? */
1334 /* we still need the value for the returncode, we assume 0x71 */
1338 if (IsAttribDir((ToAttr
& wfd
.dwFileAttributes
)))
1340 if (IsAttribDir(ToAttr
) || !SHNotifyCreateDirectoryW(pTempTo
, NULL
))
1342 /* ??? nFileOp.fFlags = (nFileOp.fFlags | FOF_MULTIDESTFILES); */
1343 SHFileStrCpyCatW(pTempFrom
, NULL
, wWildcardFile
);
1344 retCode
= SHFileOperationW(&nFileOp
);
1348 retCode
= 0x750;/* value unknown */
1354 if (!(ask_overwrite
&& SHELL_ConfirmDialogW(ASK_OVERWRITE_FILE
, pTempTo
))
1357 /* we still need the value for the returncode, we use the mostly assumed */
1361 if (SHNotifyCopyFileW(pTempFrom
, pTempTo
, TRUE
) != ERROR_SUCCESS
)
1363 retCode
= 0x77; /* value unknown */
1371 if (hFind
!= INVALID_HANDLE_VALUE
)
1373 hFind
= INVALID_HANDLE_VALUE
;
1374 HeapFree(GetProcessHeap(), 0, pTempFrom
);
1376 nFileOp
.fAnyOperationsAborted
= TRUE
;
1377 TRACE("%s level=%ld AnyOpsAborted=%s ret=0x%x, with %s %s%s\n",
1378 debug_shfileops_action(FuncSwitch
), level
,
1379 nFileOp
.fAnyOperationsAborted
? "TRUE":"FALSE",
1380 retCode
, debugstr_w(pFrom
), pTo
? "-> ":"", debugstr_w(pTo
));
1382 lpFileOp
->fAnyOperationsAborted
= nFileOp
.fAnyOperationsAborted
;
1386 #define SHDSA_GetItemCount(hdsa) (*(int*)(hdsa))
1388 /*************************************************************************
1389 * SHFreeNameMappings [shell32.246]
1391 * Free the mapping handle returned by SHFileoperation if FOF_WANTSMAPPINGHANDLE
1395 * hNameMapping [I] handle to the name mappings used during renaming of files
1398 void WINAPI
SHFreeNameMappings(HANDLE hNameMapping
)
1402 int i
= SHDSA_GetItemCount((HDSA
)hNameMapping
) - 1;
1406 LPSHNAMEMAPPINGW lp
= DSA_GetItemPtr((HDSA
)hNameMapping
, i
);
1408 SHFree(lp
->pszOldPath
);
1409 SHFree(lp
->pszNewPath
);
1411 DSA_Destroy((HDSA
)hNameMapping
);
1415 /*************************************************************************
1416 * SheGetDirA [SHELL32.@]
1419 HRESULT WINAPI
SheGetDirA(LPSTR u
, LPSTR v
)
1420 { FIXME("%p %p stub\n",u
,v
);
1424 /*************************************************************************
1425 * SheGetDirW [SHELL32.@]
1428 HRESULT WINAPI
SheGetDirW(LPWSTR u
, LPWSTR v
)
1429 { FIXME("%p %p stub\n",u
,v
);
1433 /*************************************************************************
1434 * SheChangeDirA [SHELL32.@]
1437 HRESULT WINAPI
SheChangeDirA(LPSTR u
)
1438 { FIXME("(%s),stub\n",debugstr_a(u
));
1442 /*************************************************************************
1443 * SheChangeDirW [SHELL32.@]
1446 HRESULT WINAPI
SheChangeDirW(LPWSTR u
)
1447 { FIXME("(%s),stub\n",debugstr_w(u
));
1451 /*************************************************************************
1452 * IsNetDrive [SHELL32.66]
1454 BOOL WINAPI
IsNetDrive(DWORD drive
)
1457 strcpy(root
, "A:\\");
1458 root
[0] += (char)drive
;
1459 return (GetDriveTypeA(root
) == DRIVE_REMOTE
);
1463 /*************************************************************************
1464 * RealDriveType [SHELL32.524]
1466 INT WINAPI
RealDriveType(INT drive
, BOOL bQueryNet
)
1468 char root
[] = "A:\\";
1469 root
[0] += (char)drive
;
1470 return GetDriveTypeA(root
);