Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/api/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi匿名函数和SetTimer API抛出;特权指令“;例外_Api_Delphi_Exception_Methods_Anonymous - Fatal编程技术网

Delphi匿名函数和SetTimer API抛出;特权指令“;例外

Delphi匿名函数和SetTimer API抛出;特权指令“;例外,api,delphi,exception,methods,anonymous,Api,Delphi,Exception,Methods,Anonymous,我在用Delphi中的一些匿名方法 当调用Execute方法和在该方法内创建的计时器超时时,它抛出一个“特权指令”异常 这是因为我的匿名函数超出了范围吗 unit OneShotTimerReloaded; interface uses System.SysUtils, System.Classes; type IOneShotTimerReloaded = interface ['{51DE72F0-4784-4CEB-A065-0B64D6EEA626}']

我在用Delphi中的一些匿名方法

当调用Execute方法和在该方法内创建的计时器超时时,它抛出一个“特权指令”异常

这是因为我的匿名函数超出了范围吗

unit OneShotTimerReloaded;

interface

uses
  System.SysUtils, System.Classes;

type
  IOneShotTimerReloaded = interface
    ['{51DE72F0-4784-4CEB-A065-0B64D6EEA626}']

    procedure Execute(Proc: TProc; TimeOut: Cardinal = 1000); overload;
    procedure Execute(Proc: TProcedure; TimeOut: Cardinal = 1000); overload;
    procedure Execute(Event: TNotifyEvent; TimeOut: Cardinal = 1000; Sender: TObject = nil); overload;
  end;

  TOneShotTimerReloaded = class(TInterfacedObject, IOneShotTimerReloaded)
  public
    procedure Execute(Proc: TProc; TimeOut: Cardinal = 1000); overload;
    procedure Execute(Proc: TProcedure; TimeOut: Cardinal = 1000); overload;
    procedure Execute(Event: TNotifyEvent; TimeOut: Cardinal = 1000; Sender: TObject = nil); overload;
  end;

implementation

uses
  Winapi.Windows;

{ TOneShotTimerReloaded }

procedure TOneShotTimerReloaded.Execute(Proc: TProc; TimeOut: Cardinal);
var
  TimerID: UIntPtr;

begin
  TimerID := SetTimer(HWND(0), 0, TimeOut, @procedure
    begin
      if (Assigned(Proc)) then
        Proc;

      KillTimer(HWND(0), TimerID);
    end
  );
end;

procedure TOneShotTimerReloaded.Execute(Proc: TProcedure; TimeOut: Cardinal);
var
  TimerID: UIntPtr;

begin
  TimerID := SetTimer(HWND(0), 0, TimeOut, @procedure
    begin
      if (Assigned(Proc)) then
        Proc;

      KillTimer(HWND(0), TimerID);
    end
  );
end;

procedure TOneShotTimerReloaded.Execute(Event: TNotifyEvent; TimeOut: Cardinal; Sender: TObject);
var
  TimerID: UIntPtr;

begin
  TimerID := SetTimer(HWND(0), 0, TimeOut, @procedure
    begin
      if (Assigned(Event)) then
        Event(Sender);

      KillTimer(HWND(0), TimerID);
    end
  );
end;

end.
我目前使用此类的方式是:

procedure TForm1.FormCreate(Sender: TObject);
var
  t1: TOneShotTimerReloaded;
  t2: TOneShotTimerReloaded;

begin
  t1 := TOneShotTimerReloaded.Create;
  t2 := TOneShotTimerReloaded.Create;

  t1.Execute(btn1Click, 5000, btn1);
  t2.Execute(procedure begin ShowMessage('Anonymous'); end, 2000);

  // Not worried with t1 and t2 memory leaks yet!!! ;)
end;

如有任何意见或建议,我们将不胜感激。谢谢

您不能对Win32 API回调使用匿名过程,正如您不能使用非静态类方法(不写代理存根)或本地内部函数(无论如何都不安全)一样。匿名过程实现为编译器生成的引用计数接口,该接口具有一个隐藏的
Invoke()
方法,该方法在调用过程时执行。与
SetTimer()
(或任何其他API)预期回调的签名不匹配

您的代码基本上(但不完全)在幕后执行以下操作:

type
  TOneShotTimerReloaded_Execute_AnonProc = interface(IInterface)
    procedure Invoke;
  end;

  TOneShotTimerReloaded_Execute_AnonProc_Impl = class(TInterfacedObject, TOneShotTimerReloaded_Execute_AnonProc)
  public
    Captured_Proc: ^TProc;
    Captured_TimerID: ^UIntPtr;
    procedure Invoke;
  end;

procedure TOneShotTimerReloaded_Execute_AnonProc_Impl.Invoke;
begin
  if (Assigned(Captured_Proc^)) then
    Captured_Proc^();

  KillTimer(HWND(0), Captured_TimerID^);
end

procedure TOneShotTimerReloaded.Execute(Proc: TProcedure; TimeOut: Cardinal);
var
  TimerID: UIntPtr;
  AnonProc: TOneShotTimerReloaded_Execute_AnonProc;
begin
  AnonProc := TOneShotTimerReloaded_Execute_AnonProc_Impl.Create;
  AnonProc.Captured_Proc := @Proc;
  AnonProc.Captured_TimerID := @TimerID;
  TimerID := SetTimer(HWND(0), 0, TimeOut, @AnonProc);
end;
明白为什么它不可能工作了吗

即使可能,您的匿名过程也会丢失
SetTimer()
传递给其回调的输入参数,以及
stdcall
调用约定,因此无论如何您都会管理不好调用堆栈

您使用的
@
地址运算符对您隐藏了编译器错误。摆脱
@
并让编译器失败。这应该是你第一次表明你做错了什么


要执行您正在尝试的操作,您必须创建一个动态代理存根(类似于
Classes.MakeObjectInstance()
所做的),以便
SetTimer()
可以直接调用
Proc
处理程序。匿名过程对此没有帮助。

首先,您将过程的地址作为计时器的回调传递(使用
@
),调用回调时,该地址后面没有代码。我不知道有什么方法可以将匿名方法用作Windows回调函数。您的过程也不符合
SetTimer
所要求的定义。这就是我最初的实现存储方法的原因。