15

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?ㅤㅤㅤㅤㅤ

TECNO
  • 87
  • 1
  • 2
  • 14
Chad
  • 2,920
  • 4
  • 27
  • 39

4 Answers4

9

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);
}
Chris Schmich
  • 27,532
  • 5
  • 71
  • 93
  • Chris, thanks so much for the suggestions. I'll give both a try and report back. I too figure it's some problem with what I have in the WinForms, but the MSDN docs don't imply that anything can override or disable those options. – Chad Jun 10 '10 at 04:46
  • An update. So, I tried both the ShowWindow & SetVisibleCore methods and neither are working. I even tried setting the main form invisible (form.Visible = false) in the Worker process. None of them are working. Could this have something to do with timing - do I need to wait until my WebBrowser control has loaded the browser or something strange like that? Thanks. – Chad Jun 11 '10 at 00:43
  • 1
    @Chad: You are doing the `ShowWindow` from the master process and the `SetVisibleCore` in the worker process, correct? Have you tried putting a breakpoint in `SetVisibleCore`? Does it get hit? You could also try handling some Form events (see http://msdn.microsoft.com/en-us/library/system.windows.forms.form_events.aspx), e.g. `Load`, `GotFocus`, `VisibleChanged`, or `Activated`, and then try changing the visibility there. Are you using `System.Windows.Forms.WebBrowser`? I was able to hide a form hosting that control by using the `SetVisibleCore` method. – Chris Schmich Jun 11 '10 at 03:16
  • Yes, ShowWindow is in the Master and SetVisibleCore is in the Worker. I printed out Form.Visible in the Worker and it is "false" in the form's constructor. Now, I'll look at the events to see if the visibility is changing elsewhere. My component: internal class FlashWebBrowser : WebBrowser { protected override void WndProc(ref Message m) { switch (m.Msg) { case 0x021: case 0x201: case 0x204: case 0x207: base.DefWndProc(ref m); return; } base.WndProc(ref m); } } via: http://tiredblogger.wordpress.com/2008/01/03/flash-and-proxy-support-on-net-webbrowser-controls/ – Chad Jun 11 '10 at 12:55
  • 1
    So, I tried intercepting VisibleChanged and setting Visible = false whenever it is triggered. This works almost 100%. I still see a small flicker when the Worker loads. For now, I can live with that and get back to more important features. Thanks for all your help - you got the answer. – Chad Jun 11 '10 at 15:46
6

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.

Jeff Schumacher
  • 2,906
  • 3
  • 21
  • 23
  • 2
    Jeff, thanks for your comments. Unfortunately, this also didn't work for me. I'm thinking it must have something to do with the components on my form, but I don't know why that would be the problem. Can you explain more how "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"? Thanks. – Chad Jun 11 '10 at 00:56
  • This worked for me. Does it really have to be so hard? – david.pfx May 28 '21 at 06:57
0

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

zessx
  • 65,078
  • 28
  • 121
  • 147
0

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.

MoonAche
  • 23
  • 7