|
Rank: Advanced Member Groups: Member
Joined: 4/17/2014 Posts: 35
|
Hi Could you explain a little bit the threading model behind invoking js function from C# code? Like how should i structure the link and what i shouldn't do. I'm using this helper method to communicate with page
Code: C#
public static void InvokeJs<T>(string id, string js, System.Action<T> callback)
where T : class, IConvertible
{
WebControl browser = webControls[id] as WebControl;
var call = browser.WebView.QueueScriptCall(js);
var wait = call.WaitOne();
var sresult = call.Result.ToString();
if (callback != null)
{
callback((T)Convert.ChangeType(sresult, typeof(T)));
}
}
The problem what i'm faceing is that, the call.WaitOne() will not return some times or there is a huge time cap comparing with normal execution time. The problem is probably that, this code section is accessed by different threads.
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,196
|
Hi,
You should not do that. EO.WebBrowser is designed as a single thread model because of the window handle and window messages involved. Specifically, the thread that calls WebView.Create (in a Windows Forms application you do not call WebView.Create explicitly, but it is called in UI thread during the control's creation) creates the underlying window handle, thus "owns" the WebView. Everything else must go through this thread (with a few exception, for example, Destroy can be called from a different thread). As such it will be better for you to route the asynchronously call yourself through the thread that owns the WebView. You can take a look of class System.Threading.SynchronizationContext. This class functions in both Windows Forms and WPF application. It might help you to re-route the call through the UI thread.
Thanks!
|
|
Rank: Advanced Member Groups: Member
Joined: 4/17/2014 Posts: 35
|
Thank you for the instructions. So when i use the browser's dispatcher, everything should be fine?
Code: C#
public static void InvokeJs<T>(string id, string js, System.Action<T> callback)
where T : class, IConvertible
{
WebControl browser = webControls[id] as WebControl;
browser.Dispatcher.Invoke(() =>
{
var call = browser.WebView.QueueScriptCall(js);
var wait = call.WaitOne();
var sresult = call.Result.ToString();
if (callback != null)
{
callback((T)Convert.ChangeType(sresult, typeof(T)));
}
});
}
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,196
|
Yes. Except that you can use EvalScript directly instead of QueueScriptCall. QueueScriptCall is a little bit strange as it won't actually run the script without exiting the current message (which is how Dispatcher.Invoke is implemented). So you can actually create a deadlock there. We will probably revisit this method later to refine the internal mechanism of this method. For now it is recommended that you route the call to the UI thread and then use EvalScript.
Thanks!
|
|
Rank: Advanced Member Groups: Member
Joined: 4/17/2014 Posts: 35
|
eo_support wrote:Yes. Except that you can use EvalScript directly instead of QueueScriptCall. QueueScriptCall is a little bit strange as it won't actually run the script without exiting the current message (which is how Dispatcher.Invoke is implemented). So you can actually create a deadlock there. We will probably revisit this method later to refine the internal mechanism of this method. For now it is recommended that you route the call to the UI thread and then use EvalScript.
Thanks! Im using QueueScriptCall to be sure, that the js command executes asap. EvalScript might be called on wrong time and it will end with crash. Any ideas how to handle that without creating waiting / blocking loop using CanEvalScript?
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,196
|
Hi,
If you use QueueScriptCall, make sure you handle ScriptCallDone event. In the current implementation there can be problem if you immediately call WaitOne after QueueScriptCall. We have changed our code so that this will no longer be a problem in our next build.
Thanks!
|
|