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» зробивши опис класів і використовувати IntelliSense при кодуванні на TS.

Але я прекрасно розумію, що це всього лише пишномовні слова, і мало кому це потрібно. Але мені дуже цікаво, особливо після програмування на 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
TO let = 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 (i let 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».

Спасибі за увагу!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.