Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

file 317 lines (287 sloc) 11.627 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
/// Copyright (c) Microsoft Corporation. All rights reserved.

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.Win32;

namespace Microsoft.VisualStudio.Project
{
/// <summary>
/// This type of node is used for references to COM components.
/// </summary>
[CLSCompliant(false)]
[ComVisible(true)]
public class ComReferenceNode : ReferenceNode
{
#region fields
private string typeName;
private Guid typeGuid;
private string projectRelativeFilePath;
private string installedFilePath;
private string minorVersionNumber;
private string majorVersionNumber;
private string lcid;
#endregion

#region properties
public override string Caption
{
get { return this.typeName; }
}

public override string Url
{
get
{
return this.projectRelativeFilePath;
}
}

/// <summary>
/// Returns the Guid of the COM object.
/// </summary>
public Guid TypeGuid
{
get { return this.typeGuid; }
}

/// <summary>
/// Returns the path where the COM object is installed.
/// </summary>
public string InstalledFilePath
{
get { return this.installedFilePath; }
}

[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "LCID")]
public string LCID
{
get { return lcid; }
}

public int MajorVersionNumber
{
get
{
if(string.IsNullOrEmpty(majorVersionNumber))
{
return 0;
}
return int.Parse(majorVersionNumber, CultureInfo.CurrentCulture);
}
}
public int MinorVersionNumber
{
get
{
if(string.IsNullOrEmpty(minorVersionNumber))
{
return 0;
}
return int.Parse(minorVersionNumber, CultureInfo.CurrentCulture);
}
}
private Automation.OAComReference comReference;
internal override object Object
{
get
{
if(null == comReference)
{
comReference = new Automation.OAComReference(this);
}
return comReference;
}
}
#endregion

#region ctors
/// <summary>
/// Constructor for the ComReferenceNode.
/// </summary>
public ComReferenceNode(ProjectNode root, ProjectElement element)
: base(root, element)
{
this.typeName = this.ItemNode.GetMetadata(ProjectFileConstants.Include);
string typeGuidAsString = this.ItemNode.GetMetadata(ProjectFileConstants.Guid);
if(typeGuidAsString != null)
{
this.typeGuid = new Guid(typeGuidAsString);
}

this.majorVersionNumber = this.ItemNode.GetMetadata(ProjectFileConstants.VersionMajor);
this.minorVersionNumber = this.ItemNode.GetMetadata(ProjectFileConstants.VersionMinor);
this.lcid = this.ItemNode.GetMetadata(ProjectFileConstants.Lcid);
this.SetProjectItemsThatRelyOnReferencesToBeResolved(false);
this.SetInstalledFilePath();
}

/// <summary>
/// Overloaded constructor for creating a ComReferenceNode from selector data
/// </summary>
/// <param name="root">The Project node</param>
/// <param name="selectorData">The component selctor data.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
public ComReferenceNode(ProjectNode root, VSCOMPONENTSELECTORDATA selectorData)
: base(root)
{
if(root == null)
{
throw new ArgumentNullException("root");
}
if(selectorData.type == VSCOMPONENTTYPE.VSCOMPONENTTYPE_Project
|| selectorData.type == VSCOMPONENTTYPE.VSCOMPONENTTYPE_ComPlus)
{
throw new ArgumentException("SelectorData cannot be of type VSCOMPONENTTYPE.VSCOMPONENTTYPE_Project or VSCOMPONENTTYPE.VSCOMPONENTTYPE_ComPlus", "selectorData");
}

// Initialize private state
this.typeName = selectorData.bstrTitle;
this.typeGuid = selectorData.guidTypeLibrary;
this.majorVersionNumber = selectorData.wTypeLibraryMajorVersion.ToString(CultureInfo.InvariantCulture);
this.minorVersionNumber = selectorData.wTypeLibraryMinorVersion.ToString(CultureInfo.InvariantCulture);
this.lcid = selectorData.lcidTypeLibrary.ToString(CultureInfo.InvariantCulture);

// Check to see if the COM object actually exists.
this.SetInstalledFilePath();
// If the value cannot be set throw.
if(String.IsNullOrEmpty(this.installedFilePath))
{
throw new NullReferenceException("The InstalledFilePath is null");
}
}
#endregion

#region methods
/// <summary>
/// Links a reference node to the project and hierarchy.
/// </summary>
protected override void BindReferenceData()
{
Debug.Assert(this.ItemNode != null, "The AssemblyName field has not been initialized");

// We need to create the project element at this point if it has not been created.
// We cannot do that from the ctor if input comes from a component selector data, since had we been doing that we would have added a project element to the project file.
// The problem with that approach is that we would need to remove the project element if the item cannot be added to the hierachy (E.g. It already exists).
// It is just safer to update the project file now. This is the intent of this method.
// Call MSBuild to build the target ResolveComReferences
if(this.ItemNode == null || this.ItemNode.Item == null)
{
this.ItemNode = this.GetProjectElementBasedOnInputFromComponentSelectorData();
}

this.SetProjectItemsThatRelyOnReferencesToBeResolved(true);
}

/// <summary>
/// Checks if a reference is already added. The method parses all references and compares the the FinalItemSpec and the Guid.
/// </summary>
/// <returns>true if the assembly has already been added.</returns>
protected override bool IsAlreadyAdded()
{
ReferenceContainerNode referencesFolder = this.ProjectMgr.FindChild(ReferenceContainerNode.ReferencesNodeVirtualName) as ReferenceContainerNode;
Debug.Assert(referencesFolder != null, "Could not find the References node");

for(HierarchyNode n = referencesFolder.FirstChild; n != null; n = n.NextSibling)
{
ComReferenceNode refererenceNode = n as ComReferenceNode;

if(refererenceNode != null)
{
// We check if the name and guids are the same
if(refererenceNode.TypeGuid == this.TypeGuid && String.Compare(refererenceNode.Caption, this.Caption, StringComparison.OrdinalIgnoreCase) == 0)
{
return true;
}
}
}

return false;
}

/// <summary>
/// Determines if this is node a valid node for painting the default reference icon.
/// </summary>
/// <returns></returns>
protected override bool CanShowDefaultIcon()
{
return !String.IsNullOrEmpty(this.installedFilePath);
}

/// <summary>
/// This is an helper method to convert the VSCOMPONENTSELECTORDATA recieved by the
/// implementer of IVsComponentUser into a ProjectElement that can be used to create
/// an instance of this class.
/// This should not be called for project reference or reference to managed assemblies.
/// </summary>
/// <returns>ProjectElement corresponding to the COM component passed in</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
private ProjectElement GetProjectElementBasedOnInputFromComponentSelectorData()
{

ProjectElement element = new ProjectElement(this.ProjectMgr, this.typeName, ProjectFileConstants.COMReference);

// Set the basic information regarding this COM component
element.SetMetadata(ProjectFileConstants.Guid, this.typeGuid.ToString("B"));
element.SetMetadata(ProjectFileConstants.VersionMajor, this.majorVersionNumber);
element.SetMetadata(ProjectFileConstants.VersionMinor, this.minorVersionNumber);
element.SetMetadata(ProjectFileConstants.Lcid, this.lcid);
element.SetMetadata(ProjectFileConstants.Isolated, false.ToString());

// See if a PIA exist for this component
TypeLibConverter typelib = new TypeLibConverter();
string assemblyName;
string assemblyCodeBase;
if(typelib.GetPrimaryInteropAssembly(this.typeGuid, Int32.Parse(this.majorVersionNumber, CultureInfo.InvariantCulture), Int32.Parse(this.minorVersionNumber, CultureInfo.InvariantCulture), Int32.Parse(this.lcid, CultureInfo.InvariantCulture), out assemblyName, out assemblyCodeBase))
{
element.SetMetadata(ProjectFileConstants.WrapperTool, WrapperToolAttributeValue.Primary.ToString().ToLowerInvariant());
}
else
{
// MSBuild will have to generate an interop assembly
element.SetMetadata(ProjectFileConstants.WrapperTool, WrapperToolAttributeValue.TlbImp.ToString().ToLowerInvariant());
element.SetMetadata(ProjectFileConstants.Private, true.ToString());
}
return element;
}

private void SetProjectItemsThatRelyOnReferencesToBeResolved(bool renameItemNode)
{
// Call MSBuild to build the target ResolveComReferences
bool success;
ErrorHandler.ThrowOnFailure(this.ProjectMgr.BuildTarget(MsBuildTarget.ResolveComReferences, out success));
if(!success)
throw new InvalidOperationException();

// Now loop through the generated COM References to find the corresponding one
Microsoft.Build.BuildEngine.BuildItemGroup comReferences = this.ProjectMgr.BuildProject.GetEvaluatedItemsByName(MsBuildGeneratedItemType.ComReferenceWrappers);
foreach(Microsoft.Build.BuildEngine.BuildItem reference in comReferences)
{
if(String.Compare(reference.GetMetadata(ProjectFileConstants.Guid), this.typeGuid.ToString("B"), StringComparison.OrdinalIgnoreCase) == 0
&& String.Compare(reference.GetMetadata(ProjectFileConstants.VersionMajor), this.majorVersionNumber, StringComparison.OrdinalIgnoreCase) == 0
&& String.Compare(reference.GetMetadata(ProjectFileConstants.VersionMinor), this.minorVersionNumber, StringComparison.OrdinalIgnoreCase) == 0
&& String.Compare(reference.GetMetadata(ProjectFileConstants.Lcid), this.lcid, StringComparison.OrdinalIgnoreCase) == 0)
{
string name = reference.FinalItemSpec;
if(Path.IsPathRooted(name))
{
this.projectRelativeFilePath = name;
}
else
{
this.projectRelativeFilePath = Path.Combine(this.ProjectMgr.ProjectFolder, name);
}

if(renameItemNode)
{
this.ItemNode.Rename(Path.GetFileNameWithoutExtension(name));
}
break;
}
}
}

/// <summary>
/// Verify that the TypeLib is registered and set the the installed file path of the com reference.
/// </summary>
/// <returns></returns>
private void SetInstalledFilePath()
{
string registryPath = string.Format(CultureInfo.InvariantCulture, @"TYPELIB\{0}\{1}.{2}", this.typeGuid.ToString("B"), this.majorVersionNumber, this.minorVersionNumber);
using(RegistryKey typeLib = Registry.ClassesRoot.OpenSubKey(registryPath))
{
if(typeLib != null)
{
// Check if we need to set the name for this type.
if(string.IsNullOrEmpty(this.typeName))
{
this.typeName = typeLib.GetValue(string.Empty) as string;
}
// Now get the path to the file that contains this type library.
using(RegistryKey installKey = typeLib.OpenSubKey(@"0\win32"))
{
this.installedFilePath = installKey.GetValue(String.Empty) as String;
}
}
}
}

#endregion
}
}
Something went wrong with that request. Please try again.