Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Link against a Windows .dll+.lib file combination with GCC under Cygwin?

I know how to link against libraries in Unix-ish contexts: If I'm working with .a or .so files, I specify the root search directory with -L/my/path/to/lib/ and for libMylib I add -lMyLib.

But what if I have

  • a .dll (e.g. in the Windows\System32 directory)?
  • a .dll (in Windows\System32) and a .lib (someplace else)?

These DLLs are by some other party; I don't have access to their sources - but do have access to the corresponding include files, against which I manage to compile.

like image 432
einpoklum Avatar asked Jul 31 '13 11:07

einpoklum


2 Answers

@einpoklum Converting my comment to an answer: @n.18e9 is correct in that you must use the full path name for the lib file without any -L or -l options. g++ -o foo.exe foo.o c:\something\somethingelse\some.lib. You can also link directly to the Windows DLL file g++ -o foo.exe foo.o c:\something\somethingelse\some.dll.

Important - make sure you are linking to a lib file (and associated dll) generated for a 64-bit platform (on MSVC target X64, not Win32).

OK you wanted an example, well let's go.

Here are two examples using gcc/g++ to link to a Windows native DLL which exports plain C functions (using here x86_64-w64-mingw32/8.3.0 on Windows 10).

I'm using my own free xmlsq library as an example https://www.cryptosys.net/xmlsq. You can download the core native DLL and all the source code quoted below. Make sure you use the 64-bit DLL.

The native Windows DLL diXmlsq.dll is written entirely in plain C code and exports simple C functions (extern "C"). In particular, for this example, it exports a XMLSQ_Gen_Version function that returns an integer value. The DLL was compiled using MSVC 12.0 targetting the X64 platform. The associated library file generated by MSVC is diXmlsq.lib.

I should add that this DLL works exactly the same as a Windows "Win32 API" DLL, so the instructions here should work for the standard Windows libraries in Windows\System32 (again make sure you link against the 64-bit version).

Example 1. A plain C interface.

Both these commands compile without warning on my system:

> gcc -o test-ver test-ver.c "C:\fullpath\to\x64\diXmlsq.lib"

> gcc -o test-ver test-ver.c "C:\fullpath\to\x64\diXmlsq.dll"

diXmlsq.dll is compiled using the following definition file. (You could alternatively use __declspec(dllexport))

Ref: https://docs.microsoft.com/en-us/cpp/build/exporting-from-a-dll?view=msvc-160

diXmlsq.def

LIBRARY      "diXmlsq"
EXPORTS
    XMLSQ_Gen_Version

diXmlsq.h - the C interface to diXmlsq.dll

#ifdef __cplusplus
extern "C" {
#endif

long __stdcall XMLSQ_Gen_Version(void);

#ifdef __cplusplus
}
#endif

To call the core function in a plain C program:

test-ver.c

#include <stdio.h>
#include "diXmlsq.h"
int main(void)
{
    long n;
    n = XMLSQ_Gen_Version();
    printf("Version = %ld\n", n);
    return 0;
}

Example 2. A C++ interface.

Both these commands compile without warning using g++ .

> g++ -o test-simple test-simple.cpp xmlsq.cpp "C:\fullpath\to\x64\diXmlsq.lib"

> g++ -o test-simple test-simple.cpp xmlsq.cpp "C:\fullpath\to\x64\diXmlsq.dll"

The idea of the C++ interface is to be an interface to the plain C library using the more convenient STL types like std::string and std::vector. To keep things simple we'll just demonstrate the Gen::Version method.

Extracts of the C++ code follow:

test-simple.cpp - a test C++ program.

#include <iostream>
#include "xmlsq.hpp"
int main()
{
    std::cout << "xmlsq::Gen::Version=" << xmlsq::Gen::Version() << std::endl;
}

xmlsq.hpp - the C++ interface

namespace xmlsq
{
    class Gen {
    private:
        Gen() {} // Static methods only, so hide constructor.
    public:
        /** Get version number of core diXmlsq DLL. */
        static int Version();
    };
}

xmlsq.cpp - the C++ implementation.

#include "diXmlsq.h"
#include "xmlsq.hpp"

namespace xmlsq
{
    int Gen::Version() {
        int n = XMLSQ_Gen_Version();
        return n;
    }
}

Example 3. Attempting to link to the 32-bit library by mistake.

> gcc -o test-ver test-ver.c "C:\fullpath\to\Win32\diXmlsq.lib"
C:/Strawberry/c/bin/../lib/gcc/x86_64-w64-mingw32/8.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: 
C:\Users\user\AppData\Local\Temp\cce27Dhl.o:test-ver.c:(.text+0xe): 
undefined reference to `XMLSQ_Gen_Version'
collect2.exe: error: ld returned 1 exit status
like image 183
David I Avatar answered Nov 03 '22 08:11

David I


If you can link against a .lib in Cygwin or MinGW, then you can (indirectly) link against a DLL.

In the MSVC world, it is not unusual to create an import library along with a DLL. It is a static library (.lib) that loads the DLL and wraps the interface of the DLL. You just call the wrapper functions in the (static) import library and let the import library do all the DLL-related things.

  • For the Windows API, there are import libraries in the WindowsSDK.
  • For your own MSVC DLLs, MSVC can automatically generate the import libraries when you build the DLL.
  • For a third party DLL, you can build a static wrapper library based on the corresponding header files.

Linking against the .lib file in Cygwin or MinGW is possible. Example:

g++ -o myprg myprg.o -lShlwapi

This links against Shlwapi.lib. (The library must be in the local directory or in the library path of the linker.)

Linking against import libraries of DLLs works the same way.

Note 1: Keep in mind the different ABIs and name mangeling. However, calling plain C functions in DLL or LIB files will work in most cases.

Note 2: Keep in mind that g++ requires the libraries to be specified in the correct order.

like image 32
ManuelAtWork Avatar answered Nov 03 '22 08:11

ManuelAtWork