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