Visual C++ command line compile COM library

I am trying to create COM library using this tutorial http://www.codeguru.com/cpp/com-tech/activex/tutorials/article.php/c5567/Step-by-Step-COM-Tutorial.htm How to invoke MIDL (step 2) from command line? How to generate interface (step 1) from c++ file? Is there some way to do it with MinGW?
Last edited on
Is there some way to do it with MinGW?

Nope.

But you can obtain the MIDL tool by installing the Windows SDK and then use it along with the MinGW compiler and linker.

Or, if you want to avoid having to download the SDK, you could use a hand-written header for the interface, rather than the one generated by the MIDL compiler.

It would mean that you have to tweak the code a bit, but going by my encounter with that particular article (see PS) you will probably have adjustments to make anyway.

Using a hand-written header wouldn't impact on the functionality of the demo program; generating a typelib is only actually necessary when you want to use Automation (which the demo is not doing) or to support marshalling via the standard marshaller (which the demo isn't doing either, as it loads the COM object in-process.)

It does mean the client app cannot use COM smart pointers, but as the client is rather badly written this doesn't really matter.

If you do go the hand-written route...

To start with, note that the code on the web page is missing quite a bit of code (if you try to build it, the linker will complain about a number of unresolved functions.) The source in the zip file is complete, but it a bit different to that on the web page. So, based on the code in the zip file, to get it to build without the MIDL tool.

1. remove IAdd.idl and create a new header: IAdd.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifndef Included_IAdd_H
#define Included_IAdd_H

// IAdd.h

#include "objbase.h"

// {1221db62-f3d8-11d4-825d-001043646c0}
DEFINE_GUID(IID_IAdd, 0x1221db62, 0xf3d8, 0x11d4, 0x82, 0x5d, 0x00, 0x10, 0x4b, 0x36, 0x46, 0xc0);

struct IAdd : public IUnknown
{
    virtual HRESULT __stdcall SetFirstNumber(long nX1)  = 0;
    virtual HRESULT __stdcall SetSecondNumber(long nX2) = 0;
    virtual HRESULT __stdcall DoTheAddition(long *pBuffer) = 0;
};

//
//The interface IFileIO when implemented by a COM object will allow clients
//to control the logging of activity to a disk file.
//

// {6bee2d26-f3d8-11d4-825d-00104b3646c0}
DEFINE_GUID(IID_IFileIO, 0x6bee2d26, 0xf3d8, 0x11d4, 0x82, 0x5d, 0x00, 0x10, 0x4b, 0x36, 0x46, 0xc0);

struct IFileIO : public IUnknown
{
    //
    //to enable logging , pass nEnable=1 
    //to stop logging pass nEnable=0
    //
    virtual HRESULT __stdcall EnableLog(long nEnable)  = 0;
    //
    //if logging has been enabled then, pEnabled passes back 1
    //otherwise 0 
    //
    virtual HRESULT __stdcall IsEnabled(long *pEnabled) = 0;
};

#endif // Included_IAdd_H 


2. In AddObj.cpp comment out or remove the line

#include "IAdd_i.c"

and add this before AddObj.h is included

#include <initguid.h>

so you end up with something like

1
2
3
4
5
#include    <objbase.h>
#include    <initguid.h>

#include    "AddObj.h"
//#include    "IAdd_i.c" 


The COM DLL should now build without MIDL.

The client, without COM smart pointers, ends up as (warning: the original had no error handling, and neither does this version!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//
///Client.cpp
//
//Demo of client using the COM object from AddObj.dll 
//

#include    <objbase.h> 
#include    <stdio.h> 
#include    <conio.h>
#include    <initguid.h>
#include    "IAdd.h" // rather than #import "AddObj.dll"

int main()
    {
    long n1 =100, n2=200;        
    long nOutPut = 0;

    CoInitialize(NULL);

    CLSID clsid;
    CLSIDFromProgID(L"CodeGuru.FastAddition", &clsid);

    IAdd* pFastAddAlgorithm;
    CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IAdd, (LPVOID*)&pFastAddAlgorithm);

    printf("\nEnter the first number:");
    scanf("%d",&n1);

    printf("\nEnter the second number:");
    scanf("%d",&n2);

    pFastAddAlgorithm->SetFirstNumber(n1);
    pFastAddAlgorithm->SetSecondNumber(n2);
    pFastAddAlgorithm->DoTheAddition(&nOutPut);

    printf("\nOutput after adding %d & %d is %d\n",n1,n2,nOutPut);
    printf("\nPress a key to exit the application....");
    getch();

    pFastAddAlgorithm->Release();
    CoUninitialize();
 
    return 0;
    }


Andy

PS The article you are working from does not particularly impress me. The code is poorly written, and there are a number of flaws. If you want to learn the basic principles of COM you should really check out Don Box's book, "Essential COM".

Step-by-Step-COM-Tutorial.htm only came to my attention last month ago, due to this thread:

error C1083: Cannot open type library file: 'vcclient\addobj.dll': Error loading type library/DLL.
http://www.cplusplus.com/forum/windows/104385/

The fact that the Visual Studio project is from Visual Studio 5.0 (from 1997) should warn you that it is likely to be primitive to start with. But the code does not even follow the conventions that were prevailing at that time.

I have looked around for a web site with a better example, but most sites which discuss how to write COM objects use ATL, which I assume is not an option here.

Two issues that stand out are:

1. COM objects should never allow exceptions to propagate to their client

Up to Visual C++ 6.0 new returned null if memory allocation failed, but this is no longer the case by default. Instead, an exception is thrown.

But one of the rules of COM is that exceptions should never be allowed to propagate out of the COM object. So all calls to new must either (a) be protected by a try-catch blocks or (b) use std::nothrow so that a null return value is used to signal memory allocation failure.

2. CAddFactory::CreateInstance does not handle failure

The implementation of CAddFactory::CreateInstance does not handle the case when an unsupported interface is requested when instantiating an object.

After creating a new COM object, QueryInterface is called and then the function immediately returns.

When QueryInterface fails it returns a null pointer to the client app, so the app cannot release the object. And as the CreateInstance method is not deleting the unwanted object, it is left to leak.

The correction is to replace the final call in the method (QueryInterface) with the sequence AddRef, QueryInterface, Release.

1
2
3
4
    //
    // Get the requested interface.
    //
    return pObject->QueryInterface(iid, ppv) ;


with

1
2
3
4
5
6
7
8
9
10
    //
    // Get the requested interface.
    //
    pObject->AddRef() ;
    
    HRESULT hr = pObject->QueryInterface(iid, ppv) ;

    pObject->Release() ;

    return hr ;

Last edited on
Do you know of some better tutorial, which doesn't use visual studio project or any other project files, makefiles, etc.?
And how to invoke MIDL from cmd to get needed files?
Do you know of some better tutorial, which doesn't use visual studio project or any other project files, makefiles, etc.?

Sorry, no.

And how to invoke MIDL from cmd to get needed files?

The command line the Visual studio project uses is:

midl /nologo /char signed /env win32 /Oicf /tlb IAdd.tlb /h IAdd_h.h IAdd.idl


Andy
Last edited on
Thanks for help. :) And last thing how to compile c++ class to idl file?
Last edited on
how to compile c++ class to idl file?

Sorry, but that question makes no sense.

Andy


Why?
I have c++ class implementation, which is in subset compatible with midl-language and I want to export it.
What do you mean by export here?

Andy

Generate IDL file from class file which defines which classes should be in idl file
Sorry, I don't think I can help further.

But note that there are no classes in an IDL file. Just interfaces and coclasses (which are nothing to do with C++ classes.)

Andy
Last edited on
That article is explaining to a C++ programmer how COM works in C++ (or is implemented using C++) by using a set of examples that start with C++ (vtable-only classes) and end up with fully fledged COM objects. That is, the examples evolve from C++ to COM.

The definition of the interfaces the article show are more or less the same way as I suggested in my earlier post about how to avoid IDL. The difference being that I used struct rather than class plus public. (I picked up the habit from the Windows SDK samples.)

Andy
Topic archived. No new replies allowed.