搜索
aardio官方社区 门户 查看主题

关于api结构体struct的困惑

发布者: winner | 发布时间: 2012-11-3 10:16| 查看数: 6319| 评论数: 9|帖子模式

本帖最后由 winner 于 2012-11-3 10:43 编辑

最近学习api操作,拿wlan的api练手

例如下面这个API,这是MSDN的官方C++示例
http://msdn.microsoft.com/en-us/library/windows/desktop/ms706716(v=vs.85).aspx
  1. DWORD WINAPI WlanEnumInterfaces(
  2.   _In_        HANDLE hClientHandle,
  3.   _Reserved_  PVOID pReserved,
  4.   _Out_       PWLAN_INTERFACE_INFO_LIST *ppInterfaceList
  5. );
复制代码
里面的一个参数

PWLAN_INTERFACE_INFO_LIST *ppInterfaceList

因为不懂C++,所以我查阅了一下资料,

好像ppInterfaceList参数是一个PWLAN_INTERFACE_INFO_LIST型的结构体,
加上*号代表传入的是指向PWLAN_INTERFACE_INFO_LIST型结构体的一个指针,
然后我开始写aardio代码,

先查阅了结构体类型的相关资料,在api数据类型文档中发现一句
http://bbs.aardio.com/doc/reference/libraries/kernel/raw/datatype.html
注意在API参数中,struct被转换为指针按引用传递,如果是按值传递的结构体,需要展开所有成员为普通参数.



所以我这样定义了这个API函数,对上面这句话的理解就是 只需要把结构体声明一个结构体对象,然后传进去就行了,最后发现理解的不对



结构体:
import win.guid;
class WLAN_INTERFACE_INFO_LIST{
    int dwNumberOfItems;
    int dwIndex;
    struct  InterfaceInfo[] = { {
        struct InterfaceGuid = ..win.guid();
        byte strInterfaceDescription[256];
        int isState;
    } };
}

WlanEnumInterfaces = ::Wlanapi.api("WlanEnumInterfaces","int(int hClientHandle,pointer pReserved,struct ppInterfaceList)")

pIfList = WLAN_INTERFACE_INFO_LIST();

var dwResult = WlanEnumInterfaces(hClient,null,pIfList)

io.print(dwResult,pIfList[
'InterfaceInfo'][1]['InterfaceGuid'])


结果发现他只能打印出一个空的,也就是默认的那个GUID 00000000-0000-0000-0000-000000000000




然后我又把API声明改成这个样子,

WlanEnumInterfaces = ::Wlanapi.api("WlanEnumInterfaces","int(int hClientHandle,pointer pReserved,struct& ppInterfaceList)")

pIfList = WLAN_INTERFACE_INFO_LIST();

var dwResult,pIfList2 = WlanEnumInterfaces(hClient,null,pIfList)

io.print(dwResult,pIfList2[
'InterfaceInfo'][1]['InterfaceGuid'])


结果还是输出GUID 00000000-0000-0000-0000-000000000000




接着我又改成这种,API最后一个参数传入指针(我也不知道这算不算是指针)

WlanEnumInterfaces = ::Wlanapi.api("WlanEnumInterfaces","int(int hClientHandle,pointer pReserved,pointer ppInterfaceList)")

pIfList = WLAN_INTERFACE_INFO_LIST();

var pPointer =  raw.buffer(pIfList)

var dwResult = WlanEnumInterfaces(hClient,null,pPointer)

pIfList2 = raw.convert(pPointer,pIfList)

io.print(dwResult,pIfList2[
'InterfaceInfo'][1]['InterfaceGuid'])

结果还是输出GUID 00000000-0000-0000-0000-000000000000




于是我又改,改成传入引用指针

WlanEnumInterfaces = ::Wlanapi.api("WlanEnumInterfaces","int(int hClientHandle,pointer pReserved,pointer& ppInterfaceList)")

pIfList = WLAN_INTERFACE_INFO_LIST();

var pPointer =  raw.buffer(pIfList)

var dwResult = WlanEnumInterfaces(hClient,null,pPointer)

pIfList2 = raw.convert(pPointer,pIfList)

io.print(dwResult,pIfList2[
'InterfaceInfo'][1]['InterfaceGuid'])

结果还是输出GUID 00000000-0000-0000-0000-000000000000




然后我查手册,发现传入参数加&,会有一个返回值,于是我又修改了,加了个返回值

WlanEnumInterfaces = ::Wlanapi.api("WlanEnumInterfaces","int(int hClientHandle,pointer pReserved,pointer& ppInterfaceList)")

pIfList = WLAN_INTERFACE_INFO_LIST();

var pPointer =  raw.buffer(pIfList)

var dwResult,pPointer2 = WlanEnumInterfaces(hClient,null,pPointer)

pIfList2 = raw.convert(pPointer2,pIfList)

io.print(dwResult,pIfList2[
'InterfaceInfo'][1]['InterfaceGuid'])

终于得到了正常的GUID 449b23c8-f18c-4d64-9bbd-221c2c9671fe





关键问题就是:

1.
结构体
struct
32位
table
struct

可以在API参数中使用空表 {} 表示C/C++中的null结构体指针

注意在API参数中,struct被转换为指针按引用传递,如果是按值传递的结构体,需要展开所有成员为普通参数.


手册里这么说"在API参数中,struct被转换为指针按引用传递"
我直接传入struct对象,为什么获取不到值呢?
后来必须用raw.buffer() 把结构体对象复制到内存中,然后传入指向这个结构体的指针 才能正常获取数据

2.
raw.buffer分配定长的内存,并返回托管指针.
参数如果是一个普通的结构体(struct table).raw.buffer创建相同大小的内存并复制结构体的内存数据.

托管指针一般是由指向aardio分配的内存,内存不再使用时会自动释放,是一种内部指针、伪指针。
raw.buffer创建的托管指针并可在API函数中作为pointer指针类型参数使用,也可以可以使用索引操作符直接读写内存数据.这与aardio中的字符串类似,指定索引返回指定位置的字节码,如果索引溢出(过大或过小)会返回0.

像我这个结构体,算是个普通机构体吧?
class WLAN_INTERFACE_INFO_LIST{
    int dwNumberOfItems;
    int dwIndex;
    struct  InterfaceInfo[] = { {
        struct InterfaceGuid = ..win.guid();
        byte strInterfaceDescription[256];
        int isState;
    } };
}

根据上面手册的说明,
"参数如果是一个普通的结构体(struct table).raw.buffer创建相同大小的内存并复制结构体的内存数据."

那我这样用raw.buffer()函数复制的结构体,是一个空的结构体,里面没有任何数据,申请的内存也肯定不大,
如果这个api函数往里面添加数据过多,会不会导致内存错误之类的问题?


最新评论

不争 发表于 2012-11-3 11:17:06

ppInterfaceList 是一个指针的指针, 类型应当声明为 pointer &, pointer &就是说我给你个地址,你把指针的地址写到这里面。

ppInterfaceList 是一个指针的指针,
类型应当声明为 pointer &, pointer &就是说我给你个地址,你把指针的地址写到这里面。

aardio里的pointer等价于C语言里void * (一级指针)
而pointer & 就等价于C语言里的void **(二级指针),简单的讲就是指针的传址,或者说地址的地址。

这里的内存应当是API帮你分配好了,不需要你用raw.buffer()分配。
而应当使用raw.convert() 将指针指向的内存转换为结构体。

你看他C++里的代码是这样写的
PWLAN_INTERFACE_INFO_LIST pIfList
然后传参是这样:WlanEnumInterfaces(hClient, NULL, &pIfList);
这里的 &pIfList 在C++里是取pIfList的地址,就是个二级指针,C++对于指针有很多名字,同一个类型有很多名字,其实在aardio里很简单一级指针就是pointer,二级指针就是pointer &。

那么你可能纠结一个问题,为什么不是直接传一个结构体的指针过去呢?
struct不也是一个指针么?他是指针没错,aardio里的struct实际上传递的是结构体的指针,但是他是一个特殊的指针,结构体的内存是你自已分配好,struct与struct&没有区别,都是一级指针,只不过struct&声明的是输出参数。这些在手册里都有写的。所以如果你发现有二级指针的话,不可能是声明为struct或struct&,这明显是错的。

那为什么你用raw.buffer()分配内存作为参数却也能调用API呢?
其实指针也就是一个数值,指针的指针也还是一个数值,你分配了四字节的内存,当然就可以放得下指针了,取回来你自己转换,你当然也就行得通了。

静态类型很折腾人,比较复杂很多地方很绕,
关于你的代码时间关系我没有仔细看,可能有地方说错了,上面的内容仅供参考。

aardio也就是实现了这么一个机制,把这些复杂的静态类型的调用封装到库里以后,我们使用的时候就可以使用更简单的动态类型,把一切变得更简单,更灵活,而不是在底层的类型上浪费时间。

不争 发表于 2012-11-3 11:22:27

参考手册 [url]http://bbs.aardio.com/doc/reference/libraries/kernel/raw/datatype.htm

参考手册
http://bbs.aardio.com/doc/refere ... l/raw/datatype.html

struct &对应的是C++的void*,
而PWLAN_INTERFACE_INFO_LIST*实质上就是void **,是二级指针,
显然你的用法错误了。

在WINAPI里,通常在结构体前面加一个字母P( pointer的缩写 )表示指针。
例如 PWLAN_INTERFACE_INFO_LIST 表示 WLAN_INTERFACE_INFO_LIST *
PWLAN_INTERFACE_INFO_LIST * 也就表示 WLAN_INTERFACE_INFO_LIST **,
不论什么类型名字,指针都是一个地址,在aardio中表示为pointer
MMiao79 发表于 2012-11-3 11:38:50

马克一下!参与学习~

马克一下!参与学习~
winner 发表于 2012-11-3 11:55:17

[quote][size=2][color=#999999]不争 发表于 2012-11-3 11:22[/color] [url=forum.php?mod=

不争 发表于 2012-11-3 11:22
参考手册
http://bbs.aardio.com/doc/reference/libraries/kernel/raw/datatype.html

这个参数
_Out_       PWLAN_INTERFACE_INFO_LIST *ppInterfaceList
前面有一个 _Out_ ,是不是表示这个参数是输出参数?




WlanEnumInterfaces = ::Wlanapi.api("WlanEnumInterfaces","int(int hClientHandle,pointer pReserved,pointer& ppInterfaceList)")
var dwResult,pPointer = WlanEnumInterfaces(hClient,null,type.pointer)
pIfList2 = raw.convert(pPointer,pIfList)


我发现我只需要传入一个空指针即可,这个函数会返回给我一个指针地址,

然后我根据指针地址定义好的结构体,用raw.convert转换一下就能获取到数据

不知道我这样做对不对?
不争 发表于 2012-11-3 12:07:40

type.pointer 只是类型名,不是指针。 指针类型的参数,你只要传指针进去就可以了,这种输出类型的可以传null(空指针),也就是什么都不写。

type.pointer 只是类型名,不是指针。
指针类型的参数,你只要传指针进去就可以了,这种输出类型的可以传null(空指针),也就是什么都不写。
winner 发表于 2012-11-3 12:16:20

受益匪浅,对aardio的认识 又深了不知一点

受益匪浅,对aardio的认识 又深了不知一点
821165254 发表于 2015-2-12 23:25:31

学习了

学习了
fuluobu 发表于 2015-10-6 10:32:53

[quote][size=2][url=forum.php?mod=redirect&goto=findpost&pid=43816&ptid=8513][co

不争 发表于 2012-11-3 12:07
type.pointer 只是类型名,不是指针。
指针类型的参数,你只要传指针进去就可以了,这种输出类型的可以传n ...

认识 又深了一点
messias 发表于 2017-2-15 05:27:03
学习。。。。。。。。。。。。。

手机版|未经许可严禁引用或转载本站文章|站长邮箱|aardio.com|aardio官方社区 ( 皖ICP备09012014号 )

GMT+8, 2017-9-22 07:10 , Processed in 0.062500 second(s), 21 queries , Wincache On.

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表