Checking for workstation lock/unlock change with c# - c#

DUPLICATE: How can I programmatically determine if my workstation is locked?
How can I detect (during runtime) when a Windows user has locked their screen (Windows+L) and unlocked it again. I know I could globally track keyboard input, but is it possible to check such thing with environment variables?

A SessionSwitch event may be your best bet for this. Check the SessionSwitchReason passed through the SessionSwitchEventArgs to find out what kind of switch it is and react appropriately.

You can get this notification via a WM_WTSSESSION_CHANGE message. You must notify Windows that you want to receive these messages via WTSRegisterSessionNotification and unregister with WTSUnRegisterSessionNotification.
These posts should be helpful for a C# implementation.
http://pinvoke.net/default.aspx/wtsapi32.WTSRegisterSessionNotification
http://blogs.msdn.com/shawnfa/archive/2005/05/17/418891.aspx
http://bytes.com/groups/net-c/276963-trapping-when-workstation-locked

You can use ComponentDispatcher as an alternative way to get those events.
Here's an example class to wrap that.
public class Win32Session
{
private const int NOTIFY_FOR_THIS_SESSION = 0;
private const int WM_WTSSESSION_CHANGE = 0x2b1;
private const int WTS_SESSION_LOCK = 0x7;
private const int WTS_SESSION_UNLOCK = 0x8;
public event EventHandler MachineLocked;
public event EventHandler MachineUnlocked;
public Win32Session()
{
ComponentDispatcher.ThreadFilterMessage += ComponentDispatcher_ThreadFilterMessage;
}
void ComponentDispatcher_ThreadFilterMessage(ref MSG msg, ref bool handled)
{
if (msg.message == WM_WTSSESSION_CHANGE)
{
int value = msg.wParam.ToInt32();
if (value == WTS_SESSION_LOCK)
{
OnMachineLocked(EventArgs.Empty);
}
else if (value == WTS_SESSION_UNLOCK)
{
OnMachineUnlocked(EventArgs.Empty);
}
}
}
protected virtual void OnMachineLocked(EventArgs e)
{
EventHandler temp = MachineLocked;
if (temp != null)
{
temp(this, e);
}
}
protected virtual void OnMachineUnlocked(EventArgs e)
{
EventHandler temp = MachineUnlocked;
if (temp != null)
{
temp(this, e);
}
}
}

You absolutely don't need WM_WTSSESSION_CHANGE
Just use internal WTTS apis.

Related

Firing events in C++ and handling them in C#

I have an industrial computer with some Digital I/O pins. The manufacturer provides some C++ libraries and examples to handle pin status change.
I need to integrate this events onto a C# application. AFAIK the most simple way to perform this is:
Make a managed C++/CLI wrapper for the manufacturer libraries that fires events when interruptions are issued from the DIO pins.
Reference that wrapper and handle the events in the C# part as it they were normal C# events.
I have tried to make this work with some mock objects with no luck. From the docs, the function EventHandler should do most of the "dirty work" in my case. Following info available in old threads and the EventHandler example in the MSDN docs I ended up with this test code:
C++/CLI
using namespace System;
public ref class ThresholdReachedEventArgs : public EventArgs
{
public:
property int Threshold;
property DateTime TimeReached;
};
public ref class CppCounter
{
private:
int threshold;
int total;
public:
CppCounter() {};
CppCounter(int passedThreshold)
{
threshold = passedThreshold;
}
void Add(int x)
{
total += x;
if (total >= threshold) {
ThresholdReachedEventArgs^ args = gcnew ThresholdReachedEventArgs();
args->Threshold = threshold;
args->TimeReached = DateTime::Now;
OnThresholdReached(args);
}
}
event EventHandler<ThresholdReachedEventArgs^>^ ThresholdReached;
protected:
virtual void OnThresholdReached(ThresholdReachedEventArgs^ e)
{
ThresholdReached(this, e);
}
};
public ref class SampleHandler
{
public:
static void c_ThresholdReached(Object^ sender, ThresholdReachedEventArgs^ e)
{
Console::WriteLine("The threshold of {0} was reached at {1}.",
e->Threshold, e->TimeReached);
Environment::Exit(0);
}
};
void main()
{
return;
CppCounter^ c = gcnew CppCounter(20);
c->ThresholdReached += gcnew EventHandler<ThresholdReachedEventArgs^>(SampleHandler::c_ThresholdReached);
Console::WriteLine("press 'a' key to increase total");
while (Console::ReadKey(true).KeyChar == 'a') {
Console::WriteLine("adding one");
c->Add(1);
}
}
C#
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
CppCounter cc = new CppCounter(5);
//cc.ThresholdReached += cs_ThresholdReached; //<--This is the offending line
Console.WriteLine("press 'a' key to increase total");
while (Console.ReadKey(true).KeyChar == 'a')
{
Console.WriteLine("adding one");
cc.Add(1);
}
}
static void cs_ThresholdReached(object sender, ThresholdReachedEventArgs e)
{
Console.WriteLine("The threshold of {0} was reached at {1}.", e.Threshold, e.TimeReached);
Environment.Exit(0);
}
}
class Counter
{
private int threshold;
private int total;
public Counter(int passedThreshold)
{
threshold = passedThreshold;
}
public void Add(int x)
{
total += x;
if (total >= threshold)
{
ThresholdReachedEventArgs args = new ThresholdReachedEventArgs();
args.Threshold = threshold;
args.TimeReached = DateTime.Now;
OnThresholdReached(args);
}
}
protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
{
EventHandler<ThresholdReachedEventArgs> handler = ThresholdReached;
if (handler != null)
{
handler(this, e);
}
}
public event EventHandler<ThresholdReachedEventArgs> ThresholdReached;
}
public class ThresholdReachedEventArgs : EventArgs
{
public int Threshold { get; set; }
public DateTime TimeReached { get; set; }
}
}
What am I doing wrong? Is it something I am missing?
public class ThresholdReachedEventArgs : EventArgs
The code is correct, except for this minor glitch. You accidentally re-declared this class in your C# code. Now there are two, one from your C++/CLI project and another from your C# project. That is a problem, type identity in .NET is not just determined by the namespace name and class name, it also includes the assembly it came from.
So these are two distinct types, the compiler tries to tell you that the C# version of it is not the correct one. That they have the same name doesn't exactly help you decode the error message :)
Very easy to fix, simply delete the class declaration from your C# code. Now the compiler will use the C++/CLI version of it.

How to check if user is idle on UWP? [duplicate]

This question already has answers here:
Detect if user Idle on windows universal app
(2 answers)
Closed 6 years ago.
I wanted to make a function that would timeout and navigate to the main page if the user is idle for a certain period of time. After a little research, I found that the ThreadPoolTimer should suit my needs. Testing it I decided to use a 10 sec interval.
timer =ThreadPoolTimer.CreatePeriodicTimer(Timer_Tick,TimeSpan.FromSeconds(10));
And this is where I'm at a loss. I couldn't figure out a way to check user input on a UWP without having to individually check PointerPressed, PointerExited, etc. So I did some more digging and I found a block of code that's supposed to give you a boolean value if the user is idle or not.
public static uint GetIdleTime()
{
LASTINPUTINFO lastInPut = new LASTINPUTINFO();
lastInPut.cbSize = (uint)Marshal.SizeOf(lastInPut);
GetLastInputInfo(ref lastInPut);
return ((uint)Environment.TickCount - lastInPut.dwTime);
}
public static bool IsUserIdle()
{
uint idleTime = (uint)Environment.TickCount - GetLastInputEventTickCount();
if (idleTime > 0)
{
idleTime = (idleTime / 1000);
}
else
{
idleTime = 0;
}
//user is idle for 10 sec
bool b = (idleTime >= 10);
return b;
}
private static uint GetLastInputEventTickCount()
{
LASTINPUTINFO lii = new LASTINPUTINFO();
lii.cbSize = (uint)Marshal.SizeOf(lii);
lii.dwTime = 0;
uint p = GetLastInputInfo(ref lii) ? lii.dwTime : 0;
return p;
}
[StructLayout(LayoutKind.Sequential)]
private struct LASTINPUTINFO
{
public static readonly int SizeOf = Marshal.SizeOf<LASTINPUTINFO>();
[MarshalAs(UnmanagedType.U4)]
public UInt32 cbSize;
[MarshalAs(UnmanagedType.U4)]
public UInt32 dwTime;
}
[DllImport("user32.dll")]
private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
I then call the function in the tick function and use the conditional statement if IsUserIdle() is equal to true then navigate to the main page.
public static void Timer_Tick(object sender)
{
if (IsUserIdle() == true)
{
Frame.Navigate(typeof(MainPage));
}
}
But when I start it nothing happens, and after I set a couple breakpoints I found that IsUserIdle() never returns a true value even after 10 sec of inactivity. I am completely stuck so any help would be appreciated.
GetLastInputInfo isn't supported for Windows store apps:
Minimum supported client: Windows 2000 Professional [desktop apps only]
I'm not aware of any intrinsic UWP API to detect if the user is idle, but it's definitely possible to whip up your own mechanism for doing so.
I couldn't figure out a way to check user input on a UWP without having to individually check PointerPressed, PointerExited, etc.
What's so bad about that approach? Here's my attempt:
App.xaml.cs
public sealed partial class App : Application
{
public static new App Current => (App)Application.Current;
public event EventHandler IsIdleChanged;
private DispatcherTimer idleTimer;
private bool isIdle;
public bool IsIdle
{
get
{
return isIdle;
}
private set
{
if (isIdle != value)
{
isIdle = value;
IsIdleChanged?.Invoke(this, EventArgs.Empty);
}
}
}
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
idleTimer = new DispatcherTimer();
idleTimer.Interval = TimeSpan.FromSeconds(10); // 10s idle delay
idleTimer.Tick += onIdleTimerTick;
Window.Current.CoreWindow.PointerMoved += onCoreWindowPointerMoved;
}
private void onIdleTimerTick(object sender, object e)
{
idleTimer.Stop();
IsIdle = true;
}
private void onCoreWindowPointerMoved(CoreWindow sender, PointerEventArgs args)
{
idleTimer.Stop();
idleTimer.Start();
IsIdle = false;
}
}
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
protected override void OnNavigatedTo(NavigationEventArgs e)
{
App.Current.IsIdleChanged += onIsIdleChanged;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
App.Current.IsIdleChanged -= onIsIdleChanged;
}
private void onIsIdleChanged(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine($"IsIdle: {App.Current.IsIdle}");
}
}
Idle is detected when the pointer hasn't moved for 10s within the app window. This will work also for touch-only apps (like mobile apps) because PointerMoved will fire when the window is tapped, too.

Cross-platform textbox input in MonoGame (Windows/Ubuntu)

I'm working on a game for desktop platforms that uses MonoGame. Previously, it was targeted only at Windows and used pure XNA. I'm exploring options to make it support other platforms using MonoGame and my current alternate test platform is Ubuntu 15.04.
One of the features of the game is support for text boxes that behave similarly to the standard windows UI textbox controls. In order to accomplish this, I used code from this StackOverflow answer.
My question is: How can I write a portable version of the functionality from the linked answer in such a way that it will work on alternate desktop platforms (in this case, Ubuntu)? I'm looking for something that will capture keypress events from the game window on both Windows and Ubuntu.
Additional info:
The linked answer overrides the WndProc of the game's window and passes input into the current textbox by firing events (CharEntered, KeyDown, KeyUp). I've refactored the answer's code to a point where I can encapsulate all the Windows functionality in a single "events" class:
internal class Win32KeyboardEvents : IKeyboardEvents
{
public event CharEnteredHandler CharEntered;
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
private readonly IntPtr _prevWndProc;
private readonly NativeMethods.WndProc _hookProcDelegate;
public Win32KeyboardEvents(GameWindow window)
{
_hookProcDelegate = HookProc;
_prevWndProc = (IntPtr)NativeMethods.SetWindowLong(window.Handle, NativeMethods.GWL_WNDPROC,
(int)Marshal.GetFunctionPointerForDelegate(_hookProcDelegate));
}
private IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
IntPtr returnCode = NativeMethods.CallWindowProc(_prevWndProc, hWnd, msg, wParam, lParam);
switch (msg)
{
case NativeMethods.WM_GETDLGCODE:
returnCode = (IntPtr)(returnCode.ToInt32() | NativeMethods.DLGC_WANTALLKEYS);
break;
case NativeMethods.WM_KEYDOWN:
if (KeyDown != null) //fire events that are subscribed to by textbox object based on which windows message is received
KeyDown(null, new XNAKeyEventArgs((Keys)wParam));
break;
case NativeMethods.WM_KEYUP:
if (KeyUp != null)
KeyUp(null, new XNAKeyEventArgs((Keys)wParam));
break;
case NativeMethods.WM_CHAR:
if (CharEntered != null)
CharEntered(null, new Win32CharEnteredEventArgs((char)wParam, lParam.ToInt32()));
break;
}
return returnCode;
}
}
My first attempt for something that would work on Ubuntu was creating a GameComponent-derived object that listened for keyboard input in the component's Update() and fired events appropriately, but this quickly got too complicated.
I've investigated using GTK#, but there are quite a few dependencies that I don't want to install unless GTK# is the best way to go. Here is my GTK# attempt, which is currently untested due to a missing dependency:
internal class CrossPlatformKeyboardEvents : DrawingArea, IKeyboardEvents
{
public event CharEnteredHandler CharEntered = delegate { };
public event KeyEventHandler KeyDown = delegate { };
public event KeyEventHandler KeyUp = delegate { };
public CrossPlatformKeyboardEvents()
{
Application.Init();
Application.Run();
AddEvents((int)EventMask.KeyPressMask);
AddEvents((int)EventMask.KeyReleaseMask);
}
[ConnectBefore]
protected override bool OnKeyPressEvent(EventKey evnt)
{
if (IsPastEvent(evnt))
{
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_PASTE_CODE));
return base.OnKeyPressEvent(evnt);
}
switch (evnt.Key)
{
case Key.Return:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_RETURNKEY_CODE));
break;
case Key.BackSpace:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_BACKSPACE_CODE));
break;
case Key.Tab:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_TAB_CODE));
break;
}
var keyCode = GetXNAKey(evnt.Key);
if (keyCode != Keys.None)
KeyDown(null, new XNAKeyEventArgs(keyCode));
return base.OnKeyPressEvent(evnt);
}
[ConnectBefore]
protected override bool OnKeyReleaseEvent(EventKey evnt)
{
var keyCode = GetXNAKey(evnt.Key);
if (keyCode != Keys.None)
KeyUp(null, new XNAKeyEventArgs(keyCode));
return base.OnKeyReleaseEvent(evnt);
}
private bool IsPastEvent(EventKey evnt)
{
return (evnt.State & ModifierType.ControlMask) > 0 && (evnt.Key == Key.V || evnt.Key == Key.v);
}
private Keys GetXNAKey(Key key)
{
if ((key >= Key.Key_0 && key <= Key.Key_9) ||
(key >= Key.A && key <= Key.Z))
{
return (Keys) key;
}
if (key >= Key.a && key <= Key.z)
{
return (Keys) (key - 32);
}
return Keys.None;
//switch (key)
//{
//}
}
I got around this by using the TextInput event on GameWindow in MonoGame.
This is an extension that is part of MonoGame but not part of Xna. Currently, I have 2 projects - one for standard Xna and one for Monogame. The Monogame project has links to all the files in my Xna project so they share the same code.
Because of the way my projects are set up, I also had to add a compiler flag (MONO) that excludes this code from being compiled for standard XNA.
Full class (replaces CrossPlatformKeyboardEvents posted in the question):
internal sealed class MonoGameKeyboardEvents : IKeyboardEvents
{
public event CharEnteredHandler CharEntered;
private readonly GameWindow _window;
public MonoGameKeyboardEvents(GameWindow window)
{
_window = window;
#if !MONO
if (CharEntered != null) //hide warning for "member is not used"
CharEntered(null, null);
#else
_window.TextInput += GameWindow_TextInput;
#endif
}
#if MONO
private void GameWindow_TextInput(object sender, TextInputEventArgs e)
{
if (CharEntered != null)
{
CharEntered(null, new CharEnteredEventArgs(e.Character));
}
}
~MonoGameKeyboardEvents()
{
Dispose(false);
}
#endif
public void Dispose()
{
#if !MONO
}
#else
Dispose(true);
}
private void Dispose(bool disposing)
{
if (!disposing) return;
_window.TextInput -= GameWindow_TextInput;
}
#endif
}

How can I detect when Windows 10 enters tablet mode in a Windows Forms application?

Update
While not the most elegant solution, one method that seems to work is to watch the relevant registry value. Here's an example using WMI to do this. I'd be happy to hear from anyone if there's a better solution than this.
using System;
using System.Management;
using System.Security.Principal;
using System.Windows.Forms;
using Microsoft.Win32;
public partial class MainForm : Form
{
public MainForm()
{
this.InitializeComponent();
this.UpdateModeFromRegistry();
var currentUser = WindowsIdentity.GetCurrent();
if (currentUser != null && currentUser.User != null)
{
var wqlEventQuery = new EventQuery(string.Format(#"SELECT * FROM RegistryValueChangeEvent WHERE Hive='HKEY_USERS' AND KeyPath='{0}\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\ImmersiveShell' AND ValueName='TabletMode'", currentUser.User.Value));
var managementEventWatcher = new ManagementEventWatcher(wqlEventQuery);
managementEventWatcher.EventArrived += this.ManagementEventWatcher_EventArrived;
managementEventWatcher.Start();
}
}
private void ManagementEventWatcher_EventArrived(object sender, EventArrivedEventArgs e)
{
this.UpdateModeFromRegistry();
}
private void UpdateModeFromRegistry()
{
var tabletMode = (int)Registry.GetValue("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\ImmersiveShell", "TabletMode", 0);
if (tabletMode == 1)
{
Console.Write(#"Tablet mode is enabled");
}
else
{
Console.Write(#"Tablet mode is disabled");
}
}
}
Original Question
I'm interested in make some optimizations in my Windows Forms application based on whether a user is in "Tablet Mode" (or not) using the new Windows 10 Continuum feature.
There is some guidance on how to do this in a UWP project at https://msdn.microsoft.com/en-us/library/windows/hardware/dn917883(v=vs.85).aspx (i.e. check the current view's UserInteractionMode to see if it's UserInteractionMode.Mouse or UserInteractionMode.Touch), however I'm not sure if or how I can do the same in Windows Forms.
Would there be any way I can call the necessary UWP APIs from my Windows Forms application, or is there some Windows Forms equivalent I can use?
To get whether the system is in tablet mode or not, query the system metric ConvertibleSlateMode like so (not tested, but it should work fine as far back as XP):
public static class TabletPCSupport
{
private static readonly int SM_CONVERTIBLESLATEMODE = 0x2003;
private static readonly int SM_TABLETPC = 0x56;
private static Boolean isTabletPC = false;
public static Boolean SupportsTabletMode { get { return isTabletPC; }}
public static Boolean IsTabletMode
{
get
{
return QueryTabletMode();
}
}
static TabletPCSupport ()
{
isTabletPC = (GetSystemMetrics(SM_TABLETPC) != 0);
}
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "GetSystemMetrics")]
private static extern int GetSystemMetrics (int nIndex);
private static Boolean QueryTabletMode ()
{
int state = GetSystemMetrics(SM_CONVERTIBLESLATEMODE);
return (state == 0) && isTabletPC;
}
}
(Documentation here)
I have looked everywhere for how to tell if Windows 10 is in tablet mode and here is the simplest solution I found:
bool bIsTabletMode = false;
var uiMode = UIViewSettings.GetForCurrentView().UserInteractionMode;
if (uiMode == Windows.UI.ViewManagement.UserInteractionMode.Touch)
bIsTabletMode = true;
else
bIsTabletMode = false;
// (Could also compare with .Mouse instead of .Touch)
According to this article, you cant listen to WM_SETTINGCHANGE message. Here is a short c# sample :
protected override void WndProc(ref Message m)
{
const int WM_WININICHANGE = 0x001A,
WM_SETTINGCHANGE = WM_WININICHANGE;
if (m.Msg == WM_SETTINGCHANGE)
{
if (Marshal.PtrToStringUni(m.LParam) == "UserInteractionMode")
{
MessageBox.Show(Environment.OSVersion.VersionString);
}
}
base.WndProc(ref m);
}
For Windows 10 you should then perform some COM Interfacing with some WinRT stuff, to check if you are in UserInteractionMode.Mouse (desktop) or UserInteractionMode.Touch (tablet).
The Com Interop stuff looks rather tricky but it seems to be the only way if you are in a stock win32 app.

C# delegate v.s. EventHandler

I want to send an alert message to any subscribers when a trap occurred.
The code I created works fine using a delegate method myDelegate del.
My questions are:
I want to know whether it's better to use EventHandler instead of a delegate?
I'm not sure what the differences are between a delegate and an EventHandler in my case.
notify(trapinfo t), that's what I've done here to get trap information. But it seems not to be a good idea. I read some online tutorial lesson introducing passing delegate object; I'm wondering if it's appropriate in my case? And how should I do it? Any suggestions?
Thanks a lot :)
My code:
public class trapinfo
{
public string info;
public string ip;
public string cause;
}
public class trap
{
public delegate void myDelegate(trapinfo t);
public myDelegate del;
trapinfo info = new trapinfo();
public void run()
{
//While(true)
// If a trap occurred, notify the subscriber
for (; ; )
{
Thread.Sleep(500);
foreach (myDelegate d in del.GetInvocationList())
{
info.cause = "Shut Down";
info.ip = "192.168.0.1";
info.info = "Test";
d.Invoke(info);
}
}
}
}
public class machine
{
private int _occuredtime=0;
public trapinfo info = new trapinfo();
public void notify(trapinfo t)
{
++_occuredtime;
info.cause = t.cause;
info.info = t.info;
info.ip = t.ip;
getInfo();
}
public void subscribe(trap t)
{
t.del += new trap.myDelegate(notify);
}
public void getInfo()
{
Console.WriteLine("<Alert>: cauese/{0}, info/ {1}, ip/{2}, time/{3}",
info.cause, info.info, info.ip,_occuredtime);
}
}
class Program
{
static void Main(string[] args)
{
trap t = new trap();
machine machineA = new machine();
machineA.subscribe(t);
t.run();
}
}
Update 2013-08-12
How about the observer/observable design pattern, that looks great in my case (EventHandler).
In my case, a machine subscribes to a trap messenger. (Add a machine to an invocation list)
Once a trap occurred, I send a message to all machines which are subscribed. (Call HandleEvent to handle it)
Advantages:
don't care about GetInvocationList() anymore, just use (+=) and (-=) to decide whom to send the trap.
It's easier to understand the logic of my program.
I know there are several ways to do it, but I wish I could analyze its pros and cons.
And thanks for your comments and suggestions, that would be very helpful!
I read the MSDN EventArgs article which Matthew Watson suggested.
Here's my Event Version:
public class TrapInfoEventArgs : EventArgs
{
public int info { get; set; }
public string ip { get; set; }
public string cause { get; set; }
}
public class trap
{
public event EventHandler<TrapInfoEventArgs> TrapOccurred;
protected virtual void OnTrapOccurred(TrapInfoEventArgs e)
{
EventHandler<TrapInfoEventArgs> handler = TrapOccurred;
if (handler != null)
{
handler(this, e);
}
}
public void run()
{
//While(true)
// If a trap occurred, notify the subscriber
for (; ; )
{
Thread.Sleep(500);
TrapInfoEventArgs args = new TrapInfoEventArgs();
args.cause = "Shut Down";
OnTrapOccurred(args);
}
}
}
public class machine
{
public void c_TrapOccurred(object sender, TrapInfoEventArgs e)
{
Console.WriteLine("<Alert>: cauese/{0}, info/ {1}, ip/{2}, time/{3}",
e.cause, e.info, e.ip, DateTime.Now.ToString());
}
}
class Program
{
static void Main(string[] args)
{
trap t = new trap();
machine machineA = new machine();
t.TrapOccurred += machineA.c_TrapOccurred; //notify machine A
t.run();
}
}
The difference between event and delegate is that:
event declaration adds a layer of protection on the delegate instance.
This protection prevents clients of the delegate from resetting the
delegate and its invocation list, and only allows adding or removing
targets from the invocation list
See What are the differences between delegates and events?
2) As I see it, your subscriber should not change delegates freely. One subscriber can assign = to it instead of adding +=. This will assign a new delegate, therefore, the previous delegate with its invocation list will be lost and previous subscribers will not be called anymore. So you should use Event for sure. Or you can change your code to make your delegate private and write additional functions for manipulating it to define your own event behavior.
//preventing direct assignment
private myDelegate del ;
public void AddCallback(myDelegate m){
del += m;
}
public void RemoveCallback(myDelegate m){
del -= m;
}
//or
public static trap operator +(trap x,myDelegate m){
x.AddCallback(m);
return x;
}
public static trap operator -(trap x, myDelegate m)
{
x.RemoveCallback(m);
return x;
}
//usage
//t.AddCallback(new trap.myDelegate(notify));
t+=new trap.myDelegate(notify);
It is much better to use an event for your example.
An event is understood by the Visual Studio Form and WPF designers, so you can use the IDE to subscribe to events.
When raising events, there is no need for you to write your own foreach handling to iterate through them.
events are the way that most programmers will expect this functionality to be accessed.
If you use a delegate, the consuming code can mess around with it in ways that you will want to prevent (such as resetting its invocation list). events do not allow that to happen.
As for your second question: Using an event you would create a class derived from EventArgs to hold the data, and pass that to the event when you raise it. The consumer will then have access to it.
See here for details: http://msdn.microsoft.com/en-us/library/system.eventargs.aspx

Categories