vba to screen-shot a given range on every worksheet in file

palaeontology

Active Member
Joined
May 12, 2017
Messages
444
Office Version
  1. 2016
Platform
  1. Windows
Is it possible to have code that takes a screen-shot of all sheets named with a 5-digit student ID number (eg: 69213) and save that image as a jpeg (named with the same 5-digit code as the sheet the image came from) in the same folder (called ... 2019) as where the spreadsheet is housed ?

The range (to be screen-shot) on each sheet would be A1:AU36

Each week I'd be running this code again, so is there a way for the code to delete all previously made jpeg image files in that folder and replace them with the new ones, or would I have to have them saved each week as a new sub-file ... perhaps with the word 'images' at the front and the date-stamp after it ... eg: Images 7/02/2019

The address of the folder where the spreadsheet is currently housed is ... D:\A_CHRIS (school)\A_MATHS\HW Data Collection and MailMerge\2019 ... but as this will eventually be used by others in this school on their own laptops, this address will vary considerably for each teacher.

The address of the spreadsheet itself is ... D:\A_CHRIS(school)\A_MATHS\HW Data Collection and MailMerge\2019\[Math Homework Mailmerge Practice.xlsm]

Please let me know if there's anything else I'd need to provide you.

Kind regards,

Chris
 
For giggles, I did a few time tests. As usual, the API method is faster. It is more reliable for many files if it has DoEvents as are the other routines for many files.

Here is how I added the DoEvents for the API routine.
Code:
Sub OBJtoJPGfile(obj As Object, ByVal Filename As String, Optional ByVal Quality As Byte = 100)
  DoEvents
  PicTureToJPGFile CreatePicture(obj), Filename, Quality
End Sub

While I don't like doing scratch things in the production file, some do. Here is a simple chart2 export routine.
Code:
Sub CopyOBJtoFile(obj As Object, fn$)
    Application.ScreenUpdating = False
    obj.CopyPicture xlScreen, xlPicture
    With ActiveSheet.ChartObjects.Add(0, 0, obj.Width, obj.Height)
      .Chart.Paste
      .Chart.Export fn
      .Delete
    End With
    Application.ScreenUpdating = True
End Sub

Here are the time test routines with times commented at the end of each. There are other time routines but these will give you an idea.
Code:
Sub TimeChart2()
  Dim time1#, time2#, i As Integer, r As Range, fn$
  time1 = Timer
  For i = 1 To 100
    Set r = Cells(i, "A")
    fn = ThisWorkbook.Path & "\temp" & r.Address(False, False) & ".jpg"
    DoEvents
    CopyOBJtoFile r, fn
  Next i
  time2 = Timer
  Debug.Print Format(time2 - time1, "0.00 \s\ec")
  '14.62 sec, 13.99 sec, DoEvents: 28.93 sec, 31.66 sec
End Sub

Sub TimeChart1()
  Dim time1#, time2#, i As Integer, r As Range, fn$, wb As Workbook
  time1 = Timer
  For i = 1 To 100
    Set r = Cells(i, "A")
    fn = ThisWorkbook.Path & "\temp" & r.Address(False, False) & ".jpg"
    'Error at file i=18 without DoEvents.
    Set wb = SaveOBJasJPG(r, fn, wb, False)
  Next i
  wb.Close False
  time2 = Timer
  Debug.Print Format(time2 - time1, "0.00 \s\ec")
  '19.44 sec, 19.80 sec, DoEvents: 57.78 sec, 39.16 sec, 57.78 sec, 37.90 sec
End Sub

Sub TimeAPI()
  Dim time1#, time2#, i As Integer, r As Range, fn$
  time1 = Timer
  For i = 1 To 100
    Set r = Cells(i, "A")
    fn = ThisWorkbook.Path & "\temp" & r.Address(False, False) & ".jpg"
    'Errors out on file i=63 without DoEvents in OBJtoJPGfile.
    OBJtoJPGfile r, fn, 93
  Next i
  time2 = Timer
  Debug.Print Format(time2 - time1, "0.00 \s\ec")
  'Quality=93
  '19.82 sec, 20.24 sec, 19.84 sec
  'Quality=50
  '19.43 sec, 24.48 sec (due to windows notification message delay), 20.10 sec
End Sub
 
Last edited:
Upvote 0

Excel Facts

Excel Joke
Why can't spreadsheets drive cars? They crash too often!
@ZVI
Thanks Vlad for the correction but I would just remove the Clng function altogether as it is no longer needed after correcting the declaration of the EncoderParameter structure.

In Sub PictureToJPGFile

Code:
With tParams.Parameter
      CLSIDFromString StrPtr("{1D5BE4B5-FA4A-452D-9CDD-5DB35105E7EB}"), .GUID
      .NumberOfValues = 1
      .Type = 4
[B][COLOR=#0000FF]      .Value = VarPtr(Quality)[/COLOR][/B]
End With

@Kenneth Hobson
Thanks for testing the speed of each method.
 
Last edited:
  • Like
Reactions: ZVI
Upvote 0
Thankyou all for your contribution to this ... I have to say I didn't understand a single word of any of it.

I'll have my IT guys at work run their eyes over what you've all said, but I suspect they won't want to put the time into helping me.

I'll have to come up with a different way to do what I need, but I suspect it'll be a very manual, repetitive copying of the pages.

I'm in awe of what you guys know and understand.

Thankyou again for your help on this,

Kind regards,

Chris
 
Upvote 0
There are many tutorial examples that show how to create a Module in the VBE (Visual Basic Editor). Search for "how to use macros in excel". Then you simply copy/paste the code and run the "main" routine. Some forums have a common thread that explains it. This thread is a good one for new coders: https://www.mrexcel.com/forum/excel...e-line-tutorial-series-beginner-advanced.html

Posts #6 and #9 are total solutions. Post #11 provides a 3rd solution similar to #6 . So, 3 solutions have been shown here.
 
Upvote 0
Thanks Kenneth, I'll definitely try them out.

Thankyou, so much, again, for all your work.

Very kind regards,

Chris
 
Upvote 0
Hi again Kenneth, I ran your #6 post and it works brilliantly, I am so thankful to you.

However, since writing my initial post, I've increased the size of what I need imaged from the range A1:AU36 to A1:AW49 ... I changed that part of your code accordingly, but the images being produced seem to only cover A1:AH36

Is there something else I needed to adjust ?

Kind regards,

Chris
 
Upvote 0
Hi Chris. I see that U went with Ken's code... not sure why Ken's code doesn't cover the whole range unless there's some sort of chart size limitation? Anyways, U never did get a full answer to creating pics of all the sheets and putting them in the same directory as the wb. I'm going to post the API solution based on Jaafar's code and my understanding of the adjustments made by ZVI and Jaafar...
Place in module code....
Rich (BB code):
Option Explicit
Public PFWdApp As Object
Private Type uPicDesc
    Size As Long
    Type As Long
    #If  VBA7 Then
        hPic As LongPtr
        hPal As LongPtr
    #Else 
       hPic As Long
       hPal As Long
    #End  If
End Type

Private Type GUID
   Data1 As Long
   Data2 As Integer
   Data3 As Integer
   Data4(0 To 7) As Byte
End Type

Private Type GdiplusStartupInput
   GdiplusVersion As Long
    #If  VBA7 Then
        DebugEventCallback As LongPtr
        SuppressBackgroundThread As LongPtr
    #Else 
        DebugEventCallback As Long
        SuppressBackgroundThread As Long
    #End  If
   SuppressExternalCodecs As Long
End Type

Private Type EncoderParameter
   GUID As GUID
   NumberOfValues As Long
   Type As Long
   #If  VBA7 Then
    Value As LongPtr
   #Else 
    Value As Long
   #End  If
End Type

Private Type EncoderParameters
   Count As Long
   Parameter As EncoderParameter
End Type


#If  VBA7 Then
    
    Private Declare PtrSafe Function OleCreatePictureIndirectAut Lib "oleAut32.dll" Alias "OleCreatePictureIndirect" (PicDesc As uPicDesc, RefIID As GUID, ByVal fPictureOwnsHandle As Long, iPic As IPicture) As Long
    Private Declare PtrSafe Function OleCreatePictureIndirectPro 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 CloseClipboard Lib "user32" () As Long
    Private Declare PtrSafe Function EmptyClipboard Lib "user32" () As Long
    Private Declare PtrSafe Function GetClipboardData Lib "user32" (ByVal wFormat As Long) As LongPtr
    Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As LongPtr
    Private Declare PtrSafe Function FreeLibrary Lib "kernel32" (ByVal hLibModule As LongPtr) As Long
    
    'GDI+ APIS.
    Private Declare PtrSafe Function GdiplusStartup Lib "GDIPlus" (token As LongPtr, inputbuf As GdiplusStartupInput, Optional ByVal outputbuf As Long = 0) As Long
    Private Declare PtrSafe Function GdiplusShutdown Lib "GDIPlus" (ByVal token As LongPtr) As Long
    Private Declare PtrSafe Function GdipCreateBitmapFromHBITMAP Lib "GDIPlus" (ByVal hbm As LongPtr, ByVal hPal As LongPtr, Bitmap As LongPtr) As Long
    Private Declare PtrSafe Function GdipDisposeImage Lib "GDIPlus" (ByVal Image As LongPtr) As LongPtr
    Private Declare PtrSafe Function GdipSaveImageToFile Lib "GDIPlus" (ByVal Image As LongPtr, ByVal Filename As LongPtr, clsidEncoder As GUID, encoderParams As Any) As Long
    Private Declare PtrSafe Function CLSIDFromString Lib "ole32" (ByVal str As LongPtr, id As GUID) As Long

#Else 

    Private Declare Function OleCreatePictureIndirectAut Lib "oleAut32.dll" Alias "OleCreatePictureIndirect" (PicDesc As uPicDesc, RefIID As GUID, ByVal fPictureOwnsHandle As Long, iPic As IPicture) As Long
    Private Declare Function OleCreatePictureIndirectPro 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 CloseClipboard Lib "user32" () As Long
    Private Declare Function EmptyClipboard Lib "user32" () As Long
    Private Declare Function GetClipboardData Lib "user32" (ByVal wFormat As Long) As Long
    Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
    Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long

    'GDI+ APIS.
    Private Declare Function GdiplusStartup Lib "GDIPlus" (token As Long, inputbuf As GdiplusStartupInput, Optional ByVal outputbuf As Long = 0) As Long
    Private Declare Function GdiplusShutdown Lib "GDIPlus" (ByVal token As Long) As Long
    Private Declare Function GdipCreateBitmapFromHBITMAP Lib "GDIPlus" (ByVal hbm As Long, ByVal hPal As Long, Bitmap As Long) As Long
    Private Declare Function GdipDisposeImage Lib "GDIPlus" (ByVal Image As Long) As Long
    Private Declare Function GdipSaveImageToFile Lib "GDIPlus" (ByVal Image As Long, ByVal Filename As Long, clsidEncoder As GUID, encoderParams As Any) As Long
    Private Declare Function CLSIDFromString Lib "ole32" (ByVal str As Long, id As GUID) As Long

#End  If

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


Public Sub PicTureToJPGFile(ByVal Pict As IPicture, ByVal Filename As String, Optional ByVal Quality As Byte = 100)
   
    #If  VBA7 Then
        Dim lGDIP As LongPtr, lBitmap As LongPtr
    #Else 
        Dim lGDIP As Long, lBitmap As Long
    #End  If

    Dim tSI As GdiplusStartupInput, lRes As Long
    Dim tJpgEncoder As GUID, tParams As EncoderParameters

   tSI.GdiplusVersion = 1
   lRes = GdiplusStartup(lGDIP, tSI)

   If lRes = 0 Then
      lRes = GdipCreateBitmapFromHBITMAP(Pict.handle, 0, lBitmap)
      If lRes = 0 Then
         CLSIDFromString StrPtr("{557CF401-1A04-11D3-9A73-0000F81EF32E}"), tJpgEncoder
         tParams.Count = 1
         With tParams.Parameter
         CLSIDFromString StrPtr("{1D5BE4B5-FA4A-452D-9CDD-5DB35105E7EB}"), .GUID
        .NumberOfValues = 1
        .Type = 4
        .Value = VarPtr(Quality)
        End With
         lRes = GdipSaveImageToFile(lBitmap, StrPtr(Filename), tJpgEncoder, tParams)
         GdipDisposeImage lBitmap
      End If
      GdiplusShutdown lGDIP
   End If
   
   If lRes Then
      Err.Raise 5, , "Cannot save the image. GDI+ Error:" & lRes
   End If
   
End Sub


Public Function CreatePicture(ByVal obj As Object) As IPicture

    #If  VBA7 Then
        Dim hCopy As LongPtr, hPtr As LongPtr, hLib As LongPtr
    #Else 
        Dim hCopy As Long, hPtr As Long, hLib As Long
    #End  If

    Dim IID_IDispatch As GUID, uPicinfo As uPicDesc
    Dim iPic As IPicture, lRet As Long
    
    On Error GoTo errHandler

    obj.CopyPicture Appearance:=xlScreen, Format:=xlBitmap
    OpenClipboard 0
    hPtr = GetClipboardData(CF_BITMAP)
    hCopy = CopyImage(hPtr, 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
    hLib = LoadLibrary("oleAut32.dll")
    If hLib Then
        lRet = OleCreatePictureIndirectAut(uPicinfo, IID_IDispatch, True, iPic)
    Else
        lRet = OleCreatePictureIndirectPro(uPicinfo, IID_IDispatch, True, iPic)
    End If
    FreeLibrary hLib
    If lRet = S_OK Then
        Set CreatePicture = iPic
    End If
errHandler:
    EmptyClipboard
    CloseClipboard
   
    If Err Then
      Err.Raise 5, , "Cannot Create Picture."
     End If
   
End Function

This can be sheet code or whatever...
Rich (BB code):
Sub SheetPics()
Dim Sht As Worksheet
For Each Sht In ActiveWorkbook.Sheets
If IsNumeric(Sht.Name) And Len(Sht.Name) = 5 Then
Call OBJtoJPGfile(Sht.Range("A1:AW49"), ThisWorkbook.Path & "\" & Sht.Name & ".jpg", 100)
End If
Next Sht
End Sub
'Ken's code
Sub OBJtoJPGfile(obj As Object, ByVal Filename As String, Optional ByVal Quality As Byte = 100)
  DoEvents
  PicTureToJPGFile CreatePicture(obj), Filename, Quality
End Sub
To operate...
Rich (BB code):
 Call SheetPics
Seems to work with my testing. Now if I could just figure out how to get the API code to create a picture of a Word document contents. Using the chart method works which makes me think that chart size isn't the problem with your use of Ken's code. HTH. Dave
 
Last edited:
Upvote 0
Whoops... missed the edit.
"Anyways, U never did get a full answer to creating pics of all the sheets and putting them in the same directory as the wb." should have been U never did get a full "API" answer to creating pics of all the sheets as Ken did provide some nice chart code. Dave
 
Upvote 0
Chris, I can not duplicate your problem. The solutions in #6 (chart) and #9 (API), even with your larger range, worked fine. Maybe you did not scroll right when viewing the JPG? Of course I don't have the content that you have in my mostly empty unformatted range.

Dave, either method should work for the Word doc's Range object. If you need help with that, it might be best to ask in a Word forum. You can reference this thread. If you are using some interaction with Excel, then another thread in this forum could be used. e.g. All DOCX filenames: ken.docx, ken2.docx, in A2:A10, make pic of word range to files ken.jpg, ken2.jpg, etc. For Word range, I would make a PDF myself.
 
Upvote 0

Forum statistics

Threads
1,215,093
Messages
6,123,067
Members
449,090
Latest member
fragment

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top