坚持一个pidl(ITEMIDLIST)

P D*_*ddy 6 winapi

我想在会话之间保持pidl,以便我的应用程序可以记住用户的文件夹选择,无论它们在命名空间中的哪个位置,即使它们不是文件系统文件夹.

我有一种感觉,这样做的方法是写出自己的二进制内容ITEMIDLIST,但我无法确认这一点,因为这些内容应该是不透明的,并且取决于提供者.我不知道重启后,甚至在另一个进程中,如果此数据有效.它可以包含指针,就我所知.

什么是坚持并随后重建pidl的正确方法?

更新:

Jerry Coffin 提出了一对似乎完全符合我要求的功能.然而,一个问题仍然存在.

正如Joel Spolsky所指出的那样,Raymond Chen 似乎暗示保存二进制内容ITEMIDLIST确实是持久化pidl的正确方法,人们可以从中推断出这一点ILSaveToStream并且ILLoadFromStream正是这样做的辅助函数.

但是,我无法找到证明这一点的文档.由于这个项目是在C#中,我宁愿避免必须IStreamIL...函数进行互操作,如果可能的话我自己只保留二进制数据.任何人都可以确认这是正确的吗?

解决方案说明

查看ILSaveToStreamILLoadFromStream的文档,我发现这些函数在shell 5.0版本之前甚至都不存在(Windows 2000).那么在Win2K之前如何完成呢?经过一些测试,我得出的结论是,正如我所怀疑和Joel Spolsky所假设的那样,写下原始文件ITEMIDLIST是要走的路.

C#中的一个简单实现如下:

unsafe{
    byte* start = (byte*)pidl.ToPointer();
    byte* ptr   = start;
    ushort* length;

    do{
        length = (ushort*)ptr;
        ptr += *length;
    }while(*length != 0);

    byte[] rtn = new byte[ptr + 2 - start];
    Marshal.Copy(pidl, rtn, 0, rtn.Length);
    return rtn;
}
Run Code Online (Sandbox Code Playgroud)

当然,这可以在没有使用指针的情况下完成Marshal.ReadInt16:

int offset = 0;
int length;

do{
    length = Marshal.ReadInt16(pidl, offset);
    offset += length;
}while(length != 0);

byte[] rtn = new byte[offset + 2];
Marshal.Copy(pidl, rtn, 0, rtn.Length);
return rtn;
Run Code Online (Sandbox Code Playgroud)

它只需要几个时钟周期,但它仍然需要完全信任,所以它除了远离scaaaary -looking指针之外并没有真正购买太多.

重建pidl更容易,因为数据的总长度已经知道,甚至不需要任何指针:

byte[] itemidlist = ReadPidl();
IntPtr pidl = Marshal.AllocCoTaskMem(itemidlist.Length);
Marshal.Copy(itemidlist, 0, pidl, itemidlist.Length);
Run Code Online (Sandbox Code Playgroud)

以这种方式持久和重建pidl在我的所有测试过程中都有效,并且在有限的场景中,甚至跨机器.我还没有在重新启动时进行测试,因为我不愿关闭所有内容并重新启动我的机器,但考虑到明显的跨机器兼容性,我对此解决方案充满信心.

我接受Joel Spolsky作为解决方案的答案,但是我想对未来的路人提出一个警告:Joel谈到写出一个SHITEMID结构,但这不是全部.一个ITEMIDLIST(这是pidl指向的)实际上是这些可变长度SHITEMID结构的以空值终止的列表,并且必须保持整个列表.这就是上面的代码执行循环以确定总长度的原因.它在此列表中从元素跳转到元素,读取每个元素的长度以找出到下一个元素的偏移量.仅在读取元素长度为零之后才知道整个列表的长度.

Joe*_*sky 5

根据 Raymond Chen 的说法,只需写出项目的长度,然后写出字节,就可以持久保存 pidl,或者更具体地说,SHITEMID结构。

请注意,此结构是典型的 Windows 可变长度结构,其中有一个 cb(“字节数”)元素指定该结构的长度(以字节为单位),后跟其余数据。也就是说,要写结构体,就需要写cb字节。要读取它,您需要分配cb字节的内存并设置cb字段。

请注意不要使用 sizeof(SHITEMID),因为它的声明方式假定 abID 字段只有一个字节,因此它不够大。