Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory address of an object in C#

Tags:

c#

.net

I have a function written some time ago (for .NET 3.5), and now that I have upgraded to 4.0

I can't get it to work.

The function is:

public static class MemoryAddress {     public static string Get(object a)     {         GCHandle handle = GCHandle.Alloc(a, GCHandleType.Pinned);         IntPtr pointer = GCHandle.ToIntPtr(handle);         handle.Free();         return "0x" + pointer.ToString("X");     } } 

Now, when I call it - MemoryAddress.Get(new Car("blue"))

public class Car {     public string Color;     public Car(string color)     {         Color = color;     } } 

I get the error:

Object contains non-primitive or non-blittable data.

Why doesn't it work anymore?

How can I now get the memory address of managed objects?

like image 430
lejon Avatar asked Feb 14 '11 16:02

lejon


People also ask

How do I find the address of an object?

In order to get the address of an object, from C++11, the template function std::addressof() should be used instead.

How objects are stored in memory in C?

There are two parts of memory in which an object can be stored: stack – Memory from the stack is used by all the members which are declared inside blocks/functions. Note that the main is also a function. heap – This memory is unused and can be used to dynamically allocate the memory at runtime.

What is the memory address of an array in C?

When a variable is created in C, a memory address is assigned to the variable. The memory address is the location of where the variable is stored on the computer.


2 Answers

You can use GCHandleType.Weak instead of Pinned. On the other hand, there is another way to get a pointer to an object:

object o = new object(); TypedReference tr = __makeref(o); IntPtr ptr = **(IntPtr**)(&tr); 

Requires unsafe block and is very, very dangerous and should not be used at all. ☺


Back in the day when by-ref locals weren't possible in C#, there was one undocumented mechanism that could accomplish a similar thing – __makeref.

object o = new object(); ref object r = ref o; //roughly equivalent to TypedReference tr = __makeref(o); 

There is one important difference in that TypedReference is "generic"; it can be used to store a reference to a variable of any type. Accessing such a reference requires to specify its type, e.g. __refvalue(tr, object), and if it doesn't match, an exception is thrown.

To implement the type checking, TypedReference must have two fields, one with the actual address to the variable, and one with a pointer to its type representation. It just so happens that the address is the first field.

Therefore, __makeref is used first to obtain a reference to the variable o. The cast (IntPtr**)(&tr) treats the structure as an array (represented via a pointer) of IntPtr* (pointers to a generic pointer type), accessed via a pointer to it. The pointer is first dereferenced to obtain the first field, then the pointer there is dereferenced again to obtain the value actually stored in the variable o – the pointer to the object itself.

However, since 2012, I have come up with a better and safer solution:

public static class ReferenceHelpers {     public static readonly Action<object, Action<IntPtr>> GetPinnedPtr;      static ReferenceHelpers()     {         var dyn = new DynamicMethod("GetPinnedPtr", typeof(void), new[] { typeof(object), typeof(Action<IntPtr>) }, typeof(ReferenceHelpers).Module);         var il = dyn.GetILGenerator();         il.DeclareLocal(typeof(object), true);         il.Emit(OpCodes.Ldarg_0);         il.Emit(OpCodes.Stloc_0);         il.Emit(OpCodes.Ldarg_1);         il.Emit(OpCodes.Ldloc_0);         il.Emit(OpCodes.Conv_I);         il.Emit(OpCodes.Call, typeof(Action<IntPtr>).GetMethod("Invoke"));         il.Emit(OpCodes.Ret);         GetPinnedPtr = (Action<object, Action<IntPtr>>)dyn.CreateDelegate(typeof(Action<object, Action<IntPtr>>));     } } 

This creates a dynamic method that first pins the object (so its storage doesn't move in the managed heap), then executes a delegate that receives its address. During the execution of the delegate, the object is still pinned and thus safe to be manipulated via the pointer:

object o = new object(); ReferenceHelpers.GetPinnedPtr(o, ptr => Console.WriteLine(Marshal.ReadIntPtr(ptr) == typeof(object).TypeHandle.Value)); //the first pointer in the managed object header in .NET points to its run-time type info 

This is the easiest way to pin an object, since GCHandle requires the type to be blittable in order to pin it. It has the advantage of not using implementation details, undocumented keywords and memory hacking.

like image 81
IS4 Avatar answered Sep 18 '22 08:09

IS4


Instead of this code, you should call GetHashCode(), which will return a (hopefully-)unique value for each instance.

You can also use the ObjectIDGenerator class, which is guaranteed to be unique.

like image 23
SLaks Avatar answered Sep 17 '22 08:09

SLaks