我可以将 IFileOperation 与虚拟文件 (IStream) 一起使用吗?

问题描述 投票:0回答:2

我正在尝试将文件从网络流复制到本地磁盘(使用 C#)。我想使用

IFileOperation
界面来获取现代复制 UI,但是我不确定如何为尚不存在的文件获取
IShellItem

目前我使用的是较旧的

IOperationsProgressDialog
,它确实有效,但是我希望 Shell 能够处理要求用户覆盖文件、权限问题等问题。

如何从

IShellItem
创建
IStream
以在
IFileOperation::Copy()
中使用?

windows shell winapi com ole
2个回答
2
投票

IShellItem 是由 Shell 实现的高级抽象,以避免处理像 IShellFolder 这样的旧接口(这些接口不容易使用)。问题是,如果你想创建一个“虚拟”的,你确实需要一个

IShellFolder

这就是下面的代码所做的,创建一个简单的

IShellFolder
实现,只是为了确保 Shell 可以在其上创建一个“虚拟”
IShellItem

控制台应用程序示例(请注意

STAThread
是必需的,
IFileOperation
才能正常工作)

[STAThread]
static void Main()
{
    // prepare an item with a name
    var size = 50 * 200000;
    var item = new VirtualItem("test.txt", size, new DateTime(1966, 3, 24));

    item.Read += (s, e) =>
    {
        if (size == 0) // finished?
            return;

        // in this sample, we dont put anything in the buffer
        // we just wait to force the Shell progress dialog to show
        var written = Math.Min(e.Bytes.Length, size);
        e.Written = written;
        Thread.Sleep(100);
        size -= written;
    };

    // get source item as an IShellItem
    ShellNative.SHCreateItemWithParent(IntPtr.Zero, item.VirtualFolder, item.Pidl, typeof(ShellNative.IShellItem).GUID, out var source);

    // get some target folder as an IShellItem
    ShellNative.SHCreateItemFromParsingName(@"c:\temp", null, typeof(ShellNative.IShellItem).GUID, out var target);

    // create IFileOperation and ask to copy
    var fo = (ShellNative.IFileOperation)new ShellNative.FileOperation();
    fo.CopyItem((ShellNative.IShellItem)Marshal.GetObjectForIUnknown(source), (ShellNative.IShellItem)Marshal.GetObjectForIUnknown(target), null, IntPtr.Zero);

    // if you dont use of of these and the target file already exists
    // the whole process will fail for some reason
    const uint FOFX_DONTDISPLAYSOURCEPATH = 0x04000000;
    const uint FOFX_DONTDISPLAYLOCATIONS = 0x80000000;
    fo.SetOperationFlags(FOFX_DONTDISPLAYLOCATIONS);

    // do it
    fo.PerformOperations();
}

注意,如果目标文件已经存在,则必须至少设置 FOFX_DONTDISPLAYSOURCEPATH 或 FOFX_DONTDISPLAYLOCATIONS 选项,否则系统将失败。第一个将显示这样的对话框(单击比较选项不会执行任何操作):

enter image description here

第二个将显示一个像这样的对话框(您可以自定义显示的内容“'text.txt'的路径”

enter image description here

以及具有所有 Shell 互操作性的类:

public sealed class VirtualItem : ShellNative.IPropertyStore, IStream, IDisposable
{
    private readonly ComMemory _pidl;

    public event EventHandler<ReadEventArgs> Read;

    public VirtualItem(string name, long? size = null, DateTime? dateModified = null)
    {
        ArgumentNullException.ThrowIfNull(name);
        Name = name;
        VirtualFolder = new VirtualShellFolder(this);
        _pidl = new ComMemory(2);  // 4 bytes starting with 2 is the most simple non-terminator PIDL
        Size = size; // if size is unspecified, the dialog will work but info will be wrong
        DateModified = dateModified;
    }

    public string Name { get; }
    public long? Size { get; }
    public DateTime? DateModified { get; }
    public ShellNative.IShellFolder VirtualFolder { get; }
    public IntPtr Pidl => _pidl.Pointer;

    public void Dispose() => _pidl.Dispose();

    public int GetValue(ref ShellNative.PROPERTYKEY key, IntPtr pv)
    {
        if (key.Equals(ShellNative.PROPERTYKEY.PKEY_FileName))
        {
            ShellNative.WritePropVariant(Name, pv);
            return 0;
        }

        if (key.Equals(ShellNative.PROPERTYKEY.PKEY_ItemPathDisplay))
        {
            // this will be used when FOFX_DONTDISPLAYLOCATIONS is set in SetOperationFlags
            ShellNative.WritePropVariant(@"The Path of '" + Name + "'", pv);
            return 0;
        }

        // this is mostly used for the modified date
        if (key.Equals(ShellNative.PROPERTYKEY.PKEY_FindData))
        {
            var findData = new ShellNative.WIN32_FIND_DATAW();
            if (DateModified.HasValue)
            {
                findData.ftLastWriteTime = ShellNative.ToFILETIME(DateModified.Value);
            }

            if (Size.HasValue)
            {
                findData.nFileSizeLow = (uint)(Size.Value & uint.MaxValue);
                findData.nFileSizeHigh = (uint)(Size.Value >> 32);
            }

            findData.cFileName = Name;
            using var mem = new ComMemory(findData);
            ShellNative.WritePropVariant(mem, pv);
            return 0;
        }

        // shell scans this to determine a root folder
        if (key.Equals(ShellNative.PROPERTYKEY.PKEY_Volume_IsRoot))
        {
            ShellNative.WritePropVariant(true, pv);
            return 0;
        }

        if (key.Equals(ShellNative.PROPERTYKEY.PKEY_Size) && Size.HasValue)
        {
            ShellNative.WritePropVariant(Size.Value, pv);
            return 0;
        }

        if (key.Equals(ShellNative.PROPERTYKEY.PKEY_DateModified) && DateModified.HasValue)
        {
            ShellNative.WritePropVariant(DateModified.Value, pv);
            return 0;
        }

        const int VT_EMPTY = 0;
        Marshal.WriteInt16(pv, VT_EMPTY);
        return 0;
    }

    void IStream.Read(byte[] pv, int cb, IntPtr pcbRead)
    {
        var evt = new ReadEventArgs(pv);
        Read?.Invoke(this, evt);
        if (pcbRead != IntPtr.Zero)
        {
            Marshal.WriteInt32(pcbRead, evt.Written);
        }
    }

    void IStream.Stat(out STATSTG pstatstg, int grfStatFlag)
    {
        pstatstg = new()
        {
            cbSize = Size ?? -1
        };
    }

    int ShellNative.IPropertyStore.GetCount(out int cProps) => throw new NotImplementedException();
    int ShellNative.IPropertyStore.GetAt(int iProp, out ShellNative.PROPERTYKEY pkey) => throw new NotImplementedException();
    int ShellNative.IPropertyStore.SetValue(ref ShellNative.PROPERTYKEY key, IntPtr propvar) => throw new NotImplementedException();
    int ShellNative.IPropertyStore.Commit() => throw new NotImplementedException();
    void IStream.Clone(out IStream ppstm) => throw new NotImplementedException();
    void IStream.Commit(int grfCommitFlags) => throw new NotImplementedException();
    void IStream.CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten) => throw new NotImplementedException();
    void IStream.LockRegion(long libOffset, long cb, int dwLockType) => throw new NotImplementedException();
    void IStream.Revert() => throw new NotImplementedException();
    void IStream.Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition) => throw new NotImplementedException();
    void IStream.SetSize(long libNewSize) => throw new NotImplementedException();
    void IStream.UnlockRegion(long libOffset, long cb, int dwLockType) => throw new NotImplementedException();
    void IStream.Write(byte[] pv, int cb, IntPtr pcbWritten) => throw new NotImplementedException();

    private sealed class VirtualShellFolder :
        ShellNative.IShellFolder2,
        ShellNative.IPersistIDList,
        ShellNative.IPersistFolder,
        ShellNative.IParentAndItem,
        ShellNative.IPropertyStoreFactory
    {
        private readonly VirtualItem _item;

        public VirtualShellFolder(VirtualItem item)
        {
            _item = item;
        }

        public int BindToObject(IntPtr pidl, IBindCtx pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv)
        {
            if (riid == typeof(IStream).GUID)
                return ShellNative.QueryInterface(_item, riid, out ppv);

            return ShellNative.QueryInterface(this, riid, out ppv);
        }

        public int GetAttributesOf(int cidl, IntPtr apidl, ref int rgfInOut)
        {
            // our item is only a stream
            rgfInOut = ShellNative.SFGAO_STREAM;
            return 0;
        }

        public int GetDisplayNameOf(IntPtr pidl, ShellNative.SHGDNF uFlags, IntPtr pName)
        {
            // we dont honor full parsing name because ... we can't and that's probably why it fails in case of target file already exists
            ShellNative.WriteSTRRETString(pName, _item.Name);
            return 0;
        }

        public int GetIDList(out IntPtr ppidl)
        {
            ppidl = ShellNative.ILClone(_item.Pidl);
            return 0;
        }

        public int GetParentAndItem(IntPtr ppidlParent, IntPtr ppsf, IntPtr ppidlChild)
        {
            if (ppsf != IntPtr.Zero)
            {
                ShellNative.QueryInterface(this, typeof(ShellNative.IShellFolder).GUID, out var ppv);
                Marshal.WriteIntPtr(ppsf, ppv);
            }

            if (ppidlChild != IntPtr.Zero)
            {
                var ppidl = ShellNative.ILClone(_item.Pidl);
                Marshal.WriteIntPtr(ppidlChild, ppidl);
            }
            return 0;
        }

        public int GetDetailsEx(IntPtr pidl, ref ShellNative.PROPERTYKEY pscid, IntPtr pv) => _item.GetValue(ref pscid, pv);
        public int GetPropertyStore(int flags, IntPtr pUnkFactory, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv) => ShellNative.QueryInterface(_item, riid, out ppv);
        public int GetPropertyStoreForKeys(IntPtr rgKeys, int cKeys, int flags, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv) => ShellNative.QueryInterface(_item, riid, out ppv);

        public int Initialize(IntPtr pidl) => throw new NotImplementedException();
        public int ParseDisplayName(IntPtr hwnd, IBindCtx pbc, [MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName, out int pchEaten, out IntPtr ppidl, IntPtr pdwAttributes) => throw new NotImplementedException();
        public int EnumObjects(IntPtr hwnd, int grfFlags, out IntPtr ppenumIDList) => throw new NotImplementedException();
        public int CompareIDs(long lParam, IntPtr pidl1, IntPtr pidl2) => throw new NotImplementedException();
        public int SetNameOf(IntPtr hwnd, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string pszName, ShellNative.SHGDNF uFlags, out IntPtr ppidlOut) => throw new NotImplementedException();
        public int GetDefaultSearchGUID(out Guid pguid) => throw new NotImplementedException();
        public int EnumSearches(out IntPtr ppenum) => throw new NotImplementedException();
        public int GetDefaultColumn(int dwRes, out int pSort, out int pDisplay) => throw new NotImplementedException();
        public int GetDefaultColumnState(int iColumn, out int pcsFlags) => throw new NotImplementedException();
        public int SetParentAndItem(IntPtr pidlParent, ShellNative.IShellFolder psf, IntPtr pidlChild) => throw new NotImplementedException();
        public int SetIDList(IntPtr pidl) => throw new NotImplementedException();
        public int BindToStorage(IntPtr pidl, IBindCtx pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv) => throw new NotImplementedException();
        public int CreateViewObject(IntPtr hwndOwner, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv) => throw new NotImplementedException();
        public int GetDetailsOf(IntPtr pidl, int iColumn, IntPtr psd) => throw new NotImplementedException();
        public int MapColumnToSCID(int iColumn, ref ShellNative.PROPERTYKEY pscid) => throw new NotImplementedException();
        public int GetClassID(out Guid pClassID) => throw new NotImplementedException();
        public int GetUIObjectOf(IntPtr hwndOwner, int cidl, IntPtr apidl, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, IntPtr rgfReserved, out IntPtr ppv) => throw new NotImplementedException();
    }
}

public sealed class ReadEventArgs : EventArgs
{
    public ReadEventArgs(byte[] bytes)
    {
        Bytes = bytes;
    }

    public byte[] Bytes { get; }
    public int Written { get; set; }
}

public sealed class ComMemory : IDisposable
{
    private IntPtr _pointer;
    public ComMemory(object structure) { Size = Marshal.SizeOf(structure); _pointer = Marshal.AllocCoTaskMem(Size); Marshal.StructureToPtr(structure, _pointer, false); }
    public IntPtr Pointer => _pointer;
    public int Size { get; }
    public void Dispose() { var handle = Interlocked.Exchange(ref _pointer, IntPtr.Zero); if (handle != IntPtr.Zero) Marshal.FreeCoTaskMem(handle); }
}

public static class ShellNative
{
    public static void WriteSTRRETString(IntPtr ptr, string name)
    {
        if (ptr == IntPtr.Zero)
            return;

        // implicit STRRET_TYPE is 0 => STRRET_WSTR
        ZeroMemory(ptr, IntPtr.Size == 8 ? Marshal.SizeOf<STRRET_64>() : Marshal.SizeOf<STRRET_32>());
        if (name == null)
            return;

        var strPtr = Marshal.StringToCoTaskMemUni(name);

        // skip uType wich size + padding = 4 or 8
        Marshal.WriteIntPtr(ptr + IntPtr.Size, strPtr);
    }

    public static FILETIME ToFILETIME(DateTime dt) { var ft = dt.ToFileTime(); return new FILETIME { dwLowDateTime = (int)(ft & uint.MaxValue), dwHighDateTime = (int)(ft >> 32) }; }

    public static int QueryInterface(object obj, Guid iid, out IntPtr ppv)
    {
        ppv = IntPtr.Zero;
        if (obj == null)
            return E_NOINTERFACE;

        var unk = Marshal.GetIUnknownForObject(obj);
        try
        {
            return Marshal.QueryInterface(unk, ref iid, out ppv);
        }
        finally
        {
            Marshal.Release(unk);
        }
    }

    public static void WritePropVariant(object obj, IntPtr pv)
    {
        if (obj is string s)
        {
            var ptr = Marshal.StringToCoTaskMemUni(s);
            Marshal.WriteIntPtr(pv + 8, ptr);
            const int VT_LPWSTR = 31;
            Marshal.WriteInt16(pv, VT_LPWSTR);
            return;
        }

        if (obj is bool b)
        {
            Marshal.WriteInt16(pv + 8, (short)(b ? -1 : 0));
            const int VT_BOOL = 11;
            Marshal.WriteInt16(pv, VT_BOOL);
            return;
        }

        if (obj is long l)
        {
            Marshal.WriteInt64(pv + 8, l);
            const int VT_UI8 = 21;
            Marshal.WriteInt16(pv, VT_UI8);
            return;
        }

        if (obj is DateTime dt)
        {
            InitPropVariantFromFileTime(dt.ToFileTime(), pv);
            return;
        }

        if (obj is ComMemory mem)
        {
            var hr = InitPropVariantFromBuffer(mem.Pointer, mem.Size, pv);
            return;
        }
        throw new NotSupportedException();
    }

    [DllImport("kernel32", EntryPoint = "RtlZeroMemory")]
    public static extern void ZeroMemory(IntPtr address, IntPtr length);
    public static void ZeroMemory(IntPtr address, int length) => ZeroMemory(address, (IntPtr)length);

    [DllImport("shell32")]
    public static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IBindCtx pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);

    [DllImport("shell32")]
    public static extern int SHCreateItemWithParent(IntPtr pidlParent, IShellFolder psfParent, IntPtr pidl, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);

    [DllImport("propsys")]
    public static extern int InitPropVariantFromBuffer(IntPtr pv, int cb, IntPtr ppropvar);

    [DllImport("propsys")]
    public static extern int InitPropVariantFromFileTime([MarshalAs(UnmanagedType.LPStruct)] long pftIn, IntPtr ppropvar);

    [DllImport("shell32")]
    public static extern IntPtr ILClone(IntPtr pidl);

    [ComImport, Guid("000214e6-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IShellFolder
    {
        [PreserveSig] int ParseDisplayName(IntPtr hwnd, IBindCtx pbc, [MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName, out int pchEaten, out IntPtr ppidl, IntPtr pdwAttributes);
        [PreserveSig] int EnumObjects(IntPtr hwnd, int grfFlags, out IntPtr ppenumIDList);
        [PreserveSig] int BindToObject(IntPtr pidl, IBindCtx pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
        [PreserveSig] int BindToStorage(IntPtr pidl, IBindCtx pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
        [PreserveSig] int CompareIDs(long lParam, IntPtr pidl1, IntPtr pidl2);
        [PreserveSig] int CreateViewObject(IntPtr hwndOwner, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
        [PreserveSig] int GetAttributesOf(int cidl, IntPtr apidl, ref int rgfInOut);
        [PreserveSig] int GetUIObjectOf(IntPtr hwndOwner, int cidl, IntPtr apidl, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, IntPtr rgfReserved, out IntPtr ppv);
        [PreserveSig] int GetDisplayNameOf(IntPtr pidl, SHGDNF uFlags, IntPtr pName);
        [PreserveSig] int SetNameOf(IntPtr hwnd, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string pszName, SHGDNF uFlags, out IntPtr ppidlOut);
    }

    [ComImport, Guid("93f2f68c-1d1b-11d3-a30e-00c04f79abd1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IShellFolder2 : IShellFolder
    {
        [PreserveSig] new int ParseDisplayName(IntPtr hwnd, IBindCtx pbc, [MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName, out int pchEaten, out IntPtr ppidl, IntPtr pdwAttributes);
        [PreserveSig] new int EnumObjects(IntPtr hwnd, int grfFlags, out IntPtr ppenumIDList);
        [PreserveSig] new int BindToObject(IntPtr pidl, IBindCtx pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
        [PreserveSig] new int BindToStorage(IntPtr pidl, IBindCtx pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
        [PreserveSig] new int CompareIDs(long lParam, IntPtr pidl1, IntPtr pidl2);
        [PreserveSig] new int CreateViewObject(IntPtr hwndOwner, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
        [PreserveSig] new int GetAttributesOf(int cidl, IntPtr apidl, ref int rgfInOut);
        [PreserveSig] new int GetUIObjectOf(IntPtr hwndOwner, int cidl, IntPtr apidl, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, IntPtr rgfReserved, out IntPtr ppv);
        [PreserveSig] new int GetDisplayNameOf(IntPtr pidl, SHGDNF uFlags, IntPtr pName);
        [PreserveSig] new int SetNameOf(IntPtr hwnd, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string pszName, SHGDNF uFlags, out IntPtr ppidlOut);
        [PreserveSig] int GetDefaultSearchGUID(out Guid pguid);
        [PreserveSig] int EnumSearches(out IntPtr ppenum);
        [PreserveSig] int GetDefaultColumn(int dwRes, out int pSort, out int pDisplay);
        [PreserveSig] int GetDefaultColumnState(int iColumn, out int pcsFlags);
        [PreserveSig] int GetDetailsEx(IntPtr pidl, ref ShellNative.PROPERTYKEY pscid, IntPtr pv);
        [PreserveSig] int GetDetailsOf(IntPtr pidl, int iColumn, IntPtr psd);
        [PreserveSig] int MapColumnToSCID(int iColumn, ref ShellNative.PROPERTYKEY pscid);
    }

    [ComImport, Guid("0000010c-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IPersist
    {
        [PreserveSig] int GetClassID(out Guid pClassID);
    }

    [ComImport, Guid("1079acfc-29bd-11d3-8e0d-00c04f6837d5"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IPersistIDList : IPersist
    {
        [PreserveSig] new int GetClassID(out Guid pClassID);
        [PreserveSig] int SetIDList(IntPtr pidl);
        [PreserveSig] int GetIDList(out IntPtr ppidl);
    }

    [ComImport, Guid("000214ea-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IPersistFolder : IPersist
    {
        [PreserveSig] new int GetClassID(out Guid pClassID);
        [PreserveSig] int Initialize(IntPtr pidl);
    }

    [ComImport, Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IPropertyStore
    {
        [PreserveSig] int GetCount(out int cProps);
        [PreserveSig] int GetAt(int iProp, out PROPERTYKEY pkey);
        [PreserveSig] int GetValue(ref PROPERTYKEY key, IntPtr pv);
        [PreserveSig] int SetValue(ref PROPERTYKEY key, IntPtr propvar);
        [PreserveSig] int Commit();
    }

    [ComImport, Guid("bc110b6d-57e8-4148-a9c6-91015ab2f3a5"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IPropertyStoreFactory
    {
        [PreserveSig] int GetPropertyStore(int flags, IntPtr pUnkFactory, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
        [PreserveSig] int GetPropertyStoreForKeys(IntPtr rgKeys, int cKeys, int flags, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
    }

    [ComImport, Guid("b3a4b685-b685-4805-99d9-5dead2873236"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IParentAndItem
    {
        [PreserveSig] int SetParentAndItem(IntPtr pidlParent, IShellFolder psf, IntPtr pidlChild);
        [PreserveSig] int GetParentAndItem(IntPtr ppidlParent, IntPtr ppsf, IntPtr ppidlChild);
    }

    [ComImport, Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IShellItem
    {
        // undefined as we dont need methods
    }

    [Guid("947aab5f-0a5c-4c13-b4d6-4bf7836fc9f8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IFileOperation
    {
        [PreserveSig] int Advise(IntPtr pfops, out int pdwCookie);
        [PreserveSig] int Unadvise(int dwCookie);
        [PreserveSig] int SetOperationFlags(uint dwOperationFlags);
        [PreserveSig] int SetProgressMessage([MarshalAs(UnmanagedType.LPWStr)] string pszMessage);
        [PreserveSig] int SetProgressDialog(IntPtr popd);
        [PreserveSig] int SetProperties(IntPtr pproparray);
        [PreserveSig] int SetOwnerWindow(IntPtr hwndOwner);
        [PreserveSig] int ApplyPropertiesToItem(IShellItem psiItem);
        [PreserveSig] int ApplyPropertiesToItems([MarshalAs(UnmanagedType.IUnknown)] object punkItems);
        [PreserveSig] int RenameItem(IShellItem psiItem, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, IntPtr pfopsItem);
        [PreserveSig] int RenameItems([MarshalAs(UnmanagedType.IUnknown)] object pUnkItems, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName);
        [PreserveSig] int MoveItem(IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, IntPtr pfopsItem);
        [PreserveSig] int MoveItems([MarshalAs(UnmanagedType.IUnknown)] object punkItems, IShellItem psiDestinationFolder);
        [PreserveSig] int CopyItem(IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszCopyName, IntPtr pfopsItem);
        [PreserveSig] int CopyItems([MarshalAs(UnmanagedType.IUnknown)] object punkItems, IShellItem psiDestinationFolder);
        [PreserveSig] int DeleteItem(IShellItem psiItem, IntPtr pfopsItem);
        [PreserveSig] int DeleteItems([MarshalAs(UnmanagedType.IUnknown)] object punkItems);
        [PreserveSig] int NewItem(IShellItem psiDestinationFolder, uint dwFileAttributes, [MarshalAs(UnmanagedType.LPWStr)] string pszName, [MarshalAs(UnmanagedType.LPWStr)] string pszTemplateName, IntPtr pfopsItem);
        [PreserveSig] int PerformOperations();
        [PreserveSig] int GetAnyOperationsAborted(out bool pfAnyOperationsAborted);
    }

    [ComImport, Guid("3ad05575-8857-4850-9277-11b85bdb8e09")] // CLSID_FileOperation
    public class FileOperation { }

    [StructLayout(LayoutKind.Sequential)]
    public struct PROPERTYKEY : IEquatable<PROPERTYKEY>
    {
        public Guid fmtid { get; }
        public int pid { get; }
        public PROPERTYKEY(Guid fmtid, int pid) { this.fmtid = fmtid; this.pid = pid; }
        public override string ToString() => fmtid.ToString("B") + " " + pid;
        public override int GetHashCode() => fmtid.GetHashCode() ^ pid;
        public bool Equals(PROPERTYKEY other) => other.fmtid == fmtid && other.pid == pid;
        public override bool Equals(object obj) => obj is PROPERTYKEY other && Equals(other);

        public static PROPERTYKEY PKEY_DateModified { get; } = new PROPERTYKEY(new Guid("b725f130-47ef-101a-a5f1-02608c9eebac"), 14);
        public static PROPERTYKEY PKEY_FileName { get; } = new PROPERTYKEY(new Guid("41cf5ae0-f75a-4806-bd87-59c7d9248eb9"), 100);
        public static PROPERTYKEY PKEY_FindData { get; } = new PROPERTYKEY(new Guid("28636aa6-953d-11d2-b5d6-00c04fd918d0"), 0);
        public static PROPERTYKEY PKEY_ItemPathDisplay { get; } = new PROPERTYKEY(new Guid("e3e0584c-b788-4a5a-bb20-7f5a44c9acdd"), 7);
        public static PROPERTYKEY PKEY_Size { get; } = new PROPERTYKEY(new Guid("b725f130-47ef-101a-a5f1-02608c9eebac"), 12);
        public static PROPERTYKEY PKEY_Volume_IsRoot { get; } = new PROPERTYKEY(new Guid("9b174b35-40ff-11d2-a27e-00c04fc30871"), 10);
    }

    [StructLayout(LayoutKind.Explicit, Size = 264)]
    public struct STRRET_32
    {
        [FieldOffset(0)]
        public STRRET_TYPE uType;

        [FieldOffset(4)]
        public IntPtr pOleStr;

        [FieldOffset(4)]
        public uint uOffset;

        [FieldOffset(4)]
        public IntPtr cStr;
    }

    [StructLayout(LayoutKind.Explicit, Size = 272)]
    public struct STRRET_64
    {
        [FieldOffset(0)]
        public STRRET_TYPE uType;

        [FieldOffset(8)]
        public IntPtr pOleStr;

        [FieldOffset(8)]
        public uint uOffset;

        [FieldOffset(8)]
        public IntPtr cStr;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct WIN32_FIND_DATAW
    {
        public int dwFileAttributes;
        public FILETIME ftCreationTime;
        public FILETIME ftLastAccessTime;
        public FILETIME ftLastWriteTime;
        public uint nFileSizeHigh;
        public uint nFileSizeLow;
        public int dwReserved0;
        public int dwReserved1;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string cFileName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
        public string cAlternateFileName;
    }

    public enum STRRET_TYPE
    {
        STRRET_WSTR = 0,
        STRRET_OFFSET = 1,
        STRRET_CSTR = 2,
    }

    [Flags]
    public enum SHGDNF
    {
        SHGDN_NORMAL = 0x0,
        SHGDN_INFOLDER = 0x1,
        SHGDN_FOREDITING = 0x1000,
        SHGDN_FORADDRESSBAR = 0x4000,
        SHGDN_FORPARSING = 0x8000,
    }

    public const int SFGAO_STREAM = 0x00400000;
    public const int E_NOTIMPL = unchecked((int)0x80004001);
    public const int E_NOINTERFACE = unchecked((int)0x80004002);
}

0
投票

您必须使用

IBindCtx
参数将附加数据传递给解析器,在本例中是您自己的文件元数据,因此
SHParseDisplayName()
不会尝试访问真实文件来获取元数据。 这在
IShellFolder::ParseDisplayName()
SHCreateItemFromParsingName()
文档中进行了描述:

指向绑定上下文的指针,用于将参数作为输入和输出传递给解析函数。这些传递的参数通常特定于数据源,并由数据源所有者记录。 例如,文件系统数据源使用 STR_FILE_SYS_BIND_DATA 绑定上下文参数接受正在解析的名称(作为 WIN32_FIND_DATA 结构)。 可以传递 STR_PARSE_PREFER_FOLDER_BROWSING 以指示在以下情况下使用文件系统数据源解析 URL:可能的。 使用 CreateBindCtx 构造绑定上下文对象,并使用 IBindCtx::RegisterObjectParam 填充值。有关这些的完整列表,请参阅绑定上下文字符串键

并在 MSDN“Old New Thing”博客上详细概述:

创建一个简单的pidl:当你足够关心发送非常假的东西时🕗

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.