0

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.

7
  • have you registred the COM dll with regsvr32? Commented May 22, 2014 at 14:03
  • I didn't, I just tried to add with Visual Studio -> Project (right click) -> Add Reference page and tried to add the reference with the "Browse" button. When I try to use regsrv32.exe with DLL, it says "DLL is loaded, but DllRegisterServer not found". Commented May 22, 2014 at 14:04
  • 1
    For so far I know, regsvr32 must succeed before you are able to use the DLL in .NET. I could be wrong. Commented May 22, 2014 at 14:06
  • Have you tried it with the Visual C++ ATL project template? Commented May 22, 2014 at 14:08
  • No, but are you sure I need it? ATL seems like a whole different story to me, and I've never used it before. All I need right now is to make the "library provider company" sure that writing C++ COM DLL can work for C# client code. Commented May 22, 2014 at 14:15

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Yeah, evidently COM is a whole new world. I'd love to use P/Invoke, but the problem is: the DLL is publishing some interfaces which should be filled by the client for "events notification" - which makes the P/Invoke not suitable for my needs AFAIK. Now I'm checking if the C++/CLI wrapper around a native DLL can solve my needs, but that's a lot of work too. I'm simply overwhelmed ..
is that DLL a com server itself? Because COM capabilities can be accessed via C# directly also.
I actually don't know. I thought writing some classes with deriving from IUnkown and implementing their function would make this DLL a COM server, am I mistaken here?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.