1

I think there might be a bug in the C# method

Marshal.PtrToStructure<T>(IntPtr ptr, T structure)

I wrongly assumed T structure means you can pass in your structure you want to fill but when I do this as in the example below an exception is thrown:

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

To give you a simple example:

using System;
using System.Runtime.InteropServices;

public class Program
{
    public static void Main()
    {
        MyStruct s = new MyStruct();

        var buffer = new Byte[]{1,2,3};
        var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        Marshal.PtrToStructure<MyStruct>(handle.AddrOfPinnedObject(), s);
        handle.Free();
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct MyStruct
    {
        public Byte a;
        public Byte b;
        public Byte c;
    }
}

Am I reading the method decleration wrong? Or is there a bug?

  • 2
    https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.ptrtostructure?view=netframework-4.7.2#System_Runtime_InteropServices_Marshal_PtrToStructure__1_System_IntPtr___0_ https://social.msdn.microsoft.com/Forums/vstudio/en-US/0372911d-c200-47f0-91ac-a35428751e6b/what-is-a-quotformatted-classquot?forum=clr – mjwills Feb 13 '19 at 21:07
  • 1
    If you think about it, this can't work for structs anyway, since arguments are passed by value. If the method filled out the copy of `s` you passed it, that wouldn't affect `s` for you. – Blorgbeard Feb 13 '19 at 21:12
  • I'm not going to dupe-hammer it, but there are other questions here about this. E.g. https://stackoverflow.com/questions/2079868/marshal-ptrtostructure-throwing-system-argumentexception-error – Blorgbeard Feb 13 '19 at 21:12
  • 1
    Not a bug, this is a documented exception. But sure, it was a design mistake. That's why the method overload has the [Obsolete] attribute. It can only work when T is a class, that makes the name of the method rather painful. No biggie, just omit the second argument and use the return value. You do want to dig a bit why you did not get the obsoletion diagnostic. – Hans Passant Feb 13 '19 at 23:10
  • I updated the question because I didn't relize the was missing from the method. The full method is Marshal.PtrToStructure(IntPtr ptr, T structure) – AnonyMuskOx Feb 14 '19 at 00:02
  • @Blorgbeard no this is not the same he is using the deprecated version wihtout the type specifier look at the [link](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.ptrtostructure?view=netframework-4.7.2#System_Runtime_InteropServices_Marshal_PtrToStructure__1_System_IntPtr___0_) – AnonyMuskOx Feb 14 '19 at 00:04
  • It's the same issue though. You can't pass a struct to a method "to be filled out". You need to use the other overload, which returns the result: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.ptrtostructure?view=netframework-4.7.2#System_Runtime_InteropServices_Marshal_PtrToStructure__1_System_IntPtr_ – Blorgbeard Feb 14 '19 at 00:51
  • And quoting from the link in your comment: "You cannot use this method overload with value types." - so it's working as documented. – Blorgbeard Feb 14 '19 at 00:56
  • It's definitely confusing design though, calling the parameter `structure` and then forbidding `struct` with a runtime exception that says "value class" instead of "value type". – Blorgbeard Feb 14 '19 at 00:57
  • Thanks for the clarification @Blorgbeard. – AnonyMuskOx Feb 14 '19 at 20:24

3 Answers3

1

The parameter name structure is misleading here. The documentation calls out twice that it must be a class.

The type of structure. This must be a formatted class.

You cannot use this method overload with value types.

However, it also self-contradicts itself when it says

Structure layout is not sequential or explicit.

Looking at the actual source, you don't find a where T : class constraint either which I guess given the docs should be present.

Tanveer Badar
  • 5,438
  • 2
  • 27
  • 32
0
[StructLayout(LayoutKind.Sequential, Pack = 1)]
class MyStruct
{
    public Byte a;
    public Byte b;
    public Byte c;
}

Had this problem myself and found that changing the structure to a class (ref type) over comes the problem.

Tanveer Badar
  • 5,438
  • 2
  • 27
  • 32
0

Instead of converting your struct to a class (or record, I presume), there is a new overload in 4.5.1 and newer (and .NET core), which did work for me using a struct.

var msgReceived = new FixedLengthStruct();
unsafe
{
  fixed(byte* buffer = memoryAccessor.Bytes)
  {
    msgReceived = Marshal.PtrToStructure<FixedLengthStruct>((IntPtr)buffer);
  }
}
// Process msgReceived

My struct here used LayoutKind.Explicit.

Rainmaker
  • 173
  • 3
  • 13