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.
Related
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.
This question already has answers here:
How to start WinForm app minimized to tray?
(6 answers)
Closed 8 years ago.
I have developed a windows form application and installer for it. I had installed that app on my machine. Now when I restart my PC or Log in on machine the App gets launched and shown on desktop. A sys tray icon is also shown in sys-tray. Now I want to keep app hidden and only sys tray icon should be visible. Means app should not be displayed on screen but sys tray icon should be visible. I have used "CreateProcessAsCurrentUser" method in which I have set the value of "STARTF_USESHOWWINDOW" to different values. But still its not working. Also I am not getting which method of Application gets called on System startup. Is it Main() function from Program.cs file. Please tell me the solution and also the Function which gets called.
[STAThread]
Main() function code: `static void Main()
{
Mutex mutex = new Mutex(false, "Application Name");
try
{
if (mutex.WaitOne(0, false))
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.Run(new MainForm());
}
else
{
IntPtr pf = NativeMethods.FindWindow(null, "Application Name");
NativeMethods.ShowWindow(pf, 0);
NativeMethods.SetForegroundWindow(pf);
}
}
I have set the value of flag as below.
[Flags]
public enum CreateProcessFlags : uint
{
STARTF_USESHOWWINDOW = 0x00000000,
}
try this.... put this in your form:
protected override void SetVisibleCore(bool value)
{
base.SetVisibleCore(false);
}
this will always now make the form invisible.
you will need some logic though to determine if it should be shown or not from other parts of your app. for example, set a global bool value and modify the code above to use that.
alternatively, you can use this:
protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
this.Visible = false;
}
but you will see a bit of a flash when you run the app straight away. you then, again, need to control when to make it visible so check the global bool value for the visible property so you can eventually display the form
have you try this?
private void Form1_Load(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized;
this.Hide();
this.ShowInTaskbar = true;
}
then use notifyicon
I have created setup of my application using Windows Installer.
Now I want to Start application at Windows Start-Up and move it system minimize tray as i don't want to display GUI(View) at Windows Start-Up.
I have searched in Google and i found to use Registry key But that is not enough for me as i also want to move to system minimize tray and application run.
My purpose to do it is, user do not feels annoying when application starts every time when he/she starts system.
Can anyone have answer?
Thanks..
In your application, add an event handler for the FrameworkElement.Loaded event. In that handler, add the following code:
WindowState = WindowState.Minimized;
This will minimise the application when it starts.
To start the application when the computer starts, you'll need to add your program into Windows Scheduler and set it to run at startup. You can find out more on the Schedule a task page at MSDN.
You also have to set this property to remove it from the taskbar
ShowInTaskbar= false;
Maybe this answer is late, but I still want to write it down to help those who haven't found solutions yet.
Firstly you need to add a function to minimize your app to tray when it autostarts as system startup.
In your App.xaml file, change the original StartupUri=... to Startup="App_Startup" as below. App_Startup is your function name and can be changed.
<Application x:Class="Yours.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="App_Startup">
In your App.xaml.cs file. Add the function below:
public partial class App : Application
{
private void App_Startup(object sender, StartupEventArgs e)
{
// Process command line args
var isAutoStart = false;
for (int i = 0; i != e.Args.Length; ++i)
{
if (e.Args[i] == "/AutoStart")
{
isAutoStart = true;
}
}
// Create main application window, starting minimized if specified
MainWindow mainWindow = new MainWindow();
if (isAutoStart)
{
mainWindow.WindowState = WindowState.Minimized;
}
mainWindow.OnAutoStart();
}
}
In your MainWindow.xaml.cs, add a function as below:
public void OnAutoStart()
{
if (WindowState == WindowState.Minimized)
{
//Must have this line to prevent the window start locatioon not being in center.
WindowState = WindowState.Normal;
Hide();
//Show your tray icon code below
}
else
{
Show();
}
}
Then you should set you app utostart as system start.
Now if you have a switch to decide whether you app to autostart as system start, you can just add the function below as your switch status changed event function.
private void SwitchAutoStart_OnToggled(object sender, RoutedEventArgs e)
{
const string path = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
var key = Registry.CurrentUser.OpenSubKey(path, true);
if (key == null) return;
if (SwitchAutoStart.IsOn)
{
key.SetValue("Your app name", System.Reflection.Assembly.GetExecutingAssembly().Location + " /AutoStart");
}
else
{
key.DeleteValue("Your app name", false);
}
}
If you want to automatically start the application for all users on Windows startup, just replace the forth line with
RegistryKey key = Registry.LocalMachine.OpenSubKey(path, true);
^_^
I want to create an app that behaves as follows:
On no argument it displays the main form
On argument "a" does a job but the main form isn't loaded.
On argument "b" the form loads using the argument passed (load that document)
For the 1 and 3 I can handle the arguments in the form's constructor as follows:
public ConfigurationActionManagerForm()
{
InitializeComponent();
Environment.GetCommandLineArgs();
// do stuff with that argument
}
But this approach doesn't allow me to apply the behavior of 2. in the list.
In program.cs I can edit it to handle the arguments before the form is even created, but what is the correct approach on using Application.Run() if I don't want to pass a form? How am I going to inform Program class instance that I need to terminate or show a message that something went wrong or even show a little taskbar icon that the process is doing stuff (Think of it like the unzipping process).
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ConfigurationActionManagerForm());
}
Would this approach from MSDN be correct to my application?
Do you mean in the same way that Visual Studio works?
If so then you can't do this in a normal Windows application - Visual Studio cheats.
The problem is that a Windows application can either be a Windows Forms application or a Console application, but it can't be both - its decided at compile time (for .Net applications this is in the project properties window). Your options are:
Make your application a Windows Forms application
In this case #1 and #3 will work perfecty, but for #2 you will find that you can't read from / write to the console (because there isn't one!). If your appliction doesn't need to give any feedback then this might be fine - do your work as you normally would and just don't display a form:
[STAThread]
static void Main(string[] args)
{
if (args.Length > 0)
{
// Handle #2 here
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ConfigurationActionManagerForm());
}
}
Make your application a console application
In this case #2 will work perfectly, however although #1 and #3 will work fine you will always have console window open in the background - if you close the console window your application will end.
Again this might be fine, but personally I find this to be a hack.
Cheat (do what Visual Studio Does)
Visual Studio cheats by having 2 separate applications - one is a Console Application and the other is a Windows Forms application. The easy solution is to leave it at that and require that users start a different executable when running the command line version (e.g. myprogram_g.exe and myprogram_w.exe).
Visual Studio goes one step further however and has a single entry point, devenv. It does this by using the fact that for compatability reasons the Windows shell will always run a .com file instead of a .exe if there is any ambiguity. Wheras all shortcuts etc.. point to the executable, if you run devenv on the command line the devenv.com application will run instead which uses magic to sort out whether or not it runs as a console or windows application.
My advice would be to create two different applications and leave it at that.
See How do I write a program that can be run either as a console or a GUI application? for more detail (make sure to read the comments which have additional useful suggestions).
Also see How to make an application as both GUI and Console application? for how ildasm does this.
You can call Application.Run() without a form instance.
That way, it will start the message loop without opening a form.
You can call MessageBox.Show() before calling .Run(), too.
You can even create and open a form, and then call Run() without specifying an argument - it just means that closing the form doesn't automatically exit the application.
E.g.
MessageBox.Show("Messaage!");
Form1 f = new Form1();
f.Show();
Application.Run();
As stated above, this way of doing Run() means that closing the forms doesn't automatically close the application. You need to handle this in the form's Close event handler. (Application.Exit())
MSDN online can help you out with this - check the help entry for Application.Run().
Basically you want a console applcation with a few changes.
Here's an example of how to get started, using a default aboutbox class:
using System;
using System.Windows.Forms;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("No Arguments");
}
else
{
if (args[0] == "a")
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new AboutBox1());
}
}
}
}
}
And AboutBox1 class:
using System.Reflection;
using System.Windows.Forms;
namespace ConsoleApplication1
{
partial class AboutBox1 : Form
{
public AboutBox1()
{
InitializeComponent();
this.Text = String.Format("About {0} {0}", AssemblyTitle);
this.labelProductName.Text = AssemblyProduct;
this.labelVersion.Text = String.Format("Version {0} {0}", AssemblyVersion);
this.labelCopyright.Text = AssemblyCopyright;
this.labelCompanyName.Text = AssemblyCompany;
this.textBoxDescription.Text = AssemblyDescription;
}
#region Assembly Attribute Accessors
public string AssemblyTitle
{
get
{
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
if (attributes.Length > 0)
{
AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0];
if (titleAttribute.Title != "")
{
return titleAttribute.Title;
}
}
return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
}
}
public string AssemblyVersion
{
get
{
return Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
}
public string AssemblyDescription
{
get
{
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
if (attributes.Length == 0)
{
return "";
}
return ((AssemblyDescriptionAttribute)attributes[0]).Description;
}
}
public string AssemblyProduct
{
get
{
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);
if (attributes.Length == 0)
{
return "";
}
return ((AssemblyProductAttribute)attributes[0]).Product;
}
}
public string AssemblyCopyright
{
get
{
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
if (attributes.Length == 0)
{
return "";
}
return ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
}
}
public string AssemblyCompany
{
get
{
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
if (attributes.Length == 0)
{
return "";
}
return ((AssemblyCompanyAttribute)attributes[0]).Company;
}
}
#endregion
private void okButton_Click(object sender, EventArgs e)
{
Close();
}
}
}
I found a neat and simple to implement solution using the example in my question provided by microsoft.
I created this application context class that is responsible for everything in the application and I use this instead of a form in the Application.Run() as shown below.
To achieve the behavior in the question, I am using a second form that is hidden and only the taskbar icon is shown. If the user wants to see how the process is doing, they can click the taskbar icon and see the logging window, which is actually the ConfigurationApplierForm in the example bellow.
class AnApplicationContext: ApplicationContext
{
private Form _currentForm;
Note the constructor is private, the main is inside this class and declared static.
private AnApplicationContext()
{
Application.ApplicationExit += new EventHandler(this.OnApplicationExit);
// choose which form to show based on arguments
if(Environment.GetCommandLineArgs().Contains("-apply"))
{
_currentForm = new ConfigurationApplierForm();
}
else
{
_currentForm = new ConfigurationActionManagerForm();
}
// initialize the form and attach event handlers
_currentForm.FormClosed += new FormClosedEventHandler(this.OnCurrentFormClosed);
_currentForm.ShowDialog();
}
Main is here, a little bit different from the original. Notice the argument in the Run method
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// context is passed instead of a form
Application.Run(new AnApplicationContext());
}
private void OnCurrentFormClosed(object sender, EventArgs e)
{
ExitThread();
}
private void OnApplicationExit(object sender, EventArgs e)
{
/* is there anything to do when all forms are closed
and the application is going to die?*/
}
}
Also, we need to tell the project that this is the startup project.
Project Properties -> Application -> Startup Project
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.