.NET's Event Mechanism - c#

In all the books regarding C#/.NET that I have seen till now, when they talk about Events,
they talk about Creating and Consuming Events.
I am curious to understand a bit how it works behind our code - what is the mechanism that runs it.
I know a little bit from the fact that the Windows Message Loop is acting like a Queue for the events that are raised.
For example, WM_KEYDOWN, or WM_LBUTTONDOWN, etc..
But what happens for example, If I create a class that does not inherit class Control, and this class raises an event?
(which another class, which also does not inherit class Control, receives it)
Will the raised event go thru the message loop?
It sound not so logic..
(yet assume that the project is a Windows Forms project, just the 2 classes - the sender and the receiver are not GUI classes at all, but simple classes that you wrote)
Any explanation or link to an article about the mechanism behind our code will be highly appreciated.

I hope I'm understanding your question correctly. I think we're talking about two things.
First - how events work in C#
Second - how a WinForms application written in C# knows when you've clicked a button.
Events in C# are their own distinct thing. You can write a console app, create your own event, listen to it, fire it, respond to it, etc... and it all just works. You subscribe to an event by called Add() and you unsubscribe by calling Remove(). The event itself keeps track of what methods are listening to it and when it's raised, calls all of those methods.
Jon Skeet explains it much better:
How do C# Events work behind the scenes?
But these events are just C# code. Related to, but distinct from the Win32 messages you're mentioning. In a Winforms application, when the user clicks a button, how does the application know about it? We can look using the debugger (turn off the 'My Code' https://msdn.microsoft.com/en-us/library/dn457346.aspx option) and set a breakpoint in the click event, you'll be able to see what is going on.
So in Windows.Forms.Controls.ControlNativeWindow there is a WndProc method that takes in a System.Windows.Forms.Message m.
Right before that is a 'debuggableCallback' method. That mirrors what you'd expect from a Win32API app.
Source here:
http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/NativeWindow.cs,ad40308c5b6490dd
/// <include file='doc\NativeWindow.uex' path='docs/doc[#for="NativeWindow.DebuggableCallback"]/*' />
/// <devdoc>
/// Window message callback method. Control arrives here when a window
/// message is sent to this Window. This method packages the window message
/// in a Message object and invokes the wndProc() method. A WM_NCDESTROY
/// message automatically causes the releaseHandle() method to be called.
/// </devdoc>
/// <internalonly/>
private IntPtr DebuggableCallback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) {
// Note: if you change this code be sure to change the
// corresponding code in Callback above!
Message m = Message.Create(hWnd, msg, wparam, lparam);
try {
if (weakThisPtr.IsAlive && weakThisPtr.Target != null) {
WndProc(ref m);
}
else {
DefWndProc(ref m);
}
}
finally {
if (msg == NativeMethods.WM_NCDESTROY) ReleaseHandle(false);
if (msg == NativeMethods.WM_UIUNSUBCLASS) ReleaseHandle(true);
}
return m.Result;
}
So, ultimately, if you're running on Windows, it's driven by the same Win32 API messages you'd expect. It's just that the System.Windows.Forms classes are written to encapsulate most of it from us.

Related

In Event Tracing for Windows (ETW), how do I use EventSource to point to Notepad and retrieve the menu item selected in an ETW Session?

I am trying to understand Event Tracing in Windows (ETW). What I want to do is capture which menu item was selected in Notepad. If I click Word Wrap, I want ETW to tell me this.
I have looked at the samples on GitHub. One of the Event Producers is the demo:
namespace TraceEventSamples
{
// In these demos, we generate events with System.Diagnostics.Tracing.EventSource and read them with ETWTraceEventSource
//
// Normally the EventSource and the ETWTraceEventSource would be indifferent processes, however, we
// don't do this here to make the scenario really easy to run. The code works in the multi-process case, however.
namespace Producer
{
[EventSource(Name = "Microsoft-Demos-SimpleMonitor")] // This is the name of my eventSource outside my program.
class MyEventSource : EventSource
{
// Notice that the bodies of the events follow a pattern: WriteEvent(ID, <args>) where
// ID is a unique ID starting at 1 and incrementing for each new event method. and
// <args> is every argument for the method.
// WriteEvent then takes care of all the details of actually writing out the values complete
// with the name of the event (method name) as well as the names and types of all the parameters.
public void MyFirstEvent(string MyName, int MyId) { WriteEvent(1, MyName, MyId); }
public void MySecondEvent(int MyId) { WriteEvent(2, MyId); }
public void Stop() { WriteEvent(3); }
// Typically you only create one EventSource and use it throughout your program. Thus a static field makes sense.
public static MyEventSource Log = new MyEventSource();
// You don't need to define this override, but it does show you when your eventSource gets commands, which
// is helpful for debugging (you know that your EventSource got the command.
protected override void OnEventCommand(EventCommandEventArgs command)
{
EventGenerator.Out.WriteLine("EventSource Gets command {0}", command.Command);
}
// We could add Keyword definitions so that you could turn on some events but not others
// but we don't do this here to keep it simple. Thus you either turn on all events or none.
}
This is creating events for the demo, but how can I wire this to Notepad instead? Can ETW be used for this type of logging, knowing what someone clicked on a menu of an application?
I have looked at various SO questions, but they did not help. One generic was, Is there a Microsoft (built-in) ETW Provider for tracing ETW lifecycle events?
I considered this because it does not require C#, C++ Event Tracing for Windows (ETW) wrapper but didn't understand how it helped me.
This makes me think I cannot do it with managed code if I didn't write the original program:
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.tracing.eventsource?redirectedfrom=MSDN&view=netcore-3.1
What I am attempting to do is capture which menu item is selected, and take action with my program that caught the event. The reason for this is an old VB6 program. I cannot do what I need to do, so my last resort is to capture the menu item and get my application to do what I want. I wanted to start with a simple notepad.exe.
VB6 user clicks "View X." My C# application does "View Y".

IDocHostUIHandler crashing MSHTML.dll with error 0xc0000409

I am using slightly modified IDocHostUIHandler from https://stackoverflow.com/a/21699086/592212 in simple one main window test application with only WPF WebBrowser component in that Window. The initialization code is as follows:
public MainWindow()
{
InitializeComponent();
_wbHostHandler = new WebBrowserHostUIHandler(PART_WebBrowser);
_wbHostHandler.Flags |= HostUIFlags.DpiAware;
PART_WebBrowser.Navigate("SOME_URL");
}
There is really nothing else going on in the Application. Still, after running the application, an error is thrown in COM component (therefore, I can not use a debugger to trap it) and 0xc0000409 (STATUS_STACK_BUFFER_OVERRUN) is reported in Event Viewer.
Any ideas of what is causing the error or how to get rid of it?
(Win10 Pro 1703 (build 15063.483) and .NET 4.6.2)
Source Code: https://www.dropbox.com/s/ddob6p7jh4dfsda/UIHostCrashDemo.zip?dl=1
I don't know where you got your WebBrowserHostUIHandler.cs content from but it's wrong. The definition of IDocHostUIHandler simply misses the TranslateAccelerator method.
I guess it's because my initial code used System.Windows.Forms.Message type which is a reference to the System.Windows.Forms (winforms) assembly. If this is such a problem, the method can just be replaced by this if the message is not used (wich is the case in my initial code).
So in the interface you must add this, just after ResizeBorder:
[PreserveSig]
uint TranslateAccelerator(IntPtr msg, ref Guid group, int nCmdID);
And you must implement it anywhere in the code, like this:
uint Native.IDocHostUIHandler.TranslateAccelerator(IntPtr msg, ref Guid group, int nCmdID)
{
return S_FALSE;
}
But again, this is optional, if you want something that works just carefully copy/paste my code from my post and add a reference to System.Windows.Forms if needed.

How to keep track of an instance of an event handler across fragments

I have a Xamarin android application that has a main fragment activity (named Home) which hosts a view pager and then several fragments in the view pager.
The application gets data from a bluetooth device that passes the data via an event handler once I instantiate the class.
BTScanner scanner = new BTScanner();
scanner.onScanData += BTScanner_onScanData;
The issue is, I need this event handler to be active on two of the fragments in my view pager, when that page is active. In other words, I want to receive the scan data if I am on fragment1 or fragment2, However, I can't have it go to both at the same time, there is processing of the data received and the processing is different based on which fragment you are receiving the scan data in.
What I have so far is I created a custom interface on the activity and implemented it in the fragments. It will allow me to call a method on the fragments when the view pager switches pages. Based on these methods, I can de-init the event handler on one page and init it on the other. Here is an example on the fragment activity:
private void ViewPager_PageSelected(object sender, ViewPager.PageSelectedEventArgs e)
{
int position = (int)e.Position;
ICustomFragmentLifecycleForPager fragmentToResume = (ICustomFragmentLifecycleForPager)adapter.InstantiateItem(viewPager, position);
fragmentToResume.onResumePagerFragment(previousActiveFragment);
ICustomFragmentLifecycleForPager fragmentToPause = (ICustomFragmentLifecycleForPager)adapter.InstantiateItem(viewPager, previousActiveFragment);
fragmentToPause.onPausePagerFragment();
previousActiveFragment = position;
}
Therefore, with this, I have it working as needed because when I implement the interface on the fragments, I get onPausePagerFragment called on the fragment I can de-init, and an onResumePagerFragment on the fragment I can init.
All that said, the issue I am having is actually on first startup. On first startup, I am setting the fragment to show first and when I do that, the
ViewPager_PageSelected
is not called initially, thus not calling my custom interface methods to init or de-init the scanner.
One thing I tried already is to put a method call in the onCreate of the activity (also tried it in onStartup and onResume) that would in theory only be called one time when the application starts and would then check which fragment is active at startup and force the interface methods to fire appropriately. However, this feels clunky and is not working properly. In addition to not working properly, I am also getting null exceptions in the fragment, when the interface method does fire because I force it to in onCreate of the activity, the method that is called by the interface on the fragment, onResumePagerFragment, returns a null for the activity here:
var activity = (Home)Activity;
This worked before and continues to work in other places in the code so I suspect that this is happening because the activity has not fully started yet before the interface calls the method on the fragment and it attempts to get a reference to the activity. Again, I tried doing this in onResume and onStart on the activity but I still get a null.
With all that said, what is the best way in general to handle a scenario like mine where I have the main activity which starts but then is nothing more than a fragment/view pager container and I need to have one instance to the event handler active on a fragment at a time? Should I even need to do an interface or rather use a static class? If I use a static class, I know how to get the instance of the class on each but how do you then create the event handler AND make sure to un-register is as well when another fragment grabs the instance?
Thanks!
Mike
I could not show you C# code, but below my answer could give you a hint.
https://stackoverflow.com/a/30554102/361100
Note that Android natively provides EventBus and Xamarin I found is MessageBus component alternatively.
Secondly, you can make Service so that the service is act as a delegator of all data communication whenever events are fired.
In conclusion, we could think Fragment does not gurantee to give same fragment when it restored and especially it is garbage-collected at any time when it's used with ViewPager.

Trapping messages in .NET

How can i trap a Windows system message (like WM_SETTEXT) that was sent by some window (VLC player window in my case)? I've tried to inherit NativeWindow class and override WndProc like this:
class VLCFilter : NativeWindow
{
System.IntPtr iHandle;
const int WM_SETTEXT = 0x000C;
public VLCFilter()
{
Process p = Process.GetProcessesByName("vlc")[0];
iHandle = p.MainWindowHandle;
}
protected override void WndProc(ref Message aMessage)
{
base.WndProc(ref aMessage);
if (aMessage.HWnd != iHandle)
return false;
if (aMessage.Msg == WM_SETTEXT)
{
MessageBox.Show("VLC window text changed!");
}
}
}
I have checked with Microsoft Spy++ that WM_SETTEXT message is sent by VLC player but my code doesn't seem to get the work done. I've refered mainly to:
http://www.codeproject.com/kb/dotnet/devicevolumemonitor.aspx
I'm trying to make this work for some time with no success. What am I doing wrong? What I am not doing? Maybe there is easier way to do this?
My initial goal is to catch when VLC player (that could be playing somewhere in the background and is not emmbed in my application) repeats its playback (have noticed that WM_SETTEXT message is sent then and I'm trying to find it out like this).
Is your code even being reached? I'm guessing you've inherited from NativeWindow but haven't made your actual windows inherit from your VLCFilter class. Which is in fact going to be a really difficult thing because you'll probably have to rewrite System.Windows.Forms.Form... (I'm guessing there's inheritance in there, but honestly not sure the internal structure in the framework.)
Perhaps you should inherit from Form instead and then have your forms inherit from your new class instead of Form?
I suppose, you could use hook techniques. It's designed for such cases.
Also, this links could be useful, despite they are easiely googled.
http://www.codeproject.com/KB/cs/netwin32hooks.aspx
http://www.codeproject.com/KB/system/WilsonSystemGlobalHooks.aspx
Hello and thanks for Your answers. ;)
Following the: http://www.codeproject.com/KB/system/WilsonSystemGlobalHooks.aspx did the trick and now I'm hooked up to event i wanted. Everything works fine, there's just one glitch: when ovverriding WndProc it starts recieving messages as soon as form is created. Is there a way to temporarily disable WndProc from recieving those messages and enable only when i want to get them?
Do your stuff before the call to the base implementation, else values in Message could have changed.
Somewhere in your code, you should be making a call to NativeWindow.AssignHandle. If you aren't (or if you're passing the wrong handle), then your overridden WndProc won't be called.
Edit: However, because VLC is running in a separate process, this technique won't work. The documentation for the NativeWindow.AssignHandle method states:
Note:
The handle to assign cannot be in a different application process.

How do I get a System.Windows.Form instance from its Win32 handle?

The following code implements a simple singleton that ensures only 1 instance of my application can be run. However, if another instance is started, I need to be able to grab that instance's command-line arguments, pass them to the initial instance, then terminate the second instance.
The issue comes in when I'm attempting to get hold of the first instance of the application. Once I've found the handle of that instance's main form, I pass it to the Control.FromHandle() method, expecting to get back a MainForm. Instead, the return value is always null. (Control.FromChildHandle() gives the same result.)
Therefore, my question is simply: what am I doing wrong? And is this even possible in .NET?
public class MainForm : Form
{
[DllImport("user32")]
extern static int ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32")]
extern static bool SetForegroundWindow(IntPtr hWnd);
private Mutex singletonMutex;
private void MainForm_Load(object sender, EventArgs e)
{
bool wasCreated;
singletonMutex = new Mutex(false, Application.ProductName + "Mutex", out wasCreated);
// returns false for every instance except the first
if (!wasCreated)
{
Process thisProcess = Process.GetCurrentProcess();
Process[] peerProcesses = Process.GetProcessesByName(thisProcess.ProcessName.Replace(".vshost", string.Empty));
foreach (Process currentProcess in peerProcesses)
{
if (currentProcess.Handle != thisProcess.Handle)
{
ShowWindowAsync(currentProcess.MainWindowHandle, 1); // SW_NORMAL
SetForegroundWindow(currentProcess.MainWindowHandle);
// always returns null !!!
MainForm runningForm = (MainForm) Control.FromHandle(currentProcess.MainWindowHandle);
if (runningForm != null)
{
runningForm.Arguments = this.Arguments;
runningForm.ProcessArguments();
}
break;
}
}
Application.Exit();
return;
}
}
Single-instance apps are well supported by the .NET framework. Check this thread for an example that does exactly what you need.
Control.FromHandle isn't going to work because the control you're looking for is in another process (and therefore in another appdomain).
You already have the WindowHandle but it's use is limited to the Win32 API. Nothing from WinForms is going to work.
You can send (WM_) messages but it's hard to get data across.
Options
use something low-level with a
temp-file.
use remoting (WCF)
Try the following
var form = (Form)(Control.FromHandle(myHandle));
EDIT
Re-read your question and realized you are looking at a handle in another process. There is no way to convert a handle in another process to a Form instance in the current process. My solution will only work for handles in the same process.
The only way to get ahold of the Form instance is to use Remoting. But that will require cooperation on the part of both processes which does not appear to be what you are looking for.
You are really trying to implement a singleton application. There are a few examples in the Internet (sorry, haven't really tried myself), e.g.
http://www.codeproject.com/KB/cs/SingletonApplication.aspx
http://www.nathanm.com/csharp-wpf-singleton-application/
You can't call code in another process directly, you need to use some form of inter-process communication
If you are communication only between processes started by the same user on the same computer you can use window messages (using WinAPI PostMessage and overriding WndProc), otherwise I think remoting is the easiest to use in .net
I use the Microsoft.VisualBasic.dll library described in the thread that nobugz pointed to. Yes, you can use it in C#. You just override the OnStartupNextInstance and pass the command line into your program in whatever way works best for you.
This is a whole lot easier than messing around with the threads manually.

Categories

Resources