Search code examples
c#c++.netdllcom

C++ COM DLL for use in C#


In a project I'm working for my company, there have been an inter-lingual DLL communication problem. The library provider makes the DLL in C++ and my company is going to use this DLL in C#.

I've read on the Internet that if the C++ DLL is created in COM (Component Object Model), Visual Studio can add this DLL as a reference in C# project and use it in the code.

Due to this, I've made very simple DLL and tried to follow the MSDN article "From CPP to COM" to make this DLL COM compatible, so I can use it with C# project. However, even though I can compile the DLL and get no error on build, I cannot manage to get C# project reference it, and Visual Studio gives error of "no valid COM component" when I want to add this DLL as reference.

I've made two files for this. One "Header" file:

#include "stdafx.h"
// 467B2143-85CC-404E-9050-4A754EF1DF01
static const GUID IID_Test = { 0x467B2143, 0x85CC, 0x404E , { 0x90, 0x50, 0x4A,0x75,0x4E,0xF1,0xDF,0x01} };
// 5D4EEEE9-9BE4-4DE9-B36D-86B96400F0F1
static const GUID CLSID_Test = { 0x5D4EEEE9, 0x9BE4, 0x4DE9, {0xB3, 0x6D, 0x86, 0xB9, 0x64, 0x00, 0xF0, 0xF1} };

#define DEF_EXPORT _declspec(dllexport) 

interface ITest: public IUnknown
{
    public: 

    virtual HRESULT __stdcall MultiplyNumbers( double n1, double n2, double& result) = 0;

};

And a file which implements the interface (header-impl.cpp)

#include "stdafx.h" 
#include "Header.h"

ULONG g_dwRefCount = 0;

class Test : public ITest
{
protected:
    ULONG m_dwRefCount;

public:
    Test(): m_dwRefCount(0) {   }

    virtual HRESULT __stdcall MultiplyNumbers( double n1, double n2, double& result)
    {
        result = n1 * n2;
        return NO_ERROR;
    }

    virtual HRESULT __stdcall QueryInterface(  REFIID riid, void  **ppvObject)
    {
        if (riid==IID_IUnknown || riid==IID_Test) {
            *ppvObject= this;
        } else {
            return E_NOINTERFACE;
        }
        AddRef();
        return NO_ERROR;
    }

    virtual ULONG __stdcall AddRef( void)
    {
        g_dwRefCount++; 
        m_dwRefCount++; 
        return m_dwRefCount;
    }

    virtual ULONG __stdcall Release( void)
    {
        g_dwRefCount--; 
        m_dwRefCount--; 
        if (m_dwRefCount==0) {
            delete this; 
            return 0; 
        } 
        return m_dwRefCount;
    }

};

class TestFactory : public IClassFactory {
protected:
    ULONG m_dwRefCount;

public:

    TestFactory(): m_dwRefCount(0){ }

    HRESULT __stdcall CreateInstance(IUnknown *pUnkOuter, REFIID riid, void** ppObject)  
    {
        if (pUnkOuter!=NULL) {
            return CLASS_E_NOAGGREGATION;
        }

        Test *pobj = new Test();

        if (FAILED(pobj->QueryInterface(riid, ppObject))) {
            delete pobj;
            *ppObject=NULL;
            return E_NOINTERFACE;
        }
        return NO_ERROR;
    }

    HRESULT   __stdcall LockServer(BOOL fLock)
    {
        if (fLock) {
            g_dwRefCount++;
        } else {
            g_dwRefCount--;
        }

        return NO_ERROR;
    }

    virtual HRESULT __stdcall QueryInterface(  REFIID riid, void  **ppvObject)
    {
        if (riid==IID_IUnknown || riid==IID_IClassFactory) {
            *ppvObject=  this;
        } else {
            return E_NOINTERFACE;
        }
        AddRef();
        return NO_ERROR;
    }

    virtual ULONG __stdcall AddRef( void)
    {
        g_dwRefCount++;
        m_dwRefCount++;
        return m_dwRefCount;
    }


    virtual ULONG  __stdcall Release()
    {
        g_dwRefCount--;
        m_dwRefCount--;
        if (m_dwRefCount==0) {
            delete this;
            return 0;
        }
        return m_dwRefCount;


    }
};

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID * ppObject)
{
    if (rclsid!= CLSID_Test) { // Is this the number of our class?
        return CLASS_E_CLASSNOTAVAILABLE;
    }

    TestFactory *pFactory= new TestFactory;

    if (FAILED(pFactory->QueryInterface(riid, ppObject))) {
        delete pFactory;
        *ppObject=NULL;
        return E_INVALIDARG;
    }
    return NO_ERROR;

}

Now, I know I ask for a little bit too much, but can anyone point out the problem here? I also have a "Source.def" file with a little content in it:

LIBRARY DLLTEST
EXPORTS
    DllGetClassObject

which makes the compiler saying that "this function declaration must be PRIVATE".

Am I on the wrong path here? Or is it just me, making a mistake in writing the DLL.

Thank you.

PS: I didn't make a regsvr32.exe call or such. I've read that the Windows XP and later OS'es don't require that strictly anymore and I thought that trying to add as a reference in Visual Studio can manage that for me.


Solution

  • Somebody on SO once compared writing 'old style' COM to assembly programming. I think that it is fair to say so. People are running away from COM, not the other way around. For a COM component to be properly added to VS, a DLL containing a COM server (assuming you want an inproc server) must be properly registered against the registry. That is done using regsvr32.exe program. But regsvr32. exe requires an entry point - that is DllRegisterServer() - http://msdn.microsoft.com/en-us/library/windows/desktop/ms682162(v=vs.85).aspx . (also DllUnregisterServer()) I would recommend using Active Template Library for creating COM server because it enables some abstractions that are helpful (IDL and a lot of macros).

    As for a 'normal' C++ DLL - you can use P/Invoke capabilities that are available to you in C# and access the DLL content without using any COM.