介于自己的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》里面讲字符串的那一章吧,讲的真心好。