2

我使用 BDS 2006 开发了一个应用程序,它使用 MySQL 数据库(使用 DataModule 和 MyDAC 组件连接)。
现在我想在系统启动时启动我的应用程序(Windows XP)。所以我在启动文件夹中包含了应用程序快捷方式

现在在启动时,我的应用程序在 MySQL 服务启动之前启动。因此我得到一个错误Cannot connect to 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完成吗?

4

1 回答 1

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-design-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 的 Delphi 的 Threaded Splashscreen 。请注意,此代码不使用任何 VCL 组件或任何涉及与主线程同步的内容。

您可能还想查看:在数据库连接(可能需要很长时间)运行时显示初始屏幕

于 2012-06-02T07:47:20.670 回答