How to use DEVMODE

I received a question about how to use DEVMODE. Below is some sample code that retrieves the screen’s current width/height screen resolution and rotates it for a few seconds if supported by your machine (mostly on Tablet PCs)

The user’s question continues:

The printer properties you can control start with DM_ORIENTATION and go through DM_DITHERTYPE. Each constant represents the bit in DM_FIELDS for that property, but there's a gap in the sequence between DM_SCALE and DM_COPIES. Why aren't the bits for 32, 64 and 128 used?

There is no rule saying that all values in a bitfield must be used. An API can define bit 1 to mean A, bit 4 to mean B, but bits 2 and 3 do not have to be defined. Also, APIs evolve as the OS version changes to support newer hardware/software. However, the #define’s below show that 32, 64 and 128 are used for DM_POSITION, DM_NUP, DM_DISPLAYORIENTATION

DEVMODE is defined in C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Include\wingdi.h

It’s a little bit complex with some #ifdefs and macros. WINVER is 0x0501 for Windows XP

typedef struct _devicemodeA {

    BYTE dmDeviceName[CCHDEVICENAME];

    WORD dmSpecVersion;

    WORD dmDriverVersion;

    WORD dmSize;

    WORD dmDriverExtra;

    DWORD dmFields;

    union {

      /* printer only fields */

      struct {

        short dmOrientation;

        short dmPaperSize;

        short dmPaperLength;

        short dmPaperWidth;

        short dmScale;

        short dmCopies;

        short dmDefaultSource;

        short dmPrintQuality;

      };

      /* display only fields */

      struct {

        POINTL dmPosition;

        DWORD dmDisplayOrientation;

        DWORD dmDisplayFixedOutput;

      };

    };

    short dmColor;

    short dmDuplex;

    short dmYResolution;

    short dmTTOption;

    short dmCollate;

    BYTE dmFormName[CCHFORMNAME];

    WORD dmLogPixels;

    DWORD dmBitsPerPel;

    DWORD dmPelsWidth;

    DWORD dmPelsHeight;

    union {

        DWORD dmDisplayFlags;

        DWORD dmNup;

    };

    DWORD dmDisplayFrequency;

#if(WINVER >= 0x0400)

    DWORD dmICMMethod;

    DWORD dmICMIntent;

    DWORD dmMediaType;

    DWORD dmDitherType;

    DWORD dmReserved1;

    DWORD dmReserved2;

#if (WINVER >= 0x0500) || (_WIN32_WINNT >= 0x0400)

    DWORD dmPanningWidth;

    DWORD dmPanningHeight;

#endif

#endif /* WINVER >= 0x0400 */

} DEVMODEA, *PDEVMODEA, *NPDEVMODEA, *LPDEVMODEA;

From that same file:

/* field selection bits */

#define DM_ORIENTATION 0x00000001L

#define DM_PAPERSIZE 0x00000002L

#define DM_PAPERLENGTH 0x00000004L

#define DM_PAPERWIDTH 0x00000008L

#define DM_SCALE 0x00000010L

#if(WINVER >= 0x0500)

#define DM_POSITION 0x00000020L

#define DM_NUP 0x00000040L

#endif /* WINVER >= 0x0500 */

#if(WINVER >= 0x0501)

#define DM_DISPLAYORIENTATION 0x00000080L

#endif /* WINVER >= 0x0501 */

#define DM_COPIES 0x00000100L

#define DM_DEFAULTSOURCE 0x00000200L

#define DM_PRINTQUALITY 0x00000400L

#define DM_COLOR 0x00000800L

#define DM_DUPLEX 0x00001000L

#define DM_YRESOLUTION 0x00002000L

#define DM_TTOPTION 0x00004000L

#define DM_COLLATE 0x00008000L

#define DM_FORMNAME 0x00010000L

#define DM_LOGPIXELS 0x00020000L

#define DM_BITSPERPEL 0x00040000L

#define DM_PELSWIDTH 0x00080000L

#define DM_PELSHEIGHT 0x00100000L

#define DM_DISPLAYFLAGS 0x00200000L

#define DM_DISPLAYFREQUENCY 0x00400000L

#if(WINVER >= 0x0400)

#define DM_ICMMETHOD 0x00800000L

#define DM_ICMINTENT 0x01000000L

#define DM_MEDIATYPE 0x02000000L

#define DM_DITHERTYPE 0x04000000L

#define DM_PANNINGWIDTH 0x08000000L

#define DM_PANNINGHEIGHT 0x10000000L

#endif /* WINVER >= 0x0400 */

#if(WINVER >= 0x0501)

#define DM_DISPLAYFIXEDOUTPUT 0x20000000L

#endif /* WINVER >= 0x0501 */

Here’s the VFP code to change the screen orientation. Check out the uses of BINTOC and CTOBIN (see also Tools->Task Pane->Solution Samples->New in VFP9->BINTOC Binary Conversion and Floating Point calculations: comparing with zero). The string offsets are offsets into the DEVMODE structure. They are easy to figure out in a debugger, especially with the macros, unions and nested structures. Just put &(*(DEVMODE *)0).dmDisplayOrientation in the watch window and 0x00000034 (52 base 10) is displayed. (That says interpret the address at 0 as a DEVMODE structure and give the address of the dmDisplayOrientation member.) The SUBSTR function is 1 based, so the SUBSTR offset of dmDisplayOrientation is 53.

#define ENUM_CURRENT_SETTINGS 0xffffffff

#define ENUM_REGISTRY_SETTINGS 0xfffffffe

#define DMDO_DEFAULT 0

#define DMDO_90 1

#define DMDO_180 2

#define DMDO_270 3

#define DM_DISPLAYORIENTATION 0x00000080

CLEAR

*SetDisplay(DMDO_180)

SetDisplay(DMDO_90)

INKEY(3)

SetDisplay(DMDO_DEFAULT)

PROCEDURE SetDisplay(nOrientation as Integer)

      *sizeof(DEVMODE)=156

      cDevmode=REPLICATE(CHR(0),36)+BINTOC(156,"2sr")+REPLICATE(CHR(0),118)

      DECLARE integer EnumDisplaySettings IN WIN32API integer,integer,string @

      DECLARE integer ChangeDisplaySettings IN WIN32API string @, integer

      IF EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,@cDevmode)=0

            ?"Error"

            RETURN

      ENDIF

      nWidth=CTOBIN(SUBSTR(cDevmode,109,4),"4sr")

      nHeight=CTOBIN(SUBSTR(cDevmode,113,4),"4sr")

      ?"Screen dimensions are ",nWidth,"X",nHeight

      dmFields=CTOBIN(SUBSTR(cDevmode,41,4),"4sr")

      ?TRANSFORM(dmFields,"@0x")

      IF BITAND(dmFields,DM_DISPLAYORIENTATION)>0

            fSwap=.f.

            nDisplay = CTOBIN(SUBSTR(cDevmode,53,4),"4sr")

            ?"Current DisplayOrientation=",nDisplay

            IF nOrientation != nDisplay && if different

                  fLandscape=nWidth>nHeight

                  DO CASE

                  CASE INLIST(nOrientation,DMDO_DEFAULT,DMDO_180)

                        fSwap = !fLandscape && if it wasn't lasndscape, we need to swap x,y pixels

                  CASE INLIST(nOrientation,DMDO_90,DMDO_270)

                        fSwap = fLandscape

                  ENDCASE

                  IF fSwap

                        nTemp=nWidth

                        nWidth=nHeight

                        nHeight=nTemp

                  ENDIF

            cDevmode=LEFT(cDevmode,108)+BINTOC(nWidth,"4sr")+BINTOC(nHeight,"4sr")+SUBSTR(cDevmode,117)

                  cDevmode=LEFT(cDevmode,52)+BINTOC(nOrientation,"4sr")+SUBSTR(cDevmode,57)

                  n= ChangeDisplaySettings(@cDevmode,0)

                  IF n=0

                        ?"Success"

                  ELSE

                        ?"Failed",n

                  ENDIF

            ENDIF

      ELSE

            ?"Can't get Display Orientation"

      ENDIF

RETURN