您的位置:首页 > 其它

Windows 7程序开发系列之二(JumpList篇2 - Destination)

2015-06-03 12:22 525 查看
JumpList中除了有User Task外,还有Destination。与User Task不同,Destination中是与该程序相关联的文件的链接。Destination还可以分类,Windows已经自动为我们管理了“最近“和“常用“两个类别。比如记事本程序,使用“最近”这个类别:



这对于大多数程序已经足够。但Windows也为我们提供了管理自己的类别的接口。程序可以根据自己的需要,添加自己的类别。本节将介绍如何将自己的类别加入JumpList。


一、向 JumpList中加入自定义类别

首先来定义一些准备要放入JumpList的文件:

[cpp] view
plaincopy

LPCTSTR szFiles[] = {

TEXT("TestFile1.txt"),

TEXT("TestFile2.txt"),

TEXT("TestFile3.txt")

};

响应按键消息,当按下“j”的时候,创建JumpList。

[cpp] view
plaincopy

case WM_CHAR:

switch(wParam)

{

case 'j':

CreateJumpList();

break;

}

break;

[cpp] view
plaincopy

//创建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。

[cpp] view
plaincopy

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:

[cpp] view
plaincopy

LPCTSTR APP_ID[] = {

TEXT("wilford.TestAppID.ID"),

TEXT("wilford.TestAppID.ID1"),

TEXT("wilford.TestAppID.ID2")

};

[cpp] view
plaincopy

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。当然如果不在程序中做,而是直接导入上面的注册表文件,也是可以的,对应的程序路径需要改一下。

[cpp] view
plaincopy

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程序开发系列就到这里了。以后如果还有新的研究,会发新的教程上来。

本节源代码

Application ID演示代码

原文链接:http://blog.csdn.net/ntwilford/article/details/5648781
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: