将PChar传递给Delphi 2007 DLL和XE6 EXE

时间:2014-05-02 22:21:31

标签: delphi dll

我有一个用Delphi 2007编写的DLL,由于各种原因,我无法转换为在XE6中编译。

我在XE6中编写了一个调用DLL的小测试应用程序,这一切都很好,但我需要将DLL中的字符串数据传递给exe,我正在使用PCHAR来执行此操作。 然而,在XE6 exe中使用PCHAR证明有点痛苦。

我在某处看到,在D2007中,PCHAR实际上是一个PANSICHAR,所以我尝试使用PANSICHAR并调用DLL,但它只返回一个空字符串!

我尝试过各种其他类型,如PWIDECHAR,String,WideString,ShortString和PCHAR。 PCHAR会返回看起来像宽字符串的东西,但我不确定它是否真的是因为它在我尝试时没有投射: - )

所以我想知道我做错了什么?我从D2007 DLL传递了错误的类型吗?我是否应该在XE6中以不同方式消费数据?

- 编辑 - 好吧,我没有根据Remy的想法得到任何结果,所以这次我已经包含了我的代码,我相信这个解决方案对某人来说是显而易见的。我很感激帮助: - )

这是用D2007编写的DLL的代码:

library mydll;

  uses
    SysUtils

type
  TOnCommandProc = procedure(sMessage:PAnsiChar);
stdcall;

var
  FOnCommandProc: TOnCommandProc = nil;

procedure SetOnCommandProc(CallbackProc: TOnCommandProc);
stdcall;
begin
  FOnCommandProc := CallbackProc;
end;

procedure OnEventMessage(Data: String);
var
 BuffSize: Integer;
 sOut: string;
 oData : PAnsiChar;
begin

  sOut:=Data;
  BuffSize:=SizeOf(Char)*(Length(sOut)+1);
  getmem(oData, BuffSize);
  FillChar(oData^,BuffSize,0);

  if (Length(sOut)>0) then
  begin
    Move(sOut[1], PAnsiChar(oData)^, BuffSize);
    FOnCommandProc(oData);
  end;
end;

procedure TryTheEvent;
begin
  OnEventMessage('Hello');
end;

exports
  SetOnCommandProc name 'SetOnCommandProc',
  TryTheEvent name 'TryTheEvent';
end.

这是用Delphi XE6编写的调用EXE代码:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TOnCommandProc = procedure(sMessage:PAnsiChar); stdcall;

  procedure SetOnCommandProc(CallbackProc: TOnCommandProc; sMessage:PAnsiChar); stdcall; external 'mydll.dll';
  procedure TryTheEvent; stdcall; external 'mydll.dll';

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    class procedure MyOnCommandProc(sMessage:PAnsiChar); stdcall; static;
    procedure OnCommand(sMessage : PAnsiChar);

  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  TryTheEvent;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  SetOnCommandProc(@MyOnCommandProc,0);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  SetOnCommandProc(nil,0);
end;

class procedure TForm1.MyOnCommandProc(sMessage:PAnsiChar); stdcall;
begin
  TForm1(sMessage).OnCommand(sMessage);
end;

procedure TForm1.OnCommand(sMessage :PAnsiChar);
begin
  showmessage(sMessage);
end;

end.

2 个答案:

答案 0 :(得分:3)

在D2007及更早版本中,StringAnsiString的别名,PCharPAnsiChar的别名。

在D2009及更高版本中,StringUnicodeString的别名,PCharPWideChar的别名。

如果您希望XE6项目调用D2007 DLL,则必须使用PAnsiChar而不是PChar声明DLL类型/参数。在将AnsiString指针传递到DLL时,请务必使用AnsiChar或其他基于PAnsiChar的内存分配。如果您仍然遇到问题,请更新您的问题以显示无效的实际代码。

修改:根据您现在显示的代码,请尝试使用以下代码:

D2007 DLL:

library mydll;

uses
  SysUtils;

type
  TOnCommandProc = procedure(sMessage: PChar; pUserData: Pointer); stdcall;

var
  FOnCommandProc: TOnCommandProc = nil;
  FOnCommandUserData: Pointer = nil;

procedure SetOnCommandProc(CallbackProc: TOnCommandProc; UserData: Pointer); stdcall;
begin
  FOnCommandProc := CallbackProc;
  FOnCommandUserData := UserData;
end;

procedure OnEventMessage(Data: String);
begin
  if (Data <> '') and Assigned(FOnCommandProc) then
  begin
    FOnCommandProc(PChar(Data), FOnCommandUserData);
  end;
end;

procedure TryTheEvent; stdcall;
begin
  OnEventMessage('Hello');
end;

exports
  SetOnCommandProc name 'SetOnCommandProc',
  TryTheEvent name 'TryTheEvent';
end.

XE6 app:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TOnCommandProc = procedure(sMessage: PAnsiChar; pUserData: Pointer); stdcall;

  procedure SetOnCommandProc(CallbackProc: TOnCommandProc; UserData: Pointer); stdcall; external 'mydll.dll';
  procedure TryTheEvent; stdcall; external 'mydll.dll';

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    class procedure MyOnCommandProc(sMessage: PAnsiChar: pUserData: Pointer); stdcall; static;
    procedure OnCommand(sMessage : PAnsiChar);

  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  TryTheEvent;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  SetOnCommandProc(@MyOnCommandProc, Self);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  SetOnCommandProc(nil, nil);
end;

class procedure TForm1.MyOnCommandProc(sMessage: PAnsiChar; pUserData: Pointer); stdcall;
begin
  TForm1(pUserData).OnCommand(sMessage);
end;

procedure TForm1.OnCommand(sMessage: PAnsiChar);
begin
  ShowMessage(sMessage);
end;

end.

答案 1 :(得分:0)

我可以看到您的代码存在许多问题。具体做法是:

  • SetOnCommandProc只有一个参数。你在EXE中用两个声明它。
  • 您在DLL中使用TryTheEvent调用约定实现register,但在EXE中将其声明为stdcall
  • OnEventMessage的实施不必要地复杂,实际上是泄密。
  • EXE代码执行错误的演员TForm1(sMessage)PAnsiChar永远不是对象引用。
  • 使用@运算符获取函数指针。这会抑制类型检查。

这是第一个给你最多问题的点。但你应该解决所有问题。

我会有这样的DLL:

library mydll;

type
  TOnCommandProc = procedure(sMessage: PAnsiChar); stdcall;

var
  FOnCommandProc: TOnCommandProc = nil;

procedure SetOnCommandProc(CallbackProc: TOnCommandProc); stdcall;
begin
  FOnCommandProc := CallbackProc;
end;

procedure OnEventMessage(Data: AnsiString);
begin
  if Assigned(FOnCommandProc) then
    FOnCommandProc(PAnsiChar(Data));
end;

procedure TryTheEvent; stdcall;
begin
  OnEventMessage('Hello');
end;

exports
  SetOnCommandProc, TryTheEvent;

end.

EXE就像这样:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    class procedure MyOnCommandProc(sMessage: PAnsiChar); stdcall; static;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  TOnCommandProc = procedure(sMessage: PAnsiChar); stdcall;

procedure SetOnCommandProc(CallbackProc: TOnCommandProc); stdcall; external 'mydll.dll';
procedure TryTheEvent; stdcall; external 'mydll.dll';

procedure TForm1.Button1Click(Sender: TObject);
begin
  TryTheEvent;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  SetOnCommandProc(MyOnCommandProc);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  SetOnCommandProc(nil);
end;

class procedure TForm1.MyOnCommandProc(sMessage: PAnsiChar);
begin
  ShowMessage(sMessage);
end;

end.

请注意,我已完全删除了实例方法OnCommand。如果确实需要调用实例方法,则需要传递实例和回调函数。因此,您可以将实例引用传递给DLL,键入为Pointer,然后在回调中传回该实例引用。