NanUI
NanUI copied to clipboard
如何实现C#事件通知到V8Context中?
我们在注册C#对象的时候, C# 对象中可能会存在很多的事件, 这些事件可以通过js赋值function的形式绑定, 这个需要如何完成?我目前实现了C#事件动态绑定的代码, 不知道如何才能通知到V8Context, 我尝试了CallFunction的方法, 但是报的是V8Context为null。 我现在提供C#代码动态绑定事件的代码, 希望这个功能可以实现上去。
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks;
namespace XXXXXXXXX { public delegate object DynamicEventHandler(object sender, object[] args); public class DynamicEvent : IDisposable { private IntPtr _handle = IntPtr.Zero; private Delegate _method; private DynamicEventHandler _handler; private static List<DynamicEvent> _bindEvents = new List<DynamicEvent>();
public DynamicEvent(object target, EventInfo e)
{
_handle = GCHandle.ToIntPtr(GCHandle.Alloc(this));
Target = target;
EveInfo = e;
var types = EveInfo.EventHandlerType.GetMethod("Invoke").GetParameters().Select(q => q.ParameterType).ToArray();
var method = new DynamicMethod(string.Empty, null, types, typeof(DynamicEvent).Module);
var gen = method.GetILGenerator();
if (IntPtr.Size == 8)
gen.Emit(OpCodes.Ldc_I8, _handle.ToInt64());
else
gen.Emit(OpCodes.Ldc_I4, _handle.ToInt32());
//gen.Emit(OpCodes.Ldstr, e.Name);
gen.Emit(OpCodes.Ldc_I4_S, types.Length);
gen.Emit(OpCodes.Newarr, typeof(object));
for (int i = 0; i < types.Length; i++)
{
gen.Emit(OpCodes.Dup);
gen.Emit(OpCodes.Ldc_I4, i);
gen.Emit(OpCodes.Ldarg, i);
if (types[i].IsValueType)
gen.Emit(OpCodes.Box, types[i]);
gen.Emit(OpCodes.Stelem_Ref);
}
gen.Emit(OpCodes.Call, typeof(DynamicEvent).GetMethod("OnEventExecute", BindingFlags.NonPublic | BindingFlags.Static));
gen.Emit(OpCodes.Pop);
gen.Emit(OpCodes.Ret);
_method = method.CreateDelegate(EveInfo.EventHandlerType);
}
public object Target { get; }
public EventInfo EveInfo { get; }
public void Bind(DynamicEventHandler eventHandler)
{
if (_handler == null)
{
lock (this)
{
if (_handler == null)
{
EveInfo.AddEventHandler(Target, _method);
_handler = eventHandler;
_bindEvents.Add(this);
return;
}
}
}
throw new NotSupportedException("事件已绑定, 请解绑后再尝试!");
}
public void UnBind()
{
if (_handler != null)
{
lock (this)
{
if (_handler != null)
{
EveInfo.RemoveEventHandler(Target, _method);
_handler = null;
_bindEvents.Remove(this);
}
}
}
}
internal static object OnEventExecute(IntPtr self, object[] args)
{
var context = _bindEvents.FirstOrDefault(q => q._handle.Equals(self));
if (context != null)
{
if (args.Length > 0 && args[0] == context.Target)
{
var arr = new object[args.Length - 1];
Array.Copy(args, 1, arr, 0, arr.Length);
args = arr;
}
return context._handler?.Invoke(context.Target, args);
}
return null;
}
public void Dispose()
{
try
{
UnBind();
}
catch
{ }
try
{
GCHandle.FromIntPtr(_handle).Free();
}
catch
{ }
}
}
}
另外, 这个CefGlue是否可以单独做一个浏览器控件的项目?
对于第一个问题,建议使用 WindowBinding 扩展一些方法到 JS 里,通过这些方法来调用后端的 C# 代码。
对于第二个问题,因为我年前被公司辞退,现在从头再来,因此时间没有之前多暂时没有尽力开启一个新的项目来维护;其次,作为浏览器控件 WebView2 是个更好的选择。