Выполнение JavaScript на С# с помощью CefSharp WPF вызывает ошибку

Всякий раз, когда я пытаюсь выполнить JavaScript через С# с помощью CefSharp (Stable 57.0), я получаю сообщение об ошибке. Я просто пытаюсь выполнить функцию предупреждения, поэтому я могу убедиться, что это работает, а затем проверить его с помощью моей собственной функции. Тем не менее, я, кажется, получаю ошибки, пытаясь это сделать.

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');");
    }
}

Я пробовал оба способа выполнения script.

Первый:

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

Дает мне эту ошибку:

введите описание изображения здесь

Второе:

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

Дает мне эту ошибку:

введите описание изображения здесь

Моя цель - создать функцию С#, которая может выполнять функцию JavaScript в моем браузере CefSharp.

Я пробовал много ссылок/ссылок и не было много. Я также читал FAQ для CefSharp и не мог найти простых примеров, которые позволяют мне выполнять JavaScript по своему усмотрению через С#.

Кроме того, я проверил события, в которых загружен кадр (он заканчивает загрузку), и выгружается (он не выгружается), и если webbrowser имеет значение null (а это не так) и сообщение из:

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

все еще вызывает первую ошибку.

Я тестировал GetMainFrame(). Он всегда возвращает null. ВСЕГДА. Неважно, сколько времени я жду или какие условия я проверяю.

ВАЖНО

Я забыл добавить одну важную часть информации, у меня есть 2 сборки в моем проекте. Оба они скомпилируются в отдельные исполняемые файлы:

Helper.exe Main.exe

main.exe имеет окно "CallUI", которое при нажатии кнопки запускает метод, который я создал "ExecuteJavaScript()", который находится внутри моего окна "BrowserWindow". Окно CallUI объявляется и инициализируется в Helper.exe.

Поэтому в основном я пытаюсь использовать отдельную программу для открытия окна, нажмите кнопку, вызывающую метод и выполняющий javascript. Поэтому я думаю, потому что они разные процессы, это говорит мне, что браузер имеет значение null. Однако, когда я делаю все это в Main.exe, он работает нормально. Есть ли способ обхода, который позволяет мне использовать отдельный процесс для создания окна из Helper.exe и выполнить Javascript из Main.exe?

Ответ 1

Мне пришло в голову, что я неправильно справляюсь с проблемой.

Моя проблема, по сути, не существует, если это всего лишь один процесс, содержащий весь код вместе. Однако проблема заключается в том, что мой проект имеет исполняемый файл, который пытался установить связь с другим. У меня на самом деле никогда не было способа для моего helper.exe правильно поговорить с моим main.exe.

Что я узнал из этого, так это то, что процессы пытались разговаривать друг с другом без какого-либо доступа к общим адресам. Они живут в отдельных адресных пространствах, поэтому всякий раз, когда мой helper.exe пытался выполнить эту часть javascript, принадлежащую Main.exe, он пытался выполнить script в неинициализированной версии браузера, который принадлежал его собственному адресному пространству и не main.exe.

Итак, как я решил эту проблему? Я должен был включить важную часть, которая позволила процессу helper.exe поговорить с процессом main.exe. Поскольку я googled, как процессы могут говорить друг с другом, я узнал о 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");
        }
    }
}

Это скомпилировано в dll, которое могут использовать как Main.exe, так и Helper.exe.

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);
            }
        }
    }
}

Наконец, мне пришлось инициализировать Message в моем классе WebBrowserWindow следующим образом:

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');");
    }
}

И теперь это позволяет мне выполнить javascript, который мне нужен, отправив сообщение из Helper.exe в файл Main.exe.

Ответ 2

Вы пробовали эту ссылку? Содержит фрагмент, который проверяет, был ли сначала запущен браузер.

cefsharp выполнить javascript

private void OnIsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs args)
{
    if(args.IsBrowserInitialized)
    {
        browser.ExecuteScriptAsync("alert('test');");
    }
}