[WINSPOOL] backups/colins-printing-for-freedom@73041
authorColin Finck <colin@reactos.org>
Fri, 15 Jan 2016 04:36:31 +0000 (04:36 +0000)
committerColin Finck <colin@reactos.org>
Fri, 15 Jan 2016 04:36:31 +0000 (04:36 +0000)
Additional tests show that IsValidDevmodeA/W actually does a lot more than I previously thought.
Implement the missing known features and test this little function to death in winspool_apitest to cover each and every possible call.
They pass on ReactOS, Windows Server 2003 and Windows 8.1.

Credits and many thanks go to Mark Jansen for the additional tests!

svn path=/branches/colins-printing-for-freedom/; revision=70595

reactos/win32ss/printing/base/winspool/devmode.c
rostests/apitests/winspool/IsValidDevmode.c

index d035f86..2349d5f 100644 (file)
 
 #include "precomp.h"
 
+typedef struct _MINIMUM_SIZE_TABLE
+{
+    DWORD dwField;
+    WORD wSize;
+}
+MINIMUM_SIZE_TABLE, *PMINIMUM_SIZE_TABLE;
+
+/**
+ * Minimum required DEVMODEA structure size based on the used fields. Must be in descending order!
+ */
+static MINIMUM_SIZE_TABLE MinimumSizeA[] = {
+    { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEA, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPanningHeight) },
+    { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEA, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPanningWidth) },
+    { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEA, dmDitherType) + RTL_FIELD_SIZE(DEVMODEA, dmDitherType) },
+    { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEA, dmMediaType) + RTL_FIELD_SIZE(DEVMODEA, dmMediaType) },
+    { DM_ICMINTENT, FIELD_OFFSET(DEVMODEA, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEA, dmICMIntent) },
+    { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEA, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEA, dmICMMethod) },
+    { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEA, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFrequency) },
+    { DM_NUP, FIELD_OFFSET(DEVMODEA, dmNup) + RTL_FIELD_SIZE(DEVMODEA, dmNup) },
+    { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEA, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFlags) },
+    { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEA, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPelsHeight) },
+    { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEA, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPelsWidth) },
+    { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEA, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEA, dmBitsPerPel) },
+    { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEA, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEA, dmLogPixels) },
+    { DM_FORMNAME, FIELD_OFFSET(DEVMODEA, dmFormName) + RTL_FIELD_SIZE(DEVMODEA, dmFormName) },
+    { DM_COLLATE, FIELD_OFFSET(DEVMODEA, dmCollate) + RTL_FIELD_SIZE(DEVMODEA, dmCollate) },
+    { DM_TTOPTION, FIELD_OFFSET(DEVMODEA, dmTTOption) + RTL_FIELD_SIZE(DEVMODEA, dmTTOption) },
+    { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEA, dmYResolution) + RTL_FIELD_SIZE(DEVMODEA, dmYResolution) },
+    { DM_DUPLEX, FIELD_OFFSET(DEVMODEA, dmDuplex) + RTL_FIELD_SIZE(DEVMODEA, dmDuplex) },
+    { DM_COLOR, FIELD_OFFSET(DEVMODEA, dmColor) + RTL_FIELD_SIZE(DEVMODEA, dmColor) },
+    { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEA, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFixedOutput) },
+    { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEA, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayOrientation) },
+    { DM_POSITION, FIELD_OFFSET(DEVMODEA, dmPosition) + RTL_FIELD_SIZE(DEVMODEA, dmPosition) },
+    { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEA, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEA, dmPrintQuality) },
+    { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEA, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEA, dmDefaultSource) },
+    { DM_COPIES, FIELD_OFFSET(DEVMODEA, dmCopies) + RTL_FIELD_SIZE(DEVMODEA, dmCopies) },
+    { DM_SCALE, FIELD_OFFSET(DEVMODEA, dmScale) + RTL_FIELD_SIZE(DEVMODEA, dmScale) },
+    { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEA, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPaperWidth) },
+    { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEA, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEA, dmPaperLength) },
+    { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEA, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEA, dmPaperSize) },
+    { DM_ORIENTATION, FIELD_OFFSET(DEVMODEA, dmOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmOrientation) },
+    { 0, 0 }
+};
+
+/**
+ * Minimum required DEVMODEW structure size based on the used fields. Must be in descending order!
+ */
+static MINIMUM_SIZE_TABLE MinimumSizeW[] = {
+    { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEW, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPanningHeight) },
+    { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEW, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPanningWidth) },
+    { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEW, dmDitherType) + RTL_FIELD_SIZE(DEVMODEW, dmDitherType) },
+    { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEW, dmMediaType) + RTL_FIELD_SIZE(DEVMODEW, dmMediaType) },
+    { DM_ICMINTENT, FIELD_OFFSET(DEVMODEW, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEW, dmICMIntent) },
+    { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEW, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEW, dmICMMethod) },
+    { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEW, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFrequency) },
+    { DM_NUP, FIELD_OFFSET(DEVMODEW, dmNup) + RTL_FIELD_SIZE(DEVMODEW, dmNup) },
+    { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEW, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFlags) },
+    { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEW, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPelsHeight) },
+    { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEW, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPelsWidth) },
+    { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEW, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEW, dmBitsPerPel) },
+    { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEW, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEW, dmLogPixels) },
+    { DM_FORMNAME, FIELD_OFFSET(DEVMODEW, dmFormName) + RTL_FIELD_SIZE(DEVMODEW, dmFormName) },
+    { DM_COLLATE, FIELD_OFFSET(DEVMODEW, dmCollate) + RTL_FIELD_SIZE(DEVMODEW, dmCollate) },
+    { DM_TTOPTION, FIELD_OFFSET(DEVMODEW, dmTTOption) + RTL_FIELD_SIZE(DEVMODEW, dmTTOption) },
+    { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEW, dmYResolution) + RTL_FIELD_SIZE(DEVMODEW, dmYResolution) },
+    { DM_DUPLEX, FIELD_OFFSET(DEVMODEW, dmDuplex) + RTL_FIELD_SIZE(DEVMODEW, dmDuplex) },
+    { DM_COLOR, FIELD_OFFSET(DEVMODEW, dmColor) + RTL_FIELD_SIZE(DEVMODEW, dmColor) },
+    { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEW, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFixedOutput) },
+    { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEW, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayOrientation) },
+    { DM_POSITION, FIELD_OFFSET(DEVMODEW, dmPosition) + RTL_FIELD_SIZE(DEVMODEW, dmPosition) },
+    { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEW, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEW, dmPrintQuality) },
+    { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEW, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEW, dmDefaultSource) },
+    { DM_COPIES, FIELD_OFFSET(DEVMODEW, dmCopies) + RTL_FIELD_SIZE(DEVMODEW, dmCopies) },
+    { DM_SCALE, FIELD_OFFSET(DEVMODEW, dmScale) + RTL_FIELD_SIZE(DEVMODEW, dmScale) },
+    { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEW, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPaperWidth) },
+    { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEW, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEW, dmPaperLength) },
+    { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEW, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEW, dmPaperSize) },
+    { DM_ORIENTATION, FIELD_OFFSET(DEVMODEW, dmOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmOrientation) },
+    { 0, 0 }
+};
+
+/**
+ * Replace the last character by a null terminator if the given ANSI string is not null-terminated.
+ */
+static __inline void
+_FixStringA(PBYTE String, DWORD cbString)
+{
+    const PBYTE pLastCharacter = &String[cbString / sizeof(BYTE) - 1];
+    PBYTE p = String;
+
+    while (*p)
+    {
+        if (p == pLastCharacter)
+        {
+            *p = 0;
+            break;
+        }
+
+        p++;
+    }
+}
+
+/**
+ * Replace the last character by a null terminator if the given Unicode string is not null-terminated.
+ */
+static __inline void
+_FixStringW(PWSTR String, DWORD cbString)
+{
+    const PWSTR pLastCharacter = &String[cbString / sizeof(WCHAR) - 1];
+    PWSTR p = String;
+
+    while (*p)
+    {
+        if (p == pLastCharacter)
+        {
+            *p = 0;
+            break;
+        }
+
+        p++;
+    }
+}
+
 BOOL WINAPI
 IsValidDevmodeA(PDEVMODEA pDevmode, size_t DevmodeSize)
 {
-    // Check if a Devmode was given, its dmSize member is at least as big as the DEVMODEA structure
-    // and DevmodeSize is large enough for the public and private members of the structure.
-    if (!pDevmode ||
-        pDevmode->dmSize < sizeof(DEVMODEA) ||
-        DevmodeSize < pDevmode->dmSize + pDevmode->dmDriverExtra)
+    PMINIMUM_SIZE_TABLE pTable = MinimumSizeA;
+    WORD wRequiredSize;
+
+    // Check if a Devmode was given at all.
+    if (!pDevmode)
+        goto Failure;
+
+    // Verify that DevmodeSize is large enough to hold the public and private members of the structure.
+    if (DevmodeSize < pDevmode->dmSize + pDevmode->dmDriverExtra)
+        goto Failure;
+
+    // If the structure has private members, the public structure must be 32-bit packed.
+    if (pDevmode->dmDriverExtra && pDevmode->dmSize % 4)
+        goto Failure;
+
+    // Now determine the minimum possible dmSize based on the given fields in dmFields.
+    wRequiredSize = FIELD_OFFSET(DEVMODEA, dmFields) + RTL_FIELD_SIZE(DEVMODEA, dmFields);
+
+    while (pTable->dwField)
     {
-        SetLastError(ERROR_INVALID_DATA);
-        return FALSE;
+        if (pDevmode->dmFields & pTable->dwField)
+        {
+            wRequiredSize = pTable->wSize;
+            break;
+        }
+
+        pTable++;
     }
 
+    // Verify that the value in dmSize is big enough for the used fields.
+    if (pDevmode->dmSize < wRequiredSize)
+        goto Failure;
+
+    // Check if dmDeviceName and (if used) dmFormName are null-terminated.
+    // Fix this if they aren't.
+    _FixStringA(pDevmode->dmDeviceName, sizeof(pDevmode->dmDeviceName));
+    if (pDevmode->dmFields & DM_FORMNAME)
+        _FixStringA(pDevmode->dmFormName, sizeof(pDevmode->dmFormName));
+
     // Return success without setting the error code.
     return TRUE;
+
+Failure:
+    SetLastError(ERROR_INVALID_DATA);
+    return FALSE;
 }
 
 BOOL WINAPI
 IsValidDevmodeW(PDEVMODEW pDevmode, size_t DevmodeSize)
 {
-    // Check if a Devmode was given, its dmSize member is at least as big as the DEVMODEW structure
-    // and DevmodeSize is large enough for the public and private members of the structure.
-    if (!pDevmode ||
-        pDevmode->dmSize < sizeof(DEVMODEW) ||
-        DevmodeSize < pDevmode->dmSize + pDevmode->dmDriverExtra)
+    PMINIMUM_SIZE_TABLE pTable = MinimumSizeW;
+    WORD wRequiredSize;
+
+    // Check if a Devmode was given at all.
+    if (!pDevmode)
+        goto Failure;
+
+    // Verify that DevmodeSize is large enough to hold the public and private members of the structure.
+    if (DevmodeSize < pDevmode->dmSize + pDevmode->dmDriverExtra)
+        goto Failure;
+
+    // If the structure has private members, the public structure must be 32-bit packed.
+    if (pDevmode->dmDriverExtra && pDevmode->dmSize % 4)
+        goto Failure;
+
+    // Now determine the minimum possible dmSize based on the given fields in dmFields.
+    wRequiredSize = FIELD_OFFSET(DEVMODEW, dmFields) + RTL_FIELD_SIZE(DEVMODEW, dmFields);
+
+    while (pTable->dwField)
     {
-        SetLastError(ERROR_INVALID_DATA);
-        return FALSE;
+        if (pDevmode->dmFields & pTable->dwField)
+        {
+            wRequiredSize = pTable->wSize;
+            break;
+        }
+
+        pTable++;
     }
 
+    // Verify that the value in dmSize is big enough for the used fields.
+    if (pDevmode->dmSize < wRequiredSize)
+        goto Failure;
+
+    // Check if dmDeviceName and (if used) dmFormName are null-terminated.
+    // Fix this if they aren't.
+    _FixStringW(pDevmode->dmDeviceName, sizeof(pDevmode->dmDeviceName));
+    if (pDevmode->dmFields & DM_FORMNAME)
+        _FixStringW(pDevmode->dmFormName, sizeof(pDevmode->dmFormName));
+
     // Return success without setting the error code.
     return TRUE;
+
+Failure:
+    SetLastError(ERROR_INVALID_DATA);
+    return FALSE;
 }
index a71eac3..1e02d4b 100644 (file)
 #include <winbase.h>
 #include <wingdi.h>
 #include <winspool.h>
+#include <wchar.h>
+
+typedef struct _MINIMUM_SIZE_TABLE
+{
+    DWORD dwField;
+    WORD wSize;
+}
+MINIMUM_SIZE_TABLE, *PMINIMUM_SIZE_TABLE;
+
+static MINIMUM_SIZE_TABLE MinimumSizeA[] = {
+    { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEA, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPanningHeight) },
+    { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEA, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPanningWidth) },
+    { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEA, dmDitherType) + RTL_FIELD_SIZE(DEVMODEA, dmDitherType) },
+    { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEA, dmMediaType) + RTL_FIELD_SIZE(DEVMODEA, dmMediaType) },
+    { DM_ICMINTENT, FIELD_OFFSET(DEVMODEA, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEA, dmICMIntent) },
+    { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEA, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEA, dmICMMethod) },
+    { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEA, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFrequency) },
+    { DM_NUP, FIELD_OFFSET(DEVMODEA, dmNup) + RTL_FIELD_SIZE(DEVMODEA, dmNup) },
+    { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEA, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFlags) },
+    { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEA, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPelsHeight) },
+    { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEA, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPelsWidth) },
+    { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEA, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEA, dmBitsPerPel) },
+    { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEA, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEA, dmLogPixels) },
+    { DM_FORMNAME, FIELD_OFFSET(DEVMODEA, dmFormName) + RTL_FIELD_SIZE(DEVMODEA, dmFormName) },
+    { DM_COLLATE, FIELD_OFFSET(DEVMODEA, dmCollate) + RTL_FIELD_SIZE(DEVMODEA, dmCollate) },
+    { DM_TTOPTION, FIELD_OFFSET(DEVMODEA, dmTTOption) + RTL_FIELD_SIZE(DEVMODEA, dmTTOption) },
+    { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEA, dmYResolution) + RTL_FIELD_SIZE(DEVMODEA, dmYResolution) },
+    { DM_DUPLEX, FIELD_OFFSET(DEVMODEA, dmDuplex) + RTL_FIELD_SIZE(DEVMODEA, dmDuplex) },
+    { DM_COLOR, FIELD_OFFSET(DEVMODEA, dmColor) + RTL_FIELD_SIZE(DEVMODEA, dmColor) },
+    { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEA, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFixedOutput) },
+    { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEA, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayOrientation) },
+    { DM_POSITION, FIELD_OFFSET(DEVMODEA, dmPosition) + RTL_FIELD_SIZE(DEVMODEA, dmPosition) },
+    { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEA, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEA, dmPrintQuality) },
+    { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEA, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEA, dmDefaultSource) },
+    { DM_COPIES, FIELD_OFFSET(DEVMODEA, dmCopies) + RTL_FIELD_SIZE(DEVMODEA, dmCopies) },
+    { DM_SCALE, FIELD_OFFSET(DEVMODEA, dmScale) + RTL_FIELD_SIZE(DEVMODEA, dmScale) },
+    { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEA, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPaperWidth) },
+    { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEA, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEA, dmPaperLength) },
+    { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEA, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEA, dmPaperSize) },
+    { DM_ORIENTATION, FIELD_OFFSET(DEVMODEA, dmOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmOrientation) },
+    { 0, FIELD_OFFSET(DEVMODEA, dmFields) + RTL_FIELD_SIZE(DEVMODEA, dmFields) }
+};
+
+static MINIMUM_SIZE_TABLE MinimumSizeW[] = {
+    { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEW, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPanningHeight) },
+    { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEW, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPanningWidth) },
+    { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEW, dmDitherType) + RTL_FIELD_SIZE(DEVMODEW, dmDitherType) },
+    { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEW, dmMediaType) + RTL_FIELD_SIZE(DEVMODEW, dmMediaType) },
+    { DM_ICMINTENT, FIELD_OFFSET(DEVMODEW, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEW, dmICMIntent) },
+    { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEW, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEW, dmICMMethod) },
+    { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEW, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFrequency) },
+    { DM_NUP, FIELD_OFFSET(DEVMODEW, dmNup) + RTL_FIELD_SIZE(DEVMODEW, dmNup) },
+    { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEW, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFlags) },
+    { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEW, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPelsHeight) },
+    { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEW, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPelsWidth) },
+    { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEW, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEW, dmBitsPerPel) },
+    { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEW, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEW, dmLogPixels) },
+    { DM_FORMNAME, FIELD_OFFSET(DEVMODEW, dmFormName) + RTL_FIELD_SIZE(DEVMODEW, dmFormName) },
+    { DM_COLLATE, FIELD_OFFSET(DEVMODEW, dmCollate) + RTL_FIELD_SIZE(DEVMODEW, dmCollate) },
+    { DM_TTOPTION, FIELD_OFFSET(DEVMODEW, dmTTOption) + RTL_FIELD_SIZE(DEVMODEW, dmTTOption) },
+    { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEW, dmYResolution) + RTL_FIELD_SIZE(DEVMODEW, dmYResolution) },
+    { DM_DUPLEX, FIELD_OFFSET(DEVMODEW, dmDuplex) + RTL_FIELD_SIZE(DEVMODEW, dmDuplex) },
+    { DM_COLOR, FIELD_OFFSET(DEVMODEW, dmColor) + RTL_FIELD_SIZE(DEVMODEW, dmColor) },
+    { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEW, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFixedOutput) },
+    { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEW, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayOrientation) },
+    { DM_POSITION, FIELD_OFFSET(DEVMODEW, dmPosition) + RTL_FIELD_SIZE(DEVMODEW, dmPosition) },
+    { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEW, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEW, dmPrintQuality) },
+    { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEW, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEW, dmDefaultSource) },
+    { DM_COPIES, FIELD_OFFSET(DEVMODEW, dmCopies) + RTL_FIELD_SIZE(DEVMODEW, dmCopies) },
+    { DM_SCALE, FIELD_OFFSET(DEVMODEW, dmScale) + RTL_FIELD_SIZE(DEVMODEW, dmScale) },
+    { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEW, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPaperWidth) },
+    { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEW, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEW, dmPaperLength) },
+    { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEW, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEW, dmPaperSize) },
+    { DM_ORIENTATION, FIELD_OFFSET(DEVMODEW, dmOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmOrientation) },
+    { 0, FIELD_OFFSET(DEVMODEW, dmFields) + RTL_FIELD_SIZE(DEVMODEW, dmFields) }
+};
+
 
-// With very high probability, this is everything you can test for IsValidDevmode.
-// For your own testing, you can fill a DevMode structure with random bytes multiple times,
-// always set dmSize and dmDriverExtra appropriately and feed it to IsValidDevmode.
-// The function will always succeed.
-// I'm not doing that here, because I don't want to introduce randomness.
 
 START_TEST(IsValidDevmodeA)
 {
     DEVMODEA DevMode;
+    PMINIMUM_SIZE_TABLE pTable = MinimumSizeA;
 
     // Give no Devmode at all, this has to fail without crashing.
     SetLastError(0xDEADBEEF);
@@ -72,11 +145,63 @@ START_TEST(IsValidDevmodeA)
     SetLastError(0xDEADBEEF);
     ok(IsValidDevmodeA(&DevMode, sizeof(DEVMODEA) + 1), "IsValidDevmodeA returns FALSE!\n");
     ok(GetLastError() == 0xDEADBEEF, "IsValidDevmodeA returns error %lu!\n", GetLastError());
+
+    // dmSize must be a multiple of 4 if any dmDriverExtra is given.
+    for (DevMode.dmDriverExtra = 1; DevMode.dmDriverExtra <= 4; DevMode.dmDriverExtra++)
+    {
+        for (DevMode.dmSize = sizeof(DEVMODEA); DevMode.dmSize < sizeof(DEVMODEA) + 4; DevMode.dmSize++)
+        {
+            BOOL bExpected = (DevMode.dmSize % 4 == 0);
+            DWORD dwExpectedError = (bExpected ? 0xDEADBEEF : ERROR_INVALID_DATA);
+
+            SetLastError(0xDEADBEEF);
+            ok(IsValidDevmodeA(&DevMode, DevMode.dmSize + DevMode.dmDriverExtra) == bExpected, "IsValidDevmodeA returns %d!\n", !bExpected);
+            ok(GetLastError() == dwExpectedError, "IsValidDevmodeA returns error %lu!\n", GetLastError());
+        }
+    }
+
+    // IsValidDevmodeA also fixes missing null-terminations if the corresponding fields are used.
+    ZeroMemory(&DevMode, sizeof(DEVMODEA));
+    DevMode.dmSize = sizeof(DEVMODEA);
+    memset(DevMode.dmDeviceName, 'A', 32);
+    memset(DevMode.dmFormName, 'A', 32);
+    ok(IsValidDevmodeA(&DevMode, DevMode.dmSize), "IsValidDevmodeA returns FALSE!\n");
+    ok(DevMode.dmDeviceName[31] == '\0', "Expected dmDeviceName to be '\\0', but is '%c'\n", DevMode.dmDeviceName[31]);
+    ok(DevMode.dmFormName[31] == 'A', "Expected dmFormName to be 'A', but is '%c'\n", DevMode.dmDeviceName[31]);
+
+    ZeroMemory(&DevMode, sizeof(DEVMODEA));
+    DevMode.dmSize = sizeof(DEVMODEA);
+    DevMode.dmFields = DM_FORMNAME;
+    memset(DevMode.dmDeviceName, 'A', 32);
+    memset(DevMode.dmFormName, 'A', 32);
+    ok(IsValidDevmodeA(&DevMode, DevMode.dmSize), "IsValidDevmodeA returns FALSE!\n");
+    ok(DevMode.dmDeviceName[31] == '\0', "Expected dmDeviceName to be '\\0', but is '%c'\n", DevMode.dmDeviceName[31]);
+    ok(DevMode.dmFormName[31] == '\0', "Expected dmFormName to be '\\0', but is '%c'\n", DevMode.dmDeviceName[31]);
+
+    // Depending on which fields are given in dmFields, the minimum value for dmSize is different.
+    ZeroMemory(&DevMode, sizeof(DEVMODEA));
+
+    do
+    {
+        DevMode.dmFields = pTable->dwField;
+        DevMode.dmSize = pTable->wSize;
+
+        // This size should succeed!
+        ok(IsValidDevmodeA(&DevMode, DevMode.dmSize), "IsValidDevmodeA returns FALSE!\n");
+
+        // One byte less should not succeed!
+        DevMode.dmSize--;
+        ok(!IsValidDevmodeA(&DevMode, DevMode.dmSize), "IsValidDevmodeA returns TRUE!\n");
+
+        pTable++;
+    }
+    while (pTable->dwField);
 }
 
 START_TEST(IsValidDevmodeW)
 {
     DEVMODEW DevMode;
+    PMINIMUM_SIZE_TABLE pTable = MinimumSizeW;
 
     // Give no Devmode at all, this has to fail without crashing.
     SetLastError(0xDEADBEEF);
@@ -127,4 +252,55 @@ START_TEST(IsValidDevmodeW)
     SetLastError(0xDEADBEEF);
     ok(IsValidDevmodeW(&DevMode, sizeof(DEVMODEW) + 1), "IsValidDevmodeW returns FALSE!\n");
     ok(GetLastError() == 0xDEADBEEF, "IsValidDevmodeW returns error %lu!\n", GetLastError());
+
+    // dmSize must be a multiple of 4 if any dmDriverExtra is given.
+    for (DevMode.dmDriverExtra = 1; DevMode.dmDriverExtra <= 4; DevMode.dmDriverExtra++)
+    {
+        for (DevMode.dmSize = sizeof(DEVMODEW); DevMode.dmSize < sizeof(DEVMODEW) + 4; DevMode.dmSize++)
+        {
+            BOOL bExpected = (DevMode.dmSize % 4 == 0);
+            DWORD dwExpectedError = (bExpected ? 0xDEADBEEF : ERROR_INVALID_DATA);
+
+            SetLastError(0xDEADBEEF);
+            ok(IsValidDevmodeW(&DevMode, DevMode.dmSize + DevMode.dmDriverExtra) == bExpected, "IsValidDevmodeW returns %d!\n", !bExpected);
+            ok(GetLastError() == dwExpectedError, "IsValidDevmodeW returns error %lu!\n", GetLastError());
+        }
+    }
+
+    // IsValidDevmodeW also fixes missing null-terminations if the corresponding fields are used.
+    ZeroMemory(&DevMode, sizeof(DEVMODEW));
+    DevMode.dmSize = sizeof(DEVMODEW);
+    wmemset(DevMode.dmDeviceName, L'A', 32);
+    wmemset(DevMode.dmFormName, L'A', 32);
+    ok(IsValidDevmodeW(&DevMode, DevMode.dmSize), "IsValidDevmodeW returns FALSE!\n");
+    ok(DevMode.dmDeviceName[31] == L'\0', "Expected dmDeviceName to be '\\0', but is '%C'\n", DevMode.dmDeviceName[31]);
+    ok(DevMode.dmFormName[31] == L'A', "Expected dmFormName to be 'A', but is '%C'\n", DevMode.dmDeviceName[31]);
+
+    ZeroMemory(&DevMode, sizeof(DEVMODEW));
+    DevMode.dmSize = sizeof(DEVMODEW);
+    DevMode.dmFields = DM_FORMNAME;
+    wmemset(DevMode.dmDeviceName, L'A', 32);
+    wmemset(DevMode.dmFormName, L'A', 32);
+    ok(IsValidDevmodeW(&DevMode, DevMode.dmSize), "IsValidDevmodeW returns FALSE!\n");
+    ok(DevMode.dmDeviceName[31] == L'\0', "Expected dmDeviceName to be '\\0', but is '%C'\n", DevMode.dmDeviceName[31]);
+    ok(DevMode.dmFormName[31] == L'\0', "Expected dmFormName to be '\\0', but is '%C'\n", DevMode.dmDeviceName[31]);
+
+    // Depending on which fields are given in dmFields, the minimum value for dmSize is different.
+    ZeroMemory(&DevMode, sizeof(DEVMODEW));
+
+    do
+    {
+        DevMode.dmFields = pTable->dwField;
+        DevMode.dmSize = pTable->wSize;
+
+        // This size should succeed!
+        ok(IsValidDevmodeW(&DevMode, DevMode.dmSize), "IsValidDevmodeW returns FALSE!\n");
+
+        // One byte less should not succeed!
+        DevMode.dmSize--;
+        ok(!IsValidDevmodeW(&DevMode, DevMode.dmSize), "IsValidDevmodeW returns TRUE!\n");
+
+        pTable++;
+    }
+    while (pTable->dwField);
 }