0

Hi I want to invoke a method in my angular app from C# code. My angular app resides inside WPF WebBrowser control. Below are the code snippets from C# & Angular.

C# Code snippet:

[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    [ComVisible(true)]
    public partial class WebBrowserView : UserControl
    {
        public WebBrowserView()
        {
            InitializeComponent();
            myBrowser.Unloaded += myBrowser_Unloaded;
        }
        private void myBrowser_Unloaded(object sender, RoutedEventArgs e)
        {
            // This works:
            myBrowser.InvokeScript("execScript", new object[] { "this.alert(123)", "JavaScript" });
            
            // This is what I actually want, but it doesn't work:
           // For both of these I get the Script Error:
           // System.Runtime.InteropServices.COMException: 'Exception from HRESULT: 0x80020101'
                myBrowser.InvokeScript("eval", "alertExample()");   
                string javascript = "CoreHelper.alertExample();";
                myBrowser.InvokeScript("eval", new object[] { javascript });
            }
    }

enter image description here

Angular 10 snippet:

  1. Global function in Global.ts file:

    export function alertExample(){ alert('test'); }

  2. Inside an abstract class CoreHelper.ts

    export class CoreHelper { public static alertExample(){ alert('happy'); } }

Definitely my alertExample is intended to do a lot more than alerting. I do not want to put any scripts inside index.html.

What is that I am missing/doing wrong here?

I also tried adding the script directly in index.html:

Angular 10 index.html:

<script type="text/javascript">
    function test(params) {
alert('index.html');
    }  
  </script>

C#

    // This works:
    myBrowser.InvokeScript("test");
   // This doesn't work:
     myBrowser.InvokeScript("eval", new object[] { "test" });

Also tried this:

Angular 10 index.html:

<script type="text/javascript" src="./assets/scripts/global-scripts.js">   
      </script> 

global-scripts.js

function test() {
    alert('You have arrived: ');
}

C#

    // None of these work:
    myBrowser.InvokeScript("test");
     myBrowser.InvokeScript("eval", new object[] { "test" });

Thanks,

RDV

RDV
  • 957
  • 13
  • 28
  • Sounds pretty unsafe, if an external application (like your C# app) could insert scripts to executed your (frontend) code (even if it's within your own browser). What do you want to achieve? I believe you've to approach it differently. – W.S. May 23 '22 at 21:09
  • @W.S., I understand that this is not safe, but I need this functionality for a different use case which is listed here: https://stackoverflow.com/questions/72353599/webbrowser-not-closing-websocket-connection – RDV May 23 '22 at 21:58
  • I'm totally unfamiliar with this C# code, but what if you use the Angular build in lifecycle hooks, like `ngOnInit` and `ngOnDestroy`? – W.S. May 23 '22 at 22:45
  • I need to call an API to close the WebSocket connection when the component is destroyed/ browser is closed (WebBrowser tab closed), but ngOnDestroy is not called in these cases. – RDV May 23 '22 at 23:11
  • And it's your C# application who is exposing the websocket? How did you figure out the websocket isn't closed? It's rather strange, you would have to call an API from the client to close the connection. I would expect, you do a cleanup from the server side. What about this resource? https://stackoverflow.com/questions/48405656/c-sharp-detect-when-clientwebsocket-is-disconnected-closed Again, I'm totally unfamillar with this WPF WebBrowser control. Just thinking.. – W.S. May 23 '22 at 23:35
  • I have access to WebSocketServer code (another C# application) which implements the OnClose event. On disposing WebBrowser, OnClose is not called, but on closing IE11 session it is called. I figured out that client can close the connection, only catch is that client (Angular) should close the connection on WebBrowser close. Hence trying to invoke close API in angular from C# code – RDV May 23 '22 at 23:53

1 Answers1

0

Posting my solution for this issue:

C# end: WebBrowserView (XAML View) code:

We can use WebBrowser Navigating event, I needed more control, hence using explicit API to shutdown the connection.

public Tuple<bool, string> OnShutdownEvent(string scriptName)
    {
        Tuple<bool, string> result = new Tuple<bool, string>(true, "");

        if (scriptName.IsNullOrEmpty())
        {
            return result;
        }

        try
        {
            DependencyObject sender = VisualTreeHelper.GetChild(myBrowser, 0);
            if (sender != null)
            {
                if ((sender is WebBrowser) && ((sender as WebBrowser).Document != null))
                {
                    //If the script is not present, we fall in catch condition
                    //No way to know if the script worked fine or not.
                    (sender as WebBrowser).InvokeScript(scriptName);
                    result = new Tuple<bool, string>(true, string.Format("Successfully executed script- {0}", scriptName));
                }
                else
                {
                    result = new Tuple<bool, string>(true, "No script invoked.");
                }
            }
        }
        catch (Exception e)
        {
            result = new Tuple<bool, string>(false, e.Message);
        }

        return result;
    }

WebBrowserViewModel code snippet:

public bool OnHandleShutdown()
    {
        try
        {
            Tuple<bool, string> result = (this.View as XbapPageView).OnShutdownEvent("closeConnection");               
        }
        catch (Exception) { }
        finally
        {
            XBAP_URI = "about:blank";
        }

        return true;
    }

Now in the angular app, following changes are required:

index.html

<script type="text/javascript">
function closeConnection(params) {
  window.webSocketServiceRef.zone.run(
    function () {
      window.webSocketServiceRef.closeConnection(params);
    });
}  

webSocketService.ts:

    constructor(
    private _webSocketService: TestWebsocketService,
    private _ngZone: NgZone) {
    window['webSocketServiceRef'] = {
      component: this,
      zone: this._ngZone,
      closeConnection: (params) => this.onCloseConnectionEvent(params)
    };
  }

  onCloseConnectionEvent(params) {
    this._msgSubscription.unsubscribe();
    this._webSocketMsgSubject.complete();
    return true;
  }

Now WebSocket connection is properly closed.

Thanks,

RDV

RDV
  • 957
  • 13
  • 28