Für die Programmierung der Plug-In-Struktur ist ein Plug-In-Container erforderlich, um den Betrieb jeder DLL zu steuern und jedes unterteilte Subsystem in einer DLL-Bibliotheksdatei anzuordnen. Für jedes DLL-Programm müssen Schnittstellenfunktionen für den Container reserviert werden. Zu den Schnittstellenfunktionen gehören im Allgemeinen: Funktionen, die den Aufruf der DLL-Bibliothek starten, und Funktionen, die die DLL-Bibliothek schließen. Über die Schnittstellenfunktion kann der Plug-In-Container Parameter an das DLL-Modul übergeben, um eine dynamische Steuerung zu erreichen. Ich werde die spezifischen Implementierungsdetails erläutern und den Antwortcode unten angeben.
Möglicherweise müssen Sie zunächst die Struktur von UNIT und die Struktur von Projekten in DELPHI verstehen. In diesem Artikel werden die theoretischen Details der DLL-Programmierung nicht ausführlich erläutert, sondern nur einige praktische Codes demonstriert. Ich habe das Buch „DELPHI In-Depth Programming“ von Liu Yi studiert.
Ich bin auch in der Einführungsphase von DELPHI und habe das Gefühl, dass es in dieser DLL-Entwicklung einige Dinge gibt, die es wert sind, besprochen zu werden. Deshalb schreibe ich diesen Artikel und hoffe, dass Sie mir großzügige Vorschläge machen können.
Einführung in das Beispielprogramm
Um das Lesen zu erleichtern, werde ich einen Teil des Programmcodes eines MIS-Systems verwenden, um einige Methoden der Plug-in-Programmierung zu demonstrieren. Das Beispielprogramm ist eine typische C/S-Struktur-DBMS-Anwendung. Unser Fokus liegt auf den Steueranweisungen des Rahmenprogramms (im Folgenden als Hall bezeichnet) und der Antwortsteuerung des DLL-Plug-In-Programms.
1. Programmstruktur
Der Plug-in-Container Hall wird mithilfe eines unabhängigen Projekts erstellt. Das Hauptfenster des Hall entspricht dem MDI-Containerformular im MDI-Programm. Die Schnittstellenfunktionen in der Dll werden explizit im Hall aufgerufen.
Jedes Plug-in-Programm verwendet unabhängig von gewöhnlichen Projekten ein DLL-Projekt, und die durch die entsprechende Kompilierung generierten Dateien haben das Suffix DLL.
=550) window.open('/upload/20080315181507424.jpg');" src="http://files.VeVB.COm/upload/20080315181507424.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';" border=0>
2. Schnittstellendesign
Im Beispielprogramm Narcissus behalten wir uns zwei Schnittstellenfunktionen vor:
ShowDLLForm
Diese Funktion übergibt das Handle der Anwendung an das untergeordnete DLL-Fenster und das DLL-Programm erstellt dynamisch eine Instanz des DLL-Formulars. Sie können auch einige Geschäftslogiken in Form von Parametern an das DLL-Unterfenster übergeben, z. B. den Formularnamen, den aktuell angemeldeten Benutzernamen usw. Mit dieser Funktion können Sie beim ersten Aufruf eine DLL-Formularinstanz erstellen.
FreeDLLForm
Diese Funktion zeigt die Freigabe der DLL-Fensterinstanz an und ruft die FreeDLLForm-Methode jedes DLL-Formulars auf, um die erstellte Instanz beim Beenden der Anwendung freizugeben. Andernfalls wird ein schreibgeschützter Speicherfehler verursacht. Ebenso können Sie auch einige Geschäftslogiken, die bei der Freigabe des Formulars ausgeführt werden müssen, in Form von Parametern an das DLL-Formular übergeben.
3. Debugging-Methode
Das DLL-Formularprogramm kann nicht direkt ausgeführt werden und benötigt zum Aufruf einen Plug-In-Container. Daher müssen wir zunächst ein grundlegendes Hall-Programm implementieren und dann Hall.exe in einem festen Verzeichnis speichern. Nehmen Sie für jedes DLL-Projekt folgende Einstellungen vor:
1) Öffnen Sie das DLL-Projekt
2) Wählen Sie das Menü „Ausführungsparameter“.
3) Navigieren Sie im Popup-Fenster zu unserem Container Hall.exe
Auf diese Weise wird das Hall-Programm beim Debuggen des DLL-Programms automatisch aufgerufen und das DLL-Programm wird über die im Hall reservierte Aufrufschnittstelle debuggt.
Grundlegende Implementierung des Plug-in-Programms
Die Entwurfsmethode des DLL-Programms unterscheidet sich nicht wesentlich von der von gewöhnlichem WINAPP, außer dass alle Fenster in der DLL-Bibliothek als spezielle „Ressource“ gespeichert werden und manuell aufgerufen werden müssen, anders als bei WINAPP, wo Projekte automatisch erstellt werden. Die Methode zur Deklaration von Schnittstellenfunktionen ist sehr einfach
1) Deklarieren Sie die Funktion im Abschnitt „Implementierung“ von Unit
2) Fügen Sie die Markierung stdcall am Ende der Funktionsdeklarationsanweisung hinzu
3) Verwenden Sie vor der begin-Anweisung des Projektcodes (Project View Source) die exports-Anweisung, um die Funktionsschnittstelle zu deklarieren
Um den Code prägnant zu gestalten, füge ich persönlich gerne unabhängig eine Unit-Einheit (File New - Unit) in das Projekt ein und definiere dann alle in dieser Unit auszugebenden Funktionskörper. Vergessen Sie nicht, auch die zu verwenden Einheit des Formulars, auf das verwiesen wird. Ich habe diese Einheit UnitEntrance genannt, das anzuzeigende Fenster in der ShowDLLForm-Funktion initialisiert und die Show-Methode aufgerufen, um es anzuzeigen. HALL übergibt den angemeldeten Benutzernamen als Parameter. Nach dem Abrufen des Benutzernamens kann eine gewisse Berechtigungssteuerung durchgeführt werden spiegelt sich in der Schnittstelleninitialisierung wider.
Der Code lautet wie folgt
Einheit UnitOfficeEntrance;
Schnittstelle
verwendet
Windows, Nachrichten, SysUtils, Varianten, Klassen, Grafiken, Steuerelemente, Formulare;
function ShowDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;stdcall;
function FreeDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;stdcall;
Durchführung
nutzt UnitOfficialMainForm; //Wechsel zur Einheit von MAINFORM
var
DLL_Form:TFormOfficialMain; //Ändern Sie den Namen von MAINFORM
//--------------------------
//Name: ShowDLLForm
//Func: DLL-Plugin ruft die Eingabefunktion auf
//Absatz: AHandle angehängtes Programmhandle; ACaption-Titel dieses Formulars
//Rtrn: N/A
//Auth: CST
//Datum: 3.6.2005
//--------------------------
function ShowDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;
beginnen
Ergebnis:=true;
versuchen
Application.Handle:=AHandle; //Angehängt an den Hauptprogrammcontainer
DLL_Form:=TFormOfficialMain.Create(Application); //Ändern Sie den Namen von MAINFORM
versuchen
mit DLL_Form tun
beginnen
Bildunterschrift := ACaption;
StatusBar.Panels.Items[0].Text := AUserID;
//Benutzeroberfläche konfigurieren
Zeigen ;
Ende;
außer
auf e:Exception tun
beginnen
dll_form.Free;
Ende;
Ende;
außer
Ergebnis:=false;
Ende;
Ende;
//--------------------------
//Name: FreeDLLForm
//Func: DLL-Plugin ruft die Exportfunktion auf
//Para: AHandle angehängtes Programmhandle
//Rtrn: wahr/falsch
//Auth: CST
//Datum: 2005-6-11
//--------------------------
function FreeDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;
beginnen
Application.Handle:=AHandle; //Angehängt an den Hauptprogrammcontainer
if DLL_Form.Showing then DLL_Form.Close; //Wenn das Fenster zuerst geöffnet und geschlossen wird, kann das Auslösen von FORM.CLOSEQUERY den Schließvorgang abbrechen
wenn nicht DLL_Form.Showing dann
beginnen
DLL_Form.Free;
Ergebnis:=true;
Ende //Immer noch offen, was CLOSEQUERY.CANCLOSE=FALSE anzeigt
anders
beginnen
Ergebnis:=false;
Ende;
Ende;
Ende.
Der Code der DLL-Projektdatei lautet wie folgt:
Bibliotheksbeamter;
{ Wichtiger Hinweis zur DLL-Speicherverwaltung: ShareMem muss sein
erste Einheit in der USES-Klausel Ihrer Bibliothek UND in der Ihres Projekts (select
Project-View Source) USES-Klausel, wenn Ihre DLL Prozeduren exportiert oder
Funktionen, die Zeichenfolgen als Parameter oder Funktionsergebnisse übergeben
gilt für alle Zeichenfolgen, die an und von Ihrer DLL übergeben werden – auch solche, die
sind in Datensätzen und Klassen verschachtelt. ShareMem ist die Schnittstelleneinheit zu
der Shared-Memory-Manager BORLNDMM.DLL, der zusammen bereitgestellt werden muss
Übergeben Sie mit Ihrer DLL die Verwendung von BORLNDMM.DLL
mit PChar- oder ShortString-Parametern.
verwendet
SysUtils,
Klassen,
UnitOfficialDetailForm in 'UnitOfficialDetailForm.pas' {FormOfficialDetail},
UnitOfficialMainForm in 'UnitOfficialMainForm.pas' {FormOfficialMain},
UnitOfficeEntrance in 'UnitOfficeEntrance.pas',
UnitOfficialClass in '../../Public/Library/UnitOfficialClass.pas',
UnitMyDataAdatper in '../../Public/Library/UnitMyDataAdatper.pas',
UnitMyHeaders in '../../Public/Library/UnitMyHeaders.pas';
{$R *.res}
exports ShowDLLForm,FreeDLLForm; //Schnittstellenfunktion
beginnen
Ende.
Sobald das Plug-in-Programm das DLL-Fenster aufruft, bleibt die Fensterinstanz über dem HALL-Fenster, sodass Sie sich keine Sorgen über eine Verdeckung machen müssen.
Implementierung von Containerprogrammen
1. Einführung von Schnittstellenfunktionen
Es gibt zwei Möglichkeiten, Funktionen in der DLL-Bibliothek aufzurufen: explizit und implizit. Der explizite Aufruf ist flexibler, daher verwenden wir den expliziten Aufruf. In Delphi müssen Sie den Funktionstyp für die Schnittstellenfunktion deklarieren und dann eine Instanz des Funktionstyps instanziieren. Die Instanz ist tatsächlich ein Zeiger auf die Funktion. Über den Zeiger können wir auf die Funktion zugreifen, Parameter übergeben und abrufen der Rückgabewert. Fügen Sie die Deklaration der Funktionsklasse im Abschnitt „Interface“ der Unit-Datei hinzu:
Typ
// Definieren Sie den Schnittstellenfunktionstyp. Die Schnittstellenfunktion stammt von der DLL-Schnittstelle
TShowDLLForm = Function(AHandle:THandle; ACaption: String; AUserID:string):Boolean;stdcall;
TFreeDLLForm = Function(AHandle:THandle; ACaption: String; AUserID:string):boolean;stdcall;
Für die Anzeige aufrufender Bibliotheksfunktionen sind die folgenden Schritte erforderlich:
1) Laden Sie die DLL-Bibliotheksdatei
2) Funktionsadresse abrufen
3) Funktion ausführen
4) Geben Sie die DLL-Bibliothek frei
Als nächstes werden wir diese Schritte im Detail besprechen.
2. Laden Sie die DLL-Bibliotheksdatei
Die DLL-Bibliothek kann durch Aufrufen der API-Funktion LoadLibrary in den Speicher geladen werden. Wir werden hier nicht auf die Auswirkungen von DLL auf die Speicherverwaltung eingehen. Der Parameter von LoadLibrary ist der Adresspfad der DLL-Datei. Wenn das Laden erfolgreich ist, wird eine Variable vom Typ CARDINAL als Handle der DLL-Bibliothek zurückgegeben Wenn die Datei fehlschlägt, wird eine 0 zurückgegeben.
3. Schnittstellenfunktionen instanziieren
Die API-Funktion zum Abrufen des Schnittstellenfunktionszeigers lautet GetProcAddress (Bibliotheksdateihandle, Funktionsname). Wenn die Funktion fehlschlägt, wird NIL zurückgegeben.
Definieren Sie eine Funktionszeigervariable mithilfe des oben definierten Funktionstyps und verwenden Sie dann den @-Operator, um die Funktionsadresse abzurufen, sodass Sie die Zeigervariable für den Zugriff auf die Funktion verwenden können. Der Hauptcode lautet wie folgt:
…
var
ShowDLLForm: TShowDLLForm; //DLL-Schnittstellenfunktionsinstanz
FreeDLLForm: TFreeDLLForm;
beginnen
versuchen
beginnen
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr := GetProcAddress(APlugin.ProcAddr ,'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
if ShowDllForm(Self.Handle, APlugin.Caption, APlugin.UserID) dann
Ergebnis:=Wahr
…
4. Eine spezifische Implementierungsmethode
Um Plug-Ins strukturiert zu verwalten und zukünftige Systemerweiterungen zu erleichtern, können wir die in der Datenbank aufgezeichneten verfügbaren DLL-Informationen kombinieren und dann durch Abfragen der Datenbankdatensätze dynamisch auf das DLL-Programm zugreifen.
1) Systemmodul-Tischdesign
Für MIS-Systeme können Sie vorhandene DBS-Bedingungen verwenden, um eine Systemmodultabelle zu erstellen, um DLL-Dateien und zugehörige Informationen aufzuzeichnen, die Systemmodulen zugeordnet sind.
Rollentyp „Feldname“.
AutoID indexINT
modAlias Modulalias VARCHAR
modName Modulname VARCHAR
modWndClass bildet einen eindeutigen Bezeichner VARCHAR
modFile DLL-Pfad VARCHAR
modMemo-Notiztext
・Modulaliase werden verwendet, um Benennungsregeln während der Programmierentwurfsphase zu vereinheitlichen, insbesondere als Referenz für Teammitglieder während der Teamentwicklung.
・Der Modulname wird als ACAPTION-Parameter an die SHOWDLLFORM-Funktion als Titel des DLL-Fensters übergeben.
・Der eindeutige Bezeichner des Formulars ist der KLASSENNAME des Hauptfensters im DLL-Submodul, der zur Bestimmung des zu steuernden Fensters zur Laufzeit verwendet wird.
・Der DLL-Pfad speichert den DLL-Dateinamen, der im Programm in einen absoluten Pfad umgewandelt wird.
2) Plug-in-Informationsdatenstruktur
Durch Definieren einer Datenschnittstelle, die Plug-In-bezogene Informationen aufzeichnet, können DLL-Plug-Ins zentral gesteuert werden. Fügen Sie den folgenden Code zum Abschnitt „Schnittstelle“ hinzu:
Typ
//Plugin-Informationsklasse definieren
TMyPlugins = Klasse
Caption:String; //DLL-Formulartitel
DllFileName:String; //DLL-Dateipfad
WndClass:String; //Formularidentifikation
Benutzer-ID:string; //Benutzername
ProcAddr:THandle; //Bibliothekshandle geladen von LOADLIBRARY
FuncAddr:Pointer; //SHOWDLLFORM-Funktionszeiger
FuncFreeAddr:Pointer; //FREEDLLFORM-Funktionszeiger
Ende;
…
Erstellen Sie für jedes Plug-in eine Instanz von TMyPlugins. Die Initialisierungsmethoden für diese Instanzen werden unten erläutert.
3) Plug-in-Ladefunktion
In diesem Beispiel wird das DLL-Fenster geladen und angezeigt, wenn das Ereignis das Öffnen des untergeordneten Fensters in HALL auslöst. Nachdem das Schaltflächenereignis ausgelöst wurde, stellen Sie zunächst fest, ob die DLL entsprechend der Plug-In-Strukturinstanz geladen wurde. Steuern Sie die Anzeige oder das Schließen des Fensters, und greifen Sie auf die Datentabelle zu Ordnen Sie die Felder der Plug-In-Struktur zu und führen Sie dann den Ladevorgang aus.
Der Teilcode lautet wie folgt
…
//--------------------------
//Name: OpenPlugin
//Funktion: Plug-in-Informationssteuerungsprozess: Initialisierung==》Berechtigungen festlegen==》DLL-Fenster laden
//Para: APlugin-TMyPlugins; sAlias alias; iFuncValue-Berechtigungswert
//Rtrn: N/A
//Auth: CST
//Datum: 2005-6-2
//--------------------------
procedure TFormHall.OpenPlugin(AFromActn: TAction ;APlugin:TMyPlugins; sAlias:string; sUserID:string);
var hWndPlugin:HWnd;
beginnen
//Bestimmen Sie, ob das Plug-in-Fenster geladen wurde hWndPlugin:=FindWindow(PChar(APlugin.WndClass),nil);
if hWndPlugin <> 0 then //Das Plug-In-Fenster wurde geladen
beginnen
Wenn nicht IsWindowVisible(hWndPlugin), dann
beginnen
AFromActn.Checked := True;
ShowWindow(hWndPlugin,SW_SHOWDEFAULT); //Anzeige
Ende
anders
beginnen
AFromActn.checked := False;
ShowWindow(hWndPlugin,SW_HIDE);
Ende;
Exit; // Den Plug-in-Erstellungsprozess verlassen
Ende;
//Plug-in-Klasseninstanz initialisieren
Wenn nicht, dann InitializeMyPlugins(APlugin,sAlias).
beginnen
showmessage('Fehler beim Initialisieren der Plug-in-Klasse.');
Ausfahrt;
Ende;
//Den aktuellen Berechtigungswert abrufen
APlugin.UserID := sUserID;
//DLL-Fenster laden
Wenn nicht LoadShowPluginForm(APlugin), dann
beginnen
showmessage('Fehler beim Laden des Center-Plug-Ins.');
Ausfahrt;
Ende;
Ende;
//--------------------------
//Name: InitializeMyPlugins
//Funktion: MYPLUGIN-Instanz initialisieren (Caption | DllFileName | IsLoaded)
//Absatz: APlugin-TMyPlugins
//Rtrn: N/A
//Auth: CST
//Datum: 2005-6-2
//--------------------------
function TFormHall.InitializeMyPlugins(APlugin:TMyPlugins; sAlias:String):Boolean;
var
strSQL:string;
myDA:TMyDataAdapter;
beginnen
Ergebnis:=Falsch;
myDA:=TMyDataAdapter.Create;
strSQL:='SELECT * FROM SystemModuleList WHERE modAlias='+QuotedStr(sAlias);
versuchen
myDA.RetrieveData(strSQL);
außer
auf E:Exception do
beginnen
Ergebnis:=false;
myDA.Free;
Ausfahrt;
Ende;
Ende;
versuchen
beginnen
mit myDA.MyDataSet tun
beginnen
wenn Not IsEmpty dann
beginnen
APlugin.Caption:= FieldByName('modName').Value;
APlugin.DllFileName := FieldByName('modFile').Value;
APlugin.WndClass := FieldByName('modWndClass').Value;
Ergebnis:=True;
Ende;
Schließen;
end; //end of with...do...
end; //Ende des Versuchs
außer
auf E:Exception do
beginnen
Ergebnis:=Falsch;
myDA.Free;
Ausfahrt;
end; //Ende der Ausnahme
end; //Ende des Versuchs...außer
myDA.Free;
Ende;
//--------------------------
//Name: LoadShowPluginForm
//Funktion: Laden Sie das DLL-Plugin und zeigen Sie das Fenster an
//Absatz: APlugin-TMyPlugins
//Rtrn: true-erstellt erfolgreich
//Auth: CST
//Datum: 2005-6-2
//--------------------------
function TFormHall.LoadShowPluginForm (const APlugin:TMyPlugins):boolean;
var
ShowDLLForm: TShowDLLForm; //DLL-Schnittstellenfunktionsinstanz
FreeDLLForm: TFreeDLLForm;
sPath:string; //Vollständiger Pfad der DLL-Datei
beginnen
versuchen
beginnen
sPath:=ExtractFilepath(Application.ExeName)+ 'plugins/' + APlugin.DllFileName;
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr := GetProcAddress(APlugin.ProcAddr ,'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
if ShowDllForm(Self.Handle, APlugin.Caption, APlugin.UserID) dann
Ergebnis:=Wahr
anders
Ergebnis:=Falsch;
Ende;
außer
auf E:Exception do
beginnen
Ergebnis:=Falsch;
ShowMessage('Fehler beim Laden des Plug-In-Moduls. Bitte überprüfen Sie, ob die Dateien im PLUGINS-Verzeichnis vollständig sind.');
Ende;
Ende;
Ende;
…
4) DLL-Fenstersteuerung
Wie der Code in 3) zeigt, erfolgt das Öffnen und Schließen des DLL-Fensters nur auf der Präsentationsebene. Durch das Schließen des Fensters wird das DLL-Fenster nicht tatsächlich freigegeben. Es wird lediglich die API-Funktion FindWindow aufgerufen, um das Formularhandle entsprechend dem Fenster zu erhalten Bezeichner (d. h. Form.name). Verwendung Der Parameter nCmdShow der Funktion SHOWWINDOW steuert das Anzeigen/Ausblenden des Fensters.
Tatsächlich ist dies eine Schwachstelle in der Implementierung meines Programms. Wenn die Self.close-Methode im DLL-Fenster verwendet wird, kann dies aufgrund der eingeschränkten Funktionen nicht behoben werden der letzte Ausweg. Daher muss die Schaltfläche zum Schließen des Hauptfensters jedes DLL-Programms ausgeblendet werden. :-P
5) Veröffentlichung der DLL-Bibliothek
Wenn das Programm beendet wird, muss die DLL-Bibliothek entsprechend der Plug-In-Informationsinstanz einzeln freigegeben werden. Die Funktion zum Freigeben der DLL-Bibliothek lautet wie folgt:
procedure TFormHall.ClosePlugin(aPLG:TMyPlugins);
var
FreeDLLForm:TFreeDLLForm;
beginnen
wenn aPLG.ProcAddr = 0, dann beenden;
wenn aPLG.FuncFreeAddr = nil, dann beenden;
@FreeDLLForm:=aPLG.FuncFreeAddr;
Wenn nicht FreeDLLForm(Application.Handle,'',''), dann
showMessage('err');
Ende;
Zusammenfassung
Der laufende Effekt dieses Beispielprogramms ist wie folgt:
=550) window.open('/upload/20080315181507979.jpg');" src="http://files.VeVB.COm/upload/20080315181507979.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';" border=0>
Da es unter den oben genannten Methoden aufgrund begrenzter Fähigkeiten viele ungelöste Probleme gibt, habe ich einige Vertuschungsmethoden übernommen, die nicht sinnvoll erscheinen. Ich hoffe, dass jeder nach ein wenig Ausprobieren eine bessere Lösung finden kann Tolle Möglichkeit, mehr zu erfahren.