Могу ли я использовать IFileOperation с виртуальными файлами (IStream)?

Я пытаюсь скопировать файлы из сетевого потока на локальный диск (с С#). Я хочу использовать интерфейс IFileOperation, чтобы получить современный пользовательский интерфейс копии, однако я не уверен, как получить IShellItem для файла, которого еще не существует.

В настоящее время я использую более старую IOperationsProgressDialog, которая работает, однако я хочу, чтобы Shell справлялась с просьбой к пользователю перезаписать файлы, проблемами с разрешениями и т. д.

Как создать IShellItem из IStream для использования в IFileOperation::Copy()?

Связанный/обман: IDataObject или IStream как источник копии оболочки -- без расширения оболочки

Remy Lebeau 14.02.2023 22:38

Какой язык вы используете?

Simon Mourier 15.02.2023 07:50

Я использую C#, но меня устраивают ответы на любом языке.

sbarrac 15.02.2023 19:29
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Калькулятор CGPA 12 для семестра
Калькулятор CGPA 12 для семестра
Чтобы запустить этот код и рассчитать CGPA, необходимо сохранить код как HTML-файл, а затем открыть его в веб-браузере. Для этого выполните следующие...
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
О тренинге HTML JavaScript :HTML (язык гипертекстовой разметки) и CSS (каскадные таблицы стилей) - две основные технологии для создания веб-страниц....
Как собрать/развернуть часть вашего приложения Angular
Как собрать/развернуть часть вашего приложения Angular
Вам когда-нибудь требовалось собрать/развернуть только часть вашего приложения Angular или, возможно, скрыть некоторые маршруты в определенных средах?
Запуск PHP на IIS без использования программы установки веб-платформы
Запуск PHP на IIS без использования программы установки веб-платформы
Установщик веб-платформы, предлагаемый компанией Microsoft, перестанет работать 31 декабря 2022 года. Его закрытие привело к тому, что мы не можем...
Оптимизация React Context шаг за шагом в 4 примерах
Оптимизация React Context шаг за шагом в 4 примерах
При использовании компонентов React в сочетании с Context вы можете оптимизировать рендеринг, обернув ваш компонент React в React.memo сразу после...
2
3
102
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы должны использовать параметр IBindCtx для передачи дополнительных данных парсеру, в данном случае ваших собственных метаданных файла, поэтому SHParseDisplayName() не будет пытаться получить доступ к реальному файлу для получения метаданных. Это описано в документации IShellFolder::ParseDisplayName() и SHCreateItemFromParsingName():

Указатель на контекст привязки, используемый для передачи параметров в качестве входных и выходных данных функции синтаксического анализа. Эти передаваемые параметры часто зависят от источника данных и документируются владельцами источника данных. Например, источник данных файловой системы принимает анализируемое имя (как структуру WIN32_FIND_DATA ), используя параметр контекста привязки STR_FILE_SYS_BIND_DATA . STR_PARSE_PREFER_FOLDER_BROWSING можно передать, чтобы указать, что URL-адреса анализируются с использованием источника данных файловой системы, когда это возможно. Создайте объект контекста привязки, используя CreateBindCtx , и заполните значения, используя IBindCtx::RegisterObjectParam . Полный список см. в разделе Привязать ключи строки контекста.

И подробно изложено в блоге MSDN «Старая новая вещь»:

Создание простого pidl: на тот случай, если вы достаточно заботитесь о том, чтобы отправить ту самую подделку 🕗

Вы можете создать IShellItem, который не представляет файл, но это не отвечает на вопрос, как создать «виртуальный» IShellItem, который вы можете использовать с IFileOperation (например, при поддержке пользовательского IStream).

Simon Mourier 15.02.2023 16:41

@SimonMourier О да, я не уловил этой тонкости.

Ian Boyd 16.02.2023 23:11
Ответ принят как подходящий

IShellItem — это абстракция высокого уровня, реализованная оболочкой, чтобы избежать работы со старыми интерфейсами, такими как IShellFolder (с которыми непросто работать). Проблема в том, что если вы хотите создать «виртуальный», вам как-то нужен IShellFolder.

Это то, что делает приведенный ниже код, создает простую IShellFolder реализацию только для того, чтобы оболочка могла создать «виртуальную» 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, иначе система выйдет из строя. Первый покажет диалоговое окно, подобное этому (где нажатие на выбор сравнения ничего не делает):

А второй покажет диалоговое окно, подобное этому (и вы можете настроить то, что показано как «Мой путь к 'text.txt'»:

И классы со всем взаимодействием с 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);
}

Спасибо за такой подробный ответ, это действительно полезно.

sbarrac 18.02.2023 23:46

Другие вопросы по теме