Change display resolution programmatically (not by driving the UI)

Started by snowsnowsnow, October 04, 2013, 03:32:19 AM

Previous topic - Next topic

snowsnowsnow

I think this question has been asked (more than) a few times, but I'm still curious about it.

Is there a way to change the display resolution programmatically (I.e., via a WB script) other than by driving the "desk.cpl" UI?  E.g., by setting something in the registry or maybe via WMI?

Inquiring minds...

Deana

Deana F.
Technical Support
Wilson WindowWare Inc.

snowsnowsnow

Thanks.  I'll let you know if I am able to get it to work.

(Note: Going by the comments and by the length of the file, it looks like the program does a whole lot more than just setting the resolution.  Somebody (presumably me) needs to go in and prune it back...)

mcjathan

Hi SnowSnowSnow,

If you've pruned this back, would you be willing to share?

Regards,

Jeff

Deana

Quote from: mcjathan on November 18, 2013, 09:37:52 AM
Hi SnowSnowSnow,

If you've pruned this back, would you be willing to share?

Regards,

Jeff

Jeff,
Based on the provided code sample in the tech database....Here is a totally UNDEBUGGED attempt at creating two User Defined Functions to handle: listing the resolution options then setting the resolution:



Code (winbatch) Select


#DefineFunction udfEnumDisplaySettings()
;Enumerate current and supported display settings
UserDll=StrCat(DirWindows(1),"USER32.DLL")
TDEVICEMODEA=BinaryAlloc(156)                      ;DEVMODE structure 
;Size of TDEVICEMODEA record: 156 Bytes
;Field offsets and sizes are:
offdmDeviceName       = 0   ;32
offdmSpecVersion      = 32  ;2
offdmDriverVersion    = 34  ;2
offdmSize             = 36  ;2
offdmDriverExtra      = 38  ;2
offdmFields           = 40  ;4
offdmOrientation      = 44  ;2
offdmPaperSize        = 46  ;2
offdmPaperLength      = 48  ;2
offdmPaperWidth       = 50  ;2
offdmScale            = 52  ;2
offdmCopies           = 54  ;2
offdmDefaultSource    = 56  ;2
offdmPrintQuality     = 58  ;2
offdmColor            = 60  ;2
offdmDuplex           = 62  ;2
offdmYResolution      = 64  ;2
offdmTTOption         = 66  ;2
offdmCollate          = 68  ;2
offdmFormName         = 70  ;32
offdmLogPixels        = 102 ;2
offdmBitsPerPel       = 104 ;4
offdmPelsWidth        = 108 ;4
offdmPelsHeight       = 112 ;4
offdmDisplayFlags     = 116 ;4
offdmDisplayFrequency = 120 ;4
offdmICMMethod        = 124 ;4
offdmICMIntent        = 128 ;4
offdmMediaType        = 132 ;4
offdmDitherType       = 136 ;4
offdmICCManufacturer  = 140 ;4
offdmICCModel         = 144 ;4
offdmPanningWidth     = 148 ;4
offdmPanningHeight    = 152 ;4
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;Bool EnumDisplaySettingsA (String:lpszDeviceName,Integer:iModeNum,Pointer:lpDevMode)
;   lpszDeviceName is a pointer to the target display adapter.  Null specifies default adapter.
;   iModeNum specifies whether to query the display adapter or the registry settings for the adapter.
;   lpDevMode is a pointer to a DevMode structure to store info about the specified graphics mode.
ModeIndex=-1                                               ;Value of -1 returns current settings
Selection=""
While 1
   ReturnValue=DllCall(UserDll,long:"EnumDisplaySettingsA",lpnull,long:ModeIndex,lpbinary:TDEVICEMODEA )
   If ReturnValue==0 Then Break

;   Get info on relevant display settings   
   dmPelsWidth        = BinaryPeek4(TDEVICEMODEA,offdmPelsWidth)        ;Horizontal resolution
   dmPelsHeight       = BinaryPeek4(TDEVICEMODEA,offdmPelsHeight)       ;Vertical resolution
   dmBitsPerPel       = BinaryPeek4(TDEVICEMODEA,offdmBitsPerPel)       ;Color depth
   dmDisplayFrequency = BinaryPeek4(TDEVICEMODEA,offdmDisplayFrequency) ;Refresh Rate
   dmDisplayFlags     = BinaryPeek4(TDEVICEMODEA,offdmDisplayFlags)     ;Only used if monitor is greyscale or interlaced
   
   If ModeIndex<0                                          ;Index of -1 returns current settings
      DispProb=""
      CurRes=StrCat (dmPelsWidth,"x",dmPelsHeight)         ;Extract current resolution
      CurBPP=StrCat (dmBitsPerPel,"-bit")                  ;Extract current color depth
      CurRef=StrCat (dmDisplayFrequency,"Hz")              ;Extract current refresh rate             
      If dmPelsWidth<800 Then DispProb=StrCat (DispProb,"Resolution",@TAB)
      If dmBitsPerPel<16 Then DispProb=StrCat (DispProb,"Color depth",@TAB)
      If dmDisplayFrequency<75 Then DispProb=StrCat (DispProb,"Refresh rate")
      If ItemCount (DispProb,@TAB)>1 Then DispProb=StrReplace (DispProb,@TAB,", ")
      If DispProb==""                                      ;Everything checks out
         DispProb="None"                                   ;Make it readable
      EndIf
   EndIf
   Line=StrCat(dmPelsWidth,"x",dmPelsHeight,"x",dmBitsPerPel,",",dmDisplayFrequency,"Hz");Format for list
;Don't add modes with less that 640x480 resolution or 60Hz refresh to Mode list
   If (dmPelsWidth>639 && dmDisplayFrequency>59 && ModeIndex>=0) Then Selection=StrCat(Selection,Line,@TAB)
   ModeIndex=ModeIndex + 1                                 ;Index of 0 or greater returns supported modes
EndWhile
BinaryFree(TDEVICEMODEA)
Return Selection

#EndFunction


#DefineFunction udfChangeDisplaySettingsA(NewSetting); in the format ;1920x1080x32,60hz
;Expected format: {PelsWidth}x{PelsHeight}x{BitsPerPel},{DisplayFrequency}hz

UserDll=StrCat(DirWindows(1),"USER32.DLL")
;Parse selected settings
dmPelsWidth        = ItemExtract(1,NewSetting,"x")
dmPelsHeight       = ItemExtract(2,NewSetting,"x")
dmBitsPerPel       = ItemExtract(1,ItemExtract(3,NewSetting,"x"),",")
dmDisplayFrequency = ItemExtract(2,NewSetting,",")
dmDisplayFrequency = StrSub( dmDisplayFrequency, 1, StrLen(dmDisplayFrequency)-2 )
dmDisplayFlags     = 0
If dmPelsWidth=="" || dmPelsHeight=="" || dmBitsPerPel=="" || dmDisplayFrequency=="" || dmDisplayFlags==""
    Pause('udfChangeDisplaySettingsA','Invalid parameter.')
    Return 0
Endif

TDEVICEMODEA=BinaryAlloc(156)                      ;DEVMODE structure
;Size of TDEVICEMODEA record: 156 Bytes
;Field offsets and sizes are:
offdmDeviceName       = 0   ;32
offdmSpecVersion      = 32  ;2
offdmDriverVersion    = 34  ;2
offdmSize             = 36  ;2
offdmDriverExtra      = 38  ;2
offdmFields           = 40  ;4
offdmOrientation      = 44  ;2
offdmPaperSize        = 46  ;2
offdmPaperLength      = 48  ;2
offdmPaperWidth       = 50  ;2
offdmScale            = 52  ;2
offdmCopies           = 54  ;2
offdmDefaultSource    = 56  ;2
offdmPrintQuality     = 58  ;2
offdmColor            = 60  ;2
offdmDuplex           = 62  ;2
offdmYResolution      = 64  ;2
offdmTTOption         = 66  ;2
offdmCollate          = 68  ;2
offdmFormName         = 70  ;32
offdmLogPixels        = 102 ;2
offdmBitsPerPel       = 104 ;4
offdmPelsWidth        = 108 ;4
offdmPelsHeight       = 112 ;4
offdmDisplayFlags     = 116 ;4
offdmDisplayFrequency = 120 ;4
offdmICMMethod        = 124 ;4
offdmICMIntent        = 128 ;4
offdmMediaType        = 132 ;4
offdmDitherType       = 136 ;4
offdmICCManufacturer  = 140 ;4
offdmICCModel         = 144 ;4
offdmPanningWidth     = 148 ;4
offdmPanningHeight    = 152 ;4 

;Poke Settings into DEVMODE structure
BinaryPoke4 (TDEVICEMODEA,offdmBitsPerPel      ,dmBitsPerPel)     
BinaryPoke4 (TDEVICEMODEA,offdmPelsWidth       ,dmPelsWidth)       
BinaryPoke4 (TDEVICEMODEA,offdmPelsHeight      ,dmPelsHeight)     
BinaryPoke4 (TDEVICEMODEA,offdmDisplayFrequency,dmDisplayFrequency)   
BinaryPoke4 (TDEVICEMODEA,offdmDisplayFlags    ,dmDisplayFlags)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;Begin NT Fix;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Find selected buffer again (for NT)
ModeIndex = 0
Selection=""
While 1
   ReturnValue=DllCall(UserDll,long:"EnumDisplaySettingsA",lpnull,long:ModeIndex,lpbinary:TDEVICEMODEA)
   If ReturnValue==0
      Message("Res Change","Strange error occurred")
      Exit
   EndIf
   dmPelsWidth2        = BinaryPeek4(TDEVICEMODEA,offdmPelsWidth)         ;Horizontal resolution
   dmPelsHeight2       = BinaryPeek4(TDEVICEMODEA,offdmPelsHeight)        ;Vertical resolution
   dmBitsPerPel2       = BinaryPeek4(TDEVICEMODEA,offdmBitsPerPel)        ;Color depth 
   dmDisplayFrequency2 = BinaryPeek4(TDEVICEMODEA,offdmDisplayFrequency)  ;Refresh Rate
   dmDisplayFlags2     = BinaryPeek4(TDEVICEMODEA,offdmDisplayFlags)      ;Only used if monitor is greyscale or interlaced
   If (dmBitsPerPel==dmBitsPerPel2 &&  dmPelsWidth==dmPelsWidth2  && dmPelsHeight==dmPelsHeight2 && dmDisplayFlags==dmDisplayFlags2 && dmDisplayFrequency==dmDisplayFrequency2)
      ; we found our setting...
      Break
   EndIf
   ModeIndex=ModeIndex+1
EndWhile
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;End NT Fix;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Set the field mask to determine fields to use for setting the display mode
DM_BITSPERPEL       = 262144  ; 0x40000
DM_PELSWIDTH        = 524288  ; 0x80000
DM_PELSHEIGHT       = 1048576 ; 0x100000
DM_DISPLAYFLAGS     = 2097152 ; 0x200000
DM_DISPLAYFREQUENCY = 4194304 ; 0x400000
dmFields = (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY) ; 0x7C0000 = 8126464 decimal
BinaryPoke4 (TDEVICEMODEA,offdmFields,dmFields)

CDS_DYNAMICONLY    = 0                                     ; Change current session only
CDS_UPDATEREGISTRY = 1                                     ; Change and update registry
CDS_TEST           = 2                                     ; Test only
ChangeMode         = CDS_UPDATEREGISTRY
ReturnValue        = DllCall(UserDll,long:"ChangeDisplaySettingsA",lpbinary:TDEVICEMODEA,long:ChangeMode )

DISP_CHANGE_SUCCESSFUL=  0                                 ;The settings change was successful.
DISP_CHANGE_RESTART   =  1                                 ;The computer must be restarted in order for the graphics mode to work.
DISP_CHANGE_FAILED    = -1                                 ;The display driver failed the specified graphics mode.
DISP_CHANGE_BADMODE   = -2                                 ;The graphics mode is not supported.
DISP_CHANGE_NOTUPDATED= -3                                 ;Unable to write settings to the registry.
DISP_CHANGE_BADFLAGS  = -4                                 ;An invalid set of flags was passed in.
DISP_CHANGE_BADPARAM  = -5                                 ;Bad parameters

Switch ReturnValue
   Case 1
      Line="The computer must be restarted in order for the graphics mode to work."
      Break
   Case 0
      Line="The settings change was successful."
      Break
   Case -1
      Line="The display driver failed the specified graphics mode."
      Break
   Case -2
      Line="The graphics mode is not supported."
      Break
   Case -3
      Line="Unable to write settings to the registry."
      Break
   Case -4
      Line="An invalid set of flags was passed in."
      Break
   Case -5
      Line="Bad parameters"
      Break
   Case ReturnValue
      Line="Unknown return code %ReturnValue%"
      Break
EndSwitch

If ReturnValue==1
   If ChangeMode!=CDS_UPDATEREGISTRY
      Line=StrCat (Line,@CRLF,"Because the Update Registry option was not in effect, no changes will be made")
      Message("Resolution Change",Line)
      Exit
   Else
      Pause ("Reboot machine",Line)
      IntControl (67,0,0,0,0)
   EndIf
Else
   Display (3,"Resolution Change",Line)
EndIf
BinaryFree(TDEVICEMODEA)
Return 1

#EndFunction


;Set Screen resolution
;
ret = udfChangeDisplaySettingsA("1920x1080x32,60hz")
Pause("Set Resolution",ret)
Exit

;*OR*

;List options then set resolution
list = udfEnumDisplaySettings()  ;1920x1080x32,60hz
NewSetting = AskItemList("udfEnumDisplaySettings", list, @tab, @unsorted, @single )
ret = udfChangeDisplaySettingsA(NewSetting)
Pause("done",ret)
Exit
Deana F.
Technical Support
Wilson WindowWare Inc.

mcjathan

Quote from: Deana on November 18, 2013, 10:26:21 AM
Jeff,
Based on the provided code sample in the tech database....Here is a totally UNDEBUGGED attempt at creating two User Defined Functions to handle: listing the resolution options then setting the resolution:

Thank you, Deana.    So far, it seems to be working great.