Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Bug in GetDeviceCaps API call??

4 views
Skip to first unread message

Howard Kaikow

unread,
May 16, 2001, 4:58:32 PM5/16/01
to
Pages 404-406 of Steve Roman's Win32 API Programming with Visual Basic
(ISBN: 1-56592-631-5), January 2000, First Edition, points out a problem
with HORZSIZE And VERTSIZE for the GetDeviceCaps API call.

Using a 21 inch monitor, with Win NT, he gets 320 x 240, but the result
should be 25.4 * (HORZRES/LOGPIXELSX) and 25.4 * (VERTRES/LOGPIXELSY). He
states that he gets the correct result in Win 9x.

Using a 19 inch monitor, with Win 2000, I also get 320 x 240, that cannot
be correct because, last time I checked 19 was not equal to 21. In Win 98, I
get the correct result 270 x 203.

Win 2000 seems to have a bug in reporting the correct values for HORZSIZE
And VERTSIZE.

I am using VB6 Enterprise SP5 in both Win 2000 and Win 98.

I then copied the .bas file to a Win 95 system that has a 17 inch monitor.
The Win 95 system has only VB 5 Learning Edition. Running the code, Win 95
also reported 270 x 203, but that cannot be correct because, last time I
checked 17 was not equal to 19.

This seems to preclude proper device independent scaling of userforms, etc.

I am using the following code:

Option Explicit

Private Declare Function GetDeviceCaps Lib "gdi32" _
(ByVal hDC As Long, ByVal nIndex As Long) As Long

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

Private Declare Function CreateIC Lib "gdi32" Alias "CreateICA" (ByVal
lpDriverName As String,
ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As Any)
As Long

Private Sub Main()
Dim FullScreenX As Long
Dim MaximizedX As Long
Dim ScreenX As Long
Dim FullScreenY As Long
Dim MaximizedY As Long
Dim ScreenY As Long

' GetSystemMetrics() codes
Const SM_CXSCREEN = 0
Const SM_CYSCREEN = 1
Const SM_CXFULLSCREEN = 16
Const SM_CYFULLSCREEN = 17
Const SM_CXMAXIMIZED = 61
Const SM_CYMAXIMIZED = 62

' GetDeviceCaps codes
Const LOGPIXELSX = 88
Const LOGPIXELSY = 90
Const HORZRES = 8
Const HORZSIZE = 4
Const VERTRES = 10
Const VERTSIZE = 6

' Width of the screen, in pixels.
ScreenX = GetSystemMetrics(SM_CXSCREEN)
' Height of the screen, in pixels.
ScreenY = GetSystemMetrics(SM_CYSCREEN)
' Width, in pixels, of a maximized top-level window.
MaximizedX = GetSystemMetrics(SM_CXMAXIMIZED)
' Height, in pixels, of a maximized top-level window.
MaximizedY = GetSystemMetrics(SM_CYMAXIMIZED)
' Width of the inside area of a full-screen window.
' Use spi.GetWorkArea to get the portion of the screen
' not obscured by docked trays.
FullScreenX = GetSystemMetrics(SM_CXFULLSCREEN)
' Height of the inside area of a full-screen window.
' Use spi.GetWorkArea to get the portion of the screen
' not obscured by docked trays.
FullScreenY = GetSystemMetrics(SM_CYFULLSCREEN)

MsgBox "Screen: " & ScreenX & " by " & ScreenY & vbCr & _
"FullScreen: " & FullScreenX & " by " & FullScreenY & vbCr & _
"Maximized: " & MaximizedX & " by " & MaximizedY


Dim hDC As Long
Dim lngHORZSIZE As Long
Dim lngVERTSIZE As Long
Dim lngHORZRES As Long
Dim lngVERTRES As Long
Dim lngLogPixelsX As Long
Dim lngLogPixelsY As Long

hDC = CreateIC("DISPLAY", "", "", 0&)
' Display width in millimeters
lngHORZSIZE = GetDeviceCaps(hDC, HORZSIZE)
' Display height in millimeters
lngVERTSIZE = GetDeviceCaps(hDC, VERTSIZE)
' Horizontal resolution in pixels
lngHORZRES = GetDeviceCaps(hDC, HORZRES)
' Vertical resolution in pixels
lngVERTRES = GetDeviceCaps(hDC, VERTRES)
' Horizontal pixels per inch
lngLogPixelsX = GetDeviceCaps(hDC, LOGPIXELSX)
' Vertical pixels per inch
lngLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY)
MsgBox lngLogPixelsX & " by " & lngLogPixelsY & vbCr & _
lngHORZSIZE & " by " & lngVERTSIZE & vbCr & _
lngHORZRES & " by " & lngVERTRES
End Sub


--
Please post your response to the newsgroup.

http://www.standards.com/ipusers/standards; Word macros, including
converting from WordBasic to VBA; Technical writing and reviewing; Standards
------------------------------------------------

--
Please post your response to the newsgroup.

http://www.standards.com/ipusers/standards; Word macros, including
converting from WordBasic to VBA; Technical writing and reviewing; Standards
------------------------------------------------


Michael Williams

unread,
May 16, 2001, 10:36:41 PM5/16/01
to
I think you are getting a little bit confused. The resolution is simply the
number of pixels that Windows "draws" to the display. For example, if your
resolution is set to 800 x 600 pixels then that is exactly the number of
pixels that will be displayed on the screen. A "pixel" means a single "dot"
that Windows draws to the screen, and has nothing whatsoever to do with the
number of physical dots (or dot groups) that the specific display screen
contains. So, if you set your display in Control Panel to 800 x 600 then
Windows will draw 600 "rows", each consisting of 800 "dots".

Each "row" of 800 pixels will occupy the full width of the display,
irrespective of how large the monitor screen is. A "pixel" on a very large
screen will be larger than a pixel on a smaller screen. Generally speaking,
Windows has no way of knowing exactly how large your screen is. You can, for
example, have your display set to a "generic no-name monitor" and physically
connect a 12 inch screen. On the other hand, you can instead connect a
massive 25 inch screen to the same system. Obviously, if we are talking
about "real" inches, there will be fewer pixels per inch on the large
display than there are on the small one.

So, Windows doesn't use "real" inches when it comes to displays or monitors.
Instead, it uses "logical" inches. Under normal conditions (Windows is set
to "small fonts") then one pixel is equal to 0.010417 "logical" inches
(approximately), so that a display that is set to a width of 800 pixels (800
x 600, for example) has a "logical" width of 8.33 inches. A similar system
that is set to 640 x 480 will have a "logical" width of 6.66 inches. It
doesn't matter how large, physically, the monitor is. These are "logical"
inches. Visual Basic likes to deal in Twips (there are 1440 Twips per inch)
and so the number of Twips Per Pixel on such a system is 15.

If you set your computer to something other than "small fonts" then these
values will change, but the principle is just the same. Large fonts (or
other user defined sizes) are provided so that the size of a character on
the display will be physically larger in order that it can be more clearly
seen by people with impaired vision. For example, at the standard "large
fonts" setting there are only 12 Twips Per Pixel, and so Windows displays
all characters on the screen at a larger size.

So, forget the physcial size of the monitor. It is the "logical" size that
counts, and that is determined by both the pixel resolution and the Windows
"font size" as set in Control Panel.

It's fun, this, isn't it!

Mike

"Howard Kaikow" <kai...@standards.com> wrote in message
news:9dupj3$o90$1...@pyrite.mv.net...

Michael Williams

unread,
May 16, 2001, 10:47:01 PM5/16/01
to

Oh Bugger! Such an opportunity missed!

I meant to incorporate the phrase "size matters" in my last posting, and I
completely forgot about it :-(

Mike

Howard Kaikow

unread,
May 16, 2001, 11:12:21 PM5/16/01
to
I understand that, but HORZSIZE and VERTSIZE are different critters than
HORZRES and VERTRES.
Both are logical values.

For example, see the discussion in either the Access 97 or Access 2000 VBA
Handbook by Ken Getz.

Michael Williams

unread,
May 17, 2001, 8:09:02 AM5/17/01
to
I think that you may getting a little bit confused with what all these
values actually mean, because you keep mentioning the physical size of the
monitors, which has nothing whatsoever to do with it.

Firstly, in order to standardise things in a world where monitors can be
almost any physical size, Windows sets a "standard" by which everything else
can be measured. This "standard" is the number of pixels per logical inch.
The value of this "standard" depends on your Font Settings in Control Panel,
but for the normal "small fonts" setting, the number of pixels per logical
inch is 96. So, if your computer is running at a reslution of 800 x 600 then
the "logical" width of the screen will be 8.33 inches. These are "logical"
inches, of course so that Windows "knows" that the width of this particular
screen is 8.33 logical inches, regardless of its actual width in "real"
inches (as measured with a standard ruler). The same system running at 640 x
400 will have a logical width of 6.66 inches. So, the "logical" width of the
display is determined by the resolution that your system is running. The
thing that is "fixed" (for a given Control Panel font size) is the number of
pixels per logical inch. For "small fonts" setting this is 96 pixels per
logical inch. The actual "physical" size of your monitor (17", 19", 25" etc)
has nothing whatsoever to do with it.

Now, HORZRES returns the width, in pixels, of your display (which would be
800 on a system running at a resolution of 800 x 600). LOGPIXELSX returns
the number of pixels per logical inch. This is determined by the fonts
setting in Control Panel, and for a system running at the normal "small
fonts" setting the value returned would be 96 (pixels per logical inch).
HORZSIZE returns the "logical" width of the display, in millimetres.

So, the calculation that you are performing (25.4 * (HORZRES/LOGPIXELSX) )
will return the same value as that returned by HORZSIZE, except that
HORZSIZE, of course, returns a "Long" and so there will be no decimal
fraction. For example, on a system running in small fonts at 800 x 600
resolution, 25.4 * (HORZRES/LOGPIXELSX) will return 211.66 and HORZSIZE will
return 211.

The HORZSIZE and VERTSIZE values that you should get for various resolutions
for systems running at a "small fonts" setting are:

800 x 600 = 211 x 158 (small fonts) or 169 x 127 (large fonts)
1024 x 768 = 270 x 203 (small fonts) or 216 x 162 (large fonts)
1280 x 960 = 338 x 254 (small fonts) or 270 x 203 (large fonts)

There are, of course, many other available resolutions and, additionally,
some of your systems may not be running at either the "small fonts" or the
"large fonts" setting, because it is possible to set the font size to a user
selectable value.

I do not have access to either a WinNT or a Win2000 system, however, and so
I cannot say for certain that those operating systems return the same
values. I cannot even be sure that "small fonts" on a WinNT or Win2000
machine translates to 96 dpi, although I suspect that it does. It would be
interesting to find out what results you get, though.

Have a look at all those systems again. First of all look in Control Panel
to see what font size they are running at. Then check that LOGPIXELSX
returns the correct value for that setting (96 pixels per inch for "small"
fonts and 120 pixels per inch for "large" fonts). Then see if the various
other values returned agree with those shown above for the various
resolutions.

As my friend, Maureen, often tells me - size matters!

Mike


"Howard Kaikow" <kai...@standards.com> wrote in message

news:9dvffu$gih$1...@pyrite.mv.net...

Howard Kaikow

unread,
May 17, 2001, 1:34:28 PM5/17/01
to
The issue is that HORSIZE and VERTSIZE are returning incorrect values.
Perhaps, these do not matter for the objective I desire?

Win 2000 is returning 320 x 240, whilst Win 98 is returning 270 x 203, on
the SAME monitor, a 19 inch monitor, using 1024 x 768. Win 95 returns 270 x
203 on a 17 inch monitor, using 1024 x 768.

My objective is to determine the X and Y scaling for a Userform created at
1024 x 768 on a 19 inch monitor so that when used on, say. a 17 inch
monitor with, say, 800 x 600, the userform will fit on the screen.

Say both monitors are using 1024 x 768 and the Userform on the 19 inch
monitor is quite large, perhaps, taking all of the screen area. The Userform
has be scaled to fit for the 17 inch monitor.

The other case, on the same monitor, a userform created at 1024 x 768 will
often be too large for 640 x 480. The Userform needs to be scaled.

How does one determine the appropriate scale factors?

Michael Williams

unread,
May 17, 2001, 2:54:59 PM5/17/01
to
It is *not* the monitor that determines the resolution, it is the setting of
the graphics card, as determined in Windows Control Panel.

If you write a program such that the Form fills the screen and all its
controls are visible at 1024 x 768 on a 19" monitor and you then run the
same program on a machine that is also running at 1024 x 768, but on a 17"
monitor, the Form should look exactly the same, filling the screen and with
all controls visible. Both Win95 and Win98 will return a value of 270 x 203
"logical" millimetres regardles of the actual size of the monitor. As I have
already said, I am not sure if there is a bug in WinNT that prevents it
doing this, or if in fact the "small fonts" setting on NT maps to something
other than 96 pixels per inch. I imagine that somebody out there who
actually uses Win NT will be able to tell us. The only time you will need to
alter the size of your Form and the location of its controls in code is if
the same application is run on a system that is running at some other
resolution. This can be easy, or difficult, depending on exactly what you
are doing on the Form. Have a look, for example, at the following code. It
is something that I wrote ages ago and, in my usual fashion, never finished!
This shows how to have the layout of a form maintained even when the user
resizes it. It is really intended to automatically adjust the layout when
the application is run at different screen resolutions or when the user
moves or resizes the Windows Task Bar on a maximized Form, but it will also
work "in real time" if the user resizes the Form with the mouse. Actually,
it looks quite impressive when doing that, especially if you have set up
your system to show the contents of windows whilst dragging (Control Panel /
Display / Effects / Show Window Contents Whilst Dragging). It is just a
start, really, and is probably not exactly what you want, but it will give
you some clues. Create a new project and place a number of text boxes,
scroll bars, command buttons, option buttons, line controls and stuff on the
form. The paste in the following code. Then run the project and use the
mouse to change the size of the Form. All of the controls should
automatically resize and reposition accordingly.

Mike


Option Explicit
Dim ratio1() As Single
Dim ratio2() As Single
Dim ratio3() As Single
Dim ratio4() As Single
Dim ratio5() As Single

Private Sub Form_Load()
ReDim ratio1(Controls.Count - 1)
ReDim ratio2(Controls.Count - 1)
ReDim ratio3(Controls.Count - 1)
ReDim ratio4(Controls.Count - 1)
ReDim ratio5(Controls.Count - 1)
Dim mycontrol As Control, counter As Long
counter = 0
For Each mycontrol In Controls
Select Case TypeName(mycontrol)
Case "Line":
ratio1(counter) = mycontrol.X1 / Me.ScaleWidth
ratio2(counter) = mycontrol.X2 / Me.ScaleWidth
ratio3(counter) = mycontrol.Y1 / Me.ScaleHeight
ratio4(counter) = mycontrol.Y2 / Me.ScaleHeight
Case "CommonDialog", "Timer":
'Nothing to do.
Case "HScrollBar", "ComboBox":
ratio1(counter) = mycontrol.Top / Me.ScaleHeight
ratio2(counter) = mycontrol.Left / Me.ScaleWidth
ratio3(counter) = mycontrol.Width / Me.ScaleWidth
Case "VScrollBar":
ratio1(counter) = mycontrol.Top / Me.ScaleHeight
ratio2(counter) = mycontrol.Left / Me.ScaleWidth
ratio3(counter) = mycontrol.Height / Me.ScaleHeight
Case "TextBox", "ListBox":
ratio1(counter) = mycontrol.Top / Me.ScaleHeight
ratio2(counter) = mycontrol.Left / Me.ScaleWidth
ratio3(counter) = mycontrol.Width / Me.ScaleWidth
ratio4(counter) = mycontrol.Height / Me.ScaleHeight
ratio5(counter) = mycontrol.FontSize / Me.ScaleHeight
Case "CommandButton":
ratio1(counter) = mycontrol.Top / Me.ScaleHeight
ratio2(counter) = mycontrol.Left / Me.ScaleWidth
ratio3(counter) = mycontrol.Width / Me.ScaleWidth
ratio4(counter) = mycontrol.Height / Me.ScaleHeight
ratio5(counter) = mycontrol.FontSize / Me.ScaleHeight
Case Else:
ratio1(counter) = mycontrol.Top / Me.ScaleHeight
ratio2(counter) = mycontrol.Left / Me.ScaleWidth
End Select
counter = counter + 1
Next mycontrol
End Sub

Private Sub Form_Resize()
Dim mycontrol As Control, counter As Long
counter = 0
On Error Resume Next
' The On Error is just in case there are some
' controls on the form which do not have a
' width or height property (or if any of the
' properties are "read only")
For Each mycontrol In Controls
Select Case TypeName(mycontrol)
Case "Line":
mycontrol.X1 = ratio1(counter) * Me.ScaleWidth
mycontrol.X2 = ratio2(counter) * Me.ScaleWidth
mycontrol.Y1 = ratio3(counter) * Me.ScaleHeight
mycontrol.Y2 = ratio4(counter) * Me.ScaleHeight
Case "CommonDialog", "Timer":
'Nothing
Case "HScrollBar", "ComboBox":
mycontrol.Top = ratio1(counter) * Me.ScaleHeight
mycontrol.Left = ratio2(counter) * Me.ScaleWidth
mycontrol.Width = ratio3(counter) * Me.ScaleWidth
Case "VScrollBar":
mycontrol.Top = ratio1(counter) * Me.ScaleHeight
mycontrol.Left = ratio2(counter) * Me.ScaleWidth
mycontrol.Height = ratio3(counter) * Me.ScaleHeight
Case "TextBox", "ListBox":
mycontrol.Top = ratio1(counter) * Me.ScaleHeight
mycontrol.Left = ratio2(counter) * Me.ScaleWidth
mycontrol.Width = ratio3(counter) * Me.ScaleWidth
mycontrol.Height = ratio4(counter) * Me.ScaleHeight
Case "CommandButton":
mycontrol.Top = ratio1(counter) * Me.ScaleHeight
mycontrol.Left = ratio2(counter) * Me.ScaleWidth
mycontrol.Width = ratio3(counter) * Me.ScaleWidth
mycontrol.Height = ratio4(counter) * Me.ScaleHeight
Case Else:
mycontrol.Top = ratio1(counter) * Me.ScaleHeight
mycontrol.Left = ratio2(counter) * Me.ScaleWidth
End Select
counter = counter + 1
Next mycontrol
On Error GoTo 0
End Sub


"Howard Kaikow" <kai...@standards.com> wrote in message

news:9e120e$oh4$1...@pyrite.mv.net...

Howard Kaikow

unread,
May 17, 2001, 5:48:47 PM5/17/01
to
"Michael Williams" <Mi...@peetsmill.freeserve.co.uk> wrote in message
news:9e16t4$3tm$1...@newsg3.svr.pol.co.uk...

> Both Win95 and Win98 will return a value of 270 x 203
> "logical" millimetres regardles of the actual size of the monitor. As I
have
> already said, I am not sure if there is a bug in WinNT that prevents it
> doing this, or if in fact the "small fonts" setting on NT maps to
something
> other than 96 pixels per inch. I imagine that somebody out there who
> actually uses Win NT will be able to tell us.

In an earlier post, I pointed out that Win 2000 returns 320 x 240, and uses
96.

> The only time you will need to
> alter the size of your Form and the location of its controls in code is if
> the same application is run on a system that is running at some other
> resolution. This can be easy, or difficult, depending on exactly what you
> are doing on the Form.

I've concluded that the only way to solve this is to design userforms that
are no larger than the LOGICAL dimensions available at lower resolutions.

> Have a look, for example, at the following code. It
> is something that I wrote ages ago and, in my usual fashion, never
finished!
> This shows how to have the layout of a form maintained even when the user
> resizes it.

Thanx. I already had my own version of that. And MSFT Q182070 - HOWTO Create
a Resolution-Independent Form.htm describes how to do this in VB. I tweaked
it to get it working. in VBA

I just got hung up on the monitor size issue.

James Cane

unread,
May 30, 2001, 7:07:09 AM5/30/01
to
Never mind.

"Howard Kaikow" <kai...@standards.com> wrote in message news:9e1gt7$6fd$1...@pyrite.mv.net...


> "Michael Williams" <Mi...@peetsmill.freeserve.co.uk> wrote in message
> news:9e16t4$3tm$1...@newsg3.svr.pol.co.uk...
> > Both Win95 and Win98 will return a value of 270 x 203

>
> I just got hung up on the .... ..... size issue.
>
>
>

0 new messages