介於自己的ATL學的不怎么樣,而且現在掌握的也不是很熟練,所以還是先用MFC來寫一個播放視頻的ActiveX控件吧。用MFC做ActiveX控件非常簡單,和用MFC做普通的Windows應用程序差不多是一樣的步驟。所以,如果用過MFC的話,開發起來會非常的順手。不過了,MFC是已經快過時了,不過在中國暫時還沒有過時。還有一個問題,MFC做的界面非常的丑陋~~~當然這是針對於像我這種不會搞資源文件等一系列東西的人來說的。其實現在的ActiveX控件也可以用C#來寫,但是像播放器控件這些東西,一般解碼庫(如ffmpeg)都是用C語言寫的。所以,用C++來開發ActvieX控件在某些地方是有優勢的。這里我不詳細的寫播放器是怎么做的,只是寫一下視頻控件怎么寫。因為用ffmpeg做一個播放器網上有很多的文章。比如我就參考了這篇文章,寫的非常的詳細。傳送門:http://blog.sina.com.cn/s/blog_51396f890100nd91.html。
首先我們得用Visual Studio建立一個基於MFC的ActivX控件的工程。如下圖所示:
然后自己取個想要的名字,一直點擊下一步,知道出現這一步,如下圖所示:
下面來詳細介紹一下其中幾個選項。
創建的控件基於:none 表示我們不基於現在Windows里面已有的控件,不過有的人習慣播放視頻的窗口使用Static控件,所以也可以選擇Static。
有“關於”對話框:這個可以不用選。
無窗口激活:這個地方一定不能選。因為我們播放視頻需要一個窗口,所以得選上。但是實際上也可以用無窗口控件來制作ActiveX控件,不過相對麻煩很多,因為你不能獲得相應的窗口句柄來播放視頻,你只能使用容器的窗口來播放視頻,不過那樣的話處理起來很麻煩。這里給一個MSDN上面的說明:http://msdn.microsoft.com/zh-cn/library/cc485730(v=vs.71).aspx
反正無窗口激活的控件就是說,這個窗口沒有消息循環,沒有窗口句柄,只負責處理一些容器給它的事件。無窗口的ActiveX控件和容器通過接口通信的方式來共同完成一個窗口的所有功能。無窗口控件想要播放視頻的話只能使用容器的窗口句柄,然后用DirectX或者SDL都不能很好的處理這種情況。我也只能提供一種思路。無窗口控件可以使用DirectShow來播放視頻,因為里面有個叫做VMR的東西可以用來管理窗口,指定視頻在窗口中播放的位置。當然,這個方法我也沒有試過,如果有誰搞出來了,記得通知我一聲。關於DirectShow市面上就只有兩本書,《DirectShow開發指南》和《DirectShow務實精選》,全都是同一個人寫的。另外的參考資料就是MSDN上面的了。不過你會覺得書上的東西和MSDN上面的東西差不多,有的地方連圖片都是一模一樣的,只是翻譯成中文罷了。這個就說這么多了。
其他的選項都直接默認選項,有什么不懂的地方,上面那個鏈接,里面有MSDN的最權威的解釋。
建立完工程以后,我們可以在解決方案資源管理器中看到有以下一些文件,如下圖所示:
其中構造大體上和MFC做的應用程序差不多。只不過多了幾個東西。其中多了一個Player.def和Player.idl文件。(至於targetver.h這個頭文件我也沒有仔細研究過。)其中*.idl文件是ActiveX控件的接口描述文件,其實准確來說應該是COM組件的接口描述文件。*.def是一個接口導出的定義文件,這個東西我們在寫dll文件的時候也會用到的。
PlayerCtrl.cpp就是我們主要寫代碼的文件,這個文件里面是各個接口的實現。有窗口的ActiveX控件主要是通過接口調用和消息來進行驅動的,消息機制應該說是Windows應用程序的基礎了。接口調用是COM組件的基礎,ActiveX控件的接口和COM組件的接口一樣,可以給所有的語言調用。用在網頁上的話,主要是給網頁上的腳本語言調用。
在類視圖中,我們可以看到如下所示的一些類:
其中CPlayerApp 和MFC應用程序一樣,都代表了一個進行消息循環的線程。CPlayerCtrl類則是我們處理消息和接口調用的類,我們添加的每一個消息處理函數和接口調用函數都會出現這個類里面。
我們右鍵單擊PlayerLib子目錄下的_DPlayer,選擇添加選項,在子菜單中我們可以看到添加方法和添加屬性。其中方法就是供容器調用的接口方法,而屬性了則是ActiveX控件的屬性。關於屬性的東西,請自己翻閱一些其他資料。
我們先添加一個方法Play,如下圖所示:
其中返回類型要在下拉列表框里面進行選擇,因為這些變量都是符合了COM規范的,最好不要填寫自己所定義的一些變量類型。名稱自己取一個吧。我們現在添加一個Play的方法,我要添加一個叫做filename的參數表示需要播放的文件。在COM組件中,為了統一字符串,我們需要選擇BSTR這個類型,這個類型就是一種字符串類型,至於BSTR的詳細解釋請自行查閱其他資料。這些東西輸入好以后選擇添加,然后再點下一步,然后選擇完成就可以成功的添加一個接口了。
在Player.idl文件中,可以看到增加了這么一句話
1 dispinterface _DPlayer 2 { 3 properties: 4 methods: 5 [id(1)] LONG Play(BSTR filename); 6 };這一段代碼就是描述Play接口的,當然,只有其中的那一句話才是描述接口的,所有的接口描述都出現在methods:下面,屬性描述都會出現在properties:下面。
然后在PlayerCtrl.cpp文件中添加了下面這個函數
1 LONG CPlayerCtrl::Play(LPCTSTR filename) 2 { 3 AFX_MANAGE_STATE(AfxGetStaticModuleState()); 4 5 // TODO: 在此添加調度處理程序代碼 6 7 return 0; 8 }
接着,我們在這個函數里面添加上播放視頻的代碼就可以了,就是前面給出的鏈接里面的用1000行代碼寫個播放器。當然,如果你覺得一個函數里寫的太多了,而且不好進行各種播放控制,這時候就得把播放部分封裝起來,然后再用這些接口函數調用具體封裝好的代碼。
最后需要注意的是,一般來說寫ActiveX控件都使用的是Unicode編碼,因為這樣可以和前端、其他的容器進行很好的交互,而且做COM組件都推薦使用Unicode編碼。如果不知道Unicode編碼到底是什么,那么久自己到網上去找一找資料吧,作為一個現代的程序員,Unicode編碼應該可以說是必須要掌握的,反正掌握起來也不是很難,就多學一點唄。
說到這里,關於字符串的處理,我推薦去看一下《深入解析ATL》這本書,里面有一章詳細的講訴了微軟提供的字符串處理的方法。哎,C++的字符串處理,說多了都是淚啊。微軟提供的字符串處理的方法都在atlconv.h這個頭文件里面,是以模板的形式給出的(這句簡直是廢話,以為整個ATL都是模板~~~),然后字符串的處理的模板全都包含在了ATL這個命名空間中,所以需要使用using namespace ATL這一句話,或者顯示的寫出ATL這個命名空間。
由於我們使用的是BSTR作為字符串,不過函數中出現的是LPCTSTR類型的,所以可以直接賦值給CString類型的變量。然后我們使用TCHAR*來表示這個文件名。TCHAR是一種微軟定義的字符串類型,當你的工程選用的字符集是Unicode編碼是,那么TCHAR就是寬字符,如果字符集是多字節字符集,那么TCHAR就是普通的多字節字符。所以,可以認為TCHAR是一種萬能的字符類型。其實這些都是通過宏來實現的。然后由於打開文件或者什么的,需要使用ANSI字符串,這時候我們就需要把Unicode編碼的字符串轉換成為ANSI字符串。這時候哦我們就可以用CT2A這個宏來轉換。舉個栗子:
1 CStrng sFilename = "C:\\XXX\XXX\X.mkv"; 2 TCHAR *tcsFilename; 3 tcsFilename = sFilename.GetBuffer(); 4 pf = fopen(CT2A(tcsFilename), "rb");這樣就可以把Unicode編碼的字符串轉化成為ANSI字符串。具體的內容還是看《深入解析ATL》里面講字符串的那一章吧,講的真心好。