NanUI icon indicating copy to clipboard operation
NanUI copied to clipboard

如何实现C#事件通知到V8Context中?

Open 527395632 opened this issue 2 years ago • 2 comments

我们在注册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
        { }
    }
}

}

527395632 avatar Apr 25 '22 12:04 527395632

另外, 这个CefGlue是否可以单独做一个浏览器控件的项目?

527395632 avatar Apr 25 '22 12:04 527395632

对于第一个问题,建议使用 WindowBinding 扩展一些方法到 JS 里,通过这些方法来调用后端的 C# 代码。

对于第二个问题,因为我年前被公司辞退,现在从头再来,因此时间没有之前多暂时没有尽力开启一个新的项目来维护;其次,作为浏览器控件 WebView2 是个更好的选择。

XuanchenLin avatar Apr 28 '22 14:04 XuanchenLin