How to take screenshot of second monitor?

Just starting out? Need help? Post your questions and find answers here.
novablue
Enthusiast
Enthusiast
Posts: 165
Joined: Sun Nov 27, 2016 6:38 am

How to take screenshot of second monitor?

Post by novablue »

Hello, i would like to take a screenshot my second monitor, how do i do it?

Code: Select all

Procedure MakeDesktopScreenshot(ImageNr,x,y,Width,Height) 
   hImage = CreateImage(ImageNr,Width,Height) 
   hDC    = StartDrawing(ImageOutput(ImageNr)) 
   DeskDC = GetDC_(GetDesktopWindow_()) 
      BitBlt_(hDC,0,0,Width,Height,DeskDC,x,y,#SRCCOPY) 
   StopDrawing() 
   ReleaseDC_(GetDesktopWindow_(),DeskDC) 
   ProcedureReturn hImage 
EndProcedure

ExamineDesktops()

MakeDesktopScreenshot(0, 0, 0, DesktopWidth(0), DesktopHeight(0)) 
SaveImage(0, "D:\DesktopScreenshot.bmp")
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: How to take screenshot of second monitor?

Post by Mijikai »

Dont use StartDrawing() to get the device context.
Its undefined behaviour and therefore bad practise.

If those system handles would be documented it would be another story.
I dont know why such helpful things arent... :?:

Getting to all monitors is not so easy.

Here is my version:

Code: Select all

EnableExplicit

;Procedure - Screenshot()
;Author: Mijikai

Procedure.i Screenshot(MonitorNumber.i = -1,File.s = #Null$,Width.i = #Null,Height.i = #Null)
  Protected device.DISPLAY_DEVICE
  Protected mode.DEVMODE
  Protected bounds.RECT
  Protected hmonitor.i
  Protected hdc.i
  Protected hcopy.i
  Protected hbm.i
  Protected hrestore.i
  Protected *bits
  Protected image.i
  Protected bminfo.BITMAPINFOHEADER
  device\cb = SizeOf(DISPLAY_DEVICE)
  If MonitorNumber < 0
    ProcedureReturn GetSystemMetrics_(#SM_CMONITORS)
  EndIf
  If EnumDisplayDevices_(#Null,MonitorNumber,@device,#Null)
    If device\StateFlags & (#DISPLAY_DEVICE_ATTACHED_TO_DESKTOP|#DISPLAY_DEVICE_MIRRORING_DRIVER) = #DISPLAY_DEVICE_ATTACHED_TO_DESKTOP
      mode\dmSize = SizeOf(DEVMODE)
      If EnumDisplaySettings_(@device\DeviceName[0],#ENUM_CURRENT_SETTINGS,@mode)
        bounds\left = mode\Display\dmPosition\x
        bounds\top = mode\Display\dmPosition\y
        bounds\right = mode\dmPelsWidth + bounds\left
        bounds\bottom = mode\dmPelsHeight + bounds\top
        hmonitor = MonitorFromRect_(@bounds,#MONITOR_DEFAULTTONEAREST)
        If hmonitor
          If Width < 1
            Width = mode\dmPelsWidth
          EndIf
          If Height < 1
            Height = mode\dmPelsHeight
          EndIf
          hdc = GetDC_(#Null)
          If hdc
            hcopy = CreateCompatibleDC_(hdc)
            If hcopy
              bminfo\biSize = SizeOf(BITMAPINFOHEADER)
              bminfo\biWidth = Width
              bminfo\biHeight = Height
              bminfo\biPlanes = 1
              bminfo\biBitCount = 24
              bminfo\biCompression = #BI_RGB
              bminfo\biSizeImage = Height * (Width * 3)
              hbm = CreateDIBSection_(hcopy,@bminfo,#DIB_RGB_COLORS,@*bits,#Null,#Null)
              If hbm
                SaveDC_(hcopy)
                SelectObject_(hcopy,hbm)
                SetStretchBltMode_(hcopy,#HALFTONE)
                SetBrushOrgEx_(hcopy,#Null,#Null,#Null)
                If StretchBlt_(hcopy,#Null,#Null,Width,Height,hdc,mode\Display\dmPosition\x,mode\Display\dmPosition\y,mode\dmPelsWidth,mode\dmPelsHeight,#SRCCOPY)
                  image = CreateImage(#PB_Any,Width,Height,24)
                  If image
                    If StartDrawing(ImageOutput(image))
                      CopyMemory(*bits,DrawingBuffer(),bminfo\biSizeImage)
                      StopDrawing()
                      RestoreDC_(hcopy,-1)
                      DeleteObject_(hbm)
                      DeleteDC_(hcopy)
                      ReleaseDC_(#Null,hdc)
                      CloseHandle_(hmonitor)
                      If File
                        *bits = SaveImage(image,File,#PB_ImagePlugin_BMP)
                         FreeImage(image)
                        ProcedureReturn *bits
                      Else
                        ProcedureReturn image
                      EndIf
                    EndIf
                    FreeImage(image)
                  EndIf
                EndIf
                RestoreDC_(hcopy,-1)
                DeleteObject_(hbm)
              EndIf
              DeleteDC_(hcopy)
            EndIf
            ReleaseDC_(#Null,hdc)
          EndIf
          CloseHandle_(hmonitor)
        EndIf
      EndIf
    EndIf
  EndIf
  ProcedureReturn #Null
EndProcedure

Procedure.i Main()
  Protected count.i
  Protected index.i
  count = Screenshot();<- empty will return the monitor count 
                      ;width and height will clamp (resize) the screenshot if specified
                      ;if no file is specified the function will return a image
  For index = 0 To count - 1
    Debug Screenshot(index,"screenshot_" + Str(index) + ".bmp")
  Next
  ProcedureReturn #Null
EndProcedure

Main()

End
To take a screenshot of the second monitor (first monitor is 0):

Code: Select all

Screenshot(1,"screenshot_test.bmp")
novablue
Enthusiast
Enthusiast
Posts: 165
Joined: Sun Nov 27, 2016 6:38 am

Re: How to take screenshot of second monitor?

Post by novablue »

Thank you it works perfect :D
User avatar
skywalk
Addict
Addict
Posts: 3994
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: How to take screenshot of second monitor?

Post by skywalk »

Curious, it does not work with my 3 monitor setup?
I can only grab the laptop main desktop, 0.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: How to take screenshot of second monitor?

Post by Mijikai »

Try omitting the device\StateFlags check and change @device\DeviceName[0] to PeekS(@device\DeviceName[0],32).
Not sure if it helps i have a multi-monitor setup and for me the code works fine - so i cant really do much.
Where does it hang exactly?
novablue
Enthusiast
Enthusiast
Posts: 165
Joined: Sun Nov 27, 2016 6:38 am

Re: How to take screenshot of second monitor?

Post by novablue »

is it possible to determine on which monitor the mouse currently is?
Marc56us
Addict
Addict
Posts: 1479
Joined: Sat Feb 08, 2014 3:26 pm

Re: How to take screenshot of second monitor?

Post by Marc56us »

novablue wrote: Thu Aug 26, 2021 1:49 pm is it possible to determine on which monitor the mouse currently is?
There are surely simpler ways. In the meantime, it is possible to calculate from the basic functions of PB

Code: Select all

For i = 0 To ExamineDesktops() - 1
  Debug "Display " + i +  " Name: " + DesktopName(i)
  Debug "X start from: " + DesktopX(i) + " To: " + DesktopWidth(i)
  Debug "Y start from: " + DesktopY(i) + " To: " + DesktopHeight(i)
Next

Debug "Mouse is at: " + DesktopMouseX() + "," + DesktopMouseY()
See help: PureBasic - Desktop
i.e: DesktopMouseX()
"Returns the x coordinate (in pixel) of the mouse relative to the top left corner of the primary monitor. The coordinate is negative if the mouse is on a monitor to the left of the primary monitor.".
If second monitor is on right, MouseX > DesktopWidth etc

:wink:
User avatar
skywalk
Addict
Addict
Posts: 3994
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: How to take screenshot of second monitor?

Post by skywalk »

Mijikai wrote: Mon Aug 23, 2021 12:28 am Try omitting the device\StateFlags check and change @device\DeviceName[0] to PeekS(@device\DeviceName[0],32).
Not sure if it helps i have a multi-monitor setup and for me the code works fine - so i cant really do much.
Where does it hang exactly?
Yes, I tried that initially but no love. That's why I posted.
PB ExamineDesktops() commands like in Marc56us code return actual coordinates:

Code: Select all

Procedure dt_Locate()
  Protected.s r$
  For i = 0 To ExamineDesktops() - 1
    r$ + "Display " + i +  " Name: " + DesktopName(i) + #CRLF$
    r$ + "X start from: " + Str(DesktopX(i)) + " To: " + Str(DesktopX(i)+DesktopWidth(i)) + #CRLF$
    r$ + "Y start from: " + Str(DesktopY(i)) + " To: " + Str(DesktopY(i)+DesktopHeight(i)) + #CRLF$
  Next
  r$ + "Mouse is at: " + Str(DesktopMouseX()) + "," + Str(DesktopMouseY())
  MessageRequester("Locate Desktops", r$)
EndProcedure
dt_Locate()
;---------------------------
;Locate Desktops
;---------------------------
;Display 0 Name: \\.\DISPLAY1
;X start from: 0 To: 1920
;Y start from: 0 To: 1080
;
;Display 1 Name: \\.\DISPLAY4
;X start from: 0 To: 1920
;Y start from: -2160 To: -1080
;
;Display 2 Name: \\.\DISPLAY5
;X start from: 0 To: 1920
;Y start from: -1080 To: 0
;
;Mouse is at: 300,138
;---------------------------
;OK   
;---------------------------
The failure after dropping the stat flags check is all 3 versions of EnumDisplaySettings return 0 which is a fail.

Code: Select all

        Debug EnumDisplaySettings_(@device\DeviceName[0],#ENUM_CURRENT_SETTINGS,@mode)
        dt$ = PeekS(@device\DeviceName[0],32)
        Debug EnumDisplaySettings_(@dt$,#ENUM_CURRENT_SETTINGS,@mode)
        Debug EnumDisplaySettings_(@"DISPLAY2",#ENUM_CURRENT_SETTINGS,@mode)
Maybe the prototype for PB internally is not using the unicode version?
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: How to take screenshot of second monitor?

Post by Mijikai »

Thanks for testing :)
Its probably best to make use of Marc56us code.
A alternative starting point could be EnumDisplayMonitors_().
Im still dont know where my error is :?: :?
Irritating is that it only works on some systems...

Unicode Api is fine on PB5.73 LTS x64
novablue
Enthusiast
Enthusiast
Posts: 165
Joined: Sun Nov 27, 2016 6:38 am

Re: How to take screenshot of second monitor?

Post by novablue »

Marc56us wrote: Thu Aug 26, 2021 2:26 pm
novablue wrote: Thu Aug 26, 2021 1:49 pm is it possible to determine on which monitor the mouse currently is?
There are surely simpler ways. In the meantime, it is possible to calculate from the basic functions of PB

Code: Select all

For i = 0 To ExamineDesktops() - 1
  Debug "Display " + i +  " Name: " + DesktopName(i)
  Debug "X start from: " + DesktopX(i) + " To: " + DesktopWidth(i)
  Debug "Y start from: " + DesktopY(i) + " To: " + DesktopHeight(i)
Next

Debug "Mouse is at: " + DesktopMouseX() + "," + DesktopMouseY()
See help: PureBasic - Desktop
i.e: DesktopMouseX()
"Returns the x coordinate (in pixel) of the mouse relative to the top left corner of the primary monitor. The coordinate is negative if the mouse is on a monitor to the left of the primary monitor.".
If second monitor is on right, MouseX > DesktopWidth etc

:wink:
Thank you and how can i find out on which monitor the current active window is on? :D
User avatar
skywalk
Addict
Addict
Posts: 3994
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: How to take screenshot of second monitor?

Post by skywalk »

Yes, the PB Desktop commands worked for me. I have 3 monitors stacked like this:

Code: Select all

[ 3 ] <-- external monitor, 100%DPI
[ 2 ] <-- external monitor, 100%DPI
 [1]  <-- laptop,           125%DPI
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
Post Reply