用委托实现回调
⑴ andle::Invoke”类型的已垃圾回收委托进行了回调
手头有个C++的DLL,是公司的一个远程通话功能组件。长久以来,这个DLL对外只有三个操作接口。而从合理的角度来说,这个DLL本应再提供一些回调接口或事件之类的东西,用于在通话状态被动改变时通知外层应用程序。不过银行里的规矩多多,不能用ActiveX;而使用回调,却又似不那么的稳定,尤其在与C#交互时。所以才一直就这么用着。
直到最近,外层应用程序的开发人员提意见抱怨这个DLL。于是,一狠心,决定向这个DLL添加回调的支持。虽然不能确定之前使用回调不稳定的问题在哪,但现有资料和之前自己单独写的测试却都表明C#向C++传递的回调是可以正常工作的。因此,必定是这个DLL的什么地方存在错误才会不稳定,大不了搭上一个周末的时间,总应该能解决吧。
一般来说,实现回调的要点就两点:
1. 回调应加上WINAPI或CALL关键字,以声明其调用方式;
2. 注意参数类型尤其是长度,在C++和C#中的声明要一致;避免使用指针和引用类型。
由于之前已经单独测试过,所以就差在DLL中实战演练了。花了一天时间,根据他们的要求改好了程序。为了避免遇到传说中的住在迷宫里的那个会吃迷路的人的魔鬼。其间还打了几针预防针,先在定义回调时先排除了任何字符参数,只用整数;然后去掉了之前的那个中间DLL。原来的核心DLL是MFC扩展动态链接库,不能直接用在C#中,所以又在外边套了个正规DLL。虽然间接调用DLL很正常,但让结构更简单,总没有坏处。
一切妥当,开始调试,改掉几个小问题后,发现程序竟然正常的跑起来了,回调的输出欢快的显示到屏幕上了。又各式各样的环境各测了几遍,依然OK,看来这次运气不差,哈。接下来打包,写接口和回调说明文档,画状态图。
在发出去前,想起来又改了改一些调试和日志信息,然后又神经质的测试了一把。事后证明,这是一个让人后悔也庆幸的动作。因为,这次遇到Demo的老兄Demon了,哎,怎一个惨字了得.....
幸亏本命年的我早有时刻面对打击的觉悟,早前没有得意忘形,要不这下心情落差大去了。额,不过,来就来吧,有周末两天做后盾,不信搞不定你。
看看Demon老兄说了些啥:SYSTEM.WINDOWS.FORMS.NI.DLL 0xC0000005 access violation。
完全摸不着头脑,只能慢慢来了。
先检查了一遍DLL程序,应该没有问题,所有的回调也都是在主线程上触发的,应该跟线程也没有关系。
再来确定到底是C++ DLL的还是C# EXE的问题,把DLL和EXE在DEBUG和Release下各编译了一份,然后组合运行了一下,发现当且仅当使用EXE的Release版程序时,会出现该异常。那么,就应该是C#程序的问题了。
这只是一个非常简单的示例,实在想不明白代码可能会有什么问题。而且还是Debug正常,Release异常。先改改编译选项试试,把Optimze Code关了试试,问题照旧;再把Define Debug constant开了,这就跟Debug版本的编译选项完全一样了,还是不行。看来此路不通了。
看看Google同学有没有遇到过这样的问题,把Demon老兄说的搜了一下,结果没有相关资料…看来这下有得头疼了。
忽然想起似乎刚才DLL和EXE组合测试的时刻似乎出过一个挺不寻常的提示。再去找找看,发现Debug版的DLL和Release版的EXE一起运行会有这样的提示:
File: i386\chkesp.c
Line: 42
The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
再搜索一下,这次倒有不少同样不幸的人,不过正如那句话“不幸的人各有各的不幸”,他们的原因似乎跟我的情况扯不上啥关系。
这下没招了,再求助Bai一下,看看大家用C++回调C#代码都遇到些啥情况。结果试了好几个关键字,都一无所获,正当我感觉鸭梨很大的时候,突然间,用关键字“C++ C# 回调 委托”搜到了一个有点意思的帖子 C#处理C++库回调报错_非托管代码传递委托被垃圾回收,虽然这位老兄什么都没说明,不过贴上来的异常内容很特别:
检测到 CallbackOnCollectedDelegate
Message: 对“HBVideoParser!Videocomm.Video.HB.HBSDK+SrcDataParseCBHandler::Invoke”类型的已垃圾回收委托进行了回调。这可能会导致应用程序崩溃、损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这些委托保持活动状态,直到确信不会再次调用它们。
向C++传递的委托对象回垃圾回收了,导致回调时异常?感觉有点怪怪,不过死马当活马医吧,改Demo,传了一个全局委托对象给DLL,再测试~哈,回调输出又欢快滴出来了~
看来果真如此了,确认一下,改回原来的传递临时对象的方式,在Demo的Main函数里加个异常捕获,发现确实异常会传递到此处,内容则是“Object reference not set to an instance of an object”。虽然与上述的不一致,不过应该就是一回事啦。
现在想想,这也不难理解。C#中通过引用计数来判断对象是否可被回收,在Demo中,创建的临时委托对象只是被临时使用了一下,没有任何引用,虽然在C++ DLL中保存了,但俨然那已经不属于Demo的范围了。因此被垃圾回收很正常。
再想到之前自己单独写的测试似乎并没有发现这样的问题,那又是为什么呢?难道因为程序占用内在太少还没来得及进行垃圾回收操作?再去翻出来,点住调用按钮不放,果然,在输出三四十条信息之后程序异常了。
⑵ c# 委托实现异步回调是怎么一回事
义一个委托,该委托于你需要异步执行的方法的签名一定要一样,
另外回 在定义委托时,还可以答加上两个可选的参数
意思就是说
你的方法签名 public string TestMethod(int i)
你的委托这样 public delegate string AsyncMethod(int i);
在程序里面,你可以这样用
实例化你的委托:
// ad.TestMethod 假设TestMethod方法包含在一个ad的对象中
AsyncMethod caller = new AsyncMethod(ad.TestMethod);
IAsyncResult Result = caller.BeginInvoke(1,null/*回调*/,new StateObject() /*StateObject是你的对象*/);
这个你调用会返回IAsyncResult 对象 在上面 就是Result
你可以这样获得你的对象
Result.AsyncState 但是必须强制转换。
你可以在MSDN中看一下
⑶ 委托为什么叫回调
回调(callback)函数是Windows编程的一个重要部分。如果您具备C或C++编程背景,应该就曾在许多Windows API中使用版过回调。Visual Basic添加权了AddressOf关键字后,开发人员就可以利用以前一度受到限制的API了。回调函数实际上是方法调用的指针,也称为函数指针,是一个非常强大的编程特性。.NET以委托的形式实现了函数指针的概念。它们的特殊之处是,与C函数指针不同,.NET委托是类型安全的。这说明,C中的函数指针只不过是一个指向存储单元的指针,我们无法说出这个指针实际指向什么,像参数和返回类型等就更无从知晓了。如本章所述,.NET把委托作为一种类型安全的操作。本章后面将学习.NET如何将委托用作实现事件的方式。
详情咨询
http://book.csdn.net/bookfiles/829/
⑷ 声明一个委托public delegate void atcallfack( int x);则用该委托产生的回调方法的原型应该是__
public void MethodName(int temp)
{
}
⑸ 请问委托异步回调函数是否可以获得执行后的内容(跨项目)
static void process_Callback(IAsyncResult result)
{
int value = Convert.ToInt32(ar.AsyncState);
AsyncResult aResult = (AsyncResult)ar;
putNewWeibo temp = (putNewWeibo)aResult.AsyncDelegate;
int result = temp.EndInvoke(ar);
}
⑹ 委托,事件和回调函数这3者之间到底是什么关
这是因为你的回调委托作用域问题。在回调时,委托已经被回收了 所谓成员变量: class test{ public delegate a; public void foo(){ private delegate b; } } 像这种情况,b委托的作用域只在foo()函数里面,如果你让b被非托管代码回调,当foo执行完后,非托管代码回调时很可能b已被回收了。所以出现这个错。 a委托是test类的成员变量,它是可以一直保持的,除非你对test进行了析构。 也可以使用static的变量。 也就是说,尽量扩大你的委托的作用域,保证在回调是它不会被自动回收就行了。
⑺ C# 中 委托 和C++中 回调的区别是什么
C#中。委托可以实现回调。。线程安全的一种实现方式。C#中,事件也是委托。C++不太明了。CLI我都跳过了的。。
⑻ C#事件与委托,事件与回调的用法区别
事件包括:事件发送者、事件接收者和事件处理程序。
关于事件,我们首要知道的是事件的发送者必须知道发送什么类型的事件,以及相关
的事件参数。而事件的接收者必须了解其事件的处理方法必须使用的返回类型和参数
事件的发送者和事件的接收者都只关注事件而不是对方,这样对象就只需考虑自己,而不用
考虑其他对象。
例如:足球运动员踢球射门时,产生一个击球事件,但他并不用关心守门员是谁,他
只要关心自己踢球的角度、力度等事件参数,而守门员也一样,他只需关注如何接球即可。
足球运动员踢出球,产生一个击球事件,但是订阅该事件的可能并不只守门员,其他球员包括己
方和对方球员,甚至包括观众和裁判,他们都应该订阅该事件,因为球接下来会怎么样是未
知的,他们都应该做出相应的事件处理方法。
总之,一个事件总是由一个对象产生,但是一个事件可以由多个对象响应。
当多个对象订阅同一个事件时,IDE(Integrated Development Environment)就会把它们的事
件处理程序一个接一个地串起来关联到这个事件,并按照订阅顺序,先后执行。可以用+=增加事
件处理函数。
在上面中,我们一直讲事件触发时会调用对应的事件处理方法。但是,问题是他怎么知道调用
谁的事件处理方法。任何一个对象只要订阅了事件,他的是事件处理程序就会得到调用。然而
,事件对此该如何管理呢?
这就需要一个新的类型,叫做委托。
通过委托可以创建一个引用变量,但是并不是指向类的实例,而是指向一个类中的某个方法。
设计.NET事件的5步骤:
1):定义参数类型:从类型EventArgs派生出满足要求的事件参数类
2):定义事件处理者委托:与第一步相关该步一般被泛型委托取代了
3):定义事件成员:在自定义类中,由事件处理者委托定义一个或多个事件成员
4):触发事件:自定义类的引发事件方法中,通知所有事件订阅者
5):订阅事件:注册事件处理程序
参数object sender 是产生这个事件对象的一个引用,例如处理一个按钮的单击事件,sender就
表示别单击的这个按钮。
如果没有事件数据,则传递 Empty,而不要传递 null。
可能你会执着于这样的一个问题:既然有了委托,那么事件干什么用的呢?
显然,委托可以干很多事情,但是他不知道什么时候干,这时候就需要事件来驱动。
或许你还会纠结于这样的一个问题:那为什么不直接让事件自己直接去做呢?
这就得回归到面向对象的思想上面了,面向对象讲究专注,就是一个对象最好只专注
自己的本职工作就可以,其他的事情就由其他相关的对象自己完成。
你想想,如果你是个老板,你可以触发一个事件(相当于你有某件事情),但如果处理这个事件
还要让你自己去做,你会觉得很麻烦,于是你就可以让你的职员(委托)去执行这些事件处理程序,
你只要在办公室里等待回复就可以了,这样是不是轻松了许多!
可能你没有当老板的命,无法体会,但换个例子,假如你想要买一张火车票,在没有委托之前,
毫无疑问,你得自己前晚火车站买票,但有了委托后,你就轻松了,你只要上网点击订票发布
一个事件,你的委托就会直接帮你买票,而你就不用大老远的自己跑去买票了。
生活中的例子其实很多,我们的父母其实一直充当着委托这个角色,在这里,感激他们一下:你
们辛苦了。
之前,我们说足球运动员踢球时会产生一个击球事件,并把这个击球事件与守门员直接联系
起来,这其实是不准确的,但我只是为了说明事件的作用而已。
准确的说,击球事件应该是足球运动员和球的,而球和守门员之间的事件应该是球飞出的事件。
现在我们要讨论的是这个击球事件:
既然如此,
那么假设,球场还有别的球,你可能无意中 帮这些球也订购了由运动员引发的击球事件,这样
子会造成一个后果,足球运动员一踢球这些所有的球都飞出去了。
后果严重把!
下面是关于回调的一些定义:(Callback Method)
回调:一个对象将一个方法的引用(方法的引用用委托保存)传入另一个对象,使得只有他能返
回信息,这就是一个回调。
回调方法是一种在操作或活动完成时由委托自动调用的方法。
回调不同于事件,回调不能发布,不是类型,是一种设计模式,回调在两个类之间建立一种关系,
其中一个对象会自动对另一个对象做出反应,回调一般是私有的,保存回调的类对于谁能访问这个
回调有所控制,回调通常在对象的构造函数中设置。
本人粗略定义:简单的说就是将委托作为参数传递给某个特定对象。
这样,有了回调我们就可以避免上面非赛场上的球也飞出去的问题了。
⑼ c#委托的回调 怎么个调
最典型的应用,就是在异步调用时进行回调,看看MSDN上的例子吧。 using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; // State object for reading client data asynchronously public class StateObject { // Client socket. public Socket workSocket = null; // Size of receive buffer. public const int BufferSize = 1024; // Receive buffer. public byte[] buffer = new byte[BufferSize]; // Received data string. public StringBuilder sb = new StringBuilder(); } public class AsynchronousSocketListener { // Thread signal. public static ManualResetEvent allDone = new ManualResetEvent(false); public AsynchronousSocketListener() { } public static void StartListening() { // Data buffer for incoming data. byte[] bytes = new Byte[1024]; // Establish the local endpoint for the socket. // The DNS name of the computer // running the listener is "host.contoso.com". IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName()); IPAddress ipAddress = ipHostInfo.AddressList[0]; IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000); // Create a TCP/IP socket. Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp ); // Bind the socket to the local endpoint and listen for incoming connections. try { listener.Bind(localEndPoint); listener.Listen(100); while (true) { // Set the event to nonsignaled state. allDone.Reset(); // Start an asynchronous socket to listen for connections. Console.WriteLine("Waiting for a connection..."); listener.BeginAccept( new AsyncCallback(AcceptCallback), listener ); // Wait until a connection is made before continuing. allDone.WaitOne(); } } catch (Exception e) { Console.WriteLine(e.ToString()); } Console.WriteLine("\nPress ENTER to continue..."); Console.Read(); } public static void AcceptCallback(IAsyncResult ar) { // Signal the main thread to continue. allDone.Set(); // Get the socket that handles the client request. Socket listener = (Socket) ar.AsyncState; Socket handler = listener.EndAccept(ar); // Create the state object. StateObject state = new StateObject(); state.workSocket = handler; handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); } public static void ReadCallback(IAsyncResult ar) { String content = String.Empty; // Retrieve the state object and the handler socket // from the asynchronous state object. StateObject state = (StateObject) ar.AsyncState; Socket handler = state.workSocket; // Read data from the client socket. int bytesRead = handler.EndReceive(ar); if (bytesRead > 0) { // There might be more data, so store the data received so far. state.sb.Append(Encoding.ASCII.GetString( state.buffer,0,bytesRead)); // Check for end-of-file tag. If it is not there, read // more data. content = state.sb.ToString(); if (content.IndexOf("<EOF>") > -1) { // All the data has been read from the // client. Display it on the console. Console.WriteLine("Read {0} bytes from socket. \n Data : {1}", content.Length, content ); // Echo the data back to the client. Send(handler, content); } else { // Not all data received. Get more. handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); } } } private static void Send(Socket handler, String data) { // Convert the string data to byte data using ASCII encoding. byte[] byteData = Encoding.ASCII.GetBytes(data); // Begin sending the data to the remote device. handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler); } private static void SendCallback(IAsyncResult ar) { try { // Retrieve the socket from the state object. Socket handler = (Socket) ar.AsyncState; // Complete sending the data to the remote device. int bytesSent = handler.EndSend(ar); Console.WriteLine("Sent {0} bytes to client.", bytesSent); handler.Shutdown(SocketShutdown.Both); handler.Close(); } catch (Exception e) { Console.WriteLine(e.ToString()); } } public static int Main(String[] args) { StartListening(); return 0; } }
求采纳
⑽ 委托就是回调么异步跟回调又有什么区别 如果不是 他们我该怎么去区别和理解或者他们三个有什么关联
你是新手吧,委托就是你委托证券公司为你进行股票买卖,回调是指股价上升一段行情后又下跌一小段行情,异步这词在股票里边好像没有吧