对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, 应该就可以看到效果了.
登录后方可回帖


期待后续