DllImport, Callbacks and garbage collection

  • Thread starter Thread starter Fabien Penso
  • Start date Start date
F

Fabien Penso

Hi.

I am trying to make this work but I got a weird behavior. I got a very
basic system, I call a unmanaged "dllimported" function and give it a
structure of callback functions.

Sometimes, the unmanaged part calls one of the callback functions. But
the first one has its pointer changed from its address to "0x00000001".
I can't figure out why.

My thought is it gets garbage collected, but I don't see why (I tried
different ways, I currently use the structure instance as static). Also
why does the garbage collector would set this to 1 instead of 0 ?

Any link or help will be appreciated :)

The code : http://www.rafb.net/paste/results/odKsGI15.html
 
Hi,

If you want the garbage collector not to change your addresses use the Fixed
command on your declaration, the same happened to me using unsafe code.

Best regards
SAlva
 
Salvador said:
Hi,

If you want the garbage collector not to change your addresses use the Fixed
command on your declaration, the same happened to me using unsafe code.

I am not sure it would work, "fixed" can only be used for a short amount
of time, my callbacks are called all the time until the program exits.
Also as the new address is "0x00000001" I would rather suspect a garbage
collecting that a move of the object.
 
Hi,

Defenitly the Garbarge collection changes the memory address to optimize the
access and avoiding fragmentation. Also on evert collect your object will be
moved to the third generation so it can change the address at least two times
if necessary.

As you said, you can pin objects only on a method enviroment or using
stackalloc (because the stack is not under the garbage collection domain). If
you want to prevent the object to be moved on the heap you can use a managed
array, the contents of the managed array never changes because the garbage
collector will move only the position of the array but never touches the
content.

Regards
Salva
 
Fabien Penso said:
Hi.

I am trying to make this work but I got a weird behavior. I got a very
basic system, I call a unmanaged "dllimported" function and give it a
structure of callback functions.

Sometimes, the unmanaged part calls one of the callback functions. But the
first one has its pointer changed from its address to "0x00000001". I
can't figure out why.

My thought is it gets garbage collected, but I don't see why (I tried
different ways, I currently use the structure instance as static). Also
why does the garbage collector would set this to 1 instead of 0 ?

Any link or help will be appreciated :)

The code : http://www.rafb.net/paste/results/odKsGI15.html

This can't be done, you are passing a structure containing "Delegate" types
and pretend they are function pointers in native code, which they aren't.
You have to pass the Delegate "function" pointer, this is essentially what
you do when you pass a Delegate as argument to unmanaged code like this:

public static extern void SomeNativeFunction(Delegate pd, ....

here the PInvoke layer does its magic and extracts the function pointer from
the Delegate and passes this pointer as argument, but you are passing a
struct and PInvoke doesn't care that each element is a Delegate.

Now, using v2.0 it's possible to get the function pointer of a delegate, but
in v1.x you can use this simple hack.

// use a msvcrt function to return a pointer from the delegate
[DllImport("msvcrt")]
public static extern IntPtr strncpy(Delegate pd, IntPtr src, int size);

static IntPtr GetFunctionPtrFromDelegate(Delegate d)
{
// strncpy returns the buffer address (the marshaled delegate
pointer).
//We obviously don't copy anything
return strncpy(d, IntPtr.Zero, 0);
}

Usage:
1. Change your struct to hold IntPtr's

public struct phCallbacks {
public IntPtr callProgress;
...

// Call GetFunctionPtrFromDelegate passing the delegate as argument, store
the returned IntPtr in the struct.
SIPphapi.callbacksfunc.callProgress = GetFunctionPtrFromDelegate( new
CallBackPtr(callProgress));
....

Hope this helps.
Willy.
 
Salvador said:
Hi,

If you want the garbage collector not to change your addresses use the
Fixed
command on your declaration, the same happened to me using unsafe code.

Best regards
SAlva

The GC has nothing to do with this see my other reply.

Willy.
 
Fabien Penso said:
Hey Willy, *thanks a lot*, it does the job just fine.

Great, don't forget to remove this hack when moving this code to v2.0
(search for GetFunctionPointerForDelegate in .NET 2.0 Framework).

Willy.
 
As you said, you can pin objects only on a method enviroment or using
| stackalloc (because the stack is not under the garbage collection
domain). If

You can also use a GCHandle to pin an object, but as with all pinning, the
object is restricted to a basic type (int, char, arrays, etc).

| you want to prevent the object to be moved on the heap you can use a
managed
| array, the contents of the managed array never changes because the
garbage
| collector will move only the position of the array but never touches the
| content.


This is not completely accurate. The GC can (and will) move arrays (or any
other object, for that matter) allocated on the heap, unless they're pinned.

-Chris
 
What about just decorating the fields in the struct with

[MarshalAs(UnmanagedType.FunctionPtr)]

This is available in 1.1 and 2.0. No need for hacks.
 
Ted Miller said:
What about just decorating the fields in the struct with

[MarshalAs(UnmanagedType.FunctionPtr)]

This is available in 1.1 and 2.0. No need for hacks.

Good catch, it's the default attribute for Delegate arguments, must have
missed the fact that it could be applied to fields to.

Thanks,
Willy.
 
Back
Top