SendMessage vs. WndProc - c#

I'm trying to extend TextBox control to add watermarking functionality. The example I've found on CodeProject is using imported SendMessage function.
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, uint wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
void SetWatermark()
{
SendMessage(this.Handle, 0x1501, 0, "Sample");
}
I'm wondering why not use protected WndProc instead
void SetWatermark()
{
var m =new Message() { HWnd = this.Handle, Msg = 0x1501, WParam = (IntPtr)0, LParam = Marshal.StringToHGlobalUni("Sample") };
WndProc(ref m);
}
Both seem to work fine. Almost all examples I've seen on internet use SendMessagefunction. Why is that? Isn't WndProc function designed to replace SendMessage?
P.S. I don't know right to convert string to IntPtr and found that Marshal.StringToHGlobalUni works ok. Is it right function to do this?

WndProc does not replace SendMessage, it is the .NET equivalent of WindowProc. WndProc is called by your application's message pump (which receives messages that are sent or posted by SendMessage or PostMessage) to process them. By calling WndProc directly, you by-pass the special message handling that Windows performs, such as bundling WM_PAINT messages, and can potentially cause some nasty problems where messages appear out of the order that they're expected by windows within your application.
As stated in MSDN,
All messages are sent to the WndProc
method after getting filtered through
the PreProcessMessage method.
The WndProc method corresponds exactly
to the Windows WindowProc function.
For more information about processing
Windows messages, see the WindowProc
function documentation in the MSDN
library at
http://msdn.microsoft.com/library.
By calling it directly, you deprive the system of a chance to perform preprocessing or any other handling of that message. The .NET framework runs on top of Windows and without sending or posting the message, the underlying system cannot do anything with that message, so you lose out on anything the underlying system might do for you.

Related

c# What are these volume changing codes/messages

I found this script to change the System sound volume and it works. But what are these constant volume codes called and where can I find a full list of these codes that do more things.
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
//Volume codes, or messages, or whatever they are called
const int VOLUME_MUTE = 0x80000;
const int VOLUME_DOWN = 0x90000;
const int VOLUME_UP = 0xA0000;
SendMessage(this.Handle, 0x319, IntPtr.Zero, (IntPtr)VOLUME_UP);
These are AppCommand messages.
0x319 is the Win32 Windows MSG for WM_APPCOMMAND, and the messages are more accurately APPCOMMAND_VOLUME_UP, etc...
AppCommand messages are messages sent to windows, which are handled at a global level and perform certain application functions. These tend to be linked to Keyboard hotkeys and mouse button functions.
Your app gets first crack at processing any such messages, and if you do not handle them then your apps parent does. If that doesn't handle them, then eventually it gets sent to a global message hook to process them. The key point here is that other windows can trap these messages, so it's not a guarantee that sending these messages will accomplish the task. Just like you might have seen where pressing the volume up or down on your keyboard might not always work when certain windows have focus.
You can find the details for all the messages in the Win32 API reference:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646275(v=vs.85).aspx

MFC-hosted WPF usercontrol how to close parent window on button press

I'm trying to get a legacy MFC application and a new WPF usercontrol to shut down a dialog window based on a button press in the WPF usercontrol. In essence, I would like some tips on how I can get the DoModal() function of MFC return.
For various reasons the dialog is a MFC CDialog started via DoModal, which hosts a single WPF component and nothing else. This component then has a button which will need to close the CDialog after doing some various tasks. This application is an.. exiting.. case of legacy and it's really hard to track control flow and where the actual message pumps driving this thing resides. It's also full of #defines that makes everything twice as difficult as it should be. I think I have identified the message pump, so I think I can insert something into that to make it close - if I can get to that from the WPF control.
I do not know how to send a windows message out from the usercontrol to the host, or how to get the HWND of the host from the usercontrol. I'm sure there is a way to get that, or another better way of communicating?
Is the correct approach to send a WM_CLOSE message to the parent HWND? Or perhaps I can send a WM_USER to the dialog pump and handle the actual closing there?
Try this. It should work for any WPF element (if it's a Visual), by finding it's containing HWND, tracing up the HWND tree until it finds the root parent, then sending that a WM_CLOSE message.
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
private static extern IntPtr GetParent(IntPtr hWnd);
//I'd double check this constant, just in case
static uint WM_CLOSE = 0x10;
private void CloseContainingWindow(Visual visual)
{
// Find the containing HWND for the Visual in question
HwndSource wpfHandle = PresentationSource.FromVisual(this) as HwndSource;
if (wpfHandle == null)
{
throw new Exception("Could not find Window handle");
}
// Trace up the window chain, to find the ultimate parent
IntPtr hWindow = wpfHandle.Handle;
while (true)
{
IntPtr parentHWindow = GetParent(hWindow);
if (parentHWindow == (IntPtr)0) break;
hWindow = parentHWindow;
}
// Now send the containing window a close message
SendMessage(hWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
Declare a close event in the control. Raise the event when the close button is closed.
In your CDialog-derived class, subscribe to the event, then call EngDialog in the event handler.
WM_CLOSE should work, but you have to try it.
Another approach is to post a WM_COMMAND message to the parent dialog.
Sending a WM_USER looks too complicated.

Using WH_JOURNALRECORD and cancel does seem to return the WM_CANCELJOURNAL

I'm using C# and I've got the program successfully recording the journal messages using SetWindowsHookEx with WH_JOURNALRECORD.
My problem comes when it's time to stop. The docs show that if the user pressed CTRL-ESC or CTRL-ALT-DELETE a WM_CANCELJOURNAL message will be posted that I can watch to know when to stop. My application gets unhooked but I never seem to get a WM_CANCELJOURNAL.
I have two hooks setup. One hook to do the Journal Record and one to check for the cancel message:
IntPtr hinstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);
JournalRecordProcedure = JournalRecordProc;
journalHook = SetWindowsHookEx(WH_JOURNALRECORD, JournalRecordProcedure, hinstance, 0);
GetMessageProcedure = GetMessageProc;
messageHook = SetWindowsHookEx(WH_GETMESSAGE, GetMessageProcedure, hinstance, 0);
------
public static int JournalRecordProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0) return CallNextHookEx(journalHook, nCode, wParam, lParam);
EventMsgStruct msg = (EventMsgStruct) Marshal.PtrToStructure(lParam, typeof (EventMsgStruct));
script.Add(msg); //just a quick way to record for now
return CallNextHookEx(journalHook, nCode, wParam, lParam);
}
public static int GetMessageProc(int code, IntPtr wParam, IntPtr lParam)
{
//it comes here but how do I test if it's WM_CANCELJOURNAL ??
//code always seems to be equal to zero.. I must be missing something
return CallNextHookEx(journalHook, code, wParam, lParam);
}
I suppose you're referring to this section in the documentation:
This role as a signal to stop journal recording means that a CTRL+BREAK key combination cannot itself be recorded. Since the CTRL+C key combination has no such role as a journaling signal, it can be recorded. There are two other key combinations that cannot be recorded: CTRL+ESC and CTRL+ALT+DEL. Those two key combinations cause the system to stop all journaling activities (record or playback), remove all journaling hooks, and post a WM_CANCELJOURNAL message to the journaling application.
The problem is that the WM_CANCELJOURNAL message is not sent to the callback function you installed with SetWindowsHookEx. But unlike other WM_* messages, it is also not meant to be processed by a window procedure (WndProc in WinForms) because it is posted to the message queue of the thread and is not associated with any particular window.
Rather, the documentation advises that one must process it within an application's main loop or using a WH_GETMESSAGE hook:
This message does not return a value. It is meant to be processed from within an application's main loop or a GetMessage hook procedure, not from a window procedure.
[ . . . ]
The WM_CANCELJOURNAL message has a NULL window handle, therefore it cannot be dispatched to a window procedure. There are two ways for an application to see a WM_CANCELJOURNAL message: If the application is running in its own main loop, it must catch the message between its call to GetMessage or PeekMessage and its call to DispatchMessage. If the application is not running in its own main loop, it must set a GetMsgProc hook procedure (through a call to SetWindowsHookEx specifying the WH_GETMESSAGE hook type) that watches for the message.
In managed WinForms code, you obviously don't have any access to or control over the application's main loop. I'm not sure if adding a message filter to your application will let you handle this message or not: I haven't tried it. If it will, that's probably the route you want to take, considering the alternative, which is to install a second hook, WH_GETMESSAGE, and then in that hook procedure, listen for the WM_CANCELJOURNAL message.
Update:
In the GetMessageProc callback function, the code parameter just tells you whether the hook procedure should process the message. Virtually all of the time, it's going to be 0, which is equivalent to the symbolic constant HC_ACTION. If the code parameter is less than 0, the hook procedure should simply call the CallNextHookEx function without performing any further processing. That's basically the exact same thing you did for the JournalRecordProc callback function.
The window message is going to be found in a MSG structure, a pointer to which is passed to the callback function as the lParam parameter. But that's Win32 stuff. Don't mess with raw pointers in .NET, let the P/Invoke marshaler handle all of that dirty stuff for you. The native MSG structure is equivalent to the managed System.Windows.Forms.Message structure (the same thing used by the WndProc method), so if you declare your GetMessageProc callback function like this, things will be much simpler:
public delegate int GetMessageProc(int code, IntPtr wParam, ref Message lParam);
Then, the windows message is found as the Msg member of the Message structure. That's the value you want to compare against WM_CANCELJOURNAL:
public static int GetMessageProc(int code, IntPtr wParam, ref Message lParam)
{
if (code >= 0)
{
if (lParam.Msg == WM_CANCELJOURNAL)
{
// do something
}
}
return CallNextHookEx(messageHook, code, wParam, ref lParam);
}
Note that in order for the above call to CallNextHookEx to work, you'll also have to provide an overloaded definition of the CallNextHookEx function that matches the signature of your GetMessageProc callback function:
[DllImport("user32.dll")]
public static extern int CallNextHookEx(IntPtr hHook, int nCode,
IntPtr wParam, ref Message lParam);

Receiving Windows Message on C# from managed Dll using p/invoke

I have to call some native C functions from C# using p/invoke. So far, I had no problems marshaling the different methods and structures to C#. Where my problem resides is in the fact that many of the methods I have to call are asynchronous, and return their final results to my WinForms application through Windows Messages. For instance, I have a call to a method that has the following signature in C:
HRESULT AsyncOpenSession( LPSTR lpszLogicalName,
HANDLE hApp,
LPSTR lpszAppID,
DWORD dwTraceLevel,
DWORD dwTimeOut,
USHORT lphService,
HWND hWnd,
DWORD dwSrvcVersionsRequired,
LPWFSVERSION lpSrvcVersion,
LPWFSVERSION lpSPIVersion,
ULONG lpRequestID );
Where lpszAppID expects to receive the NAME of my application (MyApp.exe) and hWnd is a pointer to the WINDOW HANDLE of my application, which I obtain with a call to
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
public static IntPtr GetCurrentWindowHandle()
{
IntPtr handle = GetForegroundWindow();
return handle;
}
The imported signature is:
[DllImport("MSXFS.DLL", BestFitMapping = true, CharSet = CharSet.Auto, SetLastError = true)]
private static extern int AsyncOpenSession([MarshalAs(UnmanagedType.LPStr)] string lpszLogicalName,
IntPtr hApp,
[MarshalAs(UnmanagedType.LPStr)] string lpszAppID,
UInt32 dwTraceLevel,
UInt32 dwTimeOut,
out Int32 lphService,
IntPtr hWnd,
UInt32 dwSrvcVersionsRequired,
out WFSVersion lpSrvcVersion,
out WFSVersion lpSPIVersion,
out Int32 lpRequestID);
I call the method as follows:
Int32 r = AsyncOpenSession(lpszLogicalName,
appHanlder,
"MyApp.exe",
dwTraceLevel,
dwTimeOut,
out hServ,
GetCurrentWindowHandle(),
dwSrvcVersionsRequired,
out srvcVersion,
out spiVersion,
out requestID);
Initially, the method call returns a result code that indicates the requested call has been put to the execution queue. Any time after the original call the native Dll completes the execution of the request and send a Windows Message to the application by means of its windows handle and name. The Windows Message code for this method is defined as follows:
#define WM_USER 0x0400
#define OPEN_SESSION_COMPLETE (WM_USER + 1)
More info about Windows Messages here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms644931(v=vs.85).aspx
In my application I have overridden the method WndProc to be able to control the Windows Messages as follows:
protected override void WndProc(ref Message m)
{
const int wm_user = 0x0400;
const int OPEN_SESSION_COMPLETE = wm_user + 1;
switch (m.Msg)
{
case OPEN_SESSION_COMPLETE:
txtEventsState.Text = m.ToString();
break;
}
base.WndProc(ref m);
}
But I never receive a Windows Message with this code. There are no error in logs or event viewer. I know my call to the method succeeded because I do not get any error code and because it is mandatory to have an opened session before calling other methods, and I can call other methods that need a session handler successfully (no error code either).
My imported signature and call to the method is in a class library project that my application is referencing. Am I doing something wrong here? Can anyone give a light on what could be happening? I do not have access to the native code, just to a test application the shows that the Windows Messages are being sent by the native C Dll.
Thank to everybody in advance.
So, here it the deal: GetForegroundWindow() is not retriving the correct Window Handle. It happens that my application has two windows. One is the main form that I use for my test and the other displays all results and logging I am doing. So, the method was actually returning the Handle of the loggin window and not the form windows.
Using this.Handle and passing that value to the native methods made everything work correctly.
Thanks to Hans for pointing this out.

C# Using PostMessage

I'm trying to send a key to an application. I tested the Handlewindow value used breakpoints to understand what I'm doing wirong but i cant find a solution. To be more detailed, its a little game and when I activate the chatbar ingame, the key I want to send will be written there, but I want to make it functionaly when I am playing to use the commands. The game doesnt have a guard or some protections.
Here is my code:
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
const uint WM_KEYDOWN = 0x0100;
private void button1_Click(object sender, EventArgs e)
{
string pName = textBox1.Text;
//Get Processes
Process[] processes = Process.GetProcessesByName(pName);
//Main part
foreach (Process p in processes)
if (p.ProcessName == (string)pName)
{
PostMessage(p.MainWindowHandle, WM_KEYDOWN, (int)Keys.W, 0);
}
}
Like I said, it can be sent 1000000 times successfully but nothing happens.
Is there another way how I can send keys to an Windows application that works minimized or even hidden? It should be only send to my app.
If i understand correctly, you want to send keys to a game.
In this case i think that the game is not fetching the keys from the Windows Message queue, it is probably reading the keyboard status directly using DirectX or similar ways.
See this similar question:
Send Key Strokes to Games
You can't fake messages like this and expect it to work reliably - all you are doing is sending a message, nothing fancy.
What this means is that if the target program tests for input in a different way (for example by directly checking the key state, reacting to different messages or just using a completely different mechanism) the target program at best may just completely ignore your input and at worst get totally confused.
Use the SendInput function instead (I'm not sure if this exposed as part of the .Net framework or not).
Some interesting blog articles which are fairly relevant:
You can't simulate keyboard input with PostMessage
Simulating input via WM_CHAR messages may fake out the recipient but it won't fake out the input system
Just note that the correct import of PostMessage is:
[DllImport("user32.dll")]
static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
Sending the keys like you are doing might not work, but you can use spy++ to capture the actual messages that are being posted to the application by windows and use those.
https://learn.microsoft.com/en-us/visualstudio/debugger/introducing-spy-increment?view=vs-2022
Often programmers will react to KeyUp events rather than down; did you try sending WM_KEYUP?
You should be using SendInput to simulate input into another window.
See this blog post for more details.

Categories