/***************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
This code is licensed under the Visual Studio SDK license terms.
THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
***************************************************************************/
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections;
using System.Collections.Generic;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.OLE.Interop;
using System.Security.Permissions;
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.VisualStudio.Package{
internal enum tagDVASPECT
{
DVASPECT_CONTENT = 1,
DVASPECT_THUMBNAIL = 2,
DVASPECT_ICON = 4,
DVASPECT_DOCPRINT = 8
}
internal enum tagTYMED
{
TYMED_HGLOBAL = 1,
TYMED_FILE = 2,
TYMED_ISTREAM = 4,
TYMED_ISTORAGE = 8,
TYMED_GDI = 16,
TYMED_MFPICT = 32,
TYMED_ENHMF = 64,
TYMED_NULL = 0
}
internal sealed class DataCacheEntry : IDisposable
{
#region fields
/// <summary>
/// Defines an object that will be a mutex for this object for synchronizing thread calls.
/// </summary>
private static volatile object Mutex = new object();
private FORMATETC format;
private long data;
private DATADIR dataDir;
private bool isDisposed;
#endregion
#region properties
internal FORMATETC Format
{
get
{
return this.format;
}
}
internal long Data
{
get
{
return this.data;
}
}
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal DATADIR DataDir
{
get
{
return this.dataDir;
}
}
#endregion
/// <summary>
/// The IntPtr is data allocated that should be removed. It is allocated by the ProcessSelectionData method.
/// </summary>
internal DataCacheEntry(FORMATETC fmt, IntPtr data, DATADIR dir)
{
this.format = fmt;
this.data = (long)data;
this.dataDir = dir;
}
#region Dispose
~DataCacheEntry()
{
Dispose(false);
}
/// <summary>
/// The IDispose interface Dispose method for disposing the object determinastically.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// The method that does the cleanup.
/// </summary>
/// <param name="disposing"></param>
private void Dispose(bool disposing)
{
// Everybody can go here.
if (!this.isDisposed)
{
// Synchronize calls to the Dispose simulteniously.
lock (Mutex)
{
if (this.data != 0)
{
Marshal.FreeHGlobal((IntPtr)this.data);
this.data = 0;
}
this.isDisposed = true;
}
}
}
#endregion
}
/// <summary>
/// Unfortunately System.Windows.Forms.IDataObject and
/// Microsoft.VisualStudio.OLE.Interop.IDataObject are different...
/// </summary>
internal sealed class DataObject : IDataObject
{
#region fields
internal const int DATA_S_SAMEFORMATETC = 0x00040130;
internal static readonly int DATA_E_FORMATETC = ForceCast(0x80040064);
EventSinkCollection map;
ArrayList entries;
#endregion
internal DataObject()
{
this.map = new EventSinkCollection();
this.entries = new ArrayList();
}
internal void SetData(FORMATETC format, IntPtr data)
{
this.entries.Add(new DataCacheEntry(format, data, DATADIR.DATADIR_SET));
}
#region IDataObject methods
int IDataObject.DAdvise(FORMATETC[] e, uint adv, IAdviseSink sink, out uint cookie)
{
STATDATA sdata = new STATDATA();
sdata.ADVF = adv;
sdata.FORMATETC = e[0];
sdata.pAdvSink = sink;
cookie = this.map.Add(sdata);
sdata.dwConnection = cookie;
return 0;
}
void IDataObject.DUnadvise(uint cookie)
{
this.map.RemoveAt(cookie);
}
int IDataObject.EnumDAdvise(out IEnumSTATDATA e)
{
e = new EnumSTATDATA((IEnumerable)this.map);
return 0; //??
}
int IDataObject.EnumFormatEtc(uint direction, out IEnumFORMATETC penum)
{
penum = new EnumFORMATETC((DATADIR)direction, (IEnumerable)this.entries);
return 0;
}
int IDataObject.GetCanonicalFormatEtc(FORMATETC[] format, FORMATETC[] fmt)
{
throw new System.Runtime.InteropServices.COMException("", DATA_S_SAMEFORMATETC);
}
void IDataObject.GetData(FORMATETC[] fmt, STGMEDIUM[] m)
{
STGMEDIUM retMedium = new STGMEDIUM();
if (fmt == null || fmt.Length < 1)
return;
foreach (DataCacheEntry e in this.entries)
{
if (e.Format.cfFormat == fmt[0].cfFormat /*|| fmt[0].cfFormat == InternalNativeMethods.CF_HDROP*/)
{
retMedium.tymed = e.Format.tymed;
// Caller must delete the memory.
retMedium.unionmember = DragDropHelper.CopyHGlobal(new IntPtr(e.Data));
break;
}
}
if (m != null && m.Length > 0)
m[0] = retMedium;
}
void IDataObject.GetDataHere(FORMATETC[] fmt, STGMEDIUM[] m)
{
}
int IDataObject.QueryGetData(FORMATETC[] fmt)
{
if (fmt == null || fmt.Length < 1)
return VSConstants.S_FALSE;
foreach (DataCacheEntry e in this.entries)
{
if (e.Format.cfFormat == fmt[0].cfFormat /*|| fmt[0].cfFormat == InternalNativeMethods.CF_HDROP*/)
return VSConstants.S_OK;
}
return VSConstants.S_FALSE;
}
void IDataObject.SetData(FORMATETC[] fmt, STGMEDIUM[] m, int fRelease)
{
}
#endregion
#region static methods
internal static int ForceCast(uint i)
{
unchecked { return (int)i; }
}
internal static uint ForceCast(int i)
{
unchecked { return (uint)i; }
}
#endregion
}
[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
internal static class DragDropHelper
{
#pragma warning disable 414
internal static readonly ushort CF_VSREFPROJECTS = 0;
internal static readonly ushort CF_VSSTGPROJECTS = 0;
internal static readonly ushort CF_VSREFPROJECTITEMS = 0;
internal static readonly ushort CF_VSSTGPROJECTITEMS = 0;
internal static readonly ushort CF_PROJECTCLIPDESCRIPTOR = 0;
internal static readonly ushort CF_VSPROJECTCLIPDESCRIPTOR = 0;
#pragma warning restore 414
static DragDropHelper()
{
CF_VSREFPROJECTS = UnsafeNativeMethods.RegisterClipboardFormat("CF_VSREFPROJECTS");
CF_VSSTGPROJECTS = UnsafeNativeMethods.RegisterClipboardFormat("CF_VSSTGPROJECTS");
CF_VSREFPROJECTITEMS = UnsafeNativeMethods.RegisterClipboardFormat("CF_VSREFPROJECTITEMS");
CF_VSSTGPROJECTITEMS = UnsafeNativeMethods.RegisterClipboardFormat("CF_VSSTGPROJECTITEMS");
CF_VSPROJECTCLIPDESCRIPTOR = UnsafeNativeMethods.RegisterClipboardFormat("CF_PROJECTCLIPBOARDDESCRIPTOR");
}
public static FORMATETC CreateFormatEtc(ushort iFormat)
{
FORMATETC fmt = new FORMATETC();
fmt.cfFormat = iFormat;
fmt.ptd = IntPtr.Zero;
fmt.dwAspect = (uint)DVASPECT.DVASPECT_CONTENT;
fmt.lindex = -1;
fmt.tymed = (uint)TYMED.TYMED_HGLOBAL;
return fmt;
}
public static FORMATETC CreateFormatEtc()
{
return CreateFormatEtc(CF_VSSTGPROJECTITEMS);
}
public static int QueryGetData(Microsoft.VisualStudio.OLE.Interop.IDataObject pDataObject, ref FORMATETC fmtetc)
{
int returnValue = VSConstants.E_FAIL;
FORMATETC[] af = new FORMATETC[1];
af[0] = fmtetc;
try
{
int result = ErrorHandler.ThrowOnFailure(pDataObject.QueryGetData(af));
if (result == VSConstants.S_OK)
{
fmtetc = af[0];
returnValue = VSConstants.S_OK;
}
}
catch (COMException e)
{
Trace.WriteLine("COMException : " + e.Message);
returnValue = e.ErrorCode;
}
return returnValue;
}
public static STGMEDIUM GetData(Microsoft.VisualStudio.OLE.Interop.IDataObject pDataObject, ref FORMATETC fmtetc)
{
FORMATETC[] af = new FORMATETC[1];
af[0] = fmtetc;
STGMEDIUM[] sm = new STGMEDIUM[1];
pDataObject.GetData(af, sm);
fmtetc = af[0];
return sm[0];
}
/// <summary>
/// Retrives data from a VS format.
/// </summary>
public static List<string> GetDroppedFiles(ushort format, Microsoft.VisualStudio.OLE.Interop.IDataObject dataObject, out DropDataType ddt)
{
ddt = DropDataType.None;
List<string> droppedFiles = new List<string>();
// try HDROP
FORMATETC fmtetc = CreateFormatEtc(format);
if (QueryGetData(dataObject, ref fmtetc) == VSConstants.S_OK)
{
STGMEDIUM stgmedium = DragDropHelper.GetData(dataObject, ref fmtetc);
if (stgmedium.tymed == (uint)TYMED.TYMED_HGLOBAL)
{
// We are releasing the cloned hglobal here.
IntPtr dropInfoHandle = stgmedium.unionmember;
if (dropInfoHandle != IntPtr.Zero)
{
ddt = DropDataType.Shell;
try
{
uint numFiles = UnsafeNativeMethods.DragQueryFile(dropInfoHandle, 0xFFFFFFFF, null, 0);
// We are a directory based project thus a projref string is placed on the clipboard.
// We assign the maximum length of a projref string.
// The format of a projref is : <Proj Guid>|<project rel path>|<file path>
uint lenght = (uint)Guid.Empty.ToString().Length + 2 * NativeMethods.MAX_PATH + 2;
char[] moniker = new char[lenght + 1];
for (uint fileIndex = 0; fileIndex < numFiles; fileIndex++)
{
uint queryFileLength = UnsafeNativeMethods.DragQueryFile(dropInfoHandle, fileIndex, moniker, lenght);
string filename = new String(moniker, 0, (int)queryFileLength);
droppedFiles.Add(filename);
}
}
finally
{
Marshal.FreeHGlobal(dropInfoHandle);
}
}
}
}
return droppedFiles;
}
public static string GetSourceProjectPath(Microsoft.VisualStudio.OLE.Interop.IDataObject dataObject)
{
string projectPath = null;
FORMATETC fmtetc = CreateFormatEtc(CF_VSPROJECTCLIPDESCRIPTOR);
if (QueryGetData(dataObject, ref fmtetc) == VSConstants.S_OK)
{
STGMEDIUM stgmedium = DragDropHelper.GetData(dataObject, ref fmtetc);
if (stgmedium.tymed == (uint)TYMED.TYMED_HGLOBAL)
{
// We are releasing the cloned hglobal here.
IntPtr dropInfoHandle = stgmedium.unionmember;
if (dropInfoHandle != IntPtr.Zero)
{
try
{
string path = GetData(dropInfoHandle);
// Clone the path that we can release our memory.
if (!String.IsNullOrEmpty(path))
{
projectPath = String.Copy(path);
}
}
finally
{
Marshal.FreeHGlobal(dropInfoHandle);
}
}
}
}
return projectPath;
}
/// <summary>
/// Returns the data packed after the DROPFILES structure.
/// </summary>
/// <param name="dropHandle"></param>
/// <returns></returns>
internal static string GetData(IntPtr dropHandle)
{
IntPtr data = UnsafeNativeMethods.GlobalLock(dropHandle);
try
{
_DROPFILES df = (_DROPFILES)Marshal.PtrToStructure(data, typeof(_DROPFILES));
if (df.fWide != 0)
{
IntPtr pdata = new IntPtr((long)data + df.pFiles);
return Marshal.PtrToStringUni(pdata);
}
}
finally
{
if (data != null)
{
UnsafeNativeMethods.GlobalUnLock(data);
}
}
return null;
}
internal static void FillFormatEtc(ref FORMATETC template, ushort clipFormat, ref FORMATETC result)
{
if (clipFormat != 0)
{
result = template;
result.cfFormat = clipFormat;
result.ptd = IntPtr.Zero;
result.dwAspect = (uint)DVASPECT.DVASPECT_CONTENT;
result.lindex = -1;
result.tymed = (uint)TYMED.TYMED_NULL;
}
}
internal static void OleCopyFormatEtc(ref FORMATETC src, ref FORMATETC dest)
{
dest.cfFormat = src.cfFormat;
dest.ptd = Marshal.AllocCoTaskMem(Marshal.SizeOf(src.ptd));
Marshal.StructureToPtr(src.ptd, dest.ptd, false);
dest.dwAspect = src.dwAspect;
dest.lindex = src.lindex;
dest.tymed = src.tymed;
}
internal static IntPtr CopyHGlobal(IntPtr data)
{
IntPtr src = UnsafeNativeMethods.GlobalLock(data);
int size = UnsafeNativeMethods.GlobalSize(data);
IntPtr ptr = Marshal.AllocHGlobal(size);
IntPtr buffer = UnsafeNativeMethods.GlobalLock(ptr);
try
{
for (int i = 0; i < size; i++)
{
byte val = Marshal.ReadByte(new IntPtr((long)src + i));
Marshal.WriteByte(new IntPtr((long)buffer + i), val);
}
}
finally
{
if (buffer != IntPtr.Zero)
{
UnsafeNativeMethods.GlobalUnLock(buffer);
}
if (src != IntPtr.Zero)
{
UnsafeNativeMethods.GlobalUnLock(src);
}
}
return ptr;
}
internal static void CopyStringToHGlobal(string s, IntPtr data, int bufferSize)
{
Int16 nullTerminator = 0;
int dwSize = Marshal.SizeOf(nullTerminator);
if ((s.Length + 1) * Marshal.SizeOf(s[0]) > bufferSize)
throw new System.IO.InternalBufferOverflowException();
// IntPtr memory already locked...
for (int i = 0, len = s.Length; i < len; i++)
{
Marshal.WriteInt16(data, i * dwSize, s[i]);
}
// NULL terminate it
Marshal.WriteInt16(new IntPtr((long)data + (s.Length * dwSize)), nullTerminator);
}
} // end of dragdrophelper
internal class EnumSTATDATA : IEnumSTATDATA
{
IEnumerable i;
IEnumerator e;
public EnumSTATDATA(IEnumerable i)
{
this.i = i;
this.e = i.GetEnumerator();
}
void IEnumSTATDATA.Clone(out IEnumSTATDATA clone)
{
clone = new EnumSTATDATA(i);
}
int IEnumSTATDATA.Next(uint celt, STATDATA[] d, out uint fetched)
{
uint rc = 0;
//uint size = (fetched != null) ? fetched[0] : 0;
for (uint i = 0; i < celt; i++)
{
if (e.MoveNext())
{
STATDATA sdata = (STATDATA)e.Current;
rc++;
if (d != null && d.Length > i)
{
d[i] = sdata;
}
}
}
fetched = rc;
return 0;
}
int IEnumSTATDATA.Reset()
{
e.Reset();
return 0;
}
int IEnumSTATDATA.Skip(uint celt)
{
for (uint i = 0; i < celt; i++)
{
e.MoveNext();
}
return 0;
}
}
internal class EnumFORMATETC : IEnumFORMATETC
{
IEnumerable cache; // of DataCacheEntrys.
DATADIR dir;
IEnumerator e;
public EnumFORMATETC(DATADIR dir, IEnumerable cache)
{
this.cache = cache;
this.dir = dir;
e = cache.GetEnumerator();
}
void IEnumFORMATETC.Clone(out IEnumFORMATETC clone)
{
clone = new EnumFORMATETC(dir, cache);
}
int IEnumFORMATETC.Next(uint celt, FORMATETC[] d, uint[] fetched)
{
uint rc = 0;
//uint size = (fetched != null) ? fetched[0] : 0;
for (uint i = 0; i < celt; i++)
{
if (e.MoveNext())
{
DataCacheEntry entry = (DataCacheEntry)e.Current;
rc++;
if (d != null && d.Length > i)
{
d[i] = entry.Format;
}
}
else
{
return VSConstants.S_FALSE;
}
}
if (fetched != null && fetched.Length > 0)
fetched[0] = rc;
return VSConstants.S_OK;
}
int IEnumFORMATETC.Reset()
{
e.Reset();
return 0;
}
int IEnumFORMATETC.Skip(uint celt)
{
for (uint i = 0; i < celt; i++)
{
e.MoveNext();
}
return 0;
}
}
}
|