Bu Blogda Ara

C# etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
C# etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

27 Nisan 2010 Salı

Dynamic-Link Library (DLL) / Giriş

DLL dosyaları Microsoft'un Windows ve OS/2 işletim sistemleri için gerçeklediği kütüphane paylaşım konsepti olarak tanımlanabilir. Bu kütüphaneler genellikle DLL, OCX ya da DRV uzuntasındadır.

DLL dosyaları aynen EXE dosyaları gibi kod, veri ve kaynak bölümlerine (ve bunların herhangi bir kombinasyonunda) olabilir.

Windows işletim sisteminin ilk sürümlerinde tüm programlar tek bir bellek bölgesinde koşturuluyordu. İşletim sistemi düzeyindeki tüm işlemler MS-DOS tarafından gerçekleştirilirken yüksek seviyeli hizmetler Windows DLL leri tarafından sağlanıyordu. Çizim API'leri yani GDI.EXE, kullanıcı arayüzü işlemleri USER32.EXE adlı kütüphane dosyasında gerçeklenmiştir.


Günümüzde daha farklı bir yaklaşım kullanılmaktadır. Sistem tarafından sunulan kütüphaneler uygulamalara ayrı ayrı yüklenmektedir. Korumalı modda olduğu için bir program başka bir programın hafıza alanına doğrudan bir müdehalede bulunamıyor.

Assembly dilinde basit bir mesaj gösteren DLL uygulaması şu şekilde olabilir:


.386 ; intel x86 architecture
.model flat,stdcall ; use flat memory management
option casemap:none ; ignore letter case

include \masm32\include\windows.inc
include \masm32\include\user32.inc ; for MessageBox func.
include \masm32\include\kernel32.inc

includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.data
message db "Test message...",0 ; content of our simple message

.code
DllEntry proc hInstance:HINSTANCE, reason:WORD, reserved1:WORD ; DLL entry point function
mov eax,TRUE
ret
DllEntry Endp

SimpleMessage proc
invoke MessageBox,NULL,addr message,NULL,MB_OK ; win32 MessageBox func.
ret
SimpleMessage Endp

End DllEntry




Bu programı derlemek için birde def dosyası oluşturmalıyız. Bu dosyada 2 satır bilgi gerekiyor. Birinci satırda kütüphanenin ismini ikinci satırda ise kütüphaneden dışarı aktarılacak yani diğer programların hizmetine sunuculacak olan fonksiyonların isimleri.

örnek olarak

LIBRARY Simple
EXPORTS SimpleMessage


Projemizi derlemek için komut satırına bazı komutlar girmemiz gerekecek. Bu işlemin otomasyonu için şöyle bir batch dosya oluşturabiliriz.


c:\masm32\bin\ml.exe /c /coff /Cp "%CD%\Simple.asm"
c:\masm32\bin\link.exe /DLL /DEF:"%CD%\Simple.def" /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm32\lib "%CD%\Simple.obj"


Not: Bu uygulamayı derleyebilmeniz için sisteminizde Masm32 derleyicisi yüklü durumda olmalıdır. http://www.masm32.com/

Şimdi oluşturduğumuz bu DLL dosyasını başka bir programdan çağıralım. Bunun için çağırı yapacak programımızı derlerken DLL dosyamızın yapısını bildirmek üzere bir .lib dosyası vereceğiz. Bu dosya DLL projemizi derlerken derleyici tarafından otomatik olarak oluşturulmaktadır.

Kısa basit bir örnek:

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib Simple.lib ; lib file of Simple.dll to inform linker
includelib \masm32\lib\kernel32.lib

SimpleMessage PROTO ; name of the function in DLL

.code
start:
invoke SimpleMessage ; invoke the function in DLL
invoke ExitProcess,NULL ; terminate process
end start




Şimdi benzer bir çağırı programını C++ dilinde dinamik çağırma yöntemiyle yapalım.


#include <stdio.h>
#include <windows.h>

typedef void (WINAPI *SimpleMessage)(); // type defination to call dll function.

int main(int argc, char **argv)
{
HINSTANCE hDLL = LoadLibrary(L"simple.dll"); // loads our DLL into application memory section.
SimpleMessage sm = (SimpleMessage)GetProcAddress(hDLL,"SimpleMessage"); // gets address of our function in the dll.
if (!sm) // function address validation.
{
printf("Couldn't load library!\n");
}
else
{
sm(); // simple call.
}
return 0;
}



C# tarafında işler çok daha kolay:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace SimpleCall
{
class Program
{
[DllImport("simple.dll")]
static extern void SimpleMessage();

static void Main(string[] args)
{
SimpleMessage();
}
}
}


Java tarafında direk bir erişim şekli mevcut değil. Bunun yerine bazı 3. parti kütüphaneleri kullabiliriz. Bunlar JNI (Java Native Interface) tabanlı kütüphaneler. Biz bu örnekte JNA (Java Native Access) kullanacağız.

Bu kütüphanenin açıklamasını daha önceki bir yazımda (Bkz: http://rbellek.blogspot.com/2010/04/java-native-access-basit-ornek.html) bahsettiğim için burada yüzeysel geçiyorum.

Örnek uygulama:


//Main.java
package simple;

import com.sun.jna.Native;

public class Main {

public static void main(String[] args)
{
Simple simpleDLL = (Simple)Native.loadLibrary("e:\\Simple.dll", Simple.class);
simpleDLL.SimpleMessage();
}
}



//Simple.java
package simple;

import com.sun.jna.Library;

public interface Simple extends Library {
void SimpleMessage();
}



DLL dosyasının aynı EXE dosyaları gibi bir giriş noktları mevcuttur. Bu noktanın adı DllEntry adlı bir fonksiyondur. Ve giriş parametreleri olarak şu 4 değerden birini almaktadır.

DLL_PROCESS_ATTACH
DLL_PROCESS_DETACH
DLL_THREAD_ATTACH
DLL_THREAD_DETACH

Basit bir toplama kütüphanesi:


.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.data
AppName db "Simple 2",0
HelloMsg db "Hello, you're calling a function in this DLL",0
LoadMsg db "The DLL is loaded",0
UnloadMsg db "The DLL is unloaded",0
ThreadCreated db "A thread is created in this process",0
ThreadDestroyed db "A thread is destroyed in this process",0

.code
DllEntry proc hInstance:HINSTANCE, reason:DWORD, reserved1:DWORD
.if reason==DLL_PROCESS_ATTACH
invoke MessageBox,NULL,addr LoadMsg,addr AppName,MB_OK
.elseif reason==DLL_PROCESS_DETACH
invoke MessageBox,NULL,addr UnloadMsg,addr AppName,MB_OK
.elseif reason==DLL_THREAD_ATTACH
invoke MessageBox,NULL,addr ThreadCreated,addr AppName,MB_OK
.else ; DLL_THREAD_DETACH
invoke MessageBox,NULL,addr ThreadDestroyed,addr AppName,MB_OK
.endif
mov eax,TRUE
ret
DllEntry Endp

Addition proc param1:DWORD, param2:DWORD
mov ebx,param1
mov eax,param2
add eax,ebx
ret
Addition endp
End DllEntry




Bu toplama fonksiyonunu (parametreli) çağıran C++ uygulaması:


#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

typedef int (WINAPI* Addition)(int,int); // two DWORD (32-bit integer) in parameters
// WINAPI same as __stdcall
// __stdcall gives order of parameters in stack
int main(int argc, char **argv)
{
HINSTANCE hDLL = LoadLibrary(L"calculator.dll");
Addition addition = (Addition)GetProcAddress(hDLL,"Addition");
if (!addition)
{
printf("Couldn't load library!");
}
else
{
printf("5 + 9 = %i",addition(5,9)); // simple two parameter call
}
return EXIT_SUCCESS;
}



Şimdilik basit bir girişle yetiniyorum. Umarım devamında detaylandırabilirim...

4 Nisan 2010 Pazar

Java Native Access Basit Örnek

Java ile program geliştirmek çok rahat ve güvenilir olabilir. Fakat diğer tüm yorumlamaya dayalı diller gibi(C# vs.) Java'da doğrudan makine diline çevrilen programlama dilleri kadar hızlı değildir. Hız söz konusu değilse tabiki hiç bir sorun yok. Fakat bizim için hız önemli olduğunda ne yapacağız?
a)Java vb. dilleri bırak, C/C++ ya da Delphi gibi dilleri kullan.
b)Kullandığın dillerden vazgeçme gerekli bölgeyi soyutla.

Ben b şıkkını seçiyorum :) Peki b şıkkında anlatamak istediğim nedir?

Örneğin bir program geliştreceğiz. Bu programda çok ağır matematiksek işlemler var. Bunları Java ile yaptığımız zaman istediğimiz sürede istediğimiz verimlilikte sonuçlar alamıyoruz. Öyle ise bize sıkıntı çıkartan kısmı alıp, C/C++, Delphi ya da bir başka donanıma yakın olan bir dil ile yazıyoruz. Bu yazdığımız bölümü DLL olarak oluşturuyoruz. Artık tek yapmamız gereken Java projemizden bu DLL içerisindeki gerekli fonksiyonları zamanı geldiğinde çağırmak.

Bunu yapmamız için Sun Microsystems tarafından yazılmış bir kütüphane mevcut. JNA (Java Native Access). Bu kütüphane ile sabit disk üzerideki (başka bir dil ile yazılmış) DLL dosyalarındaki fonksiyınları çağırabiliyoruz.

Şimdi yamamız gereken temel adımlardan bahsedeyim.

1) Bir java projesi oluşturalım.
2) https://jna.dev.java.net/ adresinden JAR dosyasını sabit diskimize indirelim.
3) İndirdiğimiz kütüphaneyi projemize ekleyelim.
(NetBeans 6.8 için ekleme)
Libraries (sağ tık) -> Add JAR/Folder...


4) Projemizde bir interface oluşturarak kullanacağımız fonksiyonların tanımlamasını yapmalım.
5) İhtiyacımız olan fonksiyonu çağıralım. :)

Örnek bir kod parçası:

import com.sun.jna.*;

public class testMessage {

public interface Simple extends Library
{
public void TestMessage();
}

public static void main(String[] args)
{
Simple dll = (Simple)Native.loadLibrary("Simple.dll", Simple.class);
dll.TestMessage();
}

}


Bu kadar basit. JNA ile yalnızca Windows değil MacOS X, Linux ve Unix platformlarında da çalışabilirsiniz. Yanlız hata kontrolünü çok iyi tutmak gerekiyor. Lakin benim bir kaç denememde JVM (java.exe) çökmesi gibi durumlarla karşılaştım. Çünkü direk düşük seviye işlemleri yapılıyor. Java için son olarak birde JNI (Java Native Interface) var. Kendi yazdığımız C programlarına direk erişim için. Onu kısmet olursa başka bir zaman anlatmayı düşünüyorum.

C# tarafında da aynı olayın mümkün olduğunu söyleyebilirim. Orada işler biraz daha basit. Herhangi bir kütüphane kullanmanıza gerek yok. Yapı şu şekilde:

[DllImport("Simple.dll")]
static extern int TestMessage();

static void Main(string[] args)
{
TestMessage();
}


Sonuç olarak Java, C# vb. dillerin görsellik, güvenilirlik ve diğer mükemmel özelliklerini alıp, hantallık gibi diğer kötü özelliklerini bu sistemle değiştirmiş oluyoruz. Her sistemin güzel özelliğini bir araya getirerek çok kaliteli ürünler ortaya çıkarmak mümkün.