关于C#调用C++动态库的文章很多,调用动态库中回调函数的方法也不在少数。但大多数调用回调函数的方法依然保留了C++的语法特点。

  比如有一段C++的回调函数代码,为了表达它的意思,我把注释也粘贴了进来:

    /*********************************************************************************************************
   ** Function name:     epcBuzzerAsyncOn
   ** Descriptions:      本函数使蜂鸣器蜂鸣指定时间
   ** input parameters:        dwOntime    蜂鸣器持续蜂鸣的时间(ms),其中0表示一直蜂鸣
   ** output parameters:    无
   ** Returned value:          TRUE:成功;FALSE:失败
   ** Note:                    本函数以异步方式执行,不会被阻塞
   *********************************************************************************************************/
EPCBUZZERLIB_API BOOL epcBuzzerAsyncOn (DWORD dwOntime);   /*********************************************************************************************************
** Function name: epcBuzzerSetCallBackFunc
** Descriptions: 设置回调函数指针,当异步的蜂鸣器操作任务完成后,会调用该回调函数通知用户程序。
** input parameters: lpfnNotify 回调函数指针,如果是NULL,则表示不需要通知用户程序。函数类
** 型为void (*lpfnNotify)(BOOL bResult),bResult为执行结果,
** TRUE表示执行成功,FALSE表示执行失败
** output parameters: 无
** Returned value: 无
*********************************************************************************************************/
EPCBUZZERLIB_API VOID epcBuzzerSetCallBackFunc( void (*lpfnNotify)(BOOL));

 

  一般的文章都会教你转换成如下代码,这些方法都属于Buzzer类:

     /// <summary>
/// 使蜂鸣器蜂鸣指定时间,异步执行,不会阻塞当前线程.
/// </summary>
/// <param name="onTime">蜂鸣时间(ms).</param>
/// <returns>true:蜂鸣成功,false:蜂鸣失败</returns>
[System.Runtime.InteropServices.DllImport("Lib\\epcBuzzerLib.dll", SetLastError = true)]
private static extern bool epcBuzzerAsyncOn(int onTime);      /// <summary>
/// 先申明一个委托,该委托定义了蜂鸣完成回调函数的指针和数据类型
/// </summary>
/// <param name="result">if set to <c>true</c> [result].</param>
public delegate void BuzzerHandler(bool result); /// <summary>
/// 再设置蜂鸣完成回调函数.
/// </summary>
/// <param name="handler">The handler.</param>
[System.Runtime.InteropServices.DllImport("Lib\\epcBuzzerLib.dll", SetLastError = true)]
private static extern void epcBuzzerSetCallBackFunc(BuzzerHandler handler);

  然后在主程序中如此调用:

//先定义一个满足委托类型的函数
public void BuzzerNotify(bool result)
{
   //在这里编写蜂鸣完成后你想要执行的代码...我这里根据result随便写几行。
if(result)
{
  MessageBox.Show("蜂鸣成功");
}
else
{
  MessageBox.Show("蜂鸣失败");
}
} //在按钮事件中调用回调函数
private void onButton_Click(object sender, EventArgs e)
{
  Buzzer.epcBuzzerCallBackFunc(this.BuzzerNotify);
  Buzzer.epcBuzzerAsyncOn();
}

这样,在蜂鸣器蜂鸣3000ms之后,就会调用BuzzerNotify,并将一个bool结果传进去。然后你可以在BuzzerNotify中做你想做的事。

  虽然这样也能够解决问题,但是我总感觉很别扭,总觉得我的代码里有C++风格,这让有代码洁癖的我感觉到浑身不自在。所以,我决定重构一下这段代码,让它符合C#的风格。

  重构的思想是这样的,虽然我不太能完全理解回调函数这个东西,但是我能隐约感觉到它就是一个事件触发机制,比如上面那段代码,就可以理解为当蜂鸣完成后,就会通知BuzzerNotify,让它开始执行。只不过BuzzerNotiy是在主函数中定义的一个函数,它不能像事件那样可以注册,你所有想在蜂鸣器完成后做的工作,都必须写在这个函数里面。很明显,如此糟糕的扩展性,完全不是C#的风格。所以,我要做的,就是将BuzzerNotify变成Buzzer类的一个事件,让它可以使用+=这样的神器!

  其实做法也是蛮简单的,我把代码一贴出来大家立马就能明白了。以下是Buzzer类的代码

        /// <summary>
/// 申明蜂鸣完成回调函数指针和数据类型
/// </summary>
/// <param name="result">if set to <c>true</c> [result].</param>
public delegate void BuzzerHandler(bool result); /// <summary>
/// 蜂鸣完成时触发该事件.
/// </summary>
public static event BuzzerHandler BuzzingComplete; /// <summary>
/// 使蜂鸣器蜂鸣指定时间,异步执行,不会阻塞当前线程.
/// </summary>
/// <param name="onTime">蜂鸣时间(ms).</param>
/// <returns>true:蜂鸣成功,false:蜂鸣失败</returns>
public static bool TurnOn(int time)
{
Buzzer.BuzzingCompleteSetCallBack();
return Buzzer.epcBuzzerAsyncOn(time);
} /// <summary>
/// 使蜂鸣器蜂鸣指定时间,异步执行,不会阻塞当前线程.
/// </summary>
/// <param name="onTime">蜂鸣时间(ms).</param>
/// <returns>true:蜂鸣成功,false:蜂鸣失败</returns>
[System.Runtime.InteropServices.DllImport("Lib\\epcBuzzerLib.dll", SetLastError = true)]
private static extern bool epcBuzzerAsyncOn(int onTime); /// <summary>
/// 定义蜂鸣完成回调函数,用来处理事件.这个方法比较关键,它相当于把之前的BuzzerNotify,只不过把它封装进Buzzer类了。
/// </summary>
/// <param name="result">if set to <c>true</c> [result].</param>
private static void Event(bool result)
{
if (BuzzingComplete != null)
{
BuzzingComplete(result);
}
} /// <summary>
/// 蜂鸣完成回调函数.
/// </summary>
private static void BuzzingCompleteSetCallBack()
{
Buzzer.epcBuzzerSetCallBackFunc(Event);
} /// <summary>
/// 设置蜂鸣完成回调函数.
/// </summary>
/// <param name="handler">The handler.</param>
[System.Runtime.InteropServices.DllImport("Lib\\epcBuzzerLib.dll", SetLastError = true)]
private static extern void epcBuzzerSetCallBackFunc(BuzzerHandler handler);

虽然多了很多代码,但为了面向对象,这绝对是值得的!
在主函数中可以如此调用:

private void FirstThing(bool result)
{
MessageBox.Show("Do my first thing");
} private void SecondThing(bool result)
{
MessageBox.Show("Do my second thing");
} private void onButton_Click(object sender, EventArgs e)
{
Buzzer.BuzzingComplete += this.FirstThing;
Buzzer.BuzzingComplete += this.SecondThing;
Buzzer.TurnOn();
}

如此一来,主程序中的代码看上去就舒服多了。值得一提的是,如果你连续点几次这个OKButton的话,这两个方法会被重复注册。

C#将C++动态库的回调函数封装成事件的更多相关文章

  1. Golang编写动态库实现回调函数

    Golang编写动态库实现回调函数 我们现在要做一个动态库,但是C++实在是比较难,于是就想能不能用更简单的golang来实现,golang也就是最近的版本才支持编译成动态库,在网上也没找到可用的案例 ...

  2. Golang调用windows下的dll动态库中的函数

    Golang调用windows下的dll动态库中的函数 使用syscall调用. package main import ( "fmt" "syscall" & ...

  3. windows下查看静态库和动态库的导出函数

    在window下查看动态库的导出函数可以用vs自带的Depends工具: 查看静态库的信息要用命令行来实现: dumpbin   /LINKERMEMBER   Test.lib   >   1 ...

  4. Golang调用windows下的dll动态库中的函数 Golang 编译成 DLL 文件

    Golang调用windows下的dll动态库中的函数 package main import ( "fmt" "syscall" "time&quo ...

  5. 回调函数与DOM事件

    原文:http://dean.edwards.name/weblog/2009/03/callbacks-vs-events/ 先看如下代码: document.addEventListener(&q ...

  6. 动态库(.so)隐藏函数名

    一.偶遇 error: undefined reference to  xxx 问题 尝试封装通用的接口到一个private.so,然后供客户端使用,private.so编译出来后由sample.cp ...

  7. c++ 回调函数封装

    std::function<void(int a,int b)> ha; //函数封装  当成参数用callback  std::bind(&fun1,this,std::plac ...

  8. PCL中将回调函数封装到类中

    这是类中的声明 private://点云回调函数 NuClearTask_MyPointCloudHandle //点云选择 static void ps_callback(const pcl::vi ...

  9. java将类和函数封装成jar,然后在别的项目中使用这个jar包

    本来想用idea安装的,不过用maven生成后发现jar有20,30M肯定不对,后来还是用eclipse生成了,方便很多 环境: eclipse luna,jdk1.8_112 1.生成jar包,首先 ...

随机推荐

  1. 基础:从概念理解Lucene的Index(索引)文档模型

    转:http://blog.csdn.net/duck_genuine/article/details/6053430   目录(?)[+]   Lucene主要有两种文档模型:Document和Fi ...

  2. RHCE7.0练习题汇总[转]

    RHCE7.0练习题汇总38 RHCSA部分 关于密码破解,在上午的考试中需要我们自己安装图形化界面和破解root密码,root的密码按照题目的要求来进行修改.可以使用单用户模式或者使用上课讲的re. ...

  3. 连接数据库的URL等于多少?

    JDBC编程步骤如下: 1.加载驱动 Class.forname(driverClass); 比如:加载MySQL的驱动 Class.forname("com.mysql.jdbc.Driv ...

  4. SVN学习之参数定义及设置

    http://jackyrong.iteye.com/blog/238835 conf目录中打开svnserve.conf ,去掉注释,搞成如下这样 [general] anon-access = r ...

  5. TMS320C54x系列DSP的CPU与外设——第1章 绪论

    第1章 绪论 TMS320C54x DSP是TMS320系列DSP产品中的定点数字信号处理器.C54x DSP满足了实时嵌入式应用的一些要求,例如通信方面的应用. C54x的中央处理单元(CPU)具有 ...

  6. Jedis 连接redis超时

    redis默认不允许远程连接 用vi打开Redis服务器的配置文件redis.conf ~ sudo vi /etc/redis/redis.conf #注释bind #bind 127.0.0.1 ...

  7. Hibernate中get和load的区别

    get获取的对象立即执行sql查询数据库中当前实体表中的数据,如果外键关联的其他实体表如果配置了懒加载关闭,则也会查询出外键关联的其他实体表中的数据,否则外键关联的其他实体表则以代理对象表示(称其为代 ...

  8. git的使用(3) 多分支情况下的pull

    当你存在多个分支的时候,你需要pull下来分支上面的内容,你需要指定分支进行同步命令: git pull origin branch (branch 是你的分支的名字)

  9. [复变函数]第05堂课 1.4 复球面与 $\infty$; 作业讲解; 2 解析函数 2.1 解析函数的概念与 Cauchy-Riemann 方程

    1. 复球面 大漠孤烟直, 长河落日圆. $$\bex \bbC\cong \bbS^2\bs \sed{N},\quad \bbC_\infty=\bbC\cup \sed{\infty}\mbox ...

  10. viewpage广告条的制作

    package com.itheima27.viewpagerdemo; import java.util.ArrayList; import java.util.List; import andro ...