Меня все спрашивают — «Зачем это нужно?». На что, я гордо отвечаю — «Я в 1С использую для доступа к торговому оборудованию, к Вэб-сервисам по ws-протоколам, готовым компонентам. 1С, Linux, Excel, Word, OpenXML,ADO и Net Core. Кроме того, сделаю передачу JS объектов на сторону .Net с использованием через DynamicObject.
Можно сделать определенную конфигурацию CEF для всех платформ и можно делать кросспалатформенные декстопные приложения. Как аналог Electron. .Net Core развивается и можно достаточно легко перевести приложения под WPF и UWP на Angular 2» сделав описание классов и использовать IntelliSense при кодировании на TypeScript.
Но я прекрасно понимаю, что это всего лишь высокопарные слова, и мало кому это нужно. Но мне чертовски интересно, особенно после программирования на 1С.
Для показа возможностей, возьму пример из моей статьи Кроссплатформенное использование классов .Net в 1С через Native ВК. Или замена COM на Linux II.
В нем куча сахара и показывает все проблемы. Сразу прошу прощения за Руслиш. Я очень стараюсь, но у меня на нем куча примеров, а времени очень мало.
На TypeScript это выглядит так:
Прежде всего видим главные отличия от C#. Для получения свойства нужно добавить "_"
Для вызова асинхронного метода нужно добавить ключевое слово async:
Для вызова дженерик метода, если нельзя вывести типы по параметрам то аргументы указываем в массиве:
Ну и главное, нужно вручную удалить ссылку на объект со стороны .Net
По скорости вызовов на моем Intel Core i3-2120 CPU 3.3 GHz.
Что в общем-то вполне приемлемо.
Приведу еще небольшой пример.
На TypeScript можно вызвать так:
Теперь за счет чего это достигается. Кроссплатформенное использование классов .Net из неуправляемого кода. Или аналог IDispatch на Linux. Через CEF встраиваем нужные методы:
Для удобного использования этих методов создадим класс прокси с Target гибридного типа:
Target должен быть функцией для возможности использования new и (). Теперь нам понадобится Handler:
Ну и понадобится сам Target:
Для обертки результата из CEF используется:
Что касается асинхронных методов то они работают через два метода:
И при получении асинхронного результата:
где:
Весь код можно посмотреть ниже под спойлером:
Прошу прощение за моё незнание С++. Но, делать было нужно сейчас, а я на нем не пишу.
Не смог прикрутить Dev Tools к CefSimple:
В планах добавить события по аналогии с 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. То скачайте отсюда opensource.spotify.com/cefbuilds/index.html 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».
Спасибо за внимание!
Можно сделать определенную конфигурацию CEF для всех платформ и можно делать кросспалатформенные декстопные приложения. Как аналог Electron. .Net Core развивается и можно достаточно легко перевести приложения под WPF и UWP на Angular 2» сделав описание классов и использовать IntelliSense при кодировании на TypeScript.
Но я прекрасно понимаю, что это всего лишь высокопарные слова, и мало кому это нужно. Но мне чертовски интересно, особенно после программирования на 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 = "https://en.wikipedia.org/wiki/List_of_The_Big_Bang_Theory_episodes"; // загружаем страницу и разбираем её //Метод расширения //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 = "https://en.wikipedia.org/wiki/List_of_The_Big_Bang_Theory_episodes"; // загружаем страницу и разбираем её 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. То скачайте отсюда opensource.spotify.com/cefbuilds/index.html 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».
Спасибо за внимание!
