坚持一个pidl(ITEMIDLIST)

时间:2009-11-21 04:20:07

标签: winapi

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

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

持久化并稍后重建pidl的正确方法是什么?

更新

杰瑞·科芬has suggested一对似乎完全符合我要求的功能。然而,还有一个问题。

作为Joel Spolsky points out,Raymond Chen seems to imply保存ITEMIDLIST的二进制内容确实是保持pidl的正确方法,从中可以推断出{{1} }}和ILSaveToStream是辅助函数,就是这样做的。

但是,我无法找到证明这一点的文档。由于这个项目是在C#中,我宁愿避免为ILLoadFromStream函数插入IStream并尽可能自己保留二进制数据。任何人都可以确认这是正确的吗?

解决方案说明:

查看ILSaveToStreamILLoadFromStream的文档,我发现这些函数在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结构的以空值终止的列表,并且必须保留整个列表。这就是上面的代码执行循环以确定总长度的原因。它在此列表中从元素跳转到元素,读取每个元素的长度以找出到下一个元素的偏移量。只有在读取元素长度为零之后才知道整个列表的长度。

3 个答案:

答案 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中作为安全的字节数组传递。