Laden...

CopyFileEx (kernel32.dll): Dateifortschritt von einem anderem Programm ermitteln

Erstellt von heulendoch vor 9 Jahren Letzter Beitrag vor 9 Jahren 2.620 Views
H
heulendoch Themenstarter:in
23 Beiträge seit 2014
vor 9 Jahren
CopyFileEx (kernel32.dll): Dateifortschritt von einem anderem Programm ermitteln

Hallo Leute,

ich möchte zum Kopieren von Dateien nicht mehr die File.Copy bzw. File.Move Methode verwenden sondern die CopyFileEx Methode aus kernel32.dll. Das Kopieren funktioniert auch, nur habe ich jetzt das Problem das ich in einem anderem Programm prüfen möchte ob das Kopieren wirklich abgeschlossen ist.

Als ich noch die File.XXX-Methoden verwendet habe war das ganze kein Problem da ich z.B. prüfen konnte ob sich *die Dateigröße (FileInfo.Length) *der letzer Schreibzugriff (FileInfo.LastWriteTime) *der letzter Zugriff (FileInfo.LastAccessTime)

verändert hat. Durch CopyFileEx wird aber nach dem Erstellen der Datei (auch wenn sie noch nicht fertig kopiert ist) an diesen "Eigenschaften" nichts mehr verändert.

Bietet CopyFileEx eventuell doch die Möglichkeit nur ich habe den DllImport nicht korrekt, bzw. vollständig implementiert?

Im Prinzip verwende ich diesen Code hier:

/// <summary>
/// PInvoke wrapper for CopyEx
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa363852.aspx
/// </summary>
public class XCopy
{
    public static void Copy(string source, string destination, bool overwrite, bool nobuffering)
    {
         new XCopy().CopyInternal(source, destination, overwrite, nobuffering, null);            
    }

    public static void Copy(string source, string destination, bool overwrite, bool nobuffering, EventHandler<ProgressChangedEventArgs> handler)
    {            
         new XCopy().CopyInternal(source, destination, overwrite, nobuffering, handler);            
    }

    private event EventHandler Completed;
    private event EventHandler<ProgressChangedEventArgs> ProgressChanged;

    private int IsCancelled;
    private int FilePercentCompleted;
    private string Source;
    private string Destination;        

    private XCopy()
    {
        IsCancelled = 0;
    }

    private void CopyInternal(string source, string destination, bool overwrite, bool nobuffering, EventHandler<ProgressChangedEventArgs> handler)
    {
        try
        {
            CopyFileFlags copyFileFlags = CopyFileFlags.COPY_FILE_RESTARTABLE;
            if (!overwrite)
                copyFileFlags |= CopyFileFlags.COPY_FILE_FAIL_IF_EXISTS;

            if (nobuffering)
                copyFileFlags |= CopyFileFlags.COPY_FILE_NO_BUFFERING;

            Source = source;
            Destination = destination;

            if (handler != null)
                ProgressChanged += handler;

            bool result = CopyFileEx(Source, Destination, new CopyProgressRoutine(CopyProgressHandler), IntPtr.Zero, ref IsCancelled, copyFileFlags);
            if (!result)
                throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        catch (Exception)
        {
            if (handler != null)
                ProgressChanged -= handler;

            throw;
        }
    }

    private void OnProgressChanged(double percent)
    {
        // only raise an event when progress has changed
        if ((int)percent > FilePercentCompleted)
        {
            FilePercentCompleted = (int)percent;

            var handler = ProgressChanged;
            if (handler != null)
                handler(this, new ProgressChangedEventArgs((int)FilePercentCompleted, null));
        }
    }

    private void OnCompleted()
    {
        var handler = Completed;
        if (handler != null)
            handler(this, EventArgs.Empty);
    }

    #region PInvoke

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CopyFileEx(string lpExistingFileName, string lpNewFileName, CopyProgressRoutine lpProgressRoutine, IntPtr lpData, ref Int32 pbCancel, CopyFileFlags dwCopyFlags);

    private delegate CopyProgressResult CopyProgressRoutine(long TotalFileSize, long TotalBytesTransferred, long StreamSize, long StreamBytesTransferred, uint dwStreamNumber, CopyProgressCallbackReason dwCallbackReason,
                                                    IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData);

    private enum CopyProgressResult : uint
    {
        PROGRESS_CONTINUE = 0,
        PROGRESS_CANCEL = 1,
        PROGRESS_STOP = 2,
        PROGRESS_QUIET = 3
    }

    private enum CopyProgressCallbackReason : uint
    {
        CALLBACK_CHUNK_FINISHED = 0x00000000,
        CALLBACK_STREAM_SWITCH = 0x00000001
    }

    [Flags]
    private enum CopyFileFlags : uint
    {
        COPY_FILE_FAIL_IF_EXISTS = 0x00000001,
        COPY_FILE_NO_BUFFERING = 0x00001000,
        COPY_FILE_RESTARTABLE = 0x00000002,
        COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x00000004,
        COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x00000008
    }

    private CopyProgressResult CopyProgressHandler(long total, long transferred, long streamSize, long streamByteTrans, uint dwStreamNumber,
                                                   CopyProgressCallbackReason reason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData)
    {
        if (reason == CopyProgressCallbackReason.CALLBACK_CHUNK_FINISHED)
            OnProgressChanged((transferred / (double)total) * 100.0);

        if (transferred >= total)
            OnCompleted();

        return CopyProgressResult.PROGRESS_CONTINUE;
    }

    #endregion

}

Quelle: Can I show file copy progress using FileInfo.CopyTo() in .NET? (Antwort 8341945)

Vielen Dank schonmal!

16.842 Beiträge seit 2008
vor 9 Jahren

Hi,

nein, das funktioniert nicht.
Bei CopyFileEx wird ja ein delegate übergeben, das den Fortschritt dann quasi anzeigt.
Ein externer Prozess kennt aber dieses Delegate nicht.

Man kann durch einen entsprechenden Shell-Hook erkennen, ob über den Windows Explorer derzeit kopiert wird und wann dieser endet. Aber ein attachen an "irgendein Copy" über die Win32 API ist leider nicht möglich.

Du musst also zB über eine Prozesskommunikation ein Event werfen, das andere Prozesse abonnieren können.
Alternativ kannst Du sogenannte I/O Filter verwenden, worauf die gesamte Dateikommunikation eigentlich bei Windows basiert: File System Filter Driver Development. Das verwenden auch AV-Anwendungen.

Standardmäßig können .NET System.IO Klassen keinen Progress anzeigen.
Dafür musst Du die Übertragung selbst übernehmen (Byte für Byte Übertragung über Streams etc), oder Du schaust Dir mal http://quickIO.NET an.

H
heulendoch Themenstarter:in
23 Beiträge seit 2014
vor 9 Jahren

Hi,

ich habe jetzt mehr oder weniger die Anwendung so geschrieben das ich einfach mehrere Threads habe etc. dann Speicher ich in einer Liste, während des Kopierens das die Datei noch nicht fertig ist und erst dann wenn die Datei nicht in der Liste eine Weiterverarbeitung statt finden kann

Jeden Falls funktioniert es so wie ich es mir vorgestellt habe.

Aber danke für die einige interessante Begriffe die du genannt hast 👍

16.842 Beiträge seit 2008
vor 9 Jahren

Ist das eine Anwendung, oder jetzt doch zwei?

Wenns nur eine Anwendung bzw. Prozess ist:
Für mich hört sich das gerade an wie ein Workflow innerhalb einer Anwendung.
Das wäre nämlich identisch mit dem sogenannten Pipelining und lässt sich sehr einfach, konstistenz und perfoment mit der TPL und dessen TPL Pipelining-Ansatz umsetzen ( auch DataFlow Pipeline genannt).