开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 345|回复: 9
收起左侧

[易语言] 关于包装回调函数 __cdecl

[复制链接]
结帖率:67% (16/24)
发表于 2026-6-4 14:54:03 | 显示全部楼层 |阅读模式   福建省厦门市
500精币
以下代码来自白银大佬的取类回调函数【包装】,它支持回调函数多种参数类型,缺点是不能外部调用,包装外部函数

有没有大佬能改改,让它能包装外部函数,谢谢了

重点说三遍:
需要支持多种参数类型       需要支持多种参数类型            需要支持多种参数类型

  
子程序名返回值类型公开备 注
取类回调函数2_ASM整数型 包装,适用于将函数指针传递到其他语言编写的DLL库或支持库中!(E也可以直接用这个但不推荐!)只能在类里面调用,释放直接用HeapFree   Code By:2962946246 (白银大佬)
参数名类 型参考可空数组备 注
index整数型索引从1开始,【_初始化】和【_销毁】 不计数
cdecl逻辑型默认:假
变量名类 型静态数组备 注
fn整数型 
fn = HeapAlloc (GetProcessHeap (), 0, 20)
如果真 (fn ≠ 0)
VirtualProtect (fn, 20, 64, 0)
置入代码 ({ 232, 88, 0, 0, 0, 185, 128, 0, 0, 0, 84, 85, 86, 87, 83, 141, 116, 36, 24, 41, 204, 252, 137, 231, 137, 229, 243, 164, 82, 255, 208, 41, 236, 141, 189, 148, 0, 0, 0, 131, 196, 4, 1, 103, 252, 141, 103, 236, 139, 15, 91, 95, 94, 93, 92, 255, 225, 185, 128, 0, 0, 0, 85, 86, 87, 83, 141, 116, 36, 20, 41, 204, 252, 137, 229, 137, 231, 243, 164, 82, 255, 208, 141, 165, 128, 0, 0, 0, 91, 95, 94, 93, 195, 139, 69, 252, 131, 125, 12, 1, 141, 72, 219, 141, 80, 15, 92, 198, 0, 186, 198, 64, 5, 184, 15, 68, 209, 198, 64, 10, 233, 41, 212, 141, 72, 16, 137, 96, 11, 201, 139, 85, 8, 137, 72, 1, 139, 18, 137, 17, 139, 76, 36, 4, 139, 18, 139, 76, 138, 4, 137, 72, 6, 194, 12, 0 })
返回 (0)


回答提醒:如果本帖被关闭无法回复,您有更好的答案帮助楼主解决,请发表至 源码区 可获得加分喔。
友情提醒:本版被采纳的主题可在 申请荣誉值 页面申请荣誉值,获得 1点 荣誉值,荣誉值可兑换荣誉会员、终身vip用户组。
快捷通道:申请荣誉值
结帖率:100% (29/29)

签到天数: 27 天

发表于 2026-6-4 18:04:05 | 显示全部楼层   浙江省温州市
我用的全局变量,储存的
回复

使用道具 举报

结帖率:100% (53/53)

签到天数: 27 天

发表于 2026-6-4 19:15:16 | 显示全部楼层   福建省宁德市
说实话,没看懂你的需求,你是要将易语言的子程序包装成cdecl调用约定的函数地址,然后返回cdecl函数地址?
回复

使用道具 举报

结帖率:80% (4/5)

签到天数: 20 天

发表于 2026-6-4 19:44:18 | 显示全部楼层   广西壮族自治区柳州市
类成员函数因参数问题需要包装,非类成员函数是不需要包装的吧。
stdcall跟cdecl就是cdecl不需要函数返回时平栈,那么改一下函数返回不就行了
函数 返回()前面
leave
ret
置入代码({201,195,0})
至于技支什么类型的参数,易语言自身有限制,能取到函数地址的函数,参数只能用数值(字节型,短整数型,整数型,长整数型,小数型,双精度小数型)和文本类型
回复

使用道具 举报

结帖率:100% (10/10)
发表于 2026-6-4 21:02:13 | 显示全部楼层   北京市北京市
这段代码是易语言中用于动态生成回调函数存根(Stub) 的汇编代码。它的作用是:在堆中分配一块可执行内存,动态写入机器码,最终返回一个函数指针,这个指针可以传递给其他语言(如C/C++的DLL)作为回调函数使用。

核心功能分析
1. 原本的限制
注释已经说明:“只能在类里面调用”,因为生成的存根依赖于:

类的 _初始化 和 _销毁 方法(不计数)

类内部的虚表布局(index 参数用于定位类方法)

2. 为什么不能直接用于外部函数?
这段代码生成的存根会做以下假设:

第一个参数(ecx) 隐含地作为类实例的 this 指针

通过 this 指针 + index 偏移来调用类方法

外部普通函数没有 this 指针,参数传递方式也不同

如何修改以支持外部函数?
需要生成直接跳转到目标函数地址的简单存根,而不是通过类的虚表。以下是修改方案:

方案一:最简单的直接跳转存根
易语言
.子程序 取外部函数回调_ASM, 整数型, 公开
.参数 目标函数地址, 整数型
.参数 cdecl, 逻辑型, 可空
.局部变量 fn, 整数型
.局部变量 代码大小, 整数型

' cdecl = 真 需要平衡栈,假 则使用 stdcall
代码大小 = 选择(cdecl, 6, 5)  ' stdcall: jmp [地址] 需要5字节? 实际要看跳转方式

fn = HeapAlloc (GetProcessHeap (), 8, 代码大小)
.如果真 (fn ≠ 0)
    VirtualProtect (fn, 代码大小, 64, 0)
   
    .如果真 (cdecl)
        ' 对于cdecl调用约定,可以直接jmp
        置入代码 ({ 233, 0, 0, 0, 0 })  ' jmp rel32
        ' 需要计算相对偏移:目标地址 - (fn + 5)
    .否则
        ' stdcall 或 fastcall 需要处理
        ' 最简单的:push 目标地址高位和低位后 ret
        ' 或 mov eax, 目标地址; jmp eax
    .如果真结束
.如果真结束
方案二:完整的通用包装器(推荐)
易语言
.子程序 包装外部函数, 整数型, 公开
.参数 目标函数地址, 整数型
.参数 参数个数, 整数型
.参数 cdecl, 逻辑型, 可空
.局部变量 fn, 整数型
.局部变量 代码, 字节集

' 生成机器码存根:
' 对于 stdcall:参数已由调用者压栈,直接跳转即可
' 对于 cdecl:需要在跳转前清栈?不对,cdecl由调用者清栈,直接跳转也行
'
' 关键区别:不同调用约定下,如何传递参数到目标函数
'
' 最简单可靠的方法:生成一个中间函数,把所有参数原样传递给目标函数

如果真 (cdecl)
    ' cdecl版本:直接 jmp 目标地址(假设参数已经在栈上)
    代码 = { 233, 0, 0, 0, 0 }  ' jmp rel32
    写整数 (代码, 2, 目标函数地址 - (fn + 5))
否则
    ' stdcall版本:同样直接jmp,但需确保参数个数匹配
    代码 = { 233, 0, 0, 0, 0 }
    写整数 (代码, 2, 目标函数地址 - (fn + 5))
结束如果

' 更复杂的场景:如果需要在调用前后做额外处理
' 可以生成类似这样的代码:
' mov eax, [esp+4]  ; 取出第一个参数
' push eax           ; 重新压栈
' call 目标地址
' ret 4             ; 根据参数个数调整
方案三:使用内联汇编模板(易语言原生)
易语言
.子程序 创建外部函数存根, 整数型
.参数 目标地址, 整数型
.参数 参数总字节数, 整数型  ' 例如:2个整数参数 = 8
.局部变量 存根地址, 整数型
.局部变量 机器码, 字节集

' 生成以下汇编的机器码:
' push ebp
' mov ebp, esp
' [可选:复制参数到新栈帧]
' call 目标地址
' leave
' ret [参数总字节数]  ' stdcall需要,cdecl不需要

机器码 = { 85, 137, 229, 232, 0, 0, 0, 0, 201, 194, 0, 0 }
写整数 (机器码, 5, 目标地址 - (存根地址 + 9))  ' call相对偏移
如果真 (参数总字节数 > 0)
    机器码 [11] = 位与 (参数总字节数, 255)
    机器码 [12] = 右移 (参数总字节数, 8)
结束如果
实际可用的简化方案
最简单的方法是使用易语言自带的 到整数 (&子程序指针) 直接传递函数指针,大多数DLL都兼容:

易语言
' 直接传递子程序指针
DLL函数 (到整数 (&我的回调函数))
如果必须用你提供的代码改造,关键修改点:

去掉 this 指针相关代码(原代码中的 ecx 处理)

直接 jmp 或 call 到目标地址

处理调用约定差异(原代码中的 cdecl 参数已预留)
回复

使用道具 举报

结帖率:75% (3/4)

签到天数: 8 天

发表于 2026-6-8 05:59:10 | 显示全部楼层   福建省泉州市
QQ20260608-053139.png
11.png
22.png

thunk.e (23.08 KB, 下载次数: 1)
回复

使用道具 举报

结帖率:67% (16/24)

签到天数: 7 天

 楼主| 发表于 2026-6-10 21:51:53 | 显示全部楼层   **

你没看明白,被调用的回调函数的参数需要正常长整数(8字节)、双精度(8字节)
回复

使用道具 举报

结帖率:67% (16/24)

签到天数: 7 天

 楼主| 发表于 2026-6-10 21:54:54 | 显示全部楼层   **
帅气与大侠 发表于 2026-6-4 19:15
说实话,没看懂你的需求,你是要将易语言的子程序包装成cdecl调用约定的函数地址,然后返回cdecl函数地址? ...

被调用的回调函数     参数,目前在论坛找到的没一个是支持8位、1位、2位的
回复

使用道具 举报

结帖率:67% (16/24)

签到天数: 7 天

 楼主| 发表于 2026-6-10 22:04:21 | 显示全部楼层   **
临时解决方案:
   把易语言的回调函数参数拆成两个,比如 长整数、双精度 拆成两个整数型,然后在回调函数中合并它们
回复

使用道具 举报

结帖率:75% (3/4)

签到天数: 8 天

发表于 2026-6-11 02:17:37 | 显示全部楼层   福建省泉州市
wan942182718 发表于 2026-6-10 21:51
你没看明白,被调用的回调函数的参数需要正常长整数(8字节)、双精度(8字节) ...

哦。原来如此。我还以为你说的多种类型是要支持 对 带有对自定义结构和字节集的参数函数 取地址。那我再研究看看。
回复

使用道具 举报

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

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报QQ: 793400750,邮箱:wp@125.la
网站简介:精易论坛成立于2009年,是一个程序设计学习交流技术论坛,隶属于揭阳市揭东区精易科技有限公司所有。
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备2025452707号) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

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