编译器之间的 Dll 兼容性

2023-11-27

有没有办法让不同编译器构建的 c++ dll 相互兼容?这些类可以具有用于创建和销毁的工厂方法,因此每个编译器都可以使用自己的 new/delete(因为不同的运行时有自己的堆)。

我尝试了以下代码,但它在第一个成员方法上崩溃了:

接口.h

#pragma once

class IRefCounted
{
public:
    virtual ~IRefCounted(){}
    virtual void AddRef()=0;
    virtual void Release()=0;
};
class IClass : public IRefCounted
{
public:
    virtual ~IClass(){}
    virtual void PrintSomething()=0;
};

用VC9编译的test.cpp,test.exe

#include "interface.h"

#include <iostream>
#include <windows.h>

int main()
{
    HMODULE dll;
    IClass* (*method)(void);
    IClass *dllclass;

    std::cout << "Loading a.dll\n";
    dll = LoadLibraryW(L"a.dll");
    method = (IClass* (*)(void))GetProcAddress(dll, "CreateClass");
    dllclass = method();//works
    dllclass->PrintSomething();//crash: Access violation writing location 0x00000004
    dllclass->Release();
    FreeLibrary(dll);

    std::cout << "Done, press enter to exit." << std::endl;
    std::cin.get();
    return 0;
}

用g++编译的a.cpp g++.exe -共享c.cpp -o c.dll

#include "interface.h"
#include <iostream>

class A : public IClass
{
    unsigned refCnt;
public:
    A():refCnt(1){}
    virtual ~A()
    {
        if(refCnt)throw "Object deleted while refCnt non-zero!";
        std::cout << "Bye from A.\n";
    }
    virtual void AddRef()
    {
        ++refCnt;
    }
    virtual void Release()
    {
        if(!--refCnt)
            delete this;
    }

    virtual void PrintSomething()
    {
        std::cout << "Hello World from A!" << std::endl;
    }
};

extern "C" __declspec(dllexport) IClass* CreateClass()
{
    return new A();
}

编辑: 我将以下行添加到 GCC CreateClass 方法中,文本已正确打印到控制台,因此它显然是函数调用杀死了它。

std::cout << "C.DLL Create Class" << std::endl;

我想知道,COM 如何设法保持二进制兼容性,甚至跨语言,因为它基本上所有类都具有继承(尽管只有单个),因此具有虚拟函数。如果我不能重载运算符/函数,只要我能够维护基本的 OOP 内容(即类和单一继承),我就不会太烦恼。


如果您降低期望并坚持使用简单的函数,您应该能够混合使用不同编译器构建的模块。

类和虚函数的行为方式由 C++ 标准定义,但实现方式取决于编译器。在这种情况下,我知道 VC++ 构建的对象具有虚拟函数,该对象的前 4 个字节(我假设是 32 位)中带有“vtable”指针,并且该指针指向指向方法条目的指针表点。

所以这一行:dllclass->PrintSomething();实际上相当于:

struct IClassVTable {
    void (*pfIClassDTOR)           (Class IClass * this) 
    void (*pfIRefCountedAddRef)    (Class IRefCounted * this);
    void (*pfIRefCountedRelease)   (Class IRefCounted * this);
    void (*pfIClassPrintSomething) (Class IClass * this);
    ...
};
struct IClass {
    IClassVTable * pVTab;
};
(((struct IClass *) dllclass)->pVTab->pfIClassPrintSomething) (dllclass);

如果 g++ 编译器以与 MSFT VC++ 不同的任何方式实现虚拟函数表(因为它是免费的并且仍然符合 C++ 标准),那么正如您所演示的那样,这只会崩溃。 VC++ 代码期望函数指针位于内存中的特定位置(相对于对象指针)。

由于继承,它变得更加复杂,而且由于多重继承和虚拟继承,它变得非常非常复杂。

Microsoft 非常公开 VC++ 实现类的方式,因此您可以编写依赖于它的代码。例如,MSFT 分发的许多 COM 对象标头在标头中同时具有 C 和 C++ 绑定。 C 绑定公开了它们的 vtable 结构,就像我上面的代码一样。

另一方面,GNU - IIRC - 保留了在不同版本中使用不同实现的选项,并且仅保证使用其编译器构建的程序(仅!)将符合标准行为,

简短的答案是坚持简单的 C 风格函数、POD 结构(普通旧数据;即没有虚函数)和指向不透明对象的指针。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编译器之间的 Dll 兼容性 的相关文章

随机推荐

  • 使用 xelatex 进行 Rmarkdown

    我正在尝试使用 Calibri 字体 但据我了解 我首先需要能够使用 xelatex 引擎 这就是我遇到问题的地方 示例代码和错误如下 请注意 我对乳胶很陌生 所以我很可能错过了一些明显的东西 如果重要的话 我安装了 MikTeX 2 9
  • 我可以覆盖 std::hash 吗?

    我可以替换实际的实现std hash用我自己的定义std hash在 C 11 中 我的意思是来自我的代码库 而不触及标准库 在这种情况下 我看不到虚拟函数 多态性有任何用处 所以我想我无论如何都无法改变 std hash 的定义 您可以将
  • 避免 DELETE 查询中的自引用

    我正在尝试删除其名称下不是最新版本的所有记录 但显然您无法引用访问您在同一查询中修改的表 我尝试了这个 但由于上述原因它不起作用 DELETE FROM table WHERE CONCAT name version NOT IN SELE
  • 使用java加密和解密密码使用什么API和算法

    我目前正在使用 Java 创建应用程序 我在 google 上搜索了 java 密码加密 但结果如此巨大 我感到不知所措 如何使用 Java 加密和解密密码 加密和解密密码的最佳实践是什么 我猜 MD5 不是一种可行的方法 因为它是一种单向
  • 如何从包含多个 GzipStream 的文件中读取

    我有一个使用代码创建的文件 如下所示 using var fs File OpenWrite tmp using GZipStream gs new GZipStream fs CompressionMode Compress true u
  • package code.google.com/p/go.example/hello: exec: "hg": 在 %PATH% 中找不到可执行文件。如何获取远程golang包?

    我按照 Golang 教程中写的那样做http golang org doc code html remote 我的环境设置 C sbox go example gt set go GOPATH C sbox go example GORO
  • Android OpenGL ES Framebuffer 对象 - 将深度缓冲区渲染到纹理

    我使用的 Android 设备运行 Froyo 支持 OpenGL ES 1 1 和 OpenGL ES 2 0 我想将深度缓冲区渲染为纹理 在看过其他平台 包括 iPhone 上的 OpenGL OpenGL ES 的许多示例后 我尝试了
  • AJAX 文件上传/表单提交无需 jquery 或 iframe?

    是否可以在没有 jQuery 或 IFrames 的情况下进行 AJAX 表单提交 因此只需纯 JavaScript 我目前正在发送到一个可以工作的struts fileUploadAction 该操作的代码是否仍适用于异步提交 或者是否需
  • 我可以在 iOS 上将 HTTP 缓存与 NSURLSessionDownloadTask 一起使用吗?

    我正在尝试使用NSURLSessionDownloadTask 并利用 Apple 内置的 URL 缓存功能 我已经成功地让缓存在使用时起作用NSURLSessionDataTask使用下面的代码 void downloadUsingNSU
  • Haskell 管道和分支

    Problem 我正在尝试使用 Haskell 和 Pipes 库实现一个简单的 Web 服务器 我现在明白循环或菱形拓扑对于管道是不可能的 但我认为我正在尝试这样做 我想要的拓扑结构是 GET gt handleGET gt gt pac
  • 即使在deleteLocalRef之后jni表也会溢出

    当我运行代码时 出现错误 无法添加到 JNI 本地引用表有 512 个条目 这是我的代码 jstring pJNIData pJNIEnv gt NewStringUTF variables 0 GetStringValue pJNIEnv
  • 如何使用自托管 ServiceStack 忽略路由

    我目前正在开发一个解决方案 我们有一个正在运行的自托管 ServiceStack 层 但问题是当我从浏览器访问它并且浏览器尝试获取网站图标时 我不断收到错误 据我所知 在运行自托管时没有忽略特定路由的选项吗 我会想象类似的事情 Routes
  • 如何使用 python 替换/删除 pdf 中的文本? [复制]

    这个问题在这里已经有答案了 我有隐藏 pdf 部分的代码 只需用白色多边形覆盖它 但问题是 文本仍然是there 如果你按 ctrl f 仍然可以找到它 我的目标是实际从 pdf 本身中删除文本 使用 pdfminer 我设法从 pdf 中
  • 为什么 App_Offline 一旦开始加载 dll 就无法工作?

    有人可以帮我解决这个问题吗 在生产站点上 app offline htm 仅在您开始上传 dll 之前有效 一旦您开始上传 dll 它就会抛出以下错误 无法加载文件或程序集 SubSonic 或其依赖项之一 该进程无法访问该文件 因为该文件
  • 关于使用 tf.image.crop_and_resize

    我正在研究适用于 fast rcnn 的 ROI 池化层 并且我习惯使用张量流 我发现tf image crop and resize可以充当 ROI 池化层 但我尝试了很多次都无法得到我期望的结果 或者真正的结果正是我得到的吗 这是我的代
  • sprintf 何时以及为何会失败?

    我正在使用 swprintf 将字符串构建到缓冲区中 使用循环等 const int MaxStringLengthPerCharacter 10 1 wchar t pTmp pBuffer for size t i 0 i lt nNu
  • Java 中的 HTTP URL 地址编码

    我的 Java 独立应用程序从用户那里获取一个 URL 指向一个文件 我需要点击它并下载它 我面临的问题是我无法正确编码 HTTP URL 地址 例子 URL http search barnesandnoble com booksearc
  • 在 JavaFx 中从输入流播放 mp3 文件

    我正在使用 JavaFX 媒体播放器使用以下代码播放 mp3 文件 new MediaPlayer new Media FileObject toURI toString play 但是现在我需要在内存中保存 mp3 字节数据而不是文件对象
  • 强制浏览器使用新的 CSS

    有没有办法检查用户的浏览器是否缓存了不同版本的 CSS 如果是 则强制他们的浏览器提取新版本 我不知道这是否是正确的用法 但我认为您可以使用查询字符串强制重新加载 css 文件 我记得几年前我使用过这种方法来强制重新加载网络摄像头图像 但时
  • 编译器之间的 Dll 兼容性

    有没有办法让不同编译器构建的 c dll 相互兼容 这些类可以具有用于创建和销毁的工厂方法 因此每个编译器都可以使用自己的 new delete 因为不同的运行时有自己的堆 我尝试了以下代码 但它在第一个成员方法上崩溃了 接口 h prag