代码之家  ›  专栏  ›  技术社区  ›  Carl G

通过其pidl识别Windows Shell特殊文件夹(即获取其csidl)(现在确定pidl是否等于c)

  •  6
  • Carl G  · 技术社区  · 14 年前

    我想在某些Windows Shell特殊文件夹(与csidl枚举中的值对应的文件夹)上执行特殊处理(我的解决方案必须与winxp兼容)。我遇到的问题是,当我在继承关系中遇到i shell folders时,我找不到将i shell folders与它们匹配的方法。R.

    这是我目前的方法:

    初始化静态一对一数据结构( 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);
    }
    

    使用桌面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);
    

    (最后,使用以下方法检索子文件夹:)

    hResultUint = parentShellItem.ShellFolder.BindToObject(childRelativePIdl, IntPtr.Zero, ShellApi.IID_IShellFolder, out _shellFolder);
    

    我遇到的问题是,无论是childrelativepidl还是fullpidl都不对应于 csidltofromfullpidl .

    短暂性脑缺血发作

    在Vista机器上, GUID corresponding to KNOWNFOLDERIDs 可能是一个解决方案(但不适合我,因为我必须与WinXP兼容。)

    我还应该说我认为使用特殊文件夹的路径(通过 SHGetSpecialFolderPath )是不够的,因为我感兴趣的几个特殊文件夹没有路径。(例如,CSIDL_驱动器和CSIDL_网络。)


    我正在尝试两种方法。第一种方法是使用shgetdatafromidlist来检索clsid,然后将其与已知的clsid进行比较:

    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在我的系统上不可用?(Win XP SP3)

    我的第二种方法是将两个指针引用的项标识符列表与项标识符列表进行比较,看看它们是否相等。我正在实现一种编码技术 here 在C中,这就是我目前为止所拥有的:

    2 回复  |  直到 10 年前
        1
  •  2
  •   Community CDub    7 年前

    Raymond Chen :itemIDlists可以是等效的,而不是字节对字节相同。使用 IShellFolder::CompareIDs 测试等效性。

        2
  •  0
  •   Carl G    13 年前
    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.
    }