Arsip: Membuat Plugin Untuk Aplikasi Delphi


by reev in TipDanTrik more 12 years ago 2631
Kita tentunya pernah melihat aplikasi yang menggunakan plugin. Winamp misalnya. Kehebatan aplikasi seperti ini adalah, aplikasi dapat lebih modular, ukuran aplikasi lebih kecil dan portabel, serta memungkinkan pihak ketiga (third party) untuk menambahkan fitur-fitur baru pada aplikasi yang kita buat.
Bagian yang akan kita lihat dari aplikasi dengan plugin adalah, aplikasi memiliki sebuah menu dengan caption, taruhlah, "Plugins" atau "Tools", yang di dalamnya terdapat submenu-submenu sesuai dengan plugin yang ditambahkan pada aplikasi ini. Tiap submenu memiliki caption yang ditentukan oleh vendor plugin, dan apabila diklik, fungsi yang dijalankan pun ditentukan oleh vendor plugin. Bagaimana ini bisa terjadi? Harus ada kesepakatan antara aplikasi dan plugin-plugin untuk memungkinkan komunikasi ini. Pertama, di mana plugin diletakkan. Plugin dapat diletakkan di mana saja, termasuk di komputer lain (network), asalkan komputer yang menampung plugin memiliki sistem operasi sama, dalam hal ini Windows. Setiap dijalankan, aplikasi akan melakukan scanning terhadap semua dirtektori untuk mencari plugin. Benar! Ini cara barbar. Bagaimana jika aplikasi menemukan plugin dalam CD-ROM, dan saat dipakai CD tersebuat di-eject? Atau plugin yang disengaja untuk tidak dipakai oleh user? Dan berapa lama waktu yang digunakan? Ada alternatif lain: semua dapat diletakkan di tempat yang semau mereka, tetapi harus mencatat/meregistrasikan namanya sebagai plugin aplikasi kita pada file tertentu, misalnya registry, dan aplikasi akan mengambil daftar plugin dari catatan ini. Cara ini efektif, seperti yang digunakan Windows terhadap file OCX. Namun sepertinya cara ini kurang terorganisir. Anda memahami yang saya maksud. Jadi saya mengusulkan cara ketiga: semua plugin diletakkan pada folder yang ditentukan oleh aplikasi, baik yang relatif terhadap aplikasi misalnya %APPDIR%\Plugins atau yang hard-coded path seperti C:\Program Files\Common Files\App Shared\Plugins.Cara ketiga, seperti yang Anda setujui, adalah cara terbaik, terutama yang menggunaan path relatif terhadap aplikasi. Aplikasi dan pluginnya akan lebih portabel (tinggal copy-paste, kan) dan mudah diinstal. Cara ini yang akan kita pakai dalam pembahasan kita, cuma plugin diletakkan dalam satu folder dengan aplikasi. Kedua, plugin dan aplikasi memiliki satu tipe fungsi yang sama untuk saling bertukar data. Seperti pada Windows, semua file OCX harus memiliki fungsi DLLRegisterServer. Sekarang mari kita buat sebuah fungsi sesuai kesepakatan di atas.
const
PLUGIN_NAME           = 'Show About';
function pluName:Pchar;
begin
Result:=PLUGIN_NAME;
end;
Sebagai vendor, tentunya pihak ketiga ingin memberi nama pluginnya. Fungsi pluName() akan mengembalikan nama plugin ini. Pada saat aplikasi melakukan scan terhadap plugin-plugin, aplikasi akan mencoba melihat keberadaan fungsi ini. Jika ada, plugin valid. Dan jika tidak, plugin tidak valid. Fungsi ini mengembalikan nama plugin yang selanjutnya dijadikan caption bagi submenu-submenu dalam aplikasi. Selanjutnya kita buat sebuah fungsi lagi, yang merupakan "entry point" bagi plugin. Kita tentukan saja fungsi pluMain():Integer; Keberhasilan fungsi pluMain dan proses di dalamnya dilihat dari nilai kembalian fungsi. Bentuk fungsi pluMain seperti ini:
function pluMain:Integer;
begin
Result:=Messagebox(0,
'This is a test plugin by Joko Rivai.'
,pchar(PLUGIN_NAME),MB_ICONINFORMATION);
end;
Selanjutnya kita mengekspor kedua fungsi. Listing lengkap untuk plugin pertama kita ini ada di bawah:
library showabout;
uses
Windows;
{$E plu}
{$R .res}
const
PLUGIN_NAME           = 'Show About';
function pluName:Pchar;
begin
Result:=PLUGIN_NAME;
end;
function pluMain:Integer;
begin
Result:=Messagebox(0,'This is a test plugin by Joko Rivai.',
pchar(PLUGIN_NAME),MB_ICONINFORMATION);
end;
exports
pluName, pluMain;
begin
end.
Sampai di sini konsep plugin kita sudah jadi. Sekarang kita sudah mempunyai dua fungsi pembentuk plugin ini, pluName() untuk mengembalikan nama plugin, dan pluMain untuk mengeksekusi plugin. Saya rasa sudah jelas. Ini cuma DLL sederhana dengan dua fungsi. OK, mengenai {$E plu} itu ya? Itu sengaja saya tambahkan agar pluginnya berekstensi ".plu", untuk mempermudah scanning plugin di antara DLL lainnya, jika ada. Buat jaga-jaga, gitu loh. Selanjutnya kita buat aplikasi yang memanggil plugin. Saya membuatnya seperti gambar:

Jika Anda ingin melihat struktur menu yang ada, boleh:

Sekarang kita tambahkan dua buah prosedur pada bagian private form
type
TForm1 = class(TForm)
MainMenu1: TMainMenu;
Plugins1: TMenuItem;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure ExecPlugin(Sender:TObject);
procedure LoadPlugin;
public
{ Public declarations }
end;
TPluName=function:pchar;
TPluMain=function:integer;
const
BASE_TAG        = 1000;
var
Form1: TForm1;
pluNames:array of String;
procedure LoadPlugin adalah prosedur untuk melakukan scanning terhadap folder aplikasi. Jika ada file berekstensi
.PLU, aplikasi akan mengecek ada kah fungsi pluName? Jika ya, submenu baru dibuat, fungsi pluName akan dipanggil dan hasilnya dijadikan caption submenu yang baru, dan nama pluginnya dicatat dalam array dinamis pluNames. Procedure ExecPlugin(sender:Object) dipakai sebagai event handler untuk event OnClick menu-menu yang dibuat. Prosedur ini memeriksa tag dari sender. Jika ternyata tag procedure TForm1.LoadPlugin; var sr:TSearchRec; I:integer; mi:TMenuItem; //untuk menambahkan submenu baru di bawah //menu Plugins fname:String; libH:HModule; //handle untuk LoadLibrary pluname:TPluName; //variabel fungsi TPluName, //untuk mengambil nama plugin begin //cari file .plu dalam folder aplikasi if FindFirst(extractFilePath(paramstr(0))+' .plu', faAnyFile,sr)<>0 then exit; i:=0; repeat fname:=ExtractFilePath(paramstr(0))+sr.Name; //ambil nama file libh:=LoadLibrary(pchar(fname)); //coba loadlibrary if libh<>-1 then begin setLength(pluNames, i+1); pluNames[i]:=sr.Name; //jika library valid, masukkan //nama file ke array @pluname:=GetProcAddress(libh,'pluName'); //cek fungsi pluName if @pluname<>nil then //jika OK, buat submenu baru begin mi:=TMenuItem.Create(self); mi.Caption:=pluname(); mi.Tag:=BASE_TAG+i; //mi.tag:=1000+i; mi.OnClick:=ExecPlugin; MainMenu1.Items[0].Add(mi); //tambahkan ke menu Plugins inc(i); end; end; until FindNext(sr)<>0; findClose(sr); end; Hasilnya:

Kini deklarasi prosedur ExecPlugin(Sender: TObject):
procedure TForm1.ExecPlugin(Sender: TObject);
var
i:integer;
hlib:HMODULE;
pluMain:TPLUMain;		//variabel padanan fungsi pluMain();
fname:String;
begin
if not (sender is TMEnuItem) then exit;
//jika bukan submenu, batalkan
i:=TMenuItem(sender).Tag-BASE_TAG;
if i<0 then exit;		// --> not a plugin menu.
fname:=ExtractFilePath(paramstr(0))+pluNames[i];
//ambil nama library dari array
hlib:=LoadLibrary(pchar(fname));
if hlib=-1 then
begin
ShowMessage('Couldn''t load plugin.');
exit;
end;
@pluMain:=GetProcAddress(hlib, 'pluMain');
//ambil proses di memori
if @pluMain=nil then
begin
ShowMessage('Couldn''t load plugin entry point.');
exit;
end;
pluMain();			//eksekusi pluMain()
@pluMain:=nil;		//bebaskan memori
FreeLibrary(hlib);		//bebaskan library
end;
Sekarang kita jalankan aplikasi dan coba mengklik menu yang ada:

OK. Ini sebenarnya cuma ide dasarnya saja. Anda mungkin memiliki metode yang lebih baik. FYI, pembuatan plugin dengan konsep OOP telah ditulis oleh Arymurthy, dapat dilihat di archive milis Delphindo ( http://groups.yahoo.com/group/delphindo/files ) folder Technical Articles.
Local Business Directory, Search Engine Submission & SEO Tools FreeWebSubmission.com SonicRun.com