// Procedure executed on background thread to perform a task. private static string PerformWork(object stateObj) { // Get the state object. JavascriptCallbackState state = (JavascriptCallbackState)stateObj; if (state.Token.IsCancellationRequested) { return(null); } int id = (int)state.Data; string imageResource = String.Format("WPFJavascriptSample.images.alphanumeric-number-{0}.png", id); string result = null; // Get the embedded resource of the Awesomium logo. var assembly = Assembly.GetExecutingAssembly(); var info = assembly.GetManifestResourceInfo(imageResource); // The resource does not exist. if (info == null) { return(null); } if (state.Token.IsCancellationRequested) { return(null); } using (var stream = assembly.GetManifestResourceStream(imageResource)) { // Get a byte array of the resource. byte[] buffer = new byte[stream.Length]; stream.Read(buffer, 0, buffer.Length); if (state.Token.IsCancellationRequested) { return(null); } result = Convert.ToBase64String(buffer); } // Throws an OperationCanceledException if this token has had // cancellation requested. When a task instance observes an // OperationCanceledException thrown by user code, it compares // the exception's token to its associated token. If they are the same // and the token's IsCancellationRequested property returns true, // the task interprets this as acknowledging cancellation and transitions // to the Canceled state. state.Token.ThrowIfCancellationRequested(); return(result); }
// Called on the UI thread after the working procedure is complete, or canceled. private void SendResponse(Task <String> t) { using (JavascriptCallbackState state = (JavascriptCallbackState)t.AsyncState) { if (!webControl.IsLive) { return; } // Check if the user has navigated away, // or the operation timed out. if (t.IsCanceled) { // If we timed out, send an indication. if (state.TimedOut) { InvokeCallback(state.Callback, "ERR_TIMEOUT"); } // Disposing the JavascriptCallbackState // will dispose the callback as well. return; } if ((t.Exception != null) || String.IsNullOrEmpty(t.Result)) { // The operation failed. // Disposing the JavascriptCallbackState // will dispose the callback as well. return; } // Invoke the callback. JSObject supports the DLR so for // this example we assign to a dynamic which makes invoking the // callback, pretty straightforward (just as you would in JS). InvokeCallback(state.Callback, t.Result); } }
// Handler of the external.app.performHeavyWork method. private void OnWork(object sender, JavascriptMethodEventArgs e) { // Must have 2 arguments passed. if (e.Arguments.Length != 3) { return; } // Must be a function object. if (!e.Arguments[0].IsFunctionObject) { return; } // Must be a timeout value. if (!e.Arguments[1].IsNumber) { return; } // Must be the image id. if (!e.Arguments[2].IsNumber) { return; } // Get the NavigationInterceptor. This will allow us to cancel the operation // if the user navigates away. INavigationInterceptor navigationInterceptor = webControl.GetService(typeof(INavigationInterceptor)) as INavigationInterceptor; // Without this, do not proceed. if (navigationInterceptor == null) { return; } // You can cache the callback and call it only when your application // has performed all work necessary and has a result ready to send. // Note that this callback object is valid for as long as the current // page is loaded; a navigation will invalidate it. This is why we // need the NavigationInterceptor service acquired above. JSFunction callbackArg = e.Arguments[0]; // Get the image id. int id = (int)e.Arguments[1]; // Get the timeout specified. int timeout = (int)e.Arguments[2]; // Make sure it's a function object. if (!callbackArg) { return; } // See it! Debug.Print(callbackArg.ToString()); // You need to copy the object if you intend to cache it. The original // object argument passed to the handler is destroyed by default when // the handler returns. A copy will keep it alive. Note that the clone // of a JSFunction, will definitely be a JSFunction as well. JSFunction callback = (JSFunction)callbackArg.Clone(); // Create a state object. This holds the callback and controls // cancellation. It is passed to both the background operation procedure // and the response procedure. JavascriptCallbackState state = new JavascriptCallbackState(navigationInterceptor, callback, id, timeout); // Perform your heavy work. Task.Factory.StartNew( /* The function that will perform the work. */ (Func <Object, String>)PerformWork, /* Pass the state object. */ state, /* The CancellationToken that will let us know if we * should cancel passing back the result. */ state.Token).ContinueWith( /* Send the result when complete. */ SendResponse, /* Make sure the result is sent on the * initial thread. */ TaskScheduler.FromCurrentSynchronizationContext()); }