WinForms equivalent of WPF WindowInteropHelper, HwndSource, HwndSourceHook - c#

I have a block of code like:
IntPtr hWnd = new WindowInteropHelper(this).Handle;
HwndSource source = HwndSource.FromHwnd(hWnd);
source.AddHook(new HwndSourceHook(WndProc));
NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_CALL, IntPtr.Zero, IntPtr.Zero);
This was originally in a WPF application. However, I need to replicate the functionality in a WinForms application. Also, NativeMethods.PostMessage just maps to user32.dll PostMessage:
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
Are there a 1 to 1 equivalents of WindowInteropHelper/HwndSource/HwndSourceHook that I can use in my WinForms applications?

The basic point is: you don't need anything except AddHook from your source. Each WinForm has a method GetHandle() which will give you the handle of the Window/Form (and you found PostMessage already by yourself).
Too translate AddHook you either write your own class implementing IMessageFilter (1) or you override WndProc() (2).
(1) will receive messages application-wide, regardless to which form you send them while (2) only receives messages for the specific form overriding the method.
I could'nt find anything regarding WM_CALL, as you have to specify the window message as an integer (usually in hex), so this is up to you.
(1):
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public partial class Form1 : Form
{
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
//private const int WM_xxx = 0x0;
//you have to know for which event you wanna register
public Form1()
{
InitializeComponent();
IntPtr hWnd = this.Handle;
Application.AddMessageFilter(new MyMessageFilter());
PostMessage(hWnd, WM_xxx, IntPtr.Zero, IntPtr.Zero);
}
}
class MyMessageFilter : IMessageFilter
{
//private const int WM_xxx = 0x0;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_xxx)
{
//code to handle the message
}
return false;
}
}
(2):
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public partial class Form 1 {
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
//private const int WM_xxx = 0x0;
//you have to know for which event you wanna register
public Form1()
{
InitializeComponent();
IntPtr hWnd = this.Handle;
PostMessage(hWnd, WM_xxx, IntPtr.Zero, IntPtr.Zero);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WMK_xxx)
{
//code to handle the message
}
}
}

Am not more of WPF background. but for me it sounds like you're looking for NativeWindow.

Related

How to fix error KeyEventHandler in c# console application?

I have a problem that when executing this code in my winform application, it runs normally but when running on the console it gets an error message, it seems that KeyEventHandler is only used in winform, yes What can be replaced in the console application. Here is my hook function:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
namespace Websevice_Core_Console
{
class Y2KeyboardHook
{
#region Win32 API Functions and Constants
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
KeyboardHookDelegate lpfn, IntPtr hMod, int dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
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("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x101;
#endregion
private KeyboardHookDelegate _hookProc;
private IntPtr _hookHandle = IntPtr.Zero;
public delegate IntPtr KeyboardHookDelegate(int nCode, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)]
public struct KeyboardHookStruct
{
public int VirtualKeyCode;
public int ScanCode;
public int Flags;
public int Time;
public int ExtraInfo;
}
#region Keyboard Events
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
#endregion
// destructor
~Y2KeyboardHook()
{
Uninstall();
}
public void Install()
{
_hookProc = KeyboardHookProc;
_hookHandle = SetupHook(_hookProc);
if (_hookHandle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
private IntPtr SetupHook(KeyboardHookDelegate hookProc)
{
IntPtr hInstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);
return SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
}
private IntPtr KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
KeyboardHookStruct kbStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
if (wParam == (IntPtr)WM_KEYDOWN)
{
if (KeyDown != null)
KeyDown(null, new KeyEventArgs((Keys)kbStruct.VirtualKeyCode));
}
else if (wParam == (IntPtr)WM_KEYUP)
{
if (KeyUp != null)
KeyUp(null, new KeyEventArgs((Keys)kbStruct.VirtualKeyCode));
}
}
return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
}
public void Uninstall()
{
UnhookWindowsHookEx(_hookHandle);
}
}
}
Here is how I use it:
_y2KeyboardHook.KeyDown += (sender, e) =>// ghi nhan thong tin tu ban phim
{
if (e.KeyCode.ToString() != "")
{
Console.WriteLine("Hello "+KeyCode);
}
      }
I got an error message on 2 lines
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
Can anyone help me, thanks a lot!
Given the code you posted and the vague "I got an error message" (which is not an actual question nor a useful problem statement…always be specific), it seems likely you are not referencing System.Windows.Forms.dll. On the other hand, if you aren't referencing the Winforms assembly, it's not clear why you don't also get an error where you use KeyEventArgs, since that's defined in the same assembly.
That said, assuming the error you're getting for your use of KeyEventHandler is simply that it's not defined, you can define the same type for your own use:
public delegate void KeyEventHandler(object sender, KeyEventArgs e);
I.e., just copy the delegate type declaration from the documentation.
Now, all that said, if you aren't referencing the Winforms assembly, and you are getting an error message as expected with the KeyEventArgs type as well, as would be expected, you might want to consider not copying the Winforms implementation of this approach, and just declaring your own event args type with your own event handler. The only thing you're returning is the virtual key code, so you might as well define your own types for that:
class KeyboardHookEventArgs : EventArgs
{
public int VirtualKeyCode { get; }
public KeyboardHookEventArgs(int virtualKeyCode)
{
VirtualKeyCode = virtualKeyCode;
}
}
…then the events:
public event EventHandler<KeyboardHookEventArgs> KeyDown;
public event EventHandler<KeyboardHookEventArgs> KeyUp;
Where you create the args object like new KeyboardHookEventArgs(kbStruct.VirtualKeyCode) instead.
Note that your event-raising implementation is not thread-safe. It would be more safe, and more idiomatic in C#, to use the null-conditional operator, e.g.:
if (wParam == (IntPtr)WM_KEYDOWN)
{
KeyDown?.Invoke(this, new KeyboardHookEventArgs(kbStruct.VirtualKeyCode));
}
You should also pass a correct value for sender, i.e. this as I've shown above. If you want for the event to have no sender, then it (and the rest of the class) should just be static.

Calling instance method from static method

I am trying to use global hooks to detect input events (a mouse click, keyboard keys) and use the detection to trigger another event, such as storing the mouse coordinates. The code I've found does work, but I cannot call on another method in the same form because the hookProc is set to static.
public static IntPtr hookProc(int code, IntPtr wParam, IntPtr lParam)
{
if (code >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if (vkCode.ToString() == "162") //162 is ASCI CTRL
{
MessageBox.Show("CTRL");
Form1 Trigger = new Form1(); // Supposed to allow calls to Form1, specifically textBox1
Trigger.textBox1.Text = "3"; // Will not change the Text value
}
return (IntPtr)1;
}
else
return CallNextHookEx(hhook, code, (int)wParam, lParam);
}
I know the code works because the CTRL MessageBox will show. I know textBox1.Text = "3"; because I can use the code elsewhere in an instance without issue. I searched and found I need to create a new instance of form1 in order to call on methods found outside the static hookProc. My code does not give me any build errors but does not do anything in regards to textBox1. Even if I wanted to call on another method when CTRL is detected, I cannot link to anything outside hookProc. Help?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Demo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
SetHook(); // Set the hook
}
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
UnHook(); // Remove the hook
}
// GLOBAL HOOK
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc callback, IntPtr hInstance, uint threadId);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, int wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
const int WH_KEYBOARD_LL = 13; // Number of global LowLevel- hook on the keyboard
const int WM_KEYDOWN = 0x100; // Messages pressing
private LowLevelKeyboardProc _proc = hookProc;
private static IntPtr hhook = IntPtr.Zero;
public void SetHook()
{
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, _proc, hInstance, 0);
}
public static void UnHook()
{
UnhookWindowsHookEx(hhook);
}
public static IntPtr hookProc(int code, IntPtr wParam, IntPtr lParam)
{
if (code >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if (vkCode.ToString() == "162") //162 is ASCI CTRL
{
MessageBox.Show("CTRL");
Form1 Trigger = new Form1(); // Allows called to Form1, specifically textBox3
Trigger.TriggeredEvent(); //Visual confirmation of CTRL has been detected
Trigger.textBox1.Text = "3";
}
return (IntPtr)1;
}
else
return CallNextHookEx(hhook, code, (int)wParam, lParam);
}
}
}
As you would have guessed, the issue is that you are creating a new instance of Form1 and attempting to update the TextBox Value in the new instance. What you require is the same instance which is currently open.
Assuming you have only a single instance of Form1, you can use Application.OpenForms to get collection of all Open Forms and use Enumerable.OfType to filter the Form1 Type.
For Example
var formInstance = Application.OpenForms.OfType<Form1>().Single();
formInstance.TriggeredEvent();
formInstance.textBox1.Text = "3";

Receiving messages from another process

It seems like I am missing something fundamental, from my application, upgraded from VB6. All I want to do, is for it to receive SendMessage messages from another application.
The older VB6 application works, however, I found that in the .Net world, overriding the WndProc(ref Message m) method doesn't work. Note, I do see messages, coming from withing the same application, e.g. if I move the mouse inside the window.
I tried mimicking what the VB6 did, i.e. using the SetWindowLong , CallWindowProc, etc... and doesn't work either.
I have sparsely read about DLL injecting (P/Invoking SetWindowLong and CallWindowProc in managed code (compact framework)) but can't seem to move forward on it.
Is this actually possible in .Net? I would hope someone can point me to the right direction.
UPDATE: There is a topic in MSDN about this. Here is the link: https://learn.microsoft.com/en-us/windows/win32/dataxchg/using-data-copy
But this is in C++. I'm wondering if this is possible in .NET
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsAppMessage
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//HandleCreated += Form1_HandleCreated;
}
protected override void WndProc(ref Message m)
{
Console.WriteLine(m.ToString());
// Listen for operating system messages or from other applications
base.WndProc(ref m);
}
/*
private void Form1_HandleCreated(object sender, EventArgs e)
{
_handle = this.Handle;
Attach();
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll")]
public static extern int CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, int Msg, int wParam, int lParam);
private delegate int WindowProc(IntPtr hWnd, int Msg, int wParam, int lParam);
private const int GWL_WNDPROC = -4;
private IntPtr _handle;
private IntPtr _oldCallback;
private WindowProc _newCallback;
public void Attach()
{
_newCallback = WndProc; // Pins WndProc - will not be garbage collected.
_oldCallback = SetWindowLong(_handle, GWL_WNDPROC,
Marshal.GetFunctionPointerForDelegate(_newCallback));
// Just to be sure...
if (_oldCallback == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
public void Detach()
{
if (_newCallback == null || _oldCallback == null)
return;
SetWindowLong(_handle, GWL_WNDPROC, _oldCallback);
_newCallback = null;
}
private int WndProc(IntPtr hWnd, int Msg, int wParam, int lParam)
{
Console.WriteLine(Msg.ToString());
// Forward the message to the original WndProc function.
return CallWindowProc(_oldCallback, hWnd, Msg, wParam, lParam);
}
}
*/
}
Just FYI, I solved this issue. The main thing that I lacked, was programmatically enabling the messages from the "other app" from getting thru.
The topic to search is User Interface Privilege Isolation (UIPI), hence the need to explictly call ChangeWindowMessageFilterEx API.
For example:
ChangeWindowMessageFilterEx(this.Handle, Constants.WM_COPYDATA, ChangeWindowMessageFilterExAction.Allow, ref filterStatus);

Intercepting Window Messages for another Window

I'm using CefGlue to make an application with an embedded webkit browser in it, and I need to listen for mousemovements in the browser window. The winforms control doesn't pass down mouse events to the control so I can't listen to them.
However, I found a bug/feature request with a solution in it, but it's beyond me on how to implement it, I'm not familiar with working directly in the WinAPI. The developer says I need to:
2. OS-specific (windows) - after browser created (CefLifeSpanHandler.OnAfterCreated) get window handle and subclass
them (window subclassing technique). Actually now we have native
window with class CefBrowserWindow (returned by
CefBrowser.GetHost().GetWindowHandle()), then child window
Chrome_WidgetWin_0, and then Chrome_RenderWidgetHostHWND. For
intercepting WM_MOUSEMOVE you are interesting in Chrome_WidgetWin_0
window, which can be easily obtained via CefBrowserWindow. Just play
with Spy++ to see precisely.
https://bitbucket.org/xilium/xilium.cefglue/issue/4/mouse-events-unaccessible
I've done some googling, but I'm not sure how to hook into this. I have this function on my form:
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
switch (m.Msg) {
case WM_MOUSEMOVE:
Console.WriteLine("Mouse move!");
break;
default:
Console.WriteLine(m.ToString());
break;
}
}
But I never see a mouse move when I'm over the control. I suspect I need to be listening on the WndProc of CefBrowser.GetHost().GetWindowHandle() But I'm not sure how to do that.
I found a solution. I call this function in the OnAfterCreated event from the WebLifeSpanHandler.
browser.Created += (sender, eventargs) => {
Console.WriteLine("Created.");
BrowserWindowPointer = browser.CefBrowser.GetHost().GetWindowHandle();
Console.WriteLine("BrowserWindowPointer: " + BrowserWindowPointer);
uint BrowserThreadId = GetWindowThreadProcessId(BrowserWindowPointer, IntPtr.Zero);
Console.WriteLine("Browser PID: " + BrowserThreadId);
MouseHookProcedure = new HookProc(this.MouseHookProc);
hHookMouse = SetWindowsHookEx(WH_MOUSE,
MouseHookProcedure,
(IntPtr)0,
BrowserThreadId);
if (hHookMouse == 0) {
Console.WriteLine("MouseHook Failed. Making cursor always visible.");
Cursor.Show();
}
KeyboardHookProcedure = new HookProc(this.KeyboardHookProc);
hHookKeyboard = SetWindowsHookEx(WH_KEYBOARD,
KeyboardHookProcedure,
(IntPtr)0,
BrowserThreadId);
};
The function I was looking for is GetWindowThreadProcessId which I can use the window handle provided by browser.CefBrowser.GetHost().GetWindowHandle()
One thing to note is that the Hook Procedure needs to have some sort of larger scope. C# doesn't see it hooked into the native process and will Garbage Collect it if you let it go out of scope. To fix this, I made them class properties.
Supporting Documents:
http://support.microsoft.com/kb/318804 How to set a Windows hook in Visual C# .NET Provided the basis for hooking into the mouse events. Modified to use the browser thread id
http://msdn.microsoft.com/en-us/library/ms644988(v=vs.85).aspx MouseProc callback function How to handle the mouse windows hook
http://msdn.microsoft.com/en-us/library/ms644984(VS.85).aspx KeyboardProc callback function How to handle the keyboard windows hook. Includes structure of lParam. Important note: wParam contains the Virtual Key Code of the key entered.
http://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspx Virtual-Key Codes List of keys and their Virtual Key Code, so I could tell what button the user was pressing.
My two hooks (not good code, I don't do anything with the marshaled data):
private int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam) {
//Marshall the data from the callback.
MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
if (nCode < 0) {
return CallNextHookEx(hHookMouse, nCode, wParam, lParam);
} else {
if (wParam.ToInt32() == WM_MOUSEMOVE) {
Screensaver_OnMouseMove(this, null);
}
return CallNextHookEx(hHookMouse, nCode, wParam, lParam);
}
}
private int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam) {
//Marshall the data from the callback.
KeyboardHookStruct keyInfo = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
int VK_ESCAPE = 0x1B;
if (nCode < 0) {
return CallNextHookEx(hHookKeyboard, nCode, wParam, lParam);
} else {
int keyCode = wParam.ToInt32();
if (keyCode == VK_ESCAPE) {
Application.Exit();
}
return CallNextHookEx(hHookKeyboard, nCode, wParam, lParam);
}
}
This is the externs and the code needed to expose the hooks, this is class level scope:
public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
//Declare the hook handle as an int.
static int hHookMouse = 0;
static int hHookKeyboard = 0;
//Declare the mouse hook constant.
//For other hook types, you can obtain these values from Winuser.h in the Microsoft SDK.
private const int WH_KEYBOARD = 2;
private const int WH_MOUSE = 7;
private const int WM_MOUSEMOVE = 0x0200;
//Declare the wrapper managed POINT class.
[StructLayout(LayoutKind.Sequential)]
public class POINT {
public int x;
public int y;
}
//Declare the wrapper managed MouseHookStruct class.
[StructLayout(LayoutKind.Sequential)]
public class MouseHookStruct {
public POINT pt;
public int hwnd;
public int wHitTestCode;
public int dwExtraInfo;
}
public struct KeyboardHookStruct {
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
//This is the Import for the SetWindowsHookEx function.
//Use this function to install a thread-specific hook.
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, uint threadId);
//This is the Import for the UnhookWindowsHookEx function.
//Call this function to uninstall the hook.
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
//This is the Import for the CallNextHookEx function.
//Use this function to pass the hook information to the next hook procedure in chain.
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
public HookProc KeyboardHookProcedure { get; set; }
public HookProc MouseHookProcedure { get; set; }
I'm not sure this is entirely required, especially since the thread being hooked to is going away on close, but just for good measure, don't forget to cleanup your hook after you're done listening. I do this on my form's dispose method:
protected override void Dispose(bool disposing) {
base.Dispose();
if (disposing) {
if (hHookKeyboard != 0) {
UnhookWindowsHookEx(hHookKeyboard);
}
if (hHookMouse != 0) {
UnhookWindowsHookEx(hHookMouse);
}
}
}

How to close a window programmatically in VB.net?

How can I close a window of external application programmatically in VB.net.
I just want to close the current window without closing the whole process.
use FindWindow and SendMessage APIs
here in C#, should be trivial to convert:
using Microsoft.Win32;
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName,string lpWindowName);
[DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_CLOSE = 0xF060;
private void closeWindow()
{
// retrieve the handler of the window
int iHandle = FindWindow("Notepad", "Untitled - Notepad");
if (iHandle > 0)
{
// close the window using API
SendMessage(iHandle, WM_SYSCOMMAND, SC_CLOSE, 0);
}
}
source: http://www.codeproject.com/KB/dialog/closewindow.aspx

Categories

Resources