马上加入TC
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
随着5.0的正式版的发布,越来越多的同学喜欢使用dllcall的功能,但是在使用的过程也出现了许多的问题.今天给大家系统的讲解一下关于tc dllcall调用win32api的方法,以及使用过程中需要注意的一些细节
总共分为六部分
1、TC dllcall类型与win32api类型对应关系
2、常规win3api的使用
3、引用传址类win32api的使用
4、常规结构体的win32api的使用
5、引用传址结构体的win32api的使用
6、带回调函数的win32api的使用
1、TC dllcall类型与win32api类型对应关系
在MSDN中,在参数前面会有_In_ 与 _Out_ 两类标识符来区分,参数是传入,还是按址传出
TC代码中的参数 | WIN32中的参数类型 | 说明 | wchar *
pwchar * | LPCWSTR,LPWSTR,LPCWCH等等 | 在WIN32中 带字符串操作的的函数都会区分 A/W两类函数,分别表示支持ASCII编码还是UNOICDE编码
wchar * 表示UNIOCDE编码的字符串,对应WIN32函数中以W尾的函数,例如MessageBoxW
同时TC内部对于字符处理都是使用的UNICODE编码,所以推荐大家使用dllcall尽量使用W类的函数,
效率会适当的提升
pwchar * 表示的是传址,就是说函数的字符串要通过参数返回到TC里面来,在WIN32里面,有许多获
取字符信息的函数需要这样操作 例如GetModuleFileNameW | char *
pchar * | LPCSTR,LPSTR,LPCCH等等 | 在WIN32中 带字符串操作的的函数都会区分 A/W两类函数,分别表示支持ASCII编码还是UNOICDE编码
char * 表示ASCII编码的字符串,对应WIN32函数中以A尾的函数,例如MessageBoxA
同时TC内部对于字符处理都是使用的UNICODE编码,使用A类字符串相关函数,内部会做一层转换,所以不推荐大家频繁使用,会影响部分效率
pchar * 表示的是传址,就是说函数的字符串要通过参数返回到TC里面来,在WIN32里面,有许多获
取字符信息的函数需要这样操作 例如GetModuleFileNameA | double
pdouble | double,DOUBLE
| _In_ double,占位8字节,双精度浮点数
_Out_ pdouble,8字节浮点数据引用传值 | float
pfloat | float,FLOAT
| _In_ float,占位4字节,单精度浮点数
_Out_ pfloat,8字节单精浮点数据引用传值 | long
plong | long,LONG,DWORD
PWORD,LPWORD,PINT,LPINT | _In_ long,占位4字节,在TC的应用中最为广泛,他不仅仅只能表示数值类型,他还可以表示指针(指针说法比较复杂放在实例中讲解)
_Out_ plong,整型数用引用传址,应用场景,例如:GetWindowThreadProcessId | longlong
plonglong
| long long,LONGLONG | _In_ longlong,占位8字节,某些特殊的api需要应用到长整型,例如StrFormatByteSizeW
_Out_ plonglong,此类型暂时预留不支持使用 | short
pshort | short,SHORT,WORD
PWORD,LPWORD | _In_ short,占位2字节,以无符号形式展示0-65535之间 _Out_ pshort,短型数用引用传址
_Out_ pshort,字节用引用传址 | byte
pbyte | char,CHAR,UCHAR,,byte,BYTE
PBYTE,LPBYTE | _In_ byte,占位1字节,以无符号形式展示 0-255之间
_Out_ pbyte,字节用引用传址 | struct
pstruct | WIN中的结构体
对应结构体的地址 | _In_ struct 普通参数的形式传入函数 应用场景,如:WindowFromPoint
_Out_ pstruct,结构体内部成员需要被函数修改,其实传入的是结构体指针,应用场景,如:GetCursorPos | callback | 回调函数地址 | 回调函数,对应win32中各种回调函数的函数地址,通过callbackmalloc申请,不使用时需使用callbackfree释放 | 常规类型请按上面请对号入座,如果没有按照对应类型,TC会在内部自动当成 long型处理,某些时间就会引起一莫名期妙的崩溃,所以大家在使用dllcall的时候一定要注意自己的类型
TC结构体类型与WIN32结构体类型对应表TC参数 | WIN32中的参数类型 | 说明 | wchar
| wchar,WCHAR | 在WIN32中 带字符操作的结构体也是区分 A/W两类函数,分别表示支持ASCII编码还是UNOICDE编码
wchar 表示UNICODE编码的字符串
在结构体中出现wchar一般都会被指定缓存长度大小 | char | char,CHAR | 在WIN32中 带字符操作的结构体也是区分 A/W两类函数,分别表示支持ASCII编码还是UNOICDE编码
char 表示ASCII编码的字符串
在结构体中出现char一般都会被指定缓存长度大小 | double
| double,DOUBLE
| double,占位8字节,双精度浮点数 | long
| long,LONG,DWORD
| long,占位4字节,在TC的应用中最为广泛,他不仅仅只能表示数值类型,他还可以表示指针(指针说法比较复杂放在实例中讲解)
| longlong
| long long,LONGLONG | longlong,占位8字节,某些特殊的api需要 | short | short,SHORT,WORD | short,占位2字节,以无符号形式展示0-65535之间 | byte | char,CHAR,UCHAR,,byte,BYTE | byte,占位1字节,以无符号形式展示 0-255之间 | struct
pstruct | WIN中的结构体
对应结构体的地址 | struct 当结构体出现嵌套的时候,需要指定为此类型
pstruct,某些情况下,结构体里面需要指向另外一个新的结构体那么,就需要用的此种类型,但是结构体指针都是由整型数值来表示 | callback | 回调函数地址 | 回调函数,结构体中也不例外,同样也是会存在此种类型,对应win32中各种回调函数的函数地址,通过callbackmalloc申请,不使用时需使用callbackfree释放 |
2、常规win3api的使用
常用的使用介绍两种使用方法:
方法1:重新封装MessageBox,让消息框以模态窗口的方式显示在当前窗口上,非常中规中矩的写法,同时也推荐大家使用这种写法- //win32 函数原型
- //
- //WINUSERAPI int WINAPI MessageBoxW(
- //HWND hWnd,
- //LPCWSTR lpText,
- //LPCWSTR lpCaption,
- //UINT uType);
- function MsgBox(str,title = "基础库")
- if(m_hwnd == null)
- m_hwnd = windowgetmyhwnd()
- end
- dllcall("user32.dll","int","MessageBoxA","int",m_hwnd,"char *",str,"char *",title,"int",0)
- end
复制代码
方法2:把字符串参数以指针的地方传入函数,刚刚上面我们有介绍,指针其实在TC里面也是以long类型来表示的,这种方法不推荐新手使用- //win32 函数原型
- //
- //WINUSERAPI int WINAPI MessageBoxW(
- //HWND hWnd,
- //LPCWSTR lpText,
- //LPCWSTR lpCaption,
- //UINT uType);
- function MsgBox(str,title = "基础库")
- if(m_hwnd == null)
- m_hwnd = windowgetmyhwnd()
- end
- var title_pointer = varaddress(title)//获取变量的指针
- var str_pointer = varaddress(str)//获取变量的指针
- dllcall("user32.dll","int","MessageBoxW","long",m_hwnd,"long",str_pointer,"long",title_pointer,"long",0)//在这里dllcall的 long表示的是变量的指针地址
- end
复制代码
3、引用传址类win32api的使用
引用传址在win32api中使用是比较多的,特别是获取一些文件路径或者相关的信息的时候,下面同样也给出两个示例
示例1:字符串引用传址返回,这个里面有一个非常重点的点要注意,因为是引用址的字符串类型,我们需要提前申请好内存,所以,在接收字符串参数的后面一定要指定接收字符串变量的申请缓存大小
否则,dllcall在执行的时候极有可能会引起崩溃- //win32 函数原型
- //
- //DWORD WINAPI GetModuleFileName(
- // _In_opt_ HMODULE hModule,
- // _Out_ LPTSTR lpFilename,
- // _In_ DWORD nSize
- //);
- function GetModuleFileNameW(hMod = 0)
- var modName //用于接收获取到的进程模块的路径
- var max_path = 255 //进程模块路径最大长度为255,超过255会被自动截断
- //当大家自己写这种方式引用返回的时候需要注意一个问题
- //"lpFilename"参数后面一定要指定一个路径存储的大小,这个申请的大小是用于内部申请内存,缓存我们返回的结果使用的
- dllcall("kernel32.dll","int","GetModuleFileNameW","long",hMod,"pwchar *",modName,"long",max_path)
- return modName
- end
复制代码
示例2:
整型值传址,整型值传地址的话比起字符串就要轻松很多,因为整型缓存大小是固定的,我们不需要特意去指定它,接下来我们还是看实际代码
- //win32 函数原型
- //
- //DWORD GetWindowThreadProcessId(
- //HWND hWnd,
- //LPDWORD lpdwProcessId
- //);
- //通过窗口句柄获取进程pid
- function GetWindowThreadProcessId(hwnd)
- var pid //整型的引起址非常简单,只需要记住类型是plong就可以了,同时注意一点,指针地址,不能使用这个类型传入,会引起dllcall功能崩溃
- dllcall("user32.dll","int","GetWindowThreadProcessId","int",hwnd,"plong",pid)
- return pid
- end
复制代码
4、常规结构体的win32api的使用稍等更新
5、引用传址结构体的win32api的使用
在结构体中,TC是使用数组来表示的与WIN32标准结构体还是有一些区别,下面我们详细解析一下TC中结构体数组中每个元素表示的意思
TC结构体数组中,他的本质是一个二维数组,(在多层结构体嵌套的情况下,他就可能会变成更多维)
我们先看一个简单的结构体数组定义,同时为了方便理解,也同样声明了一个win32结构体
- //此代码 是一个比较示例, 复制无法执行,请不要拷贝到TC工具编辑器中执行
- // var point = array() typedef struct tagPOINT
- // {
- // point["x"] = array("long"=1,"value"=0) LONG x;
- // point["y"] = array("long"=1,"value"=0) LONG y;
- // }
复制代码 从上面可以看出
point["x"] point中的 key或者说下标 ("x"),他对应的是 win32结构体中的 成员名称 x
array("long"=1,"value"=0) point中的值 ,这里面存储了一个数组,他表示成员的类型,以及成员的值,数组有两固定的元素
"long"=1 成员类型数组中第一个元素,其中key或者说下标("long") 是可变的,对应TC结构体与WIN32结构体类型表;值表示类型是否为数组以及数组的大小, 是暂时只有char 与wchar两种类型有效,其他类型暂时预留
"value"=0 成员类型数组中第二个元素,其中key或者说下标("value")是固定为"value",否则执行会出错,错误信息为错误的结构体类型;值表示win32结构体中,成员的值
示例1:
我们以最简单的,获取当前鼠标坐标入手- //定义一个函数专门用于 生成结构体,方便重复利用
- function tagPOINT()
- //win32中结构体原型
- //typedef struct tagPOINT
- //{
- // LONG x;
- // LONG y;
- //}
- //------结构体类型定义------
- var point = array()
- point["x"] = array("long"=1,"value"=0)
- point["y"] = array("long"=1,"value"=0)
- //------结构体类型定义------
- return point
- end
- //BOOL WINAPI GetCursorPos(
- // _Out_ LPPOINT lpPoint
- //);
- function GetCursorPos()
- var point = tagPOINT()
- //调用 GetCursorPos 函数获取当前鼠标坐标
- dllcall("user32.dll","long","GetCursorPos","pstruct",point)
- messagebox("x:"&point["x"]["value"]&",y:"&point["y"]["value"])
- end
复制代码
6、带回调函数的win32api的使用
回调函数在win32应用中使用也是比较广泛的,对于回调函数的具体支持的数量大家可以详细参考帮助文档回调函数在dllcall中使用是非常简单的,首先我们需要使用对应的TC函数申请一个回调函数的指针,然后把这个指针传递给对应的win32函数的参数就可以直接使用的
下面是一个全局钩子的示例,相信大部分人也比较感觉兴趣
示例:键盘按键记录器
- 变量 h
- 功能 hookProc(code,w,l)
- 如果(w == 256)//w参数 表示是 键盘按下或者弹起
- 调试输出("按键按下:"&地址取值(l,"long"))
- else
- 调试输出("按键弹起:"&地址取值(l,"long"))
- 结束
- 返回 动态库调用("user32.dll","long","CallNextHookEx","long",hook,"long",code,"long",w,"long",l)//当code小于0的时候一定要把消息传递给他一个子程
- 结束
- 变量 hook
- 功能 设置钩子_点击()
- //这里添加你要执行的代码
- 变量 hmod = 动态库调用("kernel32.dll","long","GetModuleHandleA","long",0)
- h = 回调函数申请("hookProc","hookproc")
- hook = 动态库调用("user32.dll","long","SetWindowsHookExA","long",13,"callback",h,"long",hmod,"long",0)//执行setwindowhook函数 挂起全局钩子,13全局钩子
- 调试输出(获取错误信息(1))//这里获取dllcall执行后是否有错误信息
- 调试输出(hook)//输出钩子是否设置成功
- 结束
- 功能 卸载钩子_点击()
- //这里添加你要执行的代码
- 变量 ret = 动态库调用("user32.dll","long","UnhookWindowsHookExA","long",hook)//删除钩子句柄 当hook不需要的时候记得一定要删除
- 回调函数释放(h)//删除钩子句柄之后 不要忘了 释放我们的回调函数,否则TC对应的函数里面一直会输出信息
- 结束
复制代码
|