[PATCH 1/1] kernel32: Unstub MoveFileWithProgress, add test.
James McDonnell
wine at gitlab.winehq.org
Fri May 27 18:02:00 CDT 2022
From: James McDonnell <topgamer7 at gmail.com>
---
dlls/kernel32/path.c | 20 ++++--
dlls/kernel32/tests/file.c | 139 +++++++++++++++++++++++++++++++++++++
dlls/kernelbase/file.c | 58 ++++++++++++++--
3 files changed, 206 insertions(+), 11 deletions(-)
diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c
index 2dd3eac3c26..180edf358fa 100644
--- a/dlls/kernel32/path.c
+++ b/dlls/kernel32/path.c
@@ -143,9 +143,13 @@ BOOL WINAPI CopyFileExA(LPCSTR sourceFilename, LPCSTR destFilename,
*/
BOOL WINAPI MoveFileTransactedA(const char *source, const char *dest, LPPROGRESS_ROUTINE progress, void *data, DWORD flags, HANDLE handle)
{
- FIXME("(%s, %s, %p, %p, %ld, %p)\n", debugstr_a(source), debugstr_a(dest), progress, data, flags, handle);
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
- return FALSE;
+ BOOL ret;
+
+ TRACE("(%s, %s, %p, %p, %ld, %p)\n", debugstr_a(source), debugstr_a(dest), progress, data, flags, handle);
+
+ ret = MoveFileWithProgressA(source, dest, progress, data, flags);
+ FIXME("Transaction handle not finalized.");
+ return ret;
}
/**************************************************************************
@@ -153,9 +157,13 @@ BOOL WINAPI MoveFileTransactedA(const char *source, const char *dest, LPPROGRESS
*/
BOOL WINAPI MoveFileTransactedW(const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUTINE progress, void *data, DWORD flags, HANDLE handle)
{
- FIXME("(%s, %s, %p, %p, %ld, %p)\n", debugstr_w(source), debugstr_w(dest), progress, data, flags, handle);
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
- return FALSE;
+ BOOL ret;
+
+ TRACE("(%s, %s, %p, %p, %ld, %p)\n", debugstr_w(source), debugstr_w(dest), progress, data, flags, handle);
+
+ ret = MoveFileWithProgressW(source, dest, progress, data, flags);
+ FIXME("Transaction handle not finalized.");
+ return ret;
}
/**************************************************************************
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c
index bd8d8c91b2f..cf4f0fc126a 100644
--- a/dlls/kernel32/tests/file.c
+++ b/dlls/kernel32/tests/file.c
@@ -6139,6 +6139,144 @@ static void test_eof(void)
ok(ret, "failed to delete %s, error %lu\n", debugstr_a(filename), GetLastError());
}
+const char * reasons[2] = {
+ "CALLBACK_CHUNK_FINISHED",
+ "CALLBACK_STREAM_SWITCH"
+};
+
+typedef struct {
+ LARGE_INTEGER TotalFileSize;
+ LARGE_INTEGER TotalBytesTransferred;
+ LARGE_INTEGER StreamSize;
+ LARGE_INTEGER StreamBytesTransferred;
+ DWORD dwStreamNumber;
+ DWORD dwCallbackReason;
+ HANDLE hSourceFile;
+ HANDLE hDestinationFile;
+} ProgressCall;
+
+typedef struct
+{
+ ProgressCall *calls;
+ int call_count;
+ int calls_size;
+} ProgressOutput;
+
+DWORD LpprogressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred,
+ DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, ProgressOutput *lpData)
+{
+ ProgressCall call;
+ if (dwStreamNumber > lpData->calls_size) return 0;
+ call = (ProgressCall){
+ .TotalFileSize = TotalFileSize,
+ .TotalBytesTransferred = TotalBytesTransferred,
+ .StreamSize = StreamSize,
+ .StreamBytesTransferred = StreamBytesTransferred,
+ .dwStreamNumber = dwStreamNumber,
+ .dwCallbackReason = dwCallbackReason,
+ .hSourceFile = hSourceFile,
+ .hDestinationFile = hDestinationFile
+ };
+ lpData->calls[lpData->call_count] = call;
+ lpData->call_count += 1;
+
+ return PROGRESS_CONTINUE;
+}
+
+static void check_progress_output(ProgressOutput progress_output, int expected_stream_count)
+{
+ int i, current_stream_number = 0, last_call = progress_output.call_count-1;
+ LARGE_INTEGER transfer_sum = {{0}}, stream_sum = {{0}};
+
+ ok(progress_output.call_count > 0,"Expected some progress calls, received %d\n", progress_output.call_count);
+
+ for (i = 0; i < progress_output.call_count; i++) {
+ ProgressCall call = progress_output.calls[i];
+
+ trace("i=%d %s TotalFileSize=%lld TotalBytesTransferred=%lld dwStreamNumber=%lu StreamBytesTransferred=%lld StreamSize=%lld\n",
+ i, reasons[call.dwCallbackReason], call.TotalFileSize.QuadPart, call.TotalBytesTransferred.QuadPart, call.dwStreamNumber,
+ call.StreamBytesTransferred.QuadPart, progress_output.calls[0].StreamSize.QuadPart);
+
+ if (call.dwCallbackReason == CALLBACK_STREAM_SWITCH) {
+ current_stream_number += 1;
+
+ ok(call.dwStreamNumber == current_stream_number,
+ "Received unexpected stream number, expected %d, actual %lu\n", current_stream_number, call.dwStreamNumber);
+
+ ok(call.StreamBytesTransferred.QuadPart == 0,
+ "Received unexpected stream bytes transferred, expected %lld, actual %lld\n", call.StreamBytesTransferred.QuadPart, transfer_sum.QuadPart);
+
+ if (i > 0) {
+ ok(stream_sum.QuadPart == call.StreamSize.QuadPart,
+ "Expected full stream was transferred\n");
+ }
+
+ stream_sum.QuadPart = 0;
+ } else if (call.dwCallbackReason == CALLBACK_CHUNK_FINISHED) {
+ transfer_sum.QuadPart = call.StreamBytesTransferred.QuadPart;
+ stream_sum.QuadPart = call.StreamBytesTransferred.QuadPart;
+ }
+ ok(transfer_sum.QuadPart == call.TotalBytesTransferred.QuadPart,
+ "Wrong total bytes transferred expected %lld, actual %lld\n", transfer_sum.QuadPart, call.TotalBytesTransferred.QuadPart);
+ }
+ ok(0 == progress_output.calls[0].TotalBytesTransferred.QuadPart,
+ "Expected no bytes transferred expected %d, actual %lld\n", 0, progress_output.calls[0].TotalBytesTransferred.QuadPart);
+
+ ok(transfer_sum.QuadPart == progress_output.calls[last_call].TotalFileSize.QuadPart,
+ "Wrong total bytes summation expected %lld, actual %lld\n", transfer_sum.QuadPart,
+ progress_output.calls[progress_output.call_count].TotalFileSize.QuadPart);
+
+ ok(expected_stream_count == progress_output.calls[last_call].dwStreamNumber,
+ "Wrong expected stream count %d, actual %lu\n", expected_stream_count, progress_output.calls[last_call].dwStreamNumber);
+}
+
+static void test_WithProgress(void)
+{
+ const char *ten = "1234567890";
+ HANDLE handle;
+ char path_src[MAX_PATH],
+ path_dest[MAX_PATH],
+ tmp_path[MAX_PATH],
+ buffer[70000];
+ ProgressCall calls[50];
+ ProgressOutput progress_output;
+ int i;
+
+ if (!GetTempPathA (sizeof (tmp_path), tmp_path))
+ {
+ return;
+ }
+
+ strcat (tmp_path, "filedir\\");
+ CreateDirectoryA(tmp_path, NULL);
+
+ strcpy(path_src, tmp_path);
+ strcpy(path_dest, tmp_path);
+ strcat (path_src, "testfile.ext.ext2");
+ strcat (path_dest, "testfile2.ext.ext2");
+
+ handle = CreateFileA(path_src, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
+ for (i = 0; i < 7000; i++)
+ {
+ snprintf(buffer, 70000, "%s%s", buffer, ten);
+ }
+ for (i = 0; i < 10; i++)
+ {
+ ok(WriteFile(handle, buffer, strlen(buffer), NULL, NULL), "Could not write to file");
+ }
+ CloseHandle(handle);
+
+ progress_output = (ProgressOutput){.calls_size = 10, .call_count = 0, .calls = (ProgressCall *)&calls};
+ ok(CopyFileExA(path_src, path_dest, (LPPROGRESS_ROUTINE)LpprogressRoutine, &progress_output, NULL, 0),
+ "Failed to copy file %ld %s %s\n", GetLastError(), path_src, path_dest);
+
+ check_progress_output(progress_output, 1);
+
+ DeleteFileA(path_src);
+ DeleteFileA(path_dest);
+ RemoveDirectoryA(tmp_path);
+}
+
START_TEST(file)
{
char temp_path[MAX_PATH];
@@ -6217,4 +6355,5 @@ START_TEST(file)
test_hard_link();
test_move_file();
test_eof();
+ test_WithProgress();
}
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
index 8ae982294f6..62e395e03c8 100644
--- a/dlls/kernelbase/file.c
+++ b/dlls/kernelbase/file.c
@@ -491,6 +491,37 @@ BOOL WINAPI DECLSPEC_HOTPATCH AreFileApisANSI(void)
}
+/***********************************************************************
+ * handle_progress_callback
+ * Wrapper for progress callback, if the return value is false, the copy or move operation should be cancelled
+ */
+BOOL handle_progress_callback(LPPROGRESS_ROUTINE progress, unsigned stream_count, LARGE_INTEGER total, LARGE_INTEGER total_bytes_written, unsigned int buffer_size, LARGE_INTEGER stream_bytes_transferred, BOOL *invoke_progress,
+ DWORD callback_reason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID param)
+{
+ DWORD ret_progress;
+ LARGE_INTEGER stream_size;
+ stream_size.QuadPart = buffer_size;
+
+ if (!*invoke_progress || !progress)
+ {
+ return TRUE;
+ }
+ ret_progress = progress(total, total_bytes_written, stream_size, stream_bytes_transferred, stream_count, callback_reason, hSourceFile, hDestinationFile, param);
+
+ switch(ret_progress)
+ {
+ case PROGRESS_STOP:
+ FIXME("Handle resumable copy/move operation");
+ case PROGRESS_CANCEL:
+ return FALSE;
+ case PROGRESS_QUIET:
+ *invoke_progress = 0;
+ break;
+ }
+ return TRUE;
+}
+
+
/***********************************************************************
* CopyFileExW (kernelbase.@)
*/
@@ -501,9 +532,10 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT
HANDLE h1, h2;
FILE_BASIC_INFORMATION info;
IO_STATUS_BLOCK io;
- DWORD count;
- BOOL ret = FALSE;
- char *buffer;
+ DWORD count, res, stream_count = 0;
+ BOOL ret = FALSE, invoke_progress = 1;
+ char *buffer, *p;
+ LARGE_INTEGER total, total_written = {{0}}, stream_written = {{0}};
if (!source || !dest)
{
@@ -541,6 +573,10 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT
return FALSE;
}
+ if (progress) {
+ total.u.LowPart = GetFileSize(h1, (LPDWORD)&total.u.HighPart);
+ }
+
if (!(flags & COPY_FILE_FAIL_IF_EXISTS))
{
BOOL same_file = FALSE;
@@ -569,15 +605,27 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT
return FALSE;
}
+ stream_count++;
while (ReadFile( h1, buffer, buffer_size, &count, NULL ) && count)
{
- char *p = buffer;
+ p = buffer;
+ if(total_written.QuadPart == 0 && !handle_progress_callback(progress, stream_count, total, total_written,buffer_size,
+ stream_written, &invoke_progress, CALLBACK_STREAM_SWITCH, h1, h2, param))
+ {
+ break;
+ }
while (count != 0)
{
- DWORD res;
if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done;
+ total_written.QuadPart += res;
+ stream_written.QuadPart += res;
p += res;
count -= res;
+ if(!handle_progress_callback(progress, stream_count, total, total_written, buffer_size,
+ stream_written, &invoke_progress, CALLBACK_CHUNK_FINISHED, h1, h2, param))
+ {
+ break;
+ }
}
}
ret = TRUE;
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/145
More information about the wine-devel
mailing list