Function delphi检查是否使用了函数结果值

Function delphi检查是否使用了函数结果值,function,delphi,return-value,Function,Delphi,Return Value,我的问题和你的一样,只是语言不同。我正在使用Delphi 按照您将在Java中看到的链接,这似乎是不可能的,因此我假设这也适用于Delphi,但我不能完全确定 我想知道,函数是否可以检查调用过程/函数是否使用函数提供的返回值。 我尝试过这个方法,但没有成功:(我想如果存在一个解决方案,它的行为会与此类似) 检查似乎没有用,但我的函数生成了一个加密文件,解密此文件时需要返回值,因此最好通知用户,而不仅仅是生成一个无用的文件,因此我希望检查返回值的使用情况 可能的解决办法: 我有一个想法,但要么不知

我的问题和你的一样,只是语言不同。我正在使用Delphi

按照您将在Java中看到的链接,这似乎是不可能的,因此我假设这也适用于Delphi,但我不能完全确定

我想知道,函数是否可以检查调用过程/函数是否使用函数提供的返回值。 我尝试过这个方法,但没有成功:(我想如果存在一个解决方案,它的行为会与此类似)

检查似乎没有用,但我的函数生成了一个加密文件,解密此文件时需要返回值,因此最好通知用户,而不仅仅是生成一个无用的文件,因此我希望检查返回值的使用情况

可能的解决办法: 我有一个想法,但要么不知道这是否可行,要么不知道如何实现:该函数可以扫描被我的应用程序占用的RAM部分,并在命令
Result:='类似于F8975BE8AC0192#2983FE4A#B9DFE25'
触发后检查返回值(字符串)是否存在。字符串不太可能存在两次,这个简单的检查完全符合我的目的。 一般来说,这可能不起作用,因为结果可能存在两次,但在我的情况下,这是不可能的

为此,我扩展了我的原始问题: 如何在包含该函数的应用程序占用的RAM部分中搜索字符串,该应用程序希望检查该字符串?
据我所知,要检查外部应用程序的这一点要困难得多,这在这里是不需要的。

所以我想你要问的是,你是否可以从一个函数返回两段数据,加密字符串和解密密钥。 您可以声明一个包含两个项的类并返回其实例,也可以将它们作为参数接口的一部分传递回去

procedure cruncher(incoming:string;var encrypted:string;var decryptkey:string)

就个人风格而言,我反对通过函数结果返回一半答案。

在Delphi中,您试图做的是不可能的。这不是一个“语言问题”。但这也不是问题

你真的在背对背地考虑这个问题。我会尽力解释原因;希望你能放弃这愚蠢的行为

总结
  • 函数只能做一件事。一个函数尝试做的事情越多:正确实现它就越困难,其他函数重用起来就越困难
  • 任何你可能想到的黑客都可以轻易绕过。因此,您最终会遇到更复杂的问题和相同的问题
  • 确保调用方正确使用函数不是您的责任。这就是疯狂和不断成长的兔子窝
  • 您应该只根据“合同”实现您的功能,正确使用它是调用方的责任。若调用者并没有正确地使用它,那个么调用者就有一个bug,直到调用代码被修复为止。就这么简单

只做一件事 当函数做不止一件事时,就不可能(不经过仔细的重构)使用一个特性而不使用另一个特性。该函数更难测试,因为:调用过程中更改的内容越多,需要进行的设置越多,需要检查的内容越多,可能需要考虑的排列也越多

Javascript问题中的情况甚至更糟,因为这两件事有根本的区别:

  • 函数样式版本可以保证不改变状态
  • 而另一种样式用于修改状态
这是一个可变与不可变的概念。知道什么时候保证了不变性是非常有益的。将两者捆绑到一个函数中意味着您不再具有不可变的版本

破解“解决方案”是毫无意义的 让我们假设您发现一些黑客在函数本身之外查询调用堆栈,以确认调用方将函数结果存储到变量以供以后使用

让我们把不同操作系统、CPU体系结构、编译器优化等问题放在一边

不阻止调用者执行以下操作:

S := HybridFunction(...);
if (S <> '') then S := '';
这里已经有问题了,因为你的函数显然在做两件事。将其拆分为2个函数,调用方也将更容易编写

function GetKey(): TKey;
procedure EncryptFile(AKey: TKey);
然后,调用方可以在尝试加密之前验证:

LKey := GetKey();
if Assigned(LKey) then 
  EncryptFile(LKey)
else { report the error as you choose }

这就是为什么我说你要背对背地讨论这个问题。

我相信你的问题可能源于实现某种绝对防弹的东西的愿望,或者源于一种使事情变得比实际更困难的设计选择

防弹实施 如果您作为一名开发人员做得很好,那么函数命名和签名将向调用者指示预期用途。他是否理解这一点,并最终正确使用它,很可能超出你的控制,正如评论所指出的:你在哪里停下来?可能总会有办法挫败您的实现

可以而且应该做任何事情吗?

但是考虑到您问题的技术方面,以及无法检查是否使用函数结果完成了某些操作(因为函数代码已经存在),我的想法是使结果本身“更智能”

您提到您的加密密钥是一个字符串,您可以称之为“unique”。因此,可以使用Delphi的字符串引用计数(至少对于除
WideString
之外的字符串)来确保它仍然在某个地方被引用。与同样是引用计数的
IInterfacedObject
相结合,
TInterfaceObject
可以检查它是否比监视的字符串寿命长

接口

  IRefCheck = interface
  ['{E6A54A33-E4DB-4486-935A-00549E6793AC}']
    function Value: string;
  end;
TInterfacedObject实施

  TRefCheck = class(TInterfacedObject, IRefCheck)
  strict private
    FString: string;
  public
    constructor Create(const S: string);
    destructor Destroy; override;
    function Value: string;
  end;
constructor TRefCheck.Create(const S: string);
begin
  FString := S;
end;

destructor TRefCheck.Destroy;
begin
  Assert(StringRefCount(FString) > 1, 'Reference check failed for ' + FString);
  inherited Destroy;
end;

function TRefCheck.Value: string;
begin
  Result := FString;
end;
TRefCheck = class(TInterfacedObject, IRefCheck) strict private FString: string; public constructor Create(const S: string); destructor Destroy; override; function Value: string; end;
constructor TRefCheck.Create(const S: string);
begin
  FString := S;
end;

destructor TRefCheck.Destroy;
begin
  Assert(StringRefCount(FString) > 1, 'Reference check failed for ' + FString);
  inherited Destroy;
end;

function TRefCheck.Value: string;
begin
  Result := FString;
end;
function GetEncryptedFileKey(Key: string): IRefCheck;
begin
  Result := TRefCheck.Create(Key);
end;
procedure Test;
var
  S: string;
begin
  S := GetEncryptedFileKey('Test Value 1').Value;
  GetEncryptedFileKey('Test Value 2');
end;
  TRefCheckRec = record
  private
    RefCheck: IRefCheck;
    function AsString: string;
  public
    class operator Implicit(Value: TRefCheckRec): string;
    class function CreateNew(S: string): TRefCheckRec; static;
  end;

function TRefCheckRec.AsString: string;
begin
  RefCheck.Value;
end;

class function TRefCheckRec.CreateNew(S: string): TRefCheckRec;
begin
  Result.RefCheck := TRefCheck.Create(S);
end;

class operator TRefCheckRec.Implicit(Value: TRefCheckRec): string;
begin
  Result := Value.AsString;
end;
function GetEncryptedFileKey(Key: string): TRefCheckRec;
begin
  Result := TRefCheckRec.CreateNew(Key);
end;
procedure Test2;
var
  S: string;
begin
  S := GetEncryptedFileKey('Test Value Record');
  GetEncryptedFileKey('Test Value Record 2');
end;
function GetEncryptedFileKey: string;
procedure EncryptFile(var EncryptionKey: string);
  TEncryptedFile = class
  private
    FEncryptionKey: string;
    FKeyPersisted: Boolean;
  public
    constructor Create;
    destructor Destroy; override;
    function GetEncryptionKey: string;
    procedure SaveEncryptionKeyToFile;
  end;

constructor TEncryptedFile.Create;
begin
  FKeyPersisted := False;
  FEncryptionKey := ...
end;

destructor TEncryptedFile.Destroy;
begin
  if not FKeyPersisted then
    SaveEncryptionKeyToFile;
  inherited Destroy;
end;

function TEncryptedFile.GetEncryptionKey: string;
begin
  Result := FEncryptionKey;
  FKeyPersisted := True;
end;

procedure TEncryptedFile.SaveEncryptionKeyToFile;
begin
  FKeyPersisted := True;
  // Save to file
  ...
end;
  function CreateEncryptedFile: TEncryptedFile;
  begin
    Result := TEncryptedFile.Create;
  end;