NickVasiliev
04-06-2024, 12:01 PM
Hello all!
I'm trying to create a macro which will automatically capture the screen when shortcut is pressed.
What is really important, I need to have taskbar visible on the screenshot.
I really found helpful code provided by Zack Barresse on this website (cannot post URL).
However, in my case I have users with 2-3 monitors setup. I tried to work with WinAPI and combining different solutions found in internet got a solution.
Option Explicit
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type MONITORINFO
cbSize As Long
rcMonitor As RECT
rcWork As RECT
dwFlags As Long
End Type
Private Declare PtrSafe Function GetForegroundWindow Lib "user32" () As Long
Private Declare PtrSafe Function GetMonitorInfo Lib "user32" Alias "GetMonitorInfoA" (ByVal hMonitor As Long, ByRef lpmi As MONITORINFO) As Long
Private Declare PtrSafe Function MonitorFromWindow Lib "user32" (ByVal hwnd As Long, ByVal dwFlags As Long) As Long
Private Declare PtrSafe Function EnumDisplayMonitors Lib "user32" (ByVal hdc As Long, ByVal lprcClip As Long, ByVal lpfnEnum As Long, ByVal dwData As Long) As Boolean
Private Declare PtrSafe Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare PtrSafe Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare PtrSafe Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare PtrSafe Function BitBlt Lib "gdi32" (ByVal hdcDest As Long, ByVal nXDest As Long, ByVal nYDest As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hdcSrc As Long, ByVal nXSrc As Long, ByVal nYSrc As Long, ByVal dwRop As Long) As Long
Private Declare PtrSafe Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare PtrSafe Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare PtrSafe Function CreateDC Lib "gdi32" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, ByVal lpInitData As Long) As Long
Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
Private Declare PtrSafe Function EmptyClipboard Lib "user32" () As Long
Private Declare PtrSafe Function SetClipboardData Lib "user32" (ByVal wFormat As Long, ByVal hMem As Long) As Long
Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long
Private Declare PtrSafe Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Private Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GetObject Lib "gdi32" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any) As Long
Private Const MONITOR_DEFAULTTOPRIMARY = &H1
Private Const MONITOR_DEFAULTTONEAREST = &H2
Private Const CF_BITMAP = 2
Sub GetActiveWindowMonitorBitmap()
Dim hwnd As Long
Dim hMonitor As Long
Dim mi As MONITORINFO
Dim hDCScreen As Long
Dim hDCMem As Long
Dim hBitmap As Long
Dim hPrevBitmap As Long
Dim ret As Long
Dim hData As Long
' Get the handle of the active window
hwnd = GetForegroundWindow()
' Get the handle of the monitor containing the active window
hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST)
' Initialize the structure's size
mi.cbSize = Len(mi)
' Get monitor information
ret = GetMonitorInfo(hMonitor, mi)
' Check if successful
If ret <> 0 Then
' Create a compatible DC for the screen
hDCScreen = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0&)
If hDCScreen <> 0 Then
' Create a compatible DC for the bitmap
hDCMem = CreateCompatibleDC(hDCScreen)
If hDCMem <> 0 Then
' Create a compatible bitmap
hBitmap = CreateCompatibleBitmap(hDCScreen, mi.rcMonitor.Right - mi.rcMonitor.Left, mi.rcMonitor.Bottom - mi.rcMonitor.Top)
If hBitmap <> 0 Then
' Select the bitmap into the memory DC
hPrevBitmap = SelectObject(hDCMem, hBitmap)
' Copy the screen to the bitmap
ret = BitBlt(hDCMem, 0, 0, mi.rcMonitor.Right - mi.rcMonitor.Left, mi.rcMonitor.Bottom - mi.rcMonitor.Top, hDCScreen, mi.rcMonitor.Left, mi.rcMonitor.Top, &HCC0020)
' Open the clipboard
ret = OpenClipboard(0)
If ret <> 0 Then
' Empty the clipboard
EmptyClipboard
' Set the bitmap data to clipboard
ret = SetClipboardData(CF_BITMAP, hBitmap)
' Close the clipboard
ret = CloseClipboard
End If
' Clean up
DeleteObject hBitmap
Else
MsgBox "Failed to create bitmap."
End If
' Clean up
DeleteDC hDCMem
Else
MsgBox "Failed to create compatible DC for bitmap."
End If
' Clean up
DeleteDC hDCScreen
Else
MsgBox "Failed to create DC for the screen."
End If
Else
MsgBox "Failed to get monitor information."
End If
End Sub
Sub CaptureScreens()
' Loop through each monitor
EnumDisplayMonitors 0, 0, AddressOf EnumMonitorsProc, 0
End Sub
Function EnumMonitorsProc(ByVal hMonitor As Long, ByVal hdcMonitor As Long, ByVal lprcMonitor As Long, ByVal dwData As Long) As Boolean
Dim mi As MONITORINFO
Dim hDCScreen As Long
Dim hDCMem As Long
Dim hBitmap As Long
Dim hPrevBitmap As Long
Dim ret As Long
' Initialize the structure's size
mi.cbSize = Len(mi)
' Get monitor information
ret = GetMonitorInfo(hMonitor, mi)
' Check if successful
If ret <> 0 Then
' Create a compatible DC for the screen
hDCScreen = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0&)
If hDCScreen <> 0 Then
' Create a compatible DC for the bitmap
hDCMem = CreateCompatibleDC(hDCScreen)
If hDCMem <> 0 Then
' Create a compatible bitmap
hBitmap = CreateCompatibleBitmap(hDCScreen, mi.rcMonitor.Right - mi.rcMonitor.Left, mi.rcMonitor.Bottom - mi.rcMonitor.Top)
If hBitmap <> 0 Then
' Select the bitmap into the memory DC
hPrevBitmap = SelectObject(hDCMem, hBitmap)
' Copy the screen to the bitmap
ret = BitBlt(hDCMem, 0, 0, mi.rcMonitor.Right - mi.rcMonitor.Left, mi.rcMonitor.Bottom - mi.rcMonitor.Top, hDCScreen, mi.rcMonitor.Left, mi.rcMonitor.Top, &HCC0020)
' Open the clipboard
ret = OpenClipboard(0)
If ret <> 0 Then
' Empty the clipboard
EmptyClipboard
' Set the bitmap data to clipboard
ret = SetClipboardData(CF_BITMAP, hBitmap)
' Close the clipboard
ret = CloseClipboard
End If
' Clean up
DeleteObject hBitmap
Else
MsgBox "Failed to create bitmap."
End If
' Clean up
DeleteDC hDCMem
Else
MsgBox "Failed to create compatible DC for bitmap."
End If
' Clean up
DeleteDC hDCScreen
Else
MsgBox "Failed to create DC for the screen."
End If
Else
MsgBox "Failed to get monitor information."
End If
' Continue enumeration
EnumMonitorsProc = True
End Function
The problem appears when I use laptop+monitor setup: screenshot from laptop monitor is perfect, but screenshot from secondary monitor is larger than the screen and has black areas around the screenshot.
Do you guys any thoughts on this? Cool if you have a ready solution, but i'll be thankful for just any ideas in what direction I should think or for a proper algorithm.
I'm trying to create a macro which will automatically capture the screen when shortcut is pressed.
What is really important, I need to have taskbar visible on the screenshot.
I really found helpful code provided by Zack Barresse on this website (cannot post URL).
However, in my case I have users with 2-3 monitors setup. I tried to work with WinAPI and combining different solutions found in internet got a solution.
Option Explicit
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type MONITORINFO
cbSize As Long
rcMonitor As RECT
rcWork As RECT
dwFlags As Long
End Type
Private Declare PtrSafe Function GetForegroundWindow Lib "user32" () As Long
Private Declare PtrSafe Function GetMonitorInfo Lib "user32" Alias "GetMonitorInfoA" (ByVal hMonitor As Long, ByRef lpmi As MONITORINFO) As Long
Private Declare PtrSafe Function MonitorFromWindow Lib "user32" (ByVal hwnd As Long, ByVal dwFlags As Long) As Long
Private Declare PtrSafe Function EnumDisplayMonitors Lib "user32" (ByVal hdc As Long, ByVal lprcClip As Long, ByVal lpfnEnum As Long, ByVal dwData As Long) As Boolean
Private Declare PtrSafe Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare PtrSafe Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare PtrSafe Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare PtrSafe Function BitBlt Lib "gdi32" (ByVal hdcDest As Long, ByVal nXDest As Long, ByVal nYDest As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hdcSrc As Long, ByVal nXSrc As Long, ByVal nYSrc As Long, ByVal dwRop As Long) As Long
Private Declare PtrSafe Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare PtrSafe Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare PtrSafe Function CreateDC Lib "gdi32" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, ByVal lpInitData As Long) As Long
Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
Private Declare PtrSafe Function EmptyClipboard Lib "user32" () As Long
Private Declare PtrSafe Function SetClipboardData Lib "user32" (ByVal wFormat As Long, ByVal hMem As Long) As Long
Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long
Private Declare PtrSafe Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Private Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GetObject Lib "gdi32" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any) As Long
Private Const MONITOR_DEFAULTTOPRIMARY = &H1
Private Const MONITOR_DEFAULTTONEAREST = &H2
Private Const CF_BITMAP = 2
Sub GetActiveWindowMonitorBitmap()
Dim hwnd As Long
Dim hMonitor As Long
Dim mi As MONITORINFO
Dim hDCScreen As Long
Dim hDCMem As Long
Dim hBitmap As Long
Dim hPrevBitmap As Long
Dim ret As Long
Dim hData As Long
' Get the handle of the active window
hwnd = GetForegroundWindow()
' Get the handle of the monitor containing the active window
hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST)
' Initialize the structure's size
mi.cbSize = Len(mi)
' Get monitor information
ret = GetMonitorInfo(hMonitor, mi)
' Check if successful
If ret <> 0 Then
' Create a compatible DC for the screen
hDCScreen = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0&)
If hDCScreen <> 0 Then
' Create a compatible DC for the bitmap
hDCMem = CreateCompatibleDC(hDCScreen)
If hDCMem <> 0 Then
' Create a compatible bitmap
hBitmap = CreateCompatibleBitmap(hDCScreen, mi.rcMonitor.Right - mi.rcMonitor.Left, mi.rcMonitor.Bottom - mi.rcMonitor.Top)
If hBitmap <> 0 Then
' Select the bitmap into the memory DC
hPrevBitmap = SelectObject(hDCMem, hBitmap)
' Copy the screen to the bitmap
ret = BitBlt(hDCMem, 0, 0, mi.rcMonitor.Right - mi.rcMonitor.Left, mi.rcMonitor.Bottom - mi.rcMonitor.Top, hDCScreen, mi.rcMonitor.Left, mi.rcMonitor.Top, &HCC0020)
' Open the clipboard
ret = OpenClipboard(0)
If ret <> 0 Then
' Empty the clipboard
EmptyClipboard
' Set the bitmap data to clipboard
ret = SetClipboardData(CF_BITMAP, hBitmap)
' Close the clipboard
ret = CloseClipboard
End If
' Clean up
DeleteObject hBitmap
Else
MsgBox "Failed to create bitmap."
End If
' Clean up
DeleteDC hDCMem
Else
MsgBox "Failed to create compatible DC for bitmap."
End If
' Clean up
DeleteDC hDCScreen
Else
MsgBox "Failed to create DC for the screen."
End If
Else
MsgBox "Failed to get monitor information."
End If
End Sub
Sub CaptureScreens()
' Loop through each monitor
EnumDisplayMonitors 0, 0, AddressOf EnumMonitorsProc, 0
End Sub
Function EnumMonitorsProc(ByVal hMonitor As Long, ByVal hdcMonitor As Long, ByVal lprcMonitor As Long, ByVal dwData As Long) As Boolean
Dim mi As MONITORINFO
Dim hDCScreen As Long
Dim hDCMem As Long
Dim hBitmap As Long
Dim hPrevBitmap As Long
Dim ret As Long
' Initialize the structure's size
mi.cbSize = Len(mi)
' Get monitor information
ret = GetMonitorInfo(hMonitor, mi)
' Check if successful
If ret <> 0 Then
' Create a compatible DC for the screen
hDCScreen = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0&)
If hDCScreen <> 0 Then
' Create a compatible DC for the bitmap
hDCMem = CreateCompatibleDC(hDCScreen)
If hDCMem <> 0 Then
' Create a compatible bitmap
hBitmap = CreateCompatibleBitmap(hDCScreen, mi.rcMonitor.Right - mi.rcMonitor.Left, mi.rcMonitor.Bottom - mi.rcMonitor.Top)
If hBitmap <> 0 Then
' Select the bitmap into the memory DC
hPrevBitmap = SelectObject(hDCMem, hBitmap)
' Copy the screen to the bitmap
ret = BitBlt(hDCMem, 0, 0, mi.rcMonitor.Right - mi.rcMonitor.Left, mi.rcMonitor.Bottom - mi.rcMonitor.Top, hDCScreen, mi.rcMonitor.Left, mi.rcMonitor.Top, &HCC0020)
' Open the clipboard
ret = OpenClipboard(0)
If ret <> 0 Then
' Empty the clipboard
EmptyClipboard
' Set the bitmap data to clipboard
ret = SetClipboardData(CF_BITMAP, hBitmap)
' Close the clipboard
ret = CloseClipboard
End If
' Clean up
DeleteObject hBitmap
Else
MsgBox "Failed to create bitmap."
End If
' Clean up
DeleteDC hDCMem
Else
MsgBox "Failed to create compatible DC for bitmap."
End If
' Clean up
DeleteDC hDCScreen
Else
MsgBox "Failed to create DC for the screen."
End If
Else
MsgBox "Failed to get monitor information."
End If
' Continue enumeration
EnumMonitorsProc = True
End Function
The problem appears when I use laptop+monitor setup: screenshot from laptop monitor is perfect, but screenshot from secondary monitor is larger than the screen and has black areas around the screenshot.
Do you guys any thoughts on this? Cool if you have a ready solution, but i'll be thankful for just any ideas in what direction I should think or for a proper algorithm.