Bu Blogda Ara

microsoft etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
microsoft 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

Winapi: WinExec

Merhaba bu yazımda WinExec fonksiyonunu inceliyeceğiz.

Teknik detaylara geçmeden önce özetle bu fonksiyonun görevinden bahsedeyim. Bu fonksiyon ile yeni bir uygulamayı başlatabiliyoruz. Başlattığımız uygulama fonksiyonu çağıran uygulama ile aynı yetkilere sahip oluyor.

Fonksiyonun yapısı:

UINT WINAPI WinExec(
__in LPCSTR lpCmdLine,
__in UINT uCmdShow
);


2 adet parametre alıyor. Bunlardan birincisi çalıştıracağımız uygulamanın sabit disk üzerindeki tam adresini ifade ediyor.

typedef __nullterminated CONST CHAR *LPCSTR, *PCSTR;

İkinci parametre ise açacağımızın uygulamanın başlama durumunu belirtmektedir. Örnek olarak simge durumunda, tam ekran ya da görünmez olarak başlatabiliriz.


#define SW_HIDE 0 // gizli
#define SW_SHOWNORMAL 1 // normal
#define SW_NORMAL 1
#define SW_SHOWMINIMIZED 2 // simge durumunda
#define SW_SHOWMAXIMIZED 3 // tam ekran
#define SW_MAXIMIZE 3
#define SW_SHOWNOACTIVATE 4 // normal gösterim ile aynı, tek farkı aktif olmaması
#define SW_SHOW 5 // gösterir (pencere gizli bile olsa)
#define SW_MINIMIZE 6 // simge durumuna getirir
#define SW_SHOWMINNOACTIVE 7 // simge durumuyla aynı, tek farkı aktif değil
#define SW_SHOWNA 8 // gösterir, aktif değil
#define SW_RESTORE 9 // simge durumundan kurtarır
#define SW_SHOWDEFAULT 10 // uygulamanın orijinal açılış değeri (CreateProcess'te kullanılan...)
#define SW_FORCEMINIMIZE 11 // simge durumuna zorlar (uygulama yanıt vermese bile)
#define SW_MAX 11 // tam ekrana zorlar (uygulama yanıt vermese bile)


Bu fonksiyonu kullanabilmek için C/C++ uygulamanıza WinBase.h (Windows.h içerisinde mevcut) başlık dosyasını eklemeniz gereklidir. WinApi kernel32.dll içerisindedir.

Windows 2000 Professional ve üzeri bir işletim sisteminde çalıştırılabilir.

Olası hata kodları ve açıklamaları:

0 Sistem kaynakları ya da hafızası yetersiz.

ERROR_BAD_FORMAT .exe dosyası geçersiz.

ERROR_FILE_NOT_FOUND Belirtilen dosya bulunamadı.

ERROR_PATH_NOT_FOUND Belirtilen yol bulunmadı.


Hemen basit bir uygulama yapalım.

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


int main(int argc, char **argv)
{
long hata = WinExec("notepad.exe",SW_NORMAL);

switch(hata)
{
case 0: printf("Sistem kaynakları ya da hafızası yetersiz.\n"); break;
case ERROR_BAD_FORMAT: printf(".exe dosyası geçersiz.\n"); break;
case ERROR_FILE_NOT_FOUND: printf("Belirtilen dosya bulunamadı.\n"); break;
case ERROR_PATH_NOT_FOUND: printf("Belirtilen yol bulunmadı.\n"); break;
default: printf("Uygulama başarıyla çalıştırıldı.\n");
}

system("pause");
return 0;
}


Not: Bu uygulama Microsoft Visual Studio 2008 Express Edition üzerinden test edilmiştir.

2 Nisan 2010 Cuma

C/C++ uygulamalarında Assembly komutları

Herkese merhaba,

C/C++ uygulamaları, genellikle hız ve performans gerektiren uygulamaların geliştirilmesinde kullanılan temel dillerdir. Donanıma yakın olmak isteyen bir programcı ya makine diline en yakın olan assembly ile günlerini harcayarak en hızlı ama güvenilir olmayan uygulamalar ortaya koyacaktır ya da C/C++ gibi bir programlama diliyle assembly diline yakın bir performansı daha hızlı ve bir nebze olsun daha güvenli uygulamalar geliştirecektir.

Bu yazımda sizlere Visual Studio geliştirme ortamında C/C++ uygulamaları içerinde Assembly komutlaranı nasıl kullanacağımızı göstermeye çalışacağım. Amacım Assembly dilini anlatmak değildir. Yalnızca bu komutları C/C++ uygulamalarında kullanmayı göstermektir.

Peki bir programcı buna neden gerek duyar? Hızdan ve performanstan bahsettik. Eğer çok fazla işlemi çok kısa bir sürede yapmak istiyorsak, programlarımızdan elimizden geldiğince gereksiz kodları çıkartmamız gerekcektir. Ne kadar gereksiz kodları programımızdan çıkartsakta bazı kodlar bizim isteğimizin dışında programa derleme zamanında yerleşmektedir. Bunlar derleyiciler tarafından optimize edilmiş genel amaçlı komutlardır. Eğer bir sistem ya da grafik programcısı gibi hız gerektiren bir iş yapmak istiyorsanız bu komutlarıda optimize etmelisiniz. Lafı daha fazla dolaştırmadan örneğimize geçelim.

Öncelikle herhangi bir C/C++ projesi oluşturalım. Bu konsol ya da görsel bir proje olabilir. Daha önce bahsettiğim derleyicilerin bizden habersiz programımıza eklediği kodları görmeliyiz. Bunun için Visual Studio geliştirme ortamının Disassembly özelliğini kullanacağız. Bu pencereyi programınızı debug ederken (herhangi bir satıra breakpoint koyarak) ister Alt + 8 tuş kombinasyonları ile ister Debug -> Windows -> Disassembly menüsünden açabilirsiniz. Bu pencere ile yazmış olduğunuz C/C++ uygulamasının kaynak kodlarına karşılık üretilen Assembly kodları görünmektedir.

Not: Bu proje Microsoft Visual Studio 2008 Express Edition ile oluşturulmuştur.








Şimdi kendi assembly komutlarımızı girelim. Bir C/C++ uygulamasında assembly komutları kullanmak için
__asm
{
... komutlar ...
}

şeklinde bir yapı kullanarak derleyiciyi uyarıyoruz.

Şimdi basit bir örneği inceleyelim. 32-bit Integer bir değişkene assembly ile değer atayalım.




MOV komutu ile akümülatöre 7 değerini atadık. Burada akümülatörü EAX ile ifade ederek 32-bit kelime işleyeceğimiz belirttik. Böylelikle 7 değeri 32-bit integer olarak akümülatöre atandı. Daha sonra bu değeri a değişkenine yine MOV komutu ile aktardık. Hepsi bu.

Şimdi biraz daha derine inip bir pointer (işaretçi) kullanarak bu işi nasıl yapacağımıza bakalım.




Bu örnekte akümülatöre b işaretçisinin adresini atadık. Bu atamayı yaparken DWORD PTR anahtar kelimelerini kullandık. Burada DWORD (double word - çift kelime) 32-bit uzunluğunda , PTR ise bir pointer (işaretçi) gösterdiğini belirtiyor. Derleyicimiz b işaretçisinin gösterdiği alana gidiyor ve oradaki 4 byte (32-bit) veri ile işlem yapıyor. Daha sonra bu adresin gösterdiği alana 9 değerini girerek a değişkenini dolaylı olarak değiştirmiş oluyoruz.

Böylelikle bir giriş yapmış olduk. Bir sonraki yazılarda daha derin işlemleri yaparak ışık hızında uygulamaları göstermek istiyorum.

Görüşmek üzere...