Skip to content

Instantly share code, notes, and snippets.

@sayurin
Created June 27, 2015 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sayurin/60e493429853dce46691 to your computer and use it in GitHub Desktop.
Save sayurin/60e493429853dce46691 to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;
using Sayuri.IO;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
using IDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;
namespace Sayuri.Windows{
static class UnsafeNativeMethods{
[DllImport("Kernel32.dll", SetLastError=true)]
public static extern IntPtr GlobalAlloc( int uFlags, int dwBytes );
[DllImport("Kernel32.dll")]
public static extern IntPtr GlobalFree( IntPtr hMem );
[DllImport("Kernel32.dll", SetLastError=true)]
public static extern IntPtr GlobalLock( IntPtr hMem );
[DllImport("Kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GlobalUnlock( IntPtr hMem );
}
static class HResults{
public const int S_OK = 0;
public const int S_FALSE = 1;
public const int E_UNEXPECTED = unchecked((int)0x8000FFFF);
public const int OLE_E_ADVISENOTSUPPORTED = unchecked((int)0x80040003);
public const int DV_E_FORMATETC = unchecked((int)0x80040064);
public const int DV_E_TYMED = unchecked((int)0x80040069);
}
[StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Unicode)]
struct FILEDESCRIPTORW{
public int dwFlags;
public Guid clsid;
public long sizel;
public long pointl;
public int dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public int nFileSizeHigh;
public int nFileSizeLow;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)]
public string cFileName;
}
[StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Unicode)]
struct FILEGROUPDESCRIPTORW{
public int cItems;
[MarshalAs(UnmanagedType.ByValArray)]
public FILEDESCRIPTORW[] fgd;
}
public sealed class FileDataObject: IDataObject, IEnumFORMATETC{
public FileDataObject( IFile[] files ){
this.files = files;
}
static readonly DataFormats.Format CFSTR_FILEDESCRIPTORW = DataFormats.GetFormat( "FileGroupDescriptorW" );
static readonly DataFormats.Format CFSTR_FILECONTENTS = DataFormats.GetFormat( "FileContents" );
int current;
readonly IFile[] files;
void GetFileDescriptor( out STGMEDIUM medium ){
Debug.WriteLine( "GetFileContents()" );
IntPtr hFileDescriptor = UnsafeNativeMethods.GlobalAlloc( 0x0002|0x0040/*GHND*/, sizeof(int) + Marshal.SizeOf( typeof(FILEDESCRIPTORW) ) * files.Length );
if( hFileDescriptor == IntPtr.Zero )
throw Marshal.GetExceptionForHR( Marshal.GetHRForLastWin32Error() );
IntPtr pGroupDescriptor = UnsafeNativeMethods.GlobalLock( hFileDescriptor );
if( pGroupDescriptor == IntPtr.Zero ){
UnsafeNativeMethods.GlobalFree( hFileDescriptor );
throw Marshal.GetExceptionForHR( Marshal.GetHRForLastWin32Error() );
}
FILEGROUPDESCRIPTORW fgd = new FILEGROUPDESCRIPTORW{
cItems = files.Length,
fgd = ( from file in files
select new FILEDESCRIPTORW{
dwFlags = 0x0040/*FD_FILESIZE*/|0x0004/*FD_ATTRIBUTES*/,
nFileSizeLow = file.Length,
nFileSizeHigh = 0,
dwFileAttributes = 0x00000080/*FILE_ATTRIBUTE_NORMAL*/,
cFileName = Path.GetFileName( file.Path ),
} ).ToArray()
};
Marshal.StructureToPtr( fgd, pGroupDescriptor, false );
UnsafeNativeMethods.GlobalUnlock( hFileDescriptor );
medium.tymed = TYMED.TYMED_HGLOBAL;
medium.unionmember = hFileDescriptor;
medium.pUnkForRelease = null;
}
void GetFileContents( FORMATETC format, out STGMEDIUM medium ){
Debug.WriteLine( string.Format( "GetFileContents({0},{1})", format.tymed, format.lindex ) );
// TODO: サイズが大きい場合、HGLOBALに押し込むのは無理がある
if( (format.tymed & TYMED.TYMED_HGLOBAL) != TYMED.TYMED_HGLOBAL )
throw Marshal.GetExceptionForHR( HResults.DV_E_TYMED );
IntPtr hFileContents = UnsafeNativeMethods.GlobalAlloc( 0x0002/*GMEM_MOVEABLE*/, files[ format.lindex ].Length );
if( hFileContents == IntPtr.Zero )
throw Marshal.GetExceptionForHR( Marshal.GetHRForLastWin32Error() );
IntPtr pFileContents = UnsafeNativeMethods.GlobalLock( hFileContents );
if( pFileContents == IntPtr.Zero ){
UnsafeNativeMethods.GlobalFree( hFileContents );
throw Marshal.GetExceptionForHR( Marshal.GetHRForLastWin32Error() );
}
Marshal.Copy( files[ format.lindex ].GetBytes(), 0, pFileContents, files[ format.lindex ].Length );
medium.tymed = TYMED.TYMED_HGLOBAL;
medium.unionmember = hFileContents;
medium.pUnkForRelease = null;
}
int IDataObject.DAdvise( ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection ){
throw Marshal.GetExceptionForHR( HResults.OLE_E_ADVISENOTSUPPORTED );
}
void IDataObject.DUnadvise( int connection ){
throw Marshal.GetExceptionForHR( HResults.OLE_E_ADVISENOTSUPPORTED );
}
int IDataObject.EnumDAdvise( out IEnumSTATDATA enumAdvise ){
throw Marshal.GetExceptionForHR( HResults.OLE_E_ADVISENOTSUPPORTED );
}
IEnumFORMATETC IDataObject.EnumFormatEtc( DATADIR direction ){
Debug.WriteLine( string.Format( "IDataObject.EnumFormatEtc({0})", direction ) );
if( direction != DATADIR.DATADIR_GET )
throw new NotImplementedException();
return this;
}
int IDataObject.GetCanonicalFormatEtc( ref FORMATETC formatIn, out FORMATETC formatOut ){
Debug.WriteLine( string.Format( "IDataObject.GetCanonicalFormatEtc({0})", formatIn.cfFormat ) );
throw Marshal.GetExceptionForHR( HResults.E_UNEXPECTED );
}
void IDataObject.GetData( ref FORMATETC format, out STGMEDIUM medium ){
Debug.WriteLine( string.Format( "IDataObject.GetData({0})", format.cfFormat ) );
if( format.dwAspect == DVASPECT.DVASPECT_CONTENT && format.ptd == IntPtr.Zero )
if( format.cfFormat == unchecked( (short)CFSTR_FILEDESCRIPTORW.Id ) ){
if( (format.tymed & TYMED.TYMED_HGLOBAL) != TYMED.TYMED_NULL )
if( format.lindex == 0 || format.lindex == -1 ){
GetFileDescriptor( out medium );
return;
}
}else if( format.cfFormat == unchecked( (short)CFSTR_FILECONTENTS.Id ) )
if( (format.tymed & (TYMED.TYMED_HGLOBAL|TYMED.TYMED_ISTREAM)) != TYMED.TYMED_NULL ){
GetFileContents( format, out medium );
return;
}
throw Marshal.GetExceptionForHR( HResults.DV_E_FORMATETC );
}
void IDataObject.GetDataHere( ref FORMATETC format, ref STGMEDIUM medium ){
Debug.WriteLine( string.Format( "IDataObject.GetDataHere({0})", format.cfFormat ) );
throw Marshal.GetExceptionForHR( HResults.E_UNEXPECTED );
}
int IDataObject.QueryGetData( ref FORMATETC format ){
Debug.WriteLine( string.Format( "IDataObject.QueryGetData({0})", format.cfFormat ) );
int result = HResults.DV_E_FORMATETC;
if( format.dwAspect == DVASPECT.DVASPECT_CONTENT && format.ptd == IntPtr.Zero )
if( format.cfFormat == unchecked( (short)CFSTR_FILEDESCRIPTORW.Id ) ){
if( (format.tymed & TYMED.TYMED_HGLOBAL) != TYMED.TYMED_NULL )
if( format.lindex == 0 || format.lindex == -1 )
result = HResults.S_OK;
}else if( format.cfFormat == unchecked( (short)CFSTR_FILECONTENTS.Id ) )
if( (format.tymed & (TYMED.TYMED_HGLOBAL|TYMED.TYMED_ISTREAM)) != TYMED.TYMED_NULL )
result = HResults.S_OK;
Debug.WriteLine( string.Format( " result: {0}", result ) );
return result;
}
void IDataObject.SetData( ref FORMATETC formatIn, ref STGMEDIUM medium, bool release ){
Debug.WriteLine( string.Format( "IDataObject.SetData({0})", formatIn.cfFormat ) );
throw new NotImplementedException();
}
void IEnumFORMATETC.Clone( out IEnumFORMATETC newEnum ){
Debug.WriteLine( "IEnumFORMATETC.Clone(): not impremented." );
throw Marshal.GetExceptionForHR( HResults.E_UNEXPECTED );
}
int IEnumFORMATETC.Next( int celt, FORMATETC[] rgelt, int[] pceltFetched ){
Debug.WriteLine( string.Format( "IEnumFORMATETC0.Next({0}): {1} read.", celt, current ) );
int i = 0;
if( current == 0 && celt > 0 ){
rgelt[i].cfFormat = unchecked( (short)CFSTR_FILEDESCRIPTORW.Id );
rgelt[i].ptd = IntPtr.Zero;
rgelt[i].dwAspect = DVASPECT.DVASPECT_CONTENT;
rgelt[i].lindex = -1;
rgelt[i].tymed = TYMED.TYMED_HGLOBAL;
i++;
current++;
celt--;
}
if( current == 1 && celt > 0 ){
rgelt[i].cfFormat = unchecked( (short)CFSTR_FILECONTENTS.Id );
rgelt[i].ptd = IntPtr.Zero;
rgelt[i].dwAspect = DVASPECT.DVASPECT_CONTENT;
rgelt[i].lindex = -1;
rgelt[i].tymed = TYMED.TYMED_HGLOBAL|TYMED.TYMED_ISTREAM;
i++;
current++;
celt--;
}
if( i == 0 )
return HResults.S_FALSE;
// celt=1のときNULLが許されるため、チェックが必要
if( pceltFetched != null )
pceltFetched[0] = i;
return HResults.S_OK;
}
int IEnumFORMATETC.Reset(){
Debug.WriteLine( "IEnumFORMATETC.Reset()" );
current = 0;
return HResults.S_OK;
}
int IEnumFORMATETC.Skip( int celt ){
Debug.WriteLine( string.Format( "IEnumFORMATETC.Skip({0})", celt ) );
if( current + celt < 2 ){
current += celt;
return HResults.S_OK;
}
return HResults.S_FALSE;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment