Обратная совместимость в .NET CORE

145
18 июня 2019, 10:10

Как я понял, многое вырезали из .NET CORE в угоду кроссплатформенности.

Например, COM.

Однко, как быть если вдруг будет потребность поработать с office Microsoft.Office.Interop? Или, например, мне потребовалось сделать что-то через WMI.

Ранее можно было сказать:

Не нужна кроссплатформенность? Пили на .NET Framework

Однако, .NET Framework 4.8 будет последним в своем роде, а дальше развиваться будет только .NET CORE => хочешь не хочешь, придется пользоваться .NET CORE если хочешь пользоваться новыми фичами.

Библиотека Interop к .NET CORE подключается, но висит предупреждение, что полная совместимость не гарантируется.

Answer 1

COM Interop работает в .NET Core 2.0+. В диалоге добавления ссылки для проектов .NET Core в Visual Studio нет вкладки COM, но можно нажать "Обзор" и указать tlb/dll файл вручную, для него будет корректно сформирована Interop-сборка. Однако, в .NET Core не реализовано позднее связывание (IDispatch), поэтому большая часть функционала Office Automation действительно будет трудно использовать.

Применительно к Excel, к примеру, вот такой код работает:

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";

А вот такой - уже нет:

sheet.Cells[1,1] = "Hello from .NET Core"; //ноль эффекта

Чтобы задействовать интерфейс Range, использующий позднее связывание, понадобится куда более сложный код:

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;            
        }
    }
}
Answer 2

Используйте сторонние библиотеки - аналоги. Например для работы с Office можно работать с тем же EPPlus

Кстати, .xlsx -> .zip контейнер с вложенными xml.

READ ALSO
Получение Json из файлов и папок

Получение Json из файлов и папок

В папке music на хостинге лежал папки типа

152
В чем преимущества ORM?

В чем преимущества ORM?

Что дает мне использование Doctrine и Eloquent кроме скорости разработки(помойму спорный аргумент) и всеядности к субд(здесь конечно да)И как обстаят...

135
password_verify неправильно работает

password_verify неправильно работает

Перелопатил кучу материала и других вопросов, но ответа так и не нашелЕсть вот такой вот код

132