1

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

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Olórin
  • 3,367
  • 2
  • 22
  • 42

1 Answers1

0

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

Community
  • 1
  • 1
Roman R.
  • 68,205
  • 6
  • 94
  • 158
  • Thank you for you answer. I'm still a rookie on all of this, so that I don't really get you : as I explained, I definitively want to pass a callable interface pointer as an argument, yes, and I was therefore asking how I should design this interface, as I can't answer to this question myself for now, knowing almost nothing to ATL. – Olórin Aug 07 '12 at 14:29
  • For instance, in my case, how would you design the interface ? – Olórin Aug 07 '12 at 15:06
  • I mentioned to scenarios, which are both workable. I don't have a code snippet to show, sorry. On the first suggestion of the two - you will define callback interface in your ATL project (in IDL), and you can find [how to implement this interface in VBA](http://stackoverflow.com/questions/3171324/implementing-my-own-interface-in-vba-error-object-module-needs-to-implement) right here on SO. – Roman R. Aug 07 '12 at 16:51
  • I beg your pardon, if you think that my pointer that VBA class cannot be passed as `long` at any time (which you clearly had no foggiest idea of at the time of asking) is exactly the reason that you are not going to receive any "correct" answer any longer, then good luck to you. – Roman R. Aug 07 '12 at 18:11
  • I must just admit that as I already said : I'm new into all of this, and I really don't understand what you have written, espacially the "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)". I had a look on the related discussion, and the example given is a exemple where the VBA that is passed is used through – Olórin Aug 09 '12 at 07:12
  • STDMETHODIMP CMyATLObject::RegisterCallback(IMyCallback* cb) { CComPtr p(cb); // Accessing the interface is now straightforward CComBSTR msg = "Msg from ATL"; p->Execute(msg); return S_OK; } for returning a string ("p->Execute(msg);"). My point here is that I would like to act on the passed function by pasing it to some routine. And I don't know how to do this. I'd like to see VBA passed function as object on the ATL side, not as functions. If I have understood, you say that for this, I can't use interfaces, but that should use ATL COM class with events instead, right ? – Olórin Aug 09 '12 at 07:16
  • OK, nobody wants to give details - I will: [Three ways to implement VBScript (VB6, VBA) callback from C++/ATL class](http://alax.info/blog/1381) – Roman R. Aug 11 '12 at 12:23