Windows 7程序开发系列之二(JumpList篇1 - User Task)

来源:互联网 发布:java json转化为数组 编辑:程序博客网 时间:2024/06/07 11:45

      相对于上一篇中任务栏特性的开发,JumpList的开发显得稍微麻烦一些。JumpList将分为两次讲解,这次先讲解如何添加用户任务(User Task)。同样以foobar2000为例,当右键点击任务栏按钮时,显示程序的JumpList。

       最下方3个项目为系统任务,一般不需要我们去操作。上方的两个任务:播放、参数选项,即为自定义的用户任务。用户任务本质上是一个快捷方式,对应于程序中由IShellLink接口表示。

一、ICustomDestinationList接口

      同样先创建一个窗口,然后添加一个CreateJumpList方法,在这个方法中创建JumpList。创建JumpList需要几个步骤:1、创建ICustomDestinationList接口,这个接口对应的就是JumpList。2、调用BeginList方法。3、创建IObjectCollection接口。4、向IObjectCollection中添加快捷方式。5、由IObjectCollection接口取得IObjectArray接口。6、将IObjectArray加入ICustomDestinationList。7、调用CommitList方法。在CreateJumpList方法中加入下面代码:

二、IShellLink接口

       上面的代码还不能编译,AddShellLink方法还没有编写。这个方法用于在IObjectCollection中加入一个IShellLink对象。IShellLink接口有几个方法用于设置属性:1、SetPath:设置目标的路径。2、SetWorkingDirectory设置工作目录。3、SetIconLocation设置图标。4、SetArguments设置命令行参数。5、设置标题(这一步稍微复杂一点、在独立的方法中设置)。设置完属性后,调用IObjectCollectionAddObject方法,将快捷方式加入。

      SetTitle方法用于设置标题,由于IShellLink接口本身不带有设置标题的方法。因此需要用到另一个接口IPropertyStore来设置标题。首先由IShellLink接口得到IPropertyStore接口,然后由字符串初始化一个PROPVARIANT对象,接下来将该PROPVARIANT对象设置为标题,最后提交。


      上面的例子将目标路径设置为记事本的路径,并且加上命令行参数"Test.txt",当点击后,将调用记事本并传入参数"Test.txt"。

三、由当前程序实例响应用户任务

       现在问题的是,在大多数情况下,用户任务应该不仅仅对应的是一个指向某个程序的快捷方式,而是应该对应的是当前程序的某个功能。比如在foobar2000中的参数选项,对应着程序中的功能。而且当选择这个用户任务的时候,应该是由当前程序的当前实例来响应这个操作,而不是由新的实例来完成这个操作。接下来要做的就是如何将用户任务反映到当前程序实例中。

       这里涉及到两个问题。第一个问题是,程序必须是单实例应用程序,因为当点击用户任务时,我们不能由新的程序实例来响应。解决办法是当程序启动时,检查该程序是否已有先前实例在运行,如果有,则退出,防止第二个实例运行。第二个问题是,我们的用户任务实际是通过快捷方式传递给程序的参数来反映的。而这个参数只有第二个实例能够收到。那么在第二个实例退出之前,要想办法把参数传递到第一个实例。

       我们先把之前创建的用户任务改为指向自己。修改AddShellLink方法如下,其中注释的地方即为修改过的地方:

       现在的情况是,当我们点击User Task 1时,一个新的实例启动了。接下来就来解决上面提到的问题。


       第一个问题的解决方法是使用Mutex(互斥量)。当第一个实例启动时,创建一个互斥量。当第二个实例启动时,同样创建这个互斥量。由于该互斥量已经创建,所以必然导致失败,第二个实例退出。当第一个实例结束的时候,撤销该互斥量。在WinMain函数的开头加上下面代码:

WinMain函数的结尾加上下面代码:

这次当我们点击User Task 1时已经看不见新的实例启动了(实际上新的实例仍然启动了,只不过检测到先前实例后,迅速退出了)。

      第二个问题是如何将参数传递到第一个实例。对于Windows程序来说,最容易想到的就是通过消息发送了。但又如何得到先前实例的主窗口句柄呢?这里要用到内存映射文件(Memory Mapped File)。第一个实例运行后,将自己的主窗口句柄放入到一个内存映射文件中,第二个实例从该内存映射文件中读出先前实例的主窗口句柄。在窗口创建之后加入下面代码,将当前主窗口句柄放入内存映射文件:

该内存映射文件的名字不要和之前Mutex的名字相同,否则会创建失败。在程序退出之前要关闭文件:

在WinMain函数开头,检测到有先前实例运行之后。从这个内存映射文件读入先前实例的主窗口句柄:

得到先前实例的窗口句柄后,利用WM_COPYDATA消息,将命令行参数发送到先前实例。

 

然后加入对WM_COPYDATA消息的处理:

在HandleCmdLine中,我们只简单显示一条消息即可:

验证一下现在的效果:

因为JumpList即使在程序没有运行时也是存在的,所以我们在窗口创建之后也要调用一次HandleCmdLine方法,以使第一个程序实例可以响应静止状态下的用户点击。如下面的效果:

此时程序未运行,只是锁定到任务栏,点击JumpList上的用户任务,第一个程序实例启动,并响应用户任务

       为了简化问题,上面的代码中省去了很多错误处理。在实际的使用中,我将单实例运行与实例间的数据传递封装在了一个辅助的类中,源代码中将会提供这个类。

       上面代码需要包含的头文件:

      需要连接的库:shlwapi.lib

 

     本节源代码下载

原创粉丝点击