WinApi - override GetMessage loop in WH_GETMESSAGE hook - c#

I have implemented standard Win32 MDI window with following message loop:
MSG msg;
while (NativeMethods.GetMessage(out msg, this.handle, 0, 0) > 0)
{
if (!NativeMethods.TranslateMDISysAccel(this.mdiClientHandle, ref msg))
{
NativeMethods.TranslateMessage(ref msg);
NativeMethods.DispatchMessage(ref msg);
}
}
}
I am trying to override this message loop by using WH_GETMESSAGE hook. The hook is set correctly and works.
private static IntPtr MyGetMessageHook(int code, IntPtr wparam, IntPtr lparam)
{
if (code == 0)
{
MSG msg = (MSG)Marshal.PtrToStructure(lparam, typeof(MSG));
Debug.WriteLine(msg.message);
// Execute GetMessage loop logic.
if (!NativeMethods.TranslateMDISysAccel(MyMdiWindow.MdiClientHandle, ref msg))
{
NativeMethods.TranslateMessage(ref msg);
NativeMethods.DispatchMessage(ref msg);
}
// Set msg fields to 0.
msg.message = 0;
msg.hwnd = IntPtr.Zero;
msg.lParam = IntPtr.Zero;
msg.wParam = IntPtr.Zero;
Marshal.StructureToPtr(msg, lparam, true);
}
return new IntPtr(NativeMethods.CallNextHookEx(IntPtr.Zero, code, wparam, lparam));
}
The code in hook procedure executes, but I don't know why window doesn't respond. msg.message is being set to 0 after dispatching it. What happens with message after calling DispatchMessage?

Related

How to send message from C# to C++ (MESSAGE ONLY WINDOW) using SendMessage (WM_COPYDATA)?

EDIT: I GOT IT FIXED HERE'S MY WORKING FULL CODES TO SET EXAMPLE TO NEW FRIENDS and my original question is below too.
before the codes let me introduce you to some docs (in order):
https://learn.microsoft.com/en-us/windows/win32/winmsg/about-messages-and-message-queues#message-routing
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessage
https://learn.microsoft.com/en-us/windows/win32/winmsg/window-features#message-only-windows
https://learn.microsoft.com/en-us/windows/win32/dataxchg/using-data-copy
http://pinvoke.net/default.aspx/Structures/COPYDATASTRUCT.html
C# program
using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Program
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern long SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr FindWindow(string classname, string windowname);
[StructLayout(LayoutKind.Sequential)]
struct COPYDATASTRUCT
{
public uint dwData;
public int cbData;
public IntPtr lpData;
}
public static IntPtr IntPtrAlloc<T>(T param)
{
IntPtr retval = Marshal.AllocHGlobal(Marshal.SizeOf(param));
Marshal.StructureToPtr(param, retval, false);
return (retval);
}
public static void IntPtrFree(IntPtr preAllocated)
{
if (IntPtr.Zero == preAllocated) throw (new Exception("Go Home"));
Marshal.FreeHGlobal(preAllocated); preAllocated = IntPtr.Zero;
}
const int WM_COPYDATA = 0x004A;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread.Sleep(3000);
string message = "This is a test";
IntPtr hWnd = FindWindow("MyClass", "MyTitle");
if (hWnd == IntPtr.Zero)
{
MessageBox.Show("couldn't find the window");
}
else
{
COPYDATASTRUCT cds;
cds.dwData = 1;
cds.cbData = message.Length + 1;
cds.lpData = Marshal.StringToHGlobalAnsi(message);
IntPtr cdsBuffer = IntPtrAlloc(cds);
SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, cdsBuffer);
IntPtrFree(cds.lpData);
IntPtrFree(cdsBuffer);
}
}
}
}
C++ program
#include <iostream>
#include <Windows.h>
using namespace std;
LRESULT CALLBACK WindProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
if (Msg == WM_COPYDATA)
{
PCOPYDATASTRUCT data = (PCOPYDATASTRUCT)lParam;
MessageBoxA(hWnd, (LPSTR)data->lpData, "info", 0); // The character set depends on the characters you send
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
int main(){
WNDCLASSEX wcx = { 0 };
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.lpfnWndProc = WindProc;
wcx.hInstance = GetModuleHandle(NULL);
wcx.lpszClassName = TEXT("MyClass");
RegisterClassEx(&wcx);
HWND hWnd = CreateWindowEx(0, TEXT("MyClass"), TEXT("MyTitle"), 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);
MSG message;
for(int i = 0; i < 1000; i++)
{
std::cout << "working" << std::endl;
Sleep(2 * 1000);
if(PeekMessage(&message, NULL, 0, 0, PM_NOREMOVE))
{
break;
}
}
int x;
cout<<"got it!";
cin>>x;
return 0;
}
ORGINIAL QUESTION:
I have a C# application that i want to communicate with a c++ process that i create within my C# app.
I have a code in my hand that is supposed to work i suppose but it doesn't. The message simply is not gotten by c++ app.
my C# program:
namespace ScannerGUI
{
public partial class Form1 : Form
{
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)]
struct COPYDATASTRUCT
{
public uint dwData;
public int cbData;
public IntPtr lpData;
}
public static IntPtr IntPtrAlloc<T>(T param)
{
IntPtr retval = Marshal.AllocHGlobal(Marshal.SizeOf(param));
Marshal.StructureToPtr(param, retval, false);
return (retval);
}
public static void IntPtrFree(IntPtr preAllocated)
{
if (IntPtr.Zero == preAllocated) throw (new Exception("Go Home"));
Marshal.FreeHGlobal(preAllocated); preAllocated = IntPtr.Zero;
}
const int WM_COPYDATA = 0x004A;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//creating child process
var prcsInfo = new ProcessStartInfo
{
UseShellExecute=true,
CreateNoWindow = false,
FileName = "main.exe",
};
Process myProcess = Process.Start(prcsInfo);
ChildProcessTracker.AddProcess(myProcess);
Thread.Sleep(3000);
string message = "This is a test";
IntPtr hWnd = myProcess.Handle;
if (hWnd == IntPtr.Zero)
{
MessageBox.Show("couldn't find the process");
}
else
{
COPYDATASTRUCT cds;
cds.dwData = 1;
cds.cbData = message.Length + 1;
cds.lpData = Marshal.StringToHGlobalAnsi(message);
IntPtr cdsBuffer = IntPtrAlloc(cds);
PostMessage(hWnd, WM_COPYDATA, IntPtr.Zero, cdsBuffer);
IntPtrFree(cds.lpData);
IntPtrFree(cdsBuffer);
}
}
}
}
my c++ app (main.exe):
int main(){
MSG message;
for(int i = 0; i < 1000; i++)
{
std::cout << "working" << std::endl;
Sleep(2 * 1000);
if(PeekMessage(&message, NULL, 0, 0, PM_NOREMOVE))
{
break;
}
}
int x;
cout<<"got it!";
cin>>x;
return 0;
}
when i start the c# program. no errors, no nothing.
same with c++, no errors, no nothing but never break the for loop :(
thanks for everyone for their time.
First of all, Process.Handle is the handle of the process instead of the window.
Secondly, since your main.exe is a console application and it only has a console window, you can only get the handle of the console window by using MainWindowHandle. However, the console does not belong to the main.exe, So you cannot use PeekMessage to handle the message sent to the console window. Console windows are owned by the console subsystem, csrss.exe(see https://stackoverflow.com/a/28248281/10611792). You should create your own window for your C++ app, or create a Message-Only window. Then, you can use FindWindow to get the window handle in C#:
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern long SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll",CharSet = CharSet.Unicode)]
static extern IntPtr FindWindow(string classname, string windowname);
...
private void Form1_Load(object sender, EventArgs e)
{
...
Thread.Sleep(3000);
string message = "This is a test";
IntPtr hWnd = FindWindow("MyClass", "MyTitle");
if (hWnd == IntPtr.Zero)
{
MessageBox.Show("couldn't find the process");
}
else
{
COPYDATASTRUCT cds;
cds.dwData = 1;
cds.cbData = message.Length + 1;
cds.lpData = Marshal.StringToHGlobalAnsi(message);
IntPtr cdsBuffer = IntPtrAlloc(cds);
SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, cdsBuffer);
IntPtrFree(cds.lpData);
IntPtrFree(cdsBuffer);
}
}
}
C++:
LRESULT CALLBACK WindProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
if (Msg == WM_COPYDATA)
{
PCOPYDATASTRUCT data = (PCOPYDATASTRUCT)lParam;
MessageBoxA(hWnd, (LPSTR)data->lpData, "info", 0); // The character set depends on the characters you send
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
int main() {
WNDCLASSEX wcx = { 0 };
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.lpfnWndProc = WindProc;
wcx.hInstance = GetModuleHandle(NULL);
wcx.lpszClassName = TEXT("MyClass");
RegisterClassEx(&wcx);
HWND hWnd = CreateWindowEx(0, TEXT("MyClass"), TEXT("MyTitle"), 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);
MSG msg;
for (int i = 0; i < 1000; i++)
{
std::cout << "working" << std::endl;
Sleep(2 * 1000);
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
break;
}
}
int x;
cout << "got it!";
cin >> x;
return 0;
}
Also Note that use SendMessage instead of PostMessage.
In addition, you can also choose other IPC methods.

c# why sendmessage didn't work

Why C++ WindowProc function don't receive message from c#?
C# code call c++ ExeC.exe using SendMessage(). However there is no response in Switch case WM_COPYDATA IN WindowProc function.
C# full code :
public partial class MainWindow : Window
{
public const string strFilePath = "C:/Users/gandis/Desktop/Project/ExeC/Release/ExeC.exe";
public const Int32 WM_COPYDATA = 0x004A;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 message, int wParam, ref int lParam);
public MainWindow()
{
InitializeComponent();
}
private void btn_Start_Click(object sender, RoutedEventArgs e)
{
IntPtr hWnd = GetHandle(strFilePath);
if (hWnd.ToInt32() > 0)
{
int cdss = 1;
SendMessage(hWnd, WM_COPYDATA, 1, ref cdss);
}
}
private IntPtr GetHandle(string strFilePath)
{
IntPtr hWnd = IntPtr.Zero;
hWnd = GetProcess(strFilePath).MainWindowHandle;
return hWnd;
}
private Process GetProcess(string strFilePath)
{
Process proc = new Process();
proc.StartInfo.FileName = strFilePath;
proc.Start();
proc.WaitForInputIdle();
return proc;
}
}
I add only WindowProc virtual furction in MFC.
C++ code :
LRESULT CExeCDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COPYDATA:
AfxMessageBox(_T(__FUNCTION__));
break;
}
return CDialog::WindowProc(message, wParam, lParam);
}
Your declaration of SendMessage() is wrong. The wParam should be UIntPtr, and the lParam should be IntPtr. And you are missing the SetLastError=true attribute on your DllImport.
And if (hWnd.ToInt32() > 0) should be if (hWnd != IntPtr.Zero).
And you should call proc.Refresh() after calling proc.WaitForInputIdle() and before querying proc.MainWindowHandle.
But, most importantly, your input values for the WM_COPYDATA message are wrong, so the message is likely failing to be sent at all (but you are ignoring the return values of SendMessage() and Marshal.GetLastWin32Error(), so you wouldn't know that). Read the documentation:
wParam
A handle to the window passing the data.
lParam
A pointer to a COPYDATASTRUCT structure that contains the data to be passed.
You are passing a literal 1 where an HWND is expected, and you are passing a reference to an int where a reference to a COPYDATASTRUCT is expected.
A C# definition of COPYDATASTRUCT, and an example of sending it with SendMessage(), are available on pinvoke.net:
http://www.pinvoke.net/default.aspx/Structures.COPYDATASTRUCT
[StructLayout(LayoutKind.Sequential)]
struct COPYDATASTRUCT
{
   public IntPtr dwData;    // Any value the sender chooses.  Perhaps its main window handle?
   public int cbData;       // The count of bytes in the message.
   public IntPtr lpData;    // The address of the message.
}
const int WM_COPYDATA = 0x004A;
// An example of sending a message containing a txStruct.
public SendMessage()
{
   IntPtr buffer = IntPtrAlloc(txStruct);
   COPYDATASTRUCT copyData = new COPYDATASTRUCT();
   copyData.dwData = IntPtr.Zero;
   copyData.lpData = buffer;
   copyData.cbData = Marshal.SizeOf(txStruct);
   IntPtr copyDataBuff = IntPtrAlloc(copyData);
   SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, copyDataBuff);
   IntPtrFree(ref copyDataBuff);
   IntPtrFree(ref buffer);
}

C# Pinvoke can't find the Hwnd of Controls after List count was 0 at first time

I'm trying to click a Button in another Application (started from my Programm with Process.Start)
The problem: I need to wait until the Loading screen is disappeared and the GUI pop's up...
My idea was to read all (Hwnd)Controls until a specific Control (Button: "Kill Client") from the GUI was found (=GUI Opened).
But this only works if I wait manually for the GUI and press a "Search Control" button.
If I press the "Search Button" if the Loading Screen is aktive I get a Hwnd = 0 (List<'IntPtr> Count is also 0...) and if i press it again if the GUI is opened it is 0 again(List<'IntPtr> Count too...) !!!
Here my Code:
public class WndSearcher
{
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
public static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
return true;
}
}
My Button:
List<IntPtr> AllControlHandles = WndSearcher.GetChildWindows(selectedCharacter.Botprocess.MainWindowHandle);
IntPtr ControlHandle = AllControlHandles.Find(x => PInvoke.GetWindowTextRaw(x) == "Kill Client" ? true : false);
MessageBox.Show(ControlHandle.ToString());
Part of PInvoke (Class):
const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, [Out] StringBuilder lParam);
public static string GetWindowTextRaw(IntPtr hwnd)
{
// Allocate correct string length first
int length = (int)SendMessage(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, null);
StringBuilder sb = new StringBuilder(length + 1);
SendMessage(hwnd, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
return sb.ToString();
}
Found no solution till now.
So I decided to use AutoHotKey in combination with C#.
In C# I start my AutoHotKey Script and wait until the Script is finished. (Then the external Programm is started completely)
Starting Arguments: 1.Processid 2.NewExternalProgramName
Here my AutoHotKey Script:
counter := 0
Loop, %0% ; For each parameter:
{
param := %A_Index%
if (counter = 0) ; do sth with parameter 1
winwait, ahk_pid %param% ; Not logged in ;wait until the text "Not logged in" can be read (Program started completely)
if (counter = 1) ; do sth with parameter 2
WinSetTitle, %param%
counter += 1
}

Callback from c++ to vb6 in UI Thread

Following setup:
I got a .Net Dll that got an async method called LicenceVerifier
An event will be fired when the method completed.
public class LicenceVerifier
{
private readonly ILicence _licence;
private readonly int _programkey;
public delegate void LicencedCheckedEventHandler(object sender, LicenceVerifierResultArgs args);
public event LicencedCheckedEventHandler LicencedChecked;
public LicenceVerifier(int programKey)
{
_programkey = programKey;
_licence = GetLicensing();
}
public void IsValidLicenceAsync()
{
new Task(() =>
{
LicenceVerifierResult valid = LicenceVerifierResult.NotAvailable;
if (_licence != null)
valid = _licence.IsValid(_programkey);
LicencedChecked(this, new LicenceVerifierResultArgs(valid));
}).Start();
}
On the C++ side it looks that way:
void __stdcall CheckLicenseAsyncVb(int iPrgKey, int cbAddress)
{
LicenceVerifierCallback^ callback = gcnew LicenceVerifierCallback();
callback->lCallbackAddress = cbAddress;
callback->callbackType = VB;
//.Net object
LicenceVerifier^ licenceVerifier = gcnew LicenceVerifier(iPrgKey);
licenceVerifier->LicencedChecked += gcnew LicenceVerifier::LicencedCheckedEventHandler(callback, &LicenceVerifierCallback::handler);
licenceVerifier->IsValidLicenceAsync();
}
the handler on the C++ side:
public ref class LicenceVerifierCallback
{
public:
CallbackType callbackType;
long lCallbackAddress;
void(*callbackFunction)(int);
void handler(System::Object^ sender, LicenceVerifierResultArgs^ e)
{
if(callbackType == VB)
ExecuteCallbackVb(convertResult(e->Result));
if(callbackType == C)
ExecuteCallbackC(callbackFunction, convertResult(e->Result));
};
int convertResult(LicenceVerifierResult verifierResult)
{
if(verifierResult == LicenceVerifierResult::Available)
return 0;
return 1;
}
void ExecuteCallbackVb(int result)
{
typedef void ( *FUNCPTR) (int iResult);
FUNCPTR callBackFunction;
callBackFunction = (FUNCPTR)lCallbackAddress;
callBackFunction(result);
};
vb6:
Private Sub LicenceCheck_Click()
Call CheckLicenseAsyncVb(20110, AddressOf ResultCallback)
End Sub
Public Declare Sub CheckLicenseAsyncVb Lib "LicensingIntfd.dll" Alias "_CheckLicenseAsyncVb#8" (ByVal prgKey As Long, ByVal address As Long)
Public Sub ResultCallback(ByVal result As Long) '
'MsgBox "I'll be never a friend of vb6: " & result
End Sub
I got the problem now, that the callback will run in the workerthread and not the UI thread, means the vb6 call to show the messagebox gonna fail.
I could simply write into a variable in the callback method and then poll the variable in the UI thread for a change. Don't really like that idea though.
Anyone got an idea with a cleaner solution, probably get the c++ side to execute the callback already in the UI thread (how?)?
Any help is much appreciated.
Here is how to use a hidden static window to post WM_USER on the UI thread passing result in wParam
// VC6
LRESULT CALLBACK RedirectWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
class LicenceVerifierVbCallback
{
LPVOID m_cbAddress;
HWND m_hWnd;
WNDPROC m_pOrigWndProc;
public:
LicenceVerifierVbCallback(LPVOID cbAddress)
{
m_cbAddress = cbAddress;
m_hWnd = CreateWindow("STATIC", NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0);
SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);
m_pOrigWndProc = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)RedirectWndProc);
}
~LicenceVerifierVbCallback()
{
DestroyWindow(m_hWnd);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_USER)
ExecuteCallbackVb((int)wParam);
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void ExecuteCallbackVb(int result)
{
// ToDo: impl
}
void handler(LPVOID sender, LPVOID e)
{
WPARAM wParam = 0; // wParam = convertResult(e->Result)
PostMessage(m_hWnd, WM_USER, wParam, 0);
}
};
LRESULT CALLBACK RedirectWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
LicenceVerifierVbCallback *pthis = (LicenceVerifierVbCallback *)GetWindowLong(hwnd, GWL_USERDATA);
if (pthis != NULL)
return pthis->WndProc(hwnd, msg, wParam, lParam);
return DefWindowProc(hwnd, msg, wParam, lParam);
}

WH_JOURNALPLAYBACK hook in C#

I am trying to create a callback for "WH_JOURNALPLAYBACK" hook in C#. This is the code
private delegate IntPtr JournalPlaybackProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr JournalPlaybackCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (HC_GETNEXT == nCode && curr < EventMsgs.Count)
{
EVENTMSG hookStruct = (EVENTMSG)Marshal.PtrToStructure(lParam, typeof(EVENTMSG));
EVENTMSG currentMsg = EventMsgs[curr];
hookStruct.message = currentMsg.message;
hookStruct.paramL = currentMsg.paramL;
hookStruct.paramH = currentMsg.paramH;
hookStruct.hwnd = currentMsg.hwnd;
hookStruct.time = currentMsg.time;
}
if (HC_SKIP == nCode)
{
curr++;
}
if (curr == EventMsgs.Count)
{
UnhookWindowsHookEx(_journalPlaybackProcHookID);
_journalPlaybackProcHookID = IntPtr.Zero;
}
return CallNextHookEx(_journalPlaybackProcHookID, nCode, wParam, lParam);
}
I get the callback correctly, i suppose i need to modify the value of lParam with my data to playback the events. How do i do this?
I assume you need to
Marshal.StructureToPtr(hookStruct,lParam,true);
To write it back at some point. When I run it just hangs though.

Categories