GetDC,0 -> EnumDisplayMonitors -> No results ?

5 views
Skip to first unread message

R.Wieser

unread,
Jan 24, 2021, 6:47:43 AMJan 24
to
Hello all,

I've just connected a second monitor to my 'puter and am looking a the
results returned by the "EnumDisplayMonitors" (user32.dll) call. The
callback returns both monitors *as long as* I provide a NULL as the DC
argument (and the clipping rectangle argument).

When I set the DC argument to the output of "GetDC,0" (which should return
the DC of the desktop) neither the primary nor the secondary monitor is
shown.

I must be misunderstanding something, but I have no idea what ... :-(

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors

Regards,
Rudy Wieser


JJ

unread,
Jan 24, 2021, 8:57:26 AMJan 24
to
The given HDC doesn't really matter as long as it's a valid handle or is
NULL. `EnumDisplayMonitors()` should always enumerate a monitor as long as
RECT is NULL or within a monitor's area.

So, chances are that you're giving a RECT which is outside of all monitors'
area.

Here's my test application log in a VM with two monitors.
#1 is 1204x716. #2 is 800x600.
`GetDc(0)` is stored in a variable and is reused for the enumeration.

Single monitor configuration:

enum#1: dc=none, rect=none
mon:00170281, dc:00000000, rect:(0,0)-(1204,716)=1204x716
enum#2: dc=getdc(0)=0B010893, rect=none
mon:00170281, dc:18010863, rect:(0,0)-(1204,716)=1204x716
enum#3: dc=none, rect=(100,100)-(110,110)
mon:00170281, dc:00000000, rect:(100,100)-(110,110)=10x10
enum#4: dc=getdc(0)=0B010893, rect=(100,100)-(110,110)
mon:00170281, dc:18010863, rect:(100,100)-(110,110)=10x10
enum#5: dc=getdc(0)=0B010893, rect=(1400,100)-(1410,110)

Two monitors configuration:
#2 as separate desktop (i.e. not a desktop extension)

enum#1: dc=none, rect=none
mon:009502D5, dc:00000000, rect:(0,0)-(1204,716)=1204x716
mon:00180281, dc:00000000, rect:(1204,1204)-(2004,600)=800x600
enum#2: dc=getdc(0)=4201088F, rect=none
mon:009502D5, dc:49010CCE, rect:(0,0)-(1204,716)=1204x716
mon:00180281, dc:8F010C4E, rect:(1204,1204)-(2004,600)=800x600
enum#3: dc=none, rect=(100,100)-(110,110)
mon:009502D5, dc:00000000, rect:(100,100)-(110,110)=10x10
enum#4: dc=getdc(0)=4201088F, rect=(100,100)-(110,110)
mon:009502D5, dc:49010CCE, rect:(100,100)-(110,110)=10x10
enum#5: dc=getdc(0)=4201088F, rect=(1400,100)-(1410,110)
mon:00180281, dc:8F010C4E, rect:(1400,1400)-(1410,110)=10x10

JJ

unread,
Jan 24, 2021, 9:03:37 AMJan 24
to
On Sun, 24 Jan 2021 20:57:22 +0700, JJ wrote:
> The given HDC doesn't really matter as long as it's a valid handle or is
> NULL.

Correction: HDC matters. It defines the RECT's origin. i.e. if it's NULL,
the origin would be the virtual screen, which is the entire monitors
combined as a large display are which is larger than any single monitor.
Otherwise, the origin is the window of the HDC.

R.Wieser

unread,
Jan 24, 2021, 11:23:51 AMJan 24
to
JJ,

> So, chances are that you're giving a RECT which is outside of
> all monitors' area.

Thats the thing, I'm not giving it a rectangle at all. Its NULL.

As the documentation says it, "This parameter can be NULL if you don't want
to clip the region specified by hdc."

> Correction: HDC matters.
> ...
> Otherwise, the origin is the window of the HDC.

The documentattion to "GetDC" says this : "A handle to the window whose DC
is to be retrieved. *If this value is NULL, GetDC retrieves the DC for the
entire screen*."

I'm entirely sure what the "entire screen" stands for, but I guessed thats
ment as the whole of the desktop (including all of its monitors). But even
if it just (randomly?) picks a monitor it boundaries should thus encompass
at least that monitor.

... but I'm getting nothing back ...

Also :

> Single monitor configuration:

I have no idea how you get five entries. On my machine
"EnumDisplayMonitors" only calls its callback twice, once for each monitor
on the desktop (if I detach the second monitor from the desktop the callback
gets called only once - for the primary monitor).


I've just given "EnumDisplayMonitors" both a DC and a (ludicrous large)
clipping rectange. It still fails to return anything. Keeping the
clipping retangle but setting the DC to NULL again returns both monitors.
Than shrinking the clipping rectangle to just around 0,0 returns just the
primary monitor. IOW, the clipping rectangle works as expected.

In all cases "EnumDisplayMonitors" returns a non-zero (OK) value.

hmmm... I just realized: The "clipping rectangle" isn't /clipping/ but
rather a "in which monitor(s) does this area reside" indicator.

Regards,
Rudy Wieser


JJ

unread,
Jan 25, 2021, 7:41:56 AMJan 25
to
On Sun, 24 Jan 2021 17:23:35 +0100, R.Wieser wrote:
>
> I have no idea how you get five entries.

You misunderstood. Each "enum#" log represent each call to
EnumDisplayMonitors(), but with different arguments. The indented lines are
the reports of the callback.

> On my machine
> "EnumDisplayMonitors" only calls its callback twice, once for each monitor
> on the desktop (if I detach the second monitor from the desktop the callback
> gets called only once - for the primary monitor).

As do mine. The enumerations report zero up to two monitors, depending on
the given enumeration arguments.

> but rather a "in which monitor(s) does this area reside" indicator.

I was trying to tell you that. Maybe my English isn't good enough.

R.Wieser

unread,
Jan 25, 2021, 10:43:51 AMJan 25
to
JJ,

> You misunderstood. Each "enum#" log represent each call
> to EnumDisplayMonitors(), but with different arguments.

Ah yes, now I see. Sorry 'bout that.

But that creates a problem: I've done the same four with-and-without DC and
rectangle tests, but you get results back using a DC while I get none. :-|

The only difference I can currently imagine is the used OS. In my case
thats XPsp3.

>> but rather a "in which monitor(s) does this area reside" indicator.
>
> I was trying to tell you that. Maybe my English isn't good enough.

More likely I was too stuck on the "clipping" [1] in its description to
notice what you tried to tell me.

[1] I did some bare-bones line-drawing recently (Bresenham) and implemented
a clipping rectangle. It seems that the "remove everything outside of it"
still lingered.

And by the way, English/American is not my mothertongue either (and not a
language regulary spoken around here). Its therefore quite possible I just
didn't understand and therefore grasp what you tried to tell me.

Regards,
Rudy Wieser


JJ

unread,
Jan 26, 2021, 10:06:41 AMJan 26
to
On Mon, 25 Jan 2021 16:43:43 +0100, R.Wieser wrote:
>
> But that creates a problem: I've done the same four with-and-without DC and
> rectangle tests, but you get results back using a DC while I get none. :-|
>
> The only difference I can currently imagine is the used OS. In my case
> thats XPsp3.

My test was for Win7, but it's the same for XP SP3.

Below log is for the same hardware setup (monitor #1, then #2; left to
right) except that monitor #1 resolution is 1024x704, which is different
than previous setup.

Single monitor configuration:

enum#1: dc=none, rect=none
mon:002000BF, dc:00000000, rect:(0,0)-(1024,704)=1024x704
enum#2: dc=getdc(0)=01010053, rect=none
mon:002000BF, dc:D4010226, rect:(0,0)-(1024,704)=1024x704
enum#3: dc=none, rect=(100,100)-(110,110)
mon:002000BF, dc:00000000, rect:(100,100)-(110,110)=10x10
enum#4: dc=getdc(0)=01010053, rect=(100,100)-(110,110)
mon:002000BF, dc:D4010226, rect:(100,100)-(110,110)=10x10
enum#5: dc=getdc(0)=01010053, rect=(1400,100)-(1410,110)

Two monitors configuration:

enum#1: dc=none, rect=none
mon:002C014F, dc:00000000, rect:(0,0)-(1024,704)=1024x704
mon:002100BF, dc:00000000, rect:(1024,1024)-(1824,600)=800x600
enum#2: dc=getdc(0)=01010052, rect=none
mon:002C014F, dc:3C01034F, rect:(0,0)-(1024,704)=1024x704
mon:002100BF, dc:0D0102D3, rect:(1024,1024)-(1824,600)=800x600
enum#3: dc=none, rect=(100,100)-(110,110)
mon:002C014F, dc:00000000, rect:(100,100)-(110,110)=10x10
enum#4: dc=getdc(0)=01010052, rect=(100,100)-(110,110)
mon:002C014F, dc:3C01034F, rect:(100,100)-(110,110)=10x10
enum#5: dc=getdc(0)=01010052, rect=(1400,100)-(1410,110)
mon:002100BF, dc:0D0102D3, rect:(1400,1400)-(1410,110)=10x10

Assuming that EnumDisplayMonitors() returns TRUE when non zero DC is
given...

You may need to debug your program to check the arguments right before
EnumDisplayMonitors() is called. i.e. after the arguments are pushed into
the stack.

Manually check the arguments in the memory to make sure they're passed with
the correct order, structure (plus alignments), value types (or bitness),
and values.

Then do a single step debugging to make sure that its the
EnumDisplayMonitors() function which is called, instead of other function.

This may seem overkill, but I'm trying to leave no stone unturned.

R.Wieser

unread,
Jan 26, 2021, 11:35:16 AMJan 26
to
JJ,

> You may need to debug your program to check the arguments right before
> EnumDisplayMonitors() is called. i.e. after the arguments are pushed into
> the stack.

Remember that I'm programming in Assembly ? There is *nothing* in between
me pushing the arguments and calling the function. I can give you the .lst
file if you want. :-)

> Manually check the arguments in the memory to make sure they're
> passed with the correct order, structure (plus alignments), value
> types (or bitness), and values.

Done, done and triple-checked. Also provided a special value to the fourth,
LPARM argument and got the exact same value back in the callback function.

> Then do a single step debugging

I'm sorry, but I never liked single-step debuggers and would currently not
even know if I have one. Except for DEBUG.COM that is.

> to make sure that its the EnumDisplayMonitors() function which
> is called, instead of other function.

From four test calls to the function the callback function is called twice
(for a single monitor), with the expected values. IOW, the function work,
just not always.

The function itself is called by name, and resolved by the OS
(double-checked that just now)

> This may seem overkill, but I'm trying to leave no stone unturned.

Following one of Murphies Laws : the problem will often be there where you
least expect it. :-)

Below is the output of my four tests (single monitor).

-[1] No DC, no rectangle
00010001 00000000 0012FF78 12345678
Res:00000001
-[2] DC only
Res:00000001
-[3] DC + rectangle
Res:00000001
-[4] rectangle only
00010001 00000000 0012FF78 12345678
Res:00000001

"Res:" is ofcourse the result. OK in all four.

If you know whats going on you may tell me.

Regards,
Rudy Wieser


R.Wieser

unread,
Jan 26, 2021, 11:58:51 AMJan 26
to
JJ,

> This may seem overkill, but I'm trying to leave no stone unturned.

I found it, and you will never guess to what the problem was/is ....




I ran that code from within a fullscreen (80x25 text mode) *console* . When
I switched to windowed (GUI) mode I got four results for the four tests.

Although the GetDC,0 result looked to be OK, it must have pointed to
something different than the actual virtual screen.

Regards,
Rudy Wieser


JJ

unread,
Jan 27, 2021, 9:46:43 PMJan 27
to
On Tue, 26 Jan 2021 17:34:36 +0100, R.Wieser wrote:
>
> IOW, the function work, just not always.

That raises a flag. The cause of the problem may be elsewhere in the system,
and inconsistency is difficult to troubleshoot.

JJ

unread,
Jan 27, 2021, 9:58:53 PMJan 27
to
Oh... If your program is built as a console application, it's probably due
to the fact that a console application's thread by default is not a GUI
thread. Calling any GUI related functions without converting the thread to a
GUI thread first using `IsGUIThread()`, while according to the documentation
the system will automatically do that, but it may do it a bit too late or
unreliably.

R.Wieser

unread,
Jan 28, 2021, 1:21:36 AMJan 28
to
JJ,

> Oh... If your program is built as a console application, it's
> probably due to the fact that a console application's thread
> by default is not a GUI thread.

I have a bit of a problem in understanding that. I'm asking for the destop
DC (GetDC,0) but am not getting it. Why ? What has /my/ thread not being a
GUI thread have anything to do with that ?

Also, what am I getting instead (and why does the MS documentation not
mention it ? :-( ).

> ... using `IsGUIThread()`, while according to the documentation ...

The "docs.microsoft.com" description for that function is rather lacking in
depth. Could you tell me which documentation you are referring to ?

Regards,
Rudy Wieser


JJ

unread,
Jan 29, 2021, 10:58:51 PMJan 29
to
Sorry, I was mistaken.

GUI thread is for message queue. It's not needed if the function doesn't
involve message queue.

I've tested as a console application, and sure enough, it still works.

R.Wieser

unread,
Jan 30, 2021, 2:58:48 AMJan 30
to
JJ,

> I've tested as a console application, and sure enough, it still works.

It does, as long as you are in GUI mode (a windowed console). It doesn't
(at least at my end) when you switch to full-screen text-mode. In my case I
always start with "MODE CON COLS=80 LINES=25".

And I realized just now that that is what makes it extra strange : The app
is a console app one in both cases, but in the full-screen mode it suddenly
looses track of its (GUI) desktop DC.

I just test to ask for the DC twice (and destroy it) ihn the same thread,
but in between switch from fullscreen console to GUI (windowed) mode (using
ALT-tab). I get two different DC results. As a test I also just ran it
fully in GUI and after in console mode. In the former I get the same DC
back twice. In the latter I get two different ones.

Regards,
Rudy Wieser


R.Wieser

unread,
Jan 30, 2021, 1:24:06 PMJan 30
to
> (using ALT-tab)

Whoops, that should have been ALT-Enter ofcourse.


JJ

unread,
Jan 31, 2021, 4:59:32 PMJan 31
to
Judging from what I can see from my (further) tests, either DC or its
geometry, becomes wonky in fullscreen text mode. It's exactly as what you've
experienced.

The cause is probably because GDI is not applicable for text video mode,
where the screen "window", is not graphical. However, `GetDC(0)` returns a
valid handle regardless. That confuses me.

Here's my result if I tested it as a console application, without and with
fullscreen mode. The output was done in a single console window created by
CMD. Test #1 is in windowsed mode, #2 is in fullscreen, and #3 is back to
windowed mode.

Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\user>cd Desktop

C:\Documents and Settings\user\Desktop>Project2.exe
enum#1: dc=none, rect=none
mon:0010008F, dc:00000000, rect:(0,0)-(1032,702)=1032x702
mon:00020001, dc:00000000, rect:(1032,1032)-(1832,600)=800x600
enum#2: dc=getdc(0)=01010052, rect=none
mon:0010008F, dc:AC0102B9, rect:(0,0)-(1032,702)=1032x702
mon:00020001, dc:BE010299, rect:(1032,1032)-(1832,600)=800x600
enum#3: dc=none, rect=(100,100)-(110,110)
mon:0010008F, dc:00000000, rect:(100,100)-(110,110)=10x10
enum#4: dc=getdc(0)=01010052, rect=(100,100)-(110,110)
mon:0010008F, dc:AC0102B9, rect:(100,100)-(110,110)=10x10
enum#5: dc=getdc(0)=01010052, rect=(1400,100)-(1410,110)
mon:00020001, dc:BE010299, rect:(1400,1400)-(1410,110)=10x10

C:\Documents and Settings\user\Desktop>Project2.exe
enum#1: dc=none, rect=none
mon:0010008F, dc:00000000, rect:(0,0)-(1032,702)=1032x702
mon:00020001, dc:00000000, rect:(1032,1032)-(1832,600)=800x600
enum#2: dc=getdc(0)=01010055, rect=none
enum#3: dc=none, rect=(100,100)-(110,110)
mon:0010008F, dc:00000000, rect:(100,100)-(110,110)=10x10
enum#4: dc=getdc(0)=01010055, rect=(100,100)-(110,110)
enum#5: dc=getdc(0)=01010055, rect=(1400,100)-(1410,110)

C:\Documents and Settings\user\Desktop>Project2.exe
enum#1: dc=none, rect=none
mon:0010008F, dc:00000000, rect:(0,0)-(1032,702)=1032x702
mon:00020001, dc:00000000, rect:(1032,1032)-(1832,600)=800x600
enum#2: dc=getdc(0)=01010051, rect=none
mon:0010008F, dc:AC0102B9, rect:(0,0)-(1032,702)=1032x702
mon:00020001, dc:BE010299, rect:(1032,1032)-(1832,600)=800x600
enum#3: dc=none, rect=(100,100)-(110,110)
mon:0010008F, dc:00000000, rect:(100,100)-(110,110)=10x10
enum#4: dc=getdc(0)=01010051, rect=(100,100)-(110,110)
mon:0010008F, dc:AC0102B9, rect:(100,100)-(110,110)=10x10
enum#5: dc=getdc(0)=01010051, rect=(1400,100)-(1410,110)
mon:00020001, dc:BE010299, rect:(1400,1400)-(1410,110)=10x10

C:\Documents and Settings\user\Desktop>

R.Wieser

unread,
Feb 1, 2021, 4:02:46 AMFeb 1
to
JJ,

> However, `GetDC(0)` returns a valid handle regardless. That confuses me.

Same here.

As I was not even sure if it is in fact a /valid/ handle (and not just some
random number) I tried to draw on it.

In windowed mode I saw the FillRect and MoveToExe & LineTo expected results
appear on the desktop.

In fullscreen mode I see nothing happening - which seems to indicate that
the GetDC(0) in fullscreen mode doesn't actually (somehow) point at the text
screen.

In both cases all involved functions returned 0x1 (OK), so it looks like the
returned DC is valid in textmode too.

.. no idea where it points to though - or why (and not return a NULL handle
for the full-screen text mode). :-|

Regards,
Rudy Wieser


Reply all
Reply to author
Forward
0 new messages