VT - Fr 01.01.10 16:45
Titel: DLL Programmierung - Export von Variable / Methode
Hallo,
frohes Neues euch allen und eine Frage zu Beginn des neuen Jahres :).
Ausgangssituation:
Ich habe ein Projekt das DLLs benutzt um zusätzlichen Code einzubinden. Nun stehen ja in den DLLs bestimmte Methoden die vom Hauptprogramm aufgerufen werden. Diese Methoden haben bestimmte Signaturen. Diese Signaturen habe ich im Moment im Hauptprogramm durch type TMeineMethode = procedure(blabla); stdcall; definiert. Wenn ich nun was an den DLL Funktionen ändere, muss ich diese Änderungen auch im Hauptprogramm im type Statement ändern.
Nun dachte ich mir er wäre doch schöner wenn man das an einer Stelle hätte, dort ändert man was, kompiliert DLL und Anwendung und fertig. Ich hab mir das so gedacht:
1. eine Unit1 in der die Signaturen stehen, also im interface Teil type TMeineMethode = procedure() usw...
2. binde ich Unit1 in das DLL Projekt ein, erstelle im interface Teil des DLL Codes eine Variable mit meineMethode: TMeineMethode
3. füge ich die Implementation meiner Methode im implementation Teil des DLL Codes hinzu
4. weise der Variablen meineMethode den Zeiger meiner implementierten DLL Methode im initialization teil der DLL zu
5. exportiere die Variable meineMethode
6. kompiliere DLL
7. binde ich Unit1 in meine Hauptanwendung ein, erstelle da auch eine Variable mit meineMethode: TMeineMethode
8. lade die DLL mit LoadLibrary und weise meineMethode den Zeiger der DLL Methode mit GetProcAddress() zu
9. kompiliere Anwendung
Das geht auch alles wunderbar, GetProcAddress() findet die exportierte Variable wo ja eigentlich der Methodenzeiger der DLL Funktion drin stehen sollte. Nur wenn dann die Methode aufgerufen wird gibts eine Privileged instruction exception. Also hab ich die DLL Methode noch im interface teil angegeben. Dann kommt keine Privileged instruction exception mehr aber eine Zugriffsverletzung.
Kann mir jemand sagen warum das so ist, bzw ob mein Vorhaben so überhaupt möglich ist?
Vielen Dank.
:edit:
vieleicht zur Verdeutlichung hier noch etwas Code:
Unit1.pas
Delphi-Quelltext
1: 2:
| interface TInitMethod = procedure (AData: PInitParameter); stdcall; |
DLL Main.pas
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| interface
uses Unit1.pas
var Init: TInitMethod;
procedure Library_SetInOutProperty(AProperty: PInOutProperty); stdcall;
implementation
procedure Library_Init(AData: PInitParameter); stdcall; begin
end;
initialization Init := @Library_Init;
end. |
Hauptanwendung Main.pas
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| interface TMeineKlasse = class private fSetInOutProperty: TOutputDeviceSetInOutProperty;
public function Load(): Boolean; end;
implementation
function TMeineKlasse.Load(): Boolean; var lParameter: TInOutProperty; begin ... LoadLibrary ... @fSetInOutProperty := GetProcAddress(..., 'Init');
fSetInOutProperty(@lParameter); end; |
VT - Fr 01.01.10 18:16
ok, ich glaube ich hab gefunden warum das abstürzt. Dazu folgender Thread:
http://www.delphi-forum.de/topic_DLL+GetProcAddress_95494,0.html
es ist wohl nun so:
- die Variable Init in der DLL ist ein Pointer der die Adresse der DLL Methode enthält
- die Variable fSetInOutProperty in der Hauptanwendung ist ein Pointer der nach Aufruf von GetProccAddress die Adresse der Variable Init enthält
Mann müsste also den Inhalt von fSetInOutProperty in einen Methodenzeiger umwandeln um dann die Methode aufrufen zu können. Nur wie?
VT - Fr 01.01.10 18:32
Eigene threads selbst beantworten fetzt *G*
Ich kann die DLL Methode nun aufrufen ohne Absturz indem ich folgendes mache:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| var lPointerToMethodPointer: ^TOutputDeviceSetInOutProperty; ... lPointerToMethodPointer := GetProcAddress(...'Init'); fInit := lPointerToMethodPointer^; ... fInit(...); |
Nun kann ich auch die Library_ Methode wieder aus dem interface Teil nehmen.
Ich hoffe ich zerhau mir da nicht irgendwie den Speicher.
Kennt sich jemand aus und hat da bedenken?
:edit:
Anmerkung: es macht offenbar keinen Unterschied ob man in der DLL jetzt schreibt:
Init := @Library_Init; oder
@Init := @Library_Init; oder
Init := Library_Init;
Wahrscheinlich ist hier wieder Compilermagic am Werk.