OLD | NEW |
| (Empty) |
1 /// Copyright (c) Microsoft Corporation. All rights reserved. | |
2 | |
3 using System; | |
4 using System.Collections; | |
5 using System.Diagnostics; | |
6 using System.IO; | |
7 using System.Reflection; | |
8 using System.Runtime.InteropServices; | |
9 using Microsoft.VisualStudio; | |
10 using Microsoft.VisualStudio.Shell; | |
11 using Microsoft.VisualStudio.Shell.Interop; | |
12 using MSBuild = Microsoft.Build.BuildEngine; | |
13 | |
14 namespace Microsoft.VisualStudio.Project | |
15 { | |
16 [CLSCompliant(false)] | |
17 [ComVisible(true)] | |
18 public class AssemblyReferenceNode : ReferenceNode | |
19 { | |
20 #region fieds | |
21 /// <summary> | |
22 /// The name of the assembly this refernce represents | |
23 /// </summary> | |
24 private System.Reflection.AssemblyName assemblyName; | |
25 private AssemblyName resolvedAssemblyName; | |
26 | |
27 private string assemblyPath = String.Empty; | |
28 | |
29 /// <summary> | |
30 /// Defines the listener that would listen on file changes on th
e nested project node. | |
31 /// </summary> | |
32 private FileChangeManager fileChangeListener; | |
33 | |
34 /// <summary> | |
35 /// A flag for specifying if the object was disposed. | |
36 /// </summary> | |
37 private bool isDisposed; | |
38 #endregion | |
39 | |
40 #region properties | |
41 /// <summary> | |
42 /// The name of the assembly this reference represents. | |
43 /// </summary> | |
44 /// <value></value> | |
45 internal System.Reflection.AssemblyName AssemblyName | |
46 { | |
47 get | |
48 { | |
49 return this.assemblyName; | |
50 } | |
51 } | |
52 | |
53 /// <summary> | |
54 /// Returns the name of the assembly this reference refers to on
this specific | |
55 /// machine. It can be different from the AssemblyName property
because it can | |
56 /// be more specific. | |
57 /// </summary> | |
58 internal System.Reflection.AssemblyName ResolvedAssembly | |
59 { | |
60 get { return resolvedAssemblyName; } | |
61 } | |
62 | |
63 public override string Url | |
64 { | |
65 get | |
66 { | |
67 return this.assemblyPath; | |
68 } | |
69 } | |
70 | |
71 public override string Caption | |
72 { | |
73 get | |
74 { | |
75 return this.assemblyName.Name; | |
76 } | |
77 } | |
78 | |
79 private Automation.OAAssemblyReference assemblyRef; | |
80 internal override object Object | |
81 { | |
82 get | |
83 { | |
84 if(null == assemblyRef) | |
85 { | |
86 assemblyRef = new Automation.OAAssemblyR
eference(this); | |
87 } | |
88 return assemblyRef; | |
89 } | |
90 } | |
91 #endregion | |
92 | |
93 #region ctors | |
94 /// <summary> | |
95 /// Constructor for the ReferenceNode | |
96 /// </summary> | |
97 public AssemblyReferenceNode(ProjectNode root, ProjectElement el
ement) | |
98 : base(root, element) | |
99 { | |
100 this.GetPathNameFromProjectFile(); | |
101 | |
102 this.InitializeFileChangeEvents(); | |
103 | |
104 string include = this.ItemNode.GetMetadata(ProjectFileCo
nstants.Include); | |
105 | |
106 this.CreateFromAssemblyName(new System.Reflection.Assemb
lyName(include)); | |
107 } | |
108 | |
109 /// <summary> | |
110 /// Constructor for the AssemblyReferenceNode | |
111 /// </summary> | |
112 public AssemblyReferenceNode(ProjectNode root, string assemblyPa
th) | |
113 : base(root) | |
114 { | |
115 // Validate the input parameters. | |
116 if(null == root) | |
117 { | |
118 throw new ArgumentNullException("root"); | |
119 } | |
120 if(string.IsNullOrEmpty(assemblyPath)) | |
121 { | |
122 throw new ArgumentNullException("assemblyPath"); | |
123 } | |
124 | |
125 this.InitializeFileChangeEvents(); | |
126 | |
127 // The assemblyPath variable can be an actual path on di
sk or a generic assembly name. | |
128 if(File.Exists(assemblyPath)) | |
129 { | |
130 // The assemblyPath parameter is an actual file
on disk; try to load it. | |
131 this.assemblyName = System.Reflection.AssemblyNa
me.GetAssemblyName(assemblyPath); | |
132 this.assemblyPath = assemblyPath; | |
133 | |
134 // We register with listeningto chnages onteh pa
th here. The rest of teh cases will call into resolving the assembly and registr
ation is done there. | |
135 this.fileChangeListener.ObserveItem(this.assembl
yPath); | |
136 } | |
137 else | |
138 { | |
139 // The file does not exist on disk. This can be
because the file / path is not | |
140 // correct or because this is not a path, but an
assembly name. | |
141 // Try to resolve the reference as an assembly n
ame. | |
142 this.CreateFromAssemblyName(new System.Reflectio
n.AssemblyName(assemblyPath)); | |
143 } | |
144 } | |
145 #endregion | |
146 | |
147 #region methods | |
148 /// <summary> | |
149 /// Closes the node. | |
150 /// </summary> | |
151 /// <returns></returns> | |
152 public override int Close() | |
153 { | |
154 try | |
155 { | |
156 this.Dispose(true); | |
157 } | |
158 finally | |
159 { | |
160 base.Close(); | |
161 } | |
162 | |
163 return VSConstants.S_OK; | |
164 } | |
165 | |
166 /// <summary> | |
167 /// Links a reference node to the project and hierarchy. | |
168 /// </summary> | |
169 protected override void BindReferenceData() | |
170 { | |
171 Debug.Assert(this.assemblyName != null, "The AssemblyNam
e field has not been initialized"); | |
172 | |
173 // If the item has not been set correctly like in case o
f a new reference added it now. | |
174 // The constructor for the AssemblyReference node will c
reate a default project item. In that case the Item is null. | |
175 // We need to specify here the correct project element. | |
176 if(this.ItemNode == null || this.ItemNode.Item == null) | |
177 { | |
178 this.ItemNode = new ProjectElement(this.ProjectM
gr, this.assemblyName.FullName, ProjectFileConstants.Reference); | |
179 } | |
180 | |
181 // Set the basic information we know about | |
182 this.ItemNode.SetMetadata(ProjectFileConstants.Name, thi
s.assemblyName.Name); | |
183 this.ItemNode.SetMetadata(ProjectFileConstants.AssemblyN
ame, Path.GetFileName(this.assemblyPath)); | |
184 | |
185 this.SetReferenceProperties(); | |
186 } | |
187 | |
188 /// <summary> | |
189 /// Disposes the node | |
190 /// </summary> | |
191 /// <param name="disposing"></param> | |
192 protected override void Dispose(bool disposing) | |
193 { | |
194 if(this.isDisposed) | |
195 { | |
196 return; | |
197 } | |
198 | |
199 try | |
200 { | |
201 this.UnregisterFromFileChangeService(); | |
202 } | |
203 finally | |
204 { | |
205 base.Dispose(disposing); | |
206 this.isDisposed = true; | |
207 } | |
208 } | |
209 | |
210 private void CreateFromAssemblyName(AssemblyName name) | |
211 { | |
212 this.assemblyName = name; | |
213 | |
214 // Use MsBuild to resolve the assemblyname | |
215 this.ResolveAssemblyReference(); | |
216 | |
217 if(String.IsNullOrEmpty(this.assemblyPath) && (null != t
his.ItemNode.Item)) | |
218 { | |
219 // Try to get the assmbly name from the hintpath
. | |
220 this.GetPathNameFromProjectFile(); | |
221 if(this.assemblyPath == null) | |
222 { | |
223 // Try to get the assembly name from the
path | |
224 this.assemblyName = System.Reflection.As
semblyName.GetAssemblyName(this.assemblyPath); | |
225 } | |
226 } | |
227 if(null == resolvedAssemblyName) | |
228 { | |
229 resolvedAssemblyName = assemblyName; | |
230 } | |
231 } | |
232 | |
233 /// <summary> | |
234 /// Checks if an assembly is already added. The method parses al
l references and compares the full assemblynames, or the location of the assembl
ies to decide whether two assemblies are the same. | |
235 /// </summary> | |
236 /// <returns>true if the assembly has already been added.</retur
ns> | |
237 protected override bool IsAlreadyAdded() | |
238 { | |
239 ReferenceContainerNode referencesFolder = this.ProjectMg
r.FindChild(ReferenceContainerNode.ReferencesNodeVirtualName) as ReferenceContai
nerNode; | |
240 Debug.Assert(referencesFolder != null, "Could not find t
he References node"); | |
241 bool shouldCheckPath = !string.IsNullOrEmpty(this.Url); | |
242 | |
243 for(HierarchyNode n = referencesFolder.FirstChild; n !=
null; n = n.NextSibling) | |
244 { | |
245 AssemblyReferenceNode assemblyRefererenceNode =
n as AssemblyReferenceNode; | |
246 if(null != assemblyRefererenceNode) | |
247 { | |
248 // We will check if the full assemblynam
es are the same or if the Url of the assemblies is the same. | |
249 if(String.Compare(assemblyRefererenceNod
e.AssemblyName.FullName, this.assemblyName.FullName, StringComparison.OrdinalIgn
oreCase) == 0 || | |
250 (shouldCheckPath && NativeMethod
s.IsSamePath(assemblyRefererenceNode.Url, this.Url))) | |
251 { | |
252 return true; | |
253 } | |
254 } | |
255 } | |
256 | |
257 return false; | |
258 } | |
259 | |
260 /// <summary> | |
261 /// Determines if this is node a valid node for painting the def
ault reference icon. | |
262 /// </summary> | |
263 /// <returns></returns> | |
264 protected override bool CanShowDefaultIcon() | |
265 { | |
266 if(String.IsNullOrEmpty(this.assemblyPath) || !File.Exis
ts(this.assemblyPath) || !this.ProjectMgr.HasPassedSecurityChecks) | |
267 { | |
268 return false; | |
269 } | |
270 | |
271 return true; | |
272 } | |
273 | |
274 private void GetPathNameFromProjectFile() | |
275 { | |
276 string result = this.ItemNode.GetMetadata(ProjectFileCon
stants.HintPath); | |
277 if(String.IsNullOrEmpty(result)) | |
278 { | |
279 result = this.ItemNode.GetMetadata(ProjectFileCo
nstants.AssemblyName); | |
280 if(String.IsNullOrEmpty(result)) | |
281 { | |
282 this.assemblyPath = String.Empty; | |
283 } | |
284 else if(!result.EndsWith(".dll", StringCompariso
n.OrdinalIgnoreCase)) | |
285 { | |
286 result += ".dll"; | |
287 this.assemblyPath = result; | |
288 } | |
289 } | |
290 else | |
291 { | |
292 this.assemblyPath = this.GetFullPathFromPath(res
ult); | |
293 } | |
294 } | |
295 | |
296 private string GetFullPathFromPath(string path) | |
297 { | |
298 if(Path.IsPathRooted(path)) | |
299 { | |
300 return path; | |
301 } | |
302 else | |
303 { | |
304 Uri uri = new Uri(this.ProjectMgr.BaseURI.Uri, p
ath); | |
305 | |
306 if(uri != null) | |
307 { | |
308 return Microsoft.VisualStudio.Shell.Url.
Unescape(uri.LocalPath, true); | |
309 } | |
310 } | |
311 | |
312 return String.Empty; | |
313 } | |
314 | |
315 protected override void ResolveReference() | |
316 { | |
317 this.ResolveAssemblyReference(); | |
318 } | |
319 | |
320 private void SetHintPathAndPrivateValue() | |
321 { | |
322 | |
323 // Private means local copy; we want to know if it is al
ready set to not override the default | |
324 string privateValue = this.ItemNode.GetMetadata(ProjectF
ileConstants.Private); | |
325 | |
326 // Get the list of items which require HintPath | |
327 Microsoft.Build.BuildEngine.BuildItemGroup references =
this.ProjectMgr.BuildProject.GetEvaluatedItemsByName(MsBuildGeneratedItemType.Re
ferenceCopyLocalPaths); | |
328 | |
329 // Remove the HintPath, we will re-add it below if it is
needed | |
330 if(!String.IsNullOrEmpty(this.assemblyPath)) | |
331 { | |
332 this.ItemNode.SetMetadata(ProjectFileConstants.H
intPath, null); | |
333 } | |
334 | |
335 // Now loop through the generated References to find the
corresponding one | |
336 foreach(Microsoft.Build.BuildEngine.BuildItem reference
in references) | |
337 { | |
338 string fileName = Path.GetFileNameWithoutExtensi
on(reference.FinalItemSpec); | |
339 if(String.Compare(fileName, this.assemblyName.Na
me, StringComparison.OrdinalIgnoreCase) == 0) | |
340 { | |
341 // We found it, now set some properties
based on this. | |
342 | |
343 string hintPath = reference.GetMetadata(
ProjectFileConstants.HintPath); | |
344 if(!String.IsNullOrEmpty(hintPath)) | |
345 { | |
346 if(Path.IsPathRooted(hintPath)) | |
347 { | |
348 hintPath = PackageUtilit
ies.GetPathDistance(this.ProjectMgr.BaseURI.Uri, new Uri(hintPath)); | |
349 } | |
350 | |
351 this.ItemNode.SetMetadata(Projec
tFileConstants.HintPath, hintPath); | |
352 // If this is not already set, w
e default to true | |
353 if(String.IsNullOrEmpty(privateV
alue)) | |
354 { | |
355 this.ItemNode.SetMetadat
a(ProjectFileConstants.Private, true.ToString()); | |
356 } | |
357 } | |
358 break; | |
359 } | |
360 | |
361 } | |
362 | |
363 } | |
364 | |
365 /// <summary> | |
366 /// This function ensures that some properies of the reference a
re set. | |
367 /// </summary> | |
368 private void SetReferenceProperties() | |
369 { | |
370 // Set a default HintPath for msbuild to be able to reso
lve the reference. | |
371 this.ItemNode.SetMetadata(ProjectFileConstants.HintPath,
this.assemblyPath); | |
372 | |
373 // Resolve assembly referernces. This is needed to make
sure that properties like the full path | |
374 // to the assembly or the hint path are set. | |
375 if(this.ProjectMgr.Build(MsBuildTarget.ResolveAssemblyRe
ferences) != MSBuildResult.Successful) | |
376 { | |
377 return; | |
378 } | |
379 | |
380 // Check if we have to resolve again the path to the ass
embly. | |
381 if(string.IsNullOrEmpty(this.assemblyPath)) | |
382 { | |
383 ResolveReference(); | |
384 } | |
385 | |
386 // Make sure that the hint path if set (if needed). | |
387 SetHintPathAndPrivateValue(); | |
388 } | |
389 | |
390 /// <summary> | |
391 /// Does the actual job of resolving an assembly reference. We n
eed a private method that does not violate | |
392 /// calling virtual method from the constructor. | |
393 /// </summary> | |
394 private void ResolveAssemblyReference() | |
395 { | |
396 if(this.ProjectMgr == null || this.ProjectMgr.IsClosed |
| !this.ProjectMgr.HasPassedSecurityChecks) | |
397 { | |
398 return; | |
399 } | |
400 | |
401 MSBuild.BuildItemGroup group = this.ProjectMgr.BuildProj
ect.GetEvaluatedItemsByName(ProjectFileConstants.ReferencePath); | |
402 if(group != null) | |
403 { | |
404 IEnumerator enumerator = group.GetEnumerator(); | |
405 | |
406 while(enumerator.MoveNext()) | |
407 { | |
408 MSBuild.BuildItem item = (MSBuild.BuildI
tem)enumerator.Current; | |
409 | |
410 string fullPath = this.GetFullPathFromPa
th(item.FinalItemSpec); | |
411 | |
412 System.Reflection.AssemblyName name = Sy
stem.Reflection.AssemblyName.GetAssemblyName(fullPath); | |
413 | |
414 // Try with full assembly name and then
with weak assembly name. | |
415 if(String.Compare(name.FullName, this.as
semblyName.FullName, StringComparison.OrdinalIgnoreCase) == 0 || String.Compare(
name.Name, this.assemblyName.Name, StringComparison.OrdinalIgnoreCase) == 0) | |
416 { | |
417 if(!NativeMethods.IsSamePath(ful
lPath, this.assemblyPath)) | |
418 { | |
419 // set the full path now
. | |
420 this.assemblyPath = full
Path; | |
421 | |
422 // We have a new item to
listen too, since the assembly reference is resolved from a different place. | |
423 this.fileChangeListener.
ObserveItem(this.assemblyPath); | |
424 } | |
425 | |
426 this.resolvedAssemblyName = name
; | |
427 | |
428 // No hint path is needed since
the assembly path will always be resolved. | |
429 return; | |
430 } | |
431 } | |
432 } | |
433 } | |
434 | |
435 /// <summary> | |
436 /// Registers with File change events | |
437 /// </summary> | |
438 private void InitializeFileChangeEvents() | |
439 { | |
440 this.fileChangeListener = new FileChangeManager(this.Pro
jectMgr.Site); | |
441 this.fileChangeListener.FileChangedOnDisk += this.OnAsse
mblyReferenceChangedOnDisk; | |
442 } | |
443 | |
444 /// <summary> | |
445 /// Unregisters this node from file change notifications. | |
446 /// </summary> | |
447 private void UnregisterFromFileChangeService() | |
448 { | |
449 this.fileChangeListener.FileChangedOnDisk -= this.OnAsse
mblyReferenceChangedOnDisk; | |
450 this.fileChangeListener.Dispose(); | |
451 } | |
452 | |
453 /// <summary> | |
454 /// Event callback. Called when one of the assembly file is chan
ged. | |
455 /// </summary> | |
456 /// <param name="sender">The FileChangeManager object.</param> | |
457 /// <param name="e">Event args containing the file name that was
updated.</param> | |
458 private void OnAssemblyReferenceChangedOnDisk(object sender, Fil
eChangedOnDiskEventArgs e) | |
459 { | |
460 Debug.Assert(e != null, "No event args specified for the
FileChangedOnDisk event"); | |
461 | |
462 // We only care about file deletes, so check for one bef
ore enumerating references. | |
463 if((e.FileChangeFlag & _VSFILECHANGEFLAGS.VSFILECHG_Del)
== 0) | |
464 { | |
465 return; | |
466 } | |
467 | |
468 | |
469 if(NativeMethods.IsSamePath(e.FileName, this.assemblyPat
h)) | |
470 { | |
471 this.OnInvalidateItems(this.Parent); | |
472 } | |
473 } | |
474 #endregion | |
475 } | |
476 } | |
OLD | NEW |