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.
Related
I am trying to send WM_CLOSE from my app to a target app to tell it to close. I am running in the VS debugger on NET 6.0.
I find the handle of the target app and send a WM_CLOSE message to its handle, but Spy++ says that the message never arrives at the target window, and so of course the target window does not close.
I know I have the right target handle because I can see it in Spy++ (I tag the target window title bar manually with the Spy++ crosshairs), and they match. Also when I get the window title from the handle, it gives the same window title visible in the title bar of the target app. And the return code from the PostMessage call is zero, so PostMessage is saying that it sent the message.
But neither PostMessage nor SendMessage sends a message that is visible in the messages for the target window that are being logged in real-time in the Spy++ rolling display.
Why is my WM_CLOSE message not: (1) visible in Spy++, and (2) not reaching the target app?
public static void
SendCloseToHandle(IntPtr handle) {
var result = PostMessage(handle, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
if (result != 0) {
FormAppendTextContent($"WM_CLOSE failed to send. {result:X}");
return;
}
//SendMessage(handle, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
var title = GetWindowTitle(handle);
FormAppendTextContent($"Posted WM_CLOSE message to {title} '{handle:X}'");
}
And the return code from the PostMessage call is zero, so PostMessage is saying that it sent the message.
That's just wrong. The PostMessage function returns zero on failure. So, assuming your code (as presented) doesn't show the error message, then the PostMessage call is failing, and WM_CLOSE isn't being sent to the target.
You need to change your error-handling to use if (result == 0) { ... and then, in that block, call the GetLastError function to retrieve the error code. If that is 5 (as I suspect), then the call is failing due to incorrect UIPI access, and you will need to address that issue as described in this Q/A: Cross-process PostMessage, UIPI restrictions and UIAccess=”true”.
Note: Calling the GetLastError WinAPI function from C# and/or .Net projects is not trivial; for an in-depth discussion of how to properly do so, see here: WinApi - GetLastError vs. Marshal.GetLastWin32Error.
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.
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);
}
I have a Windows Service written in C# that handles all of our external hardware I/O for a kiosk application. One of our new devices is a USB device that comes with an API in a native DLL. I have a proper P/Invoke wrapper class created. However, this API must be initialized with an HWnd to a windows application because it uses the message pump to raise asynchronous events.
Besides putting in a request to the hardware manufacturer to provide us with an API that does not depend on a Windows message pump, is there any way to manually instantiate a message pump in a new thread in my Windows Service that I can pass into this API? Do I actually have to create a full Application class, or is there a lower level .NET class that encapsulates a message pump?
Thanks all for your suggestions. Richard & overslacked, the link you provided in the comments was very helpful. Also, I did not have to allow the service to interact with the desktop in order to manually start a message pump with Application.Run. Apparently, you only need to allow the service to interact with the desktop if you want Windows to start a message pump automatically for you.
For everyone's edification, here is what I ended up doing to manually start a message pump for this 3rd party API:
internal class MessageHandler : NativeWindow
{
public event EventHandler<MessageData> MessageReceived;
public MessageHandler ()
{
CreateHandle(new CreateParams());
}
protected override void WndProc(ref Message msg)
{
// filter messages here for your purposes
EventHandler<MessageData> handler = MessageReceived;
if (handler != null) handler(ref msg);
base.WndProc(ref msg);
}
}
public class MessagePumpManager
{
private readonly Thread messagePump;
private AutoResetEvent messagePumpRunning = new AutoResetEvent(false);
public StartMessagePump()
{
// start message pump in its own thread
messagePump = new Thread(RunMessagePump) {Name = "ManualMessagePump"};
messagePump.Start();
messagePumpRunning.WaitOne();
}
// Message Pump Thread
private void RunMessagePump()
{
// Create control to handle windows messages
MessageHandler messageHandler = new MessageHandler();
// Initialize 3rd party dll
DLL.Init(messageHandler.Handle);
Console.WriteLine("Message Pump Thread Started");
messagePumpRunning.Set();
Application.Run();
}
}
I had to overcome a few hurdles to get this to work. One is that you need to make certain to create the Form on the same thread that you execute Application.Run. You also can only access the Handle property from that same thread, so I found it easiest to simply initialized the DLL on that thread as well. For all I know, it is expecting to be initialized from a GUI thread anyway.
Also, in my implementation, the MessagePumpManager class is a Singleton instance, so that only one message pump runs for all instances of my device class. Make sure that you truly lazy-initialize your singleton instance if you start the thread in your constructor. If you start the thread from a static context (such as private static MessagePumpManager instance = new MessagePumpManager();) the runtime will never context switch into the newly created thread, and you will deadlock while waiting for the message pump to start.
You have to make a Form, Windows services do not interact with the desktop by default, so you have to set the service to interact with the desktop and installing it can be a bit of a pain. The Form will not be visible though. Microsoft has been deliberately making this harder and harder to do because of security issues.
Just make a message-only window, denoted by the HWND_MESSAGE parameter in the call to CreateWindowEx. Granted, this is C code, but you can easily make these structs and P/Invoke calls in C#.
WNDCLASS w;
HWND handle;
w.hInstance = (HINSTANCE)GetModuleHandle(...); // Associate this module with the window.
w.lpfnWndProc = ... // Your windowproc
w.lpszClassName = ... // Name of your window class
RegisterClass(&w)
handle = CreateWindowEx(0, w.lpszClassName, w.lpszClassName, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, wc.hInstance, NULL);
I have a window which is hidden and I would like to send a keypress to it. An example of what I'm trying to achieve is an app that will send the key F5 to a web browser which wasn't the active window. The web browser would know to refresh the current page when the F5 keystroke is received.
I would also like to send a combination of keys to an application, e.g. Ctrl+S. One example of this usage could be a timed auto-save feature to use with applications which don't have autosave. This would spare me having to remember to save every 5 mins.
C# is my technology, does this sound realistic?
This CodeProject article shows how to send keystrokes to an external application (with C# source code).
You can use WinApi for this purpose. SendMessage or PostMessage method to send desired message to your application.
Here's C# definition of SendMessage
[DllImport("user32.dll")]
public static extern int SendMessage(
int hWnd, // handle to destination window
uint Msg, // message
long wParam, // first message parameter
long lParam // second message parameter
);
and define the message that you want to send like:
public const uint %WM_MESSAGE_HERE% = %value%;
Check PInvoke - great resource containing WinApi method definitions for .NET
This should be possible, look at www.pinvoke.net and search for sendinput, that page should tell you everything you need to know, also how to find the window (look for findwindow)