プラグイン構造のプログラミングには、各 DLL の動作を制御し、分割された各サブシステムを DLL ライブラリ ファイルに配置するためのプラグイン コンテナが必要です。 DLL プログラムごとに、インターフェイス関数をコンテナ用に予約する必要があります。通常、インターフェイス関数には、DLL ライブラリの呼び出しを開始する関数と DLL ライブラリを閉じる関数が含まれます。インターフェース関数を通じて、プラグイン コンテナーはパラメータを DLL モジュールに渡し、動的制御を実現できます。具体的な実装内容とレスポンスコードを以下に説明します。
最初に、DELPHI の UNIT の構造とプロジェクトの構造を理解する必要があるかもしれません。この記事では、DLL プログラミングの理論的な詳細については詳しく説明しませんが、Liu Yi 著の「DELPHI In- Depth Programming」という本を読んで実際に使用できるコードをいくつか紹介するだけです。
私も DELPHI の入門段階にいますが、この DLL 開発には議論すべき点がいくつかあると感じているので、私がうまくいっていない点について寛大なアドバイスをいただければ幸いです。
サンプルプログラムの紹介
読みやすくするために、MIS システムのプログラム コードの一部を使用して、プラグイン プログラミングのいくつかの方法を示します。サンプルプログラムは典型的なC/S構造DBMSアプリケーションであり、フレームワークプログラム(以下、ホール)の制御文とdllプラグインプログラムの応答制御を中心に説明します。
1. プログラムの構成
プラグイン コンテナ Hall は、独立したプロジェクトを使用して作成されます。Hall のメイン ウィンドウは、MDI プログラムの MDI コンテナ フォームと同等であり、Dll 内のインターフェイス関数は Hall 内で明示的に呼び出されます。
各プラグイン プログラムは、通常のプロジェクトとは異なり、独自のプロジェクトを使用して DLL ウィザードを作成し、対応するコンパイルによって生成されるファイルには拡張子 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. インターフェース設計
サンプル プログラム Narcissus では、2 つのインターフェイス関数を予約しています。
DLLフォームの表示
この関数はアプリケーションのハンドルを DLL 子ウィンドウに渡し、DLL プログラムは DLL フォームのインスタンスを動的に作成します。フォーム名、現在ログインしているユーザー名などのビジネス ロジックをパラメーターの形式で DLL サブウィンドウに渡すこともできます。この関数を初めて呼び出すときに、DLL フォーム インスタンスを作成するために使用します。
無料DLLフォーム
この関数は、DLL ウィンドウ インスタンスの解放を表示し、アプリケーションの終了時に各 DLL フォームの FreeDLLForm メソッドを呼び出して、作成されたインスタンスを解放します。そうしないと、メモリ読み取り専用エラーが発生します。同様に、フォームをリリースするときに実行する必要があるビジネス ロジックをパラメーターの形式で DLL フォームに渡すこともできます。
3. デバッグ方法
DLL 形式のプログラムは直接実行できず、呼び出すにはプラグイン コンテナが必要です。したがって、最初に基本的な Hall プログラムを実装し、次に Hall.exe を固定ディレクトリに保存する必要があります。 DLL プロジェクトごとに次の設定を行います。
1) DLLプロジェクトを開きます
2) メニューの「実行パラメータ」を選択します。
3) ポップアップ ウィンドウでコンテナ Hall.exe を参照します。
これにより、DLL プログラムのデバッグ時に Hall プログラムが自動的に呼び出され、Hall で予約された呼び出しインターフェイスを使用して DLL プログラムがデバッグされます。
プラグインプログラムの基本的な実装
DLL プログラムの設計方法は、プロジェクトが自動的に作成される WINAPP とは異なり、すべてのウィンドウが特別な「リソース」として DLL ライブラリに保存され、手動で呼び出す必要があることを除いて、通常の WINAPP の設計方法とあまり変わりません。インターフェース関数の宣言方法は非常に簡単です
1) ユニットの実装セクションで関数を宣言します。
2) 関数宣言文の最後にstdcallマークを追加します。
3) プロジェクト コード (プロジェクト ビュー ソース) の begin ステートメントの前に、exports ステートメントを使用して関数インターフェイスを宣言します。
コードを簡潔にするために、個人的には、プロジェクト内で独立して Unit ユニット (File New -- Unit) を追加し、出力されるすべての関数本体をこのユニット内で定義することを忘れないでください。参照されるフォームの単位を入力してください。このユニットに UnitEntrance という名前を付け、ShowDLLForm 関数で表示するウィンドウを初期化し、それを表示するために HALL がログインしているユーザー名をパラメーターとして渡し、いくつかの権限制御を実行できます。優れたインターフェイスの初期化に反映されます。
コードは次のとおりです
ユニットユニットオフィス入口;
インタフェース
用途
ウィンドウ、メッセージ、SysUtils、バリアント、クラス、グラフィックス、コントロール、フォーム。
関数 ShowDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;stdcall;
関数 FreeDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;stdcall;
実装
uses UnitOfficialMainForm //MAINFORM のユニットに変更します。
変数
DLL_Form:TFormOfficialMain //MAINFORM の名前に変更します。
//----------------------------------------
//名前: ShowDLLForm
//Func: DLL プラグインはエントリ関数を呼び出します
//Para: AHandle に添付されたプログラム ハンドル Aこのフォームのキャプション タイトル;
//Rtrn: 該当なし
//認証: CST
//日付: 2005-6-3
//----------------------------------------
関数 ShowDLLForm(AHandle: THandle; ACaption: 文字列; AUserID: 文字列):boolean;
始める
結果:= true;
試す
Application.Handle:=AHandle // メイン プログラム コンテナに固定されます。
DLL_Form:=TFormOfficialMain.Create(Application); //MAINFORM の名前に変更
試す
DLL_Form を使用して行う
始める
キャプション := Aキャプション;
StatusBar.Panels.Items[0].Text := AUserID;
//UIを設定する
見せる ;
終わり;
を除外する
e:例外で実行します
始める
dll_form.無料;
終わり;
終わり;
を除外する
結果:= false;
終わり;
終わり;
//----------------------------------------
//名前: FreeDLLForm
//Func: DLL プラグインはエクスポート関数を呼び出します
//パラ: AHandle にアタッチされたプログラム ハンドル
//Rtrn: true/false
//認証: CST
//日付: 2005-6-11
//----------------------------------------
関数 FreeDLLForm(AHandle: THandle; ACaption: 文字列; AUserID: 文字列):boolean;
始める
Application.Handle:=AHandle // メイン プログラム コンテナに固定されます。
if DLL_Form.Showing then DLL_Form.Close; //ウィンドウが最初に開いて閉じられた場合、FORM.CLOSEQUERY をトリガーすると閉じるプロセスをキャンセルできます。
DLL_Form.Showing ではない場合
始める
DLL_Form.Free;
結果:= true;
end //まだ開いており、CLOSEQUERY.CANCLOSE=FALSE を示します
それ以外
始める
結果:= false;
終わり;
終わり;
終わり。
DLL プロジェクト ファイルのコードは次のとおりです。
図書館公式;
{ DLL メモリ管理に関する重要な注意事項: ShareMem は
ライブラリの USES 句とプロジェクトの最初のユニット (選択
Project-View Source) DLL がプロシージャをエクスポートする場合、または
文字列をパラメータまたは関数の結果として渡す関数。
DLL との間で受け渡されるすべての文字列に適用されます。
ShareMem はレコードとクラスにネストされています。
BORLNDMM.DLL 共有メモリ マネージャー。これは一緒に展開する必要があります。
BORLNDMM.DLL の使用を避けるには、文字列情報を渡します。
PChar または ShortString パラメータを使用します。
用途
システムユーティリティ、
授業、
'UnitOfficialDetailForm.pas' {FormOfficialDetail} 内の UnitOfficialDetailForm、
「UnitOfficialMainForm.pas」内の UnitOfficialMainForm {FormOfficialMain}、
「UnitOfficeEntrance.pas」のUnitOfficeEntrance、
'../../Public/Library/UnitOfficialClass.pas' の UnitOfficialClass、
'../../Public/Library/UnitMyDataAdatper.pas' 内の UnitMyDataAdatper、
'../../Public/Library/UnitMyHeaders.pas' 内の UnitMyHeaders;
{$R *.res}
エクスポート ShowDLLForm,FreeDLLForm //インターフェイス関数;
始める
終わり。
プラグイン プログラムが DLL ウィンドウを呼び出すと、ウィンドウ インスタンスは HALL ウィンドウの上に残るため、オクルージョンを心配する必要はありません。
コンテナプログラムの実装
1. インターフェース機能の紹介
DLL ライブラリの関数を呼び出すには、明示的呼び出しと暗黙的呼び出しの 2 つの方法があります。明示的呼び出しのほうが柔軟性が高いため、明示的呼び出しを使用します。 Delphi では、インターフェイス関数の関数型を宣言し、その関数型のインスタンスをインスタンス化する必要があります。このインスタンスは実際には関数へのポインターであり、そのポインターを通じて関数にアクセスし、パラメーターを渡して取得することができます。戻り値。ユニット ファイルの Interface セクションに関数クラスの宣言を追加します。
タイプ
//インターフェイス関数のタイプを定義します。インターフェイス関数は DLL インターフェイスから取得されます。
TShowDLLForm = Function(AHandle:THandle; ACaption: String; AUserID:string):Boolean;stdcall;
TFreeDLLForm = Function(AHandle:THandle; ACaption: String; AUserID:string):boolean;stdcall;
ライブラリ関数の呼び出しを表示するには、次の手順が必要です。
1) DLLライブラリファイルをロードする
2) 関数アドレスの取得
3) 関数の実行
4) DLLライブラリを公開する
次に、これらの手順について詳しく説明します。
2. DLLライブラリファイルをロードします。
DLL ライブラリは、API 関数 LoadLibrary を呼び出すことでメモリにロードできます。ここでは、DLL がメモリ管理に与える影響については説明しません。 LoadLibrary のパラメータは、DLL ファイルのアドレス パスです。ロードが成功した場合、ターゲット ファイルが存在しない場合、またはその他の理由で DLL のロードが行われた場合は、CARDINAL 型変数が DLL ライブラリのハンドルとして返されます。ファイルが失敗すると、0 が返されます。
3. インターフェース関数のインスタンス化
インターフェース関数のポインタを取得する API 関数は GetProcAddress (ライブラリファイルハンドル、関数名) で、関数が見つかった場合はその関数のポインタが返され、失敗した場合は NIL が返されます。
上で定義した関数タイプを使用して関数ポインター変数を定義し、@ 演算子を使用して関数アドレスを取得すると、ポインター変数を使用して関数にアクセスできるようになります。メインのコードは次のとおりです。
…
変数
ShowDLLForm: TShowDLLForm; //DLL インターフェイス関数インスタンス;
FreeDLLForm: TFreeDLLForm;
始める
試す
始める
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) then
結果:=真
…
4. 具体的な実装方法
プラグインを構造化して管理し、将来のシステム拡張を容易にするために、データベースに記録されている利用可能な DLL 情報を結合し、データベース レコードをクエリすることで DLL プログラムに動的にアクセスできます。
1) システムモジュールテーブルの設計
MIS システムの場合、既存の DBS 条件を使用してシステム モジュール テーブルを確立し、DLL ファイルとシステム モジュールにマップされた関連情報を記録できます。
フィールド名役割タイプ
AutoID インデックスINT
modAlias モジュールエイリアス VARCHAR
modName モジュール名 VARCHAR
modWndClass フォームの一意識別子 VARCHAR
modFile DLL パス VARCHAR
modMemo メモのテキスト
・モジュールエイリアスは、プログラミング設計段階での命名規則を統一するために、特にチーム開発時にチームメンバーが参照できるようにするために使用されます。
・モジュール名はDLLウィンドウのタイトルとしてSHOWDLLFORM関数にACAPTIONパラメータとして渡されます。
・フォームの一意の識別子はDLLサブモジュール内のメインウィンドウのCLASSNAMEであり、実行時に制御するウィンドウを決定するために使用されます。
・DLLパスはDLLファイル名を保存しており、プログラム内で絶対パスに変換されます。
2) プラグイン情報のデータ構造
プラグイン関連情報を記録するデータ インターフェイスを定義すると、DLL プラグインを集中制御できます。次のコードを「インターフェース」セクションに追加します。
タイプ
//プラグイン情報クラスを定義
TMyPlugins = クラス
Caption:String; //DLL フォームのタイトル
DllFileName:String //DLL ファイルのパス;
WndClass:String; //フォーム識別
ユーザーID:文字列; //ユーザー名
ProcAddr:THandle; //LOADLIBRARY によってロードされたライブラリ ハンドル
FuncAddr:Pointer; //SHOWDLLFORM 関数ポインタ
FuncFreeAddr:Pointer; //FREEDLLFORM 関数ポインタ
終わり;
…
プラグインごとに TMyPlugins のインスタンスを作成します。これらのインスタンスの初期化メソッドについては後で説明します。
3) プラグインロード機能
この例では、HALL で子ウィンドウを開くトリガーとなるイベントで DLL ウィンドウがロードされて表示されます。ボタン イベントがトリガーされた後、まずプラグイン構造インスタンスに従って DLL がロードされているかどうかを判断し、ロードされている場合はウィンドウの表示または終了を制御し、データ テーブルにアクセスします。フィールドをプラグイン構造に割り当ててから、ポインターの取得を実行します。
部分的なコードは次のとおりです
…
//----------------------------------------
//名前: OpenPlugin
//Func: プラグイン情報管理処理: 初期化==》権限設定==》DLL読み込み画面
//パラ: APlugin-TMyPlugins; sAlias エイリアス;
//Rtrn: 該当なし
//認証: CST
//日付: 2005-6-2
//----------------------------------------
プロシージャ TFormHall.OpenPlugin(AFromActn: TAction ;APlugin:TMyPlugins; sAlias:string; sUserID:string);
var hWndPlugin:HWnd;
始める
//プラグイン ウィンドウがロードされているかどうかを判断します。 hWndPlugin:=FindWindow(PChar(APlugin.WndClass),nil);
if hWndPlugin <> 0 then //プラグイン ウィンドウがロードされました
始める
IsWindowVisible(hWndPlugin) でない場合は、
始める
AFromActn.Checked := True;
ShowWindow(hWndPlugin,SW_SHOWDEFAULT); //表示
終わり
それ以外
始める
AFromActn.checked := False;
ShowWindow(hWndPlugin,SW_HIDE);
終わり;
Exit; // プラグイン作成プロセスを終了します。
終わり;
//プラグインクラスのインスタンスを初期化する
InitializeMyPlugins(APlugin,sAlias) でない場合は、
始める
showmessage('プラグイン クラスの初期化中にエラーが発生しました。');
出口;
終わり;
// 現在の権限値を取得します
APlugin.ユーザーID := sユーザーID;
//DLLロードウィンドウ
LoadShowPluginForm(APlugin) ではない場合
始める
showmessage('センター プラグインのロード中にエラーが発生しました。');
出口;
終わり;
終わり;
//----------------------------------------
//名前: InitializeMyPlugins
//Func: MYPLUGIN インスタンスを初期化します (Caption | DllFileName | IsLoaded)
//パラ: APlugin-TMyPlugins
//Rtrn: 該当なし
//認証: CST
//日付: 2005-6-2
//----------------------------------------
関数 TFormHall.InitializeMyPlugins(APlugin:TMyPlugins; sAlias:String):Boolean;
変数
strSQL:文字列;
myDA:TMyDataAdapter;
始める
結果:=偽;
myDA:=TMyDataAdapter.Create;
strSQL:='SELECT * FROM SystemModuleList WHERE modAlias='+QuotedStr(sAlias);
試す
myDA.RetrieveData(strSQL);
を除外する
on E:例外実行
始める
結果:= false;
myDA.無料;
出口;
終わり;
終わり;
試す
始める
myDA.MyDataSet を使用して行う
始める
Not IsEmpty の場合
始める
APlugin.Caption:= FieldByName('modName').Value;
APlugin.DllFileName := FieldByName('modFile').Value;
APlugin.WndClass := FieldByName('modWndClass').Value;
結果:=真;
終わり;
近い;
end; //...で終了...
終了; // 試行の終了
を除外する
on E:例外実行
始める
結果:=偽;
myDA.無料;
出口;
終了; //例外の終了
end; // 試行の終了...を除く
myDA.無料;
終わり;
//----------------------------------------
//名前: LoadShowPluginForm
//Func: DLL プラグインをロードし、ウィンドウを表示します
//パラ: APlugin-TMyPlugins
//Rtrn: true-作成に成功しました
//認証: CST
//日付: 2005-6-2
//----------------------------------------
関数 TFormHall.LoadShowPluginForm (const APlugin:TMyPlugins):boolean;
変数
ShowDLLForm: TShowDLLForm; //DLL インターフェイス関数インスタンス;
FreeDLLForm: TFreeDLLForm;
sPath:string; // DLL ファイルのフルパス
始める
試す
始める
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) then
結果:=真
それ以外
結果:=偽;
終わり;
を除外する
on E:例外実行
始める
結果:=偽;
ShowMessage('プラグイン モジュールのロード中にエラーが発生しました。PLUGINS ディレクトリ内のファイルが完了しているかどうかを確認してください。');
終わり;
終わり;
終わり;
…
4) DLLウィンドウコントロール
3) のコードが示すように、DLL ウィンドウの開閉はプレゼンテーション層でのみ行われ、ウィンドウを閉じても実際には DLL ウィンドウが解放されず、ウィンドウに応じてフォーム ハンドルを取得する API 関数 FindWindow が呼び出されるだけです。識別子 (つまり、Form.name) を使用します。 SHOWWINDOW 関数の nCmdShow パラメータは、ウィンドウの表示/非表示を制御します。
実際、これは私のプログラムの実装の弱点であり、DLL ウィンドウで Self.close メソッドを使用すると、メモリ エラーが発生します。機能が限られているため、これを解決する方法はありません。最後の手段。したがって、各 DLL プログラムのメイン ウィンドウの閉じるボタンを非表示にする必要があります。 :-P
5) DLLライブラリの公開
プログラムが終了すると、プラグイン情報インスタンスに従って DLL ライブラリを 1 つずつ解放する必要があります。 DLLライブラリを解放する関数は以下のとおりです。
プロシージャ TFormHall.ClosePlugin(aPLG:TMyPlugins);
変数
FreeDLLForm:TFreeDLLForm;
始める
aPLG.ProcAddr = 0 の場合は終了します。
aPLG.FuncFreeAddr = nil の場合は終了します。
@FreeDLLForm:=aPLG.FuncFreeAddr;
FreeDLLForm(Application.Handle,'','') でない場合は、
showMessage('err');
終わり;
まとめ
このサンプル プログラムの実行効果は次のとおりです。
=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>
上記の方法の中には、能力が限られているために未解決の問題が多くあるため、合理的とは思えないいくつかの隠蔽方法を採用しましたが、少し試してみて、より良い解決策を設計できることを願っています。もっと学ぶための素晴らしい方法です。