LuaJIT COM Support

I am primarily a Windows based developer, so I like to take advantage of the many facilities that are available on the platform.  In the early days of Windows, most features were provided as straight ‘C’ libraries.  Sometimes, the were Pascal calling convention though, and that makes for fun and games.

Then, along came COM, and the world went off kilter for a while (my humble opinion).  COM was great for getting component parts into Word and Excel.  As a general programming paradigm, I never cared for it that much.  It tries to do too much, and while doing it, gets in my way more often than it helps me.  Now, most of the time, if you’re programming in Windows, you have the full capabilities of the Visual Studio environment to help you with your COM object interactions.  Nowadays, dealing with COM in that world isn’t much harder than dealing with any other object technologies.

But, here I am in LuaJIT, and suddenly all that support evaporates.  Now, of course there is the LuaCOM project, but why won’t I be using that?  Because it was written for ordinary Lua 5.x in mind, so it does a lot of stuff that I don’t need to do.  Furthermore, like many Lua interop libraries, it seems to have met the needs of the original authors, so it doesn’t seem to be much maintained these days.  Well, that’s good enough excuse for me to write a new interop layer using LuaJIT!!

I tell you, COM is a big hairy beast with sprawling tendrils that run all up and down the Windows OS, across multiple sub-systems, sucking in library upon library, paradigm upon paradigm.  I started innocently enough with the GUID/UIID object:

ffi.cdef[[
typedef struct {
unsigned long  Data1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[8];
} GUID, UUID, *LPGUID;
]]

That’s a good start as everything in the COM world is related to a GUID of one form or another.  So, defining the basic data structure is easy enough.  Once this data structure is defined, then creating a GUID can’t be that hard:

__index = {
Define = function(self, name, l, w1, w2, b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 )
return GUID({ l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }), name
end,
DefineOle = function(self, name, l, w1, w2)
return GUID({ l, w1, w2, { 0xC0,0,0,0,0,0,0,0x46 } }), name
end,
},

So, for creating a typical Ole GUID, you can simply do:

GUID():DefineOle(name, l, w1, w2)
GUID():Define(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7,b8)

Like in this case:

IID_INuiSensor =("IID_INuiSensor",0x1f5e088c,0xa8c7,0x41d3,0x99,0x57,0x20,0x96,0x77,0xa1,0x3e,0x85);

And, of course, the windows sytem itself has some convenience functions for creating new GUIDs, and constructing them from strings, so the following can be done as well:

IEnumMoniker = UUIDFromString("00000102-0000-0000-C000-000000000046")

Great.  That gets you the IID_ for some specific GUID.  That’s half the battle in COM.  The second half of the battle, or rather, the 2nd part of about 5 parts, is being able to get at your “interfaces”.  In COM, an “interface” is essentially defined as an structure, within which you’ve laid out the method calling table.  So, for example, here’s the definition of the “IUnknown” interface, which is a fairly common base for all other interfaces:

ffi.cdef[[
typedef HRESULT (*QueryInterfacePROC)(void * This, REFIID riid, void **ppvObject);
typedef ULONG (*AddRefPROC )(void * This);
typedef ULONG (*ReleasePROC)(void * This);

typedef struct _IUnknownVtbl {
QueryInterfacePROC QueryInterface;
AddRefPROC   AddRef;
ReleasePROC   Release;
} IUnknownVtbl;

typedef struct _IUnknown {
IUnknownVtbl *lpVtbl;
} IUnknown, *LPUNKNOWN;

Basically, first define your function prototypes.  Then define a structure in which this virtual table is the first item.  If you can load an object using the COM functions, then you can retrieve a specific interface, and make function calls.  Easy right?  So, almost half way there.  There are some issues related to the lifetime of objects, but LuaJIT gives you fairly good control of the lifetime of objects.  I know when they’re about to be garbage collected, so I can call a specific function to do whatever cleanup is necessary before the object moves on to the dearly departed.

Why bother with all this nonsense though?  Because.  First, I wanted to get simple access to my webcam, so that I could stream audio and video, and get at the hardware compression routines.  Then, I want to get at my joystick.  Although I can access it through the ancient WinMM interfaces, I’d rather use the more modern stuff.  And lastly, there’s the Kinect.  It’s got the simplest interface for getting at the video frames and audio, but you have to go through some COM calls to get there.

And so, it’s quite a ball of yarn to be pulling on, but I am managing to walk a path that gets me some basic COM interop sooner rather than later.  Once that basic interop is in place, I think getting at more of the Windows environment will just be that much easier.



Leave a comment