BITS(Background Intelligent Transfer Service) ile sessiz sedasız download ;)
// 27 Mayıs 2009 // Delphi, İşletim Sistemi, Programlama
Açılımı Background Intelligent Transfer Service olan BITS, arka planda dosya indirmeye yarayan bir windows servisidir. Windows 2000′den sonra ve daha ziyade Windows XP’ler ile tanınmış ve daha sıklıkla kullanılmıştır. Windows’un arka planda updatelerini yaptığı servisin adıdır aynı zamanda. Bu servisin en önemli özelliği network’ün bandwith’ini en uygun şekilde kullanmasıdır. Bundan sonra yazacaklarımızı deneyebilmeniz için öncelikle bu servisin makinanızda çalışıyor olduğundan emin olmalısınız.
BITS servisinin en önemli özelliğinin sizin internet trafiğinize pek zarar vermemesi olduğunu söylemiştik. C ve C++ kullanıcıları için geliştirilmiş olan BITS, kendisine verilen indirme emirlerini yerine getirmek için network’ün en az kullanıldığı zamanları takip eder(Idle). Bu zamanı tespit edebilmek için de ethernet kartını sürekli izler. Network’ün yeterince kullanılmadığını anladığında arka planda indirim işlemine başlar. Ancak network kullanımının artması durumunda hemen transfer hızını düşürür ve mevcut network sistemine zarar vermez. Tüm bu özelliklerinden dolayı Windows, kendisi için gereken update’leri bu sistem vasıtası ile karşı bilgisayardan sizin bilgisayarınıza indirebilmektedir.
Biz de bu makalemizde, mevcut ağımızı yormayan basit bir indirme yöneticisi yazmaya çalışacağız. Ancak kendi indirme yöneticimizi yazmaya başlamadan evvel BITS’i biraz daha tanımakta fayda var. BITS, mevcut ağ performansına zarar vermeden, iletişimi sekteye uğratmadan dosya indirmeleri için son derece uygundur. Yazdığımız programların internet üzerinden bir yerlerden güncellenmiş sürümlerini yada bazı dosyaları indirmesi gerektiğinde kullanılabilecek mükemmel bir yardımcıdır.
BITS servisinin her bir indirme görevine JOB(iş) adı verilir. Her bir Job birden fazla dosyayı barındırabilir. Ancak bir job en fazla 10 dosyadan müteşekkil olabilir. Bir job’un create edilmesi ile BITS yaşam döngüsüne başlamış olur. Yaşam döngüsü diyorum çünkü gerçekten de uzun bir süreci var. Bu süreçlere ileride detaylıca değineceğiz ama indirilmesi gereken herhangi bir dosyanın indirme işleminin 90 gün boyunca indirme listesinde durabileceğini belirterek yaşam döngüsüne işaret etmiş olayım. BITS, kısaca internet üzerindeki bir kaynaktan belirtilen belirli dosyaları diskiniz üzerinde belirttiğiniz yere belirttiğiniz dosya adları ile sistemi yormadan kopyalayan bir indirme yöneticisidir. İndirme işlemini gerçekleştirebilmesi için indirilecek dosyaların HTTP/HTTPS protokollerinde olması gereklidir. Bu da FTP üzerinden indirme yapamayacağı anlamına gelir. Uzaktaki dosyaların bilgisayarınıza indirilme sıraları sizin o dosyaları job’un içine hangi sırada soktuğunuz ile bire bir ilişkilidir. Yani FIFO mantığına göre çalışır. İlk eklenen dosya ilk olarak indirilecektir. BITS’in en faydalı özelliği persist yani kalıcı olmasıdır. Bazı dosyaların indirilmesi amacı ile oluşturduğunuz bir job , siz makinanızı kapatsanız da, elektrik kesilse de yada internet bağlantınız kopsa da zarara uğramaz. Daha sonra kaldığı yerden devam edebilir. BITS için bir job oluşturulduğunda oluşturulan job indirme işlemine hemen başlamaz. Önce uyku modundadır(suspend, tıpkı threadlerde olduğu gibi). İndirme işleminin başlayabilmesi için programcının kod ile uyku modundan çıkartması gerekir(Resume).
Bu indirme yöneticisinin belirli bir öncelikler seviyesi vardır(Priority). Bu seviyelere göre indirme işlemlerini yürütür. Arka planda yaptığı indirmeler 3 ayrı öncelik seviyesindedir. High, Normal ve Low. Varsayılan öncelik seviyesi Normal’dir. Aynı anda BITS içinde birden fazla indirme job’u olabilir. Ancak herzaman önceliği en yüksek olan job en önce indirilecek ve diğer job’lar onu bekleyeceklerdir. Aynı öncelik seviyesine sahip job’ların indirilmesi ise diğer daha küçük dosyalara sahip joblarda tıkanmanın önüne geçmek adına paraleldir. Yani High öncelik seviyesine sahip 3 ayrı job var ise bu bu jobların indirilmesi sırasında transfer zamanı tüm joblar için ortak bir şekilde paylaştırılır. Düşük öncelikli jobların indirme işlemine başlamasının 2 yolu vardır. Birincisi kendisinden yüksek önceliğe sahip tüm job’ların bitmiş olması yada kendisinden önceki job’ların hata durumuna düşmesi.
Dosyaların BITS tarafından makinanıza indirilmesi sırasında server tarafındaki dosyaların güncellenmesi durumunda, BITS indirmeye başlamış olduğu dosyayı yeniden indirmeye başlar. Ancak tamamen indirdiklerini bir daha indirmez. Örneğin indirme listesinde 2 dosya varsa bunlardan birini indirmiş ikincisini ise henüz indiriyor ise her 2
dosyanın da server tarafında değiştirilmesi durumunda sadece en son indirmeye çalıştığı dosyayı yeni baştan indirmeye başlar.
BITS’in karşı bilgisayardan sizin bilgisayarınıza dosyaları indirmesi sırasında kullandığı bir iletişim protokolü vardır. BITS, öncelikle karşı bilgisayara bağlanmaya çalışır eğer bağlantı kurabilir ise transferi başlatma işine girişir, aksi durumda düzeltilebilir hata konumuna geçer. Düzeltilebilir hata konumundan kastımız BITS’in 2 çeşit hata durumuna destek vermesidir. Birincisi BITS’in kendi başına düzeltemeyeceği, kullanıcı yardımına ihtiyacı olan fatal error durumu, ikincisi ise kendi başa çıkabileceği hata durumudur. Bu durumları ilerleyen zamanlarda job’un durum(state) bilgisini alırken detaylandıracağız.
BITS indirme yöneticisi indirme işlemine başladığında indirmesini söylediğiniz klasör içinde tmp uzantılı dosyalar oluşmaya başlar. İndirme işlemi bittiğinde , indirme işleminin bittiğini bizler onaylayana kadar o dosyalar tmp uzantısı ile kalırlar. İndirme işlemi sırasında BITS’in çözemeyeceği herhangi bir hata oluşması durumunda job’umuz suspend duruma geçecektir. Suspend duruma düşen bir job Cancel metodu ile iptal edilmedikçe yada Complete metodu ile onaylanmadıkça 90 gün boyunca indirme listesinde kalacaktır. 90 günün sonunda BITS tarafından otomatikman silinecektir. Bu sebeple indirme işleminin kontrolünü iyi yapmamız gerekir. Bazı bilgisayarlarda sistem yöneticileri, kullanıcı ve grup bazlı çeşitli poliçeler ile bir kullanıcının açabileceği azami job sayısını yada ilgili makinada açılabilecek azami job sayısını belirtmiş olabilirler. İndirme listesinde suspend durumda bulunan bu joblarımız bizim yeni joblar açmamıza bu kurallar nedeni ile engel olabilir.
BITS’in ciddi bir hataya düşmediği hatalarda devam edebildiğini yazmıştık. Bu devam işlemi için belirli bir zaman bekler. Bu zaman varsayılan olarak 600
saniyedir. Bekleme zamanı IBackgroundCopyJob interface’inin SetMinimumRetryDelay metodu ile değiştirilebilir. Bu fonksiyon saniye cinsinden parametre alır.
Verilebilecek en küçük saniye değeri 60′dır, 60′ın altındaki atamalarda BITS bu değeri otomatikman tekrar 60 yapacaktır. BITS’in indirme işlemine bir an evvel başlayabilmesi adına işletim sisteminin saatini ileri almak bir işe yaramaz. BITS yine ne kadar beklemesi gerekiyor ise o kadar bekleyecektir.
Bu kadar genelkültür bilgisinden sonra Delphi ile BITS yapısını kullanarak bir indirme yöneticisinin nasıl yapılacağı hususlarına biraz değinelim. Öncelikle bize BITS COM arabirimine ulaşmak için bir kütüphane gerekli. Ancak yaptığım araştırmalarda sistemimde gereken dosyayı bir türlü bulamadım. Eğer makinanızda Windows XP Service Pack 2 Platform SDK var ise siz BITS.IDL dosyasını bulabilirsiniz. Bu dosyayı bulmak başlı başına yeterli değildir. MIDL türü bir compiler ile bu dosyayı TBL haline getirmeli ve daha sonra da Delphi’de bu TBL dosyasını import etmeliyiz.
Ancak ben sizleri bu kadar uğraştırmayacak ve TBL dosyasının kaynak kodunu sizlerin indirmesi için buraya koyacağım. Sizlerin tek yapacağı indirdiğiniz tbl dosyasını uses satırına ilave etmekten ibaret olacak. Daha detaylı bir malümat için her zaman MSDN‘e uğrayabilirsiniz. Şimdi biz BITS’i Delphi’de nasıl kullanacağız gelin ona bakalım:
type TDownloadState = (dsQueue, dsConnecting, dsTransferring, dsSuspended, dsError, dsTransferred, dsAcknowlegged, dsCancelled, dsUnknown); TDownloadError = record Context, Protocol, Description : String; end; TDownloadNotify = record JobID : String; DownloadState : TDownloadState; BytesTotal : Int64; BytesTransferred : Int64; FilesTotal : Int64; FilesTransferred : Int64; Error : TDownloadError; end; TOnStateChanged = procedure(const State : TDownloadNotify) of object; TDownloadJob = class private fLocalFileNames , fRemoteFileNames : TStrings; fJobID, fDisplayName, fDescription : String; public constructor Create(const AJobID, ADisplayName, ADescription : String; const ALocalFileNames, ARemoteFileNames : array of String ); function Suspend : Boolean; function Resume : Boolean; function Cancel : Boolean; function Complete : Boolean; property LocalFileNames : TStrings read fLocalFileNames; property RemoteFileNames: TStrings read fRemoteFileNames; property JobID : String read fJobID; property DisplayName : String read fDisplayName; property Description : String read fDescription; end; TDownloadJobs = class(TList) private function GetDownloadJob(AIndex : Integer) : TDownloadJob; protected function Add(Item: TDownloadJob): Integer; procedure Delete(Index: Integer); function IndexOf(Item: TDownloadJob): Integer; function Remove(Item: TDownloadJob): Integer; procedure DeleteAll; public destructor Destroy; override; function FindJob(const ID : String) : TDownloadJob; property Jobs[AIndex : Integer] : TDownloadJob read GetDownloadJob; default; end; TDownloadManager = class; TDownloadThread = class(TThread) private fOwner : TDownloadManager; fDisplayName, fDescription : String; fLocalFileNames, fRemoteFileNames : array of String; protected procedure Execute; override; public constructor Create(const AOwner : TDownloadManager; const DisplayName, Description : String; const LocalFileNames, RemoteFileNames : array of String); destructor Destroy; override; end; TDownloadManager = class private fOnStateChanged : TOnStateChanged; fCritSect : TCriticalSection; fDownloadJobs : TDownloadJobs; Manager : IBackgroundCopyManager; procedure EnumJobs; procedure Notify(const DownloadNotify : TDownloadNotify); public constructor Create; destructor Destroy; override; procedure Download(const DisplayName, Description : String; const LocalFileNames, RemoteFileNames : array of String); property DownloadJobs : TDownloadJobs read fDownloadJobs; property OnStateChanged : TOnStateChanged read fOnStateChanged write fOnStateChanged; end;
Yukarıda tanımlı TDownloadNotify record’u TDownloadThread isimli sınıf tarafından ana uygulamayı bilgilendirmek amacı ile kullanılmaktadır. BITS içindeki jobların o anki durumları ve hata bilgilerini içerir. Bütün işi yapan kısım TDownloadThread sınıfının Execute metodudur. TDownloadManager sınıfı her bir job için TDownloadThread create eden bir Download metoduna ev sahipliği yapar. Kodumuzun geri kalan kısmı aşağıdaki gibidir:
implementation const BG_JOB_ENUM_ALL_USERS = $0001; { TDownloadManager } constructor TDownloadManager.Create; begin inherited Create; fCritSect := TCriticalSection.Create; fDownloadJobs := TDownloadJobs.Create; EnumJobs; end; destructor TDownloadManager.Destroy; begin if Assigned(fDownloadJobs) then fDownloadJobs.Free; if Assigned(fCritSect) then fCritSect.Free; inherited; end; procedure TDownloadManager.Download(const DisplayName, Description: String; const LocalFileNames, RemoteFileNames: array of String); begin TDownloadThread.Create(Self, DisplayName, Description, LocalFileNames, RemoteFileNames); end; procedure TDownloadManager.EnumJobs; var Jobs : IEnumBackgroundCopyJobs; Job : IBackgroundCopyJob; BackFiles : IEnumBackgroundCopyFiles; BackFile : IBackgroundCopyFile; retVal: HRESULT; dummy, FileCount : Cardinal; tmpJobId : GUID; iFileIndex : Integer; LocalFiles, RemoteFiles : array of String; pTemp : PWideChar; JobID, DisplayName, Description : String; DownloadJob : TDownloadJob; begin CoInitialize(nil); Manager := CoBackgroundCopyManager_.Create; try retVal := Manager.EnumJobs(BG_JOB_ENUM_ALL_USERS, Jobs); // Tüm userlara ait joblara erişmeye çalış.! if not Succeeded(retVal) then begin retVal := Manager.EnumJobs(0, Jobs); // O andaki aktif windows kullanıcısının joblarına erişmeye çalış.! if not Succeeded(retVal) then begin Manager := nil; CoUninitialize; raise Exception.Create('Joblara erişimde sorunla karşılaşıldı.!'); end; end; while Succeeded(Jobs.Next(1, Job, dummy)) and (dummy = 1) do begin JobID := ''; DisplayName := ''; Description := ''; LocalFiles := nil; RemoteFiles := nil; if Succeeded(Job.GetDisplayName(pTemp)) then begin DisplayName := pTemp; CoTaskMemFree(pTemp); // PWideChar türündeki değişkenimize hafızayı BITS ayırır, o yüzden burada serbest bırakmak gerekir. end; if Succeeded(Job.GetDescription(pTemp)) then begin Description := pTemp; CoTaskMemFree(pTemp); end; if Succeeded(Job.GetId(tmpJobId)) then JobID := GuidToString(TGuid(tmpJobID)); FileCount := 0; retVal := Job.EnumFiles(BackFiles); if not Succeeded(retVal) then Continue; BackFiles.GetCount(FileCount); if FileCount <= 0 then Continue; SetLength(LocalFiles , FileCount); SetLength(RemoteFiles, FileCount); iFileIndex := 0; while Succeeded(BackFiles.Next(1, BackFile, dummy)) and (dummy = 1) do begin if Succeeded(BackFile.GetLocalName(pTemp)) then begin LocalFiles[iFileIndex] := pTemp; CoTaskMemFree(pTemp); end; if Succeeded(BackFile.GetRemoteName(pTemp)) then begin RemoteFiles[iFileIndex] := pTemp; CoTaskMemFree(pTemp); end; Inc(iFileIndex); end; DownLoadJob := TDownloadJob.Create(JobId, DisplayName,Description,LocalFiles,RemoteFiles); DownloadJobs.Add( DownloadJob ); LocalFiles := nil; RemoteFiles:= nil; end; finally Manager := nil; CoUninitialize; end; end; procedure TDownloadManager.Notify(const DownloadNotify: TDownloadNotify); begin fCritSect.Enter; if Assigned(fOnStateChanged) then fOnStateChanged(DownloadNotify); fCritSect.Leave; end; { TDownloadJob } function TDownloadJob.Cancel : Boolean; var Manager : IBackgroundCopyManager; foundJobID : GUID; foundJob : IBackgroundCopyJob; begin Result := false; CoInitialize(nil); try Manager := CoBackgroundCopyManager_.Create; foundJobID := GUID(StringToGuid(JobID)); Result := Succeeded(Manager.GetJob(foundJobID, foundJob)); if Result then Result := Result and Succeeded(foundJob.Cancel); finally Manager := nil; CoUninitialize; end; end; function TDownloadJob.Complete : Boolean; var Manager : IBackgroundCopyManager; foundJobID : GUID; foundJob : IBackgroundCopyJob; begin Result := false; CoInitialize(nil); try Manager := CoBackgroundCopyManager_.Create; foundJobID := GUID(StringToGuid(JobID)); Result := Succeeded(Manager.GetJob(foundJobID, foundJob)); if Result then Result := Result and Succeeded(foundJob.Complete); finally Manager := nil; CoUninitialize; end; end; constructor TDownloadJob.Create(const AJobID, ADisplayName, ADescription: String; const ALocalFileNames, ARemoteFileNames: array of String); var iCounter : Integer; begin inherited Create; if Length(ALocalFileNames) <> Length(ARemoteFileNames) then raise Exception.Create('İndirilecek dosyalar ile kaydedilecek dosyaların sayısında uyumsuzluk var.!'); for iCounter := Low(ALocalFileNames) to High(ALocalFileNames) do if not DirectoryExists(ExtractFileDir(ALocalFileNames[iCounter])) then raise Exception.Create(ExtractFileDir(ALocalFileNames[iCounter]) + ' dizini bulunamadı.!'); fLocalFileNames := TStringList.Create; fRemoteFileNames:= TStringList.Create; fJobID := AJobID; fDisplayName := ADisplayName; fDescription := ADescription; for iCounter := Low(ALocalFileNames) to High(ALocalFileNames) do begin fLocalFileNames.Add(ALocalFileNames[iCounter]); fRemoteFileNames.Add(ARemoteFileNames[iCounter]); end; end; function TDownloadJob.Resume : Boolean; var Manager : IBackgroundCopyManager; foundJobID : GUID; foundJob : IBackgroundCopyJob; begin Result := false; CoInitialize(nil); try Manager := CoBackgroundCopyManager_.Create; foundJobID := GUID(StringToGuid(JobID)); Result := Succeeded(Manager.GetJob(foundJobID, foundJob)); if Result then Result := Result and Succeeded(foundJob.Resume); finally Manager := nil; CoUninitialize; end; end; function TDownloadJob.Suspend : Boolean; var Manager : IBackgroundCopyManager; foundJobID : GUID; foundJob : IBackgroundCopyJob; begin Result := false; CoInitialize(nil); try Manager := CoBackgroundCopyManager_.Create; foundJobID := GUID(StringToGuid(JobID)); Result := Succeeded(Manager.GetJob(foundJobID, foundJob)); if Result then Result := Result and Succeeded(foundJob.Suspend); finally Manager := nil; CoUninitialize; end; end; { TDownloadJobs } function TDownloadJobs.Add(Item: TDownloadJob): Integer; begin Result := inherited Add(Item); end; procedure TDownloadJobs.Delete(Index: Integer); begin try Jobs[Index].Free; except end; inherited Delete(Index); end; procedure TDownloadJobs.DeleteAll; begin while Count > 0 do Delete(0); end; destructor TDownloadJobs.Destroy; begin DeleteAll; inherited; end; function TDownloadJobs.FindJob(const ID: String): TDownloadJob; var iCounter : Integer; begin Result := nil; for iCounter := 0 to Count - 1 do if Jobs[iCounter].JobID = ID then begin Result := Jobs[iCounter]; Break; end; end; function TDownloadJobs.GetDownloadJob(AIndex: Integer): TDownloadJob; begin Result := TDownloadJob(inherited Items[AIndex]); end; function TDownloadJobs.IndexOf(Item: TDownloadJob): Integer; begin Result := inherited IndexOf(Item); end; function TDownloadJobs.Remove(Item: TDownloadJob): Integer; begin try Item.Free; except end; Result := inherited Remove(Item); end; { TDownloadThread } constructor TDownloadThread.Create(const AOwner : TDownloadManager; const DisplayName, Description: String; const LocalFileNames, RemoteFileNames: array of String); var iCounter : Integer; begin inherited Create(true); fOwner := AOwner; fDisplayName := DisplayName; fDescription := Description; SetLength(fLocalFileNames , Length(LocalFileNames)); SetLength(fRemoteFileNames, Length(RemoteFileNames)); for iCounter := Low(LocalFileNames) to High(LocalFileNames) do begin fLocalFileNames[iCounter] := LocalFileNames[iCounter]; fRemoteFileNames[iCounter] := RemoteFileNames[iCounter]; end; Resume; end; destructor TDownloadThread.Destroy; begin fLocalFileNames := nil; fRemoteFileNames:= nil; inherited; end; procedure TDownloadThread.Execute; var Manager : IBackgroundCopyManager; Job : IBackgroundCopyJob; Error : IBackgroundCopyError; pTemp : PWideChar; JobID : GUID; State : BG_JOB_STATE; Progress : _BG_JOB_PROGRESS; bRun : Boolean; iCounter : Integer; DownloadNotify : TDownloadNotify; begin inherited; CoInitialize(nil); Manager := CoBackgroundCopyManager_.Create; try if not Succeeded(Manager.CreateJob(PWideChar(fDisplayName), BG_JOB_TYPE_DOWNLOAD, JobID, Job)) then begin Manager := nil; CoUninitialize; Exit; end; if not Succeeded(Job.SetDescription(PWideChar(fDescription))) then begin Manager := nil; CoUninitialize; Exit; end; for iCounter := Low(fLocalFileNames) to High(fLocalFileNames) do if not Succeeded(Job.AddFile( StringToOleStr(fRemoteFileNames[iCounter]), StringToOleStr(fLocalFileNames[iCounter]) )) then begin Manager := nil; CoUninitialize; Exit; end; if not Succeeded(Job.SetPriority(BG_JOB_PRIORITY_HIGH)) then begin Manager := nil; CoUninitialize; Exit; end; fOwner.DownloadJobs.Add( TDownloadJob.Create( GUIDToString(TGuid(JobID)), fDisplayName, fDescription, fLocalFileNames, fRemoteFileNames ) ); bRun := false; repeat if not Succeeded(Job.GetState(State)) then Break; job.GetProgress(Progress); DownloadNotify.JobID := GUIDToString(TGuid(JobID)); DownloadNotify.BytesTotal := Progress.BytesTotal; DownloadNotify.BytesTransferred := Progress.BytesTransferred; DownloadNotify.FilesTotal := Progress.FilesTotal; DownloadNotify.FilesTransferred := Progress.FilesTransferred; DownloadNotify.Error.Context := ''; DownloadNotify.Error.Protocol := ''; DownloadNotify.Error.Description := ''; (* BG_JOB_STATE_QUEUED : Job kuyrukta ve çalıştırılmayı bekliyor. Eğer job transfer edilirken logoff olunur ise job kuyruğa atılır. BG_JOB_STATE_CONNECTING : BITS server ile bağlantı kurmaya çalışıyor. Eğer bağlantı sağlanır ise BG_JOB_STATE_TRANSFERRING durumuna geçilir, aksi durumda BG_JOB_STATE_TRANSIENT_ERROR durumuna geçilir. BG_JOB_STATE_TRANSFERRING : Veriler transfer edilmeye başlandı. BG_JOB_STATE_SUSPENDED : Job'un durdurulduğunu gösterir. Resume, Complete yada Cancel metodları çağırılmadan bu durumdan çıkmaz. BG_JOB_STATE_ERROR : Dosyanın transfer edilemediğini belirtir. Eğer server'a ulaşılamamasından yada yetki sorunlarından kaynaklanan bir hata nedeni ile bu state'de isek hata giderildikten sonra Resume metodunun çalıştırılması ile job kaldığı yerden devam edebilir. Eğer hata düzeltilemiyor ise Cancel metodu çağırılabilir, bu durumda o zamana kadar yapılan indirmeler de iptal edilmiş olur, yada Complete metodu çağırılabilir, o zamana kadar yapılan download onaylanır. BG_JOB_STATE_TRANSIENT_ERROR : Ciddi bir hata olmadığı durumlarda bu duruma alınır. BITS indirme çabalarına devam edecektir. Ancak bir job için belirtilen zamanın aşılması durumunda job durumunu BG_JOB_STATE_ERROR konumuna alacaktır.BITS network bağlantısının kesilmesi yada disk üzerinde lock işlemi uygulayan bir process'in varlığı durumunda(chkdsk gibi) indirmeyi denemekten vazgeçecektir. BG_JOB_STATE_TRANSFERRED : Dosya indirme işlemi tamamlandı. Complete metodunu çağırmalıyız. BG_JOB_STATE_ACKNOWLEDGED : Job'un Complete metodu çağırılması ile düzgün bir şekilde indirildiğinin onaylandığını belirtir. BG_JOB_STATE_CANCELLED : Cancel metodunun çağırılması vasıtası ile indirme işleminin iptal edildiği bilgisidir. *) case State of BG_JOB_STATE_QUEUED : begin DownloadNotify.DownloadState := dsQueue; fOwner.Notify(DownloadNotify); bRun := true; end; BG_JOB_STATE_CONNECTING : begin DownloadNotify.DownloadState := dsConnecting; fOwner.Notify(DownloadNotify); bRun := true; end; BG_JOB_STATE_TRANSFERRING: begin DownloadNotify.DownloadState := dsTransferring; fOwner.Notify(DownloadNotify); bRun := true; end; BG_JOB_STATE_SUSPENDED : begin DownloadNotify.DownloadState := dsSuspended; fOwner.Notify(DownloadNotify); if not Succeeded(Job.Resume) then bRun := false else bRun := true; end; BG_JOB_STATE_ERROR , BG_JOB_STATE_TRANSIENT_ERROR: begin DownloadNotify.DownloadState := dsError; if Succeeded(job.GetError(Error)) then begin if Succeeded(Error.GetErrorDescription(LANG_NEUTRAL, pTemp)) then begin DownloadNotify.Error.Description := String(pTemp); CoTaskMemFree(pTemp); end; if Succeeded(Error.GetErrorContextDescription(LANG_NEUTRAL, pTemp)) then begin DownloadNotify.Error.Context := String(pTemp); CoTaskMemFree(pTemp); end; if Succeeded(Error.GetProtocol(pTemp)) then begin DownloadNotify.Error.Protocol := String(pTemp); CoTaskMemFree(pTemp); end; end; fOwner.Notify(DownloadNotify); bRun := false; end; BG_JOB_STATE_TRANSFERRED : begin Job.Complete; DownloadNotify.DownloadState := dsTransferred; fOwner.Notify(DownloadNotify); bRun := true; end; BG_JOB_STATE_ACKNOWLEDGED : begin DownloadNotify.DownloadState := dsAcknowlegged; fOwner.Notify(DownloadNotify); bRun := false; end; BG_JOB_STATE_CANCELLED : begin DownloadNotify.DownloadState := dsCancelled; fOwner.Notify(DownloadNotify); bRun := false; end; else begin DownloadNotify.DownloadState := dsUnknown; fOwner.Notify(DownloadNotify); bRun := false; end; end; // case State of until not bRun; // repeat..until finally Manager := nil; CoUninitialize; end; end;
Son olarak kullanımından kısa bir örnek vererek makalemizi tamamlayalım:
procedure TForm1.Button2Click(Sender: TObject); var iCounter : Integer; begin Downloader := TDownloadManager.Create; Downloader.OnStateChanged := StateChanged; for iCounter := 0 to Downloader.DownloadJobs.Count - 1 do Memo1.Lines.Add(Downloader.DownloadJobs[iCounter].DisplayName + Downloader.DownloadJobs[iCounter].Description); Downloader.Download('Bits Demo', 'Bir adet açıklama', ['c:\BitsDownload\1.zip'], ['http://xyz.com/abc.zip']); Downloader.Download('Bits Demo', 'Bir adet açıklama', ['c:\BitsDownload\2.zip'], ['http://xyz.com/def.zip']); Downloader.Download('Bits Demo', 'Bir adet açıklama', ['c:\BitsDownload\3.zip', 'c:\BitsDownload\4.zip'], ['http://xyz.com/abc.zip', 'http://xyz.com/def.zip']); end;
BackgroundCopyManager_TLB ve untSilentDownloader isimli dosyaları da buradan indirebilirsiniz.
Sevgiler, saygılar..
tugrl hocam , tesekkurler. bu bloga ugradikca insan yeni yeni seyler ogreniyor.
Rica ederim üstad. Bir teşekkür bin makale eder..
Hocam çok güzel bir makale olmuş yine, sayende işletim sisteminide öğrenmiş oluyoruz. Bu makalede özellikle TBL dosyasının oluşturulma çok hoş olmuş, özellikle hazıra alışmış olan bazı programcılara(benim gibi), araştırmanın güzelliğini anlatan harika bir makale, eline sağlık…
İyi günlerde kullan Olcay’ım
Senden de makale bekliyoruz dememe gerek yok sanırım
merhaba bu siteye İlk kez google amca sayesinde uğradım
tuğrul bey gerçekten acemi bir app.developer olarak
diyebilirm ki harika uygulamalar var.
Bu uygulamaları araştırmak bile çok zor eminim.
uzun söze ne hacet Elinize Sağlık.
Çok güzel bilgiler bunlar.
Elinize, emeğinize sağlık.
Teşekkür ederim.
Tuğrul bey Merhaba,
Öcelikle burada yapmış olduğunuz çalışmalardan dolayı sizi tebrik ederim. Bu konu ile alakalı olurmu bilmiyorum ama bir sorunum var yardımcı olursanız sevinirim. Windows Service uygulaması içerisinden Winexec veya Shellexecute ile uygulama çalıştırmak istediğimde uygulamam show olmuyor.Kısacası Windows Service içerisinden Exe çalıştırma ile yardımlarınızı bekliyorum. Saygılarımla…
Merhaba Nevzat Bey, servis uygulamanız hangi işletim sistemi altında çalışıyor ? Servis uygulamaları ile desktop uygulamaları farklı güvenlik katmanlarında çalışırlar. Eğer bir servis uygulaması içinden çalıştıracağınız uygulama bir başka servis uygulaması ise o zaman o uygulamanın desktop interaction özelliğine bakmanızı önerebilirim. Aksi bir durumda, çalıştıracağınız uygulamanın desktop ortamında çalıştırılabilmesi için ilgili security özelliklerine sahip olması gerekir. Bir servisin içinden çalıştırılan uygulama otomatik olarak o ortamın güvenlik hakları ile başlatılacaktır. Ancak siz bu duruma; OpenProcessToken, CreateProcessAsUser, LookupAccountSid gibi güvenlik API’leri ile biraz haşır neşir olarak müdahale edebilirsiniz.
Eline zihine sağlık usta.
))
“BITS’in indirme işlemine bir an evvel başlayabilmesi adına işletim sisteminin saatini ileri almak bir işe yaramaz. BITS yine ne kadar beklemesi gerekiyor ise o kadar bekleyecektir.” Dumura uğrayan kodcuları düşününce burada koptum.
elinize sağlık tuğrul bey ben delphi 7 kullanıyorum ve pekte iyi sayılmam affınıza sığınarak yukarıda vermiş olduğunuz örnek koddaki Downloader kodunu tanımlatamadım yardımcı olabilirmisiniz ?
1)Private kısmında Procedure StateChanged(const State:TDownloadNotify); prosedurunu tanımla.
2)Bu Prosedurun yapacağı iş aşağıdaki gibi olabilir:
Procedure TForm1.StateChanged(const State:TDownloadNotify);
begin
Case State.DownloadState of
dsQueue:Label3.Caption:=’Kuyrukta bekliyor…’;
dsConnecting:Label3.Caption:=’Bağlanıyor…’;
dsTransferring:Label3.Caption:=’Transfer ediliyor…’;
dsTransferred:Label3.Caption:=’Bitti…’;
dsCancelled:Label3.Caption:=’İptal edildi…’;
End;
3)Button2Click Prosedüründeki Downloader değişkenin tanımını yapmayı unutma…(Downloader:TDownloadManager;) Muhtemelen çalışacaktır.