繁体   English   中英

Delphi DLL类异常

[英]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。 我有以下问题:

  1. 我必须创建一个对象( f: TFraction ),因为我需要从getFraction获取返回结果。 我是否必须用通常的try-finally语句括起来? 我认为try-except更合适,因为我想避免运行时出现异常。
  2. 如果删除了try-except,当然会发生异常。 在这种情况下,当我从Delphi / C ++程序中调用该函数时,便可以处理它。 但这安全吗? 我可以允许dll引发异常吗?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM