diff --git a/vsintegration/src/unittests/Tests.ProjectSystem.References.fs b/vsintegration/src/unittests/Tests.ProjectSystem.References.fs index 9d26f2a62e5..aa68d8413ad 100644 --- a/vsintegration/src/unittests/Tests.ProjectSystem.References.fs +++ b/vsintegration/src/unittests/Tests.ProjectSystem.References.fs @@ -16,6 +16,7 @@ open UnitTests.TestLib.ProjectSystem open Microsoft.VisualStudio.FSharp.ProjectSystem open Microsoft.VisualStudio.Shell.Interop open Microsoft.Win32 +open System.Xml.Linq [] type References() = @@ -618,4 +619,60 @@ type References() = // look for the new property inside of the project file let contents = File.ReadAllText(newProjFileName) AssertContains contents newPropVal - ) \ No newline at end of file + ) + + + [] + member public this.``AddReference.COM`` () = + DoWithTempFile "Test.fsproj" (fun projFile -> + File.AppendAllText(projFile, TheTests.SimpleFsprojText([], [], "")) + use project = TheTests.CreateProject(projFile) + + let guid = Guid("50a7e9b0-70ef-11d1-b75a-00a0c90564fe") + + let selectorData = VSCOMPONENTSELECTORDATA ( + ``type`` = VSCOMPONENTTYPE.VSCOMPONENTTYPE_Com2, + guidTypeLibrary = guid, + wTypeLibraryMinorVersion = 0us, + wTypeLibraryMajorVersion = 1us, + bstrTitle = "Microsoft Shell Controls And Automation" ) + let refContainer = GetReferenceContainerNode(project) + + let comReference = refContainer.AddReferenceFromSelectorData(selectorData) + + // check reference node properties + Assert.IsNotNull comReference + Assert.IsInstanceOf(typeof, comReference) + let comRef = comReference :?> ComReferenceNode + Assert.AreEqual(1, comRef.MajorVersionNumber) + Assert.AreEqual(0, comRef.MinorVersionNumber) + Assert.AreEqual(guid, comRef.TypeGuid) + Assert.AreEqual("Microsoft Shell Controls And Automation", comRef.Caption) + let sysDirectory = Environment.GetFolderPath(Environment.SpecialFolder.SystemX86) + StringAssert.AreEqualIgnoringCase(Path.Combine(sysDirectory, "shell32.dll"), comRef.InstalledFilePath) + + // check node exists under references + let l = new List() + project.FindNodesOfType(l) + + Assert.AreEqual(1, l.Count) + let referenceNode = l.[0] + Assert.AreSame(comRef, referenceNode) + + // check saved msbuild item + SaveProject(project) + let fsproj = XDocument.Load(project.FileName) + printfn "%O" fsproj + let xn s = fsproj.Root.GetDefaultNamespace().GetName(s) + let comReferencesXml = fsproj.Descendants(xn "COMReference") |> Seq.toList + + Assert.AreEqual(1, comReferencesXml |> List.length) + + let comRefXml = comReferencesXml |> List.head + + Assert.AreEqual("Microsoft Shell Controls And Automation", comRefXml.Attribute(XName.Get("Include")).Value) + Assert.AreEqual(guid, Guid(comRefXml.Element(xn "Guid").Value)) + Assert.AreEqual("1", comRefXml.Element(xn "VersionMajor").Value) + Assert.AreEqual("0", comRefXml.Element(xn "VersionMinor").Value) + Assert.AreEqual("0", comRefXml.Element(xn "Lcid").Value) + ) diff --git a/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/Automation/VSProject/OAComReference.cs b/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/Automation/VSProject/OAComReference.cs index 4ea1ded9d7c..e1c4f3fe22c 100644 --- a/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/Automation/VSProject/OAComReference.cs +++ b/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/Automation/VSProject/OAComReference.cs @@ -24,15 +24,8 @@ public override string Culture { get { - int locale = 0; - try - { - locale = int.Parse(BaseReferenceNode.LCID, CultureInfo.InvariantCulture); - } - catch (System.FormatException) - { - // Do Nothing - } + var locale = BaseReferenceNode.LCID; + if (0 == locale) { return string.Empty; diff --git a/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/ComReferenceNode.cs b/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/ComReferenceNode.cs index 747d356f715..9a46f493137 100644 --- a/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/ComReferenceNode.cs +++ b/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/ComReferenceNode.cs @@ -42,7 +42,7 @@ [ DllImport( "oleaut32.dll", CharSet = CharSet.Unicode, PreserveSig = false )] private string installedFilePath; private string minorVersionNumber; private string majorVersionNumber; - private string lcid; + private readonly int lcid; #endregion #region properties @@ -76,7 +76,7 @@ public string InstalledFilePath } [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "LCID")] - public string LCID + public int LCID { get { return lcid; } } @@ -133,7 +133,7 @@ internal ComReferenceNode(ProjectNode root, ProjectElement element) this.majorVersionNumber = this.ItemNode.GetMetadata(ProjectFileConstants.VersionMajor); this.minorVersionNumber = this.ItemNode.GetMetadata(ProjectFileConstants.VersionMinor); - this.lcid = this.ItemNode.GetMetadata(ProjectFileConstants.Lcid); + this.lcid = int.Parse(this.ItemNode.GetMetadata(ProjectFileConstants.Lcid)); this.SetProjectItemsThatRelyOnReferencesToBeResolved(false); this.SetInstalledFilePath(); } @@ -161,14 +161,15 @@ internal ComReferenceNode(ProjectNode root, VSCOMPONENTSELECTORDATA selectorData 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); + this.lcid = (int) selectorData.lcidTypeLibrary; // 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 ArgumentException(); + var message = string.Format(SR.GetString(SR.ReferenceCouldNotBeAdded, CultureInfo.CurrentUICulture), selectorData.bstrTitle); + throw new InvalidOperationException(message); } } @@ -195,14 +196,15 @@ internal ComReferenceNode(ProjectNode root, string filePath) this.typeGuid = typeAttr.guid; this.majorVersionNumber = typeAttr.wMajorVerNum.ToString(CultureInfo.InvariantCulture); this.minorVersionNumber = typeAttr.wMinorVerNum.ToString(CultureInfo.InvariantCulture); - this.lcid = typeAttr.lcid.ToString(CultureInfo.InvariantCulture); + this.lcid = typeAttr.lcid; // 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 ArgumentException(); + var message = string.Format(SR.GetString(SR.ReferenceCouldNotBeAdded, CultureInfo.CurrentUICulture), filePath); + throw new InvalidOperationException(message); } } finally @@ -281,21 +283,20 @@ internal ComReferenceNode(ProjectNode root, string filePath) [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.Lcid, this.lcid.ToString()); 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)) + if (typelib.GetPrimaryInteropAssembly(this.typeGuid, Int32.Parse(this.majorVersionNumber, CultureInfo.InvariantCulture), Int32.Parse(this.minorVersionNumber, CultureInfo.InvariantCulture), this.lcid, out assemblyName, out assemblyCodeBase)) { element.SetMetadata(ProjectFileConstants.WrapperTool, WrapperToolAttributeValue.Primary.ToString().ToLowerInvariant()); } @@ -325,7 +326,7 @@ private void SetProjectItemsThatRelyOnReferencesToBeResolved(bool renameItemNode if (String.Compare(MSBuildItem.GetMetadataValue(reference, ProjectFileConstants.Guid), this.typeGuid.ToString("B"), StringComparison.OrdinalIgnoreCase) == 0 && String.Compare(MSBuildItem.GetMetadataValue(reference, ProjectFileConstants.VersionMajor), this.majorVersionNumber, StringComparison.OrdinalIgnoreCase) == 0 && String.Compare(MSBuildItem.GetMetadataValue(reference, ProjectFileConstants.VersionMinor), this.minorVersionNumber, StringComparison.OrdinalIgnoreCase) == 0 - && String.Compare(MSBuildItem.GetMetadataValue(reference, ProjectFileConstants.Lcid), this.lcid, StringComparison.OrdinalIgnoreCase) == 0) + && String.Compare(MSBuildItem.GetMetadataValue(reference, ProjectFileConstants.Lcid), this.lcid.ToString(), StringComparison.OrdinalIgnoreCase) == 0) { string name = MSBuildItem.GetEvaluatedInclude(reference); if (Path.IsPathRooted(name)) @@ -365,9 +366,16 @@ private void SetInstalledFilePath() 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(string.Format(CultureInfo.InvariantCulture, @"{0}\win32", this.lcid))) + + // lcid + // The hexadecimal string representation of the locale identifier (LCID). + // It is one to four hexadecimal digits with no 0x prefix and no leading zeros. + using (RegistryKey installKey = typeLib.OpenSubKey(string.Format(CultureInfo.InvariantCulture, @"{0:X}\win32", this.lcid))) { - this.installedFilePath = installKey.GetValue(String.Empty) as String; + if (installKey != null) + { + this.installedFilePath = installKey.GetValue(String.Empty) as String; + } } } } diff --git a/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/Microsoft.VisualStudio.Package.Project.cs b/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/Microsoft.VisualStudio.Package.Project.cs index e74f855f5a3..861b5435b2a 100644 --- a/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/Microsoft.VisualStudio.Package.Project.cs +++ b/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/Microsoft.VisualStudio.Package.Project.cs @@ -153,6 +153,7 @@ protected override string GetLocalizedString(string value) /*internal, but public for FSharp.Project.dll*/ public const string ProjectProperties = "ProjectProperties"; /*internal, but public for FSharp.Project.dll*/ public const string Quiet = "Quiet"; /*internal, but public for FSharp.Project.dll*/ public const string QueryReloadNestedProject = "QueryReloadNestedProject"; + /*internal, but public for FSharp.Project.dll*/ public const string ReferenceCouldNotBeAdded = "ReferenceCouldNotBeAdded"; /*internal, but public for FSharp.Project.dll*/ public const string ReferenceAlreadyExists = "ReferenceAlreadyExists"; /*internal, but public for FSharp.Project.dll*/ public const string ReferenceWithAssemblyNameAlreadyExists = "ReferenceWithAssemblyNameAlreadyExists"; /*internal, but public for FSharp.Project.dll*/ public const string ReferencesNodeName = "ReferencesNodeName"; diff --git a/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/Microsoft.VisualStudio.Package.Project.resx b/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/Microsoft.VisualStudio.Package.Project.resx index 675fdd61893..c8a1bc33a6e 100644 --- a/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/Microsoft.VisualStudio.Package.Project.resx +++ b/vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/Microsoft.VisualStudio.Package.Project.resx @@ -125,6 +125,10 @@ Advanced Project Property Page Caption + + A reference to '{0}' could not be added. + ReferenceCouldNotBeAdded error message + A reference to '{0}' could not be added. A reference to the component '{1}' already exists in the project. ReferenceAlreadyExists error message