用 aardio 我们可以简单地调用 C++ 代码

我们今天就不说下面这种 aardio 调用原生 API 如何简单:

var point = { INT x; INT y; }::User32.GetCursorPos(point);

我们今天要玩一点难度高一些的,例如玩 C++ 地看到头都打转的 COM 接口,我们今天说的 COM 接口,也不说那种写起来累人用起来很舒服的 COM 控件。

我们今天要说几个难度高一些的:
1、直接调用 VC++ 实现了 IDispatch 接口指针。
2、直接用 C++ 专有的 thiscall 约定调用 C++ 对象。

一般调用 COM 控件,我们知道要先注册 COM 控件,但是一般注册 COM 控件还是比较麻烦,大多数需要管理权限注册,出现恼人的警告对话框,体验不是很好。在 aardio 中一般可以免管理权限注册,甚至可以免注册调用很多 COM 控件。

但是仍然有一些 COM 控件比较顽固是不能免注册调用的,其实 COM 控件注册啊什么的搞得很复杂,最终只是为了创建一个 IDispatch 对象,如果我们可以跳过上面这些所有的步骤,这会是非常舒服的。

我们用 VC++ 创建 IDispatch 对象,然后直接拿到原生的 IDispatch 接口指针,并且直接转换为 aardio 对象,这带来的好处是:

1、不用再注册 COM 控件了。
2、当然也不用什么免注册了。
3、可以直接通过内存加载 DLL,不用再带一个 DLL 文件了。

今天我们用 VC6 来写一个简单的 DLL 做一下演示,别小看 VC6,VC6 是唯一所有操作系统都自带的系统 C++ 运行时,所以可以生成极小的 DLL,而且一般是够用的,当然你要换新版 VC++ ,写法一样。

先看 aardio 调用代码有多简洁:

import console;//加载 DLLvar dll = raw.loadDll($"/test.disp.dll",,"cdecl");//创建 COM 对象,aardio 可自动支持 IDispatch 接口import com;var comObject = com.QueryObjectR( dll.CreateIDispatchObjectP() ); //使用 COM 对象comObject.name = "测试";console.log( comObject.name ); console.pause();

是不是简洁到难以置信?!
要做到上面的效果,一个编程语言需要同时做到能支持原生 API 开发,还要能支持 COM 动态接口对象,能做到这 2 样的编程语言其实非常少。而真正做到简洁方便好用的就更少了。

下面我们再看一下上面这个 test.disp.dll 的 C++ 源代码,这个先要有心理准备,世界上最复杂的编程语言不是白叫的,如果学 aardio 这样简单的编程语言让我叫苦不迭,我很难保证你看到下面的 C++ 代码不会晕过去。

不过我已经尽力把下面的代码处理到尽可能简单了,你不需要安装巨无霸的 VC++ 开发环境,直接运行下面的代码就可以编译生成 DLL,aardio 自带 VC6 扩展库。

import vc6;import console; var vc = vc6( "/",, console.open() )  //输入 C++ 源码vc.cpp  = /******     #include <afxwin.h>     #include <afxdisp.h>       class CTestObject: public CCmdTarget    {         DECLARE_DYNCREATE(CTestObject)        CTestObject();     protected:        virtual ~CTestObject();        CString strName;    public:        afx_msg BSTR GetName();        afx_msg void SetName(LPCTSTR lpszName);         DECLARE_DISPATCH_MAP()    };    IMPLEMENT_DYNCREATE(CTestObject, CCmdTarget)    CTestObject::CTestObject() {        EnableAutomation();        AfxOleLockApp();     }    CTestObject::~CTestObject() {        AfxOleUnlockApp();    }    BEGIN_DISPATCH_MAP(CTestObject, CCmdTarget)         DISP_PROPERTY_EX(CTestObject, "version", GetName, SetName, VT_BSTR)     END_DISPATCH_MAP()         BSTR CTestObject::GetName()     {          return strName.AllocSysString();    }    void  CTestObject::SetName(LPCTSTR lpszName)     {         strName = lpszName;    }     extern "C" __declspec(dllexport) LPDISPATCH __cdecl CreateIDispatchObject() {       LPDISPATCH pDispatch = ( new CTestObject() )->GetIDispatch(FALSE);//参数FALSE指定不要添加引用计数       return pDispatch;    }******/ //编译生成DLL vc.exec(    'cl *.cpp'    ,'/W3' /*警告等级*/    ,'/MD' /*使用多线程动态运行库*/    ,'/O2 /Ot /EHsc' /*代码优化选项*/    ,'/D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "_AFXDLL" ' /*定义常数和宏*/    ,'/I"./INCLUDE"'/*指定头文件目录*/    ,'kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib' /*导入库*/    ,'/link /SUBSYSTEM:WINDOWS /MACHINE:X86' /*后面是链接参数 */    ,'/out:test.disp.dll'/*输出文件名*/    ,'/dll' /*输出DLL*/     ,'/LIBPATH:".LIB" /LIBPATH:".LIB2"' /*指定库目录*/)

好吧,我们进入下一个目标,使用 thiscall 协议调用 C++ 对象,现在你见过 C++ 代码了,我就不再把 C++ 代码,aardio 代码分开发了,用 C++ 编译 DLL,再用 aardio 调用 C++ 对象,一锅炖:

import vc6;import console; console.open();var vc = vc6( "/" )  //输入C++源码vc.cpp = /******     #include <windows.h>     struct TestInfo{        int x;        int y;        BYTE name[256];    };    class CTestObject    {    public:         //注意函数声明前加上 virtual 以支持 aardio 中的 raw.interface        virtual  void getName(char *buffer,int len);         virtual  void getInfo(TestInfo *pInfo);    };    void CTestObject::getName(char *buffer,int len){        strcpy(buffer,"测试");    }    void CTestObject::getInfo(TestInfo *pInfo){        pInfo->x = 1;        pInfo->y = 2;        strcpy((char *)pInfo->name,"测试");    }    extern "C" __declspec(dllexport) CTestObject* __cdecl CreateTestObject() {         return new CTestObject();    }    extern "C" __declspec(dllexport) void __cdecl DeleteTestObject( CTestObject* pTest) {        delete pTest;    }******///编译生成DLL vc.exec(    'cl *.cpp'    ,'/W3' /*警告等级*/    ,'/MD' /*使用多线程动态运行库*/    ,'/O2 /Ot /EHsc' /*代码优化选项*/    ,'/D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL"' /*定义常数和宏*/    ,'/I"./INCLUDE"'/*指定头文件目录*/    ,'kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib' /*导入库*/    ,'/link /SUBSYSTEM:WINDOWS /MACHINE:X86' /*后面是链接参数 */    ,'/out:test.dll'/*输出文件名*/    ,'/dll' /*输出DLL*/     ,'/LIBPATH:".LIB" /LIBPATH:".LIB2"' /*指定库目录*/)var dll = raw.loadDll("/test.dll",,"cdecl");import raw.interface;class testObject{  ctor(){    //创建 C++ 对象,并获取指针,注意这里使用了 P 尾标获取指针。    var pTest = dll.CreateTestObjectP();    //C++ 对象指针转换为 aardio 对象。    this = ..raw.interface( pTest,"        void getName(string &buffer,int len);        void getInfo(struct &pInfo);         ","thiscall" //注意调用约定为thiscall    )    //添加析构函数      ..table.gc(this,"delete")    };  delete= function(){    if(!owner.deleted){      dll.DeleteTestObject( owner );      owner.deleted = true;    }   };}//创建对象var obj = testObject();//调用 C++ 函数var name = obj.getName(25,25);console.log(name);//调用 C++ 函数var info = obj.getInfo({ int x;int y;BYTE name[256]})console.log( info.name  );console.pause();

好吧,如果你只是想试着跑几句 C++ 代码,并不想生成 DLL,就像硬盘干干净净的,还能跑一下 VC++ 代码,aardio 提供了一个 vc6.loadcode() 函数可以直接运行 VC++ 代码,示例如下:

import vc6;import console; console.showLoading(" 正在加载 C++ 代码");var dll = vc6.loadcode(`#include <windows.h>  #include <winioctl.h>  extern "C" {  __declspec( dllexport ) int __cdecl getValue() {     return CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS);  }}`);var value = dll.getValue();var str = string.format("0x%X",value);console.log("已复制: " + str,value);import win.clip;win.clip.write(str);console.pause();

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2021年10月6日
下一篇 2021年10月6日

相关推荐