Executing JavaScript on C# with CefSharp WPF causes Error - javascript

Whenever I try to execute JavaScript through C# using CefSharp (Stable 57.0), I get an error. I am simply trying to execute the alert function, so I can make sure that works and later test it out with my own function. However, I seem to be getting errors trying to do so.
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');");
}
}
I have tried both ways of executing the script.
The first one:
webBrowser.GetMainFrame().ExecuteJavaScriptAsync("alert('test')");
Gives me this error:
The second:
webBrowser.ExecuteScriptAsync("alert('test');");
Gives me this error:
My objective is to create a C# function that can execute a JavaScript function in my CefSharp Browser.
I tried many links/references and there weren't that many on stack overflow. I also read The FAQ for CefSharp and couldn't find any simple examples that allow me to execute JavaScript at will through C#.
In addition, I've verified the events where the Frame is loaded (it finishes loading), and unloaded (it does not unload), and if the webbrowser is null (which it's not), and the message from the:
webBrowser.GetMainFrame().ExecuteJavaScriptAsync("alert('test')");
still causes the first error to occur.
I tested for GetMainFrame(). It always returns null. ALWAYS. Doesn't matter how long I wait, or what conditions I check for.
IMPORTANT
I forgot to add one crucial piece of information, I have 2 assemblies in my project. Both of them compile into separate executables:
Helper.exe
Main.exe
main.exe has a window "CallUI" that, when a button gets clicked, it executes the method I created "ExecuteJavaScript()", which is inside of my window "BrowserWindow". The CallUI window is declared and initialized in Helper.exe.
So basically I am trying to use a separate program to open a window, click a button that calls the method and execute javascript. So I think because they are different processes, it tells me the browser is null. However, when I do it all in Main.exe it works fine. Is there a workaround that allows me to use the separate process to create the window from Helper.exe and execute the Javascript from Main.exe?

It has come to my attention that I was handling the problem the wrong way.
My problem, in fact, doesn't exist if it's just a single process holding all the code together. However, the fact that my project has an executable that was trying to communicate with another was the problem. I actually never had a way for my helper.exe to talk to my main.exe appropriately.
What I learned from this is that the processes were trying to talk to each other without any sort of shared address access. They live in separate address spaces, so whenever my helper.exe tried to execute that javascript portion that belonged in Main.exe, it was trying to execute the script in an uninitialized version of a browser that belonged in its own address space and not main.exe.
So how did I solve that problem? I had to include an important piece that allowed the helper.exe process to talk to the main.exe process. As I googled how processes can talk to each other, I found out about MemoryMappedFiles. So I decided to implement a simple example into my program that allows Helper.exe to send messages to Main.exe.
Here is the example. This is a file I created called "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");
}
}
}
This is compiled into a dll that both Main.exe and Helper.exe can use.
Helper.exe uses the method PassMessage() to send the message to a Memory Mapped File called "mmf1". Main.exe, which must be open at all times, takes care of creating that file that can receive the messages from Helper.exe. I sends that Message to a class that holds that message and every time it receives it, it activates an event.
Here is what the Message class looks like:
[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);
}
}
}
}
Finally, I had to initialize Message in my WebBrowserWindow class like this:
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');");
}
}
And now it allows me to execute the javascript I need by sending a message from the Helper.exe to the Main.exe.

Have you tried this link? Contains a snippet that checks if the browser is initialised first.
cefsharp execute javascript
private void OnIsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs args)
{
if(args.IsBrowserInitialized)
{
browser.ExecuteScriptAsync("alert('test');");
}
}

Related

How can I call an alert box or console log inside of an MVC site?

I am trying to troubleshoot someone else's MVC project and I can't repro the issue in Visual Studio. Is it possible to call a Javascript alert or console.log from different functions within the c# mvc code?
I've tried this code below by calling it this way
JavascriptOne.ConsoleLog("psobjs count - " + psobjs.Count);
and having this function inside of one of the parent classes, however it does not work and nothing outputs to the console. Does something need to be added to the Javascript code? Or is there a better way to accomplish this?
public static class JavascriptOne
{
static string scriptTag = "<script type=\"\" language=\"\">{0}</script>";
public static void ConsoleLog(string message)
{
string function = "console.log('{0}');";
string log = string.Format(GenerateCodeFromFunction(function), message);
HttpContext.Current.Response.Write(log);
}
public static void Alert(string message)
{
string function = "alert('{0}');";
string log = string.Format(GenerateCodeFromFunction(function), message);
HttpContext.Current.Response.Write(log);
}
static string GenerateCodeFromFunction(string function)
{
return string.Format(scriptTag, function);
}
}

How to execute code when webpage navigate from one page to another in C#

private void button1_Click(object sender, EventArgs e)
{
HtmlDocument doc = webBrowser1.Document;
HtmlElement from = doc.GetElementById("fromStation");
HtmlElement to = doc.GetElementById("toStation");
HtmlElement d = doc.GetElementById("journeyDateInputDate");
HtmlElement s = doc.GetElementById("ticketType");
HtmlElement ticket = doc.GetElementById("ticketType");
HtmlElement submit = doc.GetElementById("jpsubmit");
HtmlElement hcab = doc.GetElementById("handicapPassengers");
from.SetAttribute("value", textBox3.Text);
to.SetAttribute("value", textBox4.Text);
d.SetAttribute("value", textBox5.Text);
ticket.SetAttribute("value", Properties.Settings.Default["ticket"].ToString());
string com = "true";
if (Properties.Settings.Default["check"].ToString() == com)
hcab.InvokeMember("click");
submit.InvokeMember("click");
}
I am making project on c# where i have to execute code when webpage navigate from one page to another and when web load completely.
I have used button to execute a code when webpage completely loads....but now i what it to execute without using button
To provide a substantial and correct answer, you might want to provide more specifics on the environment. But assuming a whole bunch of details, I'm making an (un)educated guess here.
If it's MVC project, you can execute the code as you're presenting the next view. If the page is navigated to from JS (which is on the client) or simply navigated away from your site, it might be much more tricky.
In any case, since it's an operation on the client, you'll need to manage that from JS on the client. The server has let the contents go and the page is viewed in the browser even of the server goes down.
$(function(){
alert("Page loaded.");
// do other stuff
});

C# WebBrowser control - document does not contain html input control [duplicate]

Most of the answers I have read concerning this subject point to either the System.Windows.Forms.WebBrowser class or the COM interface mshtml.HTMLDocument from the Microsoft HTML Object Library assembly.
The WebBrowser class did not lead me anywhere. The following code fails to retrieve the HTML code as rendered by my web browser:
[STAThread]
public static void Main()
{
WebBrowser wb = new WebBrowser();
wb.Navigate("https://www.google.com/#q=where+am+i");
wb.DocumentCompleted += delegate(object sender, WebBrowserDocumentCompletedEventArgs e)
{
mshtml.IHTMLDocument2 doc = (mshtml.IHTMLDocument2)wb.Document.DomDocument;
foreach (IHTMLElement element in doc.all)
{
System.Diagnostics.Debug.WriteLine(element.outerHTML);
}
};
Form f = new Form();
f.Controls.Add(wb);
Application.Run(f);
}
The above is just an example. I'm not really interested in finding a workaround for figuring out the name of the town where I am located. I simply need to understand how to retrieve that kind of dynamically generated data programmatically.
(Call new System.Net.WebClient.DownloadString("https://www.google.com/#q=where+am+i"), save the resulting text somewhere, search for the name of the town where you are currently located, and let me know if you were able to find it.)
But yet when I access "https://www.google.com/#q=where+am+i" from my Web Browser (ie or firefox) I see the name of my town written on the web page. In Firefox, if I right click on the name of the town and select "Inspect Element (Q)" I clearly see the name of the town written in the HTML code which happens to look quite different from the raw HTML that is returned by WebClient.
After I got tired of playing System.Net.WebBrowser, I decided to give mshtml.HTMLDocument a shot, just to end up with the same useless raw HTML:
public static void Main()
{
mshtml.IHTMLDocument2 doc = (mshtml.IHTMLDocument2)new mshtml.HTMLDocument();
doc.write(new System.Net.WebClient().DownloadString("https://www.google.com/#q=where+am+i"));
foreach (IHTMLElement e in doc.all)
{
System.Diagnostics.Debug.WriteLine(e.outerHTML);
}
}
I suppose there must be an elegant way to obtain this kind of information. Right now all I can think of is add a WebBrowser control to a form, have it navigate to the URL in question, send the keys "CLRL, A", and copy whatever happens to be displayed on the page to the clipboard and attempt to parse it. That's horrible solution, though.
I'd like to contribute some code to Alexei's answer. A few points:
Strictly speaking, it may not always be possible to determine when the page has finished rendering with 100% probability. Some pages
are quite complex and use continuous AJAX updates. But we
can get quite close, by polling the page's current HTML snapshot for changes
and checking the WebBrowser.IsBusy property. That's what
LoadDynamicPage does below.
Some time-out logic has to be present on top of the above, in case the page rendering is never-ending (note CancellationTokenSource).
Async/await is a great tool for coding this, as it gives the linear
code flow to our asynchronous polling logic, which greatly simplifies it.
It's important to enable HTML5 rendering using Browser Feature
Control, as WebBrowser runs in IE7 emulation mode by default.
That's what SetFeatureBrowserEmulation does below.
This is a WinForms app, but the concept can be easily converted into a console app.
This logic works well on the URL you've specifically mentioned: https://www.google.com/#q=where+am+i.
using Microsoft.Win32;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WbFetchPage
{
public partial class MainForm : Form
{
public MainForm()
{
SetFeatureBrowserEmulation();
InitializeComponent();
this.Load += MainForm_Load;
}
// start the task
async void MainForm_Load(object sender, EventArgs e)
{
try
{
var cts = new CancellationTokenSource(10000); // cancel in 10s
var html = await LoadDynamicPage("https://www.google.com/#q=where+am+i", cts.Token);
MessageBox.Show(html.Substring(0, 1024) + "..." ); // it's too long!
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
// navigate and download
async Task<string> LoadDynamicPage(string url, CancellationToken token)
{
// navigate and await DocumentCompleted
var tcs = new TaskCompletionSource<bool>();
WebBrowserDocumentCompletedEventHandler handler = (s, arg) =>
tcs.TrySetResult(true);
using (token.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: true))
{
this.webBrowser.DocumentCompleted += handler;
try
{
this.webBrowser.Navigate(url);
await tcs.Task; // wait for DocumentCompleted
}
finally
{
this.webBrowser.DocumentCompleted -= handler;
}
}
// get the root element
var documentElement = this.webBrowser.Document.GetElementsByTagName("html")[0];
// poll the current HTML for changes asynchronosly
var html = documentElement.OuterHtml;
while (true)
{
// wait asynchronously, this will throw if cancellation requested
await Task.Delay(500, token);
// continue polling if the WebBrowser is still busy
if (this.webBrowser.IsBusy)
continue;
var htmlNow = documentElement.OuterHtml;
if (html == htmlNow)
break; // no changes detected, end the poll loop
html = htmlNow;
}
// consider the page fully rendered
token.ThrowIfCancellationRequested();
return html;
}
// enable HTML5 (assuming we're running IE10+)
// more info: https://stackoverflow.com/a/18333982/1768303
static void SetFeatureBrowserEmulation()
{
if (LicenseManager.UsageMode != LicenseUsageMode.Runtime)
return;
var appName = System.IO.Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
Registry.SetValue(#"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION",
appName, 10000, RegistryValueKind.DWord);
}
}
}
Your web-browser code looks reasonable - wait for something, that grab current content. Unfortunately there is no official "I'm done executing JavaScript, feel free to steal content" notification from browser nor JavaScript.
Some sort of active wait (not Sleep but Timer) may be necessary and page-specific. Even if you use headless browser (i.e. PhantomJS) you'll have the same issue.

Server-Sent Events using Poco::Net::HTTPRequestHandler

I'm trying to "stream" data to an HTML5 page using server-sent events.
This tutorial http://www.html5rocks.com/en/tutorials/eventsource/basics/ was quite helpful to get the client side working.
But for the server side, I'm doing something similar to the HTTPServer example in http://pocoproject.org/slides/200-Network.pdf
The html5rocks.com tutorial gave me the following idea for the request handler's code:
void MyRequestHandler::handleRequest (HTTPServerRequest &req, HTTPServerResponse &resp)
{
resp.setStatus(HTTPResponse::HTTP_OK);
resp.add("Content-Type", "text/event-stream");
resp.add("Cache-Control", "no-cache");
ostream& out = resp.send();
while (out.good())
{
out << "data: " << "some data" << "\n\n";
out.flush();
Poco::Thread::sleep(500)
}
}
and the HTML5 page's source:
<!DOCTYPE html>
<html>
<head>
<title>HTLM5Application</title>
</head>
<body>
<p id="demo">hello</p>
<script>
var msgCounter = 0;
var source;
var data;
if(typeof(EventSource) !== "undefined")
{
source = new EventSource('/stream');
document.getElementById("demo").innerHTML = "Event source created";
}
else
{
document.getElementById("demo").innerHTML = "Are you using IE ?";
}
source.addEventListener('message', function(e)
{
msgCounter++;
document.getElementById("demo").innerHTML = "Message received (" + msgCounter + ") !<br/>"+ e.data;
}, false);
</script>
</body>
</html>
The good thing is that, when opening the html page, the data gets streamed and I get a correct outpout (the text between the tag gets updated as expected.
The problem is that when I close the page in the browser, the POCO program crashes, and I get the following message in the console:
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
Process returned 3 (0x3) execution time : 22.234 s
Press any key to continue.
(I'm using Code::Blocks, that's why the return value and the execution time are displayed)
Event when I put the while() loop between try{ }catch(...){} the program still crashes without entering the catch (same thing happens when I put the entire main()'s content in between try/catch )
The main program contains only these instructions:
int main(int argc, char* argv[])
{
MyServerApp myServer;
myServer.run(argc, argv);
return 0;
}
I want to know what could cause that crash and how I can fix it, please.
Thank you in advance for your help :)
For anyone interested, I was able to deal with this issue by registering my own error handler that simply ignores the exception thrown when an SSE-client disconnects:
#include <Poco\ErrorHandler.h>
// Other includes, using namespace..., etc.
class ServerErrorHandler : public ErrorHandler
{
public:
void exception(const Exception& e)
{
// Ignore an exception that's thrown when an SSE connection is closed.
//
// Info: When the server is handling an SSE request, it keeps a persistent connection through a forever loop.
// In order to handle when a client disconnects, the request handler must detect such an event. Alas, this
// is not possible with the current request handler in Poco (we only have 2 params: request and response).
// The only hack for now is to simply ignore the exception generated when the client disconnects :(
//
if (string(e.className()).find("ConnectionAbortedException") == string::npos)
poco_debugger_msg(e.what());
}
};
class ServerApp : public ServerApplication
{
protected:
int main(const vector<string>& args)
{
// Create and register our error handler
ServerErrorHandler error_handler;
ErrorHandler::set(&error_handler);
// Normal server code, for example:
HTTPServer server(new RequestHandlerFactory, 80, new HTTPServerParams);
server.start();
waitForTerminationRequest();
server.stop();
return Application::EXIT_OK;
}
};
POCO_SERVER_MAIN(ServerApp);
However, I must say that this is an ugly hack. Moreover, the error handler is global to the application which makes it even less desirable as a solution. The correct way would be detect the disconnection and handle it. For that Poco must pass the SocketStream to the request handler.
You can change your code to catch Poco Exceptions:
try {
MyServerApp myServer;
return myServer.run(argc, argv);
}catch(const Poco::Exception& ex) {
std::cout << ex.displayText() << std::endl;
return Poco::Util::Application::EXIT_SOFTWARE;
}

SignalR Javascript client callback not firing when using Clients.Group

I'm using SignalR RC2, this is my hub
public class ImgHub : Hub
{
public void Create(string guid)
{
Groups.Add(Context.ConnectionId, "foo");
}
public void SendMsg(string msg)
{
Clients.Group("foo").send(msg);
}
}
I have a console application and a webapplication (asp.net webforms) that connect to this hub. the console application works just as I would expect, the problem is in the Javascript part. The "send" callback doesn't fire when I'm using Clients.Group in SendMsg, if I change SendMsg to this
public void ShareImage(byte[] image, string guid)
{
Clients.All.ReceiveImage(image);
}
it works. Here is the Javascript code
<script src="Scripts/jquery-1.7.1.min.js"></script>
<script src="Scripts/jquery.signalR-1.0.0-rc2.min.js"></script>
<script src="http://localhost:4341/signalr/hubs/" type="text/javascript"></script>
<script type="text/javascript">
var mainHub;
$(function () {
$.connection.hub.url = 'http://localhost:4341/signalr';
// Proxy created on the fly
mainHub = $.connection.imgHub;
mainHub.client.send = function (msg) {
alert(msg);
};
// Start the connection
$.connection.hub.start(function() {
mainHub.server.create('vanuit den JS');
})
.done(function() {
$('#msgButton').click(function() {
mainHub.server.sendMsg("msg from JS");
});
});
});
</script>
as you can see in the JS code, I also have a button on the page that calls the SendMsg function, the message does arrive on the console application so I would guess that the JS client is correctly registered in the SignalR group.
I'm no JS specialist so I hope someone that knows more about it then I do can help me out here.
It's because you need to enable rejoining groups in global asax.
GlobalHost.HubPipeline.EnableAutoRejoiningGroups();
There's more detail about that here:
http://weblogs.asp.net/davidfowler/archive/2012/11/11/microsoft-asp-net-signalr.aspx
This method call is going away for 1.0 RTM but for now you need to do it.
One of the reasons why your send function may not be executing is because by the time you are allowing a call to sendMsg on the client the client may not be in the group "foo" yet.
In your $.connection.hub.start you're registering a function to be called when start has completed, but you're also then registering another function to be called once start has completed via the .done. Therefore, what's happening is both functions are firing almost simultaneously. So when the sendMsg function is available to be called you may not have been successfully added to the group.
Here's how you can fix that problem:
$.connection.hub.start().done(function() {
mainHub.server.create('vanuit den JS').done(function() {
$('#msgButton').click(function() {
mainHub.server.sendMsg("msg from JS");
});
});
});
Essentially I'm waiting until the group join has completed successfully until allowing a sendMsg to go through.
I know that this is a long shot answer since you're probably waiting a significant amount of time after the connection has been started and still nothings coming over the wire but I'm unable to replicate the behavior on my end.
If my fix above does not work you should ensure that your server side functions are being called by setting break points.

Categories

Resources