Filtering messages in WPF application - wpf

Windows Forms have the IMessageFilter interface to capture messages. How is this done in WPF? Specifically, I want to create a clipboard format listener.

In your Window derived class:
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
((HwndSource)PresentationSource.FromVisual(this)).AddHook(myHook)
}
private IntPtr myHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
// process messages here
default:
return IntPtr.Zero;
}
}

Related

WPF and WndProc hangs with .NET exception

I need to execute a code when shutdown/logoff. When any of these action are made im getting an application error with code 0xe0434352. What im doing wrong?
This is my code:
private void Window_SourceInitialized(object sender, EventArgs e)
{
IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.AddHook(new HwndSourceHook(WndProc));
}
private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == 0x0011)
{
EventLog.WriteEntry("WPF1", "finally!");
//my code
}
return IntPtr.Zero;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.RemoveHook(new HwndSourceHook(this.WndProc));
}
Exception error: http://pastebin.com/gaX0ekaP
Nevermind, admin priviledges were required.

WPF Listview does not scroll (with mouse wheel) when Application not in focus

I have a WPF app (written in C#) which has a Listview control which scrolls perfectly with the mouse wheel when the app is in focus.
However when the app is not in focus, even when the mouse pointer is over the app & list view area, the Listview does not scroll. I continue to see mousehover related effects on the app but no mousewheel event is received. This is inline with how most of the other apps work on my desktop however some of them (like Facebook messenger) support scrolling without focus which i would like to mimic in my WPF app.
I have searched MSDN forums and Stackoverflow and seen multiple solutions for Windows Forms however they were questions asked over 5 years ago and i was wondering if someone has managed to do it relatively easily on .net 4.5 and can point me to possible solutions.
---Edit---
I was able to progress to some extent on this thanks to this thread C# ListView mouse wheel scroll without focus
Here is how my the function that receives the mousewheel looks
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 &&
MouseMessages.WM_MOUSEWHEEL == (MouseMessages)wParam)
{
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
Console.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y);
Console.WriteLine((short)((hookStruct.mouseData)>>16));
MouseWheelEventArgs myArgs = new MouseWheelEventArgs(System.Windows.Input.Mouse.PrimaryDevice, (int)hookStruct.time, (short)((hookStruct.mouseData)>>16));
myMainFrame.SidePanelControl.ScrollTheListView(myArgs);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
As you can see i am initializing a MouseWheelEventArgs instance and have the time, delta and the mouse device attributes.
How do i go about passing this mousewheel event to my listview scrollviewer?
Managed to get this working. Here is my class.
All one needs to do to use the class is
Initialize InterceptMouse passing it the app/listview/etc pointer
Start intercepting the mouse when the app is not in focus but the corresponding mouseenter event has occured.
As long as the event is mousewheel the scrollviewer of the listview will be sent the mouseWheel event.
Stop intercepting the mouse when the app gets activated or mouseleave is called.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.Windows.Forms;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Input;
using System.Diagnostics;
using System.Windows.Forms.Integration;
namespace myApp.HelperClasses
{
public class InterceptMouse
{
public static System.Windows.Controls.ListView myListview;
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public InterceptMouse(System.Windows.Controls.ListView myListviewParam)
{
myListview = myListviewParam;
}
public static void StartIntercepting()
{
_hookID = SetHook(_proc);
}
public static void StopIntercepting()
{
UnhookWindowsHookEx(_hookID);
}
private static IntPtr SetHook(LowLevelMouseProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_MOUSE_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 &&
MouseMessages.WM_MOUSEWHEEL == (MouseMessages)wParam)
{
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
//Console.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y + "," + hookStruct.mouseData);
var delta = (short)((hookStruct.mouseData) >> 16);
var mouse = InputManager.Current.PrimaryMouseDevice;
var args = new MouseWheelEventArgs(mouse, Environment.TickCount, delta);
args.RoutedEvent = WindowsFormsHost.MouseWheelEvent;
Decorator border = VisualTreeHelper.GetChild(myListview, 0) as Decorator;
// Get scrollviewer
ScrollViewer scrollViewer = border.Child as ScrollViewer;
scrollViewer.RaiseEvent(args);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private const int WH_MOUSE_LL = 14;
private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private struct MSLLHOOKSTRUCT
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
}

When minimized into the system tray, a WPF Window does not respond to message from PostMessage from the other instance when

I am working on a task to restore and maximize a window from system tray when the second instance of the same application starts.
When the 2nd instance starts and fail to grab the mutex. It calls the following code to signal the first instance to show itself:
public static void ShowFirstInstance()
{
WinApi.PostMessage(
(IntPtr)WinApi.HWND_BROADCAST,
WM_SHOWFIRSTINSTANCE,
IntPtr.Zero,
IntPtr.Zero);
}
The message is registered using the following:
public static readonly int WM_SHOWFIRSTINSTANCE =
WinApi.RegisterWindowMessage("WM_SHOWFIRSTINSTANCE|{0}", 250);
I have following in the code behind of the window to catch the message and show the window:
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == SingleInstance.WM_SHOWFIRSTINSTANCE)
{
WinApi.ShowToFront(hwnd);
}
return IntPtr.Zero;
}
When I test it out. Whenever the first instance hide in the system tray, the message never caught. Do I miss anything?
Thanks,
Here's how I've done this in the past:
App.xaml.cs:
private static readonly Mutex Mutex = new Mutex(true, "{" + YourGuidHere + "}");
//return true if other instance is open, allowing for UI cleanup/suspension while shutdown() is completed
private bool EnforceSingleInstance()
{
//try...catch provides safety for if the other instance is closed during Mutex wait
try
{
if (!Mutex.WaitOne(TimeSpan.Zero, true))
{
var currentProcess = Process.GetCurrentProcess();
var runningProcess =
Process.GetProcesses().Where(
p =>
p.Id != currentProcess.Id &&
p.ProcessName.Equals(currentProcess.ProcessName, StringComparison.Ordinal)).FirstOrDefault();
if (runningProcess != null)
{
WindowFunctions.RestoreWindow(runningProcess.MainWindowHandle);
Shutdown();
return true;
}
}
Mutex.ReleaseMutex();
}
catch (AbandonedMutexException ex)
{
//do nothing, other instance was closed so we may continue
}
return false;
}
WindowFunctions:
//for enum values, see http://www.pinvoke.net/default.aspx/Enums.WindowsMessages
public enum WM : uint
{
...
SYSCOMMAND = 0x0112,
...
}
//for message explanation, see http://msdn.microsoft.com/en-us/library/windows/desktop/ms646360(v=vs.85).aspx
private const int SC_RESTORE = 0xF120;
public static void RestoreWindow(IntPtr hWnd)
{
if (hWnd.Equals(0))
return;
SendMessage(hWnd,
(uint)WM.SYSCOMMAND,
SC_RESTORE,
0);
}
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd,
uint msg,
uint wParam,
uint lParam);

how to disable acrobat reader contextmenu in Wpf?

I'm showing PDF documents in my application using Acrobat ActiveX.
I want to know is it possible to disable right click on PDF documents?
My previous solution didn't work, so there's another one. You just need to set a mouse hook on the child window that receives mouse notifications.
public class AcrobatReader : AxAcroPDF
{
private const int WH_MOUSE_LL = 14;
private const int WM_RBUTTONDOWN = 0x0204;
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public AcrobatReader()
{
_hookID = SetWindowsHookEx(WH_MOUSE_LL, _proc, FindWindow(null, "avpageview"), 0);
}
~AcrobatReader()
{
if (_hookID != IntPtr.Zero)
{
UnhookWindowsHookEx(_hookID);
_hookID = IntPtr.Zero;
}
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && (IntPtr)WM_RBUTTONDOWN == wParam)
{
return new IntPtr(1);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}

How do I move a wpf window into a negative top value?

If I use dragMove the wpf window will not move to a location where the y value is negative. I can however set the windows top value to a negative value. Is there a simple way to enable dragMove to allow the top of the window to be moved above the displays 0 position?
Edit:
It seems that this is the default window's handling of moveWindow. Verified with a call to SendMessage( hwnd, WM_SYSCOMMAND, SC_MOVE, null);
As I found, the window will move to "negative" location, but will then jump back. To prevent this you could do something like:
public partial class Window1: Window {
public Window1() {
InitializeComponent();
}
private void Window_MouseDown(object sender, MouseButtonEventArgs e) {
DragMove();
}
public struct WINDOWPOS {
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public UInt32 flags;
};
private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
switch(msg) {
case 0x46://WM_WINDOWPOSCHANGING
if(Mouse.LeftButton != MouseButtonState.Pressed) {
WINDOWPOS wp = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));
wp.flags = wp.flags | 2; //SWP_NOMOVE
Marshal.StructureToPtr(wp, lParam, false);
}
break;
}
return IntPtr.Zero;
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(new HwndSourceHook(WndProc));
}
}
Handling WM_WINDOWPOSCHANGING this way will prevent any movement of the window unless left mouse button is pressed. This includes maximizing the window and programmatic change of window position as well, so you'll have to adapt the code if you need another behavior.

Resources