Разработка для панели задач Windows 7 – переходим к спискам переходов – часть 3

К этому моменту вы, как я надеюсь, поняли, как создать собственный список переходов для вашего приложения. Вы также должны были оценить стандартную поддержку Windows 7 для категорий «Recent» и «Frequent» и понять, как создавать свои собственные категории. В этой статье мы рассмотрим другие возможности списков переходов и покажем, насколько просто добавлять новые задачи в список приложения.

Задачи пользователя – это настраиваемые задачи, помещаемые в собственную категорию Tasks. Как разработчик вы можете установить заголовок отображаемой задачи, иконку слева и, что более важно, приложение, которое запускается, когда данная задача активируется. Вы можете рассматривать задачи пользователя как ярлыки функций, обеспечиваемых приложением. Как вы помните, задачи пользователя в нашем словаре выражаются глаголами; например, в списке Windows Media Player есть задача «Resume last playlist», а в Sticky Notes – задача «New note».

Задача пользователя обычно представляет собой объект IShellLink, который запускает любое указанное приложение (ваше приложение или любое другое, которое вы выберите) с соответствующим параметром командной строки. Хотя вы не можете распределять задачи по категориям, вы можете разделить их, используя специальный разделительный объект. Ниже приведен пример списка с разделителем, который разбивает три задачи на группу из двух задач и одной дополнительной:

clip_image002

Так что же требуется для добавления задач в список переходов? На самом деле, не так уж много. По существу, это один единственный вызов функции AddUserTasks в интерфейсе, с которым мы уже знакомы, ICustomDestinationList (ICustomDestinationList::AddUserTasks(IObjectArray) Method). Рассматривая код, вы увидите одну единственную строку, hr = pcdl->AddUserTasks(poa); . Однако как всегда, кто-то должен создать переменную poa, IObjectArray, параметр и заполнить их соответствующий информацией. Давайте рассмотрим этот процесс.

Мы создадим коллекцию IShellLinks. Эта коллекция будет позже приведена к требуемому параметру IObjectArray. Нижеприведенный код начинает этот процесс.

 IObjectCollection *poc;
HRESULT hr = CoCreateInstance(
D_EnumerableObjectCollection, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&poc));
if (SUCCEEDED(hr))
{
    IShellLink * psl;
    hr = _CreateShellLink(L"/Task1", L"Task 1", &psl);
    if (SUCCEEDED(hr))
    {
        hr = poc->AddObject(psl);
        psl->Release();
    }
}

Здесь можно видеть, что мы использовали COM (вновь) и CoCreate и IObjectCollection, poc. Далее вызывается вспомогательная функция CreateShellLink с тремя параметрами:

  • Первый параметр – это аргумент командной строки для задачи
  • Второй параметр – это заголовок, который будет отображен
  • Третий параметр – это указатель на IShellLink.

Затем объект заполняется соответствующей информацией.

Наконец, мы добавляем недавно созданный IShellLink в коллекцию объекта. Вы можете задаться вопросом, где же параметр, который указывает путь к исполняемому файлу, который мы планируем вызвать. Что же, это хороший вопрос. Для простоты, мы жестко задаем эту информацию, как показано в следующем фрагменте кода:

 if (SUCCEEDED(hr))
{
    hr = _CreateShellLink2(
            L"C:\\Users\\<my user>\\Documents\\new text file.txt", 
            L"NotePad", 
            &psl);

    if (SUCCEEDED(hr))
    {
        hr = poc->AddObject(psl);
        psl->Release();
    }
}

Здесь вы можете видеть, что мы вызывает жестко заданную функцию _CreateShellLink2. Ей присваиваем в качестве одного из параметров путь к текстовому файлу и запускаем Notepad.

Вот код с вызовом функции CreateShellLink2:

 HRESULT _CreateShellLink2(
            PCWSTR pszArguments, PCWSTR pszTitle, 
            IShellLink **ppsl)
{
    IShellLink *psl;
    HRESULT hr = CoCreateInstance(
                    CLSID_ShellLink, 
                    NULL, 
                    CLSCTX_INPROC_SERVER, 
                    IID_PPV_ARGS(&psl));
    if (SUCCEEDED(hr))
    {
        hr = psl->SetPath(c_szNotePadExecPath);
            if (SUCCEEDED(hr))
            {
                hr = psl->SetArguments(pszArguments);
                if (SUCCEEDED(hr))
                {
                    // Свойство заголовка требуется элементам списка,
                    // представленным как экземпляр IShellLink. Это значение используется
                    // как отображаемое имя в списке переходов.
                    IPropertyStore *pps;
                    hr = psl->QueryInterface(IID_PPV_ARGS(&pps));
                    if (SUCCEEDED(hr))
                    {
                        PROPVARIANT propvar;
                        hr = InitPropVariantFromString(pszTitle, &propvar);
                        if (SUCCEEDED(hr))
                        {
                            hr = pps->SetValue(PKEY_Title, propvar);
                            if (SUCCEEDED(hr))
                            {
                                hr = pps->Commit();
                                if (SUCCEEDED(hr))
                                {
                                    hr = psl->QueryInterface
                                            (IID_PPV_ARGS(ppsl));
                                }
                            }
                            PropVariantClear(&propvar);
                        }
                        pps->Release();
                    }
                }
            }
        else
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
        }
        psl->Release();
    }
    return hr;
}

Сначала нам вновь следует использовать COM и CoCreate для создания COM-объекта IShellLink. Даже беглый просмотр SDK обнаруживает, что у объекта IShellLink есть множество функций. Вот несколько из них, которые мы будем использовать:

  • GetPath – получает путь и имя файла объекта, которые являются путем к исполняемому файлу
  • GetShowCmd – получает команду объекта Shell link, имя исполняемого файла
  • SetArguments – устанавливает аргументы командной строки для объекта Shell link
  • SetDescription – устанавливает описание для объекта Shell link; описание может быть любой определенной приложением строкой
  • SetIconLocation – устанавливает размещение (путь и индекс) иконки объекта Shell link
  • SetPath – устанавливает путь и имя файла для объекта Shell link
  • SetWorkingDirectory – задает имя рабочей папки для объекта Shell link

Как видите, для каждого параметра мы должны получить и задать соответствующие методы. Есть и дополнительные параметры; просмотрите раздел IShellLink в SDK, если хотите узнать больше.

В примере выше мы установили путь к Notepad (при установке Windows 7 по умолчанию, c:\windows\notepad.exe). Мы также жестко задали (не самая лучшая привычка) аргумент командной строки, указывающий на текстовой файл в моей папке документов (C:\Users\<my user>\Documents\new text file.txt). Оставшаяся часть кода задает свойство заголовка, требующееся для элементов списка.

Мы вызываем CreateShellLink2 и CreateShellLink еще несколько раз, чтобы добавить все три ярлыка, как показано на скриншоте выше.

Теперь давайте добавим разделитель.

Для добавления разделителя в наш TaskList, нам необходимо создать IShellLink и установить свойство PKEY_AppUserModel_IsDestListSeparator, используя свойство COM, как показано во фрагменте кода ниже:

 // Категория Tasks в списке переходов поддерживает разделительные элементы. 
// Это простые экземпляры IShellLink, у которых
// Свойство PKEY_AppUserModel_IsDestListSeparator установлено в TRUE.
// Все другие значения игнорируются.
HRESULT _CreateSeparatorLink(IShellLink **ppsl)
{
    IPropertyStore *pps;
    HRESULT hr = CoCreateInstance(
                    CLSID_ShellLink, 
                    NULL, 
                    CLSCTX_INPROC_SERVER, 
                    IID_PPV_ARGS(&pps));
    if (SUCCEEDED(hr))
    {
        PROPVARIANT propvar;
        hr = InitPropVariantFromBoolean(TRUE, &propvar);
        if (SUCCEEDED(hr))
        {
            hr = pps->SetValue(PKEY_AppUserModel_IsDestListSeparator, propvar);
            if (SUCCEEDED(hr))
            {
                hr = pps->Commit();
                if (SUCCEEDED(hr))
                {
                    hr = pps->QueryInterface(IID_PPV_ARGS(ppsl));
                }
            }
            PropVariantClear(&propvar);
        }
        pps->Release();
    }
    return hr;
}

Здесь вы можете видеть, что мы использовали CoCreate для создания объекта IShellLink. Далее мы присвоили переменной PROPVARIANT propvar значение TRUE и установили свойство PKEY_AppUserModel_IsDestListSeparator объекта IShellLink в TRUE. Это заставит ОС показать IShellLink как разделитель, а не как обычный IShellLink.

Что ж, это было не так уж сложно. Тем не менее, давайте взглянем на укороченную версию, используя .NET. Для этого мы воспользуемся Windows API Code pack for the .NET Framework.

Как и следует ожидать от .NET, мы получаем абстракцию большей части требуемого COM-кода. Пространство имен Microsoft.WindowsAPICodePack.Shell.Taskbar включает объект JumpListLink, который расширяет объект ShellLink и встраивает IJumpListTasks.

Класс JumpList содержит UserTasks-коллекцию IJumpListTasks, к которой вы можете просто добавить новые объекты JumpListLink, как показано в следующем фрагменте кода:

 // Путь к системному каталогу Windows
string systemFolder = 
        Environment.GetFolderPath(Environment.SpecialFolder.System);

jumpList.UserTasks.Add(new JumpListLink
{
    Title = "Open Notepad",
    Path = Path.Combine(systemFolder, "notepad.exe"),
    IconReference = new IconReference(
(systemFolder, "notepad.exe"), 0)
});

Используя синтаксис C# 3.0, мы инициализируем новый объект JumpListLink и добавляем его в коллекцию UserTasks. Как вы видите, управляемый код JumpListLink имеет свойства очень схожие с неуправляемым кодом (что очень разумно). Мы также добавили иконку к ярлыку Блокнота в коде выше, но не указали никаких параметров командной строки.

Вы хотите добавить разделитель? Что же, это также очень просто: просто добавьте объект JumpListSeparator к коллекции UserTasks.

 jumpList.UserTasks.Add(new JumpListSeparator());

Заметьте, пожалуйста, что, как всегда, работая с Windows Code pack API Taskbar, необходимо вызвать функцию «refresh», чтобы подтвердить изменения, как объяснялось в предыдущей статье.

 Taskbar.JumpList.RefreshTaskbarList();

После обновления список выглядит следующим образом:

clip_image004

Я скомпилировал пример в неуправляемом коде из Windows 7 SDK. Вы можете получить копию этого кода здесь. Вы можете загрузить Windows API Code Pack, включающий примеры управляемого кода, который мы использовали в этой статье.

Это завершает наш разговор о списках переходов. Наша следующая статья о панели задач будет посвящена Icon Overlay.