none
Obsolete Marshal.StructureToPtr RRS feed

  • Question

  • The online document says Marshal.StructureToPtr is obsolete. So, what is the replacement for it? If there is no replacement, then how do I accomplish what the obsolete function performs? Thanks.
    Monday, March 9, 2020 2:33 PM

Answers

  • I think in the MSDN documentation, only the call that is not generic is obsolete, and StructureToPtr<T>(T, IntPtr, Boolean) is not. Possibly this is to drive people to declare explicitly the type so the .NET Runtime can accurately and effectively copy and pin the structure instead of having to reflect it.
    • Marked as answer by Dev10110110 Tuesday, March 10, 2020 3:55 AM
    Tuesday, March 10, 2020 1:51 AM
    Answerer

All replies

  • MS uses it in .NET source in many places

    For example , in _SafeNetHandles.cs,

    if you search Marshal.StructureToPtr, it is used several times

    Monday, March 9, 2020 3:47 PM
  • I just thought it odd they didn't specify a replacement. They seem to be saying something about GCHandle.

    I think I can pin the struct in place with unsafe and use the struct directly for win32 calls.


    Monday, March 9, 2020 4:25 PM
  • The online document says Marshal.StructureToPtr is obsolete. So, what is the replacement for it? If there is no replacement, then how do I accomplish what the obsolete function performs? Thanks.

    Hi, Dev182.  I assumed you got the info that this Method was obsolete from some back-alley book or blog about DotNET, so I websearched it and sure enough - MSDN documentation itself says Marshal.StructureToPtr is obsolete.

    I'm calling shenanigans anyway.  MS has been going sharply downhill for a really long time and it frankly looks to me like somebody who doesn't program in any language at all was hired by MS to update documentation pages.  When things really are obsolete (as well as in the several year span when they're deprecated in favor of some new interface, before they go obsolete) there is always (repeat ALWAYS several times in bold, italic, underline and all caps) a reference to the big new thing that you're supposed to use instead. Without that, and considering what Marshal.StructureToPtr does exactly, it's just disinformation.

    In most cases you can pass a struct instance directly to an API function (if you've written your overload to accept the struct) but there are still some API functions that require a pointer to unmanaged/unchanging memory addresses. So without using StructureToPtr you'd have to write extra code and if you were ever to change your struct you'd have to remember to manually update that code not just to include the new field(s) but also to include them in the precise order that an API function expects to find them in memory.  That would make anybody with real DotNET experience laugh really hard when they look at what you've done, because you wasted time writing code when you could have used StructureToPtr.

    Let's just take the Win32 API version of the POINT structure for example:

            /*
                So since we'll never actually use StructureToPtr, the StructLayout attribute
                becomes meaningless too.  This would tell System.Runtime.InteropServices.Marshal
                exactly how to map out the struct's internal fields in memory, saving you the
                headache of having to write it all out yourself.
            */
            //[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
            public struct POINT
            {
                public int x;
                public int y;

                /*
                    You either have to include this function with every struct or else you have to write a
                    whole lot of stupid Reflection code to examine a struct at runtime in order to
                    accomplish the same thing.
                */
                public void CopyToMemory(IntPtr ptr)
                {
                    /*
                        Now you have to manually copy your struct into memory.
                    */
                    System.Runtime.InteropServices.Marshal.WriteInt32(ptr, this.x);
                    System.Runtime.InteropServices.Marshal.WriteInt32(ptr, this.y);

                    /*
                        Any time you add a field to your struct you have to remember
                        to add it to the above list of field-writes and you also have
                        to check the API documentation to make sure you're putting
                        that field in the exact proper position in memory.
                     
                        If your new field happens to be a string of characters, then
                        you would additionally have to be really careful when using
                        any Win32 API function that has both ANSI and Unicode
                        versions, and make sure your original pointer allocation
                        accounted for the number of bytes resulting from text-encoding
                        that string field into whatever appropriate format the
                        API function demands, including a terminating \0 character.
                    
                        Worse, most of the Win32 API functions that actually call for
                        strings don't want a real string value - they want a pointer
                        to a string - which in WinAPI means creating the string,
                        encoding the string, calling a different function to allocate
                        different memory, properly encoding the string, properly
                        terminating the encoded bytes with a \0, copying that string
                        into allocated memory, then presenting that pointer anywhere
                        that the API wants a string defined by LPTSTR (that's not the
                        only Windows API Data Type that represents a string pointer,
                        by the way), and finally freeing all the extraneous pointers.
                     
                        Marshal.StructureToPtr does all of this work for you, and its
                        accompanying Marshal.DestroyStructure does all of the cleanup.
                    */
                }
            }


            private void Form1_Load(object sender, EventArgs e)
            {
                POINT apiPoint = new POINT();
                apiPoint.x = 100;
                apiPoint.y = 200;

                /*
                    Since we don't actually have to deal with strings in a POINT struct,
                    we can just allocate the memory we really need.
                  
                    If there were any strings you'd have to encode them to the proper
                    binary format, check for and possibly add a terminating 0 to the
                    raw byte data, and make additional memory allocations.
                    
                    Marshal.StructureToPtr would do all that for you...
                */
                IntPtr apiPointPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(System.Runtime.InteropServices.Marshal.SizeOf(apiPoint));
                /*
                    Remember to copy the struct data to memory...
                */
                apiPoint.CopyToMemory(apiPointPtr);

                /*
                    Now wherever your Win32 API functions require an IntPtr to a
                    struct, you pass apiPointPtr.
                */

                // So you do your work with the manually allocated pointer here.

                /*
                    Finally, when all the work is done, you have to manually free
                    the allocated memory.
                
                    If you had any strings in your struct you would also have to
                    remember to free their pointers.
                */
                System.Runtime.InteropServices.Marshal.FreeHGlobal(apiPointPtr);
            }

    Again, using this code would be stupid because that's the job performed by Marshal.StructureToPtr.

    My advice is to continue using StructureToPtr until or unless MS starts hiring competent grown-ups to write and maintain documentation pages.


    Before you can learn anything new you have to learn that there's stuff you don't know.

    Monday, March 9, 2020 5:24 PM
  • I think in the MSDN documentation, only the call that is not generic is obsolete, and StructureToPtr<T>(T, IntPtr, Boolean) is not. Possibly this is to drive people to declare explicitly the type so the .NET Runtime can accurately and effectively copy and pin the structure instead of having to reflect it.
    • Marked as answer by Dev10110110 Tuesday, March 10, 2020 3:55 AM
    Tuesday, March 10, 2020 1:51 AM
    Answerer
  • I think in the MSDN documentation, only the call that is not generic is obsolete, and StructureToPtr<T>(T, IntPtr, Boolean) is not. Possibly this is to drive people to declare explicitly the type so the .NET Runtime can accurately and effectively copy and pin the structure instead of having to reflect it.
    Having written a number of generics, I believe that the generic function NOT being obsolete just proves my point from the previous post.  The only difference between passing an Object instance and passing a specified type to a generic is that the generic saves itself a single GetType() call by making consumers specify the type ahead of time.  There's going to be exactly the same amount of System.Reflection voodoo involved either way.

    Before you can learn anything new you have to learn that there's stuff you don't know.

    Tuesday, March 10, 2020 3:25 AM
  • I think in the MSDN documentation, only the call that is not generic is obsolete, and StructureToPtr<T>(T, IntPtr, Boolean) is not. Possibly this is to drive people to declare explicitly the type so the .NET Runtime can accurately and effectively copy and pin the structure instead of having to reflect it.
    Now that you mentioned it, it is apparent. Still, the obsolete message should have directed people properly. Thanks for having sharper eyes than mine.
    Tuesday, March 10, 2020 3:58 AM