简体   繁体   中英

Including C-DLL from C++

This feels like a noob question, so if it's a dupe, please point me to the right location :)

I tried including a DLL written in C into a C++ program. It didn't work; gcc said

test.cpp: xxx: error: too many arguments to function.

Here's a minimal working example:

Wrapper for DLL functions:

/* myWrapper.h */

#ifndef _MYWRAPPER_H
#define _MYWRAPPER_H
#include <windows.h>

#ifdef __cplusplus
extern "C" {
#endif

extern  FARPROC   EXPORTED_functionNameP;

int GetDLLpointers();

#ifdef __cplusplus
}
#endif

#endif

Implementation thereof:

/* myWrapper.c */
#include <windows.h>
#include "myHeader.h"

#ifdef __cplusplus
extern "C" {
#endif

HINSTANCE drvsHANDLE;

extern  FARPROC   EXPORTED_functionNameP;

int GetDLLpointers()
{
    static int result;

    drvsHANDLE = LoadLibrary("myLibrary.dll");
    if (drvsHANDLE == NULL) return (result=0);

    EXPORTED_functionNameP = GetProcAddress(
        drvsHANDLE, "originalFunctionName");    
    if (EXPORTED_functionNameP == NULL) return (result = 0);

    return (result = 1);
}

#ifdef __cplusplus
}
#endif

Naturally, I haven't written these nor the library myself, and preferably, they should all stay untouched. I did however add the extern "C" lines.

Then, my main file:

// my Main
#include <windows.h>
#include "myHeader.h"

int main(int argc, char **argv)
{
    int arg = 1;
    EXPORTED_functionNameP(arg);

    return 0;
}

Build commands:

gcc -I. -c -o myHeader.o myHeader.c -L. -lmyLibrary
g++ -I. -o main.exe myMain.cpp myHeader.o -L. -lmyLibrary

It works fine if I rewrite my main.cpp into valid C and compile with gcc instead of g++ .

I tried changing extern "C" into extern "C++" to no avail, I tried all permutations or gcc and g++ for the two build commands, nothing.

I know it's something to do with name mangling, but I thought gcc would take care of that when you include the extern "C" lines...Can someone please explain what I'm missing here?

In case it matters --

Windows XP Pro (will be Win7 later on)

(MinGW) gcc 4.6.2

The FARPROC type is a function pointer for a function that takes no parameters. You should declare EXPORTED_functionNameP like so (replacing void with whatever the function really returns):

extern void (*EXPORTED_functionNameP)(int);

And initialize it like so (the returned value from GetProcAddress() pretty much always needs to be cast to the correct type):

EXPORTED_functionNameP = (void (*)(int)) GetProcAddress(drvsHANDLE, "originalFunctionName");    

A typedef for the funciton type might make things a bit more readable.

After a quick Google search, it seems that FARPROC is defined as this:

typedef int (FAR WINAPI *FARPROC)();

That is, FARPROC is a function that returns an int and takes no arguments. So you can't use it for any other case.

Instead declare EXPORTED_functionNameP like this:

extern void (*EXPORTED_functionNameP)(int);

Now EXPORTED_functionNameP is a pointer to a function that takes an int argument and returns no value.

There is a difference between C and C++.

int (FAR WINAPI * FARPROC) () 

In C, the FARPROC declaration indicates a callback function that has an unspecified parameter list. In C++, however, the empty parameter list in the declaration indicates that a function has no parameters.

The MSDN page on CallWindowProc explains a bit more.

I know this is a very old question, but I am having exactly the same issues but in relation to writing a generic wrapper template for wrapping calls to LoadLibrary() and GetProcAddress()

Taking https://blog.benoitblanchon.fr/getprocaddress-like-a-boss/ as inspiration, it looks like he is taking FARPROC as a kind of "void* for Windows functions" and then casting it to the correct type subsequently.

I needed to tweak that code a little to work for me, and reproduce it here:

class ProcPtr
{
public:
    explicit ProcPtr(FARPROC ptr) : m_ptr(ptr) {}

    template <typename T>
    operator T* () const { return reinterpret_cast<T*>(m_ptr); }

private:
    FARPROC m_ptr;
};

class DllHelper
{
public:
    explicit DllHelper(LPCTSTR filename) : m_module(LoadLibrary(filename)) {}
    ~DllHelper() { FreeLibrary(m_module); }

    ProcPtr operator[](LPCSTR proc_name) const
    {
        return ProcPtr(::GetProcAddress(m_module, proc_name));
    }

private:
    HMODULE m_module;
};

So, with that helper code now available we can use it to write a wrapper class that encapsulates several functions in the Advapi32 library:

class Advapi32
{
public:
    Advapi32() : m_dll(TEXT("Advapi32"))
    {
        getUserName    = m_dll["GetUserNameA"];
        openSCManager  = m_dll["OpenSCManagerA"];
        bogusFunction  = m_dll["BogusFunctionThatDoesNotExist"];
    }

    decltype(GetUserNameA)*         getUserName;
    decltype(OpenSCManagerA)*       openSCManager;
    decltype(GetWindowsDirectoryA)* bogusFunction;

private:
    DllHelper m_dll;
};

bogusFunction is a function with the same signature as GetWindowsDirectoryA but which doesn't exist in Advapi32. This is what I was trying to achieve - graceful fallback on an older OS which might not have a certain function.

So, finally a test app...

int main()
{
    Advapi32 advapi32;

    auto func1 = advapi32.getUserName;
    if (func1)
    {
        TCHAR  infoBuf[256];
        DWORD  bufCharCount = sizeof(infoBuf);

        if (func1(infoBuf, &bufCharCount))
        {
            std::cout << "Username: " << infoBuf << std::endl;
        }
    }

    auto func2 = advapi32.openSCManager;
    if (func2)
    {
        SC_HANDLE handle = func2(NULL, NULL, SC_MANAGER_CONNECT);
        if (handle)
        {
            std::cout << "opened SC Manager" << std::endl;
        }
    }

    auto func3 = advapi32.bogusFunction;
    if (func3)
    {
        std::cerr << "This should not happen!" << std::endl;
    }
    else
    {
        std::cout << "Function not supported" << std::endl;
    }
}

Output:

Username: TestAccount 
opened SC Manager 
Function not supported

Note: This was compiled as a Windows 32-bit console application with MBCS rather than Unicode, under VS2019 with the VS2015_XP toolset, since that is what I am needing to target (don't ask).

FARPROC is defined as

typedef int (FAR WINAPI *FARPROC)();

When you pass an additional argument although the argument list of the prototype is empty you get the error.

You need a proper prototype definition for PORTED_functionNameP and cas the result from GetProcAddress to that type in your GetDLLPopinters functions.

It is because of FARPROC is defined as:

int (FAR WINAPI * FARPROC) ()

So you can not pass any parameters to such function in C++. For fix it you should define EXPORTED_functionNameP as pointer to function with equal semantics as defined in DLL-library. For example:

typedef (void* EXPORTED_functionNameP)(int value);
EXPORTED_functionNameP ExportedFns;
...
ExportedFns = GetProcAddress(drvsHANDLE, "originalFunctionName");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM