在VBA编程中,指针的支持非常有限,尤其是函数指针。这在进行泛型编程时尤其不便。为了解决这一问题,可以利用C++ ATL技术,创建一个COM DLL,使得VBA能够通过函数对象(functors)来调用函数。本文将详细介绍如何实现这一过程,包括C++的实现细节和VBA的使用方式。
VBA本身并不支持函数指针,但可以通过COM DLL来实现类似的功能。以下是一个VBA代码示例,展示了如何初始化并调用一个函数对象:
Dim opfn As Functor
Set opfn = New_Functor(AddressOf MyFunction, retvoid_2_args)
Call opfn.call_retvoid_2("This works", "fine!")
这段代码首先创建了一个函数对象,然后通过该对象调用了一个函数。
项目使用C++ATL编写,并编译为COM DLL。导出的Functor对象可以在VBA代码中使用,以存储函数地址,并在之后通过匹配函数类型的Functor对象的方法来调用它。以下是IDL声明和示例函数调用方法:
interface IFunctor : IDispatch {
[id(1), helpstring("Hooks on a function")]
HRESULT HookFunction([in] LONG fnAddress, [in] enum FuncType functionType);
[id(8), helpstring("Calls Function that a)Retruns VARIANT b)Takes 2 arguments")]
HRESULT call_retvar_2([in, out, optional] VARIANT* Arg1, [in, out, optional] VARIANT* Arg2, [out, retval] VARIANT*);
}
这些函数类型在Functor.h中定义:
typedef HRESULT (__stdcall *pfn_retvoid_0)(void);
typedef HRESULT (__stdcall *pfn_retvoid_1)(VARIANT*);
typedef HRESULT (__stdcall *pfn_retvoid_2)(VARIANT*, VARIANT*);
// 更多函数类型...
这些函数类型对应于FuncType枚举的成员,该枚举也从DLL中导出:
enum FuncType {
retvoid_0_args,
retvoid_1_args,
retvoid_2_args,
// 更多函数类型...
};
DLL还导出了一个初始化器类,用于模拟带参数的构造函数。例如,New_Functor()返回一个新初始化的Functor (IFunctor)对象。
源代码中包含的VBA_Functors_Test.xls文件包含了使用Functor对象的一些示例。要正确地从VBA初始化Functor对象,需要进行以下步骤:
以下是一个VBA代码示例,展示了如何使用New_Functor()函数:
Public Sub UseFunctors()
Dim ofn As Functor
Set ofn = New_Functor(AddressOf SimpleFunction, retvar_1_args)
Dim vbmRes As VbMsgBoxResult
vbmRes = ofn.call_retvar_1("Display this!")
End Sub
这段代码首先创建了一个函数对象,然后通过该对象调用了一个函数。
在企业环境中,安全策略可能会与COM组件的注册发生冲突。通常,这些组件是在HKEY_LOCAL_MACHINE下注册的,而普通用户没有写入权限。通过在ATL中使用AtlSetPerUserRegistration(true),可以将组件注册在HKEY_CURRENT_USER下,从而解决这个问题。
以下是DllRegisterServer的定义:
STDAPI DllRegisterServer(void) {
ATL::AtlSetPerUserRegistration(true);
HRESULT hr = _AtlModule.DllRegisterServer();
return hr;
}
com_definitions.h文件中定义了HRESULT,它通常用作COM方法的返回值。