Из-за удаленного zip-файла e.Data.GetData("FileContents") создает исключение
Я пытаюсь реализовать обработчик в моем приложении WPF для файлов, перетаскиваемых из zip-архива. Обработчик должен получить содержимое файла для дальнейшей обработки.
Моя среда: Windows7, установлен 7-zip, Visual Studio 2012 Express, .Net 4.5
Вот код простого приложения MainWindow, чтобы продемонстрировать проблему:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AllowDrop= true;
Drop += onDrop;
}
private void onDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent("FileContents"))
{
var fileContents = e.Data.GetData("FileContents");
//get file contents...
}
}
}
Когда я перетаскиваю файл, содержащийся в zip-архиве, в свое Окно, вызов e.Data.GetData("FileContents") генерирует исключение System.ArgumentException ("Аргумент вне диапазона") со следующим стеком вызовов:
System.Windows.DataObject.OleConverter.GetDataInner(formatetc, medium)
System.Windows.DataObject.OleConverter.GetDataFromOleHGLOBAL(format, aspect, index)
System.Windows.DataObject.OleConverter.GetDataFromBoundOleDataObject(format, aspect, index)
System.Windows.DataObject.OleConverter.GetData(format, autoConvert, aspect, index)
System.Windows.DataObject.OleConverter.GetData(format, autoConvert)
System.Windows.DataObject.GetData(format, autoConvert)
System.Windows.DataObject.GetData(format)
TestZip.MainWindow.onDrop(sender, e) Zeile 34 C#
Я посмотрел исходный код этого OleConverter ( http://reflector.webtropy.com/default.aspx/Dotnetfx_Win7_3@5@1/Dotnetfx_Win7_3@5@1/3@5@1/DEVDIV/depot/DevDiv/releases/Orcas/NetFXw7/wpf/src/Core/CSharp/System/Windows/dataobject@cs/1/dataobject@cs), но метод GetDataInner() реализован следующим образом
private void GetDataInner(ref FORMATETC formatetc, out STGMEDIUM medium)
{
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); // BlessedAssert
try
{
_innerData.GetData(ref formatetc, out medium);
}
finally
{
SecurityPermission.RevertAssert();
}
}
Так что это также не дает дополнительной информации о том, что здесь не так.
Я также пробовал с удаленным 7-zip и с различными zip-архивами, но без изменений.
Мой вопрос: кто-нибудь знает, что здесь происходит не так? Что мне нужно сделать, чтобы содержимое файла из zip-архива было перенесено в мое окно?
1 ответ
Старый вопрос, но мне нужно было сделать это сегодня, так что....
Используя заявления:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
Ссылка PresentationCore https://msdn.microsoft.com/en-us/library/system.windows.idataobject(v=vs.110).aspx
Сначала вам нужно получить содержимое "FileDescriptor", вот класс, который их читает.
/// <summary>
/// Specifies which fields are valid in a FileDescriptor Structure
/// </summary>
[Flags]
enum FileDescriptorFlags : uint
{
ClsId = 0x00000001,
SizePoint = 0x00000002,
Attributes = 0x00000004,
CreateTime = 0x00000008,
AccessTime = 0x00000010,
WritesTime = 0x00000020,
FileSize = 0x00000040,
ProgressUI = 0x00004000,
LinkUI = 0x00008000,
Unicode = 0x80000000,
}
internal static class FileDescriptorReader
{
internal sealed class FileDescriptor
{
public FileDescriptorFlags Flags{get;set;}
public Guid ClassId{get;set;}
public Size Size{get;set;}
public Point Point{get;set;}
public FileAttributes FileAttributes{get;set;}
public DateTime CreationTime{get;set;}
public DateTime LastAccessTime{get;set;}
public DateTime LastWriteTime{get;set;}
public Int64 FileSize{get;set;}
public string FileName{get;set;}
public FileDescriptor(BinaryReader reader)
{
//Flags
Flags = (FileDescriptorFlags)reader.ReadUInt32();
//ClassID
ClassId = new Guid(reader.ReadBytes(16));
//Size
Size = new Size(reader.ReadInt32(), reader.ReadInt32());
//Point
Point = new Point(reader.ReadInt32(), reader.ReadInt32());
//FileAttributes
FileAttributes = (FileAttributes)reader.ReadUInt32();
//CreationTime
CreationTime = new DateTime(1601,1,1).AddTicks(reader.ReadInt64());
//LastAccessTime
LastAccessTime = new DateTime(1601,1,1).AddTicks(reader.ReadInt64());
//LastWriteTime
LastWriteTime = new DateTime(1601, 1, 1).AddTicks(reader.ReadInt64());
//FileSize
FileSize = reader.ReadInt64();
//FileName
byte[] nameBytes = reader.ReadBytes(520);
int i = 0;
while(i < nameBytes.Length)
{
if (nameBytes[i] == 0 && nameBytes[i + 1] == 0)
break;
i++;
i++;
}
FileName = UnicodeEncoding.Unicode.GetString(nameBytes, 0, i);
}
}
public static IEnumerable<FileDescriptor> Read(Stream fileDescriptorStream)
{
BinaryReader reader = new BinaryReader(fileDescriptorStream);
var count = reader.ReadUInt32();
while (count > 0)
{
FileDescriptor descriptor = new FileDescriptor(reader);
yield return descriptor;
count--;
}
}
public static IEnumerable<string> ReadFileNames(Stream fileDescriptorStream)
{
BinaryReader reader = new BinaryReader(fileDescriptorStream);
var count = reader.ReadUInt32();
while(count > 0)
{
FileDescriptor descriptor = new FileDescriptor(reader);
yield return descriptor.FileName;
count--;
}
}
}
Теперь, используя это, вы можете получить соответствующее содержимое файла для каждого файла:
static class ClipboardHelper
{
internal static MemoryStream GetFileContents(System.Windows.IDataObject dataObject, int index)
{
//cast the default IDataObject to a com IDataObject
IDataObject comDataObject;
comDataObject = (IDataObject)dataObject;
System.Windows.DataFormat Format = System.Windows.DataFormats.GetDataFormat("FileContents");
if (Format == null)
return null;
FORMATETC formatetc = new FORMATETC();
formatetc.cfFormat = (short)Format.Id;
formatetc.dwAspect = DVASPECT.DVASPECT_CONTENT;
formatetc.lindex = index;
formatetc.tymed = TYMED.TYMED_ISTREAM | TYMED.TYMED_HGLOBAL;
//create STGMEDIUM to output request results into
STGMEDIUM medium = new STGMEDIUM();
//using the com IDataObject interface get the data using the defined FORMATETC
comDataObject.GetData(ref formatetc, out medium);
switch (medium.tymed)
{
case TYMED.TYMED_ISTREAM: return GetIStream(medium);
default: throw new NotSupportedException();
}
}
private static MemoryStream GetIStream(STGMEDIUM medium)
{
//marshal the returned pointer to a IStream object
IStream iStream = (IStream)Marshal.GetObjectForIUnknown(medium.unionmember);
Marshal.Release(medium.unionmember);
//get the STATSTG of the IStream to determine how many bytes are in it
var iStreamStat = new System.Runtime.InteropServices.ComTypes.STATSTG();
iStream.Stat(out iStreamStat, 0);
int iStreamSize = (int)iStreamStat.cbSize;
//read the data from the IStream into a managed byte array
byte[] iStreamContent = new byte[iStreamSize];
iStream.Read(iStreamContent, iStreamContent.Length, IntPtr.Zero);
//wrapped the managed byte array into a memory stream
return new MemoryStream(iStreamContent);
}
}
Теперь вы можете перечислять потоки в содержимом файла:
var fileDescriptor = (MemoryStream)Clipboard.GetDataObject().GetData("FileGroupDescriptorW");
var files = FileDescriptorReader.Read(fileDescriptor);
var fileIndex = 0;
foreach (var fileContentFile in files)
{
if ((fileContentFile.FileAttributes & FileAttributes.Directory) != 0)
{
//Do something with directories?
//Note that directories do not have FileContents
//And will throw if we try to read them
}
else
{
var fileData = ClipboardHelper.GetFileContents(Clipboard.GetDataObject(), FileIndex);
fileData.Position = 0;
//Do something with the fileContent Stream
}
fileIndex++;
}