Debugging hangs if an event handler for a UI element is set and event handler gets executed (V8)

Oct 3, 2016 at 1:21 AM
Edited Oct 3, 2016 at 1:22 AM
Hi,

I'm using ClearScript (v5.4.6) in a Winforms application.

I add the form object as "main" to the script engine.
            engine.AddHostObject("main", this);
            engine.AddHostType("Console", typeof(Console));
I connect to the SizeChanged event of the form using the following script:
var connect1 = main.SizeChanged.connect(function (sender, e) {
    Console.WriteLine("SizeChanged");
});
When I run the script and resize the form I see "SizeChanged" message in the output window.

But when I start the debugger and resize the form, my winforms application hangs.

Is there a workaround to overcome this problem?
Coordinator
Oct 3, 2016 at 12:37 PM
Hi ahmetuzun,

What debugger are you using? That is, are you using Visual Studio, or are you debugging the script code via Eclipse (or some other V8-compatible debugger)?

Also, when your application hangs, can you break in and get a stack trace?

Thanks!
Oct 3, 2016 at 1:00 PM
Edited Oct 3, 2016 at 1:12 PM
Hi,

I am using a modified version of node-inspector which is a V8-compatible debugger.

I think this is a thread related issue because I made another test using delegates.

Here is the related C# code:
        public delegate void FormEvent(string eventName, object sender, EventArgs e);
        private FormEvent callbacks;

        public void RegisterToFormEvents(FormEvent eventHandler)
        {
            callbacks += eventHandler;
        }

        private void MainForm_Resize(object sender, EventArgs e)
        {
                new Thread(() =>
                {
                    if (callbacks != null)
                        callbacks("Resize", sender, e);
                }).Start();           
        }

And here is the Javascript code:
var callback1 = host.del(FormEvent, function (eventName, sender, e) {
    Console.WriteLine(eventName + " triggered");
});
main.RegisterToFormEvents(callback1);
I run the script and put a breakpoint at "Console.WriteLine" line.

The following calling method makes the Winforms application hang when it hits the breakpoint:
                    if (callbacks != null)
                        callbacks("Resize", sender, e);
If I call it in a new thread, it works fine:
                new Thread(() =>
                {
                    if (callbacks != null)
                        callbacks("Resize", sender, e);
                }).Start();           
Oct 3, 2016 at 1:10 PM
Edited Oct 3, 2016 at 1:11 PM
If you mean "call stack" by "stack trace", the following is the one I copied when the application hanged by using Ctrl-Break.

[Managed to Native Transition]
ClearScriptV8-32.dll!Microsoft.ClearScript.V8.V8ObjectImpl.Invoke(object[] gcArgs, bool asConstructor) + 0x8e bytes 
ClearScript.dll!Microsoft.ClearScript.V8.V8ScriptItem.TryBindAndInvoke(System.Dynamic.DynamicMetaObjectBinder binder, object[] args, out object result) + 0x1d0 bytes   
ClearScript.dll!Microsoft.ClearScript.ScriptItem.TryWrappedBindAndInvoke.AnonymousMethod__0() + 0x5a bytes  
ClearScript.dll!Microsoft.ClearScript.ScriptEngine.ScriptInvoke<bool>(System.Func<bool> func) + 0x47 bytes  
ClearScript.dll!Microsoft.ClearScript.V8.V8ScriptEngine.ScriptInvoke<bool>.AnonymousMethod__24() + 0xf bytes    
ClearScriptV8-32.dll!<Module>.Microsoft.ClearScript.V8.?A0x792c8756.LockCallback(void* pvArg) + 0xa bytes   
[Native to Managed Transition]  
[Managed to Native Transition]  
ClearScriptV8-32.dll!Microsoft.ClearScript.V8.V8ContextProxyImpl.InvokeWithLock(System.Action gcAction) + 0xb1 bytes    
ClearScript.dll!Microsoft.ClearScript.V8.V8ScriptEngine.ScriptInvoke<bool>(System.Func<bool> func) + 0x5a bytes 
ClearScript.dll!Microsoft.ClearScript.ScriptItem.TryWrappedBindAndInvoke(System.Dynamic.DynamicMetaObjectBinder binder, object[] wrappedArgs, out object result) + 0x81 bytes   
ClearScript.dll!Microsoft.ClearScript.ScriptItem.TryWrappedInvokeOrInvokeMember(System.Dynamic.DynamicMetaObjectBinder binder, System.Reflection.ParameterInfo[] parameters, object[] args, out object result) + 0x17c bytes    
ClearScript.dll!Microsoft.ClearScript.ScriptItem.TryInvoke(System.Dynamic.InvokeBinder binder, object[] args, out object result) + 0x79 bytes   
[Lightweight Function]  
System.Core.dll!System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid3<object,object,System.EventArgs>(System.Runtime.CompilerServices.CallSite site, object arg0, object arg1, System.EventArgs arg2) + 0x2bc bytes  
ClearScript.dll!Microsoft.ClearScript.DelegateFactory.ProcShim<object,System.EventArgs,System.EventHandler>.InvokeTarget.AnonymousMethod__29() + 0x191 bytes    
ClearScript.dll!Microsoft.ClearScript.ScriptEngine.SyncInvoke(System.Action action) + 0xa bytes 
ClearScript.dll!Microsoft.ClearScript.DelegateFactory.ProcShim.Invoke(System.Action action) + 0xb bytes 
ClearScript.dll!Microsoft.ClearScript.DelegateFactory.ProcShim<object,System.EventArgs,System.EventHandler>.InvokeTarget(object a1, System.EventArgs a2) + 0xbd bytes   
System.Windows.Forms.dll!System.Windows.Forms.Control.OnSizeChanged(System.EventArgs e) + 0x9d bytes    
Coordinator
Oct 3, 2016 at 1:23 PM
Hi again,

With your application in the hung state, try attaching the Visual Studio debugger (Debug -> Attach to Process), selecting both Native and Managed (v4.x) debugging modes. With Visual Studio attached, click "Debug -> Break All" and use the Threads and Call Stack windows to find the relevant stack.

Good luck!
Oct 3, 2016 at 1:29 PM
And here are the threads. I hope this is what you want.

Not Flagged 13452 0 Worker Thread <No Name> Highest
Not Flagged 11228 6 Worker Thread vshost.RunParkingWindow [Managed to Native Transition] Normal
Not Flagged 12812 7 Worker Thread .NET SystemEvents [Managed to Native Transition] Normal
Not Flagged > 14896 8 Main Thread Main Thread CefSharp.WinForms.Internals.ParentFormMessageInterceptor.WndProc Normal
Not Flagged 10356 9 Worker Thread <No Name> Normal
Not Flagged 8572 10 Worker Thread <No Name> Normal
Not Flagged 12764 15 Worker Thread <No Name> CefSharp.Internals.MethodRunnerQueue.ConsumeTasks Normal
Not Flagged 14768 16 Worker Thread ImageAnimator System.Threading.Thread.Sleep() Normal
Not Flagged 6692 18 Worker Thread <No Name> CefSharp.Internals.MethodRunnerQueue.ConsumeTasks Normal
Not Flagged 12992 19 Worker Thread Worker Thread [Managed to Native Transition] Normal
Not Flagged 3440 11 Worker Thread <No Name> CefSharp.Internals.MethodRunnerQueue.ConsumeTasks Normal
Not Flagged 11748 0 Worker Thread <No Name> Normal
Not Flagged 4720 86 Worker Thread <No Name> Normal
Not Flagged 5960 88 Worker Thread <No Name> OpenBrowser.MainForm.RunCallbacks.AnonymousMethod__0 Normal
Coordinator
Oct 3, 2016 at 1:32 PM
Ah, thanks for posting that call stack, but it looks like the deadlock is in ClearScript or V8's native code. Can you post a mixed (native + managed) call stack? A native-only one could provide a clue as well.
Oct 3, 2016 at 1:44 PM
ntdll.dll!776ac27c()
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll] 
KernelBase.dll!75002cc7()   
KernelBase.dll!75002c02()   
v8-ia32.dll!0f41e03a()  
v8-ia32.dll!0f37195c()  
v8-ia32.dll!0f37304f()  
v8-ia32.dll!0f371ef7()  
v8-ia32.dll!0f36e513()  
v8-ia32.dll!0f4876f4()  
v8-ia32.dll!0f4b75a3()  
v8-ia32.dll!0f318965()  
v8-ia32.dll!0f318207()  
v8-ia32.dll!0f342b86()  
v8-ia32.dll!0f342984()  
ClearScriptV8-32.dll!56e46544()     
ClearScriptV8-32.dll!56e6123a()     
ClearScriptV8-32.dll!56e6079e()     
v8-ia32.dll!0f318207()  
v8-ia32.dll!0f342b86()  
v8-ia32.dll!0f342984()  
ClearScriptV8-32.dll!56e46544()     
ClearScriptV8-32.dll!56e6123a()     
ClearScriptV8-32.dll!56e6079e()     
v8-ia32.dll!0f342b86()  
v8-ia32.dll!0f342984()  
ClearScriptV8-32.dll!56e46544()     
ClearScriptV8-32.dll!56e6123a()     
ClearScriptV8-32.dll!56e6079e()     
v8-ia32.dll!0f342984()  
ClearScriptV8-32.dll!56e46544()     
ClearScriptV8-32.dll!56e6123a()     
ClearScriptV8-32.dll!56e6079e()     
ClearScriptV8-32.dll!56e46544()     
ClearScriptV8-32.dll!56e6123a()     
ClearScriptV8-32.dll!56e6079e()     
[Managed to Native Transition]  
ClearScriptV8-32.dll!Microsoft.ClearScript.V8.V8ObjectImpl.Invoke(object[] gcArgs, bool asConstructor) + 0x8e bytes 
ClearScript.dll!Microsoft.ClearScript.V8.V8ScriptItem.TryBindAndInvoke(System.Dynamic.DynamicMetaObjectBinder binder, object[] args, out object result) + 0x1d0 bytes   
ClearScript.dll!Microsoft.ClearScript.ScriptItem.TryWrappedBindAndInvoke.AnonymousMethod__0() + 0x5a bytes  
ClearScript.dll!Microsoft.ClearScript.ScriptEngine.ScriptInvoke<bool>(System.Func<bool> func) + 0x47 bytes  
ClearScript.dll!Microsoft.ClearScript.V8.V8ScriptEngine.ScriptInvoke<bool>.AnonymousMethod__24() + 0xf bytes    
ClearScriptV8-32.dll!<Module>.Microsoft.ClearScript.V8.?A0x792c8756.LockCallback(void* pvArg) + 0xa bytes   
ClearScriptV8-32.dll!56e44837()     
[Managed to Native Transition]  
ClearScriptV8-32.dll!Microsoft.ClearScript.V8.V8ContextProxyImpl.InvokeWithLock(System.Action gcAction) + 0xb1 bytes    
ClearScript.dll!Microsoft.ClearScript.V8.V8ScriptEngine.ScriptInvoke<bool>(System.Func<bool> func) + 0x5a bytes 
ClearScript.dll!Microsoft.ClearScript.ScriptItem.TryWrappedBindAndInvoke(System.Dynamic.DynamicMetaObjectBinder binder, object[] wrappedArgs, out object result) + 0x81 bytes   
ClearScript.dll!Microsoft.ClearScript.ScriptItem.TryWrappedInvokeOrInvokeMember(System.Dynamic.DynamicMetaObjectBinder binder, System.Reflection.ParameterInfo[] parameters, object[] args, out object result) + 0x17c bytes    
ClearScript.dll!Microsoft.ClearScript.ScriptItem.TryInvoke(System.Dynamic.InvokeBinder binder, object[] args, out object result) + 0x79 bytes   
[Lightweight Function]  
System.Core.dll!System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid3<object,object,System.EventArgs>(System.Runtime.CompilerServices.CallSite site, object arg0, object arg1, System.EventArgs arg2) + 0x2bc bytes  
ClearScript.dll!Microsoft.ClearScript.DelegateFactory.ProcShim<object,System.EventArgs,System.EventHandler>.InvokeTarget.AnonymousMethod__29() + 0x191 bytes    
ClearScript.dll!Microsoft.ClearScript.ScriptEngine.SyncInvoke(System.Action action) + 0xa bytes 
ClearScript.dll!Microsoft.ClearScript.DelegateFactory.ProcShim.Invoke(System.Action action) + 0xb bytes 
ClearScript.dll!Microsoft.ClearScript.DelegateFactory.ProcShim<object,System.EventArgs,System.EventHandler>.InvokeTarget(object a1, System.EventArgs a2) + 0xbd bytes   
System.Windows.Forms.dll!System.Windows.Forms.Control.OnSizeChanged(System.EventArgs e) + 0x9d bytes    
System.Windows.Forms.dll!System.Windows.Forms.Control.UpdateBounds(int x, int y, int width, int height, int clientWidth, int clientHeight) + 0xa7 bytes 
System.Windows.Forms.dll!System.Windows.Forms.Control.UpdateBounds() + 0xcd bytes   
System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m) + 0x381 bytes 
System.Windows.Forms.dll!System.Windows.Forms.ScrollableControl.WndProc(ref System.Windows.Forms.Message m) + 0x30 bytes    
System.Windows.Forms.dll!System.Windows.Forms.Form.WndProc(ref System.Windows.Forms.Message m) + 0x6a bytes 
System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.OnMessage(ref System.Windows.Forms.Message m) + 0x13 bytes    
System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.WndProc(ref System.Windows.Forms.Message m) + 0x35 bytes  
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Callback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam) + 0x80 bytes   
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DefWndProc(ref System.Windows.Forms.Message m) + 0xfb bytes  
CefSharp.WinForms.dll!CefSharp.WinForms.Internals.ParentFormMessageInterceptor.WndProc(ref System.Windows.Forms.Message m) Line 231 + 0xb bytes C#
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Callback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam) + 0x80 bytes   
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DefWndProc(ref System.Windows.Forms.Message m) + 0xfb bytes  
CefSharp.WinForms.dll!CefSharp.WinForms.Internals.ParentFormMessageInterceptor.WndProc(ref System.Windows.Forms.Message m) Line 231 + 0xb bytes C#
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Callback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam) + 0x80 bytes   
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DefWndProc(ref System.Windows.Forms.Message m) + 0xfb bytes  
CefSharp.WinForms.dll!CefSharp.WinForms.Internals.ParentFormMessageInterceptor.WndProc(ref System.Windows.Forms.Message m) Line 231 + 0xb bytes C#
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam) + 0x5e bytes 
user32.dll!750e8e71()   
user32.dll!750e90d1()   
user32.dll!751177ce()   
user32.dll!750f55df()   
user32.dll!750ec2df()   
uxtheme.dll!74862e1b()  
uxtheme.dll!74862e58()  
user32.dll!750ec275()   
user32.dll!750e8e71()   
user32.dll!750e90d1()   
user32.dll!750ed6c5()   
user32.dll!750e90d1()   
user32.dll!751177ce()   
user32.dll!750f55df()   
user32.dll!750ec2df()   
uxtheme.dll!74862e1b()  
uxtheme.dll!74862e58()  
user32.dll!750ec275()   
user32.dll!750e8e71()   
user32.dll!750e90d1()   
user32.dll!750ed6c5()   
user32.dll!751177ce()   
user32.dll!750f55df()   
user32.dll!750ec2df()   
uxtheme.dll!74862e1b()  
uxtheme.dll!74862e58()  
user32.dll!750ec275()   
user32.dll!750e8e71()   
user32.dll!750e90d1()   
user32.dll!750ed6c5()   
user32.dll!750f55df()   
user32.dll!750ec2df()   
uxtheme.dll!74862e1b()  
uxtheme.dll!74862e58()  
user32.dll!750ec275()   
user32.dll!750e8e71()   
user32.dll!750e90d1()   
user32.dll!750ed6c5()   
user32.dll!750ec2df()   
uxtheme.dll!74862e1b()  
uxtheme.dll!74862e58()  
user32.dll!750ec275()   
user32.dll!750e8e71()   
user32.dll!750e90d1()   
user32.dll!750ed6c5()   
uxtheme.dll!74862e1b()  
uxtheme.dll!74862e58()  
user32.dll!750ec275()   
user32.dll!750e8e71()   
user32.dll!750e90d1()   
user32.dll!750ed6c5()   
uxtheme.dll!74862e58()  
user32.dll!750ec275()   
user32.dll!750e8e71()   
user32.dll!750e90d1()   
user32.dll!750ed6c5()   
user32.dll!750ec275()   
user32.dll!750e8e71()   
user32.dll!750e90d1()   
user32.dll!750ed6c5()   
user32.dll!750e8e71()   
user32.dll!750e90d1()   
user32.dll!750ed6c5()   
user32.dll!750e90d1()   
user32.dll!750ed6c5()   
user32.dll!750ed6c5()   
[Managed to Native Transition]  
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DefWndProc(ref System.Windows.Forms.Message m) + 0x56 bytes  
System.Windows.Forms.dll!System.Windows.Forms.Form.DefWndProc(ref System.Windows.Forms.Message m) + 0x60 bytes  
System.Windows.Forms.dll!System.Windows.Forms.Control.WmWindowPosChanged(ref System.Windows.Forms.Message m) + 0x16 bytes   
System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m) + 0x29c bytes 
System.Windows.Forms.dll!System.Windows.Forms.ScrollableControl.WndProc(ref System.Windows.Forms.Message m) + 0x30 bytes    
System.Windows.Forms.dll!System.Windows.Forms.Form.WndProc(ref System.Windows.Forms.Message m) + 0x19b bytes    
System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.OnMessage(ref System.Windows.Forms.Message m) + 0x13 bytes    
System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.WndProc(ref System.Windows.Forms.Message m) + 0x35 bytes  
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Callback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam) + 0x80 bytes   
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DefWndProc(ref System.Windows.Forms.Message m) + 0xfb bytes  
CefSharp.WinForms.dll!CefSharp.WinForms.Internals.ParentFormMessageInterceptor.WndProc(ref System.Windows.Forms.Message m) Line 231 + 0xb bytes C#
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Callback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam) + 0x80 bytes   
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DefWndProc(ref System.Windows.Forms.Message m) + 0xfb bytes  
CefSharp.WinForms.dll!CefSharp.WinForms.Internals.ParentFormMessageInterceptor.WndProc(ref System.Windows.Forms.Message m) Line 231 + 0xb bytes C#
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Callback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam) + 0x80 bytes   
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DefWndProc(ref System.Windows.Forms.Message m) + 0xfb bytes  
CefSharp.WinForms.dll!CefSharp.WinForms.Internals.ParentFormMessageInterceptor.WndProc(ref System.Windows.Forms.Message m) Line 231 + 0xb bytes C#
Oct 3, 2016 at 1:45 PM
Is this what you want? (it was truncated)
Coordinator
Oct 3, 2016 at 2:44 PM
Edited Oct 3, 2016 at 2:47 PM
Yes, but unfortunately, as you can see, the native symbols are missing. Did you build ClearScript yourself? If so, you should be able to load the symbols (.PDBs) for the ClearScriptV8-32 and v8-ia32 assemblies into the debugger to get a stack with the relevant symbols. In the meantime we'll see if we can reproduce this with Eclipse.
Oct 3, 2016 at 3:12 PM
I am using Nuget package manager for the Clearscript library.

If the pdb files for version 5.4.6 can be downloded, I would be glad if you could provide a download link.

Othwrwise I will download and compile the source code.
Coordinator
Oct 3, 2016 at 3:34 PM
Hi again,

We don't distribute ClearScript binaries; please consider contacting the NuGet package owners or building ClearScript yourself. If you decide to do the latter, remember to copy all the ClearScript .DLLs and .PDBs to your application's binary folder.

We've now tested with Eclipse, and we couldn't reproduce the issue. The application remained responsive, writing "SizeChanged" to the console with both debuggers attached. Both script and managed/native breakpoints continued to work correctly.

Nevertheless, if you can collect a fully symbolized call stack, we'll be happy to check it out :)

Thanks!
Oct 3, 2016 at 8:27 PM
Edited Oct 3, 2016 at 8:28 PM
Hi,

Did you try putting a breakpoint to the "Console.WriteLine" line?
debugger;
var connect1 = main.SizeChanged.connect(function (sender, e) {
    Console.WriteLine("SizeChanged");
});
If you have only a "debugger;" statement at the first line and continue execution after stopping there, it works fine.

But if you put a breakpoint at that line and resize the form, the application hangs as soon as it hits the breakpoint.
Coordinator
Oct 3, 2016 at 8:48 PM
Yes, we did set a breakpoint on that line, and yes, the application stops when it hits it, but that's expected, right? The breakpoint pauses execution and gives control to the debugger. Or are you saying that the application can't resume execution after it hits the breakpoint?
Oct 3, 2016 at 9:00 PM
I've finally found the cause of the problem.

I host a chromium embedded browser (CefSharp) inside my application to integrate the debugger inside the application.

When a UI event which is bound to a script function is triggered and if there is a breakpoint in that function, the UI of the application freezes until the execution is resumed in the debugger.

Since the debugger interface is IN MY APPLICATON, I do not have the chance to resume the script because my UI is frozen and this gives me a frozen application.

When I run the debugger interface in an external Chrome instance, the Winforms application is frozen again when the breakpoint is hit but since I can click the resume button in the external interface I can unfreeze the app.

Can you imagine of a coding trick to overcome this problem?

As I wrote before I have the following "calling a delegate function in a new thread" option but this makes the code complicated and hard to maintain.
        public delegate void FormEvent(string eventName, object sender, EventArgs e);
        private FormEvent callbacks;

        public void RegisterToFormEvents(FormEvent eventHandler)
        {
            callbacks += eventHandler;
        }

        private void MainForm_Resize(object sender, EventArgs e)
        {
                new Thread(() =>
                {
                    if (callbacks != null)
                        callbacks("Resize", sender, e);
                }).Start();           
        }
Coordinator
Oct 3, 2016 at 9:29 PM
Hi again,

That sounds like a very interesting application!

Have you considered running your debugger in a separate UI thread? Windows Forms applications can have any number of UI threads. Lots of relevant information is available on MSDN and StackOverflow.

Good luck!
Oct 3, 2016 at 10:12 PM
Thanks for your quick response and this great library.
Oct 4, 2016 at 12:07 PM
Edited Oct 4, 2016 at 12:58 PM
I wrote two magical functions to handle my problem. I want to share it so that other users can benefit, too.

I added a host object referring to my Form object to the engine.
            engine.AddHostObject("main", this);
I added two public functions to the Form class:
        public EventHandler AddEventHandler(object obj, string eventName, object callback) {
            var eventInfo = obj.GetType().GetEvent(eventName);
            if (eventInfo != null)
            {
                Assembly asm = typeof(V8ScriptEngine).Assembly;
                Type asmType = asm.GetType("Microsoft.ClearScript.DelegateFactory");
                MethodInfo mi = asmType.GetMethod("CreateDelegate", new[] { typeof(ScriptEngine), typeof(object), typeof(Type) }); 
                object[] parameters = { engine, callback, eventInfo.EventHandlerType };
                Delegate scriptHandler = (Delegate)mi.Invoke(null, parameters);

                EventHandler handler = delegate (object sender, EventArgs e) {
                    Console.WriteLine("Event triggered: " + eventInfo.Name);
                    new Thread(() =>
                    {
                        object[] args = new object[] { sender, e };
                        scriptHandler.DynamicInvoke(args);
                    }).Start();
                };

                eventInfo.AddEventHandler(obj, handler);
                return handler;
            }
            return null;
        }

        public bool RemoveEventHandler(object obj, string eventName, EventHandler handler)
        {
            var eventInfo = obj.GetType().GetEvent(eventName);
            if (eventInfo != null)
            {
                eventInfo.RemoveEventHandler(obj, handler);
                return true;
            }
            return false;
        }
What solves my problem is calling the callback script in a new Thread.

This avoids the application from hanging when it hits a breakpoint in the callback. (Remember that my debugger is hosted in a web browser inside my application)

And this is how I register to the "Resize" event of the form in the script:
var handler = main.AddEventHandler(main, "Resize", function (sender, e) {
    Console.WriteLine("Resize triggered inside script");
    main.RemoveEventHandler(main, "Resize", handler); //unregister
});
The only restriction in this solution is that I can only register events whose method parameters match with the EventHandler delegate.

I would be happy to hear your feedbacks!
Coordinator
Oct 4, 2016 at 1:26 PM
Edited Oct 4, 2016 at 1:36 PM
Hi ahmetuzun,

Thanks for posting your solution! Here's an alternative that uses only public APIs and supports all events that follow the standard pattern:
public static class EventSourceExtensions {
    public static EventConnection<T> connectAsync<T>(this EventSource<T> source, object cb) {
        return source.connect(new EventHandler((sender, e) => {
            ThreadPool.QueueUserWorkItem(_ => ((dynamic)cb)(sender, e));
        }));
    }
}
And then:
engine.AddHostType(typeof(Console));
engine.AddHostType(typeof(EventSourceExtensions));
engine.Execute(@"
    var connect1 = main.SizeChanged.connectAsync(function (sender, e) {
        Console.WriteLine('SizeChanged');
    });
    var connect2 = main.MouseClick.connectAsync(function (sender, e) {
        Console.WriteLine('MouseClick({0}, {1})', e.X, e.Y);
    });
");
Cheers!
Oct 4, 2016 at 2:29 PM
That's a perfect solution.

Thank you very much for your help.
Oct 5, 2016 at 7:06 AM
Edited Oct 5, 2016 at 7:11 AM
For the sake of completeness, I want to share the final version of my helper functions which can be used to connect and disconnect to any kind of event on an object.
       private static Delegate CreateDelegate(Type delegateType, Action<object[]> target)
        {
            var sourceParameters = delegateType.GetMethod("Invoke").GetParameters();
            var parameters = sourceParameters.Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray();
            var castParameters = parameters.Select(p => Expression.TypeAs(p, typeof(object))).ToArray();
            var createArray = Expression.NewArrayInit(typeof(object), castParameters);
            var invokeTarget = Expression.Invoke(Expression.Constant(target), createArray);
            var lambdaExpression = Expression.Lambda(delegateType, invokeTarget, parameters);
            return lambdaExpression.Compile();
        }

        private static Type delegateFactory = typeof(V8ScriptEngine).Assembly.GetType("Microsoft.ClearScript.DelegateFactory");
        private static MethodInfo createDelegateX = delegateFactory.GetMethod("CreateDelegate", new[] { typeof(ScriptEngine), typeof(object), typeof(Type) });

        public Delegate AddEventHandler(object obj, string eventName, object callback) {
            var eventInfo = obj.GetType().GetEvent(eventName);
            if (eventInfo != null)
            {
                // Create a delegate from the JavaScript callback function
                Delegate scriptHandler = (Delegate)createDelegateX.Invoke(null, new object[] { engine, callback, eventInfo.EventHandlerType });

                var handler = CreateDelegate(eventInfo.EventHandlerType, (object[] args) =>
                {
                    Debug.WriteLine("Event triggered: " + eventInfo.Name);
                    ThreadPool.QueueUserWorkItem(_ => scriptHandler.DynamicInvoke(args));
                });

                try
                {
                    eventInfo.AddEventHandler(obj, handler);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("AddEventHandler could not add event handler:" + ex.Message);
                    return null;
                }

                return handler;
            }
            return null;
        }

        public bool RemoveEventHandler(object obj, string eventName, Delegate handler)
        {
            var eventInfo = obj.GetType().GetEvent(eventName);
            if (eventInfo != null)
            {
                eventInfo.RemoveEventHandler(obj, handler);
                return true;
            }
            return false;
        }