Soru Delphi Windows Hizmet Tasarımı


Delphi Windows Hizmet Tasarımı

Hiç bir windows hizmeti yaratmadım ama bulduğum her şeyi okuyordum. Karşılaştığım tüm makaleler ya da örnekler, uygulamada çok basit ve kapsamlarında sınırlı. Bunun ötesine geçen veya belirli senaryoları ele alan hiçbir şey görmedim. Yani, muhtemelen bulacağım tüm teoriye sahibim ve şimdi bu projeye dalmaya hazırım. Fikirlerimi planlamayı ve insanların ne düşündüğü hakkında geri bildirim almayı seviyorum. Uygulamadan neye ihtiyacım olduğunu ve nasıl oluşturmayı planladığımı anlatacağım. Windows hizmetleri oluşturma deneyimini paylaşan herkesin yorumlarını ve paylaşacakları tavsiyeleri takdir ediyorum.

[Senaryo] Şu anda tüm diğer uygulamalarımıza güncellemeler sağlayan bir uygulama (bu UPDAPAPICATION çağrısı yapacağım) var. Uygulamalarımızdan herhangi birini çalıştırmak için öncelikle bu UPDATEAPPLICATION programını çalıştırmanız ve istenen uygulamanın bir parametresini iletmeniz gerekir. UPDATEAPPLICATION, istenen uygulamanın herhangi bir güncellemeye sahip olup olmadığı konusunda XML bilgisi veren bir WebService çağırır.

Güncelleme varsa, UPDATEAPPLICATION güncellemeyi EXE veya ZIP formatında indirir ve hedeflenen uygulamayı güncellemek için uygun dosyaları değiştirir. Daha sonra UPDATEAPPLICATION istenen uygulamayı başlatmak için bir ShellExecute yapar ve ardından UPDATEAPPLICATION kapanır.

Yıllar boyunca iyi işleyen oldukça basit bir süreç. UPDATEAPPLICATION programı bir Delphi uygulamasıdır, diğer uygulamalarımız karışıktır: Delphi, VB6, MS Access, .NET.

[SORUN] Vista ve Windows 7'ye geçişle güvenlik önemli ölçüde değişti. UPDATEAPPLICATION UAC'nin doğası gereği, uygulamanın Yönetici erişimi olmadan çalışmasına izin vermeyeceği veya UAC'nin tamamen kapatılmayacağı. Uygulamalarımızın çoğunu .NET'e yükseltme aşamasındayız ve bu süreçte UPDATEAPPLICATION uygulamasının UAC uyumlu olmasını istiyorum. Bunu yapmanın tek yollarından, Windows Hizmeti olarak UPDATEAPPLICATION oluşturarak. Yani, aslında, UPDATEAPPLICATION işlevinin bir Windows Hizmet mimarisine çoğaltılması gerekiyor.

[BENİM TASARIMIM] DelphiXE2 kullanıyorum. Tasarımım, tek bir çözüm oluşturmak için 3 bölümden oluşacaktır: Windows Hizmeti, Windows Hizmeti ile etkileşimde bulunacak küçük bir tepsi Uygulaması ve Windows Hizmetine mesaj gönderecek yeniden tasarlanmış uygulamalarınız.

  1. Windows Hizmetim (UPDATESERVICE'ı arayacağım) bir Windows Hizmeti olarak çalışacak ve istekleri dinlemek için bir TCP sunucusu oluşturacaktır.
  2. Tepsi uygulaması (TRAYAPP'yi arayacağım), UPDATESERVICE'ı yapılandırmak / yönetmek için TCP İstemcisi kullanacaktır.
  3. Başladığım zaman, KULLANICI UYGULAMASI, "BU UYGULAMA" başlamış olan UPDATESERVICE adresine bir TCP mesajı gönderecek.

[GüncellemeHizmeti] Mesajları dinler. USERAPPLICATION uygulamasının başlattığı bir mesaj alırsa, güncelleme olup olmadığını görmek için web servisini arayacaktır. Varsa, uygulamayı kapatmak ve UPDATESERVICE uygulamasının uygulamayı güncellemesine izin vermek için kullanıcı bilgilendirilir. UPDATESERVICE uygun dosyaları indirecek ve uygulamayı güncelleyecektir.

Şimdi yapmaya çalıştığım şeyin temellerini açıkladım, cevaplandırılmam gereken özel sorularımı sorabilirim. Bunların hepsi Windows Hizmetimi nasıl kurmalıyım? Ayrıca iş parçacığı yönetimi için OmniThread'i kullanmayı planlıyorum.

Hizmetim başladığında, TCP Server'ı oluşturmam gerekiyor.

  1. TCP Hizmeti kendi iş parçacığı üzerinde oluşturulmalı mı?
  2. TCP Hizmeti kendi iş parçacığıysa, iş parçacığını nasıl canlı tutarım? Aksi takdirde, TCP Hizmetini başlatabilirim ancak iş parçacığının çalışmasını sağlamak için TCP Hizmet biriminde hangi kodu kullanacağımı bilmiyorum?
  3. Windows Hizmeti etkinliğinin TCP Hizmetini oluşturması gerekir? OnExecute? OnStart? OnCreate? Okuduğum her şeyden sonra hangi olayın kullanılması gerektiği açık değil.
  4. TCP Hizmeti bir şey yapmak için bir ileti aldığında, iş TCP Service iş parçacığında veya ana UPDATESERVICE öğesinden yeni bir iş parçacığı içinde yürütülecek mi? Örneğin:
    • TCP Service bir HTTP HTTP kullanarak bir güncelleştirme denetlemek için bir ileti alırsa, TCP Service iş parçacığı bu işi yapmak için yeni bir iş parçacığı ortaya
    • Veya, TCP Service iş parçacığı, bu işi yapmak için yeni bir iş parçacığı oluşturmak için UPDATESERVICE'a bir ileti yollamalıysa
    • Fark eder mi?
  5. Delphi Kodunda bir Windows servisini Başlat / Durdur / Kaydet / Kayıt Silme mümkün mü?

Bütün sorularım bu. Muhtemelen bunun için doğru / yanlış bir cevap değil, sadece tecrübeye dayalı bir tercih. Eğer Delphi ile hizmet kurduysanız, muhtemelen yararlı bulacağım bazı girdilerin var. Eğer daha sağlam bir projeniz varsa o zaman temel bir "bir servis ve uyku başlatın" ve bunu paylaşmaya istekliyim - koşmamam ya da sadece kodlama - kodun çok değerli olacağından eminim. Uzun soluklu sorumu okuduğunuz için teşekkür ederim. Bu konuda daha iyi bir yol düşünebiliyorsanız, lütfen düşüncelerinizi paylaşın. Bazı uygulamalarımızın genel halk tarafından indirilip çalıştırılabileceğini ekleyeceğim, bu yüzden beklenen ortamlar üzerinde tam bir kontrol sahibi olmam. Herhangi bir tavsiye / yorum / yardım takdir edilecektir.


20
2018-05-10 15:24


Menşei




Cevaplar:


hızlı cevaplar:

1 & 3) Evet. Genel kural olarak OnExecute hizmet olayını uygulamayın. Kendi iş parçacığını OnStart hizmet etkinliğinden ayırın. OnStop servis etkinliğini aldığınızda iş parçacığı sonlandırılabilir.

2) iş parçanızı bu şekilde canlı tutuyorsunuz (execute method):

while not Terminated do
begin
  // do something
end;

4) normalde her istemci bağlantısı kendi iş parçacığı üzerinde yaşayacaktır. (yani TCP sunucusu, her istemci için yeni bir iş parçacığı oluşturur). Indy veya ICS gibi iyi bilinen bir yığın kullanın. HTTP güncellemesiyle ilgili olarak, bunu ortaya çıkan istemci bağlantı iş parçacığında yapabilirsiniz.

5) evet, bunu yapmak için yüksek haklara ihtiyacınız olduğunu unutmayın.

Kariyerimde epeyce hizmet yaptım ve şu ana kadar hizmet uygulaması için hep aynı iskeleti kullanıyorum:

unit u_svc_main;

interface

uses
  // Own units
  u_globals, u_eventlog, u_MyThread, 
  // Third party units
  // Delphi units
  Windows, Messages, Registry, SysUtils, Classes, SvcMgr;

type
  TMyService = class(TService)
    procedure ServiceCreate(Sender: TObject);
    procedure ServiceAfterUninstall(Sender: TService);
    procedure ServiceAfterInstall(Sender: TService);
    procedure ServiceShutdown(Sender: TService);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
    procedure ServiceStart(Sender: TService; var Started: Boolean);
  private
    { Private declarations }
    MyThread : TMyThread;
  public
    { Public declarations }
    function GetServiceController: TServiceController; override;
  end;

var MyService : TMyService;

implementation

{$R *.DFM}

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

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

procedure TMyService.ServiceCreate(Sender: TObject);
begin
  DisplayName := 'myservice';
end;

procedure TMyService.ServiceAfterInstall(Sender: TService);
var
  Reg        : TRegistry;
  ImagePath  : string;
begin
  // create needed registry entries after service installation
  Reg := TRegistry.Create;
  try
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    // set service description
    if Reg.OpenKey(STR_REGKEY_SVC,False) then
    begin
      ImagePath := Reg.ReadString(STR_REGVAL_IMAGEPATH);
      Reg.WriteString(STR_REGVAL_DESCRIPTION, STR_INFO_SVC_DESC);
      Reg.CloseKey;
    end;
    // set message resource for eventlog
    if Reg.OpenKey(STR_REGKEY_EVENTMSG, True) then
    begin
      Reg.WriteString(STR_REGVAL_EVENTMESSAGEFILE, ImagePath);
      Reg.WriteInteger(STR_REGVAL_TYPESSUPPORTED, 7);
      Reg.CloseKey;
    end;
    // set installdir
    if ImagePath <> '' then
      if Reg.OpenKey(STR_REGKEY_FULL,True) then
      begin
        Reg.WriteString(STR_REGVAL_INSTALLDIR, ExtractFilePath(ImagePath));
        Reg.CloseKey;
      end;
  finally
    FreeAndNil(Reg);
  end;
end;

procedure TMyService.ServiceAfterUninstall(Sender: TService);
var
  Reg : TRegistry;
begin
  Reg := TRegistry.Create;
  try
    // delete self created registry keys
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    Reg.DeleteKey(STR_REGKEY_EVENTMSG);
  finally
    FreeAndNil(Reg);
  end;
end;

procedure TMyService.ServiceShutdown(Sender: TService);
var
  Stopped : boolean;
begin
  // is called when windows shuts down
  ServiceStop(Self, Stopped);
end;

procedure TMyService.ServiceStart(Sender: TService; var Started: Boolean);
begin
  Started := False;
  try
    MyThread := TMyThread.Create;
    MyThread.Resume;
    NTEventLog.Add(Eventlog_Success, STR_INFO_SVC_STARTED);
    Started := True;
  except
    on E : Exception do
    begin
      // add event in eventlog with reason why the service couldn't start
      NTEventLog.Add(Eventlog_Error_Type, Format(STR_INFO_SVC_STARTFAIL, [E.Message]));
    end;
  end;
end;

procedure TMyService.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  try
    Stopped := True; // always stop service, even if we had exceptions, this is to prevent "stuck" service (must reboot then)
    MyThread.Terminate;
    // give MyThread 60 seconds to terminate
    if WaitForSingleObject(MyThread.ThreadEvent, 60000) = WAIT_OBJECT_0 then
    begin
      FreeAndNil(MyThread);
      NTEventLog.Add(Eventlog_Success,STR_INFO_SVC_STOPPED);
    end;
  except
    on E : Exception do
    begin
      // add event in eventlog with reason why the service couldn't stop
      NTEventLog.Add(Eventlog_Error_Type, Format(STR_INFO_SVC_STOPFAIL, [E.Message]));
    end;
  end;
end;

end.

25
2018-05-10 16:10



Tatlı. Cevap vermek için zaman ayırdığınız için teşekkürler. Bu aradığım pratik giriş tipi. İskelet kodunuz, şimdiye kadar bulduğumdan çok daha iyi bir yardımcı olan başlangıç ​​noktasıdır. Diğer cevaplarınız da benim beklediğim şey. Sadece bu yoldan geçen birinden haber almak güzel. Tekrar teşekkürler. - Darren -
Benzer bir durumdayım, ilk windows hizmetimi yaratıyorum ve cevabınızı burada çok yararlı buldum. Paylaştığın için teşekkürler. Hala iki sorum var: ServiceAfterInstall'da, kayıt defteri girdisi "// set installdir" yorumundan hemen sonra yazılıyor - Amacı nedir? - Bu giriş için doğru REGKEY ve REGVAL nedir? - Neville Cook
@NevilleCook, STR_REGKEYFULL, uygulamanızın HKLM kayıt defteri anahtarıdır, HKLM \ Software \ MyCompany \ MyApp, STR_REGVAL_INSTALLDIR gibi bir şey Key "InstallDir" = hizmetinizin kurulum yolunu temsil eder. Buna gerçekten ihtiyacınız yok, kurulum yolunu (AfterInstall'de yaptığım gibi) belirlemek için hizmetinizin ImagePath tuşunu her zaman okuyabilirsiniz. - whosrdaddy
@whosrdaddy, cevap için teşekkürler. Şimdi biliyorum ki belirli bir amaç için daha fazla güven ile ilerleyebiliyorum. - Neville Cook
@RemyLebeau, içeriği nerede ImagePath görünür? - PSyLoCKe