External interfacing with struct containing virtual function

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

External interfacing with struct containing virtual function

Yar Hwee Boon-3
Hi all,

I trying to send a windows message with the LPARAM argument as a  
HTMENGINE_ENUM_OBJECTS_CALLBACK*.

The declaration for the struct is:

     typedef struct tagHTMENGINE_ENUM_OBJECTS_CALLBACK
     {
       virtual BOOL __stdcall CurrentObject(LPCSTR lpszObjectTypeName) = 0;
     } HTMENGINE_ENUM_OBJECTS_CALLBACK;

I've defined an ExternalStructure subclass with the method

HTMENGINE_ENUM_OBJECTS_CALLBACK>>defineFields
        self
                defineField: #callback
                        type: LPVOIDField new
                        offset: 0.
        self byteSize: 4

and run
     callback := ExternalCallback
                block: [:name | self halt.]
                descriptor: (ExternalDescriptor returnType: 'bool' argumentTypes:  
'lpstr')
     struct := HTMENGINE_ENUM_OBJECTS_CALLBACK new.
     struct callback: callback yourAddress.
     control sendMessage: WM_HE_ENUM_CURRENT_OBJECTS wParam: 0 lParam:  
struct yourAddress


Dolphin crashes without any error message nor write to the error log. Does  
the virtual keyword make the struct incompatible with C-structs (virtual  
ptr tables, this* etc). I know I probably did something very wrong, can  
anyone help? Thanks.

--
Regards
HweeBoon
MotionObj


Reply | Threaded
Open this post in threaded view
|

Re: External interfacing with struct containing virtual function

Chris Uppal-3
Yar Hwee Boon wrote:

> The declaration for the struct is:
>
>      typedef struct tagHTMENGINE_ENUM_OBJECTS_CALLBACK
>      {
>        virtual BOOL __stdcall CurrentObject(LPCSTR lpszObjectTypeName) =
>      0;
>      } HTMENGINE_ENUM_OBJECTS_CALLBACK;

That isn't at all the same as the 'C' struct that might be written (something
like):

    typedef struct
    {
        BOOL (*function_name)(char *name);
    } CALLBACK;

which, I think, is how you are trying to treat it.

Instead of defining a struct with 1 field which is a 'C' function pointer, the
HTMENGINE_ENUM_OBJECTS_CALLBACK is a C++ /class/ with a hidden pointer to
vtable (which itself is an array of function pointers).

I think you have a choice of two approaches.

The first is to keep it simple, and write yourself a C++ helper DLL, that would
define some new class that inherits from HTMENGINE_ENUM_OBJECTS_CALLBACK, and
implements the virtual function by calling a normal 'C' function pointers that
is stored in some field.  You would create those things (you'd need a static
factory method in the DLL), set the function pointer from Dolphin (to an
external callback), and then pass the address of that object to whatever it is
that uses the HTMENGINE_ENUM_OBJECTS_CALLBACK.

Quick sketch follows.  It /WILL/ be wrong in details of the syntax (It's
several years since I wrote any C++), and quite probably of the implementation
too...

======== in header file ========

class CallbackHolder
:  HTMENGINE_ENUM_OBJECTS_CALLBACK
{
private:
        BOOL (_stdcall * callback)(char *);

public:
        virtual BOOL __stdcall CurrentObject(char *name);

        CallbackHolder(BOOL (_stdcall * func_ptr)(char *));
}


extern 'C' {    // 'C'-style to minimise name mangling

extern CallbackHolder* MakeCallbackHolder(BOOL (_stdcall * callback)(char*));

extern void ReleaseCallbackHolder(CallbackHolder*);

}

======== in cpp file ========

CallbackHolder *
MakeCallbackHolder(BOOL (_stdcall * func_ptr)(char *))
{
    return new CallbackHolder(func_ptr);
}

void
ReleaseCallbackHolder(CallbackHolder *holder)
{
    delete holder;
}

CallbackHolder::CallbackHolder(BOOL (_stdcall * func_ptr)(char *))
{
    : callback = func_ptr
}

BOOL __stdcall CallbackHolder::CurrentObject(char *name)
{
    // this will crash if 'callback' hasn't been set yet !
    return callback(name);
}

================

(Excuse my ignoring the horrible M$-style "LPCSTR", etc -- life's too short for
that crap ;-)

Once you've set that up and created a corresponding ExternalLibrary and perhaps
an ExternalStructure (with no data elements, since the above code works with
opaque pointers), you should be able to create an instance using
MakeCallbackHolder() passing an instance of ExternalCallback as its parameter
(which will be saved in the 'callback' field), and then pass that to your HTML
library.  (Not forgetting, of course, to free it up with
ReleaseCallbackHolder() when you are finished with it.)

That's all pretty ugly, but then the alternative isn't a lot better...

Alternatively, if you want to be daring, or just can't stomach C++, then you
could do the whole thing from Dolphin.  In that case you'd have to build the
equivalent of the vtable yourself.

I haven't tested any of this (though I have manipulated vtables from Dolphin
before), but I /think/ it goes something like:

The HTMENGINE_ENUM_OBJECTS_CALLBACK object should look as if it had a single
element which you initialise to be a pointer to an array of function pointers.
Each of those function pointers should be an ExternalCallback that takes /two/
arguments, the first is a pointer to 'this' (may as well make it a void*), the
second is the official char * 'name' argument.  I seem to remember that M$ C++
vtables have a dummy entry or two at the start, so I suggest trying something
like the following.

Assuming an ExternalStructure called CALLBACK which has a single void* called
'vtable', the following Dolphin code /might/, if I've got it right, build up
the correct structure.  I'm building an array of several different
ExternalCallbacks because I'm not sure which one will actually be invoked
(that's something you can only learn by testing).

====================

descriptor := ExternalDescriptor
      callingConvention: 'stdcall:'
      returnType: 'bool'
      argumentTypes: 'void* lpstr'.

"don't use a loop for this !"
actualCallbacks := OrderedCollection new.
actualCallbacks add: (ExternalCallback
   block: [:this :name | Transcript display: 1; cr. 0]
   descriptor: descriptor).
actualCallbacks add: (ExternalCallback
   block: [:this :name | Transcript display: 2; cr. 0]
   descriptor: descriptor).
actualCallbacks add: (ExternalCallback
   block: [:this :name | Transcript display: 3; cr. 0]
   descriptor: descriptor).
actualCallbacks add: (ExternalCallback
   block: [:this :name | Transcript display: 4; cr. 0]
   descriptor: descriptor).

vtable := DWORDArray new: actualCallbacks size.
actualCallbacks keysAndValuesDo: [:i :each | vtable at: i put: each
yourAddress].

callback := CALLBACK new.
callback vtable: vtable yourAddress.

====================

I want to repeat the earlier warning: I haven't tested /any/ of this.  It may
work, it may not. It may crash your image, it may not.  I think it (or
something like it) /should/ work, but it's up to you...

HTH ;-)

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: External interfacing with struct containing virtual function

Yar Hwee Boon-3
On Sun, 26 Sep 2004 19:21:40 +0100, Chris Uppal  
<[hidden email]> wrote:

> The first is to keep it simple, and write yourself a C++ helper DLL,  
> that would
> define some new class that inherits from  
> HTMENGINE_ENUM_OBJECTS_CALLBACK, and
> implements the virtual function by calling a normal 'C' function  
> pointers that
> is stored in some field.  You would create those things (you'd need a  
> static
> factory method in the DLL), set the function pointer from Dolphin (to an
> external callback), and then pass the address of that object to whatever  
> it is
> that uses the HTMENGINE_ENUM_OBJECTS_CALLBACK.

It works. Thanks for describing it so clearly with all the example code.  
Like you said, the external structure definition is no longer needed. I  
didn't try the Smalltalk approach though.. looks fragile. Maybe save it  
for a day when I need the brain-workout :)

--
Regards
HweeBoon
MotionObj