首页 > 解决方案 > 使用 Mingw G++ 构建的 DLL,调用时崩溃

问题描述

尝试使用 mingw 使用 C++ 代码构建 DLL 并不能正常工作...

我正在尝试将更大的 C++ 类包装在 dll 中,以便在其他 .net 环境中使用。但是打电话的时候总是闪退。

它与 .net 无关,因为首先我尝试从一个用 C 和 Visual Studio 制作的小测试程序调用 DLL。

我制作的小测试程序打开库,获取函数指针,并调用一个添加 2 个整数的函数。到那里它工作。下一个调用传入一个字符串,它会跳转到 invliad 内存空间。

我感觉 Mingw 构建的库在其 libstdc++ 中包含需要初始化的部分?(在程序中调用 main() 之前通常由框架代码调用的东西?)

使用 Mingw g++ 10.2.0 和这个命令行来构建小 DLL:

g++ -o gehtnichdll.dll -s -Wl,--subsystem,windows src/dllmain.cpp

我点点头的一个奇怪的事情是我需要添加一个名为'WinMain'的函数,否则G ++会抱怨它丢失了。

我从https://www.transmissionzero.co.uk/computing/building-dlls-with-mingw/获取了有关构建 DLL 的信息。

注意到 mingw 有一个选项 -mdll ,我试了一下,得到了一个不再加载的 dll(LoadLibrary 失败 193)

我找到了一个指向http://mingw.org/wiki/MSVC_and_MinGW_DLLs页面的链接,但该页面不再可用。

这是DLL的来源。剥离到几乎最低限度以显示问题。我试图包装的类也被完全删除,因为即使是这里的简单调用也已经失败。

#include <windows.h>
#include <stdint.h>
#include <stdio.h>
#include <string>

extern "C" __declspec(dllexport)
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}


#define IID_OFFSET 0x07C007EC

typedef float A;    // removed class A, not related to problem.

extern "C" __declspec(dllexport)
uint64_t AA__create(int var)
{
    A *a;
    a = new A(var); // There was the class constructor here.
                // Removed as it is not related to the problem.
    printf("called create (%d)\n",var);
    return (uint64_t)a;
}

extern "C" __declspec(dllexport)
void AA__destroy(uint64_t iid)
{
    A *a = (A*)iid;
    if(!a)return;
    delete a;
}

extern "C" __declspec(dllexport)
void AA__reset(uint64_t iid)
{
    A *a = (A*)iid;
    if(!a)return;
}

extern "C" __declspec(dllexport)
void AA__step(uint64_t iid)
{
    A *a = (A*)iid;
    if(!a)return;
    (*a)++;
}

extern "C" __declspec(dllexport)
uint16_t strlen2(const char *str)
{
    uint16_t res=0;
    while( str && str[res] )res++;
    return res;
}

extern "C" __declspec(dllexport)
void printstring(const char *str)
{
    printf("string: '%s'\n",str);
}

// Without this WinMain, G++ exits with   undefined reference to `WinMain'
// So providing a dummy function. Never called when loading as DLL.
extern "C" __declspec(dllexport)
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     PSTR pCmdLine, int nCmdShow)
{
    printf("dll as main.\n");
    uint64_t iid = AA__create(100);
    AA__destroy(iid);
    return 0;
}

extern "C" __declspec(dllexport)
int dummy_add(int a,int b)
{
    return a+b;
}

使用 MSVC++ 调用它的 C 程序是这样的(是 C,不是 C++):

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <Windows.h>

typedef void __declspec(dllimport) (*c_step)(uint64_t);
typedef uint64_t __declspec(dllimport) (*c_create)(int);
typedef int __declspec(dllimport) (*c_add)(int,int);
typedef void __declspec(dllimport) (*c_print)(const char *);
typedef uint16_t __declspec(dllimport) (*c_strlen)(const char *);

int main()
{
    printf("Hello World!\n");

    HMODULE h;
    h = LoadLibrary("C:\\muell\\gehtnichdll\\gehtnichdll.dll");

    if(!h)
    {
        int er = GetLastError();
        fprintf(stderr,"loadlib failed, err = %d\n",er);
        return 1;
    }

    c_create func_create = (c_create)GetProcAddress(h,"AA__create");
    c_step func_step = (c_step)GetProcAddress(h,"AA__step");
    c_add func_add = (c_add)GetProcAddress(h,"dummy_add");
    c_print func_prn = (c_print)GetProcAddress(h,"strlen2");
    c_strlen func_strl = (c_strlen)GetProcAddress(h,"printstring");
    if(!(func_create&&func_step&&func_add&&func_prn&&func_strl))
        {fprintf(stderr,"could not get function pointers.\n");return 1;}

    int x = func_add(4,7);
    printf("x = %d\n",x);

    // this function call fails with an invalid jump.
    uint16_t l = func_strl("lenth of this string");
    printf("l = %u\n",(unsigned int)l);

    // this call passes, but does not do output.
    func_prn("hello world");

    // These calls also fail (if commenting out the ones before)
    uint64_t iid = func_create(0xBB1BE);
    printf("Created, iid = 0x%" PRIXPTR "\n",(void*)iid);
    func_step(iid);

    return 0;
}

标签: c++dllmingw

解决方案


我现在已经解决了我的问题。不是很满意,因为我真的不知道出了什么问题。

将 mingw G++ 调用更改为静态链接并使用 -mdll 修复它。然后我可以调用函数。我假设链接器现在生成正确的初始化代码。

g++ -o gehtnichdll.dll -mdll -static-libstdc++ -static src/dllmain.cpp

仅使用 -mdll 而不是静态生成的 dll 取决于 libstdc++-6.dll ,即使放置在正确的文件夹中也不接受。使用 ProcessMonitor,我可以观察到它再次被打开和关闭 - 然后在其他地方进行更多搜索。如果没有 -mdll 选项,生成的 dll 在同一位置接受该版本的 libstdc++-6.dll。

奇怪的是我现在可以删除 DllMain 和 WinMain,但为什么它与 MinGW 引入的 DllMain 没有冲突?

无论如何,更改构建命令行可以修复它。

此外,我发现在 Stackoverflow 上多次引用了 transmissionzero.co.ok 的链接,但似乎不起作用(使用 MinGW 10)

留下许多悬而未决的问题。现在工作......


推荐阅读