[英]Delphi DLL class exception
我需要编写一个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,但是我怀疑我还没有用google解决。 如您所见,我已经声明了另一个包含该类的单元,因此我可以将这两件事分开。
我像往常一样在delphi DLL中使用stdcall。 我有以下问题:
f: TFraction
),因为我需要从getFraction
获取返回结果。 我是否必须用通常的try-finally语句括起来? 我认为try-except更合适,因为我想避免运行时出现异常。 “ 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来分配内存,然后指定调用者稍后应使用哪种内存管理功能来释放内存。
在您的情况下,您将返回一个指向您不管理的内存的指针。 编译器为您管理它,而编译器看不到函数的调用者仍想使用该内存,因此编译器插入代码以释放您的字符串。
您的代码可以编译并运行,但是正如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;
在这里,我返回一个表明操作成功的整数(但布尔值可能还不错)。 在这种情况下,您会发现使用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
关键字)传递的第一个参数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.