Created
June 27, 2015 14:21
-
-
Save sayurin/60e493429853dce46691 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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