




已阅读5页,还剩28页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C#调用C+回调函数的问题 2008年10月24日 virus1 2 下一页 C+的回调函数中有一个参数是,是返回一个字符串,原则如下:typedef void (*TDataEvent)(char *AData ,int ALen);其中char *AData是从DLL中返回一个字符串,串的内存已经在DLL中分配了 下面中我在C#中定义的委托public delegate void TDataEvent(Byte AData, int ALen); 下面是回调函数的设置代码:Event = new clReceivelDllPoxy.TDataEvent(getDate);ReceDllPoxy.AddServer(1024, Event, 2);其中 Event是上面委托的实例,我定义成一个成员这样就不会被自己释放 下面是C#中回调函数的实现public void getDate(byte AData, int ALen)/发现每次回调是 AData只有一个字节下面转载一个别人的代码,谢谢using System;using System.Collections.Generic;using System.Text;using System.Runtime.InteropServices;using System.Reflection;using System.Reflection.Emit;namespace AppDllTest/*/ / 非托管动态调用基类,基类不能直接使用,需要实现 FunTable()的虚函数/ public abstract class clDllBasePoxy/-装入DLL-public bool Open(string dllFileName)Hand = LoadLibrary(dllFileName);if (Hand = 0)return false;FunSet(GetFunTable();return true;/-关闭DLL-public bool Close()return FreeLibrary(Hand)!=0;public abstract string GetFunTable(); /函数对应表由外部代理类通过 GetFunTable来设置/调用Windows32下的Kernele32库中的装入函数来完成对非托管DLL的引用-#region /调用Windows32下的Kernele32库中的装入函数来完成对非托管DLL的引用-/-DllImport(Kernel32)private static extern int GetProcAddress(int handle, String funcname);DllImport(Kernel32)private static extern int LoadLibrary(String funcname);DllImport(Kernel32)private static extern int FreeLibrary(int handle);private int Hand = 0; /DLL的句柄private static Delegate GetAddress(int dllModule, string functionname, Type t) /把指针转变成C#的代理int addr = GetProcAddress(dllModule, functionname);if (addr = 0)return null;elsereturn Marshal.GetDelegateForFunctionPointer(new IntPtr(addr), t);/-关联代理和DLL中的函数-private bool FunSet(string aFun)Type tp = this.GetType();string Value;for (int i = 0; i aFun.Length; i+)Value = aFuni.Split(,); /Box, TBox, _Box 第一项是代理的实例名,第二项是代理的定义名,第三项是DLL中的函数名if (Value.Length = 3)FieldInfo fi = tp.GetField(Value0.Trim();/找实例Type type = tp.GetNestedType(Value1.Trim();/找尾托if (fi != null & type != null)fi.SetValue(this, GetAddress(Hand, Value2.Trim(), type); /创建关联return true;#endregionpublic class clDllPoxy : clDllBasePoxypublic override string GetFunTable()string FunTable = new stringGetFixParamCount, TGetFixParamCount, _GetFixParamCount,GetFixParam, TGetFixParam, _GetFixParam;return FunTable;/-输出函数-public TGetFixParamCount GetFixParamCount;public TGetFixParam GetFixParam;/-代理描述-public delegate int TGetFixParamCount(); /获取固定参数个数public delegate bool TGetFixParam(int AIndex, byte AOutBuf); /固定参数/*/ / C#动态调用托管DLL的基类-/ public class clNetDllPoxy/-装入动态库-public bool Open(string dllFileName, string className)FAsembly = Assembly.LoadFrom(dllFileName);if (FAsembly = null)return false;Type type = FAsembly.GetTypes()0; /第一个对应的名字空间当成是调用的名字空间FDllName = dllFileName;FClassName = className;if (type != null)FNameSpace = type.Namespace;return true;/-设置Dll中的函数范围-public void SetArea(string nameSpace, string className)FNameSpace = nameSpace;FClassName = className;/-调用指定的方法,注:方法的所有参数都转化成对象类型public object Invoke(string funName, object ObjArray_Parameter)tryType types = FAsembly.GetTypes();foreach (Type tp in types)if (tp.Namespace = FNameSpace & tp.Name = FClassName)MethodInfo metInfo = tp.GetMethod(funName);if (metInfo != null)object obj = Activator.CreateInstance(tp); /创建类对象if (obj != null)return metInfo.Invoke(obj, ObjArray_Parameter);catch return null;private Assembly FAsembly; /Dll的程序集private string FDllName;private string FNameSpace;private string FClassName;C#回调函数 C#回调函数应用示例,形象比喻方法助理解,整理了一个简单的例子来说明回调函数的用法: namespace CallBackFunction class Program static void Main(string args) Program prog = new Program();/在静态函数Main中调用非静态方法时,必须先实例化该类对象,方可调用GetSum方法 SumClass sc = new SumClass();/实例化SumClass类 int result=sc.SumAll(prog.GetSum); Console.WriteLine(result.ToString(); private int GetSum(int a, int b) return (a + b); class SumClass public delegate int Sum(int num1, int num2); public int SumAll(Sum sum) /可以进行些别的操作 return sum(1, 2);/调用传入函数的一个引用 /可以封装更多的业务逻辑方法 这个例子非常的简单,假设SumClass类是一个被封装好的,实现某种业务逻辑的类. 其中包含一个委托(delegate)Sum返回值为int型,有两个int型的参数. 还包括一个返回值为int方法SumAll,注意这个方法的参数,是Sum类型的参数,也可以理解为是一个函数的引用(这个函数就是回调函数).作为参数的sum,可以说是某个函数的引用,这个函数返回值为int,并提供两个整型参数.现在大概可以理解了吧?所谓委托可以被看作是一组方法签名相同的函数的引用的类,其每一个实例都是符合该方法签名的函数的一个引用.在该方法中可以进行一些操作,并调用传进来的函数引用(sum),而此时sum这个方法内的具体业务逻辑是怎样的并不清楚,SumClass就充当了一个接口的作用,这时接口就需要调用SumClass类的客户端来通过定义一个回调函数来实现其具体功能了. 再看Program类,这个类其实就是调用SumClass这个类的一个客户端. 先来看GetSum这个方法,返回值为int,接受两个int型的参数.如果前面说的你都明白了,那么你一定很清楚GetSum就是作为客户端的回调函数用来作为参数的.这个函数实现了两个参数是相加并返回其和的业务逻辑. 在Main函数中,调用SubClass类的SubAll方法,将Programe类的GetSum方法的引用作为参数传递到SubAll方法中,也就是在SubAll中调用GetSum这个相加两个参数的方法,将1和2相加返回3,并将result(3)输出到屏幕上. 通常情况下SubClass类会被封装到DLL中,而参数类型即为一个委托,传入此委托的一个实例(回调函数)来帮助SubClass实现其业务逻辑. 最后再举一个生活中的例子帮助大家来理解: 一天你老婆让你去市场买菜,而你因为偷懒就让你的儿子去买,把菜买回来了你直接将菜交给老婆.而老婆只是想要菜,并不关心菜是谁买的.第二天你老婆让你去买米,结果你又交由儿子处理.第三天老婆让去买肉,可怜的儿子再次被你叫去了. 从这些事情我们可以抽象出一个类来. 这个类就是实现不管老婆让你去做什么,你都叫儿子去代劳.那么我们就可以定义一个让儿子去做这样的委托.然后传入买菜,买肉等函数进来,这些函数就是回调函数.这样不论老婆需要买菜,买肉或者其他任何的事情,都可以通过你这个类来完成c# 调用c+带有回调函数方法的实现目前正在做的一个项目,大部分数据来源都是通过调用c+函数得到的,此时就遇到一个这次要说的问题。如c+函数有个定时器,会定时调用我们c#的某个函数并把数据传给c#,让c#把数据显示到界面上,在c+中有个回调函数指针的概念,只需要某个函数在调用定时器函数时传入一个函数指针就能达到目的,但C#中没有函数指针的概念,我们该怎样来实现呢。其实说到回调函数,大家应该能想到c#中的委托,虽然名字不一样,但在各自的语言范畴都能实现相似的功能。所以我们就可以大胆的尝试下,把c#中的委托传给c+,看c+是否能够承认它就是回调函数。首先用c+写一个带有回调函数的方法 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=8010)byte buffer,int count); public static CallbackDelegate callback;复制代码注:说明一下,在给c+传入数组参数时,必须得用marshalAs(UnmanagedType.LPArray,SizeConst=8010) 处理一下,相当于是告诉c+,c#传入的是一个长度为8010的数组类型,如果不写这句话的话,你回调函数接收到的参数将只有一条数据。接下来看看怎样来调用:在调用时,我们得先写一个接受c+传回参数的方法,即我们传入委托的实现方法。private void CallBackFunction(marshalAs(UnmanagedType.LPArray,SizeConst=8010)byte buffer,int count) ./处理c+传过来的数据s复制代码一切工作准备完毕之后,我们来进行最后一步操作把public void GetData() callback=CallBackFunction; ReadMyVideo(,callback);复制代码经过验证,委托就是c+要的回调函数。C# 调用 C+ dll中含有回调函数地实现方法!2007-08-03 13:23If you have thecallbackusing stdcall liketypedef long (CALLBACK*MRBNOTIFY)(long msg, const char *msgtxt, const void *userParam);(or compiler settings)you could use code like this:/ =public delegate int MRBNOTIFY( int msg, string msgtxt,IntPtruserParam );public delegate int MRBPROXYAUTH(IntPtruserParam, string auth );public delegate int MRBHTTPAUTH(IntPtruserParam, string scheme, string realm, string auth );StructLayout(LayoutKind.Sequential, Pack=1,CharSet=CharSet.Ansi)publicstructMRBSESSIONINFOpublic string baseDir;public MRBNOTIFY notify;public MRBPROXYAUTH proxyAuth;public MRBHTTPAUTH httpAuth;public string httpUserAgent;public string userParam;public int flags;public string url;public int timeout;class InteropWithCbInStructDllImport(YourDll.dll,CharSet=CharSet.Ansi)public static extern void fnTest( In ref MRBSESSIONINFO i );STAThreadstatic void Main(string args)MRBSESSIONINFO i;i.baseDir = dir;i.notify = new MRBNOTIFY( InteropWithCbInStruct.MrbNotify );xyAuth = null; / todo: MRBPROXYAUTHi.httpAuth = null; / todo: MRBHTTPAUTHi.httpUserAgent = agent;i.userParam = usr;i.flags = 0;i.url = url;i.timeout = 77;fnTest( ref i );Console.WriteLine( end. );public static int MrbNotify( int msg, string msgtxt,IntPtruserParam )Console.WriteLine( !MrbNotify msg:0 msgtxt:1 userParam:2, msg, msgtxt, userParam );return 88;/ =例二/ /回调函数 / StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi) public struct SNAP_INFO / / 标示当前工作的图像卡的句柄。 / public int hcg; / / 标示当前工作的图像卡的序号(从1开始) / public int nDevice; / / 标示当前采集完成的图像的序号(从0开始) / public int nNumber; / / 指向用户定义的参数 / public System.IntPtr pParam; public static bool bSnapex = false; public delegate int SNAP_PROC(refSNAP_INFO sf); DllImport(CGVidEx.dll) public static extern CGSTATUS CGOpenSnapEx(int hcg, In SNAP_PROC lpSnapFunc, IntPtr pParam); DllImport(CGVidEx.dll) public static extern CGSTATUS CGStartSnapEx(int hcg, int dwMemOffset, bool bInterlace,short wSum); DllImport(CGVidEx.dll) public static extern CGSTATUS CGStopSnapEx(int hcg); DllImport(CGVidEx.dll) public static extern CGSTATUS CGCloseSnapEx(int hcg); public static int lpSnapFunc(ref SNAP_INFO tf) int numb=tf.nNumber; IntPtr pParm = tf.pParam;/传递的参数 /if (numb = 1) / MessageBox.Show(snapnumber=+numb.ToString(); return 1; public static void OnViewSnapEx(bool bsEnable,System.IntPtr pParam ) /IntPtr handle; SNAP_PROC mysnp = new SNAP_PROC(lpSnapFunc); if (bsEnable& (!bSnapex) status = CGOpenSnapEx(m_DispDesc.hcg, mysnp, (IntPtr)pParam); CG_Verify(status); status = CGStartSnapEx(m_DispDesc.hcg, 0, true, 2); CG_Verify(status); if(CG_SUCCESS(status) bSnapex = bsEnable; MessageBox.Show(status= + status.ToString(); if (!bsEnable) & (bSnapex) status = CGStopSnapEx(m_DispDesc.hcg); CG_Verify(status); if (CG_SUCCESS(status) bSnapex = false; status = CGCloseSnapEx(m_DispDesc.hcg); CG_Verify(status); 求助:C# 调用C+的dll的回调函数的结构体数组的问题,为什么只得到数组的一条记录。2011-5-5 17:49提问者:beddy1|浏览次数:916次DLL接口:struct CardEvent DWORD CardNo; /卡号 INT32 EvtYear; /年 INT32 EvtMonth; /月 INT32 EvtDay; /日 INT32 EvtHour; /小时 INT32 EvtMin; /分钟 INT32 EvtSec; /秒 INT32 EvtDoor; 门序号 INT32 EvtType; 事件类别;回调函数:hDev为收到事件的设备,pEvt:事件信息数组, nEvtCnt 事件个数typedef VOID (CALLBACK* lpOnRecvCardEvent)(HANDLE hDev, CardEvent* pEvt, INT32 nEvtCnt);VOID ACSSetupCardEventCallback (lpOnRecvCardEvent lpCall); :设定系统接收到卡事件后的回调函数.lpCall:回调函数地址。返回:无。C# 定义及调用:public delegate void CardEvent(IntPtr hDev, strcNACSCardEvent phld, Int32 nEvtCnt); DllImport(Comm.dll) /设定系统接收到卡事件后的回调函数 private extern static void ACSSetupCardEventCallback(CardEvent lpCall); public struct CardEvent public uint CardNo; /卡号 public Int32 EvtYear; /年 public Int32 EvtMonth; /月 public Int32 EvtDay; /日 public Int32 EvtHour; /小时 public Int32 EvtMin; /分钟 public Int32 EvtSec; /秒 public Int32 EvtDoor; /门序号 public Int32 EvtType; public static void themain(IntPtr hDev, CardEvent phld1, Int32 nEvtCnt) for (int i = 0; i nEvtCnt; i+) System.Console.Write(事件数: + nEvtCnt); System.Console.WriteLine(); for (int i = 0; i phld1.Length; i+) string time = phld1i.EvtYear + - + phld1i.EvtMonth + - + phld1i.EvtDay + + phld1i.EvtHour + : + phld1i.EvtMin + : + phld1i.EvtSec; System.Console.Write(时间: + time); System.Console.WriteLine(); static void Main(string args) ./前面与设备的连接 lCardEvent myhuidiao = new CardEvent(themain); ACSSetupCardEventCallback(myhuidiao); 运行后,返回的事件个数nEvtCnt 是正确的(有多少显示为多少),但返回结构体数组则只有第一条记录。不知道为什么?求高手解答,高分感谢啊。我来帮他解答检举|2011-5-6 01:03满意回答关键是 lpOnRecvCardEvent 这个函数的第二个参数是个 C+ 指针。楼主在 C# 中声明这个导出函数时,使用了并不对应的 CardEvent C# 数组类型。正确的做法是: 在 C# 声明 lpOnRecvCardEvent 时,第二个参数应该是 IntPtr ,不是数组。由于楼主的这个 VC DLL 可能是自己写的,我没办法给出正确代码。我提供一份模拟代码做参考:-VC DLL:StructMod.h#pragma once#include #ifndef STRUCTMOD_EXPORT#define STRUCTMOD_EXPORT extern C _declspec(dllimport)#endiftypedef struct _tagCardEvent DWORD CardNo;INT32 EvtYear; CardEvent, *PCardEvent;STRUCTMOD_EXPORT CardEvent;STRUCTMOD_EXPORT P
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 铸造碳化钨熔炼破碎工综合考核试卷及答案
- 鼓风炉工基础考核试卷及答案
- 木地板表面造型处理工作业指导书
- 拍卖业务员专项考核试卷及答案
- 焊接设备装配调试工主管竞选考核试卷及答案
- 选煤干燥工作业指导书
- 家庭照护员三级安全教育(班组级)考核试卷及答案
- 污泥处理工效率提升考核试卷及答案
- 信息通信信息化系统管理员作业指导书
- 营养师营养配餐方案设计实操
- 华晨宝马大东厂区天然气分布式能源站项目环评报告
- 青海2023届高校毕业生就业报告出炉:医学和法学就业率最高
- 汽车电控发动机构造与维修(第三版)
- GB/T 328.13-2007建筑防水卷材试验方法第13部分:高分子防水卷材尺寸稳定性
- 茶叶实践报告3篇
- 西门子低压电器快速选型手册
- 养羊与羊病防治技术课件
- 最新教科版五年级科学上册《第2课时 地球的结构》教学课件
- Q∕SY 05129-2017 输油气站消防设施及灭火器材配置管理规范
- 企业微信私域流量运营方案
- 中职学校《机械基础》第二学期全套电子教案(含教学进度计划)(配套教材:高教版中职统编)云天课件
评论
0/150
提交评论