Shell编程的实现方法
前面所说的直接使用Windows操作系统部分功能的编程方法就是针对Windows操作系统外壳的编程,可以通过对操作系统提供的几个编程接口对操作系统的部分功能进行调用,甚至可以按照自己的意图在应用程序中对部分功能进行修改、扩展。但这方面的资料介绍不是特别多,讲的也大都语焉不详,而且用通常的编程方法去进行外壳编程是非常麻烦的,动辄就要对相关的结构对象进行设置,而这样的结构里的数据成员少则十来个多则几十个,因此配置起来非常烦琐,下面就以一个比较简单的外壳操作--拷贝文件进行举例说明: ……
SHFILEOPSTRUCT FileOp; //外壳的文件操作结构
FileOp.hwnd=m_hWnd; //设置句柄
//设置操作方式,拷贝用FO_COPY,删除用 FO_DELETE
FileOp.wFunc=FO_COPY;
FileOp.pFrom=m_source; //源文件路径
FileOp.pTo=m_detect; //目标文件路径
FileOp.fFlags=FOF_ALLOWUNDO; //允许恢复
FileOp.hNameMappings=NULL;
FileOp.lpszProgressTitle=strTitle; //设置标题
SHFileOperation(&FileOp); //执行外壳拷贝
if(FileOp.fAnyOperationsAborted) //监测有无中止
TRACE(An Operation was aborted!!!\n);
…… 上述代码实现起来虽然效果还是不错的,但然实现起来却是比较麻烦的,这仅仅是一个比较简单的外壳操作,对于一些比较复杂的外壳操作比如系统托盘、任务条等等的编程,更是尤为严重,而且象此类编程,MFC里并没有提供封装好的程序类库,提供的只有系统的WinAPI 应用程序接口,因此在程序开发过程中往往会有一种在进行SDK编程的感觉。
COM (Component Object Model,组件对象模型)是Microsoft创建的一种二进制和网络标准,也是Microsoft大力推广并已取得广泛认可的一种组件标准。在COM标准中,COM对象被很好的封装起来,客户无法访问对象的实现细节,提供给用户的唯一的访问途径是通过COM接口来访问。对于COM接口有两方面的含义:首先它是一组可供调用的函数,由此客户可以让该对象做某些事情;其次,也是更为重要的,接口是组件及其客户程序之间的协议。也就是说接口不但定义了可用什么函数,也定义了当调用这些函数时对象要做什么。Windows操作系统本身作为一个大的COM组件对象,也提供了一些必要的COM接口给客户程序,因此我们可以通过这些COM接口来直接对Windows外壳进行编程。
在程序进行正式编写设计之前有一点是肯定的:程序里需要用到COM接口,要对COM对象进行操作。因此首先要加入初始化COM和终止COM的代码。一般是在应用程序类的InitInstance()函数的开始处和返回前添加初始化COM和终止COM代码的: ……
CoInitialize(NULL); //初始化COM
……
CoUninitialize(); //终止COM代码
…… 以上两个函数在MFC程序和非MFC程序中都可以很好的使用。另外,如果程序框架是以MFC为基础的,那么只需简单的调用AfxOleInit()函数就可以达到同样的目的。而且不必显式调用终止COM的代码。在COM标准中,访问COM对象的唯一途径是COM接口,因此在编写操纵Windows 系统外壳程序首先要得到其提供的COM接口。所用的COM接口是IShellDispatch,它是从IDispatch接口派生来的,在VC安装目录的VC98\Include\Exdisp.h头文件中有定义,下面节选了一些将要用到的接口定义: ……
EXTERN_C const IID IID_IShellDispatch;
#if defined(__cplusplus) && !defined(CINTERFACE)
interface DECLSPEC_UUID(D8F015C0-C278-11CE-A49E-444553540000)
IShellDispatch : public Idispatch
{
public:
……
virtual HRESULT STDMETHODCALLTYPE MinimizeAll( void) = 0;
virtual HRESULT STDMETHODCALLTYPE UndoMinimizeALL( void) = 0;
virtual HRESULT STDMETHODCALLTYPE FileRun( void) = 0;
virtual HRESULT STDMETHODCALLTYPE CascadeWindows( void) = 0;
virtual HRESULT STDMETHODCALLTYPE TileVertically( void) = 0;
virtual HRESULT STDMETHODCALLTYPE TileHorizontally( void) = 0;
virtual HRESULT STDMETHODCALLTYPE ShutdownWindows( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Suspend( void) = 0;
virtual HRESULT STDMETHODCALLTYPE SetTime( void) = 0;
virtual HRESULT STDMETHODCALLTYPE TrayProperties( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Help( void) = 0;
virtual HRESULT STDMETHODCALLTYPE FindFiles( void) = 0;
virtual HRESULT STDMETHODCALLTYPE FindComputer( void) = 0;
};
…… 该接口在CoCreateInstance()函数创建COM对象时将会得到指向其的指针,通过这个函数客户程序可以避免显式同类厂打交道,其实该函数内部也调用了CoGetClassObject()函数来获取COM对象的类厂,只不过它把通过类厂创建对象的过程封装起来了,只需用户指定对象类的CLSID和待输出的接口指针及接口ID,显然这样直接创建COM对象是非常便捷的,在获取到COM对象指针之后就可以通过这个指针去访问调用COM对象里的方法来实现Windows 外壳的种种功能调用了。下面是实现该功能的部分关键代码: ……
HRESULT sc;//返回结果
IShellDispatch *pShellDisp = NULL; //初始化接口指针
//直接创建COM对象
sc = CoCreateInstance( CLSID_Shell,//指定待创建的COM对象标识符
NULL, //指定被聚合时的外部对象的接口指针
CLSCTX_SERVER, //指定组件类别,可以指定进程内组件进程外组件或者进程内控制对象。
IID_IDispatch, //指定接口ID,需要注意的是这里指的是待
//创建的COM对象的接口ID,而非类厂对象的接口标识符
(LPVOID *) &pShellDisp );//存放函数返回的对象的接口指针
/* 在上述代码中,CoCreateInstance首先调用CoGetClassObject函数创建类厂对象,然后用得到的类厂对象的接口指针创建真正的COM对象,最后把类厂对象释放并返回,这样就很好的把类厂屏蔽起来,使用户用起来更为简单。*/
if( FAILED(sc) )//必须用FAILED 或SUCCECCED来判断COM对象是否创建成功
return;
pShellDisp->FindFiles(); //调用COM对象里的方法
pShellDisp->Release(); //释放申请到的接口指针
…… 在这里通过pShellDisp接口指针调用了COM对象的FindFiles()方法去进行查找文件的系统外壳操作。同样,可以根据实际需要灵活调用响应的方法来执行相应的外壳操作,主要有以下几个方法:MinimizeAll:所有窗口最小化、UndoMinimizeALL:恢复窗口最小化、 FileRun:开始菜单的运行…、CascadeWindows:层叠窗口、TileVertically:垂直平铺、TileHorizontally:水平平铺、ShutdownWindows:关闭Windows、Suspend 挂起计算机、SetTime:设定时间、TrayProperties:任务栏属性、Help Windows:帮助、FindFiles:查找文件、FindComputer:查找计算机等。
这些接口均在VC安装目录的VC98\Include\Exdisp.h头文件中有定义,可以通过对该文件的查看来编写响应的外壳操作代码。