C# İle Win32 API Entegrasyonu Nasıl Yapılır?

Merhaba,

.NET her ne kadar işlevsel bir çok özellik ve yapı barındıyor olsada bazı durumlarda bu yapılar yetersiz kalabilmektedir. Mesela fareye fiziksel olarak hükmetmemiz gerekebilir yahut DVD Writer’ı fiziksel olarak programımıza açtırmak isteyebiliriz. Bunun gibi durumlarda .NET kütüphanesinin sınırları dışına ulaşmamız icap etmekte bir nevi işletim sisteminin fonksiyonelliklerine erişebilmemiz gerekmektedir.

Windows işletim sisteminin fonksiyonlarını bizler genel anlamda Win API diye nitelendirmekteyiz. Yani bir diğer adıda Win32 API… Win32 API sayesinde .NET ile Windows işletim sisteminin yapılarını kullanabilmekte ve Microsoft Windows uyumlu programlar geliştirebilmekteyiz.

.NET kütüphanesi eski bir teknoloji olan COM ve unmanaged code(yönetilemeyen kod) ile uyumlu bir şekilde çalışabilmektedir. Anlayacağınız üzere bu makalemizde .NET kütüphanesi ile Win32 API entegrasyonu ele alınacaktır. Ee hadi başlayalım o halde…

Öncelikle şunu bilmenizi istiyorum ki; Win32 API, yönetilmeyen kod olduğu için, kendisiyle çalışıldığı vakit programın işlenişi CLR(Command Language Runtime – Ortak Dil Çalışma Zamanı)‘den çıkacaktır. Yani compiler yönetilmeyen herhangi bir kod ile karşılaştığı an ilgili kod bloğu CLR tarafından yönetilmekten çıkarılacaktır. Buda demek oluyor ki, eğer bizler Win32 API gibi yönetilmeyen kodlar kullanırsak işlevsellik CLR’den çıkacağı için “Garbage Collection – Çöp Toplayıcı” gibi .NET’e özgü bir çok servis pasifize edilecektir.

System.Runtime.InteropServices Kütüphanesi ve DllImport Attribute’u

.NET kütüphanesinde CLR tarafından yönetilmeyen kodlara erişebilmek için System.Runtime.InteropServices kütüphanesindeki DllImport attributeu kullanılmaktadır. DllImport niteliği ile belirtilen bir harici kaynaktaki metodu refere edebilmek için external modifier(nitelendirici)’ı kullanılmaktadır.

Ya hoca belirtilen kaynaktaki metodu refere etmek ne demek? diye sorar ve direkt olarak kaynaktaki metodu çağırıp, kullanalım ya! diye söylenirseniz eğer, bilmelisiniz ki COM yapılarında böyle bir dünya yoktur. Yani harici kaynağı belirttikten sonra direkt olarak içerisindeki metoda erişememekteyiz. Yapmamız gereken kullanacağımız kaynak içerisindeki metodu refere edecek bir tanımlamada bulunmaktır. Bir nevi ilgili metodun bir imzasını tanımlamamız gerekmektedir.

    class Program
    {
        [System.Runtime.InteropServices.DllImport("HariciKaynak.dll")]
        public static extern void KullanilacakMetot(int x, int y, string z, bool m);
    }

Yukarıdaki örnek kod bloğunu incelerseniz eğer “HariciKaynak.dll” isimli bir harici kaynakta bulunan “KullanilacakMetot” isimli bir metodun imzası refere edilmiş, bir başka deyişle tanımlanmıştır. Tabi ki de bu örnekte kullanılan kaynak ve metot bir örnek teşkil etmesi için verilmiştir. Lakin burada bilmemiz ve oturtmamız gereken düşünce şudur ki; harici kaynakta bulunan herhangi bir metoda .NET içerisinde direkt olarak erişemeyeceğimizden dolayı ilgili metodun imzasını yukarıdaki gibi tanımlamamız gerekmektedir. Bu imzaya uyan metot fiziksel olarak harici kaynakta bulunduğundan dolayı imzanın gövdesini oluşturmamıza gerek bulunmamaktadır. Nihayetinde bu imza, DllImport attributeu ile işaretlendiğinden dolayı çağrıldığı vakit harici kaynaktaki fiziksel metot ile irtibatlandırılacak ve çalıştırılacaktır.

Eğer ki bir metot external ile işaretlenmiş ve DllImport attribute’u kullanılmışsa otomatikman CLR tarafından yönetilmeyen kod bildirimi yapılmış demektir.

Şimdi Win32 API yapılarından bir örnek üzerinde konumuzu irdeleyelim.

    class Program
    {
        static void Main(string[] args)
        {
            MessageBox(0, "Win32 API", "Win32 API Örnek", 2);
            Console.Read();
        }

        [DllImport("user32.dll")]
        public static extern int MessageBox(int tip, string mesaj, string baslik, int secenek);
    }

Dikkat ederseniz eğer “user32.dll” dosyası DllImport attributeu ile harici kaynak olarak gösterilmiş ve “MessageBox” metodu extern ile refere edilmiştir. extern sözcüğü compilera, bildirimi yapılan bu “MessageBox” isimli metodun ilgili kaynakta değil, belirtilen harici kaynakta olduğunu bildirmekte ve orada aratmaktadır. Ayriyetten bu örnekte vermiş olduğumuz “MessageBox” bizim bildiğimiz Windows Form’da ki MessageBox yapısının akrabası sayılır. Bu örneğimizi derleyip çalıştırdığımız vakit aşağıdaki gibi bir çıktıyla karşılaşırız…

C# İle Win32 API Entegrasyonu Nasıl Yapılır?

Şimdi asıl konumuzu ilgilendiren kısma gelelim. Yukarıda yapmış olduğumuz bu işlemlerin çalışma zamanında – run time nasıl işlevsellik gösterdiğine ve durumuna göz atmaya.

Program çalıştırıldığı an CLR tarafından yönetilmeyen bir metot çağrımı yapıldığı anlaşılır ve ilgili kaynaktan metot belleğe yüklenir ve belleğe yüklenen metodun başlangıç adresi saklanır. Ardından oluşturmuş olduğumuz metodun imzasında bizlerin parametre olarak verdiğimiz değişkenler harici kaynaktaki fiziksel metoda uyumlu hale getirilir ve parametre olarak geçirilir. Eğer bir geri dönüş değeri bekleniyorsa, yönetilmeyen kod bölümünden gelen değer uygun .NET türüne dönüştürülerek işlemlere devam edilir. Tabi bu işlemler komple .NET’in alt yapısını ilgilendirdiği için biz programcıların işi metodun imzasını bildirmekten ibarettir.

Şimdi DllImport attribute’unun özelliklerini ele alalım.

  • DllImport attributeu sadece metotlara uygulanabilir.
  • DllImport ile işaretlenmiş metotların mutlaka extern keywordü ile işaretlenmesi gerekmektedir.
  • DllImport’un EntryPoint, CallingConvention, CharSet, ExactSpelling, PreserveSig ve SetLastError olmak üzere beş adet parametresi vardır.
    Tek tek bu parametreleri ele alırsak eğer;

    1. EntryPoint
      Harici kaynak içerisindeki herhangi bir metodun imzasındaki ismi fiziksel metodun ki ile birebir yapmak zorundayız. EntryPoint parametresi bu zorunluluğu aşmamızı sağlamakta ve imzada belirttiğimiz farklı isimli metodun harici kaynaktaki hangi metoda karşılık geleceğini belirtmemize yaramaktadır.

          class Program
          {
              static void Main(string[] args)
              {
                  MesajKutusu(0, "Win32 API", "Win32 API Örnek", 2);
                  Console.Read();
              }
      
              [DllImport("user32.dll", EntryPoint = "MessageBox")]
              public static extern int MesajKutusu(int tip, string mesaj, string baslik, int secenek);
          }
      

      Yukarıdaki örnek kod bloğuna bakarsanız eğer imzası tanımlanmış “MesajKutusu” isimli metodun EntryPoint parametresi ile aslında harici dosyadaki “MessageBox” metodunu refere ettiği belirtilmiştir.

    2. CallingConvention
      Assembly dili dışında hemen hemen her programlama dilinde parametrelerin, geri dönüş değerlerinin ve değişkenlerin depolanması(store edilmesi) farklıdır. Yani her dil fonksiyonu çağırırken farklı convention(düzen/karar) izler.Haliyle bu parametre ile harici metodun ne şekilde store edileceğine karar verilir.

      Winapi, Cdecl, FastCall, ThisCall ve StdCall olmak üzere beş adet sembol içerir. Bunlardan en çok Winapi kullanılır.

      Winapi, DllImport attribute’unun varsayılan CallingConvention parametre değeridir. Sadece Windows sistemlerine özgündür. Haliyle programımız Windows dışında farklı platformlarda çalışacaksa bu sembolden kaçınmak gerekir.

    3. CharSet
      Harici dosyadaki metodun çağrımında kullanılacak karakter setini belirler.
    4. ExactSpelling
      EntryPoint parametresi ile belirtilen ismin ilgili fonksiyon ismine yazım biçimi bakımından tam uyumlu olup olmayacağını belirtir. bool türünden bir özelliktir.

Sanırım COM, unmanaged code ve Win32 API üzerine oldukça detaylı ve yeterli bir anlatımda bulunduğumuzu düşünüyorum. Bu makaledeki çizdiğimiz sınır ve yaklaşımla istediğiniz her türlü çalışmayı yapabileceksiniz. Tabi bunun için içeriktekiler dışında ihtiyacınıza dönük harici kaynağı ve ilgili fonksiyonu belirlemek sizlere kalmaktadır.

Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Kaynak : http://www.csharpnedir.com/articles/read/?id=83

Bunlar da hoşunuza gidebilir...

2 Cevaplar

  1. Yakup D. dedi ki:

    Teşekkürler elinize sağlık.

  1. 13 Nisan 2017

    […] C# İle Win32 API Entegrasyonu Nasıl Yapılır? başlıklı makalemde C# ile Win32 API entegrasyonuna değinmiş bulunmaktayım. Haliyle bu içeriğimizde kullanacağımız yapıları tanımlayabilmeniz için öncelikle verdiğim adresteki makaleye göz atmanızda fayda olduğunu belirtiyor, alışılagelmişin dışındaki gramersel kullanımı bildiğinizi varsayarak ilgili konumuzu ele almaya başlıyorum… […]

Bir cevap yazın

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

*

Copy Protected by Chetan's WP-Copyprotect.