2

I got following MemoryAddressExtenstion code, it shows object address in C#

Want to know what is

IntPtr**

and

**(IntPtr**)

Thanks

public static class MemoryAddressExtenstion
{
    public static string Address(this object me)
    {
        unsafe
        {
            TypedReference tr = __makeref(me);
            var ptr = **(IntPtr**)&tr;
            return "0x" + ptr.ToString("X");
        }
    }
}

Variable Address

__makeref

TypedReference

Source Code

Max CHien
  • 133
  • 1
  • 8
  • 2
    Where did you took that code from? – xanatos May 31 '18 at 07:04
  • To understand that i'd suggest you look up on pointers in C++, that'll help you undertsand it since it's way more common in C / C++ than in C#. the `**` indicates a pointer to a pointer. – Vulpex May 31 '18 at 07:10
  • If anyone is wondering what on earth `__makeref` is, look [`here`](http://benbowen.blog/post/fun_with_makeref/). – Matthew Watson May 31 '18 at 07:40
  • updated with github source code – Max CHien May 31 '18 at 08:01
  • @MatthewWatson, thanks i put it in reference too – Max CHien May 31 '18 at 08:02
  • Pointer to a pointer to an address, this stuff used to empty half the Programming 101 class before they switched to Java. The makeref creates a pointer to me, the & operator produces its address. The first IntPtr* dereference produces the address of the caller's variable. The second IntPtr* dereference digs out pointer of the object reference stored in that variable, the address in the GC heap. It is dangerous code, the object is not pinned so it can crash with an AVE when a garbage collection occurs at the exact moment this code runs. Very low odds, not zero. Okayish in debug code. – Hans Passant May 31 '18 at 08:20
  • @HansPassant "Pointer to a pointer to an address, this stuff used to empty half the Programming 101 class..." made me chuckle a bit. I had an instructor in my assembly class who's first statement was "Half of you will fail this class; no one will get an A" - he made the first part happen, the second, well let's just say we had one student who got at least 4.0 (all A's) in every class she ever took over the 4 years before she graduated with 5 STEM majors.(while holding a full time job at night) – Mark Schultheiss Jan 13 '19 at 17:07
  • `unsafe` - yep, agree with that part :) – Mark Schultheiss Jan 13 '19 at 17:14

3 Answers3

3

Sadly I can't explain them in a better way... So if someone can correct me then thanks!

What in .NET is called a "reference", in C would be called a "pointer". So what is called object me in C#, would be a void *me, a pointer (called me) to an object.

TypedReference tr = __makeref(me);

This creates a TypedReference to the variable me. As I've said, a reference is very similar to a C pointer (and in fact if you take a look at is source code you will see that it contains a IntPtr Value). So in C it would be:

void **tr = &me;

(in .NET a pointer to a TypedReference, in C a pointer to a pointer)

Now the code is creating a third level of indirectness

void ***temp = &tr;

And then it is dereferencing it twice

void *ptr = **temp;

So we have a *ptr that is the same as the initial *me. We are doing it this way because normally in C# you can't directly convert a reference to the equivalent pointer.

Why are they doing this? Because you normally can't cast a reference to a pointer (you can't (IntPtr)me), nor you can take directly the address of reference (so you can't &me). But you can create a TypedRefence that is equivalent to doing &me. Now, sadly you can't do (IntPtr*)tr, or (IntPtr)tr. But fortunately you can &tr. Now finally you have been able to obtain a real pointer, and you can begin derefencing it to return to the initial pointer that you want.

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • 1
    An important point is that the `TypedReference` struct returned from `__makeref()` contains as its first field an `IntPtr` named `Value` that points to the object of interest, so the cast winds up with that value. (See [the implementation of `TypedReference`](https://referencesource.microsoft.com/#mscorlib/system/typedreference.cs,fa749891bb84425e).) If Microsoft changed the layout of `TypedReference` by adding a new field in front of the `Value` field, the cast would no longer return the correct value. – Matthew Watson May 31 '18 at 08:06
  • @MatthewWatson Yep. I imagined it was that way but I hadn't taken a look. As always, doing undocumented things is like playing with fate. – xanatos May 31 '18 at 08:08
1

** Is commonly used in C / C++ to point to a pointer. IntPtr** asks for a pointer to a pointer to a IntPtr

**(IntPtr**)&tr; references to the above mentioned structure.

It is Possible to use those in C#, see pointers in C# but I suggest to learn more about this you should read up on C++ / C since it is more common in those languages.

Vulpex
  • 1,041
  • 8
  • 19
  • @Amit yeah I know there are pointers in C# but rather uncommon. That's why I suggested to read up on C / C++ to learn more about it since it's more common there. – Vulpex May 31 '18 at 07:23
  • @Amit Thanks for letting me know, I added your link to the answer. – Vulpex May 31 '18 at 07:27
1

Starting from the beginning, an IntPtr is a struct that represents a memory address.

The * after a type-name means type that is a pointer to the preceding type. So IntPtr* means pointer to IntPtr. And IntPtr** means a pointer to a pointer to an IntPtr.

When a type is in parenthesis before an expression it is a type-cast operation. So (IntPtr**)x means cast x to a IntPtr**.

The & operator is the address-of operator. So &tr means get the address of the value tr. Because tr is of type TypedReference the resulting value has type pointer to TypedReference*.

The * operator at the front of the expression is the dereference operator, which returns the value at a given address. So *x is get the value at the memory location pointed to by x. And **x is get the value at the memory location pointed to at *x. This implies that x is a pointer to a pointer.

At first glance though, this should produce garbage. We are converting from a TypedReference* to a IntPtr**. It works only because the first field in a TypedReference is in fact an IntPtr, which points to the original object.

In general this is a dangerous little trick, which should be used with great caution because:

  • memory addresses of objects move (you can use GCHandle.Alloc or the fixed keyword to pin them)
  • __makref is an undocumented and unsupported part of the language
  • this depends on implementation details of TypedRef
  • it is not portable code
cdiggins
  • 17,602
  • 7
  • 105
  • 102