0

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
ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • From MSDN: **How to Fix Violations** `All fields that are smaller than 8 bytes must have offsets that are multiple of their size, and fields that are 8 bytes or greater must have offsets that are a multiple of 8. Another solution is to use LayoutKind.Sequential instead of LayoutKind.Explicit if reasonable.` – Ňɏssa Pøngjǣrdenlarp Dec 12 '15 at 21:12
  • What possessed you??!! Explicit layout?! Why? – David Heffernan Dec 12 '15 at 23:05
  • @Plutonix Thanks for comment, I know what you quoted, but this is the first time that I see the warning in a string value and a structure value, that confuses me, not sure how to solve it. This seems a not "reasonable" case in which the members can be sequential, because then the struct doesn't works anymore, at least with the members declarations that I have. – ElektroStudios Dec 13 '15 at 07:47
  • @David Heffernan Thanks for comment. Simply because when in doubt, I preferred to follow the steps of pinvoke.net that provides a working C# example. If I set the **LayoutKind.Sequential** then the struct stops working with functions like **EnumDisplaySettings** (fields of the struct are not set, empty). I'm on Windows 8.1 x64 if that matters. – ElektroStudios Dec 13 '15 at 07:48
  • 1
    pinvoke.net is often wrong, don't regard its content as even remotely trustworthy. Sequential is how the C++ type is declared. You just have to get the members right in your .net code. – David Heffernan Dec 13 '15 at 07:55
  • 1
    Basically, do what I say here: http://stackoverflow.com/questions/34248749/decalring-c-struct-with-union-on-c-sharp – David Heffernan Dec 13 '15 at 08:11
  • 1
    @David Heffernan I get the idea, thankyou, however... could you help me to apply the proper fieldoffsets?, could you supply the unions please?, I'm not sure for example about the size of a **POINTL** struct, neither the correct offset positions of other members in the unions. – ElektroStudios Dec 13 '15 at 09:41
  • The only offset you supply is 0 for the members of a union – David Heffernan Dec 13 '15 at 10:46
  • @David Heffernan I asked you the fieldoffset because in this answer I seen a different thing: http://stackoverflow.com/questions/126781/c-union-in-c-sharp The modification doesn't works for me, please give me some minutes to update my question with the advances of the unions... thanks... – ElektroStudios Dec 13 '15 at 14:07
  • @David Heffernan I've updated the question, please help me to finally solve this problem... Thankyou so much. – ElektroStudios Dec 13 '15 at 14:15
  • That answer is the wrong way to do it. Do it the way I demonstrate in my linked answer above. You've done it wrongly above. I'd really like you to try to understand my answer to that question I linked to. – David Heffernan Dec 13 '15 at 14:31
  • @David Heffernan Of course I'm trying to follow your annotations because you are the "unmanaged code" guy, however I'm failing when implementing the modifications. I did a second edit to my question. – ElektroStudios Dec 13 '15 at 14:33
  • 1
    Update 2 is the correct approach. Now you need to check that you got the offsets right. Use a C++ compiler and the `offsetof` macro, and in the .net code compare with `Marshal.OffsetOf`. Then you'll be able to find the error. – David Heffernan Dec 13 '15 at 14:38
  • @hvd Thanks for comment, you reffer to the first or to the second update of the question? because I've set the offsets and layoutkinds right in the second update, or I don't?. David Heffernan Sincerelly I'm not a C++ guy, even If I start trying your approach there will be some mistakes on where surely I'll fail in sizes translations from one lang to another then I enter in a infinite loop of questions about this struct... – ElektroStudios Dec 13 '15 at 14:49
  • 1
    @ElektroStudios Yeah, I deleted my comment when I noticed you got that right in your second update. :) –  Dec 13 '15 at 14:50
  • @Elektro I could debug this for you. But why should I? I know that you can do it. You just need to try. Use `sizeof` to begin with. The `offsetof` to debug further. – David Heffernan Dec 13 '15 at 15:02
  • @David Heffernan Maybe I could or maybe not, I used C++ just a few times... anyways I'm downloading code::blocks to try what you've pointed out, I just hope you can still be here answering my questions in these comments if I fail in my intent with C++... thanks a lot for your time. – ElektroStudios Dec 13 '15 at 15:22
  • VS has a perfectly good C++ compiler that you can use – David Heffernan Dec 13 '15 at 15:23
  • @David Heffernan yeah but I can't use C++ features (I can't create a C++ project), I decided to "cut" them when installing VS because I don't programming in C++. Maybe was a bad decission hehe. – ElektroStudios Dec 13 '15 at 15:24
  • Yeah, you need a C++ compiler on hand – David Heffernan Dec 13 '15 at 15:28
  • @David Heffernan My first union of two structs is bad, before the union all offsets matches, but after the union the dmColor field in C++ is at offset 60 while in the code of my second update above is at offset 68, so **UnionDevMode1** is wrong, but how I find out which is the conflicting member? because the Marshal.OffsetOf expects a string with the member name but the member is a struct with two sub-structs various fields. – ElektroStudios Dec 13 '15 at 15:53
  • At first view I think it should be one of the members of **SubUnionDevMode1** or **SubUnionDevMode2** struct, but In C++ the **POINTL** sizes 8 bytes, in my code the **POINT** 8 bytes too. **DeviceModeDisplayOrientation** enum is an Integer. I can't see anything wrong. – ElektroStudios Dec 13 '15 at 16:00
  • So keep digging. Look at the structs in the union. Their sizes and offsets. You are doing really well! – David Heffernan Dec 13 '15 at 16:01
  • But how I could use Marshal.OffsetOf in this case to dig more?, a hint about how to proceed now please, because it expects a string with the member name, but the member is the struct UnionDevMode1, the uninion with encapsulated structs with fields so... – ElektroStudios Dec 13 '15 at 16:04
  • I'm stuck, any idea?. – ElektroStudios Dec 13 '15 at 16:25
  • Ok, so I analyzed all the fields and all offsets are equivalent finally, however, the struct is not working with any win32 function that takes that struct as argument... – ElektroStudios Dec 13 '15 at 17:43
  • Use OffsetOf and SizeOf on the structs that are contained in the union. – David Heffernan Dec 13 '15 at 18:31

1 Answers1

1

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
ElektroStudios
  • 19,105
  • 33
  • 200
  • 417