我变得疯了。
我是初学者,我想制作我的第一个DLL。 我遵循了这个指南:
http://www.tutorialspoint.com/dll/dll_delphi_example.htm
我想设置有关程序版本的文本信息并在需要时阅读,因此通过主应用程序将其显示给用户。这只是保持对DLL的信心的一个例子,我已经知道还有很多其他方法可以实现这一点。
现在我正试图从这样的DLL中读取变量“versione”:
library Clientdll;
uses SysUtils, Classes, Dialogs;
{$R *.res}
function Versione(var messaggio, versione: String):string; export; stdcall;
begin
versione:='Nessun dato ricavato. Valore di chiamata alla DLL errato!';
if messaggio='chiama' then versione:='v4.0.0 build 31';
end;
exports versione;
begin
end.
在主应用程序中,我写了这个:
[...]
implementation
uses unit2;
{$R *.dfm}
function Versione(var messaggio, versione:string):string; stdcall; external 'Clientdll.dll'
[...]
现在我说'好吧,我只是要调用DLL而这就是......'。所以:
procedure TForm1.Button1Click(Sender: TObject);
var x, y:string;
begin
x:='chiama';
Versione(x,y);
showmessage(y);
end;
我可以在对话框中阅读v4.0.0 build 31,但是当我按下OK时我收到了这个错误:
“指针操作无效”。
有什么想法吗?
我试着去谷歌,但我的英语很差,有些答案很难理解,还有翻译工具!
答案 0 :(得分:6)
请勿使用String
作为参数类型。在使用File->New->Other->Delphi Projects->DLL Wizard
创建新DLL时,IDE生成的注释中清楚地解释了这一点:
{关于DLL内存管理的重要说明: ShareMem必须是 库中的第一个单元的USES子句和您的项目(选择 Project-View Source)USES子句,如果你的DLL导出任何程序或 将字符串作为参数或函数结果传递的函数。这个 适用于传入和传出DLL的所有字符串 - 即使是那些 嵌套在记录和类中。 ShareMem是接口单元 BORLNDMM.DLL共享内存管理器,必须部署 用你的DLL。 要避免使用BORLNDMM.DLL,请传递字符串信息 使用PChar或ShortString参数。}
此外,使用Delphi字符串意味着您的DLL函数无法从其他语言(如C语言)调用。
您还应该期望调用应用程序为您提供放置结果的内存(以及一个长度参数,告诉您内存缓冲区的大小)。
这是一个带有单个函数的Delphi dll的最小(非常无用)示例,以及一个调用它的测试应用程序。 (正如我所说,DLL是没有意义的。任何实际的DLL都应该设计为将功能代码放在它自己的单元而不是项目文件中。)
示例DLL源:
library SimpleTest;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
uses
SysUtils,
Classes;
{$R *.res}
// Parameters:
// arg: Argument that indicates whether this is a test or
// something else, so we know which value to return
// Buffer: The space in which to place the result
// Len: The length of the buffer provided
function TestDLL(const arg: PChar; const Buffer: PChar;
const Len: Integer): Boolean; stdcall;
begin
// Make sure we use the Len parameter, so we don't overflow
// the memory we were given. StrLCopy will copy a maximum of
// Len characters, even if the length of the string provided
// as the 'source' parameter is longer.
if arg = 'Test' then
StrLCopy(Buffer, 'Test result', Len)
else
StrLCopy(Buffer, 'Non-test result', Len);
Result := True;
end;
exports
TestDll;
begin
end.
调用它的测试应用程序的表单:
unit DLLTestForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form4: TForm4;
implementation
{$R *.dfm}
function TestDLL(const arg: PChar; const Buffer: PChar; const Len: Integer): Boolean; stdcall;
external 'SimpleTest.dll';
procedure TForm4.Button1Click(Sender: TObject);
var
Parm1: String;
Parm2: String;
BuffLen: Integer;
begin
Parm1 := 'Test';
// Length of buffer (including null terminator) for DLL call
// Chosen arbitrarily - I know the DLL won't return more than 15 + the
// null. I'm pretending I don't, though, and allowing extra space. The
// DLL won't return more than 30 characters, even if it has more to say,
// because it uses StrLCopy to limit the result to Len characters.
BuffLen := 30;
// Allocate space for return value
SetLength(Parm2, BuffLen);
// Call the DLL with `Test` arg
if TestDLL(PChar(Parm1), PChar(Parm2), BuffLen) then
ShowMessage(Parm2);
// Call the DLL with a different parameter value
Parm1 := 'Other';
if TestDLL(PChar(Parm1), PChar(Parm2), BuffLen) then
ShowMessage(Parm2);
end;
end.