Delphi匿名函数和SetTimer API抛出;特权指令“;例外
我在用Delphi中的一些匿名方法 当调用Execute方法和在该方法内创建的计时器超时时,它抛出一个“特权指令”异常 这是因为我的匿名函数超出了范围吗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}']
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
所要求的定义。这就是我最初的实现存储方法的原因。