将实现 ATL 接口的 VBA 类传递给 ATL 方法

Posted

技术标签:

【中文标题】将实现 ATL 接口的 VBA 类传递给 ATL 方法【英文标题】:Passing a VBA class that implements an ATL interface, to an ATL method 【发布时间】:2012-08-04 11:19:51 【问题描述】:

编辑:

我有一个 ATL 简单对象 MyATLObject,只有一个方法:

STDMETHODIMP CMyATLObject::EVAL( DOUBLE* x, long AddressOfFunc )

*x = toto<FCTPTR>(*((FCTPTR) AddressOfFunc)) ;
return S_OK;

toto 是一个由定义的模板函数

#pragma once

template<typename F>
double toto( F f )

    return f(0.0) ;

其中 FCTPTR 被定义为

typedef double (__stdcall * FCTPTR)( double );

我的 ATL 方法使用 double x 来存储 toto 的结果,以及 long AddressOfFunc,它是 VBA 函数的 AddressOf。

这通过地址将 VBA 函数传递给 ATL 方法。这太可怕了。否则,它如何能够执行相同的操作 - 将 VBA 函数传递给 ATL 方法,例如通过在 ATL 端定义一个接口?

非常感谢。

/////////////////////////////////////// ///////////////////////////////////////// ///////////////////////////////////////// ////////////////////////////////////////p>

我的问题的第一个版本:

我实际上是在做以下事情:创建一个 ATL/COM 项目,添加一个名为 MyATLObject 的“简单 ATL 对象”:

1) 在 idl 文件中:

interface IMyATLObject : IDispatch
[id(1), helpstring("method EVAL")] HRESULT EVAL(DOUBLE* x, long AddressOfFunc) ;
;

2 在 MyATLObject.h 文件中:

一个

#include "MyStupidEvaluator.h"

还有一个

typedef double (__stdcall * FCTPTR)( double );

课外,课内

public:
STDMETHOD(EVAL)(DOUBLE* x, long AddressOfFunc) ;

最后在 MyATLObject.cpp 文件中:

STDMETHODIMP CMyATLObject::EVAL( DOUBLE* x, long AddressOfFunc )

*x = toto<FCTPTR>(*((FCTPTR) AddressOfFunc)) ;
return S_OK;

MyStupidEvaluator.h 包含:

#pragma once

template<typename F>
double toto( F f )

    return f(0.0) ;

这只是一个模板函数,它返回任何函数对象的零值,即一个类型名(或一个类),它允许一个 () 运算符。

现在,编译后,这给了我一个 dll。

这就是c++部分的全部内容。现在,我在 VBA 和 VBA 中引用了这个 dll:

a) 我定义了一个函数

Public Function F(ByVal x As Double) As Double

    F = x * x - 2 * x + 1

End Function

b) 和一个子

    Public Sub Draft1()

        Dim ENGINE As MyTestProjectLib.MyATLObject
        Set ENGINE = New MyTestProjectLib.MyATLObject
        Dim x As Double
        x = 0#

        Call ENGINE.EVAL(x, AddressOf F)
        Sheets("Test1").Range("Value_at_0").Offset(0, 0).Value = x

    End Sub

c) 执行这个 sub 将给我,在工作表“Test1”的 V“alue_at_0”(这里只是一个单元格)范围内,函数 F 的 0.0 处的值。

我总是这样工作:toto 模板函数在这里很简单(评估为 0.0),但一般来说,它可能是一个数值积分例程,或者是一个求根例程,需要 c++ 来提高速度;由于 AddressOf VBA 关键字,我一直通过它们的地址传递 VBA 函数:这给了我一个 long,我将它传递给我的 ATL 方法 EVAL,我将这个 long 转换为函数指针 (FCTPTR) p,然后应用我的例程“ toto" 到 *p,这给了我一个双倍,我将它传递回我的 ATL 方法。

这很酷,一切正常,但是......我觉得这太糟糕了!而且真的很不雅。这就是为什么我想做以下事情:

在 ATL 端“以某种方式”设计一个接口,一个

interface IMyFunction : IDispatch

这将由 VBA 类 VBAClass1 在 VBA 中实现(通过 VBA 的实现),并将该 VBA 类的实例传递给我以前的 VBA 方法的新版本,以便在 0.0 执行评估。

我的问题是:我根本不是 ATL 方面的专家,也不是界面创建方面的专家:我已经成功地在界面创建方面做了真正愚蠢/琐碎的事情,好吧,但我真的不知道如何设计界面做我想做的事,那就是:

ATL 中的一个接口(根据定义,它只有方法,通过 ATL 构造!)将有“某物”扮演类成员的角色,成员跟踪 VBA 函数仍将传递给他通过 ATL 方法。

任何帮助将不胜感激,伙计们。

这是我第一次来这里,如果信息不是完美/最佳形式,请提前道歉。

提前谢谢你。

拉夫

【问题讨论】:

【参考方案1】:

您应该重新考虑您在AddressOfFunc 参数和回调地址周围的所有内容。当你设计 COM 接口并且你想提供一个回调时,你需要传递一个可调用的 COM 接口指针作为参数 (related discussion),这样 COM 类就会回调它的方法——这需要你的 VBA 类实现这个接口(@98​​7654322@)。如果您不想在 VBA 端实现接口,另一种选择是设计带有事件的 ATL COM 类 (connection points),以便 COM 类会触发一个事件,而您的调用者将在那里处理它并执行回调活动。

函数的普通地址在这里是行不通的,当你不得不强制转换东西以使它们甚至可以编译时,你应该怀疑它。

UPD。这里提供了如何回调VB代码的示例:Three ways to implement VBScript (VB6, VBA) callback from C++/ATL class

【讨论】:

感谢您的回答。我仍然是所有这一切的新手,所以我并没有真正理解你:正如我所解释的,我绝对想传递一个可调用的接口指针作为参数,是的,因此我问我应该如何设计这个接口,因为我现在无法自己回答这个问题,对 ATL 几乎一无所知。 例如,在我的情况下,您将如何设计界面? 我提到的场景都是可行的。抱歉,我没有要显示的代码 sn-p。关于两者的第一个建议 - 您将在 ATL 项目中定义回调接口(在 IDL 中),您可以在 SO 上找到 how to implement this interface in VBA。 对不起,如果您认为我的 VBA 类指针在任何时候都不能作为 long 传递(在询问时您显然没有最模糊的想法)正是因为你不会再收到任何“正确”的答案,那么祝你好运。 我必须承认,正如我已经说过的:我对这一切都是新手,我真的不明白你写了什么,尤其是“当你设计 COM 接口时,你会喜欢提供回调,需要传递一个可调用的 COM 接口指针作为参数(相关讨论)”。我查看了相关讨论,给出的示例是通过使用传递的 VBA 的示例

以上是关于将实现 ATL 接口的 VBA 类传递给 ATL 方法的主要内容,如果未能解决你的问题,请参考以下文章

ATL/COM 对象的链接方法

WinRT/Metro 风格应用程序中基于 ATL 的 COM 对象

如何通过 InvokeHelper 将 variant* 传递给 C#?

Visual C++:接口的 ATL 实现

ATL“实现界面向导”

如何添加atl工程实现接口实现ipersistfile