工劳快讯:汕尾美团骑手罢工取得阶段性胜利

记者调查泉州欣佳酒店倒塌曝惊人“案中案”:曾是卖淫场所,50名老板、官员卷入其中

退出中国市场的著名外企名单

去泰国看了一场“成人秀”,画面尴尬到让人窒息.....

生成图片,分享到微信朋友圈

自由微信安卓APP发布,立即下载! | 提交文章网址
查看原文

如何使用aardio更好的玩耍electron

Jacen He aardio 2022-12-24

这个支持库我仅写了15kb的体积,aardio小到极致,electron大到极致,但没有关系,aardio拥抱一切。


使用aardio嵌入electron的一些优势:


1、chromium + node.js这一系列方案中,electron 是其中做的最好的,至少耍CEF这些东西好几十条大街。


2、chromium 在网页呈现是做的最好的,但是JS与系统交互的通力非常弱,不能直接调用C/C++的接口函数,也无法直接调用系统API,有时候其他语言很简单的事,但用JS非常麻烦,这在Web上没有问题,但在桌面开发上是硬伤,这方面恰恰是 aardio可以优势互补的地方。


3、electron带了一大堆DLL,有60多个DLL,不知道怎么想的没有搞个子目录,electron里写软件,始终都是用 electron.exe启动,修改图标、版本信息都比较痛苦,如果使用 aardio嵌入,这都不是问题,我把 electron.exe 改名为了 electron.dll ,就想当于多了个DLL.,而且exe文件也不用放到一大堆DLL一起。


4、aardio提供了全自动下载,安装electron运行时的功能,几句代码写一个EXE成为可能。而且大家可以共享公共的运行时,体积大就不再是问题(当然,也可以自带electron),这就是说你只要几百KB的EXE就可以扔给别人用,而且可以使用  electron,顶多就是用户第一次使用下载一下,这也并不是问题,难道从你自己这里下载不是下载?!这种模式更方便。


5、electron体积非常大,而aardio小到极致,而且electron 不仅仅是自己大,添加模块,模块依赖模块,模块的安装、编译都是非常痛苦的事,但 aardio很多东西就是几句代码就可以做到,这样可以避免开发软件时体积进一步的膨胀。


6、在aardio中嵌入以后,可以全部被转换到最简单的单线程异步编程。aardio可以方便的远程调用electron中的JS函数,当然JS函数也可以选程调用aardio函数,交互调用非常方便。虽然electron是用C++写的,你用C++控制 electron,可能还没有用 aardio去控制更方便。


7、这个支持库还会提供一个嵌入式的HTTP服务器,WebSocket服务器,随机分配端口,不会与其他软件冲突,aardio,js都可以自动获取这个随机的服务器URL,所有事情后台全部做好。


那么嵌入electron的缺点也简单说一下:


1、无论electron做的多好,这种臃肿的架构,无法替代小轻快的原生桌面应用。


2、electron不支持XP,我在WIN2008上试了一下都不支持,只能支持WIN7,WIN2008 R2以上的系统,这倒没什么问题,这部份陈旧的操作系统在中国的市场份额也只有10%,你能让 90%的用户使用你的软件都几乎是不可能了,更何必在乎那 10%在用着老旧系统几乎无利可图的平台呢?!


3、体积大! 如果你正好就是喜欢大,那就不是问题。


4、使用JS你的代码是没有秘密的,这不像aardio十年来无人能反编译源码,而且关键是aardio嵌入C,C++组件是非常方便的。但这对JS是硬伤。



在分享这个支持库的用法以前,先简单谈一谈我是怎么实现这个功能的,大家可以看看,我没写几句代码就轻松的降服 electron。


首先第一步,我想到的是把electron的主窗口嵌入到aardio窗口中,这看起来很简单,但是 electron却并不太高兴不愿意让我们这么干,而这个一定要在创建窗口的时候去设置,不然窗口显示出来,再让他嵌入,这个体验就非常不好了。


那么,首先我用C语言写了一个假的CreateWindowEx函数,为了方便直接在aardio中编译这个DLL,这个C语言的源码以及编译代码我已经放到了标准库里,文件位置: ~\lib\electron\.build\CreateWindowExHook.aardio ,然后在 aardio中用如下代码启动electron并注入DLL钩子:


import process.apiHook;
var prcs = process.apiHook( "\electron\electron.exe" ); 

//先给electron安装钩子
var hookInfo = prcs.install("User32.dll","CreateWindowExW","CreateWindowExHook.dll","_CreateWindowExHook@48");

//调用外部进程中的API函数
SetCreateWindowExPtr = prcs.process.remoteApi("void(addr addrTrampoline,addr hwndParent)","CreateWindowExHook.dll","SetCreateWindowExPtr","cdecl")
SetCreateWindowExPtr(
    hookInfo.addrTrampoline,
//这是真正的CreateWindowEx函数指针地址
    winform.hwnd
);

//然后让electron进程继续运行
prcs.resume();



这样我们就通过API钩子轻松的拿到了 electron创建的窗口句柄,在electron创建主窗口的时候,就强行改掉他的样式,并将他捉进aardio窗口内。


第二步,就考虑传一些数据给 electron了,electron没有提供方法让你可以干这事,不过electron可以取环境变量,于是我们可以拿这个做文章,环境变量不仅仅可以放字符串哦,完全可以把对象序列化为JSON,再到 electron里反序列化回来,当然,这种事情我全给写好了,大家现在可以直接传对象过去。


第三步,考虑怎么让aardio与electron交互调用,electron可不仅仅是多线程,他已经是多进程了,他自己交互就很吃力了,压根没有考虑为其他语言提供可能性,怎么办呢,JS可以使用WebSocket,那么可以通过WebSocket实现RPC,然后找了半天node.js的一些WebSocket/RPC的库用了都不理想,依赖库一堆,安装麻烦,用起来有的还没有反应,这不符合我拎上几句代码走江湖的惯性思维。关键是这些JS实现的RPC都长这风格:


var startEnviron = require('startEnviron');
var wsRpc = require('json-rpc-ws');
var client = wsRpc.createClient();
client.connect(startEnviron.wsUrl, 
function connected () {
    client.send(
'hello', ['JS传过来的参数1''JS传过来的参数2'], function mirrorReply (error, reply) {
        client.send(
'aardio.print', [reply]);
    });
});


其实这让人感觉更像是一个WebSocket加JSON自动转换器。

我希望可以像在aardio中那样更方便的调用RPC函数,例如可以这样写:


 client.hello('JS传过来的参数1''JS传过来的参数2')


这样写起来方便,可读性也好的多,但是没有办法,JS不能愉快的实现这样的效果。


即然JS解决不了这个问题,那就发挥我一惯的方法了,用最粗鲁的方式解决问题。首先我在aardio中制定了一个aasdl这个接口描述语言( http://bbs.aardio.com/doc/aasdl/ ),然后我首先改进aardio的RPC模块支持aasdl,然后我决定不找那些node.js模块了,我觉得真是太大了,几句代码能搞定的事,为什么要搞那么多模块,怪不得他们把electron这个东西搞的这么大体积。


然后我在node.js中写了一个模块,在连接RPC服务器以后,立即获取aasdl,并转换为JS中的函数,最后一切麻烦的细节都被这个几十行的代码搞定了。然后在 electron中可以这样写JS


aardio = require("aardio");
    
//调用aardio中的函数,
aardio.test.hello( "你点击了" + e.target.innerHTML ) 
    
//调用aardio中其他对象的成员函数
aardio.test.hello( "你点击了" + e.target.innerHTML ) 


并且,你不需要安装 aardio这个JS模块,aardio会自动的把他复制到正确的位置,aardio从来不会麻烦用户去装任何东西。


好吧,该切入正题了,下面我们上调用 electron的演示源码( 你可以在新版  aardio范例-> Web应用  -> electron 找到这个示例)



import win.ui;
/*DSG{{*/
var winform = win.form(text="在aardio中嵌入electron";right=1278;bottom=767;bgcolor=16777215;clipch=1)
winform.add()
/*}}*/

import electron.runtime;//如果希望自动安装electron到系统目录,请先导入
import electron.app;//导入electron扩展库
var app = electron.app(winform);//创建electron进程

//可以直接在这里写electron加载主进程的main.js启动文件
app.jsMain =/** 
    const aardio = require('aardio') //导入aardio模块
    const {app, BrowserWindow} = require('electron')
    
    let win
    function createWindow () { 
          win = new BrowserWindow( aardio.startEnviron.browserWindow)
          win.loadURL(aardio.startEnviron.indexUrl)
          win.webContents.openDevTools()
         
          win.on('closed', () => { 
            win = null
          })
    }
    
    app.on('ready', createWindow)
    app.on('window-all-closed', () => {
        app.quit();
          
    }) 
**/


//可选在这里直接指定index.js的代码,实际开发请写到工程文件里
app.html = /** 
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>aardio嵌入electron演示</title>

  </head>
  <body>
    <h1>aardio嵌入electron演示!</h1>
   
   <button id="aardio">点这里调用aardio代码</button>
    
    <?
    response.write("在electron中执行aardio代码")
    ?>
  </body>    
  
  <script type="text/javascript">   
    aardio = require("aardio");
  
    //响应按钮点击事件
    document.querySelector("#aardio").onclick = function(e){ 
        
    //调用aardio中的函数,hello要调用的函数名字,后面可以跟任意个调用参数
    aardio.ex.hello( "你点击了" + e.target.innerHTML ).then(
            function(message){
                alert(message);
            } 
        )
    }
    
    //aardio服务端发起调查任务
    aardio.on("getUrl",function(){
        return document.location.href;
    });
    
    </script>
    
</html>
**/


/*
在下面的external对象中指定允许electron渲染进程中使用JS直接调用的函数
下面的external 会直接转换为js中的aardio对象,在JS中require('aardio')就可以使用
*/

app.external = {
    hello = 
function(...){
        winform.msgbox( web.json.stringify({...},
true,false) )
        
return "electron,我是aardio,一起玩怎么样!?"
    } 
    ex = {
        hello = 
function(...){
            winform.msgbox( web.json.stringify({...},
true,false) )
            
return "electron,我是aardio,你最近过的还好吗?!"
        } 
    }
}

/*
启动electron,下面使用一个aardio表对象指定的启动参数,
在electron的主进程、渲染进程中都可以直接通过  aardio.startEnviron 访问。
*/

app.start(  
    
    
//electron打开的第一个页面,必须指定应用程序目录下的aardio代码文件
    indexUrl = "/res/main.aardio";
    
    
//指定electron创建浏览器窗口的启动参数
    browserWindow = {
        width = 800;
        height = 600; 
        frame = 
false;
        resizable = 
false;
        center = 
false;
        titleBarStyle = 
"hidden";
        webPreferences =  {
            nodeIntegration = 
true;
        }
    }
);

//调查任务结束,JS代码汇报结果到这里
app.callback("getUrl",function(hSocket,result,err){
    winform.msgbox(result,
"打开了页面")
})

winform.setTimeout( 
    
function(){
        
//对所有页面发起调查任务,
        app.survey("getUrl");
    },2000
)   

winform.show() 
win.loopMessage();


文章有问题?点此查看未经处理的缓存