Memonitor Event Web Browser
March 21, 2006 at 3:04 pm | In Code Samples | Leave a Commentby: Zamrony P Juhara
Di artikel kali ini saya akan membahas mengenai cara mengetahui event-event yang terjadi pada web browser. Artikel ini saya tulis untuk menjawab pertanyaan Johan Max di milis Delphindo mengenai bagaimana mendeteksi link pada suatu dokumen HTML diklik dan bagaimana mendeteksi URL yang dituju. Tentunya dengan menggunakan tool favorite kita semua..Delphi.
Tutorial ini hanya mengasumsikan browser web adalah IE, untuk browser lain, cara ini mungkin tidak bekerja.
Source code bisa didownload di sini
Unit yang diperlukan
Import dulu ActiveX MSHTML (Ac. Jika pada sistem anda sudah terdapat file MSHTML.pas atau MSHTML_TLB.pas (cari di direktori Imports) berarti anda sudah siap mengikuti artikel ini.
Langkah-Langkah Memonitor Event Web Browser
- Buat Implementasi Event Sink
- Buka Dokumen HTML hingga Lengkap
- Ambil Elemen-elemen HTML yang ingin dimonitor
- Koneksikan Event Sink ke Elemen-elemen tersebut
Sampai pada step terakhir maka, aplikasi kita sudah siap memproses event web browser. Kalo kita sudah tidak membutuhkan memonitor event, langkah terakhir adalah
- Disconnect event Sink
Ok, kita bahas satu-satu.
Membuat Implementasi Event Sink
Event Sink harus diturunkan paling tidak dari interface IDispatch, karena fungsi Invoke() akan dipanggil tiap kali event terjadi.
Berikut ini adalah deklarasi IDispatch (unit system.pas)
IDispatch = interface(IUnknown)
['{00020400-0000-0000-C000-000000000046}']
function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
function GetIDsOfNames(const IID: TGUID; Names: Pointer;
NameCount, LocaleID: Integer;
DispIDs: Pointer): HResult; stdcall;
function Invoke(DispID: Integer;
const IID: TGUID;
LocaleID: Integer;
Flags: Word;
var Params; VarResult,
ExcepInfo, ArgErr: Pointer): HResult; stdcall;
end;
Keempat fungsi diatas harus kita buat implementasinya. IDispatch adalah turunan IUnknown, sehingga selain keempat fungsi diatas, kita perlu juga mengimplementasikan metode IUnknown. Agar tidak perlu mengimplementasi IUnknown kita bisa menurunkan kelas implementasi kita dari TInterfacedObject.
Perhatikan bahwa, selain Invoke(), fungsi lainnya kurang berguna untuk tujuan kita, sehingga bisa kita set tidak terimplementasi, berikut ini contoh kodenya.
unit ueventsink;
interface
uses classes,windows,sysutils,mshtml;
type
IEventSink=interface(IDispatch)
['{AC8E45D3-DABB-4DC0-AD94-D53FA67DD78A}']
procedure SetOnClick(const Value: THTMLElementOnClick);
function GetOnClick: THTMLElementOnClick;
procedure SetWebBrowser(const Value: IWebBrowser2);
function GetWebBrowser: IWebBrowser2;
property OnClick:THTMLElementOnClick read GetOnClick
write SetOnClick;
property WebBrowser:IWebBrowser2 read GetWebBrowser
write SetWebBrowser;
end;
TEventSink=class(TInterfacedObject,IDispatch,IEventSink)
public
procedure SetOnClick(const Value: THTMLElementOnClick);
function GetOnClick: THTMLElementOnClick;
procedure SetWebBrowser(const Value: IWebBrowser2);
function GetWebBrowser: IWebBrowser2;
function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
function GetIDsOfNames(const IID: TGUID;
Names: Pointer;
NameCount, LocaleID: Integer;
DispIDs: Pointer): HResult; stdcall;
function Invoke(DispID: Integer;
const IID: TGUID; LocaleID: Integer;
Flags: Word;
var Params; VarResult,
ExcepInfo, ArgErr: Pointer): HResult; stdcall;
end;
implementation
{ TEventSink }
function TEventSink.GetIDsOfNames(const IID: TGUID; Names: Pointer;
NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
begin
result:=E_NOTIMPL;
end;
function TEventSink.GetTypeInfo(Index,
LocaleID: Integer;
out TypeInfo): HResult;
begin
result:=E_NOTIMPL;
end;
function TEventSink.GetTypeInfoCount(out Count: Integer): HResult;
begin
result:=E_NOTIMPL;
end;
function TEventSink.Invoke(DispID: Integer; const IID: TGUID;
LocaleID: Integer;
Flags: Word; var Params;
VarResult, ExcepInfo,
ArgErr: Pointer): HResult;
begin
end;
end.
Kita hanya akan fokus ke membuat implementasi Invoke(). Ok sekarang kita tambahkan sebuah property event pada TEventSink
type
THTMLElementOnClick=procedure (Sender:TObject;
Element:IHTMLElement;
var cancel:boolean) of
object;
Tambahkan dibagian published code berikut
property OnClick:THTMLElementOnClick;
Tekan Shift+Ctrl+C untuk melengkapi kelas class completion
Berikut ini adalah implementasi Invoke().
function TEventSink.Invoke(DispID: Integer; const IID: TGUID;
LocaleID: Integer; Flags: Word; var Params;
VarResult, ExcepInfo,
ArgErr: Pointer): HResult;
var cancel:boolean;
begin
case dispID of
-600:begin
if Assigned(FOnClick) then
begin
cancel:=false;
FOnClick(self,nil,cancel);
end;
end;
end;
result:=S_OK;
end;
Implementasi Kode Koneksi.
Untuk mengkoneksi ke elemen HTML kita perlu mendapatkan connection Point ke elemen tersebut caranya sebagai berikut:
function ConnectEvSink(elem:IHTMLElement;eventGUID:TGUID;
eventSink:IUnknown):integer;
var cpc:IConnectionPointContainer;
cp:IConnectionPoint;
begin
result:=0;
elem.QueryInterface(IConnectionPointContainer,cpc);
if cpcnil then
begin
cpc.FindConnectionPoint(eventGUID,cp);
if cpnil then
cp.Advise(eventSink,result);
end;
end;
elem adalah elemen HTML yang akan dimonitor event-nya. EventGUID adalah GUID event yang akan dimonitor. Contoh untuk memonitor Elemen anchor maka eventGUID adalah DIID_HTMLAnchorEvents. EventSink adalah implementasi event sink yang kita buat.
Fungsi diatas akan mengembalikan ID yang nantinya dapat kita pergunakan untuk melakukan disconnect event sink.
Disconnect Event Sink
Caranya hampir sama dengan connect tetapi, yang kita gunakan adalah metode UnAdvice().
procedure DisconnectEvSink(elem:IHTMLElement;eventGUID:TGUID;
const ID:integer);
var cpc:IConnectionPointContainer;
cp:IConnectionPoint;
begin
elem.QueryInterface(IConnectionPointContainer,cpc);
if cpcnil then
begin
cpc.FindConnectionPoint(eventGUID,cp);
if cpnil then
cp.UnAdvise(id);
end;
end;
Implementasi Aplikasi Utama
Kode berikut adalah kode yang akan selalu dipanggil ketika sebuah link diklik
procedure TForm1.AnchorClick(Sender:TObject;
Element:IHTMLElement;
var cancel:boolean);
begin
if element<>nil then
begin
ShowMessage('Link diklik.');
end;
Konstruktor melakukan inisialisasi event sink.
constructor TForm1.Create(AOWner: TComponent);
begin
inherited;
eventSink:=TEventSink.Create;
eventSink.WebBrowser:=WebBrowser1;
eventSink.OnClick:=AnchorClick;
end;
Perhatikan, karena elemen-elemen baru dapat diakses setelah semua dokumen telah selesai didownload, maka kita harus memproses elemen-elemen di DocumentComplete browser.
procedure TForm1.WebBrowser1DocumentComplete(Sender: TObject;
const pDisp: IDispatch; var URL: OleVariant);
var doc:IHTMLDocument;
doc2:IHTMLDocument2;
i:integer;
anchors:IHTMLElementCollection;
anchor:IHTMLElement;
begin
if WebBrowser1.Document<>nil then
begin
WebBrowser1.Document.QueryInterface(IHTMLDocument,doc);
if doc<>nil then
begin
doc.QueryInterface(IHTMLDocument2,doc2);
if doc2<>nil then
begin
anchors:=GetAllAnchors(doc2);
//proses tiap link yang ada
for i:=0 to anchors.length-1 do
begin
anchor:=anchors.item(i,0) as IHTMLElement;
ConnectEvSink(anchor,DIID_HTMLAnchorEvents,eventSink);
end;
end;
end;
end;
end;
Fungsi GetAllAnchors(), dideklarasikan di unit uhtml_utility.pas, mengambil semua link yang ada pada dokumen HTML. untuk Tiap-tiap elemen, kita daftarkan evnt sink kita sehingga kita akan diberitahu melalui Invoke() ketika terjadi event pada elemen-elemen ini.
Mendapatkan Elemen Yang Menghasilkan Event
Kode Invoke() diatas, tidak mampu memberitahukan elemen apa yang menghasilkan sebuah event. Lalu bagaimana?
Interface IHTMLWindow2 memiliki property event yang mewakili event yang sedang digenerate saat ini. Dari property ini lah kita dapat mengetahui elemen apa yang menghasilkan event, melalui property src_Element milik event.
Selain itu event memiliki sebuah property bernama returnValue yang dapat kita pergunakan untuk menjalankan/membatalkan aksi default sebuah elemen.
Lalu bagaimana mendapatkan interface IHTMLWindow2? Jawabannya adalah melalui IHTMLDocument2.parentWindow.
Berikut ini adalah Invoke() yang telah diperbarui:
function TEventSink.Invoke(DispID: Integer; const IID: TGUID;
LocaleID: Integer; Flags: Word; var Params;
VarResult, ExcepInfo,
ArgErr: Pointer): HResult;
var cancel:boolean;
begin
case dispID of
-600:begin
if Assigned(FOnClick) then
begin
cancel:=false;
elemen:=nil;
if FWebBrowser<>nil and
FWebBrowser.Document<>nil then
begin
doc:=FWebBrowser.Document as IHTMLDocument2;
window:=doc.parentWindow;
if (window<>nil) and
(window.event<>nil) then
elemen:=window.event.src_Element;
end;
FOnClick(self,nil,cancel);
if (window<>nil) and
(window.event<>nil) then
window.event.returnValue:=cancel;
end;
end;
end;
result:=S_OK;
end;
Kita ubah AnchorClick sehingga tiap kali link diklik, URL-nya ditampilkan juga.
procedure TForm1.AnchorClick(Sender:TObject;
Element:IHTMLElement;
var cancel:boolean);
var anchor:IHTMLAnchorElement;
url:wideString;
begin
if element<>nil then
begin
anchor:=element as IHTMLAnchorElement;
url:='URL='+anchor.href;
end else
url:='';
ShowMessage('Link diklik.'+url);
//batalkan aksi defaultnya
cancel:=true;
end;
Ok itu saja. Source code bisa didownload di sini
No Comments Yet »
RSS feed for comments on this post. TrackBack URI
Leave a comment
Blog at WordPress.com. | Theme: Pool by Borja Fernandez.
Entries and comments feeds.