我有一种情况,我想对某些Windows shell特殊文件夹(对应于CSIDL枚举中的值)执行特殊处理。(我的解决方案必须与WinXP兼容。)我遇到的问题是当我遇到IShellFolders,当我沿着heirarchy工作时,我找不到将IShellFolders与CSIDL相匹配的方法。
这是我目前的做法:
将所有CSIDL的静态一对一数据结构(csidlToFromFullPIdl
)初始化为SHGetSpecialFolderLocation
返回的pIDL。
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启动heirarchy:
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都不对应csidlToFromFullPIdl
中的任何pIDL。
TIA。
在Vista计算机上,GUID corresponding to KNOWNFOLDERIDs可能是一个解决方案(但不适合我,因为我必须兼容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的调用返回一个对应于抛出NotImplementedException的hResult。这是否意味着SHGetDataFromIDList在我的系统上不可用? (WinXP SP3。)
我的第二种方法是将两个指针引用的项目标识符列表与项目标识符列表进行比较,看看它们是否相等。我正在C中实现一种编码here的技术。这是我到目前为止所做的:
答案 0 :(得分:2)
Per Raymond Chen:ITEMIDLISTs可以是等效的,而不是逐字节相同的。使用IShellFolder::CompareIDs来测试等效性。
答案 1 :(得分:0)
static bool pIdlsAreEquivalent(IntPtr pIdl1, IntPtr pIdl2)
{
if (pIdl1 == pIdl2) return true;
if (pIdl1 == IntPtr.Zero || pIdl2 == IntPtr.Zero) return false;
int pIdl1Size = GetItemIDSize(pIdl1);
if (pIdl1Size != GetItemIDSize(pIdl2)) return false;
byte[] byteArray1 = new byte[pIdl1Size];
byte[] byteArray2 = new byte[pIdl1Size];
Marshal.Copy(pIdl1, byteArray1, 0, pIdl1Size);
Marshal.Copy(pIdl2, byteArray2, 0, pIdl1Size);
for (int i = 0; i < pIdl1Size; i++)
{
if (byteArray1[i] != byteArray2[i])
return false;
}
return true;
}
static int GetItemIdSize(IntPtr pIdl)
{
if (pIdl == IntPtr.Zero) return 0;
int size = 0;
// Next line throws AccessViolationException
ITEMIDLIST idl = (ITEMIDLIST)Marshal.PtrToStructure(pIdl, typeof(ITEMIDLIST));
while (idl.mkid.cb > 0)
{
size += idl.mkid.cb;
pIdl = (IntPtr)((int)pIdl + idl.mkid.cb);
idl = (ITEMIDLIST)Marshal.PtrToStructure(pIdl, typeof(ITEMIDLIST));
}
return size;
}
public struct ITEMIDLIST
{
public SHITEMID mkid;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SHITEMID
{
public ushort cb; // The size of identifier, in bytes, including cb itself.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] abID; // A variable-length item identifier.
}