Skip to content

Instantly share code, notes, and snippets.

@kbkk
Created July 16, 2017 19:33
Show Gist options
  • Save kbkk/437d92af4f3fc72ee9040f633d0ce988 to your computer and use it in GitHub Desktop.
Save kbkk/437d92af4f3fc72ee9040f633d0ce988 to your computer and use it in GitHub Desktop.
COM Interop Object Mapper for AutoMapper 6
using AutoMapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Linq.Expressions;
using AutoMapper.Execution;
using AutoMapper.Internal;
using System.Reflection;
namespace Anamespace
{
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 Expression MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, PropertyMap propertyMap, Expression sourceExpression, Expression destExpression, Expression contextExpression)
{
var sourceObject = ExpressionFactory.ToObject(sourceExpression);
var sourceType = GetComObjectType(sourceObject);
return TypeMapPlanBuilder.MapExpression(configurationProvider, profileMap,
new TypePair(sourceType, destExpression.Type),
sourceExpression,
contextExpression,
propertyMap,
destExpression
);
}
}
}
@alexvilper
Copy link

Hi Jakub,

The MapExpression method above needs to be changed to accommodate changes for AutoMapper 7.0.1. It needs to be changed to:

        public Expression MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, PropertyMap propertyMap, Expression sourceExpression, Expression destExpression, Expression contextExpression)
        {
            var sourceObject = ExpressionFactory.ToObject(sourceExpression);
            var sourceType = GetComObjectType(sourceObject);

            return ExpressionBuilder.MapExpression(configurationProvider, profileMap,
                    new TypePair(sourceType, destExpression.Type),
                    sourceExpression,
                    contextExpression,
                    propertyMap,
                    destExpression
                );
        }

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment