Bu yazıma gecenin bu saatinde başladığıma göre büyük ihtimalle yarın işe geç kalacağım. Ama tamamlandığında da bu konuyu 3 gündür makaleleştirememenin verdiği sıkıntıdan da kurtulmuş olacağım. Sanırım bu kafi derecede haz verir bana.
Hayatının herhangi bir anında herhangi bir husus hakkında birşeyler karalamış olanlar bilirler ki, bir şeyi biliyor olmak ile anlatabiliyor olmak cidden farklı. Bunlar farklı kaabiliyetler sanıyorum. Ve yine yazmaya gönlü kaymış kişiler bilir ki; bazen bir konuyu layıkı ile anlatamama endişesi “acaba hiç mi yazmasam” düşüncesine hayat verir. Bende böyle gitgeller yaşadım bu makale için ama elimden geldiğince kendi üslubum gereği aklıma gelen tüm detaylara da inerek bir cesaretle ya Allah diyip başlıyorum makaleye. Şimdiden okuyanların gözlerini ve zihinlerini yoracağım için helallik diliyorum.
Aslında anlatacaklarım tam bir kiosk uygulaması değil elbette. Ancak bir ucundan dokunuyor. Daha ziyade Unix türevli işletim sistemlerinin popüler KDE, Gnome gibi arayüzlerinde görmeye alıştığımız multi-desktop meselesini anlatmaya çalışacağım. Pek çok kullanıcı bazen muzurluk adına ve bazen de gerçekten işi gereği çoklu masaüstlerine ihtiyaç duyar. İşyerinde çalışırken, başkalarından gizlemek istediği programları başka bir masaüstünde barındırmak isteyen insanların sayısı gerçekten de az değildir. Hatta irili ufaklı pek çok uygulamada vardır sevgili patron ve müdürlerin gözlerinden sizleri korumak için
Bazı yazılımlar ise, muzurluk adına değil de yapmaları icap eden iş gereği başka masaüstlerinde çalışma ihtiyacı hissederler. Kiosk uygulamaları gibi. Bu uygulamalar genelde topluma açık yerlerde herkesin kullanımına sunulduklarından işletim sistemine müdahalenin mümkün mertebe sınırlandırılması gerekir. Bunun için en güzel çözümlerden bir tanesi uygulamanın kendisine has bir masaüstünde çalışmasıdır. Böylelikle bilerek yada bilmeyerek verilecek zararlar minimize edilmiş olur.
Tüm bu düşüncelerin ışığında, bir kaç gün evvel kendime ait bir masaüstü nasıl yaparım diye oturdum araştırma yaptım. Başta herşey iyi gidiyordu. İstediğimi yapabilmiştim. İşletim sistemi üzerinde bir masaüstü oluşturabilmiş, o masaüstüne geçebilmiş ve hatta uygulamamı yalnız başına orada çalıştırmayı başarabilmiştim. Buraya kadar herşey gayet iyi idi. Ancak ne oldu ise ondan sonra oldu Programlama sanatına gönül vermiş insanların uzun makaleleri okurken sıkıldıklarını, konudan uzaklaşma eğilimi gösterdiklerini gözlemledim programlama hayatım boyunca. Bu sebeple, kodsal içeriklere başlayarak ilerlemeyi sizlerin sıkılmaması adına tercih ediyorum.
Sizlere ilk bahsedeceğim API CreateDesktop API’si olacak: CreateDesktop API’si Win32 Programmers Referance’da aşağıdaki şekilde tanımlanmıştır.

HDESK CreateDesktop(
    LPCTSTR lpszDesktop,	// name of the new desktop 
    LPCTSTR lpszDevice,	        // reserved; must be NULL. 
    LPDEVMODE pDevMode,    // reserved; must be NULL
    DWORD dwFlags,	        // flags to control interaction with other applications
    DWORD dwDesiredAccess,	// specifies access of returned handle
    LPSECURITY_ATTRIBUTES lpsa	// specifies security attributes of the desktop
   );
Aynı API’nin Delphi’deki tanımı ise:

function CreateDesktop(lpszDesktop, lpszDevice: PChar;
  pDevmode: PDeviceMode; dwFlags: DWORD; dwDesiredAccess:
  DWORD; lpsa: PSecurityAttributes): HDESK; stdcall;
şeklindedir. Adından anlaşılabileceği gibi bu fonksiyon yeni bir masaüstü oluşturmaktadır. Fonksiyonumuzun birinci parametresi desktop’umuza vereceğimiz isimdir. İkinci ve üçüncü paratemtelerin Nil(Null) olması gerektiği bildirilmektedir yardım dökümanında. dwFlags isimli dördüncü parametre ise çalıştırılacak olan uygulamanın yeni desktopta diğer uygulamalar ile nasıl bir işbirliği yapacağını belirtirmiş. Ancak bunu nasıl yapar, derinlerinde ne gibi ipuçları vardır henüz bilmiyorum. Ancak yardım dosyasında denildiği kadarı ile dwFlags’ın alabileceği değer ya sıfır olabilirmiş yada DF_ALLOWOTHERACCOUNTHOOK olabilirmiş. Biz bu parametre için bu değeri kullanacağız. DF_ALLOWOTHERACCOUNTHOOK sabiti Windows.pas’da 1 değeri ile ifade edilmiştir.
dwDesiredAccess isimli parametremiz ise yeni oluşturacağımız desktopumuzun kaabiliyetlerini ifade edecek. Yine yardım dosyasında aşağıdaki değerlere sahip olabileceğini gözlemleyebilirsiniz:

  DESKTOP_READOBJECTS = 1;
  DESKTOP_CREATEWINDOW = 2;
  DESKTOP_CREATEMENU = 4;
  DESKTOP_HOOKCONTROL = 8;
  DESKTOP_JOURNALRECORD = $10;
  DESKTOP_JOURNALPLAYBACK = $20;
  DESKTOP_ENUMERATE = $40;
  DESKTOP_WRITEOBJECTS = 128;
  DESKTOP_SWITCHDESKTOP = $100;
Bu parametrenin alabileceği değerler son derece mühim. Yeni oluşturacağınız desktop’un yapabilecekleri bu parametrelerle belirleniyor. Biz desktopumuzun her türlü hakka sahip olmasını isteyeceğiz bu uygulamamızda. O sebeple aşağıdaki gibi bir tanım yapacağız:

const
  DESKTOP_ALL = 
                DESKTOP_READOBJECTS or 
                DESKTOP_CREATEWINDOW or
                DESKTOP_CREATEMENU or 
                DESKTOP_HOOKCONTROL or
                DESKTOP_JOURNALRECORD or 
                DESKTOP_JOURNALPLAYBACK or
                DESKTOP_ENUMERATE or 
                DESKTOP_WRITEOBJECTS or 
                DESKTOP_SWITCHDESKTOP;
Son parametremiz ise işletim sisteminde tanımlı olan ve genellikle hep nil bıraktığımız güvenlik parametresi. Bu parametre işletim sistemi güvenliği ile ilgili derin güvenlik bilgileri istiyor, açıkçası bu hususta az bir bilgim var o yüzden sizlere bu parametrenin detaylarını anlatamayacağım. Bu parametremizi de Nil(Null) olarak geçeceğiz. Tüm bu bilgilerin ışığında artık desktop’umuzu oluşturan kodumuzu yazabiliriz:

var
  NewDesktop : HDESK;
begin
  NewDesktop := CreateDesktop(PChar('NewDesktop'), nil, nil, DF_ALLOWOTHERACCOUNTHOOK, DESKTOP_ALL, nil);
  ..
  ..
end;
Evet artık işletim sistemi üzerine yeni bir masaüstü daha açtık. Ama tabii ki henüz onu göremiyoruz. Kaç tane masaüstümüz olduğunu da bilmiyoruz şu anda. Bazı arkadaşlarımın, “Nasıl bilmiyoruz ? Bir tane vardı bir de biz oluşturduk etti iki..” dediğinizi duyar gibiyim Yok yok iki tane değil
İşletim sistemimizde mevcut birden fazla masaüstü var. Arzu ederseniz daha fazla ilerlemeden önce bu söylemimizin doğruluğunu test edelim. Bu bağlamda hemen Win32 API help’imizi açıyor ve CreateDesktop yazıyoruz. Bu fonksiyonun Group yada See also kısmına baktığımızda EnumDesktops isimli fonksiyonu göreceğiz. Adından da anlaşıldığı gibi bu fonksiyon bize işletim sistemindeki masaüstlerini verecek. Gelin birlikte tanımına bir göz atalım;

BOOL EnumDesktops(
    HWINSTA hwinsta,	// handle to window station to enumerate
    DESKTOPENUMPROC lpEnumFunc,	// points to application's callback function
    LPARAM lParam	// 32-bit value to pass to the callback function
   );
ve Delphi’deki tanımı ise aşağıdaki gibidir.

function EnumDesktops(hwinsta: HWINSTA; lpEnumFunc: TFNDeskTopEnumProc; lParam: LPARAM): BOOL; stdcall;
Bu fonksiyonumuzda yine açıklamakta zorlanacağım, henüz tam manası ile hakim olamadığım Window Statiton kavramına bir atıf var. Tanımda gözlemleyebileceğiniz birinci parametrenin tipine dikkat edin: HWINSTA. Yani bir window station tutacağı(handle). Peki Window Station da neyin nesi ? Window Station kendine has bir clipboard’u olan, kendine has global atom tablosu olan, içinde birden fazla masaüstüne sahip olabilen; klavye, mouse ve ekran ile etkileşim kurabildiğiniz bir nevi kapsayıcı güvenlik nesnesi. Bir makina üzerinde birden fazla Window Station olabiliyor. Ama sadece winsta0 olarak adlandırılan birinci Window Station’un interaktiflik özelliği bulunuyor. Bu güvenlik nesnesinin hangi ihtiyaçlara binaen var olduğunu kestirebildiğimi söylesem pek doğru bir kelam etmiş sayılmam. Ancak Terminal server’da her yapılan bağlantının kendi oturumunda(session) bir window station’a sahip olduğunu ve bunun interaktif(kullanıcı ile etkileşimli) olduğunu öğrenmiş bulunuyorum.
Bu parametre hakkında ilerleyen zamanlarda kendi öğrenebildiklerim kadarı ile yeni paylaşımlar yapmayı arzuluyorum, ama sanırım bu makalemiz için bu kadarı yeterlidir. Zaten kendisini pek de fazla kullanmayacağız. Biz yine işletim sistemimizde birden fazla olduğunu iddaa ettiğimiz masaüstlerimizin nasıl bulunacağı konusuna yani EnumDesktops‘a geri dönelim isterseniz. Birinci parametresini zor da olsa geçebildiğimiz fonksiyonun ikinci parametresi TFNDeskTopEnumProc türünde ve callback fonksiyon diye adlandırılan bir fonksiyon pointer’dır. Yani bir fonksiyona işaret eden bir işaretçi. Win32 API dökümanında aşağıdaki gibi tanımlanmıştır:

BOOL CALLBACK EnumDesktopProc(
    LPTSTR lpszDesktop,	// name of a desktop
    LPARAM lParam	// value specified in EnumDesktops call
   );
Delphi’ye ise aşağıdaki gibi çevireceğiz:

function DesktopProc(DesktopName : LPTSTR; Param : LPARAM) : Boolean; stdcall;
Metodun Delphi’deki tanımından sizinde gözlemleyebildiğiniz üzere metodun isminin bir önemi yoktur. Yani illa EnumDesktopProc olması lüzumu yoktur. Gerçi okunaklığı arttırmak adına genellikle API yardımındaki isimlendirme notasyonu tercih edilir ancak ben sizin özgür olduğunuzu ifade edebilmek adına farklı bir isimlendirmeye gittim. Üçüncü parametre ise EnumDesktops tarafından DesktopProc’a olduğu gibi geçilecek bir parametredir.
EnumDesktops fonksiyonu kısaca, kendisine geçilen ikinci parametredeki DesktopProc fonksiyonunu işletim sistemindeki her desktop nesnesi için bir kere çağıracaktır. Şimdi gelelim bu metodu nasıl kullanacağımıza:

  TDesktop = class
  private
    fDesktops : TStrings;
  public
    constructor Create;
    destructor Destroy; override;

    procedure Refresh;

    property Desktops : TStrings read fDesktops;
  end;

  function DesktopProc(DesktopName : LPTSTR; Param : LPARAM) : Boolean; stdcall;
  begin
    TDesktop(Param).Desktops.Add(DesktopName);
    Result := true; 
    // Burada true döndürmek önemlidir. Desktop arama işleminin devam etmesini sağlar, false döndürülmesi bu metodu çağıran EnumDesktops'un durmasına 
    //ve sonraki desktoplar için DesktopProc'u çağırmamasına neden olur.
  end;

  constructor TDesktop.Create;
  begin
    inherited Create;
    fDesktops := TStringList.Create;
    Refresh;
    ..
    ..
  end;

  procedure TDesktop.Refresh;
  begin
    Desktops.Clear;
    EnumDesktops(GetProcessWindowStation, @DesktopProc, Integer(Self));
  end;

  ..
  ..
  ..
  procedure TForm1.Button1Click(Sender : TObject);
  var
    Desktop : TDesktop;
  begin
    Desktop := TDesktop.Create;
    ListBox1.Items := Desktop.Desktops; // ListBox1 nesnesi TListBox türündedir.
    Desktop.Free;
  end;
Yukarıdaki kodumuzu çağırdımızda 3 adet desktop’un işletim sistemimizde varsayılan olarak bulunduklarını gözlemleyeceksiniz. Bunlar sırası ile Default, Disconnect ve WinLogon masaüstleridir. Default desktop’u; adından da anlayacağınız gibi hergün kullandığımız masaüstümüzdür. Disconnect desktop’u Terminal server bağlantılarında kullanılan desktop, son olarak da WinLogon desktop’u Win+L, Ctrl+Alt+Del ile geçtimiz kullanıcı girişinin olduğu masaüstümüzdür. Bu masaüstlerinin ilginç ve kısıtlayıcı bazı yanlarından ilerleyen satırlarda bahsedeceğim.
Bu arada son verdiğim kod örneğini ileride komple bir sınıf haline getireceğimiz TDesktop sınıfı üzerinden anlatmayı uygun buldum. En son kaldığımız noktada bir desktop create edebilmiştik. Ancak hala o desktop’a geçememiş ve birşeyler görememiştik. Gelin şimdi ekranda görebileceğimiz ve haz duyabileceğimiz desktoplar arası geçişi sağlayan metoda hayat verelim. Bunun için yine bir başka API fonksiyonu olan SwitchDesktop metodunu kullanacağız. Yine herzaman olduğu gibi tanımlarına bir göz gezdirelim:

BOOL SwitchDesktop(
    HDESK hDesktop	// handle of desktop to activate
   );	
ve yine herzamanki gibi Delphi tanımımız:

function SwitchDesktop(hDesktop: HDESK): BOOL; stdcall;
Nihayet son derece kısa bir tanıma sahip bir metodumuz oldu. Ancak yine de anlatmakta fayda var kanısındayım. Bu fonksiyon, CreateDesktop yada OpenDesktop API fonksiyonlarından geriye dönen desktop’un tutacağını(handle) kendisine parametre olarak alır ve anında belirtilen masaüstüne geçiş yapar. Bunun bir istisnası, geçiş yapılmak istenilen masaüstünün varsayılan Window Station‘da olmaması durumudur ki bu durumda hata verecektir. Şimdi müsaade ederseniz daha önce tanımına yer verdiğimiz CreateDesktop metodunu ve henüz içeriğini yazmadığımız SwitchDesktop metodunu ilgili sınıfımız TDesktop’a ekleyelim:

  TDesktop = class
    ..
    ..
    procedure CreateDesktop(const Name : String);
    procedure SwitchDesktop(const Name : String);
  end;

  procedure TDesktop.CreateDesktop(const Name : String);
  var
    Handle : HDESK;
  begin
    Handle := Windows.CreateDesktop(PChar(Name), nil, nil, DF_ALLOWOTHERACCOUNTHOOK, DESKTOP_ALL, nil);
    if Handle = 0 then
      raise Exception.Create(Name + ' isimli desktop oluşturulamadı.!');

    Desktops.Add(Name);
  end;

  procedure TDesktop.SwitchDesktop(const Name : String);
  var
    Index : Integer;
    Handle: HDESK;
  begin
    Index := Desktops.IndexOf(Name);
    if Index = -1 then
      raise Exception.Create(Name + ' isimli desktop mevcut değil.!');

    Handle := OpenDesktop(PChar(Name), DF_ALLOWOTHERACCOUNTHOOK, false, DESKTOP_ALL);
    if Handle = 0 then
      raise Exception.Create(Name + ' isimli desktop açılamadı.!');

    Windows.SwitchDesktop(Handle);
    CloseDesktop(Handle);
  end;
SwitchDesktop metodumuzda öncelikle geçiş yapmak istediğimiz desktop’un var olup olmadığını kontrol ediyor ardından da OpenDesktop metodu vasıtası ile o desktop’un handle’ını elde ediyoruz. Ardından SwitchDesktop’umuzu çalıştırıyoruz ve yeni masaüstümüz karşımızda. Sonrada hafıza kullanımına değer veren bir programcı olduğumuz için OpenDesktop ile hafızada ayırdığımız bir miktar yeri hemen serbest bırakıyoruz. Ancak sakın bu kodu hemen denemeyin. Biraz daha sabır istirham edeceğim sizlerden. Eğer bu kodu hemen bu hali ile denerseniz, benim defalarca yapmış olduğum gibi makinanızı restart etmekten başka bir çareniz kalmaz. İşletim sisteminde oluşturulan her desktop nesnesi kendi etki alanı içinde çalışmaktadır. Diğer desktoplardaki pencerelerle iletişim halinde değildir, onlarla haberleşemez. Alt+Tab çağrılarınız diğer desktop’taki pencereler arasında geçişinizi sağlamaz. Task Manager’ı açma çabalarınız da sonuçsuz kalacaktır. İşletim sisteminin bir bug’u olduğunu düşündüğüm Task Manager’ı açma çabası da aslında karşılıksız kalmaz ama Task Manager maalesef beklediğiniz desktop’da değil Default desktop’ta açılacaktır. Dolayısı ile bu kodu şu hali ile çalıştırmak, makinanızı restart etmeniz gerektiğinin güçlü bir delilidir. Makinamı 7-8 kez restart etmek durumunda kaldıktan sonra TDesktop sınıfına aşağıdaki metodu yazmayı uygun buldum;

  procedure TDesktop.SwitchDefaultDesktop;
  var
    Handle: HDESK;
  begin
    Handle := OpenDesktop(PChar('WinSta0\Default'), DF_ALLOWOTHERACCOUNTHOOK, false, DESKTOP_ALL);
    if Handle = 0 then // Çifte güvenlik. Bazı makinalarda WinSta0\Default isminde olabileceğini okudum, işi şansa bırakmak istemedim.
    begin
      Handle := OpenDesktop(PChar('Default'), DF_ALLOWOTHERACCOUNTHOOK, false, DESKTOP_ALL);
      if Handle = 0 then
        raise Exception.Create('Default desktop açılamadı.!');
    end;

    Windows.SwitchDesktop(Handle);
    CloseDesktop(Handle);
  end;
Artık arzu ederseniz ilk denemenizi yapabilirsiniz. Şimdi yazacağımız kod ile restart yapmak zorunda kalmayacaksınız. Buyurun bunca uğraşının sonunda artık desktop’umuzu görelim:

  procedure TForm1.Button1Click(Sender : TObject);
  var
    Desktop : TDesktop;
  begin
    Desktop := TDesktop.Create;
    Desktop.CreateDesktop('Deneme');
    Desktop.SwitchDesktop('Deneme');
    Sleep(25000); // 25 sn bekle..
    Desktop.SwitchDefaultDesktop;
    Desktop.Free;
  end;
Kodumuz Deneme isminde bir masaüstü oluşturacak ve ona geçiş yapacak, ardından 25 saniye boyunca siz desktopunuza bakarken varsayılan masaüstünüze geri döneceksiniz. Sizlerin de farkettiği gibi yeni oluşturduğumuz masaüstümüzde hiçbir şey yoktur. Ne masaüstü resmi, ne başlat menüsü, ne masaüstü simgeleri, ne de taskbar. Uygulamamızda orada değil. Şu hali ile bu masaüstü bizim hiç bir işimize yaramıyor. Bizim; yazacağımız uygulamaları yeni desktop’ta çalıştırabilmeye kesinlikle ihtiyacımız var. Aksi taktirde bunca anlatılanın hiç bir ehemmiyeti olmazdı. Arzu ederseniz onu da yapalım. İstediğimiz herhangi bir uygulamayı diğer bir masaüstünde çalıştırabilmek için CreateProcess isimli API metoduna gereksinimimiz olacak. Buyrun tanımına bakalım:

BOOL CreateProcess(
    LPCTSTR lpApplicationName,	// pointer to name of executable module 
    LPTSTR lpCommandLine,	// pointer to command line string
    LPSECURITY_ATTRIBUTES lpProcessAttributes,	// pointer to process security attributes 
    LPSECURITY_ATTRIBUTES lpThreadAttributes,	// pointer to thread security attributes 
    BOOL bInheritHandles,	// handle inheritance flag 
    DWORD dwCreationFlags,	// creation flags 
    LPVOID lpEnvironment,	// pointer to new environment block 
    LPCTSTR lpCurrentDirectory,	// pointer to current directory name 
    LPSTARTUPINFO lpStartupInfo,	// pointer to STARTUPINFO 
    LPPROCESS_INFORMATION lpProcessInformation 	// pointer to PROCESS_INFORMATION  
   );

function CreateProcess(lpApplicationName: PChar; lpCommandLine: PChar;
  lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
  bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;
  lpCurrentDirectory: PChar; const lpStartupInfo: TStartupInfo;
  var lpProcessInformation: TProcessInformation): BOOL; stdcall;
Görüldüğü gibi pek çok parametresi var. Ben bunlardan birkaçına değinip diğerlerini araştırmayı siz değerli okuyucularıma bırakacağım. Bu parametrelerden en önemlisi lpCommandLine isimli olan peremetredir. Bu parametre çalıştırmak istediğimiz dosyanın path bilgisini yazacağımız yerdir. İkinci ve en can alıcı parametremiz ise lpStartupInfo isimli parametredir. Bu parametre üzerinde biraz durmamız gerekiyor. Herzamanki gibi TStartupInfo türünde olan parametremizin tanımına bir göz gezdirelim:

  _STARTUPINFOA = record
    cb: DWORD;
    lpReserved: Pointer;
    lpDesktop: Pointer;
    lpTitle: Pointer;
    dwX: DWORD;
    dwY: DWORD;
    dwXSize: DWORD;
    dwYSize: DWORD;
    dwXCountChars: DWORD;
    dwYCountChars: DWORD;
    dwFillAttribute: DWORD;
    dwFlags: DWORD;
    wShowWindow: Word;
    cbReserved2: Word;
    lpReserved2: PByte;
    hStdInput: THandle;
    hStdOutput: THandle;
    hStdError: THandle;
  end;

  TStartupInfo = _STARTUPINFOA;
Gördüğünüz gibi bu bir record. Bu record’un içerisinde uygulamız için hayati derecede öneme sahip olan bir parametre mevcut. O da; pointer türünde tanımlanmış olan lpDesktop. İşte bu bizim desktopumuzun adını geçeceğimiz yerden başkası değil. Bir diğer önemli parametre ise DWord tipli cb isimli parametredir. Bu parametreye de SizeOf(TStartupInfo) gibi bir atama yapacağız. Dilerseniz herhangi bir masaüstünde herhangi bir uygulamanın çalıştırılması becerisine haiz metodumuzu da sınıfımıza ekleyelim:

  procedure TDesktop.ExecuteProgram(const Desktop, ExeName: String);
  var
    Index : Integer;

    StartupInfo : TStartupInfo;
    ProcessInfo	: TProcessInformation;
  begin
    Index := Desktops.IndexOf(Desktop);
    if Index = -1 then
      raise Exception.Create(Desktop + ' isimli desktop mevcut değil.!');

    if not FileExists(ExeName) then
      raise Exception.Create(ExeName + ' isimli dosya mevcut değil.!');

    FillChar(StartupInfo, SizeOf(TStartupInfo), 0); // Recordun içeriğini temizliyoruz..
    StartupInfo.cb := SizeOf(TStartupInfo);
    StartupInfo.lpDesktop := PChar(Desktop); // PChar da bir pointer tipidir.

    if not CreateProcess(nil, PChar(ExeName), nil, nil, false, NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo) then
      raise Exception.Create(ExeName + ' isimli uygulama ' + #13#10 + Desktop + ' isimli masaüstünde çalıştırılamadı.!');
  end;
Şimdi bir önceki örneğimize geri dönelim ve o anda çalışan uygulamamızı diğer masaüstünde de çalıştıralım:

  procedure TForm1.Button1Click(Sender : TObject);
  var
    Desktop : TDesktop;
  begin
    Desktop := TDesktop.Create;
    Desktop.CreateDesktop('Deneme');
    Desktop.SwitchDesktop('Deneme');
    Desktop.ExecuteProgram('Deneme', Application.ExeName);
    Sleep(25000); // 25 sn bekle..
    Desktop.SwitchDefaultDesktop;
    Desktop.Free;
  end;
Bu kodu deneyince göreceğiniz gibi, yeni bir masaüstünüz olacak ve çalışan uygulamanızın bir kopyası da diğer masaüstünde hazır halde sizi bekliyor olacaktır. Buraya kadar herşey sorunsuz(7-8 defa restart etmeyi saymaz isek) gitti. Herhangi bir mâni ile karşılaşmadık. Bundan sonra tercihlerinize göre yine karşılaşmayabilirsiniz. Ama sizlerde benim gibi hafıza kullanımı konusunda son derece titizseniz o zaman sizleri bazı sorunlar bekliyor. Benim bu makaleyi yazmaktan imtina ettiğim sorunlar. Herne kadar o sorunların hepsini aşsam da izahı son derece zor hususlar. Ancak yine de elimden geldiğince dilim döndüğünce izaha gayret edeyim.
Nedir bu sorunlar diye iyice meraklanmış olabilirsiniz. Sizleri daha fazla merakta bırakmadan anlatayım. Şimdi bir senaryo üzerinden hareket edelim. Bu senaryoda yeni oluşturduğumuz masaüstümüzde başlat menüsünü, masaüstü simgelerini ve görev çubuğunu görmek istediğimizi düşünelim. O zaman daha evvel yazdığımız ExecuteProgram metodumuz ile explorer.exe‘nin de ilgili masaüstünde çalıştırılması gerekecektir.
Bunu yapmamız kolay, ancak sorun çalıştırmış olduğumuz explorer.exe’nin sonlandırılması sırasında ortaya çıkacak. Zaten varsayılan masaüstümüzde çalışan bir explorer.exe’miz vardı, şimdi ikinci masaüstünde çalışan bir tane daha oldu ve biz hafızayı iktisatlı kullanmak istiyoruz. Dolayısı ile ikinci masaüstü için çalıştırdığımız explorer.exe’nin sonlandırılması gerekli.
Bu problem aklımıza daha önce kullanımını anlattığımız TStartupInfo yapısını getiriyor ve araştırmalara başlıyoruz. Acaba çalışan uygulamalardan elde edebileceğimiz TStarupInfo gibi bir yapı var mı ? Bu bağlamda API help’te dolaşırken GetStartupInfo gibi bir metoda rastlıyoruz. Tanımı aşağıdaki gibidir:

VOID GetStartupInfo(
    LPSTARTUPINFO lpStartupInfo 	// address of STARTUPINFO structure 
   );

procedure GetStartupInfo(var lpStartupInfo: TStartupInfo); stdcall;
Ancak incelemelerimiz bu metodun o anda çalışan uygulamanın bilgilerini döndüreceğini söylüyor. Oysaki bizim explorer.exe’nin bilgilerine ihtiyacımız vardı.! İşte bu noktada aslında bir başka makalenin konusu olabilecek kadar derin bir araştırma ve ayrıma gitmemiz gerekiyor. Uzunca bir müddet düşündükten sonra, sonlandırmak istediğimiz explorer.exe’nin hangisi olduğuna karar verilemediği için hafızada çalışır halde bulunan her iki uygulamaya da bulaşan bir dll yazmaya karar verdim. Bu DLL ilgili exe’lere bulaştıktan sonra GetStartupInfo metodunu o exe içinde çalıştırmalı ve sonucu explorer.exe’yi çalıştıran uygulamaya yada diğer masaüstünde hali hazırda çalışan uygulamamızın kopyasına göndermeliydi. Hafızada çalışan uygulamaya bulaşan kodu uzunca bir araştırma sürecinden sonra mantığı ile anlayıp kodlamıştım ancak bu sefer de bulaşan DLL’in bulaştığı kaynaktan silinmesi gerekiyordu.
Bu sorun da aşıldıktan sonra, artık hafızaki istediğimiz herhangi bir uygulamaya yüklenebilen ve tekrar silinebilen bir mekanizmaya sahiptik. Artık bu mekanizma bize bulaştığı uygulamaların içinde GetStartupInfo metodundan elde ettiği değerli desktop name bilgisini döndürebilirdi. Bir DLL ile bir EXE’nin birbirleri arasında haberleşme birden fazla yöntem ile sağlanabilir. Bu tarz işlemlere genellikle Interprocess Comminication (IPC) adı verilir. Hafızada paylaşılan bir alana yazılan veriler diğer uygulama tarafından görünür kılınabilir, yada daha basit bir yöntem ile DLL ile EXE arasında mesajlaşma API’leri vasıtası ile(SendMessage, PostMessage) haberleşilebilir. Ancak burada kesinlikle unutulmaması gereken bir şeyi daha önce söylediğim halde yeniden ifade etmekte yarar görüyorum. Birbirinden farklı desktoplardaki uygulamalar, birbirleri ile bu şekillerde kesinlikle haberleşemezler. Bu durum hasıl olduğunda socket haberleşmesi daha genel ve etkin bir çözüm olabilir. Bu vesile ile bende iletişim yöntemi olarak socket haberleşmesini tercih ettim. Böylece hangi desktop’ta olduğunuzdan bağımsız olarak sistemin hatasız çalışmasını garanti altına almış olduk.
Sorun üzerinde bunca dönüp dolaştıktan sonra birazda çözüme doğru yol alalım.Şimdi yapmamız gereken 3 şey var.
Çalışan uygulamalara bulaşan bir kod yazmak.
Çalışan uygulamalara bulaşan kodun temizlik kodunu yazmak
Sonlandırmak istediğimiz process’in bulunmasına müteakip çalışacak sonlandırma kodunu yazmak.
Sırası ile hepsini yazalım:

procedure InjectProcess(const RemoteProcessID : Cardinal; const DLLName : String);
var
  RemoteProcessHandle,
  BytesWritten,
  ThreadID,
  RemoteThreadHandle : Cardinal;
  pDLL : Pointer;
begin
  if not FileExists(DLLName) then
    raise Exception.Create(InttoStr(RemoteProcessID) + '->' + DLLName + ' bulunamadı.!');

  RemoteProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, false, RemoteProcessID);

  if RemoteProcessHandle <> 0 then
  begin
    pDLL := VirtualAllocEx(RemoteProcessHandle, nil, Length(DLLName), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    if Assigned(pDLL) then
      if WriteProcessMemory(RemoteProcessHandle, pDLL, PChar(DLLName), Length(DLLName), BytesWritten) then
      begin
        RemoteThreadHandle := CreateRemoteThread(RemoteProcessHandle, nil, 0, GetProcAddress(GetModuleHandle('kernel32.dll'), 'LoadLibraryA'), pDLL, 0, ThreadID);
        if RemoteThreadHandle <> 0 then
        begin
          WaitForSingleObject(RemoteThreadHandle, 2000);
          CloseHandle(RemoteThreadHandle);
        end;
      end;
  end; // if RemoteProcessHandle <> 0 then

  if Assigned(pDLL) then
    VirtualFreeEx(RemoteProcessHandle, pDLL, 0, MEM_RELEASE );

  CloseHandle(RemoteProcessHandle);
end;
Şimdi sıra temizlik kodunda:

procedure CleanProcess(const RemoteProcessID : Cardinal; const DLLName : String);
var
  SnapshotHandle,
  RemoteProcessHandle,
  RemoteThreadHandle,
  ThreadID : Cardinal;

  meCurrent : TModuleEntry32;
  found : Boolean;
  Module: String;
begin
  if not FileExists(DLLName) then
    raise Exception.Create(InttoStr(RemoteProcessID) + '->' + DLLName + ' bulunamadı.!');

  SnapshotHandle := CreateToolHelp32SnapShot(TH32CS_SNAPMODULE, RemoteProcessID);
  meCurrent.dwSize := SizeOf(TModuleEntry32);

  Module32First(SnapshotHandle, meCurrent);

  found := false;
  while 1 <> 2 do
  begin
    Module := meCurrent.szExePath;
    if LowerCase(Module) = LowerCase(DLLName) then
    begin
      found := true;
      Break;
    end;

    if not Module32Next(SnapshotHandle, meCurrent) then Break;
  end;

  if not found then
  begin
    CloseHandle(SnapshotHandle);
    raise Exception.Create('Remote uygulamada istenilen modül bulunamadı.!');
  end;

  RemoteProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, false, RemoteProcessID);

  if RemoteProcessHandle <> 0 then
  begin
    RemoteThreadHandle := CreateRemoteThread(RemoteProcessHandle, nil, 0, GetProcAddress(GetModuleHandle('kernel32.dll'), 'FreeLibrary'), meCurrent.modBaseAddr, 0, ThreadID);
    if RemoteThreadHandle <> 0 then
    begin
      WaitForSingleObject(RemoteThreadHandle, 2000);
      CloseHandle(RemoteThreadHandle);
    end;
  end;

  CloseHandle(RemoteProcessHandle);
  CloseHandle(SnapshotHandle);
end;
Ve nihayet bulunan process’in yok edilmesini sağlayan metodumuz:

procedure KillProcess(const ProcessID : Cardinal);
var
  ProcessHandle : Cardinal;
begin
  if ProcessId <> 0 then
  begin
    ProcessHandle := OpenProcess(PROCESS_TERMINATE, false, ProcessID);
    if ProcessHandle <> 0 then
    begin
      try
      	TerminateProcess(ProcessHandle, 0);
      finally
      	CloseHandle(ProcessHandle);
      end;
    end;
  end;
end;
Daha fazla ilerlemeden önce ne yaptığımız hakkında az çok malümat vermeye çalışalım. Bulaşma kodunda, öncelikle bulaşacağımız process’i OpenProcess ile açıyoruz. Böylelikle bulaşacağımız process’in handle’ını elde etmiş oluyoruz. Hemen ardından VirtualAllocEx ile o process’in kullandığı hafıza bloğunda DLL’imizin path’i ile birlikte adının sığabileceği kadar bir hafıza tahsis ediyoruz. Bu tahsisattan geriye dönen pointer ayrılmış hafıza bölgesini işaret ediyor.Daha sonra WriteProcessMemory ile bulaşacağımız exe’nin ayrılmış olan hafıza bölgesine DLL’imizin adını yazıyoruz. Ve en can alıcı kısım olan CreateRemoteThread metodu vasıtası ile DLL’imizi çalıştırıyoruz. CreateRemoteThread belirtilen process’in belirtilen hafıza bölgesindeki değeri parametre alarak belirtilen metodu çalıştırmakla yükümlüdür. Yani bizim örneğimizde pDLL isimli hafıza bölgesini(ki içinde DLL’imizin adı var) LoadLibraryA metoduna parametre olarak geçecek. Bildiğiniz gibi LoadLibraryA DLL kütüphanelerimizi dinamik olarak yüklemek için kullandığımız Windows API metodudur. Bulaşma işleminin temelinde bu mekanizma yatmaktadır. İlk bakışta karmaşık görünse de , anlamaya çalışarak ilerlediğinizde o derece zor ve karmaşık olmadığını göreceksiniz. Yükleme kodunun tam zıttı olan kodun beni biraz daha uğraştırdığını itiraf etmeliyim. Bu kodda processler içindeki modüllerin hepsini dolaşmak ve kendi modülünüzü(yani DLL’inizi) bulmak ve ardından yine CreateRemoteThread API’sine bu sefer FreeLibrary metodunu geçerek temizlik işlemini yapmanız gerekiyor.
Sizlerinde farkedeceği gibi, DLL’imizin yüklenmesi sırasında çalışan bazı kodlar var ve biz henüz bu kodların neye benzedikleri hakkında fikir sahibi değiliz. Arzu ederseniz biraz da DLL’imizin kodlarına göz gezdirelim:

library RemoteDLL;

uses
  SysUtils,
  Classes,
  Windows,
  Dialogs,
  Messages,
  IdTcpClient;

{$R *.res}

const
  LogFile = 'c:\CreateRemoteThread.txt';

procedure Log(const Message : String);
begin
  with TStringList.Create do
  begin
    if FileExists(LogFile) then LoadFromFile(LogFile);
    Add(Message);
    SaveToFile(LogFile);
    Free;
  end;
end;

procedure EntryPointProc(Reason : Integer);
var
  si : TStartupInfo;
  p  : PChar;
  str: String;
  client : TIdTCPClient;
begin
  case Reason of
    DLL_PROCESS_ATTACH:  //1
      begin
      	FillChar(si, SizeOf(TStartupInfo), 0);
        si.cb := SizeOf(TStartupInfo);
        GetStartupInfo(si);
        p := si.lpDesktop;
        str := p;

        try
          client := TIdTCPClient.Create(nil);
          try
            client.Host := '127.0.0.1';
            client.Port := 9999;
            client.Connect();
            client.WriteLn(str + ',' + InttoStr(GetCurrentProcessId()));
            client.Disconnect;
          except on E: Exception do Log('Connection Problem:' + E.Message);
          end;
        finally
          client.Disconnect;
          client.Free;
        end;

        Log('DLL_PROCESS_ATTACH:' + str + '/' + TimeToStr(Time) + '/' + InttoStr(GetCurrentProcessId()));
      end;
    DLL_THREAD_ATTACH:   //2
      begin
      end;
    DLL_PROCESS_DETACH:  //3
      begin
      	Log('DLL_PROCESS_DETACH:' + TimeToStr(Time));
      end;
    DLL_THREAD_DETACH:   //0
      begin
      end;
  end;
end;

begin
  DLLProc := @EntryPointProc;
  DLLProc(DLL_PROCESS_ATTACH);
end.
Görüldüğü gibi DLL’imiz hafızaya yüklenir yüklenmez DLL_PROCESS_ATTACH bölümüne gidiyor ve bir socket bağlantısı vasıtası ile gönderilmesi gereken mesajı gönderiyor. Şimdi sıra TDesktop sınıfımızın CloseRunningProcesses isimli metodunu yazmaya geldi. Buyurun onu da yazıp makalemizin sonlarına doğru ilerleyelim:

procedure TDesktop.CloseRunningProcesses(const Desktop: String;
  Processes: array of String; const InjectDLLFile : String);
var
  Index,
  iCounter : Integer;
  ProcessName : String;
  ProcessList : TStrings;

  peCurrent : TProcessEntry32;
  SnapshotHandle : Cardinal;
  TCPServer : TIdTCPServer;
  pItem	: TProcessItem;
begin
  Index := Desktops.IndexOf(Desktop);
  if Index = -1 then
    raise Exception.Create(Desktop + ' isimli desktop mevcut değil.!');

  ProcessList := TStringList.Create;
  TCPServer := TIdTCPServer.Create(nil);

  try
    TCPServer.Bindings.Clear;
    with TCPServer.Bindings.Add do
    begin
      IP := '127.0.0.1';
      Port := 9999;
    end;
    TCPServer.OnExecute := TCPServerExecuteEvent;
    TCPServer.Active := true;

    for iCounter := Low(Processes) to High(Processes) do
    begin
      ProcessName := Processes[iCounter];
      ProcessName := ExtractFileName(ProcessName); // Path şeklinde olmamalı..
      ProcessList.Add(ProcessName);
    end;

    InjectCount := 0;
    SnapshotHandle := CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS, 0);
    peCurrent.dwSize := SizeOf(TProcessEntry32);

    Process32First(SnapshotHandle, peCurrent);
    while 1 <> 2 do
    begin
      ProcessName := peCurrent.szExeFile;
      if ProcessList.IndexOf(ProcessName) <> -1 then
      begin
          Inc(InjectCount);
          InjectProcess(peCurrent.th32ProcessID, InjectDLLFile); // Load &amp;amp;amp;amp;amp;amp; Execute DLL
          CleanProcess (peCurrent.th32ProcessID, InjectDLLFile); // UnLoad DLL
      end;

      if not Process32Next(SnapshotHandle, peCurrent) then Break;
    end;

    while InjectCount > 0 do Application.ProcessMessages;

    for iCounter := 0 to KillProcessList.Count - 1 do
    begin
      try
        pItem := TProcessItem(KillProcessList[iCounter]);
        if pItem <> nil then
          if pItem.Name = Desktop then
            KillProcess(pItem.Handle);
      except
      end;
    end;
  finally
    ProcessList.Free;
    TCPServer.Active := false;
    TCPServer.OnExecute := nil;
    TCPServer.Free;
  end;

  CloseHandle(SnapshotHandle);
end;
Geriye kalan kodlar pek de ehemmiyetli kodlar değil, çeşitli kontroller, silinmesi istenilen uygulamaların listesini tutan burada anlatmaya değer olmayan kod parçacıkları. Bu sebeple onları anlatma gereği duymuyorum. Ancak kodları daha rahat ve bir bütün halinde inceleyebilmeniz adına ilgili sınıfın pas dosyasını da buraya koyuyorum.
Çok uzun bir makale oldu gerçekten, sabırla okuyabilenlere teşekkür ederim.
Saygılar, sevgiler..
Not: CleanProcess metodunu explorer.exe ve diğer şüphelendiğiniz processler için çalıştırabilirsiniz. Benim explorer.exe ile ilgili testlerim sırasında FlashGet programının bir DLL’inin explorer.exe içerisinde yüklü olduğunu gördüm. Bu normal mi değil mi tam emin olmamakla birlikte yine de şüphe çekici..
“Bir Kiosk ve CreateDesktop macerası..” için 36 Yorum
Özkan Danacı diyor ki:	
22 Mayıs 2009, 11:00	
Okuyorum.. Okuyorum.. hala Okuyorum..
Cevapla	
Tuğrul HELVACI diyor ki:	
22 Mayıs 2009, 14:09	
Sanırım biraz uzun yazmışım
Cevapla	
Olcay DAĞLI diyor ki:	
23 Mayıs 2009, 10:27	
Uzun uğraşılar sonucu elde etmiş olduğun böylesi bir bilgiyi daha zor ve uzun bir uğraşı ile yazıya döküp anlatabildiğin için tebrik ederim hocam…
Yanlız bir şey belirteyim makale o kadar uzun gelmiyor okuyunca, zira gayet akıcı yazmışsın, eline sağlık hocam
Cevapla	
Veli BOZATLI diyor ki:	
01 Haziran 2009, 13:41	
Sanal alemde gerçekten böyle yararlı ve değerli (araştırılması, oluşturulması, yazılması vs. zor) bilgileri paylaşan kişilerin olduğunu görmek beni çok mutlu ediyor.
Elimizden sadece bir kuru teşekkür etmek geliyor :
Hiçbir karşılık beklemeksizin bilgilerini paylaşanlardan Allah razı olsun…
Cevapla	
Tuğrul HELVACI diyor ki:	
01 Haziran 2009, 13:56	
Allah, “Allah razı olsun” diyenden de demiyenden de razı olsun..
Cevapla	
Numan diyor ki:	
26 Haziran 2009, 15:49	
Makaleyi henüz okumadım ama hemen teşekkür etmek istedim. Ben Delphi de yeniyim, CreateDesktop olayını visual basic de kullanıyordum, ancak bunu Delphi ye bir çırpıda dönüştürmek şu an benim için çok zor bir olaydı. Bu makaleyi henüz okumadım ancak olayı bildiğimden dolayı bunun bana çok faydalı olacağından emin gibiyim, şimdiden teşekkürler aro.
Cevapla	
Tuğrul HELVACI diyor ki:	
26 Haziran 2009, 17:15	
Allah cümlemizden razı olsun.
Cevapla	
Soylu OItu KAYA diyor ki:	
01 Ağustos 2009, 13:52	
Açıkcası çok güzel açıklamalar ve farklı bir yaklaşım.
Programlama konusunda bu tarz makaleleri okurken bazen bir satırın açıklamasını gördüğümde o satırdaki komutun araştırmasına giriyorum ve bir bakıyorum 6 gün geçmiş ve ben o satırlardaki tüm komutları en detaylı şekilde öğrenmişim. Tabiki bunu kendi yazılımıma adapte etme aşamasına gelince iş biraz farklılaşıyor kendi yazılımımda 5 satır kodu kullanıyorum öğrendiğim 500 sayfa açıklama gereksiz kalıyor Biraz düşündüm ve dedim ki kendi kendime bu arkadaş bu kadar yazmış bunun proje dosyasını yada pas dosyasını niye koymamış sonralarda fark ettim ki en altta 1 satırcık yere
” ilgili sınıfın pas dosyasını da buraya koyuyorum.” ibaresi ile pas dosyasını koymuş tebrikler. Proje dosyalarından inceleme yapmak bazen çok daha seri oluyor. Her komuta her yerde ihtiyaç olmayabiliyor. Çalışmalarında başarılar.
Cevapla	
Bora ÇAYIR diyor ki:	
28 Ağustos 2009, 11:29	
Delphiyle ilgili hereşeye varım siteyi bugün keşfettim
konular guzel
Cevapla	
İsmail Aktaş (SHURZAN) diyor ki:	
24 Ekim 2009, 04:05	
Merhaba Tuğrul,
Yazmış olduğun makleyi ve bu sayfanı, Kendi projemde geliştimek istediğim bir çalışmaya ait başka bir win32 API hakkında bilgi ararken rastgeldim, doğrusu konuya hakim ve akıcı bir uslupta ayrıntılara girmişsin ki çok başarılı eline sağlık.
Ben C++ yazılım dilinde BDS 2006 ortamında proje geliştiriyorum. Her ne kadar Delphi’ci değilsem de okuyabiliyorum.
Windows XP için çeşitili ayar ve düzenlemeler yapan Shurzanop 2.0 için makalenizde geçen çalışmayı da uygulamak istiyorum. Eğer bir mahsuru yoksa kodlamayı C++ dili ile geliştirip kullanacağım.
Bu makaleniz için teşekkürler, başka makalelerde bekliyoruz.
Cevapla	
Tuğrul HELVACI diyor ki:	
26 Ekim 2009, 15:49	
Merhaba İsmail bey, elbette gönlünüzce kullanabilirsiniz. Çalışmalarınızda başarılar dilerim.
Cevapla	
mkysoft diyor ki:	
31 Ekim 2009, 02:11	
Çok güzel bir makale olmuş, teşekkür ederim.
Cevapla	
mesut diyor ki:	
11 Mart 2010, 09:32	
hocam çok teşekkür ederim çok güzle bir makale olmuş.
benim ufak bir sorum var .
ben TMemoryStream nesnesi ile şifrelediğim bir dosyayı ram da çalıştırmak istiyorum yani bir yere kopyalanıp oradan çalışmasını istemiyorum.şifrelenmiş dosyayı direk çalıştıracak. makalenizde writeproccesmemory komutunu ksıa analtmışsınız acaba bu komutu kulalnarak direk ram da çalıştırabilirmiyim veya hangi kodu kullanmalıyım
teşekkür ederim.. ayrıetten msn desteğiniz olursa sevinirim.
Cevapla	
Tuğrul HELVACI diyor ki:	
16 Mart 2010, 14:03	
Mesut bey, WriteProcessMemory ile ilgili daha detaylı bilgi paylaşımı virüs ve trojan yapımcılarının işine yarar ve onların ilgi alanıdır genellikle. Zaten konuyu izah edebilmek adına biraz da olsa zararlı olabilecek içeriklere girdik, daha detaylarına girmeyelim
Cevapla	
Mehmet Erdem Korkmaz diyor ki:	
17 Mart 2010, 15:51	
Güzel bir çalışma olmuş. Emeğine sağlık. Bana yol gösterici olmasına rağmen hala yapmayı başaramadığım bir meseleyi sormak isterim.
Benim yapmak istediğim şey winlogon ekranına geçilse bile o ekranın görüntüsünü alabilmek. Bunu bir türlü başaramadım.
Cevapla	
MURAT diyor ki:	
07 Haziran 2010, 16:35	
Helal olsun kardeş,
Harika olmuş baya uğraşmıssın
eline sağlık.
Cevapla	
Tuğrul HELVACI diyor ki:	
07 Haziran 2010, 19:13	
Teşekkür ederim, sağolun.
Cevapla	
Mehmet Kemal Korkmaz diyor ki:	
19 Nisan 2011, 12:26	
tek kelime ile helal olsun diyorum.Bu bilgiler bana altın madeni bulmuş gibi geldi.Saygılar.
Cevapla	
fatih diyor ki:	
31 Temmuz 2011, 20:11	
Güzel makale.Elinize sağlık.
Cevapla	
Ahmet Yeşilçimen diyor ki:	
16 Kasım 2011, 10:15	
Hocam Hastanım Senin yaaa Scrollbarı bile aşşağı çekmem 1 dakikakımı aldı ama canla basla hevesle okuyacağım emekelriniz boşa gitmesn ve tüm insanlar yarar grsn Allah Sizden Razı Olsun Hocammmmmmmmmmmmmmm
Ne Muradın Varsa Versin Seni Çok Seviyorum Abicim.
Cevapla	
Ahmet Yeşilçimen diyor ki:	
16 Kasım 2011, 10:17	
Forever Tuğrul +REPPPPPPPPPP
Cevapla	
Ahmet Yeşilçimen diyor ki:	
16 Kasım 2011, 10:20	
Hocam Söyleseydiniz Sabah Sizi Özel Arabayla Aldırırdık Geç Kalmayın Sizin gibi adama, ülke ihtiyac duyuyor. sayınız az sayınızın devamını getireceğim ins
Cevapla	
Ahmet Yeşilçimen diyor ki:	
18 Kasım 2011, 12:03	
Hocam Kızmıyorsun Değil mi Seni Çok Seviyoruz Biz Özellikle Ben Çok Paylaşımcı Bir İnsan. Allah Senin Gibilerini Başımızdan Eksik Etmesin
Cevapla	
Tuğrul HELVACI diyor ki:	
19 Kasım 2011, 01:18	
Hayır neden kızayım canım. Lâkin o kadar da övgüye layık işler yapmıyorum. Sadece bildiklerimi paylaşıyorum.
Cevapla	
Ahmet Yeşilçimen diyor ki:	
21 Kasım 2011, 15:19	
Sağolun Hocam Benim İçin Gerçekden Çok Şey Yapıyorsunuz En Önemlisi Zaman Harcayıp Bizim İçin Birşeyler Paylaşıyorsunuz Başka Ne Bekliyebiliriz Ki Senden Tek Umudum Senin Gibi Olmak Senden Daha İyi Olmak Allah Razı Olsun.
Cevapla	
Ramazan Apaydın diyor ki:	
28 Nisan 2012, 19:23	
Gerçekten çok güzel bi anlatım, memory konusunda türkçe kaynak bulmak gerçekten zor.
Cevapla	
Barış ATALAY diyor ki:	
04 Temmuz 2013, 10:16	
Yıl olmuş 2013 hala çok seviliyorsunuz selamlar saygılar örnek aldığımız insan
Cevapla	
Tuğrul HELVACI diyor ki:	
07 Temmuz 2013, 14:36	
Teşekkürler Allah razı olsun. “Yıl olmuş 2013 hâla…” diyince, kendimi yaşlı hissettim bir anda
Cevapla	
volki diyor ki:	
24 Temmuz 2013, 10:35	
Merhabalar Tuğrul bey beni hatırlamanız zor ama sene 98 de falan size programlama.com üzerinden ulaşmış ve icq ile çok desteğinizi almış bir kişiyim yıllar sonra tekrar buldum sizi hala delphi ile ilgilenmeniz hoşuma gitti. Mümkün olursa sizden writeproccesmemory writeproccesmemory ile ilgili bir sorunum var çok araştırdım ama dediğiniz gibi virüs yazılabildiği için çok bi kaynak bulamadım size ulaşabileceğim bir skype, msn, email adresi varsa sorumu sormam mümkün mü acaba.
İyi Çalışmalar
Volkan ÇITIRKI
Cevapla	
Tuğrul HELVACI diyor ki:	
27 Ekim 2013, 08:42	
Merhaba volkan bey; isminiz hiç de yabancı gelmedi bana ama tabii neler konuştuk o zamanlar, iletişimimiz nasıldı bunları hatırlayamıyorum. Yaşlılık işte Size bir mail gönderdim.
Cevapla	
Barış ATALAY diyor ki:	
27 Temmuz 2013, 09:40	
Hocam ziyaretçi defteri kapalı olduğu için buradan yazıyorum. OmniThreadLibrary Kitabı ne alemde sabırsızlıkla bekliyoruzda
Cevapla	
Tuğrul HELVACI diyor ki:	
27 Ekim 2013, 08:34	
Maalesef çeviriyi Ramazan GÜLBAHAR arkadaşımız yapacaktı. Ancak o zamandan bu zamana değin kendisinden herhangi bir haber alamadık. Zaten Primoz da ümidi kesmiş olacak ki leanpub’da artık türkçe ile ilgili herhangi bir ibare bulunmuyor.
Cevapla	
Barış ATALAY diyor ki:	
16 Kasım 2013, 11:57	
Gerçekten mi çok üzüldüm hocam kaç zamandır bekliyordum. Yıkılmak deyimi bu olsa gerek :-/
Diğer arkadaşlarında dediği gibi;
“Yıl olmuş 2013 hala çok seviliyorsunuz selamlar saygılar”
Cevapla	
erkansenturk diyor ki:	
11 Aralık 2013, 21:48	
makalelerini geç..
oldu ama yeni keşfettim..açıkçası oldulça iyi ve yenilikçisiniz..daha dogrusu ezberci kod anlayışını benimsememeniz anlatım tarzınıza yansımış..bu makale için explorer.exe belllek tasarrufu için servis uygulamasına process ları gömsek daha şık olmazmı!ayrıca bu makalenizin mümkünde pas dosyasını maille gönderebilirmisiniz..şimdiden çok teşekkür ederim
Cevapla	
Tuğrul HELVACI diyor ki:	
14 Aralık 2013, 20:31	
Explorer.exe’nin çalıştırılmasını ilgili desktop’ta alışık olduğumuz öğeleri göstermek amacı ile yapmıştık. Açtığımız explorer.exe’yi kapatmamız gerektiğine göre(eğer memory’e saygılı isek); bunun birden fazla yolu olabilir. Tamamen programcının tercihi, ister sürekli çalışan bir uygulamaya bir mesaj atarak ilgili exe’nin hayat döngüsünü kontrol edersiniz isterseniz de başka yöntemler seçebilirsiniz. Ben en ideal yöntemin bu olduğunu iddia etmiyorum, zaten makalelerim de hazır çözümlere yönelik değil, daha ziyade “bir şeyler öğretebilirsem ne mutlu bana” tadında makaleler yazmaya gayret ediyorum.
Kaynak kodu için ise makalenin sonunda küçük bir paylaşımım var, oradan indirebilirsiniz. Tüm proje kodlarını bilerek indirime açmıyorum, copy/paste tembelliğine neden olmamak için. Dediğim gibi kısıtlı bilgilerimle de olsa bir şeyler öğretebilmek adına bu makaleler var..
Cevapla	
erkansenturk diyor ki:	
16 Ocak 2014, 00:32	
ben yeni masaustunde explorer exe kopyasi calistirmak istemedim.tipki linux ortamindaki gibi kabuk mantigi olusturmak istedim.. 1)problem:default masaustune gectigimde bir onceki masaustu kapanmiyor….enumdesktop fonk. kontrol ediyorum..acik donuyor..2)problem:yeni olusturduğum masaustunde istedigim kisayollar,klasor vb. olusturmama ragmen gorunmuyor..
Cevapla	
Barış ATALAY için yorum yazın
İsim (gerekli)
E-posta (gerekli)
Webite
Orhan diyor ki: Teşekkürler, güzel bir yazı kaleme almışsınız....
Coder22 diyor ki: Kitabın içeriği hakkında bir iki satır yazsaydınız...
Yüksel Demircioğlu diyor ki: Güel bir tespit olmuş.Teşekkürler......
emre diyor ki: tatukgis senin işini fazlasıyla görür...
emrah diyor ki: seydi arkadaşımızın sorduğu soru beni 2-3 haftadır...
gkyoeeo diyor ki: Спасибо за информацию!!!!!...
Absolute API Byte Arrays Class Reference COM CreateDesktop CreateProcess CreateRemoteThread CreateToolHelp32SnapShot DTS EnumDesktopProc EnumDesktops Function Pointer GetCurrentProcess GetIconInfo GetKeyState GetLastInputInfo GetProcessMemoryInfo GetStartupInfo Hacking Inheritance Interface JavaScript Method Pointer Module32First Module32Next OpenDesktop Persistance Pointer Procedure Pointer Process Process32First Process32Next Reqursion RTTI SetProcessWorkingSetSize SQL Server SwitchDesktop TerminateProcess Thread Untyped Parameters VirtualAllocEx VirtualFreeEx WaitForSingleObject WriteProcessMemory
Plugin WP FlashTime by horoscop 2009.org requires Flash Player 8 or better currency converter calculator.Plugin creat de horoscop | horoscop saptamanal | horoscop zilic | horoscop | play sonic games
Etiketler
Absolute Abstract Classes ActionScript Algoritma API Bug Byte Arrays Class Reference CloseHandle COM CreateDesktop CreateEvent CreateMutex CreateProcess CreateRemoteThread CreateSemaphore CreateToolHelp32SnapShot CreateWaitableTimer DeleteCriticalSection Delphi delphi 2010 DTS dynamic EnterCriticalSection EnumDesktopProc EnumDesktops Flash Function Pointer GetCurrentProcess GetIconInfo GetKeyState GetLastInputInfo GetProcessMemoryInfo GetStartupInfo GetThreadContext GetTickCount GetTickCount64 Hacking Inheritance InitializeCriticalSection Interface InterlockedCompareExchange InterlockedDecrement InterlockedExchange InterlockedExchangeAdd InterlockedIncrement JavaScript LeaveCriticalSection Method Pointer Module32First Module32Next MSSQL OpenDesktop OpenEvent OpenMutex OpenSemaphore OpenWaitableTimer Operator Overloading override Persistance Pointer Procedure Pointer Process Process32First Process32Next Query QueryInterface QueryPerformanceCounter Queue ReleaseMutex ReleaseSemaphore Reqursion ResetEvent ResumeThread RTTI SetEvent SetProcessWorkingSetSize SetThreadContext SetWaitableTimer Smilarity SORT SQL SQL Server static Stream SuspendThread SwitchDesktop TDateTime TerminateProcess TerminateThread Thread TInterfacedObject TValue Untyped Parameters virtual VirtualAllocEx VirtualFreeEx WaitForMultipleObjects WaitForSingleObject Weaver web service WriteProcessMemory ws-security WS-SecurityPolicy ws-trust _AddRef _Release
WP Cumulus Flash tag cloud by Roy Tanck requires Flash Player 9 or better.
Son Yorumlar
Nedir bu Thread’lerden çektiğimiz..! için Orhan
Bu kitabı almak lazım.. Ama ondan önce mümkünse dilimize kazandırmak lazım.. için Coder22
Statik ve dinamik metodlarin garip dunyasi için Yüksel Demircioğlu
Delphi ve Google Maps API için emre
DBX & DataSnap Hakkında Destek Talebi.. için emrah
Son Yazılar
Okuduğum & Okumaya Çalıştığım & Okuyacağım Kitaplar
Bir Ekmek, Bir Battaniye…
Bu kitabı almak lazım.. Ama ondan önce mümkünse dilimize kazandırmak lazım..
DBX & DataSnap Hakkında Destek Talebi..
Destek Talebi
Kategoriler
Genel (4)
IDE (1)
İşletim Sistemi (7)
Programlama (51)
.Net (3)
C# (2)
Delphi (51)
Grafik (2)
Internet (5)
Veritabanı (3)
Win32 (6)
Takvim
Aralık 2021
Pts
Sal
Çar
Per
Cum
Cts
Paz
« Eyl
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Arşivler
Eylül 2014 (1)
Aralık 2012 (2)
Mayıs 2012 (1)
Nisan 2012 (1)
Ağustos 2011 (1)
Nisan 2011 (2)
Haziran 2010 (1)
Mayıs 2010 (1)
Nisan 2010 (3)
Ağustos 2009 (1)
Temmuz 2009 (2)
Haziran 2009 (7)
Mayıs 2009 (32)
Bağlantılar
Bir Türkçe Sevdalısı..
Delphi Dünyası Forum
Delphi Türkiye Forum
Gürcan ÖZTÜRK
M.Fatih KÜÇÜKKELEPÇE
Memik YANIK Kişisel
Memik YANIK’ın Günlüğü
Nick Hodges
Sinan BARAN
Zafer Çelenk
Ziyaretçi Bilgileri
Ziyaret: 0 / 3886
Beğenilenler
Yeni Veri Tipleri ve Operator Overloading - 31 votes
Delphi 2010 (Weaver) ve TValue - 16 votes
Derinlemesine Threading..(3) - 13 votes
Derinlemesine Threading..(2) - 9 votes
DBX & DataSnap Hakkında Destek Talebi.. - 9 votes
XE2 Ile Gerceklesecek Evrime Hazir Misiniz? - 8 votes
Derinlemesine Threading..(1) - 7 votes
Win32 & .Net(Delphi->C#) - 6 votes
Statik ve dinamik metodlarin garip dunyasi - 5 votes
Interface Nedir, Nerelerde ve Neden Kullanırız ? - 5 votes
Meta
Giriş
Yazılar RSS
Yorumlar RSS
WordPress.org
Etiketler-Liste
Absolute API Byte Arrays Class Reference COM CreateDesktop CreateProcess CreateRemoteThread CreateToolHelp32SnapShot DTS EnumDesktopProc EnumDesktops Function Pointer GetCurrentProcess GetIconInfo GetKeyState GetLastInputInfo GetProcessMemoryInfo GetStartupInfo Hacking Inheritance Interface JavaScript Method Pointer Module32First Module32Next OpenDesktop Persistance Pointer Procedure Pointer Process Process32First Process32Next Reqursion RTTI SetProcessWorkingSetSize SQL Server SwitchDesktop TerminateProcess Thread Untyped Parameters VirtualAllocEx VirtualFreeEx WaitForSingleObject WriteProcessMemory