GCHandle to get address(pointer) of .net object

17,380

Solution 1

As Tim and thecoop has pointed out, GCHandle.Alloc may prevent garbage collection but actual object address can change as GC may move object around unless you pin the object. Further, your code is using GCHandleType.WeakTrackResurrection and that would not event prevent the garbage collection. GCHandle.ToIntPtr will give address of handle that can be round-tripped over unmanaged call. Actual object address will be given by AddrOfPinnedObject method.

Said all that, IMO, your code may serve the purpose of associating .NET object with unmanaged object. This is because GCHandle.To/FromIntPtr will get back you correct GCHandle and you can reach your .NET object via it (provided its not garbage collected). IMO, it should be immaterial if actual object address had changed or not.

Solution 2

You'll want to pin the GCHandle to stop the object moving around, as the GC moves objects around, so an unpinned pointer could become invalid. Pinning the object stops it moving:

GCHandle handle = GCHandle.Alloc(obj, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();

You'll also have to free the handle when you're done:

handle.Free();

Solution 3

What you are getting is not actually an address.

As you notice, it seems to act like an address most of the time, and you can recall the object, by using GCHandle.FromIntPtr. However, the interesting issue is that you are using GCHandleType.WeakTrackResurrection.

If your weakly referenced object gets collected (it presumably can, since it is only weakly referenced by the GCHandle), you still have the IntPtr, and you can pass it to GCHandle.FromIntPtr(). If you do so, then you get back null, assuming the IntPtr has not been recycled.

(If the IntPtr has been recycled by the CLR for some reason, then you are in trouble. I'm not sure whether this can happen.)

You are better off using either GCHandleType.Normal, or GCHandleType.Pinned (if you need to take the address of the object in unmanaged code), if you want a strong reference to the object.

(To use GCHandleType.Pinned, your object must e.g. be primitive, or have [StructLayout] attribute, or be an array of such objects.)

Solution 4

AFAIK the address does not change because of allocing, is it true

The address of a managed object does change. The garbage collector is free to move objects in memory. When the garbage collector runs, it collects unused objects, then rearranges the remaining objects to minimize the overall size of the managed heap.

does anyone have a better idea to serve my purpose?

I'm not sure you'll find a good way to keep hold of a pointer for a managed object for long periods of time. It's possible to pin objects in memory, and get their address that way, but in C# it's possible to pin objects only within a single method.

It would help if you explained in more detail what you're going to do with the pointer once you have it.

Share:
17,380
ali_bahoo
Author by

ali_bahoo

filled for a badge. :)

Updated on June 23, 2022

Comments

  • ali_bahoo
    ali_bahoo about 2 years

    I managed to get the address of a .net object by

    GCHandle objHandle = GCHandle.Alloc(obj,GCHandleType.WeakTrackResurrection);
    int address = GCHandle.ToIntPtr(objHandle).ToInt32();  
    

    and I can recall the object by

    Object obj = GCHandle.FromIntPtr(IntPtr(address)).Target;
    

    Well, the purpose is to store the address in a native class and have an information of which native object is releated to which .net object.
    AFAIK the address does not change because of allocing, is it true or does anyone have a better idea to serve my purpose?

    Thanks

    • Martin Ch
      Martin Ch almost 12 years
      is there possibility to set value of "pinned object" like : GCHandle.FromIntPtr(IntPtr(address)).Target = myNewObject ??
    • ali_bahoo
      ali_bahoo almost 12 years
      @MartinCh: I don't know if it is legal to set the Target property. Why don't you ask to SO?
  • ali_bahoo
    ali_bahoo over 13 years
    "An instance with nonprimitive (non-blittable) members cannot be pinned." says microsoft. that is why i use WeakTrackResurrection
  • ali_bahoo
    ali_bahoo over 13 years
  • ali_bahoo
    ali_bahoo over 13 years
    well i tested the code with creating 10 mil of objects between setting and getting the address variable and surprized to see that it didn't change. that is because it was the address to handle of the object as you said. so how can i prevent objects from garbage collection?
  • VinayC
    VinayC over 13 years
    Why not use GCHandleType.Normal - that would prevent garbage collection.
  • VinayC
    VinayC over 13 years
    On second thoughts, it may not work - for example, if you create GCHandle in a method, takes it address via ToIntPtr and pass it unmanaged code and then do handle.Free because its going out of scope then Free call would make object eligible for GC. If you are in such scenario, I would say that you can create a static dictionary/list to your objects to prevent garbage collection.
  • ali_bahoo
    ali_bahoo over 13 years
    I will create and hold the GCHandle varible inside the .net object. So if the object stays, the handle will stay also. I used GCHandleType.Normal type but as the objects are created the memory grows steadily, because handles are kept as a member in the object. But it is a must since the objects with weak handle types are surely GC collected when forced GC.collection is called.
  • Tim Lovell-Smith
    Tim Lovell-Smith over 13 years
    I think he's right it should be pinned. What's up with blittable? See the comment on MSDN "If you can't get a pinned GC Handle for a class, you're probably missing some [StructLayout(LayoutKind.*)] in your type definitions."