aardio 文档

使用结构体

请参考: 原生数据类型

创建结构体 #

在 aardio 中可以用类定义结构体,只要在类构造器的成员键名前添加原生类型声明就可以了。

参考:

//定义结构体
class Point{
   int x = 0;  
   int y = 0;  
}

//创建一个结构体实例对象
var pt = Point();

aardio 默认已经定义了所有字母大写的保留常量 ::POINT 用于表示坐标。

结构体创建的实例对象在 aardio 中仍然是一个 table 对象。

我们可以在 table 构造器中成员的键名前面添加原生类型声明,直接构造结构体实例。

例如:

var point = {
   int x = 0;
   int y = 0;
}

这非常方便,我们甚至可以在原生 API 函数的参数中直接用字面量构造结构体。。

示例:

var r,point = ::User32.GetCursorPos({
   int x;
   int y;
})

在 aardio 中设定结构体成员初始值不是必须的, 如果不指定初始值则在 aardio 代码中这些字段的值为 null,传到原生 API 函数中也会自动初始化为空值(简单讲就是存储该字段的每个内存字节的值都是 0 )。

要点:

aardio 创建的结构体都会创建一个 _struct 字段,_struct 存储了结构体的原生类型声明。通过 object[["_struct"]] 可以快速判断一个对象是不是结构体。

参考:

在 API 函数中使用 struct 结构体 #

aardio 中的「结构体」存储为一个表对象,当我们把结构体复制到 raw.buffer 或 raw.realloc 分配的内存、 或者其他可写入结构体的内存时,都会转换为原生结构体。在调用原生 API 函数时,结构体参数也会临时转换为「原生结构体」指针, 并在调用 API 结束时立即释放临时创建的「原生结构体」,这种转换是自动的

无论原生 API 的函数参数是否输出参数,所有结构体参数总是转换为结构体指针(传址),

下面是一个在 API 函数参数中使用结构体的完整范例程序:

//加载 DLL
var dll = raw.loadDll("User32.dll");

//声明 API 函数
getCursorPos = dll.api( "GetCursorPos", "word(struct& point)")

//通常,我们使用 class 来定义 API 函数中需要用到的结构体:
class Point {
   int x=0; 
   int y=0;
};

//创建一个新的结构体
var p = Point();

//使用API函数
GetCursorPos(p); 

展开结构体 #

aardio 结构在 API 参数中总是传址( 传结构体指针)。

如果需要在API参数中按值传递结构体(这种情况很罕见),则需要将结构体成员展开为多个普通的传值参数。

例如 PtInRect 的 C 语言函数原型为:

BOOL PtInRect( RECT *lprc,POINT pt);

第二个参数的 POINT 结构体是按值传递,而 POINT 结构体的成员为{int x;int y}

那么转换为 aardio 声明的格式如下:

::PtInRect := ::User32.api( "PtInRect", "int(struct lprc, int x, int y)" );

转换结构体类型

  1. 函数原型:

    raw.convert(inPointerOrStringOrBufferOrStruct,outStruct,offset=0 ) 
    
  2. 函数说明:

    将源数据转换为目标结构体数据,偏移量 @offset 是可选参数,默认为 0。 第一个参数 @pointerOrStringOrBufferOrStruct 可以是结构体(struct)、pointer、string、buffer 类型的对象。
    第二个参数 @outStruct 必须是结构体(struct)。

  3. 调用示例:

    下面是使用 raw.convert 编写的一个数值类型转换小程序。

    import console;  
    
    var struct2 = { 
      union u= { //union 联合体表示几个变量共用一个内存位置,利用此特性,可以将同一数据转换为不同的数据类型。
         byte byte = -1;
         BYTE ubyte = -1; 
      } 
    } 
    
    var struct1 = 
    { byte x= -1 }
    
    console.log( raw.convert(  struct1, struct2 ).u.ubyte ) //将 -1 转换为无符号单字节数值 255
    console.pause();
    

获取结构体内存长度

  1. 函数原型:

    raw.sizeof( structOrTypename )
    
  2. 函数说明:

    返回结构体的内存长度,参数可以是一个结构体(struct),也可以是一个字符串形式的API数据类型名字。

    import console;  
    
    var struct = { 
      union u= {
         byte byte = -1;
         BYTE ubyte = -1; 
      };
      int n = 123;
    }; 
    
    console.log( raw.sizeof( struct ) );
    console.pause();
    

自定义结构体对齐大小

在结构体中可以使用成员 _struct_aligned 自定义对齐大小。
例如C/C++中定义结构体时使用 #pragma pack(n),aardio则 需要在结构体中添加_struct_aligned = n

示例:

import console;

console.log( 
    raw.sizeof( {
        int x;
        LONG y;
        _struct_aligned = 1;
    } )
);

console.pause();

原生数组 #

aardio 语言中我们可以在一个结构体里定义原生静态数组。 原生数组与其他原生类型的区别是在字段名称后用中括号内的数值指定了原生数组的长度。

例如:

var arr  =  {
int value[2] = {1;2}
}

上面的字段声明 int value[2] 中的 [2] 指定了数组元素个数为 2。 在上面的 [2] 内只能指定数值字面量,不能用变量指定数组长度。

请参考:原生类型 - 数组

如果我们不指定数组长度,可以用 int value[] 表示这是一个原生变长数组。 原生变长数组在运行时可选采用两种方法确定长度:

例如:

var arr  =  {
int value[] = { length = 99/*可以使用变量*/ }
}

如果没有使用 length 指定数组长度,那么 aardio 将获取数组的实际长度,对于 BYTE,byte,WORD,word 等值可以为字符串的类型 - 支持使用字符串确定数组长度。

例如:

var arr  =  {
int value[] = { 1,2 }
}

如果原生数组的元素类型是结构体时(数组本身是 struct 类型的数组),
则至少需要指定第一个元素的值(aardio 用这个元素确定数组元素实际使用的结构体类型)

示例如下:

var arrLength = 99;
var arr  =  {
    struct value[] = { 
        length = arrLength; //指定数组长度
        {  //至少指定第一个结构体元素的值
            int x;
            int y;
        }; 
    }
}

我们必须用将原生数组放到一个结构体中。 例如上面的 arr 是结构体对象,arr.value 才是包含在结构体中的数组。

在声明原生 API 函数时,原生数组参数的类型也应声明为 struct 类型(或 struct& 类型),struct 类型参数在 API 参数中传址(结构体指针)而非传值。

raw.toarray 运行时创建指定长度的原生数组

  1. 函数原型:

    rawArray = raw.toarray( arrayOrLenght, rawTypename = "struct", fieldName = "array" )
    
  2. 函数说明:

    函数的主要功能是在运行时创建指定长度的原生数组。

    函数根据从第一个参数 @arrayOrLenght 获取的长度在结构体中创建一个值为原生类型数组的字段(由第三个参数 @rawTypename 指定字段名称 )。参数 @arrayOrLenght 可以直接用一个数值指定数组长度,也可以用于初始化数组内容与大小的普通数组( table 对象 )。

    第二个参数 @rawTypename 指定定原生数据类型名,默认为"struct" 类型。

    第三个参数 @rawTypename 指定创建的结构体中保存原生数组的字段名称,省略则使用默认字段名 "array"。

  3. 调用示例:

    import win;
    
    //创建一个结构体
    数组 = {}
    数组[1] = ::POINT()
    数组[1].x = 24123
    数组[1].y = 888
    
    数组[2] = ::POINT()
    数组[2].x = 45645
    数组[2].y = 999
    
    //转换为API数组
    array_c = raw.toarray( 数组 )
    

    上面的代码等价于:

    array_c =  {
      struct array[] = {}
    }
    
    array_c.array[1] = ::POINT(24123,888)
    array_c.array[2] = ::POINT(45645,999)
    
  4. 调用示例2:

    //构造定长原生数组
    var rawArray1 ={
    
      struct items[24] = { 
         { int int=0; float float=0.0; }
      } 
    }
    
    //动态创建定长数组
    var rawArray2  = raw.toarray( 24,"struct","items" )
    
    //结构体数组至少要指定一个结构体实例以确定数组类型。
    rawArray2.items[1] = { 
      int int=0;
      float float=0.0;
    }
    
    import console;
    console.logPause( 
      raw.sizeof(rawArray1)==raw.sizeof(rawArray2) 
    ); 
    

将结构体转换为字符串

请参考: raw.tostring

将结构体转换为字节数组

请参考:raw.buffer

原生数组与 buffer 相互转换

示例:

//创建一个结构体
array = {}
array[1] = ::POINT()
array[1].x = 24123
array[1].y = 888

array[2] = ::POINT()
array[2].x = 45645
array[2].y = 999

//转换为原生类型数组
var rawArray = raw.toarray( array )

//转换为二进制字节数组(buffer)
var buffer = raw.buffer(rawArray)

//再创建一个空的原生类型数组
var rawArray2 = raw.toarray( {::POINT(),::POINT()} )

//从内存里复制原生结构体的数据到 rawArray2
raw.convert( buffer,rawArray2 );

//输出
import console;
console.dumpJson(rawArray2);
console.pause();

Markdown 格式