首页 > 解决方案 > 将 JS 扩展演示 (CEF4Delphi) 从 Delphi 转换为 C++Builder 失败 OnWebKitInitialized

问题描述

我想构建从https://github.com/salvadordf/CEF4Delphi下载的 CEF4Delphi 附带的 JS 扩展演示项目,该项目已安装在 C++Builder XE7 上。我的目标是将消息和变量从 Chromium(网页、javascript)发送到本机 C++ 函数。

我发现 delphi 演示效果很好。但是我需要翻译成 C++Builder,而且我翻译了几乎所有的代码。如果我不设置 TCefApplication 的 OnWebKitInitialized 成员,它会很好。但是我需要设置才能注册我的扩展,所以当我这样做时,应用程序编译并构建良好,但浏览器有白色背景,没有显示。我需要让这个演示在 C++Builder 中工作。我在下面附上了源代码。

单元1.h

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

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include "uCEFChromium.hpp"
#include "uCEFWinControl.hpp"
#include "uCEFWindowParent.hpp"
#include <Vcl.ComCtrls.hpp>
#include <Vcl.ExtCtrls.hpp>

#include "uTestExtensionHandler.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE-managed Components
    TPanel *NavControlPnl;
    TEdit *Edit1;
    TButton *GoBtn;
    TStatusBar *StatusBar1;
    TCEFWindowParent *CEFWindowParent1;
    TChromium *Chromium1;
    TTimer *Timer1;
    void __fastcall Chromium1AfterCreated(TObject *Sender, ICefBrowser * const browser);
    void __fastcall Chromium1BeforeClose(TObject *Sender, ICefBrowser * const browser);
    void __fastcall Chromium1BeforeContextMenu(TObject *Sender, ICefBrowser * const browser,
          ICefFrame * const frame, ICefContextMenuParams * const params,
          ICefMenuModel * const model);
    void __fastcall Chromium1BeforePopup(TObject *Sender, ICefBrowser * const browser,
          ICefFrame * const frame, const ustring targetUrl, const ustring targetFrameName,
          TCefWindowOpenDisposition targetDisposition, bool userGesture,
          const TCefPopupFeatures &popupFeatures, TCefWindowInfo &windowInfo,
          ICefClient *&client, TCefBrowserSettings &settings, bool &noJavascriptAccess,
          bool &Result);
    void __fastcall Chromium1Close(TObject *Sender, ICefBrowser * const browser, bool Result);
    void __fastcall Chromium1ContextMenuCommand(TObject *Sender, ICefBrowser * const browser,
          ICefFrame * const frame, ICefContextMenuParams * const params,
          int commandId, DWORD eventFlags, bool Result);
    void __fastcall Chromium1ProcessMessageReceived(TObject *Sender, ICefBrowser * const browser,
          TCefProcessId sourceProcess, ICefProcessMessage * const message,
          bool Result);
    void __fastcall Timer1Timer(TObject *Sender);
    void __fastcall GoBtnClick(TObject *Sender);
    void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
    void __fastcall FormCreate(TObject *Sender);
    void __fastcall FormShow(TObject *Sender);
    void __fastcall FormDestroy(TObject *Sender);





private:    // User declarations
public:     // User declarations
    __fastcall TForm1(TComponent* Owner);
protected:
    // Variables to control when can we destroy the form safely
    bool FCanClose;  // Set to True in TChromium.OnBeforeClose
    bool FClosing;  // Set to True in the CloseQuery event.
    void __fastcall BrowserCreatedMsg(TMessage &Message);
    void __fastcall BrowserDestroyMsg(TMessage &Message);
    void __fastcall WMMove(TMessage &Message);
    void __fastcall WMMoving(TMessage &Message);
    void __fastcall WMEnterMenuLoop(TMessage &Message);
    void __fastcall WMExitMenuLoop(TMessage &Message);

    BEGIN_MESSAGE_MAP
     MESSAGE_HANDLER(CEF_AFTERCREATED, TMessage, BrowserCreatedMsg)
     MESSAGE_HANDLER(CEF_DESTROY, TMessage, BrowserDestroyMsg)
     MESSAGE_HANDLER(WM_MOVE, TMessage, WMMove)
     MESSAGE_HANDLER(WM_MOVING, TMessage, WMMoving)
     MESSAGE_HANDLER(WM_ENTERMENULOOP, TMessage, WMEnterMenuLoop)
     MESSAGE_HANDLER(WM_EXITMENULOOP, TMessage, WMExitMenuLoop)
    END_MESSAGE_MAP(TForm)

};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

单元1.cpp

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

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "uCEFChromium"
#pragma link "uCEFWinControl"
#pragma link "uCEFWindowParent"
#pragma resource "*.dfm"
TForm1 *Form1;

void GlobalCEFApp_OnWebKitInitialized()
{
  String TempExtensionCode;
  //ICefv8Handler *TempHandler;
  _di_ICefv8Handler TempHandler;

  // This is a JS extension example with 2 functions and several parameters.
  // Please, read the "JavaScript Integration" wiki page at
  // https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration.md

  TempExtensionCode = "var myextension;\
                       if (!myextension)\
                         myextension = {};\
                       (function() {\
                         myextension.mouseover = function(a) {\
                           native function mouseover();\
                           mouseover(a);\
                         };\
                         myextension.sendresulttobrowser = function(b,c) {\
                           native function sendresulttobrowser();\
                           sendresulttobrowser(b,c);\
                         };\
                       })();";

  try {
    TempHandler = TTestExtensionHandler();
    CefRegisterExtension("myextension", TempExtensionCode, TempHandler);
  }
  __finally {
    TempHandler = NULL;
  }
}
class TWebKitInitRef : public TCppInterfacedObject<TOnWebKitInitializedEvent>
{
public:
    //TWebKitInitRef(){  Invoke();}
    INTFOBJECT_IMPL_IUNKNOWN(TInterfacedObject);
    void __fastcall Invoke() {    GlobalCEFApp_OnWebKitInitialized(); }
};

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    GlobalCEFApp = new TCefApplication();
    GlobalCEFApp->CheckCEFFiles =true;
    GlobalCEFApp->OnWebKitInitialized = _di_TOnWebKitInitializedEvent(new TWebKitInitRef());

    GlobalCEFApp->LogFile             = "debug.log";
    GlobalCEFApp->LogSeverity         = LOGSEVERITY_INFO;

    if(! GlobalCEFApp->StartMainProcess())Form1->Close();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1AfterCreated(TObject *Sender, ICefBrowser * const browser)

{
 PostMessage(Handle, CEF_AFTERCREATED, 0, 0);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1BeforeClose(TObject *Sender, ICefBrowser * const browser)

{
 FCanClose = true;
 PostMessage(Handle, WM_CLOSE, 0, 0);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1BeforeContextMenu(TObject *Sender, ICefBrowser * const browser,
          ICefFrame * const frame, ICefContextMenuParams * const params,
          ICefMenuModel * const model)
{
  // Adding some custom context menu entries
  model->AddSeparator();
  model->AddItem(MENU_ID_USER_FIRST + 1,  "Set mouseover event");
  model->AddItem(MENU_ID_USER_FIRST + 2,  "Visit DOM in JavaScript");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1BeforePopup(TObject *Sender, ICefBrowser * const browser,
          ICefFrame * const frame, const ustring targetUrl, const ustring targetFrameName,
          TCefWindowOpenDisposition targetDisposition, bool userGesture,
          const TCefPopupFeatures &popupFeatures, TCefWindowInfo &windowInfo,
          ICefClient *&client, TCefBrowserSettings &settings, bool &noJavascriptAccess,
          bool &Result)
{
  Result = targetDisposition==WOD_NEW_FOREGROUND_TAB || targetDisposition==WOD_NEW_BACKGROUND_TAB || targetDisposition==WOD_NEW_POPUP || targetDisposition==WOD_NEW_WINDOW ? true: false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1Close(TObject *Sender, ICefBrowser * const browser,
          bool Result)
{
 PostMessage(Handle, CEF_DESTROY, 0, 0);
 Result = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1ContextMenuCommand(TObject *Sender, ICefBrowser * const browser,
          ICefFrame * const frame, ICefContextMenuParams * const params,
          int commandId, DWORD eventFlags, bool Result)

{
  Result = false;

  // Here is the code executed for each custom context menu entry

  switch( commandId)
  {
    case (MENU_ID_USER_FIRST + 1) :
      if ((browser != NULL) && (browser->MainFrame != NULL))
        browser->MainFrame->ExecuteJavaScript("document.body.addEventListener('mouseover', function(evt){\
            function getpath(n){\
              var ret = '<' + n.nodeName + '>';\
              if (n.parentNode){return getpath(n.parentNode) + ret} else \
              return ret\
            };\
            myextension.mouseover(getpath(evt.target))})",
            // This is the call from JavaScript to the extension with DELPHI code in uTestExtensionHandler.pas
          "about:blank", 0);

    case (MENU_ID_USER_FIRST + 2) :
      if ((browser != NULL) && (browser->MainFrame != NULL))
        browser->MainFrame->ExecuteJavaScript("var testhtml = document.body.innerHTML;\
          myextension.sendresulttobrowser(testhtml, " + QuotedStr((AnsiString)"customname") + ");",  // This is the call from JavaScript to the extension with DELPHI code in uTestExtensionHandler.pas
          "about:blank", 0);
  }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1ProcessMessageReceived(TObject *Sender, ICefBrowser * const browser,
          TCefProcessId sourceProcess, ICefProcessMessage * const message,
          bool Result)
{
  if ((message == NULL) || (message->ArgumentList == NULL)) exit;

  // This function receives the messages with the JavaScript results

  // Many of these events are received in different threads and the VCL
  // doesn't like to create and destroy components in different threads.

  // It's safer to store the results and send a message to the main thread to show them.

  // The message names are defined in the extension or in JS code.

  if (message->Name == "mouseover")
    {
      StatusBar1->Panels->Items[0]->Text = message->ArgumentList->GetString(0);
      Result = true;
    }
  else
    if (message->Name == "customname")
      {
        StatusBar1->Panels->Items[0]->Text = message->ArgumentList->GetString(0);
        Result = true;
      }
  else Result = false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
   Timer1->Enabled = false;
  if(! (Chromium1->CreateBrowser(CEFWindowParent1, "")) && !(Chromium1->Initialized))
    Timer1->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::GoBtnClick(TObject *Sender)
{
  Chromium1->LoadURL(Edit1->Text);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
  //Action = FCanClose ? caFree : caNone;

  if (!FClosing)
    {
      FClosing = true;
      Visible  = false;
      Chromium1->CloseBrowser(true);
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
 FCanClose = false;
 FClosing  = false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormShow(TObject *Sender)
{
   StatusBar1->Panels->Items[0]->Text= "Initializing browser. Please wait...";

  // GlobalCEFApp.GlobalContextInitialized has to be TRUE before creating any browser
  // If it's not initialized yet, we use a simple timer to create the browser later.
  if (!Chromium1->CreateBrowser(CEFWindowParent1, "")) Timer1->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
 GlobalCEFApp->~TCefApplication();
 DestroyGlobalCEFApp();
}
//---------------------------------------------------------------------------


void __fastcall TForm1::WMMove(TMessage &Message)
{
  if (Chromium1 != NULL) Chromium1->NotifyMoveOrResizeStarted();
}

void __fastcall TForm1::WMMoving(TMessage &Message)
{
  if (Chromium1 != NULL) Chromium1->NotifyMoveOrResizeStarted();
}

void __fastcall TForm1::WMEnterMenuLoop(TMessage &Message)
{
  if ((Message.WParam == 0) && (GlobalCEFApp != NULL)) GlobalCEFApp->OsmodalLoop = true;
}

void __fastcall TForm1::WMExitMenuLoop(TMessage &Message)
{
  if ((Message.WParam == 0) && (GlobalCEFApp != NULL)) GlobalCEFApp->OsmodalLoop = false;
}

void __fastcall TForm1::BrowserCreatedMsg(TMessage &Message)
{
  StatusBar1->Panels->Items[0]->Text = "";
  CEFWindowParent1->UpdateSize();
  NavControlPnl->Enabled = true;
  GoBtn->Click();
}

void __fastcall TForm1::BrowserDestroyMsg(TMessage &Message)
{
  CEFWindowParent1->Free();
}

uTestExtensionHandler.h

// ************************************************************************
// ***************************** CEF4Delphi *******************************
// ************************************************************************
//
// CEF4Delphi is based on DCEF3 which uses CEF3 to embed a chromium-based
// browser in Delphi applications.
//
// The original license of DCEF3 still applies to CEF4Delphi.
//
// For more information about CEF4Delphi visit :
//         https://www.briskbard.com/index.php?lang=en&pageid=cef
//
#ifndef uTestExtensionHandlerH
#define uTestExtensionHandlerH


#include "uCEFRenderProcessHandler.hpp"
#include "uCEFBrowserProcessHandler.hpp"
#include "uCEFInterfaces.hpp"
#include "uCEFProcessMessage.hpp"

#include "uCEFv8Context.hpp"
#include "uCEFTypes.hpp"
#include "uCEFv8Handler.hpp";

class TTestExtensionHandler : public _di_ICefv8Handler//public ICefv8Handler
{
    protected:
      //bool __fastcall Execute(ustring name, ICefv8Value *obj, TCefv8ValueArray arguments, ICefv8Value *retval, ustring exception);
      bool __fastcall Execute(const Uceftypes::ustring name, const _di_ICefv8Value obj, const TCefv8ValueArray arguments, _di_ICefv8Value &retval, Uceftypes::ustring &exception);

};



//uses uCEFMiscFunctions, uCEFConstants, uJSExtension;

bool __fastcall  TTestExtensionHandler::Execute(const Uceftypes::ustring name, const _di_ICefv8Value obj, const TCefv8ValueArray arguments, _di_ICefv8Value &retval, Uceftypes::ustring &exception)
{
  ICefProcessMessage *msg;
  bool Result;
  if (name == "mouseover")
  {
      if ((arguments.Length > 0) && arguments[0]->IsString())
        {
          msg = TCefProcessMessageRef::New("mouseover");
          msg->ArgumentList->SetString(0, arguments[0]->GetStringValue());

          TCefv8ContextRef::Current()->Browser->SendProcessMessage(PID_BROWSER, msg);
        }

      Result = true;
  }
   else
    if (name == "sendresulttobrowser")
      {
        if ((arguments.Length > 1) && arguments[0]->IsString() && arguments[1]->IsString())
          {
            msg = TCefProcessMessageRef::New(arguments[1]->GetStringValue());
            msg->ArgumentList->SetString(0, arguments[0]->GetStringValue());

            TCefv8ContextRef::Current()->Browser->SendProcessMessage(PID_BROWSER, msg);
          }

        Result = true;
      }
     else
      Result = false;
 return Result;

}

#endif

我希望正确注册我的扩展,以便能够将消息和结果变量从网页返回到本机 c++ 函数。我在 delphi 演示中做了几乎相同的事情,但不能让它在 c++builder 中工作。我应该改变什么才能让它工作?

更新:

我尝试了第二种方法:

#include "uCEFv8Value.hpp"

class MyV8Handler : public _di_ICefv8Handler {
public:
MyV8Handler() {} ;

virtual bool __fastcall Execute(const Uceftypes::ustring name, const _di_ICefv8Value obj, const TCefv8ValueArray arguments, _di_ICefv8Value &retval, Uceftypes::ustring &exception)
{         
  if (name == "myfunc") { 
// Extract argument values
// Return my string value.
 retval = TCefv8ValueRef::NewString("My Value!");
return true;
}

// Function does not exist.
return false;
}

// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(MyV8Handler);
};

class TContextRef : public TCppInterfacedObject<TOnContextCreatedEvent>
{
public:
    //TContextRef(){  Invoke();}
    INTFOBJECT_IMPL_IUNKNOWN(TInterfacedObject);
    void __fastcall Invoke(const _di_ICefBrowser browser, const _di_ICefFrame frame, const _di_ICefv8Context context)
    {    
      // Retrieve the context's window object.
      _di_ICefv8Value object = context->GetGlobal();

        // Create an instance of my CefV8Handler object.
        ICefv8Handler *handler = dynamic_cast<ICefv8Handler*>(new MyV8Handler());

        // Create the "myfunc" function.
        _di_ICefv8Value func = TCefv8ValueRef::NewFunction("myfunc", handler);

        // Add the "myfunc" function to the "window" object.
        object->SetValueByKey("myfunc", func, V8_PROPERTY_ATTRIBUTE_NONE);
    }
};

...

GlobalCEFApp->OnContextCreated = _di_TOnContextCreatedEvent(new TContextRef());

Chromium1->ExecuteJavaScript("alert(window.myfunc('someString'));", "", 0);

我认为这GlobalCEFApp->OnContextCreated不是 Ivoked,就像GlobalCEFApp->OnWebKitInitialized. 也许对函数的引用没有正确传递?

标签: delphic++builderchromium-embeddedcef4delphi

解决方案


推荐阅读