salvadordf / CEF4Delphi

CEF4Delphi is an open source project to embed Chromium-based browsers in applications made with Delphi or Lazarus/FPC for Windows, Linux and MacOS.
https://www.briskbard.com/forum/
Other
1.21k stars 367 forks source link

TChromium.createBrowser() does never initiate a browser instance in DLL #210

Closed codebeat-nl closed 5 years ago

codebeat-nl commented 5 years ago

Hi, nice project you made. To try your software, it was a bumpy road, many problems, more on the issues later.

What I try to do: I have an old app that uses TEmbeddedWB and because Microsoft has dropped MSIE completely, many services don't support IE anymore and also IE cannot handle all of new web tech (fooling using another user-agent string doesn't help).

I tried the Delphi 7 version of CEF4Delphi but this doesn't seem to work at all, tried several demos, cannot run and show CPU exception window. So I tried Delphi 10.2 and this does work however only the VCL version, the FMX version shows some incomplete windows (I suppose draw issues).

Converted my app to Delphi 10.2 (real pain) but some components used in this app perform slower and jerky so I want to stay at the older IDE.

DLL approach Came up with the idea to create a DLL made in Delphi 10.2 and use it in the old app. However I tried many things (code below has changed allot already) but can't get it to work because TChromium.createBrowser()does never initiate a browser instance so nothing visible.

The first try was to reuse the minibrowser project in the DLL to see if it can work anyway. The Form is created however no browser. To figure out what was going on, I changed the Timer1Timer procedure in uMiniBrowser.pas:

procedure TMiniBrowserFrm.Timer1Timer(Sender: TObject);
begin
  Timer1.Tag:=Timer1.Tag+1;
  Timer1.Enabled := False;
  if not(Chromium1.CreateBrowser(CEFWindowParent1, '')) and not(Chromium1.Initialized) then
    begin
     StatusBar1.Panels[1].Text:='Trying to create browser instance....('+intToStr( Timer1.Tag )+')';
     Timer1.Enabled := True;
    end
  else StatusBar1.Panels[1].Text:='Browser instance created';
end;

It seems to be endless, never a valid result.

Then I tried to use only a TChromium object, same issue,TChromium.createBrowser()does never initiate a browser instance so again nothing visible.

Another issue I found is that GlobalCEFApp.StartMainProcess() never proceed (hang). Decided to make TCefApplication.CheckCEFLibrary and TCefApplication.LoadCEFlibrarypublic so I was able to load the library manually. This also doesn't help, code will proceed succesfully however no browser instance.

The code is very complex so don't know what else to do. Any ideas? Here is my code (project created in minibrowser demo director) as minibrwsr32.dproj:

library minibrwsr32;
{$I cef.inc}
{$R *.res}

uses
  xSysUtls,
  {$IFDEF DELPHI16_UP}
  System.Types,
  System.SysUtils,
  Vcl.Forms,
  WinApi.Windows,
  Vcl.Dialogs,
  {$ELSE}
  sysUtils,
  forms,
  windows,
  dialogs,
  {$ENDIF }
  uCEFApplication,
  uCEFChromium,
  uCEFTypes,
  uCEFConstants;
  //uMiniBrowser in 'uMiniBrowser.pas' {MiniBrowserFrm},
  //uPreferences in 'uPreferences.pas' {PreferencesFrm},
  //uSimpleTextViewer in 'uSimpleTextViewer.pas' {SimpleTextViewerFrm};

{$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE}

type
 PByteChar = ^byte; // Simple PChar type, bytes (ascii) instead of Ansi bloat

var
 browser : TChromium = nil;
 bIsInit : boolean = FALSE;

procedure initLibrary();
var
  sDir : string;

begin
  GlobalCEFApp := TCefApplication.Create();
  GlobalCEFApp.FrameworkDirPath := 'L:\cef3runtime\32\Release';
  GlobalCEFApp.ResourcesDirPath := 'L:\cef3runtime\32\Resources';
  GlobalCEFApp.LocalesDirPath   := 'L:\cef3runtime\32\Resources\locales';
  GlobalCEFApp.cache            := 'L:\cef3runtime\32\cache';
  //GlobalCEFApp.cookies        := 'L:\cef3runtime\32\cache\cookies'; <- property cookies does not exists!
  GlobalCEFApp.UserDataPath     := 'L:\cef3runtime\32\cache\User Data';
  GlobalCEFApp.DisableFeatures  := 'NetworkService';

  //bIsInit:= GlobalCEFApp.StartMainProcess(); // <- hang!
  bIsInit:=( GlobalCEFApp.CheckCEFLibrary() and GlobalCEFApp.LoadCEFlibrary() );
end;

function PByteCharToStr( p : PByteChar ) : string;
var
 i : longInt;

begin
 i:=1;
 setLength( Result, 255 );

 while( assigned( p )) and ( p^ <> 0 ) and ( i < 255 ) do
 begin
  Result[i]:=char(p^);
  inc( i );
  inc( p ); // increase pointer
 end;

 setLength( Result, i-1 );
end;

procedure destroy(); stdcall;
begin
 if( assigned( browser )) then
  begin
    try
      FreeAndNil( browser );
    except;
    end;

    browser:=nil;
  end;
end;

function create( AHandle : Hwnd; pAddress : PByteChar; x : longInt; y : longInt; width : longInt; height: longInt ) : boolean; stdcall;
begin
 Result:=(bIsInit) and (AHandle > 0 ) and ( NOT assigned( browser ));
 if( NOT Result ) then
  begin
   showMessage( 'Nope' );
   exit;
  end;

 try
  browser:=TChromium.create( application );
 except
  Result:=FALSE;
 end;

 if( Result ) then
  begin
   while( NOT browser.createBrowser( AHandle, Rect( x, y, x+width, y+height )) ) do
    begin
      clockDelay( 1000 );
      // Seems to be endless
    end;

 if( assigned( pAddress )) then //and ( strlen( pAddress ) > 0 ) then
    begin
      browser.loadURL( PByteCharToStr( pAddress  ) );
    end;
  end
 else destroy();
end;

exports
  create  name 'createWebBrowser',
  destroy name 'destroyWebBrowser';

begin
  //application.initialize();
  initLibrary();
  //application.run();
end.

Not a part of this issue, just to let you know: I found also other issues with the downloaded library files, some files are in the wrong directory (Resources vs Release) or it's location is wrong specified in the code. I had to copy some files to the code expected directory. The problem is about these files:

natives_blob.bin
snapshot_blob.bin
v8_context_snapshot.bin
icudtl.dat
salvadordf commented 5 years ago

Hi,

I'm sorry you had so many problems.

This could be a lengthy conversation because I would need to check several things to see what went wrong with the Delphi 7 tests.

If you don't mind, please register in our forum : https://www.briskbard.com/forum/

If you have any problem creating the forum account just send me a message and I'll activate your account manually.

We'll continue this conversation there.

codebeat-nl commented 5 years ago

Hai, don't want to create an account sorry. In the mean time I want you to let you know got a fantastic result in Lazarus! Wrote the library in Lazarus (and use it in older Delphi) and is working fine (very limited in functionality, pretty Beta at this moment and needs fine-tuning).

Cloned minibrowser however use only the browser and not the whole form because it caused some Win32 errors (Cannot find window) while debugging. In fact I don't need the whole form so doesn't matter, just to let you know.

Found another mistake in your code, the procedure CreateGlobalCEFApp() was located in uMiniBrowser.pas and should be located in uCEFApplication.pas instead so I moved it to be able to exclude the unit uMiniBrowser.pas (in this library it is not needed).

Here is my library code if you are interested:

minibrwsr32.lpr (clone of minibrowser.lpr, save as and after this modified to avoid errors):

library minibrwsr32;

{$MODE Delphi}

{$I cef.inc}

uses
  {$IFDEF DELPHI16_UP}
  Vcl.Forms,
  WinApi.Windows,
  {$ELSE}
  Forms, Windows, Sysutils, Dialogs, Classes,
  LCLIntf, LCLType, LMessages, Interfaces,
  {$ENDIF }
  uCEFApplication,
  uCEFChromium;
  //uMiniBrowser in 'uMiniBrowser.pas' {MiniBrowserFrm};
  //uPreferences in 'uPreferences.pas' {PreferencesFrm},
  //uSimpleTextViewer in 'uSimpleTextViewer.pas' {SimpleTextViewerFrm};

{.$R *.res}

{$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE}

var
 browser : TChromium = nil;
 bIsInit : boolean = FALSE;

function initLibrary() : boolean;
begin
 Result:=FALSE;

 if( bIsInit ) then
  exit;

 CreateGlobalCEFApp();

 GlobalCEFApp.FrameworkDirPath := 'L:\cef3runtime\32\Release';
 GlobalCEFApp.ResourcesDirPath := 'L:\cef3runtime\32\Resources';
 GlobalCEFApp.LocalesDirPath   := 'L:\cef3runtime\32\Resources\locales';
 GlobalCEFApp.cache            := 'L:\cef3runtime\32\cache';
 //GlobalCEFApp.cookies        := 'L:\cef3runtime\32\cache\cookies';
 GlobalCEFApp.UserDataPath     := 'L:\cef3runtime\32\cache\User Data';
 GlobalCEFApp.DisableFeatures  := 'NetworkService';

 bIsInit:=GlobalCEFApp.StartMainProcess();
 if( NOT bIsInit ) then
  begin
    DestroyGlobalCEFApp();
  end;

 Result:=bIsInit;
end;

function hasBrowser() : boolean;
begin
  Result:=(bIsInit) and ( assigned( browser ));
end;

procedure destroy(); stdcall;
begin
 if( assigned( browser )) then
  begin
    try
      FreeAndNil( browser );
    except;
    end;

    browser:=nil;
  end;

 DestroyGlobalCEFApp();
 bIsInit:=FALSE;
end;

function create( AHandle : Hwnd; pAddress : PChar; x : longInt; y : longInt; width : longInt; height: longInt ) : boolean; stdcall;
begin
 Result:=(AHandle > 0 ) and ( NOT assigned( browser )) and (initLibrary());
 if( NOT Result ) then
  begin
   showMessage( 'Nope' );
   exit;
  end;

 try
  browser:=TChromium.create( application );
 except
  Result:=FALSE;
 end;

 if( Result ) then
  begin
   while( NOT browser.createBrowser( AHandle, Rect( x, y, x+width, y+height )) ) do
    begin
      sleep( 1 );
      application.ProcessMessages();
    end;
  end
 else destroy();
end;

function getWindowHandle() : Hwnd; stdcall;
begin
 Result:=0;
 if( hasBrowser() ) then
  try
   Result:=browser.WindowHandle;
  except;
  end;
end;

function setAddress( pAddress : PChar ) : boolean; stdcall;
begin
 Result:=( hasBrowser()) and (assigned( pAddress ));

 if( Result ) then
  try
   browser.loadURL( string( pAddress  ) );
  except
   Result:=FALSE;
  end;
end;

function paste() : boolean; stdcall;
begin
 Result:=hasBrowser();
 if( Result ) then
  try
   browser.clipboardPaste();
  except
   Result:=FALSE;
  end;
end;

exports
  create          name 'createWebBrowser',
  destroy         name 'destroyWebBrowser',
  getWindowHandle name 'getWebBrowserWindowHandle',
  setAddress      name 'setWebBrowserAddress',
  paste           name 'pasteClipboardContent';

begin
 // Nothing to do here
end.

The attached image shows the result, on the right side you will see WhatsApp and on the left side my motion doorbell functionality, works great. If an activity is noticed, an image will be copied to the clipboard and pasted into the browser window (in WhatsApp) and some text and after this a ENTER key to send it.


doorbellcam

ThiagoPedro commented 4 years ago

Hai, don't want to create an account sorry. In the mean time I want you to let you know got a fantastic result in Lazarus! Wrote the library in Lazarus (and use it in older Delphi) and is working fine (very limited in functionality, pretty Beta at this moment and needs fine-tuning).

Cloned minibrowser however use only the browser and not the whole form because it caused some Win32 errors (Cannot find window) while debugging. In fact I don't need the whole form so doesn't matter, just to let you know.

Found another mistake in your code, the procedure CreateGlobalCEFApp() was located in uMiniBrowser.pas and should be located in uCEFApplication.pas instead so I moved it to be able to exclude the unit uMiniBrowser.pas (in this library it is not needed).

Here is my library code if you are interested:

minibrwsr32.lpr (clone of minibrowser.lpr, save as and after this modified to avoid errors):

library minibrwsr32;

{$MODE Delphi}

{$I cef.inc}

uses
  {$IFDEF DELPHI16_UP}
  Vcl.Forms,
  WinApi.Windows,
  {$ELSE}
  Forms, Windows, Sysutils, Dialogs, Classes,
  LCLIntf, LCLType, LMessages, Interfaces,
  {$ENDIF }
  uCEFApplication,
  uCEFChromium;
  //uMiniBrowser in 'uMiniBrowser.pas' {MiniBrowserFrm};
  //uPreferences in 'uPreferences.pas' {PreferencesFrm},
  //uSimpleTextViewer in 'uSimpleTextViewer.pas' {SimpleTextViewerFrm};

{.$R *.res}

{$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE}

var
 browser : TChromium = nil;
 bIsInit : boolean = FALSE;

function initLibrary() : boolean;
begin
 Result:=FALSE;

 if( bIsInit ) then
  exit;

 CreateGlobalCEFApp();

 GlobalCEFApp.FrameworkDirPath := 'L:\cef3runtime\32\Release';
 GlobalCEFApp.ResourcesDirPath := 'L:\cef3runtime\32\Resources';
 GlobalCEFApp.LocalesDirPath   := 'L:\cef3runtime\32\Resources\locales';
 GlobalCEFApp.cache            := 'L:\cef3runtime\32\cache';
 //GlobalCEFApp.cookies        := 'L:\cef3runtime\32\cache\cookies';
 GlobalCEFApp.UserDataPath     := 'L:\cef3runtime\32\cache\User Data';
 GlobalCEFApp.DisableFeatures  := 'NetworkService';

 bIsInit:=GlobalCEFApp.StartMainProcess();
 if( NOT bIsInit ) then
  begin
    DestroyGlobalCEFApp();
  end;

 Result:=bIsInit;
end;

function hasBrowser() : boolean;
begin
  Result:=(bIsInit) and ( assigned( browser ));
end;

procedure destroy(); stdcall;
begin
 if( assigned( browser )) then
  begin
    try
      FreeAndNil( browser );
    except;
    end;

    browser:=nil;
  end;

 DestroyGlobalCEFApp();
 bIsInit:=FALSE;
end;

function create( AHandle : Hwnd; pAddress : PChar; x : longInt; y : longInt; width : longInt; height: longInt ) : boolean; stdcall;
begin
 Result:=(AHandle > 0 ) and ( NOT assigned( browser )) and (initLibrary());
 if( NOT Result ) then
  begin
   showMessage( 'Nope' );
   exit;
  end;

 try
  browser:=TChromium.create( application );
 except
  Result:=FALSE;
 end;

 if( Result ) then
  begin
   while( NOT browser.createBrowser( AHandle, Rect( x, y, x+width, y+height )) ) do
    begin
      sleep( 1 );
      application.ProcessMessages();
    end;
  end
 else destroy();
end;

function getWindowHandle() : Hwnd; stdcall;
begin
 Result:=0;
 if( hasBrowser() ) then
  try
   Result:=browser.WindowHandle;
  except;
  end;
end;

function setAddress( pAddress : PChar ) : boolean; stdcall;
begin
 Result:=( hasBrowser()) and (assigned( pAddress ));

 if( Result ) then
  try
   browser.loadURL( string( pAddress  ) );
  except
   Result:=FALSE;
  end;
end;

function paste() : boolean; stdcall;
begin
 Result:=hasBrowser();
 if( Result ) then
  try
   browser.clipboardPaste();
  except
   Result:=FALSE;
  end;
end;

exports
  create          name 'createWebBrowser',
  destroy         name 'destroyWebBrowser',
  getWindowHandle name 'getWebBrowserWindowHandle',
  setAddress      name 'setWebBrowserAddress',
  paste           name 'pasteClipboardContent';

begin
 // Nothing to do here
end.

The attached image shows the result, on the right side you will see WhatsApp and on the left side my motion doorbell functionality, works great. If an activity is noticed, an image will be copied to the clipboard and pasted into the browser window (in WhatsApp) and some text and after this a ENTER key to send it.

doorbellcam

Can I use this code today? What is your contact, I would like to hire a service.