API欺骗
Private Declare Function CallWindowProc Lib "user32.dll" Alias "CallWindowProcA" ( _
ByVal lpPrevWndFunc As Long, _
ByVal HWnd As Long, _
ByVal msg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
当提供lpPrevWndFunc
参数的不存在的函数指针时,将崩溃Excel 。
同样
Private Declare Sub RtlMoveMemory Lib "kernel32" (ByRef Destination As LongPtr, _
ByRef Source As LongPtr, _
ByVal Length As Long)
不幸Destination
或Source
不存在时不开心。
我认为对于这两种错误都是内存访问违规。我假设Windows告诉调用者它正在做它不能做的事情 - 也许它向Excel发送消息并且没有处理程序?MSDN对此有这样的说法:
调用Windows动态链接库(DLL)或Macintosh代码资源期间的系统错误不会引发异常,也不会使用Visual Basic错误捕获捕获。调用DLL函数时,应检查每个返回值的成功或失败(根据API规范),如果发生故障,请检查Err对象的LastDLLError属性中的值。LastDLLError始终在Macintosh上返回零。 (强调我自己)
但在这些情况下,我没有检查错误的价值,我只是遇到了崩溃。
我认为(1)可能会阐明(2),反之亦然
无论如何,如果有人知道如何在没有Excel崩溃的情况下处理像这样的API错误,或者如何避免它们发生,或者任何可能发生的事情。On Error Resume Next
似乎不起作用......
Sub CrashExcel()
On Error Resume Next 'Lord preserve us
'Copy 300 bytes from one non existent memory pointer to another
RtlMoveMemory ByVal 100, ByVal 200, 300
On Error Goto 0
Debug.Assert Err.LastDllError = 0 'Yay no errors
End Sub
我的用例是尝试创建一个友好的界面来包装WinAPI计时器。这些要求回调函数与它们一起注册,它们(鉴于VBA的限制)必须以Long
函数指针的形式出现(用AddressOf
关键字生成)。
回调来自用户代码,可能无效。我的包装的重点是提高API调用的稳定性,这是一个需要改进的领域。
内存复制问题可能超出了这个问题的范围,它与在VBA中生成生成器有关,但我认为相同的错误处理技术也适用于那里,并且它提供了一个更简单的示例。
我也从Timer API中获取错误和崩溃,为Excel生成太多未处理的消息。我再次想知道,Windows如何告诉Excel“现在崩溃的时间”,为什么我不能拦截该指令并自己处理错误(即杀死我制作的所有计时器并刷新消息队列)?
发布于 2019-05-22 14:06:40
来自评论:
当然,如果我进行了错误的API调用,那么我必须让Excel /我的代码知道它很糟糕
不必要。如果您要求API函数(例如RtlMoveMemory
)覆盖您提供指针的位置的内存,它将乐意尝试这样做。然后会发生很多事情:
来自你的评论:
我正在设计代码来附加用户提供的回调函数
另一种方法是使用客户端代码可以实现的方法设计接口。然后要求客户端传递实现该接口的类的实例。
如果您的客户端是VBA,那么定义接口的简单方法是使用一个或多个空方法创建公共VBA类模块。按照惯例,您应该使用I
(for interface)前缀命名此类- 例如IMyCallback
。空方法(子或函数)可以有任何你想要的签名,但我会保持简单:
例:
Class module name: IMyCallback
Option Explicit
Public Sub MyMethod()
End Sub
或者,如果客户端使用VBA以外的语言,则可以使用IDL定义接口,将其编译为类型库,并从VBA项目中引用类型库。我不会再进一步讨论这个问题,但如果你想跟进它,可以提出另一个问题。
然后,您的客户端应该创建一个类(VBA类模块),以他们选择的任何方式实现此接口,例如通过创建类模块ClientCallback
:
Class module name: ClientCallback
Option Explicit
Implements IMyCallback
Private Sub IMyCallback_MyMethod()
' Client adds his implementation here
End Sub
然后,您公开一个类型的参数,IMyCallback
您的客户端可以传递他的类的实例。
你的方法:
Public Sub RegisterCallback(Callback as IMyCallback)
...
End Sub
客户代码:
Dim objCallback as New ClientCallback
RegisterCallback Callback
…
然后,您可以实现从Timer调用的自己的回调函数,并通过该接口安全地调用客户端代码。
https://stackoverflow.com/questions/-100009047
复制相似问题