C++ – Why can’t I use __declspec(dllexport) to export DllGetClassObject() from a COM DLL

ccomname-decoration

I am developing a COM dll and trying to export the DllGetClassObject() method with the __declspec(dllexport).

Here is my declaration:

extern "C" HRESULT __declspec(dllexport) __stdcall DllGetClassObject(REFCLSID rclsid, 
                                                             REFIID riid, void** ppv)

But I kept get this error:

error C2375: 'DllGetClassObject' : redefinition; different linkage

So I try to check all the occurrence of the DllGetClassObject definitions. Thus found the following one in the ObjBase.h.

STDAPI  DllGetClassObject(__in REFCLSID rclsid, __in REFIID riid, __deref_out LPVOID FAR* ppv);

the STDAPI turns out to be like this:

#define STDAPI                  EXTERN_C HRESULT STDAPICALLTYPE

in other words, it's like this:

#define STDAPI                  extern "C" HRESULT __stdcall

According to MSDN:

To export functions, the
__declspec(dllexport) keyword must appear to the left of the
calling-convention keyword, if a
keyword is specified.

But my declaration mentioned before just didn't work.

So does COM DLL have to export their methods with a def file?


Update 1

I tested my declaration with a different method name, shown as below:

extern "C" HRESULT __declspec(dllexport) __stdcall f()
{
    return S_OK;
}

And this method was exported successfully. So these specifiers could be used together. It seems the Visual C++ compiler takes STDAPI and extern "C" HRESULT __declspec(dllexport) __stdcall as not compatible.

Best Answer

This problem occurs I think because a __stdcall function (for 32-bit builds) is normally decorated with a underscore prefix and an @count postfix. But if the function is also marked as __declspec(dllexport) additional decorations are added (__imp, I think).

You might be able to avoid using a .def file with the following pragma, if you're willing to live with the pragma (I think I'd go for the .def file):

#pragma comment( linker, "/export:DllGetClassObject=_DllGetClassObject@12" )

Note that for an x64 build, you might have to conditionally compile the pragma, which I think would be:

#pragma comment( linker, "/export:DllGetClassObject" )