ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 1063|回复: 11

[求助] 谁有 API 函数 CopyFileEx 复制大文件,并显示进度的代码

[复制链接]

TA的精华主题

TA的得分主题

发表于 2023-6-14 12:52 | 显示全部楼层 |阅读模式
本帖最后由 Cavan2022 于 2023-6-15 11:56 编辑

哪位老师:
       有API 函数 CopyFileEx 复制大文件,并且能显示进度的代码?

       分享一下,谢谢!!

TA的精华主题

TA的得分主题

发表于 2023-6-14 14:53 | 显示全部楼层
[API 函数] CopyFileEx - 复制档案
一般要处理复制档案使用 File.Copy 方法就可以达到需求,可是若在复制档案过程中要显示进度及取消复制,就需要使用 API 函数 CopyFileEx 来处理。下列范例程序使用 CoypFileEx 来进行复制档案,复制过程序会显示进度,按下「取消复制」钮可中断复制动作。

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-6-14 22:43 | 显示全部楼层
复制文件的方法有很多种,在这里我提供一种给大家使用,有需要的可以拿去试,但结果我就不做保证,需不需要看你!
在复制大型文件比较有用,可以显示进度,我用了API的CopyFileEx,如果你用CopyFile就只能等,CopyFileEx提供了一个回调函数功能,可以使用AddressOf到模块回调函数,这个就不说了,我在这里提供的类模块回调,不需要模块。具体代码如下:
类模块中:
Option Base 0
Option Explicit

'***********************************************************************
'自定义事件
Public Event FileCopyExBegin()
Public Event FileCopyExProgress(ByVal Progress As Long, ByRef Cancel As Boolean)
Public Event FileCopyExCancel()

'***********************************************************************
'分配内存API
Private Const HEAP_ZERO_MEMORY = &H8
Private Const PAGE_EXECUTE_READWRITE = &H40

Private Declare Sub CopyMemory Lib "Kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function IsBadReadPtr Lib "Kernel32" (Destination As Any, ByVal Length As Long) As Long
Private Declare Function GetProcessHeap Lib "Kernel32" () As Long
Private Declare Function HeapAlloc Lib "Kernel32" (ByVal hHeap As Long, ByVal dwFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function HeapFree Lib "Kernel32" (ByVal hHeap As Long, ByVal dwFlags As Long, lpMem As Any) As Long
Private Declare Function VirtualProtect Lib "Kernel32" (lpAddress As Any, ByVal dwSize As Long, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long

'************************************************************************
'复制文件API
Private Const COPY_FILE_FAIL_IF_EXISTS = &H1
Private Const COPY_FILE_RESTARTABLE = &H2

Private Const CALLBACK_STREAM_SWITCH = 1

Private Const PROGRESS_CONTINUE = 0
Private Const PROGRESS_CANCEL = 1
Private Const PROGRESS_STOP = 2
Private Const PROGRESS_QUIET = 3

Private Declare Function CopyFileExA Lib "Kernel32" (ByVal lpExistingFileName As String, ByVal lpNewFileName As String, ByVal lpProgressRoutine As Long, lpData As Any, ByVal pbCancel As Long, ByVal dwCopyFlags As Long) As Long

'*************************************************************************
'自定义类型
Private Type CopyFileExInfo
  CallBackPtr As Long
  CopyCancel As Boolean
  CopyFlags As Long
  CopyProgress As Long
End Type

Private Const CallBack_FileCopyEx = 3
Private FileCopyExInfo As CopyFileExInfo

'***************************************************************************
'IsOverWrite:如果文件存在是否覆盖源文件
Public Function FileCopyEx(ByVal SourPath As String, ByVal DestPath As String, Optional ByVal IsOverWrite As Boolean = False) As Boolean
  With FileCopyExInfo
    .CopyProgress = 0
    .CopyCancel = False
    .CallBackPtr = GetFileCopyExCallBackPtr(CallBack_FileCopyEx) '回调函数指针
    .CopyFlags = COPY_FILE_RESTARTABLE
    If Not IsOverWrite Then .CopyFlags = .CopyFlags Or COPY_FILE_FAIL_IF_EXISTS
    FileCopyEx = CBool(CopyFileExA(SourPath, DestPath, .CallBackPtr, ByVal 0&, VarPtr(.CopyCancel), .CopyFlags))
    If .CallBackPtr <> 0 Then
      Call HeapFree(GetProcessHeap, 0, ByVal .CallBackPtr)
      .CallBackPtr = 0
    End If
  End With
End Function

'***************************************************************************
'获取回调函数指针
Private Function GetFileCopyExCallBackPtr(ByVal FunctionCount As Long) As Long
  Dim FunctionPtr As Long
  Call CopyMemory(FunctionPtr, ByVal ObjPtr(Me), 4)
  FunctionPtr = FunctionPtr + (FunctionCount - 1) * 4 + &H1C
  If CBool(IsBadReadPtr(ByVal FunctionPtr, 4)) Then Exit Function
  Call CopyMemory(FunctionPtr, ByVal FunctionPtr, 4)
  If FunctionPtr = 0 Then Exit Function
  Dim AsmCode(18) As Long
  AsmCode(0) = &H83EC8B55:  AsmCode(1) = &H75FF08EC
  AsmCode(2) = &H3475FF38:  AsmCode(3) = &HFF3075FF
  AsmCode(4) = &H75FF2C75:  AsmCode(5) = &H2475FF28
  AsmCode(6) = &HFF2075FF:  AsmCode(7) = &H75FF1C75
  AsmCode(8) = &H1475FF18:  AsmCode(9) = &HFF1075FF
  AsmCode(10) = &H75FF0C75: AsmCode(11) = &HFC75FF08
  AsmCode(12) = &H50F8458D: AsmCode(13) = &H100068
  AsmCode(14) = &H2000B800: AsmCode(15) = &HC08B0000
  AsmCode(16) = &H458BD0FF: AsmCode(17) = &H34C2C9F8
  Call CopyMemory(ByVal VarPtr(AsmCode(13)) + 1, ObjPtr(Me), 4)
  Call CopyMemory(ByVal VarPtr(AsmCode(14)) + 2, FunctionPtr, 4)
  Dim Length As Long
  Length = (UBound(AsmCode) + 1) * 4
  GetFileCopyExCallBackPtr = HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, Length)
  If GetFileCopyExCallBackPtr = 0 Then Exit Function
  Call VirtualProtect(ByVal GetFileCopyExCallBackPtr, Length, PAGE_EXECUTE_READWRITE, 0&)
  Call CopyMemory(ByVal GetFileCopyExCallBackPtr, AsmCode(0), Length)
End Function

'***************************************************************************
'回调函数:CopyProgressRoutine
'原本的回调函数没有Result和EachBytesCopied参数,而且有返回值(Function),具体可看MSDN
'改成Sub(Result为返回值,EachBytesCopy为每次复制的字节数,CopyFileEx触发回调函数的条件)
Private Sub CopyProgressRoutine(ByRef Result As Long, ByVal EachBytesCopied As Long, ByVal TotalFileSize As Currency, ByVal TotalBytesTransferred As Currency, ByVal StreamSize As Currency, ByVal StreamBytesTransferred As Currency, ByVal dwStreamNumber As Long, ByVal dwCallbackReason As Long, ByVal hSourceFile As Long, ByVal hDestinationFile As Long, ByVal lpData As Long)
  On Error Resume Next
  If dwCallbackReason = CALLBACK_STREAM_SWITCH Then
    RaiseEvent FileCopyExBegin
  End If
  DoEvents
  Dim CopyProgress As Long
  CopyProgress = (TotalBytesTransferred / TotalFileSize) * 100
  If FileCopyExInfo.CopyProgress < CopyProgress Then
    RaiseEvent FileCopyExProgress(CopyProgress, FileCopyExInfo.CopyCancel)
    If FileCopyExInfo.CopyCancel Then
      RaiseEvent FileCopyExCancel
    End If
  End If
  FileCopyExInfo.CopyProgress = CopyProgress
  Result = PROGRESS_CONTINUE '也可以使用PROGRESS_CANCEL来取消
End Sub

窗体中:2个Command,1个Label
Option Explicit
Dim WithEvents iFile As IClsFile
Dim CancelCopy As Boolean

Private Sub Command1_Click()
  Dim Path1 As String
  Dim Path2 As String
  Path1 = "E:\Test.avi" '源文件
  Path2 = "D:\Av.avi" '目标文件
  CancelCopy = False
  MsgBox iFile.FileCopyEx(Path1, Path2, True)
End Sub

Private Sub Command2_Click()
  CancelCopy = True
End Sub

Private Sub Form_Load()
  Set iFile = New IClsFile
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
  Set iFile = Nothing
End Sub

Private Sub iFile_FileCopyExBegin()
  MsgBox "iFile_FileCopyExBegin"
End Sub

Private Sub iFile_FileCopyExCancel()
  MsgBox "iFile_FileCopyExCancel"
End Sub

Private Sub iFile_FileCopyExProgress(ByVal Progress As Long, Cancel As Boolean)
  Label1.Caption = "已复制:" & Progress & "%"
  Cancel = CancelCopy
End Sub

TA的精华主题

TA的得分主题

发表于 2023-6-15 09:03 | 显示全部楼层
SHFileOperation API也可以的

SHFileOperation API.zip

14.88 KB, 下载次数: 14

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2023-6-15 09:18 | 显示全部楼层
Yalishanda30 发表于 2023-6-15 09:03
SHFileOperation API也可以的

你这个64位的有吗?

TA的精华主题

TA的得分主题

发表于 2023-6-15 09:24 | 显示全部楼层
ykcbf1100 发表于 2023-6-15 09:18
你这个64位的有吗?

公司excel是32位的,不知道是不是把API用ptrsafe标记一下就可以了

TA的精华主题

TA的得分主题

发表于 2023-6-15 09:30 | 显示全部楼层
Yalishanda30 发表于 2023-6-15 09:24
公司excel是32位的,不知道是不是把API用ptrsafe标记一下就可以了

使用时源文件和目标文件都是全路径吗?

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-6-15 09:38 | 显示全部楼层
显示进度条的,标题写的不准确

TA的精华主题

TA的得分主题

发表于 2023-6-15 09:40 | 显示全部楼层
ykcbf1100 发表于 2023-6-15 09:30
使用时源文件和目标文件都是全路径吗?

可以复制文件夹,也可以复制单个文件,如果复制文件夹,(源文件夹路径,目标文件夹路径)
如果复制单个文件,(源文件路径,目标文件路径)
都需要全路径,操作和鼠标点击复制,再粘贴是类似的,只是用代码代替了鼠标选择文件/文件夹还有路径

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-6-15 11:55 | 显示全部楼层
本帖最后由 Cavan2022 于 2023-6-15 23:00 编辑

一下代码可以在VS上运行,哪位高手改成VBA的,在Excel上运行,谢谢!

  1. Imports System.Runtime.InteropServices

  2. Public Class Form1
  3.     Private pbCancel As Int32

  4.     <DllImport("kernel32.dll", CharSet:=CharSet.Auto)> _
  5.     Private Shared Function CopyFileEx( _
  6.         ByVal lpExistingFileName As String, _
  7.         ByVal lpNewFileName As String, _
  8.         ByVal lpProgressRoutine As CopyProgressRoutine, _
  9.         ByVal lpData As IntPtr, _
  10.         ByRef pbCancel As Int32, _
  11.         ByVal dwCopyFlags As ECopyFileFlags) As Boolean
  12.     End Function

  13.     ''' <summary>
  14.     ''' 執行 CopyFileEx 方法的回呼函式定義。
  15.     ''' </summary>
  16.     Delegate Function CopyProgressRoutine( _
  17.         ByVal TotalFileSize As Long, _
  18.         ByVal TotalBytesTransferred As Long, _
  19.         ByVal StreamSize As Long, _
  20.         ByVal StreamBytesTransferred As Long, _
  21.         ByVal dwStreamNumber As UInteger, _
  22.         ByVal dwCallbackReason As ECopyProgressCallbackReason, _
  23.         ByVal hSourceFile As IntPtr, _
  24.         ByVal hDestinationFile As IntPtr, _
  25.         ByVal lpData As IntPtr) As ECopyProgressResult

  26.     ''' <summary>
  27.     ''' 執行 CopyFileEx 的回呼函式的回傳列舉。
  28.     ''' </summary>
  29.     Enum ECopyProgressResult As UInteger
  30.         PROGRESS_CONTINUE = 0
  31.         PROGRESS_CANCEL = 1
  32.         PROGRESS_STOP = 2
  33.         PROGRESS_QUIET = 3
  34.     End Enum

  35.     Enum ECopyProgressCallbackReason As UInteger
  36.         CALLBACK_CHUNK_FINISHED = 0
  37.         CALLBACK_STREAM_SWITCH = 1
  38.     End Enum

  39.     <Flags()> _
  40.     Enum ECopyFileFlags As UInteger
  41.         COPY_FILE_FAIL_IF_EXISTS = 1
  42.         COPY_FILE_RESTARTABLE = 2
  43.         COPY_FILE_OPEN_SOURCE_FOR_WRITE = 4
  44.         COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 8
  45.     End Enum

  46.     Private Function XCopy(ByVal oldFile As String, ByVal newFile As String) As Boolean
  47.         Return CopyFileEx(oldFile, newFile, New CopyProgressRoutine(AddressOf Me.CopyProgressHandler), IntPtr.Zero, pbCancel, ECopyFileFlags.COPY_FILE_RESTARTABLE)
  48.     End Function

  49.     Private Function CopyProgressHandler(ByVal total As Long, ByVal transferred As Long, ByVal streamSize As Long, ByVal StreamByteTrans As Long, ByVal dwStreamNumber As UInteger, ByVal reason As ECopyProgressCallbackReason, _
  50.      ByVal hSourceFile As IntPtr, ByVal hDestinationFile As IntPtr, ByVal lpData As IntPtr) As ECopyProgressResult
  51.         Label1.Text = String.Format("進度: {0}%", CInt(transferred / total * 100))
  52.         Application.DoEvents()

  53.         Return ECopyProgressResult.PROGRESS_CONTINUE
  54.     End Function

  55.     Private Sub btnCopy_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCopy.Click
  56.         If XCopy("C:\Source.avi", "D:\New.avi") Then
  57.             MsgBox("複製成功")
  58.         Else
  59.             MsgBox("複製失敗")
  60.         End If

  61.     End Sub

  62.     Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
  63.         pbCancel = 1
  64.         Application.DoEvents()
  65.     End Sub
  66. End Class
复制代码

您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-16 13:49 , Processed in 0.046665 second(s), 14 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表