使用 CefSharp WPF 在 C# 上执行 JavaScript 会导致错误

Posted

技术标签:

【中文标题】使用 CefSharp WPF 在 C# 上执行 JavaScript 会导致错误【英文标题】:Executing JavaScript on C# with CefSharp WPF causes Error 【发布时间】:2017-11-04 02:27:23 【问题描述】:

每当我尝试使用 CefSharp (Stable 57.0) 通过 C# 执行 javascript 时,都会出现错误。我只是尝试执行警报功能,所以我可以确保它有效,然后用我自己的功能对其进行测试。但是,我似乎在尝试这样做时遇到了错误。

public partial class WebBrowserWindow : Window

    public WebBrowserWindow()
    
        InitializeComponent();
        webBrowser.MenuHandler = new ContextMenuHandler();
        webBrowser.RequestHandler = new RequestHandler();
    

    //Trying to execute this with either method gives me an error.
    public void ExecuteJavaScript()
    
        //webBrowser.GetMainFrame().ExecuteJavaScriptAsync("alert('test')");

        //webBrowser.ExecuteScriptAsync("alert('test');");
    

我尝试了两种执行脚本的方式。

第一个:

webBrowser.GetMainFrame().ExecuteJavaScriptAsync("alert('test')");

给我这个错误:

第二个:

webBrowser.ExecuteScriptAsync("alert('test');");

给我这个错误:

我的目标是创建一个可以在我的 CefSharp 浏览器中执行 JavaScript 函数的 C# 函数。

我尝试了很多链接/引用,但堆栈溢出时并没有那么多。我还阅读了The FAQ for CefSharp 并找不到任何简单的示例可以让我通过 C# 随意执行 JavaScript。

此外,我已经验证了 Frame 加载(完成加载)和卸载(未卸载)的事件,以及 webbrowser 是否为 null(它不是),以及来自以下位置的消息:

webBrowser.GetMainFrame().ExecuteJavaScriptAsync("alert('test')");

仍然会导致第一个错误发生。

我测试了 GetMainFrame()。它总是返回 null。总是。不管我等待多长时间,或者我检查什么条件。

重要

我忘记添加一条重要信息,我的项目中有 2 个程序集。它们都编译成单独的可执行文件:

Helper.exe 主程序

main.exe 有一个窗口“CallUI”,当单击按钮时,它会执行我创建的方法“ExecuteJavaScript()”,该方法位于我的窗口“BrowserWindow”内。 CallUI 窗口在 Helper.exe 中声明和初始化。

所以基本上我正在尝试使用单独的程序打开一个窗口,单击一个调用该方法的按钮并执行 javascript。所以我认为因为它们是不同的进程,它告诉我浏览器是空的。但是,当我在 Main.exe 中完成所有操作时,它工作正常。是否有一种解决方法允许我使用单独的进程从 Helper.exe 创建窗口并从 Main.exe 执行 Javascript?

【问题讨论】:

您能否创建有关此 Main.exe 和 Helper.exe 交互的最小可重现示例(源代码)?没有它,就很难为您提供解决方案并确保它有效。 嘿,我的问题得到了解决,结果我必须实现一种方法让进程相互通信(请参阅下面的长答案)。我还有一个关于我的解决方案的额外问题(在我的答案的评论部分)。 【参考方案1】:

我注意到我处理问题的方式有误。

事实上,如果只是将所有代码放在一起的单个进程,我的问题就不存在了。但是,我的项目有一个试图与另一个项目通信的可执行文件这一事实是问题所在。实际上,我从来没有办法让我的 helper.exe 正确地与我的 main.exe 对话。

我从中学到的是,进程试图在没有任何共享地址访问权限的情况下相互交谈。它们存在于不同的地址空间中,因此每当我的 helper.exe 尝试执行属于 Main.exe 的 javascript 部分时,它都会尝试在属于其自己的地址空间而不是 main 的未初始化版本的浏览器中执行脚本.exe。

那么我是如何解决这个问题的呢?我必须包含一个重要的部分,它允许 helper.exe 进程与 main.exe 进程对话。当我在谷歌上搜索进程如何相互通信时,我发现了 MemoryMappedFiles。所以我决定在我的程序中实现一个简单的例子,允许 Helper.exe 向 Main.exe 发送消息。

这是一个例子。这是我创建的名为“MemoryMappedHandler.cs”的文件

public class MemoryMappedHandler

    MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen("mmf1", 512);
    MemoryMappedViewStream stream;
    MemoryMappedViewAccessor accessor;
    BinaryReader reader;
    public static Message message = new Message();

    public MemoryMappedHandler()
    
        stream = mmf.CreateViewStream();
        accessor = mmf.CreateViewAccessor();
        reader = new BinaryReader(stream);

        new Thread(() =>
        
            while (stream.CanRead)
            
                Thread.Sleep(500);
                message.MyStringWithEvent = reader.ReadString();
                accessor.Write(0, 0);
                stream.Position = 0;
            
        ).Start();

    

    public static void PassMessage(string message)
    
        try
        
            using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("mmf1"))
            
                using (MemoryMappedViewStream stream = mmf.CreateViewStream(0, 512))
                
                    BinaryWriter writer = new BinaryWriter(stream);
                    writer.Write(message);
                
            
        
        catch (FileNotFoundException)
        
            MessageBox.Show("Cannot Send a Message. Please open Main.exe");
        
    

这被编译成 Main.exe 和 Helper.exe 都可以使用的 dll。

Helper.exe 使用方法 PassMessage() 将消息发送到名为“mmf1”的内存映射文件。 Main.exe 必须始终打开,它负责创建可以从 Helper.exe 接收消息的文件。我将该消息发送到保存该消息的类,并且每次收到该消息时,它都会激活一个事件。

Message 类如下所示:

[Serializable]
public class Message

    public event EventHandler HasMessage;

    public string _myStringWithEvent;

    public string MyStringWithEvent
    
        get  return _myStringWithEvent; 
        set
        
            _myStringWithEvent = value;
            if (value != null && value != String.Empty)
            
                if (HasMessage != null)
                    HasMessage(this, EventArgs.Empty);
            
        
    

最后,我必须像这样在我的 WebBrowserWindow 类中初始化 Message:

public partial class WebBrowserWindow : Window

    public WebBrowserWindow()
    
        InitializeComponent();
        webBrowser.MenuHandler = new ContextMenuHandler();
        webBrowser.RequestHandler = new RequestHandler();
        MemoryMappedHandler.message.HasMessage += Message_HasMessage;
    

    private void Message_HasMessage(object sender, EventArgs e)
    
        ExecuteJavaScript(MemoryMappedHandler.message.MyStringWithEvent);
    

    public void ExecuteJavaScript(string message)
    
        //webBrowser.GetMainFrame().ExecuteJavaScriptAsync("alert('test')");

        //webBrowser.ExecuteScriptAsync("alert('test');");
    

现在它允许我通过从 Helper.exe 向 Main.exe 发送消息来执行我需要的 javascript。

【讨论】:

还有一件事:我想让我的 MemoryMappedFile 更好。现在它有一个错误,如果你从 Helper.exe 发送相同的消息,它只会在第一次执行它(由于我如何设置消息属性)。如何清除接收流(MemoryMappedViewStream)(在 MemoryMappedHandler 构造函数内部)?我尝试了很多东西,比如“Flush()”,给 mmf1 写一条空消息(它只删除消息的第一个字符)等等。 经过一番思考,我解决了我在上面的评论中提到的问题。【参考方案2】:

你试过这个链接吗?包含一个检查浏览器是否首先初始化的 sn-p。

cefsharp execute javascript

private void OnIsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs args)

    if(args.IsBrowserInitialized)
    
        browser.ExecuteScriptAsync("alert('test');");
    

【讨论】:

是的,我已经尝试过了。 CefSharp 57 版在 IsBrowserInitializedChanged 事件上不使用 IsBrowserInitializedChangedEventArgs,它使用依赖属性。不仅如此,在确认浏览器初始化/不为空后,我仍然无法独立执行我的方法,给出上面列出的相同错误消息。

以上是关于使用 CefSharp WPF 在 C# 上执行 JavaScript 会导致错误的主要内容,如果未能解决你的问题,请参考以下文章

wpf中js调用C#后台方法,使用框架CefSharp

WPF使用cefsharp

CefSharp For WPF响应页面点击事件

在WPF中使用CefSharp嵌入浏览器

cefsharp wpf wpf加载svg 在同一个页面中打开链接

WPF中使用cefsharp