COM Interop works in .NET Core 2.0+ . In the Add Link dialog for .NET Core projects in Visual Studio there is no COM tab, but you can click "Browse" and specify the tlb / dll file manually, the Interop-assembly will be formed correctly for it. However, late binding is not implemented in .NET Core (IDispatch), so most of the Office Automation functionality will indeed be difficult to use.
With reference to Excel, for example, this code works:
Application app = new Application(); app.Visible = true; var books = app.Workbooks; var book = books.Add(); Worksheet sheet = book.ActiveSheet; Console.WriteLine(sheet.Name); sheet.Name = "Hello from .NET Core";
But this is no longer:
sheet.Cells[1,1] = "Hello from .NET Core"; //ноль эффекта
To use the Range interface using late binding, a much more complicated code is needed:
using System; using System.Runtime.InteropServices; using ComTypes = System.Runtime.InteropServices.ComTypes; using Microsoft.Office.Interop.Excel; namespace NetCoreTest { class Program { static void Main(string[] args) { Application app = new Application(); app.Visible = true; var books = app.Workbooks; var book = books.Add(); _Worksheet sheet = book.ActiveSheet; Range r = sheet.get_Range("A1"); SetProperty(r, "Value", "Hello from .NET Core"); Console.ReadKey(); } //Устанавливает свойство COM-объекта с использованием позднего связывания public static void SetProperty(object obj, string property, string value) { int dispId = GetDispId(obj, property); InvokePropertySetter(obj as IDispatch, dispId, value); } const uint DISPATCH_METHOD = 0x1; const uint DISPATCH_PROPERTYGET = 0x2; const uint DISPATCH_PROPERTYPUT = 0x4; // Получение DispId для указанного метода (свойства) COM-объекта с использованием позднего связывания // Источник: https://github.com/dotnet/corefx/issues/19731 public static int GetDispId(object rcw, string methodName) { IDispatch dispatchObject = rcw as IDispatch; if (dispatchObject == null) { Console.WriteLine("Passed-in argument is not a IDispatch object"); return -1; } int[] dispIds = new int[1]; Guid emtpyRiid = Guid.Empty; dispatchObject.GetIDsOfNames( emtpyRiid, new string[] { methodName }, 1, 0, dispIds); if (dispIds[0] == -1) { Console.WriteLine("Method name {0} cannot be recognized.", methodName); } return dispIds[0]; } public static object InvokePropertySetter(IDispatch target, int dispId, string val) { const int DISPID_PROPERTYPUT = -3; IntPtr pArg = IntPtr.Zero; IntPtr pNamedArgs = IntPtr.Zero; IntPtr pStr = IntPtr.Zero; IntPtr dispIdArray = IntPtr.Zero, tmpVariants = IntPtr.Zero; if (target == null) { Console.WriteLine("Cannot cast target to IDispatch."); return null; } try { pStr = Marshal.StringToBSTR(val); Variant variant = new Variant(); variant.vt = 8; //VT_BSTR variant.p = pStr; pArg = Marshal.AllocHGlobal(Marshal.SizeOf(variant)); Marshal.StructureToPtr(variant, pArg, false); pNamedArgs = Marshal.AllocHGlobal(4); Marshal.WriteInt32(pNamedArgs, DISPID_PROPERTYPUT); var paramArray = new ComTypes.DISPPARAMS[1]; paramArray[0].rgvarg = pArg; paramArray[0].cArgs = 1; paramArray[0].cNamedArgs = 1; paramArray[0].rgdispidNamedArgs = pNamedArgs; ComTypes.EXCEPINFO info = default(ComTypes.EXCEPINFO); object result = null; uint puArgErrNotUsed = 0; target.Invoke(dispId, new Guid(), 0x0409, ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT, paramArray, out result, out info, out puArgErrNotUsed); return result; } finally { if (pStr != IntPtr.Zero) Marshal.FreeBSTR(pStr); if (pArg != IntPtr.Zero) Marshal.FreeHGlobal(pArg); if (pNamedArgs != IntPtr.Zero) Marshal.FreeHGlobal(pNamedArgs); } } [DllImport("ole32.dll")] public static extern int CLSIDFromProgID([MarshalAs(UnmanagedType.LPWStr)] string lpszProgID, out Guid pclsid); [Guid("00020400-0000-0000-c000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [ComImport] public interface IDispatch { [PreserveSig] int GetTypeInfoCount(out int info); [PreserveSig] int GetTypeInfo(int iTInfo, int lcid, out ComTypes.ITypeInfo ppTInfo); void GetIDsOfNames( [MarshalAs(UnmanagedType.LPStruct)] Guid iid, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)] string[] rgszNames, int cNames, int lcid, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4)] int[] rgDispId); void Invoke( int dispIdMember, [MarshalAs(UnmanagedType.LPStruct)] Guid iid, int lcid, ComTypes.INVOKEKIND wFlags, [In, Out] [MarshalAs(UnmanagedType.LPArray)] ComTypes.DISPPARAMS[] paramArray, out object pVarResult, out ComTypes.EXCEPINFO pExcepInfo, out uint puArgErr); } [StructLayout(LayoutKind.Sequential)] public struct Variant { public ushort vt; ushort wReserved1; ushort wReserved2; ushort wReserved3; public IntPtr p; } } }