How to subscribe to clipboard events in WinApi C#? - c#

I have set a viewer which is the current process and want to recive a message WM_DRAWCLIPBOARD.
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
var result = WinapiClipboard.SetClipboardViewer(Process.GetCurrentProcess().Handle);
It says right here that i should use an-application defined function to parse that message. But it never hits the function/method.
private static IntPtr WndProc(IntPtr hWnd, uint message, IntPtr wParam, IntPtr lParam)
{
var hdc = Process.GetCurrentProcess().Handle;
var clipboard = new WinClipboard();
switch (message)
{
case WinapiClipboard.WM_DRAWCLIPBOARD:
var result = clipboard.GetUnicodeTextAsync().Result;
return IntPtr.Zero;
default:
break;
}
return WinapiClipboard.DefWindowProc(hWnd, message, wParam, lParam);
}
How i should get this message? Am i even subscribing correctly?
UPDATE:
I'm not using WinForms/WPF or any of those .NET classic framework features. All i have is .net standard 2.0 or .net core.

This is all pretty straight foward from a winforms app
Note : you cant do this from a console app (with any ease)
Declare this
[DllImport("User32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
In your form constructor
// this.handle is your forms handle
_ClipboardViewerNext = SetClipboardViewer(this.Handle);
You will then receive the WM_DRAWCLIPBOARD messages in your form by overriding WndProc
In your form
protected override void WndProc(ref Message m)
{
switch ((Win32.Msgs)m.Msg)
{
case Win32.Msgs.WM_DRAWCLIPBOARD:
// Handle clipboard changed
break;
// ...
}
// we call this so we to pass the message along
base.WndProc(ref m);
}
Another way to do this and maybe a more modern approach
Declare this
public const int WM_CLIPBOARDUPDATE = 0x031D;
public static IntPtr HWND_MESSAGE = new IntPtr(-3);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AddClipboardFormatListener(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
In your form constructor
SetParent(Handle, HWND_MESSAGE);
AddClipboardFormatListener(Handle);
In your form
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_CLIPBOARDUPDATE)
{
// handle message
}
base.WndProc(ref m);
}
if you are using a class library you'll have to create an hidden from and pass back an action or event
private class HiddenForm : Form
{
public HiddenForm()
{
SetParent(Handle, HWND_MESSAGE);
AddClipboardFormatListener(Handle);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_CLIPBOARDUPDATE)
{
// do stuff here like call event
}
base.WndProc(ref m);
}
}
Note : None of this is tested however should get you started
Update 1
SetClipboardViewer expects a window handle, not a process handle. If
you don't have a window handle, create a message-only window for the
sole purpose of receiving clipboard messages - thanks to IInspectable
Update 2
This will only ever work on the kind of machine that also always has
the full framework available. - thanks to Hans Passant

Since you are doing a winforms project override the existing Control.WndProc rather than provide a static.
protected override void WndProc(ref Message m)
{
if(WM_DRAWCLIPBOARD == msg.Msg)
{ ... }
else
base.WndProc(ref msg);
}

Related

How do I get window messages when window is out of focus/minimized (WPF)?

I'm trying to get window messages in a WPF app to be able to use the Win32 API; the problem is that every time the app goes out of focus/is minimized I'm unable to receive any more messages. I tried doing this but my implementation of it in WPF (which I'm not entirely sure of how correct it is, see code below) converts the window to a read-only window after which I can't interact with the GUI at all, it's not even there in the taskbar anymore (Not to mention how this only works if I minimize the window, it doesn't help when the app is still on the screen but out of focus).
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
private int SC_MONITORPOWER = 0xF170;
private int WM_SYSCOMMAND = 0x0112;
private int SC_MINIMIZE = 0xF020;
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_SYSCOMMAND) //Intercept System Command
{
if ((wParam.ToInt32() & 0xFFF0) == SC_MONITORPOWER)
{ //Intercept Monitor Power Message
MessageBox.Show("WORKING");
}
if ((wParam.ToInt32() & 0xFFF0) == SC_MINIMIZE)
{
ChangeToMessageOnlyWindow();
}
}
return IntPtr.Zero;
}
private void ChangeToMessageOnlyWindow()
{
IntPtr HWND_MESSAGE = new IntPtr(-3);
SetParent(new WindowInteropHelper(this).Handle, HWND_MESSAGE);
}
}

PostMessage from windows service

My issue is PostMessage windows API is not working properly as it works when running from console application.
Working code:
I have 2 application [1] is console application [2] Windows Forms application.
Requirement is I want to send message to all the running instances of application.
console application code:
class Program
{
#region Dll Imports
public const int HWND_BROADCAST = 0xFFFF;
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
#endregion Dll Imports
public static readonly int WM_ACTIVATEAPP = RegisterWindowMessage("CLOSE");
static void Main(string[] args)
{
//we tried to create a mutex, but there's already one (createdNew = false - another app created it before)
//so there's another instance of this application running
Process currentProcess = Process.GetCurrentProcess();
//get the process that has the same name as the current one but a different ID
foreach (Process process in Process.GetProcessesByName("ClientApp1"))
{
if (process.Id != currentProcess.Id)
{
IntPtr handle = process.MainWindowHandle;
//if the handle is non-zero then the main window is visible (but maybe somewhere in the background, that's the reason the user started a new instance)
//so just bring the window to front
//if (handle != IntPtr.Zero)
//SetForegroundWindow(handle);
//else
//tough luck, can't activate the window, it's not visible and we can't get its handle
//so instead notify the process that it has to show it's window
PostMessage((IntPtr)HWND_BROADCAST, WM_ACTIVATEAPP, IntPtr.Zero, IntPtr.Zero);//this message will be sent to MainForm
break;
}
}
}
}
Windows Forms application code:
public partial class Form1 : Form
{
#region Dll Imports
public const int HWND_BROADCAST = 0xFFFF;
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
#endregion Dll Imports
public static readonly int WM_ACTIVATEAPP = RegisterWindowMessage("CLOSE");
public Form1()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
//someone (another process) said that we should show the window (WM_ACTIVATEAPP)
if (m.Msg == WM_ACTIVATEAPP)
this.Close();
}
}
Above code is working as expected.
My issues start from here. I want to run the same code from windows service instead of console application. Need immediate guidance.
It seems when I run this code from windows service its not getting hold of the process or service runs in different account so message is not getting delivered.
Most probably you run your service as Local System account in session 0 and it is rather isolated for good reasons. For example you don't have access to other desktops/sessions.
You have to implement a different IPC method, e.g. pipes or memory mapped files.

PostMessage not received when application is hidden from taskbar

I use PostMessage to send message to my application if another instance is trying to open:
(CUSTOMTEXT replaced with my appname)
NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_CUSTOMTEXT_SHOWME, IntPtr.Zero, IntPtr.Zero);
And in WndProc I receive the message:
protected override void WndProc(ref Message m)
{
if (m.Msg == NativeMethods.WM_CUSTOMTEXT_SHOWME)
{
MessageBox.Show("Message received");
}
base.WndProc(ref m);
}
And NativeMethods class:
class NativeMethods
{
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_CUSTOMTEXT_SHOWME = RegisterWindowMessage("WM_CUSTOMTEXT_SHOWME");
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
}
Everything works fine, but when I hide my application from taskbar (this.ShowInTaskbar = false;) my application stops receiving that message.
Why? Is there any workaround for this?
Found alternative solution: I replaced PostMessage (asyncronous) with SendMessage (syncronous). For some reason, SendMessage gets through while PostMessage does not.
In this application it does not matter which one I use, because when the message is being sent, application just exits. If it takes little time to Windows to process this message, no harm done. Point is only that older instance of application receives this message.

Listening to another window resize events in C#

I am implementing a small application (observer) that needs to "attach" itself to the bottom of another window (observed). The latter is not a window inside the application.
At this moment I solved by getting the hWnd of the window and querying periodically in a thread the location of the observed window, moving the observer window accordingly.
However this is a very inelegant solution. What I would like to do is to listen to the resize event of the observed window so that the observer will react only when necessary.
I assume I should use a hook, and I found plenty of ways of doing it, but my lack of knowledge of the C WinAPI is blocking me in understanding which hook I need to create and how (pinvoke/parameters/etc).
I'm pretty sure this is quite trivial, and some of you familiar with C/C++ and WinAPI will have the answer ready at hand ;)
Thanks
Expanding on Chris Taylor's answer: Instead of doing the native interop yourself, you can use ManagedWinApi, which contains a Hook class.
EDIT: To use ManagedWinApi. Somewhere in your code:
Hook MyHook = new Hook(HookType.WH_CALLWNDPROC, false, false);
MyHook.Callback += MyHookCallback;
MyHook StartHook();
For the callback, reference CallWndProc and CWPSTRUCT:
private static int MyHookCallback(int code, IntPtr wParam, IntPtr lParam, ref bool callNext)
{
if (code >= 0)
{
// You will need to define the struct
var message = (CWPSTRUCT)Marshal.PtrToStructure(lParam, typeof(CWPSTRUCT));
// Do something with the data
}
return 0; // Return value is ignored unless you set callNext to false
}
A WH_CALLWNDPROC hook would probably suffice, this will allow you to monitor all messages destined for the window of interest.
Are you asking how to create a global hook using C# or are you happy to create the hook in C++ and then interop with that from .NET? The second option is the route I would go.
Basically off the top of my head, what I would do is the following
1- Create global hook in C, and export functions to InstallHook and UninstallHook, which can be called from your C# app using Interop. InstallHook take an hwnd of the window in your C# application.
2- Have the installed hook function post a custom message to the C# window provided in the call to InstallHook when ever there is a message you are interested in like WM_SIZE in your case.
3- In the C# application your window that receives the posted messages from the hook will override WndProc to handle the custom message.
That is an outline of one approach.
I suggest you use WinEvents:
public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
See also:event hooks
I ran into the same thing in some code I was working with and discovered I couldn't inject a managed .DLL into the process.
Not wanting to write a C++ unmanaged DLL that used SetWindowsHook, I went with a combination of SetWinEventHook, which signals when a window starts and ends a move event and a Timer to poll the window while it's moving to give it the appearance of following the window while it moves.
Here's a (very simplified) version of a class that can do that (just replace the TODO's with code to move the window or raise an event).
public class DockingHelper
{
private readonly uint m_processId, m_threadId;
private readonly IntPtr m_target;
// Needed to prevent the GC from sweeping up our callback
private readonly WinEventDelegate m_winEventDelegate;
private IntPtr m_hook;
private Timer m_timer;
public DockingHelper(string windowName, string className)
{
if (windowName == null && className == null) throw new ArgumentException("Either windowName or className must have a value");
m_target = FindWindow(className, windowName);
ThrowOnWin32Error("Failed to get target window");
m_threadId = GetWindowThreadProcessId(m_target, out m_processId);
ThrowOnWin32Error("Failed to get process id");
m_winEventDelegate = WhenWindowMoveStartsOrEnds;
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll")]
private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
private void ThrowOnWin32Error(string message)
{
int err = Marshal.GetLastWin32Error();
if (err != 0)
throw new Win32Exception(err, message);
}
private RECT GetWindowLocation()
{
RECT loc;
GetWindowRect(m_target, out loc);
if (Marshal.GetLastWin32Error() != 0)
{
// Do something useful with this to handle if the target window closes, etc.
}
return loc;
}
public void Subscribe()
{
// 10 = window move start, 11 = window move end, 0 = fire out of context
m_hook = SetWinEventHook(10, 11, m_target, m_winEventDelegate, m_processId, m_threadId, 0);
}
private void PollWindowLocation(object state)
{
var location = GetWindowLocation();
// TODO: Reposition your window with the values from location (or fire an event with it attached)
}
public void Unsubscribe()
{
UnhookWinEvent(m_hook);
}
private void WhenWindowMoveStartsOrEnds(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (hwnd != m_target) // We only want events from our target window, not other windows owned by the thread.
return;
if (eventType == 10) // Starts
{
m_timer = new Timer(PollWindowLocation, null, 10, Timeout.Infinite);
// This is always the original position of the window, so we don't need to do anything, yet.
}
else if (eventType == 11)
{
m_timer.Dispose();
m_timer = null;
var location = GetWindowLocation();
// TODO: Reposition your window with the values from location (or fire an event with it attached)
}
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left, Top, Right, Bottom;
}
private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
}

Registering a custom win32 window class from c#

I have a new application written in WPF that needs to support an old API that allows it to receive a message that has been posted to a hidden window. Typically another application uses FindWindow to identify the hidden window using the name of its custom window class.
1) I assume to implement a custom window class I need to use old school win32 calls?
My old c++ application used RegisterClass and CreateWindow to make the simplest possible invisible window.
I believe I should be able to do the same all within c#. I don't want my project to have to compile any unmanaged code.
I have tried inheriting from System.Windows.Interop.HwndHost and using System.Runtime.InteropServices.DllImport to pull in the above API methods.
Doing this I can successfully host a standard win32 window e.g. "listbox" inside WPF.
However when I call CreateWindowEx for my custom window it always returns null.
My call to RegisterClass succeeds but I am not sure what I should be setting the
WNDCLASS.lpfnWndProc member to.
2) Does anyone know how to do this successfully?
For the record I finally got this to work.
Turned out the difficulties I had were down to string marshalling problems.
I had to be more precise in my importing of win32 functions.
Below is the code that will create a custom window class in c# - useful for supporting old APIs you might have that rely on custom window classes.
It should work in either WPF or Winforms as long as a message pump is running on the thread.
EDIT:
Updated to fix the reported crash due to early collection of the delegate that wraps the callback. The delegate is now held as a member and the delegate explicitly marshaled as a function pointer. This fixes the issue and makes it easier to understand the behaviour.
class CustomWindow : IDisposable
{
delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[System.Runtime.InteropServices.StructLayout(
System.Runtime.InteropServices.LayoutKind.Sequential,
CharSet = System.Runtime.InteropServices.CharSet.Unicode
)]
struct WNDCLASS
{
public uint style;
public IntPtr lpfnWndProc;
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public IntPtr hbrBackground;
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public string lpszMenuName;
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public string lpszClassName;
}
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern System.UInt16 RegisterClassW(
[System.Runtime.InteropServices.In] ref WNDCLASS lpWndClass
);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern IntPtr CreateWindowExW(
UInt32 dwExStyle,
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
string lpClassName,
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
string lpWindowName,
UInt32 dwStyle,
Int32 x,
Int32 y,
Int32 nWidth,
Int32 nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam
);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern System.IntPtr DefWindowProcW(
IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam
);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern bool DestroyWindow(
IntPtr hWnd
);
private const int ERROR_CLASS_ALREADY_EXISTS = 1410;
private bool m_disposed;
private IntPtr m_hwnd;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!m_disposed) {
if (disposing) {
// Dispose managed resources
}
// Dispose unmanaged resources
if (m_hwnd != IntPtr.Zero) {
DestroyWindow(m_hwnd);
m_hwnd = IntPtr.Zero;
}
}
}
public CustomWindow(string class_name){
if (class_name == null) throw new System.Exception("class_name is null");
if (class_name == String.Empty) throw new System.Exception("class_name is empty");
m_wnd_proc_delegate = CustomWndProc;
// Create WNDCLASS
WNDCLASS wind_class = new WNDCLASS();
wind_class.lpszClassName = class_name;
wind_class.lpfnWndProc = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(m_wnd_proc_delegate);
UInt16 class_atom = RegisterClassW(ref wind_class);
int last_error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
if (class_atom == 0 && last_error != ERROR_CLASS_ALREADY_EXISTS) {
throw new System.Exception("Could not register window class");
}
// Create window
m_hwnd = CreateWindowExW(
0,
class_name,
String.Empty,
0,
0,
0,
0,
0,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero
);
}
private static IntPtr CustomWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
return DefWindowProcW(hWnd, msg, wParam, lParam);
}
private WndProc m_wnd_proc_delegate;
}
I'd like to comment the answer of morechilli:
public CustomWindow(string class_name){
if (class_name == null) throw new System.Exception("class_name is null");
if (class_name == String.Empty) throw new System.Exception("class_name is empty");
// Create WNDCLASS
WNDCLASS wind_class = new WNDCLASS();
wind_class.lpszClassName = class_name;
wind_class.lpfnWndProc = CustomWndProc;
UInt16 class_atom = RegisterClassW(ref wind_class);
int last_error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
if (class_atom == 0 && last_error != ERROR_CLASS_ALREADY_EXISTS) {
throw new System.Exception("Could not register window class");
}
// Create window
m_hwnd = CreateWindowExW(
0,
class_name,
String.Empty,
0,
0,
0,
0,
0,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero
);
}
In the constructor I copied above is slight error: The WNDCLASS instance is created, but not saved. It will eventually be garbage collected. But the WNDCLASS holds the WndProc delegate. This results in an error as soon as WNDCLASS is garbage collected. The instance of WNDCLASS should be hold in a member variable until the window is destroyed.
WNDCLASS wind_class;
put the definition in the class, not the function, and the crash will be fixed.
1) You can just subclass a normal Windows Forms class... no need for all those win32 calls, you just need to parse the WndProc message manually... is all.
2) You can import the System.Windows.Forms namespace and use it alongside WPF, I believe there won't be any problems as long as you don't intertwine too much windows forms into your WPF application. You just want to instantiate your custom hidden form to receieve a message is that right?
example of WndProc subclassing:
protected override void WndProc(ref System.Windows.Forms.Message m)
{
// *always* let the base class process the message
base.WndProc(ref m);
const int WM_NCHITTEST = 0x84;
const int HTCAPTION = 2;
const int HTCLIENT = 1;
// if Windows is querying where the mouse is and the base form class said
// it's on the client area, let's cheat and say it's on the title bar instead
if ( m.Msg == WM_NCHITTEST && m.Result.ToInt32() == HTCLIENT )
m.Result = new IntPtr(HTCAPTION);
}
Since you already know RegisterClass and all those Win32 calls, I assume the WndProc message wouldn't be a problem for you...

Categories