启动时调度服务

时间:2012-06-02 06:09:39

标签: mysql delphi service

我使用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;  

code source

// 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编程的情况下完成这些工作吗?

1 个答案:

答案 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