2012-02-13 63 views
10

我已經在Delphi 7中構建了一些服務,並沒有這個問題。現在我在XE2中開始了一個新的服務應用程序,它不會正常停止。我不知道這是我做錯了什麼,或者它可能是XE2服務中的錯誤。德爾福XE2服務沒有正常停止

的執行過程是這樣的:

procedure TMySvc.ServiceExecute(Sender: TService); 
begin 
    try 
    CoInitialize(nil); 
    Startup; 
    try 
     while not Terminated do begin 
     DoSomething; //Problem persists even when nothing's here 
     end; 
    finally 
     Cleanup; 
     CoUninitialize; 
    end; 
    except 
    on e: exception do begin 
     PostLog('EXCEPTION in Execute: '+e.Message); 
    end; 
    end; 
end; 

我永遠不會有一個例外,因爲你可以看到我日誌的任何異常。 PostLog保存到INI文件,這工作正常。現在我使用ADO組件,因此我使用CoInitialize()CoUninitialize。它確實連接到數據庫並正常工作。只有當我停止此服務時纔會出現問題。窗戶給我下面的消息:

First stop failure

然後服務繼續。我必須再次停止它。第二次,它不停止,但以下消息:

Second stop failure

日誌文件表示該服務沒有成功免費(已登錄OnDestroy事件),但它從來沒有成功地停止(OnStop從未登錄)。

在我上面的代碼中,我有兩個程序StartupCleanup。這些簡單的創建/銷燬和初始化/取消初始化我必要的東西...

procedure TMySvc.Startup; 
begin 
    FUpdateThread:= TMyUpdateThread.Create; 
    FUpdateThread.OnLog:= LogUpdate; 
    FUpdateThread.Resume; 
end; 

procedure TMySvc.Cleanup; 
begin 
    FUpdateThread.Terminate; 
end; 

正如你所看到的,我有一個輔助線程運行。這個服務實際上有許多像這樣運行的線程,而主服務線程只記錄來自每個線程的事件。每個線程都有不同的責任。線程正確報告,並且它們也被正確終止。

什麼可能導致此停止失敗?如果我貼的代碼不公開什麼,然後我可以在以後發佈更多的代碼 - 只需要「轉換」,因爲內部命名的,等等

編輯

我剛開始在新的服務項目德爾福XE2,並有相同的問題。這是我所有的代碼如下:

unit JDSvc; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, JDSvcMgr; 

type 
    TJDService = class(TService) 
    procedure ServiceExecute(Sender: TService); 
    private 
    FAfterInstall: TServiceEvent; 
    public 
    function GetServiceController: TServiceController; override; 
    end; 

var 
    JDService: TJDService; 

implementation 

{$R *.DFM} 

procedure ServiceController(CtrlCode: DWord); stdcall; 
begin 
    JDService.Controller(CtrlCode); 
end; 

function TJDService.GetServiceController: TServiceController; 
begin 
    Result := ServiceController; 
end; 

procedure TJDService.ServiceExecute(Sender: TService); 
begin 
    while not Terminated do begin 

    end; 
end; 

end. 
+1

不太可能是Delphi代碼中的錯誤。你可以把它削減到最低程度的複製。 – 2012-02-13 17:18:07

+0

^^ +1。看來你的線程永遠不會終止,因此在SCM – whosrdaddy 2012-02-13 17:39:12

+1

恕我直言,TMySvc.Cleanup例程的超時將會產生問題。你終止FUpdateThread,但你不知道什麼時候它真的被終止。添加WaitFor或使用同步對象來正確檢測終止。在這裏尋找更多的信息:http://www.eonclash.com/Tutorials/Multithreading/MartinHarvey1.1/Ch5.html – whosrdaddy 2012-02-13 17:50:49

回答

6

看看源代碼,Execute方法:

procedure TServiceThread.Execute; 
var 
    msg: TMsg; 
    Started: Boolean; 
begin 
    PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE); { Create message queue } 
    try 
    // Allow initialization of the Application object after 
    // StartServiceCtrlDispatcher to prevent conflicts under 
    // Windows 2003 Server when registering a class object with OLE. 
    if Application.DelayInitialize then 
     Application.Initialize; 
    FService.Status := csStartPending; 
    Started := True; 
    if Assigned(FService.OnStart) then FService.OnStart(FService, Started); 
    if not Started then Exit; 
    try 
     FService.Status := csRunning; 
     if Assigned(FService.OnExecute) then 
     FService.OnExecute(FService) 
     else 
     ProcessRequests(True); 
     ProcessRequests(False); 
    except 
     on E: Exception do 
     FService.LogMessage(Format(SServiceFailed,[SExecute, E.Message])); 
    end; 
    except 
    on E: Exception do 
     FService.LogMessage(Format(SServiceFailed,[SStart, E.Message])); 
    end; 
end; 

,你可以看到,如果你不分配OnExecute方法,德爾福將處理SCM請求(服務啓動,停止,...)直到服務停止。 當您在Service.Execute中創建循環時,您必須通過調用ProcessRequests(False)來自己處理SCM請求。一個好習慣是不要使用Service.execute並在Service.OnStart事件中啓動您的工作線程,並在Service.OnStop事件中終止/釋放它。

正如評論中所述,另一個問題在於FUpdateThread.Terminate部分。 大衛Heffernan與免費/等待評論點。 確保使用同步對象以正確的方式結束您的線程。

+2

+1我建議你交換這個答案中的兩點。 ProcessRequests是關鍵,它應該是第一位的。 – 2012-02-13 19:31:37

+1

^^同意。更新了我的答案。 – whosrdaddy 2012-02-13 20:31:58