Fog Creek Software
Discussion Board




Wrapping an existing C API in ActiveX

Hello all,

One of my company's 'outputs' is an API with an interface in C.  This is an extensive API with just over 400 functions.  We are increasingly having customers express interest in an ActiveX control for use in VB or (I am told) Macromedia Director, and we'd like to make this happen.

We know very, very little about ActiveX/COM/etc., and this project is involving a lot of learning, as you might imagine.  My question is this: What is the most appropriate way to expose this as an ActiveX control? 

From my point of view, the actual details of the API functionality are unimportant; we just want to expose 400 functions to the user using the same call parameters as the C API, etc.  This way documentation can be re-used, and we don't get involved with supporting/updating the interface.  (the .ocx would be generated by a script from the .H files)

I have been looking at the IDL file quite a bit and the "module" block looks like what we'd like, but somehow I doubt that would make for an actual ActiveX object.  (the API has no user interface, so it would be an invisible component)  I also investigated creating a "coclass," but ran into problems because of the mandatory HRESULT return type.

I suppose there's .Net, which we have even less knowledge of, but from what I've seen a COM object with a bunch of functions attached would satisfy our needs easily.

Any insight would be greatly appreciated. 

Adam

Adam Preble
Friday, August 29, 2003

I think your main problem will be in making any data types you have into data types safe for passing around through COM interfaces.  Strings should be BSTRs, arrays are different,etc etc.  You can't just pass pointers around.  That all depends on how complicated your API is.

However, I think doing this in C is going to be a nightmare.  All the COM stuff I've done was in C++ using ATL, and that was still rather non-trivial and required a few books to get me in the mindset, and I even had some raw COM experience from taking a class that used Don Box's COM book.

I think the best thing to do is get a COM book and start from there.

Alex
Friday, August 29, 2003

this isn't particularly difficult, but you will need to write glue to adapt to the different model. (and you'll need to understand that model.)

if you want a real activex object, you'll have to write a layer to interface the two, and here's why:

datatypes. you should use the standard ones (VARIANT is a good place to start, it's sort of a superset of the types)
refcounting. if you only have simple data types (e.g. this is a math API which maniplates numbers), you're OK. but if you have multiple objects, you should make it object oriented. so avoid magic handles, etc. even if you use magic handles, you'll have to manage refcounting.
standard return types. you really should use hresult for almost everything, unless your methods never return errors.
automation. most people call activex controls through IDispatch. this is what makes it easy to use in vb or whereever. you'll either need to write this yourself (PITA) or let a tool manage this.

one place to start is let visual studio build the control for you. it's been a while, it might use ATL or MFC maybe VB or whatever. or there's probably some other toolkits around. an activex control with no UI really needs to implement very few interfaces. (i don't remember what... maybe just iobjectwithsite?)

the activex layer will probably be very simple glue, assuming no object refcount issues.

mb
Friday, August 29, 2003

I'd just keep it simple and have the COM (Active X) object sit in-between instead of wrap. Then you only have to maintain your C DLL or libs as always and won't have that clunky C++ thing sitting in there, unless COM and C++ is the way you're going to go with it anyway.

Also you probably only need to make a simple COM object which you can still call from VB (or any other automation app); ActiveX is more intended for embedding in a GUI (e.g. if you were making a control for a dialog box) so has a lot of extra interfaces. But you can make a full ActiveX if you want, it'll just make some extra boilerplate code in the shell.

So personally I'd start in Visual Studio with the ATL wizard and a new ATL COM project, insert a simple COM object with all the defaults and create an interface mimicking all your function names (and as others have pointed out this will probably be your biggest problem if you pass lots of data back and forth because not all types can be passed through COM like it can in C).

Then the wizard will create the shell DLL and class with all of the function names stubbed out; from there it's "just" a matter of linking in your .c library and calling the corresponding function for each corresponding method in the ATL shell class. 

But translating the data will be the toughest, in general you can only easily pass long, double, float, and bool or pointers to them (no int, or char *, or array or void or function pointers). For char * you need to convert to and from BSTRs which there are easy macros for (OLE2A and A2OLE), but for arrays you would need to create SafeArrays if you want to be able to pass back and forth to VB, which can be a little of a pain but is doable. 

But if you're just passing simple data types, once you get past the learning curve (of maybe a couple months if you want to understand COM, less if you just want to do enough to make it work) it's probably less than a week or so to wire it all together.

Rick
Friday, August 29, 2003

1) There's no difference between int and long under Win32, so you can "pass ints".. you just call them longs.

2) You can actually get away with a lot of COM subversion in many cases by passing around long pointers that are then cast into the right datatype inside the wrapper.  Of course, doing this is generally a bad idea because if the object is run out of process, or (of course) remotely, you're screwed.  But it can be a useful way to get things to "just work" as a starting point for porting.

Mister Fancypants
Friday, August 29, 2003

Modules in IDL aren't COM - they're something VB specifically added, and nobody uses them for anything. So that won't work.

You're going to have to bite the bullet and do some interface work, unfortunately. You need to have HRESULT return values, and (if you want to be callable from VB) use what are called automation compatible types. Basically, limit yourself to what can be stuffed in a variant. It's annoying, and it's another layer to maintain, but it really is the best way for your clients.

While you're at it, you might want to go a step further and do a little refactoring in your API. I suspect that among your 400 flat functions you've probably got several conceptual "objects" - sets of functions that work together. Start grouping them into actual COM objects. Your customers will thank you.

Chris Tavares
Friday, August 29, 2003

Oh, and one more thing:

if you need to do this in C/C++ and you don't have any in-house COM programming expertise, don't try to do it yourself. Get some training. Hire a contractor. Get a mentor in house. COM is really hard to wrap your head around the first time, and having a teacher really, really helps.

-Chris (who does teach COM/ATL classes, but don't let that throw you)

Chris Tavares
Friday, August 29, 2003

I did this for a company once before. 

I ended up having the COM object import the dll and just serve as an intermediary between the library & the external app.  I went this way because the client didn't want me to see their internal code.  That way they just gave me the dll and the docs and I went to work.  Oh, and the docs will have to be different because you will have to pass around different data types. 

There's lot of other issues that need to be taken into account, such as the use of global variables, what kind and how many external apps will use the new ActiveX control, etc...

lr
Saturday, August 30, 2003

Adam: You might be able to save yourself a lot of work using Doxygen [ http://www.doxygen.org ] or Swig [ http://www.swig.org ].

Doxygen collects all documentation/definitions from C and C++ source, but can output, in addition to the HTML / RTF / Latex documentation, an XML file that describes all the functions in your code; You can then translate this XML file to wrappers of your choice (alternatively, it will build a Perl datastructure for you that you can use instead of the XML).

SWIG stands for Simplified Wrapper Interface Generator. It reads C and C++ code, and generates wrappers for a variety of languages (C#, Java, Python, Scheme, Perl, ...). It doesn't, as far as I know, have a COM backend, but one shouldn't be too hard. SWIG is very smart, and it can, e.g. have conversion rules that depend on set of parameters and possibly their names, so that if two parameters "char *body, int len" appear in a parameter list of a function, it can pass them as one C# string parameter instead.

Both of these are "part two" of the solution - once you know how to do COM, they'll let you do it efficiently to 400 functions, instead of having to manually wrap each. However, you still have to go through "part one" - understanding what you really have to do.

You should try getting help from someone experienced (really experienced; not one of the clueless experts whose experience is more or less attending a Don Box seminar once). COM is basically logical and understandable, but there are many places where the devil is in the details, mostly IIRC with respect to threads and exception handling.

Ori Berger
Saturday, August 30, 2003

Adam,

I would recommend using VB 6 for this. If you choose to go this route, I would sign up for the VISBAS-L list at:

http://peach.ease.lsoft.com/scripts/wa.exe?A0=visbas-l#SUB

People on this list are very likely to be helpful - Tracy (male) on the list is very good at mapping C to equivalent VB API declarations.

Seeya

Matthew
Sunday, August 31, 2003

I'd like to say thanks to all of you for your valuable advice.  Once the generalities of the interface are figured out, passing data around will definitely be a challenge.  I'm somewhat inclined to implement a simplified interface to our API, except I know that somebody will come along and want more features.

As an aside, we are presently using DocBook for our documentation. 

Finally, if you were curious, our API controls an MPEG-2 video decoder card for broadcast use.

Adam Preble
Tuesday, September 02, 2003

I've tried to use a number of ActiveX/COM interfaces where it was originally a C API. If the target for the API is VB, please get someone who is familar with VB to review it with VB. The ones I've seen, while workable, don't work anything like "normal" COM interfaces.

pdq
Tuesday, September 02, 2003

*  Recent Topics

*  Fog Creek Home