Я пытаюсь скопировать файлы из сетевого потока на локальный диск (с С#). Я хочу использовать интерфейс IFileOperation, чтобы получить современный пользовательский интерфейс копии, однако я не уверен, как получить IShellItem для файла, которого еще не существует.
В настоящее время я использую более старую IOperationsProgressDialog, которая работает, однако я хочу, чтобы Shell справлялась с просьбой к пользователю перезаписать файлы, проблемами с разрешениями и т. д.
Как создать IShellItem из IStream для использования в IFileOperation::Copy()?
Какой язык вы используете?
Я использую C#, но меня устраивают ответы на любом языке.
Вы должны использовать параметр 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).
@SimonMourier О да, я не уловил этой тонкости.
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);
}
Спасибо за такой подробный ответ, это действительно полезно.
Связанный/обман: IDataObject или IStream как источник копии оболочки -- без расширения оболочки