用惯了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接口,后面的操作请随意。
其次是填表,用过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代码不能生效。
最后,最后先到这里,下一次再说关于弹出窗口、右键菜单、摸拟鼠标点击等内容。
附:google论坛,delphi TChromium的问题讨论群组:http://groups.google.com/group/delphichromiumembedded
问题来源如下:有一个EXE和两个DLL,用到同一个公共单元:
unit UnitBpl;
interface
uses Classes;
type
TBaseClass= class of TBase;
TBase=class(TPersistent)
public
procedure show;virtual;
end;
implementation
{ TBase }
procedure TBase.show;
begin
end;
end.
在DLL_1中:
Type
TMy1=class(TBase)
public
procedure Show;override;
end;
implementation
procedure TMy1.Show;
begin
inherited;
ShowMessage('我是TMy1');
end;
initialization
RegisterClass(TMy1);
finalization
UnregisterClass(TMy1);
DLL_2中:
Type
TMy2=class(TBase)
public
procedure Show;override;
end;
implementation
procedure TMy2.Show;
begin
inherited;
ShowMessage('我是TMy2222');
end;
initialization
RegisterClass(TMy2);
finalization
UnregisterClass(TMy2);
EXE中:
procedure TFrmMain.btn1Click(Sender: TObject);
var
myObj:TBase;
BaseClass:TBaseClass;
begin
BaseClass:=TBaseClass(FindClass('TMy2'));//TBaseClass(FindClass('TMy1'));
myObj:=BaseClass.Create;
myObj.show;
end;
目的很明显了,就是想从dll_1和dll_2 中导出相同父类的不同子类,但是在loadLibary的时候,会提示如下图:

提示名为TBase的类已经存在!!!为什么呢?
RegisterClass(TMy2);
RegisterClass(TMy1);
没有注册TBase类啊?
如果注释掉其中一句RegisterClass,则哥运行,当然,
BaseClass:=TBaseClass(FindClass('TMy2'))
这一句就会出错了
因为其中有一个类未注册
期待高手为人解惑。。
关于idhttp取网页源码编码问题,在这里记录最后一次的解决方法,不想再纠结下去,再也伤不起了。(本文内容均在d2010,indy10.55的环境下产生及结束)。
网上只要搜索关于idhttp取网页乱码,idyttp编码问题的文章有一大堆,试了无数,都不满意,最后再这里记录一下个人解决方法。
在IDE中写如下代码,str:=http.get(xxxxx);然后Ctrl跟踪get,会在源码中得到如下一行,也是最关键的一行:
Result := ReadStringAsCharset(LResponse, Response.CharSet);
很明显,idhttp想给我们做点好事,直接返回解码后(能正常阅读、没有乱码)的内容,问题来了,当Response.CharSet有值时,返回的内容是完全正确的,当Response.CharSet没有值时,返回的结果值得商榷了。所以就有了网上常有人问的“为什么在UTF-8的情况下,返回内容是正常的,但是GBK的就不行了?”,经我测试,当网页是UTF-8时,Response.CharSet='utf-8',当网页是GBK时,Response.CharSet就不一定是GBK,很多时候都是空的,这时候,在使用上面的函数时,CharSet默认为西欧(可能吧)编码,得到的内容比UTF-8编码后的结果更难看懂。所以,我们要做的,就是给CharSet一个可用的、正确的值。
如果得到一个可用的、正确的CharSet值呢?首先,idhttp.Response.CharSet这个是首选的,如果这个值为空呢,就需要在返回的HTML中找了,如:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>执行 Application.Terminate 后, OnDestroy 中的代码还会执行 - 万一 - 博客园</title>
<link type="text/css" rel="stylesheet" href="http://www.cnblogs.com/css/common.css"/>
在上面代码中醒目的部分,charset=utf-8,我们要的就是这个,这部分是英文,所以不管什么编码,都能正常得到这部分内容,需要做的就是用正则把charset=后面的内容取出,再判断具体的编码,一般只需要考虑两种情况:utf-8和gbk,实现代码如下:
function Txxxxxx.getMethod(AURL: string): string;
var
LResponse: TMemoryStream;
LEncoding:TIdTextEncoding;
per:TPerlRegEx;
begin
LResponse := TMemoryStream.Create;
try
try
http.Get(AURL, LResponse);
finally
http.Disconnect;
end;
LResponse.Position := 0;
if http.Response.CharSet<>'' then
begin
LEncoding := CharsetToEncoding(http.Response.CharSet);
if AnsiSameText(http.Response.CharSet,'utf-8') then
isutf8:=true
else isutf8:=False;
end
else
begin
LEncoding := TEncoding.Default;
Result := ReadStringFromStream(LResponse, -1, LEncoding);
per:=TPerlRegEx.Create(nil);
per.Options:=[preCaseLess];
per.Subject:=Result;
per.RegEx:='<meta[\S\s]*?charset=(.*)>';
if per.Match then
begin
if AnsiContainsText(per.MatchedExpression,'utf-8') then
begin
isutf8:=true;
LEncoding := CharsetToEncoding('utf-8');
end
else
begin
isutf8:=False;
LEncoding := CharsetToEncoding('gbk');
end;
end;
FreeAndNil(per);
end;
LResponse.Position := 0;
Result := ReadStringFromStream(LResponse, -1, LEncoding);
finally
FreeAndNil(LResponse);
end;
end;
我承认,我欺骗了你,因为在上面的代码中,我没有给CharSet找一个正确的值,而是用到了IdGlobal, IdGlobalProtocols中的两个函数,直接返回一个理想的内容,就像你看到的,首先,需要一个http:IDHTTP和isutf8:boolean,然后直接调用 str:=GetMethod(xxxxxx)就可以得到内容了。
通过如上方法,能解决80%的网页编码问题,为什么不是100%呢,因为还有一部分的网页,通过idhttp.get,你会发现Response.CharSet='',并且在HTML中没有设置编码的meta标签,如果这种情况,请不要纠结,略过它吧。。。。
注明:本文只适合菜鸟阅读,也欢迎高手拍砖。
最近做一个软件,用webbrowser做软件界面,通过js的window.external调用delphi的函数实现数据库操作等功能,在需要返回值的时候,遇到问题了,来源如下图:

在接口中加入一方法,传入参数为BSTR(wideString)类型,希望返回VARIANT(OLEVariant),自动生成的函数结构如下:
在***_TLB.pas中:
Itest = interface(IDispatch)
['{D9941302-C827-4517-ADA0-003D176E9E9A}']
function Method1(const a: WideString): OleVariant; stdcall;
end;
在接口实现单元中:
type
Ttest = class(TAutoObject, Itest)
protected
function Method1(const fd: WideString): OleVariant; stdcall;
end;
implementation
uses ComServ;
function Ttest.Method1(const fd: WideString): OleVariant;
begin
end;
运行后,在网页中调用:
var a=window.external.Method1('fdsa');
alert(a);
执行时,报错;
纠结很久,对函数重新申明如下图:

在***_TLB.pas中:
Itest = interface(IDispatch)
['{D9941302-C827-4517-ADA0-003D176E9E9A}']
function Method1(const a: WideString): OleVariant; safecall;
end;
在接口实现单元中:
Ttest = class(TAutoObject, Itest)
protected
function Method1(const a: WideString): OleVariant; safecall;
end;
implementation
uses ComServ;
function Ttest.Method1(const a: WideString): OleVariant;
begin
end;
运行后可正常显示返回内容,对于我这种菜鸟来说,唯一的区别就是safecall与stdcall, 由于没有程序基础,用delphi全靠自学,不太明白这两个东西的区别,暂时不管他,只要程序能工作就行,在这里做个记录,以免再犯错。