Makale Özeti

Windows servisleri oluşturma C++ programcılarına sunulmuş bir olanaktı.Vb altında bazı 3. parti çözümler kullanılarak da bu işlem gerçekleştirilebiliyordu. Artık Windows servisleri oluşturmak için gerekli herşey .NET Frameworkun kütüphanesinde System.ServiceProcess isim uzayında.Bunun doğal sonucu olarak .NET altında istediğiniz herhangi bir dilde Windows servisleri geliştirebilirsiniz. Tabiki bu konuyu işlerken benim seçimin Vb olacak.

Makale

Windows Servisleri Oluşturma

Windows servisleri oluşturma C++ programcılarına sunulmuş bir olanaktı.Vb altında bazı 3. parti çözümler kullanılarak da bu işlem gerçekleştirilebiliyordu. Artık Windows servisleri oluşturmak için gerekli herşey .NET Frameworkun  kütüphanesinde System.ServiceProcess isim uzayında.Bunun doğal sonucu olarak  .NET altında istediğiniz herhangi bir dilde Windows servisleri geliştirebilirsiniz. Tabiki bu konuyu işlerken benim seçimin Vb olacak.

Windows servisleri kullanıcı müdahelesi gerekmeden, sistem açılışında otomatik olarak çalışabilen uygulamalardır. Windows NT,2000, XP ve sonraki işletim sistemlerinde çalıştırılabilirler.

Bu uygulamamda dosya değişikliklerini takip eden , EventLogta log tutan ve belirlediğimiz klasördeki dosya değişikliklerini belirten  e-mail gönderen bir servis oluşturacağım. Bu tarz bir uygulama yedeklerin alınıp alınmadığını kontrol etmede, ya da örneğin ağ üzerinden teslim edilen dosyaların vaktinde ulaştırılıp ulaştırılmadığını tespit etmek için kullanılabilir.

Windows servisi oluşturmak için yapmamız gereken ilk iş şablonumuzu belirlemek olacak. Bunun için Vs içinden File | New | Projecti seçin. Soldan proje tipini Vb Projesi, sağdan şablon olarak Windows servisini seçin. Proje için şablon oluşturulduktan sonra Solution Explorerda service1.vb dosyasına çift tıklayın ve Properties penceresinden ServiceName özelliklerini "VbTestService" olarak belirleyin. 

Service1.vbnin kodlarını inceleyelim;

Imports System.ServiceProcess

Projemizin System.ServiceProcess isimuzayını kullandığına dikkat edin. Bu isim uzayı windows servislerini kurmada ve çalıştırmada gerekli olan metotları sağlar.

Inherits System.ServiceProcess.ServiceBase

Service1 sınıfı ise ServiceBase sınıfından türetiliyor. Bu sınıfı kullarak  servis başladığında, durdurulduğunda, ya da sistem kapatılmak üzere olduğundan bu durumları yakalayıp istediğimiz kodları çalıştırmamıza olanak sağlar.

E-mail kullarak uyarı gönderme:
Servisimizin dosya değişikliklerini istenilen yere ulaştırmada kullanacağı yöntem email göndermek olacak. Hemen hemen herkesin bilgisayarında değişik email servisleri kuruludur ve bu da bize uyarı ve olayları aktarabilmek için gerekli iletişim yapısını sunar. Projemizde bunu gerçekleştirebilmek için .NET Framework kütüphanesindeki MailMessage, MailAttachment ve SmtpMail sınıflarının nesnelerini kullanacağız.

MailMessage sınıfı email oluşturmak için kullanılır.Bu sınıf emaili kime göndereceğimizi, konuyu, emailin önceliğini, ve mesajla birlikte göndereceğimiz eklentileri belirlememizi sağlar.

MailAttachment sınıfı mesajımızla gönderilecek eklentilerin listesini sunar.

SmtpMail sınıfı mesajı gönderecek asıl mekanizmadır. Bu sınıf email göndermek için MailMessage nesnesini parametre olarak alan Send metodunu kullanır.

.NETte email servislerini kullanmak için SMTP (Simple Mail Transfer Protocol) servisinin kurulu olması gerekir. Smtp internette mail göndermek için en yaygın kullanılan protokoldür.Uygulamamızda sisteminizde geçerli bir SMTP servisi kurulu olduğunu varsayacağız.

Geliştireceğimiz servis, email göndermek için System.Web.Mail isim uzayını kullanacak. Bu yüzden projemize, System.Web.dll dosyasına referans ekleyin. Bundan sonra aşağıdaki import deyimini service1 sınıfına ekleyin;

Imports System.Web.Mail

SendMail Yordamını oluşturma:
İlk olarak SendMail isimli, kullanıcıya mail atacak  bir yordam oluşturacağız. service1.vb deki Component Designer generated code bölümünden sonra şu kodları ekleyin:

    Private Sub SendMail(ByVal Subject As String, _
                                ByVal SendTo As String, _
                                ByVal Message As String, _
                                Optional ByVal FileName As String = "")

        Dim Mail As New MailMessage
        eğer belirtilmişse dosyayı ekle
        If FileName <> "" Then
            Mail.Attachments.Add(New MailAttachment(FileName))
        End If

        With Mail
            .From = desktek@isimsiz.com
            .To = SendTo
            .Subject = Subject
            .Body = Message
            .Priority = MailPriority.High

        End With  
            SmtpMail.Send(Mail)
    End Sub

Bu kodlar sayesinde email nesnemizi oluşturduk , gerekli düzenlemeyi yaptık ve smtpmail.send metoduyla gönderilirken kullanılacak metodu belirledik.

OnStart ve OnStop olaylarının kodlanması:
Servisimiz başladığında (OnStart) ve durduğunda (OnStop) servisin ne zaman başlatılıp ne zaman durdurulduğunu öğrenmek için yöneticiye email göndereceğiz.Bunun için aşağıdaki kodları ekleyin.

Protected Overrides Sub OnStart(ByVal args() As String)
      SendMail("VbTestServisi - Başladı", "admin@isimsiz.com", _
                    "Vb servisi başlıyor.")
End Sub

Protected Overrides Sub OnStop()
      SendMail("VbTestServisi - Durdu", "admin@isimsiz.com", _
                                   "Vb servisi durduruluyor.")
End Sub

AutoLog Özelliği:
ServiceBase sınıfı varsayılan olarak servis hakkında bazı bilgileri (ne zaman başlatıldığını , ne zaman durdurulduğunu, hata oluşursa oluşma zamanı vs...) EventViewera yazar. Bunu servisimizin Autolog özelliğinden değiştirebiliriz. Yine servis özelliklerinden, servisin duraklatılabilirliği, sonladırılabilirliği, kapatılabilirliği gibi değerler true ya da  false olarak belirlenebilir.

Dosya Monitörünü Oluşturma:
Performans açısından düşündüğümüzde yan işlemlerimizi ve ana programımızı ayrı thread lerde (thread i iş parçacığı olarak kullanmak anlam karmaşasına sebep olabilir diye terimi olduğu gibi kullanacağım.) yapmalıyız. Böylelikle ana programımız başka işlemler de yapsa, işletim sisteminden gelen uyarılara cevap verebilecektir. Bu mantığı projemize, kendi thread lerini oluşturan bileşenler kullanarak uygulayabiliriz. Timer ve FileSystemWatcher buna iyi birer örnek oluşturuyorlar. Örneğin timer nesnesi elapsed olayını gerçekleştirdiğinde aynı zamanda yeni bir thread de oluşturur ve bu elapsed olayının içine yerleştirilen kodlar, bu farklı thread içinden çalıştırılır.

Kısa bir açıklama olarak; programı threadlere bölerek daha verimli iş yapabiliriz. Bu marketlerdeki turnikelere benzetilebilir. Ne kadar çok turnike koyarsanız markette o kadar verimli iş yaparsınız.(sadece teoride :) Her thread bir turnike gibi düşünülebilir.

FileSystemWatcher Bileşeni:
FileSystemWathcer dosyalar üzerindeki değişiklikleri algılamak üzere kullanılabilecek bir bileşendir. Belirtilen dosya(lar) üzerinde değişiklik algılandığında ilgili olaylar gerçekleştirilecektir.

Biraz önce belirttiğimiz; gibi bu bileşen de created,changed,deleted veya renamed olaylarını gerçekleştirirken kendi thread lerini oluşturur.

Toolboxın Components bölmesinden FileSystemWatcher bileşenini servisimize ekleyin.Bileşenin bazı özelliklerini inceleyelim:

EnableRaisingEvents: Bileşenin dosya üzerindeki değişikliklere bağlı olarak olayları karşılayacak kodları çağırıp çağırmayacağını belirler. Biz söyleyene kadar herhangi bir olay oluşmasını istemediğimizden bu özelliği False olarak belirleyin. Bu özelliği servisin OnStart olayında True olarak ayarlayacağız.

Path:Bileşenin gözlemyeceği yolu belirtir.Bir projemizde c:\temp klasörünü inceleyeceğiz. c:\temp klasörünü oluşturduktan sonra bu özelliği c:\temp olarak belirleyin.

NotifyFilter:Bileşenin hangi olayları inceleyeceğini belirleyen özelliktir. Projemizde sadece dosyanın son güncellenme tarihinin değiştirilme olayını (dosya kopyalandığında ya da oluşturulduğunda oluşur)inceleyeceğimizden özelliği LastWrite olarak ayarlayın.

Filter:Hangi tür dosyaların inceleneceğini belirler. Projemizde *.txt olarak ayarlayalım.

Bu ayarlamalardan sonra servisimizin OnStart ve OnStop olaylarıını aşağıdaki kodlarla biraz değiştirelim;

OnStart kısmına:

SendMail("VbTestServisi - Başladı", "admin@isimsiz.com", _
                      "Vb servisi başlıyor." & AppDomain.GetCurrentThreadId)
        FileSystemWatcher1.EnableRaisingEvents = True

OnStop kısmına:

FileSystemWatcher1.EnableRaisingEvents = False
 SendMail("VbTestServisi - Durdu", "admin@isimsiz.com", _
        "Vb servisi durduruluyor.")

OnStart olayına iki eklenti yaptık. Biriyle thread ler hakkında söylediklerimizi uygulayarak görmek için app.domain.getcurrentThreadId ile geçerli thread in id sini de maile ekledik. Diğeriyle FileSystemWatcher bileşenin gözlemlemeye başlamasını sağladık.

Aynı şekilde OnStop olayında da bu bileşenin gözlemlemeyi bırakmasını sağladık.Bu sayede bileşenin oluşturduğu thread i de sonlandırmış olduk.

Created Olayı:
Bu adımda FileSystemWatcher bileşenimizin Created olayına bazı kodlar yazacağız. Bu olay incelediğimiz klasöre bir dosya yerleştirildiğinde ya da klasörde dosya oluşturulduğunda tetiklenir. Bu olaya kod yazmak için kod penceresindeyken soldaki listeden FileSystemWatcher1i, sağdan Created olayını seçerek şablonu oluşturun. Bu kısımda StreamReader nesnesini kullanacağımızdan şu import deyimini diğer import deyimlerinin altına ekleyin:

Imports System.IO

Sonra az önce oluşturduğumuz Created olay şablonun Sub... End sub arasına şu kodları yazın:

        Dim streamFile As StreamReader
        Dim lineData As String
        Dim someData As String
        Dim linesRead As Integer

        Try
            streamFile = New StreamReader(e.FullPath)
            While linesRead < 5
                linesRead += 1
                lineData = streamFile.ReadLine
                If lineData Is Nothing Then linesRead = 5
                someData = someData & lineData
            End While
            streamReaderı kullanarak dosyadan 5 satır okuyoruz.
        Catch eof As IO.EndOfStreamException
        Catch eIOExcep As IO.IOException
            SendMail("VbTest Service - Dosya okuma hatası", _
                                "admin@isimsiz.com", _
                                "Dosya: " & e.FullPath & _
                                " Hata:" & eIOExcep.Message)
            Hata oluşursa oluşan hatayı mail olarak gönderiyoruz.
        Finally
            streamFile.Close()
        End Try
        SendMail("VbTestService - Dosya mevcut", _
                    "admin@isimsiz.com", _
                    "Bulunan dosya: " & e.FullPath & _
                    ", Thread Id: " & AppDomain.GetCurrentThreadId & _
                    Chr(13) & Chr(13) & someData, e.FullPath)
        burada elde ettiğimiz satırları mail olarak gönderiyoruz

Böylelikle servisimizi tamamlamış olduk. Fakat işimiz henüz bitmedi. Geliştirdiğimiz bu servisi kurmalıyız.

Servis Kurma:
Servisler komut satırından ya da ide içinden çalıştırılamazlar. Tabir yerindeyse Windowsa gömülmelidirler. Bir servisi kurmak için bir install programı ya da bir komut satırı aracı olan InstallUtil.exe yi kullanabiliriz.

Kurulumu gerçekleştirebilmek için projemizin içinde bir Installer sınıfı oluşturmalıyız. Bu sınıf servis kurmak için gerekli işlemleri yapmamızı sağlayacaktır.

service1in dizayn bölümünde sağ tuşa basın ve Add Installerı seçin.

ProjectInstaller isimli yeni bir sınıf projeye eklenecektir. Bu sınıfa iki bileşen eklenmiş olacaktır.Bunlar ServiceProcessInstaller1 ve ServiceInstaller1. Bu bileşenler servisin adını belirlemede, servisin çalıştırılabileceği hesapları belirlemede ve servisin nasıl başlayacağını belirlemede kullanılır.

ServiceProcessInstaller1in Account özelliğini LocalSystem olarak değiştirin.Böylelikle servisin işletim sistemi başladığında kullanıcının sisteme girmesinden bağımsız olarak çalışması sağlanır.

ServiceInstaller1in ServiceName özelliğini VbTestService olarak belirleyin. Yine bu bileşenin StartType özelliğinin Manual olduğundan emin olun.Böylelikle servis biz başlatana kadar durgun halde bekleyecekir.

Artık projemizi build edebiliriz.(Control+Shift+B)

Bu ayarları da yaptıktan sonra artık servisimizi sisteme kurabiliriz. Bunun için Visual Studio .NET Command Prompttan servisimizin olduğu projenin bin klasöründe

installutil WindowsService1.exe yazmanız yeterli.
-Servisi kaldırmak için ise yine aynı yerde installutil WindowsService1.exe /u komutunu vermeniz yeterli.

Yapmamız gereken son bir işlem kaldı: servisimizi başlatmak. Bunun için Denetim Masası | Yönetimsel Araçlar | Servisler den oluşturduğumuz servisin üstüne sağ tıklayın ve açılan menüden başlat komutunu verin. Hepsi bu kadar. Artık servisimiz çalışıyor..

Böylelikle baştan sona bir windows servisinin nasıl oluşturulduğunu irdelemiş olduk. Gerisi hayal sizin gücünüze kalmış...
 

                                                                                                            Kâsım Gülcan
SQLNedir?com