GCHandle.FromIntPointer does not work as expected

Here's a very simple (complete) program for using GCHandle.FromIntPointer:

using System; using System.Runtime.InteropServices; namespace GCHandleBugTest { class Program { static void Main(string[] args) { int[] arr = new int[10]; GCHandle handle = GCHandle.Alloc(arr, GCHandleType.Pinned); IntPtr pointer = handle.AddrOfPinnedObject(); GCHandle handle2 = GCHandle.FromIntPtr(pointer); } } } 

Note that this program is essentially a transliteration of the procedure described in English on the CLR through C # (4e) on page 547. Running it, however, leads to an unmanaged exception, for example:

Additional Information: The runtime has encountered a fatal error. The address of the error was at 0x210bc39b, on thread 0x21bc. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.

Thinking that this could be a mistake in .NET 4.5, and since I see nothing obviously wrong, I tried the exact same program in Mono on Linux (v2.10.8.1). I got a slightly more informative but still cryptic exception for the GCHandle value belongs to a different domain.

As far as I know, handle really belongs to the same AppDomain as the code in which I call GCHandle.FromIntPtr . But the fact that I see an exception in both implementations makes me suspect that I am missing some important details. What's going on here?

+4
source share
2 answers

AddrOfPinnedObject not the opposite of FromIntPtr . Instead, you want ToIntPtr :

 IntPtr pointer = handle.ToIntPtr (); GCHandle handle2 = GCHandle.FromIntPtr (pointer); 

FromIntPtr does not accept the address of the object; it accepts an opaque value (which is defined as IntPtr), which is used to retrieve the object using ToIntPtr .

+5
source

You have the wrong mental model. FromIntPtr () can only convert the value you return from ToIntPtr (). These are convenient methods, convenient, in particular, for storing a reference to a managed object (and saving it in memory) in unmanaged code. the gcroot <> template class relies on it, used in C ++ projects. This is convenient because unmanaged code must store a pointer.

The base value, the actual pointer, is called a "handle", but it really is a pointer to a table that the garbage collector supports. The table creates additional references to objects, in addition to those found by the garbage collector. Essentially, it allows the managed entity to survive, even if the program no longer has a valid reference to the entity.

GCHandle.AddrOfPinnedObject () returns a completely different pointer, it points to the actual managed object, and not to the "descriptor". The exception message "belongs to another domain" is understandable because the specified table is associated with the AppDomain.

A crash in .NET 4.5 is very similar to a bug. It performs a test with an internal CLR function called MarshalNative :: GCHandleInternalCheckDomain (). Version v2 of the CLR throws an ArgumentException with the message "Cannot pass GCHandle via AppDomains". But version v4 crashes inside this method, which in turn throws an ExecutionEngineException. This does not look intentional.

Feedback report submitted at connect.microsoft.com

+7
source

Source: https://habr.com/ru/post/1488389/


All Articles