【问题标题】:Port this C code to Delphi/Lazarus将此 C 代码移植到 Delphi/Lazarus
【发布时间】:2018-08-10 18:13:56
【问题描述】:

我有这段 C 代码,我想移植到 Delphi,但我无法让它工作。

.CPP 代码

#include <Windows.h>

#include <io.h>
#include <stdio.h>

#include "GLibExp.h"
#pragma comment(lib, "GLib.lib")

void MyCFunc(LPCTSTR GStr)
{
    GFile GVar = NULL;
    GVar = GrfLoad(GStr, 1);
    if ( !GVar )
    {
        printf("Error during loading!\n");
    } else
        printf("All fine!\n");

    GrfFree(GVar);
    system("pause");
}

void main()
{
    CHAR StrG[MAX_PATH] = "Test.grf";
    MyCFunc(StrG);
    return;
}

GLibExp.h

#ifndef GLibExpH
#define GLibExpH

#if defined(GRF_DLL)
#define GEXPORT __declspec(dllexport)
#else
#define GEXPORT extern
#endif

class CGFILE;
typedef CGFILE* GFile;
//typedef void* GFile; //Also works like this

#ifdef __cplusplus
extern "C" {
#endif
GEXPORT GFile GrfLoad(const char *GName, unsigned char Mode = 1);
GEXPORT void GrfFree(GFile GVar);
#ifdef __cplusplus
}
#endif

#endif//GLibExpH

程序在运行时调用 DLL 以使用 GRFLoadGRFFree 函数。我尝试将其移植到 Delphi,但没有成功。

德尔福/拉撒路代码:

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;

{$Link GLib.lib}

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

function GrfLoad(const fname: PChar; Modo: Boolean): Pointer; cdecl; external 'GLib.dll';

procedure TForm1.Button1Click(Sender: TObject);
var
  PVar: Pointer;
begin
  PVar:= GrfLoad(PChar('test.grf'),false);
end;

end. 

如果我注释掉{$Link GLib.lib} 行,程序会运行,但是当我调用GRFLoad 时它总是崩溃(程序停止工作然后关闭)。如果我留在{$Link GLib.lib}这一行,程序不编译并报错:

project1.lpr(20,1) Error: Illegal COFF Magic while reading GLib.lib

有什么提示吗?

注意:我刚刚添加了一个指向 Visual C++ 2010 项目的链接,其中包含所有需要的文件。其实我只是做了一个“New project -> Win32 Console Application”(我在Wizard中标记了“empty project”),添加一个新的CPP文件并粘贴代码,并更改“Properties -> Configuration Properties -> Linker - > 启用增量链接:否”,仅此而已。

https://drive.google.com/open?id=1JxW4Wra_kT8gfBda1t0WWqagzazzQVne

【问题讨论】:

  • 运行时错误。 “程序停止工作”,然后关闭。
  • GEXPORT __declspec(dllexport)是否对应stdcall
  • 不知道是否对应stdcall。 lib已编译,没有源代码。
  • 你绝对不需要GLib.lib。 DLL 的失败有一些原因 - 由于名称修改、错误的校准约定、错误的参数描述、内存处理问题(不同的内存管理器、使用内部 Delphi 托管类型 lke 字符串)导致的错误导入名称。似乎第一个不是你的情况 - 加载过程中关于错误名称的静态导入报告。
  • @Victoria 不,C 和 C++ 编译器的默认调用约定将是 cdecl

标签: delphi dll external lazarus lib


【解决方案1】:

函数的正确声明应使用PAnsiCharchar 在 C 和 C++ 中始终是单字节类型):

type
  GFile = Pointer; // alternatively: GFile = THandle;

function GrfLoad(const FName: PAnsiChar; Mode: Boolean): GFile; cdecl; external 'GLib.dll' name 'GrfLoad';
procedure grfFree(GVar: GFile); cdecl; external 'GLib.dll' name 'GrfFree';

但很可能导出的名称不是GrfLoadGrfFree,而是不同的名称,例如_GrfLoad_GrfFree。您可以使用 MS 的 Dependency Walker 之类的工具或使用 Delphi 自己的 TDump.exe(查找 Exports 部分)找出实际导出的名称,即使用

tdump glib.dll

glib.dll所在目录的命令行中。

如果名称不同,则必须更改外部声明的名称部分,例如 external 'Glib.dll' name '_GrfLoad'; 等。

我的文章中有更多信息:Pitfalls of converting

当然,DLL 也有可能找不到它所依赖的另一个 DLL。 Dependency Walker 还会告诉您缺少导入的信息。

更新

请注意,您链接到的 zip 文件中的 DLL 称为 GrfLib.dll,而不是 GLib.dll。并且名称确实导出为GrfLoad 等。

很可能您的系统上也有一个 glib.dll,但其中不包含您正在寻找的功能。


另外请注意,大多数人不喜欢从未知来源下载 zip。他们不知道里面到底是什么。

【讨论】:

  • 感谢您的帮助。今天我将尝试使用您提供给我的所有信息和线索。另外,我下次会考虑您对上传 zip 文件的评论。
  • 你也可以修复unsigned char Mode... ;-)
  • @Victoria:在这种情况下,我认为布尔值是一种有用的翻译。也许 ByteBool 也是合适的。
  • @Rudy,好吧,我还没有深入研究 GLib 源代码,但是只要定义了导出参数,我宁愿保持原样(甚至是那个参数的名称)没有明确指出是布尔类型)。我想你知道的更多,只是对我来说,不知道该库的内部结构,我会将其视为unsigned char,对于 Delphi 和 FPC,它是 Byte
  • 所以您实际上将 false(序数 0)更改为 true(序数 1)并且有效?无需更改声明,请参阅 Sertac 对您的回答的评论。
【解决方案2】:

解决了!我只是将声明更改为:

function GrfLoad(const fname: PChar; Modo: Boolean): Pointer; cdecl; external 'GLib.dll';

到:

function GrfLoad(const fname: PChar; Modo: Byte): Pointer; cdecl; external 'GLib.dll';

电话来自:

PVar:= GrfLoad(PChar('test.grf'),false);

到:

PVar:= GrfLoad(PChar('test.grf'),1);

仅此而已。感谢 Mbo、Victoria 和 Rudy Velthuis 的帮助。

【讨论】:

  • 布尔值是字节大小的。因此,您有效更改的是传递“true”而不是“false”(如果您愿意,也可以使用 1 而不是 0)。它不再崩溃。没有任何意义...
  • @Sertac:确实。声明中的更改不会改变任何内容。 Boolean 和 Byte 大小相同,DLL 看不出区别。所以从 false 到 true 的变化似乎使它起作用了(现在)。
  • 正确,有效值为1到3,所以当y传递“false”(0)时程序崩溃。如果我输入“true”(1),该函数将起作用,但需要更改为字节以允许传递值 2 和 3。
  • 啊,目前还不知道有效值为 1..3。但是,当传递了错误的值时,程序崩溃并没有经过深思熟虑。无论如何,它确实应该是一个字节:... Mode: Byte = 1);.
猜你喜欢
  • 2011-01-15
  • 2011-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-18
相关资源
最近更新 更多