Общие замечания

Для управления ярлыками в Windows используется COM-класс ShellLink, имеющий идентификатор CLSID_ShellLink и реализующий два интерфейса: IShellLink, который позволяет получать и назначать свойства ярлыка, и IPersistFile, предназначенный (в данном случае) для чтения и записи файла ярлыка.

Интерфейс IShellLink

Заголовочный файл #include <shlobj.h>
Непосредственный предок интерфейс IUnknown

В действительности, существует две версии этого интерфейса: IShellLinkA и IShellLinkW, последний из которых доступен только под Windows NT/2000/XP; какая из версий будет использоваться зависит от того, в какой конфигурации компилируется приложение — ANSI или Unicode. Ниже перечислены методы данного интерфейса.

HRESULT GetPath(
   LPTSTR pszDest,
   int cchMax,
   WIN32_FIND_DATA* pwfd,
   DWORD fdwFlags
);

Копирует в буфер по адресу pszDest размером cchMax символов полное имя объекта, на который ссылается данный ярлык, и записывает в структуру, адресуемую параметром pwfd, атрибуты этого объекта. В качестве параметра fdwFlags может быть указана комбинация следующих флагов:

SLGP_SHORTPATH
вернуть имя в формате 8.3;
SLGP_UNCPRIORITY
вернуть имя в формате UNC (Universal Naming Convention);
SLGP_RAWPATH
вернуть необработанное имя (оно, согласно MSDN, представляет собой «нечто, что может не существовать и может содержать имена переменных окружения, вместо которых необходимо подставить их значения»).

Метод может вернуть одно из следующих значений:

NOERROR
успешное выполнение, в pszDest скопирован полный путь;
S_FALSE
успешное выполнение, но в pszDest записана пустая строка (это происходит в том случае, когда объекту, на который ссылается ярлык, не соответствует реальный файл на диске — Recycle Bin, Control Panel, Printers);
E_xxx
код ошибки OLE.
HRESULT SetPath(
   LPCTSTR pszSrc
);

Назначает полное имя объекта, на который ссылается данный ярлык, в соответствии с параметром pszSrc. Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

HRESULT GetArguments(
   LPTSTR pszDest,
   int cchMax
);

Копирует в буфер по адресу pszDest размером cchMax символов аргументы, передаваемые объекту при его открытии (эти аргументы задаются в свойствах ярлыка на вкладке Shortcut в поле ввода Target вслед за именем объекта). Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

HRESULT SetArguments(
   LPCTSTR pszSrc
);

Назначает аргументы, передаваемые объекту при его открытии, в соответствии со строкой pszSrc. Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

HRESULT GetWorkingDirectory(
   LPTSTR pszDest,
   int cchMax
);

Копирует в буфер по адресу pszDest размером cchMax символов имя папки, которая должна стать текущей для открытого объекта (это имя задается в свойствах ярлыка на вкладке Shortcut в поле ввода Start in). Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

HRESULT SetWorkingDirectory(
   LPCTSTR pszSrc
);

Назначает имя папки, которая должна стать текущей для открытого объекта, в соответствии со строкой pszSrc. Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

HRESULT GetHotKey(
   WORD* pwDest
);

Копирует в переменную по адресу pwDest «горячую клавишу», используемую для открытия объекта (это сочетание клавиш задается в свойствах ярлыка на вкладке Shortcut в поле Shortcut key). Младший байт скопированного значения содержит виртуальный код основной клавиши (VK_xxx), а старший байт — комбинацию флагов, соответствующих клавишам-модификаторам:

HOTKEYF_CONTROL
клавиша Ctrl;
HOTKEYF_SHIFT
клавиша Shift;
HOTKEYF_ALT
клавиша Alt;
HOTKEYF_EXT
расширенная клавиша.

Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

HRESULT SetHotKey(
   WORD wSrc
);

Назначает «горячую клавишу», используемую для открытия объекта. Младший байт параметра wSrc должен содержать виртуальный код основной клавиши, а старший — комбинацию флагов HOTKEYF_xxx, соответствующих клавишам-модификаторам, возможные значения которых приведены выше. Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

HRESULT GetShowCmd(
   int* pnDest
);

Записывает в переменную по адресу pnDest команду показа главного окна объекта (вариант отображения главного окна выбирается в свойствах ярлыка на вкладке Shortcut из списка Run). Возможно одно из следующих значений:

SW_SHOWNORMAL
соответствует варианту Normal window;
SW_SHOWMINNOACTIVE
соответствует варианту Minimized;
SW_SHOWMAXIMIZED
соответствует варианту Maximized.

Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

HRESULT SetShowCmd(
   int nSrc
);

Назначает команду показа главного окна объекта в соответствии с параметром nSrc, возможные значения которого приведены выше. Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

HRESULT GetDescription(
   LPTSTR pszDest,
   int cchMax
);

Копирует в буфер по адресу pszDest размером cchMax символов описание ярлыка (под Windows 2000/XP оно задается в свойствах ярлыка на вкладке Shortcut в поле ввода Comment и отображается при наведении курсора мышки на ярлык). Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

HRESULT SetDescription(
   LPCTSTR pszSrc
);

Назначает описание ярлыка в соответствии со строкой pszSrc. Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

HRESULT GetIconLocation(
   LPTSTR pszDest,
   int cchMax,
   int* pnIndex
);

Копирует в буфер по адресу pszDest размером cchMax символов полное имя файла, содержащего иконку для данного ярлыка, а в переменную по адресу pnIndex — индекс иконки в этом файле, начиная с 0. Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

HRESULT SetIconLocation(
   LPCTSTR pszSrc,
   int nIndex
);

Назначает для данного ярлыка иконку, содержащуюся в файле с именем pszSrc и имеющую индекс nIndex. Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

HRESULT Resolve(
   HWND hWnd,
   DWORD fdwFlags
);

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

SLR_NOSEARCH
не использовать эвристический поиск;
SLR_UPDATE
если объект, на который ссылается ярлык был изменен, необходимо обновить его полное имя;
SLR_NOUPDATE
не обновлять ярлык в любом случае;
SLR_INVOKE_MSI
запустить Microsoft Windows Installer для восстановления объекта, на который ссылается ярлык, если это необходимо;
SLR_NO_UI
не запрашивать дополнительную информацию, если объект, на который ссылается ярлык, не может быть найден (если этот флаг установлен, старшее слово параметра fdwFlags может содержать максимальный интервал времени, в течение которого будет выполняться поиск; значение 0 соответствует интервалу по умолчанию, составляющему 3000 мс).

Метод возвращает значение NOERROR при успешном выполнении или код ошибки OLE — в противном случае.

Интерфейс IPersistFile

Заголовочный файл #include <objidl.h>
Непосредственный предок интерфейс IPersist

Этот интерфейс предоставляет методы, предназначенные для работы с дисковыми файлами.

HRESULT Load(
   LPCOLESTR pstrFileName,
   DWORD fdwMode
);

Открывает файл с именем pstrFileName и инициализирует COM-объект данными из этого файла; параметр fdwMode определяет режим работы с файлом и может быть комбинацией следующих флагов:

STGM_READ
чтение данных;
STGM_WRITE
запись данных;
STGM_READWRITE
чтение и запись данных;
STGM_SHARE_EXCLUSIVE
блокировать доступ других процессов к файлу;
STGM_SHARE_DENY_READ
блокировать чтение файла другими процессами;
STGM_SHARE_DENY_WRITE
блокировать запись в файл другими процессами;
STGM_SHARE_DENY_NONE
не блокировать другим процессам доступ к файлу.

Метод может возвращать одно из следующих значений:

S_OK
успешное выполнение;
E_OUTOFMEMORY
для выполнения требуемой операции недостаточно памяти;
E_FAIL
произошла ошибка.
HRESULT Save(
   LPCOLESTR pstrFileName,
   BOOL fSetCurrent
);

Записывает данные в файл с именем pstrFileName; если параметр fSetCurrent задан равным TRUE, то для записи данных в тот же самый файл при следующих вызовах метода параметр pstrFileName можно задавать равным NULL (значение параметра fSetCurrent при этом игнорируется). Рассмотрим простой пример:

IPersistFile* ppf;
...
// обработка команды "Сохранить"
ppf->Save(NULL, FALSE);
...
// обработка команды "Сохранить как..."
ppf->Save(OLESTR("filename.ext"), TRUE);

Метод Save возвращает значение S_OK при успешном выполнении или E_FAIL — в случае ошибки.

HRESULT GetCurFile(
   LPOLESTR* ppstrDest,
);

Выделяет с помощью интерфейса IMalloc блок памяти, копирует в него полное имя файла, который COM-объект использует для чтения/записи данных, и записывает адрес выделенного блока в переменную по адресу ppstrDest. Освобождение этого блока памяти должно выполняться приложением-клиентом. Пример использования:

IPersistFile* ppf;
IMalloc* pMalloc;

...   // получили указатель на IPersistFile
LPOLESTR pstrCurFile = NULL;
ppf->GetCurFile(&pstrCurFile);
::MessageBoxW(NULL, pstrCurFile, L"Current File", MB_OK);

// получаем указатель на IMalloc
::SHGetMalloc(&pMalloc);

// освобождаем память
pMalloc->Free(pstrCurFile);

// IMalloc больше не нужен
pMalloc->Release();

Метод GetCurFile возвращает одно из следующих значений:

S_OK
полное имя файла успешно скопировано;
S_FALSE
файл не имеет имени и скопированная строка является шаблоном вида *.расширение;
E_OUTOFMEMORY
для выполнения требуемой операции недостаточно памяти;
E_FAIL
произошла ошибка.
HRESULT IsDirty(void);

Метод возвращает значение S_OK, если данные были изменены с момента последнего сохранения файла, или S_FALSE — в противном случае.

Чтение/изменение существующего ярлыка

Для чтения (и возможно — изменения) свойств уже существующего ярлыка необходимо выполнить следующую последовательность действий:

  1. Инициализировать, если это еще не было сделано, COM-библиотеку вызовом функции CoInitialize (или CoInitializeEx).
  2. Создать с помощью функции CoCreateInstance экземпляр COM-класса ShellLink и получить указатель на его интерфейс IPersistFile.
  3. Загрузить вызовом метода Load требуемый ярлык, указав желаемый режим доступа к его свойствам — чтение (STGM_READ), запись (STGM_WRITE) или чтение/запись (STGM_READWRITE).
  4. Получить указатель на интерфейс IShellLink «нашего» COM-объекта, вызвав метод QueryInterface через полученный ранее указатель на IPersistFile.
  5. Если существует вероятность того, что объект, на который ссылается ярлык, был перемещен — вызвать через полученный указатель метод Resolve для поиска объекта.
  6. Получить и (или) назначить требуемые свойства ярлыка, вызвав через указатель на IShellLink его методы GetXxxx и (или) SetXxxx соответственно.
  7. При необходимости — сохранить сделанные изменения с помощью метода Save интерфейса IPersistFile.
  8. «Отпустить» полученные интерфейсы, вызвав для каждого из них метод Release.
  9. Завершить, если это необходимо, работу с COM-библиотекой вызовом функции CoUninitialize (перед этим можно вызвать функцию CoFreeUnusedLibraries для выгрузки из памяти всех неиспользуемых более COM-серверов).

В качестве примера перечисленных действий ниже приведен текст функции, которая принимает в качестве параметра имя ярлыка и выводит на консоль полное имя файла, на который этот ярлык ссылается.

void print_shortcut_target(LPCTSTR pszShortcut)
{
   IPersistFile* ppf;
   IShellLink* pshl;
   WIN32_FIND_DATA wfd;

   // инициализируем COM-библиотеку
   ::CoInitialize(NULL);

   // создаем COM-объект и получаем указатель на IPersistFile
   ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
      IID_IPersistFile, (void**)&ppf);

   // открываем ярлык
#if defined(_UNICODE)
   ppf->Load(pszShortcut, STGM_READ);
#else
   LPWSTR pwszTemp = new WCHAR[_MAX_PATH];
   mbstowcs(pwszTemp, pszShortcut, _MAX_PATH);
   ppf->Load(pwszTemp, STGM_READ);
   delete[] pwszTemp;
#endif

   // получаем указатель на IShellLink
   ppf->QueryInterface(IID_IShellLink, (void**)&pshl);

   // ищем объект, на который ссылается ярлык
   pshl->Resolve(NULL, SLR_ANY_MATCH | SLR_NO_UI);

   // получаем имя объекта и выводим его на консоль
   LPTSTR pszTarget = new TCHAR[_MAX_PATH];
   pshl->GetPath(pszTarget, _MAX_PATH, &wfd, 0);
   _putts(pszTarget);
   delete[] pszTarget;

   // убираем за собой
   pshl->Release();
   ppf->Release();

   // завершаем работу с COM-библиотекой
   ::CoFreeUnusedLibraries();
   ::CoUninitialize();
}

Создание нового ярлыка

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

  1. Инициализировать, если это еще не было сделано, COM-библиотеку вызовом функции CoInitialize (или CoInitializeEx).
  2. Создать с помощью функции CoCreateInstance экземпляр COM-класса ShellLink и получить указатель на его интерфейс IShellLink.
  3. Назначить вызовами соответствующих методов SetXxxx полученного интерфейса желаемые свойства ярлыка.
  4. Получить указатель на интерфейс IPersistFile «нашего» COM-объекта, вызвав метод QueryInterface через полученный ранее указатель на IShellLink.
  5. Сохранить созданный ярлык в файл с заданным именем вызовом метода Save.
  6. «Отпустить» полученные интерфейсы, вызвав для каждого из них метод Release.
  7. Завершить, если это необходимо, работу с COM-библиотекой вызовом функции CoUninitialize (перед этим можно вызвать функцию CoFreeUnusedLibraries для выгрузки из памяти всех неиспользуемых более COM-серверов).

Возможный вариант реализации описанной последовательности действий содержится в методе OnButtonCreate класса CMainDialog из демонстрационного проекта к данной теме.

обновлено
29.03.2006
 
Проверка PR и ТИЦ