473,383 Members | 1,717 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,383 software developers and data experts.

How to prevent a delegate from relocating on the CLR heap

Hi everyone!

I'm trying to write a global hook in C# and C++/CLI. I'm almost
done if there wasn't this little delegate problem.
I get the function pointer to a delegate and pass it to my
hook.dll. The delegate gets called for a few seconds but then the delegate
seems to be relocated on the CLR heap.
If tried using a GCHandle but as far as I can tell this only
prevents the garbage collector from collecting my delegate and doesn't stop
relocation.
Then there is pin_ptr. But a pinning pointer only works till it
goes out of scope and it can only be declared locally so this happens
immediately after I get the function pointer. By the way, I use ...

proc =
(HOOKPROC)(Marshal::GetFunctionPointerForDelegate( innerEventHandler).ToPointer());

... to get the function pointer.
If anyone knows how to pin the delegate for as long as I need to
point to it from unmanaged code or has any ideas, workarounds, etc. please
let me know!
Thank you very much in advance!

Regards,
Florian

Nov 17 '05 #1
11 9018
Florian,
I'm trying to write a global hook in C# and C++/CLI. I'm almost
done if there wasn't this little delegate problem.
I get the function pointer to a delegate and pass it to my
hook.dll. The delegate gets called for a few seconds but then the delegate
seems to be relocated on the CLR heap.
If tried using a GCHandle but as far as I can tell this only
prevents the garbage collector from collecting my delegate and doesn't
stop relocation.


You are on the right track. You can actually tell a GCHandle that you want
the handle it references to be pinned in the heap so it is not moved during
GC. For this, use the overload of GCHandle::Alloc() that takes a
GCHandleType argument and pass in GCHandleType::Pinned to it.
--
Tomas Restrepo
to****@mvps.org
http://www.winterdom.com/
Nov 17 '05 #2
Thomas,
You are on the right track. You can actually tell a GCHandle that you want
the handle it references to be pinned in the heap so it is not moved
during GC. For this, use the overload of GCHandle::Alloc() that takes a
GCHandleType argument and pass in GCHandleType::Pinned to it.


thank you for your reply but I'm afraid it doesn't work. The Dll project
compiles fine but as soon as I run my C# test app I get an ArgumentException
("Object contains non-primitive or non-blittable data."). Seems like
GCHandle::Alloc() has a problem with my delegate ...
Any suggestions?

Florian
Nov 17 '05 #3
I guess you can pin your delegate before your application runs and cast it
to its native type and store it in a member variable in your application
class.

"Florian A." <al*****************@directbox.com> дÈëÓʼþ
news:eF**************@TK2MSFTNGP10.phx.gbl...
Hi everyone!

I'm trying to write a global hook in C# and C++/CLI. I'm almost done if there wasn't this little delegate problem.
I get the function pointer to a delegate and pass it to my
hook.dll. The delegate gets called for a few seconds but then the delegate
seems to be relocated on the CLR heap.
If tried using a GCHandle but as far as I can tell this only
prevents the garbage collector from collecting my delegate and doesn't stop relocation.
Then there is pin_ptr. But a pinning pointer only works till it goes out of scope and it can only be declared locally so this happens
immediately after I get the function pointer. By the way, I use ...

proc =
(HOOKPROC)(Marshal::GetFunctionPointerForDelegate( innerEventHandler).ToPoint
er());
... to get the function pointer.
If anyone knows how to pin the delegate for as long as I need to point to it from unmanaged code or has any ideas, workarounds, etc. please
let me know!
Thank you very much in advance!

Regards,
Florian

Nov 17 '05 #4
OK. I see I have to clarify a few things. I have two delegates. One with the
same signature as HOOKPROC, one with the signature void
(KeyboardHookEventArgs^). The reason why I need two is I can't instantiate a
managed class (KeyboardHookEventArgs) in my unmanaged callback and that's
why I can't pin it outside the Dll. That's what you are suggesting, isn't
it?

But I could rewrite my code. I planned doing so anyway.

Florian

I guess you can pin your delegate before your application runs and cast it
to its native type and store it in a member variable in your application
class.

"Florian A." <al*****************@directbox.com> дÈëÓʼþ
news:eF**************@TK2MSFTNGP10.phx.gbl...
Hi everyone!

I'm trying to write a global hook in C# and C++/CLI. I'm

almost
done if there wasn't this little delegate problem.
I get the function pointer to a delegate and pass it to my
hook.dll. The delegate gets called for a few seconds but then the
delegate
seems to be relocated on the CLR heap.
If tried using a GCHandle but as far as I can tell this only
prevents the garbage collector from collecting my delegate and doesn't

stop
relocation.
Then there is pin_ptr. But a pinning pointer only works till

it
goes out of scope and it can only be declared locally so this happens
immediately after I get the function pointer. By the way, I use ...

proc =

(HOOKPROC)(Marshal::GetFunctionPointerForDelegate( innerEventHandler).ToPoint
er());

... to get the function pointer.
If anyone knows how to pin the delegate for as long as I need

to
point to it from unmanaged code or has any ideas, workarounds, etc.
please
let me know!
Thank you very much in advance!

Regards,
Florian


Nov 17 '05 #5
I just wrote this little C# App:

namespace PinTestApp
{
public delegate void TestDelegate();

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

TestDelegate test = new TestDelegate(Test);
GCHandle handle = GCHandle.Alloc(test, GCHandleType.Pinned);
handle.Free();
}

private void Test()
{
}
}
}

As soon as the debugger hits the line 'GCHandle handle =
GCHandle.Alloc(test, GCHandleType.Pinned);' I get the same
ArgumentException.
What other options do I have if GCHandle doesn't work?
Nov 17 '05 #6
I read your reply to my Code Project post and modified my test app like
this:

[STAThread]
static void Main()
{
Form1 form = new Form1();
TestDelegate test = new TestDelegate(form.Test);
GCHandle handle = GCHandle.Alloc(test, GCHandleType.Pinned);
Application.EnableVisualStyles();
Application.Run(form);
handle.Free();
}

I still get the same ArgumentException. As I understand it, it's simply not
possible to pin 'non-primitive or non-blittable types' which obviously
includes delegates.
Nov 17 '05 #7
I have tested the callback for SHBrowseForFolder, it worked .
BFFCALLBACK is defined as a native callback type for the SHBrowseForFolder
function.

delegate int BrowseCallBackProcDelegate(
unsigned/*HWND*/ hwnd, unsigned int/*UINT*/ uMsg
, unsigned/*LPARAM*/ lParam, unsigned/*LPARAM*/ lpData);

[STAThreadAttribute]
int main(array<System::String ^> ^args)
{

Form1^ pForm1=gcnew Form1();
// Enabling Windows XP visual effects before any controls are created
Application::EnableVisualStyles();

BrowseCallBackProcDelegate^ BCBD =
gcnew BrowseCallBackProcDelegate(Form1::BrowseForFileCal lBackProc);

pin_ptr<BrowseCallBackProcDelegate^> pBCBD= &BCBD;

IntPtr delegatePointer =
Marshal::GetFunctionPointerForDelegate(BCBD);

pForm1->m_pbi->lpfn=static_cast<BFFCALLBACK>(delegatePointer.ToP ointer());

// Create the main window and run it
Application::Run();
return 0;
}

"Florian A." <al*****************@directbox.com> дÈëÓʼþ
news:ua**************@TK2MSFTNGP10.phx.gbl...
OK. I see I have to clarify a few things. I have two delegates. One with the same signature as HOOKPROC, one with the signature void
(KeyboardHookEventArgs^). The reason why I need two is I can't instantiate a managed class (KeyboardHookEventArgs) in my unmanaged callback and that's
why I can't pin it outside the Dll. That's what you are suggesting, isn't
it?

But I could rewrite my code. I planned doing so anyway.

Florian

I guess you can pin your delegate before your application runs and cast it to its native type and store it in a member variable in your application
class.

"Florian A." <al*****************@directbox.com> дÈëÓʼþ
news:eF**************@TK2MSFTNGP10.phx.gbl...
Hi everyone!

I'm trying to write a global hook in C# and C++/CLI. I'm

almost
done if there wasn't this little delegate problem.
I get the function pointer to a delegate and pass it to my
hook.dll. The delegate gets called for a few seconds but then the
delegate
seems to be relocated on the CLR heap.
If tried using a GCHandle but as far as I can tell this only prevents the garbage collector from collecting my delegate and doesn't

stop
relocation.
Then there is pin_ptr. But a pinning pointer only works
till it
goes out of scope and it can only be declared locally so this happens
immediately after I get the function pointer. By the way, I use ...

proc =

(HOOKPROC)(Marshal::GetFunctionPointerForDelegate( innerEventHandler).ToPoint er());

... to get the function pointer.
If anyone knows how to pin the delegate for as long as I
need to
point to it from unmanaged code or has any ideas, workarounds, etc.
please
let me know!
Thank you very much in advance!

Regards,
Florian



Nov 17 '05 #8
Thank you for trying but my application is written in C#.
Only the hook Dll is written in C++/CLI because it's simply not possible to
write a global hook entirely in managed code.
I have tested the callback for SHBrowseForFolder, it worked .
BFFCALLBACK is defined as a native callback type for the SHBrowseForFolder
function.

delegate int BrowseCallBackProcDelegate(
unsigned/*HWND*/ hwnd, unsigned int/*UINT*/ uMsg
, unsigned/*LPARAM*/ lParam, unsigned/*LPARAM*/ lpData);

[STAThreadAttribute]
int main(array<System::String ^> ^args)
{

Form1^ pForm1=gcnew Form1();
// Enabling Windows XP visual effects before any controls are created
Application::EnableVisualStyles();

BrowseCallBackProcDelegate^ BCBD =
gcnew BrowseCallBackProcDelegate(Form1::BrowseForFileCal lBackProc);

pin_ptr<BrowseCallBackProcDelegate^> pBCBD= &BCBD;

IntPtr delegatePointer =
Marshal::GetFunctionPointerForDelegate(BCBD);

pForm1->m_pbi->lpfn=static_cast<BFFCALLBACK>(delegatePointer.ToP ointer());

// Create the main window and run it
Application::Run();
return 0;
}

Nov 17 '05 #9
Florian,

thank you for your reply but I'm afraid it doesn't work. The Dll project
compiles fine but as soon as I run my C# test app I get an
ArgumentException ("Object contains non-primitive or non-blittable
data."). Seems like GCHandle::Alloc() has a problem with my delegate ...
Any suggestions?


Ahh, true, you are completely correct.

Thinking more closely to what you want to do, I realize you might not need
to actually pin the delegate at all; just keep a reference to it alive so
that it is not garbage collected (it actually does *not* matter that it
moves around in memory, as that won't affect the call to it from unmanaged
code in this particular case).

I looked around to verify this, and found what I was looking for:
http://blogs.msdn.com/cbrumme/archiv.../06/51385.aspx

Here, Chris Brumme explains in detail:

"Along the same lines, managed Delegates can be marshaled to unmanaged code,
where they are exposed as unmanaged function pointers. Calls on those
pointers will perform an unmanaged to managed transition; a change in
calling convention; entry into the correct AppDomain; and any necessary
argument marshaling. Clearly the unmanaged function pointer must refer to a
fixed address. It would be a disaster if the GC were relocating that! This
leads many applications to create a pinning handle for the delegate. This
is completely unnecessary. The unmanaged function pointer actually refers
to a native code stub that we dynamically generate to perform the transition
& marshaling. This stub exists in fixed memory outside of the GC heap.

However, the application is responsible for somehow extending the lifetime
of the delegate until no more calls will occur from unmanaged code. The
lifetime of the native code stub is directly related to the lifetime of the
delegate. Once the delegate is collected, subsequent calls via the
unmanaged function pointer will crash or otherwise corrupt the process. In
our recent release, we added a Customer Debug Probe which allows you to
cleanly detect this – all too common – bug in your code. If you haven’t
started using Customer Debug Probes during development, please take a look!"

Hope this helps!

--
Tomas Restrepo
to****@mvps.org
http://www.winterdom.com/
Nov 17 '05 #10
Hi Tomas!
Hope this helps!


Problem solved! :-) Thank you!
It had nothing to do with the delegate at all. The problem was the return
value in my unmanaged callback. The hook worked till the app window lost
focus. After reactivating the window my delegate didn't receive any
keystroke messages. I assumed that this was because the delegte has been
relocated on the heap. Actually at this point it should have been clear that
I'm wrong. My application didn't crash which most likely would have happend
if my function pointer was pointing "somewhere" in the memory.
I have a few more problems now but I think I can figure them out on my own.
Thanks again!

Regards,
Florian
Nov 17 '05 #11
Hi Florian,
Problem solved! :-) Thank you!
It had nothing to do with the delegate at all. The problem was the return
value in my unmanaged callback. The hook worked till the app window lost
focus. After reactivating the window my delegate didn't receive any
keystroke messages. I assumed that this was because the delegte has been
relocated on the heap. Actually at this point it should have been clear
that I'm wrong. My application didn't crash which most likely would have
happend if my function pointer was pointing "somewhere" in the memory.
I have a few more problems now but I think I can figure them out on my
own.


Cool, I'm glad you were able to fix your issue!

You are right, if the original problem had been that the delegate was been
GCed, then a crash would've happen sooner or later (either at the time of
the call, or because of memory corruption).
--
Tomas Restrepo
to****@mvps.org
http://www.winterdom.com/
Nov 17 '05 #12

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
by: ANt | last post by:
Hi, we have some major GC issues at present with a system we're trying to put live. It's a live calculation engine that's distributed across about 30 Java server processes. A set of processes...
3
by: Edward Diener | last post by:
I am surprised that a MulticastDelegate does not have a particular member function which checks if a particular delegate is in its invocation list. I can do this manually of course but a Find, or...
4
by: rd | last post by:
Hi i'm uncertain about foll. things. Thanks in advance for any help to get me out of this.. >>Each process, with their own stack, will they have their own heap or is it global one(common for...
11
by: ZorpiedoMan | last post by:
This is either a bad bug, or I'm not understanding somthing. In my mind, this should NOT work: ------------------------------------------ Class ShouldntWork Delegate Sub goHere() Sub...
24
by: arcticool | last post by:
I had an interview today and I got destroyed :( The question was why have a stack and a heap? I could answer all the practical stuff like value types live on the stack, enums are on the stack, as...
7
by: sam.m.gardiner | last post by:
I'm working with VB.NET events and I want a way to disconnect all the handlers of an event. I want to do this in the object that is the source of the event. This is slightly tricky in VB.Net as the...
1
by: teslar91 | last post by:
I've been learning VB.NET for the past few weeks. One of the problems I've run into is difficulties updating controls in events from certain components, such as the FileSystemWatcher, that raise...
0
by: Le Minh | last post by:
I used this class to dynamic memory allocation: using System; using System.Runtime.InteropServices; public unsafe class Memory { // Handle for the process heap. This handle is used in all calls...
3
by: puzzlecracker | last post by:
A cut-and-paste from the Jon's book, with a question to follow: Utter garbage! (Or not, as the case may be…)—It’s worth being aware that a delegate instance will prevent its target from being...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.