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 ;
|