Calling a COM object from VC++

I need to be able to use a COM object in a VC++ application. I have run into 2 techniques for doing this. One involves #importing the dll and the other involves using CoCreateInstance to create the object. I haven't had much luck with either one.

With CoCreateInstance I don't seem to be able to come up with the right parameters to give me a pointer to the object.

With the #import I had a working example that was a little too simplified. When I tried to transfer the technique to the sevice I was writing I couldn't find a place to put the #import statement that would make the object available to my program.

Is there any good concise documentation on how to do this? I can find an abundance of articles on how to create a COM object in C++ but none on how to use a COM object in C++.

Any help would be appreciated.

Thanks :)
[894 byte] By [DaveF] at [2007-11-18 1:37:57]
# 1 Re: Calling a COM object from VC++
Things to ensure you are doing it right:

1. Is the COM object registered with the system ?
2. If you are using Visual C++ and have installed Visual studio tools, go to Programs -> Microsoft Visual Studio -> Tools -> OleView . Go to All Objects. You should be able to locate your CLSID in this list. When you try to click on this to open it up to see the interfaces, you should not be getting any error. This means that the COM object is registsred properly and that one is able to CoCreateInstance on this. Also, if you right click , you will see a CreateInstance.

3. If it succeeds here and fails in you app, ensure that the thread in which you are making the call to CoCreateiNstance has called CoInitialize one time at least.

Hope this helps.

- Kiran
kirants at 2007-11-10 8:54:02 >
# 2 Re: Calling a COM object from VC++
Thanks for your reply Kiran. In this case I know the COM object works and is registered. I created it using VB and tested it before trying to use it in VC++.

The problem is all of the information I can find on this subject is incomplete. For example, from Dr. GUI on using #imprt:

To create the wrapper class, add the #import statement without specifying no_namespaces or named_guids. Specify the <project_name>.tlb file generated by MIDL as the input file.

Where do I add the #import statement? Where do I specify the *.tlb file?

With CoCreateInstance there are 5 parameters:

STDAPI CoCreateInstance(
REFCLSID rclsid, //Class identifier (CLSID) of the object
LPUNKNOWN pUnkOuter, //Pointer to controlling IUnknown
DWORD dwClsContext, //Context for running executable code
REFIID riid, //Reference to the identifier of the interface
LPVOID * ppv //Address of output variable that receives
// the interface pointer requested in riid
);

How do I create the rclsid? I know the classid of the object, I can get that from regedit or OLEView as you point out but
REFCLSID rclsid = {5008C9C8-B898-4999-AEAB-2B7B72B61AF8};
doesn't work. How to I get riid? I think it is looking for an IID_IClassFactory type interface reference but I don't know how to get the one for my interface.

I realize that a lot of my problem is that I am not a regular C++ programmer, I mostly use VB but am trying to expand my knowledge. I don't know anyone who programs in C/C++ so I have no one to ask, hince I'm at this forum.

As a background, all of the folks in our group are VB programmers. We occasionally have a need to develop a service. I know there are a couple of products out there that are supposed to let you run a VB program as a service but they tend to be a little clunky and generally, not recommened by Microsoft.

I've written a couple of simple interfaces in VC++ but it is very easy to write a COM object in VB, anyone in our goup could do it. If I could just figure out how to use the object in VC++ we would have the best of both worlds.

Still Lost,
Dave
DaveF at 2007-11-10 8:55:02 >
# 3 Re: Calling a COM object from VC++
Hello,

I've been working with my own COM object (which I wrote in VC++), and have written an application to test it from VC++.

Here is the VC++ code I used to call the object. The object is nammed MyObject and the method I call is nammed MyMethodHere(). This method is just wainting for a VARIANT *
parameter (nammed datefin here).

The hexa value used for IID_IMyObject, LIBID and others must correspond to the values for your object (here, they are fictitious).

Note that it's just a test program.

void TestMyObject()
{


const IID IID_IMyObject = {0xFFEE1572,0xB080,0x4891,{0x94,0x1A,0x5E,0x53,0xFF,0x4C,0x4F,0x31}};
const IID LIBID_MyObjectLib = {0xAABBCCFF,0x0BBD,0x4B80,{0x9E,0x49,0x25,0xDD,0xAA,0xFB,0x62,0xD0}};
const CLSID CLSID_MyObject = {0xAABBCCFF,0xA9F8,0x4CA1,{0xA2,0x77,0xCE,0xAA,0xB4,0x00,0xAA,0x08}};

// Declare and HRESULT and a pointer to the Simple_ATL interface
HRESULT hr;
IMyObject *IMyObject;
CString affichage;

// Now we will intilize COM
hr = CoInitialize(0);

// Use the SUCCEEDED macro and see if we can get a pointer
// to the interface
if(SUCCEEDED(hr))
{
hr = CoCreateInstance( CLSID_MyObject, NULL, CLSCTX_INPROC_SERVER,IID_IMyObject, (void**) &IMyObject);

if(SUCCEEDED(hr))
{
COleVariant mycaptor, datefin;

datefin.SetString("13/01/03 09:00:00",VT_BSTR );

hr = IMyObject->MyMethodHere(datefin,&mycaptor);
hr = INouveauGraphique->Release();
}

}

// Uninitialize COM
CoUninitialize();

}

Remeber also to add :

#include "..\MyObject\MyObject.h"

in your calling program.

Hope this helps !
BMW at 2007-11-10 8:55:59 >
# 4 Re: Calling a COM object from VC++
Note that in my previous post, there is one line I forgot to correct :

hr = INouveauGraphique->Release();

must be read :

hr = IMyObject->Release();

That's all folks !
BMW at 2007-11-10 8:57:03 >
# 5 Re: Calling a COM object from VC++
Well, I realized that you said your object was developped in VB.
So you surely do not have a MyObject.h file to include in your VC++ app.

Here's what you need to include in your .h file to be able to use your COM object :

MIDL_INTERFACE("DA231234-AB78-4291-121A-1123C6411132")
IMyObject : public IDispatch
{
public:
virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE MyMethodHere(
VARIANT __RPC_FAR *date_fin,
VARIANT __RPC_FAR *lasterror) = 0;
};

The numeric ID in the first line is just fictitious : use your own.

Now you should be able to compile and run this test program.

Let me know if it helps.
BMW at 2007-11-10 8:58:02 >
# 6 Re: Calling a COM object from VC++
Originally posted by DaveF - Still Lost #import is indeed what you're looking for though MFC also provides support for it (not generally recommended though for several reasons). #import is very easy to use once you know it and it's fully documented in MSDN but I agree that it takes some effort to fully understand things. The real problem is COM itself. If you're not experienced with it then the learning curve is more a function of how much COM you actually know, rather than the mechanics of #import itself (and knowing C++ is also an issue course). Even if you use COM all the time in VB (typically), you don't normally get your hands dirty to the extent that you do (or can) in C++. This is because VB is doing much of the COM grunge work for you. So you may not be aware of some low-level details that will help you understand #import. In any any case, #import does make things just about as easy as VB but you still need to understand some COM details first. Unfortunately, #import isn't very well explained IMO and it takes some hunting around to really piece all the info together (start with the online documentation in MSDN - search for "#import directive" though it may be a little heavy to start and also see the article by Philip Lucido entitled "Microsoft Visual C++ 5.0 Compiler Native COM Support"). Below I'm posting my response to a NG question on the topic. I've snipped the relevant portions and you can take things from there. I can also provide some links if you want but they typically show #import in use with different COM servers rather than going into extensive detail about what it's doing. Also search this site as you'll find references to it from myself and many others no doubt. Here's the NG stuff:

To set up a client merely requires you #import the type library as you've now read about. That means using the #import statement to import the ".dll" or ".exe" which houses your COM server (or less frequently the ".tlb", ".ocx" or any other file that contains the type library for your COM server). To make a long story short, simply #import your COM server as in the following trivial example:

#import <employee.exe> no_namespace // or ".dll, etc.

int main(int argc, char **argv)
{
HRESULT hr = CoInitialize(NULL);

if (SUCCEEDED(hr))
{
try
{
EmployeePtr spEmployee(__uuidof(Employee));
int EmployeeAge = spEmployee->Age;
spEmployee->LastName = "Smith";
spEmployee->DoSomethingElse();
// etc.
}
catch (const _com_error &ComError)
{
// Error info in "ComError"
}

CoUninitialize();
}

return 0;
}The #import statement reverse engineers the file you pass and creates C++ wrapper classes for all methods exposed by the #imported server. The classes are loaded into a header file with the same base name but with an extension of ".tlh" (Type Library Header). The implementation (.cpp) file also winds up in a file with a "tli" extension which is merely #included at the end of the latter header. In the above example, I #import some COM server (an "exe" in this case), and then create an "Employee" object using the smart pointer wrapper classes found in the ".tlh" file. I can then call any function noting that object properties can be accessed like ordinary *data* members instead of functions, as seen in the "Age" and "LastName"examples above (see __declspec(property) in MSDN for details). Any errors result in a "ComError" exception. You'll have to familiarize yourself with #import of course to get the hang of all this. Knowing COM also helps. It's all very easy though once you know what you're doing (simple for me to say though).
Sef at 2007-11-10 8:59:11 >
# 7 Re: Calling a COM object from VC++
To BMW,

I was able to get the CoCreateInstance method to work. I'm currently having some access problems which I'll have to troubleshoot. The testDll I wrote writes to a file and the service may not have the priviledges... I can deal with that.

To Sef,

I'm having trouble finding the article you mentioned on MSDN but will keep looking. I tried the over-simplfied example you gave and get a compile error saying "... cannot instantiate abstract class ... " because the none of the viritual functions are defined. This includes those of IUnknown (QueryInterface, etc) and of IDispatch(GetTypeInfo, etc) and also the function I defined in the dll. Perhaps your example wasn't meant to be used, just an example of how the code might typically look. I have looked through the help on #import directive and got, essentially, the same results. (the compiler couldn't instantiate the abstract class).

Thanks for the help, I'll keep digging.

Dave
DaveF at 2007-11-10 9:00:02 >
# 8 Re: Calling a COM object from VC++
I'm having trouble finding the article you mentioned on MSDN but will keep looking.I have an old copy of MSDN and you're right, I can't find it on MS's site either (the one by Lucido). I didn't exhaustively look but I should have found it easily enough just by searching on his name. Maybe they removed it but that's unusual (perhaps it's part of their .Net strategy - making the status quo more difficult so we'll all migrate to .Net).

I tried the over-simplfied example you gave and get a compile error saying "... cannot instantiate abstract class ... "You're trying to instantiate the abstract class for one of your interfaces. Do you see the "Ptr" suffix in "EmployeePtr" of my last post? That's added automatically by #import. You must include that for whatever your own class is called. That is, if you look in your ".tlh" file you'll see all the interfaces exposed by your #imported server. These are at the top under the sections "Forward references and typedefs" and "Smart pointer typedef declarations". Under the latter section you'll see a bunch _COM_SMARTPTR_TYPEDEF statements. These are creating the actual smart pointers which are named by appending the "Ptr" suffix to the first arg. That arg is the name of the COM interface being wrapped by the smart pointer which is "Employee" in my example. So it becomes "EmployeePtr" once the suffix is added. Also note the "__uuidof()" clause seen in my previous post. That applies to the "coclass" for your object which you'll find under the "Forward references and typedefs" section mentioned above (the "Employee" coclass in my example - look for the applicable "coclass" in your own ".tlh"). In my example though it's the same name as the interface itself ("Employee") which is a little confusing perhaps (bad example maybe) but that won't likely apply in your case. In any event, you don't append a "Ptr" to the coclass name, just the interface name. So the entire statement:

EmployeePtr spEmployee(__uuidof(Employee));actually calls "CoCreateInstance()" for you, creating the "Employee" object seen in the "__uuidof" clause (we're passing its UUID for the appropriate coclass), and queries for the "Employee" interface which is stored in the "EmployeePtr" wrapper class. On return you can then call its properties and methods which are defined under the "Employee" structure seen further down in the ".tlh" file (i.e., "EmployeePtr" without the "Ptr" suffx). The details surrounding this are spelled out in the #import directive" of MSDN which isn't the greatest explanation IMO but see this anyway:

http://msdn.microsoft.com/library/en-us/vclang/html/_predir_The_.23.import_Directive.asp?frame=true).

Other *real* working examples of #import can be found all over including here at dev-archive (that includes some of my own examples as I recall). Also see the following other web links which may help for starters (not completely devoted to #import but with some references):

http://msdn.microsoft.com/library/en-us/ado270/htm/mdhowhowvcusersshouldreadadodocumentation.asp?frame=true
http://msdn.microsoft.com/library/en-us/vcsample98/html/_core_com_samples_index.asp?frame=true
Sef at 2007-11-10 9:01:05 >
# 9 Re: Calling a COM object from VC++
I'm getting closer on getting #import to work. The problem with instantiating the abstract classes was with Namespace. Your original example didn't use it I tried it w/ a blank and a no_namespace attribute. Eventually I got around to using 'using namespace <dll FileName>;'

Now the program will compile but won't run. It fails on the "_clsUtilPtr spTestDll(__uuidof(_clsUtil));" statement indicating that the Class is not registered. However the class is registered as evidenced by the OleViewer and the fact that I can get the CoCreateInstance example that I'm working on to work.

Here is the code for the oversimplified example as it stands now:

#import "c:\dev\ServiceTmpl\TestDll.dll"
using namespace TestDll;

int main(void) {
HRESULT hr = CoInitialize(NULL);

if(SUCCEEDED(hr)) {
try {
_clsUtilPtr spTestDll(__uuidof(_clsUtil));

spTestDll->SendMsg();

}
catch(const _com_error &ComError) {

MessageBox(NULL, ComError.ErrorMessage(),
"COM Error", MB_OK);

}

CoUninitialize();
} //end of if

return 0;
}

===== Here is the .tlh file: ==========

#pragma once
#pragma pack(push, 8)

#include <comdef.h>

namespace TestDll {

//
// Forward references and typedefs
//

struct __declspec(uuid("ddbd9655-283b-4e45-b089-a22c297f28ed"))
/* dual interface */ _clsUtil;
struct /* coclass */ clsUtil;

//
// Smart pointer typedef declarations
//

_COM_SMARTPTR_TYPEDEF(_clsUtil, __uuidof(_clsUtil));

//
// Type library items
//

struct __declspec(uuid("ddbd9655-283b-4e45-b089-a22c297f28ed"))
_clsUtil : IDispatch
{
//
// Wrapper methods for error-handling
//

long LogMsg (
BSTR * szMessage );
long SendMsg ( );

//
// Raw methods provided by interface
//

virtual HRESULT __stdcall raw_LogMsg (
BSTR * szMessage,
long * _arg2 ) = 0;
virtual HRESULT __stdcall raw_SendMsg (
long * _arg1 ) = 0;
};

struct __declspec(uuid("5008c9c8-b898-4999-aeab-2b7b72b61af8"))
clsUtil;
// [ default ] interface _clsUtil

//
// Wrapper method implementations
//

#include "c:\dev\servicetmpl\importtest\debug\TestDll.tli"

} // namespace TestDll

#pragma pack(pop)

===========================================

The ddbd... number is the correct uuid for the interface the 5008c9c8... is the correct clsid.

I'm stumped!
Dave
DaveF at 2007-11-10 9:02:14 >
# 10 Re: Calling a COM object from VC++
Kill the underscore in the "__uuidof". Should read:

_clsUtilPtr spTestDll(__uuidof(clsUtil));
Sef at 2007-11-10 9:03:13 >
# 11 Re: Calling a COM object from VC++
Voila! That did it.

The CoCreateInstance example still has a problem with EPS calling conventions but the #import is much simpler and seems to work well.

Thanks to all for your efforts!

Dave
DaveF at 2007-11-10 9:04:11 >