现在的位置: 首页 > 综合 > 正文

刚刚完成的一个进程通信及托管非托管混合编程的总结之概述

2013年07月29日 ⁄ 综合 ⁄ 共 2628字 ⁄ 字号 评论关闭
题目的背景是这样的:有一个现成的程序A,虽然是VS2005下的VC工程,但是却是基于MFC的,它实现的功能是获取摄像头的视频,并对视频做出人脸识别。现在需要添加移动侦测的功能,即发现视频中有物体移动,则自动开始录像,无物体移动时,则停止录像。

通过搜索,最终在codeproject找到了一个程序,正是用AForge.NET(一个开源项目,用.NET开发的一个实现各种计算的类库)做的一个视频移动侦测,而且也带有录像功能,可以说正符合我的要求,而且程序封装的几个类,也很清晰很好用。下面的问题就是如何把它用到A里面去了。

由于找到的移动侦测算法是用C#写的,所以如何用到MFC的非托管代码中就是首先面对的问题。我先在一个C#的dll工程中,把要用到的类(主要是移动侦测的计算类和把视频帧录制成视频文件的类)编译成一个托管的dll,取名叫VideoMonitorLib.dll,然后试着在A中使用它。

一开始打算像一般的托管工程那样,简单地添加引用,但是发现非托管的基于MFC的框架视图架构工程无法添加引用,解决办法是在工程属性里面打开托管代码支持,把工程变成托管、非托管混合的,然后使用#using引入dll文件。但是很快发现了一个严重的问题:由于A是基于MFC的框架视图结构,而MFC类里面不允许有托管代码,再加上托管代码不允许被写今全局范围,导致托管代码毫无立足之地,于是这种直接在A里面添加移动侦测功能的路走不通了。

在这种情况下,决定再做一个程序B,实现对视频帧的移动侦测运算和录像功能,A和B之间通过进程通信传递数据。

关于进程通信,最先知道的是PIPE,而后又了解到,进程通信的办法很多,共享内存映射、共享dll、剪贴板、DDE、消息等等,曾经看到帖子总结了11种办法。这些办法各有千秋,具体的这里暂不赘述。经过仔细的了解、学习和比较,最终决定用共享内存映射的办法,因为它是比较基础的办法,如WM_COPYDATA实际上也是通过它实现的,而且做同一台机器的本地进程通信,没必要用socket那种办法,而像剪贴板这种,只能算是没办法的办法,谁也不希望进程通信要影响到系统其它软件的复制粘贴吧。另外,搜到了有人用C++写好的CFileMapping类,把CreateFileMapping等共享内存映射的API封装起来使用,很是方便。

这里补充一下,由于要使用封装好的CFileMapping类,该类是非托管的c++,同时又要使用托管代码的VideoMonitorLib.dll,故程序B采用了托管、非托管混合编程,并且使用MFC(包含afx.h),但是采用控制台程序,而非MFC的框架视图,也不是基于对话框的,使得托管代码能够最大限度地摆脱MFC的束缚,同时又能随时利用MFC。这样也使得B可以直接在工程的属性中添加对System.Drawing.dll和VideoMonitorLib.dll的引用。

具体实现过程中,也有许多问题需要解决。

首先是传输的数据问题。A可以实时得到视频帧,虽然是A类库中自定义的封装类型,但是可以转换为WIN32下的BITMAP类型,也可以得到HBITMAP。一开始想着把BITMAP、HBITMAP传递过去就行了,但是后来忽然顿悟(其实是在读了《Windows核心编程》这一经典的相关章节后所领悟),BITMAP结构体中只是保存了位图的一些属性数据(长、宽、调色板等等),关于实际的像素数据,它记录的是像素数据的指针地址(LPVOID  bmBits),至于HBITMAP,更是只是一个句柄。这些指针、句柄,指向的地址是A内部的,单单把它们传递给B是毫无意义的,B无法使用这些指针、句柄,所以必须想办法把位图的像素数据直接在A、B间共享,这样才是真正意义上把实际的位图数据进行传递了。好在通过HBITMAP,可以用GetBitmapBits得到像素数据。

在B中,由于移动侦测算法以及录像的函数所处理的是.NET中的System.Drawing.Bitmap类型的位图,所以需要根据得到的位图的各个属性及其像素数据还原出一个System.Drawing.Bitmap类型的对象。这个问题曾经困扰了我很久,后来忽然发现Bitmap有一个构造函数

public: Bitmap(
int width,
int height,
int stride,
PixelFormat format,
IntPtr scan0
);

能够轻松地解决这个问题。

在解决位图像素数据的传递问题时,对位图数据又有了进一步的认识:比如一个800×600的24位彩色位图,“24位”表示说每一个像素需要24个比特位来记录它的颜色信息,即BITMAP结构中的bmBitsPixel元素记录的信息,这意味着这个位图的一个像素的数据占据3个字节的大小。那么,800×600的位图每行有800个像素,则每行有800×3=2400个字节了,就是BITMAP结构中bmWidthBytes的意义。那么对于一个800×600的24位彩色位图,如果保存成一个bmp文件的话,会发现它的大小是1.4M多一点,如果用ultraedit打开的话,会看到更加精确的字节数,正好是比800×600×3=1440000多一点(多出的那些是位图文件的一些信息)。

解决了位图数据传递的问题,下面需要注意的就是两个进程同步的问题了。A程序是在一个OnTimer函数中,每5毫秒得到一个视频帧进行处理,把位图数据传递给B的过程也是在这里实现的。

在我的第一个版本中,是通过在传递位图数据的同时传递一些标志信息来实现进程同步的。A和B两个进程对这些标志信息读写,以决定是否发送数据、是否处理数据,以达到进程同步的目的。做完这个版本后,觉得这种办法有些原始,于是又学习了一下自定义消息编程,通过A和B之间互发消息实现同步。这里遇到的问题是,B是个控制台程序,无法使用windows的窗口消息机制,但是有另外的办法,即如果A和B互相知道了对方的主线程ID,则可以通过PostThreadMessage、GetMessage、PeekMessage进行消息传输。于是在A启动B的时候,二者先是通过共享内存交换了一下主线程ID。这里我使用了PeekMessage,因为我需要在没有收到消息的时候也进行处理。若是GetMessage,它收不到消息就会等在那里不动,不符合我的要求。

以上是整个题目的大体思路过程,具体代码另开帖吧。

抱歉!评论已关闭.