Меня все спрашивают — «Зачем это нужно?». На что, я гордо отвечаю — «Я в 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».
Спасибо за внимание!