Click here to Skip to main content
15,891,253 members
Articles / Desktop Programming / Win32

Managed Fusion

Rate me:
Please Sign up or sign in to vote.
4.96/5 (20 votes)
19 Nov 2009LGPL35 min read 49.6K   428   44  
Managed Fusion - A managed API to view and manipulate the Global Assembly Cache
#region Copyright (C) 2009-2010 Simon Allaeys

/*
    Copyright (C) 2009-2010 Simon Allaeys
 
    This file is part of AppStract

    AppStract is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    AppStract is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with AppStract.  If not, see <http://www.gnu.org/licenses/>.
*/

#endregion

using System;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using ManagedFusion.Helpers;

namespace ManagedFusion.WrappedFusion
{
  /// <summary>
  /// Extension methods for <see cref="AssemblyName"/> and for <see cref="IAssemblyName"/>.
  /// </summary>
  internal static class AssemblyNameExt
  {

    #region Public Methods

    /// <summary>
    /// Converts the value of this instance to its equivalent <see cref="AssemblyName"/>.
    /// </summary>
    /// <param name="assemblyName"></param>
    /// <returns></returns>
    public static AssemblyName ToAssemblyName(this IAssemblyName assemblyName)
    {
      var result = new AssemblyName();
      result.Name = assemblyName.GetName();
      result.Version = assemblyName.GetVersion();
      result.CultureInfo = new CultureInfo(assemblyName.GetProperty<string>(AssemblyNamePropertyId.Culture));
      result.CodeBase = assemblyName.GetProperty<string>(AssemblyNamePropertyId.CodebaseUrl);
      result.SetPublicKey(assemblyName.GetProperty<byte[]>(AssemblyNamePropertyId.PublicKey));
      result.SetPublicKeyToken(assemblyName.GetProperty<byte[]>(AssemblyNamePropertyId.PublicKeyToken));
      // ToDo: The following line will always return null, why? And how to fix this?
      //  assemblyName.GetProperty<object>(AssemblyNamePropertyId.ProcessorIdArray);
      // A workaround is available by using the displayname of the IAssemblyName
      var tmp = assemblyName.GetDisplayName(DisplayNameFlags.ProcessArchitecture);
      tmp = tmp.Substring(tmp.LastIndexOf('=') + 1);
      ProcessorArchitecture architecture;
      if (ParserHelper.TryParseEnum(tmp, out architecture))
        result.ProcessorArchitecture = architecture;
      return result;
    }

    /// <summary>
    /// Converts the value of this instance to its equivalent <see cref="IAssemblyName"/>.
    /// </summary>
    /// <param name="assemblyName"></param>
    /// <returns></returns>
    public static IAssemblyName ToIAssemblyName(this AssemblyName assemblyName)
    {
      IAssemblyName result;
      AssemblyCache.CreateAssemblyNameObject(out result, assemblyName.FullName, CreateDisposition.ParseDisplayName, IntPtr.Zero);
      result.SetProperty(AssemblyNamePropertyId.CodebaseUrl, assemblyName.CodeBase);
      result.SetProperty(AssemblyNamePropertyId.Culture, assemblyName.CultureInfo == null ? null : assemblyName.CultureInfo.ToString());
      result.SetProperty(AssemblyNamePropertyId.MajorVersion, assemblyName.Version == null ? null : (object)(short)assemblyName.Version.Major);
      result.SetProperty(AssemblyNamePropertyId.MinorVersion, assemblyName.Version == null ? null : (object)(short)assemblyName.Version.Minor);
      result.SetProperty(AssemblyNamePropertyId.Name, assemblyName.Name);
      result.SetProperty(AssemblyNamePropertyId.PublicKey, assemblyName.GetPublicKey());
      result.SetProperty(AssemblyNamePropertyId.PublicKeyToken, assemblyName.GetPublicKeyToken());
      return result;
    }

    /// <summary>
    /// Returns the most specific available name, which is still compatible with the fusion API.
    /// </summary>
    /// <remarks>
    /// <see cref="AssemblyName.FullName"/> is not compatible with fusion because of "PublicKeyToken" being specified.
    /// </remarks>
    /// <param name="assemblyName"></param>
    /// <returns></returns>
    public static string GetFusionCompatibleFullName(this AssemblyName assemblyName)
    {
      return assemblyName.Name
             + (assemblyName.Version == null? "" : ", Version=" + assemblyName.Version)
             + (assemblyName.CultureInfo == null || string.IsNullOrEmpty(assemblyName.CultureInfo.Name) ? "" : ", Culture=" + assemblyName.CultureInfo.Name);
    }

    #endregion
    
    #region Private Methods - IAssemblyName

    private static string GetName(this IAssemblyName name)
    {
      uint bufferSize = 255;
      var buffer = new StringBuilder((int)bufferSize);
      name.GetName(ref bufferSize, buffer);
      return buffer.ToString();
    }

    private static string GetDisplayName(this IAssemblyName name, DisplayNameFlags which)
    {
      uint bufferSize = 255;
      var buffer = new StringBuilder((int)bufferSize);
      name.GetDisplayName(buffer, ref bufferSize, which);
      return buffer.ToString();
    }

    private static Version GetVersion(this IAssemblyName name)
    {
      uint versionHi, versionLow;
      name.GetVersion(out versionHi, out versionLow);
      return new Version((int) (versionHi >> 16), (int) (versionHi & 0xFFFF),
                         (int) (versionLow >> 16), (int) (versionLow & 0xFFFF));
    }

    private static T GetProperty<T>(this IAssemblyName name, AssemblyNamePropertyId propertyId)
    {
      uint bufferSize = 512;
      var bufferPointer = Marshal.AllocHGlobal((int)bufferSize);
      try
      {
        var hResult = name.GetProperty(propertyId, bufferPointer, ref bufferSize);
        if (!HResult.IsSuccess(hResult))
          Marshal.ThrowExceptionForHR(hResult);
        return bufferSize > 0  // IAssemblyName.GetProperty() will always return a bufferSize greater than 0
                 ? bufferPointer.Read<T>(bufferSize)
                 : default(T);
      }
      finally
      {
        Marshal.FreeHGlobal(bufferPointer);
      }
    }

    private static void SetProperty(this IAssemblyName name, AssemblyNamePropertyId propertyId, object value)
    {
      int allocatedBytes = 0;
      var ptr = value == null ? IntPtr.Zero : value.ToPointer(out allocatedBytes);
      try
      {
        // First clear the property
        var hResult = name.SetProperty(propertyId, IntPtr.Zero, 0);
        Marshal.ThrowExceptionForHR(hResult);
        // Now set the property
        hResult = name.SetProperty(propertyId, ptr, (uint)allocatedBytes);
        Marshal.ThrowExceptionForHR(hResult);
      }
      finally
      {
        Marshal.FreeHGlobal(ptr);
      }
    }

    #endregion

  }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Belgium Belgium
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions