Kernel32: CopyFileExW support (Try2)

Arthur Ice addtheice at gmail.com
Wed Jun 22 16:40:40 CDT 2011


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-patches/attachments/20110622/23d7586a/attachment-0001.htm>
-------------- next part --------------
From 571adf55d1fec75116e50476350eab290c5921a2 Mon Sep 17 00:00:00 2001
From: Arthur Dene Ice <addtheice at gmail.com>
Date: Wed, 22 Jun 2011 14:10:52 -0700
Subject: Previous version of CopyFileEx(W,A) failed to use the callback function. This version correctly calls the callback function and handles on the fly canceling and callback canceling.

Added support for callback calls.
---
 dlls/kernel32/path.c       |  179 +++++++++++++++++++++++++++++++++++++++++++-
 dlls/kernel32/tests/file.c |   37 +++++++++
 2 files changed, 212 insertions(+), 4 deletions(-)

diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c
index e72848c..6778294 100644
--- a/dlls/kernel32/path.c
+++ b/dlls/kernel32/path.c
@@ -1023,10 +1023,181 @@ BOOL WINAPI CopyFileExW(LPCWSTR sourceFilename, LPCWSTR destFilename,
                         LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
                         LPBOOL cancelFlagPointer, DWORD copyFlags)
 {
-    /*
-     * Interpret the only flag that CopyFile can interpret.
-     */
-    return CopyFileW(sourceFilename, destFilename, (copyFlags & COPY_FILE_FAIL_IF_EXISTS) != 0);
+    static const int buffer_size = 65536;
+    HANDLE h1, h2;
+    BY_HANDLE_FILE_INFORMATION info;
+    DWORD count;
+    BOOL ret = FALSE;
+    BOOL fail_if_exists = (copyFlags & COPY_FILE_FAIL_IF_EXISTS) != 0;
+    char *buffer;
+    LARGE_INTEGER TotalFileSize;
+    LARGE_INTEGER TotalBytesTransferred;
+    LARGE_INTEGER StreamSize;
+    LARGE_INTEGER StreamBytesTransferred;
+    DWORD CallbackResults = 0;
+    DWORD dwStreamNumber = 1;
+    DWORD dwCallbackReason = 1; /* CALLBACK_STREAM_SWITCH */
+    BOOL cancelFlagDummy = TRUE;
+    if (cancelFlagPointer == NULL)
+        cancelFlagPointer = &cancelFlagDummy;
+
+
+    TotalFileSize.QuadPart = 0;
+    TotalBytesTransferred.QuadPart = 0;
+    StreamSize.QuadPart= 0;
+    StreamBytesTransferred.QuadPart = 0;
+
+
+    if (!sourceFilename)
+    {
+        SetLastError(ERROR_PATH_NOT_FOUND);
+        /* seems strange but testing shows that it returns */
+        /* ERROR_PATH_NOT_FOUND on sourceFilename NULL */
+        return FALSE;
+    }
+
+    if (!destFilename)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        /* Testing shows this actually causes a crash rather */
+        /* then invalid parameter, but lets be better behaved. */
+        return FALSE;
+    }
+
+    if (!(buffer =(char *) HeapAlloc( GetProcessHeap(), 0, buffer_size )))
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return FALSE;
+    }
+
+    TRACE("%s -> %s\n", debugstr_w(sourceFilename), debugstr_w(destFilename));
+
+    if ((h1 = CreateFileW(sourceFilename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+            NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
+    {
+        WARN("Unable to open source %s\n", debugstr_w(sourceFilename));
+        HeapFree( GetProcessHeap(), 0, buffer );
+        return FALSE;
+    }
+
+    if (!GetFileInformationByHandle( h1, &info ))
+    {
+        WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(sourceFilename));
+        HeapFree( GetProcessHeap(), 0, buffer );
+        CloseHandle( h1 );
+        return FALSE;
+    }
+
+    TotalFileSize.QuadPart = info.nFileSizeHigh;
+    TotalFileSize.QuadPart = (TotalFileSize.QuadPart << 32)+ info.nFileSizeLow;
+    StreamSize.QuadPart = TotalFileSize.QuadPart;
+
+    if (!fail_if_exists)
+    {
+        BOOL same_file = FALSE;
+        h2 = CreateFileW( destFilename, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                OPEN_EXISTING, 0, 0);
+
+        if (h2 != INVALID_HANDLE_VALUE)
+        {
+            same_file = is_same_file( h1, h2 );
+            CloseHandle( h2 );
+        }
+        if (same_file)
+        {
+            HeapFree( GetProcessHeap(), 0, buffer );
+            CloseHandle( h1 );
+            SetLastError( ERROR_SHARING_VIOLATION );
+            return FALSE;
+        }
+    }
+
+    if ((h2 = CreateFileW( destFilename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                fail_if_exists ? CREATE_NEW : CREATE_ALWAYS,
+                info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE)
+    {
+        WARN("Unable to open dest %s\n", debugstr_w(destFilename));
+        HeapFree( GetProcessHeap(), 0, buffer );
+        CloseHandle( h1 );
+        return FALSE;
+    }
+
+    if (progressRoutine != NULL)
+        CallbackResults = (*progressRoutine)(TotalFileSize,TotalBytesTransferred,StreamSize,
+                            StreamBytesTransferred,dwStreamNumber,dwCallbackReason,
+                            h1,h2,appData);
+
+    if (CallbackResults == PROGRESS_CANCEL)
+    {
+        ret = FALSE;
+        goto done;
+    } else if (CallbackResults == PROGRESS_STOP)
+    {
+        ret = FALSE;
+        SetLastError(ERROR_REQUEST_ABORTED);
+        goto done;
+    } else if (CallbackResults == PROGRESS_QUIET)
+    {
+        progressRoutine == NULL;
+    }
+
+    dwCallbackReason = 0;
+
+    while (ReadFile( h1, buffer, buffer_size, &count, NULL ) && count)
+    {
+        char *p = buffer;
+        while (count != 0)
+        {
+            DWORD res;
+            if (!WriteFile( h2, p, count, &res, NULL ) || !res)
+                goto done;
+            p += res;
+            TotalBytesTransferred.QuadPart += res;
+            count -= res;
+        }
+
+        if (*cancelFlagPointer == TRUE)
+        {
+            ret = FALSE;
+            SetLastError(ERROR_REQUEST_ABORTED);
+            goto done;
+        }
+
+        if (progressRoutine != NULL)
+            CallbackResults = (*progressRoutine)(TotalFileSize,TotalBytesTransferred,StreamSize,
+                                StreamBytesTransferred,dwStreamNumber,dwCallbackReason,
+                                h1,h2,appData);
+        if (CallbackResults == PROGRESS_CANCEL)
+        {
+            ret = FALSE;
+            SetLastError(ERROR_REQUEST_ABORTED);
+            goto delete;
+        } else if (CallbackResults == PROGRESS_STOP)
+        {
+            ret = FALSE;
+            SetLastError(ERROR_REQUEST_ABORTED);
+            goto done;
+        } else if (CallbackResults == PROGRESS_QUIET)
+        {
+            progressRoutine == NULL;
+        }
+    }
+    ret =  TRUE;
+
+done:
+    /* Maintain the timestamp of source file to destination file */
+    SetFileTime(h2, NULL, NULL, &info.ftLastWriteTime);
+    HeapFree( GetProcessHeap(), 0, buffer );
+    CloseHandle( h1 );
+    CloseHandle( h2 );
+    return ret;
+
+delete:
+    HeapFree( GetProcessHeap(), 0, buffer );
+    CloseHandle( h1 );
+    CloseHandle( h2 );
+    DeleteFileW(destFilename);
+    return ret;
 }
 
 
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c
index 90b90e8..8292a5f 100644
--- a/dlls/kernel32/tests/file.c
+++ b/dlls/kernel32/tests/file.c
@@ -779,6 +779,42 @@ static void test_CopyFileW(void)
     ok(ret, "DeleteFileW: error %d\n", GetLastError());
 }
 
+static void test_CopyFileExW(void)
+{
+    WCHAR temp_path[MAX_PATH];
+    WCHAR source[MAX_PATH], dest[MAX_PATH];
+    static const WCHAR prefix[] = {'p','f','x',0};
+    DWORD ret;
+
+    ret = GetTempPathW(MAX_PATH, temp_path);
+    if (ret == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+    {
+        win_skip("GetTempPathW is not available\n");
+        return;
+    }
+    ok(ret != 0, "GetTempPathW error %d\n", GetLastError());
+    ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+    ret = GetTempFileNameW(temp_path, prefix, 0, source);
+    ok(ret != 0, "GetTempFileNameW error %d\n", GetLastError());
+
+    ret = GetTempFileNameW(temp_path, prefix, 0, dest);
+    ok(ret != 0, "GetTempFileNameW error %d\n", GetLastError());
+
+
+
+    ret = CopyFileExW(source, dest,NULL,NULL,NULL, TRUE);
+    ok(!ret && GetLastError() == ERROR_FILE_EXISTS,
+        "CopyFileExW: unexpected error %d\n", GetLastError());
+
+    ret = CopyFileExW(source, dest,NULL,NULL,NULL, FALSE);
+    ok(ret, "CopyFileExW: error %d\n", GetLastError());
+
+    ret = DeleteFileW(source);
+    ok(ret, "DeleteFileW: error %d\n", GetLastError());
+    ret = DeleteFileW(dest);
+    ok(ret, "DeleteFileW: error %d\n", GetLastError());
+}
 
 /*
  *   Debugging routine to dump a buffer in a hexdump-like fashion.
@@ -3134,6 +3170,7 @@ START_TEST(file)
     test_GetTempFileNameA();
     test_CopyFileA();
     test_CopyFileW();
+    test_CopyFileExW();
     test_CreateFileA();
     test_CreateFileW();
     test_DeleteFileA();
-- 
1.7.4.1


More information about the wine-patches mailing list