我需要编写一个DLL,但这是我第一次(总有一个),我找到了解决这个文档的解决方案。我最终得到了这段代码:
library DLLFrazioni;
uses
System.SysUtils,
System.Classes,
Fractions in 'Fractions.pas';
{$R *.res}
function getFraction(a: integer; b: integer): PChar; stdcall; overload;
var f: TFraction;
begin
f := TFraction.Create(a, b);
try
Result := PChar(f.getFraction);
except
Result := PChar('NaN');
end;
end;
function getFraction(a: PChar): PChar; stdcall; overload;
var f: TFraction;
begin
f := TFraction.Create(a);
try
Result := PChar(f.getFraction);
except
Result := PChar('NaN');
end;
end;
exports
getFraction(a: integer; b: integer),
getFraction(a: Pchar);
begin
end.
Fraction.pas中有一个名为TFraction
的类,它具有此实现(如果需要):
type
TFraction = class
private
number: double;
num, den: integer;
fraction: string;
function hcf(x: integer; y: integer): integer;
public
//input num+den -> result is a fraction num/den
constructor Create(numerator: integer; denominator: integer); overload;
//input string 'num/den' -> result is a reduced num/den
constructor Create(value: PChar); overload;
function getFraction: string;
end;
这里的一切都很简单。
我必须能够使用Delphi和C ++(Visual Studio)加载这个dll,但我怀疑我还没有用谷歌解决。正如你所看到的,我已经声明了另一个包含该类的单元,所以我可以将这两个东西分开。
我在delphi DLL中像往常一样使用stdcall。我有以下问题:
f: TFraction
)因为我需要从getFraction
获得返回结果。我是否必须使用通常的try-finally语句来包围它?我认为try-except更合适,因为我想在运行时避免异常。答案 0 :(得分:5)
except
块与finally
块的用途完全不同。这绝不是选择一个而不是另一个的问题。使用满足需求的任何一种。如果你需要两者,那么使用两者。 except
块用于处理错误。 finally
块用于保护资源。
您对except
阻止的使用是正确的,可以处理getFraction
失败的情况。
您应该包含finally
块以保护您分配的资源,即TFraction
对象。你现在根本没有释放对象,所以你有内存泄漏。
不允许异常转义DLL函数。你不能假设调用者知道如何处理抛出的Delphi对象。
编写DLL是一种情况,它对您有编写C和使用Windows API的经验非常有帮助。如果您编写的DLL遵循您在Windows API中看到的相同模式,那么您就是一个良好的基础。您会注意到Windows API函数从不引发异常。它们总是返回一个状态值,可能带有错误代码。
您的代码中还有其他问题。特别是,当你的DLL函数终止时,你将返回一个指向一个字符串的指针,因此指针是陈旧的。再次,遵循Windows API的模型是有帮助的。 API几乎从不返回字符串。相反,API函数接收缓冲区指针和长度,然后填充调用者提供的缓冲区。 返回字符串的API通常会使用文档化的API分配内存,然后指定调用者稍后应使用哪种内存管理函数来释放内存。
在您的情况下,您将返回指向您无法管理的内存的指针。编译器为您管理它,并且编译器无法看到您的函数调用者仍然想要使用该内存,因此编译器会插入代码以释放您的字符串。
答案 1 :(得分:0)
你的代码编译并运行,但Rob说你应该用结果填充一个缓冲区。一般来说,当你处理DLL时(比如你的情况),它们必须加载不同的语言,所以如果你返回整数或双精度等简单类型会更好。
这段代码会给你一个想法:
//STATUS: 0 = ok | 1 = error
function getFraction(a, b: integer; Buffer: PChar): integer; stdcall;
var f: TFraction;
l: PChar;
begin
f := TFraction.Create(a, b);
try
l := PChar(f.getFraction);
try
StrLCopy(Buffer, l, SizeOf(l));
Result := 0; //ok
except
//something to report an error
Result := 1;
end;
finally
f.Free;
end;
end;
这里我返回一个整数(但也可能是一个好的bool),表示操作成功。在这种情况下,您会看到try ... except
很有用。
结果存储在缓冲区中,我使用了适当的函数StrLCopy。在这里,我使用SizeOf
运算符来获取MaxLen的值。而不是这样,您可以在名为bufLen
的函数中使用参数。
function getFraction(a, b: integer; Buffer: PChar; bufLen: integer): integer; stdcall;
var f: TFraction;
l: PChar;
begin
f := TFraction.Create(a, b);
try
l := PChar(f.getFraction);
try
StrLCopy(Buffer, l, bufLen);
Result := 0; //ok
except
//something to report an error
Result := 1;
end;
finally
f.Free;
end;
end;
在第二个版本中,您可以获得更多控制权,并且可以确定缓冲区的维度。请注意,StrLCopy
仅使用bufLen指示的空间量。如果l: PChar
的大小大于bufLen,则不会复制超出的部分。在主程序中,您可以使用以下功能:
SetLength(A, 10);
k := getFraction(10,25, PChar(A));
if k = 0 then
Writeln(a)
else
Writeln('fail');
此代码是控制台应用程序,A是string
。您必须声明一个字符串变量并将其转换为PChar,因为SetLength
接受通过引用(var
关键字)传递的第一个参数字符串或动态数组。