Search code examples
c#.netvb.netstructpinvoke

How to fix Warning CA1900: Member "X" contains the following non-portable fields


The fields Position and FormName of my DevMode struct are marked with the CA1900 code-analysis warning.

This seems a dangerous struct in the meaning of a little change in the definition can generate a lot of problems and stop working as expected (see comments of users in those urls above), so I don't know exactlly how to adapt the mentioned fields to work as expected in 32-bit and 64-bit Windows.

How I could make those marked fields portable?.

DevMode struct (is just a translation with little adaptations of the C# code published in pinvoke.net):

<DebuggerStepThrough>
<StructLayout(LayoutKind.Explicit)>
Public Structure DevMode

    Public Const CchDeviceName As Integer = 32
    Public Const CchFormName As Integer = 32

    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CchDeviceName)>
    <FieldOffset(0)>
    Public DeviceName As String

    <FieldOffset(32)>
    Public SpecVersion As Short

    <FieldOffset(34)>
    Public DriverVersion As Short

    <FieldOffset(36)>
    Public Size As Short

    <FieldOffset(38)>
    Public DriverExtra As Short

    <FieldOffset(40)>
    Public Fields As DeviceModeFields

    <FieldOffset(44)>
    Public Orientation As Short

    <FieldOffset(46)>
    Public PaperSize As Short

    <FieldOffset(48)>
    Public PaperLength As Short

    <FieldOffset(50)>
    Public PaperWidth As Short

    <FieldOffset(52)>
    Public Scale As Short

    <FieldOffset(54)>
    Public Copies As Short

    <FieldOffset(56)>
    Public DefaultSource As Short

    <FieldOffset(58)>
    Public PrintQuality As Short

    <FieldOffset(44)>
    Public Position As Win32.Types.Point

    <FieldOffset(52)>
    Public DisplayOrientation As DeviceModeDisplayOrientation

    <FieldOffset(56)>
    Public DisplayFixedOutput As Integer

    <FieldOffset(60)>
    Public Color As Short

    <FieldOffset(62)>
    Public Duplex As Short

    <FieldOffset(64)>
    Public YResolution As Short

    <FieldOffset(66)>
    Public TTOption As Short

    <FieldOffset(68)>
    Public Collate As Short

    <FieldOffset(72)>
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CchFormName)>
    Public FormName As String

    <FieldOffset(102)>
    Public LogPixels As Short

    <FieldOffset(104)>
    Public BitsPerPixel As Integer

    <FieldOffset(108)>
    Public PixelsWidth As Integer

    <FieldOffset(112)>
    Public PixelsHeight As Integer

    <FieldOffset(116)>
    Public DisplayFlags As Integer

    <FieldOffset(116)>
    Public Nup As Integer

    <FieldOffset(120)>
    Public DisplayFrequency As Integer

End Structure

Point struct:

<DebuggerStepThrough>
<StructLayout(LayoutKind.Sequential)>
Public Structure Point

    Public X As Integer
    Public Y As Integer

    Public Sub New(ByVal x As Integer, ByVal y As Integer)
        Me.X = x
        Me.Y = y
    End Sub

    Public Sub New(ByVal pt As System.Drawing.Point)
        Me.New(pt.X, pt.Y)
    End Sub

    Public Shared Widening Operator CType(ByVal pt As Point) As System.Drawing.Point
        Return New System.Drawing.Point(pt.X, pt.Y)
    End Operator

    Public Shared Widening Operator CType(ByVal pt As System.Drawing.Point) As Point
        Return New Point(pt.X, pt.Y)
    End Operator

End Structure

UPDATE

So this is the original member declarations:

enter image description here

And this is what I've tried following @David Heffernan suggestions, however I did somthing wrong, because the struct doesn't work anymore when I test it in some winApi functions.

What I did wrong?. How to finally fix this?.

<StructLayout(LayoutKind.Sequential)>
Public Structure DevMode

    Private Const CchDeviceName As Integer = 32
    Private Const CchFormName As Integer = 32

    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CchDeviceName)> 
    Public DeviceName As String
    Public SpecVersion As Short
    Public DriverVersion As Short
    Public Size As Short
    Public DriverExtra As Short
    Public Fields As DeviceModeFields
    Public test1 As UnionDevMode1
    Public test2 As UnionDevMode2
    Public Color As Short
    Public Duplex As Short
    Public YResolution As Short
    Public TTOption As Short
    Public Collate As Short
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CchFormName)> 
    Public FormName As String
    Public LogPixels As Short
    Public BitsPerPixel As Integer
    Public PixelsWidth As Integer
    Public PixelsHeight As Integer
    Public test3 As UnionDevMode3
    Public DisplayFrequency As Integer
    Public IcmMethod As Integer
    Public IcmIntent As Integer
    Public MediaType As Integer
    Public DitherType As Integer
    Public Reserved1 As Integer
    Public Reserved2 As Integer
    Public PanningWidth As Integer
    Public PanningHeight As Integer

End Structure

<StructLayout(LayoutKind.Explicit)>
Public Structure UnionDevMode1

    <FieldOffset(0)> Public Orientation As Short
    <FieldOffset(0)> Public PaperSize As Short
    <FieldOffset(0)> Public PaperLength As Short
    <FieldOffset(0)> Public PaperWidth As Short
    <FieldOffset(0)> Public Scale As Short
    <FieldOffset(0)> Public Copies As Short
    <FieldOffset(0)> Public DefaultSource As Short
    <FieldOffset(0)> Public PrintQuality As Short

End Structure

<StructLayout(LayoutKind.Explicit)>
Public Structure UnionDevMode2

    <FieldOffset(0)> Public Position As Win32.Types.Point
    <FieldOffset(0)> Public DisplayOrientation As DeviceModeDisplayOrientation
    <FieldOffset(0)> Public DisplayFixedOutput As Integer

End Structure

<StructLayout(LayoutKind.Explicit)>
Public Structure UnionDevMode3

    <FieldOffset(0)> Public DisplayFlags As Integer
    <FieldOffset(0)> Public Nup As Integer

End Structure

Update2

Just other way that I've tried without success...

<StructLayout(LayoutKind.Sequential)>
Public Structure DevMode

    Private Const CchDeviceName As Integer = 32
    Private Const CchFormName As Integer = 32

    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CchDeviceName)>
    Public DeviceName As String
    Public SpecVersion As Short
    Public DriverVersion As Short
    Public Size As Short
    Public DriverExtra As Short
    Public Fields As DeviceModeFields
    Public test1 As UnionDevMode1
    Public Color As Short
    Public Duplex As Short
    Public YResolution As Short
    Public TTOption As Short
    Public Collate As Short
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CchFormName)>
    Public FormName As String
    Public LogPixels As Short
    Public BitsPerPixel As Integer
    Public PixelsWidth As Integer
    Public PixelsHeight As Integer
    Public test3 As UnionDevMode3
    Public DisplayFrequency As Integer
    Public IcmMethod As Integer
    Public IcmIntent As Integer
    Public MediaType As Integer
    Public DitherType As Integer
    Public Reserved1 As Integer
    Public Reserved2 As Integer
    Public PanningWidth As Integer
    Public PanningHeight As Integer

End Structure

<StructLayout(LayoutKind.Explicit)>
Public Structure UnionDevMode1

    <FieldOffset(0)>
    Public subUnion1 As SubUnionDevMode1

    <FieldOffset(0)>
    Public subUnion2 As SubUnionDevMode2

End Structure

<StructLayout(LayoutKind.Sequential)>
Public Structure SubUnionDevMode1

    Public Orientation As Short
    Public PaperSize As Short
    Public PaperLength As Short
    Public PaperWidth As Short
    Public Scale As Short
    Public Copies As Short
    Public DefaultSource As Short
    Public PrintQuality As Short

End Structure

<StructLayout(LayoutKind.Sequential)>
Public Structure SubUnionDevMode2

    Public Position As Win32.Types.Point
    Public DisplayOrientation As DeviceModeDisplayOrientation
    Public DisplayFixedOutput As Integer

End Structure

<StructLayout(LayoutKind.Explicit)>
Public Structure UnionDevMode3

    <FieldOffset(0)> Public DisplayFlags As Integer
    <FieldOffset(0)> Public Nup As Integer

End Structure

Solution

  • Finally I solved it, thanks to @David Heffernan and company who helped too...

    One thing I discovered (luckily) that was getting me crazy to make the struct work is that if CharSet attribute is set as Auto then the struct stops working because whatever reason.

    In the function(s) that uses a DEVMODE struct, an Ansi charset should be set too (like EnumDisplaySettings, ChangeDisplaySettingsEx, etc) otherwise the struct will not work in my case, more than getting wrong chars I mean other fields will be wrong.

    Here below is the working code, or at least it seems to work as expected. All offsets are equivalents and the size of the struct is 156 as the original, at least in my Windows.

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
    Public Structure DevMode
    
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=32)>
        Public DeviceName As String
        Public SpecVersion As Short
        Public DriverVersion As Short
        Public Size As Short
        Public DriverExtra As Short
        Public Fields As Integer
        Public Mode As DeviceMode
        Public Color As Short
        Public Duplex As Short
        Public YResolution As Short
        Public TTOption As Short
        Public Collate As Short
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=32)>
        Public FormName As String
        Public LogPixels As Short
        Public BitsPerPixel As Integer
        Public PixelsWidth As Integer
        Public PixelsHeight As Integer
        Public Flags As DeviceFlags
        Public DisplayFrequency As Integer
        Public IcmMethod As Integer
        Public IcmIntent As Integer
        Public MediaType As Integer
        Public DitherType As Integer
        Public Reserved1 As Integer
        Public Reserved2 As Integer
        Public PanningWidth As Integer
        Public PanningHeight As Integer
    
    End Structure
    
    <StructLayout(LayoutKind.Explicit)>
    Public Structure DeviceMode
    
        <FieldOffset(0)> Public PrinterDevMode As PrinterDevMode
        <FieldOffset(0)> Public DisplayDevMode As DisplayDevMode
    
    End Structure
    
    <StructLayout(LayoutKind.Explicit)>
    Public Structure DeviceFlags
    
        <FieldOffset(0)> Public DisplayFlags As Integer
        <FieldOffset(0)> Public Nup As Integer
    
    End Structure
    
    <StructLayout(LayoutKind.Sequential)>
    Public Structure PrinterDevMode
    
        Public Orientation As Short
        Public PaperSize As Short
        Public PaperLength As Short
        Public PaperWidth As Short
        Public Scale As Short
        Public Copies As Short
        Public DefaultSource As Short
        Public PrintQuality As Short
    
    End Structure
    
    <StructLayout(LayoutKind.Sequential)>
    Public Structure DisplayDevMode
    
        Public Position As Win32.Types.Point ' 8 bytes.
        Public DisplayOrientation As Integer
        Public DisplayFixedOutput As Integer
    
    End Structure