由于长度限制,所以分开发了ControlsScroller Part1unit ControlsScroller;interfaceuses
SysUtils, Classes, Controls, ExtCtrls, DateUtils, Windows, SyncObjs;type
ScrollOrientation = (Horizontal, Vertica);type
RegisterInfo = record
control : TControl;
width : Integer;
height : Integer;
scroll : Boolean;
pageRollComplete : Boolean;
rollingCount : Integer;
lastCompleteTime : TDateTime;
end;const MAXCONTROLS = 50;type
TControlsScroller = class(TComponent)
private
m_controlsCount : Integer; //注册过的控件数量
m_registerInfos : array [0 .. 49] of RegisterInfo; //控件信息
m_Speed : Cardinal; //滚动速度
m_ResidenceTime : Cardinal; //停留时间
m_ScrollOrientation : ScrollOrientation; //滚动方向 //m_CurrentPast : Boolean; //当前滚动方向:过去、回滚
m_CSRegisterInfos: TCriticalSection ; //临界区
m_Enable : Boolean; //是否滚动 m_Thread : THANDLE;
m_EixtThread : Boolean; //是否退出线程
m_Tid: DWord;
protected
procedure ScrollingControls();
procedure HorizontalScrolling();
procedure VerticalScrolling(); procedure SetSpeed(value:Cardinal);
procedure SetResidenceTime(value:Cardinal); procedure SetEnable(value:Boolean);
public
constructor Create(AOwner:TComponent);override;
destructor destroy();override; function Registration(control:TControl; width:Integer; height:Integer; scroll:Boolean):Boolean;
procedure ResetRegisterInfo(control:TControl; width:Integer; height:Integer; scroll:Boolean);
procedure Cancellation(control:TControl);
published
property Speed : Cardinal read m_Speed write SetSpeed;
property ResidenceTime:Cardinal read m_ResidenceTime write SetResidenceTime;
property Enable:Boolean read m_Enable write SetEnable;
property Orientation : ScrollOrientation read m_ScrollOrientation write m_ScrollOrientation;
end;procedure TheardFunc(Param: TControlsScroller);stdcall;procedure Register;implementationconstructor TControlsScroller.Create(AOwner:TComponent);
begin
inherited Create(AOwner); m_Speed := 2;
m_ResidenceTime := 5;
m_controlsCount := 0;
m_Enable := True;
m_ScrollOrientation := Horizontal; m_CSRegisterInfos := TCriticalSection.Create;
m_Thread := CreateThread(nil, 0, @TheardFunc, Self, 0, m_Tid);end;
destructor TControlsScroller.destroy();
begin
m_EixtThread := true;
WaitForSingleObject(m_Thread, 300); m_CSRegisterInfos.Destroy; inherited destroy;
end;
function TControlsScroller.Registration(control:TControl; width:Integer; height:Integer; scroll:Boolean):Boolean;
var
i : Integer;
begin
Result := false;
for i := 0 to MAXCONTROLS - 1 do
begin
if m_registerInfos[i].control = nil then
begin
m_registerInfos[m_controlsCount].control := control;
m_registerInfos[m_controlsCount].width := width;
m_registerInfos[m_controlsCount].height := height;
m_registerInfos[m_controlsCount].scroll := scroll;
m_registerInfos[m_controlsCount].pageRollComplete := true;
m_registerInfos[m_controlsCount].rollingCount := 0;
m_registerInfos[m_controlsCount].lastCompleteTime := Now();
m_controlsCount := m_controlsCount + 1;
Result:= True;
break;
end;
end;
end;
SysUtils, Classes, Controls, ExtCtrls, DateUtils, Windows, SyncObjs;type
ScrollOrientation = (Horizontal, Vertica);type
RegisterInfo = record
control : TControl;
width : Integer;
height : Integer;
scroll : Boolean;
pageRollComplete : Boolean;
rollingCount : Integer;
lastCompleteTime : TDateTime;
end;const MAXCONTROLS = 50;type
TControlsScroller = class(TComponent)
private
m_controlsCount : Integer; //注册过的控件数量
m_registerInfos : array [0 .. 49] of RegisterInfo; //控件信息
m_Speed : Cardinal; //滚动速度
m_ResidenceTime : Cardinal; //停留时间
m_ScrollOrientation : ScrollOrientation; //滚动方向 //m_CurrentPast : Boolean; //当前滚动方向:过去、回滚
m_CSRegisterInfos: TCriticalSection ; //临界区
m_Enable : Boolean; //是否滚动 m_Thread : THANDLE;
m_EixtThread : Boolean; //是否退出线程
m_Tid: DWord;
protected
procedure ScrollingControls();
procedure HorizontalScrolling();
procedure VerticalScrolling(); procedure SetSpeed(value:Cardinal);
procedure SetResidenceTime(value:Cardinal); procedure SetEnable(value:Boolean);
public
constructor Create(AOwner:TComponent);override;
destructor destroy();override; function Registration(control:TControl; width:Integer; height:Integer; scroll:Boolean):Boolean;
procedure ResetRegisterInfo(control:TControl; width:Integer; height:Integer; scroll:Boolean);
procedure Cancellation(control:TControl);
published
property Speed : Cardinal read m_Speed write SetSpeed;
property ResidenceTime:Cardinal read m_ResidenceTime write SetResidenceTime;
property Enable:Boolean read m_Enable write SetEnable;
property Orientation : ScrollOrientation read m_ScrollOrientation write m_ScrollOrientation;
end;procedure TheardFunc(Param: TControlsScroller);stdcall;procedure Register;implementationconstructor TControlsScroller.Create(AOwner:TComponent);
begin
inherited Create(AOwner); m_Speed := 2;
m_ResidenceTime := 5;
m_controlsCount := 0;
m_Enable := True;
m_ScrollOrientation := Horizontal; m_CSRegisterInfos := TCriticalSection.Create;
m_Thread := CreateThread(nil, 0, @TheardFunc, Self, 0, m_Tid);end;
destructor TControlsScroller.destroy();
begin
m_EixtThread := true;
WaitForSingleObject(m_Thread, 300); m_CSRegisterInfos.Destroy; inherited destroy;
end;
function TControlsScroller.Registration(control:TControl; width:Integer; height:Integer; scroll:Boolean):Boolean;
var
i : Integer;
begin
Result := false;
for i := 0 to MAXCONTROLS - 1 do
begin
if m_registerInfos[i].control = nil then
begin
m_registerInfos[m_controlsCount].control := control;
m_registerInfos[m_controlsCount].width := width;
m_registerInfos[m_controlsCount].height := height;
m_registerInfos[m_controlsCount].scroll := scroll;
m_registerInfos[m_controlsCount].pageRollComplete := true;
m_registerInfos[m_controlsCount].rollingCount := 0;
m_registerInfos[m_controlsCount].lastCompleteTime := Now();
m_controlsCount := m_controlsCount + 1;
Result:= True;
break;
end;
end;
end;
begin
m_Speed := value;
end;procedure TControlsScroller.SetResidenceTime(value:Cardinal);
begin
m_ResidenceTime:= value;
end;procedure TControlsScroller.ScrollingControls();
begin
if m_ScrollOrientation = Horizontal then
HorizontalScrolling()
else
VerticalScrolling();
end;procedure TControlsScroller.HorizontalScrolling();
var
i : Integer;
tmpPageComplete : Boolean;
tmpDirectionComplete : Boolean;
begin
for i := 0 to m_controlsCount -1 do
begin
if not ( m_registerInfos[i].control = nil) and (m_registerInfos[i].scroll) then
begin
if (not m_registerInfos[i].pageRollComplete)
or (SecondsBetween(now(), m_registerInfos[i].lastCompleteTime) >= m_ResidenceTime) then
begin
tmpPageComplete := true;
tmpDirectionComplete := true;
//当前方向没有滚动完
if m_registerInfos[i].control.Left > (-m_registerInfos[i].control.Width) then
begin
tmpDirectionComplete := False;
//当前页没有滚动完
if m_registerInfos[i].control.Left > -(m_registerInfos[i].rollingCount + 1) * m_registerInfos[i].width then
begin
tmpPageComplete := False; try
m_CSRegisterInfos.Enter; //进入临界区
if not (m_registerInfos[i].control = nil) then
m_registerInfos[i].control.Left := m_registerInfos[i].control.Left - m_Speed; //关闭窗口会偶尔出问题
m_CSRegisterInfos.Leave; //离开临界区
except
end;
end;
end; if tmpPageComplete = True then
begin
m_registerInfos[i].lastCompleteTime := Now();
m_registerInfos[i].rollingCount := m_registerInfos[i].rollingCount + 1;
end;
if tmpDirectionComplete = True then
begin
m_registerInfos[i].rollingCount := -1;
m_registerInfos[i].control.Left := m_registerInfos[i].width + 5;
end; m_registerInfos[i].pageRollComplete := tmpPageComplete; end;
end; end;
end;procedure TControlsScroller.VerticalScrolling();
var
i : Integer;
tmpPageComplete : Boolean;
tmpDirectionComplete : Boolean;
begin
for i := 0 to m_controlsCount -1 do
begin
if not ( m_registerInfos[i].control = nil) and (m_registerInfos[i].scroll) then
begin
if (not m_registerInfos[i].pageRollComplete)
or (SecondsBetween(now(), m_registerInfos[i].lastCompleteTime) >= m_ResidenceTime) then
begin
tmpPageComplete := true;
tmpDirectionComplete := true;
//当前方向没有滚动完
if m_registerInfos[i].control.Top > (-m_registerInfos[i].control.Height) then
begin
tmpDirectionComplete := False;
//当前页没有滚动完
if m_registerInfos[i].control.Top > -(m_registerInfos[i].rollingCount + 1) * m_registerInfos[i].Height then
begin
tmpPageComplete := False; try
m_CSRegisterInfos.Enter; //进入临界区
if not (m_registerInfos[i].control = nil) then
m_registerInfos[i].control.Top := m_registerInfos[i].control.Top - m_Speed; //关闭窗口会偶尔出问题
m_CSRegisterInfos.Leave; //离开临界区
except
end;
end;
end; if tmpPageComplete = True then
begin
m_registerInfos[i].lastCompleteTime := Now();
m_registerInfos[i].rollingCount := m_registerInfos[i].rollingCount + 1;
end;
if tmpDirectionComplete = True then
begin
m_registerInfos[i].rollingCount := -1;
m_registerInfos[i].control.Top := m_registerInfos[i].Height + 5;
end; m_registerInfos[i].pageRollComplete := tmpPageComplete; end;
end; end;
end;procedure TControlsScroller.ResetRegisterInfo(control:TControl; width:Integer; height:Integer; scroll:Boolean);
var i : Integer;
begin
for i := 0 to MAXCONTROLS -1 do
begin
if m_registerInfos[i].control = control then
begin
m_registerInfos[i].width := width;
m_registerInfos[i].height := height;
m_registerInfos[i].scroll := scroll;
break;
end;
end;
end;procedure TControlsScroller.SetEnable(value:Boolean);
begin
m_Enable := value;
end;procedure TControlsScroller.Cancellation(control:TControl);
var i:Integer;
begin
for i := 0 to MAXCONTROLS -1 do
begin
if m_registerInfos[i].control = control then
begin
m_CSRegisterInfos.Enter; //进入临界区
m_registerInfos[i].control := nil;
m_CSRegisterInfos.Leave; //离开临界区
Break;
end; end;
end;procedure TheardFunc(Param: TControlsScroller);stdcall;
var
ControlsScroller : TControlsScroller;
startTime, currentTime, frequency, lastRefreshTime, tick: Int64;
resolution : Double;
begin
ControlsScroller := Param; QueryPerformanceFrequency(frequency);
QueryPerformanceCounter(startTime);
resolution := 1.0/frequency;
lastRefreshTime := 0; while not ControlsScroller.m_EixtThread do
begin
try
QueryPerformanceCounter(tick);
currentTime := Trunc(( tick - startTime) * resolution * 1000.0);
if (ControlsScroller.Enable) and (currentTime - lastRefreshTime >= 16) then
begin
ControlsScroller.ScrollingControls(); QueryPerformanceCounter(tick);
lastRefreshTime := Trunc(( tick - startTime) * resolution * 1000.0);
end;
except end;
Sleep(2);
end;
end;procedure Register;
begin
RegisterComponents('Standard', [TControlsScroller]);
end;end.
constructor TControlsScroller.Create(AOwner:TComponent);
begin
inherited Create(AOwner); m_Speed := 2;
m_ResidenceTime := 5;
m_Enable := True;
m_ScrollOrientation := Horizontal; m_CSRegisterInfos := TCriticalSection.Create;
m_Thread := CreateThread(nil, 0, @TheardFunc, Self, 0, m_Tid);end;
function TControlsScroller.Registration(control:TControl; width:Integer; height:Integer; scroll:Boolean):Boolean;
var
i : Integer;
begin
Result := false;
for i := 0 to MAXCONTROLS - 1 do
begin
if m_registerInfos[i].control = nil then
begin
m_registerInfos[i].control := control;
m_registerInfos[i].width := width;
m_registerInfos[i].height := height;
m_registerInfos[i].scroll := scroll;
m_registerInfos[i].pageRollComplete := true;
m_registerInfos[i].rollingCount := 0;
m_registerInfos[i].lastCompleteTime := Now();
Result:= True;
break;
end;
end;
end;
procedure TControlsScroller.HorizontalScrolling();
var
i : Integer;
tmpPageComplete : Boolean;
tmpDirectionComplete : Boolean;
begin
for i := 0 to MAXCONTROLS -1 do
begin
if not ( m_registerInfos[i].control = nil) and (m_registerInfos[i].scroll) then
begin
if (not m_registerInfos[i].pageRollComplete)
or (SecondsBetween(now(), m_registerInfos[i].lastCompleteTime) >= m_ResidenceTime) then
begin
tmpPageComplete := true;
tmpDirectionComplete := true;
//当前方向没有滚动完
if m_registerInfos[i].control.Left > (-m_registerInfos[i].control.Width) then
begin
tmpDirectionComplete := False;
//当前页没有滚动完
if m_registerInfos[i].control.Left > -(m_registerInfos[i].rollingCount + 1) * m_registerInfos[i].width then
begin
tmpPageComplete := False; try
m_CSRegisterInfos.Enter; //进入临界区
if not (m_registerInfos[i].control = nil) then
m_registerInfos[i].control.Left := m_registerInfos[i].control.Left - m_Speed; //关闭窗口会偶尔出问题
m_CSRegisterInfos.Leave; //离开临界区
except
end;
end;
end; if tmpPageComplete = True then
begin
m_registerInfos[i].lastCompleteTime := Now();
m_registerInfos[i].rollingCount := m_registerInfos[i].rollingCount + 1;
end;
if tmpDirectionComplete = True then
begin
m_registerInfos[i].rollingCount := -1;
m_registerInfos[i].control.Left := m_registerInfos[i].width + 5;
end; m_registerInfos[i].pageRollComplete := tmpPageComplete; end;
end; end;
end;procedure TControlsScroller.VerticalScrolling();
var
i : Integer;
tmpPageComplete : Boolean;
tmpDirectionComplete : Boolean;
begin
for i := 0 to MAXCONTROLS -1 do
begin
if not ( m_registerInfos[i].control = nil) and (m_registerInfos[i].scroll) then
begin
if (not m_registerInfos[i].pageRollComplete)
or (SecondsBetween(now(), m_registerInfos[i].lastCompleteTime) >= m_ResidenceTime) then
begin
tmpPageComplete := true;
tmpDirectionComplete := true;
//当前方向没有滚动完
if m_registerInfos[i].control.Top > (-m_registerInfos[i].control.Height) then
begin
tmpDirectionComplete := False;
//当前页没有滚动完
if m_registerInfos[i].control.Top > -(m_registerInfos[i].rollingCount + 1) * m_registerInfos[i].Height then
begin
tmpPageComplete := False; try
m_CSRegisterInfos.Enter; //进入临界区
if not (m_registerInfos[i].control = nil) then
m_registerInfos[i].control.Top := m_registerInfos[i].control.Top - m_Speed; //关闭窗口会偶尔出问题
m_CSRegisterInfos.Leave; //离开临界区
except
end;
end;
end; if tmpPageComplete = True then
begin
m_registerInfos[i].lastCompleteTime := Now();
m_registerInfos[i].rollingCount := m_registerInfos[i].rollingCount + 1;
end;
if tmpDirectionComplete = True then
begin
m_registerInfos[i].rollingCount := -1;
m_registerInfos[i].control.Top := m_registerInfos[i].Height + 5;
end; m_registerInfos[i].pageRollComplete := tmpPageComplete; end;
end; end;
end;
procedure TControlsScroller.HorizontalScrolling(); 的
m_registerInfos[i].control.Top := m_registerInfos[i].control.Top - m_Speed; //关闭窗口会偶尔出问题和 procedure TControlsScroller.VerticalScrolling(); 的
m_registerInfos[i].control.Top := m_registerInfos[i].control.Top - m_Speed; //关闭窗口会偶尔出问题这两个地方。
进一步跟踪调试,发现出现问题的时候 TViewLabel 和 TControlsScroller 的析构函数根本就没执行,如果TViewLabel 和 TControlsScroller 的析构函数成功执行了,那么就不会出现这个问题。另外这个问题不一定会出现,会偶尔出现,一次不出现多次 运行 -> 关闭 -> 运行 -> 关闭... 就可以看到这个问题了
以前我在Registration 、 HorizontalScrolling 和 VerticalScrolling 的开始处和结尾处加了临界区代码,后来由于卡设计界面的原因就去掉了Registration 的临界区,然后就把HorizontalScrolling 和 VerticalScrolling 的临界区代码改在 m_registerInfos[i].control.Top := m_registerInfos[i].control.Top - m_Speed; 这上面了,卡住的原因在于线程执行频率过高,导致设计界面等待而卡死。
现在这两个控件运行没发现任何问题,只有在进程关闭时才会出现这个问题,而且这个问题是挂在编译器上的,也就是说你单独执行EXE是不会出现这个的。我怀疑是不是早起DELPHI的BUG?
以前我在Registration 、 HorizontalScrolling 和 VerticalScrolling 的开始处和结尾处加了临界区代码,后来由于卡设计界面的原因就去掉了Registration 的临界区,然后就把HorizontalScrolling 和 VerticalScrolling 的临界区代码改在 m_registerInfos[i].control.Top := m_registerInfos[i].control.Top - m_Speed; 这上面了,卡住的原因在于线程执行频率过高,导致设计界面等待而卡死。
现在这两个控件运行没发现任何问题,只有在进程关闭时才会出现这个问题,而且这个问题是挂在编译器上的,也就是说你单独执行EXE是不会出现这个的。我怀疑是不是早起DELPHI的BUG?判断ComponentState是csDesigning就不开启线程,他就不卡了。该线程同步的就要同步!
以前我在Registration 、 HorizontalScrolling 和 VerticalScrolling 的开始处和结尾处加了临界区代码,后来由于卡设计界面的原因就去掉了Registration 的临界区,然后就把HorizontalScrolling 和 VerticalScrolling 的临界区代码改在 m_registerInfos[i].control.Top := m_registerInfos[i].control.Top - m_Speed; 这上面了,卡住的原因在于线程执行频率过高,导致设计界面等待而卡死。
现在这两个控件运行没发现任何问题,只有在进程关闭时才会出现这个问题,而且这个问题是挂在编译器上的,也就是说你单独执行EXE是不会出现这个的。我怀疑是不是早起DELPHI的BUG?判断ComponentState是csDesigning就不开启线程,他就不卡了。该线程同步的就要同步!
但是这样就没办法直接看到效果了啊 而且我觉得这个地方同不同步没太大影响
现在的问题是退出程序会偶尔弹CPU窗口 ret 0
我试了楼主的代码,点到手都软了都没弹出CPU窗口,我用的是D2007。
建议楼主把这段代码改一下试试,也有可能就不是问题,是编译器的BUG。 try
m_CSRegisterInfos.Enter; //进入临界区
try
if not (m_registerInfos[i].control = nil) then
begin
control := m_registerInfos[i].control;
control.Left := control.Left - m_Speed; //关闭窗口会偶尔出问题
end;
finally
m_CSRegisterInfos.Leave; //离开临界区
end;
except
end;
我试了楼主的代码,点到手都软了都没弹出CPU窗口,我用的是D2007。
建议楼主把这段代码改一下试试,也有可能就不是问题,是编译器的BUG。 try
m_CSRegisterInfos.Enter; //进入临界区
try
if not (m_registerInfos[i].control = nil) then
begin
control := m_registerInfos[i].control;
control.Left := control.Left - m_Speed; //关闭窗口会偶尔出问题
end;
finally
m_CSRegisterInfos.Leave; //离开临界区
end;
except
end;
昨天早上又看了一下代码,这段代码确实有问题,m_CSRegisterInfos.Leave; //离开临界区 这段代码不能放在 finally 这里, 这里 enter leave最好包围这个if。另外 destructor TViewLabel.Destroy();
begin
if not (m_ControlsScroller = nil) then
m_ControlsScroller.Cancellation(m_SubLabel);
m_SubLabel.Free;
inherited Destroy;
end;
这里也有问题,当m_ControlsScroller先释放的时候,这里会崩溃。所以在 ResetRegisterInfo 里添加了 msg:pinteger,当ControlsScroller释放的时候,会修改msg的值,另外 Enter、leave 包围了整个ControlsScroller::Cancellation(),m_CSRegisterInfos释放之前添加了 enter 和leave 来保证不运行不出问题。