Dinamik bağlama kütüphaneleri DLL'ler

Cansiz

New member
Yazan : Sanal Programcı

Tarih : 16.04.2003

Konu : Dinamik bağlama kütüphaneleri

Zorluk Derecesi : Başlangıç [ ] Orta [X] Zor [ ] İleri Seviye []

Ön Şart : Visual C++ 6.0, Biraz W32, Biraz Thread



DLL
Dinamik bağlama kütüphaneleri ( dll ), windows işletim sistemi ile birlikte programlamada çok önemli bir yere oturmuştur. Windows işletim sisteminde birçok önemli fonksiyon dll’lere dağıtılmıştır. Örnek olarak : Kernel32.dll : hafıza ile ilgili işlemler, User32.dll : pencere yaratımı, mesajlaşma vs.



Dll’leri yaratmak
İnanın bir dll yazmak, bir program yazmaktan daha kolaydır. Bir takım sentaks kurallarına uyarak yazdığınız programınızı linker da birleştirirken sadece /DLL anahtarını kullanırsınız.

Bir program, bir dlldeki fonksiyon/ları kullanacağı zaman ilk önce , bu dll’i kendi adres alanına yüklemelidir. Bu 2 tane yöntemle yapılır, yöntemlerin isimleri çok önemli değil ama biri derleme anı ekleme, biri çalışma anı ekleme diyebiliriz. Ama her iki yöntemde de fonksiyonu kullanmadan önce dll mutlaka adres alanına yüklenmeli. ( Bu adres alanları ,process vs ilgili yazı isteyen olursa istekte bulunursa sevinirim ).

Dll, prosessin adres alanına yüklendikten sonra, artık dll ‘e ait fonksiyonlar, sanki programınızın içindeki sıradan fonksiyonlardan biriymiş gibi kullanabilirsiniz. Hatta threadleriniz bu fonksiyonların bir dll’den geldiğini bile anlamayabilir. Kendi fonksiyonunuz , dll’deki bir fonksiyonu çağırdığında, fonksiyona göndermek istediği parametreleri stack (yığıt) alanına basar. Daha sonra dll’deki fonksiyon çalışırken yığıttan ilgili parametreleri çekerek, hangi parametrelerler çalışması gerektiğini anlar. Buradan anlaşılması gereken nokta, dll ile process veya thread aynı yığıt alanını kullanır. Dll’lerdeki fonksiyonlarınız bir bellek alanı ayırıyorsa, bunuda kendisinin eklendiği process’in bellek alanından ayırır. Mesela, diyelimki dll’deki bir fonksiyon dinamik bir alan tahsis etti, sonrada processinizden bu dll’I unmap ettiniz ( şimdilik bunu dll’I kovmak olarak düşünün ), bu dinamik alan gidermi ? Hayır, gitmez, çünkü bu adres bölümü processin adres bölümünde ayrılmıştır ve process yok edene dek veya processin kendisi yok olana dek orada duracaktır.

Peki Dll’imizde global değişkenler tanıttık diyelim, ve birden fazla process bu dll’I kendisine bağladı, bu global değişkenler ne olur ? . Aslında sorumuzun cevabı yukarda var. Her dll, kendisini bağlayan process’in adres alanına yüklenir, dolayısıyla global nesneleri, o adres alanında global olur, başka bir process’de bu dll’I bağlarsa onun global değişkenleri bağımsız olur.

Aslında biraz değişik yöntemlerle, bazı değişkenlerin bir dll’de statik yapmak mümkün. Yani ne kadar başka process’e yüklenirse yüklensin bu statik alanlar birbirlerini etkileyecek şekilde ayarlanabilir. Bunun örneğini daha sonra göreceğiz.



Dll’I Adres alanına Yüklemek

Dll’leri processlerin adres alanına yüklemek için 2 farklı yöntem vardır.



1-) Derleme Anında Yükleme
Belkide en çok kullanılan yöntemdir. Bu yöntemde, dll’lerin library dosyalarına ihtiyaç vardır. Siz derlerken, lib dosyasını programınıza eklersiniz, ve libdeki fonksiyonları istediğini gibi kullanırsınız. Daha sonra exe programınız çalıştırıldığında aynı isimli dll dosyayı aranır sistem tarafından , ve derlenirken kullanılan fonksiyonlar, bu dll içinden çağırılır. Yani sistem dll’I otomatik olarak process’in adres alanına yükler. Peki exe çalıştırıldıktan sonra sistem bu dll’I nerden bulup yükler :

Exe’nin çalıştığı dizin aranır
Process’in bulunduğu dizin aranır
Windows işletim sisteminin dizini
PATH ile tanımlanan tüm dizinler.


Eğer bu 4 arama şeklindede dll bulunamazsa işletim sistemi bir mesaj çıkartır , dll bulunamadı türünden bir mesaj verip, altta aradığı tüm dizinleri yazar.



2-) Çalışma Anında Yükleme
Bu yöntemde, exe fonksiyonunu kullanmadan hemen önce LoadLibrary(Ex) api’sini kullanarak istediği dll’I adres alanına yükler.Gerekli dll’in yüklenemediğini ise LoadLibrary api’sinin dönüş değerinden anlayabiliriz.

Şimdi bu apiyi inceleyelim :

Apinin biraz eski hali : HINSTANCE LoadLibrary(LPCTSTR lpszFileName);
Şimdi lpszFileName denilen kısım, yüklenecek dll’in ismi. Buraya pathide girebilirsiniz.

Geriye dönüş değeri ise dll’in yüklendiği adres alanın karşılığıdır. Eğer dll dosyası bulunamazsa NULL değeri döner. Peki neden NULL döner ? ( Cidden processlerin adres alanlarını bir ara anlatmalıyım sanırım, sebebi adres alanı ile ilgili )

Daha değişik bir hali : HINSTANCE LoadLibrayEx(LPCTSTR lpszFileName, HANDLE hFile, DWORD dwFlags);
HFile her zaman NULL olması gereken bir değer , neden ? çünkü microsoft bunu reserve etmiş kendine. Peki dwFlags nedir ? bu değer ile ilginç işler yaptırabiliriz.

Bu bayrak şu değerleri alabilir :

1. DON’T_RESOLVE_DLL_REFERENCE

Yuhh değere bak mı dediniz ? bir ara ms’in sabit değerlerinden bazılarını yazıyım, aklınız durur, adamlar değişken ismimi vermiş, yoksa hikayemi yazmış belli değil. Neyse, bu değer verilirse, sistem dll’deki DllMain fonksiyonunu çağırmaz. ( bu nedir demeyin sabredin ). Kısaca özetlemek gerekirse, dll bir process’e eklendiğinde , process dlldeki DllMain isimli bir fonksiyonu çağırır, tıpkı bir c programında main’in çağırılması gibi. Ama bunun çağırılmasını istemiyorsak bu değeri veririz. Bunun kötü bür yanı, eğer dll’iniz initialize kısmında, başka dllere ihtiyacı varsa, ve bunları DllMain de yüklüyorsa, onlarda yüklenmez.

2. LOAD_LIBRAY_AS_DATAFILE

Gene epey uzun vede kendini açıklayan bir değişken ismi. Çok kısa bir özet : Eğer dll dosyanızda, bitmap, icon vs gibi kaynaklar varsa sadece bunu kullanın, adı üstünde, hiçbir initialize’ini yapma, yer ayırma vs vs, sadece ekle bulunsun.

3. LOAD_WITH_ALTERED_SEARCH_PATH

Bunu neden kullanırlar pek bilmem. Yaptığı tek şey, yukarda bahsettiğim dll’I çağırıldığı zamanki arama sırasını değiştirir.

Bu arada not olarak birşey düşelim :

Dll’I yüklerken dosya ismi olarak gireceğiniz kısımda, uzantıyı eklemeyin yani bilmemne.dll gibi. Yoksa ilk önce registerydeki HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDlls deki dllerle karşılaştırılır.

Şimdi diyeceksiniz ki örnek ver : ( yada buraya kadar koptunuz yazıyı bıraktınız )

Registrynin bahsedilen o bölümünde şöyle bir kayt olsun :

Value name : abc

Value Data : c:\ceviz\xyz.dll


Şimdi sizde LoadLibrary(“abc”) dediniz. Sistem yukarılarda bir yerde bahsedilen sıraya göre gidip abc.dll dosyasını arar ve bulursa yükler.

Peki LoadLibrary(“abc.dll”) dersek ne olur. Uzantı bulundu, bu durumda registry’e bakılır. Registeryde abc diye bir value name görülür, ve c:\ceviz\xyz.dll yüklenir, yani alakasız bir dll…

Gene yukarılarda bir yerde dll’I unmap etmek yada kovmak diye birşeyden bahsettik ya, işte oda FreeLibrary(HINSTANCE hInst) ile yapılır.

LoadLibraryden dönen hInst değerini bu apiye parametre olarak verirseniz, dll sizin adres alanınızdan çıkartılır. Ama tekrar uyarı, dll’in yarattığı dinamik alanlar otomatik olarak silinmez, process’in bunları silmesi beklenir.

Şimdi biraz teknik detaya girelim.

Windows işletim sisteminde kernel objelerinde birer kullanım adedi gibi bir ek bulunur. Şimdi siz LoadLibrary diyerek bir dll yüklediğinizde , bu dll’e ait kullanım sayısı 1 artırılarak yükleme gerçekleştirilir. Eğer process ikinci kez aynı dll’I yüklemeye çalışırsa, sistem sadece kullanım sayısını 1 artırır ama yeniden yüklemez. Yeni sürekli Loadlibrary diyerek pratikte hafızanızı şişiremezsiniz.

Peki FreeLibrary’I çağırdığınızda ne olur. Bu seferde kullanım sayısı 1 azaltılır. Eğer kullanım sayısı sıfıra ulaşırsa, dll , o processten ayrılır. Kullanım sayısı sıfır oldugu halde freelibrary yazarsanız da hata alırsınız.

Bu arada bu kullanım sayısı, process bazındadır. Yani başka bir process o dll’I yüklerse kullanım sayısı o process’te bağımsızdır.

Peki multithread çalışan bir sistemde, bir thread kullanacağı dll’in başka bir dll tarafından yüklenip yüklenmediğini nasıl anlayaibilir ?

GetModuleHandle apisini kullanabilir .

Örnek :
HINSTANCE hInst = GetModuleHandle(“abc”);

If (NULL == hInst ) ise yüklenmemiş


Bu arada GetModuleFileName vede FreeLibraryAndExitThread apilerini helpten bir bakın derim.

Alıntıdır!
 

Cansiz

New member
Dll’lerin Giriş/Çıkış Bölümleri

Dll’lerin Giriş/Çıkış Bölümleri
Yazdığınız dll’in bir giriş çıkış fonksiyonu olabilir, olabilir dedik, çünkü zorunluluk değil. Ne demek giriş çıkış fonksiyonu ?, Nasıl ki c de yazılmış bir program çalıştırıldığında, main isimli bir fonksiyon çağırılırsa otomatik olarak, dll’lerdede bu tip fonksiyon vardır. Windows uygulamarında bildiğiniz gibi WinMain dir bu fonksiyon adı, dll lerde ise DllMain ( ne kadar yaratıcı dimi J)

Bu fonksiyonun tanımı şu şekildedir :
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD dwReason,LPVOID fpLoad);

Tekrar edelim, bu giriş fonksiyonu olmak zorunda değildir, seçimliliktir.

Şimdi bu fonksiyonu inceleyelim. Bu fonksiyonun içeriğide şöyle olmalıdır :




BOOL WINAPI DllMain(HINSTANCE hInst,DWORD dwReason,LPVOID fp) { switch(dwReason) { case DLL_PROCESS_ATTACH: // Bir process bizi adres bölümüne atamak istiyor, // initialise işlemlerini burada yapalım .. .. break; case DLL_THREAD_ATTACH: //Bir tane thread yaratıldı. .. .. break; case DLL_THREAD_DETACH: //bir thread normal bir şekilde dönüş yapıyor break; case DLL_PROCESS_DETACH: //process normal şekilde dönüş yapıyor break; } return TRUE; }




İşletim sisteminiz, bu fonksiyonu birçok defa çağıracaktır. HInst parametresi her seferinde, dll’in adres bölümündeki yerini gösteren bir değer olacaktır.( Yani process bazlı bir değer ).

Eğer dll’inizde hInstance değişkenine ihtiyaç duyacak fonksiyonlar olacaksa ( mesela CreateWindow vs ), bu değer global bir değişkende saklayın. En son parametre olan fp değişkeni, eğer sıfırsa, dll’iniz çalışma anında yüklenmiştir, yok eğer sıfırdan farklı ise derleme anında yüklenmiş demektir. Belki biraz copy-protection amaçlı kullanabilirsiniz.

Gelelim dwReason değişkeninin alabileceği değerlere.



DLL_PROCESS_ATTACH:


Bir process ilk defa olarak dll’I hafıza alanına yüklemeye çalıştığında ( yani o anda dll’in kullanım sayısı sıfır ise ), dll’deki dllmain çağırılırken, dwReason olarak : DLL_PROCESS_ATTACH geçirilir. Dikkat bu değer sadece sadece dll kullanım sayısı sıfır ise geçirilir, process’in kendisi ve threadleri( aslında process diye bir kavram yoktur, process sadece bir kaptan ibarettir, belki sonra bunuda yazı konusu yaparız ), daha sonra, unmap etmeden aynı dll’I yüklemeye kalkarsa, bu değer çağırılmaz. Bu bölümde sizde, dll’inizde process ile ilgili initialize işlemleriniz varsa onları yaratırsınız, mesela dinamik alan yaratımı, virtual memory yaratımı ( virtuyal memory yaratımı ? yazı mı lazım :O) ).

DllMain ‘den eğer DLL_PROCESS_ATTACH bölümündeki herşey yolunda giderse TRUE değeri ile çıkmanız gerekmektedir. Bu bölümde eğer daha sonra yok edilmesi gereken alanlar yarattıysanız, bu alanları DLL_PROCESS_DETACH ile ilgili bölümde yok edebilirsiniz.

Eğer bu bölümden TRUE ile dönmezseniz, sistem ekrana bir hata mesajı verir ve ilgili processi kapatarak işlemi sonlandırır.



Şimdilik, bu kadar. Yazının devamının gelmesini isteyenler, forumda belirtirse yazmaya devam ama pek okunması istenilmeyen bir yazıyada devam etmek istemem.

Yazının orjinal hali, Ms için kutsal kitap olan Advanced Windows Programming de bulabilirsiniz.
 

HTML

Üst