Biblioteka DLL w oparciu o źródła w języku C Tutorial
Należy rozpocząć od plików źródłowych w C: Pliku nagłówkowego zawierającego prototypy funkcji. Plików modułów składowych (plików źródłowych C zawierających procedury/funkcje). Zarówno pliki nagłówkowe, jak i funkcje mogą być utworzone w dowolnym edytorze/IDE – ważne, aby były w pełni kompilowalne (tzn. musi istnieć platforma systemowa, np. Linux/Windows, w której istnieje narzędzie pozwalające skompilować te źródła). Chodzi o to, że środowisko C# (MS Visual Studio) nie skompiluje niczego, czego inne narzędzie nie jest w stanie skompilować.
Nasz projekt przykładowej biblioteki DLL będzie zawierał dwa hipotetyczne moduły biblioteczne: module1.cpp - zawierający procedury(ę) matematyczne(ą) add. module2.cpp - zawierający procedury(ę) do porównywania łańcuchów checkstr. Plik nagłówkowy header.h zawierający prototypy funkcji bibliotecznych. UWAGA. Wszystkie funkcje biblioteczne, które mają być udostępnione dla aplikacji docelowej powinny mieć prototyp poprzedzony przez: __declspec(export)
//plik module1.cpp #include "header.h" extern "C" { __declspec(dllexport) int add(int a,int b) { return(a+b); } Konieczne jest zamieszczenie funkcji add w obrębie sekcji extern ”C” ponieważ daje to możliwość udostępnienia jej na zasadach public.
//plik module2.cpp #include "header.h" extern "C" { __declspec(dllexport) int checkstr(char *src,char *dst) { if (strstr(src,dst) != NULL) return(0); else return(-1); } Podobnie jak w pliku poprzednim, konieczne jest użycie sekcji extern ”C”.
#ifndef HEADER_H #define HEADER_H #include <stdio.h> #include <string.h> #include <stdlib.h> extern "C" { __declspec(dllexport) int add(int a,int b); __declspec(dllexport) int checkstr(char *src,char *dst); } #else #endif Prototypy funkcji umieszczonych w obrębie sekcji extern ”C” w polikach modułów powinny być umieszczone w obrębie sekcji extern ”C” także w pliku nagłówkowym.
2. Należy utworzyć projekt w MS Visual studio pozwalający na skompilowanie źródeł C do biblioteki DLL. W tym celu należy uruchomić środowisko MS Visual Studio i wybrać typ projektu: File->New->Project->Visual C++->Win32->Win32 Project. Należy podać nazwę projektu (w moim wypadku jest to CDll).
2. Po kliknięciu OK pojawi się kreator projektu:
3. Należy wybrać DLL oraz zaznaczyć Empty project.
4. Ponieważ utworzyliśmy pusty projekt, nie otworzy się żadne okno, natomiast w okienku Solution Explorer (po prawej stronie) pojawią się trzy kategorie plików. Klikając prawym przyciskiem myszy w kategorię Header Files należy dodać nasz plik nagłówkowy (Add->Existing Item…->Browse), a klikając prawym przyciskiem myszy w kategorię Source Files należy dodać dwa (Add->Existing Item…->Browse) pliki źródłowe modułów. Dodanie jest intuicyjne – wymaga wskazania stosownego pliku.
Bibliotekę w wersji Release można teraz zbudować (Build->Build Solution): 1>------ Build started: Project: CLibDll, Configuration: Release Win32 ------ 1>Compiling... 1>module2.cpp 1>module1.cpp 1>Linking... 1> Creating library C:\Users\Mariusz\Documents\Visual Studio 2008\Projects\CLibDll\Release\CLibDll.lib and object C:\Users\Mariusz\Documents\Visual Studio 2008\Projects\CLibDll\Release\CLibDll.exp 1>Generating code 1>Finished generating code 1>Embedding manifest... 1>Build log was saved at "file://c:\Users\Mariusz\Documents\Visual Studio 2008\Projects\CLibDll\CLibDll\Release\BuildLog.htm" 1>CLibDll - 0 error(s), 0 warning(s) ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
6. Teraz można stworzyć aplikację typu Windows Form Application korzystającą z biblioteki. W aplikacji tej będą 3 okienka tekstowe (2 do wprowadzania danych i jedno wynikowe pokazujące wynik dodawania lub wynik sprawdzenia, czy łańcuch z okienka textBox1 jest podłańcuchem łańcucha textBox2; o tym, która operacja zostanie wykonana można zdecydować klikając jeden z dwóch przycisków). Formularz aplikacji znajduje się na następnych slajdzie.
[DllImport("CLibDll.dll")] Jeśli chodzi o kod aplikacji, to jest kilka „smaczków”: Każdą z funkcji bibliotecznych z biblioteki CLibDll, którą chcemy wykorzystać w aplikacji należy osobno zaimportować z biblioteki używając w obrębie klasy formularza dyrektywy DllImport. Sama dyrektywa DllImport jest rozpoznawalnaprzez kompilator po włączeniu przestrzeni nazewniczej System.Runtime.InteropServices; [DllImport("CLibDll.dll")] unsafe public static extern int add(int a, int b); [DllImport("CLibDll.dll")] unsafe public static extern int checkstr(string src, string dst); 2. Funkcje napisane w C należą do tzw. kodu niezarządzalnego, dlatego powinny być oznaczone jako unsafe.
Nie wszystkie typy danych, które występują w języku C, występują także w języku C#. Tak jest z typem char*. Proszę zwrócić uwagę na sposób importu oryginalnej funkcji checkstr z modułu2: unsafe public static extern int checkstr(string src, string dst); Zamiast typu char* należało tutaj użyć typu string, który jest kompatybilny z typem char* z C. Dzięki temu do funkcji checkstr można bezpośrednio przekazywać zawartość okienek edycyjnych textBoxX. Podobnie jest z wykorzystaniem funkcji add, do której przekazywane, są elementy typu obiektowego Int32, choć oryginalnie jest ona zdefiniowana z argumentami typu int.
8. Kompilacja kodu C#, w którym są elementy unsafe jest możliwa tylko przy włączeniu specjalnej opcji kompilatora (Project->Nazwa_projektu Properties->Build->Allow unsafe code):
9. Na etapie kompilacji trzeba dopilnować, aby biblioteka CLibDll 9. Na etapie kompilacji trzeba dopilnować, aby biblioteka CLibDll.dll znalazła się albo w katalogu wynikowym, w którym zapisana aplikacja uruchamialna (exe), albo w katalogu systemowym, w którym są biblioteki systemowe (to pierwsze rozwiązanie jest pewniejsze).
10. Pozostaje zbudować aplikację docelową i uruchomić ją aby sprawdzić, czy wszystko działa jak trzeba.
Oprócz tej prezentacji przekazuję Państwu skompresowane archiwum Projects.zip zawierające: Źródłowe pliki C i nagłówkowe. Projekt biblioteki CLibDll.dll omówionej powyżej. Aplikację wykorzystującą tę bibliotekę (UseCDll).