Skip to content

Instantly share code, notes, and snippets.

@domartynov
Forked from lbargaoanu/ComObjectMapper.cs
Created June 14, 2016 14:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save domartynov/9d189978573cb31c2792fea6b5a427ef to your computer and use it in GitHub Desktop.
Save domartynov/9d189978573cb31c2792fea6b5a427ef to your computer and use it in GitHub Desktop.
using ...
static void Main(string[] args)
{
var x = (Microsoft.Office.Interop.Excel._Application) Marshal.GetActiveObject("Excel.Application");
x.ChartDataPointTrack = false;
MapperRegistry.Mappers.Add(new ComObjectMapper());
var config = new MapperConfiguration(c => c.CreateMap<Microsoft.Office.Interop.Excel._Application, App>());
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();
var result = mapper.Map<App>(x);
}
class ComObjectMapper : IObjectMapper
{
private static readonly Func<ITypeInfo, Guid> GetTypeInfoGuid = (Func<ITypeInfo, Guid>) Delegate.CreateDelegate(typeof(Func<ITypeInfo, Guid>), typeof(Marshal).GetMethod("GetTypeInfoGuid", BindingFlags.NonPublic | BindingFlags.Static), throwOnBindFailure: true);
private static readonly Type ComObjectType = typeof(object).Assembly.GetType("System.__ComObject");
private static readonly Dictionary<Guid, Type> typesByGuid = GetTypes();
private static Dictionary<Guid, Type> GetTypes()
{
var types =
from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.ExportedTypes
where type.IsInterface && type.IsImport
select type;
var typesByGuid = new Dictionary<Guid, Type>();
foreach(var type in types)
{
typesByGuid[type.GUID] = type;
}
return typesByGuid;
}
/// <summary>
/// Exposes objects, methods and properties to programming tools and other
/// applications that support Automation.
/// </summary>
[ComImport()]
[Guid("00020400-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IDispatch
{
[PreserveSig]
int GetTypeInfoCount(out int Count);
ITypeInfo GetTypeInfo([MarshalAs(UnmanagedType.U4)] int iTInfo, [MarshalAs(UnmanagedType.U4)] int lcid);
[PreserveSig]
int GetIDsOfNames(
ref Guid riid,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)]
string[] rgsNames,
int cNames,
int lcid,
[MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);
[PreserveSig]
int Invoke(
int dispIdMember,
ref Guid riid,
uint lcid,
ushort wFlags,
ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams,
out object pVarResult,
ref System.Runtime.InteropServices.ComTypes.EXCEPINFO pExcepInfo,
IntPtr[] pArgErr);
}
public static Type GetComObjectType(object comObject)
{
if(!Marshal.IsComObject(comObject))
{
throw new ArgumentOutOfRangeException(nameof(comObject), "Expected a COM object.");
}
var dispatch = (IDispatch) comObject;
var typeInfo = dispatch.GetTypeInfo(0, 0);
var typeGuid = GetTypeInfoGuid(typeInfo);
return typesByGuid[typeGuid];
}
public bool IsMatch(TypePair context)
{
return context.SourceType == ComObjectType;
}
public static Exception MissingMapException(Type sourceType, Type destinationType)
{
var source = sourceType.Name;
var destination = destinationType.Name;
throw new InvalidOperationException($"Missing map from {source} to {destination}. Create using Mapper.CreateMap<{source}, {destination}>.");
}
public object Map(ResolutionContext context)
{
var sourceType = GetComObjectType(context.SourceValue);
var typeMap = context.ConfigurationProvider.ResolveTypeMap(sourceType, context.DestinationType);
if(typeMap == null)
{
throw MissingMapException(sourceType, context.DestinationType);
}
var newContext = new ResolutionContext(context.SourceValue, context.DestinationValue, new TypePair(sourceType, context.DestinationType), context);
newContext.TypeMap = typeMap;
return context.Mapper.Map(newContext);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment