Control multiple display settings using VBA (1 Viewer)

isladogs

MVP / VIP
Local time
Today, 22:36
Joined
Jan 14, 2017
Messages
18,217
Hi

Does anyone know how to control multiple display settings using VBA?
I'll explain the reason in a later post if I get anywhere with this...

As I think it's a fairly obscure topic I'm posting this on more than one forum.
Also at: http://www.utteraccess.com/forum/index.php?showtopic=2044813

Specifically I want to do the following using VBA when running a particular db:
1. check if there is more than one monitor
2. if so, identify the current display setting e.g. extend these displays
3. change the display setting to one of the following:
a) duplicate displays
b) show display only on monitor 1

Then when I close the database, I would restore the original display setting

I would imagine this can be done using an API but haven't been able to find anything about this online

The nearest I've got is the following which changes the resolution of monitor 2.

Code from http://www.vbforums.com/showthread.php?644046-RESOLVED-Change-display-settings-for-2nd-monitor

Code:
Option Compare Database
Option Explicit

'Code from http://www.vbforums.com/showthread.php?644046-RESOLVED-Change-display-settings-for-2nd-monitor

Private Declare Function EnumDisplaySettings Lib "user32" Alias "EnumDisplaySettingsA" (ByVal lpszDeviceName As Long, ByVal iModeNum As Long, lpDevMode As Any) As Boolean

Private Declare Function ChangeDisplaySettingsEx Lib "user32" Alias _
                          "ChangeDisplaySettingsExA" (lpszDeviceName As Any, lpDevMode As Any, _
                           ByVal hWnd As Long, ByVal dwFlags As Long, lParam As Any) As Long

Const CCDEVICENAME = 32
Const CCFORMNAME = 32
Const DM_PELSWIDTH = &H80000
Const DM_PELSHEIGHT = &H100000
Const CDS_TEST = &H4

Private Type DEVMODE
    dmDeviceName As String * CCDEVICENAME
    dmSpecVersion As Integer
    dmDriverVersion As Integer
    dmSize As Integer
    dmDriverExtra As Integer
    dmFields As Long
    dmOrientation As Integer
    dmPaperSize As Integer
    dmPaperLength As Integer
    dmPaperWidth As Integer
    dmScale As Integer
    dmCopies As Integer
    dmDefaultSource As Integer
    dmPrintQuality As Integer
    dmColor As Integer
    dmDuplex As Integer
    dmYResolution As Integer
    dmTTOption As Integer
    dmCollate As Integer
    dmFormName As String * CCFORMNAME
    dmUnusedPadding As Integer
    dmBitsPerPel As Integer
    dmPelsWidth As Long
    dmPelsHeight As Long
    dmDisplayFlags As Long
    dmDisplayFrequency As Long
End Type
Dim DevM As DEVMODE

Private Sub Command1_Click()
Dim a As Boolean: Dim i&
i = 0

Do
a = EnumDisplaySettings(0&, i, DevM)
i = i + 1
Loop Until (a = False)

Dim b&

DevM.dmFields = DM_PELSWIDTH Or DM_PELSHEIGHT
DevM.dmPelsWidth = 1024 '(Horizontal)
DevM.dmPelsHeight = 768 '(Vertical)

Call ChangeDisplaySettingsEx(ByVal "\\.\DISPLAY2", DevM, ByVal 0&, CDS_TEST, ByVal 0&)
End

End Sub

Thanks in advance for any help with this
 
Last edited:

ashleedawg

"Here for a good time"
Local time
Today, 14:36
Joined
Jun 22, 2017
Messages
154
I have a faint memory of playing with a multi-monitor API, many moons ago.
I believe there is a specific API but I can't find it at the moment (and currently I don't have multiple monitors to test, anyways).

The GetSystemMetrics user32 function has a few monitor-related functions which may or may not help you. Indeed, one function does correctly tell me that I have 1 monitor... However, a different function in the API tells me that I have 16 mouse buttons, so I might be doing something wrong!

The code below will return the values of all GetSystemMetrics functions that mention 'Monitor' in the description. (The full list was too large to post but is attached as a TXT - although are likely unrelated to your search.)


Code:
[SIZE="3"]Option Compare Database
Option Explicit

'Sources:
'GetSystemMetrics function :            https://msdn.microsoft.com/en-us/library/windows/desktop/ms724385(v=vs.85).aspx
'Using the GetSystemMetrics Function :  https://msdn.microsoft.com/en-us/library/aa227571(v=vs.60).aspx

'Monitor references:
'Multiple Display Monitors Reference :  https://msdn.microsoft.com/en-us/library/windows/desktop/dd145073(v=vs.85).aspx
'GetMonitorInfo function:               https://msdn.microsoft.com/en-us/library/windows/desktop/dd144901(v=vs.85).aspx

'Monitor Related:
Const SM_CMONITORS = 80  'The number of display monitors on a desktop. For more information, see the Remarks section in this topic.
Const SM_CXFULLSCREEN = 16  'The width of the client area for a full-screen window on the primary display monitor, in pixels. To get the coordinates of the portion of the screen that is not obscured by the system taskbar or by application desktop toolbars, call the SystemParametersInfo function with the SPI_GETWORKAREA value.
Const SM_CXMAXIMIZED = 61  'The default width, in pixels, of a maximized top-level window on the primary display monitor.
Const SM_CXSCREEN = 0  'The width of the screen of the primary display monitor, in pixels. This is the same value obtained by calling GetDeviceCaps as follows: GetDeviceCaps( hdcPrimaryMonitor, HORZRES).
Const SM_CXVIRTUALSCREEN = 78  'The width of the virtual screen, in pixels. The virtual screen is the bounding rectangle of all display monitors. The SM_XVIRTUALSCREEN metric is the coordinates for the left side of the virtual screen.
Const SM_CYFULLSCREEN = 17  'The height of the client area for a full-screen window on the primary display monitor, in pixels. To get the coordinates of the portion of the screen not obscured by the system taskbar or by application desktop toolbars, call the SystemParametersInfo function with the SPI_GETWORKAREA value.
Const SM_CYMAXIMIZED = 62  'The default height, in pixels, of a maximized top-level window on the primary display monitor.
Const SM_CYSCREEN = 1  'The height of the screen of the primary display monitor, in pixels. This is the same value obtained by calling GetDeviceCaps as follows: GetDeviceCaps( hdcPrimaryMonitor, VERTRES).
Const SM_CYVIRTUALSCREEN = 79  'The height of the virtual screen, in pixels. The virtual screen is the bounding rectangle of all display monitors. The SM_YVIRTUALSCREEN metric is the coordinates for the top of the virtual screen.
Const SM_SAMEDISPLAYFORMAT = 81  'Nonzero if all the display monitors have the same color format, otherwise, 0. Two displays can have the same bit depth, but different color formats. For example, the red, green, and blue pixels can be encoded with different numbers of bits, or those bits can be located in different places in a pixel color value.
Const SM_XVIRTUALSCREEN = 76  'The coordinates for the left side of the virtual screen. The virtual screen is the bounding rectangle of all display monitors. The SM_CXVIRTUALSCREEN metric is the width of the virtual screen.
Const SM_YVIRTUALSCREEN = 77  'The coordinates for the top of the virtual screen. The virtual screen is the bounding rectangle of all display monitors. The SM_CYVIRTUALSCREEN metric is the height of the virtual screen.


Private Declare Function GetSystemMetrics Lib "user32" _
    (ByVal nIndex As Long) As Long


Sub testGetSystemMetrics()

Debug.Print "Monitor Related:"
Debug.Print "SM_CMONITORS = " & GetSystemMetrics(SM_CMONITORS), "The number of display monitors on a desktop. For more information, see the Remarks section in this topic."
Debug.Print "SM_CXFULLSCREEN = " & GetSystemMetrics(SM_CXFULLSCREEN), "The width of the client area for a full-screen window on the primary display monitor, in pixels. To get the coordinates of the portion of the screen that is not obscured by the system taskbar or by application desktop toolbars, call the SystemParametersInfo function with the SPI_GETWORKAREA value."
Debug.Print "SM_CXMAXIMIZED = " & GetSystemMetrics(SM_CXMAXIMIZED), "The default width, in pixels, of a maximized top-level window on the primary display monitor."
Debug.Print "SM_CXSCREEN = " & GetSystemMetrics(SM_CXSCREEN), "The width of the screen of the primary display monitor, in pixels. This is the same value obtained by calling GetDeviceCaps as follows: GetDeviceCaps( hdcPrimaryMonitor, HORZRES)."
Debug.Print "SM_CXVIRTUALSCREEN = " & GetSystemMetrics(SM_CXVIRTUALSCREEN), "The width of the virtual screen, in pixels. The virtual screen is the bounding rectangle of all display monitors. The SM_XVIRTUALSCREEN metric is the coordinates for the left side of the virtual screen."
Debug.Print "SM_CYFULLSCREEN = " & GetSystemMetrics(SM_CYFULLSCREEN), "The height of the client area for a full-screen window on the primary display monitor, in pixels. To get the coordinates of the portion of the screen not obscured by the system taskbar or by application desktop toolbars, call the SystemParametersInfo function with the SPI_GETWORKAREA value."
Debug.Print "SM_CYMAXIMIZED = " & GetSystemMetrics(SM_CYMAXIMIZED), "The default height, in pixels, of a maximized top-level window on the primary display monitor."
Debug.Print "SM_CYSCREEN = " & GetSystemMetrics(SM_CYSCREEN), "The height of the screen of the primary display monitor, in pixels. This is the same value obtained by calling GetDeviceCaps as follows: GetDeviceCaps( hdcPrimaryMonitor, VERTRES)."
Debug.Print "SM_CYVIRTUALSCREEN = " & GetSystemMetrics(SM_CYVIRTUALSCREEN), "The height of the virtual screen, in pixels. The virtual screen is the bounding rectangle of all display monitors. The SM_YVIRTUALSCREEN metric is the coordinates for the top of the virtual screen."
Debug.Print "SM_SAMEDISPLAYFORMAT = " & GetSystemMetrics(SM_SAMEDISPLAYFORMAT), "Nonzero if all the display monitors have the same color format, otherwise, 0. Two displays can have the same bit depth, but different color formats. For example, the red, green, and blue pixels can be encoded with different numbers of bits, or those bits can be located in different places in a pixel color value."
Debug.Print "SM_XVIRTUALSCREEN = " & GetSystemMetrics(SM_XVIRTUALSCREEN), "The coordinates for the left side of the virtual screen. The virtual screen is the bounding rectangle of all display monitors. The SM_CXVIRTUALSCREEN metric is the width of the virtual screen."
Debug.Print "SM_YVIRTUALSCREEN = " & GetSystemMetrics(SM_YVIRTUALSCREEN), "The coordinates for the top of the virtual screen. The virtual screen is the bounding rectangle of all display monitors. The SM_CYVIRTUALSCREEN metric is the height of the virtual screen."

End Sub[/SIZE]
 

Attachments

  • testGetSystemMetrics.txt
    81 KB · Views: 237

isladogs

MVP / VIP
Local time
Today, 22:36
Joined
Jan 14, 2017
Messages
18,217
Hi Rob

Brilliant - you are a star!

I had thought about the Metrics API and actually use bits of it for other purposes but I hadn't followed it up in this context.

I've just run your code on 4 different display settings and get these results



I can see exactly what I need to change.
Now 'all' I have to do is work out how to change the values using VBA & the job's done!

Thanks so very much for spending time on this and giving me a big kick start ...
 

Attachments

  • Capture.PNG
    Capture.PNG
    19.1 KB · Views: 2,591

isladogs

MVP / VIP
Local time
Today, 22:36
Joined
Jan 14, 2017
Messages
18,217
A quick update on this.

So far, I've not found any way of using GetSystemMetrics API to MODIFY settings
As far as I can tell, its intended purely to detect settings

So I looked again at ChangeDisplaySettingsEx API which can certainly be used to detect monitor position & change resolution. However I don't think it can change the display settings for a dual monitor system as I'm trying to do:
a) extend these displays - which means you can drag objects from one monitor to the other
b) duplicate displays - same objects shown on both monitors
c) show only on monitor 1 - so monitor 2 is blank (black)
d) show only on monitor 2 - so monitor 1 is blank (black)

The default is normally option a)

Perhaps I can somehow merge info from both APIs to do the job?

I did find some code at DevShed to switch off a monitor using VBA.

Code:
Private Declare Function SendMessage Lib _
"user32" Alias "SendMessageA" (ByVal hWnd As Long, _
ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Any) As Long

Const SC_MONITORPOWER = &HF170&
Public Const MONITOR_ON = -1&
Public Const MONITOR_OFF = 2&
Const WM_SYSCOMMAND = &H112

Sub TurnMonitorOff()
       SendMessage Application.hWndAccessApp, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_OFF
End Sub

Sub TurnMonitorOn()
       SendMessage Application.hWndAccessApp, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_ON
End Sub

The first of these instantly turns the screen black or both screens on a dual monitor setup.
OK I only want to lose monitor 2 but it still looked promising
... until I moved the mouse or clicked on the keyboard - back on again ....

Back to square one!
 

Attachments

  • Monitor Info.Txt
    1.2 KB · Views: 191

isladogs

MVP / VIP
Local time
Today, 22:36
Joined
Jan 14, 2017
Messages
18,217
I'm still completely stuck on this issue

I'm just giving this post a 'bump' in the hope that someone will have an idea on how to solve this problem

I know I need to use ChangeDisplaySettingEx API but am making no headway. GetSystemMetrics API provides useful info but doesn't allow modification to the settings it provides

There are solutions online for doing this in VB or C# but I've found nothing for VBA that works.

Here's a link to someone who asked the same question and got part of the way 6 years ago - nothing new since!
http://www.vbaexpress.com/forum/showthread.php?37272-Dual-Monitor-Setting-in-VBA
 

isladogs

MVP / VIP
Local time
Today, 22:36
Joined
Jan 14, 2017
Messages
18,217
Brilliant!
Thanks so much for this link - none of my searches had led me to it

I've had a quick look & I'm impressed by what I see.
Its a VB site but there's lots on there I can learn from & adapt from VB to VBA

By chance I finally solved by multiple monitors issue by a totally different route yesterday.
I will be posting the solution when I have time in the next week or so

However, before I do, I'll have a proper look and see whether this provides a better solution.
 

muttley005

New member
Local time
Today, 14:36
Joined
Oct 31, 2014
Messages
5
Hi @Isladog, I know it's an old 3D but ... can u post your solution please?
 

isladogs

MVP / VIP
Local time
Today, 22:36
Joined
Jan 14, 2017
Messages
18,217
It took me a few seconds to work out what 3D meant in this context.
As I mentioned in my reply to your PM, almost four years on and I'm not exactly sure what I was trying to solve in this thread.

In case it helps, I will attach a utility I created related to this topic ...but there is one feature that I never completely solved.
Let me know if that helps.
If that doesn't solve your issue, please explain exactly what you are trying to do.
I'll help if I can .... or if I can't, I'll contact someone who may be able to do so.
 

Attachments

  • Multi Monitors v3.zip
    114.5 KB · Views: 224

muttley005

New member
Local time
Today, 14:36
Joined
Oct 31, 2014
Messages
5
It took me a few seconds to work out what 3D meant in this context.
As I mentioned in my reply to your PM, almost four years on and I'm not exactly sure what I was trying to solve in this thread.

In case it helps, I will attach a utility I created related to this topic ...but there is one feature that I never completely solved.
Let me know if that helps.
If that doesn't solve your issue, please explain exactly what you are trying to do.
I'll help if I can .... or if I can't, I'll contact someone who may be able to do so.
many thanks for your reply!
3D is an abbreviation of thread (as you later understood) that is used in another programming forum that I usually frequent: D
Now ... I'll download your project, I'll study it and then I'll update you.
Thanks again!
 

muttley005

New member
Local time
Today, 14:36
Joined
Oct 31, 2014
Messages
5
After a first quick test I don't succeed in what I would like to.
I'd like to have 2 monitors in duplicate mode and disable the second if necessary, as if returning to single monitor mode.
Example: a button that turn ON the second monitor and another button that turn it OFF.

something like pressing the appropriate keys (in my case FN + F8) but without the various modes appearing.
 

isladogs

MVP / VIP
Local time
Today, 22:36
Joined
Jan 14, 2017
Messages
18,217
As I suspected, you want to do the one thing I was unable to solve using VBA as is clear from the screenshot below
.
1615827679011.png


I can see some point in disabling the secondary monitor when using extended displays as I do.
However, I can't really see the point of either using duplicate displays or, when doing so, switching off the secondary monitor.
Perhaps I'm missing something obvious!

In short, I'm not sure its worth the effort but that's your decision not mine. If you really want to do this:
You could use SendKeys to simulate Fn+F8 but I wouldn't advise that as it won't work on all machines - it does nothing on mine.
OR use the Off button on the monitor
OR you will probably need to use complex API calls or perhaps a PowerShell script.

Sorry but I can't assist you further
 

muttley005

New member
Local time
Today, 14:36
Joined
Oct 31, 2014
Messages
5
yes, unfortunately I had understood this.
wanting to at least try ... just out of curiosity ... I don't know how to use sendkey for FN key.
microsoft guide talks about shift, alt, ctrl and others but not about FN key
 

isladogs

MVP / VIP
Local time
Today, 22:36
Joined
Jan 14, 2017
Messages
18,217
Ah. That may be so. There are a few commands that cannot be run using SendKeys.
IIRC neither PrtScn nor Win keys can be simulated. Perhaps also so for Fn.
 

Users who are viewing this thread

Top Bottom