|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.Utilities;
// TYPELIBATTR clashes with the one in InteropServices.
using TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR;
#nullable disable
namespace Microsoft.Build.Tasks
{
/// <summary>
/// Internal class representing information about a COM reference.
/// </summary>
internal class ComReferenceInfo
{
#region Properties
/// <summary>
/// ITypeLib pointer
/// </summary>
internal ITypeLib typeLibPointer;
/// <summary>
/// type library attributes for the reference. Taken from the task item itself or type library if
/// reference is specified as file on disk.
/// </summary>
internal TYPELIBATTR attr;
/// <summary>
/// type library name
/// </summary>
internal string typeLibName;
/// <summary>
/// path to the reference, with typelibrary number stripped, if any (so ref1.dll\2 becomes ref1.dll).
/// The full path is only used for loading the type library, and it's not necessary
/// to do it after the interface pointer is cached in this object.
/// </summary>
internal string strippedTypeLibPath;
/// <summary>
/// When using TlbImp.exe, we need to make sure that we keep track of the non-stripped typelib path,
/// because that's what we need to pass to TlbImp.
/// </summary>
internal string fullTypeLibPath;
/// <summary>
/// reference to the original ITaskItem, if any
/// </summary>
internal ITaskItem taskItem;
/// <summary>
/// Path to the resolved reference.
/// </summary>
internal ComReferenceInfo primaryOfAxImpRef;
/// <summary>
/// The wrapper that resulted from resolving the COM reference.
/// </summary>
internal ComReferenceWrapperInfo resolvedWrapper;
/// <summary>
/// List of the paths to COM wrapper assemblies that this reference is dependent upon.
/// </summary>
internal List<string> dependentWrapperPaths;
/// <summary>
/// Reference to the ITaskItem generated from the resolved reference, if any.
/// </summary>
internal ITaskItem referencePathItem;
#endregion
#region Constructors
/// <summary>
/// Default constructor
/// </summary>
internal ComReferenceInfo()
{
this.dependentWrapperPaths = new List<string>();
}
/// <summary>
/// Construct a new ComReferenceInfo copying all state from the given ComReferenceInfo instance
/// </summary>
internal ComReferenceInfo(ComReferenceInfo copyFrom)
{
this.attr = copyFrom.attr;
this.typeLibName = copyFrom.typeLibName;
this.strippedTypeLibPath = copyFrom.strippedTypeLibPath;
this.fullTypeLibPath = copyFrom.fullTypeLibPath;
this.typeLibPointer = copyFrom.typeLibPointer;
this.primaryOfAxImpRef = copyFrom.primaryOfAxImpRef;
this.resolvedWrapper = copyFrom.resolvedWrapper;
this.taskItem = new TaskItem(copyFrom.taskItem);
this.dependentWrapperPaths = copyFrom.dependentWrapperPaths;
this.referencePathItem = copyFrom.referencePathItem;
}
#endregion
#region Methods
/// <summary>
/// Initialize the object with type library attributes
/// </summary>
internal bool InitializeWithTypeLibAttrs(TaskLoggingHelper log, bool silent, TYPELIBATTR tlbAttr, ITaskItem originalTaskItem, string targetProcessorArchitecture)
{
TYPELIBATTR remappableTlbAttr = tlbAttr;
ComReference.RemapAdoTypeLib(log, silent, ref remappableTlbAttr);
// for attribute references, the path is not specified, so we need to get it from the registry
if (!ComReference.GetPathOfTypeLib(log, silent, ref remappableTlbAttr, out this.fullTypeLibPath))
{
return false;
}
// Now that we have the path, we can call InitializeWithPath to get the correct TYPELIBATTR set up
// and the correct ITypeLib pointer.
return InitializeWithPath(log, silent, this.fullTypeLibPath, originalTaskItem, targetProcessorArchitecture);
}
/// <summary>
/// Initialize the object with a type library path
/// </summary>
internal bool InitializeWithPath(TaskLoggingHelper log, bool silent, string path, ITaskItem originalTaskItem, string targetProcessorArchitecture)
{
ErrorUtilities.VerifyThrowArgumentNull(path);
this.taskItem = originalTaskItem;
// Note that currently we DO NOT remap file ADO references. This is because when pointing to a file on disk,
// it seems unnatural to remap it to something else - a file reference means "use THIS component".
// This is still under debate though, and may be revised later.
// save both the stripped and full path in our object -- for the most part we just need the stripped path, but if
// we're using tlbimp.exe, we need to pass the full path w/ type lib number to it, or it won't generate the interop
// assembly correctly.
this.fullTypeLibPath = path;
this.strippedTypeLibPath = ComReference.StripTypeLibNumberFromPath(path, File.Exists);
// use the unstripped path to actually load the library
switch (targetProcessorArchitecture)
{
case ProcessorArchitecture.AMD64:
case ProcessorArchitecture.IA64:
this.typeLibPointer = (ITypeLib)NativeMethods.LoadTypeLibEx(path, (int)NativeMethods.REGKIND.REGKIND_NONE | (int)NativeMethods.REGKIND.REGKIND_LOAD_TLB_AS_64BIT);
break;
case ProcessorArchitecture.X86:
this.typeLibPointer = (ITypeLib)NativeMethods.LoadTypeLibEx(path, (int)NativeMethods.REGKIND.REGKIND_NONE | (int)NativeMethods.REGKIND.REGKIND_LOAD_TLB_AS_32BIT);
break;
case ProcessorArchitecture.ARM:
case ProcessorArchitecture.MSIL:
default:
// Transmit the flag directly from the .targets files and rely on tlbimp.exe to produce a good error message.
this.typeLibPointer = (ITypeLib)NativeMethods.LoadTypeLibEx(path, (int)NativeMethods.REGKIND.REGKIND_NONE);
break;
}
try
{
// get the type lib attributes from the retrieved interface pointer.
// do NOT remap file ADO references, since we'd end up with a totally different reference than specified.
ComReference.GetTypeLibAttrForTypeLib(ref this.typeLibPointer, out this.attr);
// get the type lib name from the retrieved interface pointer
if (!ComReference.GetTypeLibNameForITypeLib(
log,
silent,
this.typeLibPointer,
GetTypeLibId(log),
out this.typeLibName))
{
ReleaseTypeLibPtr();
return false;
}
}
catch (COMException)
{
ReleaseTypeLibPtr();
throw;
}
return true;
}
/// <summary>
/// A unique id string of this reference, it's either the item spec or (in the case of a dependency ref)
/// guid and version from typelib attributes
/// </summary>
private string GetTypeLibId(TaskLoggingHelper log)
{
if (taskItem != null)
{
return taskItem.ItemSpec;
}
else
{
return log.FormatResourceString("ResolveComReference.TypeLibAttrId", attr.guid, attr.wMajorVerNum, attr.wMinorVerNum);
}
}
/// <summary>
/// Get the source item, if available. Null otherwise.
/// </summary>
internal string SourceItemSpec => taskItem?.ItemSpec;
/// <summary>
/// Release the COM ITypeLib pointer for this reference
/// </summary>
internal void ReleaseTypeLibPtr()
{
if (typeLibPointer != null)
{
Marshal.ReleaseComObject(typeLibPointer);
typeLibPointer = null;
}
}
#endregion
}
}
|