对aardio生成activeX EXE功能的研究学习
假设有个需求,需要将目前写好的某种控制算法库共享给别人用,他们用的是其他编程软件,如果重新移植一遍就需要花费更多的时间和精力。如果用户那边是c#那么移植到c#就会有算法被还原代码风险,那么就要用c++把算法重新实现一遍生成dll,那么还需要下载安装vs并学c++,反正挺麻烦。
于是考虑将aardio生成的程序通过activex来提供给其他软件使用, 这里研究下.
用这个方法生成的exe注册后, 就和ocx一样的使用方式 , 不仅能提供函数方法也能提供界面控件方式.
这样就可以将aardio里封装的一些方法库通过这种方式提供给其他程序直接使用,而不需要重新移植一遍,
先测试下aardio的IDE里有相关生成activex的示例
测试步骤:
打开main工程后, 直接点击[发布] , 发布编译为exe文件.
打开编译好的exe目录吗双击exe , 选择[注册]
看到AardioCOM.exe文件上面那个test文件夹, 打开后, 双击test.aardio,运行
运行test.vbs
测试范例没问题, 那么后面慢慢测试编写模式.
复制出所有工程代码, 自己新建个生成activex的工程 , 删除不需要的多余代码, 一步步简化测试.
简化后的 工程:
生成activex最重要的文件就是那个SampleControl.tlb
这个文件定义了Activex接口和函数, 它是由SampleControl.odl 这个文件通过vc编译而来. 编译用到的文件正是 MKTYPLIB.aardio 这个文件.
打开MKTYPLIB.aardio , 直接运行, 会看到编译过程.
然后就生成了同名.tlb文件
这里重点看下 odl文件 , 文件夹里有个简易的ODL参考手册,
import "ocidl.idl"; [ uuid(84DF165D-F254-4A88-9D32-445B69DB340F), version(1.0) ] library AardioTestControl { importlib("stdole32.tlb"); [ uuid(9DFAA296-6DFC-4B21-BACD-B8E1CB0587C2) ] dispinterface IAardioTestControlSample { properties: methods: [ id(1) ] int Add( [in] int a, [in] int b ); [ id(2) ] void Quit( void ); }; [ uuid(72E56402-47CE-47F3-B6B5-09F5DD1F6746),control ] coclass Sample { [default] dispinterface IAardioTestControlSample; }; };
对比观看, 可以得出一个简单的使用方法:
如果修改了odl文件, 就重新生成下tlb.
调用其他库 ,
新建个test.aardio库,
namespace test; add = function(a,b){ return a+b; }
修改Sample.aardio库, 将Add改为引用test.add()
import test; namespace AardioTestControl; class Sample{ ctor(activeX){ this.activeX = activeX; } Quit = function(){ //COM控件调用方必须调用这个函数退出进程 this.activeX.stop(); } Add = function(a,b){ //return a+b; return ..test.add(a,b); } }
结果没问题.
这就意味着调用其他库应该没问题.
下面调用sio库试试,将sio库改为ActiveX方式.
新增一个testSio库
import sys.comPort; namespace testSio; var comPorts = ..sys.comPort.list(); getcomPortsNum = function(){ return #comPorts; } getcomPortsName = function(index){ if(#comPorts){ return comPorts[index].portName; } return null; }
然后将Sample库更改为 , 这个是用来给activeX提供函数实现的.
import testSio; namespace AardioTestControl; class Sample{ ctor(activeX){ this.activeX = activeX; } Quit = function(){ //COM控件调用方必须调用这个函数退出进程 this.activeX.stop(); } getNameNum = function(){ return ..testSio.getcomPortsNum(); } getNameStr = function(index){ return ..testSio.getcomPortsName(index); } }
然后, 需要把 ODL文件内容修改下:
import "ocidl.idl"; [ uuid(84DF165D-F254-4A88-9D32-445B69DB340F), version(1.0) ] library AardioTestControl { importlib("stdole32.tlb"); [ uuid(9DFAA296-6DFC-4B21-BACD-B8E1CB0587C2) ] dispinterface IAardioTestControlSample { properties: methods: [ id(1) ] int getNameNum( ); [ id(3) ] BSTR getNameStr( [in] int index ); [ id(2) ] void Quit( void ); }; [ uuid(72E56402-47CE-47F3-B6B5-09F5DD1F6746),control ] coclass Sample { [default] dispinterface IAardioTestControlSample; }; };
运行 MKTYPLIB.aardio 编译ODL文件为TLB二进制文件.
发布exe , 注册exe , 编写测试代码:
import console; import com; try{ obj = com.CreateObject("AardioTestControl.Sample") } var num = obj.getNameNum() ; for(i=1;num;1){ console.log( obj.getNameStr(i) ); } obj.Quit(); console.pause();
执行:
同楼上,
testSio.aarido添加sio代码如下:
import sio; import sys.comPort; namespace testSio; var sioPort; var comPorts = ..sys.comPort.list(); getcomPortsNum = function(){ return #comPorts; } getcomPortsName = function(index){ if(#comPorts){ return comPorts[index].portName; } return null; } open = function(name){ if(sioPort){ sioPort.close(); } var err; sioPort,err = ..sio.port(name); if(!sioPort){ return false; } return true; } close = function(){ if(sioPort){ sioPort.close(); } } writeStr = function(str){ if(sioPort){ if( !sioPort.write( str ) ){ return false; } }else { return false; } return true; }
Sample.aardio 库 添加sio函数进去
import testSio; namespace AardioTestControl; class Sample{ ctor(activeX){ this.activeX = activeX; } Quit = function(){ //COM控件调用方必须调用这个函数退出进程 this.activeX.stop(); } getNameNum = function(){ return ..testSio.getcomPortsNum(); } getNameStr = function(index){ return ..testSio.getcomPortsName(index); } open = function(name){ return ..testSio.open(name); } close = function(){ ..testSio.close(); } writeStr = function(str){ return ..testSio.writeStr(str); } }
编辑ODL文件:
[ id(1) ] int getNameNum( ); [ id(3) ] BSTR getNameStr( [in] int index ); [ id(4) ] int open( [in] BSTR name ); [ id(5) ] void close( ); [ id(6) ] bool writeStr( [in] BSTR name ); [ id(2) ] void Quit( void );
测试代码:
import console; import com; try{ obj = com.CreateObject("AardioTestControl.Sample") } var num = obj.getNameNum() ; for(i=1;num;1){ console.log( obj.getNameStr(i) ); } var ret = obj.open(obj.getNameStr(1)); console.log(ret) if(ret){ obj.writeStr("abcd1234"); obj.close(); } obj.Quit(); console.pause();
测试成功.
修改test.vbs代码:
Dim sample,num Set sample = CreateObject("AardioTestControl.Sample") sample.open(sample.getNameStr(1)) sample.writeStr("vbs-abcd1234") sample.close() sample.Quit() msgbox "ok"
测试下, 如果acitveX的函数里执行耗时操作, 直接调用会不会卡界面?
答案是: 会
writeStr = function(str){ sleep(3000); return ..testSio.writeStr(str); }
将发送增加个延时, 模拟耗时 , 换成win.delay(3000); 也一样
测试代码:
winform.button.oncommand = function(id,event){ var ret = obj.open("COM1"); console.log(ret) if(ret){ console.log( obj.writeStr("abcd1234") ); console.log("send ok") } }
会发现耗时执行的时候卡界面.
所以, ActiveX EXE和ocx一样, 耗时操作需要在线程里执行避免卡界面.
测试 ActiveX 界面功能:
aardio的范例里已经有了这个form基本功能代码定义, 只是缺少使用示例, 在这里我们只需要就像调用ocx一样winform.createEmbed() 加载即可.
我这里随意增加一些其他按键, 然后新建一个demo界面, 添加引用代码:
这里演示下 调用事件, 定义事件的代码参看范例 SampleForm.aardio
import win.ui; /*DSG{{*/ var winform = win.form(text="aardio form";right=759;bottom=469;bgcolor=15780518) winform.add( custom={cls="custom";text="自定义控件";left=114;top=76;right=618;bottom=355;bgcolor=12639424;z=1} ) /*}}*/ import console console.open() var obj = winform.custom.createEmbedEx("AardioTestControl.Sample",,{ onHello = function(str){ obj._object.onHello(str); }; }); obj.onHello = function(str){ console.log(str) } winform.show(); win.loopMessage(); return winform;
实例: 将ScottPlot波形图生成ActiveX进行调用.
import win.ui; namespace AardioTestControl; class SampleForm{ ctor(parent,tParam){ /*DSG{{*/ this = ..win.form(text="test.control";right=823;bottom=525;bgcolor=12632256;border="none";exmode="none";mode="child";parent=parent;tParam=tParam) this.add( button={cls="button";text="aardio Button";left=19;top=473;right=183;bottom=521;z=2}; custom={cls="custom";text="自定义控件";left=16;top=12;right=814;bottom=464;bgcolor=12639424;db=1;dl=1;dr=1;dt=1;z=1}; edit={cls="edit";text="Edit";left=209;top=477;right=438;bottom=516;edge=1;z=3} ) /*}}*/ this.button.oncommand = function(id,event){ this.externalEvent.onHello("来自com点击事件 hello"); } import ScottPlot; import dotNet; var Chart = ..ScottPlot.FormsPlot(); //绑定界面 ..dotNet.setParent(Chart,this.custom); // plot one set of data using the primary Y axis var sigSmall = Chart.Plot.AddSignal(..ScottPlot.DataGen.Sin(51, 1)); sigSmall.YAxisIndex = 0; Chart.Plot.YAxis.Label("Primary Axis"); Chart.Plot.YAxis.Color(sigSmall.Color); // plot another set of data using an additional axis var sigBig = Chart.Plot.AddSignal(..ScottPlot.DataGen.Cos(51,1,0,100)); var yAxis3 = Chart.Plot.AddAxis(0/*Left*/, 2); sigBig.YAxisIndex = 2; yAxis3.Label("Additional Axis"); yAxis3.Color(sigBig.Color); var Crosshair = Chart.Plot.AddCrosshair(15.0, 0.25); Crosshair.VerticalLine.IsVisible = false; Crosshair.HorizontalLine.IsVisible = false; Chart.Refresh(true); Chart.MouseEnter = function(e,r){ Crosshair.VerticalLine.IsVisible = true; Crosshair.HorizontalLine.IsVisible = true Chart.Refresh(false,false); } Chart.MouseLeave = function(e,r){ Crosshair.VerticalLine.IsVisible = false; Crosshair.HorizontalLine.IsVisible = false; Chart.Refresh(false,false); } Chart.MouseMove = function(){ var mouseCoordinates = Chart.GetMouseCoordinates(0,0); Crosshair.X = mouseCoordinates.Item1; Crosshair.Y = mouseCoordinates.Item2; Chart.Refresh(false,true); } this.show() }; } if( !owner ) ..AardioTestControl.SampleForm().doModal(); /**intellisense() AardioTestControl.SampleForm() = 窗口类\n!winform. end intellisense**/
以上是将scottPlot图表那个里面的代码直接复制到了窗体定义那里.
工程代码:
测试的时候, 先取dist目录,双击注册AardioCOM.exe , 然后, 到test目录里运行demoform.aardio, 应该就可以看到效果了.
登录后方可回帖
期待后续