1

編集 :

メソッドが 1 つしかない 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 メソッドは、toto の結果を格納するために double x を消費し、VBA 関数の AddressOf である long AddressOfFunc を消費します。

この VBA 関数をアドレスによって ATL メソッドに渡します。それはかなりひどいです。たとえば、ATL側でインターフェイスを定義するなど、そうでなければ、VBA関数をATLメソッドに渡す同じことをどのように行うことができますか?

どうもありがとうございました。

/////////////////////////////////////////////// /////////////////////////////////////////////// /////////////////////////////////////////////// ///////////////////////////////

私の質問の最初のバージョン:

私は実際に次のことを行っています: 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) このサブルーチンを実行すると、シート「Test1」の範囲 V「alue_at_0」(ここではセルのみ) で、関数 F の 0.0 の値が得られます。

私はいつもこのように作業しています: toto テンプレート関数はここでは自明です (0.0 での評価) が、一般的には数値積分ルーチン、または高速化のために C++ が必要なルート検索ルーチンである可能性があります。AddressOf VBA キーワードのおかげで、私は常に VBA 関数をそのアドレスを介して渡しています。これにより、ATL メソッド EVAL に渡す long が得られ、この long を関数ポインタ (FCTPTR) p に変換し、ルーチンを適用します" toto" を *p に変換すると、ATL メソッドに返される double が返されます。

それはクールです、すべてが機能しますが...私はこれがひどいと思います!!! そして本当にエレガントではありません。それが私が次のことをしたい理由です:

ATL側のインターフェースを「何とか」設計し、

interface IMyFunction : IDispatch

これは、VBA クラス VBAClass1 によって VBA に (VBA の実装を通じて) 実装され、その VBA クラスのインスタンスを以前の VBA メソッドの新しいバージョンに渡して、0.0 で評価を実行します。

私の問題は次のとおりです。私はATLの専門家でも、インターフェイスの作成の専門家でもありません。インターフェイスの作成で本当にばかげた/些細なことをすることに成功しました。私がやりたいこと、つまり:

ATL のインターフェイス (定義上、ATL 構造によってメソッドのみを持ちます!) クラスのメンバーの役割を果たす「何か」を持ち、ATL を介して渡される VBA 関数を追跡するメンバー方法。

皆さん、どんな助けも大歓迎です。

ここに来るのは初めてなので、メッセージが完璧/最適な形式でない場合は、事前に申し訳ありません.

前もって感謝します。

ラフ

4

1 に答える 1

0

AddressOfFuncパラメータとコールバック アドレスに関するすべてを再考する必要があります。COM インターフェイスを設計し、コールバックを提供する場合は、呼び出し可能な COM インターフェイス ポインターを引数として渡す必要があります (関連する説明)。これにより、COM クラスはそのメソッドをコールバックします。VBA クラスがこのインターフェイスを実装する必要があります。 (関連する質問)。VBA 側でインターフェイスを実装したくない場合は、別のオプションとして、イベント (接続ポイント) を使用して ATL COM クラスを設計し、COM クラスがイベントを発生させ、呼び出し元がそこでコールバック アクティビティを実行してそれを処理するようにします。

関数のプレーンアドレスはここでは機能しません。コンパイルするためにキャストする必要がある場合は、それを疑う必要があります。

UPD。これは、VB コードをコールバックする方法のサンプルを提供します。C++/ATL クラスからの VBScript (VB6、VBA) コールバックを実装する 3 つの方法

于 2012-08-07T14:10:47.677 に答える