none
Obsolete Marshal.PtrToStructure( ... ) RRS feed

  • Question

  • I have some related questions about Marshal.PtrToStructure. Thanks for any help.

    1. How is PtrToStructure(IntPtr, Object) used? Can I please have a short simple sample? Even though I have no idea how it works, I think I need to use it.

    2. Because Marshal.PtrToStructure(...) is obsolete, but Marshal.PtrToStructure<T>(...) isn't, how do I use the latter in my code below? It seems to be impossible, therefore the former should not be made obsolete because there's a use case for it. Am I right?

    3. I want a generic struct filler function that fills an arbitrary struct from a binary source (byte stream), is there a better way than what I have? It's slower than it should be because on return of the Fill function the filled data had to be duplicated by the assignment: s1 = (S1) Fill( s1 ). It would be better if the Fill function can work on struct s1 directly rather than having to fill a separate instance.

    namespace ConsoleApp1
    {
      class Program
      {
        [StructLayout(LayoutKind.Sequential, Pack=1)]
        struct S1
        {
          public byte   A;
          public UInt16 B;
          public UInt32 C;
        }
    
    
        [StructLayout(LayoutKind.Sequential, Pack=1)]
        struct S2
        {
          public byte   A;
          public UInt32 B;
          public UInt64 C;
        }
    
    
        static object Fill( object SomeStruct )
        {
          byte[] a = new byte[ Marshal.SizeOf( SomeStruct ) ];
    
          for( int i = 0; i < a.Length; i++ )
            a[i] = 0; // pretend here we are filling with real data
    
          unsafe
          {
            fixed( void *p = a )
            {
              return Marshal.PtrToStructure( (IntPtr) p, SomeStruct.GetType() );
            }
          }
          
        }
    
    
        static void Main( string[] args )
        {
          S1 s1 = new S1(){ A=1, B=2, C=3 };
          S2 s2 = new S2(){ A=4, B=5, C=6 };
    
          s1 = (S1) Fill( s1 );
          s2 = (S2) Fill( s2 );
    
          Console.WriteLine( "" + s1.A + " " + s1.B + " " + s1.C );
          Console.WriteLine( "" + s2.A + " " + s2.B + " " + s2.C );
    
          Console.ReadLine();
        }
      }
    }





    • Edited by Dev10110110 Thursday, March 12, 2020 12:04 AM
    Wednesday, March 11, 2020 11:50 PM

Answers

  • You can find the example on the page of Marshal.StructureToPtr() .

    For the example code you provided, maybe you can change it like this:

            static T Fill<T>(T SomeStruct) where T : struct
            {
                byte[] a = new byte[Marshal.SizeOf(SomeStruct)];
    
                for (int i = 0; i < a.Length; i++)
                    a[i] = 0; // pretend here we are filling with real data
    
                unsafe
                {
                    fixed (void* p = a)
                    {
                        return Marshal.PtrToStructure<T>((IntPtr)p);
                    }
                }
            }

    Make sure you target .NET v4.5.1+ for this to work.


    Thursday, March 12, 2020 1:45 AM
    Answerer
  • Why, of course. This method can only be used on classes which have layout specified.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
    	
    	///
    	/// To be compiled with "csc MarshalTest.cs -unsafe+"
    	///
        class MarshalTest
        {
            [StructLayout(LayoutKind.Sequential, Pack = 1)]
            class S1
            {
                public byte A;
                public UInt16 B;
                public UInt32 C;
            }
    
    
            [StructLayout(LayoutKind.Sequential, Pack = 1)]
            class S2
            {
                public byte A;
                public UInt32 B;
                public UInt64 C;
            }
    
            static void Fill(object SomeStruct, object NewStruct)
            {
                byte[] a = new byte[Marshal.SizeOf(SomeStruct)];
    
                for (int i = 0; i < a.Length; i++)
                    a[i] = 0; // pretend here we are filling with real data
    
                unsafe
                {
                    fixed (void* p = a)
                    {
                        Marshal.PtrToStructure((IntPtr)p, NewStruct);
                    }
                }
    
            }
    
            static void Main(string[] args)
            {
                S1 s1 = new S1() { A = 1, B = 2, C = 3 };
                S2 s2 = new S2() { A = 4, B = 5, C = 6 };
    
                S1 s1a = new S1();
                S2 s2a = new S2();
    
                Fill(s1, s1a);
                Fill(s2, s2a);
    
                Console.WriteLine("" + s1a.A + " " + s1a.B + " " + s1a.C);
                Console.WriteLine("" + s2a.A + " " + s2a.B + " " + s2a.C);
    
                Console.ReadLine();
            }
        }
    }
    

    • Marked as answer by Dev10110110 Thursday, March 12, 2020 3:23 AM
    Thursday, March 12, 2020 3:04 AM
    Answerer

All replies

  • You can find the example on the page of Marshal.StructureToPtr() .

    For the example code you provided, maybe you can change it like this:

            static T Fill<T>(T SomeStruct) where T : struct
            {
                byte[] a = new byte[Marshal.SizeOf(SomeStruct)];
    
                for (int i = 0; i < a.Length; i++)
                    a[i] = 0; // pretend here we are filling with real data
    
                unsafe
                {
                    fixed (void* p = a)
                    {
                        return Marshal.PtrToStructure<T>((IntPtr)p);
                    }
                }
            }

    Make sure you target .NET v4.5.1+ for this to work.


    Thursday, March 12, 2020 1:45 AM
    Answerer
  • Thanks. I need the sample for PtrToStructure(IntPtr, Object). The sample in your link is for PtrToStructure(IntPtr, Type) that I am already using.

    • Edited by Dev10110110 Thursday, March 12, 2020 1:55 AM
    Thursday, March 12, 2020 1:54 AM
  • Thanks for the template suggestion. Code size is a consideration for me. If two pieces of compiled code do the same thing, then I will have to go for the smaller one.

    Of course, if the obsolete function is eventually removed, then there will be no choice but to use the templates. That choice can be delayed until later. Nevertheless, the question of how to use the template version is answered. Silly me, I wasn't thinking about templates when I looked at that function. *slap head*, I don't use templates much.


    • Edited by Dev10110110 Thursday, March 12, 2020 2:16 AM
    Thursday, March 12, 2020 2:03 AM
  • You just "new" the struct variable passed as second parameter here to allocate enough memory for it, then pass it to there.

                static void Fill(object SomeStruct, object NewStruct)
                {
                    byte[] a = new byte[Marshal.SizeOf(SomeStruct)];
    
                    for (int i = 0; i < a.Length; i++)
                        a[i] = 0; // pretend here we are filling with real data
    
                    unsafe
                    {
                        fixed (void* p = a)
                        {
                            Marshal.PtrToStructure((IntPtr)p, NewStruct);
                        }
                    }
    
                }
    In fact you can still find example from .NET source code.
    1) Declare object variable, and initialize it with default constructor of the type.
    2) Call PtrToStructure() to fill it.


    Thursday, March 12, 2020 2:23 AM
    Answerer
  • How do I call this? Like: Fill( s1, s1 );
    Thursday, March 12, 2020 2:29 AM
  • Yes. Although you would want to create a new variable for return value.

    Passing the second parameter by reference means the function can change the content of it.
    Thursday, March 12, 2020 2:31 AM
    Answerer
  • No go. Runtime error on Marshal.PtrToStructure((IntPtr)p, NewStruct):

    System.ArgumentException: 'The structure must not be a value class. Parameter name: structure'

    Also passing by ref doesn't work because the input type must be identical to the declared type of the function. They are different. The caller type is S1, the declared function type is object.

    • Edited by Dev10110110 Thursday, March 12, 2020 2:44 AM
    Thursday, March 12, 2020 2:42 AM
  • Why, of course. This method can only be used on classes which have layout specified.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
    	
    	///
    	/// To be compiled with "csc MarshalTest.cs -unsafe+"
    	///
        class MarshalTest
        {
            [StructLayout(LayoutKind.Sequential, Pack = 1)]
            class S1
            {
                public byte A;
                public UInt16 B;
                public UInt32 C;
            }
    
    
            [StructLayout(LayoutKind.Sequential, Pack = 1)]
            class S2
            {
                public byte A;
                public UInt32 B;
                public UInt64 C;
            }
    
            static void Fill(object SomeStruct, object NewStruct)
            {
                byte[] a = new byte[Marshal.SizeOf(SomeStruct)];
    
                for (int i = 0; i < a.Length; i++)
                    a[i] = 0; // pretend here we are filling with real data
    
                unsafe
                {
                    fixed (void* p = a)
                    {
                        Marshal.PtrToStructure((IntPtr)p, NewStruct);
                    }
                }
    
            }
    
            static void Main(string[] args)
            {
                S1 s1 = new S1() { A = 1, B = 2, C = 3 };
                S2 s2 = new S2() { A = 4, B = 5, C = 6 };
    
                S1 s1a = new S1();
                S2 s2a = new S2();
    
                Fill(s1, s1a);
                Fill(s2, s2a);
    
                Console.WriteLine("" + s1a.A + " " + s1a.B + " " + s1a.C);
                Console.WriteLine("" + s2a.A + " " + s2a.B + " " + s2a.C);
    
                Console.ReadLine();
            }
        }
    }
    

    • Marked as answer by Dev10110110 Thursday, March 12, 2020 3:23 AM
    Thursday, March 12, 2020 3:04 AM
    Answerer
  • Thanks. That does it. I have improved it with:

            static void Fill(object SomeStruct)
            {
                byte[] a = new byte[Marshal.SizeOf(SomeStruct)];
    
                for (int i = 0; i < a.Length; i++)
                    a[i] = 0; // pretend here we are filling with real data
    
                unsafe
                {
                    fixed (void* p = a)
                    {
                        Marshal.PtrToStructure((IntPtr)p, SomeStruct);
                    }
                }
    
            }

    I just knew PtrToStructure(IntPtr, Object) had something to do with it but didn't know how. I have to admit I didn't know classes could be used like structs.



    • Edited by Dev10110110 Thursday, March 12, 2020 3:26 AM
    Thursday, March 12, 2020 3:22 AM