Using DLL compiled with Cygwin inside Visual Studi

2020-04-13 20:28发布

问题:

I've read already tremendous amount of articles, tutorials, and instructions about how to solve my problem - but still - no gain. I just can't put it into work.

My goal is pretty simple: I want to compile a DLL file using Cygwin gcc tool, and then use it within MSVC2010. I'm actually want to do it with my own DLL code, but as for simplicity - I've tried the very basic example on Cygwin's site - and failed with that too..

What I've done so far: (Most is taken from Cygwin User's Guide, DLL section)

  1. Created mydll.c file as following:

    #include <stdio.h>
    
    int hello()    {
      printf ("Hello World!\n");
    }
    
  2. Compiled mydll.c into mydll.dll using gcc -c mydll.c and gcc -shared -o mydll.dll mydll.o

  3. Opened an empty Win32 Console project in Visual Studio, with the following code as test.c (code taken from Oleg's code in here, and based on this) :

    #include <windows.h>
    
    typedef int (*PFN_HELLO)();
    typedef void (*PFN_CYGWIN_DLL_INIT)();
    
    int main()
    {
        PFN_HELLO fnHello;
        HMODULE hLib, h = LoadLibrary(TEXT("cygwin1.dll")); 
        PFN_CYGWIN_DLL_INIT init = (PFN_CYGWIN_DLL_INIT)GetProcAddress(h,"cygwin_dll_init");
        init(); 
    
        hLib = LoadLibrary (TEXT("D:\\test\\mydll.dll"));
        fnHello = (PFN_HELLO) GetProcAddress (hLib, "hello");
        return fnHello();
    }
    
  4. Set the path variable on Windows system to include "Cygwin\bin\" directory.

  5. Build & Run.

I ended up with the following exception: 0xc0000005: Access violation reading location 0x003a0048.

Here is the full MSVC2010 Debug Output:

'CygwinDLLTest.exe': Loaded 'C:\Users\xxxx\Documents\Visual Studio 2010\Projects\CygwinDLLTest\Debug\CygwinDLLTest.exe', Symbol loaded.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\ntdll.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\kernel32.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\kernelBase.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\msvcr100d.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'D:\Cygwin\bin\cygwin1.dll', Binary was not built with debug information.
cYgFFFFFFFF 6119F510 0cYgstd 0x27a70b d 3'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\user32.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\gdi32.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\lpk.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\usp10.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\msvcrt.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\advapi32.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\sechost.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\rpcrt4.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\sspicli.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\cryptbase.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\imm32.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'C:\Windows\SysWOW64\msctf.dll', cannot find or open the PDB file.
'CygwinDLLTest.exe': Loaded 'D:\test\mydll.dll', Binary was not built with debug information.
First-chance exception at 0x611075a8 in CygwinDLLTest.exe: 0xc0000005: Access violation reading location 0x003a0048
Unhandled exception at 0x611075a8 in CygwinDLLTest.exe: 0xc0000005: Access violation reading location 0x003a0048
The program '[784] CygwinDLLTest.exe: Native' has exited with code 0 (0x0).

Now, it's important to notice that the problem wasn't loading those DLL's, as their handlers all got an address different from NULL. The line in the code that cause the exception, was the call to the hello func, inside the DLL file.

And before you go and say anything about extern "C" or __declspec(dllimport/dllexport) - won't help. I've tried all of them and it didn't help. (although, AFAIK - I'm using Explicit Linking in terms of MS, or Dynamic Loading in terms of UNIX, so __declspec(dllimport/dllexport) is not necessary).

I'm really hope the problem is not at the stack definition, as lying over here:

"Make sure you have 4K of scratch space at the bottom of your stack"

Because I've no clue how to make this happen on MSVC2010 (and apparently, neither do Oleg...:)

Now, I know there's a direct reference to my problem in Hans Passant's words over here - still - I couldn't understand how to solve my problem.

UPDATE: I'm updating this, because I think I know what cause the problem, I just don't know how to solve it. And forgive me, Arkady, but I really don't think that it has to do anything with all what you mentioned. I was taking about really simple .dll file, with only one function, so there's nothing to 'wrap' there..
Anyway, I think my problem is the use of msvcrt.dll file, as opposed to what stated here:

Q: Can I link with both MSVCRT*.DLL and cygwin1.dll?
A:No, you must use one or the other, they are mutually exclusive.

I know I'm linking against both of them, I just don't know how to convince Visual Studio to link only against cygwin1.dll alone..

I'll appriciate an answer to this.

回答1:

To create DLL using gcc and use it in Visual Studio, you need to follow next steps:

1) check that your packing is same. I mean:

#pragma pack(push, N) 
#pragma pack(pop)

For all structures, functions and data types you want to export. To avoid different default data packing.

2) check that you don't use external header files (i.e. your exporting header don't contains anything that is taken from external header files). I mean "windows.h" may be and will be different in VS2010 and CygWin. Usually difference is not important, but since it exist, you may have problems with it. Same with STL and others, versions may not (and will not!) be compatibility, so you will have problems with memory.

3) check that you export just simple structures and global functions. So, you really have to export C-like interface. Theory tells that if you will export also abstract interfaces, such as in example:

#pragma pack(push, 4)
struct A
{
    virtual ~A() {}
    virtual int32_t DoA() = 0;
    virtual int32_t PrepareA(const char* settings) = 0;
};

void GlobalFunction(A** ret);
#pragma pack(pop)

Or avoid virtual destructor at all, and add global function that will release memory of objects who were allocated in DLL. Because gcc and msvc-10.0 will have different data management, so all allocated with gcc allocators have to be released by gcc deallocators.

it have to work correctly, because will be initialized inside DLL by calling GlobalFunction, and will have destructor, realized inside DLL. Still, I would offer to avoid it, using C-like style with just simple data and global functions.

4) You also have to be sure int at your gcc and int at your VS2010 have same size. And you set same architecture. To avoid that problem you should use int32_t, for example.

5) And you should be sure that all exported dll functions don't throws exceptions. At all. By same memory problem-reason.