- PVSM.RU - https://www.pvsm.ru -
Это продолжение статьи CEF, ES6, Angular 2, TypeScript использование классов .Net Core для расширения возможностей [1].
Как и ожидалось, она не привлекла особого внимания. Но огромное спасибо тем, кого заинтересовало моё творчество. Именно вы даете мне стимул на продолжение изысканий.
Хочу немного остановиться на CEF [2].
Это кроссплатформенный браузер (с ядром используемым Google Chrome), с неограаниченными расширениями за счет использования натива на С++, позволяющее писать полноценное крооссплатформенное декстопное приложение с UI.
Кроме того Chrome 57 принесёт поддержку языков C и C++ для веб-сайтов [3]
Сегодня я покажу как использовать события объектов .Net Core классов в Angular 2.
Многие прочитав мою первую статью приводили довод, что вместо использования классов .Net можно использовать HTTP сервисы.
Но вот с событиями мы можем делать полноценное декстопное приложение используя торговое оборудование, обмен данными по различным протоколам, использование мессенджеров итд итп.
Для примера возьмем класс с событиями.
public class EventTest
{
public event Action<string, int> EventWithTwoParameter;
public event Action<string> EventWithOneParameter;
public event Action EventWithOutParameter;
public bool IsRun = false;
public void Test()
{
EventWithTwoParameter?.Invoke(DateTime.Now.ToString(), 1);
EventWithOneParameter?.Invoke(DateTime.UtcNow.ToString());
EventWithOutParameter?.Invoke();
}
public async void Run()
{
if (IsRun) return;
IsRun = true;
while (IsRun)
{
await Task.Delay(2000);
Test();
}
}
}
Теперь мы можем использовать этот класс в Angular 2:
export class TestEventComponent {
EventsRes: EventRes[] = [];
WOWE: WrapperObjectWithEvents;
test: any;
EventTest: any;
constructor(private ngZone: NgZone) {
let Net = NetObject.NetWrapper;
// Получим тип используемого класса.
this.EventTest = Net.GetType("TestDllForCoreClr.EventTest", "TestDllForCoreClr");
// Создадим объект
this.test = new this.EventTest();
// Создадим обёртку для событий, через которую будем подписываться
// и отписываться от событий.
this.CreateWrapperForEvents(this.test);
}
// Этот код автоматически создается для уменьшения писанины
// Описывается структура параметров.
// параметр value:Анонимный Тип
// Свойства параметра
// arg1:System.String
// arg2:System.Int32
public EventWithTwoParameter(value: any) {
this.AddComment("EventWithTwoParameter", NetObject.NetWrapper.toString(value));
value(NetObject.FlagDeleteObject);
}
// параметр value:System.String
public EventWithOneParameter(value: any) {
this.AddComment("EventWithOneParameter ",NetObject.NetWrapper.toString(value));
}
public EventWithOutParameter(value: any) {
this.AddComment("EventWithOutParameter", NetObject.NetWrapper.toString(value));
}
CreateWrapperForEvents(obj: any): void {
let wrapForEvents = NetObject.GetWrapperForObjectWithEvents(obj, this.ngZone);
wrapForEvents.AddEventHandler("EventWithTwoParameter", this.EventWithTwoParameter.bind(this));
wrapForEvents.AddEventHandler("EventWithOneParameter", this.EventWithOneParameter.bind(this));
wrapForEvents.AddEventHandler("EventWithOutParameter", this.EventWithOutParameter.bind(this));
// установить переменную wrapForEvents переменной класса
this.WOWE = wrapForEvents;
}
Ну и не забыть очистить ссылки на стороне .Net при разрушении компонента:
ngOnDestroy() {
NetObject.DeleteNetObjets(this.EventTest, this.test);
this.WOWE.Close();
alert("Количество ссылок на стороне .Net ="+Net.CountItemsInStore());
}
Отписаться от событий можно тремя способами.
// получим результат мотода subscribe объета Subject.
this.AddEventHandlerResult= wrapForEvents.AddEventHandler("EventWithTwoParameter", this.EventWithTwoParameter.bind(this));
И используя его опишемся от события:
this.AddEventHandlerResult.unsubscribe();
Но события из .Net будут обрабатываться на стороне JS.
Следующие два варианта говорят сам за себя.
this.WOWE.RemoveEventHandler("EventWithTwoParameter");
this.WOWE.RemoveAllEventHandler();
Получить текст TS модуля для описания событий можно получить так:
let DescribeMethodsTS= Net.GetType("NetObjectToNative.DescribeMethodsTS", "NetObjectToNative");
this.CodeModule = DescribeMethodsTS.GetCodeModuleTS(this.EventTest);
Для чего нужен NgZone можно почитать здесь. Что такое Зоны(Zones)? [4]
Теперь перейдем к подноготной. Для получении обертки событий используется динамическая компиляция. Процесс подробно описан 1С,.Net Core. Динамическая компиляция класса обертки для получения событий .Net объекта в 1С [5]
Для CEF внесены некоторые изменения:
//Данный класс используется для подписки на событие и передачи данных на сторону CEF
public class ClassForEventCEF
{
EventInfo EI;
public string EventKey;
public IntPtr CppHandler;
public object WrapperForEvent;
public ClassForEventCEF(object WrapperForEvent, string EventKey, EventInfo EI, IntPtr CppHandler)
{
this.EventKey = EventKey;
this.EI = EI;
this.CppHandler = CppHandler;
this.WrapperForEvent = WrapperForEvent;
// Подпишемся на событие
EI.AddEventHandler(WrapperForEvent, new System.Action<object>(CallEvent));
}
public void CallEvent(object value)
{
IntPtr ResIntPtr = AutoWrap.AllocMem(48);
var EventKeyPtr = WorkWithVariant.WriteStringInIntPtr(EventKey);
WorkWithVariant.SetObjectInIntPtr(AutoWrap.WrapObject(value), ResIntPtr);
// Вызовем объектный метод на стороне CEF
// С передачей Ключа события и параметры события
AutoWrap.EventCall(CppHandler, EventKeyPtr, ResIntPtr);
}
public void RemoveEventHandler()
{
EI.RemoveEventHandler(WrapperForEvent, new System.Action<object>(CallEvent));
}
}
Этот класс сформирован динамически:
public class WrapperForEventTestDllForCoreClr_EventTest
{
public IntPtr CppHandler;
public TestDllForCoreClr.EventTest Target;
Dictionary<string, ClassForEventCEF> EventStoage=new Dictionary<string, ClassForEventCEF>();
public event Action<object> EventWithTwoParameter;
public event Action<object> EventWithOneParameter;
public event Action<object> EventWithOutParameter;
public WrapperForEventTestDllForCoreClr_EventTest(IntPtr CppHandler, TestDllForCoreClr.EventTest Target)
{
this.CppHandler = CppHandler;
this.Target = Target;
Target.EventWithTwoParameter += (arg1,arg2) =>
{
if (EventWithTwoParameter!=null)
{
var EventWithTwoParameterObject = new {arg1=arg1,arg2=arg2};
EventWithTwoParameter(EventWithTwoParameterObject);
}
};
Target.EventWithOneParameter += (obj) =>
{
if (EventWithOneParameter!=null)
EventWithOneParameter(obj);
};
Target.EventWithOutParameter += () =>
{
if (EventWithOutParameter!=null)
EventWithOutParameter(null);
};
}
public void AddEventHandler(string EventKey, string EventName)
{
EventInfo ei = GetType().GetEvent(EventName);
var forEvent = new ClassForEventCEF(this,EventKey, ei,CppHandler);
EventStoage.Add(EventKey, forEvent);
}
public void RemoveEventHandler(string EventKey)
{
ClassForEventCEF cfe = null;
if (EventStoage.TryGetValue(EventKey,out cfe))
{
EventStoage.Remove(EventKey);
cfe.RemoveEventHandler();
}
}
public void RemoveAllEventHandler()
{
foreach( var cfe in EventStoage.Values)
cfe.RemoveEventHandler();
EventStoage.Clear();
}
public static object CreateObject(IntPtr Self, TestDllForCoreClr.EventTest Target)
{
return new WrapperForEventTestDllForCoreClr_EventTest(Self, Target);
}
}
return new Func<IntPtr, TestDllForCoreClr.EventTest, object>(WrapperForEventTestDllForCoreClr_EventTest.CreateObject);
Ну и на стороне JS событие обрабатывается так:
class EventEmitter{
public subject = new Subject<any>();
constructor(private ngZone: NgZone) {
// this.data = Observable.create((observer: any) => this.dataObserver = <Observer<any>>observer);
}
public subscribe(EventHandler: (value: any) => void) {
return this.subject.subscribe({
next: (v) => this.ngZone.run(()=> EventHandler(v))
});
}
public emit(value: any) {
this.subject.next(value);
}
public Complete() {
this.subject.complete();
}
}
class EventItem {
constructor(public EventKey: string, public Event:EventEmitter){}
}
//EventEmitter
export class WrapperObjectWithEvents {
// словарь имен события и EventKey с EventEmitter
EventsList = new Map<string, EventItem>();
// Словарь EventKey и EventEmitter
EventEmittersList = new Map<string, EventEmitter>();
constructor(private NetTarget: any, private ngZone: NgZone) { };
// Вызывается при получении внешнего события из .Net
public RaiseEvent(EventKey: string, value: any) {
// Если есть подписчики, то вызываем их
if (this.EventEmittersList.has(EventKey)) {
let Event = this.EventEmittersList.get(EventKey);
Event.emit(value);
}
}
public AddEventHandler(EventName: string, EventHandler: (value: any) => void): any {
let ei: EventItem;
let isFirst = false;
if (!this.EventsList.has(EventName)) {
let EventKey = window.CallNetMethod(0, "GetUniqueString");
let Event = new EventEmitter(this.ngZone);
ei = new EventItem(EventKey, Event);
this.EventsList.set(EventName, ei);
this.EventEmittersList.set(EventKey, Event);
NetObject.EventCallers.set(EventKey, this.RaiseEvent.bind(this));
isFirst = true;
}
else
ei = this.EventsList.get(EventName);
// let res = ei.Event.subscribe(this.ngZone.run(() =>EventHandler));
let res = ei.Event.subscribe((value: any) => { EventHandler(value) });
if (isFirst)
this.NetTarget.AddEventHandler(ei.EventKey, EventName);
return res;
}
public RemoveEventHandler(EventName: string) {
if (this.EventsList.has(EventName)) {
let ei = this.EventsList.get(EventName);
let EventKey = ei.EventKey
this.NetTarget.RemoveEventHandler(EventKey);
NetObject.EventCallers.delete(EventKey);
this.EventEmittersList.delete(EventKey);
this.EventsList.delete(EventName);
ei.Event.Complete();
}
}
public RemoveAllEventHandler() {
this.NetTarget.RemoveAllEventHandler();
for (let ei of this.EventsList.values()) {
{
NetObject.EventCallers.delete(ei.EventKey);
ei.Event.Complete();
}
this.EventsList.clear();
this.EventEmittersList.clear();
}
}
public Close()
{
this.RemoveAllEventHandler();
this.NetTarget(NetObject.FlagDeleteObject);
}
}
Не сложно сделать передачу JS объектов и функций на сторону .Net только на время вызова метода.
Или по аналогии с Net сделать хранилище объектов JS. Благо в .Net есть финализаторы и не так критично следить за освобождением ссылок.
Хотя разработку всего скачало 5 человек. Но на самом деле там много интереного как для программирующих на TS,C# и связки между C++ и .Net.
Если вдруг кого то заинтересовало, то проекты и исходники можно скачать здесь [6].
Краткое описание содержимого. В каталоге cefsimpleRelease лежит исполняемый файл с библиотеками и начальной страницей Test.html. В каталоге cefsimpleNetObjectToNative
лежат все файлы для обмена между CEF и .Net Core. ManagedDomainLoader и ClrLoader отвечают за загрузку .Net Core, получения и передачу методов для обмена данными.
В CefV8HandlersForNet реализованы Хэндлеры для обмена между JS и CEF. В NetConverter конвертация данными между Net и Cef.
В NetObjectToCEF лежат файлы которые реализуют обмен с CEF. В TestDllForCoreClr лежат все используемые примеры для Тестовый.
В файле TestTypeScriptTestTypeScriptapp лежат файлы ts которые и реализуют Proxy. NetProxy.ts файл реализующий Proxy.
home.component.ts тест с AngleSharp. counter.component.ts различные тесты возможностей. TestSpeed.ts тесты скорости выполнения.
Так же проект без node_modules. установите через вызов в директории TestTypeScript npm install.
Суть тестов такова. Запускаете TestTypeScript и CefProgectscefsimpleReleasecefsimple.exe. На начальной странице можно попробовать тесты на JS. Для использования тестов на TS нужно перейти на сайт который нужно указать в поле ниже «Введите адрес сайта « что бы перейти на него»». Там три теста.
Если хотите компилировать cefsimple. То скачайте отсюда [7] 32-разрядный Standard Distribution и замените в директории testscefsimple сс и h файлы и скопируйте директорию NetObjectToNative.
Для использования VS 2015 введите в корневом каталоге CEF cmake.exe -G «Visual Studio 14».
Для VS 2017 cmake.exe -G «Visual Studio 15 2017».
Автор: Serginio1
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-2/241778
Ссылки в тексте:
[1] CEF, ES6, Angular 2, TypeScript использование классов .Net Core для расширения возможностей: https://habrahabr.ru/post/320960/
[2] CEF: https://en.wikipedia.org/wiki/Chromium_Embedded_Framework
[3] Chrome 57 принесёт поддержку языков C и C++ для веб-сайтов: http://4pda.ru/2017/02/06/335097/
[4] Что такое Зоны(Zones)?: http://www.sdblog.ru/archives/zony-i-change-detection-v-ionic-2-i-angular-2/
[5] 1С,.Net Core. Динамическая компиляция класса обертки для получения событий .Net объекта в 1С : https://habrahabr.ru/post/309850/
[6] здесь: https://yadi.sk/d/0If7C5jZ3CEhmf
[7] отсюда: http://opensource.spotify.com/cefbuilds/index.html
[8] Источник: https://habrahabr.ru/post/321452/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.