I'm trying to count the number of monitors (i.e. screens) attached to the console from a service application. I do the following:
int CountMonitors()
{
int nCnt = 0;
if(!EnumDisplayMonitors(NULL, NULL, _countMonitorEnumProc, (LPARAM)&nCnt))
{
//Error
nCnt = -1;
}
return nCnt;
}
BOOL _countMonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
int* pCnt = (int*)dwData;
(*pCnt)++;
return TRUE;
}
but the count is always 1 (when I'm testing it on a dual-monitor Windows 7.) I then do this (which is not exactly what I need due to its limitation):
int nCnt = GetSystemMetrics(SM_CMONITORS);
and the result is also 1.
So how do you count monitors from a service?
First, why is the console special to a service, vs number of displays attached on a remote session? Then, what about display mirroring / extended desktop / eyefinity?
Now, learn about Window Stations and Desktops. Then learn about shatter attacks.
Finally, if what you're really after is hardware enumeration, there are APIs for that. SetupDiGetClassDevs on the display monitor setup class will tell you how many physical screens the video card(s) expose.
Related
This question already has answers here:
How can I get monitors numbers from Screen Resolution dialog in win7 programmatically?
(3 answers)
Closed 5 years ago.
So I've been looking through the web for clues how to do this but I can't seem to get it right. I have a dual monitor setup by default and i want to "pick" 'Monitor 2', so i can put a window there in full-screen when a user presses a button in the program.
So far as I've understood i need the handler of the specified monitor as the first step, my approach is to call EnumDisplayMonitors according to msdn
"To retrieve information about all of the display monitors, use code like this":
int main()
{
EnumDisplayMonitors(NULL, NULL, MyInfoEnumProc, 0);
}
where MyInfoEnumProc is callback defined as follows:
std::vector<HMONITOR> handlerList;
static BOOL CALLBACK MyInfoEnumProc(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData)
{
MONITORINFOEX info_;
info_.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(hMon, &info_); // retrieve monitor info, put it in info_?
handlerList.push_back(hMon); // push handler to array
std::cout << info_.szDevice; // attempt to print data
std::cout << std::endl;
return true;
}
So this callback should go through all the monitors connected to the system but i don't quite understand how i obtain data like resolution, id and name? Like if i go into Monitor settings from Desktop, there is an ID assigned to each monitor, it would be useful to get these so I can place my window in monitor 2 instead of my primary monitor, monitor 1.
Also regarding the handler, I put this in an array but i really need the data so i know which monitor's handler that I have acquired I suppose? When i print the device name of the monitor std::cout << info_.szDevice; i just get the same number for both monitors.
I'm newbie at c++ so i have might have missed something obvious. Hope anyone can help.
Edit:
Thanks to Iinspectable, he mentioned that in the callback function, you can basically check the dwFlags attribute to find the primary monitor and then you know which one is the second screen:
static BOOL CALLBACK MyInfoEnumProc(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData)
{
MONITORINFOEX info_;
info_.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(hMon, &info_);
if (info_.dwFlags == 0) {
std::cout << std::endl;
std::cout << "Found the non-primary monitor" << std::endl;
handlerList.push_back(hMon);
}
return true;
}
Would be useful to have a general solution to this problem in case i wanna connect a third screen, dwFlags = 0 in 2 cases for an example with 3 monitors.
The MONITORINFOEX structure populated by the call to GetMonitorInfo has an rcMonitor field, storing the size of the display (in virtual coordinates).
The dwFlags field has the MONITORINFOF_PRIMARY set for the primary monitor. Since you only have 2 displays, you are looking for the monitor that doesn't have this flag set.
I tried to get the brightness of the primary monitor using the following code:
POINT monitorPoint = { 0, 0 };
HANDLE monitor = MonitorFromPoint(monitorPoint, MONITOR_DEFAULTTOPRIMARY);
DWORD minb, maxb, currb;
if (GetMonitorBrightness(monitor, &minb, &currb, &maxb) == FALSE) {
std::cout << GetLastError() << std::endl;
}
But it fails and GetLastError() returns 87 which means Invalid Parameter.
EDIT: I managed to solve this using EnumDisplayMonitors() and GetPhysicalMonitorsFromHMONITOR() like this:
std::vector<HANDLE> pMonitors;
BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
DWORD npm;
GetNumberOfPhysicalMonitorsFromHMONITOR(hMonitor, &npm);
PHYSICAL_MONITOR *pPhysicalMonitorArray = new PHYSICAL_MONITOR[npm];
GetPhysicalMonitorsFromHMONITOR(hMonitor, npm, pPhysicalMonitorArray);
for (unsigned int j = 0; j < npm; ++j) {
pMonitors.push_back(pPhysicalMonitorArray[j].hPhysicalMonitor);
}
delete pPhysicalMonitorArray;
return TRUE;
}
// and later inside main simply:
EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, NULL);
// and when I need to change the brightness:
for (unsigned int i = 0; i < pMonitors.size(); ++i) {
SetMonitorBrightness(pMonitors.at(i), newValue);
}
Now I encounter 2 new problems:
1) From EnumDisplayMonitors() I get 2 monitor handles since I have 2 monitors. The problem is that only my primary works. Whenever I try to so something with the secondary monitor I get this error:
0xC0262582: ERROR_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA
2) After using SetMonitorBrightness() for some time it stops working even for the primary monitor and I get the following error:
0xC026258D
You are passing an HMONITOR to the function. However, its documentation states that a handle to a physical monitor is required instead, and suggests that you call GetPhysicalMonitorsFromHMONITOR() to obtain it. Indeed, since MonitorFromPoint() returns an HMONITOR your code would have failed to compile with STRICT enabled, a practise that helps root out such mistakes.
You should include error checking for the call to MonitorFromPoint(). And the documentation also suggests that you should call GetMonitorCapabilities() passing MC_CAPS_BRIGHTNESS to make sure the monitor supports brightness requests.
Please refer to the documentation of GetMonitorBrightness() for more detail:
In VC++, I use EnumWindows(...), GetWindow(...), and GetWindowLong(), to get the list of windows and check whether the window is top window (no other window as owner), and whether the window is visible (WS_VISIBLE). However, although my desktop is showing only 5 windows, this EnumWindows is giving me 50 windows, how funny! Any Windows geek here please help me clarify...
The way to list out only windows in taskbar (or similarly in Alt-Tab box) is described by Raymond in this article on MSDN blog:
Which windows appear in the Alt+Tab list?
And this is the super function to check whether a window is shown in alt-tab:
BOOL IsAltTabWindow(HWND hwnd)
{
TITLEBARINFO ti;
HWND hwndTry, hwndWalk = NULL;
if(!IsWindowVisible(hwnd))
return FALSE;
hwndTry = GetAncestor(hwnd, GA_ROOTOWNER);
while(hwndTry != hwndWalk)
{
hwndWalk = hwndTry;
hwndTry = GetLastActivePopup(hwndWalk);
if(IsWindowVisible(hwndTry))
break;
}
if(hwndWalk != hwnd)
return FALSE;
// the following removes some task tray programs and "Program Manager"
ti.cbSize = sizeof(ti);
GetTitleBarInfo(hwnd, &ti);
if(ti.rgstate[0] & STATE_SYSTEM_INVISIBLE)
return FALSE;
// Tool windows should not be displayed either, these do not appear in the
// task bar.
if(GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW)
return FALSE;
return TRUE;
}
Credited to the source code here:
http://www.dfcd.net/projects/switcher/switcher.c
The windows that you are talking about, with an X button and a title bar, etc. are not the only kind of windows. Buttons, dropdown menus, labels, icons, text boxes, the task bar, and just about everything else is a window too1. So EnumWindows is doing exactly what it's supposed to do: enumerate all the top level windows.
1 Even though this is true, EnumWindows only enumerates the top level windows. That means it won't enumerate any child windows:
The EnumWindows function does not enumerate child windows, with the exception of a few top-level windows owned by the system that have the WS_CHILD style.
However, many things on your desktop are windows as well, not just the "windows" you're thinking about.
The answer provided by #jondinham does work perfectly for me. So I work out my own solution.
1.Problems I met with previous solution
Running on Windows 10 home edition 1909., I get two extra unexpected Windows "Calculator" and "Setting".
In addition, windows of Tencent QQ can not be detected, because the following fails:
// the following removes some task tray programs and "Program Manager"
ti.cbSize = sizeof(ti);
GetTitleBarInfo(hwnd, &ti);
if(ti.rgstate[0] & STATE_SYSTEM_INVISIBLE)
return FALSE;
However, I think the bug may be resulted by the particularity of Tencent QQ, I can not even make its' window TOPMOST with DeferWindowPos.
Perhaps someone can help me figure out why this happened and help improving the previous solution by #jondinham.
2.My Solution
I tried to examing the icons of the windows, and filter out windows that does not have its own icon or uses the icon same as the system default. I use code snippets from answer and answer and do some modification. This solution works very well for me.
HICON get_windows_HICON_critical(HWND hwnd)
{
// Get the window icon
HICON icon = reinterpret_cast<HICON>(::SendMessageW(hwnd, WM_GETICON, ICON_SMALL, 0));
if (icon == 0) {
// Alternative method. Get from the window class
icon = reinterpret_cast<HICON>(::GetClassLongPtrW(hwnd, GCLP_HICONSM));
}
// Alternative method: get the first icon from the main module (executable image of the process)
if (icon == 0) {
icon = ::LoadIcon(GetModuleHandleW(0), MAKEINTRESOURCE(0));
}
// // Alternative method. Use OS default icon
// if (icon == 0) {
// icon = ::LoadIcon(0, IDI_APPLICATION);
// }
if(icon == ::LoadIcon(0, IDI_APPLICATION)){
// Filter out those with default icons
icon = 0;
}
return icon;
}
static BOOL CALLBACK enumWindowCallback(HWND hWnd, LPARAM lparam) {
int length = GetWindowTextLength(hWnd);
char* buffer = new char[length + 1];
GetWindowText(hWnd, buffer, length + 1);
std::string windowTitle(buffer);
// List visible windows with a non-empty title
if (IsWindowVisible(hWnd) && length != 0) {
HICON icon = get_windows_HICON_critical(hWnd);
if(icon!=0){
std::cout << hWnd << ": " << windowTitle << std::endl;
}
}
return TRUE;
}
3.Problems with my solution
My solution can not deal with Windows Store APP, according to this question.
For all people looking to find a way to remove Invisible windows like Settings or Microsoft Store from the list:
These windows are cloaked, meaning they still have the dwStyle WS_VISIBLE, but the user can't see them.
You can detect this using the function DwmGetWindowAttribute. The dwAttribute you want to get is DWMWA_CLOAKED (enum constant 14). Only if the value in pvAttribute after the method call is 0, the window is not cloacked.
I want to count the number of active displays. For Mac I can use the following:
CGDisplayCount nDisplays;
CGGetActiveDisplayList(0,0, &nDisplays);
log.printf("Displays connected: %d",(int)nDisplays);
How can I achieve the same in Windows? I've found EnumDisplayMonitors but I can't work out how to use it.
As you have discovered, EnumDisplayMonitors() will do the job but it is a little tricky to call. The documentation states:
The EnumDisplayMonitors function enumerates display monitors (including invisible pseudo-monitors associated with the mirroring drivers) that intersect a region formed by the intersection of a specified clipping rectangle and the visible region of a device context. EnumDisplayMonitors calls an application-defined MonitorEnumProc callback function once for each monitor that is enumerated. Note that GetSystemMetrics (SM_CMONITORS) counts only the display monitors.
This leads us to an easier solution: GetSystemMetrics(SM_CMONITORS). Indeed this may be even better than EnumDisplayMonitors() if you have psuedo-monitors.
As illustration of calling EnumDisplayMonitors() try this:
BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
int *Count = (int*)dwData;
(*Count)++;
return TRUE;
}
int MonitorCount()
{
int Count = 0;
if (EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&Count))
return Count;
return -1;//signals an error
}
Not tested, but essentially you only need to provide the callback for the enum function:
int numMonitors = 0;
BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
//lprcMonitor holds the rectangle that describes the monitor position and resolution)
numMonitors++;
return true;
}
int main()
{
EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, NULL);
}
The problem with above approaches is that they do not detect the correct number of monitors in Clone, Internal, External topologies. They get the correct number only in Extended topology. There is a way to get the correct number of monitors in any topology with QueryDisplayConfig function. You have to use QDC_ALL_PATHS in flags and count only unique monitors found.
DISPLAYCONFIG_TOPOLOGY_ID currTopologyId = 0;
UINT32 numPathArrayElements = 0;
UINT32 numModeInfoArrayElements = 0;
LONG retCode = ::GetDisplayConfigBufferSizes(flags, &numPathArrayElements, &numModeInfoArrayElements);
auto pathArray = std::make_unique<DISPLAYCONFIG_PATH_INFO[]>(numPathArrayElements);
auto modeInfoArray = std::make_unique<DISPLAYCONFIG_MODE_INFO[]>(numModeInfoArrayElements);
retCode = ::QueryDisplayConfig(flags, &numPathArrayElements, pathArray.get(), &numModeInfoArrayElements, modeInfoArray.get(), &currTopologyId);
Use DisplayConfigGetDeviceInfo to get the monitor name from the path target.
DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {};
targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
targetName.header.size = sizeof(targetName);
targetName.header.adapterId = pathInfo.targetInfo.adapterId;
targetName.header.id = pathInfo.targetInfo.id;
LONG retCode = ::DisplayConfigGetDeviceInfo(&targetName.header);
You will get the monitor name in:
targetName.monitorDevicePath, targetName.monitorFriendlyDeviceName
In VC++, I use EnumWindows(...), GetWindow(...), and GetWindowLong(), to get the list of windows and check whether the window is top window (no other window as owner), and whether the window is visible (WS_VISIBLE). However, although my desktop is showing only 5 windows, this EnumWindows is giving me 50 windows, how funny! Any Windows geek here please help me clarify...
The way to list out only windows in taskbar (or similarly in Alt-Tab box) is described by Raymond in this article on MSDN blog:
Which windows appear in the Alt+Tab list?
And this is the super function to check whether a window is shown in alt-tab:
BOOL IsAltTabWindow(HWND hwnd)
{
TITLEBARINFO ti;
HWND hwndTry, hwndWalk = NULL;
if(!IsWindowVisible(hwnd))
return FALSE;
hwndTry = GetAncestor(hwnd, GA_ROOTOWNER);
while(hwndTry != hwndWalk)
{
hwndWalk = hwndTry;
hwndTry = GetLastActivePopup(hwndWalk);
if(IsWindowVisible(hwndTry))
break;
}
if(hwndWalk != hwnd)
return FALSE;
// the following removes some task tray programs and "Program Manager"
ti.cbSize = sizeof(ti);
GetTitleBarInfo(hwnd, &ti);
if(ti.rgstate[0] & STATE_SYSTEM_INVISIBLE)
return FALSE;
// Tool windows should not be displayed either, these do not appear in the
// task bar.
if(GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW)
return FALSE;
return TRUE;
}
Credited to the source code here:
http://www.dfcd.net/projects/switcher/switcher.c
The windows that you are talking about, with an X button and a title bar, etc. are not the only kind of windows. Buttons, dropdown menus, labels, icons, text boxes, the task bar, and just about everything else is a window too1. So EnumWindows is doing exactly what it's supposed to do: enumerate all the top level windows.
1 Even though this is true, EnumWindows only enumerates the top level windows. That means it won't enumerate any child windows:
The EnumWindows function does not enumerate child windows, with the exception of a few top-level windows owned by the system that have the WS_CHILD style.
However, many things on your desktop are windows as well, not just the "windows" you're thinking about.
The answer provided by #jondinham does work perfectly for me. So I work out my own solution.
1.Problems I met with previous solution
Running on Windows 10 home edition 1909., I get two extra unexpected Windows "Calculator" and "Setting".
In addition, windows of Tencent QQ can not be detected, because the following fails:
// the following removes some task tray programs and "Program Manager"
ti.cbSize = sizeof(ti);
GetTitleBarInfo(hwnd, &ti);
if(ti.rgstate[0] & STATE_SYSTEM_INVISIBLE)
return FALSE;
However, I think the bug may be resulted by the particularity of Tencent QQ, I can not even make its' window TOPMOST with DeferWindowPos.
Perhaps someone can help me figure out why this happened and help improving the previous solution by #jondinham.
2.My Solution
I tried to examing the icons of the windows, and filter out windows that does not have its own icon or uses the icon same as the system default. I use code snippets from answer and answer and do some modification. This solution works very well for me.
HICON get_windows_HICON_critical(HWND hwnd)
{
// Get the window icon
HICON icon = reinterpret_cast<HICON>(::SendMessageW(hwnd, WM_GETICON, ICON_SMALL, 0));
if (icon == 0) {
// Alternative method. Get from the window class
icon = reinterpret_cast<HICON>(::GetClassLongPtrW(hwnd, GCLP_HICONSM));
}
// Alternative method: get the first icon from the main module (executable image of the process)
if (icon == 0) {
icon = ::LoadIcon(GetModuleHandleW(0), MAKEINTRESOURCE(0));
}
// // Alternative method. Use OS default icon
// if (icon == 0) {
// icon = ::LoadIcon(0, IDI_APPLICATION);
// }
if(icon == ::LoadIcon(0, IDI_APPLICATION)){
// Filter out those with default icons
icon = 0;
}
return icon;
}
static BOOL CALLBACK enumWindowCallback(HWND hWnd, LPARAM lparam) {
int length = GetWindowTextLength(hWnd);
char* buffer = new char[length + 1];
GetWindowText(hWnd, buffer, length + 1);
std::string windowTitle(buffer);
// List visible windows with a non-empty title
if (IsWindowVisible(hWnd) && length != 0) {
HICON icon = get_windows_HICON_critical(hWnd);
if(icon!=0){
std::cout << hWnd << ": " << windowTitle << std::endl;
}
}
return TRUE;
}
3.Problems with my solution
My solution can not deal with Windows Store APP, according to this question.
For all people looking to find a way to remove Invisible windows like Settings or Microsoft Store from the list:
These windows are cloaked, meaning they still have the dwStyle WS_VISIBLE, but the user can't see them.
You can detect this using the function DwmGetWindowAttribute. The dwAttribute you want to get is DWMWA_CLOAKED (enum constant 14). Only if the value in pvAttribute after the method call is 0, the window is not cloacked.