Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / Managed C++ / July 2005

Tip: Looking for answers? Try searching our database.

How to prevent a delegate from relocating on the CLR heap

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Florian A. - 15 Jul 2005 17:45 GMT
           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
Tomas Restrepo (MVP) - 16 Jul 2005 03:37 GMT
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.
[quoted text clipped - 4 lines]
> 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.

Signature

Tomas Restrepo
tomasr@mvps.org
http://www.winterdom.com/

Florian A. - 16 Jul 2005 06:15 GMT
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
Tomas Restrepo (MVP) - 16 Jul 2005 21:48 GMT
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/archive/2003/05/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!

Signature

Tomas Restrepo
tomasr@mvps.org
http://www.winterdom.com/

Florian A. - 17 Jul 2005 09:36 GMT
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
Tomas Restrepo (MVP) - 17 Jul 2005 16:37 GMT
Hi Florian,

> Problem solved! :-) Thank you!
> It had nothing to do with the delegate at all. The problem was the return
[quoted text clipped - 6 lines]
> 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).

Signature

Tomas Restrepo
tomasr@mvps.org
http://www.winterdom.com/

jiangsheng[MVP] - 16 Jul 2005 06:19 GMT
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.

>             Hi everyone!
>
[quoted text clipped - 11 lines]
>
>             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
[quoted text clipped - 4 lines]
>             Regards,
>             Florian
Florian A. - 16 Jul 2005 10:24 GMT
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
[quoted text clipped - 33 lines]
>>             Regards,
>>             Florian
Florian A. - 16 Jul 2005 15:20 GMT
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?
Florian A. - 16 Jul 2005 15:43 GMT
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.
jiangsheng[MVP] - 16 Jul 2005 16:50 GMT
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::BrowseForFileCallBackProc);

pin_ptr<BrowseCallBackProcDelegate^> pBCBD= &BCBD;

IntPtr delegatePointer =
 Marshal::GetFunctionPointerForDelegate(BCBD);

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

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

> 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
[quoted text clipped - 30 lines]
> >>
> >>             proc =

(HOOKPROC)(Marshal::GetFunctionPointerForDelegate(innerEventHandler).ToPoint
> > er());
> >>
[quoted text clipped - 8 lines]
> >>             Regards,
> >>             Florian
Florian A. - 16 Jul 2005 18:56 GMT
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
[quoted text clipped - 26 lines]
> return 0;
> }

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.