Issue with NotifyIcon not disappearing on Winforms App - c#

I've got a .Net 3.5 C# Winforms app. It's got no GUI as such, just a NotifyIcon with a ContextMenu.
I've tried to set the NotifyIcon to visible=false and dispose of it in the Application_Exit event, as follows:
if (notifyIcon != null)
{
notifyIcon.Visible = false;
notifyIcon.Dispose();
}
The app gets to the code inside the brackets, but throws a null ref exception when it tries to set Visible = false.
I've read in a few places to put it in the form closing event, but that code never gets hit (maybe as I don't have a form showing as such?).
Where can I put this code so it actually works? If I don't put it in, I get the annoying lingering icon in the tray until you move the mouse over it.
Cheers.
EDIT
Just something extra I've noticed...........
I'm using ClickOnce in the app.........if I just exit the app via the ContextMenu on the NotifyIcon, no exception is logged.
Just when the Application_Exit event is fired after the applicaiton has checked for an upgrade here..
private void CheckForUpdate()
{
EventLogger.Instance.LogEvent("Checking for Update");
if (ApplicationDeployment.IsNetworkDeployed && ApplicationDeployment.CurrentDeployment.CheckForUpdate())
{
EventLogger.Instance.LogEvent("Update available - updating");
ApplicationDeployment.CurrentDeployment.Update();
Application.Restart();
}
}
Does this help?

On Windows 7, I had to also set the Icon property to null. Otherwise, the icon remained in the tray's "hidden icons" popup after the application had closed. HTH somebody.
// put this inside the window's class constructor
Application.ApplicationExit += new EventHandler(this.OnApplicationExit);
private void OnApplicationExit(object sender, EventArgs e)
{
try
{
if (trayIcon != null)
{
trayIcon.Visible = false;
trayIcon.Icon = null; // required to make icon disappear
trayIcon.Dispose();
trayIcon = null;
}
}
catch (Exception ex)
{
// handle the error
}
}

This code works for me, but I don't know how you are keeping your application alive, so... without further ado:
using System;
using System.Drawing;
using System.Windows.Forms;
static class Program
{
static System.Threading.Timer test =
new System.Threading.Timer(Ticked, null, 5000, 0);
[STAThread]
static void Main(string[] args)
{
NotifyIcon ni = new NotifyIcon();
ni.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
ni.Visible = true;
Application.Run();
ni.Visible = false;
}
static void Ticked(object o) {
Application.Exit();
}
}

This is what I'm doing in WPF.
I am using this in conjunction to David Anson's Minimize to tray sample app, which lets you hook up a tray icon to a window (you may have multiple windows open).
Just added this code to the constructor for MinimizeToTrayInstance.
_window.Closed += (s, e) =>
{
if (_notifyIcon != null)
{
_notifyIcon.Visible = false;
_notifyIcon.Dispose();
_notifyIcon = null;
}
};

Sometimes Application_Exit event can be raised several times
Just put notifyIcon = null; in the end
if (notifyIcon != null)
{
notifyIcon.Visible = false;
notifyIcon.Dispose();
notifyIcon = null;
}

This code worked for me
this.Closed += (a, b) =>
{
if (notifyIcon1 != null)
{
notifyIcon1.Dispose();
notifyIcon1.Icon = null;
notifyIcon1.Visible = false;
}
};

Have you overridden the dispose method of the object where you've initialised the notifyIcon to also dispose the notifyIcon?
protected override void Dispose(bool disposing)
{
if (disposing)
{
notifyIcon.Dispose();
notifyIcon = null;
}
base.Dispose(disposing);
}

before im sorry for my bad english.
if u use "end" for exit program. then dont close notify icon.
before u will close notifyicon later close form.
u need to use me.close() for run form closing
example
its work...
notifyIcon1.Icon = Nothing
notifyIcon1.Visible = False
notifyIcon1.Dispose()
Me.Close()
but its not work
End
or only
Me.Close()

Related

ManagementObjectSearcher causes re-entrancy issues for onclick handler

I am having an odd problem with protecting a section of code. My application is a tray app. I create a NotifyIcon inside my class (ApplicationContext). I have assigned a balloon click handler and a double click handler to the NotifyIcon object. there is also a context menu but I am not showing all code. Only important pieces.
public class SysTrayApplicationContext: ApplicationContext
{
private NotifyIcon notifyIcon;
private MainForm afDashBoardForm;
public SysTrayApplicationContext()
{
this.notifyIcon = new NotifyIcon();
this.notifyIcon.BalloonTipClicked += notifyIcon_BalloonTipClicked;
this.notifyIcon.MouseDoubleClick += notifyIcon_MouseDoubleClick;
// ... more code
}
Both handlers launch or create/show my form:
private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
openDashboard();
}
}
private void notifyIcon_BalloonTipClicked(object sender, EventArgs e)
{
openDashboard();
}
private void openDashboard()
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
else
{
log.Debug("Dashboard form does not exist, create it");
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
There is a problem with the above code. Maybe more than 1. Issue: it is possible to display 2 dashboard forms which is not what I want. If user double clicks on tray icon while balloon message is displaying causes a race condition in openDashboard. I can reproduce this easily. So I added a lock around the code in openDashboard code and, to my surprise, that did NOT prevent 2 dashboard forms from displaying. I should not be able to create 2 MainForms. Where am I going wrong here?
here is the updated code with lock statement:
private void openDashboard()
{
lock (dashBoardFormlocker)
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
else
{
log.Debug("Dashboard form does not exist, create it");
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
}
Note: lock object was added to the class and initialized in constructor.
private object dashBoardFormlocker;
UPDATE: Showing more code. this is how code gets started :
static void Main()
{
if (SingleInstance.Start())
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
XmlConfigurator.Configure();
// For a system tray application we don't want to create
// a form, we instead create a new ApplicationContext. The Run method takes
Application.Run(new SysTrayApplicationContext());
SingleInstance.Stop();
SingleInstance.Dispose();
}
}
}
UPDATE 2: Provide more code for clarity
public partial class MainForm : Form
{
public MainForm()
{
log.Trace("MainForm constructor...");
InitializeComponent();
// ... code not shown
this.label_OSVersion.Text = getOSFriendlyName();
// .. more code
}
private string getOSFriendlyName()
{
try
{
string result = string.Empty;
var mgmtObj = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().OfType<ManagementObject>()
select x.GetPropertyValue("Caption")).FirstOrDefault();
result = mgmtObj != null ? mgmtObj.ToString() : string.Empty;
OperatingSystem os = Environment.OSVersion;
String sp = os.ServicePack ?? string.Empty;
return !string.IsNullOrWhiteSpace(result) ? result + sp : "Unknown";
}
catch (System.Exception ex)
{
log.Error("Error trying to get the OS version", ex);
return "Unknown";
}
}
}
The main UI thread must always pump a message loop to support communication from COM components.
So when you do a blocking operation from the UI thread like locking or joining a thread, (EDIT: edited based on Peter Duniho's fix) the UI thread will enter an 'alertable' state, allowing COM to dispatch certain type of messages, which in turn can cause re-entrancy issues like in your scenario.
Look at the answer to this question (Why did entering a lock on a UI thread trigger an OnPaint event?) for a much more accurate explanation.
Looking at the source code of ManagementObjectSearcher.Get there is a lock (inside Initialize), and since you call it from the constructor of your form, it may lead to the second event triggering while the form's constructor has not finished. The assignment to the dashBoardFormlocker variable only happens after the constructor finishes, so that would explain why it was null on the second entry.
The moral of the story is never do blocking operations on the UI thread.
Without a good, minimal, complete code example that reliably reproduces the problem, it's impossible to know for sure what the problem is. But the guess by answerer tzachs seems reasonable. If so, you can fix your problem by changing your method to look like this:
private bool _dashboardOpen;
private void openDashboard()
{
if (_dashboardOpen)
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
}
else
{
log.Debug("Dashboard form does not exist, create it");
_dashboardOpen = true;
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
In that way, any re-entrant attempt to open the window will be detected. Note that you still need the check for null before actually activating; you can't activate a window that hasn't actually finished being created yet. The subsequent call to Show() will take care of activation anyway, so ignoring the activation in the re-entrant case shouldn't matter.

Kinect New Window Wpf

So I'm making a Kinect application using buttons, and to navigate the app, I'm making new windows for each button. I'm come across an issue I haven't been able to find any help at all on, and would appreciate any help.
So to open the new window, I'm using this:
private void button1_Click(object sender, RoutedEventArgs e)
{
//SUPPOSED to uninitialize the Kinect
UninitializeKinectSensor(this.Kinect;
//To open the new window
Window1 newwindow = new Window1();
newwindow.Show();
//To close the first window...
Close();
{
SO that one line is supposed to uninitialize the Kinect so it'll be free for the new window to use, but when it goes to the new window, the Kinect freezes. If I use the mouse to go back to the first window, it works on the first window again, which it shouldn't.
I also added in this line in the initialization phase
public Window1()
{
//Other init code is here, but this is the line I added. It doesn't seem to do anything.
InitializeKinectSensor(this.Kinect);
}
Any help is greatly appreciated!! I'm sure it's something simple and I just failed miserably haha XD
Do you really have to create a new window instead of using pages?
In your MainWindow you create a frame that takes all the window and use this frame to navigate between pages. This way, you'll keep the focus of the kinect in your whole application.
Depends alot on what UninitializeKinectSensor is actually doing. Just as a quick fix, though, you can try calling uninitialize on a background worker and see if that helps at all.
Instead of using the "Show()" use "ShowDialog()".It's better if you can create a static class or method to initialize and uninitialized kinect.
public static void start()
{
KinectSensor.KinectSensors.StatusChanged += kinectSensorsStatusChanged;
DiscoverSensor();
}
private static void kinectSensorsStatusChanged(object sender, StatusChangedEventArgs e)
{
KinectSensor oldSensor = Kinect;
if (oldSensor != null)
{
UninitializeKinect();
}
var status = e.Status;
if (Kinect == null)
{
//updateStatus(status);
if (e.Status == KinectStatus.Connected)
{
Kinect = e.Sensor;
DiscoverSensor();
}
}
else
{
if (Kinect == e.Sensor)
{
//updateStatus(status);
if (e.Status == KinectStatus.Disconnected ||
e.Status == KinectStatus.NotPowered)
{
Kinect = null;
sensorConflict = false;
DiscoverSensor();
}
}
}
}
private static DispatcherTimer readyTimer;
private static void UninitializeKinect()
{
if (speechRecognizer != null && Kinect != null)
{
Kinect.AudioSource.Stop();
Kinect.SkeletonFrameReady -= kinect_SkeletonFrameReady;
Kinect.SkeletonStream.Disable();
Kinect.Stop();
//this.FrameSkeletons = null;
speechRecognizer.RecognizeAsyncCancel();
speechRecognizer.RecognizeAsyncStop();
}
if (readyTimer != null)
{
readyTimer.Stop();
readyTimer = null;
}
}

Simple UI issue in winforms

I have a simple winforms application, on performing operations it shows a child window everytime. If I open a browser window (fully maximized) or some other window as usual
the application goes back with its childwindow, on clicking the exe which is in the taskbar
only the child window gets visible,but the application window doesn't come into view. I want to know how to show both the windows when I select it from taskbar.
childwindow is also a winform,whose toplevel property is set as true,apart from it nothing
is new(JUST BY CLICKING A BUTTON OR CELL IN GRID I CREATE AN OBJECT FOR THE FORM AND USES IT SHOW PROPERTY TO SHOW)
AlertMsgWindow _alertMsg;
void dataGridViewAlerts_MouseDoubleClick(object sender, MouseEventArgs e)
{
try
{
if (!string.IsNullOrEmpty(this.dataGridViewAlerts.getValue(0, this.dataGridViewAlerts.SelectedRow)))
{
this.dataGridViewAlerts.setCellImage(0, this.dataGridViewAlerts.SelectedRow, "NewsIconRead");
if (_alertMsg == null || _alertMsg.IsDisposed)
{
if (_alertMsg != null)
{
_alertMsg.onDeleteMessageRequest -= new DeleteMessage(_alertMsg_onDeleteMessageRequest);
_alertMsg.Dispose();
}
_alertMsg = new AlertMsgWindow();
_alertMsg.onDeleteMessageRequest += new DeleteMessage(_alertMsg_onDeleteMessageRequest);
}
_alertMsg.FillDetails(alertDetails[IDcollection[this.dataGridViewAlerts.SelectedRow]]);
if (!_alertMsg.Visible)
{
_alertMsg.Location = PointToScreen(new Point(this.Width / 4, -this.Height));
_alertMsg.Show(this);
}
if (onReadMessageReq != null)
onReadMessageReq(IDcollection[this.dataGridViewAlerts.SelectedRow]);
}
}
catch (Exception)
{ }
}
Note: THIS IS HAPPENING ONLY IN WINDOWS2000
I used a component named Dotnetmagic.dll,i dont know whether it causes the problem.can somebody helps me to solve this
I replaced these lines
_alertMsg.Location = PointToScreen(new Point(this.Width / 4, -this.Height));
With
_alertMsg.Left = x;
_alertMsg.Top = y;
and it solved my problem

How to use AxWebBrowser in console application

I wanna use AxWebBrowser on console application, but it give me following exception:
Exception of type 'System.Windows.Forms.AxHost+InvalidActiveXStateException' was thrown.
anybody please help me on this by any sample code for using AxWebBrowser in console application c# without any exeption ...
Yes, the [STAThread] attribute is required on your Main() method so that COM is initialized properly to make the main thread a Single Threaded Apartment. That's not all though, you will also need to pump a message loop. That's a requirement for an STA. Without one, WebBrowser cannot update its state or run its event handlers, you'll never get the DocumentCompleted event for example. You can get a message loop with Application.Run().
Your console application is now indistinguishable from a Windows Forms application. It is actually easier to get everything right by starting a new project with the Windows Forms application project template, then Project + Properties, Output type = Console Application. Edit the Application.Run() call in Program.cs so it doesn't create a form. It won't make dealing with Application.Run() any easier, consider a Timer to run code.
Add the STAThread attribute to your Main method.
However, you should not be using the "raw" ActiveX control.
Instead, add a reference to System.Windows.Forms.dll and use the WebBrowser class. (Yes, you can do that in a Console app)
Also, automating IE is not ideal. You should consider using the WebCLient class.
My class is as below but in the run time it gives me System.Windows.Forms.AxHost+InvalidActiveXStateException:
public class Browse
{
private static AxWebBrowser wBrowser;
public static Result StartBrowse(string url)
{
var validUri = (url.Contains("http://") ? url : "http://" + url);
wBrowser = new AxWebBrowser();
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(AxWebBrowser));
((ISupportInitialize) (wBrowser)).BeginInit();
wBrowser.OcxState = ((AxHost.State)(resources.GetObject("wBrowser.OcxState")));
wBrowser.NewWindow2 += wBrowser_NewWindow2;
wBrowser.NewWindow3 += wBrowser_NewWindow3;
wBrowser.DocumentComplete += wBrowser_DocumentComplete;
wBrowser.DownloadComplete += wBrowser_DownloadComplete;
if (string.IsNullOrEmpty(html) || validUri != url)
{
object empty = System.Reflection.Missing.Value;
wBrowser.Silent = true;
wBrowser.Navigate(validUri, ref empty, ref empty, ref empty, ref empty);
}
return null;
}
static void wBrowser_DownloadComplete(object sender, EventArgs e)
{
doAlgorithm();
}
static void wBrowser_DocumentComplete(object sender, DWebBrowserEvents2_DocumentCompleteEvent e)
{
doAlgorithm();
}
static void wBrowser_NewWindow3(object sender, DWebBrowserEvents2_NewWindow3Event e)
{
e.cancel = true;
}
static void wBrowser_NewWindow2(object sender, DWebBrowserEvents2_NewWindow2Event e)
{
e.cancel = true;
}
}
First, the thread in which the control is hosted must be in single-threaded apartment, you can either put the STAThread in your Main method, or create a separated Thread like this:
var thread = new Thread(() =>
{
//My code
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join(); //Wait for thread termination
Second, you must start a message loop:
while (true) //Put some exit condition
System.Windows.Forms.Application.DoEvents();
Third the control must be hosted in a visible form. The form must be visible just once, so to avoid "flickering", you can write this code:
var browser = new AxWebBrowser();
var hostForm = new Form();
//Set form 0 size, without any control box / title / icon
hostForm.Width = 0;
hostForm.Height = 0;
hostForm.ShowInTaskbar = false;
hostForm.ControlBox = false;
hostForm.ShowIcon = false;
hostForm.MinimizeBox = false;
hostForm.MaximizeBox = false;
//Add browser control
hostForm.Controls.Add(browser);
//Show and immediately hide
hostForm.Show();
hostForm.Hide();
Finally you might want to disable the "click" sound (How to disable click sound in WebBrowser Control)
The final code:
class Program
{
[STAThread]
static void Main(string[] args)
{
URLSecurityZoneAPI.InternetSetFeatureEnabled(URLSecurityZoneAPI.InternetFeaturelist.DISABLE_NAVIGATION_SOUNDS, URLSecurityZoneAPI.SetFeatureOn.PROCESS, true);
var browser = new AxWebBrowser();
var hostForm = new Form();
hostForm.Width = 0;
hostForm.Height = 0;
hostForm.ShowInTaskbar = false;
hostForm.ControlBox = false;
hostForm.ShowIcon = false;
hostForm.MinimizeBox = false;
hostForm.MaximizeBox = false;
hostForm.Controls.Add(browser);
hostForm.Show();
hostForm.Hide();
browser.DocumentComplete += delegate(object sender, DWebBrowserEvents2_DocumentCompleteEvent e)
{
var doc = (IHTMLDocument3)browser.Document;
Console.WriteLine(doc.documentElement.innerHTML);
};
browser.Navigate("www.google.com");
while (true)
System.Windows.Forms.Application.DoEvents();
}
}

Any way to create a hidden main window in C#?

I just want a c# application with a hidden main window that will process and respond to window messages.
I can create a form without showing it, and can then call Application.Run() without passing in a form, but how can I hook the created form into the message loop?
Is there another way to go about this?
Thanks in advance for any tips!
Excellent! That link pointed me in the right direction. This seems to work:
Form f = new Form1();
f.FormBorderStyle = FormBorderStyle.FixedToolWindow;
f.ShowInTaskbar = false;
f.StartPosition = FormStartPosition.Manual;
f.Location = new System.Drawing.Point(-2000, -2000);
f.Size = new System.Drawing.Size(1, 1);
Application.Run(f);
To keep it from showing up in Alt-Tab, you need it to be a tool window. Unfortunately, this prevents it from starting minimized. But setting the start position to Manual and positioning it offscreen does the trick!
In the process of re-writing a VC++ TaskTray App, in C# .NET, I found the following method truly workable to achieve the following.
No initial form dislayed at startup
Running Message Loop that can be used with Invoke/BeginInvoke as needed as IsWindowHandle is true
The steps I followed:
Used an ApplicationContext in Application.Run() Instead of a form. See http://www.codeproject.com/Articles/18683/Creating-a-Tasktray-Application for the example I used.
Set the Form's ShowInTaskbar property to true within the GUI Designer. (This seems counter productive but it works)
Override the OnLoad() method in your Form Class setting Visible and ShowInTaskbar to false as shown below.
protected override void OnLoad(EventArgs e)
{
Visible = false;
ShowInTaskbar = false;
base.OnLoad(e);
}
I know this is old question, but it ranks well in google, so I will provide my solution anyway.
I do two things:
private void Form_Load(object sender, EventArgs e)
{
Opacity = 0;
}
private void Form_Shown(object sender, EventArgs e)
{
Visible = false;
Opacity = 100;
}
The best way is to use the following 1-2 lines in the constuctor:
this.WindowState = FormWindowState.Minimized;
this.ShowInTaskbar = false; // This is optional
You can even set the Minimized property in the VS Property window.
You can create a class that inherits from System.Windows.Forms.NativeWindow (which provides basic message loop capability) and reference the Handle property in its constructor to create its handle and hook it into the message loop. Once you call Application.Run, you will be able to process messages from it.
I solved the problem like this:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Main main = new Main();
Application.Run();
//Application.Run(new Main());
}
This code resides in the Program.cs file, and you can see the original Application.Run method call commented out. I just create a Main class object (my main form class is named Main) and start application message loop w/o any parameters. This starts the application, initializes any form components but doesn't show the form.
Note: you have to have some method to get your window showing (like system tray icon, hotkey or timer or anything you might like).
public partial class Form1 : Form
{
private bool _isApplicationRun;
public Form1(bool applicationRun)
{
InitializeComponent();
_isApplicationRun = applicationRun;
}
protected override void SetVisibleCore(bool value)
{
if (_isApplicationRun)
{
_isApplicationRun = false;
base.SetVisibleCore(false);
return;
}
base.SetVisibleCore(value);
}
}
static class Program
{
[STAThread]
static void Main()
{
Application.Run(new Form1(true));
}
}
Why can't you just pass the form when you call Application.Run? Given that it's clearly a blocking call, on what event do you want to show the form? Just calling form.Show() should be enough.
Using Kami's answer as an inspiration, I created a more complete concept. If you use this solution, don't ever show the hidden window. If you do, the user might close it and then you've lost the ability to control the application exit in an orderly way. This approach can be used to manage a Timer, NotifyIcon, or any other component that is happy living on an invisible form.
using System;
using System.Windows.Forms;
namespace SimpleHiddenWinform
{
internal class HiddenForm : Form
{
private Timer _timer;
private ApplicationContext _ctx;
public HiddenForm(ApplicationContext ctx)
{
_ctx = ctx;
_timer = new Timer()
{
Interval = 5000, //5 second delay
Enabled = true
};
_timer.Tick += new EventHandler(_timer_Tick);
}
void _timer_Tick(object sender, EventArgs e)
{
//tell the main message loop to quit
_ctx.ExitThread();
}
}
static class Program
{
[STAThread]
static void Main()
{
var ctx = new ApplicationContext();
var frmHidden = new HiddenForm(ctx);
//pass the application context, not the form
Application.Run(ctx);
}
}
}
Form1 f1=new Form1();
f1.WindowState = FormWindowState.Minimized;
f1.ShowInTaskbar = false;
in the Form1 code file add this.Visible = false; to the constructor.
This will hide the window but it will flash for a sec before it is hidden. Alternatively you can write your own Application.Run command.
for more info http://social.msdn.microsoft.com/forums/en-US/winforms/thread/dece45c8-9076-497e-9414-8cd9b34f572f/
also you may want to set the this.ShowInTaskbar to false.
You should look at creating a 'service' as this is an application without a form.
See http://support.microsoft.com/kb/816169

Categories

Resources