Calling Win32 DLL from C++

2019-04-14 08:37发布

问题:

I am new to the DLL world. I have been given a Win32 DLL which has a lot of functions. Need to call these DLL functions from C++

I want to call CreateNewScanner which creates a new scanner object and get the results in C++. Function mentioned in the DLL is:

BOOL CreateNewScanner(NewScanner *newScan);

and NewScanner is a struct, as below,

// Structure NewScanner is defined in "common.h" .
typedef struct{
  BYTE host_no; // <- host_no =0
  LONG time; // <- command timeout (in seconds)
  BYTE status; // -> Host adapter status
  HANDLE obj; // -> Object handle for the scanner
}NewScanner;

How will I call this function? Started with C++ and here is what I managed,

#include <iostream>
#include <windows.h>
using namespace std;
int main(){
  HINSTANCE hInstance;    
  if(!(hInstance=LoadLibrary("WinScanner.dll"))){
      cout << "could not load library" << endl;        
  }
  /* get pointer to the function in the dll*/
  FARPROC handle = GetProcAddress(HMODULE(hInstance), "CreateNewScanner");
  if(!handle){
    // Handle the error
    FreeLibrary(hInstance);
    return "-1";
  }else{    
    // Call the function
    //How to call here??
  }
}

回答1:

First of all, return "-1" is no good. You are expected to return an integer. So you surely mean return -1.

Now to the question. Instead of declaring the function pointer as FARPROC, it's easier to declare it as a function pointer type.

typedef BOOL (*CreateNewScannerProc)(NewScanner*);

Then call GetProcAddress like this:

HMODULE hlib = LoadLibrary(...);
// LoadLibrary returns HMODULE and not HINSTANCE
// check hlib for NULL

CreateNewScannerProc CreateNewScanner = 
    (CreateNewScannerProc) GetProcAddress(hlib, "CreateNewScanner");
if (CreateNewScanner == NULL)
    // handle error

// now we can call the function
NewScanner newScan;
BOOL retval = CreateNewScanner(&newScan);

Having said all of that, usually a library will come with a header file (yours clearly does so you should include it) and a .lib file for load-time linking. Make sure that you pass the .lib file to your linker and you can simply do this:

#include "NameOfTheHeaderFileGoesHere.h"
....
NewScanner newScan;
BOOL retval = CreateNewScanner(&newScan);

No need to mess around with LoadLibrary, GetProcAddress and so on.



回答2:

If you want to follow the LoadLibrary/GetProcAddress/FreeLibrary approach, consider the following "code path" (note that if you have the DLL public header file and the corresponding .lib file, just #include the public DLL header, and link with the .lib file, and just use the function whose prototype is defined in the DLL header as you would do with an ordinary C function called from C++ code).

Define a typedef for a pointer to the function exported from the DLL.
Note that the calling convention is specified (usually, Win32 DLLs with pure-C interfaces use __stdcall calling convention):

//
// Prototype of the DLL function, with *calling convention* specified
// (usually it's __stdcall for DLL with pure-C interface).
//
typedef BOOL (__stdcall *CreateNewScannerPtr)(NewScanner *);

Then you try loading the DLL using LoadLibrary:

//
// Try loading the DLL.
//
HMODULE hDll = LoadLibrary(L"WinScanner.dll"); // <--- Note the use of L"..." for Unicode
if (! hDll)
{
    .... error
}

Note that the file name of the DLL is a Unicode string (note the L"..." decoration). In general, you should use Unicode in modern C++/Win32 code.

Then you can try getting the function pointer using GetProcAddress:

//
// Try getting the pointer to CreateNewScanner DLL function.
//
auto pCreateNewScanner = reinterpret_cast<CreateNewScannerPtr>
(
  GetProcAddress
  (
    hDll,               // DLL handle
    "CreateNewScanner"  // Function name
  ) 
);

if (! pCreateNewScanner)
{
    .... error

    // Release the DLL
    FreeLibrary(hDll);

    // Avoid dangling references
    hDll = nullptr;
}

Note that since you are using C++, it's better using C++-style casts (like reinterpret_cast<> in this case), instead of old C-style casts.
Moreover, since the type of the function pointer is specified in reinterpret_cast, it's useless to repeat it at the beginning of the statement, so the new C++11's keyword auto can be used.

You can use the returned function pointer to call the DLL function:

BOOL retCode = pCreateNewScanner( .... );

// Note: some other common prefix used in this case is "pfn"
// as "pointer to function" (e.g. pfnCreateNewScanner).

Once you have finished using the DLL, you can release it, calling FreeLibrary:

//
// Release the DLL
//
FreeLibrary(hDll);
hDll = nullptr;

In addition, note that you can use the C++ RAII pattern, and define a class with a destructor that automatically frees the DLL (this simplifies the code that manages the library loading/releasing parts).
e.g.

class RaiiDll
{
public:
    // Load the DLL.
    explicit RaiiDll(const std::wstring& filename)  // may also provide an overload 
                                                    // with (const wchar_t*)
    {
        m_hDll = ::LoadLibrary(filename.c_str());
        if (! m_hDll)
        {
            // Error
            throw std::runtime_error("Can't load the DLL - LoadLibrary() failed.");
            // .... or use some other exception...
        }
    }

    // Safely and automatically release the DLL.
    ~RaiiDll()
    {
        if (m_hDll)
        {
            ::FreeLibrary(m_hDll);
            m_hDll = nullptr;
        }
    }

    // Get DLL module handle.
    HMODULE Get() const
    {
        return m_hDll;
    }

private:
    HMODULE m_hDll;  // DLL instance handle

    //
    // Ban copy (if compiler supports new C++11 =delete, use it)
    //
private:
    RaiiDll( RaiiDll & );
    RaiiDll & operator=( RaiiDll & );
};

Then, in some code block, you can have:

{
    // Load the library (throws on error).
    RaiiDll scannerDll(L"WinScanner.dll");

    // Get DLL function pointer
    auto pCreateNewScanner = reinterpret_cast<CreateNewScannerPtr>( 
        GetProcAddress(scannerDll.Get(), "CreateNewScanner"));
    if (! pCreateNewScanner)
    {
        .... error.       
    }

    .... use the function

} // <--- DLL automatically released thanks to RaiiDll destructor!!!

Note how code is simplified thanks to automatic invocation of RaiiDll destrutor (and so of FreeLibrary), also in the error path case.



标签: c++ winapi dll