Membuat Plugin Untuk Aplikasi Delphi

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.

<pre>const
  PLUGIN_NAME           = 'Show About';
function pluName:Pchar;
begin
  Result:=PLUGIN_NAME;
end;</pre>
</pre>

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:

<pre>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

<pre>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<BASE_TAG maka sender bukanlah submenu yang terhubung dengan plugin. Jadi berhenti di situ. Sebaliknya, prosedur mengambil elemen array dinamis pluNames pada index yang sesuai dengan tag, meloadnya dengan LoadLibrary, dan memanggil fungsi pluMain. Sederhana bukan?

TPluName adalah tipe fungsi yang harus sesuai dengan fungsi pluName pada plugin, fungsi tanpa parameter dan tipe kembalian Pchar. Mengapa Pchar? Penggunaan String akan mengharuskan Anda menyertakan file borlandmm.dll dan unit sharemem.pas. Jika Anda membuat library dengan library wizard, ini yang pertama Anda temukan.

TPluMain adalah padanan untuk fungsi pluMain dalam plugin.
Kita lihat deklarasi kedua prosedur, LoadPlugin dan ExecPlugin(sender:TObject):


procedure TForm1.LoadPlugin;

var

   sr:TSearchRec;

  I:integer;
<pre>  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;</pre>

Hasilnya:

Kini deklarasi prosedur ExecPlugin(Sender: TObject):

<pre>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.

6 thoughts on “Membuat Plugin Untuk Aplikasi Delphi

  1. Mas, salam kenal. saya ingin membuat program aplikasi Delphi yang dapat berjalan di Mobile Phone. APakah bisa Mas ?
    Terima kasih.

  2. Salam kenal juga🙂

    Delphi tidak mespesialisasikan diri pada mobile phone. Anda bisa coba MS Visual Studio 2005 atau CodeGear JBuilder. JBuilder mirip dengan Delphi dan dibuat oleh developer yang sama.
    Anda juga bisa menggunakan Midlet Pascal, yang membuat aplikasi berbasis Java untuk mobile phone, tapi interface pemrogramannya menggunakan bahasa Pascal.

    Atau, Anda bisa lihat opsi-opsi lainnya di sini. Ada VCL dan library juga component untuk koneksi Delphi dan mobile phone.

    Terimakasih atas komentarnya.🙂

  3. salam knal..
    mas ak kok masih g bisa ya buat plug-innya, aku minta source codenya donk, niq mau buat TA, aplikasi pembelajaran gtu. Please kirm ke email ni za..
    thanks…

  4. @sholikhin:
    Mohon maaf, saya tidak lagi punya source codenya, soalnya sudah lama sekali. Namun intinya sudah saya tuliskan pada tutorial di atas.
    Untuk menulis ulang kodenya, saya tidak yakin, persoalan waktu….

  5. Salam Kenal yah mas
    mau Tanya kalau kita mau Pakai DBGrid Sperti yang ada Di SQL manager 2005 itu gimana yah mas
    jadi DBGrid nya Udah mempuyai Filter sendiri,….
    Mohon Pencerahannya Terima Kasih

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s