Bi-directional messages between a COM class and WPF Application - c#

I have a WPF application which I build as a dll and run from a COM class (MyComClass), developed in c#, as follows.
private void runDlg()
{
m_app = new Application();
m_app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
m_appRunning = true;
m_app.Run();
}
public void Open()
{
if(m_appRunning && !m_windowOpen)
{
m_app.Dispatcher.Invoke(new Action( () => new EwokApp().Show() ) );
Thread.Sleep(cWaitPeriod1000_ms);
m_windowOpen = true;
}
}
I then pass messages from the COM class to the WPF application as follows
[DllImport("User32.dll", EntryPoint = "FindWindow")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam);
public void Start()
{
if(m_windowOpen)
{
int hWnd = FindWindow(null, "MY-WPF-APP");
if(hWnd != 0)
{
m_msgHelper.sendMessage(hWnd, WM_START, 0, 0);
Thread.Sleep(cWaitPeriod2000_ms);
}
}
}
In my WPF application, I create a message handler as follows
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(new HwndSourceHook(WndProc));
}
private static IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// address the messages you are receiving using msg, wParam, lParam
if (msg == WM_START)
{
MyApp window = (MyApp)HwndSource.FromHwnd(hWnd).RootVisual;
window.start();
}
return IntPtr.Zero;
}
I can successfully post messages to the application from my COM class by obtaining a handler to the WPF window and passing it into the SendMessage function.
I would also like for the WPF application to obtain a handler to the COM class that created it and post messages to it. Can someone please advise how to do this.
Thanks

Related

SetWindowsHookEx does not work in C# .net core?

I'm using the following code trying to get OS wide keyboard inputs with no luck:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
class InterceptKeys
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
while(true)
continue;
}
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
Console.WriteLine(vkCode);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc 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);
}
HookCallback is simply not being called. I have a suspicion it's trying to listen only to a form which doesn't exist rather than running system wide.
Low-level Windows hooks internally use Windows messaging. The thread that calls SetWindowsHookEx must have the message loop in the end, which allows to call HookCallback function. In C++ message loop looks like this:
MSG msg;
BOOL result;
for (;;)
{
result = GetMessage(&msg, nullptr, 0, 0);
if (result <= 0)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Find all required PInvoke definitions for GetMessage, TranslateMessage, DispatchMessage and MSG, translate this code to C# and place it instead of your endless loop while(true). You can find all this stuff at PInvoke.Net, see also this Microsoft forum discussion:
Console keyboard hook not getting called
https://social.msdn.microsoft.com/Forums/vstudio/en-US/ed5be22c-cef8-4615-a625-d05caf113afc/console-keyboard-hook-not-getting-called?forum=csharpgeneral
I'm obviously very late but just hoping I could help (if the OP haven't have yet the help he/she needed) so I'm posting my answer.
It says in the MSDN documentation that when you want to set a system-wide hook, you must give the hMod parameter a
handle to the DLL containing the hook procedure pointed to by the lpfn
parameter
and
If the dwThreadId parameter is zero or specifies the identifier of a
thread created by a different process, the lpfn parameter must point
to a hook procedure in a DLL
but, look at this:
SetWindowsHookEx(2, kbdHookProc, GetModuleHandle("user32"), 0)
kbdHookProc is a function in my C# winforms application but the value I gave in the hMod parameter is the hinstance obtained by loading user32.dll via GetModuleHandle. I am using the keyboard hook (WH_KEYBOARD) to monitor locking of capslock, numlock and scroll lock keys. Don't ask me why I did that and would it work or why it works because I don't know but, yes, IT WORKS!
For a full answer to this;
As Alex says, you'll need a message loop to process windows messages and call your hooks,
public class MessageLoop
{
[DllImport("user32.dll")]
private static extern int GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin,
uint wMsgFilterMax);
[DllImport("user32.dll")]
private static extern bool TranslateMessage([In] ref MSG lpMsg);
[DllImport("user32.dll")]
private static extern IntPtr DispatchMessage([In] ref MSG lpmsg);
[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
IntPtr hwnd;
uint message;
UIntPtr wParam;
IntPtr lParam;
int time;
POINT pt;
int lPrivate;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
X = x;
Y = y;
}
public static implicit operator System.Drawing.Point(POINT p)
{
return new System.Drawing.Point(p.X, p.Y);
}
public static implicit operator POINT(System.Drawing.Point p)
{
return new POINT(p.X, p.Y);
}
public override string ToString()
{
return $"X: {X}, Y: {Y}";
}
}
private Action InitialAction { get; }
private Thread? Thread { get; set; }
public bool IsRunning { get; private set; }
public MessageLoop(Action initialAction)
{
InitialAction = initialAction;
}
public void Start()
{
IsRunning = true;
Thread = new Thread(() =>
{
InitialAction.Invoke();
while (IsRunning)
{
var result = GetMessage(out var message, IntPtr.Zero, 0, 0);
if (result <= 0)
{
Stop();
continue;
}
TranslateMessage(ref message);
DispatchMessage(ref message);
}
});
Thread.Start();
}
public void Stop()
{
IsRunning = false;
}
}
I use a separate thread here to avoid blocking the main thread.
The InterceptKeys class as shown in the question needs no modification;
class InterceptKeys
{
// ...
public static void Main()
{
var loop = new MessageLoop(() => {
_hookID = SetHook(_proc);
});
while (Console.ReadKey(true) != ConsoleKey.X) // For exemplary purposes
{
continue;
}
loop.Stop();
}
// ...
}

CreateParams in the Compact Framework

I'm using the .NET Compact Framework and would like to subclass a PictureBox control (in this case to remove the CS_DBLCLKS style from specific instances of the PictureBox control). The code below works on .NET standard, but not the Compact Framework:
using System;
using System.Windows.Forms;
namespace NoDblClick
{
public partial class NoDblClickPicControl : PictureBox
{
private const int CS_DBLCLKS = 0x008;
public NoDblClickPicControl()
{
}
protected override CreateParams CreateParams
{
get
{
// No compile, missing directive or assembly directive
CreateParams cp = base.CreateParams;
cp.ClassStyle &= ~CS_DBLCLKS;
return cp;
}
}
}
}
How do I get this to work on the Compact Framework? Perhaps I can PInvoke the functionality (from coredll.dll say)?
These styles are applied when the window class is created, and to my knowledge cannot be changed on the compact framework. Besides CreateParams, the full framework allows a window handle to be recreated, which also is not possible on the compact framework.
You could manually filter the messages sent to the control, and convert the double click message back to a mouse down message:
public partial class NoDblClickPicControl : PictureBox
{
private const int WM_LBUTTONDBLCLK = 0x0203;
private const int WM_LBUTTONDOWN = 0x0201;
private const int GWL_WNDPROC = -4;
[DllImport("coredll.dll", SetLastError = true)]
private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr newWndProc);
[DllImport("coredll.dll", SetLastError = true)]
private static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
private IntPtr prevWndProc;
private WndProcDelegate #delegate;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
#delegate = new WndProcDelegate(MyWndProc);
prevWndProc = SetWindowLong(Handle, GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(#delegate));
}
protected override void OnHandleDestroyed(EventArgs e)
{
base.OnHandleDestroyed(e);
SetWindowLong(Handle, GWL_WNDPROC, prevWndProc);
}
private IntPtr MyWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
if (msg == WM_LBUTTONDBLCLK)
{
msg = WM_LBUTTONDOWN;
}
return CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);
}
}

WinForms equivalent of WPF WindowInteropHelper, HwndSource, HwndSourceHook

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.

How do I create a message-only window from windows forms?

I'm trying to create a message-only window to receive window messages from an MFC library class, within a winforms application.
I've tried subclassing NativeWindow, and in the constructor requesting a window handle like this:
CreateParams cp = new CreateParams();
cp.Parent = (IntPtr)HWND_MESSAGE;
this.CreateHandle(cp);
but I get a Win32Exception thrown with the message "Error creating window handle". How do I create a message-only window from windows forms? Is using NativeWindow the right approach?
Try that :
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
static IntPtr HWND_MESSAGE = new IntPtr(-3);
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
SetParent(this.Handle, HWND_MESSAGE);
}
I know this is 7.5 years old, but just in case anyone finds this, I thought I would respond. I used Microsoft's TimerNativeWindow code and removed the timer functionality. I ended up using this approach:
public class MyNativeWindow : NativeWindow
{
private readonly string _caption;
private const int WmClose = 0x0010;
[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
private static readonly HandleRef HwndMessage = new HandleRef(null, new IntPtr(-3));
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
private static extern IntPtr PostMessage(HandleRef hwnd, int msg, int wparam, int lparam);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.Process)]
private static extern int GetWindowThreadProcessId(HandleRef hWnd, out int lpdwProcessId);
[DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.Process)]
private static extern int GetCurrentThreadId();
public MyNativeWindow(string caption)
{
_caption = caption;
}
public bool CreateWindow()
{
if (Handle == IntPtr.Zero)
{
CreateHandle(new CreateParams
{
Style = 0,
ExStyle = 0,
ClassStyle = 0,
Caption = _caption,
Parent = (IntPtr)HwndMessage
});
}
return Handle != IntPtr.Zero;
}
public void DestroyWindow()
{
DestroyWindow(true, IntPtr.Zero);
}
private bool GetInvokeRequired(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero) return false;
int pid;
var hwndThread = GetWindowThreadProcessId(new HandleRef(this, hWnd), out pid);
var currentThread = GetCurrentThreadId();
return (hwndThread != currentThread);
}
private void DestroyWindow(bool destroyHwnd, IntPtr hWnd)
{
if (hWnd == IntPtr.Zero)
{
hWnd = Handle;
}
if (GetInvokeRequired(hWnd))
{
PostMessage(new HandleRef(this, hWnd), WmClose, 0, 0);
return;
}
lock (this)
{
if (destroyHwnd)
{
base.DestroyHandle();
}
}
}
public override void DestroyHandle()
{
DestroyWindow(false, IntPtr.Zero);
base.DestroyHandle();
}
}
I believe that you'll need to also specify a window class.
I fear that you must derive from a Form, and force the window invisible.
Another approach (in the case the class library is modifiable) is to run a message pump without a window (see Application.Run and Application.AddMessageFilter, or if you prefer pinvokes using PeekMessage & Co).
In this case you can send messages using PostThreadMessage by having the thread id which as run Application.Run, but actually you cannot synch with the application message pump thread because it doesn't wait message acknowledge.

Subclassing a external window in C# .NET

I'm trying to subclass an external window in C#.
I have used something similar before in VB6 without any problem BUT the below code just won't work. Can anybody help me out?
//API
[DllImport("user32")]
private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr newProc);
[DllImport("user32")]
private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, WinProc newProc);
[DllImport("user32.dll")]
private static extern IntPtr DefWindowProc(IntPtr hWnd, int uMsg, int wParam, int lParam);
[DllImport("user32")]
private static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, int Msg, int wParam, int lParam);
private delegate IntPtr WinProc(IntPtr hWnd, int Msg, int wParam, int lParam);
private const int GWL_WNDPROC = -4;
private enum winMessage : int
{
WM_GETMINMAXINFO = 0x024,
WM_ENTERSIZEMOVE = 0x231,
WM_EXITSIZEMOVE = 0x232
}
private WinProc newWndProc = null;
private IntPtr oldWndProc = IntPtr.Zero;
private IntPtr winHook = IntPtr.Zero;
//Implementation
public void hookWindow(IntPtr winHandle)
{
if (winHandle != IntPtr.Zero)
{
winHook = winHandle;
newWndProc = new WinProc(newWindowProc);
oldWndProc = SetWindowLong(winHook, GWL_WNDPROC,newWndProc);
}
}
public void unHookWindow()
{
if (winHook != IntPtr.Zero)
{
SetWindowLong(winHook, GWL_WNDPROC, oldWndProc);
winHook = IntPtr.Zero;
}
}
private IntPtr newWindowProc(IntPtr hWnd, int Msg, int wParam, int lParam)
{
switch (Msg)
{
case (int)winMessage.WM_GETMINMAXINFO:
MessageBox.Show("Moving");
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
ok im done with the coding, but in your solution you have to have your form solution and a dll solution and it can work, if you want that code let me know. but you cannot subclass within a same exe. so it can all be done in c# but you do need that dll, when i got down to converting my c++ project
all because of
BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
{
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
{
hInstance=(HINSTANCE)hinstDLL;
}
break;
case DLL_PROCESS_DETACH:
{
if((int)hndll>1)
{
SetWindowLong(hndll,GWL_WNDPROC,OldWndHndl); //Set back the old window procedure
return 1;
}
}
}
}
It's impossible with C#. Only unmanaged C/C++ can do it..
oldWndProc = SetWindowLong(winHook, GWL_WNDPROC,newWndProc); will always return 0(which means failed) if winHook is from another process.
Reference: https://social.msdn.microsoft.com/Forums/vstudio/en-US/8dd657b5-647b-443b-822d-ebe03ca4033c/change-wndproc-of-another-process-in-c

Categories

Resources