提交 5e5cb072 编写于 作者: J Jeremy Koritzinsky 提交者: GitHub

Move ComHost generation from dotnet/sdk to Microsoft.NET.HostModel (dotnet/core-setup#8293)

* Copy files over from dotnet/sdk.

* Update JSON code for ComHost to use System.Text.Json.

* Rename bundle tests folder to also make sense for other tests for Microsoft.NET.HostModel.

* Add tests for Clsidmap generation.

* Add RegFreeComManifest tests.

* PR Feedback.


Commit migrated from https://github.com/dotnet/core-setup/commit/7544cb6e71d3dc34c9b7e0a20a25a8bccd8e3f6e
上级 ad8e1d33
......@@ -25,15 +25,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtils", "src\test\TestU
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel", "src\managed\Microsoft.NET.HostModel\Microsoft.NET.HostModel.csproj", "{325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppHost.Bundle.Tests", "src\test\BundleTests\AppHost.Bundle.Tests\AppHost.Bundle.Tests.csproj", "{2745A51D-3425-4F68-8349-A8B8BC27DD87}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppHost.Bundle.Tests", "src\test\Microsoft.NET.HostModel.Tests\AppHost.Bundle.Tests\AppHost.Bundle.Tests.csproj", "{2745A51D-3425-4F68-8349-A8B8BC27DD87}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel.Bundle.Tests", "src\test\BundleTests\Microsoft.NET.HostModel.Bundle.Tests\Microsoft.NET.HostModel.Bundle.Tests.csproj", "{1E76A78E-9E39-480D-8CD3-B7D0A858FECB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel.Bundle.Tests", "src\test\Microsoft.NET.HostModel.Tests\Microsoft.NET.HostModel.Bundle.Tests\Microsoft.NET.HostModel.Bundle.Tests.csproj", "{1E76A78E-9E39-480D-8CD3-B7D0A858FECB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BundleHelper", "src\test\BundleTests\Helpers\BundleHelper.csproj", "{8116F946-FB24-4524-9028-43F5A3A80EE3}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BundleHelper", "src\test\Microsoft.NET.HostModel.Tests\Helpers\BundleHelper.csproj", "{8116F946-FB24-4524-9028-43F5A3A80EE3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.CoreSetup.Packaging.Tests", "src\test\Microsoft.DotNet.CoreSetup.Packaging.Tests\Microsoft.DotNet.CoreSetup.Packaging.Tests.csproj", "{F1584324-A41A-4CE7-B23F-DB2252CFE276}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel.AppHost.Tests", "src\test\BundleTests\Microsoft.NET.HostModel.AppHost.Tests\Microsoft.NET.HostModel.AppHost.Tests.csproj", "{AA3430BA-AE4A-4D52-B29D-B072A646C230}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel.AppHost.Tests", "src\test\Microsoft.NET.HostModel.Tests\Microsoft.NET.HostModel.AppHost.Tests\Microsoft.NET.HostModel.AppHost.Tests.csproj", "{AA3430BA-AE4A-4D52-B29D-B072A646C230}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel.ComHost.Tests", "src\test\Microsoft.NET.HostModel.Tests\Microsoft.NET.HostModel.ComHost.Tests\Microsoft.NET.HostModel.ComHost.Tests.csproj", "{35EF7826-6D47-4C91-AFDF-91091C05FB13}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
......@@ -223,6 +225,22 @@ Global
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.Debug|Any CPU.Build.0 = Debug|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.Debug|x64.ActiveCfg = Debug|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.Debug|x64.Build.0 = Debug|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.Release|Any CPU.ActiveCfg = Release|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.Release|Any CPU.Build.0 = Release|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.Release|x64.ActiveCfg = Release|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.Release|x64.Build.0 = Release|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{35EF7826-6D47-4C91-AFDF-91091C05FB13}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -239,6 +257,7 @@ Global
{8116F946-FB24-4524-9028-43F5A3A80EE3} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
{F1584324-A41A-4CE7-B23F-DB2252CFE276} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
{AA3430BA-AE4A-4D52-B29D-B072A646C230} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
{35EF7826-6D47-4C91-AFDF-91091C05FB13} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {28B9726D-802B-478D-AF7A-B9243B9E180B}
......
......@@ -119,9 +119,10 @@
Description="The test projects. Note that building this doesn't execute tests: you must also pass the '-test' argument." />
</ItemGroup>
<ItemGroup Condition="'$(SubsetToLower)' == '' or $(SubsetToLower.Contains('test'))">
<TestProjectToBuild Include="$(RepoRoot)src\test\BundleTests\AppHost.Bundle.Tests\AppHost.Bundle.Tests.csproj" />
<TestProjectToBuild Include="$(RepoRoot)src\test\BundleTests\Microsoft.NET.HostModel.AppHost.Tests\Microsoft.NET.HostModel.AppHost.Tests.csproj" />
<TestProjectToBuild Include="$(RepoRoot)src\test\BundleTests\Microsoft.NET.HostModel.Bundle.Tests\Microsoft.NET.HostModel.Bundle.Tests.csproj" />
<TestProjectToBuild Include="$(RepoRoot)src\test\Microsoft.NET.HostModel.Tests\AppHost.Bundle.Tests\AppHost.Bundle.Tests.csproj" />
<TestProjectToBuild Include="$(RepoRoot)src\test\Microsoft.NET.HostModel.Tests\Microsoft.NET.HostModel.AppHost.Tests\Microsoft.NET.HostModel.AppHost.Tests.csproj" />
<TestProjectToBuild Include="$(RepoRoot)src\test\Microsoft.NET.HostModel.Tests\Microsoft.NET.HostModel.Bundle.Tests\Microsoft.NET.HostModel.Bundle.Tests.csproj" />
<TestProjectToBuild Include="$(RepoRoot)src\test\Microsoft.NET.HostModel.Tests\Microsoft.NET.HostModel.ComHost.Tests\Microsoft.NET.HostModel.ComHost.Tests.csproj" />
<TestProjectToBuild Include="$(RepoRoot)src\test\HostActivation.Tests\HostActivation.Tests.csproj" />
<TestProjectToBuild Include="$(RepoRoot)src\test\Microsoft.DotNet.CoreSetup.Packaging.Tests\Microsoft.DotNet.CoreSetup.Packaging.Tests.csproj" />
<TestProjectToBuild Include="$(RepoRoot)src\test\Microsoft.Extensions.DependencyModel.Tests\Microsoft.Extensions.DependencyModel.Tests.csproj" />
......
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.NET.HostModel.ComHost
{
public static class ClsidMap
{
private struct ClsidEntry
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("assembly")]
public string Assembly { get; set; }
[JsonPropertyName("progid")]
public string ProgId { get; set; }
}
public static void Create(MetadataReader metadataReader, string clsidMapPath)
{
Dictionary<string, ClsidEntry> clsidMap = new Dictionary<string, ClsidEntry>();
string assemblyName = GetAssemblyName(metadataReader).FullName;
bool isAssemblyComVisible = IsComVisible(metadataReader, metadataReader.GetAssemblyDefinition());
foreach (TypeDefinitionHandle type in metadataReader.TypeDefinitions)
{
TypeDefinition definition = metadataReader.GetTypeDefinition(type);
// Only public COM-visible classes can be exposed via the COM host.
if (TypeIsPublic(metadataReader, definition) && TypeIsClass(metadataReader, definition) && IsComVisible(metadataReader, definition, isAssemblyComVisible))
{
Guid guid = GetTypeGuid(metadataReader, definition);
string guidString = GetTypeGuid(metadataReader, definition).ToString("B");
if (clsidMap.ContainsKey(guidString))
{
throw new ConflictingGuidException(clsidMap[guidString].Type, GetTypeName(metadataReader, definition), guid);
}
string progId = GetProgId(metadataReader, definition);
clsidMap.Add(guidString,
new ClsidEntry
{
Type = GetTypeName(metadataReader, definition),
Assembly = assemblyName,
ProgId = !string.IsNullOrWhiteSpace(progId) ? progId : null
});
}
}
using (StreamWriter writer = File.CreateText(clsidMapPath))
{
writer.Write(JsonSerializer.Serialize(clsidMap, new JsonSerializerOptions { IgnoreNullValues = true }));
}
}
private static bool TypeIsClass(MetadataReader metadataReader, TypeDefinition definition)
{
if ((definition.Attributes & TypeAttributes.Interface) != 0)
{
return false;
}
EntityHandle baseTypeEntity = definition.BaseType;
if (baseTypeEntity.Kind == HandleKind.TypeReference)
{
TypeReference baseClass = metadataReader.GetTypeReference((TypeReferenceHandle)baseTypeEntity);
if (baseClass.ResolutionScope.Kind == HandleKind.AssemblyReference)
{
if (HasTypeName(metadataReader, baseClass, "System", "ValueType") || HasTypeName(metadataReader, baseClass, "System", "Enum"))
{
return false;
}
}
}
return true;
}
private static bool TypeIsPublic(MetadataReader reader, TypeDefinition type)
{
switch (type.Attributes & TypeAttributes.VisibilityMask)
{
case TypeAttributes.Public:
return true;
case TypeAttributes.NestedPublic:
return TypeIsPublic(reader, reader.GetTypeDefinition(type.GetDeclaringType()));
default:
return false;
}
}
private static string GetTypeName(MetadataReader metadataReader, TypeDefinition type)
{
if (!type.GetDeclaringType().IsNil)
{
return $"{GetTypeName(metadataReader, metadataReader.GetTypeDefinition(type.GetDeclaringType()))}+{metadataReader.GetString(type.Name)}";
}
return $"{metadataReader.GetString(type.Namespace)}{Type.Delimiter}{metadataReader.GetString(type.Name)}";
}
private static bool HasTypeName(MetadataReader metadataReader, TypeReference type, string ns, string name)
{
return metadataReader.StringComparer.Equals(type.Namespace, ns) && metadataReader.StringComparer.Equals(type.Name, name);
}
private static AssemblyName GetAssemblyName(MetadataReader metadataReader)
{
AssemblyName name = new AssemblyName();
AssemblyDefinition definition = metadataReader.GetAssemblyDefinition();
name.Name = metadataReader.GetString(definition.Name);
name.Version = definition.Version;
name.CultureInfo = CultureInfo.GetCultureInfo(metadataReader.GetString(definition.Culture));
name.SetPublicKey(metadataReader.GetBlobBytes(definition.PublicKey));
return name;
}
private static bool IsComVisible(MetadataReader reader, AssemblyDefinition assembly)
{
CustomAttributeHandle handle = GetComVisibleAttribute(reader, assembly.GetCustomAttributes());
if (handle.IsNil)
{
return false;
}
CustomAttribute comVisibleAttribute = reader.GetCustomAttribute(handle);
CustomAttributeValue<KnownType> data = comVisibleAttribute.DecodeValue(new TypeResolver());
return (bool)data.FixedArguments[0].Value;
}
private static bool IsComVisible(MetadataReader metadataReader, TypeDefinition definition, bool assemblyComVisible)
{
// We need to ensure that all parent scopes of the given type are not explicitly non-ComVisible.
bool? IsComVisibleCore(TypeDefinition typeDefinition)
{
CustomAttributeHandle handle = GetComVisibleAttribute(metadataReader, typeDefinition.GetCustomAttributes());
if (handle.IsNil)
{
return null;
}
CustomAttribute comVisibleAttribute = metadataReader.GetCustomAttribute(handle);
CustomAttributeValue<KnownType> data = comVisibleAttribute.DecodeValue(new TypeResolver());
return (bool)data.FixedArguments[0].Value;
}
if (!definition.GetDeclaringType().IsNil)
{
return IsComVisible(metadataReader, metadataReader.GetTypeDefinition(definition.GetDeclaringType()), assemblyComVisible) && (IsComVisibleCore(definition) ?? assemblyComVisible);
}
return IsComVisibleCore(definition) ?? assemblyComVisible;
}
private static CustomAttributeHandle GetComVisibleAttribute(MetadataReader reader, CustomAttributeHandleCollection customAttributes)
{
foreach (CustomAttributeHandle attr in customAttributes)
{
CustomAttribute attribute = reader.GetCustomAttribute(attr);
MemberReference attributeConstructor = reader.GetMemberReference((MemberReferenceHandle)attribute.Constructor);
TypeReference attributeType = reader.GetTypeReference((TypeReferenceHandle)attributeConstructor.Parent);
if (reader.StringComparer.Equals(attributeType.Namespace, "System.Runtime.InteropServices") && reader.StringComparer.Equals(attributeType.Name, "ComVisibleAttribute"))
{
return attr;
}
}
return new CustomAttributeHandle();
}
private static Guid GetTypeGuid(MetadataReader reader, TypeDefinition type)
{
// Find the class' GUID by reading the GuidAttribute value.
// We do not support implicit runtime-generated GUIDs for the .NET Core COM host.
foreach (CustomAttributeHandle attr in type.GetCustomAttributes())
{
CustomAttribute attribute = reader.GetCustomAttribute(attr);
MemberReference attributeConstructor = reader.GetMemberReference((MemberReferenceHandle)attribute.Constructor);
TypeReference attributeType = reader.GetTypeReference((TypeReferenceHandle)attributeConstructor.Parent);
if (reader.StringComparer.Equals(attributeType.Namespace, "System.Runtime.InteropServices") && reader.StringComparer.Equals(attributeType.Name, "GuidAttribute"))
{
CustomAttributeValue<KnownType> data = attribute.DecodeValue(new TypeResolver());
return Guid.Parse((string)data.FixedArguments[0].Value);
}
}
throw new MissingGuidException(GetTypeName(reader, type));
}
private static string GetProgId(MetadataReader reader, TypeDefinition type)
{
foreach (CustomAttributeHandle attr in type.GetCustomAttributes())
{
CustomAttribute attribute = reader.GetCustomAttribute(attr);
MemberReference attributeConstructor = reader.GetMemberReference((MemberReferenceHandle)attribute.Constructor);
TypeReference attributeType = reader.GetTypeReference((TypeReferenceHandle)attributeConstructor.Parent);
if (reader.StringComparer.Equals(attributeType.Namespace, "System.Runtime.InteropServices") && reader.StringComparer.Equals(attributeType.Name, "ProgIdAttribute"))
{
CustomAttributeValue<KnownType> data = attribute.DecodeValue(new TypeResolver());
return (string)data.FixedArguments[0].Value;
}
}
return GetTypeName(reader, type);
}
private enum KnownType
{
Bool,
String,
SystemType,
Unknown
}
private class TypeResolver : ICustomAttributeTypeProvider<KnownType>
{
public KnownType GetPrimitiveType(PrimitiveTypeCode typeCode)
{
switch (typeCode)
{
case PrimitiveTypeCode.Boolean:
return KnownType.Bool;
case PrimitiveTypeCode.String:
return KnownType.String;
default:
return KnownType.Unknown;
}
}
public KnownType GetSystemType()
{
return KnownType.SystemType;
}
public KnownType GetSZArrayType(KnownType elementType)
{
return KnownType.Unknown;
}
public KnownType GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
{
return KnownType.Unknown;
}
public KnownType GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
{
return KnownType.Unknown;
}
public KnownType GetTypeFromSerializedName(string name)
{
return KnownType.Unknown;
}
public PrimitiveTypeCode GetUnderlyingEnumType(KnownType type)
{
throw new BadImageFormatException("Unexpectedly got an enum parameter for an attribute.");
}
public bool IsSystemType(KnownType type)
{
return type == KnownType.SystemType;
}
}
}
}
using System;
using System.IO;
using System.Text;
namespace Microsoft.NET.HostModel.ComHost
{
class ComHost
{
// These need to match RESOURCEID_CLISDMAP and RESOURCETYPE_CLSIDMAP defined in comhost.h.
private const int ClsidmapResourceId = 64;
private const int ClsidmapResourceType = 1024;
/// <summary>
/// Create an ComHost with an embedded CLSIDMap file to map CLSIDs to .NET Classes.
/// </summary>
/// <param name="comHostSourceFilePath">The path of Apphost template, which has the place holder</param>
/// <param name="comHostDestinationFilePath">The destination path for desired location to place, including the file name</param>
/// <param name="clsidmapFilePath">The path to the *.clsidmap file.</param>
public static void Create(
string comHostSourceFilePath,
string comHostDestinationFilePath,
string clsidmapFilePath)
{
var destinationDirectory = new FileInfo(comHostDestinationFilePath).Directory.FullName;
if (!Directory.Exists(destinationDirectory))
{
Directory.CreateDirectory(destinationDirectory);
}
// Copy apphost to destination path so it inherits the same attributes/permissions.
File.Copy(comHostSourceFilePath, comHostDestinationFilePath, overwrite: true);
if (!ResourceUpdater.IsSupportedOS())
{
throw new ComHostCustomizationUnsupportedOSException();
}
string clsidMap = File.ReadAllText(clsidmapFilePath);
byte[] clsidMapBytes = Encoding.UTF8.GetBytes(clsidMap);
using (ResourceUpdater updater = new ResourceUpdater(comHostDestinationFilePath))
{
updater.AddResource(clsidMapBytes, (IntPtr)ClsidmapResourceType, (IntPtr)ClsidmapResourceId);
updater.Update();
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Microsoft.NET.HostModel.ComHost
{
/// <summary>
/// The application host executable cannot be customized because adding resources requires
/// that the build be performed on Windows (excluding Nano Server).
/// </summary>
public class ComHostCustomizationUnsupportedOSException : Exception
{
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.NET.HostModel.ComHost
{
/// <summary>
/// The same Guid has been specified for two public ComVisible classes in the assembly.
/// </summary>
public class ConflictingGuidException : Exception
{
public ConflictingGuidException(string typeName1, string typeName2, Guid guid)
{
if (typeName1 is null)
{
throw new ArgumentNullException(nameof(typeName1));
}
if (typeName2 is null)
{
throw new ArgumentNullException(nameof(typeName2));
}
TypeName1 = typeName1;
TypeName2 = typeName2;
Guid = guid;
}
public string TypeName1 { get; }
public string TypeName2 { get; }
public Guid Guid { get; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.NET.HostModel.ComHost
{
/// <summary>
/// The type <see cref="TypeName"/> is public and ComVisible but does not have a <see cref="System.Runtime.InteropServices.GuidAttribute"/> attribute.
/// </summary>
public class MissingGuidException : Exception
{
public MissingGuidException(string typeName)
{
if (typeName is null)
{
throw new ArgumentNullException(nameof(typeName));
}
TypeName = typeName;
}
public string TypeName { get; }
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Xml;
using System.Xml.Linq;
namespace Microsoft.NET.HostModel.ComHost
{
public class RegFreeComManifest
{
/// <summary>
/// Generates a side-by-side application manifest to enable reg-free COM.
/// </summary>
/// <param name="assemblyName">The name of the assembly.</param>
/// <param name="comHostName">The name of the comhost library.</param>
/// <param name="assemblyVersion">The version of the assembly.</param>
/// <param name="clsidMapPath">The path to the clsidmap file.</param>
/// <param name="comManifestPath">The path to which to write the manifest.</param>
public static void CreateManifestFromClsidmap(string assemblyName, string comHostName, string assemblyVersion, string clsidMapPath, string comManifestPath)
{
XNamespace ns = "urn:schemas-microsoft-com:asm.v1";
XElement manifest = new XElement(ns + "assembly", new XAttribute("manifestVersion", "1.0"));
manifest.Add(new XElement(ns + "assemblyIdentity",
new XAttribute("type", "win32"),
new XAttribute("name", $"{assemblyName}.X"),
new XAttribute("version", assemblyVersion)));
XElement fileElement = new XElement(ns + "file", new XAttribute("name", comHostName));
JsonElement clsidMap;
using (FileStream clsidMapStream = File.OpenRead(clsidMapPath))
{
clsidMap = JsonDocument.Parse(clsidMapStream).RootElement;
}
foreach (JsonProperty property in clsidMap.EnumerateObject())
{
string guidMaybe = property.Name;
Guid guid = Guid.Parse(guidMaybe);
XElement comClassElement = new XElement(ns + "comClass", new XAttribute("clsid", guid.ToString("B")), new XAttribute("threadingModel", "Both"));
if (property.Value.TryGetProperty("progid", out JsonElement progIdValue))
{
comClassElement.Add(new XAttribute("progid", progIdValue.GetString()));
}
fileElement.Add(comClassElement);
}
manifest.Add(fileElement);
XDocument manifestDocument = new XDocument(new XDeclaration("1.0", "UTF-8", "yes"), manifest);
XmlWriterSettings settings = new XmlWriterSettings()
{
Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)
};
using (XmlWriter manifestWriter = XmlWriter.Create(comManifestPath, settings))
{
manifestDocument.WriteTo(manifestWriter);
}
}
}
}
......@@ -15,6 +15,7 @@
<PackageReference Include="System.Reflection.Metadata">
<Version>1.5.0</Version>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="$(SystemTextJsonVersion)" />
</ItemGroup>
</Project>
......
......@@ -12,4 +12,45 @@ public Server()
Console.WriteLine($"New instance of {nameof(Server)} created");
}
}
[Guid("6e30943e-b8ab-4e02-a904-9f1b5bb1c97d")]
public class NotComVisible
{
}
[ComVisible(true)]
[Guid("36e75747-aecd-43bf-9082-1a605889c762")]
public class ComVisible
{
[ComVisible(true)]
[Guid("c82e4585-58bd-46e0-a76d-c0b6975e5984")]
public class Nested
{
}
}
[ComVisible(true)]
[Guid("cf55ff0a-19a6-45a6-9aea-52597be13fb5")]
internal class ComVisibleNonPublic
{
[ComVisible(true)]
[Guid("8a0a7085-aca4-4651-9878-ca42747e2206")]
public class Nested
{
}
}
[ComVisible(true)]
[Guid("f5ad253b-845e-4c91-95a7-3ff2fa0c91cd")]
[ProgId("CustomProgId")]
public class ComVisibleCustomProgId
{
}
[ComVisible(true)]
[Guid("4c8bd844-593d-43cb-b605-f0bc52f674fa")]
[ProgId("")]
public class ExplicitNoProgId
{
}
}
\ No newline at end of file
using System;
using System.Runtime.InteropServices;
namespace ComLibrary
{
// These two types must have the same GUID for testing.
[ComVisible(true)]
[Guid("cc6e9910-18d5-484a-a2d2-fa8910fd0261")]
public class Server
{
public Server()
{
Console.WriteLine($"New instance of {nameof(Server)} created");
}
}
[ComVisible(true)]
[Guid("cc6e9910-18d5-484a-a2d2-fa8910fd0261")]
public class Server2
{
public Server2()
{
Console.WriteLine($"New instance of {nameof(Server2)} created");
}
}
}
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(NETCoreAppFramework)</TargetFramework>
<RuntimeFrameworkVersion>$(MNAVersion)</RuntimeFrameworkVersion>
</PropertyGroup>
</Project>
using System;
using System.Runtime.InteropServices;
namespace ComLibrary
{
[ComVisible(true)]
public class Server
{
public Server()
{
Console.WriteLine($"New instance of {nameof(Server)} created");
}
}
}
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(NETCoreAppFramework)</TargetFramework>
<RuntimeFrameworkVersion>$(MNAVersion)</RuntimeFrameworkVersion>
</PropertyGroup>
</Project>
using Microsoft.DotNet.CoreSetup.Test;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text;
using Xunit;
namespace Microsoft.NET.HostModel.ComHost.Tests
{
public class ClsidMapTests : IClassFixture<ClsidMapTests.SharedTestState>
{
private readonly SharedTestState sharedTestState;
public ClsidMapTests(SharedTestState fixture)
{
sharedTestState = fixture;
}
[Fact]
public void PublicComVisibleTypeWithGuidAdded()
{
JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture);
JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ComVisibleGuid);
Assert.NotNull(comVisibleEntry);
JObject entry = (JObject)comVisibleEntry.Value;
Assert.Equal(SharedTestState.ComVisibleTypeName, entry.Property("type").Value.ToString());
Assert.Equal(SharedTestState.ComVisibleAssemblyName, entry.Property("assembly").Value.ToString());
}
[Fact]
public void PublicComVisibleTypeWithoutGuidThrows()
{
var exception = Assert.Throws<MissingGuidException>(() => CreateClsidMap(sharedTestState.ComLibraryMissingGuidFixture));
Assert.Equal(SharedTestState.MissingGuidTypeName, exception.TypeName);
}
[Fact]
public void PublicNestedTypeOfPublicTypeAdded()
{
JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture);
JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ComVisibleNestedGuid);
Assert.NotNull(comVisibleEntry);
JObject entry = (JObject)comVisibleEntry.Value;
Assert.Equal(SharedTestState.ComVisibleNestedTypeName, entry.Property("type").Value.ToString());
}
[Fact]
public void NonPublicTypeNotAdded()
{
JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture);
JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ComVisibleNonPublicGuid);
Assert.Null(comVisibleEntry);
}
[Fact]
public void PublicNestedTypeOfNonPublicTypeNotAdded()
{
JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture);
JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ComVisibleNonPublicNestedGuid);
Assert.Null(comVisibleEntry);
}
[Fact]
public void PublicComVisibleTypeWithDuplicateGuidThrows()
{
var exception = Assert.Throws<ConflictingGuidException>(() => CreateClsidMap(sharedTestState.ComLibraryConflictingGuidFixture));
Assert.Equal(Guid.Parse(SharedTestState.ConflictingGuid), exception.Guid);
Assert.Equal(SharedTestState.ConflictingGuidTypeName1, exception.TypeName1);
Assert.Equal(SharedTestState.ConflictingGuidTypeName2, exception.TypeName2);
}
[Fact]
public void DefaultProgIdIsTypeName()
{
JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture);
JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ComVisibleGuid);
Assert.NotNull(comVisibleEntry);
JObject entry = (JObject)comVisibleEntry.Value;
Assert.Equal(SharedTestState.ComVisibleTypeName, entry.Property("progid").Value.ToString());
}
[Fact]
public void ExplicitProgIdUsed()
{
JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture);
JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ComVisibleCustomProgIdGuid);
Assert.NotNull(comVisibleEntry);
JObject entry = (JObject)comVisibleEntry.Value;
Assert.Equal(SharedTestState.ComVisibleCustomProgIdProgId, entry.Property("progid").Value.ToString());
}
[Fact]
public void ExplicitlyEmptyProgIdNotInClsidMap()
{
JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture);
JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ExplicitNoProgIdGuid);
Assert.NotNull(comVisibleEntry);
JObject entry = (JObject)comVisibleEntry.Value;
Assert.Null(entry.Property("progid"));
}
private JObject CreateClsidMap(TestProjectFixture project)
{
using var testDirectory = TestDirectory.Create();
string clsidMapPath = Path.Combine(testDirectory.Path, "test.clsidmap");
using (var assemblyStream = new FileStream(project.TestProject.AppDll, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read))
using (PEReader peReader = new PEReader(assemblyStream))
{
if (peReader.HasMetadata)
{
MetadataReader reader = peReader.GetMetadataReader();
ClsidMap.Create(reader, clsidMapPath);
}
}
using (var clsidMapFile = File.OpenText(clsidMapPath))
using (var clsidMapReader = new JsonTextReader(clsidMapFile))
{
return JObject.Load(clsidMapReader);
}
}
public class SharedTestState : IDisposable
{
public const string ComVisibleAssemblyName = "ComLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
public const string NotComVisibleGuid = "{6e30943e-b8ab-4e02-a904-9f1b5bb1c97d}";
public const string ComVisibleGuid = "{36e75747-aecd-43bf-9082-1a605889c762}";
public const string ComVisibleTypeName = "ComLibrary.ComVisible";
public const string ComVisibleNestedGuid = "{c82e4585-58bd-46e0-a76d-c0b6975e5984}";
public const string ComVisibleNestedTypeName = "ComLibrary.ComVisible+Nested";
public const string ComVisibleNonPublicGuid = "{cf55ff0a-19a6-45a6-9aea-52597be13fb5}";
public const string ComVisibleNonPublicNestedGuid = "{8a0a7085-aca4-4651-9878-ca42747e2206}";
public const string ComVisibleCustomProgIdGuid = "{f5ad253b-845e-4c91-95a7-3ff2fa0c91cd}";
public const string ComVisibleCustomProgIdProgId = "CustomProgId";
public const string ExplicitNoProgIdGuid = "{4c8bd844-593d-43cb-b605-f0bc52f674fa}";
public const string MissingGuidTypeName = "ComLibrary.Server";
public const string ConflictingGuidTypeName1 = "ComLibrary.Server";
public const string ConflictingGuidTypeName2 = "ComLibrary.Server2";
public const string ConflictingGuid = "{cc6e9910-18d5-484a-a2d2-fa8910fd0261}";
public SharedTestState()
{
RepoDirectories = new RepoDirectoriesProvider();
ComLibraryFixture = new TestProjectFixture("ComLibrary", RepoDirectories)
.EnsureRestored(RepoDirectories.CorehostPackages)
.BuildProject();
ComLibraryMissingGuidFixture = new TestProjectFixture("ComLibraryMissingGuid", RepoDirectories)
.EnsureRestored(RepoDirectories.CorehostPackages)
.BuildProject();
ComLibraryConflictingGuidFixture = new TestProjectFixture("ComLibraryConflictingGuid", RepoDirectories)
.EnsureRestored(RepoDirectories.CorehostPackages)
.BuildProject();
}
public RepoDirectoriesProvider RepoDirectories { get; }
public TestProjectFixture ComLibraryFixture { get; }
public TestProjectFixture ComLibraryMissingGuidFixture { get; }
public TestProjectFixture ComLibraryConflictingGuidFixture { get; }
public void Dispose()
{
ComLibraryFixture.Dispose();
ComLibraryMissingGuidFixture.Dispose();
ComLibraryConflictingGuidFixture.Dispose();
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Microsoft.NET.HostModel.ComHost Tests</Description>
<TargetFramework>$(TestInfraTargetFramework)</TargetFramework>
<AssemblyName>Microsoft.NET.HostModel.ComHost.Tests</AssemblyName>
<PackageId>Microsoft.NET.HostModel.ComHost.Tests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<!-- Reduce the length of the test output dir to make it more reliable on Windows. -->
<TestsOutputName>hmc</TestsOutputName>
<UsesTestAssets>true</UsesTestAssets>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\TestUtils\TestUtils.csproj" />
<ProjectReference Include="..\..\..\managed\Microsoft.NET.HostModel\Microsoft.NET.HostModel.csproj" />
</ItemGroup>
</Project>
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Xunit;
namespace Microsoft.NET.HostModel.ComHost.Tests
{
public class RegFreeComManifestTests
{
private static XNamespace regFreeComManifestNamespace = "urn:schemas-microsoft-com:asm.v1";
[Fact]
public void RegFreeComManifestCorrectlyIncludesComHostFile()
{
using TestDirectory directory = TestDirectory.Create();
JObject clsidMap = new JObject
{
};
string clsidmapPath = Path.Combine(directory.Path, "test.clsidmap");
string json = JsonConvert.SerializeObject(clsidMap);
string comHostName = "comhost.dll";
File.WriteAllText(clsidmapPath, json);
string regFreeComManifestPath = Path.Combine(directory.Path, "test.manifest");
RegFreeComManifest.CreateManifestFromClsidmap("assemblyName", comHostName, "1.0.0.0", clsidmapPath, regFreeComManifestPath);
using FileStream manifestStream = File.OpenRead(regFreeComManifestPath);
XElement manifest = XElement.Load(manifestStream);
XElement fileElement = manifest.Element(regFreeComManifestNamespace + "file");
Assert.NotNull(fileElement);
Assert.Equal(comHostName, fileElement.Attribute("name").Value);
}
[Fact]
public void EntryInClsidMapAddedToRegFreeComManifest()
{
using TestDirectory directory = TestDirectory.Create();
string guid = "{190f1974-fa98-4922-8ed4-cf748630abbe}";
string assemblyName = "ComLibrary";
string typeName = "ComLibrary.Server";
string assemblyVersion = "1.0.0.0";
JObject clsidMap = new JObject
{
{
guid,
new JObject() { {"assembly", assemblyName }, {"type", typeName } }
}
};
string clsidmapPath = Path.Combine(directory.Path, "test.clsidmap");
string json = JsonConvert.SerializeObject(clsidMap);
string comHostName = "comhost.dll";
File.WriteAllText(clsidmapPath, json);
string regFreeComManifestPath = Path.Combine(directory.Path, "test.manifest");
RegFreeComManifest.CreateManifestFromClsidmap(assemblyName, comHostName, assemblyVersion, clsidmapPath, regFreeComManifestPath);
using FileStream manifestStream = File.OpenRead(regFreeComManifestPath);
XElement manifest = XElement.Load(manifestStream);
XElement fileElement = manifest.Element(regFreeComManifestNamespace + "file");
Assert.Single(fileElement.Elements(regFreeComManifestNamespace + "comClass").Where(cls => cls.Attribute("clsid").Value == guid));
}
[Fact]
public void EntryInClsidMapAddedToRegFreeComManifestIncludesProgId()
{
using TestDirectory directory = TestDirectory.Create();
string guid = "{190f1974-fa98-4922-8ed4-cf748630abbe}";
string assemblyName = "ComLibrary";
string typeName = "ComLibrary.Server";
string progId = "CustomProgId";
string assemblyVersion = "1.0.0.0";
JObject clsidMap = new JObject
{
{
guid,
new JObject() { {"assembly", assemblyName }, {"type", typeName }, { "progid", progId } }
}
};
string clsidmapPath = Path.Combine(directory.Path, "test.clsidmap");
string json = JsonConvert.SerializeObject(clsidMap);
string comHostName = "comhost.dll";
File.WriteAllText(clsidmapPath, json);
string regFreeComManifestPath = Path.Combine(directory.Path, "test.manifest");
RegFreeComManifest.CreateManifestFromClsidmap(assemblyName, comHostName, assemblyVersion, clsidmapPath, regFreeComManifestPath);
using FileStream manifestStream = File.OpenRead(regFreeComManifestPath);
XElement manifest = XElement.Load(manifestStream);
XElement fileElement = manifest.Element(regFreeComManifestNamespace + "file");
Assert.Single(fileElement.Elements(regFreeComManifestNamespace + "comClass").Where(cls => cls.Attribute("clsid").Value == guid && cls.Attribute("progid").Value == progId));
}
}
}
using System;
using System.IO;
using System.Runtime.CompilerServices;
namespace Microsoft.NET.HostModel.ComHost.Tests
{
internal class TestDirectory : IDisposable
{
public string Path { get; private set; }
private TestDirectory(string path)
{
Path = path;
Directory.CreateDirectory(path);
}
public static TestDirectory Create([CallerMemberName] string callingMethod = "")
{
string path = System.IO.Path.Combine(
System.IO.Path.GetTempPath(),
"dotNetSdkUnitTest_" + callingMethod + (Guid.NewGuid().ToString().Substring(0, 8)));
return new TestDirectory(path);
}
public void Dispose()
{
if (Directory.Exists(Path))
{
Directory.Delete(Path, true);
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册