2

Hello i am trying to send a key to browser using the code below, the new chrome window opens for me but it does not send the key to the browser.

When i debugged i found out chrome process does not have any title name how can i solve this problem?

 Process.Start("chrome.exe", "https://labs.sketchfab.com/sculptfab/");
        System.Threading.Thread.Sleep(2000);
        foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
        {
            if (p.ProcessName == "chrome" && p.MainWindowTitle == "SculptFab - SculptGL + Sketchfab" &&
                p.MainWindowHandle != IntPtr.Zero)
            {
                System.Threading.Thread.Sleep(2000);
                for (int i = 0; i < 50; i++)
                {
                    KeyHandle.SetForeGround(p.MainWindowHandle);
                    SendKeys.Send("s");
                    System.Threading.Thread.Sleep(2000);
                }

            }
        }

In the above mentioned page when "S" is pressed on the keyboard it zooms out of the object i want too achieve this using my C# code

Fahdie
  • 211
  • 3
  • 4
  • 14
  • Why not Selenium? Apart from windows captions problem, SendKeys.Send is not enough stable on recent Windows versions because of UAC... – Mike Makarov May 10 '15 at 21:56
  • I am actually making a C# application using kinect which would give input through gestures, I dont think Selenium can help me do that – Fahdie May 10 '15 at 22:16

3 Answers3

9

You can create a new instance of Process use it to send your keystrokes. See https://stackoverflow.com/a/12892316/2058898 for further information.

Update

I've done some researching and it seems Chrome does in fact not react to the SendKeys.Send method. However you can use the Windows API to call the SendMessage function and send Keydown/-up signals to the window. Here's a simple wrapper for using in Chrome:

public class ChromeWrapper
{
    // you might as well use those methods from your helper class
    [DllImport("User32.dll")]
    private static extern int SetForegroundWindow(IntPtr point);
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
    // the keystroke signals. you can look them up at the msdn pages
    private static uint WM_KEYDOWN = 0x100, WM_KEYUP = 0x101;

    // the reference to the chrome process
    private Process chromeProcess;
    
    public ChromeWrapper(string url)
    {
        // i'm using the process class as it gives you the MainWindowHandle by default
        chromeProcess = new Process();
        chromeProcess.StartInfo = new ProcessStartInfo("chrome.exe", url);
        chromeProcess.Start();
    }

    public void SendKey(char key)
    {
        if (chromeProcess.MainWindowHandle != IntPtr.Zero)
        {
            // send the keydown signal
            SendMessage(chromeProcess.MainWindowHandle, ChromeWrapper.WM_KEYDOWN, (IntPtr)key, IntPtr.Zero);
            
            // give the process some time to "realize" the keystroke
            System.Threading.Thread.Sleep(100); 

            // send the keyup signal
            SendMessage(chromeProcess.MainWindowHandle, ChromeWrapper.WM_KEYUP, (IntPtr)key, IntPtr.Zero);
        }
    }
}

Using this class is pretty simple:

ChromeWrapper chrome = new ChromeWrapper("https://labs.sketchfab.com/sculptfab/");
System.Threading.Thread.Sleep(5000);

chrome.SendKey('S');

Works on my machine™ (Windows 8.1 Pro N, Google Chrome 42).

Additional information

This solution only works if there's no Chrome running yet, as Chrome only sends the new URL to it's main process which opens it then. So either close other Chrome instances beforehand or use the SendMessage method on the process you found using Process.GetProcesses

Community
  • 1
  • 1
michaeln
  • 1,032
  • 17
  • 33
  • is it possible to send a keystroke to a certain Chrome Extension button to load it? – Volatil3 Jan 23 '16 at 10:19
  • I don't see a reason why this should not work as this sends the keystroke to the window. You might need to emulate a click on the extension's button to display it at first, but it should work. – michaeln Jan 23 '16 at 19:48
  • You need to run your C# code/executable as administrator to make it work with Google Chrome. – John Jul 18 '16 at 22:11
  • It doesn't work for alphanumeric keys, only for special ones, like: TAB, SPACE, etc. I've run Visual Studio with administrator and it still doesn't work. – Cosmin Ioniță Sep 27 '16 at 16:59
  • @CosminIoniță, I found the solution in https://stackoverflow.com/a/20493025/2612547 – CSharper Sep 30 '18 at 18:53
2

Here is an updated version of ChromeWrapper that:

  • Works also when chrome was already open (attaches to existing chrome window)
  • Works when the computer is locked (unlike SendKeys.SendWait)

.

public class ChromeWrapper
{
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
    // the keystroke signals. you can look them up at the msdn pages
    private static uint WM_KEYDOWN = 0x100, WM_KEYUP = 0x101;

    // the reference to the chrome process
    private Process chromeProcess;

    public ChromeWrapper(string url)
    {
        chromeProcess = new Process();
        chromeProcess.StartInfo = new ProcessStartInfo("chrome.exe", url);
        chromeProcess.StartInfo.UseShellExecute = true;
        chromeProcess.Start(); //no need to keep reference to this process, because if chrome is already opened, this is NOT the correct reference.
        Thread.Sleep(600); //without this behavior is altered (tap key presses operate on other objects on the page)
        

        Process[] procsChrome = Process.GetProcessesByName("chrome");
        foreach (Process chrome in procsChrome)
        {
            if (chrome.MainWindowHandle == IntPtr.Zero)// the chrome process must have a window
                continue;
            chromeProcess = chrome; //now you have a handle to the main chrome (either a new one or the one that was already open).
            return;
        }
    }

    public void SendKey(char key)
    {
        try
        {
            if (chromeProcess.MainWindowHandle != IntPtr.Zero)
            {
                // send the keydown signal
                SendMessage(chromeProcess.MainWindowHandle, ChromeWrapper.WM_KEYDOWN, (IntPtr)key, IntPtr.Zero);
                // give the process some time to "realize" the keystroke
                Thread.Sleep(30); //On my system it works fine without this Sleep.
                // send the keyup signal
                SendMessage(chromeProcess.MainWindowHandle, ChromeWrapper.WM_KEYUP, (IntPtr)key, IntPtr.Zero);
            }
        }
        catch (Exception e) //without the GetProcessesByName you'd get an exception.
        {
        }
    }
}

I use it as below, for sending Tab and Enter keys.

ChromeWrapper chrome = new ChromeWrapper(@"https://stackoverflow.com");
Thread.Sleep(300);
chrome.SendKey((char)9);// tab
chrome.SendKey((char)13);//enter
Guy
  • 1,232
  • 10
  • 21
0

I modified Guy's code to run properly in .NET6

Just replace this:

Process.Start("chrome.exe", url); //no need to keep reference to this process, because if chrome is already opened, this is NOT the correct reference.
Thread.Sleep(600);

by this:

 // i'm using the process class as it gives you the MainWindowHandle by default
 chromeProcess = new Process();
 chromeProcess.StartInfo = new ProcessStartInfo("chrome.exe", url);
 chromeProcess.StartInfo.UseShellExecute = true;
 chromeProcess.Start();
Fredy
  • 532
  • 3
  • 11