You've already forked wine-staging
mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2025-04-13 14:42:51 -07:00
Added patch for support of shell32 file operation progress dialog.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,453 @@
|
||||
From a6f9101ab518d9cf5a9ab34533ba1d684ee61d8b Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
|
||||
Date: Fri, 27 Feb 2015 01:04:33 +0100
|
||||
Subject: shell32: Implement file operation progress dialog.
|
||||
|
||||
Based on a patch by Huw Campbell.
|
||||
---
|
||||
dlls/shell32/shell32.rc | 7 ++
|
||||
dlls/shell32/shlfileop.c | 277 +++++++++++++++++++++++++++++++++++++++++++++--
|
||||
dlls/shell32/shresdef.h | 8 ++
|
||||
3 files changed, 285 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/dlls/shell32/shell32.rc b/dlls/shell32/shell32.rc
|
||||
index 37acbe7..dd054fb 100644
|
||||
--- a/dlls/shell32/shell32.rc
|
||||
+++ b/dlls/shell32/shell32.rc
|
||||
@@ -184,6 +184,13 @@ If the files in the destination folder have the same names as files in the\n\
|
||||
selected folder they will be replaced. Do you still want to move or copy\n\
|
||||
the folder?"
|
||||
|
||||
+ IDS_FILEOP_COPYING "Copying"
|
||||
+ IDS_FILEOP_MOVING "Moving"
|
||||
+ IDS_FILEOP_DELETING "Deleting"
|
||||
+ IDS_FILEOP_FROM_TO "From %1 to %2"
|
||||
+ IDS_FILEOP_FROM "From %1"
|
||||
+ IDS_FILEOP_PREFLIGHT "Preflight"
|
||||
+
|
||||
/* message box strings */
|
||||
IDS_RESTART_TITLE "Restart"
|
||||
IDS_RESTART_PROMPT "Do you want to simulate a Windows reboot?"
|
||||
diff --git a/dlls/shell32/shlfileop.c b/dlls/shell32/shlfileop.c
|
||||
index ed8ff38..cef2246 100644
|
||||
--- a/dlls/shell32/shlfileop.c
|
||||
+++ b/dlls/shell32/shlfileop.c
|
||||
@@ -65,6 +65,10 @@ typedef struct
|
||||
DWORD dwYesToAllMask;
|
||||
BOOL bManyItems;
|
||||
BOOL bCancelled;
|
||||
+ IProgressDialog *progress;
|
||||
+ ULARGE_INTEGER completedSize;
|
||||
+ ULARGE_INTEGER totalSize;
|
||||
+ WCHAR szBuilderString[64];
|
||||
} FILE_OPERATION;
|
||||
|
||||
typedef struct
|
||||
@@ -103,6 +107,12 @@ static DWORD SHFindAttrW(LPCWSTR pName, BOOL fileOnly);
|
||||
static DWORD copy_files(FILE_OPERATION *op, BOOL multidest, const FILE_LIST *flFrom, FILE_LIST *flTo);
|
||||
static DWORD move_files(FILE_OPERATION *op, BOOL multidest, const FILE_LIST *flFrom, const FILE_LIST *flTo);
|
||||
|
||||
+static void progressbar_calc_totalsize(FILE_OPERATION *op, const FILE_LIST *from);
|
||||
+static void progressbar_update_title(FILE_OPERATION *op);
|
||||
+static void progressbar_update_files(FILE_OPERATION *op, LPCWSTR src, LPCWSTR dst);
|
||||
+static DWORD CALLBACK progressbar_copy_routine(LARGE_INTEGER total_size, LARGE_INTEGER total_transferred, LARGE_INTEGER stream_size,
|
||||
+ LARGE_INTEGER stream_transferred, DWORD stream_number, DWORD reason, HANDLE src_file, HANDLE dst_file, LPVOID user);
|
||||
+
|
||||
/* Confirm dialogs with an optional "Yes To All" as used in file operations confirmations
|
||||
*/
|
||||
static const WCHAR CONFIRM_MSG_PROP[] = {'W','I','N','E','_','C','O','N','F','I','R','M',0};
|
||||
@@ -395,6 +405,13 @@ static DWORD SHELL_DeleteDirectoryW(FILE_OPERATION *op, LPCWSTR pszDir, BOOL bSh
|
||||
ret = SHELL_DeleteDirectoryW(op, szTemp, FALSE);
|
||||
else
|
||||
ret = SHNotifyDeleteFileW(op, szTemp);
|
||||
+
|
||||
+ /* Check if dialog was cancelled in the meantime */
|
||||
+ if (op->progress != NULL)
|
||||
+ op->bCancelled |= IProgressDialog_HasUserCancelled(op->progress);
|
||||
+ if (op->bCancelled)
|
||||
+ break;
|
||||
+
|
||||
} while (!ret && FindNextFileW(hFind, &wfd));
|
||||
}
|
||||
FindClose(hFind);
|
||||
@@ -548,10 +565,22 @@ static DWORD SHNotifyDeleteFileA(FILE_OPERATION *op, LPCSTR path)
|
||||
static DWORD SHNotifyDeleteFileW(FILE_OPERATION *op, LPCWSTR path)
|
||||
{
|
||||
BOOL ret;
|
||||
+ LARGE_INTEGER filesize;
|
||||
+ filesize.QuadPart = 0;
|
||||
|
||||
TRACE("(%s)\n", debugstr_w(path));
|
||||
|
||||
- /* FIXME: Implement progress dialog - op can also be zero! */
|
||||
+ /* Warning: can also be called with empty op */
|
||||
+ if (op)
|
||||
+ {
|
||||
+ WIN32_FILE_ATTRIBUTE_DATA info;
|
||||
+ progressbar_update_files(op, path, NULL);
|
||||
+ if (GetFileAttributesExW(path, GetFileExInfoStandard, &info))
|
||||
+ {
|
||||
+ filesize.u.HighPart = info.nFileSizeHigh;
|
||||
+ filesize.u.LowPart = info.nFileSizeLow;
|
||||
+ }
|
||||
+ }
|
||||
|
||||
ret = DeleteFileW(path);
|
||||
if (!ret)
|
||||
@@ -564,6 +593,14 @@ static DWORD SHNotifyDeleteFileW(FILE_OPERATION *op, LPCWSTR path)
|
||||
}
|
||||
if (ret)
|
||||
{
|
||||
+ if (op)
|
||||
+ {
|
||||
+ /* There is no progress while deleting a file,
|
||||
+ * simply report full file size when we are done. */
|
||||
+ progressbar_copy_routine(filesize, filesize, filesize, filesize, 0,
|
||||
+ CALLBACK_STREAM_SWITCH, NULL, NULL, op);
|
||||
+ }
|
||||
+
|
||||
SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, path, NULL);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
@@ -598,9 +635,10 @@ static DWORD SHNotifyMoveFileW(FILE_OPERATION *op, LPCWSTR src, LPCWSTR dest)
|
||||
|
||||
TRACE("(%s %s)\n", debugstr_w(src), debugstr_w(dest));
|
||||
|
||||
- /* FIXME: Implement progress dialog */
|
||||
+ progressbar_update_files(op, src, dest);
|
||||
|
||||
- ret = MoveFileExW(src, dest, MOVEFILE_REPLACE_EXISTING);
|
||||
+ ret = MoveFileWithProgressW(src, dest, progressbar_copy_routine,
|
||||
+ op, MOVEFILE_REPLACE_EXISTING);
|
||||
|
||||
/* MOVEFILE_REPLACE_EXISTING fails with dirs, so try MoveFile */
|
||||
if (!ret)
|
||||
@@ -650,14 +688,15 @@ static DWORD SHNotifyCopyFileW(FILE_OPERATION *op, LPCWSTR src, LPCWSTR dest, BO
|
||||
|
||||
TRACE("(%s %s %s)\n", debugstr_w(src), debugstr_w(dest), bFailIfExists ? "failIfExists" : "");
|
||||
|
||||
- /* FIXME: Update progress dialog */
|
||||
+ progressbar_update_files(op, src, dest);
|
||||
|
||||
/* Destination file may already exist with read only attribute */
|
||||
attribs = GetFileAttributesW(dest);
|
||||
if (IsAttrib(attribs, FILE_ATTRIBUTE_READONLY))
|
||||
SetFileAttributesW(dest, attribs & ~FILE_ATTRIBUTE_READONLY);
|
||||
|
||||
- ret = CopyFileW(src, dest, bFailIfExists);
|
||||
+ ret = CopyFileExW(src, dest, progressbar_copy_routine, op,
|
||||
+ &op->bCancelled, bFailIfExists);
|
||||
if (ret)
|
||||
{
|
||||
SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, dest, NULL);
|
||||
@@ -1293,6 +1332,8 @@ static DWORD copy_files(FILE_OPERATION *op, BOOL multidest, const FILE_LIST *flF
|
||||
}
|
||||
|
||||
/* Vista return code. XP would return e.g. ERROR_FILE_NOT_FOUND, ERROR_ALREADY_EXISTS */
|
||||
+ if (op->progress != NULL)
|
||||
+ op->bCancelled |= IProgressDialog_HasUserCancelled(op->progress);
|
||||
if (op->bCancelled)
|
||||
return ERROR_CANCELLED;
|
||||
}
|
||||
@@ -1374,13 +1415,17 @@ static DWORD delete_files(FILE_OPERATION *op, const FILE_LIST *flFrom)
|
||||
|
||||
/* delete the file or directory */
|
||||
if (IsAttribFile(fileEntry->attributes))
|
||||
- ret = DeleteFileW(fileEntry->szFullPath) ?
|
||||
- ERROR_SUCCESS : GetLastError();
|
||||
+ ret = SHNotifyDeleteFileW(op, fileEntry->szFullPath);
|
||||
else
|
||||
ret = SHELL_DeleteDirectoryW(op, fileEntry->szFullPath, FALSE);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
+
|
||||
+ if (op->progress != NULL)
|
||||
+ op->bCancelled |= IProgressDialog_HasUserCancelled(op->progress);
|
||||
+ if (op->bCancelled)
|
||||
+ return ERROR_CANCELLED;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
@@ -1485,6 +1530,11 @@ static DWORD move_files(FILE_OPERATION *op, BOOL multidest, const FILE_LIST *flF
|
||||
move_to_dir(op, entryToMove, fileDest);
|
||||
else
|
||||
SHNotifyMoveFileW(op, entryToMove->szFullPath, fileDest->szFullPath);
|
||||
+
|
||||
+ if (op->progress != NULL)
|
||||
+ op->bCancelled |= IProgressDialog_HasUserCancelled(op->progress);
|
||||
+ if (op->bCancelled)
|
||||
+ return ERROR_CANCELLED;
|
||||
}
|
||||
|
||||
if (mismatched > 0)
|
||||
@@ -1544,6 +1594,7 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
|
||||
{
|
||||
FILE_OPERATION op;
|
||||
FILE_LIST flFrom, flTo;
|
||||
+ HRESULT co_ret = E_FAIL;
|
||||
int ret = 0;
|
||||
|
||||
if (!lpFileOp)
|
||||
@@ -1562,9 +1613,31 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
|
||||
|
||||
ZeroMemory(&op, sizeof(op));
|
||||
op.req = lpFileOp;
|
||||
+ op.totalSize.QuadPart = 0;
|
||||
+ op.completedSize.QuadPart = 0;
|
||||
op.bManyItems = (flFrom.dwNumFiles > 1);
|
||||
lpFileOp->fAnyOperationsAborted = FALSE;
|
||||
|
||||
+ if (lpFileOp->wFunc != FO_RENAME && !(lpFileOp->fFlags & FOF_SILENT))
|
||||
+ {
|
||||
+ co_ret = CoInitialize(NULL);
|
||||
+ ret = CoCreateInstance(&CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER,
|
||||
+ &IID_IProgressDialog, (void**)&op.progress);
|
||||
+ if (SUCCEEDED(ret))
|
||||
+ {
|
||||
+ IProgressDialog_StartProgressDialog(op.progress, op.req->hwnd, NULL,
|
||||
+ PROGDLG_NORMAL | PROGDLG_AUTOTIME, NULL);
|
||||
+
|
||||
+ progressbar_update_title(&op);
|
||||
+ progressbar_calc_totalsize(&op, &flFrom);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ FIXME("Failed to create progress dialog\n");
|
||||
+ op.progress = NULL;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
switch (lpFileOp->wFunc)
|
||||
{
|
||||
case FO_COPY:
|
||||
@@ -1584,6 +1657,12 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
|
||||
break;
|
||||
}
|
||||
|
||||
+ if (op.progress)
|
||||
+ {
|
||||
+ IProgressDialog_StopProgressDialog(op.progress);
|
||||
+ IProgressDialog_Release(op.progress);
|
||||
+ }
|
||||
+
|
||||
destroy_file_list(&flFrom);
|
||||
|
||||
if (lpFileOp->wFunc != FO_DELETE)
|
||||
@@ -1592,6 +1671,9 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
|
||||
if (ret == ERROR_CANCELLED)
|
||||
lpFileOp->fAnyOperationsAborted = TRUE;
|
||||
|
||||
+ if (SUCCEEDED(co_ret))
|
||||
+ CoUninitialize();
|
||||
+
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1822,3 +1904,184 @@ HRESULT WINAPI SHPathPrepareForWriteW(HWND hwnd, IUnknown *modless, LPCWSTR path
|
||||
else
|
||||
return HRESULT_FROM_WIN32(ERROR_DIRECTORY);
|
||||
}
|
||||
+
|
||||
+static BOOL progressbar_calc_size(FILE_OPERATION *op, LPWSTR buf, BOOL is_folder, DWORD *ticks)
|
||||
+{
|
||||
+ WIN32_FIND_DATAW wfd;
|
||||
+ HANDLE find;
|
||||
+ UINT i = strlenW(buf);
|
||||
+ WCHAR *file = buf + i;
|
||||
+ size_t size = MAX_PATH - i;
|
||||
+
|
||||
+ if (size < 3)
|
||||
+ return FALSE;
|
||||
+
|
||||
+ if (is_folder)
|
||||
+ {
|
||||
+ *file++ = '\\';
|
||||
+ size--;
|
||||
+
|
||||
+ file[0] = '*';
|
||||
+ file[1] = 0;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ file[0] = 0;
|
||||
+ }
|
||||
+
|
||||
+ find = FindFirstFileW(buf, &wfd);
|
||||
+ if (find == INVALID_HANDLE_VALUE)
|
||||
+ {
|
||||
+ WARN("FindFirstFileW %s failed\n", debugstr_w(buf));
|
||||
+ return FALSE;
|
||||
+ }
|
||||
+
|
||||
+ do
|
||||
+ {
|
||||
+ if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
+ {
|
||||
+ if (wfd.cFileName[0] == '.')
|
||||
+ {
|
||||
+ if (wfd.cFileName[1] == 0) continue;
|
||||
+ if (wfd.cFileName[1] == '.' && wfd.cFileName[2] == 0) continue;
|
||||
+ }
|
||||
+
|
||||
+ if (!lstrcpynW(file, wfd.cFileName, size)) continue;
|
||||
+ progressbar_calc_size(op, buf, TRUE, ticks);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ LARGE_INTEGER filesize;
|
||||
+ filesize.u.LowPart = wfd.nFileSizeLow;
|
||||
+ filesize.u.HighPart = wfd.nFileSizeHigh;
|
||||
+ op->totalSize.QuadPart += filesize.QuadPart;
|
||||
+ }
|
||||
+
|
||||
+ if (GetTickCount() - *ticks > (DWORD) 500)
|
||||
+ {
|
||||
+ if (op->progress != NULL)
|
||||
+ op->bCancelled |= IProgressDialog_HasUserCancelled(op->progress);
|
||||
+ if (op->bCancelled)
|
||||
+ break;
|
||||
+ *ticks = GetTickCount();
|
||||
+ }
|
||||
+
|
||||
+ }
|
||||
+ while (FindNextFileW(find, &wfd));
|
||||
+
|
||||
+ FindClose(find);
|
||||
+ return TRUE;
|
||||
+}
|
||||
+
|
||||
+static void progressbar_calc_totalsize(FILE_OPERATION *op, const FILE_LIST *from)
|
||||
+{
|
||||
+ WCHAR filename[MAX_PATH];
|
||||
+ DWORD ticks = GetTickCount();
|
||||
+ UINT i;
|
||||
+
|
||||
+ op->totalSize.QuadPart = 0;
|
||||
+
|
||||
+ for (i = 0; i < from->dwNumFiles && !op->bCancelled; i++)
|
||||
+ {
|
||||
+ if (!lstrcpynW(filename, from->feFiles[i].szFullPath, sizeof(filename)/sizeof(filename[0])))
|
||||
+ continue;
|
||||
+ progressbar_calc_size(op, filename, IsAttribDir(from->feFiles[i].attributes), &ticks);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void progressbar_update_title(FILE_OPERATION *op)
|
||||
+{
|
||||
+ WCHAR buf[64];
|
||||
+ UINT title_id, builder_id;
|
||||
+
|
||||
+ if (op->progress == NULL)
|
||||
+ return;
|
||||
+
|
||||
+ switch (op->req->wFunc)
|
||||
+ {
|
||||
+ case FO_COPY:
|
||||
+ title_id = IDS_FILEOP_COPYING;
|
||||
+ builder_id = IDS_FILEOP_FROM_TO;
|
||||
+ break;
|
||||
+
|
||||
+ case FO_DELETE:
|
||||
+ title_id = IDS_FILEOP_DELETING;
|
||||
+ builder_id = IDS_FILEOP_FROM;
|
||||
+ break;
|
||||
+
|
||||
+ case FO_MOVE:
|
||||
+ title_id = IDS_FILEOP_MOVING;
|
||||
+ builder_id = IDS_FILEOP_FROM_TO;
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ LoadStringW(shell32_hInstance, title_id, buf, sizeof(buf)/sizeof(WCHAR));
|
||||
+ IProgressDialog_SetTitle(op->progress, buf);
|
||||
+
|
||||
+ LoadStringW(shell32_hInstance, builder_id, op->szBuilderString,
|
||||
+ sizeof(op->szBuilderString)/sizeof(WCHAR));
|
||||
+
|
||||
+ LoadStringW(shell32_hInstance, IDS_FILEOP_PREFLIGHT, buf, sizeof(buf)/sizeof(WCHAR));
|
||||
+ IProgressDialog_SetLine(op->progress, 1, buf, FALSE, NULL);
|
||||
+}
|
||||
+
|
||||
+static void progressbar_update_files(FILE_OPERATION *op, LPCWSTR src, LPCWSTR dst)
|
||||
+{
|
||||
+ LPWSTR src_file, dst_file;
|
||||
+ WCHAR src_dir[64], dst_dir[64], final[260];
|
||||
+ DWORD_PTR args[2] = {0, 0};
|
||||
+
|
||||
+ if (!op->progress || !src || (op->req->wFunc == FO_MOVE && !dst))
|
||||
+ return;
|
||||
+
|
||||
+ if (op->req->wFunc != FO_COPY &&
|
||||
+ op->req->wFunc != FO_MOVE &&
|
||||
+ op->req->wFunc != FO_DELETE)
|
||||
+ {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ src_file = PathFindFileNameW(src);
|
||||
+ lstrcpynW(src_dir, src, min(sizeof(src_dir) / sizeof(WCHAR) - 1, src_file - src));
|
||||
+ args[0] = (DWORD_PTR)&src_dir;
|
||||
+
|
||||
+ if (op->req->wFunc == FO_MOVE ||
|
||||
+ op->req->wFunc == FO_COPY)
|
||||
+ {
|
||||
+ if (PathIsDirectoryW(dst))
|
||||
+ args[1] = (DWORD_PTR)&dst;
|
||||
+ else
|
||||
+ {
|
||||
+ dst_file = PathFindFileNameW(dst);
|
||||
+ lstrcpynW(dst_dir, dst, min(sizeof(dst_dir) / sizeof(WCHAR) - 1, dst_file - dst));
|
||||
+ args[1] = (DWORD_PTR)&dst_dir;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ FormatMessageW(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, op->szBuilderString,
|
||||
+ 0, 0, final, sizeof(final)/sizeof(final[0]), (__ms_va_list *)&args);
|
||||
+
|
||||
+ IProgressDialog_SetLine(op->progress, 1, src_file, FALSE, NULL);
|
||||
+ IProgressDialog_SetLine(op->progress, 2, final, FALSE, NULL);
|
||||
+}
|
||||
+
|
||||
+static DWORD CALLBACK progressbar_copy_routine(LARGE_INTEGER total_size, LARGE_INTEGER total_transferred, LARGE_INTEGER stream_size,
|
||||
+ LARGE_INTEGER stream_transferred, DWORD stream_number, DWORD reason, HANDLE src_file, HANDLE dst_file, LPVOID user)
|
||||
+{
|
||||
+ FILE_OPERATION *op = (FILE_OPERATION *)user;
|
||||
+
|
||||
+ if (!op->progress)
|
||||
+ return PROGRESS_CONTINUE;
|
||||
+
|
||||
+ if (reason == CALLBACK_STREAM_SWITCH)
|
||||
+ op->completedSize.QuadPart += total_size.QuadPart;
|
||||
+
|
||||
+ IProgressDialog_SetProgress64(op->progress, op->completedSize.QuadPart - total_size.QuadPart +
|
||||
+ total_transferred.QuadPart, op->totalSize.QuadPart);
|
||||
+
|
||||
+ op->bCancelled |= IProgressDialog_HasUserCancelled(op->progress);
|
||||
+ return op->bCancelled ? PROGRESS_CANCEL : PROGRESS_CONTINUE;
|
||||
+}
|
||||
diff --git a/dlls/shell32/shresdef.h b/dlls/shell32/shresdef.h
|
||||
index 183a75e..8ee525a 100644
|
||||
--- a/dlls/shell32/shresdef.h
|
||||
+++ b/dlls/shell32/shresdef.h
|
||||
@@ -149,6 +149,14 @@
|
||||
#define IDM_RECYCLEBIN_RESTORE 301
|
||||
#define IDM_RECYCLEBIN_ERASE 302
|
||||
|
||||
+/* Strings for file operations */
|
||||
+#define IDS_FILEOP_COPYING 333
|
||||
+#define IDS_FILEOP_MOVING 334
|
||||
+#define IDS_FILEOP_DELETING 335
|
||||
+#define IDS_FILEOP_FROM_TO 336
|
||||
+#define IDS_FILEOP_FROM 337
|
||||
+#define IDS_FILEOP_PREFLIGHT 338
|
||||
+
|
||||
/* Note: this string is referenced from the registry*/
|
||||
#define IDS_RECYCLEBIN_FOLDER_NAME 8964
|
||||
|
||||
--
|
||||
2.3.0
|
||||
|
2
patches/shell32-Progress_Dialog/definition
Normal file
2
patches/shell32-Progress_Dialog/definition
Normal file
@@ -0,0 +1,2 @@
|
||||
Fixes: Support for shell32 file operation progress dialog
|
||||
Depends: kernel32-CopyFileEx
|
Reference in New Issue
Block a user