Hooks, delegates and callbacks

  • Thread starter Chris Tacke, eMVP
  • Start date
C

Chris Tacke, eMVP

I'm not sure whether this is a C# or C++ question, so I'm asking in both
groups.

Here's my scenario:
I need to get all text added to a ListBox that is in another process that I
have no control over. I want to get this information in my C# application
through a callback.

Here's what I've done:
Since global hooks must be done in C, I've written a simple DLL that finds
the handle of the ListBox I want the info from, hooks it, and gets the
string data as it is added. This all works just fine.

The problem:
From C# I create a delegate and pass it into the DLL prior to hooking the
ListBox. The DLL then saves the function pointer to a global variable for
use in the hook procedure. I *can* successfully call the callback from the
routine that accepts the handler. When I try to use said handle from the
hook proc, it is NULL and things go bad.

So this is basically what I've got:

#pragma data_seg(".shared")
CallBack m_callback = NULL;
#pragma data_seg()

typedef void (__stdcall *CallBack)(LPCTSTR);

__declspec(dllexport)
BOOL __stdcall LoadHook(CallBack callback)
{
// If already hooked, don't do it again.

// save the hook to a global area
m_callback = callback;

// I *CAN* call the callback here - this works:
m_callback("test");
}

LRESULT CALLBACK HookProc(int nCode,WPARAM wParam,LPARAM lParam)
{
CWPSTRUCT *lpcwt;
lpcwt = (CWPSTRUCT *)lParam;

switch (lpcwt->message)
{

case LB_ADDSTRING:
// my global callback handle is ALWAYS NULL here
if(m_callback)
m_callback("test");

}
}


I've also tried using a memory-mapped file to store the callback function
pointer, but that also fails.

Is there something special about a hook proc that makes this so it can't
work?

Any pointers are GREATLY appreciated!
 
G

Grant Richins [MS]

Are you sure you're keeping a reference to the delegate in your C# code?
Otherwise the GC will think the delegate is available for collection.
 
C

Chris Tacke, eMVP

It's a member variable of the C# calling class, which is a member variable
of my main app Form, so it should be valid until the form dies. Is it
possible that it's getting moved by the memory manager or something? Here
are the C# guts....

public delegate void HookCallback(string Message);

public class DataGrabber : System.Windows.Forms.Form
{
private HookClass m_hook;

....
}

public class HookClass
{
private HookCallback hookcallback;

public HookClass()
{
hookcallback = new HookCallback(HookProc);

LoadHook(hookcallback);
}

public void HookProc(string Message)
{
}

[DllImport("MyHook.dll", CallingConvention=CallingConvention.StdCall)]
internal static extern int LoadHook(HookCallback cb);
}
 
M

Mattias Sjögren

Chris,
Here's my scenario:
I need to get all text added to a ListBox that is in another process that I
have no control over. I want to get this information in my C# application
through a callback.

Do you have to get new strings when they are added to the listbox? I
think it would be easier to do a polling solution that checks for
changes in the listbox at a fixed interval.

The problem:
From C# I create a delegate and pass it into the DLL prior to hooking the
ListBox.

That won't work since the callback address is only valid in the
process space of your app, not the hooked app.

You have to find another way to pass back the information to the C#
app. Perhaps sending a WM_COPYDATA message or putting the data in a
memory mapped file.



Mattias
 
1

100

Hi Chris,
Do you use /SECTION I can't seet in your code. Without that you don't share
anything

#pragma comment(linker, "/SECTION:.shared,RWS")
#pragma data_seg(".shared")
int nShared = 0;
#pragma data_seg()

HTH
B\rgds
100
 
C

Chris Tacke, eMVP

Yep, it's time dependent and actually must link with another thread
gathering other data at the same time, so polling won't work.
 
C

Chris Tacke, eMVP

the #pragma comment line just adds a linker option without me having to go
to the project settings. The shared segment name is ".shared"
 
G

Grant Richins [MS]

My only experience is using the function pointer while still inside the
original function that got passed the delegate. My best guess is that once
the function returns the runtime is tearing down the marshaled function
pointer. I would try 2 things: pass the delegate in on a new thread, so it
can block until the native function pointer is no longer needed, or try to
figure out how to use Marshal.GetUnmanagedThunkForManagedMethodPtr.

HTH
 
C

Chris Tacke, eMVP

Godd ideas, thanks. Right now I'm chasing an alternate method of creating a
Window in my C# app, passing it's handle down to the DLL, which then uses
WM_COPYDATA to send the data back. Of course it's not working, but
hopefully it's something I've overlooked. I can cathc a WM_USER message,
but the lParam string is empty. When I move to WM_COPYDATA, I don't get the
message. Go figure.
 
C

Chris Tacke, eMVP

Well, I got the WM_COPYDATA piece working. I may go back and try to get the
callback working in the future, but the WM_COPYDATA showed an interesting
insight. If I pass in a target HWND for the WM_COPYDATA target and store it
in the shared data section it works just fine. It's just the callback
method pointer that gets destroyed. I'm thinking that you're right about
the marshaler tearing down the callback pointer.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top