SystemEvents.TimeChanged event doesn't work in a console app.

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

Can anyone tell me why the code below doesn't work? Run the console app and change the system date/time: why is the TimeChanged event handler not executed?

It works fine in a WinForms application, but I can find nothing in the documentation to suggest that the SystemEvents class can only be used in a WinForms app (if this were true, surely it would be in the Windows.Forms namespace).

Thanks,

Joe

using System;
namespace ConsoleApplication1
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
Class1 class1 = new Class1();
Console.Write("Change the system time or Press ENTER to exit");
Console.ReadLine();
}
public Class1()
{
Microsoft.Win32.SystemEvents.TimeChanged +=new EventHandler(SystemEvents_TimeChanged);
}
private void SystemEvents_TimeChanged(object sender, EventArgs e)
{
Console.WriteLine("Time changed");
}
}
}
 
Hi Joe,

Your app is single threaded. What thread is it supposed to execute the
event handler on, if you are waiting for input from the console?

--- Nick

Joe said:
Can anyone tell me why the code below doesn't work? Run the console app
and change the system date/time: why is the TimeChanged event handler not
executed?
It works fine in a WinForms application, but I can find nothing in the
documentation to suggest that the SystemEvents class can only be used in a
WinForms app (if this were true, surely it would be in the Windows.Forms
namespace).
Thanks,

Joe

using System;
namespace ConsoleApplication1
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
Class1 class1 = new Class1();
Console.Write("Change the system time or Press ENTER to exit");
Console.ReadLine();
}
public Class1()
{
Microsoft.Win32.SystemEvents.TimeChanged +=new EventHandler(SystemEvents_TimeChanged);
}
private void SystemEvents_TimeChanged(object sender, EventArgs e)
{
Console.WriteLine("Time changed");
}
}
}
 
Nick,

Thanks for responding. The framework documentation states "When a system event is raised calls back the corresponding delegate from a different thread. "
I assumed (and disassembly of the SystemEvents class seems to confirm) that it would create a thread with a hidden window to listen for a WM_TIMECHANGED message.

Joe

Nick Malik said:
Hi Joe,

Your app is single threaded. What thread is it supposed to execute the
event handler on, if you are waiting for input from the console?

--- Nick

Joe said:
Can anyone tell me why the code below doesn't work? Run the console app
and change the system date/time: why is the TimeChanged event handler not
executed?
It works fine in a WinForms application, but I can find nothing in the
documentation to suggest that the SystemEvents class can only be used in a
WinForms app (if this were true, surely it would be in the Windows.Forms
namespace).
Thanks,

Joe

using System;
namespace ConsoleApplication1
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
Class1 class1 = new Class1();
Console.Write("Change the system time or Press ENTER to exit");
Console.ReadLine();
}
public Class1()
{
Microsoft.Win32.SystemEvents.TimeChanged +=new EventHandler(SystemEvents_TimeChanged);
}
private void SystemEvents_TimeChanged(object sender, EventArgs e)
{
Console.WriteLine("Time changed");
}
}
}
 
Hi Joe,

Yes, but your program has the [STAThread] attribute. That means "Single
Threaded Apartment Threading model". In other words, you are planning to
call COM components, and you need to be certain that you will only run on
one thread.

If you want MTA Threading, just drop the attribute.

--- Nick

Joe said:
Nick,

Thanks for responding. The framework documentation states "When a system
event is raised calls back the corresponding delegate from a different
thread. "
I assumed (and disassembly of the SystemEvents class seems to confirm)
that it would create a thread with a hidden window to listen for a
WM_TIMECHANGED message.
Joe

Nick Malik said:
Hi Joe,

Your app is single threaded. What thread is it supposed to execute the
event handler on, if you are waiting for input from the console?

--- Nick

Joe said:
Can anyone tell me why the code below doesn't work? Run the console
app
and change the system date/time: why is the TimeChanged event handler not
executed?
It works fine in a WinForms application, but I can find nothing in the
documentation to suggest that the SystemEvents class can only be used in a
WinForms app (if this were true, surely it would be in the Windows.Forms
namespace).
Thanks,

Joe

using System;
namespace ConsoleApplication1
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
Class1 class1 = new Class1();
Console.Write("Change the system time or Press ENTER to exit");
Console.ReadLine();
}
public Class1()
{
Microsoft.Win32.SystemEvents.TimeChanged +=new EventHandler(SystemEvents_TimeChanged);
}
private void SystemEvents_TimeChanged(object sender, EventArgs e)
{
Console.WriteLine("Time changed");
}
}
}
 
Nick,

Thanks again. Removing the [STAThread] attribute solves the problem, but I'd still like to understand why. I'd have thought [STAThread] would be irrelevant for a console application main thread (not sure why VS.NET adds it automatically). Also the documentation for STAThreadAttribute states that "Using this attribute in an application that does not use COM interop has no effect."

I disassembled SystemEvents.EnsureSystemEvents and it includes code like the following:

if (!SystemEvents.UserInteractive || (Thread.CurrentThread.ApartmentState == ApartmentState.STA))
{
... initialise ...
}
else
{
... create a background thread and initialise ...
}

So I can see what is happening, but not why.

Nick Malik said:
Hi Joe,

Yes, but your program has the [STAThread] attribute. That means "Single
Threaded Apartment Threading model". In other words, you are planning to
call COM components, and you need to be certain that you will only run on
one thread.

If you want MTA Threading, just drop the attribute.

--- Nick

Joe said:
Nick,

Thanks for responding. The framework documentation states "When a system
event is raised calls back the corresponding delegate from a different
thread. "
I assumed (and disassembly of the SystemEvents class seems to confirm)
that it would create a thread with a hidden window to listen for a
WM_TIMECHANGED message.
Joe

Nick Malik said:
Hi Joe,

Your app is single threaded. What thread is it supposed to execute the
event handler on, if you are waiting for input from the console?

--- Nick

Can anyone tell me why the code below doesn't work? Run the console app
and change the system date/time: why is the TimeChanged event handler not
executed?

It works fine in a WinForms application, but I can find nothing in the
documentation to suggest that the SystemEvents class can only be used in a
WinForms app (if this were true, surely it would be in the Windows.Forms
namespace).

Thanks,

Joe

using System;
namespace ConsoleApplication1
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
Class1 class1 = new Class1();
Console.Write("Change the system time or Press ENTER to
exit");
Console.ReadLine();
}
public Class1()
{
Microsoft.Win32.SystemEvents.TimeChanged +=new
EventHandler(SystemEvents_TimeChanged);
}
private void SystemEvents_TimeChanged(object sender, EventArgs e)
{
Console.WriteLine("Time changed");
}
}
}
 
Well, you've found two annoying things:
1) a misleading bit in the help text, and
2) an overzealously helpful default in new console applications.

COM interop for console apps pretty much requires the STAThread, especially
if you are using APIs like MAPI that actually do the *wrong* thing in a
multi-threaded environment. That said, I suppose there are other reasons
that someone would want to force the app to be apartment threaded (although
COM compatibility is surely the most common). The attribute doesn't say
[COMCompatible] or some such nonsense. It says [STAThread]. Not the same
meaning. Therefore, the comment in the help text is anything but helpful.

It doesn't really have anything to do with console apps, per se.

I'm glad you found and fixed your problem.
--- Nick

Joe said:
Nick,

Thanks again. Removing the [STAThread] attribute solves the problem, but
I'd still like to understand why. I'd have thought [STAThread] would be
irrelevant for a console application main thread (not sure why VS.NET adds
it automatically). Also the documentation for STAThreadAttribute states
that "Using this attribute in an application that does not use COM interop
has no effect."
I disassembled SystemEvents.EnsureSystemEvents and it includes code like the following:

if (!SystemEvents.UserInteractive || (Thread.CurrentThread.ApartmentState == ApartmentState.STA))
{
... initialise ...
}
else
{
... create a background thread and initialise ...
}

So I can see what is happening, but not why.

Nick Malik said:
Hi Joe,

Yes, but your program has the [STAThread] attribute. That means "Single
Threaded Apartment Threading model". In other words, you are planning to
call COM components, and you need to be certain that you will only run on
one thread.

If you want MTA Threading, just drop the attribute.

--- Nick

Joe said:
Nick,

Thanks for responding. The framework documentation states "When a
system
event is raised calls back the corresponding delegate from a different
thread. "
I assumed (and disassembly of the SystemEvents class seems to confirm)
that it would create a thread with a hidden window to listen for a
WM_TIMECHANGED message.
Joe

:

Hi Joe,

Your app is single threaded. What thread is it supposed to execute the
event handler on, if you are waiting for input from the console?

--- Nick

Can anyone tell me why the code below doesn't work? Run the
console
app
and change the system date/time: why is the TimeChanged event
handler
not
executed?

It works fine in a WinForms application, but I can find nothing in the
documentation to suggest that the SystemEvents class can only be
used in
a
WinForms app (if this were true, surely it would be in the Windows.Forms
namespace).

Thanks,

Joe

using System;
namespace ConsoleApplication1
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
Class1 class1 = new Class1();
Console.Write("Change the system time or Press ENTER to
exit");
Console.ReadLine();
}
public Class1()
{
Microsoft.Win32.SystemEvents.TimeChanged +=new
EventHandler(SystemEvents_TimeChanged);
}
private void SystemEvents_TimeChanged(object sender,
EventArgs
e)
{
Console.WriteLine("Time changed");
}
}
}
 
Thanks again Nick. Actually I haven't completely solved my problem. The console app was just a test harness for the code I'm trying to write. What I really want is to detect changes to the system clock in an ASP.NET web application. I'm trying to work around what seems to me to be a bug in the ASP.NET Cache implementation (see recent post "cache absolute expiration" in the dotnet.framework.aspnet.caching group).

But I seem to have the same problem in a Web application - the event never fires. I've seen some posts talking about problems with SystemEvents in a Windows Service: possibly it's the same reason (no desktop available).

It's a pity the documentation isn't clearer about when you can and can't use SystemEvents. Meanwhile I think I'll have to roll my own technique to detect system clock changes.

Nick Malik said:
Well, you've found two annoying things:
1) a misleading bit in the help text, and
2) an overzealously helpful default in new console applications.

COM interop for console apps pretty much requires the STAThread, especially
if you are using APIs like MAPI that actually do the *wrong* thing in a
multi-threaded environment. That said, I suppose there are other reasons
that someone would want to force the app to be apartment threaded (although
COM compatibility is surely the most common). The attribute doesn't say
[COMCompatible] or some such nonsense. It says [STAThread]. Not the same
meaning. Therefore, the comment in the help text is anything but helpful.

It doesn't really have anything to do with console apps, per se.

I'm glad you found and fixed your problem.
--- Nick

Joe said:
Nick,

Thanks again. Removing the [STAThread] attribute solves the problem, but
I'd still like to understand why. I'd have thought [STAThread] would be
irrelevant for a console application main thread (not sure why VS.NET adds
it automatically). Also the documentation for STAThreadAttribute states
that "Using this attribute in an application that does not use COM interop
has no effect."
I disassembled SystemEvents.EnsureSystemEvents and it includes code like the following:

if (!SystemEvents.UserInteractive || (Thread.CurrentThread.ApartmentState == ApartmentState.STA))
{
... initialise ...
}
else
{
... create a background thread and initialise ...
}

So I can see what is happening, but not why.

Nick Malik said:
Hi Joe,

Yes, but your program has the [STAThread] attribute. That means "Single
Threaded Apartment Threading model". In other words, you are planning to
call COM components, and you need to be certain that you will only run on
one thread.

If you want MTA Threading, just drop the attribute.

--- Nick

Nick,

Thanks for responding. The framework documentation states "When a system
event is raised calls back the corresponding delegate from a different
thread. "
I assumed (and disassembly of the SystemEvents class seems to confirm)
that it would create a thread with a hidden window to listen for a
WM_TIMECHANGED message.

Joe

:

Hi Joe,

Your app is single threaded. What thread is it supposed to execute the
event handler on, if you are waiting for input from the console?

--- Nick

Can anyone tell me why the code below doesn't work? Run the console
app
and change the system date/time: why is the TimeChanged event handler
not
executed?

It works fine in a WinForms application, but I can find nothing in the
documentation to suggest that the SystemEvents class can only be used in
a
WinForms app (if this were true, surely it would be in the Windows.Forms
namespace).

Thanks,

Joe

using System;
namespace ConsoleApplication1
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
Class1 class1 = new Class1();
Console.Write("Change the system time or Press ENTER to
exit");
Console.ReadLine();
}
public Class1()
{
Microsoft.Win32.SystemEvents.TimeChanged +=new
EventHandler(SystemEvents_TimeChanged);
}
private void SystemEvents_TimeChanged(object sender, EventArgs
e)
{
Console.WriteLine("Time changed");
}
}
}
 
Back
Top