2

我在 Visual Studio 中编译了一个 DLL(源代码是 C++,我几乎看不懂)。这是一块Scraper.h

struct SWin
{
   char title[512];
   HWND hwnd;
};

SCRAPER_API bool ScraperGetWinList(SWin winList[100]);

现在我正在尝试在我的 Delphi 应用程序中使用上述功能:

type
  tWin = record
    title: String;
    hwnd: HWND;
  end;

function ScraperGetWinList(var WinList: Array of tWin): Boolean; external 'Scraper.dll';

var
  myWinList: Array [1..100] of tWin;

procedure TMainForm.GetWinListButtonClick(Sender: TObject);
begin
  ScraperGetWinList(myWinList);
  ...

项目无法编译,我收到以下消息:过程入口点 ScraperGetWinList 无法位于动态链接库中: Scraper.dll

我究竟做错了什么?

4

3 回答 3

6

根据我的 Linux 经验,我会说您遇到了所谓的“名称修改”问题。您的过程的入口点不称为“ScraperGetWinList”,而是类似于“_ZN18ScraperGetWinListEpN4SWin”。

问题是,与 C 不同,在 C++ 语言中,入口点的名称与函数名称不同。难怪:假设,你有一组重载函数;它们在您的 DLL 中应该有不同的入口点。这就是名称修饰发挥作用的地方。

这个问题最常见的解决方案是定义库的接口,使其使用 C 调用约定。然后,接口函数不会发生名称修改。

请注意,您不必用 C 编写整个库,您只需为它们声明函数以发出类似 C 的入口点。

通常是这样写的:

extern "C" {

SCRAPER_API bool ScraperGetWinList(SWin winList[100]);
// More functions

}

重新编译您的库并在 Delphi 中毫无问题地使用它。

请注意,您还应该调整调用约定(stdcall 或 cdecl)以使其在您的 C++ 标头和 Delphi 代码中匹配。但是,最好在另一个问题中对此进行解释。

于 2009-10-18T09:44:10.523 回答
4

名称修改很可能是问题所在。名称修改通常是在 C++ 代码中完成的,当用 C++ 编写一个应该由其他语言的代码使用的 DLL 时,您应该使用 Pavel Shved 已经建议的 Extern“C”构造。

使用 DLL 时,尤其是使用其他语言编写时,您还应该注意调用约定。我建议您在 delphi 和 c++ 中都指定使用 stdcall 调用约定。这也是 windows api 使用的调用约定,因此它保证了不同编译器之间的最佳互操作性。

这意味着

外部“C”{

SCRAPER_API __stdcall bool ScraperGetWinList(SWin winList[100]);

}

function ScraperGetWinList(var WinList: Array of tWin): Boolean; 外部'Scraper.dll';

但这还不是全部,stdcall 调用约定对名称修改有影响,结果会类似于 _ScraperGetWinList@4 (其中 4 是参数的大小,其中数组将具有指向第一个元素的指针,所以 4 个字节)

为了确认要使用的正确符号,我建议使用 Dependency Walker ( http://www.dependencywalker.com/ ),该程序显示 dll 准确地导出了函数名称。确认名称为 '_ScraperGetWinList@4' 后,您将其添加到 delpgi 中,如下所示:

function ScraperGetWinList(var WinList: Array of tWin): Boolean; 外部“Scraper.dll”名称“_ScraperGetWinList@4”;

于 2009-10-18T10:01:55.063 回答
1

您是否真的在 c++ 代码中导出了入口点函数?当我第一次在 Visual Studio 中编译 C++ dll 以用于 dotnet 应用程序时,这真的让我很难过。

例如,我需要在非托管代码中公开打印驱动程序,以便其他开发人员可以在 VB.net 中访问它。这就是我所做的。

在 foo.cpp 中:

extern "c" {
  ___declspec(dllexport) bool FooBar()
  {
     // Call some code on my cpp objects to implement foobar
  }
}

然后在一个名为 foo.def 的文件中:

LIBRARY   "mylib"

EXPORTS
        FooBar 
        AnyOtherFunctionsItExports

这就是我让它工作的方式。我可能没有以最好的方式做事。我对 C++ 经验有点了解,而且主要不在 Windows 上工作。

于 2009-10-18T13:12:40.317 回答