Automate #Google login using C# and MasterDev ChromeDevTools

Automating a Chrome login in C# is difficult, because Google is clever enough to detect if you are using an embedded WebBrowser – either the embedded InternetExplorer Control, or CEFSharp, and probably others. It’s also a multi-step process, so you have to get the synchronisation right.

So, this is where ChromeDevTools by MasterDev comes in; The original repo is here:

https://github.com/MasterDevs/ChromeDevTools

I’ve forked this, and made my own changes and fixes here;

https://github.com/infiniteloopltd/ChromeDevTools/blob/master/source/Sample/Program.cs

How MasterDev ChromeDevTools differs from CEFSharp, is that chrome is spawned in a separate process, and it uses WebSockets (WebSocket4Net.WebSocket) to communicate between the host application in C# and Chrome. This also means you can run it in AnyCPU mode, and you don’t have to worry about x86 or x64 CPU types. Oh, and of course, Google can’t detect that you’re listening in to what Chrome is doing.

Some organisational changes I made to the sample code, so that it would run in a synchronous manner was this helper function WaitForEvent<T> which effectively does nothing until a given Event is received from the browser

private static void WaitForEvent<T>(IChromeSession chromeSession) where T : class
{
var waiter = new ManualResetEventSlim();
chromeSession.Subscribe<T>(eventFired =>
{
waiter.Set();
});
waiter.Wait();
}

This method will block indefinitely if the event is never received, but that’s another issue.

I also added this helper method to run Javascript on the page, since I would be calling it repeatedly;

private static async Task<string> ExecuteJavascript(IChromeSession chromeSession, string javaScriptToExecute)
{
var evalResponse = await chromeSession.SendAsync(new EvaluateCommand
{
Expression = javaScriptToExecute,
});
if (evalResponse.Result.ExceptionDetails != null)
{
return evalResponse.Result.ExceptionDetails.ToString();
}
return evalResponse.Result.Result.Value == null ? “” : evalResponse.Result.Result.Value.ToString();
}

Here, I am returning everything as a string, regardless of type – even errors. I’d leave it up to the client application to deal with unexpected values. In my case, I didn’t even need return values.

Everything else is quite straightforward. It’s a matter of navigating to the login page, wait for the page to load, enter the username, press next, wait for the next frame to load, then enter the password, press next, wait for a login, then I can process the page.

Of course, Anything unexpected will make the process hang indefinitely, so this is in no way robust, but it’s a proof of concept, that I hope it helps someone.

Leave a Reply

Your email address will not be published.