C++ Dlls mit Blitz verwenden

Übersicht BlitzBasic FAQ und Tutorials

Neue Antwort erstellen

 

David

Betreff: C++ Dlls mit Blitz verwenden

BeitragFr, Jul 20, 2007 18:16
Antworten mit Zitat
Benutzer-Profile anzeigen
Visual C++ Dlls mit Blitz verwenden

Neues Projekt anlegen

Nachdem die IDE gestartet wurde muss zunächst ein neues Projekt angelegt werden. Dies geht üblicherweise über den Menüpunkt "File->New...->Project" (oder dem Shortcut Ctrl+Shift+N).
Das Projekt selbst muss von Typ Visual C++ Project->Win32 Application sein, hat man den entsprechenden Eintrag ausgewählt, einen (sinnvollen) Namen vergeben und den OK-Button betätigt wird man zum Projekt-Wizard weitergeleitet. Hier ist es wichtig das als Projekttyp DLL eingestellt wird, der Punkt ist unter Application Settings zu finden. Will man kein Template vor die Nase geknallt bekommen wählt man den Punkt Empty project unter Additional options an.

Um mit dem Programmieren starten zu können muss, zu guter Letzt, noch eine Datei in das Projekt eingefügt werden. Ist diese Datei angelegt kanns los gehen.

Das Grundgerüst der DLL

Da eine DLL, wie jede Applikation, einen Einstiegspunkt für die ausführende Anwendung benötigt ist es von Nöten hier eine vorgegebene Funktion zu Definieren. Diese Funktion heißt in dem Fall DllMain und hat folgende Signatur:

Code: [AUSKLAPPEN]

BOOL WINAPI DllMain( HANDLE hInstDLL, DWORD dwReason, LPVOID lpvReserved );


hInstDLL
Der erste Parameter ist der DLL Handle. Der Handle kann verwendet werden für Funktionsaufrufe die einen Handle benötigen.

dwReason
Der zweite Parameter ist ein Integraler Wert und gibt an warum die Funktion aufgerufen wurde. Insgesamt gibt es vier "Gründe" warum ein Programm auf die Idee kommen könnte die Hauptfunktion der Dll aufzurufen. Da in diesem Tutorial aber keiner der Gründe von Belang ist spar ich mir die Erklärung an dieser Stelle einfach.

lpvReserved
Der letzte Parameter muss in Kombination mit dwReason ausgewerted werden. Wenn z.B. dwReason den Wert DLL_PROCESS_ATTACH (1) hat und lpvReserved NULL (0) ist so wurde die Dll dynamisch geladen, ist lpvReserved nicht NULL so wurde die Dll statisch gelinkt.

Code: [AUSKLAPPEN]

#include <windows.h>

BOOL WINAPI DllMain( HANDLE hInstDLL, DWORD dwReason, LPVOID lpvReserved )
{
   return TRUE;
}


Nun könnte die DLL bereits kompiliert werden. Allerdings macht es wenig Sinn eine DLL zu kompilieren ohne auch nur eine Funktion zu exportieren, darum schreiten wir voran!

Funktionen exportieren

Um eine Funktion in Visual C++ zu exportieren definiert Microsoft ein Schlüsselwort das die, zu exportierenden Funktionen, kennzeichnet.

Code: [AUSKLAPPEN]

__declspec( dllexport )


Um eine simple Funktion zu exportieren muss einfach das Schlüsselwort __declspec mit dem Attribut dllexport vorangesetzt werden:

Code: [AUSKLAPPEN]

#include <windows.h>

__declspec( dllexport ) const char* foo();

BOOL WINAPI DllMain( HANDLE hInstDLL, DWORD dwReason, LPVOID lpvReserved )
{
   return TRUE;
}

const char* foo()
{
   return "Hello World";
}


Nach einem Rebuild (Ctrl+Shift+B) ist im Projektordner eine Dll zu finden welche, oh Wunder, eine Funktion exportiert, nämlich const char* foo().
Der motivierte Leser wird an dieser Stelle den Webbrowser geschlossen, sich in höchster Erwartung in den in seinen Userlib Ordner begeben und versucht haben die neugebackene Funktion in Blitz zu nutzen.
Selbiger Leser wird auch traurig feststellen das die Funktion sich einfach nicht verwenden lässt. Ein Blick in die Dll (per PE Exporer z.B.) zeigt das da tatsächlich eine Funktion exportiert wurde, allerdings mit einem etwas Wirr anmutenden Namen.

Code: [AUSKLAPPEN]

?foo@@YAPBDXZ


Das Problem mit dem Name Mangling

Seit es C++ gibt, gibt es das Problem von eindeutigen Namen. Namen können doppelt vergeben werden wenn sie in eigenen Namespaces stehen oder sich in Strukturen befinden. Namen dürfen aber nicht doppelt vorhanden sein. Deshalb fügt der C++ Compiler zusätzliche Informationen an den Namen an um ihn eindeutig zu machen.
Wie der Name codiert wird ist von Compiler zu Compiler unterschiedlich. Nehmen wir also enfach unsrere Funktion als simples Beispiel für Visual C++.

Ein jeder Name beginnt mit dem Prefix "?", danach kommt der Funktionsname. Soweit sollte alles klar sein, aber jetzt gehts los mit einen Wirrwarr von Buchstaben. Das erste "@" sagt uns das die Funktion kein Operator ist. Das zweite "@" gibt normal Informationen über die Klasse an in der die Funktion sich befindet. Da wir aber garkeine Klasse verwendet haben gibts auch keine entsprechenden Infos auch zu erkennen an dem "Y" das auf das "@" folgt.

Jetzt wirds aber interessant! Das folgende "A" gibt die Speicherklasse an, in dem Fall "normal" das "P" steht für einen Zeiger (engl. Pointer) das "B" zeigt an das der Zeiger konstant ist. "D" gibt den Typ des Zeigers an, nämlich ein char. "X" und "Z" stehen für void und elipsis und schließen die Argumentliste ab.

Raus kommt folgendes:

Code: [AUSKLAPPEN]

const char* foo()


Wie man sieht kann man ganz leicht die korrekte Deklaration der Funktion rausfinden, durch die mangled names.
Aber, in unserem Fall wollen wir ja den ganzen Unsinn nicht haben und müssen eine Möglichkeit finden Name Mangling zu unterbinden. In C gab es diese Namensenkodierung noch nicht, warum nicht also mit einer C Funktion versuchen?

Code: [AUSKLAPPEN]

#include <windows.h>

extern "C" __declspec( dllexport ) const char* foo();

BOOL WINAPI DllMain( HANDLE hInstDLL, DWORD dwReason, LPVOID lpvReserved )
{
   return TRUE;
}

const char* foo()
{
   return "Hello World";
}


Und tatsächlich, nach einem Rebuild ist der Name ganz nackt (ohne komische Zusätze) in die Dll exportiert worden.

Dll mit Blitz verwenden

Hierzu muss im Unterverzeichnis der Blitz Installation (userlibs) eine neue Datei, mit der Endung .decls, angelegt werden. Ist diese Angelegt so kann sie mit einem Texteditor geöffnet und mit folgendem Inhalt versehen werden:

Code: [AUSKLAPPEN]

.lib "dlltest.dll"
foo$():"foo"


Die Zeichenkette hinter .lib muss natürlich durch den exakten Namen der Dll ersetzt werden, wenn er nicht zufällig "dlltest" war. Die Zeile darunter definiert eine neue Funktion, hinter dem ":" wird auf die Funktion in der Dll verwendet. Da der Name nicht weiter kodiert wurde kann hier einfach "foo" stehen.

Nachdem die Dll in das Verzeichnis verschoben und der Inhalt der .decls gespeichert wurde. Kann Blitz geöffnet und die neue Funktion in vollen Zügen genossen werden.

Code: [AUSKLAPPEN]

Print foo()
WaitKey


Und... oh Wunder der Technik. In dem sich jetzt öffnenden Fenster steht tatsächlich "Hello World".

Viele Grüße

P.S.: Tippfehler kann der Finder behalten!
http://bl4ckd0g.funpic.de
 

$tankY

BeitragSa, Aug 18, 2007 15:39
Antworten mit Zitat
Benutzer-Profile anzeigen
Und unter Dev-C++:
1. Neues Projekt anlegen: Datei -> Neu -> Projekt -> DLL (es werden automatisch 2 Dateien erstellt)
2. Nun in dllmain.cpp:
Code: [AUSKLAPPEN]
#include <windows.h>
#include "dll.h"

EXPORT char* CALLBACK foo()
{
    return "Hello World!";
}

3. In dll.h:
Code: [AUSKLAPPEN]
#ifdef __cplusplus
#define EXPORT extern "C" __declspec (dllexport)
#else
#define EXPORT __declspec (dllexport)
#endif

EXPORT char* CALLBACK foo();

4. Projekt kompilieren.

Der Rest funktioniert wie oben beschrieben. (Hab meinen Code nicht getestet, sollte aber funktionieren!)

Neue Antwort erstellen


Übersicht BlitzBasic FAQ und Tutorials

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group