Delphi中Chrome Chromium、Cef3学习笔记(一)
2015年06月25日 13:25:02
阅读数:6421
官方下载地址:https://cefbuilds.com/
尊重作者原创,转载请注明出处,原文地址:
http://blog.csdn.net/xtfnpgy/article/details/46635225
CEF简介:
嵌入式Chromium框架(简称CEF) 是一个由Marshall Greenblatt在2008建立的开源项目,它主要目的是开发一个基于Google Chromium的Webbrowser控件。CEF支持一系列的编程语言和操作系统,并且能很容易地整合到新的或已有的工程中去。
它的设计思想政治就是易用且兼顾性能。CEF基本的框架包含C/C++程序接口,通过本地库的接口来实现,而这个库则会隔离宿主程序和Chromium&Webkit的操作细节。它在浏览器控件和宿主程序之间提供紧密的整合,它支持用户插件,协议,javascript对象以及javascript扩展,宿主程序可以随意地控件资源下载,导航,下下文内容和打印等,并且可以跟Google Chrome浏览器一起,支持高性能和Html5 技术,
这段代码你可以在[test]文件夹下面的cefclient工程里面看到。
文件结构:
include -- 这个文件夹里面放CEF客户应用程序所需的头文件
libcef -- 此文件夹存放CEF的静态库
libcef_dll -- 此文件夹CEF的动态拉链库
tests -- 此文件夹存放测试的例子
cefclient -- 一个简单的客户程序
unittests -- CEF界面单元测试
浏览器事件:
应用程序通过调用CefBrowser和CefFrame的方法来处理浏览器控件事件:
a.Back, Forward, Reload and Stop Load。控件浏览器的导航
b.Undo, Redo, Cut, Copy, Paste, Delete, Select All.控件目标框架的选取
c.Print。打印目标框架
d.Get Source。以字符串的形式来获取目标框架的HTML源码
e.View Source. 用缓存文件来保存目的框架的HTML源码,并且用系统默认的文本查看器打开
f.Load URL.加载特定的URL到目标框架
g.Load String. 加载一个特定的字符串到目标框架,通过一个随意指定的虚拟URL
h.Load Stream. 加载一个特定的二进制文件到目标框架,通过一个随意指定的虚拟URL
i.Load Request, 加载一个特定的请求到目标框架
j.Execute JavaScript: 在目标框架里面执行一个特定的Javscript命令
k. Zoom。 缩放特定框架的网页内容
插入网景风格插件:
CEF支持插入网景风格的插件,插入插件的动作跟正常插入基本动态链接库的插件一样,但是做为一个单独的Dll,必须通过容器来创建,并且要用CefRegisterPlugin()这个函数来注册到系统里面去。你要调用这个函数的话,你就得包含cef_nplugin.h这个头文件。
JavaScript扩展:
CEF支持能连接本地程序源代码的Javascript扩展,演示程序你可以去看cef_v8.h进而的CefRegisterExtension()方法,还有CEFClient下面的演示例子,你如果想得到更多的信息,可以点下面的链接: JavaScriptIntegration。
用户计划:
CEF支持注册和处理像myscheme://mydomain一样的用户计划,你可去查看cef_scheme.h里面的CefRegisterScheme()和Scheme Handler测试单元,得到详细的说明。
框架总览:
所有的文件前缀都是Cef
框架的建立和销毁:
CEF的UI消息寄宿在一个框架建立的单线程中,用户只负责用CefInitialize()和CefShutdown()来建立和销毁这个线程,但是你如果设置了CefSettings.multi_threaded_message_loop, 这个UI消息循环也可以运行在一个单独的线程里面。
引用计数:
所有的框架数都从CefBase这个基类继承而来,并且所有的实例指针都用CefRefPtr智能指针来管理,可以用AddRef()和Release()函数来自动处理引用计数。
平台无关:
CEF框架被设计成平台无关的,现在它支持Windows,Mac os-x和Linux,为了无缝地支持多种平台,框架定义了一系列的平台无关的接口和类型定义。
框架接口:
CefBrowser是主要的浏览器窗口类,可以用静态的函数CreateBrowser() 和CreateBrowserSync() 来创建一个新的浏览器窗口。
CefFrame 代表一个浏览器窗口的框架,每个浏览器窗口有一个顶层的主框架,而这个主框架可以用GetMainFrame() 方法得到。
CefClient是主浏览器窗口的代表接口,这个接口做为参数传递给CreateBrowser()
CefRequest 代表URL,方法,发送数据和头文件等这样的请求。
CefSchemeHandleFactory 类是被用来处理像myscheme://mydomain类似客户计划的请求
CefReadHandler和CefWriteHandle是一个读写数据的简单接口。
CefV8Handler,CefV8Value和CefV8Context是被用来创建和访问JavaScript对象。
Cef3下载下来的文件结构:
bin目录是主要存放DLL,你需要把bin目录下所有的文件,复制到你的Exe的当前文件夹,否则就会报错!code 126
demo 为例子
packages为压缩包
src主要源代码部分,需要在delphi的library添加这个src的路径
安装完成之后,新建一个Demo1,拖一个chromium控件出来并命名为chrm1,并把bin目录下所有文件,复制到你的程序的当前文件夹;
再拖一个button,双击加入代码:
chrm1.Browser.MainFrame.LoadUrl('about:blank'); //第一次必须先加载空白页,才能浏览其他网页
chrm1.Browser.MainFrame.LoadUrl('你要浏览的网址');
至此第一个例子完成!
安装完成之后,新建一个Demo1,拖一个chromium控件出来并命名为chrm1,并把bin目录下所有文件,复制到你的程序的当前文件夹;
再拖一个button,双击加入代码:
chrm1.Browser.MainFrame.LoadUrl('about:blank'); //第一次必须先加载空白页,才能浏览其他网页
chrm1.Browser.MainFrame.LoadUrl('你要浏览的网址');
至此第一个例子完成!
安装完成之后,新建一个Demo1,拖一个chromium控件出来并命名为chrm1,并把bin目录下所有文件,复制到你的程序的当前文件夹;
再拖一个button,双击加入代码:
chrm1.Browser.MainFrame.LoadUrl('about:blank'); //第一次必须先加载空白页,才能浏览其他网页
chrm1.Browser.MainFrame.LoadUrl('你要浏览的网址');
尊重作者原创,转载请注明出处,原文地址:
http://blog.csdn.net/xtfnpgy/article/details/46635225
尊重作者原创,转载请注明出处,原文地址:
http://blog.csdn.net/xtfnpgy/article/details/46635225
Delphi中Chrome Chromium、Cef3学习笔记(二)
2015年06月25日 14:09:54
阅读数:3422
尊重作者原创,转载请注明出处,原文地址:
http://blog.csdn.net/xtfnpgy/article/details/46635739
用惯了EmbeddedWB,不想换,但是IE内核一直存在内存泄漏问题,没办法,只有寻找替代品了。
要把用习惯的EmbeddedWB换成完全不一样的TChromium,有点挑战,特别是在资料不多,英语没过三级的情况下。未来趋势是这样,只有慢慢啃了。
首先,想到的是跨域,如果不能跨域,就没办法替代手上的成品。TChromium的跨域比想像中的简单,直接通过chrm.Browser.GetFrameNames(list);//list:tstringList;取得各个IFrame/Frame的名称(所谓名称不是指name属性,只是一个标识,在有name时,返回name,没有name时,ID 也行,都没有时,自动生成一个唯一名称),如:
ff //这个是一个框架的ID
<!--framePath //ff/<!--frame0-->-->
<!--framePath //ff/<!--frame1-->-->
<!--framePath //ff/<!--frame2-->--> //后面三个是自动生成的唯一名称
获取指定Frame时,通过chrm1.Browser.Frame['frame 的名称'],取得ICefFrame接口,后面的操作请随意。
常用方法:
获取主框架 chrm1.browser.MainFrame
获取源代码 s := chrm1.browser.MainFrame.Source;
其次是填表,用过google浏览器的都知道,其填表功能实在强大,在webbrowser时代,一般都是获取表单元素的各种接口,然后设置其value、checked等属性,而在TChromium中,一切皆js,把想做的事都让js去执行吧,所以,用TChromium,js功力深厚的会轻松很多了。比如:
strTemp := 'document.forms[0].inmembername.value="User_Name";'; JavaExec(strTemp); strTemp := 'document.forms[0].inpassword.value="Password";'; JavaExec(strTemp); strTemp := 'document.forms[0].submit.click();'; chrm.browser.Frame['ff'].ExecuteJavaScript(str,'about:blank',0);; //想在哪一层frame执行,就调用哪一层
更神奇的是,还可以通过以下方法加载属于自己的jquery到浏览器中:
procedure TForm3.chrm1LoadEnd(Sender: TCustomChromium; const browser: ICefBrowser; const frame: ICefFrame; httpStatusCode: Integer; out Result: TCefRetval); var str:string; begin str:='var oHead = document.getElementsByTagName(''HEAD'').item(0);'#13#10+ 'var oScript = document.createElement( "script" );'#13#10+ 'oScript.language = "javascript";'#13#10+ 'oScript.type = "text/javascript";'#13#10+ 'oScript.id = "sid";'#13#10+ 'oScript.defer = true;'#13#10+ 'oScript.src = "jquery.js";'#13#10+ 'oHead.appendChild( oScript );'#13#10+ 'alert("8")' ; Frame.ExecuteJavaScript(str,'about:blank',0); end;
直接连接本地的js!!!这段代码最后写在LoadEnd中,每加载完成一个frame,就让这个frame加载自己的jquery,如果在需要使用的时候再去加载jquery,jquery加载是需要时间的,可能会导致紧接着的js代码不能生效。
注意事项:Delphi执行JS的代码,必须注意大小写,特别是自己写的JS函数,因为JS是区分大小写的!~
下一篇主要讲解DELPHI与JS交互的更深层次方面。
Delphi中Chrome Chromium、Cef3学习笔记(三)
2015年06月25日 17:37:50
阅读数:4084
Delphi与JS的交互问题:
尊重原创,转载请注明出处,原文地址:
http://blog.csdn.net/xtfnpgy/article/details/46635871
一、执行简单的JS
上一篇已经讲过:
chrm1.browser.MainFrame.ExecuteJavaScript('alert("abc");','about:blank',0);
chrm1.browser.MainFrame.ExecuteJavaScript('function aaa(){alert("abc");}aaa();','about:blank',0);
二、注入自定义的JS
首先必须在网页加载完成之后,才可以执行,不能为空页面!
str_js := 'var oScript = document.createElement( "script" );oScript.id = "sid";alert(oScript.id);';
chrm1.browser.MainFrame.ExecuteJavaScript(str_js,'about:blank',0);
三、delphi与JS交互(通过ceflib实现)
Cef3的Demo里面,有一个cefclient的例子,可以看下,关键代码:
TExtension = class(TCefv8HandlerOwn)
private
FTestParam: ustring;
protected
function Execute(const name: ustring; const obj: ICefv8Value;
const arguments: TCefv8ValueArray; var retval: ICefv8Value;
var exception: ustring): Boolean; override; //重写Execute事件
end;
//通过不同的name,执行不同的动作
function TExtension.Execute(const name: ustring; const obj: ICefv8Value;
const arguments: TCefv8ValueArray; var retval: ICefv8Value;
var exception: ustring): Boolean;
begin
if(name = 'SetTestParam') then
begin
// Handle the SetTestParam native function by saving the string argument
// into the local member.
if (Length(arguments) <> 1) or (not arguments[0].IsString) then
begin
Result := false;
Exit;
end;
FTestParam := arguments[0].GetStringValue; //多个参数arguments[i].GetStringValue 依次类推
Result := true;
end
else if(name = 'GetTestParam') then
begin
// Handle the GetTestParam native function by returning the local member
// value.
retval := TCefv8ValueRef.CreateString(Ftestparam);
Result := true;
end
else if (name = 'GetTestObject') then
begin
// Handle the GetTestObject native function by creating and returning a
// new V8 object.
retval := TCefv8ValueRef.CreateObject(nil);
// Add a string parameter to the new V8 object.
retval.SetValueByKey('param', TCefv8ValueRef.CreateString(
'Retrieving a parameter on a native object succeeded.'));
// Add a function to the new V8 object.
retval.SetValueByKey('GetMessage',
TCefv8ValueRef.CreateFunction('GetMessage', Self));
Result := true;
end
else if(name = 'GetMessage') then
begin
// Handle the GetMessage object function by returning a string.
retval := TCefv8ValueRef.CreateString(
'Calling a function on a native object succeeded.');
Result := true;
end else
Result := false;
end;
{ 注册JS扩展 }
procedure RegisterExtension;
var
Code: string;
begin
Code :=
'var cef;if(!cef)cef={};if(!cef.taobao)cef.taobao={};(function(){cef.taobao.test_object=function(){native function GetTestObject();return GetTestObject();};})();';
if Code <> '' then
try
CefRegisterExtension('example/v8',Code,TExtension.Create as ICefv8Handler);
except
end;
end;
在FormCreate中注册类:
RegisterExtension;
调用实例:
chrm1.browser.MainFrame.ExecuteJavaScript('alert("abc");','about:blank',0);
str_temp := 'function aaa(){var CefObj = new cef.taobao.test_object;'+ //先new一个实例对象
'CefObj.SetTestParam("abc");var b=CefObj.GetTestParam();alert(b);'+ //设置、获取、输出参数
'}aaa();';
chrm1.Browser.MainFrame.ExecuteJavaScript(str_temp,'about:blank',0);
如果要实现多个参数,可以定义个数组替代FTestParam
private
FTestParam : ustring;
ArrayParam : array of ustring; //多个参数
关键代码:
if(name = 'SetTestParam') then
begin
// **************原来一个参数的情况**********
// if (Length(arguments) <> 1) or (not arguments[0].IsString) then
// begin
// Result := false;
// Exit;
// end;
// FTestParam := arguments[0].GetStringValue;
// **************原来一个参数的情况**********
SetLength(ArrayParam,Length(arguments));
for i := 0 to Length(arguments) - 1 do
begin
ArrayParam[i] := arguments[i].GetStringValue;
if s='' then
s := ArrayParam[i]
else
s := s+','+ArrayParam[i];
end;
FTestParam := s;
end
else if(name = 'GetTestParam') then
begin
// **************原来一个参数的情况**********
// retval := TCefv8ValueRef.CreateString(FTestParam);
// **************原来一个参数的情况**********
for i := 0 to Length(ArrayParam) - 1 do
begin
if s='' then
s := ArrayParam[i]
else
s := s+','+ArrayParam[i];
end;
retval := TCefv8ValueRef.CreateString(FTestParam);
end;
这样调用GetTestParam返回的还是一个字符串,多个参数用逗号隔开了而已,可以对其再进行改写即可;
尊重原创,转载请注明出处,原文地址:
http://blog.csdn.net/xtfnpgy/article/details/46635871
Delphi中Chrome Chromium、Cef3学习笔记(四)
2015年09月01日 11:11:02
阅读数:4189
一、遍历网页元素并点击JS:
转载请注明出处,原文地址:
http://blog.csdn.net/xtfnpgy/article/details/48155323
1.遍历所有元素
var eles=document.all;
for(var i=0;i<eles.length;i++){var a;
var d=eles[i];
var s=eles[i].getAttribute("class");
if(s!=null){
if((s=='img')||(s=='J_ItemPic img')||(s=='productImg')){
a=eles[i];
a.click();break;
}}}
2.遍历所有A标签
下面代码为找到淘宝宝贝页面,成交记录元素的代码:
for(var i=0;i<document.getElementsByTagName("A").length;i++){var a;
var d=document.getElementsByTagName("A")[i];
var s=document.getElementsByTagName("A")[i].innerHTML;
if(s!=null){
if(s.indexOf("成交记录")>-1){
a=document.getElementsByTagName("A")[i];alert(a.innerHTML);
ClickElement(a); //由于chromium控件元素不支持click事件,所以需要自己重新绑定事件
break;
}}}
//-----------------点击元素的代码------------------------------
function ClickElement(AObj) {
var e = document.createEvent("MouseEvent");
e.initEvent("click", false, false);
AObj.dispatchEvent(e);
}
二、根据ID或Name获取元素
1.根据ID
var a=document.getElementById("元素id");
2.根据Name
var b=document.getElementsByName("元素name").item(0);
b.setAttribute("value","输入值");
3.根据TagName
var c=document.getElementsByTagName("INPUT")[0];
var d=document.getElementsByTagName("INPUT").item(0);
三、如何在Delphi中直接操作Document接口
Button1.OnClick:
chrm1.Browser.MainFrame.VisitDomProc(ChrmDomProc);
procedure ChrmDomProc(const doc : ICefDomDocument); //其他接口有待自己研究
var
s : string;
begin
doc.GetElementById('txtUsername').SetElementAttribute('Value','abc');
s := doc.GetElementById('txtUsername').GetValue;
showmessage(s);
end;
简单实例:
var ele;
ele=document.getElementById('ID');
ele=document.getElementsByName('Name').iteme(0);
ele=docuemnt.getElementsByTagName('TAG');
ele.click();
var e=document.createEvent("MouseEvent");
e.initEvent("Click",false,false);
ele.dispatchEvent(e);
var c=document.getElementsByTagName("INPUT").item(0);
转载请注明出处,原文地址:
http://blog.csdn.net/xtfnpgy/article/details/48155323
个人分类: Delphi中Chrome Chromium学习笔记
Delphi中Chrome Chromium、Cef3学习笔记(五)
2015年09月16日 09:52:33
阅读数:4408
一、模拟移动鼠标
转载请注明出处,原文地址:
http://blog.csdn.net/xtfnpgy/article/details/48489489
// SetCursorPos(StrToInt(Edit1.Text),StrToInt(Edit2.Text)); //相对屏幕位置
SetCursorPos(Self.Left+chrm1.Left+strtoint(Edit1.Text),Self.Top+chrm1.Top+strtoint(Edit2.Text)); //相对Chrm位置
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);//模拟按下鼠标左键。
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);//模拟放开鼠标左键。
keybd_event(VK_SHIFT,MapVirtualKey(VK_SHIFT,0),0,0); //按下SHIFT键。
keybd_event(0x52,MapVirtualKey(0x52,0),0,0);//按下R键。
keybd_event(0x52,MapVirtualKey(0x52,0),KEYEVENTF_KEYUP,0);//放开R键。
keybd_event(VK_SHIFT,MapVirtualKey(VK_SHIFT,0),KEYEVENTF_KEYUP,0);//放开SHIFT键。
二、Chromiume控件指定坐标点击
SendMessage(GetWindow(GetWindow(form1.chrm1.Handle, GW_CHILD), GW_CHILD),WM_LBUTTONDOWN,MK_LBUTTON, MAKELONG(StrToIntDef(Edit1.Text,100),StrToIntDef(Edit2.Text,100)));
SendMessage(GetWindow(GetWindow(form1.chrm1.Handle, GW_CHILD), GW_CHILD),WM_LBUTTONUP,MK_LBUTTON, MAKELONG(StrToIntDef(Edit1.Text,100),StrToIntDef(Edit2.Text,100)));
MK_LBUTTON可以改为0,MAKELONG可以改为MAKELParam
三、JS控制滚动条的位置:
window.scrollTo(x,y);
竖向滚动条置顶(window.scrollTo(0,0);
竖向滚动条置底 window.scrollTo(0,document.body.scrollHeight)
或: window.scrollByo(0,document.body.scrollHeight)
四、设置代理ip
ceflib.pas单元:
uses iniFiles;
procedure TInternalApp.OnBeforeCommandLineProcessing(const processType: ustring;
const commandLine: ICefCommandLine);
var
ini : TIniFile;
str_ip : string;
begin
ini := TIniFile.Create(StringReplace(ParamStr(0),'.exe','.ini',[]));
str_ip := ini.ReadString('default','Proxy','');
ini.Free;
commandLine.AppendSwitchWithValue('proxy-server',str_ip);
if Assigned(CefOnBeforeCommandLineProcessing) then
CefOnBeforeCommandLineProcessing(processType, commandLine);
end;
[default]
Proxy=218.189.26.20:8080
Proxy1=218.207.212.79:80
Proxy2=120.203.159.18:8118
转载请注明出处,原文地址:
http://blog.csdn.net/xtfnpgy/article/details/48489489
Delphi中Chrome Chromium、Cef3学习笔记(六)
2017年05月12日 09:35:17
阅读数:1335
一.CEF加载网页时空白
chrm1.Load(‘你的网址’);
出现空白,跟踪进去:
frm := FBrowser.MainFrame; //此时为nil ,可改为:
frm := FBrowser.GetMainFrame;
其他地方同上;
后续待更新。。
二、CEF程序退出时报错
{$R *.res}
procedure RegisterSchemes(const registrar: ICefSchemeRegistrar);
begin
registrar.AddCustomScheme('local', True, True, False);
end;
begin
// CefCache := getEnvironmentVariable('USERPROFILE')+'\GetTBData\cookies\';
CefCache := 'cache';
CefOnRegisterCustomSchemes := RegisterSchemes;
CefSingleProcess := False;
if not CefLoadLibDefault then
Exit;
// CefUserAgent := 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;';
// CefUserAgent := 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11';
Application.Initialize;
Application.CreateForm(TuMain, uMain);
Application.Run;
end.
三、Cef3加载flash网页方法:
修改ceflib.pas:
procedure TInternalApp.OnBeforeCommandLineProcessing(const processType: ustring;
const commandLine: ICefCommandLine);
begin
CommandLine.AppendSwitch('ppapi-out-of-process');
CommandLine.AppendSwitchWithValue('ppapi-flash-version', '22.0.0.168');
CommandLine.AppendSwitchWithValue('ppapi-flash-path', 'Plugins\\pepflashplayer.dll');
if Assigned(CefOnBeforeCommandLineProcessing) then
CefOnBeforeCommandLineProcessing(processType, commandLine);
end;
网上下载pepflashplayer.dll放入..\Plugins\\目录下面。
四、cef3启动加载flash网页时Dos窗口闪一下的问题
网上的解决方法,参考此文。
http://blog.csdn.net/zx2356/article/details/51514403
按照该文及提供的下载挂钩createProcessA,在xp上无效。
2.解决方法
有可能createprocessw也得挂钩。但懒得修改了。因此决定改为反编译flash插件,看看显示命令行窗口的逻辑。
反编译flash插件。发现显示cmd的逻辑是,读取环境变量comspec(cmd.exe的全路径),读取到就执行它,读取不到执行cmd.exe.
用winhex修改flash插件,搜索comspec为soms1ec,修改cmd.exe为cm1.exe.
重新测试,成功。
修改后插件下载链接
http://download.csdn.net/detail/qsy2000/9768385
转载请注明出处,原文地址:
http://blog.csdn.net/xtfnpgy/article/details/71703317