I've googled this problem for the past week, it's killing my peace! Please help... EventArrivedEventHandler is stuck in a loop, and if I stop it, then it won't catch events. But when I use a handler method, the thread is still concentrating on the loop, and won't give attention to the new form I'm trying to make in the handler! Strange thing is, if I just use something small, like a MessageBox, it doesn't cause an issue, just trying to instantiate a form causes the buttons to NOT draw. Then shortly after the program stops responding. In case you're wondering where the form code is, it's just a standard form made by .NET, that works everywhere else in the code except for in the event handler.
Thanks!
class MainClass
{
public static void Main()
{
TaskIcon taskbarIcon;
EventWatch myWatcher;
taskbarIcon = new TaskIcon();
taskbarIcon.Show();
myWatcher = new EventWatch();
myWatcher.Start();
Application.Run();
}
}
public class TaskIcon
{
public void Show()
{
NotifyIcon notifyIcon1 = new NotifyIcon();
ContextMenu contextMenu1 = new ContextMenu();
MenuItem menuItem1 = new MenuItem();
MenuItem menuItem2 = new MenuItem();
contextMenu1.MenuItems.AddRange(new MenuItem[] { menuItem1, menuItem2 });
menuItem1.Index = 0;
menuItem1.Text = "Settings";
menuItem1.Click += new EventHandler(notifyIconClickSettings);
menuItem2.Index = 1;
menuItem2.Text = "Exit";
menuItem2.Click += new EventHandler(notifyIconClickExit);
notifyIcon1.Icon = new Icon("app.ico");
notifyIcon1.Text = "Print Andy";
notifyIcon1.ContextMenu = contextMenu1;
notifyIcon1.Visible = true;
}
private static void notifyIconClickSettings(object Sender, EventArgs e)
{
MessageBox.Show("Settings Here");
}
private static void notifyIconClickExit(object Sender, EventArgs e)
{
//taskbarIcon.Visible = false; // BONUS QUESTION: Why can't I hide the tray icon before exiting?
Application.Exit();
}
}
public class EventWatch
{
public void Start()
{
string thisUser = WindowsIdentity.GetCurrent().Name.Split('\\')[1];
WqlEventQuery query = new WqlEventQuery();
query.EventClassName = "__InstanceCreationEvent";
query.Condition = #"TargetInstance ISA 'Win32_PrintJob'";
query.WithinInterval = new TimeSpan(0, 0, 0, 0, 1);
ManagementScope scope = new ManagementScope("root\\CIMV2");
scope.Options.EnablePrivileges = true;
ManagementEventWatcher watcher = new ManagementEventWatcher(scope, query);
watcher.EventArrived += new EventArrivedEventHandler(showPrintingForm);
watcher.Start();
}
void showPrintingForm(object sender, EventArrivedEventArgs e)
{
// MessageBox.Show("This will draw just fine");
Form1 myForm;
myForm = new Form1();
myForm.Show(); // This causes a hangup
}
}
My guess would be that the ManagementEventWatcher calls the EventArrived handler from a different thread than the UI thread. Then your showPrintingForm is executed on that thread and accessing UI from a different thread than the UI thread is bad. You need to marshal your code back onto the UI thread.
Related
Async/Await is nice way to code responsive GUI but as I suppose cheese here isn't free. I think only the way to realize Await statement is a memory stack. Each time you call Await, there is a new pointer put in memory stack. Therefore if you call to frequently Await and result delays, you get stack overflow (that is the keyword you can't search here :))
Let's suppose we check some connection status asynchronously periodically.
Create a simple windows forms project with Form1 and form code is here.
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace testasync
{
public partial class Form1 : Form
{
int CallCount = 0;
int CalBackCount = 0;
Random r = new Random();
private Button butStart = new Button();
private Button butStop = new Button();
private Timer timer1 = new Timer();
private Label labCalls =new Label();
private Label labReturns = new Label();
public Form1()
{
this.ClientSize = new System.Drawing.Size(450, 200);
butStart.Location = new System.Drawing.Point(51, 38);
butStart.Size = new System.Drawing.Size(139, 47);
butStart.Text = "Start";
butStart.Click += new System.EventHandler(this.butStart_Click);
butStop.Location = new System.Drawing.Point(237, 38);
butStop.Size = new System.Drawing.Size(139, 47);
butStop.Text = "Stop";
butStop.Click += new System.EventHandler(this.butStop_Click);
labCalls.Location = new System.Drawing.Point(48, 149);
labCalls.Size = new System.Drawing.Size(100, 23);
labReturns.Location = new System.Drawing.Point(237, 149);
labReturns.Size = new System.Drawing.Size(100, 23);
Controls.Add(this.labCalls);
Controls.Add(this.labReturns);
Controls.Add(this.butStart);
Controls.Add(this.butStop);
timer1.Interval = 5;
timer1.Tick += new System.EventHandler(this.timer1_Tick);
}
private void butStart_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
}
private void butStop_Click(object sender, EventArgs e)
{
timer1.Enabled = false;
}
private async Task myWork()
{
CallCount++;
await Task.Delay(2000);
if (r.Next(100)>10) //Check Connection
await Task.Delay(100000000);
CalBackCount++;
}
private async void timer1_Tick(object sender, EventArgs e)
{
labCalls.Text = CallCount.ToString();
await myWork();
labReturns.Text = CalBackCount.ToString();
}
}
}
instead of checking connection I use random check to wait long time for connection response.
If we run the project and hit start button, we'll see that memory consumption by the application rises by time. Therefore I suppose this is not the right way to use Await statement, I think better way is use threading with delegates or nice component BackgroundWorker with progress events. Or maybe there are better solutions?
Your assumptions on how async-await works aren't correct.
Each asynchronous method will be converted in a state machine that splits the method into several methods (the parts between the start, awaits and end) and orchestrates its execution.
A rogue asynchronous method will more likely exhaust the heap than the stack.
My WPF application shows a window and when the user clicks a button, it begins to run its tasks and minimizes to a tray item in the notification area with a context menu where I would like the user to be able to cancel the operation.
The context menu worked before using a BackgroundWorker, however, cancellation did not. Since I've implemented a background worker,the context menu does not appear once the .runworkerasync() method has run.
My Notify Icon:
public NotifyIcon myNotifyIcon;
When my application runs I set it up like this:
private void setup_NotifyIcon()
{
myNotifyIcon = new NotifyIcon();
setTrayIcon();
myNotifyIcon.MouseDown += new MouseEventHandler(myNotifyIcon_MouseDown);
var menuItemCancel = new MenuItem("Cancel Parsing");
var contextMenu = new ContextMenu();
menuItemCancel.Click += new System.EventHandler(this.menuItemCancel_Click);
contextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { menuItemCancel });
myNotifyIcon.ContextMenu = contextMenu;
}
private void menuItemCancel_Click(object Sender, EventArgs e)
{
//do something
}
void myNotifyIcon_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
//do something
}
}
Then when the user clicks the button:
worker.RunWorkerAsync();
Why won't myNotifyIcon.MouseDown += new MouseEventHandler(myNotifyIcon_MouseDown); trigger the context menu?
The solution turned out to be a threading issue as suggested by Sebastian in the comments.
The key was to start the icon on another thread using Application.Run() and to make the icon visible within that code.
Once this was done, right-clicking on the icon worked, as did the cancellation functionality being handled.
private void setup_NotifyIcon()
{
Thread notifyThread = new Thread(
delegate ()
{
myNotifyIcon = new NotifyIcon();
setTrayIcon();
myNotifyIcon.MouseDown += new MouseEventHandler(myNotifyIcon_MouseDown);
mnuCancel = new MenuItem("Cancel Parsing");
menu = new ContextMenu();
mnuCancel.Click += new System.EventHandler(menuItemCancel_Click);
menu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { mnuCancel });
myNotifyIcon.ContextMenu = menu;
myNotifyIcon.BalloonTipIcon = System.Windows.Forms.ToolTipIcon.Info; //Shows the info icon so the user doesn't thing there is an error.
myNotifyIcon.BalloonTipText = "The P6 Parser will minimize to the system tray while working.";
myNotifyIcon.BalloonTipTitle = "Processing...";
myNotifyIcon.Visible = true;
myNotifyIcon.ShowBalloonTip(500);
myNotifyIcon.Visible = true;
System.Windows.Forms.Application.Run();
});
notifyThread.Start();
}
in Program.cs change the Attribute [STAThread] to [MTAThread]
Please in my code i try to catch win32.systemevents.sessionended event to procede with saving of my app data by the end of session in case the app is not closed manually .. some time ago this has been workin and now that i had my project grown a lil it is not any more..? i have tried to find something meaningfull for few days but found nothing really.. when i try to catch another systemevent like MonitorResolutionChanged it works well but this one not. I have also tried to register within the mainWindow (app form ..), nothing :-( Please any idea?
I think all the relevant information should be in the beginning till void Main but i put it all in case you would need or want to see more .. Thanx a lot Tomas
My code:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading;
using MovablePython; // my own class within this project
using Avn; //my own referenced assembly
namespace DirDist
{
class Program
{
private static string appGuid = "Cddbserviceman";
private static System.Windows.Forms.ContextMenu nIMenu;
internal static System.Windows.Forms.NotifyIcon notifyIcon1;
private static MenuItem showItem;
public static MenuItem justCDsItem;
private static MenuItem searchItem;
private static MenuItem settingsItem;
private static MenuItem quitItem;
internal static Form1 mainWindow;
private static Hotkey hk;
internal static Registration.LicenceState mode; // app mode - registered/trial/blocked/demaged ..
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
using (Mutex mutex = new Mutex(false, appGuid))
{
if (!mutex.WaitOne(0, false))
{
MessageBox.Show("CDDB is already running on your machine \n (Check status bar for access ..)");
return;
}
GC.Collect();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
mode = Registration.Startup();
Program.mainWindow = new Form1();
mainWindow.Activate();
//mainWindow.Validate();
//mainWindow.Update();
mainWindow.Visible = false;
PutIcon();
//Microsoft.Win32.SystemEvents.SessionEnded += SystemEvents_SessionEnded;
Microsoft.Win32.SystemEvents.SessionEnded += new Microsoft.Win32.SessionEndedEventHandler(SystemEvents_SessionEnded);//**zkousime zda funguje pro hibernaci ..
RegisterHotKey(true);
Application.Run();
}
}
static void SystemEvents_SessionEnded(object sender, Microsoft.Win32.SessionEndedEventArgs e)
{
//MessageBox.Show("SessionEnded fired");
RegisterHotKey(false);
notifyIcon1.Visible = false;
notifyIcon1.Dispose();
notifyIcon1 = null;
if (!mainWindow.dBSaved) mainWindow.SaveDb(Form1.settings.dBPath);
if (mainWindow.index != null) mainWindow.SaveIndex(Form1.settings.indexPath);
Microsoft.Win32.SystemEvents.SessionEnded -= new Microsoft.Win32.SessionEndedEventHandler(SystemEvents_SessionEnded);
mainWindow.Close();
}
// zaregistruje globalni hotkey ctrl+shift+F Pro hledani
private static void RegisterHotKey(bool active)
{
if (!active)
{
if (hk != null) hk.Unregister();
}
else
{
if(hk ==null) hk = new Hotkey();
hk.KeyCode = Keys.F;
//hk.Windows = true;
hk.Shift = true;
hk.Control = true;
//hk.Pressed += delegate { Console.WriteLine("Windows+1 pressed!"); };
hk.Pressed += delegate { searchItemClick(new object(), new EventArgs()); };
if (hk.GetCanRegister(mainWindow)) hk.Register(mainWindow);
else ; // just do nothing
}
}
private static void PutIcon()
{
if (notifyIcon1 == null)
{
showItem = new MenuItem ("&Show interface", new System.EventHandler (showInfaceClick));
justCDsItem = new MenuItem ("&Jus'CDs",new System.EventHandler ( justCDsClick));
justCDsItem.Checked = Form1.settings.justCDs;
searchItem = new MenuItem("Search CDDB",new System.EventHandler (searchItemClick));
searchItem.Shortcut = Shortcut.CtrlShiftF;
searchItem.ShowShortcut = true;
settingsItem = new MenuItem("Settings", new System.EventHandler(settingsItemClick));
quitItem = new MenuItem("&Quit", new System.EventHandler(quitItemClick));
nIMenu = new System.Windows.Forms.ContextMenu(new MenuItem[5] { showItem, justCDsItem, searchItem,settingsItem, quitItem });
notifyIcon1 = new System.Windows.Forms.NotifyIcon();
notifyIcon1.ContextMenu = nIMenu;
notifyIcon1.Icon = new System.Drawing.Icon(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Icon1.ico");
//notifyIcon1.Icon = new System.Drawing.Icon(System.IO.Path.GetDirectoryName(
//System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase ) + "Icon1.ico");
//notifyIcon1.Icon = new System.Drawing.Icon("Icon1.ico");
notifyIcon1.DoubleClick += new EventHandler(notifyIcon1_DoubleClick);
notifyIcon1.Visible = true;
}
}
/* private static void notifyIcon1_MouseMove(object sender, MouseEventArgs mea)
* aby to fungovalo je treba upravit contextmenu na contextmenustrip a taky ty items .. az nakonec
* je tu kolem uz rozdelana priprava ..
{
notifyIcon1.ShowBalloonTip(2000,AppName,"Active",ToolTipIcon.None);
} */
// clicks on NotificationIcon context menu ..
private static void showInfaceClick(object sender, EventArgs e)
{
mainWindow.tabControl1.SelectedIndex = 0;
mainWindow.Show();
}
private static void justCDsClick(object sender, EventArgs e)
{
Form1.settings.justCDs = mainWindow.checkBox1.Checked = justCDsItem.Checked = !Form1.settings.justCDs;
if (mainWindow.Visible) mainWindow.Update();
}
private static void searchItemClick(object sender, EventArgs e)
{
mainWindow.tabControl1.SelectedIndex = 1 ;
//this.Size = new Size(this.Width, SystemInformation.PrimaryMonitorSize.Height);
mainWindow.Location = new System.Drawing.Point(SystemInformation.PrimaryMonitorSize.Width - mainWindow.Width, SystemInformation.PrimaryMonitorSize.Height - mainWindow.Height);
//mainWindow.Location = new System.Drawing.Point(880, 500);
mainWindow.Show();
}
private static void settingsItemClick(object sender, EventArgs e)
{
mainWindow.tabPage3_GotFocus(new Object(), new EventArgs());
mainWindow.tabControl1.SelectedIndex = 2;
mainWindow.Show();
}
public static void quitItemClick(object sender, EventArgs e)
{
if (DialogResult.Cancel == MessageBox.Show("Really exit application and stop scanning?",Form1.AppName,MessageBoxButtons.OKCancel,MessageBoxIcon.Question)) return;
if (!mainWindow.dBSaved) mainWindow.SaveDb(Form1.settings.dBPath);
//if (mainWindow.index != null) mainWindow.SaveIndex(Form1.settings.indexPath);
if (Form1.settings.fileIndex) mainWindow.SaveIndex(Form1.settings.indexPath);
mainWindow.Close();
mainWindow = null;
notifyIcon1.Visible = false;
Application.Exit();
}
static void notifyIcon1_DoubleClick(object sender, EventArgs e)
{
//throw new NotImplementedException();
//if (!mainWindow.Visible) mainWindow.WindowState = FormWindowState.Normal; else mainWindow.WindowState = FormWindowState.Minimized;
//if (!mainWindow.Visible) mainWindow.Show(); else mainWindow.Hide();
if (!mainWindow.Visible) mainWindow.Visible = true; else mainWindow.Visible = false;
}
}
}
OK. So here is the catch and solution . In Windows it is not determined whether win32.systemevents.sessionended shall be risen or form.close() will be called first by operating system. moreover it seems that if form.close() is called first then sessionended is omited even though form is not closed and disposed due to canceling closing process. in my system this behaviour changed after i ran some registry cleaning software. anyway understanding this we have to take care of both possible scenarios.
1. catch win32.systemevents.sessionended (or sessionending) event whatever suits our needs better and be
.
.
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Microsoft.Win32.SystemEvents.SessionEnded += new Microsoft.Win32.SessionEndedEventHandler(SystemEvents_SessionEnded);
Program.mainWindow = new Form1();
mainWindow.Activate();
mainWindow.Visible = false;
PutIcon();
RegisterHotKey(true);
Application.Run();
}
}
public static void SystemEvents_SessionEnded(object sender, Microsoft.Win32.SessionEndedEventArgs e)
{
// do whatever needed and exit application ..
RegisterHotKey(false);
notifyIcon1.Visible = false;
notifyIcon1.Dispose();
notifyIcon1 = null;
if (!mainWindow.dBSaved) mainWindow.SaveDb(Form1.settings.dBPath);
if (mainWindow.index != null) mainWindow.SaveIndex(Form1.settings.indexPath);
Microsoft.Win32.SystemEvents.SessionEnded -= new Microsoft.Win32.SessionEndedEventHandler(SystemEvents_SessionEnded);
if (mainWindow != null)
{
mainWindow.Dispose();
mainWindow = null;
}
Application.Exit();
}
2. properly override form.OnClosing() because this is being called when form is closing either manually by user or by system when shuting down, loging off etc. or create hanler for main form.Closing:
public Form1()
{
this.Closing += new CancelEventHandler(this.Form1_Closing);
InitializeComponent();
}
private void Form1_Closing(Object sender, CancelEventArgs e)
{
if (systemShutdown) Program.SystemEvents_SessionEnded(this, new Microsoft.Win32.SessionEndedEventArgs(Microsoft.Win32.SessionEndReasons.SystemShutdown));
else
{
e.Cancel = true;
this.Hide();
}
}
just want to mention that message pump must be runnig in order to sessionended be risen. Application.run() accomplishes that.
in my case as you can see i had to dig even deeper as i had closing redirected just to hide the app not to close it ( i just hide the app to notification irea icon and close it manually when i need .. ) and so i had to use some kind of way to specify the situation when this is called because sender is unfortunatelly and unexpectedly always this ..?
this is done by overring WndProc and catching propper message .here you can listen pretty much to everything inside windows ( like disc inserted / removed )but it is hooked only to a form and implementation gets often not so simple as you have to manully define various values and structs and compare against those values .. other then that its pretty simple:
private static int WM_QUERYENDSESSION = 0x11;
private static bool systemShutdown = false;
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg==WM_QUERYENDSESSION)
{
systemShutdown = true;
}
base.WndProc(ref m);
}
this was found here:
http://msdn.microsoft.com/en-us/library/microsoft.win32.systemevents.sessionending.aspx
Thinking a bit further we can possibly omit point 1 as system shall probably always try to call mainForm.close() but i keep it as i can not be certain about windows behaviour once it runs those things in different order again .. and also it is the mainly suggested solution for reacting to system shut down ..
hope this is helpfull for someone. greets from prague tomas
Here is something that you could try
For a shutdown, override the OnShutdown method:
protected override void OnShutdown()
{
//your code here
base.OnShutdown();
}
For a logoff:
First, add an event handler to Microsoft.Win32.SystemEvents.SessionEnded in the Service Constructor:
public MyService()
{
InitializeComponent;
Microsoft.Win32.SystemEvents.SessionEnded += new Microsoft.Win32.SessionEndedEventHandler(SystemEvents_SessionEnded);
}
Then add the handler:
void SystemEvents_SessionEnded(object sender, Microsoft.Win32.SessionEndedEventArgs e)
{
//your code here
}
This should catch any ended session, including the console itself (the one running the services).
I am generating new windows via the code below and displaying a webpage there and then close it. But after a while software gives out of memory exception. So there is memory leak happening. What might be the cause and how to solve it ? Thank you.
This is how do i start new window . i am doing a loop so there are being started thousands of new windows. As you can see after 60 seconds new window get itself close.
NewWindowThread<TitleWindow, string>(c => new TitleWindow(c), "the url that is going to be displayed at new window");
private void NewWindowThread<T, P>(Func<P, T> constructor, P param) where T : Window
{
Thread thread = new Thread(() =>
{
T w = constructor(param);
w.Show();
w.Closed += (sender, e) => w.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
public class TitleWindow : Window
{
WebBrowser webnew = new WebBrowser();
public TitleWindow(string srUrl)
{
DockPanel dk = new DockPanel();
dk.Width = 900;
dk.Height = 600;
this.AddChild(dk);
webnew.Navigated += new NavigatedEventHandler(wbMain_Navigated);
System.Windows.Threading.DispatcherTimer dispatcherTimer3 = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer3.Tick += new EventHandler(dispatcherTimer_Tick3);
dispatcherTimer3.Interval = new TimeSpan(0, 0, 0, 60, 0);
dispatcherTimer3.Start();
webnew.Height = 600;
webnew.Width = 900;
dk.Children.Add(webnew);
webnew.Navigate(srUrl);
this.WindowState = WindowState.Minimized;
}
void dispatcherTimer_Tick3(object sender, EventArgs e)
{
this.Close();
}
}
I immediately suspect that the WebBrowser control is not releasing it's resources when the window is closed as you are not calling it's Dispose() method. The WebBrowser control is a thin wrapper around the ActiveX MSHTML control.
Add a callback on the window Closing or Closed events to do this. e.g. webnew.Dispose().
I am using the following code to open a form in a new thread:
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadProc);
thread.Start();
}
public void ThreadProc()
{
Form form = new Form();
form.TopMost = true;
form.ShowDialog();
}
But the newly created form isn't TopMost even though I set it to true.
How can I make a form in a thread TopMost ?
Usually you don't need another thread, you open the form as usual in modal or non modal mode, if the form needs to do a heavy process then you do the process inside a thread.
Specific to your question one option is to run the form from an Application.Run as described here.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadProc);
thread.Start();
}
public void ThreadProc()
{
using (Form1 _form = new Form1())
{
_form.TopMost = true;
Application.Run(_form);
}
}
}
That will launch a new thread with its own message pump and will keep it as a TopMost form.
Just ran into this problem myself. It seems that if the form has an Owner, then TopMost works as expected. If the owning form was created on another thread, though, it's a little tricky to set. Here's what I used:
var form = new Form();
form.Shown += (sender, e) => {
Control.CheckForIllegalCrossThreadCalls = false;
form.Owner = /* Owning form here */;
form.CenterToParent(); // Not necessary
Control.CheckForIllegalCrossThreadCalls = true;
form.TopMost = true; // Works now!
};
Application.Run(form);
Instead of calling ShowDialog directly, try using this.Invoke to gain ownership of the form.
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadProc);
thread.Start();
}
public void ThreadProc()
{
Form form = new Form();
form.TopMost = true;
this.Invoke((Action)delegate() { form.ShowDialog(); });
}