📜 ⬆️ ⬇️

CEF, Angular 2 using .Net Core class events

This is a continuation of the article CEF, ES6, Angular 2, TypeScript using .Net Core classes to expand capabilities .

As expected, she did not attract much attention. But many thanks to those who are interested in my work. It is you who give me the incentive to continue research.

I want to stay a bit at CEF .
')
This is a cross-platform browser (with the kernel used by Google Chrome), with unlimited extensions due to the use of native C ++, which allows you to write a full-fledged cross-platform dext-based application with UI.

In addition, Chrome 57 will bring support for C and C ++ languages ​​for websites.

Today I will show how to use the events of the objects .Net Core classes in Angular 2.
Many people after reading my first article argued that HTTP services can be used instead of using .Net classes.

But with events we can make a full-fledged dextup application using trading equipment, exchanging data using various protocols, using instant messengers and so on.

For example, take a class with events.

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(); } } } 

Now we can use this class in 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; } 

Well, do not forget to clear the links on the .Net side when the component is destroyed:

 ngOnDestroy() { NetObject.DeleteNetObjets(this.EventTest, this.test); this.WOWE.Close(); alert("    .Net ="+Net.CountItemsInStore()); } 

You can unsubscribe from events in three ways.

// get the result of the subscribe method of the subject Subject.

 this.AddEventHandlerResult= wrapForEvents.AddEventHandler("EventWithTwoParameter", this.EventWithTwoParameter.bind(this)); 

And using it we will describe from the event:

 this.AddEventHandlerResult.unsubscribe(); 

But events from .Net will be processed on the side of JS.

The following two options speak for themselves.

 this.WOWE.RemoveEventHandler("EventWithTwoParameter"); this.WOWE.RemoveAllEventHandler(); 

Get the text of the TS module to describe the events can be obtained as follows:

 let DescribeMethodsTS= Net.GetType("NetObjectToNative.DescribeMethodsTS", "NetObjectToNative"); this.CodeModule = DescribeMethodsTS.GetCodeModuleTS(this.EventTest); 

What is NgZone for you can read here. What are Zones?

We now turn to the inside story. Dynamic compilation is used to get event wrappers. The process is described in detail in 1C, .Net Core. Dynamic compilation of a wrapper class for receiving events of a .Net object in 1C

Some changes have been made to CEF:

Event dynamic wrapper code
 //            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)); } } 

This class is dynamically generated:

 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); 


Well, on the JS side, the event is handled like this:

TS event wrapper code
 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); } } 



By the way, a good article on the topic. Jet extensions in .Net
As for gluttony
CEF with the initial page without Angular 2, but with loaded .Net Core assemblies. takes 20 mb
With the call of unpretentious methods comes to 30mb
If you connect a dynamic compilation, it grows to 70 mb

If you connect Angular 2, then the size immediately reaches 90 MB.

But even further I use dynamic compilation. Size does not go over 100 MB

It should be borne in mind that 2 garbage collectors are working.
It is quite acceptable.

It is not difficult to make the transfer of JS objects and functions to the .Net side only for the duration of the method call.

Or, by analogy with Net, make a JS object storage. Fortunately, in .Net there are finalizers and it is not so critical to follow the release of links.

Although the development of all downloaded 5 people. But in fact, there are a lot of interne for programmers in TS, C # and the bundles between C ++ and .Net.

If someone is interested, then the projects and sources can be downloaded here .

A brief description of the content. In the cefsimple \ Release \ directory there is an executable file with libraries and the initial page Test.html. In the cefsimple \ NetObjectToNative \ directory
There are all files for sharing between CEF and .Net Core. ManagedDomainLoader and ClrLoader are responsible for downloading .Net Core, receiving and passing methods for exchanging data.

Handlers for exchange between JS and CEF are implemented in CefV8HandlersForNet. NetConverter converts data between Net and Cef.

In NetObjectToCEF are files that implement the exchange with CEF. In TestDllForCoreClr lie all used examples for Test.

In the file TestTypeScript \ TestTypeScript \ app \ there are ts files that implement the proxy. NetProxy.ts file that implements the proxy.

home.component.ts test with AngleSharp. counter.component.ts various feature tests. TestSpeed.ts speed tests.

Also a project without node_modules. install through a call in the TestTypeScript directory npm install.

The essence of the tests is as follows. Run TestTypeScript and CefProgects \ cefsimple \ Release \ cefsimple.exe. On the initial page, you can try the tests on JS. To use TS tests, you need to go to the site that you need to specify in the field below “Enter the site address“ to go to it ”.” There are three tests.

If you want to compile cefsimple. Then download the 32-bit Standard Distribution from here and replace the tests \ cefsimple \ cc and h files in the directory and copy the NetObjectToNative directory.

To use VS 2015, type cmake.exe -G "Visual Studio 14" in the root directory of the CEF.

For VS 2017 cmake.exe -G "Visual Studio 15 2017".

Source: https://habr.com/ru/post/321452/


All Articles