ActiveX技术虽然是一项古老的技术,但是却有着广泛的应用,支付宝的密码输入控件,各大银行的密码输入控件,网页聊天室中的截屏功能,网页播放器中的p2p播放...甚至Flash,Silverlight等等,在IE中都表现为ActiveX。虽然C#也能开发"用于网页的com应用",能达到类似ActiveX的效果,但是有一个要命的问题是必须得安装几百M的.net Framework框架,如果仅仅为了安全的输入一个密码,而要用户下载几百M的安装程序,这是很多人不能接受的,Delphi做为win32下的原生开发工具,能很好的支持微软各种"古老"的经典技术。(再做点小广告:delphi的kyrix版本还能编译跨平台的应用哦!)
ok,开工吧:
开发工具:推荐用delphi 2010(d7也可以,不过添加属性,方法等过程要手动,稍微麻烦点)
1.启用delphi2010-->File->New->Other-->Active Library
2.项目命名为MyActiveX
3.File-->Save All 全部保存
实际上这样就能编译了,不过只是空的dll
4.File-->New-->Other-->Active Form
改名为MyForm
将对应的单元文件,保存为UMyForm.pas
5.打开MyAcitveX.ridl文件,切换到design视图,选中IMyForm接口,右击New-->Property 添加一个属性Msg
将Msg属性的Type改为BSTR 即WideString类型
完了之后,点击工具栏中的Refresh Implementation(即上图中工具栏中圈起来的部分)--这一步很重要,点击之后,它将自动生成属性Msg对应的声明和实现代码模板
6.打开UMyForm.pas--即ActiveForm对应的单元,找到Set_Msg以及Get_Msg的实现部分,补充代码如下:
function TMyForm.Get_Msg: WideString; begin result:=_msg; end; procedure TMyForm.Set_Msg(const Value: WideString); begin _msg := value; end;
当然TMyForm的private部分,得先加一个私有成员
type TMyForm = class(TActiveForm, IMyForm) private { Private declarations } _msg:WideString; ...
这样我们就为即将生成的ActiveX控件,添加了一个字符串类型的属性Msg,下面来测试一下:
7.编译项目,会生成一个MyActiveX.ocx,在运行栏里输入
regsvr32 C:\Users\jimmy.yang\Desktop\Delphi_activex\MyActiveX\MyActiveX.ocx
注:这里ocx的路径,请各位根据自己的实际路径修改
这样就完成了ocx的注册。
8.放到html里测试一下:
<OBJECT ID='x' name='x' CLASSID='CLSID:52D17094-0687-4A2F-B2DB-30F3189AC659' align=center hspace=0 vspace=0 ></OBJECT> <script type='text/JavaScript'> var x = document.getElementById("x"); alert(x.Msg); </script>
关于CLSID在哪里查看,打开:MyActiveX_TLB.pas文件,定位到下面这里:
const // TypeLibrary Major and minor versions MyActiveXMajorVersion = 1; MyActiveXMinorVersion = 0; LIBID_MyActiveX: TGUID = '{49138437-8265-4B1A-9EAE-D0F615D68464}'; IID_IMyForm: TGUID = '{54A20855-29A3-4C92-85DE-A419DA457C7A}'; DIID_IMyFormEvents: TGUID = '{60BBC967-E1E6-4E98-BAE5-776BFD06E9CC}'; CLASS_MyForm: TGUID = '{52D17094-0687-4A2F-B2DB-30F3189AC659}';
其中 CLASS_MyForm: TGUID对应的就是ClassID
运行后,除了弹出一个空白的警告框,暂时看不到其它:)(可不就是这样么?Msg属性没给任何初始值,当然是空字符串,所以弹出一个空的警告框是正常的)
9.我们再来添加一些控件和方法,以验证刚才设置的属性确实有效
在MyForm上添加一个文件框,一个按钮
按钮的事件如下:
procedure TMyForm.Button1Click(Sender: TObject); begin _msg:= self.Edit1.Text; end;
即把文本框的值赋给属性Msg
再继续定位到Set_Msg,略做修改
procedure TMyForm.Set_Msg(const Value: WideString); begin _msg := value; self.Edit1.Text := _msg; end;
即设置Msg属性时,同时也把值显示在文本框里,以便等会儿我们好测试在js中给activeX属性赋值的效果
ok了,再来测试一下,编译一下,如果通不过,请先运行
regsvr32 C:\Users\jimmy.yang\Desktop\Delphi_activex\MyActiveX\MyActiveX.ocx /u
将刚才注册的ocx反注册,同时关掉浏览器,不然该ocx文件一直被占用,无法更新.
修改一下html的代码:
<OBJECT ID='x' name='x' CLASSID='CLSID:52D17094-0687-4A2F-B2DB-30F3189AC659' align=center hspace=0 vspace=0 ></OBJECT> <hr /> <input type='button' value='显示Msg属性的值' onclick='ShowMsg()'/> <input type='button' value='设置Msg属性的值' onclick='SetMsg()'/> <script type='text/JavaScript'> var x = document.getElementById("x"); var ShowMsg = function(){ alert(x.Msg); } var SetMsg = function(){ x.Msg = 'js传过来的值'; } </script>
运行效果:
10.添加Method
我们已经知道了如何给ActiveX添加对外公开的属性,但是光有属性显然不够,我们再添加一个Method,参考第5步中的截图,选择new-->Method,添加
一个方法,命名为ShowMsg,Return参数项用默认值HRESULT,然后Parameters添加一个参数,如下图:
同样不要忘记了点击工具栏中的更新按钮,再打开UMyForm.pas,会发现自动添加了一个过程的定义:
procedure ShowMsg(const p: WideString); safecall;
转到它的实现部分,写几行测试代码:
procedure TMyForm.ShowMsg(const p: WideString); begin showmessage('Msg属性的值为:' + _msg + #13 + '传入的参数为:' + p); end;
再编译,html代码中添加一些代码:
<OBJECT ID='x' name='x' CLASSID='CLSID:52D17094-0687-4A2F-B2DB-30F3189AC659' align=center hspace=0 vspace=0 ></OBJECT> <hr /> <input type='button' value='显示Msg属性的值' onclick='ShowMsg()'/> <input type='button' value='设置Msg属性的值' onclick='SetMsg()'/> <input type='button' value='调用ShowMsg方法' onclick='CallShowMsg()'/> <script type='text/JavaScript'> var x = document.getElementById("x"); var ShowMsg = function(){ alert(x.Msg); } var SetMsg = function(){ x.Msg = 'js传过来的值'; } var CallShowMsg = function(){ x.ShowMsg('这是js传过来的参数'); } </script>
运行看下:
类似的,我们还可以为ActiveX添加带返回值的function,而非过程procedure,但是比较郁闷的是,我试了半天,Delphi中编译正常后,但是在JavaScript中就是无法取得返回值,估计是delphi的变量类型与Javascript的变量类型不匹配引起的,哪位 delphi高人如果知道原因,还请指点一二,在此先谢过.
11.深入看下ActiveX中到底有哪些玩意儿?
既然ActiveX能加载到网页中,肯定也是dom树的一份子了,想知道ActiveX到底提供了哪些其它属性或方法吗?以下的js代码可以测试出来:
<div id="info"></div> <script type="text/javascript"> var _info=""; for(var p in x){ _info += p + ":" + x[p] + "<br/>"; } document.getElementById("info").innerHTML = _info; </script>
当然如果你用IE8的js调试功能,也能看到刚才定义的那些方法和属性:
注意一下这里还有其它很多属性,比如Caption,所以你在js中用alert(x.Caption)也能弹出ActiveForm的标题,这是我们通过IE/JS从外部来看ActiveX的,其实也能换个角度从delphi内部看下activex的结构,com技术号称就是一组通用的接口规范,所以我们在delphi内部确实也能发现不少接口:
MyActiveX.ridl中可以看到
library MyActiveX { ... interface IMyForm; ... ...
表明IMyForm就是一个接口,再定位到MyActiveX_TLB.pas可以发现:
type ... IMyForm = interface; ... MyForm = IMyForm; IMyForm = interface(IDispatch) ...
说明MyForm就是从IDispatch继承下来的一个接口
最后再到UMyForm.pas中可以看到
type TMyForm = class(TActiveForm, IMyForm) Edit1: TEdit; ...
说明最终的运行窗口,就是继承自TActiveForm并实现了IMyForm的一个类
12.事件支持
打开MyActiveX.ridl,查看IMyFormEvents部分,可以看到Delphi生成的ActiveX控件中已经预置了很多事件
dispinterface IMyFormEvents { properties: methods: [id(0x000000C9)] void OnActivate(void); [id(0x000000CA)] void OnClick(void); [id(0x000000CB)] void OnCreate(void); [id(0x000000CC)] void OnDblClick(void); [id(0x000000CD)] void OnDestroy(void); [id(0x000000CE)] void OnDeactivate(void); [id(0x000000CF)] void OnKeyPress([in, out] short* Key); [id(0x000000D0)] void OnMouseEnter(void); [id(0x000000D1)] void OnMouseLeave(void); [id(0x000000D2)] void OnPaint(void); };
我们可以用JavaScript来响应这些事件,比如就拿我们最熟悉的OnClick事件,js中要这么处理:
<OBJECT ID='x' name='a' CLASSID='CLSID:52D17094-0687-4A2F-B2DB-30F3189AC659' align=center hspace=0 vspace=0 ></OBJECT> <script type="text/Javascript" event="OnClick" for="a"> alert('你点击了ActiveX控件'); </script>
运行后,鼠标在ActiveX的空白处点击,会弹出一个警告框:"你点击了ActiveX控件"
13.其它问题
前面提到了带返回值的function不好弄,其实这个不是什么大问题,完全可以迂回用procedure与属性解决
比如我们可以定义一个带参数的procedure,js调用时传入参数,然后在procedure内部,对参数进行处理后,将其赋值为 ActiveX的任何一个类型匹配的属性,比如前面提到的Caption属性,然后js获取Caption属性,相当于就是ActiveX处理后的返回值了
<HTML> <p align="center"><font face="华文彩云" size="7" color="#0000FF">测试的窗口</font></p> <HR><center><P> <OBJECT classid="clsid:6DB1F144-7C16-40A3-89F7-737B7B6D4EB4" codebase="E:\my\Myactivex\dbgrideh\Win32\Release\xdbgrideh.ocx" width=690 height=455 align=center hspace=0 vspace=0 > </OBJECT> </HTML>
