C++.NET中怎样调用dll调用exe中回调函数数

博客访问: 2456831
博文数量: 102
博客积分: 671
博客等级: 上尉
技术积分: 22450
注册时间:
www.kernel.org
APP发帖 享双倍积分
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: LINUX
& &&本文主要从进程栈空间的层面复习一下C语言中函数调用的具体过程,以加深对一些基础知识的理解。
& &&先看一个最简单的程序:
/*test.c*/
#include <stdio.h>
int foo1(int m,int n,int p)
&&&&&&&&int x = m + n + p;
&&&&&&&&return x;
int main(int argc,char** argv)
&&&&&&&&int x,y,z,result;
&&&&&&&&x=11;
&&&&&&&&y=22;
&&&&&&&&z=33;
&&&&&&&&result = foo1(x,y,z);
&&&&&&&&printf("result=%d\n",result);
&&&&&&&&return 0;
& &&主函数main里定义了4个局部变量,然后调用同文件里的foo1()函数。4个局部变量毫无疑问都在进程的栈空间上,当进程运行起来后我们逐步了解一下main函数里是如何基于栈实现了对foo1()的调用过程,而foo1()又是怎么返回到main函数里的。为了便于观察的粒度更细致一些,我们对test.c生成的汇编代码进行调试。如下:
.file "test.c"
&&&&&&&&.text
.globl foo1
&&&&&&&&.type foo1, @function
&&&&&&&&pushl
&&&&&&&&movl
%esp, %ebp
&&&&&&&&subl
&&&&&&&&movl
12(%ebp), %eax
&&&&&&&&movl
8(%ebp), %edx
&&&&&&&&leal (%edx,%eax), %eax
&&&&&&&&addl
16(%ebp), %eax
&&&&&&&&movl
%eax, -4(%ebp)
&&&&&&&&movl -4(%ebp), %eax
&&&&&&&&leave
&&&&&&&&ret
&&&&&&&&.size foo1, .-foo1
&&&&&&&&.section .rodata
&&&&&&&&.string "result=%d\n"
&&&&&&&&.text
.globl main
&&&&&&&&.type main, @function
&&&&&&&&pushl
&&&&&&&&movl
%esp, %ebp
&&&&&&&&andl
$-16, %esp
&&&&&&&&subl
&&&&&&&&movl
$11, 16(%esp)
&&&&&&&&movl
$22, 20(%esp)
&&&&&&&&movl
$33, 24(%esp)
&&&&&&&&movl
24(%esp), %eax
&&&&&&&&movl
%eax, 8(%esp)
&&&&&&&&movl
20(%esp), %eax
&&&&&&&&movl
%eax, 4(%esp)
&&&&&&&&movl
16(%esp), %eax
&&&&&&&&movl
%eax, (%esp)
&&&&&&&&call foo1
&&&&&&&&movl
%eax, 28(%esp)
&&&&&&&&movl
$.LC0, %eax
&&&&&&&&movl
28(%esp), %edx
&&&&&&&&movl
%edx, 4(%esp)
&&&&&&&&movl
%eax, (%esp)
&&&&&&&&call printf
&&&&&&&&movl
&&&&&&&&leave
&&&&&&&&ret
&&&&&&&&.size main, .-main
&&&&&&&&.ident "GCC: (GNU) 4.4.4
(Red Hat 4.4.4-13)"
&&&&&&&&.section .note.GNU-stack,"",@progbits
& &&上面的汇编源代码和最终生成的可执行程序主体结构上已经非常类似了:
[root@maple 1]# gcc -g -o test test.s
[root@maple 1]# objdump -D test >
[root@maple 1]# vi testbin
&//… 省略部分不相关代码
80483c0:&&&&&& ff d0&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&call&& *%eax
&80483c2:&&&&&&
c9 & & & & & & & & & & & &&&&&&&&&&&&leave
&80483c3:&&&&&&
c3 & & & & & & & & & & & &&&&&&&&&&&&ret
&80483c4:&&&&&& 55 & & & & & & & & & & & &&&&& & & &push&&
&80483c5:&&&&&&
89 e5 & & & & & & & & & & & & & & &&mov&&&
&80483c7:&&&&&&
83 ec 10 & & & & & & & & & & & & &sub&&& $0x10,%esp
&80483ca:&&&&&&
8b 45 0c & & & & & & & &&&&&&&&& &mov&&& 0xc(%ebp),%eax
&80483cd:&&&&&&
8b 55 08 & & & & & & &&&&&& & & &mov&&&
0x8(%ebp),%edx
&80483d0:&&&&&&
8d 04 02 & & & & & & &&&&&& & & &lea&&& (%edx,%eax,1),%eax
&80483d3:&&&&&&
03 45 10 & & & & & & &&&&&& & & &add&&& 0x10(%ebp),%eax
&80483d6:&&&&&&
89 45 fc & & & & & & & &&&&& & & &mov&&& %eax,-0x4(%ebp)
&80483d9:&&&&&&
8b 45 fc & & & & & & & &&&&& & & &mov&&& -0x4(%ebp),%eax
&80483dc:&&&&&&
c9 & & & & & & & & & & & & & & & & & leave
&80483dd:&&&&&&
c3 & & & & & & & & & & & & & & & & & ret
080483de :
&80483de:&&&&&&
55 & & & & & & & & & & & & & & & & & &&push&& %ebp
&80483df:&&&&&&
89 e5 & & & & & & & & & & & & & & & & mov&&&
&80483e1:&&&&&&
83 e4 f0 & & & & & & & & & & & & &&&and&&& $0xfffffff0,%esp
&80483e4:&&&&&&
83 ec 20 & & & & & & & & & & & & &&sub&&& $0x20,%esp
&80483e7:&&&&&&
c7 44 24 10 0b 00 00 & & & movl&& $0xb,0x10(%esp)
&80483ee:&&&&&&
&80483ef:&&&&&&
c7 44 24 14 16 00 00 & & & &movl&& $0x16,0x14(%esp)
&80483f6:&&&&&&
&80483f7:&&&&&&
c7 44 24 18 21 00 00 & & & &movl&& $0x21,0x18(%esp)
&80483fe:&&&&&&
&80483ff:&&&&&&
8b 44 24 18 & & & & & & & & & & &mov&&& 0x18(%esp),%eax
&8048403:&&&&&&
89 44 24 08 & & & & & & & & & &mov&&& %eax,0x8(%esp)
&8048407:&&&&&&
8b 44 24 14 & & & & & & & & & &mov&&& 0x14(%esp),%eax
&804840b:&&&&&&
89 44 24 04 & & & & & & & & & &mov&&& %eax,0x4(%esp)
&804840f:&&&&&&
8b 44 24 10 & & & & & & & & & &&mov&&& 0x10(%esp),%eax
&8048413:&&&&&&
89 04 24 & & & & & & & & & & & &&mov&&& %eax,(%esp)
&8048416:&&&&&& e8 a9 ff ff ff & & & & & & & & & &&call&&
&804841b:&&&&&&
89 44 24 1c & & & & & & & & & &&mov&&& %eax,0x1c(%esp)
&804841f:&&&&&&
b8 04 85 04 08 & & & & & & & &&mov&&& $0x8048504,%eax
&8048424:&&&&&&
8b 54 24 1c & & & & & & & & & &&mov&&& 0x1c(%esp),%edx
&8048428:&&&&&&
89 54 24 04 & & & & & & & & & &&mov&&& %edx,0x4(%esp)
&804842c:&&&&&&
89 04 24 & & & & & & & & & & & &&mov&&& %eax,(%esp)
&804842f:&&&&&&
e8 c0 fe ff ff & & & & & & & & & &&call&& 80482f4
&8048434:&&&&&&
b8 00 00 00 00 & & & & & & &mov&&& $0x0,%eax
&8048439: &&&&&&c9 & & & & & & & & & & & & & & & & &leave
&804843a:&&&&&&
c3 & & & & & & & & & & & & & & & & &ret
&804843b:&&&&&&
90 & & & & & & & & & & & & & & & & nop
&804843c:&&&&&&
90 & & & & & & & & & & & & & & & & nop
&//… 省略部分不相关代码
& &&用GDB调试可执行程序test:
& &&在main函数第一条指令执行前我们看一下进程test的栈空间布局。因为我们最终的可执行程序是通过glibc库启动的,在main的第一条指令运行前,其实还有很多故事的,这里就不展开了,以后有时间再细究,这里只要记住一点:main函数执行前,其进程空间的栈里已经有了相当多的数据。我的系统里此时栈顶指针esp的值是0xbffff63c,栈基址指针ebp的值0xbffff6b8,指令寄存器eip的值是0x80483de正好是下一条马上即将执行的指令,即main函数内的第一条指令“push %ebp”。那么此时,test进程的栈空间布局大致如下:
& &&然后执行如下三条指令:
pushl %ebp & & & &&//将原来ebp的值0xbffff6b8如栈,esp自动增长4字节
movl %esp, %ebp & &//用ebp保存当前时刻esp的值
$-16, %esp & &//内存地址对其,可以忽略不计
& &执行完上述三条指令后栈里的数据如上图所示,从0xbffff630到0xbffff638的8字节是为了实现地址对齐的填充数据。此时ebp的值0xbffff638,该地址处存放的是ebp原来的值0xbffff6b8。详细布局如下:
& &第28条指令“subl& $32,
%esp”是在栈上为函数里的本地局部变量预留空间,这里我们看到main主函数有4个int型的变量,理论上说预留16字节空间就可以了,但这里却预留了32字节。GCC编译器在生成汇编代码时,已经考虑到函数调用时其输入参数在栈上的空间预留的问题,这一点我们后面会看到。当第28条指令执行完后栈空间里的数据和布局如下:
& &&然后main函数里的变量x,y,z的值放到栈上,就是接下来的三条指令:
$11, 16(%esp)
$22, 20(%esp)
$33, 24(%esp)
& &这是三条寄存器间接寻址指令,将立即数11,22,33分别放到esp寄存器所指向的地址0xbffff610向高位分别偏移16、20、24个字节处的内存单元里,最后结果如下:
& &注意:这三条指令并没有改变esp寄存器的值。
& &接下来main函数里就要为了调用foo1函数而做准备了。由于mov指令的两个操作数不能都是内存地址,所以要将x,y和z的值传递给foo1函数,则必须借助通用寄存器来完成,这里我们看到eax承担了这样的任务:
24(%esp), %eax
%eax, 8(%esp)
20(%esp), %eax
%eax, 4(%esp)
16(%esp), %eax
%eax, (%esp)
& &&当foo1函数所需要的所有输入参数都已经按正确的顺序入栈后,紧接着就需要调用call指令来执行foo1函数的代码了。前面的博文说过,call指令执行时分两步:首先会将call指令的下一条指令(movl& %eax,
28(%esp))的地址(0x0804841b)压入栈,然后跳转到函数foo1入口处开始执行。当第38条指令“call foo1”执行完后,栈空间布局如下:
& &call指令自动将下一条要执行的指令的地址0x0804841b压入栈,栈顶指针esp自动向低地址处“增长”4字节。所以,我们以前在C语言里所说的函数返回地址,应该理解为:当被调用函数执行完之后要返回到它的调用函数里下一条马上要执行的代码的地址。为了便于观察,我们把foo1函数最后生成指令再列出来:
3 .globl foo1
4 &&&&& & &&.type foo1, @function
6 &&&&&&&&& pushl
7 &&&&&&&&& movl
%esp, %ebp
8 &&&&&&&&& subl
9 &&&&&&&&& movl
12(%ebp), %eax
10 &&&&&&&& movl
8(%ebp), %edx
11 &&&&&&&& leal (%edx,%eax), %eax
12 &&&&&&&& addl
16(%ebp), %eax
13 &&&&&&&& movl
%eax, -4(%ebp)
14 &&&&&&&& movl -4(%ebp), %eax
15 &&&&&&&& leave
16 &&&&&&&& ret
17 &&&&&&&& .size foo1, .-foo1
& &&进入到foo1函数里,开始执行该函数里的指令。当执行完第6、7、8条指令后,栈里的数据如下。这三条指令就是汇编层面函数的“序幕”,分别是保存ebp到栈,让ebp指向当前栈顶,然后为函数里的局部变量预留空间:
& &接下来第9和第10条指令,也并没有改变栈上的任何数据,而是将函数输入参数列表中的的x和y的值分别转载到eax和edx寄存器,和main函数刚开始时做的事情一样。此时eax=22、edx=11。然后用了一条leaf指令完成x和y的加法运算,并将运算结果存在eax里。第12条指令“addl 16(%ebp), %eax”将第三个输入参数p的值,这里是实参z的值为33,同样用寄存器间接寻址模式累加到eax里。此时eax=11+22+33=66就是我们最终要得计算结果。
& &因为我们foo1()函数的C代码中,最终计算结果是保存到foo1()里的局部变量x里,最后用return语句将x的值通过eax寄存器返回到mian函数里,所以我们看到接下来的第13、14条指令有些“多此一举”。这足以说明gcc人家还是相当严谨的,C源代码的函数里如果有给局部变量赋值的语句,生成汇编代码时确实会在栈上为本地变量预留的空间里的正确位置为其赋值。当然gcc还有不同级别的优化技术来提高程序的执行效率,这个不属于本文所讨论的东西。让我们继续,当第13、14条指令执行完后,栈布局如下:
& &将ebp-4的地址处0xbffff604(其实就是foo1()里的第一个局部参数x的地址)的值设置为66,然后再将该值复制到eax寄存器里,等会儿在main函数里就可以通过eax寄存器来获取最终的计算结果。当第15条指令leave执行完后,栈空间的数据和布局如下:
& &&我们发现,虽然栈顶从0xbffff5f8移动到0xbffff60c了,但栈上的数据依然存在。也就是说,此时你通过esp-8依旧可以访问foo1函数里的局部变量x的值。当然,这也是说得通的,因为函数此时还没有返回。我们看栈布局可以知道当前的栈顶0xbffff60c处存放的是下一条即将执行的指令的地址,对照反汇编结果可以看到这正是main函数里的第18条指令(在整个汇编源文件test.s里的行号是39)“movl& %eax, 28(%esp)”。leave指令其实完成了两个任务:
& &1、将栈上为函数预留的空间“收回”;
& &2、恢复ebp;
& &也就是说leave指令等价于下面两条指令,你将leave替换成它们编译运行,结果还是对的:
movl %ebp,%esp
& &前面我们也说过,ret指令会自动到栈上去pop数据,相当于执行了“popl %eip”,会使esp增大4字节。所以当执行完第16条指令ret后,esp从0xbffff60c增长到0xbffff610处,栈空间结构如下:
& &现在已经从foo1里返回了,但是由于还没执行任何push操作,栈顶“上部”的数据依旧还是可以访问到了,即esp-12的值就是foo1里的局部变量x的值、esp-4的值就是函数的返回地址,当执行第39条指令“movl %eax,28(%esp)”后栈布局变成下面的样子:
& &第39条指令就相当于给main里的result变量赋值66,如上红线标注的地方。接下来main函数里要执行printf("result=%d\n",result)语句了,而printf又是C库的一个常用的输出函数,这里就又会像前面调用foo1那样,初始化栈,然后用“call printf的地址”来调用C函数。当40~43这4条指令执行完后,栈里的数据如下:
$.LC0, %eax
28(%esp), %edx
%edx, 4(%esp)
%eax, (%esp)
& &上图为了方便理解,将栈顶的0x替换了成字符串“result=%d\n”,但进程实际运行时此时栈顶esp的值是字符串所在的内存地址。当第44条指令执行完后,栈布局如下:
& &由于此时栈已经用来调用printf了,所以栈顶0xbffff610“以上”部分的空间里就找不到foo1的任何影子了。最后在main函数里,当第46、47条指令执行完后栈的布局分别是:
& &&当main函数里的ret执行完,其实是返回到了C库里继续执行剩下的清理工作。
& &所以,最后关于C的函数调用,我们可以总结一下:
& &1、函数输入参数的入栈顺序是函数原型中形参从右至左的原则;
& &2、汇编语言里调用函数通常情况下都用call指令来完成;
& &3、汇编语言里的函数大部分情况下都符合以下的函数模板:
.globl fun_name
.type fun_name, @function
&&&&&&&&pushl
&&&&&&&&movl
%esp, %ebp
&&&&&&&&<函数主体代码>
&&&&&&&&leave
&&&&&&&&ret
& &如果我们有个函数原型:int funtest(int x,int y int z char* ptr),在汇编层面,当调用它时栈的布局结构一般是下面这个样子:
& &&而有些资料上将ebp指向函数返回地址的地方,这是不对的。正常情况下应该是ebp指向old ebp才对,这样函数末尾的leave和ret指令才可以正常工作。
阅读(40909) | 评论(20) | 转发(42) |
相关热门文章
给主人留下些什么吧!~~
感谢楼主,终于明白了。
好文,先顶再看。
:博主辛苦了,&很大帮助。谢谢。有一个疑问,还请指教。如果这个进程在运行过程中,遭到了调度,那么它用户空间的这些数据会不会丢失呢?因为最终它的数据是放在物理内存上面的呀。这个进程的上下文是保存在哪里的?万一新来的进程的用户空间映射到物理内存上去刚好和前一个进程的某个地址重回了,那么前一个进程的数据不就被破坏了吗?OS是怎样保护的呀?我的问题有点多,见谅。
内存管理是操作系统的基础功能,如果操作系统连虚拟内存到物理内存的映射、物理内存分配都管理不好,那么我觉得这个操作系统就可以费了。关于你的第一个问题,进程上下文保存在哪里,我不敢拍胸脯向你保证,因为没有调查就没发言权。从我的理解,进程的很多信息都是保存在寄存器里的,而寄存器里的值有的是保存在CPU的高速缓冲里,有的是保存在进程地址空间里,纯属个人理解,仅作参考,一切以代码为准,毕竟自己没具体研究过。第二个问题,别忘了Linux有一个叫做swap的分区,你可以去网上搜一下这个分区的作用就明白了&:) |
博主辛苦了,&很大帮助。谢谢。有一个疑问,还请指教。如果这个进程在运行过程中,遭到了调度,那么它用户空间的这些数据会不会丢失呢?因为最终它的数据是放在物理内存上面的呀。这个进程的上下文是保存在哪里的?万一新来的进程的用户空间映射到物理内存上去刚好和前一个进程的某个地址重回了,那么前一个进程的数据不就被破坏了吗?OS是怎样保护的呀?我的问题有点多,见谅。
:写的不错,图画的比我强不少。咱俩的风格很接近。
图比较占篇幅,就可以少打字了,这是一种偷懒的方式 |
请登录后评论。java中回调函数的理解 - 推酷
java中回调函数的理解
不要忽律学习中的一点点细节,也许他会决定你的成败。
这是在c/c++中的定义:
回调函数就是一个通过函数指针(函数地址)调用的函数。如果把函数的指针(也即函数的地址)作为参数传递给另一个函数,当通过这个指针调用它所指向的函数时,称为函数的回调。 回调函数不是由该函数的实现方直接调用的,而是在特定的事件或条件发生时,由另外的一方调用的,用于对该事件或条件进行响应。&
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:
同步调用、回调和异步调用。
同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;
回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;
异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。&
回调函数机制提供了系统对异步事件的处理能力。首先将异步事件发生时需要执行的代码编写成一个函数,并将该函数注册成为回调函数,这样当该异步事件发生时,系统会自动调用事先注册好的回调函数,回调函数的注册实际上就是将回调函数的信息填写到一个用于注册回调函数的结构体变量中。&
所谓回调,就是客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数。例如Win32下的窗口过程函数就是一个典型的回调函数。一般说来,C不会自己调用B,C提供B的目的就是让S来调用它,而且是C不得不提供。由于S并不知道C提供的B姓甚名谁,所以S会约定B的接口规范(函数原型),然后由C提前通过S的一个函数R告诉S自己将要使用B函数,这个过程称为回调函数的注册,R称为注册函数。Web Service以及 Java 的RMI都用到回调机制,可以访问远程服务器程序。
&&& 下面举个通俗的例子:
&&& 某天,我打电话向你请教问题,当然是个难题,^_^,你一时想不出解决方法,我又不能拿着电话在那里傻等,于是我们约定:等你想出办法后打手机通知我,这样,我就挂掉电话办其它事情去了。过了XX分钟,我的手机响了,你兴高采烈的说问题已经搞定,应该如此这般处理。故事到此结束。这个例子说明了“异步+回调”的编程模式。其中,你后来打手机告诉我结果便是一个“回调”过程;我的手机号码必须在以前告诉你,这便是注册回调函数;我的手机号码应该有效并且手机能够接收到你的呼叫,这是回调函数必须符合接口规范。
通过上面个人感觉到回调更多的应用就是结合异步。比如:Ajax中js通过组件和服务器的异步通信。
程序员A写了一段程序(程序a),其中预留有回调函数接口,并封装好了该程序。程序员B要让a调用自己的程序b中的一个方法,于是,他通过a中的接口回调自己b中的方法。目的达到。
在C/C++中,要用回调函数,被掉函数需要告诉调用者自己的指针地址,但在JAVA中没有指针,怎么办?我们可以通过接口(interface)来实现定义回调函数。
&&&& 假设我是程序员A,以下是我的程序a:
public class Caller
public MyCallI
public void setCallfuc(MyCallInterface mc)
public void call(){
this.mc.method();
我还需要定义一个接口,以便程序员B根据我的定义编写程序实现接口。
public interface MyCallInterface
public void method();
于是,程序员B只需要实现这个接口就能达到回调的目的了:
public class B implements MyCallInterface
public void method()
System.out.println(&回调&);
public static void main(String args[])
Caller call = new Caller();
call.setCallfuc(new B());
call.call();
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致6198人阅读
jockey--c++(53)
最近在看代码,写代码的人很喜欢用回调函数和函数指针。一直觉得回调函数和函数指针挺神秘的,所以查了一些资料,来与大家一起来分享。
什么是回调函数
简而言之,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。
为什么要使用回调函数
&&&因为使用回调函数可以把调用者和被调用者分开,调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回&#20540;为int)的被调用函数。回调函数就好像是一个中断处理函数,系统在符合你设定的条件时自动调用。
如何使用回调函数
&使用回调函数,我们需要做三件事:
声明定义设置触发条件:在你的函数种把你的回调函数名称转化为地址作为一个参数,以便于系统调用。
声明和定义时应注意,回调函数由系统调用,所以可以认为它属于windows系统,不要把它当作你的某个类的成员函数。
回调函数是一个程序员不能显示调用的函数,通过将回调函数的地址传给调用者从而实现调用。回调函数是十分必要的,在我们想通过一个统一接口实现不同的内容,这时回调函数非常合适。
函数指针的声明
对回调函数有了一个初步的了解,下面我们来说一下函数指针。因为要实现回调,必须首先定义函数指针。
void (*) ()
左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(void)和右边圆括弧中的入口参数
为函数指针声明类型定义:
Typedef void(* pfv)()
pfv&是一个函数指针,它指向的函数没有输入参数,返回类型为voie。使用这个类型定义名称可以隐藏负责的函数指针语法。
void (*p)();
void&func()
p的赋&#20540;可以不同,但一定要是函数的指针,并且参数和返回类型相同。
现学现卖的一个小例子
#include&&iostream&
using&namespace&
typedef&void&(*PF)();
void&func()
&&cout&&&&&func&&&&&
void&caller(&PF&pf)
int&main()
&&caller(p);
&&system(&pause&);
&&return&0;
在visual c&#43;&#43;中,可以在函数类型前加_cdecl,_stdcall或者_pascal来表示调用规范(默认为_cdecl)。调用规范影响编译器产生的给定函数名,参数传递的顺序,堆栈清理责任以及参数传递机制。
不过,在win32的程序中,我见得比较多的是CALLBACK,这个宏定义在windef.h中,
#define CALLBACK&&&&__stdcall
它约定了函数在它们返回到调用者之前,都会从堆栈中移除掉参数。
回调函数以及钩子函数的概念
声明函数指针并实现回调
/document/viewdoc/?id=195
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:109009次
积分:1269
积分:1269
排名:千里之外
转载:76篇
(1)(1)(1)(1)(4)(1)(1)(2)(1)(5)(6)(3)(1)(1)(12)(1)(13)(3)(3)(16)(4)(3)(1)c#调用c++带有回调函数方法的实现 - 博客频道 - CSDN.NET
宋金时的专栏
士不可以不弘毅,任重而道远!你无法改变过去,却可以把握现在和未来,相信自己,无限可能!
分类:C/C++ Programing
在c&#43;&#43;中有个回调函数指针的概念,只需要某个函数在调用定时器函数时传入一个函数指针就能达到目的,但C#中没有函数指针的概念,我们该怎样来实现呢。
其实说到回调函数,大家应该能想到c#中的委托,虽然名字不一样,但在各自的语言范畴都能实现相&#20284;的功能。所以我们就可以大胆的尝试下,把c#中的委托传给c&#43;&#43;,看c&#43;&#43;是否能够承认它就是回调函数。
首先用c&#43;&#43;写一个带有回调函数的方法 Test,在此省略。
接着,在c#中调用,如:
[DllImport(&Test.dll&,ChartSet.Ansi,EntryPoint=&ReadMyVideo&,ExactSpelling=false,CallingConvertion=CallingConvertion.StdCall)]
private static extern void Test(string fileName,CallbackDelegate callback);
接下来我们再定义一个委托:&
public delegate void CallbackDelegate([marshalAs(UnmanagedType.LPArray,SizeConst=<span style="color:#10)]byte[] buffer,int count);
public static CallbackD
注:说明一下,在给c&#43;&#43;传入数组参数时,必须得用[marshalAs(UnmanagedType.LPArray,SizeConst=8010)] 处理一下,相当于是告诉c&#43;&#43;,c#传入的是一个长度为8010的数组类型,如果不写这句话的话,你回调函数接收到的参数将只有一条数据。&
接下来看看怎样来调用:
在调用时,我们得先写一个接受c&#43;&#43;传回参数的方法,即我们传入委托的实现方法。
private void CallBackFunction([marshalAs(UnmanagedType.LPArray,SizeConst=<span style="color:#10)]byte[] buffer,int count)
...//处理c&#43;&#43;传过来的数据s
一切工作准备完毕之后,我们来进行最后一步操作把
public void GetData()
callback=CallBackF
ReadMyVideo(&&,callback);
经过验证,委托就是c&#43;&#43;要的回调函数。
songjinshi
排名:第792名
欢迎大家交流学习!
(50)(29)(61)(5)(1)(15)(5)(3)(11)(30)(14)(6)(12)(6)(12)(1)(1)(0)(0)(8)(1)
(168648)}

我要回帖

更多关于 c 调用c dll 回调函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信