TC官方合作论坛

 找回密码
 立即注册
查看: 6535|回复: 17

dllcall使用介绍

  [复制链接]
发表于 2015-4-28 16:13:20 | 显示全部楼层 |阅读模式

马上加入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一般都会被指定缓存长度大小
charchar,CHAR在WIN32中 带字符操作的结构体也是区分 A/W两类函数,分别表示支持ASCII编码还是UNOICDE编码
char 表示ASCII编码的字符串
在结构体中出现char一般都会被指定缓存长度大小
double
double,DOUBLE
double,占位8字节,双精度浮点数
long
long,LONG,DWORD
long,占位4字节,在TC的应用中最为广泛,他不仅仅只能表示数值类型,他还可以表示指针(指针说法比较复杂放在实例中讲解)
longlong
long long,LONGLONGlonglong,占位8字节,某些特殊的api需要
shortshort,SHORT,WORDshort,占位2字节,以无符号形式展示0-65535之间
bytechar,CHAR,UCHAR,,byte,BYTEbyte,占位1字节,以无符号形式展示 0-255之间
struct
pstruct
WIN中的结构体
对应结构体的地址
struct 当结构体出现嵌套的时候,需要指定为此类型
pstruct,某些情况下,结构体里面需要指向另外一个新的结构体那么,就需要用的此种类型,但是结构体指针都是由整型数值来表示
callback回调函数地址回调函数,结构体中也不例外,同样也是会存在此种类型,对应win32中各种回调函数的函数地址,通过callbackmalloc申请,不使用时需使用callbackfree释放


2、常规win3api的使用
常用的使用介绍两种使用方法:
方法1:重新封装MessageBox,让消息框以模态窗口的方式显示在当前窗口上,非常中规中矩的写法,同时也推荐大家使用这种写法
  1. //win32 函数原型
  2. //
  3. //WINUSERAPI int WINAPI MessageBoxW(
  4. //HWND hWnd,
  5. //LPCWSTR lpText,
  6. //LPCWSTR lpCaption,
  7. //UINT uType);

  8. function MsgBox(str,title = "基础库")
  9.     if(m_hwnd == null)
  10.         m_hwnd = windowgetmyhwnd()
  11.     end
  12.     dllcall("user32.dll","int","MessageBoxA","int",m_hwnd,"char *",str,"char *",title,"int",0)
  13. end
复制代码

方法2:把字符串参数以指针的地方传入函数,刚刚上面我们有介绍,指针其实在TC里面也是以long类型来表示的,这种方法不推荐新手使用
  1. //win32 函数原型
  2. //
  3. //WINUSERAPI int WINAPI MessageBoxW(
  4. //HWND hWnd,
  5. //LPCWSTR lpText,
  6. //LPCWSTR lpCaption,
  7. //UINT uType);

  8. function MsgBox(str,title = "基础库")
  9.     if(m_hwnd == null)
  10.         m_hwnd = windowgetmyhwnd()
  11.     end
  12.     var title_pointer = varaddress(title)//获取变量的指针
  13.     var str_pointer =  varaddress(str)//获取变量的指针
  14.     dllcall("user32.dll","int","MessageBoxW","long",m_hwnd,"long",str_pointer,"long",title_pointer,"long",0)//在这里dllcall的 long表示的是变量的指针地址
  15. end
复制代码



3、引用传址类win32api的使用
引用传址在win32api中使用是比较多的,特别是获取一些文件路径或者相关的信息的时候,下面同样也给出两个示例
示例1:字符串引用传址返回,这个里面有一个非常重点的点要注意,因为是引用址的字符串类型,我们需要提前申请好内存,所以,在接收字符串参数的后面一定要指定接收字符串变量的申请缓存大小
否则,dllcall在执行的时候极有可能会引起崩溃
  1. //win32 函数原型
  2. //
  3. //DWORD WINAPI GetModuleFileName(
  4. //    _In_opt_  HMODULE hModule,
  5. //    _Out_     LPTSTR lpFilename,
  6. //    _In_      DWORD nSize
  7. //);

  8. function GetModuleFileNameW(hMod = 0)
  9.     var modName                         //用于接收获取到的进程模块的路径
  10.     var max_path = 255                //进程模块路径最大长度为255,超过255会被自动截断
  11.     //当大家自己写这种方式引用返回的时候需要注意一个问题
  12.     //"lpFilename"参数后面一定要指定一个路径存储的大小,这个申请的大小是用于内部申请内存,缓存我们返回的结果使用的
  13.     dllcall("kernel32.dll","int","GetModuleFileNameW","long",hMod,"pwchar *",modName,"long",max_path)
  14.     return modName
  15. end
复制代码

示例2:
整型值传址,整型值传地址的话比起字符串就要轻松很多,因为整型缓存大小是固定的,我们不需要特意去指定它,接下来我们还是看实际代码
  1. //win32 函数原型
  2. //
  3. //DWORD GetWindowThreadProcessId(
  4. //HWND hWnd,
  5. //LPDWORD lpdwProcessId
  6. //);
  7. //通过窗口句柄获取进程pid
  8. function GetWindowThreadProcessId(hwnd)
  9.     var pid //整型的引起址非常简单,只需要记住类型是plong就可以了,同时注意一点,指针地址,不能使用这个类型传入,会引起dllcall功能崩溃
  10.     dllcall("user32.dll","int","GetWindowThreadProcessId","int",hwnd,"plong",pid)
  11.     return pid
  12. end
复制代码



4、常规结构体的win32api的使用稍等更新


5、引用传址结构体的win32api的使用
在结构体中,TC是使用数组来表示的与WIN32标准结构体还是有一些区别,下面我们详细解析一下TC中结构体数组中每个元素表示的意思
TC结构体数组中,他的本质是一个二维数组,(在多层结构体嵌套的情况下,他就可能会变成更多维)
我们先看一个简单的结构体数组定义,同时为了方便理解,也同样声明了一个win32结构体
  1. //此代码 是一个比较示例, 复制无法执行,请不要拷贝到TC工具编辑器中执行
  2. //    var point = array()                                   typedef struct tagPOINT
  3. //                                                        {
  4. //    point["x"] = array("long"=1,"value"=0)                  LONG  x;
  5. //    point["y"] = array("long"=1,"value"=0)                  LONG  y;
  6. //                                                         }
复制代码
从上面可以看出
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:
我们以最简单的,获取当前鼠标坐标入手
  1. //定义一个函数专门用于 生成结构体,方便重复利用
  2. function tagPOINT()
  3.     //win32中结构体原型
  4.     //typedef struct tagPOINT
  5.     //{
  6.     //    LONG  x;
  7.     //    LONG  y;
  8.     //}
  9.     //------结构体类型定义------
  10.     var point = array()
  11.     point["x"] = array("long"=1,"value"=0)
  12.     point["y"] = array("long"=1,"value"=0)
  13.     //------结构体类型定义------
  14.     return point
  15. end
  16. //BOOL WINAPI GetCursorPos(
  17. //  _Out_ LPPOINT lpPoint
  18. //);
  19. function GetCursorPos()
  20.     var point = tagPOINT()
  21.     //调用 GetCursorPos 函数获取当前鼠标坐标
  22.     dllcall("user32.dll","long","GetCursorPos","pstruct",point)
  23.     messagebox("x:"&point["x"]["value"]&",y:"&point["y"]["value"])
  24. end
复制代码



6、带回调函数的win32api的使用
回调函数在win32应用中使用也是比较广泛的,对于回调函数的具体支持的数量大家可以详细参考帮助文档回调函数在dllcall中使用是非常简单的,首先我们需要使用对应的TC函数申请一个回调函数的指针,然后把这个指针传递给对应的win32函数的参数就可以直接使用的
下面是一个全局钩子的示例,相信大部分人也比较感觉兴趣

示例:键盘按键记录器
  1. 变量 h
  2. 功能 hookProc(code,w,l)
  3.     如果(w == 256)//w参数 表示是 键盘按下或者弹起
  4.         调试输出("按键按下:"&地址取值(l,"long"))
  5.     else
  6.         调试输出("按键弹起:"&地址取值(l,"long"))
  7.     结束
  8.     返回 动态库调用("user32.dll","long","CallNextHookEx","long",hook,"long",code,"long",w,"long",l)//当code小于0的时候一定要把消息传递给他一个子程
  9. 结束

  10. 变量 hook
  11. 功能 设置钩子_点击()
  12.     //这里添加你要执行的代码
  13.     变量 hmod = 动态库调用("kernel32.dll","long","GetModuleHandleA","long",0)
  14.     h = 回调函数申请("hookProc","hookproc")
  15.     hook = 动态库调用("user32.dll","long","SetWindowsHookExA","long",13,"callback",h,"long",hmod,"long",0)//执行setwindowhook函数 挂起全局钩子,13全局钩子
  16.     调试输出(获取错误信息(1))//这里获取dllcall执行后是否有错误信息
  17.     调试输出(hook)//输出钩子是否设置成功
  18. 结束

  19. 功能 卸载钩子_点击()
  20.     //这里添加你要执行的代码
  21.     变量 ret = 动态库调用("user32.dll","long","UnhookWindowsHookExA","long",hook)//删除钩子句柄 当hook不需要的时候记得一定要删除
  22.     回调函数释放(h)//删除钩子句柄之后 不要忘了 释放我们的回调函数,否则TC对应的函数里面一直会输出信息
  23. 结束
复制代码






评分

参与人数 2威望 +10 金币 +20 收起 理由
laowantong + 10 + 10 很给力!
sam7894604 + 10 前排~~

查看全部评分

回复

使用道具 举报

发表于 2015-4-28 16:42:41 | 显示全部楼层
期待更新
回复

使用道具 举报

发表于 2015-4-28 16:48:37 | 显示全部楼层
前排围观了阿!!!!!
回复 支持 反对

使用道具 举报

发表于 2015-4-28 16:51:02 | 显示全部楼层
前排点赞
回复

使用道具 举报

发表于 2015-4-28 17:01:28 | 显示全部楼层
呵呵,看看,很好的东西。
回复 支持 反对

使用道具 举报

发表于 2015-4-28 19:21:38 | 显示全部楼层
回复

使用道具 举报

发表于 2015-5-24 16:30:37 | 显示全部楼层
开始弄结构,声明中 array("long"=1,"value"=0)  long后面的1是什么意思?

Private Type WSAData

    wVersion As Integer

    wHighVersion As Integer

    szDescription(0 To 255) As Byte

    szSystemStatus(0 To 128) As Byte

    iMaxSockets As Integer

    iMaxUdpDg As Integer

    lpVendorInfo As Long
End type

以前能不能给弄个TC的例子,或许比较直观明白
回复 支持 反对

使用道具 举报

发表于 2015-5-24 22:35:29 来自手机 | 显示全部楼层
学习        
回复 支持 反对

使用道具 举报

发表于 2015-10-28 21:18:57 | 显示全部楼层
"long"=1                                                    成员类型数组中第一个元素,其中key或者说下标("long") 是可变的,对应TC结构体与WIN32结构体类型表;值表示类型是否为数组以及数组的大小, 是暂时只有char 与wchar两种类型有效,其他类型暂时预留

这还是说得很不清楚,不明白地方是,后面的数值1表示 是否数组,1是表示非数组?那0表示什么,大于等于2以上的值又表示什么?

第二个问题, 暂时只有char和wchar两种类型有效,指的是什么,那long不也是可以吗?你在说什么啊?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-10-29 09:25:56 | 显示全部楼层
venski 发表于 2015-10-28 21:18
"long"=1                                                    成员类型数组中第一个元素,其中key或者说下 ...

除 char wchar 以外的类型暂时是预留 没有做对数组的支持
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条

关闭

小黑屋|TC官方合作论坛 (苏ICP备18045623号)

GMT+8, 2024-5-2 22:01 , Processed in 0.042174 second(s), 24 queries .

Powered by 海安简单软件服务部

© 2008-2019 版权所有 保留所有权利

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