У меня есть ситуация, когда я хочу выполнить специальную обработку некоторых специальных папок оболочки Windows (соответствующих значениям в перечислении CSIDL.) (Мое решение должно быть совместимо с WinXP.) У меня проблема в том, что когда ясталкиваясь с IShellFolders, пока я прохожу путь вниз по иерархии, я не могу найти способ сопоставить IShellFolders с их CSIDL.
Это мой текущий подход:
Инициализация статического однозначногоодна структура данных (csidlToFromFullPIdl
) из всех CSIDL в их pIDL, возвращаемая SHGetSpecialFolderLocation
.
foreach (CSIDL csidl in Enum.GetValues(typeof(CSIDL))
{
IntPtr fullPIdl = IntPtr.Zero;
int hResult = ShellApi.SHGetSpecialFolderLocation(IntPtr.Zero, csidl, ref fullPIdl);
if (hResult != 0)
Marshal.ThrowExceptionForHR(hResult);
csidlToFromFullPIdl.Add(csidl, fullPIdl);
}
Начать иерархию с Desktop IShellFolder:
int hResult = ShellApi.SHGetDesktopFolder(ref _shellFolder);
hResult = ShellApi.SHGetSpecialFolderLocation(IntPtr.Zero, CSIDL.CSIDL_DESKTOP, ref _fullPIdl);
Получить дочерние элементы следующим образом:
hResult = _shellFolder.EnumObjects(IntPtr.Zero, SHCONTF.SHCONTF_FOLDERS, out pEnum);
// Then repeatedly call:
pEnum.Next(1, out childRelativePIdl, out numberGotten);
Создайте новые полностью квалифицированные pIDL для детей следующим образом:
_fullPIdl = ShellApi.ILCombine(parentFullPIdl, childRelativePIdl);
(Наконец, получите IShellFolder для ребенка, используя:)
hResultUint = parentShellItem.ShellFolder.BindToObject(childRelativePIdl, IntPtr.Zero, ShellApi.IID_IShellFolder, out _shellFolder);
Проблема, с которой я сталкиваюсь, заключается в том, что ни childRelativePIdl, ни _fullPIdl не соответствуют никаким pIDL в csidlToFromFullPIdl
.
TIA.
FYI на компьютерах Vista, GUID correspoОбнаружение KNOWNFOLDERID может быть решением (но не для меня, поскольку я должен быть совместимым с WinXP.)
Я также должен сказать, что я считаю, что использование путей к специальным папкам (через SHGetSpecialFolderPath
) - этонедостаточно, потому что некоторые из специальных папок, в которых я заинтересован, не имеют путей.(Например, CSIDL_DRIVES и CSIDL_NETWORK.)
Я пробую два подхода к этому.Во-первых, использовать SHGetDataFromIDList для получения Clsid, который затем можно сравнить с известными Clsids:
public static Guid GetClsidFromFullPIdl(IntPtr fullPIdl)
{
// Get both parent's IShellFolder and pIDL relative to parent from full pIDL
IntPtr pParentShellFolder;
IntPtr relativePIdl = IntPtr.Zero;
int hResultInt = ShellApi.SHBindToParent(fullPIdl, ShellGuids.IID_IShellFolder, out pParentShellFolder, ref relativePIdl);
if (hResultInt != (int)HRESULT.S_OK)
Marshal.ThrowExceptionForHR(hResultInt);
object parentShellFolderObj = System.Runtime.InteropServices.Marshal.GetTypedObjectForIUnknown(
pParentShellFolder, typeof(IShellFolder));
IShellFolder parentShellFolder = (IShellFolder)parentShellFolderObj;
SHDESCRIPTIONID descriptionId = new SHDESCRIPTIONID();
IntPtr pDescriptionId = MarshalToPointer(descriptionId);
// Next line returns hResult corresponding to NotImplementedException
hResultInt = ShellApi.SHGetDataFromIDList(parentShellFolder, ref relativePIdl, SHGDFIL.SHGDFIL_DESCRIPTIONID, pDescriptionId,
Marshal.SizeOf(typeof(SHDESCRIPTIONID)));
if (hResultInt != (int)HRESULT.S_OK)
Marshal.ThrowExceptionForHR(hResultInt);
if (parentShellFolder != null)
Marshal.ReleaseComObject(parentShellFolder);
return descriptionId.Clsid;
}
static IntPtr MarshalToPointer(object data)
{
IntPtr pData = Marshal.AllocHGlobal(Marshal.SizeOf(data));
Marshal.StructureToPtr(data, pData, false);
return pData;
}
Проблема этого подхода заключается в том, что вызов SHGetDataFromIDList возвращает hResult, который соответствует выбрасыванию NotImplementedException.Означает ли это, что SHGetDataFromIDList недоступен в моей системе?(WinXP SP3.)
Мой второй подход - сравнить списки идентификаторов элементов, на которые ссылаются два указателя, со списками идентификаторов элементов и посмотреть, равны ли они.Я реализую технику, закодированную здесь в C. Это то, что я до сих пор: