[B][COLOR=#008000]'\\This code takes a screenshot of a verically scrollable userform[/COLOR][/B]
[B][COLOR=#008000]'\\and create a IPic Object for the *entire* client area.[/COLOR][/B]
[B][COLOR=#008000]'\\The Ipicture Object can then be loaded into a control[/COLOR][/B]
[B][COLOR=#008000]'\\via its Picture Property,copied to the clipboard as a BITMAP[/COLOR][/B]
[B][COLOR=#008000]'\\and pasted to a worksheet for subsequent printing and/or saved to disk as a BMP file.[/COLOR][/B]
[B][COLOR=#008000]'\\Contrary to the native 'PrintForm' Method of the userform object model[/COLOR][/B]
[B][COLOR=#008000]'\\this code permits printing the full scrollable area (Not just the currently visible part of the form)[/COLOR][/B]
[B][COLOR=#008000]'\\Code written by Jaafar Tribak @ MrExcel.com on 24/01/2018.[/COLOR][/B]
Option Explicit
Private Type POINTAPI
x As Long
y As Long
End Type
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type LOGFONT
lfHeight As Long
lfWidth As Long
lfEscapement As Long
lfOrientation As Long
lfWeight As Long
lfItalic As Byte
lfUnderline As Byte
lfStrikeOut As Byte
lfCharSet As Byte
lfOutPrecision As Byte
lfClipPrecision As Byte
lfQuality As Byte
lfPitchAndFamily As Byte
lfFaceName As String * 32
End Type
Private Type uPicDesc
Size As Long
Type As Long
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL] VBA7 Then
hPic As LongPtr
hPal As LongPtr
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL]
hPic As Long
hPal As Long
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL] If
End Type
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(0 To 7) As Byte
End Type
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL] VBA7 Then
Private Declare PtrSafe Function GetDeviceCaps Lib "gdi32" (ByVal hDc As LongPtr, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) As Long
Private Declare PtrSafe Function WindowFromAccessibleObject Lib "oleacc" (ByVal pacc As IAccessible, phwnd As LongPtr) As Long
Private Declare PtrSafe Function GetDC Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
Private Declare PtrSafe Function ReleaseDC Lib "user32" (ByVal hwnd As LongPtr, ByVal hDc As LongPtr) As Long
Private Declare PtrSafe Function CreateCompatibleDC Lib "gdi32" (ByVal hDc As LongPtr) As LongPtr
Private Declare PtrSafe Function CreateCompatibleBitmap Lib "gdi32" (ByVal hDc As LongPtr, ByVal nWidth As Long, ByVal nHeight As Long) As LongPtr
Private Declare PtrSafe Function BitBlt Lib "gdi32" (ByVal hDestDC As LongPtr, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As LongPtr, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Declare PtrSafe Function SelectObject Lib "gdi32" (ByVal hDc As LongPtr, ByVal hObject As LongPtr) As LongPtr
Private Declare PtrSafe Function DeleteObject Lib "gdi32" (ByVal hObject As LongPtr) As Long
Private Declare PtrSafe Function GetWindowRect Lib "user32" (ByVal hwnd As LongPtr, lpRect As RECT) As Long
Private Declare PtrSafe Function ScreenToClient Lib "user32" (ByVal hwnd As LongPtr, lpPoint As POINTAPI) As Long
Private Declare PtrSafe Function OleCreatePictureIndirect64 Lib "oleAut32.dll" Alias "OleCreatePictureIndirect" (PicDesc As uPicDesc, RefIID As GUID, ByVal fPictureOwnsHandle As Long, iPic As IPicture) As Long
Private Declare PtrSafe Function OleCreatePictureIndirect32 Lib "olepro32.dll" Alias "OleCreatePictureIndirect" (PicDesc As uPicDesc, RefIID As GUID, ByVal fPictureOwnsHandle As Long, iPic As IPicture) As Long
Private Declare PtrSafe Function CopyImage Lib "user32" (ByVal handle As LongPtr, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As LongPtr
Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function EmptyClipboard Lib "user32" () As Long
Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long
Private Declare PtrSafe Function SetClipboardData Lib "user32" (ByVal wFormat As Long, ByVal hMem As LongPtr) As LongPtr
Private Declare PtrSafe Function TextOut Lib "gdi32" Alias "TextOutA" (ByVal hDc As LongPtr, ByVal x As Long, ByVal y As Long, ByVal lpString As String, ByVal nCount As Long) As Long
Private Declare PtrSafe Function CreateFontIndirect Lib "gdi32" Alias "CreateFontIndirectA" (lpLogFont As LOGFONT) As LongPtr
Private Declare PtrSafe Function SetBkMode Lib "gdi32" (ByVal hDc As LongPtr, ByVal nBkMode As Long) As Long
Private Declare PtrSafe Function SetTextColor Lib "gdi32" (ByVal hDc As LongPtr, ByVal crColor As Long) As Long
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL]
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 WindowFromAccessibleObject Lib "oleacc" (ByVal pacc As IAccessible, phwnd As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hDc As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hDc As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hDc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hDc As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function ScreenToClient Lib "user32" (ByVal hwnd As Long, lpPoint As POINTAPI) As Long
Private Declare Function OleCreatePictureIndirect64 Lib "oleAut32.dll" Alias "OleCreatePictureIndirect" (PicDesc As uPicDesc, RefIID As GUID, ByVal fPictureOwnsHandle As Long, iPic As IPicture) As Long
Private Declare Function OleCreatePictureIndirect32 Lib "olepro32.dll" Alias "OleCreatePictureIndirect" (PicDesc As uPicDesc, RefIID As GUID, ByVal fPictureOwnsHandle As Long, iPic As IPicture) As Long
Private Declare Function CopyImage Lib "user32" (ByVal handle As Long, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As Long
Private Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function EmptyClipboard Lib "user32" () As Long
Private Declare Function CloseClipboard Lib "user32" () As Long
Private Declare Function SetClipboardData Lib "user32" (ByVal wFormat As Long, ByVal hMem As Long) As Long
Private Declare Function TextOut Lib "gdi32" Alias "TextOutA" (ByVal hDc As Long, ByVal x As Long, ByVal y As Long, ByVal lpString As String, ByVal nCount As Long) As Long
Private Declare Function CreateFontIndirect Lib "gdi32" Alias "CreateFontIndirectA" (lpLogFont As LOGFONT) As Long
Private Declare Function SetBkMode Lib "gdi32" (ByVal hDc As Long, ByVal nBkMode As Long) As Long
Private Declare Function SetTextColor Lib "gdi32" (ByVal hDc As Long, ByVal crColor As Long) As Long
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL] If
Private Const SRCCOPY = &HCC0020
Private Const IMAGE_BITMAP = 0
Private Const PICTYPE_BITMAP = 1
Private Const LR_COPYRETURNORG = &H4
Private Const CF_BITMAP = 2
Private Const S_OK = 0
Private Const LOGPIXELSY = 90
Private Const LOGPIXELSX = 88
Private Const POINTS_PER_INCH = 72
Private Const SM_CYDLGFRAME = 8
Private Const SM_CYCAPTION = 4
Public Function IPictureFromUserForm( _
ByVal UForm As Object, _
Optional ByVal PasteToNewSheetAsShape As Boolean, _
Optional ByVal SaveBmpToFilePathName As String _
) As IPicture
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL] VBA7 Then
Dim hwnd As LongPtr
Dim hDc As LongPtr, hInitDC As LongPtr, hMemDC1 As LongPtr, hMemDC2 As LongPtr
Dim hBmp1 As LongPtr, hBmp2 As LongPtr
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL]
Dim hwnd As Long
Dim hDc As Long, hInitDC As Long, hMemDC1 As Long, hMemDC2 As Long
Dim hBmp1 As Long, hBmp2 As Long
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL] If
Dim Wdth As Long, Hght1 As Long, Hght2 As Long
Dim lInitScrollBarVal As Long
Dim sngVal As Single
Dim sngPrevScrollTop As Single
Dim tFormRect1 As RECT
Dim tFormRect2 As RECT
Dim tPt As POINTAPI
Dim tpt1 As POINTAPI
Dim tpt2 As POINTAPI
On Error GoTo errHandler
With UForm
.ScrollTop = 0
lInitScrollBarVal = .ScrollBars
.ScrollBars = fmScrollBarsVertical
DoEvents
WindowFromAccessibleObject UForm, hwnd
GetWindowRect hwnd, tFormRect1
GetWindowRect hwnd, tFormRect2
With tFormRect1
tpt1.x = .Left: tpt1.y = .Top
tpt2.x = .Right: tpt2.y = .Bottom
ScreenToClient hwnd, tpt1
ScreenToClient hwnd, tpt2
.Left = tpt1.x: .Top = tpt1.y
.Right = tpt2.x: .Bottom = tpt2.y
End With
sngVal = IIf(.ScrollHeight >= .InsideHeight, .ScrollHeight, .InsideHeight)
With tFormRect1
Wdth = .Right - .Left: Hght1 = PTtoPX(CDbl(sngVal), True) + GetSystemMetrics(4) + GetSystemMetrics(8)
Hght2 = .Bottom - .Top
End With
hDc = GetDC(hwnd)
hMemDC1 = CreateCompatibleDC(hDc)
hBmp1 = CreateCompatibleBitmap(hDc, Wdth, Hght1)
DeleteObject SelectObject(hMemDC1, hBmp1)
hInitDC = GetDC(0)
hMemDC2 = CreateCompatibleDC(hInitDC)
hBmp2 = CreateCompatibleBitmap(hInitDC, Wdth, Hght2)
DeleteObject SelectObject(hMemDC2, hBmp2)
Call BitBlt(hMemDC1, 0, 0, Wdth, Hght1, hDc, tFormRect1.Left, tFormRect1.Top, SRCCOPY)
Call ShowCopyingUpdate(hDc)
With tFormRect2: tPt.x = .Left: tPt.y = .Top: End With
Call BitBlt(hMemDC2, 0, 0, Wdth, Hght2, hInitDC, tFormRect2.Left, tFormRect2.Top, SRCCOPY)
sngPrevScrollTop = 0
Do
With tFormRect2
Call BitBlt(hInitDC, .Left, .Top, Wdth, Hght2, hMemDC2, 0, 0, SRCCOPY)
End With
sngPrevScrollTop = .ScrollTop
.Scroll 0, fmScrollActionLineDown
.Repaint
Call BitBlt(hMemDC1, 0, PTtoPX(.ScrollTop, True) + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYDLGFRAME), _
Wdth, Hght1, hDc, tFormRect1.Left, tFormRect1.Top + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYDLGFRAME), SRCCOPY)
Loop Until sngPrevScrollTop >= .ScrollTop
.ScrollBars = lInitScrollBarVal
.ScrollTop = 0
End With
Set IPictureFromUserForm = CreatePicture(hBmp1, PasteToNewSheetAsShape, SaveBmpToFilePathName)
errHandler:
ReleaseDC 0, hInitDC
ReleaseDC 0, hDc
DeleteObject hMemDC1
DeleteObject hMemDC2
DeleteObject hBmp1
DeleteObject hBmp2
End Function
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL] VBA7 Then
Private Sub ShowCopyingUpdate(ByVal hDc As LongPtr)
Dim hNewFont As LongPtr
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL]
Private Sub ShowCopyingUpdate(ByVal hDc As Long)
Dim hNewFont As Long
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL] If
Dim tFont As LOGFONT, sCopying As String
sCopying = "Capturing UserForm Screen ... Please Wait"
With tFont
.lfHeight = 18: .lfFaceName = "Arial" & Chr(0): .lfWeight = 900
End With
hNewFont = (CreateFontIndirect(tFont))
Call DeleteObject(SelectObject(hDc, hNewFont))
SetTextColor hDc, vbRed
SetBkMode hDc, 1
Call TextOut(hDc, 4, 10, sCopying, Len(sCopying))
End Sub
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=If]#If[/URL] VBA7 Then
Private Function CreatePicture(ByVal BMP As LongPtr, Optional ByVal PasteToNewSheetAsShape As Boolean, _
Optional ByVal SaveBmpToFilePathName As String) As IPicture
Dim hCopy As LongPtr
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=Else]#Else[/URL]
Private Function CreatePicture(ByVal BMP As Long, Optional ByVal PasteToNewSheetAsShape As Boolean, _
Optional ByVal SaveBmpToFilePathName As String) As IPicture
Dim hCopy As Long
[URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=End]#End[/URL] If
Dim lRet As Long
Dim IID_IDispatch As GUID
Dim uPicinfo As uPicDesc
Dim iPic As IPicture
hCopy = CopyImage(BMP, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)
With IID_IDispatch
.Data1 = &H20400
.Data4(0) = &HC0
.Data4(7) = &H46
End With
With uPicinfo
.Size = Len(uPicinfo)
.Type = PICTYPE_BITMAP
.hPic = hCopy
.hPal = 0
End With
If InStr(1, Application.OperatingSystem, "32-bit") Then
lRet = OleCreatePictureIndirect32(uPicinfo, IID_IDispatch, True, iPic)
End If
If InStr(1, Application.OperatingSystem, "64-bit") Then
lRet = OleCreatePictureIndirect64(uPicinfo, IID_IDispatch, True, iPic)
End If
If lRet = S_OK Then
Set CreatePicture = iPic
If PasteToNewSheetAsShape Then
If Not ThisWorkbook.ProtectStructure Then
OpenClipboard 0
EmptyClipboard
SetClipboardData CF_BITMAP, BMP
CloseClipboard
ThisWorkbook.Worksheets.Add.Paste
End If
End If
If Len(SaveBmpToFilePathName) Then
SaveBmpToFilePathName = SaveBmpToFilePathName & ".bmp"
stdole.SavePicture iPic, SaveBmpToFilePathName
End If
End If
End Function
Private Function ScreenDPI(bVert As Boolean) As Long
Static lDPI(1), lDC
If lDPI(0) = 0 Then
lDC = GetDC(0)
lDPI(0) = GetDeviceCaps(lDC, LOGPIXELSX)
lDPI(1) = GetDeviceCaps(lDC, LOGPIXELSY)
lDC = ReleaseDC(0, lDC)
End If
ScreenDPI = lDPI(Abs(bVert))
End Function
Private Function PTtoPX(Points As Double, bVert As Boolean) As Long
PTtoPX = Points * ScreenDPI(bVert) / POINTS_PER_INCH
End Function