본문 바로가기

프로그래밍/Delphi

[Demo-Delphi]웹브라우저에서 자바스크립트로 델파이 코드 실행하기

[Demo-Delphi]웹브라우저에서 자바스크립트로 델파이 코드 실행하기


이전 글 "[강좌-Delphi]웹브라우저에서 자바스크립트로 델파이 코드 실행하기" 에서 살펴본 임베딩된 웹브라우저에서 델파이로 작성된 코드를 호출하는 데모 프로그램을 작성해 보자



1. 개요

로딩된 웹페이지에서 자바스크립트를 이용해 델파이 폼과 상호 작용하고 델파이로 작성된 코드로 대체하여 호출 하는 샘플 프로그램을 제작해 본다.



2. 작업 절차

    • 델파이 TForm을 디자인 한다.
    • Type Library / Interface를 생성하고 외부 객체(External Object)를 정의 한다.
    • 외부 객체(External Object)를 구현한다.
    • IDocHostUIHandler 인터페이스의 GetExternal 메소드를 구현한다.
    • 구현된 IDocHostUIHandler를 TWebBrowser 컨트롤로 등록한다.
    • TWebBrowser에서 로딩할 HTML 파일을 작성한다.

3. 델파이 TForm 디자인

TWebBrowser를 폼에 배치 시킨다(Align = alClient, name = formTest)

TStatusBar를 폼에 배치 시키고 AutoHint / SimplePanel 속성을 true로 지정한다.(name = sBar)



4. Type Library / Interface 생성 및 외부객체 정의

외부 객체는 다음 기능을 정의 한다.

    • GetPrecis : 각각의 프로그램명을 클릭할 때 프로그램에 지정된 프로그램의 개요 얻기 메소드(각 프로그램별 고유 아이디 사용)
    • ShowURL : 마우스 커서가 프로그램명 위에 위치할 때 현재 웹페이지의 URL을 보여주는 메소드
    • HideURL : 마우스 커서가 프로그램명에서 벗어 날 때 URL을 숨기기
    • MyAlert : 원래 웹페이지에서 제공된 alert를 대체할 델파이 코드로 제작된 Alert 메시지창 노출 메소드

위 메서드들을 구현 할 인터페이스를 생성한다. 

File > New > Other 를 선택하여 "New Item"에서 "Delphi Projects > ActiveX" 노드의 "Type Library" 선택


외부 객체로 사용할 인터페이스를 생성한다.



IMyExternal 인터페이스명 지정


IMyExternal에 메소드 정의


IMyExternal Methods

 Name

Parameters 

Types 

Modifier 

 GetPrecis

 ProgID

 Result

 BSTR 

 BSTR *

 [in] 

 [out, retval]

 ShowURL

 ProgID

 BSTR

 [in] 

 HideURL

 -

 - 

 - 

 MyAlert

Msg 

 BSTR  

 [in] 


인터페이스와 메서드가 추가된 Type Library Editor 상태



위 과정을 거쳐 저장(Ctrl+S)을 하게 되면 프로젝트명.ildl 파일과 프로그램명_TLB.pas 파일이 생성된다.

프로젝트 명을 execDelphiCode라고 지정한 경우 다음 파일이 생성됨

execDelphiCode.ildl        : 타입라이브러리 정의 파일

execDelphiCode_TLB.pas  : 인터페이스 정의 파일 소스코드


결과적으로 다음과 같은 인터페이스 코드가 자동으로 생성 된다.


...
// *********************************************************************//
// Interface: IMyExternal
// Flags:     (4416) Dual OleAutomation Dispatchable
// GUID:      {707D36D1-EBCC-4F07-8474-8E35C738C7D0}
// *********************************************************************//
  IMyExternal = interface(IDispatch)
    ['{707D36D1-EBCC-4F07-8474-8E35C738C7D0}']
    function GetPrecis(const ProgID: WideString): WideString; safecall;
    procedure ShowURL(const ProgID: WideString); safecall;
    procedure HideURL; safecall;
    procedure MyAlert(const Msg: WideString); safecall;
  end;
 ...

 * 중요 : 작성중인 델파이 프로젝트에 타입라이브러리 리소스를 포함 시키려면 프로젝트 소스에 다음 라인을 추가 해야 한다. 그렇지 않으면 Type Library 로딩 오류가 발생하므로 주의 할것.


{$R *.tlb}


5. 외부객체(External Object) 인터페이스의 구현

이 절에서는 위에 생성된 IMyExternal 인터페이스를 상속받는 TMyExternal 클래스를 선언하여 인터페이스를 구현 한다.


먼저 IMyExternal을 상속받는 TMyExternal 클래스를 선언하고 IMyExternal 인터페이스 메소드들을 구현 한다.

참고로 TMyExternal이 상속받아야 할 "TAutoIntfObject" 오토메이션 객체는 "ComObj" 유닛을 Uses절에 포함 시켜야 한다.


TMyExternal 객체는 _TLB.pas 파일내에 인터페이스와 같은 모듈에 구현 해 보도록 한다(별도로 분리해도 됨)


이렇게 구현된 외부객체 관련 소스는 다음과 같다.


unit execDelphiCode_TLB;

// ************************************************************************ //
// WARNING
// -------
// The types declared in this file were generated from data read from a
// Type Library. If this type library is explicitly or indirectly (via
// another type library referring to this type library) re-imported, or the
// 'Refresh' command of the Type Library Editor activated while editing the
// Type Library, the contents of this file will be regenerated and all
// manual modifications will be lost.
// ************************************************************************ //

// $Rev: 52393 $
// File generated on 2018-10-30 오후 2:02:13 from Type Library described below.

// ************************************************************************  //
// Type Lib: C:\Users\Administrator\Documents\Embarcadero\Studio\Projects\DelphiDemo\ExecDelphiCodeinWebBrowser\execDelphiCode (1)
// LIBID: {F9AE4ACE-C6F2-46EF-9C50-456297D1CB7C}
// LCID: 0
// Helpfile:
// HelpString:
// DepndLst:
//   (1) v2.0 stdole, (C:\Windows\SysWOW64\stdole2.tlb)
//   (2) v4.0 StdVCL, (C:\Windows\SysWOW64\stdvcl40.dll)
// SYS_KIND: SYS_WIN32
// ************************************************************************ //
{$TYPEDADDRESS OFF} // Unit must be compiled without type-checked pointers.
{$WARN SYMBOL_PLATFORM OFF}
{$WRITEABLECONST ON}
{$VARPROPSETTER ON}
{$ALIGN 4}

interface

uses
  Winapi.Windows, System.Classes, System.Variants, System.Win.StdVCL,
  Vcl.Graphics, Winapi.ActiveX,

  ComObj, SysUtils, StdActns, Dialogs;


// *********************************************************************//
// GUIDS declared in the TypeLibrary. Following prefixes are used:
//   Type Libraries     : LIBID_xxxx
//   CoClasses          : CLASS_xxxx
//   DISPInterfaces     : DIID_xxxx
//   Non-DISP interfaces: IID_xxxx
// *********************************************************************//
const
  // TypeLibrary Major and minor versions
  execDelphiCodeMajorVersion = 1;
  execDelphiCodeMinorVersion = 0;

  LIBID_execDelphiCode: TGUID = '{F9AE4ACE-C6F2-46EF-9C50-456297D1CB7C}';

  IID_IMyExternal: TGUID = '{707D36D1-EBCC-4F07-8474-8E35C738C7D0}';
type

// *********************************************************************//
// Forward declaration of types defined in TypeLibrary
// *********************************************************************//
  IMyExternal = interface;
  IMyExternalDisp = dispinterface;

// *********************************************************************//
// Declaration of structures, unions and aliases.
// *********************************************************************//
  PWideString1 = ^WideString; {*}


// *********************************************************************//
// Interface: IMyExternal
// Flags:     (4416) Dual OleAutomation Dispatchable
// GUID:      {707D36D1-EBCC-4F07-8474-8E35C738C7D0}
// *********************************************************************//
  IMyExternal = interface(IDispatch)
    ['{707D36D1-EBCC-4F07-8474-8E35C738C7D0}']
    function GetPrecis(const ProgID: WideString): WideString; safecall;
    procedure ShowURL(const ProgID: WideString); safecall;
    procedure HideURL; safecall;
    procedure MyAlert(const Msg: WideString); safecall;
  end;

// *********************************************************************//
// DispIntf:  IMyExternalDisp
// Flags:     (4416) Dual OleAutomation Dispatchable
// GUID:      {707D36D1-EBCC-4F07-8474-8E35C738C7D0}
// *********************************************************************//
  IMyExternalDisp = dispinterface
    ['{707D36D1-EBCC-4F07-8474-8E35C738C7D0}']
    function GetPrecis(const ProgID: WideString): WideString; dispid 201;
    procedure ShowURL(const ProgID: WideString); dispid 202;
    procedure HideURL; dispid 203;
    procedure MyAlert(const Msg: WideString); dispid 204;
  end;


// IMyExternal 인터페이스에 대한 구현 객체
  TMyExternal = class(TAutoIntfObject, IMyExternal, IDispatch)
  private
    FData : TStringList; //데이터 파일로 부터 파일 내용을 읽어서 저장할 객체
    // TStringList를 사용하기 위해서는 Uses절에 SysUtils 유닛을 포함 시켜야 함
    procedure ShowStatusBarMsg(Amsg : String); //상태바에 메시지 표시
  protected
    // 프로그램 아이디로 프로그램 정보를 얻는다
    function GetPrecis(const ProgID: WideString): WideString; safecall;
    // 현재 웹페이지 URL을 상태바에 표시한다.
    procedure ShowURL(const ProgID: WideString); safecall;
    // 상태바에 표시된 현재 페이지 URL 정보를 감춘다
    procedure HideURL; safecall;
    // 웹페이지의 원래 alert 스크립트 실행을 대체할 델파이로 구현된 알림창
    procedure MyAlert(const Msg: WideString); safecall;
  public
    constructor Create;
    destructor Destroy; override;

  end;
implementation

uses System.Win.ComObj;

{ TMyExternal }

//객체 생성자
constructor TMyExternal.Create;
var
  TypeLib: ITypeLib;    // 타입 라이브러리(Type Library) 정보
  ExeName: WideString;  // 실행파일명
begin
  // 현재 실행중인 파일명을 얻는다(ParamStr(0)은 프로그램 명이다(경로 포함))
  ExeName := ParamStr(0);
  // 실행중인 프로그램의 리소스에서 타입 라이브러리를 얻는다.
  OleCheck(LoadTypeLib(PWideChar(ExeName), TypeLib));
  // TAutoIntfObject.Create 생성자 상속 실행(타입 라이브러리를 지정해야 함)
  inherited Create(TypeLib, IMyExternal);
  // 현재 실행중인 프로그램의 정보를 ".dat" 파일로 부터 얻음
  fData := TStringList.Create;
  fData.LoadFromFile(StringReplace(ExeName, '.exe', '.dat', [rfIgnoreCase]));
end;

//객체 소멸자
destructor TMyExternal.Destroy;
begin
  fData.Free;

  inherited;
end;

function TMyExternal.GetPrecis(const ProgID: WideString): WideString;
begin
  // 데이터 파일에는 ProgID=프로그램정보 처럼 Key=Value 리스트 형식으로 저장되어
  // 있다고 가정함.
  Result := fData.Values[ProgId]; //Key=ProgID에 해당하는 Value 값을 리턴
end;

procedure TMyExternal.HideURL;
begin
  ShowStatusBarMsg('');
end;

procedure TMyExternal.MyAlert(const Msg: WideString);
begin
  //ShowMessage를 이용하기 위해서는 Dialogs 를 Uses절에 포함 시켜야 한다.
  ShowMessage('델파이 메시지 알림창'#13#10+Msg);
end;

procedure TMyExternal.ShowStatusBarMsg(Amsg: String);
var
  //Uses절에 StdActns을 추가해야 사용할 수 있다.
  //THintAction은 저장된 힌트를 폼에 상태바(TStatusBar)가 있고 "AutoHint"가 true이면
  //상태바에 이 힌트가 표시 되는 매커니즘을 가지고 있음^^
  HintAct: THintAction;
begin
  HintAct := THintAction.Create(nil);
  try
    HintAct.Hint := AMsg;
    HintAct.Execute;
  finally
    HintAct.Free;
  end;
end;

procedure TMyExternal.ShowURL(const ProgID: WideString);
begin
  ShowStatusBarMsg('http://localhost/software?id=' + ProgID);
end;

end.


6. IDocHostUIHandler 인터페이스의 GetExternal 메소드를 구현


외부 객체(External Object)를 웹브라우저에서 호스팅 하려면 앞서 언급했던 IDocHostUIHandler의 객체를 캡슐화 해서 아무것도 안하는 객체를 만들었던 "TNulWBContainer" 객체를 재 사용 하도록 한다. 이 부분에 대해서는 이전 글을 참고( [강좌-Delphi]웹브라우저(TWebBrowser)의 사용자 인터페이스 커스터마이징 )

TNulWBContainer를 상속 받아서 GetExternal 인터페이스를 구현할 TExternalContainer 클래스를 메인 폼에 추가 해보자

 Uses 절에는 TMyExternal이 구현된 _TLB.pas 유닛을 포함시키는 것을 잊지 말아야 

... in main form unit

interface
...
type
  TExternalContainer = class(TNulWBContainer,
    IDocHostUIHandler, IOleClientSite)
  private
    fExternalObj: IDispatch;  // 외부 구현 객체
  protected
    {GetExternal 메서드 오버라이드 구현 }
    function GetExternal(out ppDispatch: IDispatch): HResult; stdcall;
  public
    constructor Create(const HostedBrowser: TWebBrowser);
  end;
...

implementation

uses
    execDelphiCode_TLB;

constructor TExternalContainer.Create(
  const HostedBrowser: TWebBrowser);
begin
  inherited Create(HostedBrowser);
  fExternalObj := TMyExternal.Create;
end;

function TExternalContainer.GetExternal(
  out ppDispatch: IDispatch): HResult;
begin
  ppDispatch := fExternalObj;
  Result := S_OK;
end;


7. 구현된 IDocHostUIHandler를 TWebBrowser 컨트롤로 등록


이제 TWebBrowser에 TMyExternal 외부 객체를 등록하는 과정이 남아 있다. 정확히는 TWebBrowser가 TMyExternal 외부 객체를 호스팅하게 하기 위해 등록한다는 표현이 더 적절 할 수 있겠다.


메인 폼의 OnShow 이벤트에 등록하는 코드를 넣기로 하자.


procedure TformTest.FormShow(Sender: TObject); begin FContainer := TExternalContainer.Create(WB); WB.Navigate( ExtractFilePath(ParamStr(0)) + 'MyWebPage.html' ); end; procedure TformTest.FormHide(Sender: TObject); begin FContainer.Free; end;


8. TWebBrowser에서 로딩할 HTML 파일 / 프로그램 데이터 파일 작성


이제 위 FormShow 이벤트 핸들러에 있는 코드와 같이 폼이 보여질 때 "MyWebPage.html" 파일을 웹브라우저에 로딩하게 되는데 이때 이용할 "MyWebPage.html" HTML 파일을 작성해 보자. 이 파일은 프로그램 실행 경로에서 찾기 때문에 ".exe" 파일이 있는 곳에 위치 시켜야 할 것임.


웹페이지 파일 (MyWebPage.html)


<?xml version="1.0"?>
<!DOCTYPE html
  PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xml:lang="en" lang="en">
  <head>
    <title>[DEMO Project] 웹브라우저에서 델파이 코드 호출하기</title>
    <style type="text/css">
      <!--
      body {font-family: Tahoma; font-size: 10pt;}
      h1 {font-size: 12pt;}
      #precis {
        border: 1px solid silver;
        padding: 4px;
      }
      -->
    </style>
    <script type="text/javascript">
      <!--
      function ShowPrecis(progID) {
        precisObj = document.getElementById("precis");
        progObj = document.getElementById(progID);
        precisObj.innerHTML = progObj.innerHTML.bold()
          + '\<br />'
          + external.GetPrecis(progID);
      }
      //-->
    </script>
  </head>
  <body>
    <h1>프로그램 설명</h1>
    <ul>
      <li><a href="javascript:void(0);" id="dbview"
        onclick="ShowPrecis('dbview');"
        onmouseover="external.ShowURL('dbview');"
        onmouseout="external.HideURL()";
        >테스트 데이터베이스 뷰어</a>
      </li>
      <li><a href="javascript:void(0);" id="webview"
        onclick="ShowPrecis('webview');"
        onmouseover="external.ShowURL('webview');"
        onmouseout="external.HideURL()";
        >웹페이지 뷰어</a>
      </li>
      <li><a href="javascript:void(0);" id="installapp"
        onclick="ShowPrecis('installapp');"
        onmouseover="external.ShowURL('installapp');"
        onmouseout="external.HideURL()";
        >데모 설치 프로그램 </a>
      </li>
      <li><a href="javascript:void(0);" id="guestman"
        onclick="ShowPrecis('guestman');"
        onmouseover="external.ShowURL('guestman');"
        onmouseout="external.HideURL()";
        >외부 방문객 관리 프로그램</a>
      </li>
    </ul>
    <div id="precis">
      프로그램 설명을 보려면 프로그램명을 클릭 하세요.
    </div>
		<div>
			<input id="btnTest" type="button" value="알림" onClick="external.MyAlert('알림 메시지')"> 
		</div>        
  </body>
</html>


프로그램 데이터 파일(execDelphiCode.dat)


dbview=사용자 데이터베이스를 로딩해서 보여 줍니다.
webview=사용자별 웹페이지를 보여 줍니다.
installapp=프로그램을 설치합니다.
guestman=회사 방문객을 별도로 관리합니다.


참고 : 

[강좌-Delphi]웹브라우저에서 자바스크립트로 델파이 코드 실행하기

[강좌-Delphi]웹브라우저(TWebBrowser)의 사용자 인터페이스 커스터마이징

How to call Delphi code from scripts running in a TWebBrowser


전체소스 다운로드 : ExecDelphiCodeinWebBrowser.zip