Application exit even not firing in c# windows application - c#

I am using the below class to hide and show the windows task bar.
I am calling the the class like below.
Problem : when i start the application, the taskbar is hiding perfectly.
But when i exit the application by stopping the debugging, the task bar is
not showing up. I mean the application exit is not firing in my code.
I need such a way like, whatever the way i close my application, it shud
finially show the tashbar() before exiting.
Please help. Thanks.
PROGRAM :
static class Program
{
[STAThread]
static void Main()
{
Taskbar.Hide();
Form1 TargerForm = new Form1();
Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
Application.EnableVisualStyles();
Application.Run(TargerForm);
}
static void Application_ApplicationExit(object sender, EventArgs e)
{
Taskbar.Show();
}
}
CLASS :
public class Taskbar
{
[DllImport("user32.dll")]
public static extern int FindWindow(string className, string windowText);
[DllImport("user32.dll")]
public static extern int ShowWindow(int hwnd, int command);
public const int SW_HIDE = 0;
public const int SW_SHOW = 1;
public int _taskbarHandle;
protected static int Handle
{
get
{
return FindWindow("Shell_TrayWnd", "");
}
}
public Taskbar()
{
_taskbarHandle = FindWindow("Shell_TrayWnd", "");
}
public static void Show()
{
ShowWindow(Handle, SW_SHOW);
}
public static void Hide()
{
ShowWindow(Handle, SW_HIDE);
}
}

Why not just call Taskbar.Show() after the call to Application.Run? Application.Run will block until the form is closed.
Your code could look like this:
[STAThread]
static void Main()
{
Taskbar.Hide();
Form1 TargerForm = new Form1();
Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
Application.EnableVisualStyles();
Application.Run(TargerForm);
Taskbar.Show();
}

When you stop debugging, that's a "rude" exit to a program, and nothing fires after that. MSFT's own Visual Studio integrated web browser suffers from this too. If this is super important to you, you might want to consider running something in the background which takes care of this cleanup for you and doesn't run in the context of the debugger?
I don't think there's a way to capture a "stop debugging" event, but I'd love to be proven wrong :)

Related

SendToBack on startup not working for two or more modeless forms

I create several modeless forms on application startup and try to send them to the background immediately after showing (the forms represent some kind of "yellow notes" the content of which gets deserialized on startup).
For just one form this works as expected, for two or more forms they stay in the foreground. What is wrong?
My demo code:
using System;
using System.Windows.Forms;
namespace TestSendToBack
{
internal sealed class Program
{
[STAThread]
private static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainForm form1 = new MainForm();
// MainForm form2 = new MainForm(); // comment in to make it fail!
form1.Show();
// form2.Show(); // comment in to make it fail!
Application.Run();
}
}
}
and
using System;
using System.Windows.Forms;
namespace TestSendToBack
{
public class MainForm : Form
{
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
SendToBack();
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
Application.Exit();
}
}
}
Don't mind the OnClosed, it's only for being able to stop the process. Otherwise it is not part of the problem, of course.
I have found an even better solution to the problem! Instead of sending the forms to the background manually (and keeping them there), I have made the desktop the parent of my forms (see also C# Position Window On Desktop):
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
void SetParentWindowToDesktop()
{
IntPtr hwndThis = this.Handle;
IntPtr hwndParent = FindWindow("ProgMan", null);
SetParent(hwndThis,hwndParent);
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
SetParentWindowToDesktop();
}
This has two additional benefits:
The forms always stay in the background without any further measures
They don't appear in the Alt-Tab-Menu (which I expect from "yellow notes" staying on the desktop)
Since all of this is part of a tray application there is still full control over window creation/destruction and application exit.
Two caveats: Don't use ShowInTaskbar=false because this brings the forms back to the Alt-Tab-Menu (WTF...). SendToBack() makes the forms disappear altogether.

C# app not running beyond initialisation

Program.cs
namespace PerformanceMonitor
{
static class Program
{
private static int NumberOfCores;
private static List<int> CPULoadVals;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MonitorGUI());
NumberOfCores = getNumberOfCores();
CPULoadVals = getCoreLoadVals();
}
private static int getNumberOfCores()
{
int coreCount = 0;
foreach (var core in new ManagementObjectSearcher("SELECT * FROM Win32_Processor").Get())
{
coreCount += int.Parse(core["NumberOfCores"].ToString());
}
return coreCount;
}
...
MonitorGUI.cs
namespace PerformanceMonitor
{
public partial class MonitorGUI : Form
{
public static List<Label> labels;
private static List<int> CPULoadVals;
public MonitorGUI()
{
InitializeComponent();
}
public void Form1_Load(object sender, EventArgs e)
{
...
}
Debugging the app I can see that InitializeComponent() is invoked causing a new form to be created (Application.Run(new MonitorGUI());) but trying to step through after that and nothing is called. The method on form load is not even called even though I can visually see that it's loaded
Application.Run()
Begins running a standard application message loop on the current thread, and makes the specified form visible.
This method blocks and only returns when you close the Form passed as argument. So all calls after that are executed when you close your main window.
You might want to change the order:
[STAThread]
static void Main()
{
NumberOfCores = getNumberOfCores();
CPULoadVals = getCoreLoadVals();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MonitorGUI());
}
And Form1_Load() is only called if you subscribed to the Load event of the Form:
public MonitorGUI()
{
InitializeComponent();
Load += Form1_Load; // <--- subscribe to the event
}
But this can also be done in designer. Check if you have set this event correctly.

Hide iTunes window from out of C# program

I want to use the COM interface of iTunes in my C# program. I already imported the type library successfully and tested a bit of code:
using Apple.iTunes;
public partial class MainForm : Form {
private iTunesApp _itapp;
public MainForm() {
InitializeComponent();
_itapp = new iTunesApp();
}
private void Form_Shown(object sender, EventArgs e) {
MessageBox.Show(_itapp.Version);
}
}
That works very well so far. But my problem is that iTunes will be started in background everytime I run my own app.
I know that it's necessary to get access on the COM interface but is there a possibility to let iTunes start with a hidden window or minimize it after starting from within my C# program?
+++ EDIT +++
The FindWindow function from the given MSDN article seems not working anymore. But it was a good reference point and I solved the problem now using the Process class:
public class WindowHandler {
private const int SW_SHOWMINIMIZED = 2;
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
public static void Minimize(string processName) {
Process[] p = Process.GetProcessesByName(processName);
if (p.Length > 0) {
ShowWindowAsync(p[0].MainWindowHandle, SW_SHOWMINIMIZED);
}
}
}
WindowHandler.Minimize("iTunes");
But for the other problem - to start iTunes even in minimized or hidden mode - I still found no way.

Pass arguments to running application

I am making an image uploader (upload image to image hosting website) and I'm having some issues passing an argument (image location to an already running application)
First of all let's say MyApp.exe is always running
Whenever I right click on an image I have added an item in the default windows context menu that says "Upload image".
When that's clicked it needs to pass the location to the already running application.
My program.cs:
static class Program
{
[DllImport("user32.dll")]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, UIntPtr
wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
[STAThread]
static void Main(params string[] Arguments)
{
if (Arguments.Length > 0)
{
//This means that the the upload item in the context menu is clicked
//Here the method "uploadImage(string location)"
//of the running application must be ran
}
else
{
//just start the application
Application.Run(new ControlPanel());
}
}
}
Note that the ControlPanel class doesn't have a visible form, only a tray icon is present since a form is not needed.
Could I get any help on how to do this?
I have figured it out, so awesome thanks for the person who posted the http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/a5bcfc8a-bf69-4bbc-923d-f30f9ecf5f64 link, this is exactly what I was looking for!
Here's a the full solution:
static class Program
{
[STAThread]
static void Main(params string[] Arguments)
{
SingleInstanceApplication.Run(new ControlPanel(), NewInstanceHandler);
}
public static void NewInstanceHandler(object sender, StartupNextInstanceEventArgs e)
{
string imageLocation = e.CommandLine[1];
MessageBox.Show(imageLocation);
e.BringToForeground = false;
ControlPanel.uploadImage(imageLocation);
}
public class SingleInstanceApplication : WindowsFormsApplicationBase
{
private SingleInstanceApplication()
{
base.IsSingleInstance = true;
}
public static void Run(Form f, StartupNextInstanceEventHandler startupHandler)
{
SingleInstanceApplication app = new SingleInstanceApplication();
app.MainForm = f;
app.StartupNextInstance += startupHandler;
app.Run(Environment.GetCommandLineArgs());
}
}
}
Thanks alot all, and especially the person who posted that link I mentioned above but I guess he deleted his answer?
Regards,
Kenny
Well you will have to establish a communication channel for other applications to post the images to. This communication channel can be one of the following - not a complete list just samples:
A directory that is watched by your app and the file is added once it is added to the directory.
A port where other applications can send information to.
A self-hosted web service that accepts the images.
A TCP port that receives the images.
A named pipe.
....
As you see there are several possibilities. The right one for you depends on your scenario. The file system is an option that can be implemented easily using a FileSystemWatcher for a sample see here.
A self-hosted web sevice exposes a web service that can receive images. See here for a sample.
IMHO, these are the two options that are easiest. But ... there are several more.
For the TCP port see Tim's post.
Assuming that you have control over the execution environment, the listening application could just expose an endpoint using WCF or even a raw TCP socket. That way, any other application can connect to it in a dynamic but structured fashion.
Even though both the sender and receiver are on the same machine, using a network transport solution (like WCF or TCP) is a great way to safely send data across processes.
Here's an example of how to do it in TCP with c#: http://www.switchonthecode.com/tutorials/csharp-tutorial-simple-threaded-tcp-server
WCF can be a bit more complicated (due in part to it's flexibility, and also due to serialization restrictions) but there is plenty of documentation online on how to use it. WCFis a more object-oriented solution because proxy classes can be generated that allow you to make strongly-typed calls to actual objects, versus just sending messages.
To avoid the running of second instance after passing the command line arguments to existing instance, I added below code snippet.
static class Program
{
[STAThread]
static void Main(params string[] Arguments)
{
Form1 MainForm;
bool bInstanceFlag;
Mutex MyApplicationMutex = new Mutex(true, "MyApp_Mutex", out bInstanceFlag);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (!bInstanceFlag)
{
MainForm = new Form1();
SingleInstanceApplication.Run(MainForm, NewInstanceHandler);
}
else
{
MainForm = new Form1();
SingleInstanceApplication.Run(MainForm, NewInstanceHandler);
MainForm.Close();
}
}
public static void NewInstanceHandler(object sender, StartupNextInstanceEventArgs e)
{
MainForm.AddItem = e.CommandLine[1];
e.BringToForeground = false;
}
public class SingleInstanceApplication : WindowsFormsApplicationBase
{
private SingleInstanceApplication()
{
base.IsSingleInstance = true;
}
public static void Run(Form f, StartupNextInstanceEventHandler startupHandler)
{
SingleInstanceApplication app = new SingleInstanceApplication();
app.MainForm = f;
app.StartupNextInstance += startupHandler;
app.Run(Environment.GetCommandLineArgs());
}
}
}
I added some small additions to the previous solution to reference a setter on the form in order to pass the arguments to it.
So first of all, create a static reference to the initial instance of the form (MainForm).
Then, on subsequent sending of arguments, the NewInstanceHandler can use the saved reference to the form to access it's public methods/properties (in my case, a setter called AddItem).
An easy way to test this out is to add a public property to your form with a setter to change the text property of the form (the title text).
[STAThread]
static Form1 MainForm;
static void Main(params string[] Arguments)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainForm = new Form1();
SingleInstanceApplication.Run(MainForm, NewInstanceHandler);
}
public static void NewInstanceHandler(object sender, StartupNextInstanceEventArgs e)
{
MainForm.AddItem = e.CommandLine[1];
e.BringToForeground = false;
}
public class SingleInstanceApplication : WindowsFormsApplicationBase
{
private SingleInstanceApplication()
{
base.IsSingleInstance = true;
}
public static void Run(Form f, StartupNextInstanceEventHandler startupHandler)
{
SingleInstanceApplication app = new SingleInstanceApplication();
app.MainForm = f;
app.StartupNextInstance += startupHandler;
app.Run(Environment.GetCommandLineArgs());
}
}

How to open an app main interface from the executable instead on the tray icon in c#

Here is the scenario, i opened my application then the tray icon shows, if i double-clicked the tray icon, the main interface will be shown. if i open again my application, the main interface should be given focus or if it is not yet shown then it should be shown instead of opening another instance of my app.
here is how my code looks like:
//Program.cs
.....
if(myAppIsNotRunningYet) //the program has not been open yet
{
MyTray = new MyTray();
Application.Run();
}
else //the program is already on the tray
{
//the code to give focus to the mainForm or open it up if not yet open
}
//MyTray.cs
.....
public MyTray()
{
notifyIcon = new NotifyIcon();
....
notifyIcon.Visible = true;
}
private void notifyIcon_DoubleClick(object sender, EventArgs e)
{
MainForm mainForm = new MainForm();
mainForm.ShowDialog();
}
EDIT: OK, It seems that to override WndProc correctly, you have to use a form which is/has been visible. So below is a different solution using a MessageFilter. This does work, so hopefully you are good to go from here!
internal sealed class Program
{
/// <summary>
/// Program entry point.
/// </summary>
[STAThread]
public static void Main(string[] args)
{
bool newMutex;
System.Threading.Mutex mutex = new System.Threading.Mutex(true, "{9F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}", out newMutex);
// Attempt aquire the mutex
if(newMutex)
{
// If we are able to aquire the mutex, it means the application is not running.
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Create the new tray icon
MyTray myTray = new MyTray();
Application.AddMessageFilter(myTray);
Application.Run();
// Release the mutex on exit
mutex.ReleaseMutex();
}
else
{
// If the aquire attempt fails, the application is already running
// so we broadcast a windows message to tell it to wake up.
NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_SHOWME, IntPtr.Zero, IntPtr.Zero);
}
}
}
}
internal class NativeMethods
{
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
}
public class MyTray : IMessageFilter
{
private NotifyIcon notifyIcon = new NotifyIcon();
private Form myForm = new Form();
public MyTray()
{
this.notifyIcon.Icon = System.Drawing.Icon.FromHandle(new System.Drawing.Bitmap(16,16).GetHicon());
this.notifyIcon.Visible = true;
this.notifyIcon.DoubleClick += delegate(object sender, EventArgs e) { ShowForm(); };
}
void ShowForm()
{
this.notifyIcon.Visible = false;
this.myForm.ShowDialog();
this.notifyIcon.Visible = true;
}
public bool PreFilterMessage(ref Message m)
{
// If the message is the 'show me' message, then hide the icon and show the form.
if(m.Msg == NativeMethods.WM_SHOWME)
{
if (!this.myForm.Visible)
{
ShowForm();
return true; // Filter the message
}
}
return false; // Forward the message
}
}
EDIT: I put together a sample which is closer to your scenario:
internal sealed class Program
{
static System.Threading.Mutex mutex = new System.Threading.Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
/// <summary>
/// Program entry point.
/// </summary>
[STAThread]
private static void Main(string[] args)
{
// Attempt aquire the mutex
if(mutex.WaitOne(TimeSpan.Zero, true))
{
// If we are able to aquire the mutex, it means the application is not running.
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Create the new tray icon
MyTray myTray = new MyTray();
Application.Run();
// Release the mutex on exit
mutex.ReleaseMutex();
}
else
{
// If the aquire attempt fails, the application is already running
// so we broadcast a windows message to tell it to wake up.
NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_SHOWME, IntPtr.Zero, IntPtr.Zero);
}
}
}
internal class NativeMethods
{
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
}
public class MyTray : Control
{
private NotifyIcon notifyIcon = new NotifyIcon();
public MyTray()
{
this.notifyIcon.Visible = true;
}
/// <summary>
/// This method listens to all windows messages either broadcast or sent to this control
/// </summary>
protected override void WndProc(ref Message m)
{
// If the message is the 'show me' message, then hide the icon and show the form.
if(m.Msg == NativeMethods.WM_SHOWME)
{
this.notifyIcon.Visible = false;
using (Form mainForm = new Form())
{
mainForm.ShowDialog();
this.notifyIcon.Visible = true;
}
}
else
{
base.WndProc(ref m);
}
}
}
EDIT: I found an example in C# that combines the mutex and windows message:
C# .NET Single Instance Application
A mutex is probably the best way to go. Combine this with a custom windows message which your application listens for to bring itself into focus (see VB.NET, VB6 and C# Interprocess communication via Window Messaging).
Check out this sample:
C# - Single Application Instance
I don't think you can raise events on another application's windows (even if they are the same executable file).
The way I would solve it though is to use some IPC mechanism to tell the running instance to open up the main window. The same IPC mechanism can also be used to determine whether another instance is running or not.
You can also take a look at the SwitchToThisWindow function.
When a second instance of the application starts you can just call that function with the main window handle of the first instance. You can get the main window handle with the Process.MainWindowHandle property.

Categories