Ярлыки являются сериализуемыми объектами, это означает, что они могут быть сохранены не только в файле
Известно, что ярлык бо́льшую часть своего времени хранится в файле .lnk, который люди зачастую считают синонимом ярлыка, но он находится там не все время. Вы можете поместить ярлык в любое место, куда только можно поместить набор байт. Вот программа, которая создает ярлык для файла, путь к которому передан в аргументе командной строки (убедитесь, что передан полный путь к файлу), и сериализует его в набор байт (в виде объекта типа HGLOBAL). После этого она восстанавливает ярлык обратно из полученного набора байт и выводит информацию о нем.
#define UNICODE #define _UNICODE #include <windows.h> #include <shlobj.h> #include <ole2.h> #include <stdio.h> #include <tchar.h> #include <atlbase.h> HGLOBAL CreateShellLinkInMemory(PCWSTR pszFile) { BOOL fSuccess = FALSE; HGLOBAL hglob = GlobalAlloc(GMEM_MOVEABLE, 0); if (hglob) { CComPtr<IStream> spstm; if (SUCCEEDED(CreateStreamOnHGlobal(hglob, FALSE, &spstm))) { CComPtr<IShellLink> spsl; if (SUCCEEDED(spsl.CoCreateInstance(CLSID_ShellLink))) { if (SUCCEEDED(spsl->SetPath(pszFile))) { CComQIPtr<IPersistStream> spps(spsl); fSuccess = spps && SUCCEEDED(spps->Save(spstm, TRUE)); } } } } if (fSuccess) return hglob; if (hglob) GlobalFree(hglob); return NULL; }
После создания объекта ярлыка мы сериализуем его в поток, связанный с участком памяти, ссылка на который хранится в переменной типа HGLOBAL. Объект ярлыка больше ниоткуда не виден. Он был подвергнут процессу дегидратации и превращен в горстку праха, как в той серии старого сериала «Звездный путь».
Но в этот раз мы знаем, как вернуть его обратно.
IShellLink *CreateShellLinkFromMemory(HGLOBAL hglob) { IShellLink *pslReturn = NULL; CComPtr<IStream> spstm; if (SUCCEEDED(CreateStreamOnHGlobal(hglob, FALSE, &spstm))) { CComPtr<IShellLink> spsl; if (SUCCEEDED(spsl.CoCreateInstance(CLSID_ShellLink))) { CComQIPtr<IPersistStream> spps(spsl); if (spps && SUCCEEDED(spps->Load(spstm))) { pslReturn = spsl.Detach(); } } } return pslReturn; }
Мы создаем новый объект ярлыка и говорим ему восстановить себя из припасенного нами участка памяти. Бам, ярлык вернулся обратно и готов к использованию.
int __cdecl wmain(int argc, WCHAR **argv) { if (SUCCEEDED(CoInitialize(NULL))) { HGLOBAL hglob = CreateShellLinkInMemory(argv[1]); if (hglob) { CComPtr<IShellLink> spsl; spsl.Attach(CreateShellLinkFromMemory(hglob)); if (spsl) { WCHAR szTarget[MAX_PATH]; if (spsl->GetPath(szTarget, MAX_PATH, NULL, 0) == S_OK) { wprintf(L"Welcome back, shortcut to %s\n", szTarget); } } GlobalFree(hglob); } CoUninitialize(); } return 0; }
Поскольку ярлыки могут быть сохранены где угодно, вы не можете использовать имя файла для разделения ссылок на файлы и ссылок на папки, потому что имени файла может и не быть вовсе! (Какое имя файла у нашего объекта типа HGLOBAL?) Даже если вы решите применять это соглашение только для ярлыков, сохраненных в файлах, вы взваливаете дополнительную ношу на людей, производящих какие-либо действия над этими файлами: им нужно проверять, является ли целевой объект, на который будет указывать ярлык, файлом или папкой, перед выбором имени файла, и если целевой объект ярлыка изменится, возможно, также придется изменить и имя файла ярлыка. Это довольно серьезная проблема для стандартного окна свойств файла: если вы измените целевой объект ярлыка на вкладке «Ярлык», это может привести к изменению имени файла ярлыка. Если вы также вносили какие-либо изменения на вкладке «Безопасность», она попытается обновить атрибуты безопасности для файла со старым именем, даже если на вкладке «Ярлык» оно уже было изменено. Ой, ни одна из остальных вкладок окна свойств не будет работать, потому что теперь они работают с файлом, которого больше не существует!
Упражнение: при каких условиях может быть полезным сохранение ярлыка в памяти вместо файла? (Ответ)