Problem mit eigenen Plugins
-
Hallo,
ich habe hier schon mal einen Post über dieses Problem schon in diesem Forum geschrieben, aber irgendwie gab es keine Antworten, deshalb versuche ich es nochmal hier.Also, wie die Überschrift schon sagt, entwickle ich eine Anwendung, die Plugins (DLLs) unterstützt. Das Problem dabei ist das Laden der Plugins. Dabei kommen Zugriffsverletzungen beim Laden. Ich weiss nicht woran das liegt - vielleicht sehe ich den Wald vor lauter Bäumen nicht und brauche deshalb eure Hilfe.
Da das Projekt sehr groß ist, dachte ich, dass es an etwas anderen im Projekt liegen könnte und deshalb habe ich ein anderes neues kleines Projekt erstellt, dass nur die Plugins lädt. Und dabei taucht das gleiche Problem auf - Zugriffverletzungen!
Das Projekt habe ich mal hochgeladen: http://rapidshare.com/files/455570709/Test.zip
Ich muss dazu sagen, dass es zwei Projekte sind, also einmal das Programm, dass die Plugins lädt und einmal die DLL selbst. Das Problem tritt auf, wenn ich mehrere DLLs zum laden habe. Und da ich faul bin, habe ich das eine Plugin 4x in das Plugin-Verzeichnis des Programms kopiert zum testen (ich habe auch 4 verschiedene ausprobiert - kommt auf das gleiche hinaus).
Ich bin mittlerweise verzweifelt! Ich möchte Plugins über das C-Interface erstellen nicht über COM oder ähnliches...
danke
-
Könntest du deine Aversion gegen COM kurz mal erklären? Du brauchst ja nicht gleich eine Typbibliothek erstellen, aber zumindest COM-Interfaces würden die Arbeit schon enorm vereinfachen.
Wer schreibt denn für deine Anwendung alles Plug-ins, und mit welchen Compilern?
Hawkxxx schrieb:
RapidShare ist IMO unzumutbar. Wenn du keinen eigenen Webserver hast, leg dir doch bitte so etwas wie Dropbox zu. Dann steigt auch die Wahrscheinlichkeit, daß du mehr Antworten bekommst.
Ich hab mir deinen Code trotzdem mal angesehen. Folgendes ist mir aufgefallen:
// File1.cpp
#include <vcl.h>Es gilt grundsätzlich: wenn du Zeiger auf Klassen zwischen verschiedenen Modulen austauschst, *müssen* beide Module mit Laufzeit-Packages und mit dynamischer RTL gelinkt werden, außerdem jeweils mit derselben Version von Delphi/C++Builder. Andernfalls kracht es früher oder später. * ```cpp extern "C" { __declspec(dllexport) void __stdcall InitPlugin(DWORD MDBHandle)...
Aha, die DLL exportiert __stdcall-Funktionen. Warum fehlt __stdcall dann hier:
// Unit1.h typedef void (*INITPLUGIN) (DWORD MainFormHandle);
?
// Unit1.cpp
//-----Überprüfen ob DLL ein Plugin ist
PLGs->plgDllHandle[plgcount] =LoadLibraryW((plgPath+s.Name).w_str());
if (PLGs->plgDllHandle[plgcount]==NULL) continue;
if (GetProcAddress(PLGs->plgDllHandle[plgcount],"InitPlugin")==NULL)
{
FreeLibrary(PLGs->plgDllHandle[plgcount]);
continue;
}Einfach mal DLLs draufloszuladen ist eine ganz schlechte Idee. Ob die DLL ein Plug-in ist, solltest du schon vorab entscheiden. Wenn du die DLL einfach lädst, wird der Initialisierungscode (DllMain() und statische Initializer) aufgerufen, und du weißt nie, was der Initialisierungscode einer unbekannten DLL mit deinem Programm anstellt. Die einfachste und zugleich eleganteste Lösung wäre, wenn du in einem bestimmten Key in der Registry eine Liste der installierten Plug-ins unterhältst und jedem Plug-in-Schreiber abverlangst, sein Plug-in dort einzutragen. Wenn du es unbedingt so haben möchtest, daß man die Plug-ins einfach nur in ein bestimmtes Unterverzeichnis entpacken muß, dann kannst du z.B. [so etwas](http://stackoverflow.com/questions/1128150/win32-api-to-enumerate-dll-export-functions) machen oder die [PE-Exporttabelle](http://msdn.microsoft.com/de-de/magazine/cc301805%28en-us%29.aspx) selbst einlesen - aber dazu mußt du sehr genau wissen, was du machst, und selbst wenn dem so wäre, rechtfertigt der Ertrag den Aufwand nicht. * ```cpp //---------Funktionen laden aus DLL und an PlugIn-Struktur übergeben INITPLUGIN InitPlugin =(INITPLUGIN)GetProcAddress(PLGs->plgDllHandle[plgcount],"InitPlugin"); GETPLUGININFO GetPluginInfo =(GETPLUGININFO)GetProcAddress(PLGs->plgDllHandle[plgcount],"GetPluginInfo"); EXECPLUGIN ExecPlugin=(EXECPLUGIN)GetProcAddress(PLGs->plgDllHandle[plgcount],"ExecPlugin"); EXITPLUGIN ExitPlugin=(EXITPLUGIN)GetProcAddress(PLGs->plgDllHandle[plgcount],"ExitPlugin"); GETPLUGINVERSION GetPluginVersion=(GETPLUGINVERSION)GetProcAddress(PLGs->plgDllHandle[plgcount],"GetPluginVersion"); ONNOTIFY OnNotify=(ONNOTIFY)GetProcAddress(PLGs->plgDllHandle[plgcount],"OnNotify");
Du wirst mir zustimmen, daß dieser Ansatz furchtbar redundant und fehleranfällig ist. COM-Interfaces sind schon eine tolle Sache.
try
{
wchar_t *plginfo = new wchar_t[256];
...
delete []plginfo;
}
catch(...)Mit exceptionsicherem Programmieren hast du es auch nicht so, oder? ;) Schau dir mal std::vector<> an. * ```cpp void __fastcall TForm1::FormShow(TObject *Sender) { LoadPlugins(); }
Gehört das nicht eher in den Konstruktor? So werden die Plug-ins jedesmal geladen, wenn FormShow() aufgerufen wird.
-
-
Vielen, vielen Dank!!!!
Ich habe den Fehler gefunden! Es war das __stdcall was gefehlt hatte!! Super Danke dir!
Man sieht den Wald wirklich vor lauter Bäumen nicht
Um deine Fragen zu beantworten, also es ist nicht der Original-Code, der im Hauptprogramm zu finden ist, denn der ist geschickter programmiert. Wie gesagt ich habe soviel daran rumgebastelt und auch unmögliche Dinge probiert wie dir richtig aufgefallen ist mit dem OnShow-Event, ich dachte vielleicht übersehe ich was und habe mal den Code von OnCreate nach OnShow verlagert.
Und mit dem Try....catch das war auch nur aus lauter Verzweiflung, weil ich wirklich nicht wusste warum das nicht funktioniert und bekanntlich ist ja in einer Test-Anwendung alles erlaubt -oder so ähnlich
Also die COM-Schnittstelle will ich nicht verwenden, da meiner Menung nach für so ein simples Plugin man sich nicht noch mit der COM auseinandersetzen muss. Es sollen Plugins mit Delphi, VC++ und anderen Compilern möglich sein. Denn dank deiner Hilfe funktioniert das jetzt auch so!
Danke nochmal!
-
Hawkxxx schrieb:
Um deine Fragen zu beantworten, also es ist nicht der Original-Code, der im Hauptprogramm zu finden ist, denn der ist geschickter programmiert. Wie gesagt ich habe soviel daran rumgebastelt und auch unmögliche Dinge probiert wie dir richtig aufgefallen ist mit dem OnShow-Event, ich dachte vielleicht übersehe ich was und habe mal den Code von OnCreate nach OnShow verlagert.
Sicher ist dein Hauptprogramm dann auch geschickt genug, keine Objekte über Modulgrenzen hinweg auszutauschen, oder? Ich habe das oben nicht zum Spaß geschrieben
Desgleichen gilt für die Warnung bezüglich des "Drauflosladens" von DLLs.
Hawkxxx schrieb:
Und mit dem Try....catch das war auch nur aus lauter Verzweiflung, weil ich wirklich nicht wusste warum das nicht funktioniert und bekanntlich ist ja in einer Test-Anwendung alles erlaubt -oder so ähnlich
Mich amüsierte nicht das try/catch, sondern daß du es sozusagen im gleichen Atemzug geschafft hast, ein potentielles Speicherleck zu produzieren
Hawkxxx schrieb:
Also die COM-Schnittstelle will ich nicht verwenden, da meiner Menung nach für so ein simples Plugin man sich nicht noch mit der COM auseinandersetzen muss.
Selbst in diesem Fall sind COM-Interfaces eine Erleichterung. Ich kann das morgen mal erläutern, heute wird's nichts mehr...
Hawkxxx schrieb:
Es sollen Plugins mit Delphi, VC++ und anderen Compilern möglich sein. Denn dank deiner Hilfe funktioniert das jetzt auch so!
Das bleibt die Frage