我有一个用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.
答案 0 :(得分:3)
在D2007及更早版本中,String
是AnsiString
的别名,PChar
是PAnsiChar
的别名。
在D2009及更高版本中,String
是UnicodeString
的别名,PChar
是PWideChar
的别名。
如果您希望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中用两个声明它。TryTheEvent
调用约定实现register
,但在EXE中将其声明为stdcall
。OnEventMessage
的实施不必要地复杂,实际上是泄密。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
,然后在回调中传回该实例引用。