Reputation: 3814
Edit :
I have an ATL simple object MyATLObject, having only one method :
STDMETHODIMP CMyATLObject::EVAL( DOUBLE* x, long AddressOfFunc )
{
*x = toto<FCTPTR>(*((FCTPTR) AddressOfFunc)) ;
return S_OK;
}
where toto is a template function defined by
#pragma once
template<typename F>
double toto( F f )
{
return f(0.0) ;
}
and where FCTPTR is defined as
typedef double (__stdcall * FCTPTR)( double );
My ATL method consume double x for storing the result of toto, and long AddressOfFunc which is the AddressOf of a VBA function.
This passing VBA function to ATL methods by Address. it is quite awful. How would it be able to do the same - passing VBA functions - to ATL method otherwise, for instance by defining an interface on the ATL side ?
Thank you very much.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
First version of my question :
I am actually doing the following : creating an ATL/COM project, adding a "simple ATL object" called MyATLObject to it with :
1) in the idl file :
interface IMyATLObject : IDispatch{
[id(1), helpstring("method EVAL")] HRESULT EVAL(DOUBLE* x, long AddressOfFunc) ;
};
2 in the MyATLObject.h file :
an
#include "MyStupidEvaluator.h"
and a
typedef double (__stdcall * FCTPTR)( double );
outside the class, and in the class
public:
STDMETHOD(EVAL)(DOUBLE* x, long AddressOfFunc) ;
and finally in the MyATLObject.cpp file :
STDMETHODIMP CMyATLObject::EVAL( DOUBLE* x, long AddressOfFunc )
{
*x = toto<FCTPTR>(*((FCTPTR) AddressOfFunc)) ;
return S_OK;
}
MyStupidEvaluator.h contains :
#pragma once
template<typename F>
double toto( F f )
{
return f(0.0) ;
}
This is just a template function that returns the value at zero of any function object, that is a typename (or a class) admitting a () operator.
Now, this gives to me, after compilation, a dll.
That's all for the c++ part. Now, I'm referencing this dll in VBA and in VBA :
a) I define a function
Public Function F(ByVal x As Double) As Double
F = x * x - 2 * x + 1
End Function
b) and a sub
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) Executing this sub will give to me, in the range V"alue_at_0" (just a cell here) of the sheet "Test1" the value at 0.0 of the function F.
I'm always working like this : the toto template function is trivial here (evaluation at 0.0) but in general it could be a numerical integration routine, or a root finding routine for which c++ is needed for quickness ; I'm alwas passing VBA functions through their addresses, thanks to the AddressOf VBA keyword : this gives me a long that I pass to my ATL method EVAL, I convert this long in function pointer (FCTPTR) p, and I apply my routine "toto" to *p, which gives a double to me, that I pass back to my ATL method.
That's cool, everything works, but... I find this awful !!! And really inelegant. That's why I would like to do the following :
design "somehow" an interface on the ATL side, a
interface IMyFunction : IDispatch
that would be implemented (through VBA's implements) in VBA by a VBA class VBAClass1, and pass an instance of that VBA class to a new version of my former VBA method for performing the evaluation at 0.0.
My problem is : I'm not an expert at all in ATL, nor in interface creating : I've succeeded in doing real stupid/trivial things in interface creating, ok, but I really don't see how to design the interface doing what I want to do, that is :
an interface in ATL (which will by definition have only methods, by ATL construction !) that would have "something" playing the role of a member of a class, member keeping track of a VBA function still to be passed to him through an ATL method.
Any help would be greatly appreciated, folks.
It's my first time here, so sorry per advance if the message is not in the perfect/optimal form.
Thank you in advance.
Raph
Upvotes: 1
Views: 582
Reputation: 69632
You should reconsider everything you have around AddressOfFunc
parameter and callback address. When you design COM interface and you would like to provide a callback, you need to pass a callable COM interface pointer as an argument (related discussion), so that COM class would call its methods back - the would require your VBA class implements this interface (related question). If you prefer to not implement interfaces on VBA side, another option is to design ATL COM class with events (connection points), so that COM class would fire an event and your caller would handle it doing the callback activity there.
Plain address of function is not going to work here, you should have suspected it when you had to cast things to make them even compile.
UPD. This provides sample on how to call back VB code: Three ways to implement VBScript (VB6, VBA) callback from C++/ATL class
Upvotes: 0