我使用BDS 2006开发了一个使用MySQL数据库的应用程序(使用DataModule和MyDAC组件连接)。
现在我想在系统启动时启动我的应用程序(Windows XP)。所以我加入了application shortcut in the start up folder 。
现在,在启动时,我的应用程序在MySQL服务开始之前启动。因此我收到错误无法连接到MySQL 。
所以我在应用程序启动时插入一个空白并执行检查以查看MySQL是否正在运行。如果没有运行,则等待它运行。
function ServiceGetStatus(sMachine, sService: PChar): DWORD;
{******************************************}
{*** Parameters: ***}
{*** sService: specifies the name of the service to open
{*** sMachine: specifies the name of the target computer
{*** ***}
{*** Return Values: ***}
{*** -1 = Error opening service ***}
{*** 1 = SERVICE_STOPPED ***}
{*** 2 = SERVICE_START_PENDING ***}
{*** 3 = SERVICE_STOP_PENDING ***}
{*** 4 = SERVICE_RUNNING ***}
{*** 5 = SERVICE_CONTINUE_PENDING ***}
{*** 6 = SERVICE_PAUSE_PENDING ***}
{*** 7 = SERVICE_PAUSED ***}
{******************************************}
var
SCManHandle, SvcHandle: SC_Handle;
SS: TServiceStatus;
dwStat: DWORD;
begin
dwStat := 0;
// Open service manager handle.
SCManHandle := OpenSCManager(sMachine, nil, SC_MANAGER_CONNECT);
if (SCManHandle > 0) then
begin
SvcHandle := OpenService(SCManHandle, sService, SERVICE_QUERY_STATUS);
// if Service installed
if (SvcHandle > 0) then
begin
// SS structure holds the service status (TServiceStatus);
if (QueryServiceStatus(SvcHandle, SS)) then
dwStat := ss.dwCurrentState;
CloseServiceHandle(SvcHandle);
end;
CloseServiceHandle(SCManHandle);
end;
Result := dwStat;
end;
// if MySQL not running then sleep until its running
procedure TForm1.FormCreate(Sender: TObject);
begin
while(ServiceGetStatus(nil, 'MySQL5.5') <>4 ) do
begin
sleep (200);
end;
end;
我想知道我的方法是否正确?如果不建议相同。
也可以在没有使用windows编程的情况下完成这些工作吗?
答案 0 :(得分:3)
在主线程中睡觉绝不是一个好主意。
最好在线程中进行等待,并在MySQL运行时向主线程发送消息。
回答@mghie的评论:
为什么等待事件比调用Sleep()更好(或任何不同)?
事件驱动的GUI被认为是良好的编程习惯。没有等待。 触发事件时,将通知GUI有关数据库连接的状态更改。 如果您要在Sleep()循环中等待,则应用程序似乎无响应。 并且调用Application.ProcessMessages来处理这个问题,实际上并不是一个好习惯。
如何等待MySQL在线程中运行的示例:
const
WM_MySQL_READY = WM_USER + 1; // The unique message id
type
TForm1 = class(TForm)
...
private
procedure OnMySqlReady( var Msg: TMessage); message WM_MySQL_READY;
...
end;
在你的主题中:
Constructor TMyThread.Create( OwnerForm : TForm);
begin
Inherited Create( false);
FOwnerForm := OwnerForm; // Keep for later use
Self.FreeOnTerminate := true;
end;
procedure TMyThread.Execute;
var
SQL_started : boolean;
sleepEvent : TSimpleEvent;
begin
sleepEvent := TSimpleEvent.Create;
try
repeat
SQL_started := (ServiceGetStatus(nil, 'MySQL5.5') = 4);
sleepEvent.WaitFor(200); // Better than sleep();
until SQL_started or Terminated;
finally
sleepEvent.Free;
end;
// Inform main thread
PostMessage( FOwnerForm.Handle,WM_MySQL_READY,WPARAM(SQL_started),0);
end;
好的,我误解了@mghie,他的问题是why the TSimpleEvent.WaitFor() is better than Sleep() inside the thread
。
有关背景信息,请参阅:thread-sleep-is-a-sign-of-a-poorly-designed-program。
简而言之,Sleep()
将线程置于深度睡眠状态,并且控制权不会以最佳周期速率返回(如果在某些极端情况下)。
TSimpleEvent.WaitFor()
在时间和醒来方面的响应速度要快得多。 (请记住,Windows不是真正的实时操作系统,并且不保证计时)。无论如何,在线程中,使用TSimpleEvent.Waitfor()而不是Sleep()。
当需要停止等待连接MySQL服务器时,可以对代码进行一些调整:
constructor TMyThread.Create(OwnerForm: TForm; cancelEvent : TSimpleEvent);
begin
inherited Create(false);
FOwnerForm := OwnerForm; // Make sure it's assigned
FCancelEvent := cancelEvent; // Make sure it's assigned
Self.FreeOnTerminate := true;
end;
procedure TMyThread.Execute;
var
SQL_started : boolean;
cancel : boolean;
begin
repeat
SQL_started := (ServiceGetStatus(nil, 'MySQL5.5') = 4);
cancel := (FCancelEvent.WaitFor(200) = wrSignaled);
until SQL_started or Terminated or cancel;
// Inform main thread
PostMessage( FOwnerForm.Handle,WM_MySQL_READY,WPARAM(SQL_started),0);
end;
要在建立连接之前中止线程,只需调用MyEvent.SetEvent。
如果您想通知用户在等待期间发生的事情,您甚至可以在线程中显示启动画面。
有关此类示例,请参阅Peter Below's Threaded Splashscreen for Delphi。请注意,此代码不使用任何VCL组件或涉及与主线程同步的任何内容。
您可能还想查看:Show a splash screen while a database connection (that might take a long time) runs。