我想在会话之间保留pidl,以便我的应用程序可以记住用户的文件夹选择,无论它们位于命名空间中的哪个位置,即使它们不是文件系统文件夹。 / p>
我有一种感觉,这样做的方法是写出ITEMIDLIST
本身的二进制内容,但我无法确认这一点,因为这些内容应该是不透明的,并且是由提供者决定。我不知道重启后,甚至在另一个进程中,如果此数据有效。它可能包含指针,就我所知。
持久化并稍后重建pidl的正确方法是什么?
作为Joel Spolsky points out,Raymond Chen seems to imply保存ITEMIDLIST
的二进制内容确实是保持pidl的正确方法,从中可以推断出{{1} }}和ILSaveToStream
是辅助函数,就是这样做的。
ILLoadFromStream
函数插入IStream
并尽可能自己保留二进制数据。任何人都可以确认这是正确的吗?
查看ILSaveToStream和ILLoadFromStream的文档,我发现这些函数在shell的5.0版本(Windows 2000)之前甚至都不存在。那么在Win2K之前如何完成呢?经过一些测试后,我得出结论,正如我所怀疑和Joel Spolsky所假设的那样,写出原始IL...
是可行的方法。
C#中的一个简单实现如下:
ITEMIDLIST
当然,这可以在没有使用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;
}
的指针的情况下完成:
Marshal.ReadInt16
它只需花费更多的时钟周期,但它仍然需要完全信任,所以除了远离 scaaaary -looking指针之外,它并没有真正买得多。
重建pidl更加容易,因为数据的总长度已经知道,甚至不需要任何指针:
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;
以这种方式持久化和重建pidl在我的所有测试过程中都有效,在有限的情况下,甚至跨机器。我还没有在重新启动时进行测试,因为我不愿关闭所有内容并重启我的机器,但考虑到明显的跨机器兼容性,我对此解决方案充满信心。
我接受Joel Spolsky作为解决方案的答案,但我想对未来的路人提出一个警告:Joel谈到写出byte[] itemidlist = ReadPidl();
IntPtr pidl = Marshal.AllocCoTaskMem(itemidlist.Length);
Marshal.Copy(itemidlist, 0, pidl, itemidlist.Length);
结构,但这不是全部。 SHITEMID
(这是pidl指向的)实际上是这些可变长度ITEMIDLIST
结构的以空值终止的列表,并且必须保留整个列表。这就是上面的代码执行循环以确定总长度的原因。它在此列表中从元素跳转到元素,读取每个元素的长度以找出到下一个元素的偏移量。只有在读取元素长度为零之后才知道整个列表的长度。
答案 0 :(得分:4)
根据Raymond Chen you can persist a pidl - 或者更具体地说,SHITEMID结构,只需写出项目的长度,然后写出字节。
请注意,此结构是典型的Windows变量长度结构,带有cb(“count of bytes”)元素,以字节为单位指定结构的长度,后跟其余数据。换句话说,要编写结构,需要编写cb字节。要读取它,您需要分配cb字节的内存并设置cb字段。
注意不要使用sizeof(SHITEMID),因为它声明的方式只假定abID字段有一个字节,因此不够大。
答案 1 :(得分:1)
ILSaveToStream和ILLoadFromStream(可能还有ILLoadFromStreamEx)。但有一点需要注意:ILLoadFromStream和ILLoadFromStreamEx已被弃用,如果微软已经说过应该替换它们,我就不知道了。
答案 2 :(得分:1)
pidls的内部没有标准。毕竟,pidl仅用于定位物品。只要pidl被PIDL发生器识别(或可预测地失败),并且解析器(Windows资源管理器或第三方shell命名空间扩展),Explorer.exe很高兴。没有人说PIDL必须在机器重启或用户桌面上保持不变。
在一种情况下,如果pidl在机器重新启动后可以保持,则可以进行有根据的猜测,即检查SFGAO_CANLINK属性。通过声明此属性,PIDL解析器声明您可以拖动文件夹中的项目以创建包含项目的PIDL的桌面快捷方式。解析器必须处理PIDL中的持久性和向后兼容性,否则双击快捷方式会使Explorer进程崩溃。 Buggy编程发生了,所以不要把它视为理所当然。
在黑暗的旧时代,pidl只是保存在字节数组中,或者当需要传递COM对象时,在VARIANT中作为安全的字节数组传递。