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);
}
Related
In my application's form, I have two RichTextBox objects. They will both always have the same number of lines of text. I would like to "synchronize" the vertical scrolling between these two, so that when the user changes the vertical scroll position on one, the other scrolls the same amount. How might I go about doing this?
Thanks Jay for your answer; after some more searching I also found the method described here. I'll outline it below for anyone else interested.
First, declare the following enums:
public enum ScrollBarType : uint {
SbHorz = 0,
SbVert = 1,
SbCtl = 2,
SbBoth = 3
}
public enum Message : uint {
WM_VSCROLL = 0x0115
}
public enum ScrollBarCommands : uint {
SB_THUMBPOSITION = 4
}
Next, add external references to GetScrollPos and SendMessage.
[DllImport( "User32.dll" )]
public extern static int GetScrollPos( IntPtr hWnd, int nBar );
[DllImport( "User32.dll" )]
public extern static int SendMessage( IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam );
Finally, add an event handler for the VScroll event of the appropriate RichTextBox:
private void myRichTextBox1_VScroll( object sender, EventArgs e )
{
int nPos = GetScrollPos( richTextBox1.Handle, (int)ScrollBarType.SbVert );
nPos <<= 16;
uint wParam = (uint)ScrollBarCommands.SB_THUMBPOSITION | (uint)nPos;
SendMessage( richTextBox2.Handle, (int)Message.WM_VSCROLL, new IntPtr( wParam ), new IntPtr( 0 ) );
}
In this case, richTextBox2's vertical scroll position will be synchronized with richTextBox1.
I did this for a small project a while ago, and here's the simplist solution I found.
Create a new control by subclassing RichTextBox:
public class SynchronizedScrollRichTextBox : System.Windows.Forms.RichTextBox
{
public event vScrollEventHandler vScroll;
public delegate void vScrollEventHandler(System.Windows.Forms.Message message);
public const int WM_VSCROLL = 0x115;
protected override void WndProc(ref System.Windows.Forms.Message msg) {
if (msg.Msg == WM_VSCROLL) {
if (vScroll != null) {
vScroll(msg);
}
}
base.WndProc(ref msg);
}
public void PubWndProc(ref System.Windows.Forms.Message msg) {
base.WndProc(ref msg);
}
}
Add the new control to your form and for each control explicitly notify the other instances of the control that its vScroll position has changed. Somthing like this:
private void scrollSyncTxtBox1_vScroll(Message msg) {
msg.HWnd = scrollSyncTxtBox2.Handle;
scrollSyncTxtBox2.PubWndProc(ref msg);
}
I think this code has problems if all the 'linked' controls don't have the same number of displayable lines.
[Visual Studio C# 2010 Express, v10.0.30319 on a Windows 7 64bit installation]
I've used Donut's solution posted above, but found a problem when scrolling to the end of RichTextBoxes that contain many lines.
If the result of GetScrollPos() is >0x7FFF then when nPos is shifted, the top bit is set. The creation of the IntPtr with the resulting wParam variable will then fail with an OverflowException. You can easily test this with the following (the second line will fail):
IntPtr ip = new IntPtr(0x7FFF0000);
IntPtr ip2 = new IntPtr(0x80000000);
A version of SendMessage() that uses UIntPtr would appear to be a solution, but I couldn't get that to work. So, I've use the following:
[DllImport("User32.dll")]
public extern static int SendMessage(IntPtr hWnd, uint msg, UInt32 wParam, UInt32 lParam);
This should be good up to 0xffff, but would fail after that. I've not yet experienced a >0xffff result from GetScrollPos(), and assume that User32.dll is unlikely to have a 64bit version of SendCommand(), but any solutions to that problem would be greatly appreciated.
const int WM_USER = 0x400;
const int EM_GETSCROLLPOS = WM_USER + 221;
const int EM_SETSCROLLPOS = WM_USER + 222;
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int msg, int wParam, ref Point lParam);
private void RichTextBox1_VScroll(object sender, EventArgs e)
{
Point pt;
SendMessage(RichTextBox1.Handle, EM_GETSCROLLPOS, 0, ref pt);
SendMessage(RichTextBox2.Handle, EM_SETSCROLLPOS, 0, ref pt);
}
private void RichTextBox2_VScroll(object sender, EventArgs e)
{
Point pt;
SendMessage(RichTextBox1.Handle, EM_GETSCROLLPOS, 0, ref pt);
SendMessage(RichTextBox2.Handle, EM_SETSCROLLPOS, 0, ref pt);
}
A variation of Jay's subclass approach can be found in Joseph Kingry's answer here: Synchronizing Multiline Textbox Positions in C#.
Joseph's approach also subclasses but doesn't require a _VScroll event handler. I used that approach to do a 3-way bind between 3 boxes and added WM_HSCROLL.
#Sudhakar MuthuKrishnan's answer needs some fixes, but works. Thanks!
First GetScrollPos which rised event and then set scroll position for others.
private void RichTextBox1_VScroll(object sender, EventArgs e)
{
Point pt = new Point();
SendMessage(RichTextBox1.Handle, EM_GETSCROLLPOS, 0, ref pt);
SendMessage(RichTextBox2.Handle, EM_SETSCROLLPOS, 0, ref pt);
}
private void RichTextBox2_VScroll(object sender, EventArgs e)
{
Point pt = new Point();
SendMessage(RichTextBox2.Handle, EM_GETSCROLLPOS, 0, ref pt);
SendMessage(RichTextBox1.Handle, EM_SETSCROLLPOS, 0, ref pt);
}
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.
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);
}
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?
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.