持久化 pidl (ITEMIDLIST) [英] Persist a pidl (ITEMIDLIST)

查看:24
本文介绍了持久化 pidl (ITEMIDLIST)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'd like to persist pidls between sessions, so that my application can remember the users' folder selections, wherever they may be in the namespace, even if they're not file-system folders.

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

I have a feeling that the way to do this is to write out the binary contents of the ITEMIDLIST itself, but I can't confirm this for sure, since these contents are supposed to be opaque, and are up to the provider. I don't know if after a reboot, or even in another process, if this data is valid. It could contain pointers, for all I know.

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

What is the proper way to persist and later reconstruct a pidl?

Jerry Coffin 建议一对似乎完全正确的函数我问的.然而,一个问题仍然存在.

Jerry Coffin has suggested a pair of functions that seem to do exactly what I asked. One question remains, however.

正如 Joel Spolsky 指出,Raymond Chen 似乎暗示保存ITEMIDLIST<的二进制内容/code> 确实是持久化 pidl 的正确方法,从中可以推断 ILSaveToStreamILLoadFromStream 是执行此操作的辅助函数.

As Joel Spolsky points out, Raymond Chen seems to imply that saving the binary contents of the ITEMIDLIST is indeed the proper way to persist a pidl, from which one can infer that ILSaveToStream and ILLoadFromStream are helper functions that do just that.

但是,我找不到证明这一点的文档.由于这个项目是用 C# 编写的,我宁愿避免为 IL... 函数互操作一个 IStream,如果可能的话,我自己只保留二进制数据.有人可以确认这是正确的吗?

I can't find documentation that proves this, however. Since this project is in C#, I'd prefer to avoid having to interop up an IStream for the IL... functions and just persist the binary data myself if possible. Can anybody confirm that this is correct?

查看 ILSaveToStream 的文档和 ILLoadFromStream,我看到这些函数没有甚至直到 shell 的 5.0 版(Windows 2000)才存在.那么在 Win2K 之前这是如何完成的呢?经过一些测试,我得出的结论是,正如我怀疑的和 Joel Spolsky 假设的那样,写出原始 ITEMIDLIST 是可行的方法.

Looking at the docs for ILSaveToStream and ILLoadFromStream, I see that these functions didn't even exist until version 5.0 of the shell (Windows 2000). So how was this done prior to Win2K? After some testing, I conclude that, as I suspected and Joel Spolsky postulated, writing out the raw ITEMIDLIST is the way to go.

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

A simple implementation in C# follows:

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 来完成:

Of course, this could be done without pointers using 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;

它只需要多花几个时钟周期,但它仍然需要完全信任,所以除了远离看起来像scaaaary的指针之外,它并没有真正买多少.

It only costs a few more clock cycles, but it still requires full trust, so it doesn't really buy much besides staying away from scaaaary-looking pointers.

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

Reconstructing the pidl is even easier, since the total length of the data is already known, and it doesn't even need any pointers:

byte[] itemidlist = ReadPidl();
IntPtr pidl = Marshal.AllocCoTaskMem(itemidlist.Length);
Marshal.Copy(itemidlist, 0, pidl, itemidlist.Length);

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

Persisting and reconstructing pidls in this way worked in all of my tests across processes, and, in limited scenarios, even across machines. I haven't yet tested across reboots, as I'm loath to close everything and restart my machine, but given the apparent cross-machine compatibility, I'm confident in this solution.

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

I'm accepting Joel Spolsky's answer as the solution, but do want to give a caveat for future passersby: Joel talks about writing out a SHITEMID structure, but this is not the whole story. An ITEMIDLIST (which is what a pidl points to) is actually a null-terminated list of these variable-length SHITEMID structures, and the whole list must be persisted. This is why the code above executes a loop to determine the total length. It jumps from element to element in this list, reading the length of each element to find out the offset to the next one. Only after an element length of zero is read is the length of the entire list known.

推荐答案

据 Raymond Chen 您可以保留一个 pidl -- 或者,更具体地说,一个 SHITEMID 结构,只需写出项目的长度,然后是字节.

According to Raymond Chen you can persist a pidl -- or, more specifically, a SHITEMID structure, just by writing out length of the item and then the bytes.

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

Notice that this struct is a typical Windows variable length struct, with a cb ("count of bytes") element specifying the length of the structure in bytes, followed by the rest of the data. In other words, to write the structure, you need to write cb bytes. To read it, you need to allocated cb bytes of memory and set the cb field.

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

Be careful not to use sizeof(SHITEMID) because the way it's declared assumes only one byte for the abID field so that won't be big enough.

这篇关于持久化 pidl (ITEMIDLIST)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆