2012-02-13 27 views
10

Tôi đã xây dựng một vài dịch vụ trong Delphi 7 và không có vấn đề này. Bây giờ tôi đã bắt đầu một ứng dụng dịch vụ mới trong XE2, nó sẽ không dừng lại đúng cách. Tôi không biết nếu đó là một cái gì đó tôi đang làm sai hoặc nếu nó có thể là một lỗi trong các dịch vụ XE2.Dịch vụ Delphi XE2 không dừng đúng cách

Các thủ tục thực hiện như sau:

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; 

Tôi chưa bao giờ có một ngoại lệ, như bạn thấy tôi đăng nhập bất kỳ ngoại lệ. PostLog lưu vào tệp INI, hoạt động tốt. Bây giờ tôi sử dụng các thành phần ADO, vì vậy tôi sử dụng CoInitialize()CoUninitialize. Nó kết nối với DB và thực hiện công việc của nó đúng cách. Vấn đề chỉ xảy ra khi tôi ngừng dịch vụ này. Windows cung cấp cho tôi thông báo sau:

First stop failure

Sau đó, dịch vụ vẫn tiếp tục. Tôi phải dừng lại lần thứ hai. Lần thứ hai nó dừng lại, nhưng với thông báo sau:

Second stop failure

các log file chỉ ra rằng dịch vụ đã thành công miễn phí (OnDestroy Sự kiện này được đăng nhập) nhưng nó không bao giờ dừng lại thành công (OnStop không bao giờ đăng nhập).

Trong mã ở trên, tôi có hai thủ tục StartupCleanup. Chúng chỉ đơn giản là tạo/phá hủy và khởi tạo/uninitialize những thứ cần thiết của tôi ...

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

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

Như bạn có thể thấy, tôi có một chuỗi thứ cấp đang chạy. Dịch vụ này thực sự có nhiều luồng chạy như thế này, và luồng dịch vụ chính chỉ ghi nhật ký các sự kiện từ mỗi luồng. Mỗi luồng có các trách nhiệm khác nhau. Các chủ đề đang báo cáo đúng cách và chúng cũng bị chấm dứt đúng cách.

Điều gì có thể gây ra lỗi dừng này? Nếu mã đăng tải của tôi không tiếp xúc với bất cứ điều gì, sau đó tôi có thể gửi mã hơn sau này - chỉ phải 'biến đổi' nó vì đặt tên nội bộ, vv

EDIT

Tôi chỉ mới bắt đầu dự án dịch vụ MỚI trong Delphi XE2, và có cùng một vấn đề. Đây là tất cả các mã của tôi dưới đây:

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

Không thể là lỗi trong mã Delphi.Bạn có thể cắt giảm này xuống một bản sao tối thiểu. –

+0

^^ +1. Có vẻ như thread của bạn không bao giờ chấm dứt, do đó thời gian chờ trong SCM – whosrdaddy

+1

IMHO thói quen TMySvc.Cleanup là ràng buộc để tạo ra vấn đề. Bạn chấm dứt FUpdateThread nhưng bạn không biết khi nào nó thực sự bị chấm dứt. Thêm một WaitFor hoặc sử dụng một đối tượng đồng bộ để phát hiện kết thúc đúng cách. Nhìn ở đây để biết thêm thông tin: http://www.eonclash.com/Tutorials/Multithreading/MartinHarvey1.1/Ch5.html – whosrdaddy

Trả lời

6

xem mã nguồn cho các phương pháp 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; 

như bạn có thể thấy nếu bạn không chỉ định một phương pháp OnExecute, Delphi sẽ xử lý yêu cầu SCM (Khởi động dịch vụ, Dừng, ...) cho đến khi dịch vụ dừng lại. Khi bạn thực hiện một vòng lặp trong Service.Execute bạn phải tự xử lý các yêu cầu SCM bằng cách gọi ProcessRequests(False). Một thói quen tốt là không sử dụng Service.execute và bắt đầu workerthread của bạn trong sự kiện Service.OnStart và chấm dứt/giải phóng nó trong sự kiện Service.OnStop.

Như đã nói trong phần nhận xét, một vấn đề khác nằm ở phần FUpdateThread.Terminate. David Heffernan đã có mặt tại chỗ với nhận xét Free/WaitFor. Đảm bảo bạn kết thúc chuỗi của mình theo đúng thời trang bằng cách sử dụng các đối tượng đồng bộ hóa.

+2

+1 Tôi khuyên bạn nên trao đổi xung quanh hai điểm trong câu trả lời này. ProcessRequests là chìa khóa và nó nên đến trước. –

+1

^^ đã đồng ý. đã cập nhật câu trả lời của tôi. – whosrdaddy

Các vấn đề liên quan