Systray Application Exiting - c#

I have a small, simple C# application which updates an icon in the system tray. I use it to graphically show the CPU usage. The application works great. I keep the Window hidden and don't show it in the taskbar so it doesn't get in the way.
My issue is that it will run great for a while. Often several hours. But then it will mysteriously quit. No warnings. Nothing. The icon is just gone and the program is no longer running. I have tested the program in the debugger under varying conditions, so I don't think that is it. Is there something obvious I am missing? If the program encounters an error and quits should I be expecting a message if the Form is hidden? Is there some "keep-alive" message or something that I need to handle?
Here is the relevant section of code:
public Form1()
{
InitializeComponent();
trayIcon = new NotifyIcon();
trayIcon.Text = "CPU Utilization";
trayIcon.Icon = new Icon(SystemIcons.Application, 40, 40);
trayIcon.Visible = true;
update = new Thread(new ThreadStart(UpdateCPU));
update.Start();
}
protected override void OnLoad(EventArgs e)
{
Visible = false;
ShowInTaskbar = false;
base.OnLoad(e);
}
private void UpdateCPU()
{
Bitmap bm = new Bitmap(32, 32);
Graphics g = Graphics.FromImage(bm);
while (true)
{
g.FillRectangle(new SolidBrush(c3), 17, 17, 15, 15);
trayIcon.Icon = System.Drawing.Icon.FromHandle(bm.GetHicon());
Thread.Sleep(1000);
}
}
Any help would be greatly appreciated!

I would suggest you add an Unhandled Exception Handler
Global Exception Handling for winforms control
An Exception is likely being thrown, causing your program to exit.
Then, introduce logging to log what the Exception was. Personally I prefer NLog.
I'm a bit surprised that you can update trayIcon from a non-UI thread without receiving a cross-thread exception.

The documentation seems to agree with Viktor Latypov's comment, you should be doing something like this:
Icon newIcon = Icon.FromHandle(bm.GetHicon());
trayIcon.Icon = newIcon;
DestroyIcon(newIcon.Handle);

Related

Image resource release in c# application

I am doing an image processing project using Windows Forms (c#). You can see the design of my application below.
What does this app do : take the original image, create a copy and modify the copy.
My app is working well but, if I process the same original image another time without closing the app, I get an error due to (I think) the display of the modified image. I think that the display on the bottom right corner uses the resources of the image and, when I try to modify it again, the system considers that the image is already used by another program so it can't be modified.
So my question is : "How can I stop using the modified image if the user clicks on PROCESS again ?"
I tried to use the .Dispose() method but it didn't work.
Code of the c# function linked to the PROCESS button :
private async void button8_Click(object sender, EventArgs e)
{
// start the waiting animation
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
if (csI != csP)
{
MessageBox.Show("The selected profil does not match the selected image. Colorspaces are different.", "WARNING",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
pictureBox2.Image = null;
if (checkBox2.Checked == false)
{
rendered = false;
button8.Enabled = false;
await Task.Run(() => wrapper.DominantColors(trackBar1.Value, rendered));
//wrapper.DominantColors(trackBar1.Value, rendered);
}
else
{
rendering = comboBox1.Text;
string outputImage = wrapper.Link(rendering, bpc);
rendered = true;
button8.Enabled = false;
await Task.Run(() => wrapper.DominantColors(trackBar1.Value, rendered));
//wrapper.DominantColors(trackBar1.Value, rendered);
}
// re-enable things
button8.Enabled = true;
progressBar1.Visible = false;
MessageBox.Show("processing done");
Bitmap bit = new Bitmap(imgDstPath);
float WidthImg = bit.Width;
float HeightImg = bit.Height;
float alpha = WidthImg / pictureBox2.Width;
float beta = HeightImg / pictureBox2.Height;
alpha = Math.Max(alpha, beta);
float newWidthf = WidthImg / alpha;
float newHeightf = HeightImg / alpha;
int newHeight = (int)newHeightf;
int newWidth = (int)newWidthf;
pictureBox2.ClientSize = new Size(newWidth, newHeight);
pictureBox2.Image = bit;
pictureBox2.SizeMode = PictureBoxSizeMode.StretchImage;
}
}
If possible, I'd like to clear the use of the resources when I click on the process button.
Thank you in advance for your help
The basic rule is that all objects you create that implements IDisposable need to be disposed. When writing winforms apps all controls added to a forms are disposed when the form is disposed. But whenever you change things you might need to handle disposal yourself.
For example:
pictureBox2.Image = bit;
If pictureBox2.Image is already set to something you need to ensure that it is disposed.
var oldImage = pictureBox2.Image;
pictureBox2.Image = bit;
oldImage.Dispose();
I'm not sure this is the actual problem you are having, your example code is insufficient to make that determination. To discover this you need to debug your program! Start by examining your exceptions, does it fail when opening a file or some other resource? Where was that resource created? where is it disposed? Perhaps even use a memory debugger to produce a list of all objects that are alive to see if there are any suspicious objects kept around. Will disposal correctly occur if any arbitrary code throws an exception?
It is sometimes useful to check the identity of objects in the debugger, to see if it has been switched out, or see what object your breakpoint was triggered in. You can rightclick an object in the watch panel in visual studio and select "Make ObjectId", this will associate a number with the object that appears at the end of the value.
If anyone in the future wants to know the solution I found, here it is :
At the beginning of the PROCESS function I added those simple lines :
if (pictureBox2.Image != null)
{
pictureBox2.Image.Dispose();
pictureBox2.Image = null;}

"shutdown.exe" destroys application settings

I have a one-window WPF application (Win8.1 / .net4.7), the Window.Closing-Event is unhandled, the Window.Closed-Event is handled as follows:
private void Window_Closed(object sender, EventArgs e)
{
Properties.Settings.Default.WinMainLocationX = this.Left; // ok
Properties.Settings.Default.WinMainLocationY = this.Top; // ok
Properties.Settings.Default.WinMain_size = new Size(this.Width, this.Height); // crucial setting
Properties.Settings.Default.WinMain_state = this.WindowState; // ok
Properties.Settings.Default.Save();
}
I'm closing the app (at this point always in idle state) once a day by a batch file containing C:\WINDOWS\system32\shutdown.exe /s /t 20 and nothing afterwards. By this the computer shuts down properly. The parameters of shutdown.exe can be seen by command line input of shutdown /?.
Problem: Every 7 or 8 days the window size gets corrupted in a way that the application (after having started in the morning) looks like this:
How can I protect my application settings from interference by shutdown.exe?
I think the problem is storing the settings while the application window is minimized. Width and Height of the window will be 0 in this case.
You can use the RestoreBounds property of your window to get its restored size independent of its current state:
Properties.Settings.Default.WinMainLocationX = this.RestoreBounds.Left;
Properties.Settings.Default.WinMainLocationY = this.RestoreBounds.Top;
Properties.Settings.Default.WinMain_size = new Size(this.RestoreBounds.Width, this.RestoreBounds.Height);
Properties.Settings.Default.WinMain_state = this.WindowState;
Some answers to this question show another approach using the WinAPI functions GetWindowPlacement / SetWindowPlacement:
.NET WPF Remember window size between sessions
Adding Environment.Exit(0)has solved the issue. I can imagine the cause of the problem was that the Window.Closed-Handler has been reached twice.
private void Window_Closed(object sender, EventArgs e)
{
Properties.Settings.Default.WinMainLocationX = this.Left; // ok
Properties.Settings.Default.WinMainLocationY = this.Top; // ok
Properties.Settings.Default.WinMain_size = new Size(this.Width, this.Height); // crucial setting
Properties.Settings.Default.WinMain_state = this.WindowState; // ok
Properties.Settings.Default.Save();
Environment.Exit(0);
}

Directx device doesn't initialize for window, Program hang

I've tried following several tutorials an have seem to be having trouble.
I've got an existing program that i'm trying to add a directx window to as an additional popup forum that will run as a child to the main application form.
Here is the windows form class:
public partial class DxWindow : Form
{
Device device;
public DxWindow()
{
InitializeComponent();
initDevice();
}
private void initDevice()
{
MessageBox.Show("hello");
PresentParameters pp = new PresentParameters();
pp.Windowed = true;
pp.SwapEffect = SwapEffect.Discard;
device = new Device(0, DeviceType.Hardware, this, CreateFlags.HardwareVertexProcessing, pp);
}
private void Render()
{
//render stuff
}
private void DxWindow_Paint(object sender, PaintEventArgs e)
{
Render();
}
}
and here is where i initialize the form (from a UI button in main window)
private void toolStripButton3_Click_1(object sender, EventArgs e)
{
if (DirectxWindow == null)
{
DirectxWindow = new DxWindow();
DirectxWindow.Show();
}
}
When i run the program and click the button. it seems create the form in memory but never shows up. when i step through it in the debugger, it gets to "DirectxWindow = new DxWindow();" and then automatically jumps out of break mode and continues running with the main window frozen and no new Dxwindow().
when i break execution is seems to still be on "DirectxWindow = new DxWindow();"
Also, "MessageBox.Show("hello");" in the DxWindow constructor is never called"
Edit: I've deduced that as soon as it hits "PresentParameters pp = new Microsoft.DirectX.Direct3D.PresentParameters();" the application becomes unresponsive without throwing any errors.
Turns out my problem was needing to use
<startup useLegacyV2RuntimeActivationPolicy="true">
in the "App.config" File
Solution was found here: Mixed mode assembly is built against version 'v1.1.4322'
Although i never got the error as described by the OP. i simply had this problem as described in the comments:
"Thank you!!!! This is the weirdest problem I'd ever encountered. In VS 2012 .Net 4.0 my application would just hang the moment I initialized any variable of a type related to this DLL. I'd never seen anything like it. Couldn't find anything about the problem until I found this!" – Quinxy von Besiex

How to fix message boxes appearing behind splash screen?

My WinForms app's main window is slow to load (up to 20 seconds, depending on arguments), so it needs a splash screen.
The main window constructor is slow because it exercises thousands of lines of code (some of it beyond my influence). Sometimes this code pops up message boxes.
I've tried two splash screen designs, they each have problems. Any better ideas?
Splash screen with BackgroundWorker
static void Main(string[] args)
{
var splash = !args.Contains("--no-splash");
if (splash)
{
var bw = new BackgroundWorker();
bw.DoWork += (sender, eventArgs) => ShowSplash();
bw.RunWorkerAsync();
}
var app = new FormMain(args); // slow. sometimes opens blocking message boxes.
Application.Run(app);
}
private static void ShowSplash()
{
using (var splash = new FormSplash())
{
splash.Show();
splash.Refresh();
Thread.Sleep(TimeSpan.FromSeconds(2));
}
}
Problems:
Splash screen sometimes expires before main window open (user thinks app has crashed)
Main window sometimes minimises when splash closes.
Splash screen with WindowsFormsApplicationBase
sealed class App : WindowsFormsApplicationBase
{
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new FormSplash();
}
protected override void OnCreateMainForm()
{
// slow. sometimes opens blocking message boxes.
this.MainForm = new FormMain(this.CommandLineArgs);
}
}
Problems:
Any MessageBoxes opened appear behind splash screen, silently. User won't notice it and thinks app is stuck.
If splash screen is 'always on top', the message box is inaccessible and unclickable.
I agree with Hans Passant in that the code needs to be re-evaluated as the design seems incorrect.
As for the problem at hand, you should be able to resolve this by creating your own instance of a messageBox.
I tested using this code;
public DialogResult TopMostMessageBox(string message, string title, MessageBoxButtons button, MessageBoxIcon icon)
{
return DisplayMessageBox(message, title, button, icon);
}
public DialogResult DisplayMessageBox(string message, string title, MessageBoxButtons buttons, MessageBoxIcon icon)
{
DialogResult result;
using (var topmostForm = new Form {Size = new System.Drawing.Size(1, 1), StartPosition = FormStartPosition.Manual})
{
var rect = SystemInformation.VirtualScreen;
topmostForm.Location = new System.Drawing.Point(rect.Bottom + 10, rect.Right + 10);
topmostForm.Show();
topmostForm.Focus();
topmostForm.BringToFront();
topmostForm.TopMost = true;
result = MessageBox.Show(topmostForm, message, title, buttons, icon);
topmostForm.Dispose();
}
//You might not need all these properties...
return result;
}
//Usage
TopMostMessageBox("Message","Title" MessageBoxButtons.YesNo, MessageBoxIcon.Question)
Again, I need to stress that I agree that the original code needs to be re-factored and am only providing a possible solution to the question.
Hope this helps?
You can implement our own message box and use TopMost property, with TopMost you will get message in-front of splash screen loader.
More about topmost: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.topmost.aspx
In the end, moved the slow code from the constructor to a handler for the OnShown event.
Used WindowsFormsApplicationBase for splash screen as Hans Passant suggested, carefully checked the remaining constructor code to make sure it'll never open an message boxes.

System tray icon with c# Console Application won't show menu

I've got a small C# (.NET 4.0) Console Application that I'd like the user to be able to interact by showing a menu when they right-click the System Tray icon. I can add an icon to the Tray with no problems, but I just cannot get the menu to appear. I'm using the following code:
NotifyIcon trayIcon = new NotifyIcon();
trayIcon.Text = "TestApp";
trayIcon.Icon = new Icon(SystemIcons.Application, 40, 40);
ContextMenu trayMenu = new ContextMenu();
trayMenu.MenuItems.Add("Blah", item1_Click);
trayMenu.MenuItems.Add("Blah2", item1_Click);
trayMenu.MenuItems.Add("Blah3", item1_Click);
trayIcon.ContextMenu = trayMenu;
trayIcon.Visible = true;
... which puts the icon in the tray. However, right-clicking the icon does nothing. I've tried various permutations of MenuItems.Add, but nothing will make the menu appear. I'm sure I'm missing something simple - any ideas what?
Try adding this after you create the icon:
Application.Run()
Note that this method will not return, so you can't do anything after calling it. This means that you'll have to do all your other work in a separate thread.
What happens is that the OS sends your application a message telling it that the tray icon has been right-clicked, but the tray icon code never sees it (because these messages are processed by Application.Run) and so can't respond by opening the menu.
Concerning Application.Run(), this is an alternative to placing all the other code in another thread would be to create the NotifyIcon, menu, events, etc on a thread other than the main thread.
This should include Application.Run() as this allows the standard application message loop to work on the current thread. Then since the events were created on the same thread, the Application.Exit() can be used to close out the notification messaging but still allow the main thread to continue. Here's a small example for a console app...
class Program
{
public static ContextMenu menu;
public static MenuItem mnuExit;
public static NotifyIcon notificationIcon;
static void Main(string[] args)
{
Thread notifyThread = new Thread(
delegate()
{
menu = new ContextMenu();
mnuExit = new MenuItem("Exit");
menu.MenuItems.Add(0, mnuExit);
notificationIcon = new NotifyIcon()
{
Icon = Properties.Resources.Services,
ContextMenu = menu,
Text = "Main"
};
mnuExit.Click += new EventHandler(mnuExit_Click);
notificationIcon.Visible = true;
Application.Run();
}
);
notifyThread.Start();
Console.ReadLine();
}
static void mnuExit_Click(object sender, EventArgs e)
{
notificationIcon.Dispose();
Application.Exit();
}
}
Here is the solution:
You have to use Application.Run() because events of gui in console mode not working.
But you can use this solution:
var task = System.Threading.Tasks.Task.Factory.StartNew(() => ShowTrayIcon());
void ShowTrayIcon()
{
some code with tray icon ...
}
This will start your setup of try icon in new thread ...
Did you add the event-handler for tray Icon mouse click?
trayIcon .MouseDown += new MouseEventHandler(trayIcon_MouseDown);
create context menu and do as following inside the trayIcon_MouseDown function
private void trayIcon_MouseDown (object sender,MouseEventArgs e)
{
//add you menu items to context menu
contextMenu.Items.Add(item);
contextMenu.IsOpen = true;
}
Try this. Think this will help you.

Categories

Resources