从 10.3 项目调用 c++ 函数(bcb6 dll/lib)函数

Posted

技术标签:

【中文标题】从 10.3 项目调用 c++ 函数(bcb6 dll/lib)函数【英文标题】:Calling c++ function (bcb6 dll/lib) function from 10.3 project 【发布时间】:2021-11-09 00:50:35 【问题描述】:

我遇到了一个非常令人困惑的现象,试图结合嵌套结构指针参数调用定义为 _declspec(dllexport) 的 borland c++ builder 6 dll 的函数。 结构声明包含 AnsiString、std::string 和 std::vector 成员,当我从 bcb6 项目调用 dll 时,它确实有效。 不幸的是,当我尝试从 Rad Studio 10.3 项目中调用相同的方法并尝试从向量访问成员时,我确实遇到了访问冲突。为了找出问题的根源,我询问了向量的大小: 在 10.3 中调用之前:1 在 bcb6 中调用后:大值 > 80000

好像是struct被移位了,否则我无法解释这个奇怪值的原因。在循环期间确实发生了访问冲突:

int methodCall(const char* val, const A3* data, const char* info)

  for(std::vector<A2*>::iterator it = data->PersonData.begin(); it !=    
      data->PersonData.end(); ++it) 
  
     AnsiString test = it->Name; //access violation
  

我确实已经检查了 c++ 编译器对齐,它在两个 IDE 中都设置为 quad word

代码是这样的:

struct A1

  AnsiString Val1;
  AnsiString Val2;
  std::string S1;

struct A2

  AnsiString Name;
  std::string Street;
  A1 Details;

struct A3

  AnsiString Val1;
  AnsiString Val2;
  std::vector<A2*> PersonData;

方法定义如下:

int __declspec(dllexport) __stdcall methodCall(const char* val, const A3* data, const char* info)

向量是这样填充的:

A3* a3 = new A3;
A2* a2 = new A2;
a2->Name = "Test";
a3->PersonData.push_back(a2);

尝试访问 data->PersonData 的向量元素,例如循环中的 data->PersonData.Name(在迭代器的帮助下),我收​​到这样的错误消息: 地址 BC3F2D3E 的访问冲突。读取地址 00000000。

真正让我感到困惑的是,当我在 RAD Studio 中调试相同的代码(使用迭代器等)时,它确实如此,并且它也确实可以与 bcb6bcb6 结合使用.这必须是一些编译器问题,但我没有具体的想法。我正在使用 10.3 中的经典编译器。

我非常感谢任何建议,因为我不知道可能是什么原因。从结构切换到类有帮助吗?

【问题讨论】:

不分享有问题的代码和确切的错误,我们只能盲目猜测 @CoryKramer:我已经编辑了我的帖子,如果需要任何进一步的信息,我会尝试扩展我的问题。 你能说明PersonData是如何填充的吗?那么导致你描述的错误的循环呢? 我希望添加的信息对您有所帮助。 【参考方案1】:

您根本不能跨 DLL 边界使用非平凡类型,例如 AnsiStringstd::stringstd::vector 等。在任何版本中这样做都是不安全的。 DLL 和 EXE 在所使用的编译器、对齐、内存管理等配置方面的差异都会影响兼容性。即使 EXE 和 DLL 在相同的编译器版本中编译(如在 BCB6 BCB6 案例中),仍然可能存在影响兼容性的细微差异,例如,如果 DLL 是静态编译的,那么一个常见的 RTL 实例不是与 EXE 共享。

在这种情况下,由于您要从一个版本的 C++Builder 更改为另一个版本,因此 C++Builder 6 (STLPort) 与 10.3 (Dinkumware) 中使用的 STL 库是非常不同的实现,它们与二进制不兼容彼此。甚至 AnsiString 的 RTL 内部结构在 C++Builder 2009 中也发生了更改(以适应对 UnicodeStringRawByteString 的支持),因此即使是 DLL 的 AnsiString 版本也与 EXE 的 @ 版本不兼容987654328@,或者。

您的 BCB6 编写的 DLL 与您的 10.3 编写的 EXE 根本不兼容。当跨越这些编译器版本时,显示的代码将永远不会按照您想要的方式工作。但是,如上所述,即使在 10.3 中重新编译 DLL 也不能保证解决所有问题。

您确实需要重新设计 DLL 接口,以完全不使用任何重要的类型。接口需要在所有编译器和设置中保持一致和稳定。 structs 很好用(如果你使用一致的填充和对齐方式),但坚持使用简单的成员类型,如整数、固定大小的数组、指向 C 样式字符串和动态数组的指针(然后进入更多交叉-编译器内存管理问题)等。

或者,您可以将现有的 DLL 重新实现为 BPL 包,然后您可以安全地跨 DLL 边界使用非平凡类型。但是,您将被锁定在特定的编译器版本和特定的 RTL/STL 实现中,因此如果您将来再次升级,则需要再次重新编译。

如果更改现有 DLL 不是一个选项,则必须将现有 DLL 包装在一个新的 BCB6 编写的 DLL 中,该 DLL 确实公开了此类接口。

【讨论】:

感谢您提供非常详细的答案。字符串也是如此,对吗?只是一个理论上的问题:如果 bcb6 库在 10.3 应用程序进入的方法中使用 String,它会在 10.3 应用程序进程中被翻译成 AnsiString 吗?由于它是一个动态 dll,使用与应用程序相同的配置编译,我会先尝试迁移 bcb6 dll,然后尝试一下。当然,我会进行测试以验证它是否仍然以相同的方式运行。否则我将需要遵循其他路线。在接口的情况下,是否可以将向量替换为 A2* PersonData[]? @kvirk System::String 更糟糕,因为它只是一个别名,但在两个版本中都不是同一类型。在 BCB6 中,它映射到 AnsiString,在 CB2009+ 中,它映射到 UnicodeString 我明白了,谢谢@Remy Lebeau。你的答案是,这些天你不再从书本中真正获得这种知识了。可能唯一的机会是通过长老的知识、邮件列表或经验来获取它。 csproj 结构或在 exe 和动态 dll 之间共享公共 RTL 实例也是如此。所有这些似乎都隐藏在亚特兰蒂斯岛深处的书籍中的不平凡的知识。​​ @kvirk 不幸的是真的

以上是关于从 10.3 项目调用 c++ 函数(bcb6 dll/lib)函数的主要内容,如果未能解决你的问题,请参考以下文章

从 C++ 调用 DLL 中的函数

BCB6 重装后的项目编译莫名问题

怎么查看用C++ builder编写的程序都调用了哪些dll文件,谢谢!

未导出成员函数时,从 C# 调用 C++ 本机/非托管成员函数

转:Delphi10.3 中通过JNI调用 Java 函数

bcb6 中安装 delphi7 的控件包