JumpList中除了有User Task外,还有Destination。与User Task不同,Destination中是与该程序相关联的文件的链接。Destination还可以分类,Windows已经自动为我们管理了“最近“和“常用“两个类别。比如记事本程序,使用“最近”这个类别:
这对于大多数程序已经足够。但Windows也为我们提供了管理自己的类别的接口。程序可以根据自己的需要,添加自己的类别。本节将介绍如何将自己的类别加入JumpList。
一、向 JumpList中加入自定义类别
首先来定义一些准备要放入JumpList的文件:
- LPCTSTR szFiles[] = {
- TEXT("TestFile1.txt"),
- TEXT("TestFile2.txt"),
- TEXT("TestFile3.txt")
- };
响应按键消息,当按下“j”的时候,创建JumpList。
- case WM_CHAR:
- switch(wParam)
- {
- case 'j':
- CreateJumpList();
- break;
- }
- break;
- //创建Destination List
- void CreateJumpList()
- {
- //创建文件
- for (int i = 0; i < ARRAYSIZE(szFiles); ++i)
- {
- CreateFile(szFiles[i], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
- }
- //JumpList
- ICustomDestinationList *pCDL = NULL;
- HRESULT hr = CoCreateInstance(CLSID_DestinationList, NULL,
- CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pCDL));
- if(SUCCEEDED(hr))
- {
- //BeginList
- UINT uMaxSlots;
- IObjectArray *pOARemoved = NULL;
- hr = pCDL->BeginList(&uMaxSlots, IID_PPV_ARGS(&pOARemoved));
- if (SUCCEEDED(hr))
- {
- //ObjectCollection
- IObjectCollection *pOC = NULL;
- hr = CoCreateInstance(CLSID_EnumerableObjectCollection, NULL,
- CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pOC));
- if(SUCCEEDED(hr))
- {
- //每个文件分别创建ShellItem
- for(int i = 0; i < ARRAYSIZE(szFiles); ++i)
- {
- //拼接文件路径
- WCHAR pszPath[MAX_PATH];
- WCHAR pszCurDir[MAX_PATH];
- GetCurrentDirectory(ARRAYSIZE(pszCurDir), pszCurDir);
- PathCombine(pszPath, pszCurDir, szFiles[i]);
- //根据文件路径创建ShellItem
- IShellItem *pSI = NULL;
- hr = SHCreateItemFromParsingName(pszPath, NULL, IID_PPV_ARGS(&pSI));
- if (SUCCEEDED(hr))
- {
- pOC->AddObject(pSI);
- pSI->Release();
- }
- }
- IObjectArray *pOA = NULL;
- hr = pOC->QueryInterface(IID_PPV_ARGS(&pOA));
- if (SUCCEEDED(hr))
- {
- //将自定义Category加入JumpList
- pCDL->AppendCategory(TEXT("My Custom Category"), pOA);
- pOA->Release();
- }
- hr = pCDL->CommitList();
- pOC->Release();
- }
- pOARemoved->Release();
- }
- pCDL->Release();
- }
- }
上面的代码首先在当前文件夹内,将上面定义的几个文件创建出来。然后其他的步骤与创建User Task类似。不过中途向IObejctCollection 接口中加入的是IShellItem 接口,而不是IShellLink 接口。创建IShellItem 接口时,先取得文件的完整路径,然后使用API函数SHCreateItemFromParsingName 创建。取得IObjectArray 接口后调用AppendCategory将自定义的类别加入JumpList。
执行上面程序,在窗口上按“j”。没有作用,效果没有达到。前面提到过,Destination中的文件是与我们的应用程序关联的文件。我们加入的是3个txt文件,而我们的应用程序目前并没有与txt文件关联。下面还有一些工作要做。
二、Application ID
首先要提一下AppID,它是一个字符串,Windows用它来标识一个程序。在缺省的情况下,我们的程序不需要设置AppID,Windows会自动为我们生成和管理AppID。但是在某些情况下,我们自己管理AppID更好,比如上面提到的文件关联。只有与我们的程序关联的文件才会显示到Destination中。Windows7中,任务栏按钮的分组也是以AppID为依据的,具有相同AppID的窗口,他们的任务栏按钮会被分为一组,即使它们由不同的程序创建。反之,具有不同AppID的窗口,即使她们是由同一个程序创建,它们的任务栏按钮也不会被分到一组中。下面我们来做一下这个实验:
1.为应用程序设置AppID。API函数SetCurrentProcessExplicitAppUserModelID 用于设置应用程序的AppID(函数名字稍微有点长)。
2.为窗口设置AppID。窗口缺省的AppID与创建它的应用程序相同。因此,由同一个程序创建的两个窗口,在任务栏中会被归为一组(前提是这两个窗口都具有任务栏按钮)。如果我们为窗口设置不同的AppID,这两个窗口就不会被归为一组。设置窗口的AppID没有直接的API函数。与设置IShellLink 接口的title一样,需要用到IPropertyStore 这个接口。编写下面的函数,用于设置窗口的AppID。
- void SetWndAppID( HWND hWnd, LPCTSTR szAppID )
- {
- IPropertyStore *pPS = NULL;
- HRESULT hr = SHGetPropertyStoreForWindow(hWnd, IID_PPV_ARGS(&pPS));
- if(SUCCEEDED(hr))
- {
- PROPVARIANT pv;
- if(szAppID != NULL)
- {
- hr = InitPropVariantFromString(szAppID, &pv);
- }
- else
- {
- PropVariantInit(&pv);
- }
- if(SUCCEEDED(hr))
- {
- hr = pPS->SetValue(PKEY_AppUserModel_ID, pv);
- if(SUCCEEDED(hr))
- {
- pPS->Commit();
- }
- PropVariantClear(&pv);
- }
- pPS->Release();
- }
- }
该函数接受一个窗口句柄和AppID(字符串)。通过API函数SHGetPropertyStoreForWindow 取得IPropertyStore接口。通过字符串初始化一个PropVariant ,然后将其设置为AppID。
新建一个Windows程序,在其中创建两个窗口,最好都是overlappped窗口,这样的窗口在任务栏中会有按钮。在没有为窗口设置AppID的情况下,两个窗口的任务栏按钮被合并为一组(前提是打开了Windows7的任务栏按钮合并)。
任务栏按钮被合并:
如果我们为窗口设置不同的 AppID:
- LPCTSTR APP_ID[] = {
- TEXT("wilford.TestAppID.ID"),
- TEXT("wilford.TestAppID.ID1"),
- TEXT("wilford.TestAppID.ID2")
- };
- SetWndAppID(g_hWnd1, APP_ID[1]);
- SetWndAppID(g_hWnd2, APP_ID[2]);
任务栏按钮不会被分组:
三、将文件类型与AppID关联
文件关联是通过注册表来实现的,所要设置的项较多,我使用了一些辅助的函数(这些代码来自Microsoft的教程),用来完成这些工作。如何操作注册表不是本篇的课题,因此就不作讲解了,在文末的代码下载中会提供这些辅助代码,下面只说一下需要设置哪些键值。
1.在注册表中注册AppID。通过下面的注册表导出文件的内容,我们可以看到注册一个AppID所需要的注册表结构。
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT/Wilford.JumpList2]
"FriendlyTypeName"="Custom Jump List Document"
[HKEY_CLASSES_ROOT/Wilford.JumpList2/CurVer]
@="Wilford.JumpList2"
[HKEY_CLASSES_ROOT/Wilford.JumpList2/DefaultIcon]
@="c://Users//wilford//Documents//Visual Studio 2008//Projects//Course//JumpList2//Debug//JumpList2.exe,0"
[HKEY_CLASSES_ROOT/Wilford.JumpList2/shell]
@="Open"
[HKEY_CLASSES_ROOT/Wilford.JumpList2/shell/Open]
[HKEY_CLASSES_ROOT/Wilford.JumpList2/shell/Open/Command]
@="c://Users//wilford//Documents//Visual Studio 2008//Projects//Course//JumpList2//Debug//JumpList2.exe %1"
2.将文件类型与AppID关联。这里将.txt类型的文件与我们上面创建的AppID相关联。
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT/.txt/OpenWithProgids]
"Wilford.JumpList2"=hex(0):
在程序中加入注册AppID的代码,当按下“r”键时,注册AppID。当然如果不在程序中做,而是直接导入上面的注册表文件,也是可以的,对应的程序路径需要改一下。
- case 'r':
- if (!FileRegistration::AreFileExtensionsRegistered(APP_ID))
- {
- if (E_ACCESSDENIED == FileRegistration::RegisterFileExtensions(
- APP_ID, szExt, ARRAYSIZE(szExt)))
- {
- MessageBox(g_hWnd, TEXT("Access Denied!"), TEXT("Error"), MB_OK | MB_ICONERROR);
- }
- }
- break;
现在再重新创建JumpList,可以看到应有的效果了。
Windows7程序开发系列就到这里了。以后如果还有新的研究,会发新的教程上来。
原文链接:http://blog.csdn.net/ntwilford/article/details/5648781