C# - Launch Invisible Process (CreateNoWindow & WindowStyle not working?) - c#

I have 2 programs (.exe) which I've created in .NET. We'll call them the Master and the Worker. The Master starts 1 or more Workers. The Worker will not be interacted with by the user, but it is a WinForms app that receives commands and runs WinForms components based on the commands it receives from the Master.
I want the Worker app to run completely hidden (except showing up in the Task Manager of course). I thought that I could accomplish this with the StartInfo.CreateNoWindow and StartInfo.WindowStyle properties, but I still see the Client.exe window and components in the form. However, it doesn't show up in the taskbar.
Process process = new Process
{
EnableRaisingEvents = true,
StartInfo =
{
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
FileName = "Client.exe",
UseShellExecute = false,
ErrorDialog = false,
}
};
What do I need to do to let Client.exe run, but not show up?ㅤㅤㅤㅤㅤ

Your usage of CreateNoWindow/WindowStyle works fine on my system with notepad.exe (e.g. it is hidden but running in the background), so it's probably something the WinForms app is doing. Some ideas:
Option 1: If you control the WinForms worker process, you can override Control.SetVisibleCore to always hide the form. If you don't want to always hide it, you can pass a command-line argument to it, e.g. /hide that will cause it to be hidden. Example (assuming there's already code-behind for the form):
public partial class MyForm : Form
{
public MyForm()
{
InitializeComponent();
}
protected override void SetVisibleCore(bool value)
{
// You'd probably want to parse the command line.
if (Environment.CommandLine.Contains("/hide"))
base.SetVisibleCore(false);
else
base.SetVisibleCore(value);
}
}
With this, running MyForm.exe results in a process with a visible form. Running MyForm.exe /hide results in a process with a hidden form. You could pass the /hide argument from your master process, so then normal users running the application will still see it.
Option 2: You can hide the application after it starts by doing a P/Invoke to ShowWindow. More info on this here. This has the drawback that you can sometimes see the worker window flicker into existence before being hidden. Example:
class Program
{
public static void Main(string[] args)
{
ProcessStartInfo psi = new ProcessStartInfo()
{
FileName = #"C:\windows\system32\notepad.exe",
};
Process process = Process.Start(psi);
// Wait until the process has a main window handle.
while (process.MainWindowHandle == IntPtr.Zero)
{
process.Refresh();
}
ShowWindow(process.MainWindowHandle, SW_HIDE);
}
const int SW_HIDE = 0;
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
}

The problem is with UseShellExecute = false, set this to true and the process will be started as hidden. Using the shell to start the process understands how to make the application hidden, where as starting the process directly with UseShellExecute = false starts the process directly, and as Chris Schmich mentioned, you'd have to handle hiding the window from inside the client application. This might be more desirable if you want the option of running the application manually for debugging or testing purposes.

You have to add base.Visibility = Visibility.Hidden; in Win Form application constructor.

Since my reputation is too low, i can only post my answer, ubable to comment Chris Schmich's answer.
Before start process, you can set WindowStyle = ProcessWindowStyle.Minimized to avoid window flicker.
Here is my code:
private const int SW_HIDE = 0;
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hwnd, int nCmdShow);
public int RunWithoutWindow(string exePath)
{
var startInfo = new ProcessStartInfo(exePath)
{
WindowStyle = ProcessWindowStyle.Minimized
};
var process = Process.Start(startInfo);
while (process.MainWindowHandle == IntPtr.Zero)
{
process.Refresh();
}
ShowWindow(process.MainWindowHandle, SW_HIDE);
return process.Id;
}
Finally thanks good answer provided by Chris Schmich, while ProcessWindowStyle.Hidden sometimes not work.

Related

WPF ShowDialog in started process is not blocking main window of current process

I have window application that starts process with WPF window. I send current process ID to the starting process, so WPF window can set owner properly:
MainWindow mainWindow = new MainWindow();
int parentProcessId = int.Parse(e.Args[0]);
System.Diagnostics.Process parentProcess = System.Diagnostics.Process.GetProcessById(parentProcessId);
WindowInteropHelper helper = new WindowInteropHelper(mainWindow);
helper.Owner = parentProcess.MainWindowHandle;
mainWindow.ShowDialog();
Alt+Tab works as expected - WPF window blocks parent window. But it does not block parent widow's controls so I can push every button in parent window and even close it.
I'm starting new process like this:
string arguments = Process.GetCurrentProcess().Id.ToString();
ProcessStartInfo startInfo = new ProcessStartInfo(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "WPF.App.exe"), arguments);
Process.Start(startInfo);
How can I open WPF window so it blocks owner window's controls (and not only appears on top and blocks Alt+Tab)?
Thank you.
Assuming that the parent process is also a WPF app:
private void SomeMethod()
{
string arguments = Process.GetCurrentProcess().Id.ToString();
ProcessStartInfo startInfo = new ProcessStartInfo(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "WPF.App.exe"), arguments);
childProcess = Process.Start(startInfo);
childProcess.Exited += childProcess_Exited;
childProcessWaitFrame = new DispatcherFrame;
Dispatcher.PushFrame(childProcessWaitFrame);
}
private void childProcess_Exited(object sender, EventArgs e)
{
childProcessWaitFrame.Continue = false;
}

How to launch another program and interact with its textbox when fully loaded (or in idle)

I have a lookup program (.exe) that I want to launch that contains a textbox. I want to launch this program from my main form and copy paste data from my main form into the lookup program (.exe).
I've already wrote some code to do this and it works fine but I'm using Thread.Sleep() to wait until the program loads. I was thinking of something like WaitForInputIdle() but I'm not sure how that works.
private IntPtr handle;
private void launchProgram()
{
// Copy the text in the datafield to Clipboard
Clipboard.SetText(serialTextBox.Text, TextDataFormat.Text);
bool isRunning = Process.GetProcessesByName("Serial Search.exe").Any();
using (Process process = new Process())
{
if (!isRunning)
{
process.StartInfo.FileName = #"C:\Serial Search.exe";
process.Start();
// Give the process some time to startup - I want to remove this line and interact when fully loaded
Thread.Sleep(2000);
Process[] pname = Process.GetProcessesByName("Serial Search.exe");
if (pname.Length > 0)
{
handle = pname[0].MainWindowHandle;
SetForegroundWindow(handle);
}
var activatedHandle = GetForegroundWindow();
var vPprocess = Process.GetProcessesByName("Serial Search.exe");
while (GetActiveWindowTitle() == "Serial Search")
{
SendKeys.Send("^V");
SendKeys.Send("{ENTER}");
break;
}
}
else
{
Process[] pname = Process.GetProcessesByName("Serial Search.exe");
handle = pname[0].MainWindowHandle;
SetForegroundWindow(handle);
var activatedHandle = GetForegroundWindow();
var vPprocess = Process.GetProcessesByName("Serial Search.exe");
while (GetActiveWindowTitle() == "Serial Search")
{
SendKeys.Send("^A");
SendKeys.Send("^V");
SendKeys.Send("{ENTER}");
break;
}
}
}
}
}
I want to remove the Thread.Sleep part and just have the form instantaneously interact with the .exe whenever it's loaded. Any ideas how I could do this?
You could simplify the code down to:
private IntPtr handle;
private void launchProgram()
{
handle = IntPtr.Zero;
string fullPathFileName = #"C:\Serial Search.exe";
string friendlyFileName = System.IO.Path.GetFileNameWithoutExtension(fullPathFileName);
Process P = Process.GetProcessesByName(friendlyFileName).FirstOrDefault();
if (P != null)
{
handle = P.MainWindowHandle;
}
else
{
P = Process.Start(fullPathFileName);
P.WaitForInputIdle();
handle = P.MainWindowHandle;
}
if (!handle.Equals(IntPtr.Zero))
{
// Copy the text in the datafield to Clipboard
Clipboard.SetText(serialTextBox.Text, TextDataFormat.Text);
handle = pname[0].MainWindowHandle;
SetForegroundWindow(handle);
SendKeys.Send("^A");
SendKeys.Send("^V");
SendKeys.Send("{ENTER}");
}
}
Note that Process.GetProcessesByName() expects the "friendly" version of the filename without the ".exe" on the end:
The process name is a friendly name for the process, such as Outlook,
that does not include the .exe extension or the path.
Maybe take the messy route and check for color at a specific pixel which will indicate the program is loaded. Or maybe look for some CPU usage drop or hard drive usage drop of the process that can be associated with the program done loading

Bringing another application into foreground and sending input to it

I am trying to restore( or even maximize ) another application and send input to it from my project. I can do it for certain applications like Notepad or Skype, but it doesn't work for other applications such as TeamSpeak.
Any ideas why and how I can solve the problem ?
Here's my code :
private void winact()
{
IntPtr hWnd; //change this to IntPtr
Process[] processRunning = Process.GetProcesses();
string title, name;
foreach (Process pr in processRunning)
{
title = pr.MainWindowTitle.ToLower();
name = pr.ProcessName.ToLower();
if (title.Contains("teamspeak".ToLower()) || name.Contains("teamspeak".ToLower()))
{
hWnd = pr.MainWindowHandle;
ShowWindow(hWnd, 3);
SetForegroundWindow(hWnd); //set to topmost
break;
}
}
}
I use InputSimulator to send the input.
Could you just try this? I think this should work for you.
private void winact()
{
IntPtr hWnd; //change this to IntPtr
Process[] processRunning = Process.GetProcesses();
string title, name;
foreach (Process pr in processRunning)
{
title = pr.MainWindowTitle.ToLower();
name = pr.ProcessName.ToLower();
if (title.Contains("teamspeak".ToLower()) || name.Contains("teamspeak".ToLower()))
{
hWnd = pr.MainWindowHandle;
HwndSource hwndSource = HwndSource.FromHwnd(hWnd);
Window window = hwndSource.RootVisual as Window;
window.Activate();
window.TopMost = true;
ShowWindow(hWnd, 3);
SetForegroundWindow(hWnd); //set to topmost
break;
}
}
}
Apparently the problem was that my application did not run with administrator rights. In order to be able to send input to other apps efficiently, the role of administrator is required.
To do this, I've added an app.manifest to the application, and changed requestedExecutionLevel level from asInvoker to requireAdministrator.
Using InputSimulator worked for other applications, but certain applications, such as games, consider the input to be a simple message and do not process keystrokes as actions. They are however passed to the chat. (this is what I've noticed)
In order to solve this issue, I have written a very script in autoIT which is simply taking the parameter passed to it (only 1) and sends the parameter as a keystroke to the window, currently in foreground. The script is compiled resulting in an executable which I am calling.
I am sure that there is a better way of doing it, but this is the best I've managed to do.

How to show/hide an application with Visible and ShowInTaskBar as false

How to show/hide an application that's running like:
Visible = false;
ShowInTaskBar = false;
using C#?
I tried unsuccessfully, using:
ShowWindow(handle, SW_SHOWNORMAL);
but it does not show it if the application is running in this above situation.
UPDATE; My scenery:
I have an application(written by me) that when WindowState is FormWindowState.Minimized I hide application of TaskBar and put it in "tray icon mode".
I'm using the following method for ensure application single instance:
[STAThread]
static void Main()
{
bool createdNew;
Mutex m = new Mutex(true, "...", out createdNew);
if (!createdNew)
{
Process currentProc = Process.GetCurrentProcess();
foreach (Process proc in Process.GetProcessesByName(currentProc.ProcessName))
{
if (proc.Id != currentProc.Id)
{
IntPtr handle = currentProc.Handle;
SetForegroundWindow(handle);
break;
}
}
}
else
{
Application.SetCompatibleTextRenderingDefault(false);
Application.EnableVisualStyles();
Application.Run(new Form1());
}
}
the problem is,it works fine for ensure the single instance,but I want show application(exit of tray icon mode) if application is running.
I thought to do communication in applications,something like send message from app1(1) to app2, app2 read the message that's 1 and do some action. but I have no idea how do this, ShowWindow() seemed at ago hours the best way to do this,but #Hans Passant pointed some points,that it's not possible. I hope this is clear.
Different ways to solve this is very appreciated. Thanks again!
Changing the ShowInTaskbar property changes the Handle value. It is one of several Form class properties that can only be specified in the native CreateWindowEx() call and can't be changed later. So changing the property requires Winforms to re-create the window. And that gives it a different handle, making it very likely that the ShowWindow() call uses the wrong value.
You didn't find out that this was the problem because you are not checking the ShowWindow() return value. Very important when you pinvoke Windows calls, you don't have a friendly .NET exception to whack you over the head when the call failed.
I was looking for how to do this without single instance, and I just found the solution.
[STAThread]
static void Main()
{
string proc = Process.GetCurrentProcess().ProcessName;
Process[] processes = Process.GetProcessesByName(proc);
// No form has been created
if (processes.Length <= 1)
{
Form1 form = new Form1();
Application.Run(form);
}
// Form has been created
else
{
for (int i = 0; i < processes.Length; i++)
{
IntPtr formhwnd = FindWindow(null, "Form1");// get HWND by the text name of form
// Use WIN32 methods
int style = GetWindowLong(formhwnd, GWL_EXSTYLE);// Get EX-Style of the window
style |= WS_EX_APPWINDOW;// Add the APP style that shows icon in taskbar
style &= ~(WS_EX_TOOLWINDOW);// Delete the tool style that does not show icon in taskbar
SetWindowLong(formhwnd, GWL_EXSTYLE, style);// Set the EX-Style
ShowWindow(formhwnd, SW_SHOW);// Show the Window
SwitchToThisWindow(formhwnd, true);// Focus on the window
}
}
}
If you want to show/hide a window from another app. This can still be a reference.Just get the handle of that window and use the win32 methods (import from C++ dll) to set the window styles.

Way to quickly show/hide WinForms GUI C#

I'm creating an app that is threaded. I started GUI (Announce : Form) as separate thread.
That window will be very minimalistic with one input box and maybe a button. On another thread there will be Tcp Client running and when it gets info from TcpServer it should pass what it gets into that inputbox and show the gui (and topmost windows). After couple of seconds the gui should hide itself and wait for another tcp msg and so on.
public void setTextBox(string varText) {
if (InvokeRequired) {
textBox.BeginInvoke(new textBoxCallBack(setTextBox), new object[] {varText});
} else {
textBox.Text = varText;
}
}
This code is used to fill the textBox from the Tcp Thread. The only problem now is getting the window show and hide properly. Been trying many solutions and always something went wrong. Like:
private void windowStateChange(string varState) {
if (InvokeRequired) {
Invoke(new WindowStateChangeCallBack(windowStateChange), new object[] {varState});
} else {
if (varState == "Hide") {
//Hide();
// TopMost = false;
//TopMost = varState != FormWindowState.Minimized;
} else {
//Show();
//MessageBox.Show("TEST1");
}
}
}
public void windowStateChangeDiffrent(FormWindowState varState) {
if (InvokeRequired) {
Invoke(new WindowStateChangeCallBack(windowStateChange), new object[] {varState});
} else {
WindowState = varState;
// Hide();
TopMost = varState != FormWindowState.Minimized;
}
}
What would be best aproach to do this (and fastest, as time matters)?
Answer 1 which seems to work:
private static void windowStateChange(string varState) {
if (mainAnnounceWindow.InvokeRequired) {
mainAnnounceWindow.BeginInvoke(new StateCallBack(windowStateChange), new object[] {varState});
} else {
if (varState == "Hide") {
mainAnnounceWindow.Hide();
mainAnnounceWindow.TopMost = false;
} else {
mainAnnounceWindow.Show();
mainAnnounceWindow.TopMost = true;
}
}
}
Anything bad about it?
Making the form hide with form.Hide() should pose no problems.
However I've experienced making the form show again does not always work.
So if you're encountering the same problem, you could use something like this:
string RunningProcess = Process.GetCurrentProcess().ProcessName;
Process[] processes = Process.GetProcessesByName(RunningProcess);
int SW_SHOW = 5, SW_HIDE = 0, SW_RESTORE = 9, SW_SHOWNORMAL = 1;
for (int a = 0; a < processes.Length; a++)
{
IntPtr hWnd = processes[a].MainWindowHandle;
ShowWindowAsync(hWnd, SW_RESTORE);
ShowWindowAsync(hWnd, SW_SHOWNORMAL);
ShowWindowAsync(hWnd, SW_SHOW);
SetForegroundWindow((int)hWnd);
}
//Required Win32 API imports
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern bool ShowWindowAsync(IntPtr windowHandle, int cmd);
[System.Runtime.InteropServices.DllImportAttribute("User32.dll")]
private static extern IntPtr SetForegroundWindow(int hWnd);
you might try
form.Hide();
but make sure that you show/hide the form from the same thread that created them
this.Invoke(new MethodInvoker(this.hide()));
Another approach would be to expose events on your TCP thread object. It could define an event such as RecievedData(...) then the GUI could subscribe to that event and update itself without having to do any InvokeRequired checks etc.
Update: link to C# events tutorial
http://msdn.microsoft.com/en-us/library/aa645739%28VS.71%29.aspx
Form.Hide() is the right method to hide the form. I remember having issues with Form.Show(), I have a vague memory of having to use Form.Activate() as well to get the form to restore correctly.
You're already dealing with thread marshalling correctly (InvokeRequired and Invoke). You could also use Form.BeginInvoke(), which is an async version of Form.Invoke. That might be faster.

Categories

Resources