views:

404

answers:

6

Hi there.

I am trying to build an app that uses a COM component in VisualStudio ´05 in native C++. The mix of native and managed desciptions of things in the MSDN totally wrecked my brain. (I think the MSDN is a total mess in that respect) I need a short and simple native C++ sample of code to load my Component and make it usable. I am ok with the compiler creating wrappers and the like.

Please don't advise me to use the dialog based MFC example, because it does not work with this component and is in itself a huge pile of c... code.

Can this be an issue native com vs managed com?

I am totally lost, please give me some bearings...

EDIT: Thanks for all the help. My problem is that all I have is a registered dll (actually the OCX, see below) . I (personally) know what the Interface should look like, but how do I tell my program? There are no headers that define IDs for Interfaces that I could use. But I read that the c++ compiler can extract and wrap it up for me. Anyone know how this is done?

CLARIFICATION: I have only the OCX and a clue from the documentation of the component, what methods it should expose.

+1  A: 

I applaud your efforts to go with native C++ to deal with COM - you need to go through the pain to truly appreciate today's luxurious (managed) development environment :)

Back when the world (and I) were younger, Kraig Brockshmidt's book "Inside OLE" was the tome for making sense of COM (before COM even was COM). This book predates managed code, so no chance of managed confusion here. There's a second edition, too.

Don Box's books "Essential COM" and "Effective COM" were later, but welcome additions to the store of (unmanaged) COM knowledge.

However, if your wallet doesn't extend to acquiring these dusty old books, the Microsoft COM tutorial material here could help set you on the right track.

Happy hacking.

Dan Blanchard
Well. Thanks :). I could say: "I don't read!" but that would not be a good career move, would it ;)
AndreasT
+1  A: 

The bare minimums for instantiating a COM object are as follows:

1) Must have a COM apartment available.

This is accomplished by most applications by calling CoInitialize / CoInitializeEx to setup the COM library and the initial COM apartment if it is the first time for a process.

2) Call CoCreateInstance / CoCreateInstanceEx to create an object, and specify flags to denote how it will be instantiated.

3) Properly balance calls of AddRef and Release on the interfaces of any COM components that you create, calling the last Release() when you are done using a COM component.

-

In a managed application, #1 is almost always handled for you. #2 is abstracted away if you import a reference to a COM library, and you can just use the imported names as if they were .NET class definitions and such. #3 is automatically handled for you, but your needs may vary. Unfortunately, there are sometimes quirks in how references are handled in managed applications, which can cause COM objects to stick around longer than intended. The Marshal helper class in System.Runtime has methods that can help you out there if you encounter issues.

-

In an unmanaged application, you will have to do some legwork if you are creating an application from scratch.

  1. Call CoInitialize/CoInitializeEx early in your application's main thread to setup the apartment.
  2. When your application's main thread is about to exit, call CoUninitialize() to close out the apartment.
  3. For additional threads that you create, you should also call CoInitialize/CoInitializeEx when they start if you need to use COM objects from those threads. Also, depending on your application you may want to set the apartment parameters.
  4. For those threads, also call CoUninitialize() when they are exiting to cleanup properly.
meklarian
+1  A: 

It would help if you gave a little more information about what you're exactly doing. Do you know what interfaces the object implements, etc?

In general though, the API that you can Google for more specific help is CoCreateInstance. You'll need to pass it the GUID of the object you want to play with. All COM objects implement the IUnknown interface, and you can query for any others it might have. So some sample pseudocode to get you started might look something like:

CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
CoCreateInstance( CLSID,
                       ptrIUnknown,
                       ClassCxt, // generally CLSCTX_INPROC_SERVER,
                       riid , // reference id
                       (void **)&pRequest); // the interface that corresponds to the riid

Here you can query additional interfaces using the IUnknown interface you got from the ptrIUnknown.

Then clean up with

CoUninitialize()

Don Box's Essential COM is a great book on this topic. Also, just for testing how your COM object works, using something like VBScript makes this super easy. Also, it's probably worthy of note that the GUID of the class ID is stored in a somewhat unusual way, so if you're just ripping a GUID from the registry, you might have some trouble figuring out the ordering. That's probably for a different question though.

mrduclaw
Essential COM: +1
Dan Blanchard
A: 

I use a combination of ATL smart COM pointers and the ATL::CAxWindow class for COM objects and components. I find the smart pointers particularly easy to use.

http://www.murrayc.com/learning/windows/usecomfromatl.shtml

http://76.105.92.243/notes/atlcom.html#import

http://msdn.microsoft.com/en-us/library/yx242b61%28VS.80%29.aspx

Rob
Thanks for the links, number 2 looks really promising. It will take till tomorrow to parse all this :|
AndreasT
I'm interested to know why someone voted this down! I was only trying to help.
Rob
Well and you succeded. Thanks.
AndreasT
A: 

The problem you're facing has nothing to do with managed code, if I understand correctly. You shouldn't care about the way that component was created, and COM predates .NET in about a million years. From what I can tell, the problem you're facing is using the automation interface from native C++. Automation means all functions are activated via a single method - Invoke. The method gets the ID of the function to run and its arguments, makes the call internally and returns the results in a uniform way. Having a single predefined channel allows dynamic languages to use COM objects without having to import an h or tlb file. Given you have nothing but the OCX itself, you'll have to go the hard way.

The basic steps for using a component of an undetailed type is as follows:

  • Initialize COM and use CoCreateInstance to create the object
  • Pack the function's arguments in a VARIANT union
  • Call the Invoke method of the object, passing in the function ID and the arguments
  • Convert the returned value from a VARIANT to the real type

I wouldn't say this is extremely complicated, but working this way has its share of pitfalls. Doing this in native C++ will take probably 100 times more code than, say, VB. If you do go this way, the following link might help: How To How To Call Automation Methods with Variable Argument Lists.

eran
A: 

Try using #import from Visual C++. This will create smart pointer wrappers for the interfaces.