Using COM Objects from Max

BlitzMax Forums/BlitzMax Programming/Using COM Objects from Max

Shaun Sullivan(Posted 2005) [#1]
Any of you have any experience doing this? Any samples would be greatly appreciated!

Shaun Sullivan
PureSim Baseball
www.puresim.com


Nennig(Posted 2005) [#2]
Yes, I agree this would be marvelous!
If you know, please teach us!


Red Ocktober(Posted 2005) [#3]
i dunno... from what i can remember, COM (activex) is a two way street... you app has to be a COM container before it can host a COM server...

but that may apply to in process only, cause there is such a thing as an ActiveX exe...

anyway, i think that a COM wrapper that is a COM container, and also exports the COM interfaces (iUknown and QueryInterface me thinks) will have to be written...

in short... i don't remember, but i will be looking into this in the near future...

--Mike


Nennig(Posted 2005) [#4]
Hope to hear from you again about this.
Have fun.


JoJo(Posted 2005) [#5]
Mark mentioned Max using COM Objects.

http://www.blitzbasic.com/Community/posts.php?topic=44853


ziggy(Posted 2005) [#6]
You have to 'comunicate' with the COM objetc in the same way you would do with a DLL, but you have to follow an specific protocol, you have to interact with the IDispatch of the Object and 'emulate' a container. You have to pass function pointer to let the COM object throw events (or event). and then interpret the info received by the COM. In fact, once the COM is registered in the container app. You have to deal basically only with one function, that uses only two parameters, the first one is the name of the 'logical' function to call, and the second one is an array with all the paramenters to pass to the 'logical' function.
What I mean is that a COM object has is just and interface and this interface can be managed from a Blitz Max application.


marksibly(Posted 2005) [#7]
Hi,

I think all that dispatch stuff is to do with OLE - which is based on COM.

There are no plans to add OLE to Max - too complex for my liking. You may be able to do it yourself, though.


Sweenie(Posted 2005) [#8]
Here is an interesting article on how to use COM in Visual Basic.
I think it could be "ported" to Bmax.
In the article a helper dll is used but I'm sure the functions in the helper dll could be made directly in Bmax.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncomg/html/msdn_house6.asp


Shaun Sullivan(Posted 2005) [#9]
There are no plans to add OLE to Max - too complex for my liking. You may be able to do it yourself, though


Mark, I saw an earlier post from you where you were calling DirectX9 (for some new stuff that is coming). How were you able to do that? DX9 is based on COM.


Drago(Posted 2005) [#10]
mark wont be giving us access to the directX interface in blitz, the C/C++ code for the library will be doing all that, since if mark writes the engine properly (abstract interface) then engine wont know if it is running in openGL or DX, it will just tell it to render, or add a vertex, and the interface does all the work.


marksibly(Posted 2005) [#11]

Mark, I saw an earlier post from you where you were calling DirectX9 (for some new stuff that is coming). How were you able to do that? DX9 is based on COM



I've 'tweaked' the compiler so that you can use 'native' C++ objects from within Max, like this:

Extern "Win32"
Type IUnknown
   Method QueryInterface()
   Method AddRef()
   Method Release()
End Type
Type IDirect3D9 Extends IUnknown
  Method Blah()
  Method Etc()
End Type
End Extern


There are some limitations: You can't 'New' such 'extern objects' - this must be done by somewhere else by non-Max code, and of course they're not managed for you. They can't extend normal Blitz Types, and vice versa.

COM objects are really simplified C++ objects, so this is enough to be able to access DirectX pretty cleanly. I haven't looked into the 'CoCreateInstance' stuff, as DirectX functions do this for you, but I don't think that would be a huge headache.

As far as I know, OLE/ActiveX and all that crud is built on top of COM and CoCreate etc, but its not something I'll be spending any time on.


gman(Posted 2005) [#12]
and when might we see this 'tweaked' version of the compiler? :) it would seriously impact the direction i take on my current project if the capability of using an object natively were available to me.


GW(Posted 2005) [#13]
Wouldnt that also allow someone to make a port of some of the open source, cross platform C++ graphics libraries out there like Ogre?


gman(Posted 2005) [#14]
you can do that now by wrapping the methods up in functions and passing in pointers to instances of the classes... but mark's tweak would make something like that much, much easier and more intuitive :)


Red Ocktober(Posted 2005) [#15]
yes... that would save us a ton of work... ok, maybe not a ton :)

so far i'm just using the mac demo version, but this looks like it would definitely be a work saver...

--Mike


marksibly(Posted 2005) [#16]

Wouldnt that also allow someone to make a port of some of the open source, cross platform C++ graphics libraries out there like Ogre?



Alas, no.

All methods in the c++ class must be 'virtual' - this is rarely the case for most C++ libs.


Drago(Posted 2005) [#17]
so it is only COM objects then, not c++ classes?


StuC(Posted 2005) [#18]
IDispatch is used for making late bound calls to COM objects, and being able to discover their methods and properties at runtime. To happen seemlessly, it must be generated by the compiler, so you can write code like to call a method e.g. 'ObjInstance.Method'. The compiler generates calls to GetIDsOfNames, and Invoke behind the scenes. Not good if you want fast code, as you are making indirect calls to the COM objects methods.

Also, these COM objects must implement IDispatch before this is even possible.

If Mark has 'tweaked' the compiler, as he says, I assume that the IUnknown declaration above generates a virtual method table (vtable), compatible with COM (and C++ for that matter).

This means that you could call any COM object, but you must inspect the type library yourself to extract the exact definition of the interface. In the case of IUnknown, the methods "QueryInterface", "AddRef" and "Release" MUST be defined in this order, so the vtables match.

Say COM object MyObj was defined as :

interface IMyObj : IUnknown
{
  void Initialize;
  void SomeMethod;
  void Uninitialize;
}


You could define your BMax object as:

Type MyObj Extends IUnknown
  Method Initialize
  Method SomeMethod
  Method Uninitialize
End Type


All COM objects must be derived from IUnknown.

If the object you wanted to call was derived from IDispatch, you would have to include a definition of IDispatch in BMax.
Since it wouldn't be called by your code, and is just a place holder for the vtable, I've only defined the methods, and not their parameters

Type IDispatch Extends IUnknown
  
  Method GetTypeInfoCount
  Method GetTypeInfo
  Method GetIDsOfNames
  Method Invoke
  
End Type


Your IDispatch derived object would then be defined as:

Type MyObj Extends IDispatch
  Method Initialize
  Method SomeMethod
  Method Uninitialize
End Type


I don't have BMax, so I can't show you how to create the COM object, but check out CoCreateInstance and CLSIDFromProgID.

Cheers,

Stu


Nennig(Posted 2005) [#19]
Hi Stuart,

You seem to know your way around COM....
Too bad you did not purchase Blitzmax yet...

You could bring a very nice addition to the product.
I would do it myself, if only I had the skills...
;-)


StuC(Posted 2005) [#20]
Hi Nennig,

what COM object specifically do you want to interact with? I will purchase Blitz Max soon (I am already a Blitz3D owner). I can then show you how to create your COM object.

I just don't have a Mac, and there was no literature indicating I would get the beta of Blitz Max for the PC - but as I understand, I will :)

Cheers,

Stu


Nennig(Posted 2005) [#21]
Hi Stuart,

It is very generous of you.
I would like to learn how to interact with Excel.

This would open for me huge possibilities to use Bmax at work.

Thank you.

Nennig


StuC(Posted 2005) [#22]
Nennig,

I purchased BMax, and did some experimentation. I added an ole32.bmx module to the Pub.Win32 space, to include the functions I mentioned above.

I was able to create an instance of Excel and receive an IUnknown pointer, however there are a number of limitations with BMax at the moment, that would mean you have to jump through considerable hoops to use Excel.

A question you ask yourself when choosing a development environment for a particular task is 'How well suited is it for the task I'm trying to solve?'. Also 'Will I be most productive in this environment when trying to solve this task?'.
If you ask youself that question for this problem, BMax is not that environment.

I spent several hours yesterday attempting to interact with Excel. Granted, some time was spent getting up to speed with BMax, but not a great deal.
I could have achieved the same result in less than 10 minutes in Delphi, C#, C++ or an active script language.


Some food for thought:

There are two primary ways we interact with COM objects, either using early binding or late binding.

Early binding involves importing the COM object's type library, which generates a set of interface definitions (like an abstract Type in BMax) and additionally class / type / object wrappers (depending on your language) for you to use natively in your development environment. These tools are specific to each development environment.

Using early binding generates code that is almost as fast as using native objects. It also has the benefit of compile-time error checking and type verification.

For late bound interaction, firstly the COM object must be derived from IDispatch, and to be of any use, your dev environment should support late bound calls. Late binding means the compiler generates calls to IDispatch->Invoke behind the scenes at runtime. There are a few disadvantages to this method.
1. If your object doesn't support a particular method, you will receive runtime errors, as there is no type checking performed at compile time (as the compiler doesn't know what the instance of your object will be). (Not impossible, but no environment I have used does).

So back to BMax.

Reason 1. BMax does not support late bound calls to IDispatch-able COM objects (like VBScript, JScript, Delphi).

Reason 2. BMax does not have the supporting tools for early bound COM interaction (like C++, Delphi and .Net).

I would recommend you either create a DLL to wrap calls to Excel or use VB.Net or some other more suited environment for COM.

Cheers,

Stu


Nennig(Posted 2005) [#23]
Hi Stuart,

Thanks a lot for all your explanations. I am very aware that Bmax is probably not the best tool for playing with Excel.

I was just hoping that this would have been possible in a way, I was not aware of.

Now, thanks to you, I have a better picture of the situation.

I have to admit, that I would have loved to have Bmax installed on my PC at work with my manager's approval.

It would have give me, very certainly, the opportunity to spend a bit of time on my own projects. :-)

Thanks for trying.

Bye

Nennig


gman(Posted 2005) [#24]
@StuC

i will be the first to admit i know little about the inner-workings of COM so this may come out a little goofy... but would there be a way to use the ability of BMAX to build in C++ code via MingW, exposing functions through the Extern statement, and function pointers to potentially build a dynamic calling system? use C++ underneath to make all the calls to the COM contol and expose a single interface through Extern... kinda like:

Module StuC.ReallyCoolCOMInterfaceMod

Import "COMinterface.cpp"

Public
(this exposes a few functions like)
Extern
Function createCOMControl:Int(comidstring:Byte Ptr) ' returns handle to instantiated control
Function closeCOM(handle:Int)
Function callMethod(handle:Int, stuff...)
Function getProp(handle:Int, prop:Byte Ptr)
Function setProp(handle:Int, prop:Byte Ptr, stuff...)
EndExtern

--------------------------

then in BMAX we could interact with the control using your interface functions. data would probably have to go back and forth via banks... heck i dont know... is this type of thing possible?

basically the goal would be to keep the instance of the COM object in C++ and use a handle to access it via BMAX using functions exposed via the Extern statement. the exposed functions would be written in C++ and access the COM object directly using the handle passed in.


StuC(Posted 2005) [#25]
Yes, absolutely. You are pretty much spot on.

It would require using all late bound calls, since we wouldn't want to rebuild the C++ module anytime we wanted to add a new COM object. No a big deal though

The only problem is handling the variety of parameter lists for the method calls. My thinking would be to provide some way to add the parameters to a list or array. I'd hate to have to deal with poking / peeking bytes.. We can be smarter than that.

Something like:

Extern
  Function COMCreateArgList:Int()  ' returns handle to arg list

  Function COMAddArg(value:Byte Ptr, ArgType:Int) 
  ' value would be a pointer to the variable containing the data.  Variable would be any type
  ' ArgType would be a constant, indicating the type of pointer.  Somewhat similar in principal to a variant type.  e.g. ARG_INT, ARG_STRING, ARG_FLOAT...

  Function COMCallMethod(Handle:Int, MethodName:Byte Ptr, ArgList:Int) ' ArgList could be Null.

End Extern