刷新文件系统中不存在的文件夹
Refreshing a folder that doesn't exist in the file system
在我的 shell 扩展中,我有一些文件夹实际上并不存在于文件系统中,但只对用户如此显示。
当这些文件夹的内容发生变化时,我想刷新它们,目前我使用与常规文件夹相同的方法进行刷新:
Win32.SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);
而 PIDL
是 shell 个文件夹 ID 的列表,根据 SHCNF_IDLIST
的要求。
问题是资源管理器无法处理我不存在的文件夹。它没有刷新它们,而是将我送回根文件夹。
我知道我构建 PIDL
是正确的,因为如前所述,此机制适用于现有文件夹。
如何覆盖 SHChangeNotify
的处理程序?或者有更好的调用刷新的方法吗?
编辑:
我的 PIDL
是如何生成的:
IntPtr GetPIDL(IFolderItem target)
{
Stack stack = new Stack(5);
IntPtr data = IntPtr.Zero;
byte[] rootPIDL = null;
IFolderItem curr = target;
while (curr != null)
{
if (curr.rootPIDL != null)
{
rootPIDL = curr.rootPIDL;
}
else
{
data = curr.SerializeInt();
stack.Push(data);
}
curr = curr.ParentFolder;
}
if (rootPIDL == null && stack.Count == 0)
return IntPtr.Zero;
object[] x = stack.ToArray();
IntPtr[] pidls = null;
int count = stack.Count;
if (count > 0)
{
pidls = new IntPtr[stack.Count];
for (int i = 0; i < count; i++)
{
pidls[i] = (IntPtr)stack.Pop();
}
}
return CreatePIDL(rootPIDL, pidls);
}
我的 CreatePIDL
实现:
internal unsafe static IntPtr CreatePIDL(byte[] rootPIDL,IntPtr[] pidls)
{
int headerSize = Marshal.SizeOf(typeof(ushort));
int totalSize = headerSize;
if (rootPIDL != null)
totalSize += rootPIDL.Length - headerSize;
if (pidls!=null && pidls.Length > 0)
{
foreach (IntPtr data in pidls)
{
totalSize += PIDLSize(data);
}
}
IntPtr ret = PIDLAlloc(totalSize);
IntPtr currPos = ret;
if(rootPIDL!=null)
{
Marshal.Copy(rootPIDL, 0, currPos, rootLPIFQ.Length - headerSize);
currPos = Win32.AdvancePtr(currPos, rootLPIFQ.Length - headerSize);
}
if (pidls != null && pidls.Length>0)
{
foreach (IntPtr data in pidls)
{
int dataLength = PIDLSize(data);
Win32.CopyMemory(currPos, data, dataLength);
currPos = Win32.AdvancePtr(currPos, dataLength);
}
}
Marshal.WriteInt16(currPos, (short)0);
return ret;
}
internal static unsafe int PIDLSize(IntPtr ptr)
{
return (int) (*((ushort*)ptr));
}
internal unsafe static IntPtr PIDLAlloc(int size)
{
IntPtr ret = Marshal.AllocCoTaskMem(size);
if (ret == IntPtr.Zero)
throw new OutOfMemoryException();
return ret;
}
我找到了解决方法。它既不漂亮也不理想,但效果很好。
我没有使用 SHCNE_UPDATEDIR
调用通知,而是依次执行以下所有三个通知程序:
Win32.SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);
Win32.SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);
Win32.SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);
在我的 shell 扩展中,我有一些文件夹实际上并不存在于文件系统中,但只对用户如此显示。
当这些文件夹的内容发生变化时,我想刷新它们,目前我使用与常规文件夹相同的方法进行刷新:
Win32.SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);
而 PIDL
是 shell 个文件夹 ID 的列表,根据 SHCNF_IDLIST
的要求。
问题是资源管理器无法处理我不存在的文件夹。它没有刷新它们,而是将我送回根文件夹。
我知道我构建 PIDL
是正确的,因为如前所述,此机制适用于现有文件夹。
如何覆盖 SHChangeNotify
的处理程序?或者有更好的调用刷新的方法吗?
编辑:
我的 PIDL
是如何生成的:
IntPtr GetPIDL(IFolderItem target)
{
Stack stack = new Stack(5);
IntPtr data = IntPtr.Zero;
byte[] rootPIDL = null;
IFolderItem curr = target;
while (curr != null)
{
if (curr.rootPIDL != null)
{
rootPIDL = curr.rootPIDL;
}
else
{
data = curr.SerializeInt();
stack.Push(data);
}
curr = curr.ParentFolder;
}
if (rootPIDL == null && stack.Count == 0)
return IntPtr.Zero;
object[] x = stack.ToArray();
IntPtr[] pidls = null;
int count = stack.Count;
if (count > 0)
{
pidls = new IntPtr[stack.Count];
for (int i = 0; i < count; i++)
{
pidls[i] = (IntPtr)stack.Pop();
}
}
return CreatePIDL(rootPIDL, pidls);
}
我的 CreatePIDL
实现:
internal unsafe static IntPtr CreatePIDL(byte[] rootPIDL,IntPtr[] pidls)
{
int headerSize = Marshal.SizeOf(typeof(ushort));
int totalSize = headerSize;
if (rootPIDL != null)
totalSize += rootPIDL.Length - headerSize;
if (pidls!=null && pidls.Length > 0)
{
foreach (IntPtr data in pidls)
{
totalSize += PIDLSize(data);
}
}
IntPtr ret = PIDLAlloc(totalSize);
IntPtr currPos = ret;
if(rootPIDL!=null)
{
Marshal.Copy(rootPIDL, 0, currPos, rootLPIFQ.Length - headerSize);
currPos = Win32.AdvancePtr(currPos, rootLPIFQ.Length - headerSize);
}
if (pidls != null && pidls.Length>0)
{
foreach (IntPtr data in pidls)
{
int dataLength = PIDLSize(data);
Win32.CopyMemory(currPos, data, dataLength);
currPos = Win32.AdvancePtr(currPos, dataLength);
}
}
Marshal.WriteInt16(currPos, (short)0);
return ret;
}
internal static unsafe int PIDLSize(IntPtr ptr)
{
return (int) (*((ushort*)ptr));
}
internal unsafe static IntPtr PIDLAlloc(int size)
{
IntPtr ret = Marshal.AllocCoTaskMem(size);
if (ret == IntPtr.Zero)
throw new OutOfMemoryException();
return ret;
}
我找到了解决方法。它既不漂亮也不理想,但效果很好。
我没有使用 SHCNE_UPDATEDIR
调用通知,而是依次执行以下所有三个通知程序:
Win32.SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);
Win32.SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);
Win32.SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);