Archive

Archive for October, 2007

Meng-Indonesia-kan MessageDlg

October 22, 2007 5 comments

Secara standar, bahasa yang digunakan oleh Delphi untuk menampilkan pesan pada kotak dialog pada MessageDlg adalah bahasa Inggris.

  MessageDlg('Professional SKU', mtInformation, [mbOK], 0);

Kode diatas akan menampilkan kotak dialog seperti gambar berikut:
MessagDlg Standard

Nah lalu bagaimanakah kode yang dibuat untuk menampilkan kotak dialog seperti gambar berikut?
MessagDlg ala Indonesia

Built From Scratch ?
Alternatif pertama adalah dengan membuat sendiri pustaka untuk menampilkan kotak dialog dengan bahasa Indonesia. Disini, diperlukan form dan konstanta tombol berbahasa Indonesia, membuat rutin yang berkaitan dengan User Interface, membuat event handler yang dibutuhkan dan sebagainya. Ah.. sudah terbayang betapa merepotkannya!

Modify The Source ?
Alternatif kedua, memodifikasi bahasa / teks yang digunakan oleh rutin MessageDlg yang disimpan pada unit Consts. Namun opsi ini menuntut Anda untuk mengkompilasi ulang source unit Delphi, sangat banyak dan beresiko terutama jika terdapat unit yang Anda tidak memiliki source-code nya.

Facade Pattern CreateMessageDialog
Alternatif yang lebih elegan adalah dengan membuat facade pattern dari fungsi CreateMessageDialog yang boleh jadi merupakan inti dari pembuatan kotak dialog. Kotak dialog dapat lebih variatif, seperti yang ditunjukkan oleh rekan Wisnu Widiarta pada tulisannya: classIndonesianDialog.

Nah.. menurut Anda saya menggunakan teknik yang mana? hmm tidak satupun, saya menggunakan teknik dan pendekatan yang berbeda, yaitu mengubah teks yang digunakan oleh MessageDlg di memori pada saat runtime.

Mengapa Modifikasi Memori ?
Tentu saja alasan saya modifikasi memori adalah saya tidak perlu susah payah membangun dari awal (built from scratch), memodifikasi source dan mengkompilasi ulang dan saya tidak perlu membuat facade pattern dari CreateMessageDlg. Saya cukup sekali saja mengganti teks yang digunakan oleh MessageDlg pada saat runtime, hasil modifikasi akan terus berlaku sampai aplikasi ditutup dan tentu saja tetap dengan memanggil fungsi MessageDlg. Dan hasilnya cukup efektif.

Langkah Pertama
Langkah pertama meng-Indonesia-kan MessageDlg adalah dengan menentukan dan mendeklarasikan pesan teks baru yang akan digunakan pada MessageDlg. Selengkapnya mengenai konstanta teks yang digunakan dapat disimak pada unit consts.pas. Sebagai petunjuk, konstanta yang terkait dengan MessageDlg adalah konstanta yang mengandung awalan (prefix) SMsgDlg. Berikut pendeklarasian konstanta yang baru:

const
  _NewSMsgDlgWarning = 'Peringatan';
  _NewSMsgDlgError = 'Kesalahan';
  _NewSMsgDlgInformation = 'Informasi';
  _NewSMsgDlgConfirm = 'Konfirmasi';
  _NewSMsgDlgYes = '&Ya';
  _NewSMsgDlgNo = '&Tidak';
  _NewSMsgDlgOK = 'OK';
  _NewSMsgDlgCancel = 'Batal';
  _NewSMsgDlgHelp = '&Panduan';
  _NewSMsgDlgHelpNone = 'Panduan tidak tersedia';
  _NewSMsgDlgHelpHelp = 'Panduan';
  _NewSMsgDlgAbort = '&Batal';
  _NewSMsgDlgRetry = '&Ulang';
  _NewSMsgDlgIgnore = 'A&cuh';
  _NewSMsgDlgAll = '&Semua';
  _NewSMsgDlgNoToAll = 'T&idak untuk Semua';
  _NewSMsgDlgYesToAll = 'Ya untuk S&emua';

Yang Kedua
Nah, disinilah bagian yang paling menarik, yaitu pembuatan kode untuk memanipulasi isi memori, terutama yang berkaitan dengan teks pada MessageDlg.

 1| procedure ReplaceResourceString(RStringRec: PResStringRec; AString: PChar);
 2| var
 3|   OldProtect: Cardinal;
 4| begin
 5|   if RStringRec = nil then Exit;
 6|   if VirtualProtect(RStringRec, SizeOf(RStringRec^), PAGE_EXECUTE_READWRITE, OldProtect) then
 7|   begin
 8|     RStringRec^.Identifier := Integer(AString);
 9|     VirtualProtect(RStringRec, SizeOf(RStringRec^), OldProtect, @OldProtect);
10|   end;
11| end;

Prosedur ReplaceResourceString memiliki 2 parameter. Parameter 1 bertipe PResStringRec, yaitu pointer untuk resource-string teks standar yang digunakan oleh MessageDlg. Sedangkan parameter 2 bertipe PChar, berisi teks yang akan digunakan untuk memodifikasi MessageDlg.

Pada baris 6 dan 9 terdapat rutin WindowsAPI VirtualProtect. API ini digunakan untuk mengubah proteksi akses suatu blok memori, apakah hanya baca saja, tulis saja, baca-tulis dan sebagainya. Berikut kutipan dari dokumentasi VirtualProtect.

The VirtualProtect function changes the access protection on a region of committed pages in the virtual address space of the calling process.

BOOL VirtualProtect(
LPVOID lpAddress, // address of region of committed pages
DWORD dwSize, // size of the region
DWORD flNewProtect, // desired access protection
PDWORD lpflOldProtect // address of variable to get old protection
);

Return Values

If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.

Penggunaan VirtualProtect pada baris 6 bertujuan untuk mengubah proteksi akses blok memori menjadi baca-tulis pada alamat yang ditunjuk, dalam hal ini alamat resource-string tertentu. Hal ini dilakukan untuk memastikan bahwa blok memori tersebut dapat dimodifikasi. Baris 8 digunakan untuk mengubah teks pesan pada resource-string yang ditunjuk. Sedangkan pada baris 9 digunakan untuk mengembalikan proteksi akses blok memori sebelum modifikasi.

Terapkan Modifikasi
Tentu saja prosedur ReplaceResourceString tidak hanya sebatas dideklarasikan, namun harus digunakan. Agar efek modifikasi teks dapat langsung diterapkan, maka pemanggilan prosedur dilakukan pada bagian initialization unit.

initialization
  ReplaceResourceString(@SMsgDlgWarning, _NewSMsgDlgWarning);
  ReplaceResourceString(@SMsgDlgError, _NewSMsgDlgError);
  ReplaceResourceString(@SMsgDlgInformation, _NewSMsgDlgInformation);
  ReplaceResourceString(@SMsgDlgConfirm, _NewSMsgDlgConfirm);
  ReplaceResourceString(@SMsgDlgYes, _NewSMsgDlgYes);
  ReplaceResourceString(@SMsgDlgNo, _NewSMsgDlgNo);
  ReplaceResourceString(@SMsgDlgOK, _NewSMsgDlgOK);
  ReplaceResourceString(@SMsgDlgCancel, _NewSMsgDlgCancel);
  ReplaceResourceString(@SMsgDlgHelp, _NewSMsgDlgHelp);
  ReplaceResourceString(@SMsgDlgHelpNone, _NewSMsgDlgHelpNone);
  ReplaceResourceString(@SMsgDlgHelpHelp, _NewSMsgDlgHelpHelp);
  ReplaceResourceString(@SMsgDlgAbort, _NewSMsgDlgAbort);
  ReplaceResourceString(@SMsgDlgRetry, _NewSMsgDlgRetry);
  ReplaceResourceString(@SMsgDlgIgnore, _NewSMsgDlgIgnore);
  ReplaceResourceString(@SMsgDlgAll, _NewSMsgDlgAll);
  ReplaceResourceString(@SMsgDlgNoToAll, _NewSMsgDlgNoToAll);
  ReplaceResourceString(@SMsgDlgYesToAll, _NewSMsgDlgYesToAll);

end.

Sediakan Demo
Selanjutnya, persiapkan demo MessageDlg generator untuk menguji apakah modifikasi yang dilakukan berjalan dengan baik.

procedure TfrmDialogIndonesia.btnTampilClick(Sender: TObject);
var
  JenisDialog   : TMsgDlgType;
  PilihanTombol : set of TMsgDlgBtn;
  I             : Integer;
begin
  JenisDialog := TMsgDlgType(rgJenisDialog.ItemIndex);
  PilihanTombol := [];
  for I := 0 to chklbPilihanTombol.Count - 1 do
    if chklbPilihanTombol.Checked[I] then Include(PilihanTombol, TMsgDlgBtn(I));
  MessageDlg(mmoPesan.Text, JenisDialog, PilihanTombol, 0);
end;

Full .pas Code
Berikut kode kustomisasi MessageDlg selengkapnya:

{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.

The Original Code is: DialogIndonesia.pas, released on 2007-10-18

The Initial Developer of the Original Code is Bayu Prasetio
Portions created by Bayu Prasetio are Copyright (C) 2007 Bayu Prasetio.
All Rights Reserved.
-----------------------------------------------------------------------------}

unit DialogIndonesia;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ComCtrls, CheckLst;

type
  TfrmDialogIndonesia = class(TForm)
    StatusBar1: TStatusBar;
    rgJenisDialog: TRadioGroup;
    gbPilihanTombol: TGroupBox;
    chklbPilihanTombol: TCheckListBox;
    GroupBox2: TGroupBox;
    mmoPesan: TMemo;
    bvTombol: TBevel;
    btnTampil: TButton;
    btnKeluar: TButton;
    procedure btnTampilClick(Sender: TObject);
    procedure btnKeluarClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmDialogIndonesia: TfrmDialogIndonesia;

implementation

{$R *.dfm}

uses
  Consts;

procedure TfrmDialogIndonesia.btnKeluarClick(Sender: TObject);
begin
  Close;
end;

procedure TfrmDialogIndonesia.btnTampilClick(Sender: TObject);
var
  JenisDialog   : TMsgDlgType;
  PilihanTombol : set of TMsgDlgBtn;
  I             : Integer;
begin
  JenisDialog := TMsgDlgType(rgJenisDialog.ItemIndex);
  PilihanTombol := [];
  for I := 0 to chklbPilihanTombol.Count - 1 do
    if chklbPilihanTombol.Checked[I] then Include(PilihanTombol, TMsgDlgBtn(I));
  MessageDlg(mmoPesan.Text, JenisDialog, PilihanTombol, 0);
end;

//------------------------------------------------------------------------------

const
  _NewSMsgDlgWarning = 'Peringatan';
  _NewSMsgDlgError = 'Kesalahan';
  _NewSMsgDlgInformation = 'Informasi';
  _NewSMsgDlgConfirm = 'Konfirmasi';
  _NewSMsgDlgYes = '&Ya';
  _NewSMsgDlgNo = '&Tidak';
  _NewSMsgDlgOK = 'OK';
  _NewSMsgDlgCancel = 'Batal';
  _NewSMsgDlgHelp = '&Panduan';
  _NewSMsgDlgHelpNone = 'Panduan tidak tersedia';
  _NewSMsgDlgHelpHelp = 'Panduan';
  _NewSMsgDlgAbort = '&Batal';
  _NewSMsgDlgRetry = '&Ulang';
  _NewSMsgDlgIgnore = 'A&cuh';
  _NewSMsgDlgAll = '&Semua';
  _NewSMsgDlgNoToAll = 'T&idak untuk Semua';
  _NewSMsgDlgYesToAll = 'Ya untuk S&emua';


{-- taken from bpCodeReplacement.pas by Bayu Prasetio}
procedure ReplaceResourceString(RStringRec: PResStringRec; AString: PChar);
var
  OldProtect: Cardinal;
begin
  if RStringRec = nil then Exit;
  if VirtualProtect(RStringRec, SizeOf(RStringRec^), PAGE_EXECUTE_READWRITE, OldProtect) then
  begin
    RStringRec^.Identifier := Integer(AString);
    VirtualProtect(RStringRec, SizeOf(RStringRec^), OldProtect, @OldProtect);
  end;
end;

initialization
  ReplaceResourceString(@SMsgDlgWarning, _NewSMsgDlgWarning);
  ReplaceResourceString(@SMsgDlgError, _NewSMsgDlgError);
  ReplaceResourceString(@SMsgDlgInformation, _NewSMsgDlgInformation);
  ReplaceResourceString(@SMsgDlgConfirm, _NewSMsgDlgConfirm);
  ReplaceResourceString(@SMsgDlgYes, _NewSMsgDlgYes);
  ReplaceResourceString(@SMsgDlgNo, _NewSMsgDlgNo);
  ReplaceResourceString(@SMsgDlgOK, _NewSMsgDlgOK);
  ReplaceResourceString(@SMsgDlgCancel, _NewSMsgDlgCancel);
  ReplaceResourceString(@SMsgDlgHelp, _NewSMsgDlgHelp);
  ReplaceResourceString(@SMsgDlgHelpNone, _NewSMsgDlgHelpNone);
  ReplaceResourceString(@SMsgDlgHelpHelp, _NewSMsgDlgHelpHelp);
  ReplaceResourceString(@SMsgDlgAbort, _NewSMsgDlgAbort);
  ReplaceResourceString(@SMsgDlgRetry, _NewSMsgDlgRetry);
  ReplaceResourceString(@SMsgDlgIgnore, _NewSMsgDlgIgnore);
  ReplaceResourceString(@SMsgDlgAll, _NewSMsgDlgAll);
  ReplaceResourceString(@SMsgDlgNoToAll, _NewSMsgDlgNoToAll);
  ReplaceResourceString(@SMsgDlgYesToAll, _NewSMsgDlgYesToAll);

end.

Dan berikut salah satu aksi dari kode di atas.
MessagDlg ala Indonesia

Source Code: MessageDlgIndonesia.7z

Semoga bermanfaat.

Categories: Delphi, Did You Know