Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenshlfileop.cpp
Go to the documentation of this file.
00001 /* 00002 * SHFileOperation 00003 * 00004 * Copyright 2000 Juergen Schmied 00005 * Copyright 2002 Andriy Palamarchuk 00006 * Copyright 2004 Dietrich Teickner (from Odin) 00007 * Copyright 2004 Rolf Kalbermatter 00008 * 00009 * This library is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Lesser General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 2.1 of the License, or (at your option) any later version. 00013 * 00014 * This library is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 * Lesser General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU Lesser General Public 00020 * License along with this library; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00022 */ 00023 00024 //#define NO_SHLWAPI_STREAM 00025 #include <precomp.h> 00026 00027 WINE_DEFAULT_DEBUG_CHANNEL(shell); 00028 00029 #define IsAttrib(x, y) ((INVALID_FILE_ATTRIBUTES != (x)) && ((x) & (y))) 00030 #define IsAttribFile(x) (!((x) & FILE_ATTRIBUTE_DIRECTORY)) 00031 #define IsAttribDir(x) IsAttrib(x, FILE_ATTRIBUTE_DIRECTORY) 00032 #define IsDotDir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0)))) 00033 00034 #define FO_MASK 0xF 00035 #define WM_FILE (WM_USER + 1) 00036 #define TIMER_ID (100) 00037 00038 static const WCHAR wWildcardFile[] = {'*',0}; 00039 static const WCHAR wWildcardChars[] = {'*','?',0}; 00040 00041 static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec); 00042 static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path); 00043 static DWORD SHNotifyDeleteFileW(LPCWSTR path); 00044 static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest); 00045 static DWORD SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bFailIfExists); 00046 static DWORD SHFindAttrW(LPCWSTR pName, BOOL fileOnly); 00047 00048 typedef struct 00049 { 00050 SHFILEOPSTRUCTW *req; 00051 DWORD dwYesToAllMask; 00052 BOOL bManyItems; 00053 BOOL bCancelled; 00054 } FILE_OPERATION; 00055 00056 #define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026 00057 00058 typedef struct 00059 { 00060 DWORD attributes; 00061 LPWSTR szDirectory; 00062 LPWSTR szFilename; 00063 LPWSTR szFullPath; 00064 BOOL bFromWildcard; 00065 BOOL bFromRelative; 00066 BOOL bExists; 00067 } FILE_ENTRY; 00068 00069 typedef struct 00070 { 00071 FILE_ENTRY *feFiles; 00072 DWORD num_alloc; 00073 DWORD dwNumFiles; 00074 BOOL bAnyFromWildcard; 00075 BOOL bAnyDirectories; 00076 BOOL bAnyDontExist; 00077 } FILE_LIST; 00078 00079 typedef struct 00080 { 00081 FILE_LIST * from; 00082 FILE_LIST * to; 00083 FILE_OPERATION * op; 00084 DWORD Index; 00085 HWND hDlgCtrl; 00086 HWND hwndDlg; 00087 }FILE_OPERATION_CONTEXT; 00088 00089 00090 /* Confirm dialogs with an optional "Yes To All" as used in file operations confirmations 00091 */ 00092 static const WCHAR CONFIRM_MSG_PROP[] = {'W','I','N','E','_','C','O','N','F','I','R','M',0}; 00093 00094 struct confirm_msg_info 00095 { 00096 LPWSTR lpszText; 00097 LPWSTR lpszCaption; 00098 HICON hIcon; 00099 BOOL bYesToAll; 00100 }; 00101 00102 /* as some buttons may be hidden and the dialog height may change we may need 00103 * to move the controls */ 00104 static void confirm_msg_move_button(HWND hDlg, INT iId, INT *xPos, INT yOffset, BOOL bShow) 00105 { 00106 HWND hButton = GetDlgItem(hDlg, iId); 00107 RECT r; 00108 00109 if (bShow) 00110 { 00111 POINT pt; 00112 int width; 00113 00114 GetWindowRect(hButton, &r); 00115 width = r.right - r.left; 00116 pt.x = r.left; 00117 pt.y = r.top; 00118 ScreenToClient(hDlg, &pt); 00119 MoveWindow(hButton, *xPos - width, pt.y - yOffset, width, r.bottom - r.top, FALSE); 00120 *xPos -= width + 5; 00121 } 00122 else 00123 ShowWindow(hButton, SW_HIDE); 00124 } 00125 00126 /* Note: we paint the text manually and don't use the static control to make 00127 * sure the text has the same height as the one computed in WM_INITDIALOG 00128 */ 00129 static INT_PTR ConfirmMsgBox_Paint(HWND hDlg) 00130 { 00131 PAINTSTRUCT ps; 00132 HFONT hOldFont; 00133 RECT r; 00134 HDC hdc; 00135 00136 BeginPaint(hDlg, &ps); 00137 hdc = ps.hdc; 00138 00139 GetClientRect(GetDlgItem(hDlg, IDC_YESTOALL_MESSAGE), &r); 00140 /* this will remap the rect to dialog coords */ 00141 MapWindowPoints(GetDlgItem(hDlg, IDC_YESTOALL_MESSAGE), hDlg, (LPPOINT)&r, 2); 00142 hOldFont = (HFONT)SelectObject(hdc, (HFONT)SendDlgItemMessageW(hDlg, IDC_YESTOALL_MESSAGE, WM_GETFONT, 0, 0)); 00143 DrawTextW(hdc, (LPWSTR)GetPropW(hDlg, CONFIRM_MSG_PROP), -1, &r, DT_NOPREFIX | DT_PATH_ELLIPSIS | DT_WORDBREAK); 00144 SelectObject(hdc, hOldFont); 00145 EndPaint(hDlg, &ps); 00146 00147 return TRUE; 00148 } 00149 00150 static INT_PTR ConfirmMsgBox_Init(HWND hDlg, LPARAM lParam) 00151 { 00152 struct confirm_msg_info *info = (struct confirm_msg_info *)lParam; 00153 INT xPos, yOffset; 00154 int width, height; 00155 HFONT hOldFont; 00156 HDC hdc; 00157 RECT r; 00158 00159 SetWindowTextW(hDlg, info->lpszCaption); 00160 ShowWindow(GetDlgItem(hDlg, IDC_YESTOALL_MESSAGE), SW_HIDE); 00161 SetPropW(hDlg, CONFIRM_MSG_PROP, info->lpszText); 00162 SendDlgItemMessageW(hDlg, IDC_YESTOALL_ICON, STM_SETICON, (WPARAM)info->hIcon, 0); 00163 00164 /* compute the text height and resize the dialog */ 00165 GetClientRect(GetDlgItem(hDlg, IDC_YESTOALL_MESSAGE), &r); 00166 hdc = GetDC(hDlg); 00167 yOffset = r.bottom; 00168 hOldFont = (HFONT)SelectObject(hdc, (HFONT)SendDlgItemMessageW(hDlg, IDC_YESTOALL_MESSAGE, WM_GETFONT, 0, 0)); 00169 DrawTextW(hdc, info->lpszText, -1, &r, DT_NOPREFIX | DT_PATH_ELLIPSIS | DT_WORDBREAK | DT_CALCRECT); 00170 SelectObject(hdc, hOldFont); 00171 yOffset -= r.bottom; 00172 yOffset = min(yOffset, 35); /* don't make the dialog too small */ 00173 ReleaseDC(hDlg, hdc); 00174 00175 GetClientRect(hDlg, &r); 00176 xPos = r.right - 7; 00177 GetWindowRect(hDlg, &r); 00178 width = r.right - r.left; 00179 height = r.bottom - r.top - yOffset; 00180 MoveWindow(hDlg, (GetSystemMetrics(SM_CXSCREEN) - width)/2, 00181 (GetSystemMetrics(SM_CYSCREEN) - height)/2, width, height, FALSE); 00182 00183 confirm_msg_move_button(hDlg, IDCANCEL, &xPos, yOffset, info->bYesToAll); 00184 confirm_msg_move_button(hDlg, IDNO, &xPos, yOffset, TRUE); 00185 confirm_msg_move_button(hDlg, IDC_YESTOALL, &xPos, yOffset, info->bYesToAll); 00186 confirm_msg_move_button(hDlg, IDYES, &xPos, yOffset, TRUE); 00187 00188 return TRUE; 00189 } 00190 00191 static INT_PTR CALLBACK ConfirmMsgBoxProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 00192 { 00193 switch (uMsg) 00194 { 00195 case WM_INITDIALOG: 00196 return ConfirmMsgBox_Init(hDlg, lParam); 00197 case WM_PAINT: 00198 return ConfirmMsgBox_Paint(hDlg); 00199 case WM_COMMAND: 00200 EndDialog(hDlg, wParam); 00201 break; 00202 case WM_CLOSE: 00203 EndDialog(hDlg, IDCANCEL); 00204 break; 00205 } 00206 return FALSE; 00207 } 00208 00209 int SHELL_ConfirmMsgBox(HWND hWnd, LPWSTR lpszText, LPWSTR lpszCaption, HICON hIcon, BOOL bYesToAll) 00210 { 00211 struct confirm_msg_info info; 00212 00213 info.lpszText = lpszText; 00214 info.lpszCaption = lpszCaption; 00215 info.hIcon = hIcon; 00216 info.bYesToAll = bYesToAll; 00217 return DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_YESTOALL_MSGBOX), hWnd, ConfirmMsgBoxProc, (LPARAM)&info); 00218 } 00219 00220 /* confirmation dialogs content */ 00221 typedef struct 00222 { 00223 HINSTANCE hIconInstance; 00224 UINT icon_resource_id; 00225 UINT caption_resource_id, text_resource_id; 00226 } SHELL_ConfirmIDstruc; 00227 00228 static BOOL SHELL_ConfirmIDs(int nKindOfDialog, SHELL_ConfirmIDstruc *ids) 00229 { 00230 ids->hIconInstance = shell32_hInstance; 00231 switch (nKindOfDialog) 00232 { 00233 case ASK_DELETE_FILE: 00234 ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE; 00235 ids->caption_resource_id = IDS_DELETEITEM_CAPTION; 00236 ids->text_resource_id = IDS_DELETEITEM_TEXT; 00237 return TRUE; 00238 00239 case ASK_DELETE_FOLDER: 00240 ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE; 00241 ids->caption_resource_id = IDS_DELETEFOLDER_CAPTION; 00242 ids->text_resource_id = IDS_DELETEITEM_TEXT; 00243 return TRUE; 00244 00245 case ASK_DELETE_MULTIPLE_ITEM: 00246 ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE; 00247 ids->caption_resource_id = IDS_DELETEITEM_CAPTION; 00248 ids->text_resource_id = IDS_DELETEMULTIPLE_TEXT; 00249 return TRUE; 00250 00251 case ASK_TRASH_FILE: 00252 ids->icon_resource_id = IDI_SHELL_TRASH_FILE; 00253 ids->caption_resource_id = IDS_DELETEITEM_CAPTION; 00254 ids->text_resource_id = IDS_TRASHITEM_TEXT; 00255 return TRUE; 00256 00257 case ASK_TRASH_FOLDER: 00258 ids->icon_resource_id = IDI_SHELL_TRASH_FILE; 00259 ids->caption_resource_id = IDS_DELETEFOLDER_CAPTION; 00260 ids->text_resource_id = IDS_TRASHFOLDER_TEXT; 00261 return TRUE; 00262 00263 case ASK_TRASH_MULTIPLE_ITEM: 00264 ids->icon_resource_id = IDI_SHELL_TRASH_FILE; 00265 ids->caption_resource_id = IDS_DELETEITEM_CAPTION; 00266 ids->text_resource_id = IDS_TRASHMULTIPLE_TEXT; 00267 return TRUE; 00268 00269 case ASK_CANT_TRASH_ITEM: 00270 ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE; 00271 ids->caption_resource_id = IDS_DELETEITEM_CAPTION; 00272 ids->text_resource_id = IDS_CANTTRASH_TEXT; 00273 return TRUE; 00274 00275 case ASK_DELETE_SELECTED: 00276 ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE; 00277 ids->caption_resource_id = IDS_DELETEITEM_CAPTION; 00278 ids->text_resource_id = IDS_DELETESELECTED_TEXT; 00279 return TRUE; 00280 00281 case ASK_OVERWRITE_FILE: 00282 ids->hIconInstance = NULL; 00283 ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE; 00284 ids->caption_resource_id = IDS_OVERWRITEFILE_CAPTION; 00285 ids->text_resource_id = IDS_OVERWRITEFILE_TEXT; 00286 return TRUE; 00287 00288 case ASK_OVERWRITE_FOLDER: 00289 ids->hIconInstance = NULL; 00290 ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE; 00291 ids->caption_resource_id = IDS_OVERWRITEFILE_CAPTION; 00292 ids->text_resource_id = IDS_OVERWRITEFOLDER_TEXT; 00293 return TRUE; 00294 00295 default: 00296 FIXME(" Unhandled nKindOfDialog %d stub\n", nKindOfDialog); 00297 } 00298 return FALSE; 00299 } 00300 00301 static BOOL SHELL_ConfirmDialogW(HWND hWnd, int nKindOfDialog, LPCWSTR szDir, FILE_OPERATION *op) 00302 { 00303 WCHAR szCaption[255], szText[255], szBuffer[MAX_PATH + 256]; 00304 SHELL_ConfirmIDstruc ids; 00305 DWORD_PTR args[1]; 00306 HICON hIcon; 00307 int ret; 00308 00309 assert(nKindOfDialog >= 0 && nKindOfDialog < 32); 00310 if (op && (op->dwYesToAllMask & (1 << nKindOfDialog))) 00311 return TRUE; 00312 00313 if (!SHELL_ConfirmIDs(nKindOfDialog, &ids)) return FALSE; 00314 00315 LoadStringW(shell32_hInstance, ids.caption_resource_id, szCaption, sizeof(szCaption)/sizeof(WCHAR)); 00316 LoadStringW(shell32_hInstance, ids.text_resource_id, szText, sizeof(szText)/sizeof(WCHAR)); 00317 00318 args[0] = (DWORD_PTR)szDir; 00319 FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY, 00320 szText, 0, 0, szBuffer, sizeof(szBuffer), (va_list*)args); 00321 hIcon = LoadIconW(ids.hIconInstance, (LPWSTR)MAKEINTRESOURCE(ids.icon_resource_id)); 00322 00323 ret = SHELL_ConfirmMsgBox(hWnd, szBuffer, szCaption, hIcon, op && op->bManyItems); 00324 if (op) 00325 { 00326 if (ret == IDC_YESTOALL) 00327 { 00328 op->dwYesToAllMask |= (1 << nKindOfDialog); 00329 ret = IDYES; 00330 } 00331 if (ret == IDCANCEL) 00332 op->bCancelled = TRUE; 00333 if (ret != IDYES) 00334 op->req->fAnyOperationsAborted = TRUE; 00335 } 00336 return ret == IDYES; 00337 } 00338 00339 BOOL SHELL_ConfirmYesNoW(HWND hWnd, int nKindOfDialog, LPCWSTR szDir) 00340 { 00341 return SHELL_ConfirmDialogW(hWnd, nKindOfDialog, szDir, NULL); 00342 } 00343 00344 static DWORD SHELL32_AnsiToUnicodeBuf(LPCSTR aPath, LPWSTR *wPath, DWORD minChars) 00345 { 00346 DWORD len = MultiByteToWideChar(CP_ACP, 0, aPath, -1, NULL, 0); 00347 00348 if (len < minChars) 00349 len = minChars; 00350 00351 *wPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 00352 if (*wPath) 00353 { 00354 MultiByteToWideChar(CP_ACP, 0, aPath, -1, *wPath, len); 00355 return NO_ERROR; 00356 } 00357 return E_OUTOFMEMORY; 00358 } 00359 00360 static void SHELL32_FreeUnicodeBuf(LPWSTR wPath) 00361 { 00362 HeapFree(GetProcessHeap(), 0, wPath); 00363 } 00364 00365 EXTERN_C HRESULT WINAPI SHIsFileAvailableOffline(LPCWSTR path, LPDWORD status) 00366 { 00367 FIXME("(%s, %p) stub\n", debugstr_w(path), status); 00368 return E_FAIL; 00369 } 00370 00371 /************************************************************************** 00372 * SHELL_DeleteDirectory() [internal] 00373 * 00374 * Asks for confirmation when bShowUI is true and deletes the directory and 00375 * all its subdirectories and files if necessary. 00376 */ 00377 BOOL SHELL_DeleteDirectoryW(HWND hwnd, LPCWSTR pszDir, BOOL bShowUI) 00378 { 00379 BOOL ret = TRUE; 00380 HANDLE hFind; 00381 WIN32_FIND_DATAW wfd; 00382 WCHAR szTemp[MAX_PATH]; 00383 00384 /* Make sure the directory exists before eventually prompting the user */ 00385 PathCombineW(szTemp, pszDir, wWildcardFile); 00386 hFind = FindFirstFileW(szTemp, &wfd); 00387 if (hFind == INVALID_HANDLE_VALUE) 00388 return FALSE; 00389 00390 if (!bShowUI || (ret = SHELL_ConfirmDialogW(hwnd, ASK_DELETE_FOLDER, pszDir, NULL))) 00391 { 00392 do 00393 { 00394 if (IsDotDir(wfd.cFileName)) 00395 continue; 00396 PathCombineW(szTemp, pszDir, wfd.cFileName); 00397 if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes) 00398 ret = SHELL_DeleteDirectoryW(hwnd, szTemp, FALSE); 00399 else 00400 ret = (SHNotifyDeleteFileW(szTemp) == ERROR_SUCCESS); 00401 } while (ret && FindNextFileW(hFind, &wfd)); 00402 } 00403 FindClose(hFind); 00404 if (ret) 00405 ret = (SHNotifyRemoveDirectoryW(pszDir) == ERROR_SUCCESS); 00406 return ret; 00407 } 00408 00409 /************************************************************************** 00410 * Win32CreateDirectory [SHELL32.93] 00411 * 00412 * Creates a directory. Also triggers a change notify if one exists. 00413 * 00414 * PARAMS 00415 * path [I] path to directory to create 00416 * 00417 * RETURNS 00418 * TRUE if successful, FALSE otherwise 00419 */ 00420 00421 static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec) 00422 { 00423 TRACE("(%s, %p)\n", debugstr_w(path), sec); 00424 00425 if (CreateDirectoryW(path, sec)) 00426 { 00427 SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW, path, NULL); 00428 return ERROR_SUCCESS; 00429 } 00430 return GetLastError(); 00431 } 00432 00433 /**********************************************************************/ 00434 00435 EXTERN_C BOOL WINAPI Win32CreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec) 00436 { 00437 return (SHNotifyCreateDirectoryW(path, sec) == ERROR_SUCCESS); 00438 } 00439 00440 /************************************************************************ 00441 * Win32RemoveDirectory [SHELL32.94] 00442 * 00443 * Deletes a directory. Also triggers a change notify if one exists. 00444 * 00445 * PARAMS 00446 * path [I] path to directory to delete 00447 * 00448 * RETURNS 00449 * TRUE if successful, FALSE otherwise 00450 */ 00451 static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path) 00452 { 00453 BOOL ret; 00454 TRACE("(%s)\n", debugstr_w(path)); 00455 00456 ret = RemoveDirectoryW(path); 00457 if (!ret) 00458 { 00459 /* Directory may be write protected */ 00460 DWORD dwAttr = GetFileAttributesW(path); 00461 if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY)) 00462 if (SetFileAttributesW(path, dwAttr & ~FILE_ATTRIBUTE_READONLY)) 00463 ret = RemoveDirectoryW(path); 00464 } 00465 if (ret) 00466 { 00467 SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHW, path, NULL); 00468 return ERROR_SUCCESS; 00469 } 00470 return GetLastError(); 00471 } 00472 00473 /***********************************************************************/ 00474 00475 EXTERN_C BOOL WINAPI Win32RemoveDirectoryW(LPCWSTR path) 00476 { 00477 return (SHNotifyRemoveDirectoryW(path) == ERROR_SUCCESS); 00478 } 00479 00480 /************************************************************************ 00481 * Win32DeleteFile [SHELL32.164] 00482 * 00483 * Deletes a file. Also triggers a change notify if one exists. 00484 * 00485 * PARAMS 00486 * path [I] path to file to delete 00487 * 00488 * RETURNS 00489 * TRUE if successful, FALSE otherwise 00490 */ 00491 static DWORD SHNotifyDeleteFileW(LPCWSTR path) 00492 { 00493 BOOL ret; 00494 00495 TRACE("(%s)\n", debugstr_w(path)); 00496 00497 ret = DeleteFileW(path); 00498 if (!ret) 00499 { 00500 /* File may be write protected or a system file */ 00501 DWORD dwAttr = GetFileAttributesW(path); 00502 if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)) 00503 if (SetFileAttributesW(path, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))) 00504 ret = DeleteFileW(path); 00505 } 00506 if (ret) 00507 { 00508 SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, path, NULL); 00509 return ERROR_SUCCESS; 00510 } 00511 return GetLastError(); 00512 } 00513 00514 /***********************************************************************/ 00515 00516 EXTERN_C DWORD WINAPI Win32DeleteFileW(LPCWSTR path) 00517 { 00518 return (SHNotifyDeleteFileW(path) == ERROR_SUCCESS); 00519 } 00520 00521 /************************************************************************ 00522 * SHNotifyMoveFile [internal] 00523 * 00524 * Moves a file. Also triggers a change notify if one exists. 00525 * 00526 * PARAMS 00527 * src [I] path to source file to move 00528 * dest [I] path to target file to move to 00529 * 00530 * RETURNS 00531 * ERORR_SUCCESS if successful 00532 */ 00533 static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest) 00534 { 00535 BOOL ret; 00536 00537 TRACE("(%s %s)\n", debugstr_w(src), debugstr_w(dest)); 00538 00539 ret = MoveFileExW(src, dest, MOVEFILE_REPLACE_EXISTING); 00540 00541 /* MOVEFILE_REPLACE_EXISTING fails with dirs, so try MoveFile */ 00542 if (!ret) 00543 ret = MoveFileW(src, dest); 00544 00545 if (!ret) 00546 { 00547 DWORD dwAttr; 00548 00549 dwAttr = SHFindAttrW(dest, FALSE); 00550 if (INVALID_FILE_ATTRIBUTES == dwAttr) 00551 { 00552 /* Source file may be write protected or a system file */ 00553 dwAttr = GetFileAttributesW(src); 00554 if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)) 00555 if (SetFileAttributesW(src, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))) 00556 ret = MoveFileW(src, dest); 00557 } 00558 } 00559 if (ret) 00560 { 00561 SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_PATHW, src, dest); 00562 return ERROR_SUCCESS; 00563 } 00564 return GetLastError(); 00565 } 00566 00567 static DWORD WINAPI SHOperationProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData) 00568 { 00569 FILE_OPERATION_CONTEXT * Context; 00570 LARGE_INTEGER Progress; 00571 00572 /* get context */ 00573 Context = (FILE_OPERATION_CONTEXT*)lpData; 00574 00575 if (TotalBytesTransferred.QuadPart) 00576 { 00577 Progress.QuadPart = (TotalBytesTransferred.QuadPart * 100) / TotalFileSize.QuadPart; 00578 } 00579 else 00580 { 00581 Progress.QuadPart = 1; 00582 } 00583 00584 /* update progress bar */ 00585 SendMessageW(Context->hDlgCtrl, PBM_SETPOS, (WPARAM)Progress.u.LowPart, 0); 00586 00587 if (TotalBytesTransferred.QuadPart == TotalFileSize.QuadPart) 00588 { 00589 /* file was copied */ 00590 Context->Index++; 00591 PostMessageW(Context->hwndDlg, WM_FILE, 0, 0); 00592 } 00593 00594 return PROGRESS_CONTINUE; 00595 } 00596 00597 BOOL 00598 QueueFile( 00599 FILE_OPERATION_CONTEXT * Context) 00600 { 00601 FILE_ENTRY * from, *to = NULL; 00602 BOOL bRet = FALSE; 00603 00604 if (Context->Index >= Context->from->dwNumFiles) 00605 return FALSE; 00606 00607 /* get current file */ 00608 from = &Context->from->feFiles[Context->Index]; 00609 00610 if (Context->op->req->wFunc != FO_DELETE) 00611 to = &Context->to->feFiles[Context->Index]; 00612 00613 /* update status */ 00614 SetDlgItemTextW(Context->hwndDlg, 14001, from->szFullPath); 00615 00616 if (Context->op->req->wFunc == FO_COPY) 00617 { 00618 bRet = CopyFileExW(from->szFullPath, to->szFullPath, SHOperationProgressRoutine, (LPVOID)Context, &Context->op->bCancelled, 0); 00619 } 00620 else if (Context->op->req->wFunc == FO_MOVE) 00621 { 00622 //bRet = MoveFileWithProgressW(from->szFullPath, to->szFullPath, SHOperationProgressRoutine, (LPVOID)Context, MOVEFILE_COPY_ALLOWED); 00623 } 00624 else if (Context->op->req->wFunc == FO_DELETE) 00625 { 00626 bRet = DeleteFile(from->szFullPath); 00627 } 00628 00629 return bRet; 00630 } 00631 00632 static INT_PTR CALLBACK SHOperationDialog(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 00633 { 00634 FILE_OPERATION_CONTEXT * Context; 00635 00636 Context = (FILE_OPERATION_CONTEXT*) GetWindowLongPtr(hwndDlg, DWLP_USER); 00637 00638 switch(uMsg) 00639 { 00640 case WM_INITDIALOG: 00641 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG)lParam); 00642 00643 /* get context */ 00644 Context = (FILE_OPERATION_CONTEXT*)lParam; 00645 00646 /* store progress bar handle */ 00647 Context->hDlgCtrl = GetDlgItem(hwndDlg, 14000); 00648 00649 /* store window handle */ 00650 Context->hwndDlg = hwndDlg; 00651 00652 /* set progress bar range */ 00653 (void)SendMessageW(Context->hDlgCtrl, (UINT) PBM_SETRANGE, 0, MAKELPARAM(0, 100)); 00654 00655 /* start file queueing */ 00656 SetTimer(hwndDlg, TIMER_ID, 1000, NULL); 00657 //QueueFile(Context); 00658 00659 return TRUE; 00660 00661 case WM_CLOSE: 00662 Context->op->bCancelled = TRUE; 00663 EndDialog(hwndDlg, Context->op->bCancelled); 00664 return TRUE; 00665 00666 case WM_COMMAND: 00667 if (LOWORD(wParam) == 14002) 00668 { 00669 Context->op->bCancelled = TRUE; 00670 EndDialog(hwndDlg, Context->op->bCancelled); 00671 return TRUE; 00672 }; break; 00673 00674 case WM_TIMER: 00675 if (wParam == TIMER_ID) 00676 { 00677 QueueFile(Context); 00678 KillTimer(hwndDlg, TIMER_ID); 00679 }; break; 00680 00681 case WM_FILE: 00682 if (!QueueFile(Context)) 00683 EndDialog(hwndDlg, Context->op->bCancelled); 00684 default: 00685 break; 00686 } 00687 return FALSE; 00688 } 00689 00690 HRESULT 00691 SHShowFileOperationDialog(FILE_OPERATION *op, FILE_LIST *flFrom, FILE_LIST *flTo) 00692 { 00693 HWND hwnd; 00694 BOOL bRet; 00695 MSG msg; 00696 FILE_OPERATION_CONTEXT Context; 00697 00698 Context.from = flFrom; 00699 Context.to = flTo; 00700 Context.op = op; 00701 Context.Index = 0; 00702 Context.op->bCancelled = FALSE; 00703 00704 hwnd = CreateDialogParam(shell32_hInstance, MAKEINTRESOURCE(IDD_FILE_COPY), NULL, SHOperationDialog, (LPARAM)&Context); 00705 if (hwnd == NULL) 00706 { 00707 ERR("Failed to create dialog\n"); 00708 return E_FAIL; 00709 } 00710 ShowWindow(hwnd, SW_SHOWNORMAL); 00711 00712 while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) 00713 { 00714 if (!IsWindow(hwnd) || !IsDialogMessage(hwnd, &msg)) 00715 { 00716 TranslateMessage(&msg); 00717 DispatchMessage(&msg); 00718 } 00719 } 00720 00721 return NOERROR; 00722 } 00723 00724 00725 /************************************************************************ 00726 * SHNotifyCopyFile [internal] 00727 * 00728 * Copies a file. Also triggers a change notify if one exists. 00729 * 00730 * PARAMS 00731 * src [I] path to source file to move 00732 * dest [I] path to target file to move to 00733 * bFailIfExists [I] if TRUE, the target file will not be overwritten if 00734 * a file with this name already exists 00735 * 00736 * RETURNS 00737 * ERROR_SUCCESS if successful 00738 */ 00739 static DWORD SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bFailIfExists) 00740 { 00741 BOOL ret; 00742 DWORD attribs; 00743 00744 TRACE("(%s %s %s)\n", debugstr_w(src), debugstr_w(dest), bFailIfExists ? "failIfExists" : ""); 00745 00746 /* Destination file may already exist with read only attribute */ 00747 attribs = GetFileAttributesW(dest); 00748 if (IsAttrib(attribs, FILE_ATTRIBUTE_READONLY)) 00749 SetFileAttributesW(dest, attribs & ~FILE_ATTRIBUTE_READONLY); 00750 00751 if (GetFileAttributesW(dest) & FILE_ATTRIBUTE_READONLY) 00752 { 00753 SetFileAttributesW(dest, attribs & ~FILE_ATTRIBUTE_READONLY); 00754 if (GetFileAttributesW(dest) & FILE_ATTRIBUTE_READONLY) 00755 { 00756 TRACE("[shell32, SHNotifyCopyFileW] STILL SHIT\n"); 00757 } 00758 } 00759 00760 ret = CopyFileW(src, dest, bFailIfExists); 00761 if (ret) 00762 { 00763 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, dest, NULL); 00764 return ERROR_SUCCESS; 00765 } 00766 00767 return GetLastError(); 00768 } 00769 00770 /************************************************************************* 00771 * SHCreateDirectory [SHELL32.165] 00772 * 00773 * This function creates a file system folder whose fully qualified path is 00774 * given by path. If one or more of the intermediate folders do not exist, 00775 * they will be created as well. 00776 * 00777 * PARAMS 00778 * hWnd [I] 00779 * path [I] path of directory to create 00780 * 00781 * RETURNS 00782 * ERROR_SUCCESS or one of the following values: 00783 * ERROR_BAD_PATHNAME if the path is relative 00784 * ERROR_FILE_EXISTS when a file with that name exists 00785 * ERROR_PATH_NOT_FOUND can't find the path, probably invalid 00786 * ERROR_INVALID_NAME if the path contains invalid chars 00787 * ERROR_ALREADY_EXISTS when the directory already exists 00788 * ERROR_FILENAME_EXCED_RANGE if the filename was to long to process 00789 * 00790 * NOTES 00791 * exported by ordinal 00792 * Win9x exports ANSI 00793 * WinNT/2000 exports Unicode 00794 */ 00795 int WINAPI SHCreateDirectory(HWND hWnd, LPCWSTR path) 00796 { 00797 return SHCreateDirectoryExW(hWnd, path, NULL); 00798 } 00799 00800 /************************************************************************* 00801 * SHCreateDirectoryExA [SHELL32.@] 00802 * 00803 * This function creates a file system folder whose fully qualified path is 00804 * given by path. If one or more of the intermediate folders do not exist, 00805 * they will be created as well. 00806 * 00807 * PARAMS 00808 * hWnd [I] 00809 * path [I] path of directory to create 00810 * sec [I] security attributes to use or NULL 00811 * 00812 * RETURNS 00813 * ERROR_SUCCESS or one of the following values: 00814 * ERROR_BAD_PATHNAME or ERROR_PATH_NOT_FOUND if the path is relative 00815 * ERROR_INVALID_NAME if the path contains invalid chars 00816 * ERROR_FILE_EXISTS when a file with that name exists 00817 * ERROR_ALREADY_EXISTS when the directory already exists 00818 * ERROR_FILENAME_EXCED_RANGE if the filename was to long to process 00819 * 00820 * FIXME: Not implemented yet; 00821 * SHCreateDirectoryEx also verifies that the files in the directory will be visible 00822 * if the path is a network path to deal with network drivers which might have a limited 00823 * but unknown maximum path length. If not: 00824 * 00825 * If hWnd is set to a valid window handle, a message box is displayed warning 00826 * the user that the files may not be accessible. If the user chooses not to 00827 * proceed, the function returns ERROR_CANCELLED. 00828 * 00829 * If hWnd is set to NULL, no user interface is displayed and the function 00830 * returns ERROR_CANCELLED. 00831 */ 00832 int WINAPI SHCreateDirectoryExA(HWND hWnd, LPCSTR path, LPSECURITY_ATTRIBUTES sec) 00833 { 00834 LPWSTR wPath; 00835 DWORD retCode; 00836 00837 TRACE("(%s, %p)\n", debugstr_a(path), sec); 00838 00839 retCode = SHELL32_AnsiToUnicodeBuf(path, &wPath, 0); 00840 if (!retCode) 00841 { 00842 retCode = SHCreateDirectoryExW(hWnd, wPath, sec); 00843 SHELL32_FreeUnicodeBuf(wPath); 00844 } 00845 return retCode; 00846 } 00847 00848 /************************************************************************* 00849 * SHCreateDirectoryExW [SHELL32.@] 00850 * 00851 * See SHCreateDirectoryExA. 00852 */ 00853 int WINAPI SHCreateDirectoryExW(HWND hWnd, LPCWSTR path, LPSECURITY_ATTRIBUTES sec) 00854 { 00855 int ret = ERROR_BAD_PATHNAME; 00856 TRACE("(%p, %s, %p)\n", hWnd, debugstr_w(path), sec); 00857 00858 if (PathIsRelativeW(path)) 00859 { 00860 SetLastError(ret); 00861 } 00862 else 00863 { 00864 ret = SHNotifyCreateDirectoryW(path, sec); 00865 /* Refuse to work on certain error codes before trying to create directories recursively */ 00866 if (ret != ERROR_SUCCESS && 00867 ret != ERROR_FILE_EXISTS && 00868 ret != ERROR_ALREADY_EXISTS && 00869 ret != ERROR_FILENAME_EXCED_RANGE) 00870 { 00871 WCHAR *pEnd, *pSlash, szTemp[MAX_PATH + 1]; /* extra for PathAddBackslash() */ 00872 00873 lstrcpynW(szTemp, path, MAX_PATH); 00874 pEnd = PathAddBackslashW(szTemp); 00875 pSlash = szTemp + 3; 00876 00877 while (*pSlash) 00878 { 00879 while (*pSlash && *pSlash != '\\') pSlash++; 00880 if (*pSlash) 00881 { 00882 *pSlash = 0; /* terminate path at separator */ 00883 00884 ret = SHNotifyCreateDirectoryW(szTemp, pSlash + 1 == pEnd ? sec : NULL); 00885 } 00886 *pSlash++ = '\\'; /* put the separator back */ 00887 } 00888 } 00889 00890 if (ret && hWnd && (ERROR_CANCELLED != ret)) 00891 { 00892 /* We failed and should show a dialog box */ 00893 FIXME("Show system error message, creating path %s, failed with error %d\n", debugstr_w(path), ret); 00894 ret = ERROR_CANCELLED; /* Error has been already presented to user (not really yet!) */ 00895 } 00896 } 00897 00898 return ret; 00899 } 00900 00901 /************************************************************************* 00902 * SHFindAttrW [internal] 00903 * 00904 * Get the Attributes for a file or directory. The difference to GetAttributes() 00905 * is that this function will also work for paths containing wildcard characters 00906 * in its filename. 00907 00908 * PARAMS 00909 * path [I] path of directory or file to check 00910 * fileOnly [I] TRUE if only files should be found 00911 * 00912 * RETURNS 00913 * INVALID_FILE_ATTRIBUTES if the path does not exist, the actual attributes of 00914 * the first file or directory found otherwise 00915 */ 00916 static DWORD SHFindAttrW(LPCWSTR pName, BOOL fileOnly) 00917 { 00918 WIN32_FIND_DATAW wfd; 00919 BOOL b_FileMask = fileOnly && (NULL != StrPBrkW(pName, wWildcardChars)); 00920 DWORD dwAttr = INVALID_FILE_ATTRIBUTES; 00921 HANDLE hFind = FindFirstFileW(pName, &wfd); 00922 00923 TRACE("%s %d\n", debugstr_w(pName), fileOnly); 00924 if (INVALID_HANDLE_VALUE != hFind) 00925 { 00926 do 00927 { 00928 if (b_FileMask && IsAttribDir(wfd.dwFileAttributes)) 00929 continue; 00930 dwAttr = wfd.dwFileAttributes; 00931 break; 00932 } while (FindNextFileW(hFind, &wfd)); 00933 00934 FindClose(hFind); 00935 } 00936 return dwAttr; 00937 } 00938 00939 /************************************************************************* 00940 * 00941 * SHNameTranslate HelperFunction for SHFileOperationA 00942 * 00943 * Translates a list of 0 terminated ASCII strings into Unicode. If *wString 00944 * is NULL, only the necessary size of the string is determined and returned, 00945 * otherwise the ASCII strings are copied into it and the buffer is increased 00946 * to point to the location after the final 0 termination char. 00947 */ 00948 static DWORD SHNameTranslate(LPWSTR* wString, LPCWSTR* pWToFrom, BOOL more) 00949 { 00950 DWORD size = 0, aSize = 0; 00951 LPCSTR aString = (LPCSTR)*pWToFrom; 00952 00953 if (aString) 00954 { 00955 do 00956 { 00957 size = lstrlenA(aString) + 1; 00958 aSize += size; 00959 aString += size; 00960 } while ((size != 1) && more); 00961 00962 /* The two sizes might be different in the case of multibyte chars */ 00963 size = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*pWToFrom, aSize, *wString, 0); 00964 if (*wString) /* only in the second loop */ 00965 { 00966 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*pWToFrom, aSize, *wString, size); 00967 *pWToFrom = *wString; 00968 *wString += size; 00969 } 00970 } 00971 return size; 00972 } 00973 /************************************************************************* 00974 * SHFileOperationA [SHELL32.@] 00975 * 00976 * Function to copy, move, delete and create one or more files with optional 00977 * user prompts. 00978 * 00979 * PARAMS 00980 * lpFileOp [I/O] pointer to a structure containing all the necessary information 00981 * 00982 * RETURNS 00983 * Success: ERROR_SUCCESS. 00984 * Failure: ERROR_CANCELLED. 00985 * 00986 * NOTES 00987 * exported by name 00988 */ 00989 int WINAPI SHFileOperationA(LPSHFILEOPSTRUCTA lpFileOp) 00990 { 00991 SHFILEOPSTRUCTW nFileOp = *((LPSHFILEOPSTRUCTW)lpFileOp); 00992 int retCode = 0; 00993 DWORD size; 00994 LPWSTR ForFree = NULL, /* we change wString in SHNameTranslate and can't use it for freeing */ 00995 wString = NULL; /* we change this in SHNameTranslate */ 00996 00997 TRACE("\n"); 00998 if (FO_DELETE == (nFileOp.wFunc & FO_MASK)) 00999 nFileOp.pTo = NULL; /* we need a NULL or a valid pointer for translation */ 01000 if (!(nFileOp.fFlags & FOF_SIMPLEPROGRESS)) 01001 nFileOp.lpszProgressTitle = NULL; /* we need a NULL or a valid pointer for translation */ 01002 while (1) /* every loop calculate size, second translate also, if we have storage for this */ 01003 { 01004 size = SHNameTranslate(&wString, &nFileOp.lpszProgressTitle, FALSE); /* no loop */ 01005 size += SHNameTranslate(&wString, &nFileOp.pFrom, TRUE); /* internal loop */ 01006 size += SHNameTranslate(&wString, &nFileOp.pTo, TRUE); /* internal loop */ 01007 01008 if (ForFree) 01009 { 01010 retCode = SHFileOperationW(&nFileOp); 01011 HeapFree(GetProcessHeap(), 0, ForFree); /* we cannot use wString, it was changed */ 01012 break; 01013 } 01014 else 01015 { 01016 wString = ForFree = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); 01017 if (ForFree) continue; 01018 retCode = ERROR_OUTOFMEMORY; 01019 nFileOp.fAnyOperationsAborted = TRUE; 01020 SetLastError(retCode); 01021 return retCode; 01022 } 01023 } 01024 01025 lpFileOp->hNameMappings = nFileOp.hNameMappings; 01026 lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted; 01027 return retCode; 01028 } 01029 01030 static void __inline grow_list(FILE_LIST *list) 01031 { 01032 FILE_ENTRY *newx = (FILE_ENTRY *)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, list->feFiles, 01033 list->num_alloc * 2 * sizeof(*newx) ); 01034 list->feFiles = newx; 01035 list->num_alloc *= 2; 01036 } 01037 01038 /* adds a file to the FILE_ENTRY struct 01039 */ 01040 static void add_file_to_entry(FILE_ENTRY *feFile, LPCWSTR szFile) 01041 { 01042 DWORD dwLen = lstrlenW(szFile) + 1; 01043 LPCWSTR ptr; 01044 01045 feFile->szFullPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR)); 01046 lstrcpyW(feFile->szFullPath, szFile); 01047 01048 ptr = StrRChrW(szFile, NULL, '\\'); 01049 if (ptr) 01050 { 01051 dwLen = ptr - szFile + 1; 01052 feFile->szDirectory = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR)); 01053 lstrcpynW(feFile->szDirectory, szFile, dwLen); 01054 01055 dwLen = lstrlenW(feFile->szFullPath) - dwLen + 1; 01056 feFile->szFilename = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR)); 01057 lstrcpyW(feFile->szFilename, ptr + 1); /* skip over backslash */ 01058 } 01059 feFile->bFromWildcard = FALSE; 01060 } 01061 01062 static LPWSTR wildcard_to_file(LPCWSTR szWildCard, LPCWSTR szFileName) 01063 { 01064 LPCWSTR ptr; 01065 LPWSTR szFullPath; 01066 DWORD dwDirLen, dwFullLen; 01067 01068 ptr = StrRChrW(szWildCard, NULL, '\\'); 01069 dwDirLen = ptr - szWildCard + 1; 01070 01071 dwFullLen = dwDirLen + lstrlenW(szFileName) + 1; 01072 szFullPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwFullLen * sizeof(WCHAR)); 01073 01074 lstrcpynW(szFullPath, szWildCard, dwDirLen + 1); 01075 lstrcatW(szFullPath, szFileName); 01076 01077 return szFullPath; 01078 } 01079 01080 static void parse_wildcard_files(FILE_LIST *flList, LPCWSTR szFile, LPDWORD pdwListIndex) 01081 { 01082 WIN32_FIND_DATAW wfd; 01083 HANDLE hFile = FindFirstFileW(szFile, &wfd); 01084 FILE_ENTRY *file; 01085 LPWSTR szFullPath; 01086 BOOL res; 01087 01088 if (hFile == INVALID_HANDLE_VALUE) return; 01089 01090 for (res = TRUE; res; res = FindNextFileW(hFile, &wfd)) 01091 { 01092 if (IsDotDir(wfd.cFileName)) 01093 continue; 01094 01095 if (*pdwListIndex >= flList->num_alloc) 01096 grow_list( flList ); 01097 01098 szFullPath = wildcard_to_file(szFile, wfd.cFileName); 01099 file = &flList->feFiles[(*pdwListIndex)++]; 01100 add_file_to_entry(file, szFullPath); 01101 file->bFromWildcard = TRUE; 01102 file->attributes = wfd.dwFileAttributes; 01103 01104 if (IsAttribDir(file->attributes)) 01105 flList->bAnyDirectories = TRUE; 01106 01107 HeapFree(GetProcessHeap(), 0, szFullPath); 01108 } 01109 01110 FindClose(hFile); 01111 } 01112 01113 /* takes the null-separated file list and fills out the FILE_LIST */ 01114 static HRESULT parse_file_list(FILE_LIST *flList, LPCWSTR szFiles) 01115 { 01116 LPCWSTR ptr = szFiles; 01117 WCHAR szCurFile[MAX_PATH]; 01118 DWORD i = 0; 01119 01120 if (!szFiles) 01121 return ERROR_INVALID_PARAMETER; 01122 01123 flList->bAnyFromWildcard = FALSE; 01124 flList->bAnyDirectories = FALSE; 01125 flList->bAnyDontExist = FALSE; 01126 flList->num_alloc = 32; 01127 flList->dwNumFiles = 0; 01128 01129 /* empty list */ 01130 if (!szFiles[0]) 01131 return ERROR_ACCESS_DENIED; 01132 01133 flList->feFiles = (FILE_ENTRY *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 01134 flList->num_alloc * sizeof(FILE_ENTRY)); 01135 01136 while (*ptr) 01137 { 01138 if (i >= flList->num_alloc) grow_list( flList ); 01139 01140 /* change relative to absolute path */ 01141 if (PathIsRelativeW(ptr)) 01142 { 01143 GetCurrentDirectoryW(MAX_PATH, szCurFile); 01144 PathCombineW(szCurFile, szCurFile, ptr); 01145 flList->feFiles[i].bFromRelative = TRUE; 01146 } 01147 else 01148 { 01149 lstrcpyW(szCurFile, ptr); 01150 flList->feFiles[i].bFromRelative = FALSE; 01151 } 01152 01153 /* parse wildcard files if they are in the filename */ 01154 if (StrPBrkW(szCurFile, wWildcardChars)) 01155 { 01156 parse_wildcard_files(flList, szCurFile, &i); 01157 flList->bAnyFromWildcard = TRUE; 01158 i--; 01159 } 01160 else 01161 { 01162 FILE_ENTRY *file = &flList->feFiles[i]; 01163 add_file_to_entry(file, szCurFile); 01164 file->attributes = GetFileAttributesW( file->szFullPath ); 01165 file->bExists = (file->attributes != INVALID_FILE_ATTRIBUTES); 01166 01167 if (!file->bExists) 01168 flList->bAnyDontExist = TRUE; 01169 01170 if (IsAttribDir(file->attributes)) 01171 flList->bAnyDirectories = TRUE; 01172 } 01173 01174 /* advance to the next string */ 01175 ptr += lstrlenW(ptr) + 1; 01176 i++; 01177 } 01178 flList->dwNumFiles = i; 01179 01180 return S_OK; 01181 } 01182 01183 /* free the FILE_LIST */ 01184 static void destroy_file_list(FILE_LIST *flList) 01185 { 01186 DWORD i; 01187 01188 if (!flList || !flList->feFiles) 01189 return; 01190 01191 for (i = 0; i < flList->dwNumFiles; i++) 01192 { 01193 HeapFree(GetProcessHeap(), 0, flList->feFiles[i].szDirectory); 01194 HeapFree(GetProcessHeap(), 0, flList->feFiles[i].szFilename); 01195 HeapFree(GetProcessHeap(), 0, flList->feFiles[i].szFullPath); 01196 } 01197 01198 HeapFree(GetProcessHeap(), 0, flList->feFiles); 01199 } 01200 01201 static void copy_dir_to_dir(FILE_OPERATION *op, const FILE_ENTRY *feFrom, LPCWSTR szDestPath) 01202 { 01203 WCHAR szFrom[MAX_PATH], szTo[MAX_PATH]; 01204 SHFILEOPSTRUCTW fileOp; 01205 01206 static const WCHAR wildCardFiles[] = {'*','.','*',0}; 01207 01208 if (IsDotDir(feFrom->szFilename)) 01209 return; 01210 01211 if (PathFileExistsW(szDestPath)) 01212 PathCombineW(szTo, szDestPath, feFrom->szFilename); 01213 else 01214 lstrcpyW(szTo, szDestPath); 01215 01216 if (!(op->req->fFlags & FOF_NOCONFIRMATION) && PathFileExistsW(szTo)) 01217 { 01218 if (!SHELL_ConfirmDialogW(op->req->hwnd, ASK_OVERWRITE_FOLDER, feFrom->szFilename, op)) 01219 { 01220 /* Vista returns an ERROR_CANCELLED even if user pressed "No" */ 01221 if (!op->bManyItems) 01222 op->bCancelled = TRUE; 01223 return; 01224 } 01225 } 01226 01227 szTo[lstrlenW(szTo) + 1] = '\0'; 01228 SHNotifyCreateDirectoryW(szTo, NULL); 01229 01230 PathCombineW(szFrom, feFrom->szFullPath, wildCardFiles); 01231 szFrom[lstrlenW(szFrom) + 1] = '\0'; 01232 01233 fileOp = *op->req; 01234 fileOp.pFrom = szFrom; 01235 fileOp.pTo = szTo; 01236 fileOp.fFlags &= ~FOF_MULTIDESTFILES; /* we know we're copying to one dir */ 01237 01238 /* Don't ask the user about overwriting files when he accepted to overwrite the 01239 folder. FIXME: this is not exactly what Windows does - e.g. there would be 01240 an additional confirmation for a nested folder */ 01241 fileOp.fFlags |= FOF_NOCONFIRMATION; 01242 01243 SHFileOperationW(&fileOp); 01244 } 01245 01246 static BOOL copy_file_to_file(FILE_OPERATION *op, const WCHAR *szFrom, const WCHAR *szTo) 01247 { 01248 if (!(op->req->fFlags & FOF_NOCONFIRMATION) && PathFileExistsW(szTo)) 01249 { 01250 if (!SHELL_ConfirmDialogW(op->req->hwnd, ASK_OVERWRITE_FILE, PathFindFileNameW(szTo), op)) 01251 return 0; 01252 } 01253 01254 return SHNotifyCopyFileW(szFrom, szTo, FALSE) == 0; 01255 } 01256 01257 /* copy a file or directory to another directory */ 01258 static void copy_to_dir(FILE_OPERATION *op, const FILE_ENTRY *feFrom, const FILE_ENTRY *feTo) 01259 { 01260 if (!PathFileExistsW(feTo->szFullPath)) 01261 SHNotifyCreateDirectoryW(feTo->szFullPath, NULL); 01262 01263 if (IsAttribFile(feFrom->attributes)) 01264 { 01265 WCHAR szDestPath[MAX_PATH]; 01266 01267 PathCombineW(szDestPath, feTo->szFullPath, feFrom->szFilename); 01268 copy_file_to_file(op, feFrom->szFullPath, szDestPath); 01269 } 01270 else if (!(op->req->fFlags & FOF_FILESONLY && feFrom->bFromWildcard)) 01271 copy_dir_to_dir(op, feFrom, feTo->szFullPath); 01272 } 01273 01274 static void create_dest_dirs(LPCWSTR szDestDir) 01275 { 01276 WCHAR dir[MAX_PATH]; 01277 LPCWSTR ptr = StrChrW(szDestDir, '\\'); 01278 01279 /* make sure all directories up to last one are created */ 01280 while (ptr && (ptr = StrChrW(ptr + 1, '\\'))) 01281 { 01282 lstrcpynW(dir, szDestDir, ptr - szDestDir + 1); 01283 01284 if (!PathFileExistsW(dir)) 01285 SHNotifyCreateDirectoryW(dir, NULL); 01286 } 01287 01288 /* create last directory */ 01289 if (!PathFileExistsW(szDestDir)) 01290 SHNotifyCreateDirectoryW(szDestDir, NULL); 01291 } 01292 01293 /* the FO_COPY operation */ 01294 static HRESULT copy_files(FILE_OPERATION *op, const FILE_LIST *flFrom, FILE_LIST *flTo) 01295 { 01296 DWORD i; 01297 const FILE_ENTRY *entryToCopy; 01298 const FILE_ENTRY *fileDest = &flTo->feFiles[0]; 01299 01300 if (flFrom->bAnyDontExist) 01301 return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND; 01302 01303 if (flTo->dwNumFiles == 0) 01304 { 01305 /* If the destination is empty, SHFileOperation should use the current directory */ 01306 WCHAR curdir[MAX_PATH+1]; 01307 01308 GetCurrentDirectoryW(MAX_PATH, curdir); 01309 curdir[lstrlenW(curdir)+1] = 0; 01310 01311 destroy_file_list(flTo); 01312 ZeroMemory(flTo, sizeof(FILE_LIST)); 01313 parse_file_list(flTo, curdir); 01314 fileDest = &flTo->feFiles[0]; 01315 } 01316 01317 if (op->req->fFlags & FOF_MULTIDESTFILES) 01318 { 01319 if (flFrom->bAnyFromWildcard) 01320 return ERROR_CANCELLED; 01321 01322 if (flFrom->dwNumFiles != flTo->dwNumFiles) 01323 { 01324 if (flFrom->dwNumFiles != 1 && !IsAttribDir(fileDest->attributes)) 01325 return ERROR_CANCELLED; 01326 01327 /* Free all but the first entry. */ 01328 for (i = 1; i < flTo->dwNumFiles; i++) 01329 { 01330 HeapFree(GetProcessHeap(), 0, flTo->feFiles[i].szDirectory); 01331 HeapFree(GetProcessHeap(), 0, flTo->feFiles[i].szFilename); 01332 HeapFree(GetProcessHeap(), 0, flTo->feFiles[i].szFullPath); 01333 } 01334 01335 flTo->dwNumFiles = 1; 01336 } 01337 else if (IsAttribDir(fileDest->attributes)) 01338 { 01339 for (i = 1; i < flTo->dwNumFiles; i++) 01340 if (!IsAttribDir(flTo->feFiles[i].attributes) || 01341 !IsAttribDir(flFrom->feFiles[i].attributes)) 01342 { 01343 return ERROR_CANCELLED; 01344 } 01345 } 01346 } 01347 else if (flFrom->dwNumFiles != 1) 01348 { 01349 if (flTo->dwNumFiles != 1 && !IsAttribDir(fileDest->attributes)) 01350 return ERROR_CANCELLED; 01351 01352 if (PathFileExistsW(fileDest->szFullPath) && 01353 IsAttribFile(fileDest->attributes)) 01354 { 01355 return ERROR_CANCELLED; 01356 } 01357 01358 if (flTo->dwNumFiles == 1 && fileDest->bFromRelative && 01359 !PathFileExistsW(fileDest->szFullPath)) 01360 { 01361 return ERROR_CANCELLED; 01362 } 01363 } 01364 01365 for (i = 0; i < flFrom->dwNumFiles; i++) 01366 { 01367 entryToCopy = &flFrom->feFiles[i]; 01368 01369 if ((op->req->fFlags & FOF_MULTIDESTFILES) && 01370 flTo->dwNumFiles > 1) 01371 { 01372 fileDest = &flTo->feFiles[i]; 01373 } 01374 01375 if (IsAttribDir(entryToCopy->attributes) && 01376 !lstrcmpiW(entryToCopy->szFullPath, fileDest->szDirectory)) 01377 { 01378 return ERROR_SUCCESS; 01379 } 01380 01381 create_dest_dirs(fileDest->szDirectory); 01382 01383 if (!lstrcmpiW(entryToCopy->szFullPath, fileDest->szFullPath)) 01384 { 01385 if (IsAttribFile(entryToCopy->attributes)) 01386 return ERROR_NO_MORE_SEARCH_HANDLES; 01387 else 01388 return ERROR_SUCCESS; 01389 } 01390 01391 if ((flFrom->dwNumFiles > 1 && flTo->dwNumFiles == 1) || 01392 IsAttribDir(fileDest->attributes)) 01393 { 01394 copy_to_dir(op, entryToCopy, fileDest); 01395 } 01396 else if (IsAttribDir(entryToCopy->attributes)) 01397 { 01398 copy_dir_to_dir(op, entryToCopy, fileDest->szFullPath); 01399 } 01400 else 01401 { 01402 if (!copy_file_to_file(op, entryToCopy->szFullPath, fileDest->szFullPath)) 01403 { 01404 op->req->fAnyOperationsAborted = TRUE; 01405 return ERROR_CANCELLED; 01406 } 01407 } 01408 01409 /* Vista return code. XP would return e.g. ERROR_FILE_NOT_FOUND, ERROR_ALREADY_EXISTS */ 01410 if (op->bCancelled) 01411 return ERROR_CANCELLED; 01412 } 01413 01414 /* Vista return code. On XP if the used pressed "No" for the last item, 01415 * ERROR_ARENA_TRASHED would be returned */ 01416 return ERROR_SUCCESS; 01417 } 01418 01419 static BOOL confirm_delete_list(HWND hWnd, DWORD fFlags, BOOL fTrash, const FILE_LIST *flFrom) 01420 { 01421 if (flFrom->dwNumFiles > 1) 01422 { 01423 WCHAR tmp[8]; 01424 const WCHAR format[] = {'%','d',0}; 01425 01426 wnsprintfW(tmp, sizeof(tmp)/sizeof(tmp[0]), format, flFrom->dwNumFiles); 01427 return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_MULTIPLE_ITEM:ASK_DELETE_MULTIPLE_ITEM), tmp, NULL); 01428 } 01429 else 01430 { 01431 const FILE_ENTRY *fileEntry = &flFrom->feFiles[0]; 01432 01433 if (IsAttribFile(fileEntry->attributes)) 01434 return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_FILE:ASK_DELETE_FILE), fileEntry->szFullPath, NULL); 01435 else if (!(fFlags & FOF_FILESONLY && fileEntry->bFromWildcard)) 01436 return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_FOLDER:ASK_DELETE_FOLDER), fileEntry->szFullPath, NULL); 01437 } 01438 return TRUE; 01439 } 01440 01441 /* the FO_DELETE operation */ 01442 static HRESULT delete_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom) 01443 { 01444 const FILE_ENTRY *fileEntry; 01445 DWORD i; 01446 BOOL bPathExists; 01447 BOOL bTrash; 01448 01449 if (!flFrom->dwNumFiles) 01450 return ERROR_SUCCESS; 01451 01452 /* Windows also checks only the first item */ 01453 bTrash = (lpFileOp->fFlags & FOF_ALLOWUNDO) 01454 && TRASH_CanTrashFile(flFrom->feFiles[0].szFullPath); 01455 01456 if (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) || (!bTrash && lpFileOp->fFlags & FOF_WANTNUKEWARNING)) 01457 if (!confirm_delete_list(lpFileOp->hwnd, lpFileOp->fFlags, bTrash, flFrom)) 01458 { 01459 lpFileOp->fAnyOperationsAborted = TRUE; 01460 return 0; 01461 } 01462 01463 for (i = 0; i < flFrom->dwNumFiles; i++) 01464 { 01465 bPathExists = TRUE; 01466 fileEntry = &flFrom->feFiles[i]; 01467 01468 if (!IsAttribFile(fileEntry->attributes) && 01469 (lpFileOp->fFlags & FOF_FILESONLY && fileEntry->bFromWildcard)) 01470 continue; 01471 01472 if (bTrash) 01473 { 01474 BOOL bDelete; 01475 if (TRASH_TrashFile(fileEntry->szFullPath)) 01476 continue; 01477 01478 /* Note: Windows silently deletes the file in such a situation, we show a dialog */ 01479 if (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) || (lpFileOp->fFlags & FOF_WANTNUKEWARNING)) 01480 bDelete = SHELL_ConfirmDialogW(lpFileOp->hwnd, ASK_CANT_TRASH_ITEM, fileEntry->szFullPath, NULL); 01481 else 01482 bDelete = TRUE; 01483 01484 if (!bDelete) 01485 { 01486 lpFileOp->fAnyOperationsAborted = TRUE; 01487 break; 01488 } 01489 } 01490 01491 /* delete the file or directory */ 01492 if (IsAttribFile(fileEntry->attributes)) 01493 bPathExists = DeleteFileW(fileEntry->szFullPath); 01494 else 01495 bPathExists = SHELL_DeleteDirectoryW(lpFileOp->hwnd, fileEntry->szFullPath, FALSE); 01496 01497 if (!bPathExists) 01498 { 01499 DWORD err = GetLastError(); 01500 01501 if (ERROR_FILE_NOT_FOUND == err) 01502 { 01503 // This is a windows 2003 server specific value which ahs been removed. 01504 // Later versions of windows return ERROR_FILE_NOT_FOUND. 01505 return 1026; 01506 } 01507 else 01508 { 01509 return err; 01510 } 01511 } 01512 } 01513 01514 return ERROR_SUCCESS; 01515 } 01516 01517 static void move_dir_to_dir(LPSHFILEOPSTRUCTW lpFileOp, const FILE_ENTRY *feFrom, LPCWSTR szDestPath) 01518 { 01519 WCHAR szFrom[MAX_PATH], szTo[MAX_PATH]; 01520 SHFILEOPSTRUCTW fileOp; 01521 01522 static const WCHAR wildCardFiles[] = {'*','.','*',0}; 01523 01524 if (IsDotDir(feFrom->szFilename)) 01525 return; 01526 01527 SHNotifyCreateDirectoryW(szDestPath, NULL); 01528 01529 PathCombineW(szFrom, feFrom->szFullPath, wildCardFiles); 01530 szFrom[lstrlenW(szFrom) + 1] = '\0'; 01531 01532 lstrcpyW(szTo, szDestPath); 01533 szTo[lstrlenW(szDestPath) + 1] = '\0'; 01534 01535 fileOp = *lpFileOp; 01536 fileOp.pFrom = szFrom; 01537 fileOp.pTo = szTo; 01538 01539 SHFileOperationW(&fileOp); 01540 } 01541 01542 /* moves a file or directory to another directory */ 01543 static void move_to_dir(LPSHFILEOPSTRUCTW lpFileOp, const FILE_ENTRY *feFrom, const FILE_ENTRY *feTo) 01544 { 01545 WCHAR szDestPath[MAX_PATH]; 01546 01547 PathCombineW(szDestPath, feTo->szFullPath, feFrom->szFilename); 01548 01549 if (IsAttribFile(feFrom->attributes)) 01550 SHNotifyMoveFileW(feFrom->szFullPath, szDestPath); 01551 else if (!(lpFileOp->fFlags & FOF_FILESONLY && feFrom->bFromWildcard)) 01552 move_dir_to_dir(lpFileOp, feFrom, szDestPath); 01553 } 01554 01555 /* the FO_MOVE operation */ 01556 static HRESULT move_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, const FILE_LIST *flTo) 01557 { 01558 DWORD i; 01559 const FILE_ENTRY *entryToMove; 01560 const FILE_ENTRY *fileDest; 01561 01562 if (!flFrom->dwNumFiles || !flTo->dwNumFiles) 01563 return ERROR_CANCELLED; 01564 01565 if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) && 01566 flTo->dwNumFiles > 1 && flFrom->dwNumFiles > 1) 01567 { 01568 return ERROR_CANCELLED; 01569 } 01570 01571 if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) && 01572 !flFrom->bAnyDirectories && 01573 flFrom->dwNumFiles > flTo->dwNumFiles) 01574 { 01575 return ERROR_CANCELLED; 01576 } 01577 01578 if (!PathFileExistsW(flTo->feFiles[0].szDirectory)) 01579 return ERROR_CANCELLED; 01580 01581 if ((lpFileOp->fFlags & FOF_MULTIDESTFILES) && 01582 flFrom->dwNumFiles != flTo->dwNumFiles) 01583 { 01584 return ERROR_CANCELLED; 01585 } 01586 01587 fileDest = &flTo->feFiles[0]; 01588 for (i = 0; i < flFrom->dwNumFiles; i++) 01589 { 01590 entryToMove = &flFrom->feFiles[i]; 01591 01592 if (lpFileOp->fFlags & FOF_MULTIDESTFILES) 01593 fileDest = &flTo->feFiles[i]; 01594 01595 if (!PathFileExistsW(fileDest->szDirectory)) 01596 return ERROR_CANCELLED; 01597 01598 if (fileDest->bExists && IsAttribDir(fileDest->attributes)) 01599 move_to_dir(lpFileOp, entryToMove, fileDest); 01600 else 01601 SHNotifyMoveFileW(entryToMove->szFullPath, fileDest->szFullPath); 01602 } 01603 01604 return ERROR_SUCCESS; 01605 } 01606 01607 /* the FO_RENAME files */ 01608 static HRESULT rename_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, const FILE_LIST *flTo) 01609 { 01610 const FILE_ENTRY *feFrom; 01611 const FILE_ENTRY *feTo; 01612 01613 if (flFrom->dwNumFiles != 1) 01614 return ERROR_GEN_FAILURE; 01615 01616 if (flTo->dwNumFiles != 1) 01617 return ERROR_CANCELLED; 01618 01619 feFrom = &flFrom->feFiles[0]; 01620 feTo= &flTo->feFiles[0]; 01621 01622 /* fail if destination doesn't exist */ 01623 if (!feFrom->bExists) 01624 return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND; 01625 01626 /* fail if destination already exists */ 01627 if (feTo->bExists) 01628 return ERROR_ALREADY_EXISTS; 01629 01630 return SHNotifyMoveFileW(feFrom->szFullPath, feTo->szFullPath); 01631 } 01632 01633 /* alert the user if an unsupported flag is used */ 01634 static void check_flags(FILEOP_FLAGS fFlags) 01635 { 01636 WORD wUnsupportedFlags = FOF_NO_CONNECTED_ELEMENTS | 01637 FOF_NOCOPYSECURITYATTRIBS | FOF_NORECURSEREPARSE | 01638 FOF_RENAMEONCOLLISION | FOF_WANTMAPPINGHANDLE; 01639 01640 if (fFlags & wUnsupportedFlags) 01641 FIXME("Unsupported flags: %04x\n", fFlags); 01642 } 01643 01644 /************************************************************************* 01645 * SHFileOperationW [SHELL32.@] 01646 * 01647 * See SHFileOperationA 01648 */ 01649 int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp) 01650 { 01651 FILE_OPERATION op; 01652 FILE_LIST flFrom, flTo; 01653 int ret = 0; 01654 01655 if (!lpFileOp) 01656 return ERROR_INVALID_PARAMETER; 01657 01658 check_flags(lpFileOp->fFlags); 01659 01660 ZeroMemory(&flFrom, sizeof(FILE_LIST)); 01661 ZeroMemory(&flTo, sizeof(FILE_LIST)); 01662 01663 if ((ret = parse_file_list(&flFrom, lpFileOp->pFrom))) 01664 return ret; 01665 01666 if (lpFileOp->wFunc != FO_DELETE) 01667 parse_file_list(&flTo, lpFileOp->pTo); 01668 01669 ZeroMemory(&op, sizeof(op)); 01670 op.req = lpFileOp; 01671 op.bManyItems = (flFrom.dwNumFiles > 1); 01672 01673 switch (lpFileOp->wFunc) 01674 { 01675 case FO_COPY: 01676 ret = copy_files(&op, &flFrom, &flTo); 01677 break; 01678 case FO_DELETE: 01679 ret = delete_files(lpFileOp, &flFrom); 01680 break; 01681 case FO_MOVE: 01682 ret = move_files(lpFileOp, &flFrom, &flTo); 01683 break; 01684 case FO_RENAME: 01685 ret = rename_files(lpFileOp, &flFrom, &flTo); 01686 break; 01687 default: 01688 ret = ERROR_INVALID_PARAMETER; 01689 break; 01690 } 01691 01692 destroy_file_list(&flFrom); 01693 01694 if (lpFileOp->wFunc != FO_DELETE) 01695 destroy_file_list(&flTo); 01696 01697 if (ret == ERROR_CANCELLED) 01698 lpFileOp->fAnyOperationsAborted = TRUE; 01699 01700 return ret; 01701 } 01702 01703 #define SHDSA_GetItemCount(hdsa) (*(int*)(hdsa)) 01704 01705 /************************************************************************* 01706 * SHFreeNameMappings [shell32.246] 01707 * 01708 * Free the mapping handle returned by SHFileOperation if FOF_WANTSMAPPINGHANDLE 01709 * was specified. 01710 * 01711 * PARAMS 01712 * hNameMapping [I] handle to the name mappings used during renaming of files 01713 * 01714 * RETURNS 01715 * Nothing 01716 */ 01717 void WINAPI SHFreeNameMappings(HANDLE hNameMapping) 01718 { 01719 if (hNameMapping) 01720 { 01721 int i = SHDSA_GetItemCount((HDSA)hNameMapping) - 1; 01722 01723 for (; i>= 0; i--) 01724 { 01725 LPSHNAMEMAPPINGW lp = (SHNAMEMAPPINGW *)DSA_GetItemPtr((HDSA)hNameMapping, i); 01726 01727 SHFree(lp->pszOldPath); 01728 SHFree(lp->pszNewPath); 01729 } 01730 DSA_Destroy((HDSA)hNameMapping); 01731 } 01732 } 01733 01734 /************************************************************************* 01735 * SheGetDirA [SHELL32.@] 01736 * 01737 * drive = 0: returns the current directory path 01738 * drive > 0: returns the current directory path of the specified drive 01739 * drive=1 -> A: drive=2 -> B: ... 01740 * returns 0 if successful 01741 */ 01742 EXTERN_C DWORD WINAPI SheGetDirA(DWORD drive, LPSTR buffer) 01743 { 01744 WCHAR org_path[MAX_PATH]; 01745 DWORD ret; 01746 char drv_path[3]; 01747 01748 /* change current directory to the specified drive */ 01749 if (drive) { 01750 strcpy(drv_path, "A:"); 01751 drv_path[0] += (char)drive-1; 01752 01753 GetCurrentDirectoryW(MAX_PATH, org_path); 01754 01755 SetCurrentDirectoryA(drv_path); 01756 } 01757 01758 /* query current directory path of the specified drive */ 01759 ret = GetCurrentDirectoryA(MAX_PATH, buffer); 01760 01761 /* back to the original drive */ 01762 if (drive) 01763 SetCurrentDirectoryW(org_path); 01764 01765 if (!ret) 01766 return GetLastError(); 01767 01768 return 0; 01769 } 01770 01771 /************************************************************************* 01772 * SheGetDirW [SHELL32.@] 01773 * 01774 * drive = 0: returns the current directory path 01775 * drive > 0: returns the current directory path of the specified drive 01776 * drive=1 -> A: drive=2 -> B: ... 01777 * returns 0 if successful 01778 */ 01779 EXTERN_C DWORD WINAPI SheGetDirW(DWORD drive, LPWSTR buffer) 01780 { 01781 WCHAR org_path[MAX_PATH]; 01782 DWORD ret; 01783 char drv_path[3]; 01784 01785 /* change current directory to the specified drive */ 01786 if (drive) 01787 { 01788 strcpy(drv_path, "A:"); 01789 drv_path[0] += (char)drive-1; 01790 01791 GetCurrentDirectoryW(MAX_PATH, org_path); 01792 01793 SetCurrentDirectoryA(drv_path); 01794 } 01795 01796 /* query current directory path of the specified drive */ 01797 ret = GetCurrentDirectoryW(MAX_PATH, buffer); 01798 01799 /* back to the original drive */ 01800 if (drive) 01801 SetCurrentDirectoryW(org_path); 01802 01803 if (!ret) 01804 return GetLastError(); 01805 01806 return 0; 01807 } 01808 01809 /************************************************************************* 01810 * SheChangeDirA [SHELL32.@] 01811 * 01812 * changes the current directory to the specified path 01813 * and returns 0 if successful 01814 */ 01815 EXTERN_C DWORD WINAPI SheChangeDirA(LPSTR path) 01816 { 01817 if (SetCurrentDirectoryA(path)) 01818 return 0; 01819 else 01820 return GetLastError(); 01821 } 01822 01823 /************************************************************************* 01824 * SheChangeDirW [SHELL32.@] 01825 * 01826 * changes the current directory to the specified path 01827 * and returns 0 if successful 01828 */ 01829 EXTERN_C DWORD WINAPI SheChangeDirW(LPWSTR path) 01830 { 01831 if (SetCurrentDirectoryW(path)) 01832 return 0; 01833 else 01834 return GetLastError(); 01835 } 01836 01837 /************************************************************************* 01838 * IsNetDrive [SHELL32.66] 01839 */ 01840 EXTERN_C int WINAPI IsNetDrive(int drive) 01841 { 01842 char root[4]; 01843 strcpy(root, "A:\\"); 01844 root[0] += (char)drive; 01845 return (GetDriveTypeA(root) == DRIVE_REMOTE); 01846 } 01847 01848 01849 /************************************************************************* 01850 * RealDriveType [SHELL32.524] 01851 */ 01852 EXTERN_C INT WINAPI RealDriveType(INT drive, BOOL bQueryNet) 01853 { 01854 char root[] = "A:\\"; 01855 root[0] += (char)drive; 01856 return GetDriveTypeA(root); 01857 } 01858 01859 /*********************************************************************** 01860 * SHPathPrepareForWriteW (SHELL32.@) 01861 */ 01862 EXTERN_C HRESULT WINAPI SHPathPrepareForWriteW(HWND hwnd, IUnknown *modless, LPCWSTR path, DWORD flags) 01863 { 01864 DWORD res; 01865 DWORD err; 01866 LPCWSTR realpath; 01867 int len; 01868 WCHAR* last_slash; 01869 WCHAR* temppath=NULL; 01870 01871 TRACE("%p %p %s 0x%80x\n", hwnd, modless, debugstr_w(path), flags); 01872 01873 if (flags & ~(SHPPFW_DIRCREATE|SHPPFW_ASKDIRCREATE|SHPPFW_IGNOREFILENAME)) 01874 FIXME("unimplemented flags 0x%08x\n", flags); 01875 01876 /* cut off filename if necessary */ 01877 if (flags & SHPPFW_IGNOREFILENAME) 01878 { 01879 last_slash = StrRChrW(path, NULL, '\\'); 01880 if (last_slash == NULL) 01881 len = 1; 01882 else 01883 len = last_slash - path + 1; 01884 temppath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 01885 if (!temppath) 01886 return E_OUTOFMEMORY; 01887 StrCpyNW(temppath, path, len); 01888 realpath = temppath; 01889 } 01890 else 01891 { 01892 realpath = path; 01893 } 01894 01895 /* try to create the directory if asked to */ 01896 if (flags & (SHPPFW_DIRCREATE|SHPPFW_ASKDIRCREATE)) 01897 { 01898 if (flags & SHPPFW_ASKDIRCREATE) 01899 FIXME("treating SHPPFW_ASKDIRCREATE as SHPPFW_DIRCREATE\n"); 01900 01901 SHCreateDirectoryExW(0, realpath, NULL); 01902 } 01903 01904 /* check if we can access the directory */ 01905 res = GetFileAttributesW(realpath); 01906 01907 HeapFree(GetProcessHeap(), 0, temppath); 01908 01909 if (res == INVALID_FILE_ATTRIBUTES) 01910 { 01911 err = GetLastError(); 01912 if (err == ERROR_FILE_NOT_FOUND) 01913 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); 01914 return HRESULT_FROM_WIN32(err); 01915 } 01916 else if (res & FILE_ATTRIBUTE_DIRECTORY) 01917 return S_OK; 01918 else 01919 return HRESULT_FROM_WIN32(ERROR_DIRECTORY); 01920 } 01921 01922 /*********************************************************************** 01923 * SHPathPrepareForWriteA (SHELL32.@) 01924 */ 01925 EXTERN_C HRESULT WINAPI SHPathPrepareForWriteA(HWND hwnd, IUnknown *modless, LPCSTR path, DWORD flags) 01926 { 01927 WCHAR wpath[MAX_PATH]; 01928 MultiByteToWideChar( CP_ACP, 0, path, -1, wpath, MAX_PATH); 01929 return SHPathPrepareForWriteW(hwnd, modless, wpath, flags); 01930 } Generated on Mon May 28 2012 04:26:06 for ReactOS by
1.7.6.1
|