VBA DLL 从内核32调用写文件创建巨大的文件



我正在尝试使用 excel 7 中的 VBA7 32 位将一个文本文件附加到另一个文本文件,在 Windows 7 64 位上进行原型设计。一旦这起作用,我将使用相同的方法将许多文件中的 wav 数据附加到一起,并修改标头信息以适合附加的 wav 数据的大小。

我遇到的问题是当我调用WriteFile(同步)时,需要很长时间才能完成,原因是它正在向文本文件写入 4 个 gig,它应该只写入 20 个字节(one.txt的大小)。出了什么问题或如何调试它?

我在这台机器上可用的工具有限,因为它是由大型组织管理的。我只能访问用于编程环境的 VBA。可以使用 Powershell 和普通命令 shell 实用程序。

我做了以下研究: 阅读所有 dll 调用的 msdn 文章,设置断点以验证值是否正确,阅读有关 Office 2010 中的 32 位与 64 位兼容性的信息,阅读并理解(主要是)有关在 VB 中将信息传递到 DLL 过程的 msdn 文章,找到这个关于 varptr 和在 VB 中调用 dll 函数的精彩页面,并从 msdn C++示例中获取代码, 在很多学习中。

Private Sub cmdCopy_Click()
#If Win64 Then
MsgBox ("Win 64")
#Else
MsgBox ("Not win 64 bit") ' Developing on 32-bit excel 2010, windows 7 64 bit
#End If

'Dim dummyPtr As SECURITY_ATTRIBUTES ' not used, just changed Createfile declare last parameter type to Any to
' allow ByVal 0& to be used
'dummyPtr = Null
Dim hFile As LongPtr
hFile = CreateFile("C:testone.txt", GENERIC_READ, 0, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, ByVal 0&)
'hFile = CreateFile("C:testone.txt", GENERIC_READ, 0, vbNullString, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, ByVal 0&)
If hFile = INVALID_HANDLE_VALUE Then
MsgBox ("Could not open one.txt")
End If
Dim hAppend As LongPtr
hAppend = CreateFile("C:testtwo.txt", FILE_WRITE_DATA, FILE_SHARE_READ, ByVal 0&, _
OPEN_ALWAYS, _
FILE_ATTRIBUTE_NORMAL, _
vbNull) ' no template file
If hAppend = INVALID_HANDLE_VALUE Then
MsgBox ("Could not open two.txt")
End If
Dim cBuff(4096) As Byte
Dim dwBytesRead As Long
Dim dwBytesWritten As Long
Dim dwPos As Long
Dim bRet As Boolean
Dim lRet As Long

' not actually a long ptr
Dim lpBytesRead As Long
'lpBytesRead = VarPtr(dwBytesRead) ' extraeneous because byref in function declare causes VB to pass a pointer to lpBytesRead
'    While (ReadFile(hFile, cBuff, Len(cBuff(LBound(cBuff))), ' a way to not hard-code the buffer length in the function call
lRet = ReadFile(hFile, ByVal VarPtr(cBuff(0)), 4096, _
lpBytesRead, ByVal 0&)
Debug.Print ("Outside while loop: Readfile: lret, lpBytesRead: " + CStr(lRet) + ", " + CStr(lpBytesRead))
While (lRet And lpBytesRead > 0)
dwPos = SetFilePointer(hAppend, 0, vbNull, FILE_END)
Debug.Print ("cmdCombine: SetFilePointer: dwPos: " + CStr(dwPos))
Dim i As Long
'Print the contents of the buffer from ReadFile
For i = 0 To lpBytesRead
Debug.Print Hex(cBuff(i)); "='" & Chr(cBuff(i)) & "'"
Next
'bRet = LockFile(hAppend, dwPos, 0, dwBytesRead, 0) 'commented for debugging
Dim lpBuffPointer As Long
lpBuffPointer = VarPtr(cBuff(0))
Dim lpBytesWritten As Long
lpBytesWritten = VarPtr(dwBytesWritten)
Dim lpTest As LongPtr
bRet = WriteFile(hAppend, ByVal VarPtr(cBuff(0)), 20, ByVal lpBytesWritten, ByVal 0&)
'bRet = WriteFile(hAppend, ByVal VarPtr(cBuff(0)), lpBytesRead, ByVal lpBytesWritten, ByVal 0&)
'bRet = WriteFile(hAppend, lpBuffPointer, lpBytesRead, lpBytesWritten, ByVal 0&) ' another option for calling
Debug.Print ("cmdCombine: Writefile: bRet, lpBytesRead, lpBytesWritten: " + _
CStr(bRet) + " " + CStr(lpBytesRead) + " " + CStr(dwBytesWritten))
'bRet = UnlockFile(hAppend, dwPos, 0, dwBytesRead, 0)
lRet = ReadFile(hFile, ByVal VarPtr(cBuff(0)), 4096, _
lpBytesRead, ByVal 0&)
Debug.Print ("Readfile: lret, lpBytesRead: " + CStr(lRet) + ", " + CStr(lpBytesRead))
Wend
' TODO: set EOF to the current file pointer location?
'SetEndOfFile (hAppend)
CloseHandle (hFile)
CloseHandle (hAppend)
End Sub

在模块中,我从Win32API_PtrSafe.txt中获取了声明,修改为允许我为 UDT 传递 Null:

Declare PtrSafe Function WriteFile Lib "kernel32" (ByVal hFile As LongPtr, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, lpOverlapped As Any) As Long
'Declare PtrSafe Function WriteFile Lib "kernel32" (ByVal hFile As LongPtr, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, lpOverlapped As OVERLAPPED) As Long
Declare PtrSafe Function ReadFile Lib "kernel32" (ByVal hFile As LongPtr, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As Any) As Long
'Declare PtrSafe Function ReadFile Lib "kernel32" (ByVal hFile As LongPtr, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As OVERLAPPED) As Long
Declare PtrSafe Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As LongPtr) As LongPtr
'Declare PtrSafe Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As SECURITY_ATTRIBUTES, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As LongPtr) As LongPtr
Declare PtrSafe Function SetFilePointer Lib "kernel32" (ByVal hFile As LongPtr, ByVal lDistanceToMove As Long, lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As LongPtr) As Long
Declare PtrSafe Function LockFile Lib "kernel32" (ByVal hFile As LongPtr, ByVal dwFileOffsetLow As Long, ByVal dwFileOffsetHigh As Long, ByVal nNumberOfBytesToLockLow As Long, ByVal nNumberOfBytesToLockHigh As Long) As Long
Declare PtrSafe Function UnlockFile Lib "kernel32" (ByVal hFile As LongPtr, ByVal dwFileOffsetLow As Long, ByVal dwFileOffsetHigh As Long, ByVal nNumberOfBytesToUnlockLow As Long, ByVal nNumberOfBytesToUnlockHigh As Long) As Long

您正在将vbNull传递给SetFilePointer

vbNull是一个枚举常量,等于1。这是VarType()可以返回的可能结果之一。这不是C++的nullptr或VB的Nothing。将此值作为lpDistanceToMoveHigh传递指示函数使用 64 位寻址并将1作为高dword

显然你想通过ByVal 0&.当您要传递空指针时,它是传递给byref参数的内容。

最新更新