...

среда, 1 февраля 2017 г.

CEF, ES6, Angular 2, TypeScript использование классов .Net Core для расширения возможностей

Меня все спрашивают — «Зачем это нужно?». На что, я гордо отвечаю — «Я в 1С использую для доступа к торговому оборудованию, к Вэб-сервисам по ws-протоколам, готовым компонентам. 1С, Linux, Excel, Word, OpenXML,ADO и Net Core. Кроме того, сделаю передачу JS объектов на сторону .Net с использованием через DynamicObject.

На самом деле можно сделать определенную конфигурацию CEF для всех платформ и можно делать кросспалатформенные декстопные приложения. Как аналог Electron. .Net Core развивается и можно достаточно легко перевести приложения под WPF и UWP на Angular 2»

Но я прекрасно понимаю, что это всего лишь высокопарные слова, и мало кому это нужно. Но мне чертовски интересно, особенно после программирования на 1С.

Для показа возможностей, возьму пример из моей статьи Кроссплатформенное использование классов .Net в 1С через Native ВК. Или замена COM на Linux II.

В нем куча сахара и показывает все проблемы. Сразу прошу прощения за Руслиш. Я очень стараюсь, но у меня на нем куча примеров, а времени очень мало.

 // Метод расширения
    //IConfiguration WithDefaultLoader(this IConfiguration configuration, Action<LoaderSetup> setup = null, IEnumerable<IRequester> requesters = null);
    var config = Configuration.Default.WithDefaultLoader();
    // Устанавливаем адрес страницы сайта
    var address = "http://ift.tt/2aIHUiw";
    // загружаем страницу и разбираем её
 
    //Метод расширения
    //Task<IDocument> OpenAsync(this IBrowsingContext context, string address);
    var document = BrowsingContext.New(config).OpenAsync(address).Result;
    // Используем CSS селектор для получения строк таблицы с классом  
    var rowSelector = "tr.vevent";
    var Строки = document.QuerySelectorAll<IHtmlTableRowElement>(rowSelector);
    foreach (var str in Строки)


На TypeScript это выглядит так:
            let Net = NetObject.NetWrapper;
            let $$ = NetObject.FlagDeleteObject; // Символ для признака удаления при вызове объекта как метода

            // Загрузим сборку AngleSharpж
            let СборкаAngleSharp = Net.Сборка("AngleSharp");
            // Получим из неё используемые типы
            let Configuration = СборкаAngleSharp.GetType("AngleSharp.Configuration");
            let BrowsingContext = СборкаAngleSharp.GetType("AngleSharp.BrowsingContext");
            let HtmlParser = СборкаAngleSharp.GetType("AngleSharp.Parser.Html.HtmlParser");
            let IHtmlTableRowElement = СборкаAngleSharp.GetType("AngleSharp.Dom.Html.IHtmlTableRowElement");
            let ApiExtensions = СборкаAngleSharp.GetType("AngleSharp.Extensions.ApiExtensions");

            let Default = Configuration._Default;
            var config = Default.WithDefaultLoader();
            // Устанавливаем адрес страницы сайта
            var address = "http://ift.tt/2aIHUiw";
            // загружаем страницу и разбираем её

            let Context = BrowsingContext.New(config);

            //Метод расширения
            //Task<IDocument> OpenAsync(this IBrowsingContext context, string address);
            let document = await Context.async.OpenAsync(address);
            // Не могу установить результат асинхронной функции класс Proxy с Target fuction
            // Поэтому для объектов нужно вручную обернуть
            document = NetObject.WrapResult(document, true);

            // Используем CSS селектор для получения строк таблицы с классом  
            let rowSelector = "tr.vevent";

            // Для дженериков пока не сделал поиск в расширениях поэтому вместо
            //let rows = document.QuerySelectorAll([IHtmlTableRowElement], rowSelector);
           
            // используем метод расширения явно
            //IEnumerable < TElement > QuerySelectorAll<TElement>(this IParentNode parent, string selectors) where TElement : IElement;
             let rows = ApiExtensions.QuerySelectorAll([IHtmlTableRowElement], document, rowSelector);
         
            // можно и так вызвать, но нужно обыграть все варианты
            //let rows = document.QuerySelectorAll(rowSelector);

// Пройдемся по нужным строкам распарсенной таблицы
            for (let row of rows) {
                let Cells = row._Cells;
                let i = 0;
                let данныеСайта = new ДанныеСайта();
                this.ResultParse.push(данныеСайта);
// Загрузим данные ячеек в поля объекта ДанныеСайта
                for (let Cell of Cells) {
 // Нужно дождаться окончания итератора, что бы освободить ссылку на итератор
                    if (i < 8)                        {
                    данныеСайта[this.Colums[i]] = Cell._TextContent;
                    Cell($$); // Удалим ссылку из хранилища объектов 
                    i++;
                    }
                }
                Cells($$);
                row($$);
            }
            rows($$);

            // Удалим вручную испльзуемые объекты
            NetObject.DeleteNetObjets(СборкаAngleSharp, Configuration, BrowsingContext, HtmlParser, IHtmlTableRowElement, ApiExtensions, Default, config, Context, document); 
            alert("Количество элементов в хранилище "+Net.КоличествоЭлементовВХранилище());

Прежде всего видим главные отличия от C#. Для получения свойства нужно добавить "_"
let Default = Configuration._Default;

Для вызова асинхронного метода нужно добавить ключевое слово async:
let document = await Context.async.OpenAsync(address);

Для вызова дженерик метода, если нельзя вывести типы по параметрам то аргументы указываем в массиве:
let rows = ApiExtensions.QuerySelectorAll([IHtmlTableRowElement], document, rowSelector);

Ну и главное, нужно вручную удалить ссылку на объект со стороны .Net
Cells(NetObject.FlagDeleteObject);
для уменьшения писанины
Cells($$);

По скорости вызовов на моем Intel Core i3-2120 CPU 3.3 GHz.
Скорость вызова без Proxy 60к вызовов в секунду
Скорость вызова с прокси Proxy 45k вызовов в секунду
Скорость вызова итератора 160k вызовов в секунду

Что в общем-то вполне приемлемо.

Приведу еще небольшой пример.

 public class Тестовый
    {
       public string СвойствоОбъекта { get; set; }
       public  Тестовый(string СвойствоОбъекта)
            {
            this.СвойствоОбъекта = СвойствоОбъекта;
            }
 public object ПолучитьExpandoObject()
        {

            dynamic res = new ExpandoObject();
            res.Имя = "Тест ExpandoObject";
            res.Число = 456;
            res.ВСтроку = (Func<string>)(() => res.Имя);
            res.Сумма = (Func<int, int, int>)((x, y) => x + y);

            return res;
        }
     }

На TypeScript можно вызвать так:
// Получим Тип из сборки лежащей в каталоге приложения
let Тестовый = Net.GetType("TestDllForCoreClr.Тестовый", "TestDllForCoreClr");
// Создадим объект используя new
let TO = new Тестовый("Свойство из Конструктора");
// Получим ExpandoObject
var EO = TO.ПолучитьExpandoObject();
let Имя=EO._Имя;// Свойства через _
let Число=EO._Число;
let делегат = EO._ВСтроку;
let res= делегат());// Вызовем как делегат
 // Для ExpandoObject можно вызвать как метод
 res= EO.ВСтроку());// Для ExpandoObject


Теперь за счет чего это достигается. Кроссплатформенное использование классов .Net из неуправляемого кода. Или аналог IDispatch на Linux. Через CEF встраиваем нужные методы:
   declare var window: WindowInterface;
    export interface WindowInterface {
    CallNetMethod(Id: number, MethodName: string, args?: any[]): any;// Вызов метода
    CallNetDelegate(Id: number, args?: any[]): any; // Вызов делегата
    CallNetPropertySet(Id: number, PropertyName: string, value: any): void; // Установка свойства
    CallNetPropertyGet(Id: number, PropertyName: string): any; // Получение значения свойства
    DeleteNetObject(Id: number): void; // Освободить ссылку на объект со стороны .Net
    // Асинхронный вызов метода возвращающий Task или Task<T>
    CallAsyncNetObjectFunction(Id: number, MethodName: string, TaskId: string, args?: any[]): any; 
    // Регистрация метода на стороне CEF, для установки результата Promise 
    RegisterCallBacks(SetAsyncResult: (Successfully: boolean, TaskId: string, result: any) => void): void; 
    // Вызов дженерик метода с указанием типов аргументов
    CallNetObjectGenericFunction(Id: number, MethodName: string, types: any[], args?: any[]): any;
    // Вызов итератора IEnumerator MoveNext на стороне .Net
    IteratorNext(Id: number): any;
}

Для удобного использования этих методов создадим класс прокси с Target гибридного типа:
export interface NetObjectinterface {
    (): void;
    Id: number;
    isNetObject: boolean;
    IsAsyncCall?: boolean;
    CallAsProp(target: NetObject, name: any): ResultCallAsProp;
    Execute: (target: NetObject, name: any, args: any[]) => any;

}

Target должен быть функцией для возможности использования new и (). Теперь нам понадобится Handler:
export var NetObjectHandler: ProxyHandler<NetObjectinterface> = {
    get: (target, name: any) => {
// Вызывается как PropertyGet как для свойств так и методов
// Что бы их разделить  свойства начинаются с "_"

        let res = target.CallAsProp(target, name);
        if (res.Successfully)
            return res.result;

        return (...args: any[]) => {
            return target.Execute(target, name, args);
        }

    },
    set: function (target, prop, value, receiver) {
        return NetObject.SetPropertyValue(target, prop, value, receiver);
    },

    apply: (target, that, args) => {
        if (args.length == 1) {
            var param = args[0];
            if (param === NetObject.FlagGetObject)
                return target;
            else if (param === NetObject.FlagDeleteObject) {
                window.DeleteNetObject(target.Id);
                return undefined;
            }
        }

        NetObject.SetArgs(args);
        let res = window.CallNetDelegate(target.Id, args)
        return NetObject.WrapResult(res, true);
    },

    construct: (target, argumentsList, newTarget) => {
// Используем метод на стороне Net 
// object Новый(object Тип, params object[] argOrig)
        NetObject.SetArgs(argumentsList);
        argumentsList.unshift(target);
        let res = window.CallNetMethod(0, "Новый", argumentsList);
        return NetObject.WrapResult(res, true);

    }
}

Ну и понадобится сам Target:
function getNetObject(id: number): NetObjectinterface {
    let netObject = <NetObjectinterface>function (start: number) { };
    netObject.Id = id;
    netObject.isNetObject = true;
    netObject[NetObject.isNetclass] = true;
    netObject.Execute = NetObject.Execute;
    netObject.CallAsProp = NetObject.CallAsProp;
    return netObject;
}

Для обертки результата из CEF используется:
static WrapResult(value: any, ReturnProxy: boolean = false): any {
        if (typeof value == "object") {
            if ("IsNetObject" in value) {
                let res = getNetObject(value.Id);
                if (ReturnProxy)
                    return new Proxy(res, NetObjectHandler);
                else
                    return res

            }


        }
        return value;
    }

Что касается асинхронных методов то они работают через два метода:
static GetPromise(Target: NetObjectinterface, name: any, args: any[]) {

        let key = window.CallNetMethod(0, "GetUniqueString");
        let promise = new Promise((resolve, reject) => {
            NetObject.PromiseDictioanary.set(key, { resolve: resolve, reject: reject });
            window.CallAsyncNetObjectFunction(Target.Id, name, key, args);
        });
        return promise;
    }

И при получении асинхронного результата:
 static SetPromiseResult(Successfully: boolean, TaskId: string, result: any) {
        let item = NetObject.PromiseDictioanary.get(TaskId);
        try {

            NetObject.PromiseDictioanary.delete(TaskId);
           // Вот здесь не могу установить результат Proxy с Target function
            //  result = NetObject.WrapResult(result, true);
           // возникает исключение "Не найден then"
            if (Successfully)
                item.resolve(result);
            else
                item.reject(result);
        }

        catch (e) {
            item.reject("ошибка установки асинхронного результата " + e);
            alert("ошибка установки асинхронного результата " + e);
        }
    }

где:
 static PromiseDictioanary = new Map();

Весь код можно посмотреть ниже под спойлером:
Весь код NetProxy
declare var window: WindowInterface;
declare var $_: Symbol;
declare var _$: Symbol;

export interface WindowInterface {
    CallNetMethod(Id: number, MethodName: string, args?: any[]): any;// Вызов метода
    CallNetDelegate(Id: number, args?: any[]): any; // Вызов делегата
    CallNetPropertySet(Id: number, PropertyName: string, value: any): void; // Установка свойства
    CallNetPropertyGet(Id: number, PropertyName: string): any; // Полусение значения свойства
    DeleteNetObject(Id: number): void; // Освободить ссылку на объект со стороны .Net
    // Асинхронный вызов метода возвращающий Task или Task<T>
    CallAsyncNetObjectFunction(Id: number, MethodName: string, TaskId: string, args?: any[]): any; 
    // Регистрация метода на стороне CEF, для установки результата Promise 
    RegisterCallBacks(SetAsyncResult: (Successfully: boolean, TaskId: string, result: any) => void): void; 
    // Вызов дженерик метода с указанием типов аргументов
    CallNetObjectGenericFunction(Id: number, MethodName: string, types: any[], args?: any[]): any;
    // Вызов итератора IEnumerator MoveNext на стороне .Net
    IteratorNext(Id: number): any;
}

class ResultCallAsProp {
    constructor(public Successfully: boolean, public result?: any) { };
}
export interface NetObjectinterface {
    (): void;
    Id: number;
    isNetObject: boolean;
    IsAsyncCall?: boolean;
    CallAsProp(target: NetObject, name: any): ResultCallAsProp;
    Execute: (target: NetObject, name: any, args: any[]) => any;

}

export var NetObjectHandler: ProxyHandler<NetObjectinterface> = {
    get: (target, name: any) => {


        let res = target.CallAsProp(target, name);
        if (res.Successfully)
            return res.result;

        return (...args: any[]) => {
            return target.Execute(target, name, args);
        }


    },
    set: function (target, prop, value, receiver) {
        return NetObject.SetPropertyValue(target, prop, value, receiver);
    },

    apply: (target, that, args) => {
        if (args.length == 1) {
            var param = args[0];
            if (param === NetObject.FlagGetObject)
                return target;
            else if (param === NetObject.FlagDeleteObject) {
                window.DeleteNetObject(target.Id);
                return undefined;
            }
        }

        NetObject.SetArgs(args);
        let res = window.CallNetDelegate(target.Id, args)
        return NetObject.WrapResult(res, true);
    },

    construct: (target, argumentsList, newTarget) => {

        //  var res = NetObject.GetNetObject(5);
        //  return new Proxy(res, NetObjectHandler)
        NetObject.SetArgs(argumentsList);
        argumentsList.unshift(target);
        let res = window.CallNetMethod(0, "Новый", argumentsList);
        return NetObject.WrapResult(res, true);

    }


}

function getNetObject(id: number): NetObjectinterface {
    let netObject = <NetObjectinterface>function (start: number) { };
    netObject.Id = id;
    netObject.isNetObject = true;
    netObject[NetObject.isNetclass] = true;
    netObject.Execute = NetObject.Execute;
    netObject.CallAsProp = NetObject.CallAsProp;
    return netObject;
}

function GetNetProxy(): any {
    let res = getNetObject(0);
    if (NetObject.FlagFirstLoad) {
        try {
            window.RegisterCallBacks(NetObject.SetPromiseResult);

        }
        catch (e) {
            // alert("ошибка " + e);
        }

        NetObject.FlagFirstLoad = false;
    }

    return new Proxy(res, NetObjectHandler);

}
export class NetObject {
    static GetNetObject(id: number) { return getNetObject(id); }
    static isNetclass = Symbol();
    static IsAsyncCall = Symbol();
    static FlagGetObject = Symbol();
    static FlagDeleteObject = Symbol();
    static FlagFirstLoad = true;
    static NetWrapper = GetNetProxy();

    static PromiseDictioanary = new Map();
    static GetIterator(target: NetObjectinterface): any {
        return function () {
            let IdIterator = window.CallNetMethod(0, "GetIterator", [target]).Id;

            return {
                next: function () {

                    let value = window.IteratorNext(IdIterator);
                    if (value === undefined) {
                        return { value: undefined, done: true };

                    } else
                        return { value: NetObject.WrapResult(value, true), done: false }

                }
            }


        }
    }


    static WrapResult(value: any, ReturnProxy: boolean = false): any {
        if (typeof value == "object") {
            if ("IsNetObject" in value) {
                let res = getNetObject(value.Id);
                if (ReturnProxy)
                    return new Proxy(res, NetObjectHandler);
                else
                    return res

            }


        }
        return value;
    }

    static WrapObject(value: any): any {
        if (typeof value == "function") {
            if (NetObject.isNetclass in value)
                return new Proxy(value, NetObjectHandler);
        }
    }

    static GetPropertyValue(target: NetObjectinterface, name: any): any {
        let res = window.CallNetPropertyGet(target.Id, name);
        return NetObject.WrapResult(res, true);

    }

    static SetPropertyValue(target: NetObjectinterface, prop: any, value: any, receiver: any): any {
        let res = window.CallNetPropertySet(target.Id, prop, NetObject.GetTarget(value));
        return true;

    }

    static CallAsProp(Target: NetObjectinterface, name: any): ResultCallAsProp {
        if (name === Symbol.iterator) {
            return new ResultCallAsProp(true, NetObject.GetIterator(Target));
        }

        if (name === Symbol.toPrimitive) {
            return new ResultCallAsProp(true, () => { return `Id= ${Target.Id}, isNetObject= ${Target.isNetObject}` });
        }
        if (name.startsWith('_')) {

            return new ResultCallAsProp(true, NetObject.GetPropertyValue(Target, name.substring(1)));

        }

        if (name === "async") {

            let res = getNetObject(Target.Id);

            res.Execute = NetObject.ExecuteAsync;
            res.CallAsProp = NetObject.CallAsPropAsync;
            return new ResultCallAsProp(true, new Proxy(res, NetObjectHandler));
        }

        return new ResultCallAsProp(false);
    }

    static CallAsPropAsync(Target: NetObjectinterface, name: any): ResultCallAsProp {


        return new ResultCallAsProp(false);
    }

    static GetPromise(Target: NetObjectinterface, name: any, args: any[]) {

        let key = window.CallNetMethod(0, "GetUniqueString");
        let promise = new Promise((resolve, reject) => {
            NetObject.PromiseDictioanary.set(key, { resolve: resolve, reject: reject });
            window.CallAsyncNetObjectFunction(Target.Id, name, key, args);
        });
        return promise;
    }

    static GetTarget(obj: any): any {
        if (typeof obj == "function") {
            if (NetObject.isNetclass in obj)
                return obj(NetObject.FlagGetObject);


        }
        return obj;
    }

    static SetArgs(args: any[]) {
        for (let i in args) {
            let obj = args[i];
            if (typeof obj == "function") {
                if (NetObject.isNetclass in obj)
                    args[i] = obj(NetObject.FlagGetObject);


            }

        }
    }


    static SetPromiseResult(Successfully: boolean, TaskId: string, result: any) {
        let item = NetObject.PromiseDictioanary.get(TaskId);
        try {

            NetObject.PromiseDictioanary.delete(TaskId);
            //  result = NetObject.WrapResult(result, true);
            if (Successfully)
                item.resolve(result);
            else
                item.reject(result);
        }

        catch (e) {
            item.reject("ошибка установки асинхронного результата " + e);
            alert("ошибка установки асинхронного результата " + e);
        }
    }

    static CheckGenericMethod(args: any[]): any {

        var argsCount = args.length;

        if (argsCount > 0 && args[0] instanceof Array) {
            var types = args[0].slice();
            NetObject.SetArgs(types);

            var args2 = args.slice(1);
            NetObject.SetArgs(args2);
            return { IsGeneric: true, types: types, args: args2 }
        }

        return { IsGeneric: false };

    }
    static Execute(Target: NetObjectinterface, name: any, args: any[]) {

        let res = undefined;
        let chek = NetObject.CheckGenericMethod(args);

        if (chek.IsGeneric) {
            res = window.CallNetObjectGenericFunction(Target.Id, name, chek.types, chek.args);

        }
        else {
            NetObject.SetArgs(args);

            res = window.CallNetMethod(Target.Id, name, args);
        }

        return NetObject.WrapResult(res, true);
    }

    static ExecuteAsync(Target: NetObjectinterface, name: any, args: any[]) {


        let res = undefined;
        let chek = NetObject.CheckGenericMethod(args);

        if (chek.IsGeneric) {
            let Target0 = getNetObject(0);
            let task = window.CallNetObjectGenericFunction(Target.Id, name, chek.types, chek.args);
            res = NetObject.GetPromise(Target0, "ReturnParam", [getNetObject(task.Id)]);
            window.DeleteNetObject(task.Id);

        }
        else {
            NetObject.SetArgs(args);

            res = NetObject.GetPromise(Target, name, args);
        }

        return res;
    }

    static New(Target: NetObjectinterface, name: any, args: any[]): any {
        NetObject.SetArgs(args);
        var res = window.CallNetMethod(0, "Новый", args);
        return NetObject.WrapResult(res, true);
    }

    static DeleteNetObjets(...args: any[]) {

        for (let item of args)
            item(NetObject.FlagDeleteObject);
    }


}


Прошу прощение за моё незнание С++. Но, делать было нужно сейчас, а я на нем не пишу.
Не смог прикрутить Dev Tools к CefSimple:
Код CEF обертки для обмена между JS и .Net Core
#include "include/CEF_V8.H"
#include "ManagedDomainLoader.h"
#include "CefV8HandlersForNet.h"

#include "types.h"
#include "NetConverter.h"


#include "include/base/cef_bind.h"
#include "include/wrapper/cef_closure_task.h"
#include <thread>
#include "include/base/cef_platform_thread.h"
namespace NetObjectToNative{

        BaseClassForNetHandlers::BaseClassForNetHandlers(ManagedDomainLoader* mD)
        {
                this->mD = mD;
        }

         bool CallNetObjectFunction::Execute(const CefString& name,
                CefRefPtr<CefV8Value> object,
                const CefV8ValueList& arguments,
                CefRefPtr<CefV8Value>& retval,
                CefString& exception)  {


                const size_t argumentsCount = arguments.size();
                vector<wstring> savedstrings;
                NetObjectToNative::tVariant* Params = nullptr;

                int Target = arguments[0]->GetIntValue();
                wstring MethodMame = arguments[1]->GetStringValue().ToWString();

                CefRefPtr<CefV8Value> params;

                size_t  argCount = 0;
                if (argumentsCount == 3)
                {


                        params = arguments[2];

                        if (!params->IsArray())
                        {
                                exception = CefString(L"Для вызова метода 3 параметр должен быть массивом");
                                return true;

                        }
                        argCount = params->GetArrayLength();


                }

                if (argCount > 0)
                {
                        savedstrings.reserve(argCount);
                        Params = new NetObjectToNative::tVariant[argumentsCount];
                        NetObjectToNative::tVariant* Param = Params;

                        for (size_t i = 0; i < argCount; ++i)
                        {

                                NetObjectToNative::ConvertCEFtoNet(params->GetValue(i), &Param[i], savedstrings);
                        }

                }
                wchar_t*  Error = nullptr;
                NetObjectToNative::tVariant RetVal;

                bool res = mD->pCallAsFunc(Target, MethodMame.c_str(), &RetVal, Params, argCount, &Error);

                if (res)
                {

                        retval = NetObjectToNative::ConvertNetToCef(&RetVal, true);
                }
                else
                {
                        if (Error)
                                exception = CefString(std::wstring(Error));
                        delete Error;
                }

                if (Params) delete[] Params;

                return true;
        }

         //====================== ============================================
         bool CallAsyncNetObjectFunction::Execute(const CefString& name,
                 CefRefPtr<CefV8Value> object,
                 const CefV8ValueList& arguments,
                 CefRefPtr<CefV8Value>& retval,
                 CefString& exception) {


                 const size_t argumentsCount = arguments.size();
                 vector<wstring> savedstrings;
                 NetObjectToNative::tVariant* Params = nullptr;

                 int Target = arguments[0]->GetIntValue();
                 wstring MethodMame = arguments[1]->GetStringValue().ToWString();
                 wstring TaskId = arguments[2]->GetStringValue().ToWString();

                 CefRefPtr<CefV8Value> params;

                 size_t  argCount = 0;
                 if (argumentsCount == 4)
                 {


                         params = arguments[3];

                         if (!params->IsArray())
                         {
                                 exception = CefString(L"Для вызова асинхронного метода 4 параметр должен быть массивом");
                                 return true;

                         }
                         argCount = params->GetArrayLength();


                 }

                 if (argCount > 0)
                 {
                         savedstrings.reserve(argCount);
                         Params = new NetObjectToNative::tVariant[argumentsCount];
                         NetObjectToNative::tVariant* Param = Params;

                         for (size_t i = 0; i < argCount; ++i)
                         {

                                 NetObjectToNative::ConvertCEFtoNet(params->GetValue(i), &Param[i], savedstrings);
                         }

                 }
                 wchar_t*  Error = nullptr;
                 NetObjectToNative::tVariant RetVal;

                 //bool res = mD->pCallAsFunc(Target, MethodMame.c_str(), &RetVal, Params, argCount, &Error);
                 bool res = mD->pCallAsyncFunc(Target, MethodMame.c_str(), this->cfn, TaskId.c_str(), Params, argCount, &Error);

                 if (res)
                 {

                         retval = NetObjectToNative::ConvertNetToCef(&RetVal, true);
                 }
                 else
                 {
                         if (Error)
                                 exception = CefString(std::wstring(Error));
                         delete Error;
                 }

                 if (Params) delete[] Params;

                 return true;
         }

        //============================ Call Generic Function

         bool CallNetObjectGenericFunction::Execute(const CefString& name,
                 CefRefPtr<CefV8Value> object,
                 const CefV8ValueList& arguments,
                 CefRefPtr<CefV8Value>& retval,
                 CefString& exception) {


                 const size_t argumentsCount = arguments.size();
                 vector<wstring> savedstrings;
                 NetObjectToNative::tVariant* Params = nullptr;
                 NetObjectToNative::tVariant* ParamsTypes = nullptr;

                 int Target = arguments[0]->GetIntValue();
                 wstring MethodMame = arguments[1]->GetStringValue().ToWString();

                 CefRefPtr<CefV8Value> params;
                 CefRefPtr<CefV8Value> types= arguments[2];
                 size_t  typesCount= types->GetArrayLength();


                 size_t  argCount = 0;
                 if (argumentsCount == 4)
                 {


                         params = arguments[3];

                         if (!params->IsArray())
                         {
                                 exception = CefString(L"Для вызова метода 4 параметр должен быть массивом");
                                 return true;

                         }
                         argCount = params->GetArrayLength();


                 }

                 
                 savedstrings.reserve(argCount+ typesCount);
                 ParamsTypes = new NetObjectToNative::tVariant[typesCount];
                 for (size_t i = 0; i < typesCount; ++i)
                 {

                         NetObjectToNative::ConvertCEFtoNet(types->GetValue(i), &ParamsTypes[i], savedstrings);
                 }


                 if (argCount > 0)
                 {
                        
                         Params = new NetObjectToNative::tVariant[argumentsCount];
                         NetObjectToNative::tVariant* Param = Params;

                         for (size_t i = 0; i < argCount; ++i)
                         {

                                 NetObjectToNative::ConvertCEFtoNet(params->GetValue(i), &Param[i], savedstrings);
                         }

                 }



                 wchar_t*  Error = nullptr;
                 NetObjectToNative::tVariant RetVal;

                 bool res = mD->pCallAsGenericFunc(Target, MethodMame.c_str(), &RetVal, ParamsTypes, typesCount, Params, argCount, &Error);

                 if (res)
                 {

                         retval = NetObjectToNative::ConvertNetToCef(&RetVal, true);
                 }
                 else
                 {
                         if (Error)
                                 exception = CefString(std::wstring(Error));
                         delete Error;
                 }

                 if (Params) delete[] Params;
                 delete[] ParamsTypes;

                 return true;
         }


         //===================== CallNetDelegate

         bool CallNetDelegate::Execute(const CefString& name,
                 CefRefPtr<CefV8Value> object,
                 const CefV8ValueList& arguments,
                 CefRefPtr<CefV8Value>& retval,
                 CefString& exception) {


                 const size_t argumentsCount = arguments.size();
                 vector<wstring> savedstrings;
                 NetObjectToNative::tVariant* Params = nullptr;

                 int Target = arguments[0]->GetIntValue();
                

                 CefRefPtr<CefV8Value> params;

                 size_t  argCount = 0;
                 if (argumentsCount == 2)
                 {


                         params = arguments[1];

                         if (!params->IsArray())
                         {
                                 exception = CefString("Для вызова делегата  2 параметр должен быть массивом");
                                 return true;

                         }
                         argCount = params->GetArrayLength();


                 }

                 if (argCount > 0)
                 {
                         savedstrings.reserve(argCount);
                         Params = new NetObjectToNative::tVariant[argumentsCount];
                         NetObjectToNative::tVariant* Param = Params;

                         for (size_t i = 0; i < argCount; ++i)
                         {

                                 NetObjectToNative::ConvertCEFtoNet(params->GetValue(i), &Param[i], savedstrings);
                         }

                 }
                 wchar_t*  Error = nullptr;
                 NetObjectToNative::tVariant RetVal;

                 bool res = mD->pCallAsDelegate(Target, &RetVal, Params, argCount, &Error);

                 if (res)
                 {

                         retval = NetObjectToNative::ConvertNetToCef(&RetVal, true);
                 }
                 else
                 {
                         if (Error)
                                 exception = CefString(std::wstring(Error));
                         delete Error;
                 }

                 if (Params) delete[] Params;

                 return true;
         }

         // CallNetObjectPropertySet
         bool   CallNetObjectPropertySet::Execute(const CefString& name,
                 CefRefPtr<CefV8Value> object,
                 const CefV8ValueList& arguments,
                 CefRefPtr<CefV8Value>& retval,
                 CefString& exception) {


                 const size_t argumentsCount = arguments.size();

                 if (argumentsCount != 3)
                 {


                                 exception = CefString(L"Для PropertySet должно быть 3 параметра");
                                 return true;

                        }
                         
                 vector<wstring> savedstrings;


                 int Target = arguments[0]->GetIntValue();
                 wstring PropertyName = arguments[1]->GetStringValue().ToWString();
                 CefRefPtr<CefV8Value> value = arguments[2];

                         
                         savedstrings.reserve(1);
                         NetObjectToNative::tVariant Param;
                        
                         NetObjectToNative::ConvertCEFtoNet(value, &Param, savedstrings);
                         


                 wchar_t*  Error = nullptr;
        
                 bool res = mD->pSetPropVal(Target, PropertyName.c_str(), &Param, &Error);

                 if (!res)
                 {
                         if (Error)
                         {
                                 exception = CefString(std::wstring(Error));
                                 delete Error;
                         }
                         else 
                                 exception = CefString(L"Ошибка при установке свойства"+ PropertyName);
                        
                 }

                

                 return true;
         }

         bool   DeleteNetObject::Execute(const CefString& name,
                 CefRefPtr<CefV8Value> object,
                 const CefV8ValueList& arguments,
                 CefRefPtr<CefV8Value>& retval,
                 CefString& exception) {


                 const size_t argumentsCount = arguments.size();

                 if (argumentsCount != 1)
                 {
                         exception = CefString(L"Для DeleteObject Должно быть 1 параметра");
                         return true;
                 }

                 CefRefPtr<CefV8Value> value = arguments[0];
                 
                 
                 mD->pDeleteObject(value->GetIntValue());

                

                 return true;
         }

         bool   IteratorNext::Execute(const CefString& name,
                 CefRefPtr<CefV8Value> object,
                 const CefV8ValueList& arguments,
                 CefRefPtr<CefV8Value>& retval,
                 CefString& exception) {


                 const size_t argumentsCount = arguments.size();

                 if (argumentsCount != 1)
                 {
                         exception = CefString(L"Для IteratorNext Должно быть 1 параметра");
                         return true;
                 }

                 CefRefPtr<CefV8Value> value = arguments[0];
                 wchar_t*  Error = nullptr;
                 NetObjectToNative::tVariant RetVal;

                bool res= mD->pIteratorNext(value->GetIntValue(),&RetVal, &Error);
                if (res)
                {
                        retval = NetObjectToNative::ConvertNetToCef(&RetVal, true);

                }
                else
                {
                        retval = CefV8Value::CreateUndefined();
                        if (Error)
                        {
                                exception = CefString(std::wstring(Error));
                                delete Error;
                        }

                }
                 return true;
         }

         bool   CallNetObjectPropertyGet::Execute(const CefString& name,
                 CefRefPtr<CefV8Value> object,
                 const CefV8ValueList& arguments,
                 CefRefPtr<CefV8Value>& retval,
                 CefString& exception) {


                 int Target = arguments[0]->GetIntValue();
                 wstring PropertyName = arguments[1]->GetStringValue().ToWString();

                 wchar_t*  Error = nullptr;
                 NetObjectToNative::tVariant RetVal;

                 bool res = mD->pGetPropVal(Target, PropertyName.c_str(), &RetVal,&Error);

                 if (!res)
                 {
                         if (Error)
                         {
                                 exception = CefString(std::wstring(Error));
                                 delete Error;
                         }
                         else
                                 exception = CefString(L"Ошибка при установке свойства " + PropertyName);

                 }
                 else
                         retval = NetObjectToNative::ConvertNetToCef(&RetVal, true);



                 return true;
         }

         void SetHandlerToContex(CefRefPtr<CefV8Handler> Handler, CefRefPtr<CefV8Value> object, const char* MetodName)
         {

                 CefRefPtr<CefV8Value> CallNetObject = CefV8Value::CreateFunction(MetodName, Handler);

                 // Add the "myfunc" function to the "window" object.
                 object->SetValue(MetodName, CallNetObject, V8_PROPERTY_ATTRIBUTE_NONE);
         }

         void ContextForNetHandlers::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
         {
                 this->context = context;
                 // Retrieve the context's window object.
                 CefRefPtr<CefV8Value> object = context->GetGlobal();
                 NetObjectToNative::ManagedDomainLoader* mD = NetObjectToNative::ManagedDomainLoader::InitManagedDomain(L"c:\\Program Files\\DNX\\runtimes\\dnx-coreclr-win-x86.1.0.0-rc1-update1\\bin\\", L"", L"");

                 //=========== CallNetMethod =======================================================
                 SetHandlerToContex(new CallNetObjectFunction(mD), object, "CallNetMethod");
                 //=========== CallNetDelegate =======================================================
                 SetHandlerToContex(new CallNetDelegate(mD), object, "CallNetDelegate");
                 //=========== PropertySet =======================================================
                 SetHandlerToContex(new CallNetObjectPropertySet(mD), object, "CallNetPropertySet");
                 //=========== PropertyGet =======================================================
                 SetHandlerToContex(new CallNetObjectPropertyGet(mD), object, "CallNetPropertyGet");
                 //=========== PropertyGet =======================================================
                 SetHandlerToContex(new DeleteNetObject(mD), object, "DeleteNetObject");
                 //=========== SetCallBacks =======================================================
                 SetHandlerToContex(new SetCallBacks(mD, this, object), object, "RegisterCallBacks");
                 //============ CallAsyncNetObjectFunction ================================
                 SetHandlerToContex(new CallAsyncNetObjectFunction(mD, this), object, "CallAsyncNetObjectFunction");
                 //============ CallNetObjectGenericFunction ================================
                 SetHandlerToContex(new CallNetObjectGenericFunction(mD), object, "CallNetObjectGenericFunction");
                 //============ IteratorNext ================================
                 SetHandlerToContex(new IteratorNext(mD), object, "IteratorNext");

         }

        

         
         void ContextForNetHandlers::AsyncCalBack(const wchar_t* TaskID, bool Successfully, tVariant* ReturnValue)
         {
                

                 if (!CefCurrentlyOn(TID_RENDERER)) {
                         // Execute on the UI thread.
                        // CefPostTask(TID_UI, base::Bind(&AsyncCalBack2, TaskID, Successfully,ReturnValue, CallbackContext));
                         CefPostTask(TID_RENDERER, base::Bind(&SetCallBacks::AsyncCalBack, this->scb, TaskID, Successfully, ReturnValue));
                         return;
                 }

                 scb->AsyncCalBack(TaskID, Successfully, ReturnValue);
         }

         //==================== Set CallBacs
         bool SetCallBacks::Execute(const CefString& name,
                 CefRefPtr<CefV8Value> object,
                 const CefV8ValueList& arguments,
                 CefRefPtr<CefV8Value>& retval,
                 CefString& exception) {

                    this_id = std::this_thread::get_id();
                         if (arguments.size() == 1 && arguments[0]->IsFunction()) {
                                 AsyncMetodCall = arguments[0];
                                 CallbackContext = CefV8Context::GetCurrentContext();
                                 cfn->scb = this;

                                 /*CefV8ValueList args;
                                 args.push_back(CefV8Value::CreateBool(true));
                                 args.push_back(CefV8Value::CreateString(L"Первый"));
                                 args.push_back(CefV8Value::CreateString(L"Второй"));

                                 if (AsyncMetodCall->ExecuteFunctionWithContext(CallbackContext, globalObj, args)) {
                                        
                                 }*/
                                
                                 return true;
                         }
                

                 return true;

         }

         void SetCallBacks::AsyncCalBack(const wchar_t* TaskID, bool Successfully, tVariant* ReturnValue)
         {
                 CefV8ValueList args;
                 
                 std::thread::id Curr_id = std::this_thread::get_id();
                 if (this_id != Curr_id)
                 {
                 }

                 if (CallbackContext.get() && CallbackContext->Enter()) {

                         args.push_back(CefV8Value::CreateBool(true));

                         args.push_back(CefV8Value::CreateString(TaskID));
                         delete[] TaskID;

                         if (ReturnValue==nullptr)
                                 args.push_back(CefV8Value::CreateUndefined());
                         else
                         {
                         args.push_back(NetObjectToNative::ConvertNetToCef(ReturnValue, true));
                         delete[] ReturnValue;
                         }
 
                         
                         if (AsyncMetodCall->ExecuteFunctionWithContext(CallbackContext, globalObj, args)) {
                                 
                         }
                         CallbackContext->Exit();
                 }
         }      
}



В планах добавить события по аналогии с 1С,.Net Core. Динамическая компиляция класса обертки для получения событий .Net объекта в 1С.

Если вдруг кого то заинтересовало, то проекты и исходники можно скачать здесь.

Краткое описание содержимого. В каталоге cefsimple\Release\ лежит исполняемый файл с библиотеками и начальной страницей Test.html. В каталоге cefsimple\NetObjectToNative\
лежат все файлы для обмена между CEF и .Net Core. ManagedDomainLoader и ClrLoader отвечают за загрузку .Net Core, получения и передачу методов для обмена данными.

В CefV8HandlersForNet реализованы Хэндлеры для обмена между JS и CEF. В NetConverter конвертация данными между Net и Cef.

В NetObjectToCEF лежат файлы которые реализуют обмен с CEF. В TestDllForCoreClr лежат все используемые примеры для Тестовый.

В файле TestTypeScript\TestTypeScript\app\ лежат файлы ts которые и реализуют Proxy. NetProxy.ts файл реализующий Proxy.

home.component.ts тест с AngleSharp. counter.component.ts различные тесты возможностей. TestSpeed.ts тесты скорости выполнения

Так жепроект без node_modules. установите через вызов в директории TestTypeScript npm install

Суть тестов такова. Запускаете TestTypeScript и CefProgects\cefsimple\Release\cefsimple.exe. На начальной странице можно попробовать тесты на JS. Для использования тестов на TS нужно перейти на сайт который нужно указать в поле ниже «Введите адрес сайта « что бы перейти на него»». Там три теста.

Если хотите компилировать cefsimple. То скачайте отсюда http://ift.tt/2ixa81U 32-разрядный Standard Distribution и замените в директории tests\cefsimple\ сс и h файлы и скопируйте директорию NetObjectToNative.

Для использования VS 2015 введите в корневом каталоге CEF cmake.exe -G «Visual Studio 14»

Для VS 2017 cmake.exe -G «Visual Studio 15 2017».

Спасибо за внимание!

Комментарии (0)

    Let's block ads! (Why?)

    Комментариев нет:

    Отправить комментарий