Event when postmessage() receives c# - c#

I have an unmanaged DLL with delphi and integrated there are some events that fire at certain points on runtime.
On its manual it says that it calls the Win32 api PostMessage().
When the event is fired a text message is sent and wParam and IParam have some other information.
How can i fire an event from my c# application when a message from the dll is posted and ofcourse get the information?
UPDATE:
Trying to describe what the manual is saying:
So i have a function called init() which accepts the handle parameter which i am passing as follows:
wr.Init((IntPtr)this.Handle);
from the c# Winform application to the unmanaged dll which returns true if it is fine and that is what i am seeing.
Now the manual says:
When event (from the dll) is fired it sends a windows message to the window handle supplied with the Init function, infact PostMessage() Win32 API is called.
And i have the list of the messages that are sent on the PostMessage() for example:
TextMessage: WM_TECH_BROKENLINE
wParam: Point (which is a number displaying for example where is broken)
IParam: 0
Now as i explained i want that when a message like the one above is posted from the dll i fire an event from c# and ofcourse get the text message and the Params

The steps necessary are as follows:
Obtain a window handle for a window in your C# program.
Pass that window handle to the unmanaged DLL so that it knows where to send the messages.
In your C# code, add code to receive and respond to the message arriving.
As an alternative to the above you might prefer to keep the GUI windows of your program separate from this message passing mechanism. In which case do the following:
Create a subclass of NativeWindow to handle incoming messages.
Create an instance of your class and pass its window handle to the unmanaged DLL.
Override WndProc in your window class and then handle the message there. At which point you can surface it as an event.
This latter approach gives better separation of your interop code from your GUI, and will allow you to make your wrapper less tangled with your GUI.
In order to describe any of this in more detail, one would need to know the details of your program. For instance, is it WinForms or WPF? Does your program have a window at hand to receive messages? How is the string encoded? In which message arg does it arrive? How is memory for the string deallocated if the message is delivered asynchronously?
I suspect you'll have answers to some of these questions, but perhaps not all. You now know at a high level what you need to do, but now need to find out more details for your own program.

Try overriding WndProc method of the form which handle you're passing to the dll. An example can be found here: https://msdn.microsoft.com/en-us/library/system.windows.forms.control.wndproc(v=vs.110).aspx
A simplified example for your case (add it into your form class):
const int WM_TECH_BROKENLINE = 0x0401; // It's WM_USER + 1 in my example.
// Set it according to you dll's spec
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_TECH_BROKENLINE)
{
long dataPassedFromTheDll = (long)m.WParam;
// Your stuff here:
this.Text = string.Format("The dll sent us: {0}", dataPassedFromTheDll);
}
base.WndProc(ref m);
}

Related

Clipboard Monitor

I have a problem. I am trying to use the ClipboardMonitor for my C# Application. The goal is to monitor every change in the clipboard.
To start monitoring:
AddClipboardFormatListener(this.Handle);
To stop the listener:
RemoveClipboardFormatListener(this.Handle);
And the override WndProc() method:
const int WM_DRAWCLIPBOARD = 0x308;
const int WM_CHANGECBCHAIN = 0x030D;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_DRAWCLIPBOARD:
IDataObject iData = Clipboard.GetDataObject();
if (iData.GetDataPresent(DataFormats.Text))
{
ClipboardMonitor_OnClipboardChange((string)iData.GetData(DataFormats.Text));
}
break;
default:
base.WndProc(ref m);
break;
}
}
And of course the DLL import:
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool AddClipboardFormatListener(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool RemoveClipboardFormatListener(IntPtr hwnd);
But when putting a breakpoint at the method call ClipboardMonitor_OnClipboardChange and changing the clipboard, I never get the method called.
How can I change my code so that I receive a WM_ message notifying me that the clipboard has changed?
The problem is that you're handling the wrong window message. Quoting the documentation for AddClipboardFormatListener:
When a window has been added to the clipboard format listener list, it is posted a WM_CLIPBOARDUPDATE message whenever the contents of the clipboard have changed.
With that knowledge, change the code to:
const int WM_CLIPBOARDUPDATE = 0x031D;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_CLIPBOARDUPDATE:
IDataObject iData = Clipboard.GetDataObject();
if (iData.GetDataPresent(DataFormats.Text))
{
string data = (string)iData.GetData(DataFormats.Text);
}
break;
default:
base.WndProc(ref m);
break;
}
}
SharpClipboard as a library could be of more benefit as it encapsulates the same features into one fine component library. You can then access its ClipboardChanged event and detect various data-formats when they're cut/copied.
You can choose the various data-formats you want to monitor:
var clipboard = new SharpClipboard();
clipboard.ObservableFormats.Texts = true;
clipboard.ObservableFormats.Files = true;
clipboard.ObservableFormats.Images = true;
clipboard.ObservableFormats.Others = true;
Here's an example using its ClipboardChanged event:
private void ClipboardChanged(Object sender, ClipboardChangedEventArgs e)
{
// Is the content copied of text type?
if (e.ContentType == SharpClipboard.ContentTypes.Text)
{
// Get the cut/copied text.
Debug.WriteLine(clipboard.ClipboardText);
}
// Is the content copied of image type?
else if (e.ContentType == SharpClipboard.ContentTypes.Image)
{
// Get the cut/copied image.
Image img = clipboard.ClipboardImage;
}
// Is the content copied of file type?
else if (e.ContentType == SharpClipboard.ContentTypes.Files)
{
// Get the cut/copied file/files.
Debug.WriteLine(clipboard.ClipboardFiles.ToArray());
// ...or use 'ClipboardFile' to get a single copied file.
Debug.WriteLine(clipboard.ClipboardFile);
}
// If the cut/copied content is complex, use 'Other'.
else if (e.ContentType == SharpClipboard.ContentTypes.Other)
{
// Do something with 'e.Content' here...
}
}
You can also find out the application that the cut/copy event occurred on together with its details:
private void ClipboardChanged(Object sender, SharpClipboard.ClipboardChangedEventArgs e)
{
// Gets the application's executable name.
Debug.WriteLine(e.SourceApplication.Name);
// Gets the application's window title.
Debug.WriteLine(e.SourceApplication.Title);
// Gets the application's process ID.
Debug.WriteLine(e.SourceApplication.ID.ToString());
// Gets the application's executable path.
Debug.WriteLine(e.SourceApplication.Path);
}
There are also other events such as the MonitorChanged event which listens whenever clipboard-monitoring is disabled, meaning that you can enable or disable monitoring the clipboard at runtime.
In addition to all this, since it's a component, you can use it in Designer View by dragging-and-dropping it to a Windows Form, making it super easy for anyone to customize its options and work with its inbuilt events.
SharpClipboard seems to be the very best option for clipboard-monitoring scenarios in .NET.
Just to add to theB's excellent answer ...
The WM_DRAWCLIPBOARD message is an older message used after setting up a "Clipboard Viewer Window." This dates back at least to Windows 95. Starting with Windows Vista and Windows Server 2008, the "Clipboard Format Listener" API was added.
The old Clipboard Viewer Window required more work:
Add your window into the chain of objects receiving clipboard messages by calling the SetClipboardViewer() API.
You receive a WM_CHANGECBCHAIN message when another window in the chain adds or removes itself from the chain, and you are required to process that message by passing it on to the next window in the chain with the SendMessage() API.
When you receive the WM_DRAWCLIPBOARD message, you also needed to pass the message along to the next object (hwnd) in the chain, using the SendMessage() API. Then you could process the changed contents of the Clipboard.
When you close your program, you needed to remove it from the chain, using the ChangeClipboardChain() API.
The newer Clipboard Format Listener only requires:
Start monitoring the clipboard by calling the AddClipboardFormatListener() API.
Wait for the WM_CLIPBOARDUPDATE message to notify you of changed clipboard contents.
Before closing your program, stop the monitoring by calling RemoveClipboardFormatListener().
All issues relating to the chain of notification are handled automatically by Windows.
Another key difference: WM_DRAWCLIPBOARD is a sent (non-queued) message, which will have to be processed by your program immediately. WM_CLIPBOARDUPDATE is a posted (queued) message, which will be added to a queue, where it will be handled after your program is finished executing its current process.
This means that when the WM_DRAWCLIPBOARD message comes through, your program will have to process it immediately, even if you are in the middle executing code. For example, let's say you've created a clipboard monitor that allows you to save and retrieve past clips, and load those past clips to the clipboard. You've decided to use the older SetClipboardViewer() API, which will trigger the WM_DRAWCLIPBOARD message when the clipboard changes. When you execute the code to load the old clip back into the clipboard, you have just changed the clipboard contents, which will trigger the message. If you are stepping through your C# code line by line in Visual Studio, the Clipboard.SetText() method will trigger the message and you will be taken immediately back to your WndProc() method, starting that whole process all over again. When the WndProc() method is finished, you will continue on to the next line of code after Clipboard.SetText().
If instead, you use the newer AddClipboardFormatListener() API, you can use the Clipboard.SetText() method also, but it won't trigger an immediate message. The WM_CLIPBOARDUPDATE message is added to a queue, and it won't be processed by your program until your code is finished executing. If you are stepping through the code line by line, you can execute the Clipboard.SetText() method and simply move on to the next line. When your program finishes whatever it is doing, you will then be taken back to the WndProc() method to deal with the WM message. Here is some Microsoft documentation about message queuing and routing:
Windows Message Routing
There will be times when our program will need to treat WM messages in different ways, depending on what has triggered the message. If the end user copies a new clip into the clipboard, we will want to process that WM message. If the end user uses our program to load a saved old clip back into the clipboard, we will want our program to ignore the WM message that results from that. One way to ignore a WM message is to create a public class integer property called "Ignore". Every time our code changes the clipboard contents (instead of the user copying something new), we FIRST increment (increase) the Class.Ignore property. We add an If statement in the WndProc() method to only process a WM clipboard message if the ignore property is zero. If Class.Ignore is greater than zero, WndProc() will ignore the message. Every time it ignores a message, it also decrements (decreases) the Class.Ignore integer. That is how the program can know if the WM_ message was generated from copying a new clip, or from an old clip being re-loaded into the clipboard.
Just remember that the newer AddClipboardFormatListener() API requires Windows Vista (Client machine) or Windows Server 2008 (server machine) or later. But according to this article in August, 2021, only about 0.5% or less of Windows users are still on XP or older.
Here is a writeup for implementing the Clipboard Format Listener in C#:
Monitor for clipboard changes using AddClipboardFormatListener
And here is the Microsoft Documentation (in C++) for anyone interested:
Microsoft Documentation on monitoring the clipboard
Best of luck to everyone in their coding.

Is there a way Usercontrols or IMessageFilter implementations can get WM_DEVICECHANGE messages?

I'm by no means a windows message expert - so forgive me if I'm asking something stupid, but I wasn't able to help myself:
I have a form hooking up to Windows Messages by overriding WndProc to get notified when usb devices are connected or removed (WM_DEVICECHANGE). Everything works fine.
When I moved this code to an underlying user control, I was surprised that it wasn't called any more for WM_DEVICECHANGE messages - other messages do get catched though. Here's the code I used to override.
private const int WM_DEVICECHANGE = 0x0219;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_DEVICECHANGE:
//Device changed - do something
//never passing here when overriding in UserControl
//works when overridden in my main form.
break;
}
base.WndProc(ref m);
}
Further looking for ways to resolve without simply putting the code back in the form, I found the IMessageFilter Interface but implementing and registering it with Application.AddMessageFilter didn't resolve my issue - in fact the WM_DEVICECHANGE message didn't even pass on my PreFilterMessage when I registered my main window - it only passes the WndProc override.
So obviously my understanding of window-messages is far from complete, but what did I miss?
Is there a way to catch WM_DEVICECHANGE without passing by the main window? And to help me for the future: Where would I have to look into to find a good reference which Window Messages get sent where and where not and why?

WinForm not receiving messages except right after creation

I've got some unmanaged code sitting in a DLL. It publishes some methods that my calling (managed) code uses to hook into some COM notifications. Rather than deal with unmanaged code calling back into managed code, I've created a hidden Control derived object and am passing its handle property which the unmanaged code then uses as a parameter to SendMessage.
My Control derived class:
class InteropWindow : Control
{
//delegate
private Handler m_callback;
//window message
private uint m_message;
public InteropWindow(Handler callback, uint message)
: base()
{
m_callback = callback;
m_message = message;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == m_message)
{
m_callback(new IntPtr((int)m.WParam));
}
base.WndProc(ref m);
}
}
Relevant line in unmanaged code:
SendMessage(m_notify, m_window_message, (WPARAM)pData, 0);
m_window_message & m_message are the same (both from RegisterWindowMessage), and m_notify == InteropWindow.Handle (pData varies, but is used as an opaque handle in the managed code). The unmanaged code is being invoked. These facts have been confirmed via debugging.
Shortly after I create the InteropWindow, the calls to SendMessage succeed. Afterwards (seconds later) the messages stop getting to WndProc, though there is no indication of any error.
The question is, what am I doing wrong here?
I've ruled out lifecycle issues (to the best of knowledge anyway), and played with HandleRef to no avail.
Edit the second.
I've re-written this to use function calls instead, which while fraught with its own perils, works a bit more like I'd expect. I've come to suspect this is a COM threading issue, but that's just a gut feeling.
Did you try passing your managed window's handle as a HandleRef? C# can marshal a HandleRef as an IntPtr and vice versa, I've seen Microsoft use that trick quite a bit when decompiling some of their stuff.
You can also load up a .Net profiler and watch the GC. It would be nice to know if your app is breaking right after a collect.

Can I send / receive window messages without a window?

I'm writing a .NET wrapper around an old MFC-based library we have. It's based around a class that sends notifications using window messages; it has a function that lets the user pass in a handle to a window, and that window will receive the messages.
I could just require the users of my wrapper to subclass Control and pass their control's handle in order to receive messages, but that's horrible. I want my wrapper class to have events which fire whenever the old library sends a message, and then I can do the decoding of the message into something sensible. But, I don't want my wrapper class to have to be a control.
Is there a way for me to create a 'dummy' window handle, and receive the messages sent to that handle, without creating a window?
There is a concept of MessageOnly Windows which can help you. You may create an internal message only window in your wrapper class and pass this handle to the old library.
You could try creating a thread with a message pump and sending your messages to that. The thread then raises any necessary events that you want to handle in your C# code.
You can't create a window handle without having a window, since the window handle is the window as far Windows is concerned, but you can make a window without the WS_VISIBLE flag set, and use it for message relaying only. I use that technique sometimes to do cross-thread communication in MFC-only applications (don't tell anyone ;) ). You could derive a (c++) class from CWnd, let it process the messages, and call functions or emit signals for every message that is received. I guess that would make it work with your C# code although I don't have experience with that.
To create a window that does not display, but receives messages, use the following code.
The header file:
#pragma once
#include <afxwin.h>
#define ID_NOTHING_MGS WM_USER + 1001
class control : public CWnd
{
public:
control();
LRESULT control::nothing(WPARAM wp, LPARAM lp);
DECLARE_MESSAGE_MAP()
};
Function implementation in header file.
#include "control.h"
BEGIN_MESSAGE_MAP(control, CWnd)
ON_MESSAGE(ID_NOTHING_MGS, nothing)
END_MESSAGE_MAP()
control::control()
{
// ref1:https://codeantenna.com/a/s6NmJb6YD8
// ref2:https://www.cnblogs.com/greatverve/archive/2012/11/04/mfc-message.html
this->CreateEx(WS_EX_NOACTIVATE, AfxRegisterWndClass(0), _T("windowName"),
WS_CAPTION | WS_OVERLAPPEDWINDOW | WS_EX_NOPARENTNOTIFY,// | WS_VISIBLE,
0, 0, 400, 350, NULL, NULL, NULL);
}
LRESULT control::nothing(WPARAM wp, LPARAM lp)
{
MessageBox(TEXT("control receive message"));
return 0;
}
Since control inherit from the CWnd class, it can receive messages。CreateEx function,creates the specified window and appends it to the CWnd object. If you do not call CreateEx, the window handle value of the created Control object is 0.
No 'WS_VISIBLE' is used, so the window is not visible.

Message Pumps and AppDomains

I have a a C# (FFx 3.5) application that loads DLLs as plug-ins. These plug-ins are loaded in separate AppDomains (for lots of good reasons, and this architecture cannot change). This is all well and good.
I now have a requirement to show a Dialog from one of those plug-ins. Bear in mind that I cannot return the dialog Form to the main application and have it displayed there (the current infrastructure doesn't support it).
Failure 1
In my DLL I created a Form and called Show. The dialog outline showed up but did not paint and it doesn't respond to mouse events. I assumed that this is becasue the DLL is in a separate AppDomain and the message pump for the app is somehow unable to dispatch messages to the new Form.
Failure 2
In my DLL I created a Form and called ShowDialog, which by all rights should create an internal message pump for the dialog.. The dialog is displayed and responded to clicks (hooray), but it appears that the primary app no longer is processing or dispatching windows messages because it quits painting and no longer responds to mouse events. For some reason now it seems that the main app's message pump is not dispatching.
Failure 3
In my DLL I created a Form and called Application.Run. This will certainly create a complete second message pump. I get the same behavior as Failure 2 - the Dialog behaves, but the calling app does not.
Any thoughts on what exactly is going on here and how I might go about showing a dialog from the other AppDomain's DLL and have both the caller and the callee still respond and paint properly?
Try using appdomain1's main form's BeginInvoke with a delegate that displays the form from appdomain2. So in Pseudocode:
Appdomain1:
AppDomain2.DoSomething(myMainForm);
AppDomain2:
DoSomething(Form parent)
{
Form foolishForm = new Form();
parent.BeginInvoke(new Action( delegate { foolishForm.Show(); } ));
}
The code may not be perfect, but it demonstrates the concept.
By the way, if you are having problems passing forms around because of remoting you can:
public class Container<T> : MarshalByRefObject
{
private T _value;
public T Value { get { return _value; } set { _value = value; } }
public Container() { }
public Container(T value) { Value = value; }
public static implicit operator T(Container<T> container)
{
return container.Value;
}
}
That will contain object you throw at it.
We have a very similarly architected application that loads DLL files and plugins. Each DLL file is loaded in a separate application domain, which is created on a separate thread. We have a third-party control in a form that would not appear unless we call System.Windows.Forms.Application.DoEvents() regularly.
Pseudo code:
<In new thread>
<Application domain created. Start called inside new application domain.>
<Start loads new DLL file, calls init function in DLL file>
<Start loops, calling DoEvents until the DLL file exits>
<Application domain unloaded>
<Thread exits>
This solved all of our GUI issues.
One thing that I've used before is implementing a DomainManager. It's possible to customize the various application domain security/binding/context's to handle complex or chicken-egg type problems with respect to pumping your data where you want ;)
I've ususally done this from a native.exe, bootstrapping the CLR through the COM interfaces (psudo code but the order and method names are correct ;):
CorBindToRuntimeEx()
SetHostControl()
GetCLRControl()
SetAppDomainManagerType("yourdomainmanger","info")
// Domain manager set before starting runtime
Start()
HostControl -- GetDomainManagerForDefaultDomain()
DomainManager -- Run()
Your domain manager can be any CLR class library, so their's not that much more native C.
A side note, if you were in WPF; I really like using the "Microsoft.DwayneNeed.Controls" method. Where you may have disperate threads with their own Dispatcher pump in the same UI control (not needing to resort to entirely new Window()'s).
The unique thing about using this approach, is that even if the primary UI thread is blocked/busy (some heavy operation, scanning the filesystem, etc...), these other threads may paint/update their UIElement's without any hiccup.

Categories