飞跃、空等大佬关于dllcall高级应用的精彩阐述整理-dbgba

群友问:

SetWorkingDir %A_ScriptDir%
ret:=DllCall("rime.dll\rime_get_api", "Ptr")
MsgBox % ErrorLevel

@梦醒(FeiYue) 麻烦看下

空 答:
结构体类比于姓名 不能靠提供“姓名”就去叫别人 而需要实际的叫出 张三 李四等等

或者再类比一个 密码吧 就是叫你输入密码的时候 不是让输入密码两个字 明白了吧

飞跃 答:
要调用api函数,你首先要知道函数的原型声明,说明需要哪些参数,各个参数需不需要数据结构和字符串,返回值是错误代码还是有效的数据。
看你的调用,这个函数不需要任何参数,而且返回一个数字。

具体调用方法,需要看到原型声明才知道。

群友问:
【萌新】䑝曻-AHK1 19:23:40
@梦醒(FeiYue) 不是数字 是个结构体指针

【萌新】䑝曻-AHK1 19:27:57

RimeApi* rime_get_api();

【萌新】䑝曻-AHK1 19:28:05
这样的

【萌新】䑝曻-AHK1 19:30:55
RimeApi 是个struct 里面有多函数指针

梦醒(FeiYue)  答:
其实调用Dll中的函数,跟调用AHK自身的函数是类似的,简单看看API函数需要几个参数:

第一步:写上 DllCall(“dll路径\api函数名”, ptr,参数1, ptr,参数2, ptr,参数3, ptr,参数4, “返回类型Ptr 和 可能的CDecl”)。
第二步:有的参数需要数据结构,就要 VarSetCapacity(var, 100) 申请内存,NumPut()写入数据到数据结构。
第三步:有的参数是字符串,就把对应的Ptr类型改为str类型,如果字符串要转码的话,可以使用AStr或者WStr。
第四步:有的参数可以通过参数给的地址传出一个数值数据,就把对应的ptr改为”ptr*”或者”uint*“类型。
第五步:再检查一下很少见的参数有没有浮点数类型,将对应ptr改为float或double,
             有没有规定用64位整数的,比如long long或int64,将对应的ptr改为int64。
第六步:等DllCall调用完毕后,可以用NumGet()从数据结构中获取数据,或者用StrGet()获取字符串。

【萌新】䑝曻-AHK1 19:34:47
我看了大多数别人写的例子 都没怎么关注函数的返回值

【萌新】䑝曻-AHK1 19:35:51
“返回类型Ptr 和 可能的CDecl” 关注这种的例子真的很少

梦醒(FeiYue)  答:
DllCall默认返回int类型,是32位的,如果返回指针的话,一般不能用默认的,要用ptr以适应64位地址

对于输入的参数(用__in__标记)int和ptr是等效的,但是对于输出的参数和返回值,要注意ptr*可以返回64位,int*返回32位。返回类型也一样。

RimeApi* rime_get_api();  这个不需要参数就很容易

ret:=DllCall("rime.dll\rime_get_api", "Ptr")
MsgBox % ret  ; 这个ret就是数据结构的内存地址指针

【萌新】䑝曻-AHK1 19:43:10
但是我这么写   MsgBox % ErrorLevel 不为0

【管理员】梦醒(FeiYue) 19:43:47
你干嘛要关心ErrorLevel呢

【萌新】䑝曻-AHK1 19:44:29
呃 因为MsgBox % ret 没东西

【萌新】䑝曻-AHK1 19:44:35
我才关注的

hicon := DllCall("User32.dll\CreateIconFromResourceEx", "Ptr",&BIL, "Int",nBytes, "Int",True, "Int",0x30000
                 , "Int",0, "Int",0, "Int",0, "Ptr")

【管理员】梦醒(FeiYue) 19:46:46
DllCall调用要注意,AHK如果是64位的,它将每个参数作为64位数值传递给DLL函数,
如果DLL编译为32位的,函数接收的每个参数都是假定32位的,就会错误。
所以64位的DLL用64位的AHK调用,用32位的调用肯定失败。

【管理员】梦醒(FeiYue) 19:47:11
比如调用这个WinAPI函数

int MessageBox(
  HWND    hWnd,
  LPCTSTR lpText,
  LPCTSTR lpCaption,
  UINT    uType
);

内容:="你好呀,这是一个测试!", 标题:="欢迎界面"
DllCall("MessageBox", ptr,0, ptr,&内容, ptr,&标题, ptr,0)
DllCall("MessageBox", ptr,0, str,内容, str,标题, ptr,0)

【萌新】䑝曻-AHK1 19:56:53
你要是能给我举个 返回值不为基础类型的函数就好了

【管理员】梦醒(FeiYue) 19:57:34
传递字符串其实是传递字符串的内存首地址,所以用&变量取内存首地址。
str类型可以自动帮我们传递内存首地址,所以就不用&取地址了。

 

【萌新】䑝曻-AHK1 19:59:10
我的问题是不是出在32位上面啊 我是按你这样写的@梦醒(FeiYue)

【管理员】梦醒(FeiYue) 19:59:34
我不是说了吗 :
DllCall默认返回int类型,是32位的,如果返回指针的话,一般不能用默认的,要用ptr以适应64位地址
ret:=DllCall(“rime.dll\rime_get_api”, “Ptr”)  这个调用就应该返回ptr类型

【萌新】䑝曻-AHK1 20:00:18
嗯 我是这么写的

【萌新】䑝曻-AHK1 20:00:34
但是失败了

 

【管理员】梦醒(FeiYue) 20:01:12
返回值是指针地址的情况是很常见的。包括句柄也是指针。

调用的格式是没错的,可能需要一些其他条件才能运行。

是不是要初始化呢?这就要看API的说明文档了。最好看看别人用C++是怎么调用的。

 

@䑝曻-AHK1  我初步看了一下,好像要先安装输入法,才能调用输入法的函数。

void RimeSetup(RimeTraits *traits);       这个是安装的。
void RimeInitialize(RimeTraits *traits);   这个是初始化的。

先计算数据结构大小,再先后调用:

VarSetCapacity(var, A_PtrSize*12)
DllCall("rime.dll\RimeSetup", ptr,&var)
DllCall("rime.dll\RimeInitialize", ptr,&var)
ret:=DllCall("rime.dll\rime_get_api", "Ptr")

 typedef struct rime_traits_t {
  int data_size;   //4 -->对齐实际占用ptr
  const char* shared_data_dir; //ptr
  const char* user_data_dir; //ptr
  const char* distribution_name; //ptr
  const char* distribution_code_name; //ptr
  const char* distribution_version; //ptr
  const char* app_name; //ptr
  const char** modules; //ptr
  int min_log_level; //4 -->对齐实际占用ptr
  const char* log_dir; //ptr
  const char* prebuilt_data_dir; //ptr
  const char* staging_dir; //ptr
} RimeTraits; //总大小:ptr*12

 

;===========H版的推荐==============
【萌新】任性 20:10:09
ahk有个Struct库用来DllCall调用系统API挺好用的,结构体直接复制

【萌新】任性 20:10:34
AHK_H自带这个功能

 

飞跃的一些补充:

比如调用这个WinAPI函数

int MessageBox(
  HWND    hWnd,
  LPCTSTR lpText,
  LPCTSTR lpCaption,
  UINT    uType
);

内容:="你好呀,这是一个测试!", 标题:="欢迎界面"
DllCall("MessageBox", "ptr", 0, "ptr", &内容, "ptr", &标题, "ptr",0)
DllCall("MessageBox", "ptr",0, "str",内容, "str",标题, "ptr",0)

 

dllcall调用其实很简单,因为每个参数都会自动对齐到ptr类型的字节数,所以基本上参数类型全部用Ptr或者int就行。

例外的是,要明白参数类型的必要原因两个:

  1. ptr表达不了浮点数,所以如果API的声明指出该参数是浮点数,就要用float或double。
  2. 在32位系统中ptr是4字节,但是如果API的声明指出该参数是64位的long long,参数自动对齐到不了8字节,就要用int64。

除了前面的两个必要原因,都可以用Ptr或int。
输入字符串是字符串的变量地址,即&变量。

另外为了让ahk帮我们转换字符串和从Ptr地址帮我们读取API声明中的输出值,我们对字符串不要自己取地址&变量,而用str/astr/wstr类型,对于API的地址输出值不用Ptr,而用uint*或者Ptr*,让ahk帮我们转换字符串或读取出值。

最终,也就是先全写Ptr,然后考虑必要的两种情况,再考虑有好处的两种情况,参数类型的设置就完成了,万变不离其宗。

给TA买糖
共{{data.count}}人
人已赞赏
AHKV1学习

【函数】调出文件夹选择对话框

2021-11-24 8:59:23

AHKV1办公

AHK利用地址栏实现网页自动化By FeiYue

2021-11-25 9:23:24

4 条回复 A文章作者 M管理员
  1. 1河许人

    关于返回值是结构体的,我记得好像讨论过两次了,最后也是没有什么完美的结果,还得具体问题具体分析。

  2. 月下马

    我来抄写笔记了:
    调用Dll中的函数,跟调用AHK自身的函数是类似的,简单看看API函数需要几个参数:

    第一步:写上 DllCall(“dll路径api函数名”, ptr,参数1, ptr,参数2, ptr,参数3, ptr,参数4, “返回类型Ptr 和 可能的CDecl”)。
    第二步:有的参数需要数据结构,就要 VarSetCapacity(var, 100) 申请内存,NumPut()写入数据到数据结构。
    第三步:有的参数是字符串,就把对应的Ptr类型改为str类型,如果字符串要转码的话,可以使用AStr或者WStr。
    第四步:有的参数可以通过参数给的地址传出一个数值数据,就把对应的ptr改为”ptr*”或者”uint*“类型。
    第五步:再检查一下很少见的参数有没有浮点数类型,将对应ptr改为float或double,
    有没有规定用64位整数的,比如long long或int64,将对应的ptr改为int64。
    第六步:等DllCall调用完毕后,可以用NumGet()从数据结构中获取数据,或者用StrGet()获取字符串。

  3. maxs

    看不懂,已经超纲了

  4. sibogao

    基础都看不太通顺,还需要学习呀

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索