Я заметил, что метод IShellLinkW:: GetPath возвращает пустую строку, если цель ярлыка указывает на специальную папку, например «Мой компьютер», или апплет панели управления.
Образец кода
' CLSID_ShellLink from ShlGuid.h
Dim cShellLink As New CShellLink()
' https://msdn.microsoft.com/en-us/library/windows/desktop/ms687223%28v=vs.85%29.aspx
Dim persistFile As IPersistFile = DirectCast(cShellLink, IPersistFile)
persistFile.Load(lnkFilePath, 0)
Dim target As New StringBuilder(260)
' https://docs.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nn-shobjidl_core-ishelllinkw
Dim lnkW As IShellLinkW = DirectCast(cShellLink, IShellLinkW)
lnkW.GetPath(target, target.Capacity, Nothing, IShellLinkGetPathFlags.RawPath)
(Извините, здесь слишком много P/Invoke, но вы можете найти их в репозитории это.)
Приведенный выше код правильно работает для получения цели любого файла .lnk, указывающего на файл или каталог. Он не может получить цель, указывающую на специальный элемент/виртуальные папки.
Мои вопросы:
Это по замыслу?. Если нет, то что я упускаю или делаю неправильно? (может быть, мои определения неверны?). И что мне нужно сделать, чтобы получить специальную цель? Обратите внимание, что я хотел бы получить цель, чтобы отобразить ее в PropertyGrid, а также создать новую цель с клоном специальной цели.
Обратите внимание, что язык программирования, на котором я разрабатывал этот код, не имеет значения, потому что я в первую очередь прошу для ориентации, а также принимаю любое решение, написанное на C#.
См. SHGetPathFromIDList, SHGetFileInfo (обратите внимание, что он принимает LPCWSTR, но также и pidl), SHGetSpecialFolderLocation. Интерфейс IShellLinkW также предоставляет метод .Resolve(), который может пригодиться. Используйте его на том же объекте оболочки, затем получите адрес out, возвращаемый GetIDList()
Спасибо за комментарии, но если я вызову IShellLinkW.GetIDList, затем SHGetPathFromIDListW с полученным (действительным, ненулевым) PIDL, я всегда получаю пустую строку, и функция возвращает ложный, но код последней ошибки win32 равен нулю (Сетластеррор установлен в истинный ). Почему?. Пробовал разные подписи, в том числе и с сайта pinvoke.net.
Кроме того, я не понимаю, в чем смысл использования SHGetFileInfo, потому что, похоже, я смог получить только имя файла (SHGFI_DISPLAYNAME) и описание типа файла (SHGFI_TYPENAME). Кстати, эта функция принимает PIDL, который я получаю с помощью IShellLinkW.GetIDList, но, как я уже упоминал, кажется, что я не могу получить «сырую» целевую строку lnk с помощью этой функции... только имя файла или описание типа файла.
При анализе этих lnk-файлов в шестнадцатеричном редакторе у них есть CLSID для цели (например, ::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}, который указывает на Мой компьютер), и если я использую этот CLSID при создании ярлыка, вызывающего SetPath (без возни с SetIDList и чем-то тривиальным), это работает как положено. Это обнаружение упрощает решение проблемы, потому что я думаю, что могу получить CLSID из PIDL. PIDL — это концепция, которую я вообще мало читал, так что извините, если говорю глупости. Но я все равно буду исследовать доступные функции win32, чтобы получить значение CLSID...
После того, как я проанализировал назначение каждой задокументированной функции SH*, я до сих пор не знаю, как это сделать. Кроме того, я не уверен, является ли PIDL, возвращаемый IShellLinkW.GetIDList, «абсолютным» или «относительным» PIDL, что меня смущает. больше, и я не вижу способа определить, какой из трех типов PIDL это, кроме вызова SHGetRealIDL и забыть. Кроме того, я попробовал функцию SHGetPathFromIDListEx, но у меня точно такие же проблемы, как и при вызове SHGetPathFromIDList. Черт, мне просто нужно получить CLSID, записанный в файле lnk... Я не понимаю, как получить этот CLSID из PIDL.





Как упоминалось в поле комментариев к основному сообщению, чтобы получить необработанную цель ссылки оболочки, которая указывает на специальный путь, не относящийся к файловой системе (например, «Мой компьютер»), сначала нам нужно получить PIDL с помощью звоню IShellLinkW::GetIDList().
Затем мы передаем этот (абсолютный) PIDL в SHGetNameFromIDList с флагом SIGDN_DESKTOPABSOLUTEPARSING, и, наконец, это вернет строку CLSID, которую мы можем указать для цели файла .lnk с помощью IShellLinkW::SetPath(). Он работает так, как ожидалось.
Обратите внимание, что я не уверен, могут ли существовать особые сценарии (редкие цели файла lnk), в которых это не может работать должным образом. Я не нашел этот случай ошибки.
А при необходимости нормальное отображаемое имя можно получить с помощью флага SIGDN_NORMALDISPLAY, а также с помощью вызова функции SHGetFileInfo, как указано в поле для комментариев к основному сообщению.
Кредиты для пользователей, которые предложили мне поиск PIDL и его использование, потому что я не был уверен в том, что исследовать, чтобы решить эту проблему.
Я также отдаю должное библиотеке с открытым исходным кодом Ванара, которая помогла мне найти правильные P/Invokes каждой функции SH* в их исходном коде, особенно подпись для SHGetNameFromIDList.
Множество способов получить DisplayName из pidl, некоторые из которых доступны через интерфейс ShellItem (например, GetDisplayName()), для связанных элементов — относительные или абсолютные. То, что вы используете, связано с контекстом и типом пути. Одно замечание по поводу маршаллера: вы часто видите в примере кода использование Marshal.AllocHGlobal. Вы должны использовать Marshal.AllocCoTaskMem (собственный CoTaskMemAlloc) для освобождения, как описано.
Если вы видите, что кто-то использует SHGetMalloc для инициализации интерфейса IMalloc, это старый код: этот метод заменен CoTaskMemAlloc.
Спасибо за комментарий и за советы!. Возможно, следующее не по теме, но я хотел бы сказать, что для меня WindowsAPICodePack кажется намного лучшим (последовательным) исходным кодом, чтобы взять в качестве примера (и скопировать) любой интерфейс, связанный с IShellItem/IShellFolder/IShellView и т. д. В любом случае, Vanara имеет обширную коллекцию P/Invokes, и в некоторых случаях это действительно полезно.
Пути работают только для объектов файловой системы. Вам нужно ознакомиться с PIDL, IShellLinkW::GetIDList() возвращает их. Не рекомендуется.