Win32 Debugger Yapıyoruz – 2

    Önceki makalemde sizlerle birlikte Windows debug API’lerini tanımış ve bu API’leri bir C# uygulaması içerisinden nasıl erişebileceğimizi görmüştük. Makalemin sonuna geldiğimizde ise en basit haliyle bir debug uygulaması oluşturabilmiş, çalışan bir uygulamaya bağlanabilmiş ya da debug moduyla yeni bir işlem başlatabilmiştik. Bir debugger’ın en temel bileşeni olan debug döngüsünün nasıl olduğunu görerek basit bir debug döngüsü yapmıştık. Bu makalemde, bağlanılan uygulama içerisinden gönderilen debug mesajlarını ve yüklenen/kaldırılan dll isimlerini nasıl bulabileceğimizi, hata olay bildirimlerinde hatanın seviyesini nasıl bulabileceğimizi sizlerle paylaşıyor olacağım.

   Önceki makalem ve bu makalemdeki paylaştığım konuların pekişmesi açısından örnek bir uygulama üzerinden gidiyor olacağım;

Örnek Debugger uygulamasından bir ekran görüntüsü

    Ekran görüntüsünde de gördüğünüz gibi örnek uygulamamız basit düzeyde bir debugger. Üst bölümde çalışan işlemler listelenmekte ve kullanıcı seçtiği bir işleme "Seçili İşlemi Debug Et" butonu vasıtasıyla hata ayıklama amacıyla bağlanabilmekte. Hata ayıklama amacıyla bir işlem başlatılmak istenirse, ekranda yer alan "Debug Oturumu Başlat" butonuna basılarak gelen diyalogda ilgili exe seçilebilmekte. Hata ayıklama sürecinde işlemden gelen mesajlar ekranın alt tarafında yer alan "İşlem Hareketleri" bölümünde gösterilecektir. Uygulamamız Win32 debug API’lerini kullanması nedeniyle .Net uygulamalarını debug etmeye tam olarak elverişli olmasa da hatalı durumlarda bekleme, debug mesajını alma gibi bazı temel işlevleri .Net uygulamalarında da kullanılabilmekte.

Debuggerı test edebileceğiniz örnek CRM uygulamasından bir ekran görüntüsü

   Örnek debugger uygulamamız ile birlikte, testlerde kullanılmak üzere, oldukça basit ve herhangi bir iş mantığı bulunmayan bir CRM uygulaması bulabilirsiniz. Hata ayıklama konusundaki önceki makalelerimi takip edenlerinizin aşina olacağı bu CRM uygulaması hata yakalamayı test edilebilmesi adına bir hata barındırmaktadır. Uygulama açıldığında gelen ekranda herhangi bir değer girmeden kaydet butonuna basıldığında uygulamada hata oluşacaktır. Örnek bir debug mesajını debuggerımızda görebilmek amacıyla bu örnek CRM uygulaması kayıt işlemine başlamadan önce aşağıdaki satırla bir debug mesajı vermektedir;

Debug.WriteLine("Müşteri kayıt işlemi başlatılıyor");

    İsterseniz debuggerımızla öncelikle CRM uygulamasınca gönderilen bu debug mesajını nasıl gösterebileceğimizi görelim. Pek çok uygulama debugger’a mesaj gönderebilmek için Kernel32.dll içerisinde yer alan OutputDebugString  Win32 API fonksiyonunu kullanmaktadır. .Net ailesi de buna bir istisna olmayıp mesajların gönderimi sırasında aynı fonksiyonu kullanmaktadır. Dolayısıyla native uygulamalarda olduğu gibi managed CRM uygulamamızdan göndereceğimiz debug mesajını da yakalayabilmemiz mümkün. İşletim sistemi hata ayıklamak için takip edilen bir uygulama bu fonksiyonu kullanarak debug mesajı gönderdiğinden bu durumu debugger’a OUTPUT_DEBUG_STRING_EVENT olayını bildirerek ilgili olaya ait verileri OUTPUT_DEBUG_STRING_INFO struct’ıyla paylaşıyor olacaktır. MSDN web sitesinden detaylı dokümantasyonunu bulabileceğiniz struct’ını C# içerisinde aşağıdaki yapıyla kullanabiliyor oluruz;

[StructLayout(LayoutKind.Sequential)]
public struct OUTPUT_DEBUG_STRING_INFO {
    public IntPtr lpDebugStringData;
    public UInt16 fUnicode;
    public UInt16 nDebugStringLength;
}

    Pekçoğunuzun beklediğinin aksine yukarıda da görüldüğü gibi debug mesajı altın bir tepsi ile bize sunulmamakta, ulaşmak için biraz çaba sarf etmemiz gerekli 🙂 Olay bildirimi sonrasında elimizde şu üç bilgi olacaktır; debug mesajının bulunduğu hafıza adresi, unicode olup olmadığı ve debug mesajının uzunluğu. Benim gibi C++ geçmişi olanlar bu noktada nasıl hareket edeceklerini az çok biliyor olacaktır; verilen hafıza adresine giderek unicode olup olmamasına göre verilen uzunluk kadar hafıza alınıp okunmalı ve gelen veri string’e dönüştürülmeli! Burada dikkatinizi çekecektir; unicode olup olmadığı bilgisi bool türünde değildir. fUnicode alanının 0 değerine sahip olması durumunda mesaj ANSI, aksi durumlarda ise UNICODE olarak tutulmaktadır. Buradaki püf noktası, iletilen adres bilgisinin takip edilen uygulamanın hafıza alanına göre verilmiş bir adres, göreceli bir adres olmasıdır. Veri uygulamanın hafıza alanı içerisinden okunmalıdır. Bu veriyi okumak için kernel32.dll içerisinde bulunan ReadProcessMemory fonksiyonu kullanılabilir. Aşağıda C# içerisinde kullanılması için gerekli olan tanımlamayı bulabileceğiniz bu fonksiyon öncelikle okumanın yapılacağı işleme ait handle’ı beklemektedir. Verilen adresten okunan bilgiler ise lpBuffer ile belirtilen byte dizisine yazılıyor olacaktır. Okunan veri boyutu ise lpNumberOfBytesRead parametresi ile geri bildirilmektedir.

[DllImport("kernel32", SetLastError = true, PreserveSig = true)]
public static extern bool ReadProcessMemory(IntPtr hProcess, 
                                            IntPtr lpBaseAddress, 
                                            [Out] byte[] lpBuffer, 
                                            int dwSize, 
                                            out int lpNumberOfBytesRead);

   ReadProcessMemory  fonksiyonu bir hafıza alanını okurken çağrılan işlemin okunacak olan işlem hafıza alanına erişim yetkisi olmasını beklemektedir. Önceki makalemi hatırlayacak olursanız yeni bir oturumla işlem başlatarak ya da bağlanarak debug işlemine başladığımız noktada işletim sistemi, hata ayıklaması yapılan işlem ile debugger arasında parent-child ilişkisi kurmakta. Bu durumda da üst işlem olan debugger rahatlıkla alt işlemi olan hata ayıklaması yapılan uygulamanın hafıza alanına erişebiliyor olacaktır.

    Bu bilgiler ışığında, aşağıdaki C# kod parçacığı ile uygulamanın belirtilen hafıza alanına ulaşabilir ve debug mesajını okuyabiliriz;

var outputDebugString = debugEvent.union.OutputDebugString;
var debugStringUzunlugu = outputDebugString.nDebugStringLength;

var tampon = new byte[debugStringUzunlugu];
int okunanByteSayisi;

ReadProcessMemory(hProcess, outputDebugString.lpDebugStringData, tampon, debugStringUzunlugu, out okunanByteSayisi);

    Veriyi byte dizisi olarak okuduktan sonraki adımımız ise string ifadeye çevirmek olmalı;

if (outputDebugString.fUnicode == 0) {
    encoding = System.Text.Encoding.GetEncoding(1254);
}
else {
    encoding = new System.Text.UnicodeEncoding();
}

var mesaj = encoding.GetString(tampon);

   Burada  göz önüne almanız gereken bir diğer noktada mesajın unicode olup olmamasına göre okunması gerekli alan uzunluğunun değişiyor olduğudur. Bildiğiniz gibi UNICODE karakterler hafızada ANSI karakterlerden farklı olarak 2 byte yer kaplamaktadır. Bu durumda UNICODE mesajlar için okunması gereken alan boyutu bildirilenin iki katı olmalıdır.

Debug işlemi sırasında gelen debug mesajı

   İlgili hafıza alanına ulaşarak debug mesajını okumamız ardında debugger uygulamamız bunu kullanıcıya gösterebilecektir. Örnek debuggerımızda bu mesaj İşlem Hareketleri bölümünde yer alan  RichTextBox içerisinde görülebilecektir.

   İşletim sistemince iletilen hata ayıklaması yapılan uygulamanın dll yüklemesi/kaldırması olay bildirimleri de debug mesajında olduğu gibi bize doğrudan kullanıma hazır bir bilgi sunmamaktadır. LOAD_DLL_DEBUG_EVENT ve UNLOAD_DLL_DEBUG_EVENT bildirimleri ile iletilen bu olaylara ait bilgiler sırasıyla LOAD_DLL_DEBUG_INFO ve UNLOAD_DLL_DEBUG_INFO struct’ları ile bizlere iletilecektir. Aşağıda her iki struc’ın C# içerisinde tanımlanmasına dair kod parçacıklarını bulabilirsiniz;

[StructLayout(LayoutKind.Sequential)]
public struct LOAD_DLL_DEBUG_INFO {
    public IntPtr hFile;
    public IntPtr lpBaseOfDll;
    public UInt32 dwDebugInfoFileOffset;
    public UInt32 nDebugInfoSize;
    public IntPtr lpImageName;
    public UInt16 fUnicode;
}

[StructLayout(LayoutKind.Sequential)]
public struct UNLOAD_DLL_DEBUG_INFO {
    public IntPtr lpBaseOfDll;
}

    Dikkat edecek olursanız yüklenen ya da kaldırılan dll adına doğrudan bir referans verilmeyerek dll hakkındaki bilgilere yer verilmiş durumda. Debug mesajını almak için yaptığımız işlemlere benzer şekilde dll adına ulaşabilmek için de Win32 API’leri ile fazlasıyla muhatap olmamız gerekecektir. Verilen dll handle’ını kullanarak dll adına ulaşmayı sağlayan kodu aşağıda bulabilirsiniz. Benim tarafımdan yazılmamış olan bu kodun orjinali stackoverflow sitesinde bulunabilir;

internal class FileName {
    [DllImport("kernel32.dll")]
    static extern uint GetFileSize(IntPtr hFile, IntPtr lpFileSizeHigh);

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern IntPtr CreateFileMapping(
        IntPtr hFile,
        IntPtr lpFileMappingAttributes,
        FileMapProtection flProtect,
        uint dwMaximumSizeHigh,
        uint dwMaximumSizeLow,
        [MarshalAs(UnmanagedType.LPTStr)]string lpName);

    [Flags]
    public enum FileMapProtection : uint {
        PageReadonly = 0x02,
        PageReadWrite = 0x04,
        PageWriteCopy = 0x08,
        PageExecuteRead = 0x20,
        PageExecuteReadWrite = 0x40,
        SectionCommit = 0x8000000,
        SectionImage = 0x1000000,
        SectionNoCache = 0x10000000,
        SectionReserve = 0x4000000,
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr MapViewOfFile(
        IntPtr hFileMappingObject,
        FileMapAccess dwDesiredAccess,
        uint dwFileOffsetHigh,
        uint dwFileOffsetLow,
        uint dwNumberOfBytesToMap);

    [Flags]
    public enum FileMapAccess : uint {
        FileMapCopy = 0x0001,
        FileMapWrite = 0x0002,
        FileMapRead = 0x0004,
        FileMapAllAccess = 0x001f,
        fileMapExecute = 0x0020,
    }

    [DllImport("psapi.dll", SetLastError = true)]
    public static extern uint GetMappedFileName(IntPtr m_hProcess, IntPtr lpv, StringBuilder
            lpFilename, uint nSize);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool CloseHandle(IntPtr hObject);

    public static string GetFileNameFromHandle(IntPtr FileHandle) {
        string fileName = String.Empty;
        IntPtr fileMap = IntPtr.Zero, fileSizeHi = IntPtr.Zero;
        UInt32 fileSizeLo = 0;

        fileSizeLo = GetFileSize(FileHandle, fileSizeHi);

        if (fileSizeLo == 0) {
            // cannot map an 0 byte file
            return "Empty file.";
        }

        fileMap = CreateFileMapping(FileHandle, IntPtr.Zero, FileMapProtection.PageReadonly, 0, 1, null);

        if (fileMap != IntPtr.Zero) {
            IntPtr pMem = MapViewOfFile(fileMap, FileMapAccess.FileMapRead, 0, 0, 1);
            if (pMem != IntPtr.Zero) {
                StringBuilder fn = new StringBuilder(250);
                GetMappedFileName(System.Diagnostics.Process.GetCurrentProcess().Handle, pMem, fn, 250);
                if (fn.Length > 0) {
                    UnmapViewOfFile(pMem);
                    CloseHandle(FileHandle);
                    return fn.ToString();
                }
                else {
                    UnmapViewOfFile(pMem);
                    CloseHandle(FileHandle);
                    return "Empty filename.";
                }
            }
        }

        return "Empty filemap handle.";
    }
}

    Elinizde bu sınıf olunca işlerin geri kalanı oldukça kolaylaşıyor. Tek yapmanız gereken aşağıdaki kod ile dll dosyası handle’ını bu sınıf içerisindeki static GetFileNameFromHandle fonksiyonuna ileterek dll adının bulunmasını beklemek; 

var yuklenenDllAdi = FileName.GetFileNameFromHandle(debugEvent.union.LoadDll.hFile);

   Dikkat edecek olursanız dll yüklenirken verilen pek çok bilginin aksine dll’in kaldırılması bildirilirken lpBaseOfDll parametresi ile sadece dll’in temel adresine dair bir işaretçi iletilmekte. Peki bu durumda kaldırılan dll adına nasıl ulaşabilirim? Bunun cevabı biraz akıllıca davranmaya dayalı aslına bakarsanız. Bu takip ettiğiniz uygulama için tekil olacağından ve bir dll’in kaldırılabilmesi için öncelikle yüklenmiş olması gerekeceğinden dll yüklenirken temel adresi ve bulunan adı bir sözlükte tutulabilir. Dll kaldırılması olay bildiriminde ise bu sözlükten dll’in temel adresi ile yapılacak bir sorgu ile kaldırılan dll adına ulaşılabilir.

   Takip edilen işlem sonlanırken işletim sistemi debugger’a EXIT_PROCESS_DEBUG_EVENT olay bildirimini yapacaktır. Bu olay bildirimiyle birlikte iletilen ve C# içerisindeki tanımlaması aşağıda verilen EXIT_PROCESS_DEBUG_INFO struct’ı dwExitCode alanında bize uygulamanın çıkış kodunu verecektir.

[StructLayout(LayoutKind.Sequential)]
public struct EXIT_PROCESS_DEBUG_INFO {
    public UInt32 dwExitCode;
}

   Debuggerımız bu olay bildirimi ile birlikte işlemin sonlandığını anlayarak debug döngüsünü sonlandırabilir. Hata ayıklama amacıyla bağlandığımız uygulama ile olan bağlandı beklenmedik bir hata nedeniyle koparak hata ayıklama işlemi yarıda da kalabilir. Bu durumlarda işletim sistemi RIP_EVENT olay bildirimini yapıyor olacaktır.

   Hata ayıklama işleminin en önemli adımlarından birisi de takip edilen uygulama içerisindeki hataların izleniyor olmasıdır. Önceki makalemden de hatırlayacağınız gibi, işlem içerisine konulan breakpoint’ler debugger’a EXCEPTION_DEBUG_EVENT olay bildirimi ile iletilmektedir. Debugger’ın hata ile breakpoint’i STATUS_BREAKPOINT (0×80000003) hata kodu ile ayırt edebileceği bu durumlarda gelen EXCEPTION_DEBUG_INFO struct’ının C# içerisindeki kullanımını aşağıda bulabilirsiniz;

[StructLayout(LayoutKind.Sequential)]
public struct EXCEPTION_DEBUG_INFO {
    public EXCEPTION_RECORD ExceptionRecord;
    public bool dwFirstChance;
}

[StructLayout(LayoutKind.Sequential)]
public struct EXCEPTION_RECORD {
    public ExceptionCode ExceptionCode;
    public ExceptionFlags ExceptionFlags;

    public IntPtr ExceptionRecord;
    public IntPtr ExceptionAddress;

    public UInt32 NumberParameters;

    public IntPtr ExceptionInformation0;
    public IntPtr ExceptionInformation1;
    public IntPtr ExceptionInformation2;
    public IntPtr ExceptionInformation3;
    public IntPtr ExceptionInformation4;
    public IntPtr ExceptionInformation5;
    public IntPtr ExceptionInformation6;
    public IntPtr ExceptionInformation7;
    public IntPtr ExceptionInformation8;
    public IntPtr ExceptionInformation9;
    public IntPtr ExceptionInformation10;
    public IntPtr ExceptionInformation11;
    public IntPtr ExceptionInformation12;
    public IntPtr ExceptionInformation13;
    public IntPtr ExceptionInformation14;
}

public enum ExceptionCode : uint {
    None = 0x0,
    STATUS_BREAKPOINT = 0x80000003,
    STATUS_SINGLESTEP = 0x80000004,
    STATUS_ENTRYPOINT_NOT_FOUND = 0xC0000139,

    EXCEPTION_INT_DIVIDE_BY_ZERO = 0xC0000094,

    EXCEPTION_STACK_OVERFLOW = 0xC00000FD,
    EXCEPTION_NONCONTINUABLE_EXCEPTION = 0xC0000025,
    EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
}

[Flags]
public enum ExceptionFlags : uint {
    None = 0x0,
    EXCEPTION_NONCONTINUABLE = 0x1,
}

    Hata koduna ExceptionRecord.ExceptionCode üzerinden ulaşılabilmesi mümkündür. Dikkat edecek olursanız yukarıdaki kod parçacığında hata kodlarını bir enum içerisinde tutmaktayım. Bu enum sık karşılaşılabilecek olan hata kodlarını listelemekte olup tüm hata kodları yer almamaktadır. Kendi iş mantığınıza göre bu enum’u zenginleştirmeniz mümkündür. .Net uygulamalarında gelen hata mesajı tek olacağından maalesef ki gerçek hata mesajına ulaşmak sizi zorlayan bir süreç olacaktır. ExceptionFlags alanı bize hatanın kritik bir hata olup olmadığı konusunda bilgi verirken dwFirstChance alanında İlk şans ya ikinci şans hatası olup olmadığı bilgisini verecektir.

   Hata olay bildirimi alan debugger, hatanın türüne (ilk şans/ikinci şans)  ve hata koduna göre belirtilen hatayı kendisi yönetebilir (otomatik ya da kullanıcıya sorarak). İş mantığı gereği alınan bu hata bilgisi başarılı bir şekilde yönetilmiş ve gerekli müdahaleler ardından uygulamanın işleyişine kaldığı yerden devam etmesi isteniyorsa DBG_CONTINUE (0x00010002), hata yönetilememiş ise işlemin kendisinin yönetmesi için DBG_EXCEPTION_NOT_HANDLED (0x80010001) devam kodları ile durum işletim sistemine iletilmelidir.

   Alınan hata kodu dışında registery’lerin durumu gibi ek bilgileri kullanıcıya iletmek isterseniz Win32 API’sini biraz daha derinlemesine araştırmanız gerekecektir. Örnek olması adına birlikte hata anındaki registry değerlerini yazdıralım;

   Hata anındaki işlem durumunu alabilmek için kernel32.dll içerisinde yer alan GetThreadContext fonksiyonu kullanılabilir. Verilen thread handle’ı için anlık içeriği verecek olan bu fonksiyonu C# içerisinde kullanabilmek için aşağıdaki kod parçacıkları kullanılabilir;

[DllImport("kernel32", SetLastError = true)]
public static extern bool GetThreadContext(IntPtr hThread, ref CONTEXT lpContext);
   
[StructLayout(LayoutKind.Sequential)]
public struct CONTEXT {
    public CONTEXT_FLAGS ContextFlags; 
        
    public uint Dr0;
    public uint Dr1;
    public uint Dr2;
    public uint Dr3;
    public uint Dr6;
    public uint Dr7;
        
    public FLOATING_SAVE_AREA FloatSave;
        
    public uint SegGs;
    public uint SegFs;
    public uint SegEs;
    public uint SegDs;
        
    public uint Edi;
    public uint Esi;
    public uint Ebx;
    public uint Edx;
    public uint Ecx;
    public uint Eax;
        
    public uint Ebp;
    public uint Eip;
    public uint SegCs;
    public uint EFlags;
    public uint Esp;
    public uint SegSs;
        
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)]
    public byte[] ExtendedRegisters;
}

public enum CONTEXT_FLAGS : uint {
    CONTEXT_i386 = 0x10000,
    CONTEXT_i486 = 0x10000,
    CONTEXT_CONTROL = CONTEXT_i386 | 0x01,
    CONTEXT_INTEGER = CONTEXT_i386 | 0x02,
    CONTEXT_SEGMENTS = CONTEXT_i386 | 0x04,
    CONTEXT_FLOATING_POINT = CONTEXT_i386 | 0x08,
    CONTEXT_DEBUG_REGISTERS = CONTEXT_i386 | 0x10,
    CONTEXT_EXTENDED_REGISTERS = CONTEXT_i386 | 0x20,
    CONTEXT_FULL = CONTEXT_CONTROL | 
                    CONTEXT_INTEGER | 
                    CONTEXT_SEGMENTS,
    CONTEXT_ALL = CONTEXT_CONTROL | 
                    CONTEXT_INTEGER | 
                    CONTEXT_SEGMENTS | 
                    CONTEXT_FLOATING_POINT | 
                    CONTEXT_DEBUG_REGISTERS | 
                    CONTEXT_EXTENDED_REGISTERS
}

[StructLayout(LayoutKind.Sequential)]
public struct FLOATING_SAVE_AREA {
    public uint ControlWord;
    public uint StatusWord;
    public uint TagWord;
    public uint ErrorOffset;
    public uint ErrorSelector;
    public uint DataOffset;
    public uint DataSelector;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)]
    public byte[] RegisterArea;
    public uint Cr0NpxState;
}     

    Aşağıdaki kod parçacığı ise bize anlık context bilgisini iletecektir;

var context = new CONTEXT();
context.ContextFlags = CONTEXT_FLAGS.CONTEXT_ALL;

GetThreadContext(hThread, ref context);

    Dikkat edecek olursanız GetThreadContext  fonksiyonu bizden içeriği istenen iş parçacığının handle’ını beklemekte. Bu bilgi işlem ilk başlatıldığında ya da bağlanıldığında oluşan CREATE_PROCESS_DEBUG_EVENT olayı sırasında aşağıdaki şekilde bulunabilir;

hThread = union.CreateProcess.hThread;

    Kodumuzun devamında ise elde ettiğimiz bu içerikten istediğimiz registery değerlerini kullanıcıya gösterebiliriz.

   Bu noktadan sonra yapılabilecek olan registery değerlerinin değiştirilmesi, kodun bir sonraki/önceki adıma  taşınması değişken değerlerinin okunması/değiştirilmesi gibi işlemler tamamen Win32 API’leri üzerinden yapılabilecektir. İleri kullanımlarda işlemci ve registery’ler hakkında da ayrıntılı bilgi sahibi olmak kesinlikle bir şart olarak karşınıza çıkacaktır.

    Aşağıda, şimdiye kadar sizinle paylaşmış olduğum bilgilerden yola çıkarak hazırlanmış basit bir debugger projesi bulabilirsiniz. Proje içerisinde yer alan CRM uygulaması ile de debugger’ı test edebilmeniz mümkün. Örnek debugger’ımızın bir Windows Forms uygulaması olması nedeniyle debug döngüsü sırasında arayüzün kilitlenmemesi adına debug işlemleri arka planda çalışan bir iş parçacığına taşınmıştır. Proje içerisinde yer alan Debugger sınıfı içerisindeki olay bildirimlerinin bu ayrı iş parçacığında bulunan debug döngüsünce tetiklenmesi ve arayüz etkileşimi yapılması nedeniyle bir senkronizasyon problemi oluşuyor. Bu durumu çözmek için; iş parçacığı başlatılırken arayüz olaylarının işletildiği ana iş parçacığına ait SynchronizationContext (SynchronizationContext.Current) bilgisi verilerek olay tetiklenmelerinde aşağıda örneklendiği şekilde kod çalıştırılması sonucunda problem aşılmaktadır;

synchronizationContext.Send(/*Çağrılacak fonksiyon*/, /* parametreler*/);

   Bu şekilde; belirtilen fonksiyon, verilen parametrelerle ilgili SynchronizationContext içerisinde (bizim örneğimizde arayüz) çalıştırılıyor olacaktır.

   Umuyorum ki şimdiye kadar ki paylaşımlarım ardından kafanızda debug süreçleri hakkında bir resim, bir fikir oluşmuştur. Aşağıdaki örnek proje üzerinden de pratik yapabilir, kodlar üzerinde çalışarak kendi ihtiyaçlarınız doğrultusunda özelleştirilmiş debugger’lar yazabilmeniz mümkün.

alt

Win32 Debugger

Fatih Boy

Ankara'da yaşayan Fatih, bir kamu kurumunda danışman olarak çalışmaktadır. ALM süreçleri, kurumsal veri yolu sistemleri, kurumsal altyapı ve yazılım geliştirme konularında destek vermektedir. Boş zamanlarında açık kaynak kodlu projeler geliştirmeyi ve bilgisini yazdığı makalelerle paylaşmayı seven Fatih, aynı zamanda Visual C# ve Visual Studio teknolojileri konusundan Microsoft tarafından altı yıl üst üste MVP (En Değerli Profesyonel) ödülüne layık görülmüştür. İş hayatı boyunca masaüstü uygulamaları, web teknolojileri, akıllı istemciler gibi konularda Asp.Net, Php, C#, Java programlama dilleri ve MySql, MsSql ve Oracle gibi veritabanı yönetim yazılımları ile çalışmıştır. İngilizce ve Türkçe olarak yayınlanan makalelerini gerek İngilizce bloğunda, gerekse de Türkçe bloğunda bulabileceğiniz gibi web sitesinden de açık kaynak kodlu geliştirdiği yazılımlarına ulaşabilirsiniz. vCard - Twitter - Facebook - Google+

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir