Replaced the old VS templates with ones that offer more flexiblity. Started replacing the Content Project for the samples with our custom project type. Inlcuded a basic not yet working AssimpImporter.
584 lines
18 KiB
C#
584 lines
18 KiB
C#
/* ****************************************************************************
|
|
*
|
|
* Copyright (c) Microsoft Corporation.
|
|
*
|
|
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
|
* copy of the license can be found in the License.html file at the root of this distribution. If
|
|
* you cannot locate the Apache License, Version 2.0, please send an email to
|
|
* vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
|
* by the terms of the Apache License, Version 2.0.
|
|
*
|
|
* You must not remove this notice, or any other, from this software.
|
|
*
|
|
* ***************************************************************************/
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Permissions;
|
|
using Microsoft.VisualStudio;
|
|
using Microsoft.VisualStudio.OLE.Interop;
|
|
using Microsoft.VisualStudio.Shell;
|
|
|
|
namespace Microsoft.VisualStudio.Project
|
|
{
|
|
public enum tagDVASPECT
|
|
{
|
|
DVASPECT_CONTENT = 1,
|
|
DVASPECT_THUMBNAIL = 2,
|
|
DVASPECT_ICON = 4,
|
|
DVASPECT_DOCPRINT = 8
|
|
}
|
|
|
|
public 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
|
|
}
|
|
|
|
public 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
|
|
public FORMATETC Format
|
|
{
|
|
get
|
|
{
|
|
return this.format;
|
|
}
|
|
}
|
|
|
|
public long Data
|
|
{
|
|
get
|
|
{
|
|
return this.data;
|
|
}
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
|
public 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>
|
|
public 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 (disposing && 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>
|
|
public sealed class DataObject : IDataObject
|
|
{
|
|
#region fields
|
|
public const int DATA_S_SAMEFORMATETC = 0x00040130;
|
|
EventSinkCollection map;
|
|
ArrayList entries;
|
|
#endregion
|
|
|
|
public DataObject()
|
|
{
|
|
this.map = new EventSinkCollection();
|
|
this.entries = new ArrayList();
|
|
}
|
|
|
|
public 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)
|
|
{
|
|
VsUtilities.ArgumentNotNull("e", e);
|
|
|
|
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 == publicNativeMethods.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 == publicNativeMethods.CF_HDROP*/)
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
return VSConstants.S_FALSE;
|
|
}
|
|
|
|
void IDataObject.SetData(FORMATETC[] fmt, STGMEDIUM[] m, int fRelease)
|
|
{
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
|
|
public static class DragDropHelper
|
|
{
|
|
#pragma warning disable 414
|
|
public static readonly ushort CF_VSREFPROJECTITEMS;
|
|
public static readonly ushort CF_VSSTGPROJECTITEMS;
|
|
public static readonly ushort CF_VSPROJECTCLIPDESCRIPTOR;
|
|
#pragma warning restore 414
|
|
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
|
|
static DragDropHelper()
|
|
{
|
|
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 int QueryGetData(Microsoft.VisualStudio.OLE.Interop.IDataObject pDataObject, ref FORMATETC fmtetc)
|
|
{
|
|
FORMATETC[] af = new FORMATETC[1];
|
|
af[0] = fmtetc;
|
|
int result = pDataObject.QueryGetData(af);
|
|
if (result == VSConstants.S_OK)
|
|
{
|
|
fmtetc = af[0];
|
|
return VSConstants.S_OK;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
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>
|
|
public 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;
|
|
}
|
|
|
|
public 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;
|
|
}
|
|
|
|
public 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
|
|
|
|
public 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;
|
|
}
|
|
}
|
|
|
|
public 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;
|
|
}
|
|
}
|
|
}
|