using C# to close Google chrome incognito windows only - c#

I wanted to create a small program to close google incogneto windows.
I have the code to kill ALL chrome windows but im not sure how to isolate just the incognito windows
existing code:
Process[] proc = Process.GetProcessesByName("MyApp");
foreach (Process prs in proc)
{
prs.Kill();
}

I played around with this a little but didn't have complete success. I was able to determine which windows were incognito, and from there, technically kill the process.
However, it appears the chrome executable has to be killed to close the actual window, which unfortunately closes all the chrome windows.
You may be able to get something like SendKeys to simulate an Alt-F4 using the windows handle, or if I'm not mistaken, .Net 4.5 has some additional closing routines you could try.
Nonetheless, here is the code to determine which windows are chrome and which of those are incognito. They then "kill", but it doesn't close the window, just kills the browsing (Aw, Snap! as Chrome puts it).
[DllImport("user32.dll")]
static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
[DllImport("user32.dll")]
static extern bool CloseWindow(IntPtr hWnd);
[DllImport("user32")]
private static extern bool SetForegroundWindow(IntPtr hwnd);
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_CLOSE = 0xF060;
private void button1_Click(object sender, EventArgs e)
{
var proc = Process.GetProcesses().OrderBy(x => x.ProcessName);
foreach (Process prs in proc)
if (prs.ProcessName == "chrome" && WmiTest(prs.Id))
{
prs.Kill();
//To test SendKeys, not working, but gives you the idea
//SetForegroundWindow(prs.Handle);
//SendKeys.Send("%({F4})");
}
}
private bool WmiTest(int processId)
{
using (ManagementObjectSearcher mos = new ManagementObjectSearcher(string.Format("SELECT CommandLine FROM Win32_Process WHERE ProcessId = {0}", processId)))
foreach (ManagementObject mo in mos.Get())
if (mo["CommandLine"].ToString().Contains("--disable-databases"))
return true;
return false;
}

Related

How to kill a Shell executed process in c#

I want to display a tiff file using shell execute. I am assuming the default app is the photoviewer. My Problem is that when i want to kill the process with photoviewer.Kill() i get an System.InvalidOperationException. When setting a breakpoint after photoViewer.Start() i realised that photoviewer does not conatain an Id.
I there a sufficent way to kill it? As it runs via dllhost.exe i do not want to retrun all processes named dllhost and kill them all since i do not know what else is run by dllhost.
Process photoViewer = new Process();
private void StartProcessUsingShellExecute(string filePath)
{
photoViewer.StartInfo = new ProcessStartInfo(filePath);
photoViewer.StartInfo.UseShellExecute = true;
photoViewer.Start();
}
I have another approach without shell execute but this approach seems to have dpi issues.
Approach without shell execute
Found a solution, may help anyone with a similar problem.
When i looked into the task manager i found that the windows 10 photoviewer runs detached from the application via dllhost. So since i have 4 dllhost processes up and running and just want to close the window. I do:
private void StartProcessAsShellExecute(string filePath)
{
photoViewer.StartInfo = new ProcessStartInfo(filePath);
photoViewer.StartInfo.UseShellExecute = true;
photoViewer.Start();
Process[] processes = Process.GetProcessesByName("dllhost");
foreach (Process p in processes)
{
IntPtr windowHandle = p.MainWindowHandle;
CloseWindow(windowHandle);
// do something with windowHandle
}
viewerOpen = true;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
//I'd double check this constant, just in case
static uint WM_CLOSE = 0x10;
public void CloseWindow(IntPtr hWindow)
{
SendMessage(hWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
that closes all dllhost windows (of which i have just 1, the photoviewer)

Is it possible to send keystrokes to background window using the InputSimulator API?

I am trying to simulate keypress and text entry to a virtual machine without bringing it to the front, so that I can do my other work, without interruption. I am using the InputSimulator API for Visual Studio.
I've tried using
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg);
I've tried it with the process name and with the windows name and I can't get it to work.
It would be great if it works, because right now there is no issue in sending keystrokes to the active window, but I can't manage to make it work on a background window.
EDIT:
This is an example of an attempt to use PostMessage.
string processName = "myprocessname";
Process[] processList = Process.GetProcesses();
foreach (Process P in processList)
{
if (P.ProcessName.Equals(processName))
{
IntPtr myprocess = P.MainWindowHandle;
PostMessage(myprocess, InputSimulator.KeyPress(VirtualKeyCode.VK_A);
}
}

Close open Explorer windows without terminating explorer.exe

I've tried searching but nothing really matches my demand.
I don't want explorer.exe to be terminated or restarted.
I just want any open explorer windows to close.
[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsDelegate lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out IntPtr lpdwProcessId);
[DllImport("user32.dll")]
private static extern uint RealGetWindowClass(IntPtr hwnd, StringBuilder pszType, uint cchType);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
static uint WM_CLOSE = 0x10;
private delegate bool EnumWindowsDelegate(IntPtr hwnd, IntPtr lParam);
private static bool EnumWindowsCallback(IntPtr hwnd, IntPtr lParam)
{
IntPtr pid = new IntPtr();
GetWindowThreadProcessId(hwnd, out pid);
var wndProcess = System.Diagnostics.Process.GetProcessById(pid.ToInt32());
var wndClass = new StringBuilder(255);
RealGetWindowClass(hwnd, wndClass, 255);
if (wndProcess.ProcessName == "explorer" && wndClass.ToString() == "CabinetWClass")
{
//hello file explorer window...
SendMessage(hwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); // ... bye file explorer window
}
return (true);
}
static void Main()
{
EnumWindowsDelegate childProc = new EnumWindowsDelegate(EnumWindowsCallback);
EnumWindows(childProc, IntPtr.Zero);
Console.ReadKey();
}
edit:
so i guess the only interesting thing is the callback which will be called by windows for each enumerated window (handle of said window in hwnd)
GetWindowThreadProcessId provides us with the processid for a given window handle
GetProcessById then provides us with a process object to read things like the process name from
RealGetWindowClass provides us with the registered class name for a given window handle
finally we can look to see if the process for the current window is the explorer and if the window class is "CabinetWClass", which is the window class for the normal file explorer window
last but not least, if our check is ok, send a WM_CLOSE message to kindly ask the window to close itself...
The following alternative uses the COM API of the Shell object to retrieve and identify File Explorer windows. It requires the addition of the COM references to:
Microsoft Shell Controls And Automation
Microsoft Internet Controls
The object returned by Shell.Windows method is an IEnumerable. Each object in the collection is a SHDocVw.InternetExplorer instance. If the Document object is a Shell32.ShellFolderView, then the explorer is a File Explorer.
private static void CloseExplorerWindows()
{
Shell32.Shell shell = new Shell32.Shell();
// ref: Shell.Windows method
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb774107(v=vs.85).aspx
System.Collections.IEnumerable windows = shell.Windows() as System.Collections.IEnumerable;
if (windows != null)
{
// ref: ShellWindows object
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb773974(v=vs.85).aspx
foreach (SHDocVw.InternetExplorer window in windows)
{
object doc = window.Document;
if (doc != null && doc is Shell32.ShellFolderView)
{
window.Quit(); // closes the window
}
}
}
}
public static void CloseExplorerWindows() => EnumWindows(new EnumWindowsProc(EnumTheWindows), IntPtr.Zero);
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
[DllImport("user32.dll")]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
static uint WM_CLOSE = 0x10;
private static bool EnumTheWindows(IntPtr hWnd, IntPtr lParam)
{
int size = GetWindowTextLength(hWnd);
if (size++ > 0 && IsWindowVisible(hWnd))
{
var sb = new StringBuilder(size);
GetWindowText(hWnd, sb, size);
var threadID = GetWindowThreadProcessId(hWnd, out var processID);
var s = System.Diagnostics.Process.GetProcessById((int)processID).ProcessName;
if (s == "explorer" && sb.ToString() != "Program Manager")
SendMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
return true;
}
By default, explorer runs as single process, and any windows that open are just a thread of the process.
Normally, to close a program, you'd send a close message to the process. In this case, closing explorer.exe will close all explorer windows.
To close individual windows, you'd open each window via it's own process.
This can be done via registry setting or enabling under View->Options->View->Advanced Settings: "Launch ... separate process"
a) Find PID (process ID) of window you wanna close.
via taskmanager:
1. In list of processes, click the arrow to the left of "Windows Explorer"
2. Check the window name matches the window you wanna close
3. Right click on "Windows Explorer", click "Go to Details"
4. Record the pid
via CMD:
tasklist /V /FI "IMAGENAME eq explorer.exe"
If each explorer window is open in it's own process, the above command would display the window title in the last column.
Otherwise "N/A" would be displayed.
The pid of all explorer windows would be the same. Explorer.exe processes have their own pid, and title "N/A"
If 'separate process' has been enabled eg. via Folder View option, then each window can be closed via the process id & filter option of taskkill.
To close, the desired window has to be activated first, otherwise closing with pid will close the last active window, or closing with window title filter will give error:
INFO: No tasks running with the specified criteria.
b) taskkill /pid <pid>
will close the last active window.
Repeating this command will the next window.
or taskkill /im explorer.exe /fi "windowtitle eq <window name>"
or taskkill /fi "IMAGENAME eq explorer.exe" /fi "windowtitle eq <window name>"
< window name > is not case sensitive
If full path in title bar has been enabled in Folder view, then include full path or wildcards.
To close all explorer windows:
taskkill /im explorer.exe
Notes:
To activate explorer window, issue same command to open the window, if window reusing is enabled.
The pid of explorer window(ing) process is in the last row of the response table, in column "PID"; can be accessed via FOR loop.
A vbs workaround to close window from #HelpingHand: https://superuser.com/questions/1263315/how-to-close-a-particular-opened-folder-using-cmd-or-batch-file
A vbs workaround to activate window: http://superuser.com/questions/327676/application-to-automatically-switch-between-two-applications-in-windows
Tested on Win 10

Create an app for both console usage and windows usage [duplicate]

Is there a way to launch a C# application with the following features?
It determines by command-line parameters whether it is a windowed or console app
It doesn't show a console when it is asked to be windowed and doesn't show a GUI window when it is running from the console.
For example, myapp.exe /help would output to stdout on the console you used, but myapp.exe by itself would launch my Winforms or WPF user interface.
The best answers I know of so far involve having two separate exe and use IPC, but that feels really hacky.
What options do I have and trade-offs can I make to get the behavior described in the example above? I'm open to ideas that are Winform-specific or WPF-specific, too.
Make the app a regular windows app, and create a console on the fly if needed.
More details at this link (code below from there)
using System;
using System.Windows.Forms;
namespace WindowsApplication1 {
static class Program {
[STAThread]
static void Main(string[] args) {
if (args.Length > 0) {
// Command line given, display console
if ( !AttachConsole(-1) ) { // Attach to an parent process console
AllocConsole(); // Alloc a new console
}
ConsoleMain(args);
}
else {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
private static void ConsoleMain(string[] args) {
Console.WriteLine("Command line = {0}", Environment.CommandLine);
for (int ix = 0; ix < args.Length; ++ix)
Console.WriteLine("Argument{0} = {1}", ix + 1, args[ix]);
Console.ReadLine();
}
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AllocConsole();
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AttachConsole(int pid);
}
}
I basically do that the way depicted in Eric's answer, additionally I detach the console with FreeConsole and use the SendKeys command to get the command prompt back.
[DllImport("kernel32.dll")]
private static extern bool AllocConsole();
[DllImport("kernel32.dll")]
private static extern bool AttachConsole(int pid);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FreeConsole();
[STAThread]
static void Main(string[] args)
{
if (args.Length > 0 && (args[0].Equals("/?") || args[0].Equals("/help", StringComparison.OrdinalIgnoreCase)))
{
// get console output
if (!AttachConsole(-1))
AllocConsole();
ShowHelp(); // show help output with Console.WriteLine
FreeConsole(); // detach console
// get command prompt back
System.Windows.Forms.SendKeys.SendWait("{ENTER}");
return;
}
// normal winforms code
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
Write two apps (one console, one windows) and then write another smaller app which based on the parameters given opens up one of the other apps (and then would presumably close itself since it would no longer be needed)?
I've done this by creating two separate apps.
Create the WPF app with this name: MyApp.exe. And create the console app with this name: MyApp.com. When you type your app name in the command line like this MyApp or MyApp /help (without .exe extension) the console app with the .com extension will take precedence. You can have your console application invoke the MyApp.exe according to the parameters.
This is exactly how devenv behaves. Typing devenv at the command line will launch Visual Studio's IDE. If you pass parameters like /build, it will remain in the command line.
NOTE: I haven't tested this, but I believe it would work...
You could do this:
Make your app a windows forms application. If you get a request for console, don't show your main form. Instead, use platform invoke to call into the Console Functions in the Windows API and allocate a console on the fly.
(Alternatively, use the API to hide the console in a console app, but you'd probably see the console "flicker" as it was created in this case...)
As far as I am aware there is a flag in the exe that tells it whether to run as console or windowed app. You can flick the flag with tools that come with Visual Studio, but you cann't do this at runtime.
If the exe is compiled as a console, then it will always open a new console if its not started from one.
If the the exe is an application then it can't output to the console. You can spawn a separate console - but it won't behave like a console app.
I the past we have used 2 separate exe's. The console one being a thin wrapper over the forms one (you can reference an exe as you would reference a dll, and you can use the [assembly:InternalsVisibleTo("cs_friend_assemblies_2")] attribute to trust the console one, so you don't have to expose more than you need to).
I would create a solution that is a Windows Form App since there are two functions you can call that will hook into the current console. So you can treat the program like a console program. or by default you can launch the GUI.
The AttachConsole function will not create a new console. For more information about AttachConsole, check out PInvoke: AttachConsole
Below a sample program of how to use it.
using System.Runtime.InteropServices;
namespace Test
{
/// <summary>
/// This function will attach to the console given a specific ProcessID for that Console, or
/// the program will attach to the console it was launched if -1 is passed in.
/// </summary>
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AttachConsole(int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FreeConsole();
[STAThread]
public static void Main()
{
Application.ApplicationExit +=new EventHandler(Application_ApplicationExit);
string[] commandLineArgs = System.Environment.GetCommandLineArgs();
if(commandLineArgs[0] == "-cmd")
{
//attaches the program to the running console to map the output
AttachConsole(-1);
}
else
{
//Open new form and do UI stuff
Form f = new Form();
f.ShowDialog();
}
}
/// <summary>
/// Handles the cleaning up of resources after the application has been closed
/// </summary>
/// <param name="sender"></param>
public static void Application_ApplicationExit(object sender, System.EventArgs e)
{
FreeConsole();
}
}
One way to do this is to write a Window app that doesn't show a window if the command line arguments indicate it shouldn't.
You can always get the command line arguments and check them before showing the first window.
The important thing to remember to do after AttachConsole() or AllocConsole() calls to get it to work in all cases is:
if (AttachConsole(ATTACH_PARENT_PROCESS))
{
System.IO.StreamWriter sw =
new System.IO.StreamWriter(System.Console.OpenStandardOutput());
sw.AutoFlush = true;
System.Console.SetOut(sw);
System.Console.SetError(sw);
}
I have found that works with or without VS hosting process. With output being sent with System.Console.WriteLine or System.Console.out.WriteLine before call To AttachConsole or AllocConsole. I have included my method below:
public static bool DoConsoleSetep(bool ClearLineIfParentConsole)
{
if (GetConsoleWindow() != System.IntPtr.Zero)
{
return true;
}
if (AttachConsole(ATTACH_PARENT_PROCESS))
{
System.IO.StreamWriter sw = new System.IO.StreamWriter(System.Console.OpenStandardOutput());
sw.AutoFlush = true;
System.Console.SetOut(sw);
System.Console.SetError(sw);
ConsoleSetupWasParentConsole = true;
if (ClearLineIfParentConsole)
{
// Clear command prompt since windows thinks we are a windowing app
System.Console.CursorLeft = 0;
char[] bl = System.Linq.Enumerable.ToArray<char>(System.Linq.Enumerable.Repeat<char>(' ', System.Console.WindowWidth - 1));
System.Console.Write(bl);
System.Console.CursorLeft = 0;
}
return true;
}
int Error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
if (Error == ERROR_ACCESS_DENIED)
{
if (log.IsDebugEnabled) log.Debug("AttachConsole(ATTACH_PARENT_PROCESS) returned ERROR_ACCESS_DENIED");
return true;
}
if (Error == ERROR_INVALID_HANDLE)
{
if (AllocConsole())
{
System.IO.StreamWriter sw = new System.IO.StreamWriter(System.Console.OpenStandardOutput());
sw.AutoFlush = true;
System.Console.SetOut(sw);
System.Console.SetError(sw);
return true;
}
}
return false;
}
I also called this when I was done in case I needed command prompt to redisplay when I was done doing output.
public static void SendConsoleInputCR(bool UseConsoleSetupWasParentConsole)
{
if (UseConsoleSetupWasParentConsole && !ConsoleSetupWasParentConsole)
{
return;
}
long LongNegOne = -1;
System.IntPtr NegOne = new System.IntPtr(LongNegOne);
System.IntPtr StdIn = GetStdHandle(STD_INPUT_HANDLE);
if (StdIn == NegOne)
{
return;
}
INPUT_RECORD[] ira = new INPUT_RECORD[2];
ira[0].EventType = KEY_EVENT;
ira[0].KeyEvent.bKeyDown = true;
ira[0].KeyEvent.wRepeatCount = 1;
ira[0].KeyEvent.wVirtualKeyCode = 0;
ira[0].KeyEvent.wVirtualScanCode = 0;
ira[0].KeyEvent.UnicodeChar = '\r';
ira[0].KeyEvent.dwControlKeyState = 0;
ira[1].EventType = KEY_EVENT;
ira[1].KeyEvent.bKeyDown = false;
ira[1].KeyEvent.wRepeatCount = 1;
ira[1].KeyEvent.wVirtualKeyCode = 0;
ira[1].KeyEvent.wVirtualScanCode = 0;
ira[1].KeyEvent.UnicodeChar = '\r';
ira[1].KeyEvent.dwControlKeyState = 0;
uint recs = 2;
uint zero = 0;
WriteConsoleInput(StdIn, ira, recs, out zero);
}
Hope this helps...
No 1 is easy.
No 2 can't be done, I don't think.
The docs say:
Calls to methods such as Write and WriteLine have no effect in Windows applications.
The System.Console class is initialized differently in console and GUI applications. You can verify this by looking at the Console class in the debugger in each application type. Not sure if there's any way to re-initialize it.
Demo:
Create a new Windows Forms app, then replace the Main method with this:
static void Main(string[] args)
{
if (args.Length == 0)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
else
{
Console.WriteLine("Console!\r\n");
}
}
The idea is that any command line parameters will print to the console and exit. When you run it with no arguments, you get the window. But when you run it with a command line argument, nothing happens.
Then select the project properties, change the project type to "Console Application", and recompile. Now when you run it with an argument, you get "Console!" like you want. And when you run it (from the command line) with no arguments, you get the window. But the command prompt won't return until you exit the program. And if you run the program from Explorer, a command window will open and then you get a window.
I have worked out a way to do this including using stdin, but I must warn you that it is not pretty.
The problem with using stdin from an attached console is that the shell will also read from it. This causes the input to sometimes go to your app but sometimes to the shell.
The solution is to block the shell for the duration of the apps lifetime (although technically you could try to block it only when you need input). The way I choose to do this is by sending keystrokes to the shell to run a powershell command that waits for the app to terminate.
Incidentally this also fixes the problem of the prompt not getting back after the app terminates.
I have briefly attempted to get it to work from the powershell console as well. The same principles apply, but I didn't get it to execute my command. It might be that powershell has some security checks to prevent running commands from other applications. Because I don't use powershell much I didn't look into it.
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AllocConsole();
[DllImport("kernel32", SetLastError = true)]
private static extern bool AttachConsole(int dwProcessId);
private const uint STD_INPUT_HANDLE = 0xfffffff6;
private const uint STD_OUTPUT_HANDLE = 0xfffffff5;
private const uint STD_ERROR_HANDLE = 0xfffffff4;
[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(uint nStdHandle);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern int SetStdHandle(uint nStdHandle, IntPtr handle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern int GetConsoleProcessList(int[] ProcessList, int ProcessCount);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
/// <summary>
/// Attach to existing console or create new. Must be called before using System.Console.
/// </summary>
/// <returns>Return true if console exists or is created.</returns>
public static bool InitConsole(bool createConsole = false, bool suspendHost = true) {
// first try to attach to an existing console
if (AttachConsole(-1)) {
if (suspendHost) {
// to suspend the host first try to find the parent
var processes = GetConsoleProcessList();
Process host = null;
string blockingCommand = null;
foreach (var proc in processes) {
var netproc = Process.GetProcessById(proc);
var processName = netproc.ProcessName;
Console.WriteLine(processName);
if (processName.Equals("cmd", StringComparison.OrdinalIgnoreCase)) {
host = netproc;
blockingCommand = $"powershell \"& wait-process -id {Process.GetCurrentProcess().Id}\"";
} else if (processName.Equals("powershell", StringComparison.OrdinalIgnoreCase)) {
host = netproc;
blockingCommand = $"wait-process -id {Process.GetCurrentProcess().Id}";
}
}
if (host != null) {
// if a parent is found send keystrokes to simulate a command
var cmdWindow = host.MainWindowHandle;
if (cmdWindow == IntPtr.Zero) Console.WriteLine("Main Window null");
foreach (char key in blockingCommand) {
SendChar(cmdWindow, key);
System.Threading.Thread.Sleep(1); // required for powershell
}
SendKeyDown(cmdWindow, Keys.Enter);
// i haven't worked out how to get powershell to accept a command, it might be that this is a security feature of powershell
if (host.ProcessName == "powershell") Console.WriteLine("\r\n *** PRESS ENTER ***");
}
}
return true;
} else if (createConsole) {
return AllocConsole();
} else {
return false;
}
}
private static void SendChar(IntPtr cmdWindow, char k) {
const uint WM_CHAR = 0x0102;
IntPtr result = PostMessage(cmdWindow, WM_CHAR, ((IntPtr)k), IntPtr.Zero);
}
private static void SendKeyDown(IntPtr cmdWindow, Keys k) {
const uint WM_KEYDOWN = 0x100;
const uint WM_KEYUP = 0x101;
IntPtr result = SendMessage(cmdWindow, WM_KEYDOWN, ((IntPtr)k), IntPtr.Zero);
System.Threading.Thread.Sleep(1);
IntPtr result2 = SendMessage(cmdWindow, WM_KEYUP, ((IntPtr)k), IntPtr.Zero);
}
public static int[] GetConsoleProcessList() {
int processCount = 16;
int[] processList = new int[processCount];
// supposedly calling it with null/zero should return the count but it didn't work for me at the time
// limiting it to a fixed number if fine for now
processCount = GetConsoleProcessList(processList, processCount);
if (processCount <= 0 || processCount >= processList.Length) return null; // some sanity checks
return processList.Take(processCount).ToArray();
}

Sending keyboard key to browser in C# using sendkey function

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

Categories

Resources