Status update (June 24th)

Below is the status report from June 24th.

The current state of the code in Subversion is as follows: There is support for global functions and variables and for member functions (member variables should be not too hard, given that they are just transformed into member functions). Their definitions are transformed into an IDL file which can then be compiled into a type library – at this stage the generated IDL file requires no additional tweaking and can be passed to MIDL as is. Also the wrapper file now includes the vtable layouts and the implementations of QueryInterface/AddRef/Release (and of course other functions depending on the class) for the wrapped objects. Wrapped objects also include an implementation of an interface that I called ISWIGCOMWrappedObject; this interface allows to differentiate between objects wrapped by SWIG and external objects that may be provided by the COM client (in the latter case directors will be needed). The wrapper code also includes an implementation of a class factory and an implementation of DllGetClassObject. The implementation of the COM objects is rather low-level, using structs and function pointers (and lots of pointer arithmetic!;)) instead of classes and virtual methods. The reason for this is to be as compatible with various compilers as possible (especially MinGW which does not support the object layout used in COM), and also to allow for wrapping C-only code. The downside is that the generated code is rather hard to read.

The generated code can be compiled (without the need of any tweaking) and linked with the C/C++ code to form a DLL. Therefore I am planning to start testing the implementations using languages with COM object support (Delphi and possibly also VB if it doesn’t require IDispatch). Then I plan to finish the wrapping code by supporting static functions, static member variables, enums and constants.

Below is an example header file and fragments of the files generated from it (using swig -c++ -com simple.i)

// simple.h
extern int globalVar;
double globalFunc(int,long);

class anAbstractClass {
public:
  int memberVar;
  virtual int memberFunc(int, double) = 0;
};

class aClass : public anAbstractClass {
public:
  virtual int memberFunc(int, double);
};

class aSubclass : public aClass {
public:
  virtual int anotherMemberFunc(anAbstractClass *);
};
// simple.i
%module simple

%{
#include "simple.h"
%}

%include "simple.h"

The generated IDL file looks as follows:

/*  ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 1.3.36
*
* Do not make changes to this file unless you know what you are  doing--modify
* the SWIG interface file instead.
*  -----------------------------------------------------------------------------  */

import "unknwn.idl";

[
  uuid(4EC3E0ED-3CB2-4F1A-8185-73FADCC57505)
]
library simpleTLB {

  interface anAbstractClass;
  interface aClass;
  interface aSubclass;

  [
    object,
    local,
    uuid(4EC3E0ED-3CB2-4F1A-8185-73FADCC57506)
  ]
  interface simple : IUnknown {
    void globalVar_set(/* comtype */ int arg0);
    /* comtype */ int globalVar_get();
    /* comtype */ double globalFunc(/* comtype */ int arg0, /* comtype  */ long arg1);
  };
  [
    object,
    local,
    uuid(4EC3E0ED-3CB2-4F1A-8185-73FADCC57500)
  ]
  interface anAbstractClass : IUnknown {
    /* comtype */ int memberFunc(/* comtype */ int arg0, /* comtype */  double arg1);
  };
  [
    uuid(4EC3E0ED-3CB2-4F1A-8185-73FADCC57502)
  ]
  coclass aClassImpl {
    interface aClass;
  };

  [
    object,
    local,
    uuid(4EC3E0ED-3CB2-4F1A-8185-73FADCC57501)
  ]
  interface aClass : anAbstractClass {
  };
  [
    uuid(4EC3E0ED-3CB2-4F1A-8185-73FADCC57504)
  ]
  coclass aSubclassImpl {
    interface aSubclass;
  };

  [
    object,
    local,
    uuid(4EC3E0ED-3CB2-4F1A-8185-73FADCC57503)
  ]
  interface aSubclass : aClass {
    /* comtype */ int anotherMemberFunc(/* comtype */ anAbstractClass *  arg0);
  };
};

The GUIDs are for the moment not generated properly – they are just consecutive GUIDs starting with a harcoded value. The /* comtype */ comments are just for debugging, so that I can easily see which typemap is being used. Below is a fragment which shows what the wrapper code looks like (with aClass as an example):

struct _SWIGCOMWrappedObject {
  _SWIG_funcptr *vtable; /* vtable for the methods of the wrapped object */
  _SWIG_funcptr *SWIGCOMWrappedObject_vtable; /* vtable for helper  methods */
  void *cPtr; /* pointer to the wrapped object */
  int cMemOwn; /* memory owned by the proxy? */
  long refCount; /* reference count */
};

/* ... */

/* ctype */ int _wrap_aClass_memberFunc(/* ctype */ _SWIGCOMIUnknown *  jarg1, /* ctype */ int jarg2, /*ctype */ double jarg3) {
  /* ctype */ int jresult ;
  aClass *arg1 = (aClass *) 0 ;
  int arg2 ;
  double arg3 ;
  int result;

  {
    _SWIGCOMIUnknown *wrapper;
    /* Call to QueryInterface */
    HRESULT hr = ((HRESULT (*)(_SWIGCOMIUnknown *, REFIID, void **)) jarg1->vtable[0])(jarg1, IID_ISWIGCOMWrappedObject, (void **)  &wrapper);
    if (hr != S_OK) {
    /* Argument was not wrapped by SWIG - directors will be needed */
    } else {
    /* GetCPtr */
    arg1 = (aClass *) ((void * (*)(_SWIGCOMIUnknown *))  (wrapper->vtable[3]))(wrapper);
    /* Release */
    ((long (*)(_SWIGCOMIUnknown *)) (wrapper->vtable[2]))(wrapper);
    }
  }

  /* in */ arg2 = (int)jarg2;
  /* in */ arg3 = (double)jarg3;
  result = (int)(arg1)->memberFunc(arg2,arg3);
  /* out */ jresult = result;
  return jresult;
}

/* ctype */ _SWIGCOMIUnknown * _wrap_new_aClass() {
  /* ctype */ _SWIGCOMIUnknown * jresult ;
  aClass *result = 0 ;

  result = (aClass *)new aClass();

  jresult = (_SWIGCOMIUnknown *) _SWIGCOM_wrap_aClass((void *) result);

  return jresult;
}

/* ... */

GUID IID_aClass = { 0x4ec3e0ed, 0x3cb2, 0x4f1a, { 0x81, 0x85, 0x73,  0xfa, 0xdc, 0xc5, 0x75, 0x01}};

GUID CLSID_aClass = { 0x4ec3e0ed, 0x3cb2, 0x4f1a, { 0x81, 0x85, 0x73,  0xfa, 0xdc, 0xc5, 0x75, 0x02}};

HRESULT _wrap_aClass_QueryInterface1(void *that, REFIID iid, void **  ppvObject) {
  if (iid == IID_ISWIGCOMWrappedObject) {
  /* FIXME: This could be more elegant */
  _SWIGCOMAddRef1(that);
  *ppvObject = (void *) ((void **)that + 1);
  return S_OK;
}

  if (iid == IID_IUnknown ||
      iid == IID_aClass ||
      iid == IID_anAbstractClass) {
    /* FIXME: This could be more elegant */
    _SWIGCOMAddRef1(that);
    *ppvObject = that;
    return S_OK;
  }

  return E_NOINTERFACE;
}

HRESULT _wrap_aClass_QueryInterface2(void *that, REFIID iid, void **  ppvObject) {
  return _wrap_aClass_QueryInterface1((void *) ((void **) that - 1),  iid, ppvObject);
}

_SWIG_funcptr _wrap_aClass_SWIGCOMWrappedObject_vtable[] = {
  (_SWIG_funcptr) _wrap_aClass_QueryInterface2,
  (_SWIG_funcptr) _SWIGCOMAddRef2,
  (_SWIG_funcptr) _SWIGCOMRelease2,
  (_SWIG_funcptr) _SWIGCOMGetCPtr
};

_SWIG_funcptr _wrap_aClass_vtable[] = {
  (_SWIG_funcptr) _wrap_aClass_QueryInterface1,
  (_SWIG_funcptr) _SWIGCOMAddRef1,
  (_SWIG_funcptr) _SWIGCOMRelease1,
  (_SWIG_funcptr)_wrap_anAbstractClass_memberFunc
};

void *_SWIGCOM_wrap_aClass(void *arg) {
  _SWIGCOMWrappedObject *res = new _SWIGCOMWrappedObject;
  res->vtable = _wrap_aClass_vtable;
  res->SWIGCOMWrappedObject_vtable =  _wrap_aClass_SWIGCOMWrappedObject_vtable;
  res->cPtr = arg;
  res->cMemOwn = 0;
  res->refCount = 0;
  return (void *) res;
}

Each wrapped object has two vtables (here _wrap_aClass_vtable and _wrap_aClass_SWIGCOMWrappedObject_vtable) with implementations of its own interface and ISWIGCOMWrappedObject. As you can see the code looks rather gory 😉 and I’ll try to make it somewhat more elegant. I have attached the complete example, together with the generated simple.idl and simple_wrap.cxx for convenience, as a zipfile.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: