Challenge 9

Delphi'de kod yazma ile ilgili sorularınızı bu foruma yazabilirsiniz.
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Challenge 9

Mesaj gönderen thelvaci »

Bildiğiniz gibi bir uygulama 5 saniye boyunca mesaj kuyruğunu tüketmez ise; yanıt vermez(Not Responding) durumuna düşer. Hele bu süre daha da uzar ise, kullanıcılar sıkılabilir ve uygulamanın kilitlendiğini düşünüp; uygulamayı sonlandırabilirler.

Bazen bu yanıt vermez görünüm; bizim uzun süreli döngülere girip UI'yi güncellemememizden kaynaklanabileceği gibi; bazen de gerçek kilitlenmelerden de kaynaklanabilir. Velhasıl kullanıcılar sabırsızdır ve her daim cevap veren bir UI isterler. Dolayısı ile uygulamanızı sonlandırabilir kullanıcılar.

Peki ya o arada kritik bir iş yapıyorsanız ! Örneğin; çeşitli nedenlerden ötürü veritabanı ile ilgili yaptığınız işlemleri hafızada yapıyorsunuz. Misal; bir fatura girişi ekranındasınız ve kullanıcı bir alış faturası giriyor. Fatura kalemlerinin de 100 adet olduğunu düşünelim. 60 tanesini girdi ve bir nedenden ötürü uygulamanız yanıt vermez duruma düştü. En iyi ihtimalle; kullanıcı bir müddet bekleyip sonra küfür ederek uygulamayı bir ümit ile kapatıp yeniden açar ve girdiği faturanın sistemde olmasını umar. Ancak, siz işlemlerinizi hafızada (memory dataset) yaptığınız için maalesef sisteme kayıt etme imkanını bulamadınız.

Bu senaryolar elbette fazlası ile çoğaltılabilir. Bu tarz negatif etkiler kullanıcılarınızın karşısına çıkar ise; amiyane tabir ile uygulamanız ağzı ile kuş da tutsa; kısa bir süre sonra değiştirilir ya da iade edilir.

Elbette verilecek cevaplar arasında uzun süren döngüsel işlemlerde ara ara Application.ProcessMessages çağrımını yaparak mesaj kuyruğunu tüketirim diyebilir ya da uzun işlemleri ana thread dışında başka bir thread'in içinde yaparım diyebilirsiniz. Ki bunlar güzel alışkanlıklar. Ancak ben gerçek bir koruma mekanizmasından bahsediyorum. Uygulamanızda thread'de kullansanız bazen deadlock'lar ile karşılaşabilirsiniz. Önceki challange'larımızda dile getirdiğimiz gibi.

Sorunu izah edebilmişimdir umarım. Kullanıcınızın yaptığı kritik bir iş var ve bu işi uygulamanın kilitlenmesi olasılıklarına karşı korumanız gerekiyor. Ne yapardınız, nasıl ? Yaptığınız ya da yapacağınız öneriler lütfen sadece öneride kalmasın; burada paylaşmasanız dahi araştırmanızı somut bir örnek ile destekleyin ki arzuladığımız amaca ulaşabilmiş olalım.

Kurallar: Tek bir kural var; veri kaybetmemek ve bunu merkezi ve esnek bir mekanizma ile sağlamak.
Kullanıcı avatarı
G.Arkas
Üye
Mesajlar: 829
Kayıt: 01 Eki 2007 07:16
Konum: İstanbul
İletişim:

Re: Challenge 9

Mesaj gönderen G.Arkas »

Wallahi hocam aklıma ilk gelen bir arka plan servisi oldu. Yapılan işin ehemmiyetine göre o görevi servise yıkabilirim. En basit örnek Antivirüs yazılımları her şeye kanca atıp duruyorlar gelen geçeni uçanı kaçanı yakalamak için arkada servislerini koşturuyorlar. Her kritik görev için ayrı servis çalışıyor. Ön tarafta kasma esneme gevşeme yok. :) Yalnız burada yapılacak iş önemli tabii. Her şeye de servis olmaz. Scheduler gibi bir mekanizma yapıp gelen işe göre otomatiğe bağlayabiliriz. (Tabii iş statik ise)

Bir diğer seçenek Dll olabilir. Bu dll yi memory'de çalıştırıp orada iş gördürebiliriz ama bu servisten daha riskli bir durum. Memory hızlıdır ama güvenli değildir. Veri kaybına açıktır. (Örnek Windows / Macintosh) Sen bu seçeneği zaten elemişsin yukarıda :?

Başka şeylerde geliyor aklıma ama çok hakim değilim onlara biraz araştırayım yazarım.

http://homes.cs.washington.edu/~tom/pubs/thread89.pdf
Resim
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 9

Mesaj gönderen thelvaci »

Aslında AV yazılımları, tüm kontrolü driver'lar vasıtası ile yapıyorlar. Önceden SSDT hook yapıyorlardı; şimdi ne yapıyorlar bilmiyorum açıkçası. Userland'de her api'yi hook etmek bir AV için kabul edilemez bir şey olurdu ve aynı zamanda da güvensiz. Hook edilebilen bir API kolaylıkla unhook da edilebilir. Genelde ilgili API'nin ilk byte'ının $e9 olması yada bir jump opcode olmasına bakılarak anlaşılabilir hook edilmiş apiler. Elbette başka yöntemlerde var, hook edilen api bulunduktan sonra ilgili API'nin bulunduğu DLL dosyasından PE header okunup EAT üzerinden orjinal byte'ları okunabilir ve hook bertaraf edilebilir. Velhasıl, AV'lerin kullandığı metodoloji değil hedefimiz. Servislerde değil.

Örneğin senin Crew yazılımından dem vuralım. Bu yazılımında önemli bir iş yapıyorsun; kullanıcı için mühim bazı kodlar çalışıyor ve kodlamadaki bir nedenden ötürü uygulama bir müddet yanıt vermez görünüyor, kullanıcı da o arada beklemekten sıkıldı ve uygulamanı sonlandırdı. Ne olacak kullanıcının girdiği yada arka planda işlediğin datalar ? Programı bir daha açtığında kullanıcı kaldığı yerden nasıl devam edecek ?

Küçücük bir ipucu vereyim; yanlış kaynakları araştırmamanız adına. Bu iş için bir API grubu var ve Vista+ sistemlerde kullanılabiliyor ;)
Kullanıcı avatarı
G.Arkas
Üye
Mesajlar: 829
Kayıt: 01 Eki 2007 07:16
Konum: İstanbul
İletişim:

Re: Challenge 9

Mesaj gönderen G.Arkas »

KTM olabilir mi? Kernel Transaction Manager?
Resim
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 9

Mesaj gönderen thelvaci »

G.Arkas yazdı:KTM olabilir mi? Kernel Transaction Manager?
Değil ;-)
xxxjedixxx
Üye
Mesajlar: 216
Kayıt: 10 Ara 2013 03:50

Re: Challenge 9

Mesaj gönderen xxxjedixxx »

Sanırım söz konusu API'ler bunlar:

ApplicationRecoveryCallback
ApplicationRecoveryFinished
ApplicationRecoveryInProgress
GetApplicationRecoveryCallback
GetApplicationRestartSettings
RegisterApplicationRecoveryCallback
RegisterApplicationRestart
UnregisterApplicationRecoveryCallback
UnregisterApplicationRestart

https://msdn.microsoft.com/en-us/librar ... s.85).aspx

Şahsen bana oldukça ilginç geldi. Delphi ile yapılmış bir kurtarma örneği varsa çok memnun olurum.

Teşekkürler.
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 9

Mesaj gönderen thelvaci »

xxxjedixxx yazdı:Sanırım söz konusu API'ler bunlar:

ApplicationRecoveryCallback
ApplicationRecoveryFinished
ApplicationRecoveryInProgress
GetApplicationRecoveryCallback
GetApplicationRestartSettings
RegisterApplicationRecoveryCallback
RegisterApplicationRestart
UnregisterApplicationRecoveryCallback
UnregisterApplicationRestart

https://msdn.microsoft.com/en-us/librar ... s.85).aspx

Şahsen bana oldukça ilginç geldi. Delphi ile yapılmış bir kurtarma örneği varsa çok memnun olurum.

Teşekkürler.
:bravo: Tebrikler, evet API'ler bunlar. Biraz uğraşın bakalım; maksat yeni bir şeyler öğrenmeye kapı aralamak zaten. Deneyip kendiniz yapar iseniz çok daha kalıcı olacaktır. Ben zaten bir müddet sonra bir örnek proje paylaşacağım.
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 9

Mesaj gönderen thelvaci »

Küçük bir ipucu: İlgili API'lerin çalışıp görevlerini yerine getirebilmeleri için uygulamanızın en az 1 dakika boyunca çalışıyor olması gerekir ;)
Kullanıcı avatarı
esistem
Üye
Mesajlar: 464
Kayıt: 02 Eki 2007 11:22
İletişim:

Re: Challenge 9

Mesaj gönderen esistem »

Şahsen bu işin api ler ile yapılabilieceğini bilmiyordum, gerçi ben sadece veritabanı olarak bakıyorum olaya.
Benim Şahsi çözümüm şu şekilde oluyor,
Kullanıcı username ve şifre ile oturum açtığında yaptığı her işlemde kullanıcı kodunu kaydediyorum, diyelim veritabanında fatura tablosu var, birde fatura_temp adında tablo oluşturuyorum, kullanıcı sürekli olarak bu geçici tabloda işlem yapıyor , ne zamanki kaydet butonuna basıyor, bu tablodaki verileri alıp ana tabloya ekliyorum. Diyelim temp tablosuna 10-20-30 neyse stok girdi, elektrik kesildi başka bişi oldu program kilitlendi vs.vs. kullanıcı yeniden girdiğinde, işlem, fatura, irsaliye, çek, senet vs.vs. her nerde yarım kaldıysa, o menuye girdiği gibi daha önce kaydettiği verileri ekrana döküyorum, devam etmek istermisiniz gibi bir uyarı veriyorum oluyor bitiyor.

Sanırım sizin bahsettiğiniz sadece VT değilde ne bilim mesela excelde bi dosya açık, oldu elektrik gitti, makina yeniden başlayınca excel tablosu ekrana geliyor gibi bişi.
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 9

Mesaj gönderen thelvaci »

esistem yazdı:Şahsen bu işin api ler ile yapılabilieceğini bilmiyordum, gerçi ben sadece veritabanı olarak bakıyorum olaya.
Benim Şahsi çözümüm şu şekilde oluyor,
Kullanıcı username ve şifre ile oturum açtığında yaptığı her işlemde kullanıcı kodunu kaydediyorum, diyelim veritabanında fatura tablosu var, birde fatura_temp adında tablo oluşturuyorum, kullanıcı sürekli olarak bu geçici tabloda işlem yapıyor , ne zamanki kaydet butonuna basıyor, bu tablodaki verileri alıp ana tabloya ekliyorum. Diyelim temp tablosuna 10-20-30 neyse stok girdi, elektrik kesildi başka bişi oldu program kilitlendi vs.vs. kullanıcı yeniden girdiğinde, işlem, fatura, irsaliye, çek, senet vs.vs. her nerde yarım kaldıysa, o menuye girdiği gibi daha önce kaydettiği verileri ekrana döküyorum, devam etmek istermisiniz gibi bir uyarı veriyorum oluyor bitiyor.

Sanırım sizin bahsettiğiniz sadece VT değilde ne bilim mesela excelde bi dosya açık, oldu elektrik gitti, makina yeniden başlayınca excel tablosu ekrana geliyor gibi bişi.
Temp tablo kullanmadığınızı; bu işi memory dataset'te yaptığınızı bir hayal edin ;) Örnekler elbette çoğaltılabilir.
PROGRAMADOR
Üye
Mesajlar: 239
Kayıt: 04 Oca 2008 01:53
Konum: Karşıyaka/İzmir

Re: Challenge 9

Mesaj gönderen PROGRAMADOR »

Api bilgileri şöyle imiş:

Kod: Tümünü seç



WORD WINAPI ApplicationRecoveryCallback(
                       PVOID pvParameter
);


VOID WINAPI ApplicationRecoveryFinished(
  __in          BOOL bSuccess
);

HRESULT WINAPI ApplicationRecoveryInProgress(
  __out         PBOOL pbCanceled
);

HRESULT WINAPI GetApplicationRecoveryCallback(
  __in          HANDLE hProcess,
  __out         APPLICATION_RECOVERY_CALLBACK* pRecoveryCallback,
  __out         PVOID* ppvParameter,
  __out         DWORD dwPingInterval,
  __out         DWORD dwFlags
);

HRESULT WINAPI GetApplicationRestartSettings(
  __in          HANDLE hProcess,
  __out_opt     PWSTR pwzCommandline,
  __in_out      PDWORD pcchSize,
  __out_opt     PDWORD pdwFlags
);

HRESULT WINAPI RegisterApplicationRecoveryCallback(
  __in          APPLICATION_RECOVERY_CALLBACK pRecoveryCallback,
  __in_opt      PVOID pvParameter,
  __in          DWORD dwPingInterval,
  __in          DWORD dwFlags
);


HRESULT WINAPI RegisterApplicationRestart(

  __in_opt      PCWSTR pwzCommandline,
  __in          DWORD dwFlags
);


HRESULT WINAPI UnregisterApplicationRecoveryCallback(void);

HRESULT WINAPI UnregisterApplicationRestart(void);

Bunları delphi'ye uyarlayarak şuraya kadar geldim:

Kod: Tümünü seç

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;



function ApplicationRecoveryCallback(pvParameter:pointer):word; stdcall;
procedure ApplicationRecoveryFinished(bSuccess:boolean);stdcall;
function ApplicationRecoveryInProgress(pbCanceled:boolean):LongInt;stdcall;
function GetApplicationRecoveryCallback
        (hProcess:tHANDLE;
        pRecoveryCallback:^APPLICATION_RECOVERY_CALLBACK;
        ppvParameter:PPointer;
        dwPingInterval:cardinal;
        dwFlags:cardinal):LongInt;stdcall;
function GetApplicationRestartSettings
        (hProcess:tHANDLE;
        pwzCommandline : pwidechar;
        pcchSize : ^cardinal;
        pdwFlags : ^cardinal):LongInt;stdcall;
function RegisterApplicationRecoveryCallback
        (pRecoveryCallback : APPLICATION_RECOVERY_CALLBACK;
        pvParameter : Pointer;
        dwPingInterval : DWORD;
        dwFlags : cardinal):LongInt;stdcall;
function RegisterApplicationRestart
        (pwzCommandline: PWideChar;
        dwFlags: cardinal):LongInt;stdcall;
function UnregisterApplicationRecoveryCallback:LongInt;stdcall;
function UnregisterApplicationRestart:LongInt;stdcall;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function ApplicationRecoveryCallback; external 'kernel32.dll'
         name 'ApplicationRecoveryCallback';
procedure ApplicationRecoveryFinished; external 'kernel32.dll'
         name 'ApplicationRecoveryFinished';
function ApplicationRecoveryInProgress; external 'kernel32.dll'
         name 'ApplicationRecoveryInProgress';
function GetApplicationRecoveryCallback; external 'kernel32.dll'
         name 'GetApplicationRecoveryCallback';
function GetApplicationRestartSettings; external 'kernel32.dll'
         name 'GetApplicationRestartSettings';
function RegisterApplicationRecoveryCallback; external 'kernel32.dll'
         name 'RegisterApplicationRecoveryCallback';
function RegisterApplicationRestart; external 'kernel32.dll'
        name 'RegisterApplicationRestart';
function UnregisterApplicationRecoveryCallback; external 'kernel32.dll'
         name 'UnregisterApplicationRecoveryCallback';
function UnregisterApplicationRestart; external 'kernel32.dll'
         name 'UnregisterApplicationRestart';

procedure TForm1.FormCreate(Sender: TObject);
begin
  //

end;

end.
Çalışmaya devam ediyorum, ancak "APPLICATION_RECOVERY_CALLBACK" ı nasıl tanımlayacağımı anlayamadım.
In dubio pro reo...
Şüpheden sanık/özgürlük yararlanır...
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 9

Mesaj gönderen thelvaci »

Tanımlamalar kısmında aşağıdaki gibi bir ipucunu vereyim:

Kod: Tümünü seç

const
  RECOVERY_DEFAULT_PING_INTERVAL = 5000;

type
  TApplicationRecoveryCallback = function (pvParameter : Pointer) : DWORD; stdcall;
  TRegisterApplicationRestart = function (pwzCommandline: PWideChar; dwFlags: DWORD): HRESULT; stdcall;
  TUnRegisterApplicationRestart = function : HRESULT; stdcall;

  TRegisterApplicationRecoveryCallback = function (
                                                    pRecoveryCallback : TApplicationRecoveryCallback;
                                                    pvParameter : Pointer;
                                                    dwPingInterval : DWORD;
                                                    dwFlags : DWORD) : HRESULT; stdcall;

  TUnregisterApplicationRecoveryCallback = function : HRESULT; stdcall;
  TApplicationRecoveryInProgress = function (out pbCanceled : BOOL) : HRESULT; stdcall;
  TApplicationRecoveryFinished = function (bSuccess : BOOL) : HRESULT; stdcall;
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 9

Mesaj gönderen thelvaci »

Yok mu hâla bu API'leri kullanıp, örnek uygulama geliştirebilen ?
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 9

Mesaj gönderen thelvaci »

Yukarıda verdiğim tanımlara rağmen herhangi bir yerde takılan arkadaşlarım var ise bildirsinler yardımcı olmaya çalışayım. Adı sanı, şekli şemali belli ve son derece faydalı bir API grubuna ilgisiz kaldığınızı düşünmek istemiyorum açıkçası. Acemi arkadaşların yanı sıra, profesyonel arkadaşların da konu ile ilgilenmesini rica ediyorum.

Zorla bir şeyler yaptırmaya çalışıyorum gibi hissettiriyorsunuz arkadaşlar. Lütfen biraz daha ilgili olun.
Kullanıcı avatarı
SimaWB
Üye
Mesajlar: 1316
Kayıt: 07 May 2009 10:42
Konum: İstanbul
İletişim:

Re: Challenge 9

Mesaj gönderen SimaWB »

JEDI kütüphanesinden aldığım yardımlarla aşağıdaki tanımlamaları yaptım:

Kod: Tümünü seç

type
  APPLICATION_RECOVERY_CALLBACK = function (pvParameter : Pointer) : DWORD; stdcall;
  TApplicationRecoveryCallback = APPLICATION_RECOVERY_CALLBACK;

  function ApplicationRecoveryFinished(bSuccess : BOOL) : HRESULT; stdcall;
  function ApplicationRecoveryInProgress(out pbCanceled : BOOL) : HRESULT; stdcall;
  function GetApplicationRecoveryCallback(hProcess: THandle; pRecoveryCallback : TApplicationRecoveryCallback; ppvParameter : PPointer; dwPingInterval : PDWORD; dwFlags : PDWORD) : HRESULT; stdcall;
  function GetApplicationRestartSettings(hProcess: THandle; pwzCommandline : PWideChar; var pcchSize : DWORD; pdwFlags : PDWORD) : HRESULT; stdcall;
  function RegisterApplicationRecoveryCallback(pRecoveryCallback : TApplicationRecoveryCallback;pvParameter : Pointer;dwPingInterval : DWORD;dwFlags : DWORD) : HRESULT; stdcall;
  function RegisterApplicationRestart(pwzCommandline: PWideChar; dwFlags: DWORD): HRESULT; stdcall;
  function UnregisterApplicationRecoveryCallback : HRESULT; stdcall;
  function UnregisterApplicationRestart : HRESULT; stdcall;

implementation

function ApplicationRecoveryFinished;  external kernel32 Name 'ApplicationRecoveryFinished';
function ApplicationRecoveryInProgress; external kernel32 Name 'ApplicationRecoveryInProgress';
function GetApplicationRecoveryCallback; external kernel32 Name 'GetApplicationRecoveryCallback';
function GetApplicationRestartSettings; external kernel32 Name 'GetApplicationRestartSettings';
function RegisterApplicationRecoveryCallback; external kernel32 Name 'RegisterApplicationRecoveryCallback';
function RegisterApplicationRestart; external kernel32 Name 'RegisterApplicationRestart';
function UnregisterApplicationRecoveryCallback; external kernel32 Name 'UnregisterApplicationRecoveryCallback';
function UnregisterApplicationRestart;  external kernel32 Name 'UnregisterApplicationRestart';
Daha sonra; program göçtüğünde bir log dosyası oluşturmak için bir callback fonksiyonu tanımladım:

Kod: Tümünü seç

function RecoveryCallback(pvParameter: Pointer): DWORD; stdcall;
var
  FS: TFileStream;
  MyClass: TObject;
  Mesaj: String;
begin
  FS := TFileStream.Create('C:\Log.txt', fmCreate);
  try    
    Mesaj := Format('%s => %s', [FormatDateTime('dd/mm/yyyy hh:nn:ss', Now), 'Program göçtü :(']);
    FS.Write(Mesaj[1], Length(Mesaj));
    ApplicationRecoveryFinished(True);
  finally
    FS.Free;
  end;  
  Result := 0;
end;
Bir butona basıldığında bu callback fonksiyonu program kurtarma için register ettim:

Kod: Tümünü seç

procedure TFormTest.RegisterClick(Sender: TObject);
var
  Res: HRESULT;
begin
  Res := RegisterApplicationRecoveryCallback(RecoveryCallback, nil, 5000, 0);
  if (Res <> S_OK) then
    ShowMessage('Register error');
end;
Yine başka bir butonla exception oluşturuyorum ama RecoveryCallback fonksiyonu çalışmıyor :(
There's no place like 127.0.0.1
Cevapla