excel - HeapFree 崩溃了
问题描述
概括:
我有 VBA 代码,它收集大量信息并将其写入一个或多个工作表。为了提高将大量信息写入工作表的性能,我创建了一个类似于缓冲复制/粘贴流的类:调用者向它发送 CSV 格式的字符串,它在内存中缓冲,直到缓冲区已满;满时,它会粘贴到一张纸中,清除缓冲区并继续。
最初,我使用全局内存,但后来在 MSDN 上看到建议使用堆而不是全局或本地,因为开销较小。所以现在我正在使用堆。
我正在为 64 位 Office 调整一切。完成所有 PtrSafe 的工作后,我就可以运行代码了。但是现在 Excel 在HeapFree()
调用时崩溃了。
问题:为什么它会崩溃,我需要改变什么来避免它?
细节:
我想出了以下是重现崩溃的最小化示例:有一个模块,sub
我可以为重现和课程运行。这不做缓冲;每次调用.SendText
都会将文本放在剪贴板上并粘贴到活动单元格中。
首先是模块。这有以下声明语句
' memory APIs
Public Const HEAP_ZERO_MEMORY = &H8
Declare PtrSafe Function GetProcessHeap Lib "kernel32" () As LongPtr 'returns HANDLE
Declare PtrSafe Function HeapAlloc Lib "kernel32" (ByVal hHeap As LongPtr, ByVal dwFlags As Long, ByVal dwBytes As LongPtr) As LongPtr 'returns HANDLE
Declare PtrSafe Function HeapFree Lib "kernel32" (ByVal hHeap As LongPtr, ByVal dwFlags As Long, lpMem As Any) As Long 'returns BOOL
Declare PtrSafe Function lstrcpy Lib "kernel32" Alias "lstrcpyW" (ByVal lpDestString As Any, ByVal lpSrcString As Any) As LongPtr 'returns HANDLE
' clipboard APIs
Public Const CF_UNICODETEXT = 13
Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As Long 'returns BOOL
Declare PtrSafe Function EmptyClipboard Lib "user32" () As Long 'returns BOOL
Declare PtrSafe Function CloseClipboard Lib "user32" () As Long 'returns BOOL
Declare PtrSafe Function SetClipboardData Lib "user32" (ByVal wFormat As Long, ByVal hMem As LongPtr) As LongPtr 'returns HANDLE
然后sub
:
Private Sub TestMemoryBugRepro()
Dim s As String
Dim clip As clsHeapBugRepro
s = Chr(34) & "a" & vbLf & "b" & Chr(34) & ",c" & vbCrLf & "d" & vbTab & ",e" & vbCrLf
Set clip = New clsHeapBugRepro
clip.Initialize &H100
clip.SendText s
'Crash happens during the following
Set clip = Nothing
End Sub
现在上课。崩溃发生在调用Class_Terminate()
时。HeapFree
Option Explicit
Private m_hHeap As LongPtr 'handle to the process heap
Private m_hMem As LongPtr 'handle to memory
Private m_pMem As LongPtr 'pointer to locked memory
Private m_cbMem As Long 'size of the memory buffer
Private m_BytesWritten As Long '
'**************************************
' Event procedures
Private Sub Class_Initialize()
m_hHeap = GetProcessHeap()
End Sub
Private Sub Class_Terminate()
If m_hMem <> 0 And m_hHeap <> 0 Then
HeapFree m_hHeap, 0, m_hMem 'CRASH OCCURS HERE
End If
End Sub
'**************************************
' Public methods
Public Function Initialize(Optional bufferSize As Long = &H8000) As Boolean
Initialize = False
m_BytesWritten = 0
If m_hHeap <> 0 Then
m_cbMem = bufferSize
m_hMem = HeapAlloc(m_hHeap, (HEAP_ZERO_MEMORY), m_cbMem)
End If
If m_hMem <> 0 Then Initialize = True
End Function
Public Function SendText(text As String) As Boolean
Dim nStrLen As Long
nStrLen = LenB(text) + 2&
Debug.Assert nStrLen < (m_cbMem + m_BytesWritten)
m_pMem = m_hMem 'in lieu of locking heap memory
lstrcpy m_pMem, StrPtr(text)
m_pMem = 0 'in lieu of unlocking heap memory
m_BytesWritten = m_BytesWritten + nStrLen
DoEvents
OpenClipboard 0&
EmptyClipboard
SetClipboardData CF_UNICODETEXT, m_hMem
CloseClipboard
ActiveCell.PasteSpecial
DoEvents
SendText = True
End Function
解决方案
您如何调用 HeapFree 存在问题,但您真正的问题是您根本不应该使用 HeapAlloc/HeapFree。
HeapAlloc 分配的内存是不可移动的,而SetClipboardData
要求它是可移动的。
另一个考虑是SetClipboardData
将内存的所有权转移给系统,这意味着您不应该自己释放它。(一旦所有权转移到系统,应用程序可能不会写入或释放数据。)
因此,我会尝试使用 GlobalAlloc 而不是 HeapAlloc 重写您的逻辑,并且不要在将内存放入剪贴板后尝试释放内存。
HeapFree
现在,如果您要使用它,您的调用方式存在问题。HeapFree
想要返回的指针HeapAlloc
。
相反,您传递了一个指向该指针的指针,因为您声明了 的第三个参数HeapFree
As Any
,这意味着ByRef As Any
.
要么重新声明参数ByVal As LongPtr
/ ByVal As Any
,例如:
Declare PtrSafe Function HeapFree Lib "kernel32" (ByVal hHeap As LongPtr, _
ByVal dwFlags As Long,
ByVal lpMem As LongPtr) As Long 'returns BOOL
或ByVal
在调用时指定:
HeapFree m_hHeap, 0, ByVal m_hMem
推荐阅读
- java - JFormattedTextField 显示逗号并接受小数点
- python - 当 Min_count=1 时 Groupby Sum 等于 0
- python-3.x - 将自己的文件/包添加到自定义 anaconda 环境
- android - 无法在 BLE onn android 应用程序上写入特征
- javascript - 将 cognitoUser.authenticateUser 回调转换为 observable
- solr - 如何解决 org.springframework.data.solr.repository.config.SolrRepositoryConfigExtension 的 NoSuchMethodError?
- azure-cosmosdb - 获取文档页面时发生 CosmosDB 错误:{“code”:400,“body”:“命令查找失败:处理时发生未知服务器错误
- node.js - 无法弄清楚如何等到 forEach 循环完全迭代并使用该结果
- php - PHP字符串和foreach多个结果
- python - 如何为 Google Cloud Storage 存储分区选择存储类别和位置