4 Temmuz 2013 Perşembe

WinAPI Fonksiyonlarının C#'ta Kullanımı

C# ve dotNET ile birlikte yazılım geliştirmeye yeni bir soluk gelmiş olsada C# ile eskiden yazılmış COM komponentlerine erişebilmek mümkündür. Daha önceki iki makalede .NET ve COM ilişkisini detaylı bir şekilde incelemiştik. Bu makalede .NET'in Win 32 API ile nasıl entegre edildiği anlatılacaktır. .NET ve C#'ın yeni imkanlarının yanısıra eski bir teknoloji olan COM ve yönetilmeyen(unmanaged) kodlarla uyumlu bir şekilde çalışması belkide C# ve .NET'i diğer yazılım geliştirme platformlarından ayıran en önemli özelliktir.

Bildiğiniz gibi C#'ta gösterici kullanımı tamamen serbesttir. Bu yüzden eskiden(.NET öncesi) yazılmış ve parametre olarak gösterici alan COM komponentleri ve Windows API fonksiyonları C# ile sorunsuz bir şekilde çalıştırılabilmektedir. Bu yazıda Win API fonksiyonlarının .NET ortamında ne şekilde ele alındığı incelenecektir.

Win32 sistem fonksiyonları kullanıldığında, kod CLR tarafından yönetilmekten çıkar. .NET ortamında geliştirilen bir uygulamada yönetilmeyen kod segmenti ile kaşılaşılırsa ilgi kod segmenti CLR tarafından yönetilmekten çıkar. Dolayısıyla "garbage collection" mekanizması ve .NET'e özgü diğer servisler kullanım dışı olur.

CLR tarafından yönetilmeyen kodlara erişebilmek için C#'ta System.Runtime.InteropServices isim alanında bulunan ve DllImprtAttribute sınıfını temsil eden DllImport niteliği kullanılmaktadır. DllImport niteliği ile harici bir kaynakta bulunan metoda referans vermek için external anahtar sözcüğü kullanılır. Bir sınıf bildiriminin en başında external anahtar sözcüğü ve DllImport niteliği kullanılarak CLR tarafından yönetilmeyen bir metot bildirimi yapılır. Tabi metodun gövdesi harici bir kaynakta zaten var olduğu için bizim metodun gövdesini yazmamızın bir anlamı yoktur. Ardından bu metot sınıfın istenildiği yerinde kullanılabilir. İsterseniz basit bir örnekle DllImport niteliğinin kullanımını gösterelim.

Win API windows sistemlerinin programlanabilir arayüzünü içermektedir. Windows uygulamarının tamamı bu arayüzdeki fonksiyonları ve diğer yapıları kullanmaktadır. Aşağıdaki programda Win32 sistemlerinde bulunan MessageBox() fonksiyonunun kullanımına bir örnek verilmiştir.

using System;
using System.Runtime.InteropServices;
class Class1
{
[DllImport("user32.dll")]
public static extern int MessageBox(int tip,string mesaj,string baslik,int secenek);
static void Main()
{
MessageBox(0,"Mesaj","Win API MessageBox",2);
}
}
DllImportAttribute sınıfının bir tane yapıcı metodu bulunmaktadır. Bu metot parametre olarak harici kaynağın adı belirtmektedir. Yukarıdaki kaynak kodda MessageBox fonksiyonunun bulunduğu "user32.dll" isimli dosya DllImport niteliğine parametre olarak verilmiştir. Bu örnekte dikkat edilmesi gereken diğer nokta ise extern anahtar sözcüğünün kullanımıdır. Bu anahtar sözcük ile bildirimi yapılan metodun harici bir dosyada olduğu belirtilmektedir. Dolayısıyla C# derleyicisi metodun gövdesini kaynak kodda aramayacaktır.


Yukurıdaki çıktıdan ve kaynak koddan da görüldüğü üzere Win API deki bir fonksiyonun çağrımı klasik metot çağrımından farklı değildir. Değişen tek şey metodun bildirim şeklidir.

Not : Gösterilen mesaj kutusunun farklı formlarını görmek için MessageBox metodunun parametreleri ile oynayın.

Şimdi kısaca yukarıdaki programın çalışma zamanındaki durumunu inceleyelim. Program çalıştırıldığında, CLR tarafından yönetilmeyen bir metot çağrımı yapıldığında ilgili kaynaktan metot belleğe yüklenir ve belleğe yüklenen metodun başlangıç adresi saklanır. Ardından bizim parametre olarak geçtiğimiz değişkenler DLL' deki fonksiyona 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. Bu işlemler tamamen .NET'in alt yapısını ilgilendirmektedir. Dolayısıyla programcının yapacağı iş sadece metodun bildirimini doğru bir şekilde gerçekleştirmektir.

DllImportAttribute sınıfının bir kaç önemli özelliği daha vardır. Bunlardan en önemlisi harici kaynaktaki bir fonksiyona takma isim verebilmemizi sağlayan string türünde olan EntryPoint özelliğidir. EntryPoint özelliğini kullanarak MessagBox fonksiyonuna takma isim verebiliriz. Fonksiyon çağrımı bu takma isim ile gerçekleştirilebilmektedir. Örneğin MessageBox fonksiyonuna TebrikMesajiVer şeklinde bir takma isim vermek için aşağıdaki gibi bir bildirim yapılmalıdır.

using System;
using System.Runtime.InteropServices;
class Class1
{
[DllImport("user32.dll",EntryPoint = "MessageBox")]
public static extern int TebrikMesajiVer(int tip,string mesaj,string baslik,int secenek);
static void Main()
{
TebrikMesajiVer(0,"Tebrikler","Takma İsim Verme",0);
}
}
Şimdi de DllImport niteliği ile ilgili diğer özelliklere ve önemli noktalara bakalım.

1 - DllImport niteliği yalnızca metotlara uygulanabilir.

2 - DllImport niteliği ile işaretlenmiş metotlar mutlaka extern anahtar sözcüğü ile bildirilmelidir.

3 - DllImport niteliğinin EntryPoint'in haricinde 4 tane isimli parametresi(named parameter) daha vardır. Bunlar : CallingConvention, CharSet, ExactSpelling, PreserveSig ve SetLastError parametreleridir. Bu parametrelerden önemli olanlar aşağıda açıklanmıştır.

4 - CallingConvention : Bu paramtre CallingConvention numaralandırması türündendir. Bu parametre ile harici metodun ne şekilde çağrılacağı ile ilgili bilgi verilir. CallingConvention numaralandırması 5 tane sembol içerir. Varsayılan olarak bu sembol Winapi olacak şekilde ayarlanmıştır. Yani CallingConvention parametresini DllImport ile kullanmıyorsak varsayılan olarak bu Winapi'dir. Diğer semboller ise Cdecl, FastCall, ThisCall, StdCall şeklindedir. En çok Winapi sembolu kullanıldığı için diğer sembollerin ne anlama geldiği anlatılmayacaktır. Diğer sembollerin ne anlama geldiklerini MSDN kütüphanesinden detaylı bir şekilde öğrenebilirsiniz.

5 - CharSet : Harici fonksiyonun çağrımında kullanılacak karekter setini belirler. Bu parametre CharSet numaralandırması ile belirtilir. Varsayılan olarak CharSet.Auto şeklindedir. CharSet numaralandırmasının diğer sembolleri Ansi, Unicode ve None şeklindedir.
6 - ExactSpelling : EntryPoint ile belirtilen ismin ilgili fonksiyon ismine yazım biçimi bakımından tam uyumlu olup olmayacağını belirtir. Bu özellik bool türündendir ve varsayılan olarak false değerdir.

DllImport metodunun varsayılan çağrım biçimi olan Winapi sadece Windows sistemlerine özgün olduğu için sistemler arası taşınabilirliğin yüksek olması gereken projelerde bu niteliğin kullanımından kaçınmak gerekir. Bu yüzden DllImport özelliğini kullanmadan önce ilgili fonksiyonun .NET Framework içinde olup olmadığını kontrol etmek gerekir. Örneğin MessageBox fonksiyonu zaten System.Windows.Forms isim alanında bulunduğu için API kullanarak bu fonksiyondan yararlanmak mantıklı değildir. Zira ileride programınızın Linux ortamında yada diğer farklı ortamlarda da çalışmasını istiyorsanız programınızı değiştirip yeniden derlemeniz gerekecektir. Oysa .NET Framework içinde bulunan standart sınıfları ve onların metotlarını kullanırsanız böyle bir derdiniz olmayacaktır.


Hiç yorum yok:

Yorum Gönder