OLD | NEW |
| (Empty) |
1 /// Copyright (c) Microsoft Corporation. All rights reserved. | |
2 | |
3 using System; | |
4 using System.Collections; | |
5 using System.Collections.Generic; | |
6 using System.Diagnostics; | |
7 using System.Diagnostics.CodeAnalysis; | |
8 using System.Runtime.InteropServices; | |
9 using System.Security.Permissions; | |
10 using Microsoft.VisualStudio; | |
11 using Microsoft.VisualStudio.OLE.Interop; | |
12 using Microsoft.VisualStudio.Shell; | |
13 | |
14 namespace Microsoft.VisualStudio.Project | |
15 { | |
16 internal enum tagDVASPECT | |
17 { | |
18 DVASPECT_CONTENT = 1, | |
19 DVASPECT_THUMBNAIL = 2, | |
20 DVASPECT_ICON = 4, | |
21 DVASPECT_DOCPRINT = 8 | |
22 } | |
23 | |
24 internal enum tagTYMED | |
25 { | |
26 TYMED_HGLOBAL = 1, | |
27 TYMED_FILE = 2, | |
28 TYMED_ISTREAM = 4, | |
29 TYMED_ISTORAGE = 8, | |
30 TYMED_GDI = 16, | |
31 TYMED_MFPICT = 32, | |
32 TYMED_ENHMF = 64, | |
33 TYMED_NULL = 0 | |
34 } | |
35 | |
36 internal sealed class DataCacheEntry : IDisposable | |
37 { | |
38 #region fields | |
39 /// <summary> | |
40 /// Defines an object that will be a mutex for this object for s
ynchronizing thread calls. | |
41 /// </summary> | |
42 private static volatile object Mutex = new object(); | |
43 | |
44 private FORMATETC format; | |
45 | |
46 private long data; | |
47 | |
48 private DATADIR dataDir; | |
49 | |
50 private bool isDisposed; | |
51 #endregion | |
52 | |
53 #region properties | |
54 internal FORMATETC Format | |
55 { | |
56 get | |
57 { | |
58 return this.format; | |
59 } | |
60 } | |
61 | |
62 internal long Data | |
63 { | |
64 get | |
65 { | |
66 return this.data; | |
67 } | |
68 } | |
69 | |
70 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledP
rivateCode")] | |
71 internal DATADIR DataDir | |
72 { | |
73 get | |
74 { | |
75 return this.dataDir; | |
76 } | |
77 } | |
78 | |
79 #endregion | |
80 | |
81 /// <summary> | |
82 /// The IntPtr is data allocated that should be removed. It is a
llocated by the ProcessSelectionData method. | |
83 /// </summary> | |
84 internal DataCacheEntry(FORMATETC fmt, IntPtr data, DATADIR dir) | |
85 { | |
86 this.format = fmt; | |
87 this.data = (long)data; | |
88 this.dataDir = dir; | |
89 } | |
90 | |
91 #region Dispose | |
92 ~DataCacheEntry() | |
93 { | |
94 Dispose(false); | |
95 } | |
96 | |
97 /// <summary> | |
98 /// The IDispose interface Dispose method for disposing the obje
ct determinastically. | |
99 /// </summary> | |
100 public void Dispose() | |
101 { | |
102 Dispose(true); | |
103 GC.SuppressFinalize(this); | |
104 } | |
105 | |
106 /// <summary> | |
107 /// The method that does the cleanup. | |
108 /// </summary> | |
109 /// <param name="disposing"></param> | |
110 private void Dispose(bool disposing) | |
111 { | |
112 // Everybody can go here. | |
113 if(!this.isDisposed) | |
114 { | |
115 // Synchronize calls to the Dispose simultenious
ly. | |
116 lock(Mutex) | |
117 { | |
118 if(disposing && this.data != 0) | |
119 { | |
120 Marshal.FreeHGlobal((IntPtr)this
.data); | |
121 this.data = 0; | |
122 } | |
123 | |
124 this.isDisposed = true; | |
125 } | |
126 } | |
127 } | |
128 #endregion | |
129 } | |
130 | |
131 /// <summary> | |
132 /// Unfortunately System.Windows.Forms.IDataObject and | |
133 /// Microsoft.VisualStudio.OLE.Interop.IDataObject are different... | |
134 /// </summary> | |
135 internal sealed class DataObject : IDataObject | |
136 { | |
137 #region fields | |
138 internal const int DATA_S_SAMEFORMATETC = 0x00040130; | |
139 EventSinkCollection map; | |
140 ArrayList entries; | |
141 #endregion | |
142 | |
143 internal DataObject() | |
144 { | |
145 this.map = new EventSinkCollection(); | |
146 this.entries = new ArrayList(); | |
147 } | |
148 | |
149 internal void SetData(FORMATETC format, IntPtr data) | |
150 { | |
151 this.entries.Add(new DataCacheEntry(format, data, DATADI
R.DATADIR_SET)); | |
152 } | |
153 | |
154 #region IDataObject methods | |
155 int IDataObject.DAdvise(FORMATETC[] e, uint adv, IAdviseSink sin
k, out uint cookie) | |
156 { | |
157 STATDATA sdata = new STATDATA(); | |
158 | |
159 sdata.ADVF = adv; | |
160 sdata.FORMATETC = e[0]; | |
161 sdata.pAdvSink = sink; | |
162 cookie = this.map.Add(sdata); | |
163 sdata.dwConnection = cookie; | |
164 return 0; | |
165 } | |
166 | |
167 void IDataObject.DUnadvise(uint cookie) | |
168 { | |
169 this.map.RemoveAt(cookie); | |
170 } | |
171 | |
172 int IDataObject.EnumDAdvise(out IEnumSTATDATA e) | |
173 { | |
174 e = new EnumSTATDATA((IEnumerable)this.map); | |
175 return 0; //?? | |
176 } | |
177 | |
178 int IDataObject.EnumFormatEtc(uint direction, out IEnumFORMATETC
penum) | |
179 { | |
180 penum = new EnumFORMATETC((DATADIR)direction, (IEnumerab
le)this.entries); | |
181 return 0; | |
182 } | |
183 | |
184 int IDataObject.GetCanonicalFormatEtc(FORMATETC[] format, FORMAT
ETC[] fmt) | |
185 { | |
186 throw new System.Runtime.InteropServices.COMException(""
, DATA_S_SAMEFORMATETC); | |
187 } | |
188 | |
189 void IDataObject.GetData(FORMATETC[] fmt, STGMEDIUM[] m) | |
190 { | |
191 STGMEDIUM retMedium = new STGMEDIUM(); | |
192 | |
193 if(fmt == null || fmt.Length < 1) | |
194 return; | |
195 | |
196 foreach(DataCacheEntry e in this.entries) | |
197 { | |
198 if(e.Format.cfFormat == fmt[0].cfFormat /*|| fmt
[0].cfFormat == InternalNativeMethods.CF_HDROP*/) | |
199 { | |
200 retMedium.tymed = e.Format.tymed; | |
201 | |
202 // Caller must delete the memory. | |
203 retMedium.unionmember = DragDropHelper.C
opyHGlobal(new IntPtr(e.Data)); | |
204 break; | |
205 } | |
206 } | |
207 | |
208 if(m != null && m.Length > 0) | |
209 m[0] = retMedium; | |
210 } | |
211 | |
212 void IDataObject.GetDataHere(FORMATETC[] fmt, STGMEDIUM[] m) | |
213 { | |
214 } | |
215 | |
216 int IDataObject.QueryGetData(FORMATETC[] fmt) | |
217 { | |
218 if(fmt == null || fmt.Length < 1) | |
219 return VSConstants.S_FALSE; | |
220 | |
221 foreach(DataCacheEntry e in this.entries) | |
222 { | |
223 if(e.Format.cfFormat == fmt[0].cfFormat /*|| fmt
[0].cfFormat == InternalNativeMethods.CF_HDROP*/) | |
224 return VSConstants.S_OK; | |
225 } | |
226 | |
227 return VSConstants.S_FALSE; | |
228 } | |
229 | |
230 void IDataObject.SetData(FORMATETC[] fmt, STGMEDIUM[] m, int fRe
lease) | |
231 { | |
232 } | |
233 #endregion | |
234 } | |
235 | |
236 [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPerm
issionFlag.UnmanagedCode)] | |
237 internal static class DragDropHelper | |
238 { | |
239 #pragma warning disable 414 | |
240 internal static readonly ushort CF_VSREFPROJECTITEMS; | |
241 internal static readonly ushort CF_VSSTGPROJECTITEMS; | |
242 internal static readonly ushort CF_VSPROJECTCLIPDESCRIPTOR; | |
243 #pragma warning restore 414 | |
244 | |
245 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Perf
ormance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] | |
246 static DragDropHelper() | |
247 { | |
248 CF_VSREFPROJECTITEMS = UnsafeNativeMethods.RegisterClipb
oardFormat("CF_VSREFPROJECTITEMS"); | |
249 CF_VSSTGPROJECTITEMS = UnsafeNativeMethods.RegisterClipb
oardFormat("CF_VSSTGPROJECTITEMS"); | |
250 CF_VSPROJECTCLIPDESCRIPTOR = UnsafeNativeMethods.Registe
rClipboardFormat("CF_PROJECTCLIPBOARDDESCRIPTOR"); | |
251 } | |
252 | |
253 | |
254 public static FORMATETC CreateFormatEtc(ushort iFormat) | |
255 { | |
256 FORMATETC fmt = new FORMATETC(); | |
257 fmt.cfFormat = iFormat; | |
258 fmt.ptd = IntPtr.Zero; | |
259 fmt.dwAspect = (uint)DVASPECT.DVASPECT_CONTENT; | |
260 fmt.lindex = -1; | |
261 fmt.tymed = (uint)TYMED.TYMED_HGLOBAL; | |
262 return fmt; | |
263 } | |
264 | |
265 public static int QueryGetData(Microsoft.VisualStudio.OLE.Intero
p.IDataObject pDataObject, ref FORMATETC fmtetc) | |
266 { | |
267 int returnValue = VSConstants.E_FAIL; | |
268 FORMATETC[] af = new FORMATETC[1]; | |
269 af[0] = fmtetc; | |
270 try | |
271 { | |
272 int result = ErrorHandler.ThrowOnFailure(pDataOb
ject.QueryGetData(af)); | |
273 if(result == VSConstants.S_OK) | |
274 { | |
275 fmtetc = af[0]; | |
276 returnValue = VSConstants.S_OK; | |
277 } | |
278 } | |
279 catch(COMException e) | |
280 { | |
281 Trace.WriteLine("COMException : " + e.Message); | |
282 returnValue = e.ErrorCode; | |
283 } | |
284 | |
285 return returnValue; | |
286 } | |
287 | |
288 public static STGMEDIUM GetData(Microsoft.VisualStudio.OLE.Inter
op.IDataObject pDataObject, ref FORMATETC fmtetc) | |
289 { | |
290 FORMATETC[] af = new FORMATETC[1]; | |
291 af[0] = fmtetc; | |
292 STGMEDIUM[] sm = new STGMEDIUM[1]; | |
293 pDataObject.GetData(af, sm); | |
294 fmtetc = af[0]; | |
295 return sm[0]; | |
296 } | |
297 | |
298 /// <summary> | |
299 /// Retrives data from a VS format. | |
300 /// </summary> | |
301 public static List<string> GetDroppedFiles(ushort format, Micros
oft.VisualStudio.OLE.Interop.IDataObject dataObject, out DropDataType ddt) | |
302 { | |
303 ddt = DropDataType.None; | |
304 List<string> droppedFiles = new List<string>(); | |
305 | |
306 // try HDROP | |
307 FORMATETC fmtetc = CreateFormatEtc(format); | |
308 | |
309 if(QueryGetData(dataObject, ref fmtetc) == VSConstants.S
_OK) | |
310 { | |
311 STGMEDIUM stgmedium = DragDropHelper.GetData(dat
aObject, ref fmtetc); | |
312 if(stgmedium.tymed == (uint)TYMED.TYMED_HGLOBAL) | |
313 { | |
314 // We are releasing the cloned hglobal h
ere. | |
315 IntPtr dropInfoHandle = stgmedium.unionm
ember; | |
316 if(dropInfoHandle != IntPtr.Zero) | |
317 { | |
318 ddt = DropDataType.Shell; | |
319 try | |
320 { | |
321 uint numFiles = UnsafeNa
tiveMethods.DragQueryFile(dropInfoHandle, 0xFFFFFFFF, null, 0); | |
322 | |
323 // We are a directory ba
sed project thus a projref string is placed on the clipboard. | |
324 // We assign the maximum
length of a projref string. | |
325 // The format of a projr
ef is : <Proj Guid>|<project rel path>|<file path> | |
326 uint lenght = (uint)Guid
.Empty.ToString().Length + 2 * NativeMethods.MAX_PATH + 2; | |
327 char[] moniker = new cha
r[lenght + 1]; | |
328 for(uint fileIndex = 0;
fileIndex < numFiles; fileIndex++) | |
329 { | |
330 uint queryFileLe
ngth = UnsafeNativeMethods.DragQueryFile(dropInfoHandle, fileIndex, moniker, len
ght); | |
331 string filename
= new String(moniker, 0, (int)queryFileLength); | |
332 droppedFiles.Add
(filename); | |
333 } | |
334 } | |
335 finally | |
336 { | |
337 Marshal.FreeHGlobal(drop
InfoHandle); | |
338 } | |
339 } | |
340 } | |
341 } | |
342 | |
343 return droppedFiles; | |
344 } | |
345 | |
346 | |
347 | |
348 public static string GetSourceProjectPath(Microsoft.VisualStudio
.OLE.Interop.IDataObject dataObject) | |
349 { | |
350 string projectPath = null; | |
351 FORMATETC fmtetc = CreateFormatEtc(CF_VSPROJECTCLIPDESCR
IPTOR); | |
352 | |
353 if(QueryGetData(dataObject, ref fmtetc) == VSConstants.S
_OK) | |
354 { | |
355 STGMEDIUM stgmedium = DragDropHelper.GetData(dat
aObject, ref fmtetc); | |
356 if(stgmedium.tymed == (uint)TYMED.TYMED_HGLOBAL) | |
357 { | |
358 // We are releasing the cloned hglobal h
ere. | |
359 IntPtr dropInfoHandle = stgmedium.unionm
ember; | |
360 if(dropInfoHandle != IntPtr.Zero) | |
361 { | |
362 try | |
363 { | |
364 string path = GetData(dr
opInfoHandle); | |
365 | |
366 // Clone the path that w
e can release our memory. | |
367 if(!String.IsNullOrEmpty
(path)) | |
368 { | |
369 projectPath = St
ring.Copy(path); | |
370 } | |
371 } | |
372 finally | |
373 { | |
374 Marshal.FreeHGlobal(drop
InfoHandle); | |
375 } | |
376 } | |
377 } | |
378 } | |
379 | |
380 return projectPath; | |
381 } | |
382 | |
383 /// <summary> | |
384 /// Returns the data packed after the DROPFILES structure. | |
385 /// </summary> | |
386 /// <param name="dropHandle"></param> | |
387 /// <returns></returns> | |
388 internal static string GetData(IntPtr dropHandle) | |
389 { | |
390 IntPtr data = UnsafeNativeMethods.GlobalLock(dropHandle)
; | |
391 try | |
392 { | |
393 _DROPFILES df = (_DROPFILES)Marshal.PtrToStructu
re(data, typeof(_DROPFILES)); | |
394 if(df.fWide != 0) | |
395 { | |
396 IntPtr pdata = new IntPtr((long)data + d
f.pFiles); | |
397 return Marshal.PtrToStringUni(pdata); | |
398 } | |
399 } | |
400 finally | |
401 { | |
402 if(data != null) | |
403 { | |
404 UnsafeNativeMethods.GlobalUnLock(data); | |
405 } | |
406 } | |
407 | |
408 return null; | |
409 } | |
410 | |
411 internal static IntPtr CopyHGlobal(IntPtr data) | |
412 { | |
413 IntPtr src = UnsafeNativeMethods.GlobalLock(data); | |
414 int size = UnsafeNativeMethods.GlobalSize(data); | |
415 IntPtr ptr = Marshal.AllocHGlobal(size); | |
416 IntPtr buffer = UnsafeNativeMethods.GlobalLock(ptr); | |
417 | |
418 try | |
419 { | |
420 for(int i = 0; i < size; i++) | |
421 { | |
422 byte val = Marshal.ReadByte(new IntPtr((
long)src + i)); | |
423 | |
424 Marshal.WriteByte(new IntPtr((long)buffe
r + i), val); | |
425 } | |
426 } | |
427 finally | |
428 { | |
429 if(buffer != IntPtr.Zero) | |
430 { | |
431 UnsafeNativeMethods.GlobalUnLock(buffer)
; | |
432 } | |
433 | |
434 if(src != IntPtr.Zero) | |
435 { | |
436 UnsafeNativeMethods.GlobalUnLock(src); | |
437 } | |
438 } | |
439 return ptr; | |
440 } | |
441 | |
442 internal static void CopyStringToHGlobal(string s, IntPtr data,
int bufferSize) | |
443 { | |
444 Int16 nullTerminator = 0; | |
445 int dwSize = Marshal.SizeOf(nullTerminator); | |
446 | |
447 if((s.Length + 1) * Marshal.SizeOf(s[0]) > bufferSize) | |
448 throw new System.IO.InternalBufferOverflowExcept
ion(); | |
449 // IntPtr memory already locked... | |
450 for(int i = 0, len = s.Length; i < len; i++) | |
451 { | |
452 Marshal.WriteInt16(data, i * dwSize, s[i]); | |
453 } | |
454 // NULL terminate it | |
455 Marshal.WriteInt16(new IntPtr((long)data + (s.Length * d
wSize)), nullTerminator); | |
456 } | |
457 | |
458 } // end of dragdrophelper | |
459 | |
460 internal class EnumSTATDATA : IEnumSTATDATA | |
461 { | |
462 IEnumerable i; | |
463 | |
464 IEnumerator e; | |
465 | |
466 public EnumSTATDATA(IEnumerable i) | |
467 { | |
468 this.i = i; | |
469 this.e = i.GetEnumerator(); | |
470 } | |
471 | |
472 void IEnumSTATDATA.Clone(out IEnumSTATDATA clone) | |
473 { | |
474 clone = new EnumSTATDATA(i); | |
475 } | |
476 | |
477 int IEnumSTATDATA.Next(uint celt, STATDATA[] d, out uint fetched
) | |
478 { | |
479 uint rc = 0; | |
480 //uint size = (fetched != null) ? fetched[0] : 0; | |
481 for(uint i = 0; i < celt; i++) | |
482 { | |
483 if(e.MoveNext()) | |
484 { | |
485 STATDATA sdata = (STATDATA)e.Current; | |
486 | |
487 rc++; | |
488 if(d != null && d.Length > i) | |
489 { | |
490 d[i] = sdata; | |
491 } | |
492 } | |
493 } | |
494 | |
495 fetched = rc; | |
496 return 0; | |
497 } | |
498 | |
499 int IEnumSTATDATA.Reset() | |
500 { | |
501 e.Reset(); | |
502 return 0; | |
503 } | |
504 | |
505 int IEnumSTATDATA.Skip(uint celt) | |
506 { | |
507 for(uint i = 0; i < celt; i++) | |
508 { | |
509 e.MoveNext(); | |
510 } | |
511 | |
512 return 0; | |
513 } | |
514 } | |
515 | |
516 internal class EnumFORMATETC : IEnumFORMATETC | |
517 { | |
518 IEnumerable cache; // of DataCacheEntrys. | |
519 | |
520 DATADIR dir; | |
521 | |
522 IEnumerator e; | |
523 | |
524 public EnumFORMATETC(DATADIR dir, IEnumerable cache) | |
525 { | |
526 this.cache = cache; | |
527 this.dir = dir; | |
528 e = cache.GetEnumerator(); | |
529 } | |
530 | |
531 void IEnumFORMATETC.Clone(out IEnumFORMATETC clone) | |
532 { | |
533 clone = new EnumFORMATETC(dir, cache); | |
534 } | |
535 | |
536 int IEnumFORMATETC.Next(uint celt, FORMATETC[] d, uint[] fetched
) | |
537 { | |
538 uint rc = 0; | |
539 //uint size = (fetched != null) ? fetched[0] : 0; | |
540 for(uint i = 0; i < celt; i++) | |
541 { | |
542 if(e.MoveNext()) | |
543 { | |
544 DataCacheEntry entry = (DataCacheEntry)e
.Current; | |
545 | |
546 rc++; | |
547 if(d != null && d.Length > i) | |
548 { | |
549 d[i] = entry.Format; | |
550 } | |
551 } | |
552 else | |
553 { | |
554 return VSConstants.S_FALSE; | |
555 } | |
556 } | |
557 | |
558 if(fetched != null && fetched.Length > 0) | |
559 fetched[0] = rc; | |
560 return VSConstants.S_OK; | |
561 } | |
562 | |
563 int IEnumFORMATETC.Reset() | |
564 { | |
565 e.Reset(); | |
566 return 0; | |
567 } | |
568 | |
569 int IEnumFORMATETC.Skip(uint celt) | |
570 { | |
571 for(uint i = 0; i < celt; i++) | |
572 { | |
573 e.MoveNext(); | |
574 } | |
575 | |
576 return 0; | |
577 } | |
578 } | |
579 } | |
OLD | NEW |