TEmbeddedWB/TWebbrowser:window.external是一个空对象,但可以调用函数,为什么它首先是"空"?

Cod*_*eat 3 javascript delphi interface webbrowser-control

用例(首先是一些信息):

我制作了一些HTML/CSS3/Javascript游戏,可以在平台特定的可执行文件中在WebView/Embedded浏览器中的不同平台上运行.我自己设计了它,因为我厌倦了所有的"框架",告诉我使用它们的框架是多么简单.我不需要这些框架的所有臃肿与他们令人印象深刻的类和东西,它必须简单作为ABC,对吧?另外,因为webview比本机代码慢,所以它必须简单直接才能获得最佳性能.

所以我设计了一个在javascript中作为变量提供的接口,无需加载额外的javascript类(如cordova或phonegap或其他).因为我也使用Windows(Windows无法将对象变量的名称更改为'publish'),所以可以通过javascript通过window.external访问它.在webview /浏览器中加载html时,此变量将成为对象.

这一切都很好(在不同的平台上),但window.external变量似乎是Windows上的一个空对象,但是当你尝试调用一个类似的函数时window.external.vibrate(500),它将被执行而没有错误(此函数存在于该对象的所有平台版本中).

然而,类似的typeof window.external.vibrate结果'undefined'.遍历对象不会做任何事情,例如:

for( var p in window.external ) {
   alert( p );
}
Run Code Online (Sandbox Code Playgroud)

因此,测试外部对象是否是"真正的"外部对象并且不可能看到支持哪些功能(如果需要)并不容易.

我该怎么办?我想念一些东西吗?

为了给你一些信息,我已经按照这个'指南':http: //www.delphidabbler.com/articles?article = 22.

我的代码(简化):

类型库:

unit WebBrowserBridge_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.                                         
// ************************************************************************ //

// PASTLWTR : $Revision:   1.88.1.0.1.0  $
// File generated on 4-3-2014 6:50:23 from Type Library described below.

// ************************************************************************ //
// Type Lib: ExternalInterface\WebBrowserBridge.tlb (1)
// IID\LCID: {517F7078-5E73-4E5A-A8A2-8F0FF14EF21B}\0
// Helpfile: 
// DepndLst: 
//   (1) v2.0 stdole, (C:\Windows\SysWOW64\stdole2.tlb)
// ************************************************************************ //
{$TYPEDADDRESS OFF} // Unit must be compiled without type-checked pointers. 
interface

uses Windows, ActiveX, Classes, Graphics, OleServer, OleCtrls, StdVCL;

// *********************************************************************//
// 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
  WebBrowserBridgeMajorVersion = 1;
  WebBrowserBridgeMinorVersion = 0;

  LIBID_WebBrowserBridge: TGUID = '{517F7078-5E73-XXXX-B8A2-8F0FF14EF21B}';

  IID_IWebBrowserBridge: TGUID = '{4F995D09-XXXX-4042-993E-C71A8AED661E}';
type

// *********************************************************************//
// Forward declaration of types defined in TypeLibrary                    
// *********************************************************************//
  IWebBrowserBridge = interface;
  IWebBrowserBridgeDisp = dispinterface;

// *********************************************************************//
// Interface: IWebBrowserBridge
// Flags:     (4416) Dual OleAutomation Dispatchable
// GUID:      {4F995D09-CF9E-XXX-993E-C71A8AED661E}
// *********************************************************************//
  IWebBrowserBridge = interface(IDispatch)
    ['{4F995D09-CF9E-4042XXXX-C71A8AED661E}']
    procedure isAvailable; safecall;
    procedure vibrate(ms: Integer); safecall;
  end;

// *********************************************************************//
// DispIntf:  IWebBrowserBridgeDisp
// Flags:     (4416) Dual OleAutomation Dispatchable
// GUID:      {4F995D09-CF9E-XXX-993E-C71A8AED661E}
// *********************************************************************//
  IWebBrowserBridgeDisp = dispinterface
    ['{4F995D09-CF9E-404XXXE-C71A8AED661E}']
    procedure isAvailable; dispid 200;
    procedure vibrate(ms: Integer); dispid 201;
  end;

implementation

uses ComObj;

end.
Run Code Online (Sandbox Code Playgroud)

对象库(类):

unit WebBrowserBridge;

interface

uses
  // Delphi
  ActiveX, SHDocVw, Windows, Classes, ComObj, Dialogs,
  // Project
  IntfDocHostUIHandler, UNulContainer, WebBrowserBridge_TLB;

type
  TWebBrowserBridge = class(TAutoIntfObject, IWebBrowserBridge, IDispatch)
  public
    { IMyExternal methods }
    procedure isAvailable(); safecall;
    procedure vibrate(ms: Integer); safecall;

  public
    constructor Create;
    destructor Destroy; override;
  end;

  {
  TWebBrowserBridgeContainer:
    UI handler that extends browser's external object.
  }
  TWebBrowserBridgeContainer = class(TNulWBContainer, IDocHostUIHandler, IOleClientSite)
  private
    fExternalObj: IDispatch;  // external object implementation
  protected
    { Re-implemented IDocHostUIHandler method }
    function GetExternal(out ppDispatch: IDispatch): HResult; stdcall;
  public
    constructor Create(const WBDefaultInterface: IDispatch);
  end;

implementation

uses
  SysUtils, StdActns;

  { TWebBrowserBridgeContainer }
constructor TWebBrowserBridgeContainer.Create(const WBDefaultInterface: IDispatch);
begin
  inherited;
  fExternalObj := TWebBrowserBridge.Create;
end;

function TWebBrowserBridgeContainer.GetExternal(out ppDispatch: IDispatch): HResult;
begin
  ppDispatch := fExternalObj;
  Result := S_OK; // indicates we've provided script
end;


 { TWebBrowserBridge }
constructor TWebBrowserBridge.Create;
var
  TypeLib: ITypeLib;    // type library information
  ExeName: WideString;  // name of our program's exe file
begin
  // Get name of application
  ExeName := ParamStr(0);
  // Load type library from application's resources
  OleCheck(LoadTypeLib(PWideChar(ExeName), TypeLib));
  // Call inherited constructor
  inherited Create(TypeLib, IWebBrowserBridge);
end;

destructor TWebBrowserBridge.Destroy;
begin
  inherited;
end;

procedure TWebBrowserBridge.isAvailable();
begin
 //Result:=1;
end;

procedure TWebBrowserBridge.vibrate(ms: Integer);
begin
  windows.beep( 100, ms );
  //showMessage( IntToStr( ms ));
end;
Run Code Online (Sandbox Code Playgroud)

PS:

我还想知道如何在类型库中创建一个函数,因为它只允许创建过程或属性(但不像Android那样支持属性).

编辑:

另请参阅我的回答,但由于上面的PS,问题仍然存在.

JRL*_*JRL 7

使用ObjComAuto.TObjectDispatchexternal提供的后期绑定功能,可以使用更简单的方法在Delphi中实现方法.

这样您就不需要定义任何接口或类型库.您需要的只是一个实现所需事件的简单类,以及$ METHODINFO提供的扩展RTTI信息.

您可以实现过程和函数,并接收Delphi类型或javascript对象作为参数.也可以从Delphi中使用Javascript对象(可以访问属性和方法).

示例:(只需在表单中删除TEmbeddedWB)

uses MSHTML_EWB, ObjComAuto;

type
{$METHODINFO ON} // activate detailed RTTI
  TJavascriptReceiver = class
    procedure MyMouseMove(event: variant);
    procedure MyClick(event: variant);
    function MyGet(msg: string): string;
  end;
{$METHODINFO OFF}

{ TJavascriptReceiver }

procedure TJavascriptReceiver.MyMouseMove(event: variant);
begin
  Form1.Caption := IntToStr(event.clientX) + ', ' + IntToStr(event.clientY);
end;

procedure TJavascriptReceiver.MyClick(event: variant);
var
  w: variant;
begin
  w := (Form1.EmbeddedWB1.Document as IHTMLDocument2).parentWindow;
  w.testGet('Caption: ');
end;

function TJavascriptReceiver.MyGet(msg: string): string;
begin
  Result := msg + Form1.Caption;
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
var
  strs: TStringStream;
begin
  strs := TStringStream.Create;
  try
    strs.WriteString(
      '<!DOCTYPE html>'
      +'<html>'
      +'<head>'
      +'  <style>'
      +'    html, body { margin: 0; padding: 0; height: 100%; }'
      +'  </style>'

      +'  <script>'
      +'    function testGet(msg) {'
      +'      alert(external.MyGet(msg));'
      +'    }'
      +'  </script>'
      +'</head>'

      +'<body'
      +'  onmousemove="external.MyMouseMove(event)"'
      +'  onclick="external.MyClick(event)"'
      +'>'
      +'Click anywhere'
      +'</body>'
      +'</html>'
    );
    EmbeddedWB1.LoadFromStream(strs);
  finally
    strs.Free;
  end;
end;

procedure TForm1.EmbeddedWB1GetExternal(Sender: TCustomEmbeddedWB;
  var ppDispatch: IDispatch);
begin
  ppDispatch := TObjectDispatch.Create(TJavascriptReceiver.Create);
end;
Run Code Online (Sandbox Code Playgroud)

注意:

Javascript数组很稀疏,因此您无法使用通常的myArray[3]语法从Delphi访问它们.相反,你需要使用索引,就好像它是一个属性,即某种类型myArray.3.这不是Delphi直接支持的,而是使用ComObj.GetDispatchPropValue : GetDispatchPropValue(myArray, '3'). 更多信息在这里.

编辑:

有关如何通过window.external方法进行迭代的信息,请参阅我的其他答案.