我遇到了一个奇怪的问题。我的主表单上有一个TTimer,应该触发500毫秒。表单创建后。
当我从IDE运行它时工作正常,但是当我在其他W7 PC上运行它时,主窗体被创建,但计时器不会触发。 (某些组件未更新)如果我单击一个控件,一切都会更新,计时器会触发,一切都很好。如果我移动表单,每个东西都会更新,但计时器不会启动。如果我在安装了Delphi的PC上运行它,它可以正常工作。没问题。
MyForm.OnCreate中的代码执行正常。 Timer.Enabled := True
不做任何改变。
知道是什么原因引起的吗?我真的被困在这里了。
最好的问候。
答案 0 :(得分:5)
有各种各样的可能性:
WM_TIMER消息仅在消息队列为空时传递。如果您的应用程序中的某些内容或在其他计算机上运行的其他应用程序中的某些内容经常将消息发布到窗口句柄,该窗口的消息队列永远不会清空,则WM_TIMER事件将永远不会触发。如果发生这种情况,您可能需要等待正常的TTimer期间的10倍或20倍或30倍,但事件最终可能会发生。到目前为止,我还没有发现计时器根本没有发射的任何情况,但这当然在理论上是可能的......
虽然您说您确定计时器已启用(您已将其设置为启用),但您的代码中的某个ELSE可能会禁用它。
如果你正在做一些尝试......除了......端块并忽略异常,那么可能会发生一些你在其他机器上看不到的坏事。
您的计时器代码可能会被触发,但在计时器上运行的代码中可能会发生异常,崩溃或挂起。
在您的代码中,您可能会遇到一些Delphi事件处理程序,这些处理程序创建了一个几乎“无限循环”的情况,因为您编写的某些事件处理程序在您不希望它们时触发,导致一边效果,让您的应用程序保持忙碌。你提到你在某个地方点击,问题就消失了。该单击可能足以中断代码中的其他恶意循环。
您提到它适用于安装了delphi的任何PC。您使用的第三方控件是否存在一些限制(例如要求您在调试器中运行?)。或者您的应用加载了一些未安装在其他计算机上的DLL或BPL?
从一个没有任何内容的全新应用程序开始。添加TTimer。现在在timer事件上增加一个整数字段值并将该值写入表单的标题。现在在其他机器上运行它。它会正常工作。
现在去看看你编写的大量代码,然后决定如何将你的巨大代码一分为二,找到被破坏的一半。经过足够的步骤,你会发现你的问题。这里没有人可以为你调试它。
尝试添加一些日志消息,使用OutputDebugString
并在其他计算机上运行DebugView,如果要在另一台计算机上查看应用程序的某些内部结构
答案 1 :(得分:0)
我知道一个古老的问题,但是想与您分享这块黄金,虽然古老,但是黄金;-)
您还可以将其转换为线程(由于计时器受限制),我对此做了一个简单的解决方案,称为TimerAsThread
。将代码另存为TimerAsThread.pas。您需要在extctrls之后包括该单元,或将其作为最后一个单元。您不必更改代码,但是现在基于线程,其工作原理完全相同。线程是一个单独的过程,如果您需要更多信息,请在Google上搜索。
玩得开心。
{*******************************************************}
{ }
{ Delphi VCL Extensions (RX) }
{ }
{ Copyright (c) 1996 AO ROSNO }
{ Copyright (c) 1997, 1998 Master-Bank }
{ }
{*******************************************************}
unit TimerAsThread;
interface
uses {$IFDEF WIN32} Windows, {$ELSE} WinTypes, WinProcs, {$ENDIF}
Messages, SysUtils, Classes;
type
{ TTimer }
TTimer = class(TComponent)
private
FEnabled: Boolean;
FInterval: Cardinal;
FOnTimer: TNotifyEvent;
FWindowHandle: HWND;
{$IFDEF WIN32}
FSyncEvent: Boolean;
FThreaded: Boolean;
FTimerThread: TThread;
FThreadPriority: TThreadPriority;
procedure SetThreaded(Value: Boolean);
procedure SetThreadPriority(Value: TThreadPriority);
{$ENDIF}
procedure SetEnabled(Value: Boolean);
procedure SetInterval(Value: Cardinal);
procedure SetOnTimer(Value: TNotifyEvent);
procedure UpdateTimer;
procedure WndProc(var Msg: TMessage);
protected
procedure Timer; dynamic;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
{$IFDEF WIN32}
procedure Synchronize(Method: TThreadMethod);
{$ENDIF}
published
property Enabled: Boolean read FEnabled write SetEnabled default True;
property Interval: Cardinal read FInterval write SetInterval default 1000;
{$IFDEF WIN32}
property SyncEvent: Boolean read FSyncEvent write FSyncEvent default True;
property Threaded: Boolean read FThreaded write SetThreaded default True;
property ThreadPriority: TThreadPriority read FThreadPriority write
SetThreadPriority default tpNormal;
{$ENDIF}
property OnTimer: TNotifyEvent read FOnTimer write SetOnTimer;
end;
implementation
uses Forms, Consts, VCLUtils;
{$IFDEF WIN32}
{ TTimerThread }
type
TTimerThread = class(TThread)
private
FOwner: TTimer;
FInterval: Cardinal;
FException: Exception;
procedure HandleException;
protected
procedure Execute; override;
public
constructor Create(Timer: TTimer; Enabled: Boolean);
end;
constructor TTimerThread.Create(Timer: TTimer; Enabled: Boolean);
begin
FOwner := Timer;
inherited Create(not Enabled);
FInterval := 1000;
FreeOnTerminate := True;
end;
procedure TTimerThread.HandleException;
begin
if not (FException is EAbort) then begin
if Assigned(Application.OnException) then
Application.OnException(Self, FException)
else
Application.ShowException(FException);
end;
end;
procedure TTimerThread.Execute;
function ThreadClosed: Boolean;
begin
if( Application.Terminated ) and ( NOT Terminated ) then
Terminate;
Result := Terminated or Application.Terminated or (FOwner = nil);
end;
begin
repeat
if not ThreadClosed then
if SleepEx(FInterval, False) = 0 then begin
if not ThreadClosed and FOwner.FEnabled then
with FOwner do
if SyncEvent then Synchronize(Timer)
else
try
Timer;
except
on E: Exception do begin
FException := E;
HandleException;
end;
end;
end;
until Terminated;
end;
{$ENDIF}
{ TTimer }
constructor TTimer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FEnabled := True;
FInterval := 1000;
{$IFDEF WIN32}
FSyncEvent := True;
FThreaded := True;
FThreadPriority := tpNormal;
FTimerThread := TTimerThread.Create(Self, False);
{$ELSE}
FWindowHandle := AllocateHWnd(WndProc);
{$ENDIF}
end;
destructor TTimer.Destroy;
begin
Destroying;
FEnabled := False;
FOnTimer := nil;
{$IFDEF WIN32}
{TTimerThread(FTimerThread).FOwner := nil;}
// while FTimerThread.Suspended do FTimerThread.Resume;
FTimerThread.Terminate;
{if not SyncEvent then FTimerThread.WaitFor;}
if FWindowHandle <> 0 then begin
{$ENDIF}
KillTimer(FWindowHandle, 1);
DeallocateHWnd(FWindowHandle);
{$IFDEF WIN32}
end;
{$ENDIF}
inherited Destroy;
end;
procedure TTimer.WndProc(var Msg: TMessage);
begin
with Msg do
if Msg = WM_TIMER then
try
Timer;
except
Application.HandleException(Self);
end
else Result := DefWindowProc(FWindowHandle, Msg, wParam, lParam);
end;
procedure TTimer.UpdateTimer;
begin
{$IFDEF WIN32}
if FThreaded then begin
if FWindowHandle <> 0 then begin
KillTimer(FWindowHandle, 1);
DeallocateHWnd(FWindowHandle);
FWindowHandle := 0;
end;
if not FTimerThread.Suspended then FTimerThread.Suspend;
TTimerThread(FTimerThread).FInterval := FInterval;
if (FInterval <> 0) and FEnabled and Assigned(FOnTimer) then begin
FTimerThread.Priority := FThreadPriority;
while FTimerThread.Suspended do FTimerThread.Resume;
end;
end
else begin
if not FTimerThread.Suspended then FTimerThread.Suspend;
if FWindowHandle = 0 then FWindowHandle := AllocateHWnd(WndProc)
else KillTimer(FWindowHandle, 1);
if (FInterval <> 0) and FEnabled and Assigned(FOnTimer) then
if SetTimer(FWindowHandle, 1, FInterval, nil) = 0 then
raise EOutOfResources.Create(ResStr(SNoTimers));
end;
{$ELSE}
KillTimer(FWindowHandle, 1);
if (FInterval <> 0) and FEnabled and Assigned(FOnTimer) then
if SetTimer(FWindowHandle, 1, FInterval, nil) = 0 then
raise EOutOfResources.Create(ResStr(SNoTimers));
{$ENDIF}
end;
procedure TTimer.SetEnabled(Value: Boolean);
begin
if Value <> FEnabled then begin
FEnabled := Value;
UpdateTimer;
end;
end;
procedure TTimer.SetInterval(Value: Cardinal);
begin
if Value <> FInterval then begin
FInterval := Value;
UpdateTimer;
end;
end;
{$IFDEF WIN32}
procedure TTimer.SetThreaded(Value: Boolean);
begin
if Value <> FThreaded then begin
FThreaded := Value;
UpdateTimer;
end;
end;
procedure TTimer.SetThreadPriority(Value: TThreadPriority);
begin
if Value <> FThreadPriority then begin
FThreadPriority := Value;
if FThreaded then UpdateTimer;
end;
end;
procedure TTimer.Synchronize(Method: TThreadMethod);
begin
if (FTimerThread <> nil) then begin
with TTimerThread(FTimerThread) do begin
if Suspended or Terminated then Method
else TTimerThread(FTimerThread).Synchronize(Method);
end;
end
else Method;
end;
{$ENDIF}
procedure TTimer.SetOnTimer(Value: TNotifyEvent);
begin
if Assigned(FOnTimer) <> Assigned(Value) then begin
FOnTimer := Value;
UpdateTimer;
end else FOnTimer := Value;
end;
procedure TTimer.Timer;
begin
if FEnabled and not (csDestroying in ComponentState) and
Assigned(FOnTimer) then FOnTimer(Self);
end;
end.