Delphi & JavaScript Kardeşliği

// 1 Haziran 2009 // Delphi, Internet, Programlama

Son zamanlarda bir raporlama aracının Delphi’ye adaptasyonu ile uğraşıyorum. Bu aracın adı Fusion Charts. Son derece şık ve yetenekli bir araç. Arka planında Flash animasyonları kullanan, kendisine veriyi XML üzerinden alabilen , HTML & JavaScript kullanarak flash nesnelerinin erişimine müsaade eden bir raporlama aracı. Bu hali ile Fusion Charts aracı; Delphi üzerinde TWebBrowser nesnesi vasıtası ile rahatlıkla kullanılabiliyor. Yeterki uygulamanızın çalıştığı klasörde ihtiyaç duyduğu dosyalar olsun. Benim ileride yazacağım Fusion Charts & Delphi entegrasyonunda XML, HTML ve JavaScript dosyalarına gerek olmayacak. Ama bu raporlama aracı üzerinde çalışırken, JavaScript’e amiyane tabir ile biraz bulaşmak durumunda kaldım.

Meraklı bir mizacım olduğu için, Delphi üzerinde TWebBrowser nesnesi vasıtası ile bir JavaScript sınıfını yada metodunu kullanıp kullanamayacağımı araştırdım. Ve gördüm ki, bizim sevgili Delphi’mizin yapamayacağı şey yok :) Delphi üzerinden TWebBrowser nesnesi vasıtası ile(daha doğru bir ifade ile COM vasıtası ile) JavaScript’e ulaşmak ve JavaScript kodları çalıştırmak mümkün. Öncelikle söylemek isterim ki; JavaScript konusunda pek bilgili sayılmam. Dolayısı ile yazacağım örnekler sizlere basit gelirse yahut bazı hatalarım olursa şimdiden affınıza sığınırım.

Herşeyden evvel, TWebBrowser nesnesi kendisine yükleyeceği bir HTML dosya alır. Bu dosya disk üzerinde yada internet üzerinde bir yerdedir ve TWebBrowser’ın Navigate metoduna parametre olarak aktarılır. Navigate metodu, kendisine verilen HTML dosyayı yada linkin gösterdiği verileri parse eder ve TWebBrowser üzerinde gösterir. Buraya kadar herşey normal. Lâkin ben yapım gereği disk üzerinde bulunan dosyalar ile çalışmayı pek sevmem. Bu dosyaların bozulabilme ihtimalleri yada silinebilme ihtimallerinin olması programınızın doğru çalışmasını engelleyeceği için dosya erişimli yöntemlerden mümkün mertebe uzak kalmanızı tavsiye ederim. Bu bağlamda yaptığım araştırma sonucu, pek çok kaynakta HTML kaynak kodlarının herhangi bir dosyaya ihtiyaç duymadan TWebBrowser nesnesinde gösterilebildiğini öğrendim. Bulduğum metod aşağıdaki gibidir:

procedure LoadHTML(WebBrowser: TWebBrowser; HTMLCode: String);
var
  sl: TStringList;
  ms: TMemoryStream;
begin
  WebBrowser.Navigate('about:blank') ;

  if Assigned(WebBrowser.Document) then
  begin
     sl := TStringList.Create;
     try
        ms := TMemoryStream.Create;
        try
           sl.Text := HTMLCode;
           sl.SaveToStream(ms);
           ms.Seek(0, 0);
           (WebBrowser.Document as IPersistStreamInit).Load(TStreamAdapter.Create(ms));
        finally
           ms.Free;
        end;
     finally
        sl.Free;
     end;
  end;
end;


MSDN yardım dökümanlarından yada Microsoft’un online MSDN kaynaklarından TWebBrowser sınıfının özelliklerini inceleyecek olursanız, pek çok interface’i implemente ettiğini gözlemlersiniz. Bu interface’lerden bir tanesi de IPersistStreamInit‘dir. IPersistStreamInit interface’i TWebBrowser tarafından implemente edildiğine göre, bu interface’in içinde mevcut bulunan IsDirty,Load,Save,GetSizeMax ve InitNew metodlarının da implemente edilmiş olması gerekir. Bizim ihtiyacımız olan Load metodu kendisine IStream türünde bir parametre bekler. IStream adından da anlaşılabileceği gibi bir interface’dir. Delphi’nin Classes.pas dosyasında tanımlanmış olan TStreamAdapter isimli sınıfı da IStream interface’ini implemente etmiştir ve asıl amacı Delphi TStream sınıflarının IStream bekleyen yapılarla haberleşmesini sağlamaktır. Dolayısı ile IStream parametresi bekleyen Load metoduna TStreamAdapter sınıfından bir nesne geçilebilir.

Yukarıdaki kodumuz, programımız içerisinde html kodları yazabilmemizi ve bu kodları bir TWebBrowser nesnesi üzerinde gözlemleyebilmemizi sağlamaktadır. Şimdi ufak bir test yapalım:

// wBrowser formumuz üzerinde bulunan TWebBrowser nesnesinin adıdır.

const
  Enter                  = Char(13) + Char(10);
  HtmlStartSection  = '<html>';
  HtmlEndSection    = '</html>';
  HeadStartSection  = '<head>';
  HeadEndSection    = '</head>';
  BodyStartSection  = '<body>';
  BodyEndSection    = '</body>';
  ScriptStartSection= '<script type="text/javascript">';
  ScriptEndSection  = '</script>';

  HTML = HtmlStartSection              + Enter +
               HeadStartSection           + Enter +
                 '%HEADSECTION%'       + Enter +
                   '%SCRIPTSECTION%'  + Enter +
               HeadEndSection             + Enter +
               BodyStartSection           + Enter +
                 '%BODYSECTION%'       + Enter +
               BodyEndSection             + Enter +
             HtmlEndSection;

const
  JScriptShowMessage =
                     'function ShowMessage(Message)' + Enter +
                     '{' + Enter +
                        'alert(Message);' + Enter +
                     '}';

procedure TfrmJavaScript.Button1Click(Sender: TObject);
var
  sHTML,
  sScript : String;
begin
  sHTML := HTML;
  sScript := ScriptStartSection     + Enter +
                  JscriptShowMessage + Enter +
                ScriptEndSection;

  sHTML := StringReplace(sHTML, '%HEADSECTION%'   ,''          , [rfReplaceAll]);
  sHTML := StringReplace(sHTML, '%SCRIPTSECTION%', sScript  , [rfReplaceAll]);
  sHTML := StringReplace(sHTML, '%BODYSECTION%'  , 'Delphi ve JavaScript' , [rfReplaceAll]);

  LoadHTML(wBrowser, sHTML);

  while wBrowser.ReadyState < READYSTATE_INTERACTIVE do Application.ProcessMessages;
end;

Örneğimiz son derece basit bir html sayfasının yapısını oluşturacak sabitlerden ve yine ShowMessage isimli bir JavaScript fonksiyonundan ibaret. Şu aşamada bu kodumuz TWebBrowser sınıfından türemiş wBrowser isimli nesnemize belirtilen HTML kodlarını yüklemekten başka bir işe yaramıyor. Yukarıdaki kodda ilginç olabilecek tek husus TWebBrowser sınıfının ReadyState özelliğinin READYSTATE_INTERACTIVE olmasının kontrolü olabilir. Bu kontrol TWebBrowser nesnesine html kodlarının yüklenmesinin beklenmesi içindir. Peki makalemizin başlığına konu olan JavaScript metodlarının çağrılmasını nasıl yapacağız diye düşünüyor olabilirsiniz. Sizleri daha fazla bekletmeden ShowMessage isimli JavaScript metodunu Delphi’den nasıl çağırdığımızı göstermeye çalışayım:

procedure TfrmJavaScript.Button2Click(Sender: TObject);
var
  Doc : IHTMLDocument2;
  Win : IHTMLWindow2;
begin
  Doc := wBrowser.Document as IHTMLDocument2;
  Win := Doc.parentWindow;
  Win.execScript('ShowMessage(' + #39+ TimeToStr(Time) + #39 + ');', 'JavaScript');
end;

Bir kaç paragraf önce TWebBrowser sınıfının pek çok interface’i implemente ettiğini söylemiştik. Bu interface’lerden bir tanesi de IHTMLDocument2‘dir. IHTMLDocument2 interface’inin parentWindow property’si IHTMLWindow2 türünden başka bir interface döndürür. IHTMLWindow2 interface’inin execScript metodu tam da aradığımız işi yapmaktadır. Son parametresinden analayacağınız üzere bu interface’in gücü sadece JavaScript kodları çalıştırmak ile sınırlı değildir. Ancak biz örneğimizde en yaygın kullanılan JavaScript olduğu için onu ele alacağız. Gördüğünüz gibi ShowMessage JavaScript metoduna, Delphi’nin TimeToStr metodundan dönen değeri parametre olarak aktarıyor ve metodu çalıştırıyoruz. ShowMessage JavaScript metodunun içinde alert komutunun olduğunu hatırlıyorsunuzdur. Bu da ekrana zaman bilgisini gösteren küçük bir dialog kutusu çıkartacaktır. Öyle zamanlar gelirki, JavaScript içinde bazı nesneleri oluşturur ve kullanırız. Bu nesneler bazen kendi yazdığımız JavaScript sınıfları olabileceği gibi, bazen de 3′üncü parti yazılımların ActiveX denetimleri olabilir. Fusion Charts buna bir örnek. Yada Flash nesneleri başka bir örnek..

Peki biz JavaScript içinde oluşturduğumuz bu nesnelere Delphi altından nasıl erişeceğiz ? Delphi içinde nesnelerin property’lerine erişebilsek, bazı metodlarını çalıştırabilsek ne güzel olurdu dediğinizi duyar gibiyim. Onun içinde aşağıdaki yöntemi kullanacağız:

const
  JScriptShowMessage =
                     'function ShowMessage(Message)' + Enter +
                     '{' + Enter +
                        'alert(Message);' + Enter +
                     '}';

  JScriptClass =  'function TPerson(Adi, Yasi)' + Enter +
                  '{' + Enter +
                      'var fAdi  = Adi;' + Enter +
                      'var fYasi = Yasi;' + Enter +
                      'this.SetAdi = SetAdi;' + Enter +
                      'this.SetYasi = SetYasi;' + Enter +
                      'this.ToString = ToString;' + Enter +

                      'function ToString()' + Enter +
                      '{' + Enter +
                      '  return fAdi + ' + #39 + ' / ' + #39 + ' + fYasi;' + Enter +
                      '}' + Enter +

                      'function SetAdi( Value )' + Enter +
                      '{' + Enter +
                          'fAdi = Value;' + Enter +
                      '}' + Enter +

                      'function SetYasi( Value )' + Enter +
                      '{' + Enter +
                          'fYasi = Value;' + Enter +
                      '}' + Enter +
                  '}' + Enter +
                  ' var Person = new TPerson(' + #39 + 'Tuğrul HELVACI' + #39 + ', 34);';

procedure TfrmJavaScript.Button1Click(Sender: TObject);
var
  sHTML,
  sScript : String;
begin
  sHTML := HTML;
  sScript := ScriptStartSection + Enter +
                  JscriptShowMessage+ Enter +
                  JscriptClass      + Enter +
                ScriptEndSection;

  sHTML := StringReplace(sHTML, '%HEADSECTION%'   , ''      , [rfReplaceAll]);
  sHTML := StringReplace(sHTML, '%SCRIPTSECTION%' , sScript , [rfReplaceAll]);
  sHTML := StringReplace(sHTML, '%BODYSECTION%'   , 'Delphi ve JavaScript' , [rfReplaceAll]);

  LoadHTML(wBrowser, sHTML);

  while wBrowser.ReadyState < READYSTATE_INTERACTIVE do Application.ProcessMessages;
end;

procedure TfrmJavaScript.Button2Click(Sender: TObject);
var
  Doc : IHTMLDocument2;
  Win : IHTMLWindow2;
begin
  Doc := wBrowser.Document as IHTMLDocument2;
  Win := Doc.parentWindow;
  Win.execScript('ShowMessage(' + #39+ TimeToStr(Time) + #39 + ');', 'JavaScript');
  Win.execScript('ShowMessage(Person.ToString());', 'JavaScript');

  Win.execScript('Person.SetYasi(18);', 'JavaScript');
  Win.execScript('ShowMessage(Person.ToString());', 'JavaScript');
end;

Bu örneğimizin bir öncekinden tek farkı TPerson isminde bir JavaScript sınıfına sahip olması ve bu sınıfın metodlarının çalıştırılıyor olmasıdır. Buradaki en can alıcı nokta TPerson isimli JavaScript sınıfının bir örneğinin var Person = new TPerson(..) biçiminde oluşturulması ve global bir değişkene atanmasıdır. Artık bizler Delphi tarafında TWebBrowser’ın IHTMLWindow2 interface’i üzerinden bu sınıf nesnesine erişebilir ve istediğimiz gibi kullanabiliriz.

Gördüğünüz gibi Delphi üzerinden JavaScript’e ait olan metodlara ve sınıflara erişebildik. Acaba, JavaScript Delphi üzerinde tanımlı olan sınıf ve metodlara erişebilir mi ?

Bu sorunun cevabı da evet. Lâkin biraz daha karmaşık bir mekanizmaya sahip. MSDN help üzerinden yaptığım araştırmada JavaScript’in harici dillere erişimi sağlayabilmesi için, TWebBrowser’ın bazı interface’leri implemente etmesi gerektiğini öğrendim. Bu interface’ler IDocHostUIHandler, IDocHostUIHandler2 interface’leri. Bu interface’lerin pek çok metodu mevcut. Bu metodları burada paylaşmayı düşünmüyorum, ancak bu interface’lerin tanımlandığı dosyayı makalenin sonunda indirebilmeniz açısından ekleyeceğim.

IDocHostUIHandler ve IDocHostUIHandler2 interfacelerinin tanım dosyası hazırlandıktan sonra, TWebBrowser sınıfımızın IDocHostUIHandler interface’ini desteklemesini sağlamamız gerekiyor. Tanımlamamız aşağıdaki şekildeki gibi görünecek:

  TWebBrowser = class(SHDocVw.TWebBrowser, IDocHostUIHandler)
  private
    function GetHTMLWindow2: IHTMLWindow2;
    function GetDocument2: IHTMLDocument2;
  protected
    { IDocHostUIHandler }
    function EnableModeless(fEnable: Integer): HResult; stdcall;
    function FilterDataObject(const pDO: IDataObject; out ppDORet: IDataObject): HResult; stdcall;
    function GetDropTarget(const pDropTarget: IDropTarget; out ppDropTarget: IDropTarget): HResult; stdcall;
    function GetExternal(out ppDispatch: IDispatch): HResult; stdcall;
    function GetHostInfo(var pInfo: _DOCHOSTUIINFO): HResult; stdcall;
    function GetOptionKeyPath(out pchKey: PWideChar; dw: UINT): HResult; stdcall;
    function HideUI: HResult; stdcall;
    function OnDocWindowActivate(fActivate: Integer): HResult; stdcall;
    function OnFrameWindowActivate(fActivate: Integer): HResult; stdcall;
    function ResizeBorder(var prcBorder: tagRECT; const pUIWindow: IOleInPlaceUIWindow;
      fRameWindow: Integer): HResult; stdcall;
    function ShowContextMenu(dwID: UINT; ppt: PtagPOINT; const pcmdtReserved: IUnknown;
       const pdispReserved: IDispatch): HResult; stdcall;
    function ShowUI(dwID: UINT; const pActiveObject: IOleInPlaceActiveObject;
      const pCommandTarget: IOleCommandTarget; const pFrame: IOleInPlaceFrame;
      const pDoc: IOleInPlaceUIWindow): HResult; stdcall;
    function TranslateAccelerator(var lpmsg: tagMSG; var pguidCmdGroup: TGUID; nCmdID: UINT): HResult; stdcall;
    function TranslateUrl(dwTranslate: UINT; pchURLIn: PWideChar; out ppchURLOut: PWideChar): HResult; stdcall;
    function UpdateUI: HResult; stdcall;
  public
    property Document2: IHTMLDocument2 read GetDocument2;
    property HTMLWindow2: IHTMLWindow2 read GetHTMLWindow2;
  end;

Yukarıda yeni tanımı bulunan TWebBrowser artık IDocHostUIHandler interface’ini implemente etmiştir.
Not: Tanım satırımızda TWebBrowser = class(SHDocVw.TWebBrowser, IDocHostUIHandler) şeklinde bir yazım tarzı görmekteyiz. Bu tanım türüne interpose adı verilir. Delphi’de mevcut bir sınıfın genişletilebilmesi, yapısının değiştirilebilmesi adına yeniden tanımına müsaade edilir. Tıpkı .Net terminolojisindeki helper sınıflar gibi.

TWebBrowser sınıfımızın implemente ettiği interface’in bizim için en önemli metodu GetExternal metodudur. Bu metod haricindeki hiç bir metoda şu an için ihtiyacımız yok. Bu sebep ile tüm metodların içine E_NOTIMPL yani implemente etmeyeceğiz anlamına gelen sabiti döndürecek kodu yazıyoruz. GetExternal fonksiyonu; JavaScript’in harici uygulamalarla haberleşebilmesi adına IDispatch arabirimini destekleyen bir parametre alır. Bu parametreye geçilmesi gereken nesne sınıfının IDispatch interface’ini implemente etmesi gereklidir. Bunun içib ObjComAuto.pas unitinde tanımlı olan TObjectDispatch sınıfı kullanılabilir.

COM programlamanın karmaşıklığı benim kafamı karıştırdığı gibi eminim sizin de kafanızı karıştırmıştır. Bu sebeple, biraz da kod örneği görerek ilerlemek sanırım hepimize biraz nefes aldıracaktır.

  TWebBrowser = class(SHDocVw.TWebBrowser, IDocHostUIHandler)
  private
    function GetHTMLWindow2: IHTMLWindow2;
    function GetDocument2: IHTMLDocument2;
  protected
    { IDocHostUIHandler }
    function EnableModeless(fEnable: Integer): HResult; stdcall;
    function FilterDataObject(const pDO: IDataObject; out ppDORet: IDataObject): HResult; stdcall;
    function GetDropTarget(const pDropTarget: IDropTarget; out ppDropTarget: IDropTarget): HResult; stdcall;
    function GetExternal(out ppDispatch: IDispatch): HResult; stdcall;
    function GetHostInfo(var pInfo: _DOCHOSTUIINFO): HResult; stdcall;
    function GetOptionKeyPath(out pchKey: PWideChar; dw: UINT): HResult; stdcall;
    function HideUI: HResult; stdcall;
    function OnDocWindowActivate(fActivate: Integer): HResult; stdcall;
    function OnFrameWindowActivate(fActivate: Integer): HResult; stdcall;
    function ResizeBorder(var prcBorder: tagRECT; const pUIWindow: IOleInPlaceUIWindow;
      fRameWindow: Integer): HResult; stdcall;
    function ShowContextMenu(dwID: UINT; ppt: PtagPOINT; const pcmdtReserved: IUnknown;
       const pdispReserved: IDispatch): HResult; stdcall;
    function ShowUI(dwID: UINT; const pActiveObject: IOleInPlaceActiveObject;
      const pCommandTarget: IOleCommandTarget; const pFrame: IOleInPlaceFrame;
      const pDoc: IOleInPlaceUIWindow): HResult; stdcall;
    function TranslateAccelerator(var lpmsg: tagMSG; var pguidCmdGroup: TGUID; nCmdID: UINT): HResult; stdcall;
    function TranslateUrl(dwTranslate: UINT; pchURLIn: PWideChar; out ppchURLOut: PWideChar): HResult; stdcall;
    function UpdateUI: HResult; stdcall;
  public
    property Document2: IHTMLDocument2 read GetDocument2;
    property HTMLWindow2: IHTMLWindow2 read GetHTMLWindow2;
  end;

  TApplicationWrapper = class(TObjectWrapper)
  private
    function GetApplication: TApplication;
    function GetCaption: string;
    procedure SetCaption(S: string);
  published
    property Application: TApplication read GetApplication;
    property Caption: string read GetCaption write SetCaption;
  end;

...
...
...
...
...
{ TWebBrowser }

function TWebBrowser.EnableModeless(fEnable: Integer): HResult;
begin
  Result := E_NOTIMPL;
end;

function TWebBrowser.FilterDataObject(const pDO: IDataObject;
  out ppDORet: IDataObject): HResult;
begin
  Result := E_NOTIMPL;
end;

function TWebBrowser.GetDocument2: IHTMLDocument2;
begin
  Supports(Document, IHTMLDocument2, Result);
end;

function TWebBrowser.GetDropTarget(const pDropTarget: IDropTarget;
  out ppDropTarget: IDropTarget): HResult;
begin
  Result := E_NOTIMPL;
end;

function TWebBrowser.GetExternal(out ppDispatch: IDispatch): HResult;
var
  W: TApplicationWrapper;
begin
  W := TApplicationWrapper.Connect(Forms.Application);
  ppDispatch := TAutoObjectDispatch.Create(W) as IDispatch;
  Result := S_OK;
end;

function TWebBrowser.GetHostInfo(var pInfo: _DOCHOSTUIINFO): HResult;
begin
  Result := E_NOTIMPL;
end;

function TWebBrowser.GetHTMLWindow2: IHTMLWindow2;
begin
  Result := Document2.parentWindow;
end;

function TWebBrowser.GetOptionKeyPath(out pchKey: PWideChar; dw: UINT): HResult;
begin
  Result := E_NOTIMPL;
end;

function TWebBrowser.HideUI: HResult;
begin
  Result := E_NOTIMPL;
end;

function TWebBrowser.OnDocWindowActivate(fActivate: Integer): HResult;
begin
  Result := E_NOTIMPL;
end;

function TWebBrowser.OnFrameWindowActivate(fActivate: Integer): HResult;
begin
  Result := E_NOTIMPL;
end;

function TWebBrowser.ResizeBorder(var prcBorder: tagRECT;
  const pUIWindow: IOleInPlaceUIWindow; fRameWindow: Integer): HResult;
begin
  Result := E_NOTIMPL;
end;

function TWebBrowser.ShowContextMenu(dwID: UINT; ppt: PtagPOINT;
  const pcmdtReserved: IInterface; const pdispReserved: IDispatch): HResult;
begin
  Result := E_NOTIMPL;
end;

function TWebBrowser.ShowUI(dwID: UINT;
  const pActiveObject: IOleInPlaceActiveObject;
  const pCommandTarget: IOleCommandTarget; const pFrame: IOleInPlaceFrame;
  const pDoc: IOleInPlaceUIWindow): HResult;
begin
  Result := E_NOTIMPL;
end;

function TWebBrowser.TranslateAccelerator(var lpmsg: tagMSG;
  var pguidCmdGroup: TGUID; nCmdID: UINT): HResult;
begin
  Result := E_NOTIMPL;
end;

function TWebBrowser.TranslateUrl(dwTranslate: UINT; pchURLIn: PWideChar;
  out ppchURLOut: PWideChar): HResult;
begin
  Result := E_NOTIMPL;
end;

function TWebBrowser.UpdateUI: HResult;
begin
  Result := E_NOTIMPL;
end;

{ TApplicationWrapper }

function TApplicationWrapper.GetApplication: TApplication;
begin
  Result := Forms.Application;
end;

function TApplicationWrapper.GetCaption: string;
begin
  Result := frmJavaScript.Caption;
end;

procedure TApplicationWrapper.SetCaption(S: string);
begin
  frmJavaScript.Caption := S;
end;

Yukarıdaki kodda JavaScript’in ulaşmasını istediğimiz sınıfın TApplicationWrapper olduğunu TWebBrowser’ın GetExternal metodunda söylüyoruz. Bu söylem bize, JavaScript içinde TApplicationWrapper sınıfının tüm public property’lerine ve metodlarına erişim hakkı sağlayacaktır. Artık JavaScript içinde TApplicationWrapper sınıfını istediğimiz gibi kullanabileceğiz. Ancak; sizin de gözlemlediğiniz gibi TAutoObjectDispatch isimli bir başka sınıf da mevcut GetExternal metodunun içinde. Bu sınıf Delphi sınıflarından değildir. Bu ve benzer sınıfların tanımlandığı dosyayı da indirebilmeniz için makalenin en sonuna ekleyeceğim.

Bu sınıfın var olmasının nedeni, IDispatch arabirimini destekleyen bir yapıya sahip olmamızın gerekliliğidir. Hatırlarsanız bir kaç paragraf önce ObjComAuto.pas dosyası içindeki TObjectDispatch isimli sınıfın IDispatch arabirimini bizim için implemente ettiğini söylemiştik. JavaScript dış dünyadaki(örneğimizde Delphi) ortamlara erişim sağlayacağı zaman external söz dizimini kullanır. Örneğin, external.Caption = ‘Selam’; gibi. external direktifi ile erişeceği nesnenin Caption isimli bir property’sinin olup olmadığının kontrolünün yapılması gerekir. Yada, external.ShowMessage(‘Selam’); çağrısında olduğu gibi bir metod çağrımının Delphi tarafında bulunabiliyor olması gerekir. Gerek metod gerekse de property çağrımlarının bulunamaması bir hata ile sonuçlanacaktır.

TWebBrowser’ın GetExternal metoduna parametre olarak geçtiğimiz TApplicationWrapper isimli sınıfın kodlarının içinde TObjectDispatch’in metod ve property aramak için gerekli olan metodlarının yazılması gerekmektedir. Bu metodlar yazılsın ki JavaScript tarafında çağrımı yapılan property yada metodların Delphi tarafında karşılıkları bulunabilsin ve işletilebilsin. Bunun için TObjectDispatch sınıfından türeyen ve bu karmaşık çağrımları bizden gizleyen bir başka unit daha var. Daha önce dediğim gibi o unit’i de sizinle paylaşacağım.

Bu noktaya kadar özetlememiz gerekirse eğer; Delphi’den JavaScript’e ulaşmak ve gerek sınıf gerekse de herhangi bir metodu çalıştırmak bizim için son derece kolay olmuştu. Ancak; JavaScript’in Delphi sınıflarına erişmesi bir o kadar da zor göründü. Aslında kodu indirip incelediğinizde pek de zor olmadığını göreceksiniz, lâkin COM programlamanın karmaşık yapısından mütevellit anlatabilmek ve anlayabilmek haliyle biraz zor oluyor.

Son kod örneğimizi de verip makalemizi burada neticelendirelim:

const
  JScriptShowMessage =
                     'function ShowMessage(Message)' + Enter +
                     '{' + Enter +
                        'alert(Message);' + Enter +
                        'external.Caption = ' + #39 + 'Selam Delphi nasılsın, ben JavaScript ;) ' + #39 + ';' + Enter +
                     '}';

procedure TfrmJavaScript.Button2Click(Sender: TObject);
var
  Doc : IHTMLDocument2;
  Win : IHTMLWindow2;
begin
  Doc := wBrowser.Document as IHTMLDocument2;
  Win := Doc.parentWindow;
  Win.execScript('ShowMessage(' + #39+ TimeToStr(Time) + #39 + ');', 'JavaScript');
end;

Bu örneğimizdeki tek fark; JScriptShowMessage sabitinin içinde external.Caption atamasının yapılmasıdır. Bu atama ile TWebBrowser nesnemiz, kendi üzerinde tanımladığımız GetExternal metodunu çalıştıracak , böylece TApplicationWrapper sınıfına erişebilecek ve formumuzun caption bilgisi güncellenecektir.

Örnek uygulamamızı buradan indirebilirsiniz.

Not: Örneğimiz Delphi 2009 ile geliştirilmiştir.

Saygılar, sevgiler..

“Delphi & JavaScript Kardeşliği” için 7 Yorum

  1. sadettinpolat diyor ki:

    tugrul hocam hizina yetismek mumkun degil :)
    biz bi makaleyi hazmedemeden sen bir digerini yayinliyorsun. bu teknikle biz google mapse programimiz icinden takla bile attiririz valla :) tesekkurler…

  2. Tuğrul HELVACI diyor ki:

    Rica ederim üstadım, vazifemiz :) Google Maps konusunda haklısın, yakın bir zamanda onunla ilgili de makale yazmayı düşünüyordum. O yüzden JavaScript Delphi entegrasyonuna girmiştim :)

    Senden de bir şey kaçmıyor maaşallah :)

  3. izle diyor ki:

    tugrul bey merhaba,
    bu kodlari sizmi yaziyorsunuz, yoksa sagdan soldan buldugunuz kodlari alip ben yazdim diye milletemi yutturuyorsunuz?
    saygilar

    • Tuğrul HELVACI diyor ki:

      Sizce ? Makalelerimi dikkatle okuduysanız eğer kod paylaşmaktan ziyade nasıl ve nedenlerini anlattığımı görebilirsiniz. Ama belki de onları da Google Translate ile çeviriyorumdur. Kimbilir !

  4. Adem diyor ki:

    Tebrik ediyorum,
    çok güzel bir makale olmuş.
    Yazılımcılar özellikle bildiklerini paylaşmak istemezler.
    Bilen ve paylaşan nadir insanlardan birisiniz.
    Paylaşımlarınızın devamını diliyorum.
    Teşekkürler.

  5. Quantum diyor ki:

    Gerçekten çok faydalı bilgiler paylaşmışsınız, ellerinize sağlık. Teşekkürler.

  6. Ömer diyor ki:

    Hocam Size konuyla alakalı olmayan bir şey sormam mümkün mü.

Yorum Yazın