Drag-and-Drop multiple Attached File From Outlook to C# Window Form
Solution 1
I solve my problem Just add that code in any cs file.
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Reflection;
using System.Windows.Forms;
namespace iwantedue.Windows.Forms
{
public class OutlookDataObject: System.Windows.Forms.IDataObject
{
#region NativeMethods
private class NativeMethods
{
[DllImport("kernel32.dll")]
static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("ole32.dll", PreserveSig = false)]
public static extern ILockBytes CreateILockBytesOnHGlobal(IntPtr hGlobal, bool fDeleteOnRelease);
[DllImport("OLE32.DLL", CharSet = CharSet.Auto, PreserveSig = false)]
public static extern IntPtr GetHGlobalFromILockBytes(ILockBytes pLockBytes);
[DllImport("OLE32.DLL", CharSet = CharSet.Unicode, PreserveSig = false)]
public static extern IStorage StgCreateDocfileOnILockBytes(ILockBytes plkbyt, uint grfMode, uint reserved);
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("0000000B-0000-0000-C000-000000000046")]
public interface IStorage
{
[return: MarshalAs(UnmanagedType.Interface)]
IStream CreateStream([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved1, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
[return: MarshalAs(UnmanagedType.Interface)]
IStream OpenStream([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr reserved1, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
[return: MarshalAs(UnmanagedType.Interface)]
IStorage CreateStorage([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved1, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
[return: MarshalAs(UnmanagedType.Interface)]
IStorage OpenStorage([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr pstgPriority, [In, MarshalAs(UnmanagedType.U4)] int grfMode, IntPtr snbExclude, [In, MarshalAs(UnmanagedType.U4)] int reserved);
void CopyTo(int ciidExclude, [In, MarshalAs(UnmanagedType.LPArray)] Guid[] pIIDExclude, IntPtr snbExclude, [In, MarshalAs(UnmanagedType.Interface)] IStorage stgDest);
void MoveElementTo([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.Interface)] IStorage stgDest, [In, MarshalAs(UnmanagedType.BStr)] string pwcsNewName, [In, MarshalAs(UnmanagedType.U4)] int grfFlags);
void Commit(int grfCommitFlags);
void Revert();
void EnumElements([In, MarshalAs(UnmanagedType.U4)] int reserved1, IntPtr reserved2, [In, MarshalAs(UnmanagedType.U4)] int reserved3, [MarshalAs(UnmanagedType.Interface)] out object ppVal);
void DestroyElement([In, MarshalAs(UnmanagedType.BStr)] string pwcsName);
void RenameElement([In, MarshalAs(UnmanagedType.BStr)] string pwcsOldName, [In, MarshalAs(UnmanagedType.BStr)] string pwcsNewName);
void SetElementTimes([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In] System.Runtime.InteropServices.ComTypes.FILETIME pctime, [In] System.Runtime.InteropServices.ComTypes.FILETIME patime, [In] System.Runtime.InteropServices.ComTypes.FILETIME pmtime);
void SetClass([In] ref Guid clsid);
void SetStateBits(int grfStateBits, int grfMask);
void Stat([Out]out System.Runtime.InteropServices.ComTypes.STATSTG pStatStg, int grfStatFlag);
}
[ComImport, Guid("0000000A-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ILockBytes
{
void ReadAt([In, MarshalAs(UnmanagedType.U8)] long ulOffset, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, [In, MarshalAs(UnmanagedType.U4)] int cb, [Out, MarshalAs(UnmanagedType.LPArray)] int[] pcbRead);
void WriteAt([In, MarshalAs(UnmanagedType.U8)] long ulOffset, IntPtr pv, [In, MarshalAs(UnmanagedType.U4)] int cb, [Out, MarshalAs(UnmanagedType.LPArray)] int[] pcbWritten);
void Flush();
void SetSize([In, MarshalAs(UnmanagedType.U8)] long cb);
void LockRegion([In, MarshalAs(UnmanagedType.U8)] long libOffset, [In, MarshalAs(UnmanagedType.U8)] long cb, [In, MarshalAs(UnmanagedType.U4)] int dwLockType);
void UnlockRegion([In, MarshalAs(UnmanagedType.U8)] long libOffset, [In, MarshalAs(UnmanagedType.U8)] long cb, [In, MarshalAs(UnmanagedType.U4)] int dwLockType);
void Stat([Out]out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, [In, MarshalAs(UnmanagedType.U4)] int grfStatFlag);
}
[StructLayout(LayoutKind.Sequential)]
public sealed class POINTL
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
public sealed class SIZEL
{
public int cx;
public int cy;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public sealed class FILEGROUPDESCRIPTORA
{
public uint cItems;
public FILEDESCRIPTORA[] fgd;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public sealed class FILEDESCRIPTORA
{
public uint dwFlags;
public Guid clsid;
public SIZEL sizel;
public POINTL pointl;
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public sealed class FILEGROUPDESCRIPTORW
{
public uint cItems;
public FILEDESCRIPTORW[] fgd;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public sealed class FILEDESCRIPTORW
{
public uint dwFlags;
public Guid clsid;
public SIZEL sizel;
public POINTL pointl;
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
}
}
#endregion
#region Property(s)
/// <summary>
/// Holds the <see cref="System.Windows.Forms.IDataObject"/> that this class is wrapping
/// </summary>
private System.Windows.Forms.IDataObject underlyingDataObject;
private System.Runtime.InteropServices.ComTypes.IDataObject comUnderlyingDataObject;
private System.Windows.Forms.IDataObject oleUnderlyingDataObject;
private MethodInfo getDataFromHGLOBLALMethod;
#endregion
#region Constructor(s)
public OutlookDataObject(System.Windows.Forms.IDataObject underlyingDataObject)
{
this.underlyingDataObject = underlyingDataObject;
this.comUnderlyingDataObject = (System.Runtime.InteropServices.ComTypes.IDataObject)this.underlyingDataObject;
FieldInfo innerDataField = this.underlyingDataObject.GetType().GetField("innerData", BindingFlags.NonPublic | BindingFlags.Instance);
this.oleUnderlyingDataObject = (System.Windows.Forms.IDataObject)innerDataField.GetValue(this.underlyingDataObject);
this.getDataFromHGLOBLALMethod = this.oleUnderlyingDataObject.GetType().GetMethod("GetDataFromHGLOBLAL", BindingFlags.NonPublic | BindingFlags.Instance);
}
#endregion
#region IDataObject Members
public object GetData(Type format)
{
return this.GetData(format.FullName);
}
public object GetData(string format)
{
return this.GetData(format, true);
}
public object GetData(string format, bool autoConvert)
{
switch(format)
{
case "FileGroupDescriptor":
IntPtr fileGroupDescriptorAPointer = IntPtr.Zero;
try
{
//use the underlying IDataObject to get the FileGroupDescriptor as a MemoryStream
MemoryStream fileGroupDescriptorStream = (MemoryStream)this.underlyingDataObject.GetData("FileGroupDescriptor", autoConvert);
byte[] fileGroupDescriptorBytes = new byte[fileGroupDescriptorStream.Length];
fileGroupDescriptorStream.Read(fileGroupDescriptorBytes, 0, fileGroupDescriptorBytes.Length);
fileGroupDescriptorStream.Close();
//copy the file group descriptor into unmanaged memory
fileGroupDescriptorAPointer = Marshal.AllocHGlobal(fileGroupDescriptorBytes.Length);
Marshal.Copy(fileGroupDescriptorBytes, 0, fileGroupDescriptorAPointer, fileGroupDescriptorBytes.Length);
//marshal the unmanaged memory to to FILEGROUPDESCRIPTORA struct
object fileGroupDescriptorObject = Marshal.PtrToStructure(fileGroupDescriptorAPointer, typeof(NativeMethods.FILEGROUPDESCRIPTORA));
NativeMethods.FILEGROUPDESCRIPTORA fileGroupDescriptor = (NativeMethods.FILEGROUPDESCRIPTORA)fileGroupDescriptorObject;
//create a new array to store file names in of the number of items in the file group descriptor
string[] fileNames = new string[fileGroupDescriptor.cItems];
//get the pointer to the first file descriptor
IntPtr fileDescriptorPointer = (IntPtr)((int)fileGroupDescriptorAPointer + Marshal.SizeOf(fileGroupDescriptor.cItems));
//loop for the number of files acording to the file group descriptor
for(int fileDescriptorIndex = 0;fileDescriptorIndex < fileGroupDescriptor.cItems;fileDescriptorIndex++)
{
//marshal the pointer top the file descriptor as a FILEDESCRIPTORA struct and get the file name
NativeMethods.FILEDESCRIPTORA fileDescriptor = (NativeMethods.FILEDESCRIPTORA)Marshal.PtrToStructure(fileDescriptorPointer, typeof(NativeMethods.FILEDESCRIPTORA));
fileNames[fileDescriptorIndex] = fileDescriptor.cFileName;
//move the file descriptor pointer to the next file descriptor
fileDescriptorPointer = (IntPtr)((int)fileDescriptorPointer + Marshal.SizeOf(fileDescriptor));
}
//return the array of filenames
return fileNames;
}
finally
{
//free unmanaged memory pointer
Marshal.FreeHGlobal(fileGroupDescriptorAPointer);
}
case "FileGroupDescriptorW":
//override the default handling of FileGroupDescriptorW which returns a
//MemoryStream and instead return a string array of file names
IntPtr fileGroupDescriptorWPointer = IntPtr.Zero;
try
{
//use the underlying IDataObject to get the FileGroupDescriptorW as a MemoryStream
MemoryStream fileGroupDescriptorStream = (MemoryStream)this.underlyingDataObject.GetData("FileGroupDescriptorW");
byte[] fileGroupDescriptorBytes = new byte[fileGroupDescriptorStream.Length];
fileGroupDescriptorStream.Read(fileGroupDescriptorBytes, 0, fileGroupDescriptorBytes.Length);
fileGroupDescriptorStream.Close();
//copy the file group descriptor into unmanaged memory
fileGroupDescriptorWPointer = Marshal.AllocHGlobal(fileGroupDescriptorBytes.Length);
Marshal.Copy(fileGroupDescriptorBytes, 0, fileGroupDescriptorWPointer, fileGroupDescriptorBytes.Length);
//marshal the unmanaged memory to to FILEGROUPDESCRIPTORW struct
object fileGroupDescriptorObject = Marshal.PtrToStructure(fileGroupDescriptorWPointer, typeof(NativeMethods.FILEGROUPDESCRIPTORW));
NativeMethods.FILEGROUPDESCRIPTORW fileGroupDescriptor = (NativeMethods.FILEGROUPDESCRIPTORW)fileGroupDescriptorObject;
//create a new array to store file names in of the number of items in the file group descriptor
string[] fileNames = new string[fileGroupDescriptor.cItems];
//get the pointer to the first file descriptor
//get the pointer to the first file descriptor
IntPtr fileDescriptorPointer = (IntPtr)((int)fileGroupDescriptorWPointer + Marshal.SizeOf(fileGroupDescriptor.cItems));
//loop for the number of files acording to the file group descriptor
for (int fileDescriptorIndex = 0; fileDescriptorIndex < fileGroupDescriptor.cItems; fileDescriptorIndex++)
{
//marshal the pointer top the file descriptor as a FILEDESCRIPTORW struct and get the file name
NativeMethods.FILEDESCRIPTORW fileDescriptor = (NativeMethods.FILEDESCRIPTORW)Marshal.PtrToStructure(fileDescriptorPointer, typeof(NativeMethods.FILEDESCRIPTORW));
fileNames[fileDescriptorIndex] = fileDescriptor.cFileName;
//move the file descriptor pointer to the next file descriptor
fileDescriptorPointer = (IntPtr)((int)fileDescriptorPointer + Marshal.SizeOf(fileDescriptor));
}
//return the array of filenames
return fileNames;
}
finally
{
//free unmanaged memory pointer
Marshal.FreeHGlobal(fileGroupDescriptorWPointer);
}
case "FileContents":
//override the default handling of FileContents which returns the
//contents of the first file as a memory stream and instead return
//a array of MemoryStreams containing the data to each file dropped
//get the array of filenames which lets us know how many file contents exist
string[] fileContentNames = (string[])this.GetData("FileGroupDescriptor");
//create a MemoryStream array to store the file contents
MemoryStream[] fileContents = new MemoryStream[fileContentNames.Length];
//loop for the number of files acording to the file names
for(int fileIndex = 0;fileIndex < fileContentNames.Length;fileIndex++)
{
//get the data at the file index and store in array
fileContents[fileIndex] = this.GetData(format, fileIndex);
}
//return array of MemoryStreams containing file contents
return fileContents;
}
//use underlying IDataObject to handle getting of data
return this.underlyingDataObject.GetData(format, autoConvert);
}
/// <summary>
/// Retrieves the data associated with the specified data format at the specified index.
/// </summary>
/// <param name="format">The format of the data to retrieve. See <see cref="T:System.Windows.Forms.DataFormats"></see> for predefined formats.</param>
/// <param name="index">The index of the data to retrieve.</param>
/// <returns>
/// A <see cref="MemoryStream"/> containing the raw data for the specified data format at the specified index.
/// </returns>
public MemoryStream GetData(string format, int index)
{
//create a FORMATETC struct to request the data with
FORMATETC formatetc = new FORMATETC();
formatetc.cfFormat = (short)DataFormats.GetFormat(format).Id;
formatetc.dwAspect = DVASPECT.DVASPECT_CONTENT;
formatetc.lindex = index;
formatetc.ptd = new IntPtr(0);
formatetc.tymed = TYMED.TYMED_ISTREAM | TYMED.TYMED_ISTORAGE | 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
this.comUnderlyingDataObject.GetData(ref formatetc, out medium);
//retrieve the data depending on the returned store type
switch(medium.tymed)
{
case TYMED.TYMED_ISTORAGE:
//to handle a IStorage it needs to be written into a second unmanaged
//memory mapped storage and then the data can be read from memory into
//a managed byte and returned as a MemoryStream
NativeMethods.IStorage iStorage = null;
NativeMethods.IStorage iStorage2 = null;
NativeMethods.ILockBytes iLockBytes = null;
System.Runtime.InteropServices.ComTypes.STATSTG iLockBytesStat;
try
{
//marshal the returned pointer to a IStorage object
iStorage = (NativeMethods.IStorage)Marshal.GetObjectForIUnknown(medium.unionmember);
Marshal.Release(medium.unionmember);
//create a ILockBytes (unmanaged byte array) and then create a IStorage using the byte array as a backing store
iLockBytes = NativeMethods.CreateILockBytesOnHGlobal(IntPtr.Zero, true);
iStorage2 = NativeMethods.StgCreateDocfileOnILockBytes(iLockBytes, 0x00001012, 0);
//copy the returned IStorage into the new IStorage
iStorage.CopyTo(0, null, IntPtr.Zero, iStorage2);
iLockBytes.Flush();
iStorage2.Commit(0);
//get the STATSTG of the ILockBytes to determine how many bytes were written to it
iLockBytesStat = new System.Runtime.InteropServices.ComTypes.STATSTG();
iLockBytes.Stat(out iLockBytesStat, 1);
int iLockBytesSize = (int)iLockBytesStat.cbSize;
//read the data from the ILockBytes (unmanaged byte array) into a managed byte array
byte[] iLockBytesContent = new byte[iLockBytesSize];
iLockBytes.ReadAt(0, iLockBytesContent, iLockBytesContent.Length, null);
//wrapped the managed byte array into a memory stream and return it
return new MemoryStream(iLockBytesContent);
}
finally
{
//release all unmanaged objects
Marshal.ReleaseComObject(iStorage2);
Marshal.ReleaseComObject(iLockBytes);
Marshal.ReleaseComObject(iStorage);
}
case TYMED.TYMED_ISTREAM:
//to handle a IStream it needs to be read into a managed byte and
//returned as a MemoryStream
IStream iStream = null;
System.Runtime.InteropServices.ComTypes.STATSTG iStreamStat;
try
{
//marshal the returned pointer to a IStream object
iStream = (IStream)Marshal.GetObjectForIUnknown(medium.unionmember);
Marshal.Release(medium.unionmember);
//get the STATSTG of the IStream to determine how many bytes are in it
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 and return it
return new MemoryStream(iStreamContent);
}
finally
{
//release all unmanaged objects
Marshal.ReleaseComObject(iStream);
}
case TYMED.TYMED_HGLOBAL:
//to handle a HGlobal the exisitng "GetDataFromHGLOBLAL" method is invoked via
//reflection
return (MemoryStream)this.getDataFromHGLOBLALMethod.Invoke(this.oleUnderlyingDataObject, new object[] { DataFormats.GetFormat((short)formatetc.cfFormat).Name, medium.unionmember });
}
return null;
}
/// <summary>
/// Determines whether data stored in this instance is associated with, or can be converted to, the specified format.
/// </summary>
/// <param name="format">A <see cref="T:System.Type"></see> representing the format for which to check. See <see cref="T:System.Windows.Forms.DataFormats"></see> for predefined formats.</param>
/// <returns>
/// true if data stored in this instance is associated with, or can be converted to, the specified format; otherwise, false.
/// </returns>
public bool GetDataPresent(Type format)
{
return this.underlyingDataObject.GetDataPresent(format);
}
/// <summary>
/// Determines whether data stored in this instance is associated with, or can be converted to, the specified format.
/// </summary>
/// <param name="format">The format for which to check. See <see cref="T:System.Windows.Forms.DataFormats"></see> for predefined formats.</param>
/// <returns>
/// true if data stored in this instance is associated with, or can be converted to, the specified format; otherwise false.
/// </returns>
public bool GetDataPresent(string format)
{
return this.underlyingDataObject.GetDataPresent(format);
}
/// <summary>
/// Determines whether data stored in this instance is associated with the specified format, using a Boolean value to determine whether to convert the data to the format.
/// </summary>
/// <param name="format">The format for which to check. See <see cref="T:System.Windows.Forms.DataFormats"></see> for predefined formats.</param>
/// <param name="autoConvert">true to determine whether data stored in this instance can be converted to the specified format; false to check whether the data is in the specified format.</param>
/// <returns>
/// true if the data is in, or can be converted to, the specified format; otherwise, false.
/// </returns>
public bool GetDataPresent(string format, bool autoConvert)
{
return this.underlyingDataObject.GetDataPresent(format, autoConvert);
}
/// <summary>
/// Returns a list of all formats that data stored in this instance is associated with or can be converted to.
/// </summary>
/// <returns>
/// An array of the names that represents a list of all formats that are supported by the data stored in this object.
/// </returns>
public string[] GetFormats()
{
return this.underlyingDataObject.GetFormats();
}
public string[] GetFormats(bool autoConvert)
{
return this.underlyingDataObject.GetFormats(autoConvert);
}
/// <summary>
/// Stores the specified data in this instance, using the class of the data for the format.
/// </summary>
/// <param name="data">The data to store.</param>
public void SetData(object data)
{
this.underlyingDataObject.SetData(data);
}
/// <summary>
/// Stores the specified data and its associated class type in this instance.
/// </summary>
/// <param name="format">A <see cref="T:System.Type"></see> representing the format associated with the data. See <see cref="T:System.Windows.Forms.DataFormats"></see> for predefined formats.</param>
/// <param name="data">The data to store.</param>
public void SetData(Type format, object data)
{
this.underlyingDataObject.SetData(format, data);
}
/// <summary>
/// Stores the specified data and its associated format in this instance.
/// </summary>
/// <param name="format">The format associated with the data. See <see cref="T:System.Windows.Forms.DataFormats"></see> for predefined formats.</param>
/// <param name="data">The data to store.</param>
public void SetData(string format, object data)
{
this.underlyingDataObject.SetData(format, data);
}
/// <summary>
/// Stores the specified data and its associated format in this instance, using a Boolean value to specify whether the data can be converted to another format.
/// </summary>
/// <param name="format">The format associated with the data. See <see cref="T:System.Windows.Forms.DataFormats"></see> for predefined formats.</param>
/// <param name="autoConvert">true to allow the data to be converted to another format; otherwise, false.</param>
/// <param name="data">The data to store.</param>
public void SetData(string format, bool autoConvert, object data)
{
this.underlyingDataObject.SetData(format, autoConvert, data);
}
#endregion
}
}
And write drop function like that
private void FormRegion2_DragDrop_1(object sender, DragEventArgs e)
{
string[] fileNames = null;
if (e.Data.GetDataPresent(DataFormats.FileDrop, false) == true)
{
fileNames = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (string fileName in fileNames)
{
// do what you are going to do with each filename
}
}
else if (e.Data.GetDataPresent("FileGroupDescriptor"))
{
OutlookDataObject dataObject = new OutlookDataObject(e.Data);
string[] filenames = (string[])dataObject.GetData("FileGroupDescriptor");
for (int fileIndex = 0; fileIndex < filenames.Length; fileIndex++)
{
//Write your logic here u get multiple file name here
}
}
}
Solution 2
There is a bug in the answer by V_B. It is in the section:
case "FileGroupDescriptorW":
The original implementation below to determine the number of items in the array is a bit wonky, besides it would throw Memory corruption exeptions when we would drag files over an infragistics treeview on win 8.
Searching for "DRAG DROP WIN8 FILEGROUPDESCRIPTORW write protected memory" led me to a posting on
That explained that it's a problem with the marshaller and they suggested to use Marshal.ReadInt32 and then 'increase the pointer' and 'read each within a loop'
Seeing as that is what exactly is being done in the code below (which I did not write, ), I think that this was the authors intention all along. The site teases us with the promise of more info with another dead link.
I tried to edit the comment, but it was rejected by peer review. I guess working software is not important.
replace
object fileGroupDescriptorObject = Marshal.PtrToStructure(fileGroupDescriptorWPointer, typeof(NativeMethods.FILEGROUPDESCRIPTORW));
NativeMethods.FILEGROUPDESCRIPTORW fgd = (NativeMethods.FILEGROUPDESCRIPTORW)fileGroupDescriptorObject;
files = new NativeMethods.FILEDESCRIPTOR[fgd.cItems];
pdata = (IntPtr)((int)pdata + Marshal.SizeOf(pdata));
for (int index = 0; index < fgd.cItems; index++)
with
int ITEMCOUNT = Marshal.ReadInt32(pdata);
files = new NativeMethods.FILEDESCRIPTOR[ITEMCOUNT];
// Set our pointer offset to the beginning of the FILEDESCRIPTOR* array
pdata = (IntPtr)((long)pdata + Marshal.SizeOf(pdata));
// Walk the array, converting each FILEDESCRIPTOR* to a FILEDESCRIPTOR
for (int index = 0; index < ITEMCOUNT; index++)
![V_B](https://i.stack.imgur.com/mdKgk.png?s=256&g=1)
Comments
-
V_B about 2 years
I Use following code for single file drag and drop.
private void FormRegion2_DragEnter_1(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) { e.Effect = DragDropEffects.Copy; } // or this tells us if it is an Outlook attachment drop else if (e.Data.GetDataPresent("FileGroupDescriptor")) { e.Effect = DragDropEffects.Copy; } // or none of the above else { e.Effect = DragDropEffects.None; } } private void FormRegion2_DragDrop_1(object sender, DragEventArgs e) { string[] fileNames = null; if (e.Data.GetDataPresent(DataFormats.FileDrop, false) == true) { fileNames = (string[])e.Data.GetData(DataFormats.FileDrop); foreach (string fileName in fileNames) { } } else if (e.Data.GetDataPresent("FileGroupDescriptor")) { object s = e.Data.GetData("FileGroupDescriptor"); Stream theStream = (Stream)e.Data.GetData("FileGroupDescriptor"); byte[] fileGroupDescriptor = new byte[512]; theStream.Read(fileGroupDescriptor, 0, 512); StringBuilder fileName = new StringBuilder(""); for (int i = 76; fileGroupDescriptor[i] != 0; i++) { fileName.Append(Convert.ToChar(fileGroupDescriptor[i])); } string theFile = fileName.ToString(); String fileName1 = System.IO.Path.GetFileName(theFile); } }
My problem is i am not able to get multiple file name. How it is possible to get multiple file name
Thx
-
Tim Post over 12 yearsIs all that code really needed (to illustrate your solution)?
-
V_B over 12 yearsyes All the code I write for both FileGroupDescriptor and FileGroupDescriptorW
-
Cody Gray over 12 yearsI'm reasonably sure that all this code is not necessary. You don't need to P/Invoke that many functions; several of them have managed wrappers. I also doubt that you wrote all of this code yourself. It looks like something you copied from someone else. You should give credit where credit is due.
-
toong over 12 yearsLooks partly copied from codeproject.com/Articles/28209/Outlook-Drag-and-Drop-in-C ? I would be interested to know how to use TYMED.TYMED_ISTORAGE and wrap it in a stream, instead of copying this into a byte-buffer.
-
Sebastian about 11 years@CodyGray, if all that code is not necessary, do you have any alternative? thx
-
pquest about 9 yearsYou didnt happen to find that the file name was missing its first couple characters when you were doing this, did you?
-
Christian about 9 yearsUnfortunately, this code doesn't make much sense. What's the point of reading an integer (into
ITEMCOUNT
) and then modifyingpdata
for no reason? Also, none of the code is inside the loop, which isn't what that link suggested. -
Brain2000 almost 8 yearsI think the most important thing you did here was change the (int) to a (long) so 64 bit memory addresses would be handed properly in 64 bit office environments. There are four places that this needs to be fixed, two in the fileGroupDescriptorWPointer and two in the fileGroupDescriptorAPointer case statements.
-
Brian Rice almost 8 yearsI changed all (IntPtr)((int) to (IntPtr)((long) and it fixed crash problems I was having.
-
Brian Rice almost 8 yearsI had to change all (IntPtr)((int) to (IntPtr)((long) to prevent crash problems I was having.
-
Kabwla-TwoLips almost 8 yearsthe code was originally written but not supported by a MS employee (years and years ago) and it has been copied many times over the internet, with all its flaws. The code breaks just about anytime MS comes up with a new office version or when when we moved to 64 bit. I do not remember the name of the original author, sounded Spanish, I think.
-
Kabwla-TwoLips almost 8 years@pquest, odd, the code seems to be working for us and the few 100 users we have.
-
Kabwla-TwoLips almost 8 years@christian, ITEMCOUNT is just to initialize the array before we start looping in the last line here. I only wrote down the change. The original pdata points to the start of the array which says.. this is an array, I am this large. After the modification it points to the elements in the array. At the end of the loop you can tell that we are moving pdata to the next element.
-
Kabwla-TwoLips almost 8 years@Brain2000, true, but on some machines the first line would fail too. And it is creating an array just to know its size, not needed.
-
Kabwla-TwoLips almost 8 years@CodyGray, You are correct. The code is originally written by Fabio Poroli
-
Michael Würthner over 7 yearsAfter having changed all (IntPtr)((int) to (IntPtr)((long) I seemingly had to subtract 4 from fileDescriptorPointer to get a filename not having cut off the frist 4 characters. This is not a change making me feel very comfortable. Is there a better way to solve the problem?
-
Melvyn over 6 yearsYou have to substract 4 because there is an error in the fileDescriptorPointer definition: it should be (IntPtr)((int)fileGroupDescriptorAPointer + Marshal.SizeOf(fileGroupDescriptor.cItems)) (and not + fileGroupDescriptorAPointer). See the comments in the code project article: codeproject.com/Articles/28209/…
-
David Brunelle over 3 yearsIs this still valid ? It seems with either office 365 or win10, I get an error when using Marshall.PtrToStructure now. This was not the case before.