none
GCHandle routines fail: AddrOfPinnedObject() and FromIntPtr() RRS feed

  • Question

  • Why does the following code crash the executable (I launch from the VS debugger):

        static int Main(string[] args)
        {
            byte[] hash = new byte[20];
            GCHandle gch = GCHandle.Alloc(hash, GCHandleType.Pinned);
            IntPtr MyPtr = gch.AddrOfPinnedObject();
            GCHandle temp = GCHandle.FromIntPtr(MyPtr);  //This line crashes the executable (the exception cannot be caught!!!!).
            temp.Free();

            return 0;
        }

    The system event log says:

         .NET Runtime version 2.0.50727.3053 - Fatal Execution Engine Error (000007FEF95CAA6E) (80131506)

    I am running on Vista x64 SP1 with VS2008 SP1 with .net 3.5 SP1.  I have found a thread of discussion where somebody says its a .net bug for which MS provided a February 2009 hotfix:

       http://stackoverflow.com/questions/334706/fatal-execution-engine-error-79ffee24-80131506 

    But when I look up the the listed hotfix ( https://connect.microsoft.com/VisualStudio/Downloads/DownloadDetails.aspx?DownloadID=16827&wa=wsignin1.0) it doesn't say it has any relationship to the problem I'm facing (nor does it say which files I need or how to install them).

    Have I found a new .net bug?


    -Brent Arias
    Tuesday, June 30, 2009 1:41 AM

Answers

  • Actually, I would create a separate Interop layer for your situation. All the memory allocation/deallocation (and marshaling) should be done specifically in that layer. This is a common approach to more complex interop scenarios.

    That way, you don't end up with a structure that has an IntPtr that may or may not need to be free'd.

            -Steve


    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
    MSBuild user? Try out the DynamicExecute task in the MSBuild Extension Pack source; it's currently in Beta so get your comments in!
    • Marked as answer by Zhi-Xin Ye Monday, July 6, 2009 11:35 AM
    Tuesday, June 30, 2009 5:21 PM

All replies

  • You can only call FromIntPtr passing an IntPtr that was gotten from ToIntPtr. Sorry; not a bug.

           -Steve
    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
    MSBuild user? Try out the DynamicExecute task in the MSBuild Extension Pack source; it's currently in Beta so get your comments in!
    Tuesday, June 30, 2009 1:51 AM
  • Well, you've at most found a validation bug.  GCHandle.FromIntPtr() requires its argument to be a tracking handle.  You didn't pass a tracking handle, you passed a raw memory pointer.  The only way to generate a valid one in C# code is with GCHandle.ToIntPtr().  Not sure that really is a serious bug, the outcome would be the same, an unhandled exception.

    Hans Passant.
    Tuesday, June 30, 2009 2:00 AM
    Moderator
  • This is awful!!!

    This means there is no way to provide object-oriented or RAII behavior for interop clean up.  That is, I can't do this:

    [StructLayout(LayoutKind.Sequential)]
    public MyStruct {
       public IntPtr hash;
       public int hashLength;

       public MyStruct(byte[] myHash){
           GCHandle gch = GCHandle.Alloc(myHash, GCHandleType.Pinned);
           hash = gch.AddrOfPinnedObject();
           hashLength = myHash.Length;
       }
       ~MyStruct(){
           GCHandle temp = GCHandle.FromIntPtr(hash);
           temp.Free();
       }
    }

    Instead I will have to allocate this memory seperately from "MyStruct" and clean it up seperately.  And if I have several levels of nested structs involved in interop, then I'm having to do the allocation and cleanup for all of them - as if there was no such thing as Object Orientation.

    Am I missing something?  Please tell me this isn't true...
    -Brent Arias
    Tuesday, June 30, 2009 2:28 AM
  • I'm not sure what you're trying to do with that. Could you describe your interop scenario?

    You can call GCHandle.FromIntPtr, but you HAVE TO PASS a value that was gotten from GCHandle.ToIntPtr. FromIntPtr has absolutely nothing to do with AddrOfPinnedObject.

    RAII is possible in managed code, but it can't be enforced (the users of the class may choose to nondeterministically release resources instead).

           -Steve


    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
    MSBuild user? Try out the DynamicExecute task in the MSBuild Extension Pack source; it's currently in Beta so get your comments in!
    Tuesday, June 30, 2009 3:22 AM
  • A bit dramatic, don't you think?  If you can't afford to add fields, just put a wrapper around the structure:

        [StructLayout(LayoutKind.Sequential)]
        public struct UnmanagedStruct {
          public IntPtr hash;
          public int hashLength;
        }

        public class Wrapper {
          private GCHandle gch;
          public UnmanagedStruct us;
          public Wrapper(byte[] myHash) {
            gch = GCHandle.Alloc(myHash, GCHandleType.Pinned);
            us.hash = gch.AddrOfPinnedObject();
            us.hashLength = myHash.Length;
          }
          ~Wrapper() {
            gch.Free();
          }
        }


    Hans Passant.
    Tuesday, June 30, 2009 3:34 AM
    Moderator
  • I'm not sure what you're trying to do with that. Could you describe your interop scenario?
    -Steve

    Imagine you've got nested structs A, B, C, D - all containing a variety of value types.  However, in addition,  struct C needs to have an IntPtr to a byte array made through a GCHandle.Alloc(,pinned) invocation (*footnote1) and struct D needs to have another IntPtr created either through AllocHGlobal or AllocCoTaskMem (*footnote2).  These nested structures need to be sent to *or* received from Win32 native code simply by passing struct A.  That means half the time I will be doing the special allocation for structs C and D, and half the time the Win32 native code will allocate the memory for me.  When Win32 allocates the memory and provides struct A to me, I have two scenarios for cleanup:

    1) If struct A was provided to me through the call notation CallFunc(ref A foo), then I assume cleanup (including for contained struct C and D) will happen automatically.
    2) If struct A was provided to me through the call notation CallFunc(out IntPtr foo), then I assume I must somehow explicitly free 'foo'.  Its not clear if I must explicitly free contained elements from struct C and D as well.

    Either way, is there an RAII story that can be told?

    (*footnote1): I don't know how else to pass a byte array pointer in a cross-platform accessible fashion.  MSDN documentation does not cover this scenario.  It only covers arrays in pinvoke signatures:
    http://msdn.microsoft.com/en-us/library/hk9wyw21.aspx
    (*footnote2): MSDN documentation doesn't provide the rationale of why I'd choose one over the other, but it seems every example I find online uses AllocCoTaskMem (is there something wrong with AllocHGlobal?)
    -Brent Arias
    Tuesday, June 30, 2009 4:14 PM
  • A bit dramatic, don't you think?  If you can't afford to add fields, just put a wrapper around the structure:


    A wrapper will work, but in this case it violates the principal "design and code so that your code is difficult to use incorrectly, and simple to use correctly."  I think the direction I am gravitating, since I'm not seeing an RAII story for interop, is to add to my struct a method that might look like this:

    public GCHandle AllocAndInit(byte[] hash);

    Now the work of how that hash is handled is still kept internal to the struct, but the use of the word "Alloc" in the name and the return value of a GCHandle is a big hint to people using my code...that they need to free that thing themselves.
    -Brent Arias
    Tuesday, June 30, 2009 4:20 PM
  • Actually, I would create a separate Interop layer for your situation. All the memory allocation/deallocation (and marshaling) should be done specifically in that layer. This is a common approach to more complex interop scenarios.

    That way, you don't end up with a structure that has an IntPtr that may or may not need to be free'd.

            -Steve


    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
    MSBuild user? Try out the DynamicExecute task in the MSBuild Extension Pack source; it's currently in Beta so get your comments in!
    • Marked as answer by Zhi-Xin Ye Monday, July 6, 2009 11:35 AM
    Tuesday, June 30, 2009 5:21 PM
  • Sorry for being too late to reply :) I've met the same problem.

    These two IntPtr's are different.. One is just a C* unmanaged, but another is an IntPtr - managed

    What I've done, I just added to the passing structure another field of IntPtr

    And pass that IntPtr together with IntPtr that has been gotten from AddrOfPinnedObject();

    And after I got back that struct I Free the one IntPtr that has been gotten from GCHandle.ToIntPtr

    struct STR {IntPtr data, IntPtr handle};
    
    //When send>
    STR str = new STR();
    GCHandle bufferPin = GCHandle.Alloc(new(byte[1000]), GCHandleType.Pinned);
    str.data = bufferPin.AddrOfPinnedObject();
    str.handle = GCHandle.ToIntPtr(bufferPin);  
    
    //When received
    str.data ---> proceed with data
    bufferGCHandle.FromIntPtr(str.handle).Free();

    These two IntPtr's are different.. One is just a C* unmanaged, but another is an IntPtr - managed


    Friday, March 29, 2019 10:08 PM