1. 程式人生 > >剛剛完成的一個程序通訊及託管非託管混合程式設計的總結之概述

剛剛完成的一個程序通訊及託管非託管混合程式設計的總結之概述

題目的背景是這樣的:有一個現成的程式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有一個建構函式
能夠輕鬆地解決這個問題。

在解決點陣圖畫素資料的傳遞問題時,對點陣圖資料又有了進一步的認識:比如一個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,它收不到訊息就會等在那裡不動,不符合我的要求。

以上是整個題目的大體思路過程,具體程式碼另開帖吧。