Consulting

Page 1 of 2 1 2 LastLast
Results 1 to 20 of 32

Thread: A Class Module to store Images from a folder for later reuse in Workbook Excel VBA

  1. #1

    A Class Module to store Images from a folder for later reuse in Workbook Excel VBA

    I am importing csv data and images from a folder into a temporary Excel workbook, which is used for readying the data for reporting purposes and resizing the data-related images. I then plan to import this data and corresponding images into my current workbook.


    With ADO, i am able to import the csv data from the temporary workbook into my current workbook, BUT NOT THEIR CORRESPONDING IMAGES to generate different reports.


    I came to know that a Custom Class Module can store objects for later use. My question is, is there a way to store the pictures directly from the folder into a Class Module by the picture name (instead of inserting them into the temporary workbook), for later use for generating reports in current workbook?


    e.g. a Class Module to hold the pictures till the current workbook is open (maintain active session).


    Is this possible?

  2. #2
    VBAX Expert Dave's Avatar
    Joined
    Mar 2005
    Posts
    832
    Location
    Seems similar to your previous query except that U now want to use a temporary wb to store the images. You can load images into a collection for later use however the collection will only exist as long as the temporary wb is open. HTH. Dave

  3. #3
    Hi @Dave,

    If i plan to do away with the temp.xlsx workbook and just load the csvs and images into a Class Module in my Current workbook, so that they can remain in memory till the current workbook is open, how feasible is this to do?

  4. #4
    VBAX Expert Dave's Avatar
    Joined
    Mar 2005
    Posts
    832
    Location
    This is confusing reading both threads on the same topic. I'm not sure why U need a temporary wb?
    As I mentioned, U can load images into a collection that will persist as long as the wb is open.
    To illustrate, create a userform and add an image control and 3 command buttons. Add the following
    code to the userform....
    Private Sub CommandButton1_Click()
    'adjust file path to suit
    UserForm1.Image1.Picture = LoadPicture("C:\TestFolder\SomePic.gif")
    End Sub
    
    Private Sub CommandButton2_Click()
    Set PicCollect = New Collection
    PicCollect.Add UserForm1.Image1.Picture
    End Sub
    Private Sub CommandButton3_Click()
    With UserForm1.Image1
    .Picture = PicCollect(1)
    End With
    End Sub
    Add a module and insert this line of code...
    Public PicCollect As Collection
    To trial change the picture path to a valid picture file path. Show the userform.
    Select command1 to load the picture in the image. Select command2 to load the picture to the collection.
    Unload the userform. Show the userform. Select command3 to load the image from the collection.
    U could loop through all of your pics and load them into the collection and retrieve them as needed.
    HTH. Dave

  5. #5
    Sorry for the confusion @Dave. Just working on a weekend trying to close this issue has made me hanky-panky!

    You mean, do i need to add a Userform inorder to load each image to an image control to store them in my current workbook (if i go with your suggestion of doing away with the temporary workbook)?

    Is there a way to avoid the userform but create an image control instance for each image, store it in a collection and then use it as needed while current workbook is open?

  6. #6
    VBAX Expert Dave's Avatar
    Joined
    Mar 2005
    Posts
    832
    Location
    You will need some method of creating the images to store in the collection. The pic file
    address is just a string. You probably could use a chart or a frame control to load the
    image into. U only need to show the userform/chart/frame once and load the collection.
    Sticking with the previous example, change command2 to the following code
    (Adjust your folder path of images and file extension to suit)....
    Private Sub CommandButton2_Click()
    Dim FSO As Object, FolDir As Object, FileNm As Object
    Set PicCollect = New Collection
    Set FSO = CreateObject("scripting.filesystemobject")
    'change folder to suit
    Set FolDir = FSO.GetFolder("C:\TestFolder")
    For Each FileNm In FolDir.Files
    If FileNm.Name Like "*.gif" Then 'change file extension to suit
    UserForm1.Image1.Picture = LoadPicture("C:\TestFolder\" & FileNm.Name)
    PicCollect.Add UserForm1.Image1.Picture
    End If
    Next FileNm
    Set FSO = Nothing
    Set FolDir = Nothing
    End Sub
    Add a spin control to the userform. Add this code to the userform...
    Dim spincnt As Integer
    Private Sub SpinButton1_SpinDown()
    spincnt = spincnt - 1
    If spincnt < 1 Then
    spincnt = 1
    End If
    With UserForm1.Image1
    .Picture = PicCollect(spincnt)
    End With
    End Sub
    
    Private Sub SpinButton1_SpinUp()
    spincnt = spincnt + 1
    If spincnt > PicCollect.Count Then
    spincnt = PicCollect.Count
    End If
    With UserForm1.Image1
    .Picture = PicCollect(spincnt)
    End With
    End Sub
    To operate, select command2. The spin button will then loop through the pics.
    Unload the userform. Show the userform. The spin button will continue to show
    the pics in the collection. Not sure if this helps but the pics are converted to memory. Dave

  7. #7
    Thanks for the example @Dave. Though it doesn't help with my situation, it does answer one question that i had in mind, i.e. Pictures cannot be imported and stored directly to a collection or dictionary without loading them to some OLE control without which they may not be imported to workbook later. Also, it means Ole controls loaded with pictures cannot be saved to a Dictionary or Collection without a Userform to hold them.

    Appreciate your help here.

  8. #8
    VBAX Expert Dave's Avatar
    Joined
    Mar 2005
    Posts
    832
    Location
    You are welcome. Yes U need some container to first generate the images to store them in the collection after that they are available from the collection to do whatever U want just like any picture file (ie. U don't need the userform to access them). Dave

  9. #9
    You mean if the container is the Image Control, can it be stored in a Dictionary/Collection without the need of a userform?

  10. #10
    VBAX Expert Dave's Avatar
    Joined
    Mar 2005
    Posts
    832
    Location
    No. U initially need some container to generate the images. After U generate the images and the images are stored in a collection, U no longer need the container for anything. Dave

  11. #11
    VBAX Expert Dave's Avatar
    Joined
    Mar 2005
    Posts
    832
    Location
    It's Sunday and I'm bored. Thought maybe I'd blow off some web space to further illustrate
    that it is possible to load pictures to memory for later use. It seems like that is your
    objective (without the use of a userform). All you need is a folder location with pics for
    this to work. It adds the pic to a sheet as a shape, then copies the shape, loads it to the
    collection and then deletes the pic/shape. The pics are loaded in the collection in whatever order
    the files have within the folder. To access the pics from the collection, reference their
    location within the collection eg. UserForm1.Image1.Picture = PicCollect(1)
    Note: the code has some API stuff which requires a 32 bit office installation
    Module code..(Thanks mostly to Jaafar for the creating pic from the clipboard code)
    Public PicCollect As Collection
    'Declare a UDT to store a GUID for the IPicture OLE Interface
        Private Type GUID
            Data1 As Long
            Data2 As Integer
            Data3 As Integer
            Data4(0 To 7) As Byte
        End Type
        
        'Declare a UDT to store the bitmap information
        Private Type uPicDesc
            Size As Long
            Type As Long
            hPic As Long
            hPal As Long
        End Type
        
        'Does the clipboard contain a bitmap/metafile?
        Private Declare Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Integer) As Long
        'Open the clipboard to read
        Public Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
        'Get a pointer to the bitmap/metafile
        Private Declare Function GetClipboardData Lib "user32" (ByVal wFormat As Integer) As Long
        'Close the clipboard
        Public Declare Function CloseClipboard Lib "user32" () As Long
        Public Declare Function EmptyClipboard Lib "user32" () As Long
        'Convert the handle into an OLE IPicture interface.
        Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (PicDesc As uPicDesc, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
        'Create our own copy of the metafile, so it doesn't get wiped out by subsequent clipboard updates.
        Private Declare Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As Long, ByVal lpszFile As String) As Long
        'Create our own copy of the bitmap, so it doesn't get wiped out by subsequent clipboard updates.
        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
        
        'The API format types we're interested in
        Public Const CF_BITMAP = 2
        Private Const CF_PALETTE = 9
        Public Const CF_ENHMETAFILE = 14
        Private Const IMAGE_BITMAP = 0
        Private Const LR_COPYRETURNORG = &H4
        
        'OLE Picture types
        Private Const PICTYPE_BITMAP = 1
        Private Const PICTYPE_ENHMETAFILE = 4
    '======================================================================================================
    Public Function PastePicture(Optional lXlPicType As Long = xlPicture) As IPicture
        Dim h As Long, hPicAvail As Long, hPtr As Long, hPal As Long, lPicType As Long, hCopy As Long
        
        'Convert the type of picture requested from the xl constant to the API constant
        lPicType = IIf(lXlPicType = xlBitmap, CF_BITMAP, CF_ENHMETAFILE)
        'Check if the clipboard contains the required format
        hPicAvail = IsClipboardFormatAvailable(lPicType)
        
        If hPicAvail <> 0 Then
            h = OpenClipboard(0&)   'Get access to the clipboard
            If h > 0 Then
                hPtr = GetClipboardData(lPicType) 'Get a handle to the image data
                'Create our own copy of the image on the clipboard, in the appropriate format.
                If lPicType = CF_BITMAP Then
                    hCopy = CopyImage(hPtr, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)
                Else
                    hCopy = CopyEnhMetaFile(hPtr, vbNullString)
                End If
                
               'clear then close clipboard
                EmptyClipboard
                h = CloseClipboard  'Release the clipboard to other programs
                
                'If we got a handle to the image, convert it into a Picture object and return it
                If hPtr <> 0 Then Set PastePicture = CreatePicture(hCopy, 0, lPicType)
            End If
        End If
    End Function
    Private Function CreatePicture(ByVal hPic As Long, ByVal hPal As Long, ByVal lPicType) As IPicture
        Dim r As Long, uPicInfo As uPicDesc, IID_IDispatch As GUID, IPic As IPicture
        
        ' Create the Interface GUID (for the IPicture interface)
        With IID_IDispatch
            .Data1 = &H7BF80980
            .Data2 = &HBF32
            .Data3 = &H101A
            .Data4(0) = &H8B
            .Data4(1) = &HBB
            .Data4(2) = &H0
            .Data4(3) = &HAA
            .Data4(4) = &H0
            .Data4(5) = &H30
            .Data4(6) = &HC
            .Data4(7) = &HAB
        End With
        
        ' Fill uPicInfo with necessary parts.
        With uPicInfo
            .Size = Len(uPicInfo) ' Length of structure.
            .Type = IIf(lPicType = CF_BITMAP, PICTYPE_BITMAP, PICTYPE_ENHMETAFILE) ' Type of Picture
            .hPic = hPic ' Handle to image.
            .hPal = IIf(lPicType = CF_BITMAP, hPal, 0) ' Handle to palette (if bitmap).
        End With
        
        ' Create the Picture object.
        r = OleCreatePictureIndirect(uPicInfo, IID_IDispatch, True, IPic)
        ' If an error occured, show the description
        If r <> 0 Then Debug.Print "Create Picture Error" ' & fnOLEError(r) 'Requires a reference to the "OLE Automation" type library
        
        Set CreatePicture = IPic    ' Return the new Picture object.
    End Function
    Public Function PicCollection(ShtName As String, Flder As String, Filext As String) As Boolean
    Dim FSO As Object, FolDir As Object, FileNm As Object, Shp As Shape, Ws As Worksheet
    'Call PicCollection("sheet1", "C:\Pftrial", "gif")
    'Loads pics into collection
    On Error GoTo ErFix
    Set PicCollect = New Collection
    Set FSO = CreateObject("scripting.filesystemobject")
    Set FolDir = FSO.GetFolder(Flder)
    Set Ws = ThisWorkbook.Worksheets(ShtName)
    Application.ScreenUpdating = False
    'loop files
    For Each FileNm In FolDir.Files
    If FileNm.Name Like "*." & Filext Then
    Set Shp = Ws.Shapes.AddPicture(Flder & "\" & FileNm.Name, _
          False, True, 100, 100, 1, 1)
    'Use picture'Shp height and width.
    Shp.ScaleHeight 1, msoCTrue
    Shp.ScaleWidth 1, msoCTrue
    Shp.CopyPicture
    PicCollect.Add PastePicture(CF_ENHMETAFILE)
    Shp.Delete
    End If
    Next FileNm
    Application.ScreenUpdating = True
    Set FSO = Nothing
    Set FolDir = Nothing
    PicCollection = True
    Exit Function
    ErFix:
    On Error GoTo 0
    Application.ScreenUpdating = True
    Set FSO = Nothing
    Set FolDir = Nothing
    PicCollection = False
    End Function
    To operate...
    'adjust sheetname, foldername/path and file extension to suit
    If Not PicCollection("sheetname", "C:\foldername", "gif") Then
    MsgBox "Picture collection NOT created!"
    End If
    That's about it for this thread. Dave

  12. #12
    @Dave, i tried the above code but it didnt work on my 64 bit Excel, 64 bit Windows 10 system. :-(

    I finally found a 64 bit code on this same site :

    http://www.vbaexpress.com/forum/show...-picture/page2


    which works sometime and sometimes gives an Error on "CloseClipboard" :

    "1004. Application Defined or Object Defined Error."
    - I am not sure but i think it may be happening because of Clipboard not closing quickly. So added a Do...DoEvents Loop Until H=0. It works most of the time, but still gives same error after loading a couple of Pictures from folder. How to tackle this issue?

    - Once the Dictionary gets populated, i tried running a different Procedure to check if Dictionary was still populated. It maintained its contents until an error occurred. In which case, i have to run the "picollection" procedure again.

    Question: How do i insert the picture from the Dictionary or Collection to my Thisworkbook Sheet?

    Here's the full code i am using. Maybe you will be able to quickly identify the root cause and suggest an appropriate solution :


    Option Explicit
    
    
        Public PicCollect As Object 'Collection
        
        '***************************************************************************'*
        '* AUTHOR & DATE:   STEPHEN BULLEN, Office Automation Ltd
        '*                  15 November 1998
        
        
        '*
        '* DESCRIPTION:     Creates a standard Picture object from whatever is on the clipboard.
        '*                  This object can then be assigned to (for example) and Image control
        '*                  on a userform.  The PastePicture function takes an optional argument of
        '*                  the picture type - xlBitmap or xlPicture.
        '*
        '*                  The code requires a reference to the "OLE Automation" type library
        '*
        '*                  The code in this module has been derived from a number of sources
        '*                  discovered on MSDN.
        '*
        '*                  To use it:
        '*                      Set Image1.Picture = PastePicture(xlPicture)
        '*                  to paste a picture of whatever is on the clipboard into a standard image control.
        '*
        '* PROCEDURES:
        '*   PastePicture   The entry point for the routine
        '*   CreatePicture  Private function to convert a bitmap or metafile handle to an OLE reference
        '*   fnOLEError     Get the error text for an OLE error code
        '***************************************************************************
        'Option Private Module
        Option Compare Text
        
        
        ''' User-Defined Types for API Calls
        
        
        'Declare a UDT to store a GUID for the IPicture OLE Interface
        Private Type guid
            Data1 As Long
            Data2 As Integer
            Data3 As Integer
            Data4(0 To 7) As Byte
        End Type
        
    
    
    '''Windows API Function Declarations
    #If VBA7 Then
        
        
        Private Type uPicDesc
            Size As Long
            Type As Long
            hPic As LongPtr
            hPal As LongPtr
        End Type
        
        
        'Does the clipboard contain a bitmap/metafile?
        Private Declare PtrSafe Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Integer) As Long
        
        
        'Open the clipboard to read
        Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As Long
        
        
        'Get a pointer to the bitmap/metafile
        Private Declare PtrSafe Function GetClipboardData Lib "user32" (ByVal wFormat As Integer) As LongPtr
        
        
        'Close the clipboard
        Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long
        
        
        'Convert the handle into an OLE IPicture interface.
        Private Declare PtrSafe Function OleCreatePictureIndirect Lib "oleaut32.dll" (PicDesc As uPicDesc, RefIID As guid, ByVal fPictureOwnsHandle As LongPtr, IPic As IPicture) As Long
        
        'Create our own copy of the metafile, so it doesn't get wiped out by subsequent clipboard updates.
        Declare PtrSafe Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As LongPtr, ByVal lpszFile As String) As LongPtr
        
        'Create our own copy of the bitmap, so it doesn't get wiped out by subsequent clipboard updates.
        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
    
    
    #Else
        
        'Declare a UDT to store the bitmap information
        Private Type uPicDesc
            Size As Long
            Type As Long
            hPic As Long
            hPal As Long
        End Type
        
        
        'Does the clipboard contain a bitmap/metafile?
        Private Declare Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Integer) As Long
        
        'Open the clipboard to read
        Private Declare Function OpenClipboard Lib "user32" (ByVal hWnd As Long) As Long
        
        'Get a pointer to the bitmap/metafile
        Private Declare Function GetClipboardData Lib "user32" (ByVal wFormat As Integer) As Long
        
        'Close the clipboard
        Private Declare Function CloseClipboard Lib "user32" () As Long
        
        'Convert the handle into an OLE IPicture interface.
        Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (PicDesc As uPicDesc, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
        
        'Create our own copy of the metafile, so it doesn't get wiped out by subsequent clipboard updates.
        Declare Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As Long, ByVal lpszFile As String) As Long
        
        'Create our own copy of the bitmap, so it doesn't get wiped out by subsequent clipboard updates.
        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
    
    
    #End If
    
    
    'The API format types we're interested in
    Const CF_BITMAP = 2
    Const CF_PALETTE = 9
    Const CF_ENHMETAFILE = 14
    Const IMAGE_BITMAP = 0
    Const LR_COPYRETURNORG = &H4
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ''' Subroutine: PastePicture
    '''
    ''' Purpose:    Get a Picture object showing whatever's on the clipboard.
    '''
    ''' Arguments:  lXlPicType - The type of picture to create.  Can be one of:
    '''                          xlPicture to create a metafile (default)
    '''                          xlBitmap to create a bitmap
    '''
    ''' Date        Developer           Action
    ''' ------------------------------------------------------c--------------------
    ''' 30 Oct 98   Stephen Bullen      Created
    ''' 15 Nov 98   Stephen Bullen      Updated to create our own copies of the clipboard images
    
    
    Function PastePicture(Optional lXlPicType As Long = xlPicture) As IPicture
    
    
       #If VBA7 Or Win64 Then
            PastePictureVBA7 (lXlPicType)
       #Else
            PastePictureWin32 (lXlPicType)
       #End If
    
    
    End Function
    
    
    Function PastePictureVBA7(Optional lXlPicType As Long = xlPicture) As IPicture
        'Some pointers
        Dim H As Long, hPicAvail As Long, hPtr As LongPtr, hPal As LongPtr, lPicType As Long, hCopy As LongPtr
        
        'Convert the type of picture requested from the xl constant to the API constant
        lPicType = IIf(lXlPicType = xlBitmap, CF_BITMAP, CF_ENHMETAFILE)
        
        'Check if the clipboard contains the required format
        hPicAvail = IsClipboardFormatAvailable(lPicType)
        
        If hPicAvail <> 0 Then
            'Get access to the clipboard
            H = OpenClipboard(0&)
        
            If H > 0 Then
                'Get a handle to the image data
                hPtr = GetClipboardData(lPicType)
        
                'Create our own copy of the image on the clipboard, in the appropriate format.
                If lPicType = CF_BITMAP Then
                    hCopy = CopyImage(hPtr, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)
                Else
                    hCopy = CopyEnhMetaFile(hPtr, vbNullString)
                End If
        
                'Release the clipboard to other programs.*******HERE I TRIED DOWN CODE BY ADDING DOEVENTS.*******
                Do
                    H = CloseClipboard
                    EmptyClipboard
                    DoEvents
                Loop Until H = 0
        
                'If we got a handle to the image, convert it into a Picture object and return it
                If hPtr <> 0^ Then Set PastePictureVBA7 = CreatePictureVBA7(hCopy, 0, lPicType)
            End If
        End If
    End Function
    
    
    
    
    Function PastePictureWin32(Optional lXlPicType As Long = xlPicture) As IPicture
    
    
    
    
        'Some pointers
        Dim H As Long, hPicAvail As Long, hPtr As Long, hPal As Long, lPicType As Long, hCopy As Long
        
        'Convert the type of picture requested from the xl constant to the API constant
        lPicType = IIf(lXlPicType = xlBitmap, CF_BITMAP, CF_ENHMETAFILE)
        
        'Check if the clipboard contains the required format
        hPicAvail = IsClipboardFormatAvailable(lPicType)
        
        If hPicAvail <> 0 Then
            'Get access to the clipboard
            H = OpenClipboard(0&)
        
            If H > 0 Then
                'Get a handle to the image data
                hPtr = GetClipboardData(lPicType)
        
                'Create our own copy of the image on the clipboard, in the appropriate format.
                If lPicType = CF_BITMAP Then
                    hCopy = CopyImage(hPtr, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)
                Else
                    hCopy = CopyEnhMetaFile(hPtr, vbNullString)
                End If
        
                'Release the clipboard to other programs. *******HERE I TRIED DOWN CODE BY ADDING DOEVENTS.*******
                Do
                    H = CloseClipboard
                    EmptyClipboard
                    DoEvents
                Loop Until H = 0
                
                'If we got a handle to the image, convert it into a Picture object and return it
                If hPtr <> 0 Then Set PastePictureWin32 = CreatePicture(hCopy, 0, lPicType)
            End If
        End If
    
    
    End Function
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ''' Subroutine: CreatePicture
    '''
    ''' Purpose:    Converts a image (and palette) handle into a Picture object.
    '''
    '''             Requires a reference to the "OLE Automation" type library
    '''
    ''' Arguments:  None
    '''
    ''' Date        Developer           Action
    ''' --------------------------------------------------------------------------
    ''' 30 Oct 98  Stephen Bullen      Created
    '''
    Private Function CreatePictureVBA7(ByVal hPic As LongPtr, ByVal hPal As LongPtr, ByVal lPicType) As IPicture
        
        
        ' IPicture requires a reference to "OLE Automation"
        Dim r As Long, uPicInfo As uPicDesc, IID_IDispatch As guid, IPic As IPicture
        
        
        'OLE Picture types
        Const PICTYPE_BITMAP = 1
        Const PICTYPE_ENHMETAFILE = 4
        
        
        ' Create the Interface GUID (for the IPicture interface)
        With IID_IDispatch
            .Data1 = &H7BF80980
            .Data2 = &HBF32
            .Data3 = &H101A
            .Data4(0) = &H8B
            .Data4(1) = &HBB
            .Data4(2) = &H0
            .Data4(3) = &HAA
            .Data4(4) = &H0
            .Data4(5) = &H30
            .Data4(6) = &HC
            .Data4(7) = &HAB
        End With
        
        
        ' Fill uPicInfo with necessary parts.
        With uPicInfo
            .Size = Len(uPicInfo)                                                   ' Length of structure.
            .Type = IIf(lPicType = CF_BITMAP, PICTYPE_BITMAP, PICTYPE_ENHMETAFILE)  ' Type of Picture
            .hPic = hPic                                                            ' Handle to image.
            .hPal = IIf(lPicType = CF_BITMAP, hPal, 0)                              ' Handle to palette (if bitmap).
        End With
             
            ' Create the Picture object.
        r = OleCreatePictureIndirect(uPicInfo, IID_IDispatch, 1, IPic)
        
        
        ' If an error occured, show the description
        If r <> 0 Then Debug.Print "Create Picture: " & fnOLEError(r)
        
        
        ' Return the new Picture object.
        Set CreatePictureVBA7 = IPic
       
    End Function
    
    
    
    
    Private Function CreatePicture(ByVal hPic As Long, ByVal hPal As Long, ByVal lPicType) As IPicture
        
        
        ' IPicture requires a reference to "OLE Automation"
        Dim r As Long, uPicInfo As uPicDesc, IID_IDispatch As guid, IPic As IPicture    
        
        'OLE Picture types
        Const PICTYPE_BITMAP = 1
        Const PICTYPE_ENHMETAFILE = 4    
        
        ' Create the Interface GUID (for the IPicture interface)
        With IID_IDispatch
            .Data1 = &H7BF80980
            .Data2 = &HBF32
            .Data3 = &H101A
            .Data4(0) = &H8B
            .Data4(1) = &HBB
            .Data4(2) = &H0
            .Data4(3) = &HAA
            .Data4(4) = &H0
            .Data4(5) = &H30
            .Data4(6) = &HC
            .Data4(7) = &HAB
        End With
            
        ' Fill uPicInfo with necessary parts.
        With uPicInfo
            .Size = Len(uPicInfo)                                                   ' Length of structure.
            .Type = IIf(lPicType = CF_BITMAP, PICTYPE_BITMAP, PICTYPE_ENHMETAFILE)  ' Type of Picture
            .hPic = hPic                                                            ' Handle to image.
            .hPal = IIf(lPicType = CF_BITMAP, hPal, 0)                              ' Handle to palette (if bitmap).
        End With    
        
        ' Create the Picture object.
        r = OleCreatePictureIndirect(uPicInfo, IID_IDispatch, True, IPic)    
        
        ' If an error occured, show the description
        If r <> 0 Then Debug.Print "Create Picture: " & fnOLEError(r)
        
        ' Return the new Picture object.
        Set CreatePicture = IPic
    End Function
    
    
    
    
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ''' Subroutine: fnOLEError
    '''
    ''' Purpose:    Gets the message text for standard OLE errors
    '''
    ''' Arguments:  None
    '''
    ''' Date        Developer           Action
    ''' --------------------------------------------------------------------------
    ''' 30 Oct 98   Stephen Bullen      Created
    Private Function fnOLEError(lErrNum As Long) As String
        
        
        'OLECreatePictureIndirect return values
        Const E_ABORT = &H80004004
        Const E_ACCESSDENIED = &H80070005
        Const E_FAIL = &H80004005
        Const E_HANDLE = &H80070006
        Const E_INVALIDARG = &H80070057
        Const E_NOINTERFACE = &H80004002
        Const E_NOTIMPL = &H80004001
        Const E_OUTOFMEMORY = &H8007000E
        Const E_POINTER = &H80004003
        Const E_UNEXPECTED = &H8000FFFF
        Const S_OK = &H0
        
        
        Select Case lErrNum
        Case E_ABORT
            fnOLEError = " Aborted"
        Case E_ACCESSDENIED
            fnOLEError = " Access Denied"
        Case E_FAIL
            fnOLEError = " General Failure"
        Case E_HANDLE
            fnOLEError = " Bad/Missing Handle"
        Case E_INVALIDARG
            fnOLEError = " Invalid Argument"
        Case E_NOINTERFACE
            fnOLEError = " No Interface"
        Case E_NOTIMPL
            fnOLEError = " Not Implemented"
        Case E_OUTOFMEMORY
            fnOLEError = " Out of Memory"
        Case E_POINTER
            fnOLEError = " Invalid Pointer"
        Case E_UNEXPECTED
            fnOLEError = " Unknown Error"
        Case S_OK
            fnOLEError = " Success!"
        End Select
    End Function
    
    
    Public Function PicCollection(ShtName As String, Flder As String, Filext As String) As Boolean
    
    
        Dim FSO As Object, FolDir As Object, FileNm As Object, Shp As Shape, Ws As Worksheet
        
        'Call PicCollection("sheet1", "C:\Pftrial", "gif")
        
        'Loads pics into collection
        On Error GoTo ErFix
    '    Set PicCollect = New Collection
        Set PicCollect = CreateObject("Scripting.Dictionary")
        Set FSO = CreateObject("scripting.filesystemobject")
        Set FolDir = FSO.GetFolder(Flder)
        Set Ws = ThisWorkbook.Worksheets(ShtName)
        With Ws
            For Each Shp In .Shapes
                If Shp.Type = msoPicture Then
                    Shp.Delete
                End If
            Next Shp
        End With
        
        Application.ScreenUpdating = False
        'loop files
        For Each FileNm In FolDir.Files
            If FileNm.Name Like "*." & Filext Then
                Set Shp = Ws.Shapes.AddPicture(Flder & "\" & FileNm.Name, False, True, 50, 50, 1, 1)
                
                'Use picture'Shp height and width.
                Shp.ScaleHeight 1, msoCTrue
                Shp.ScaleWidth 1, msoCTrue
                Shp.CopyPicture 'xlScreen, xlBitmap
                
                If Not PicCollect.Exists(FileNm.Name) Then
                    PicCollect.Add key:=FileNm.Name, Item:=PastePicture
                    Shp.Delete
                End If
            End If
        Next FileNm
        
        Application.ScreenUpdating = True
        Set FSO = Nothing
        Set FolDir = Nothing
        PicCollection = True
        Exit Function
    
    
    ErFix:
        MsgBox Err.Number & "_" & Err.Description
        On Error GoTo 0
        Application.ScreenUpdating = True
        Set FSO = Nothing
        Set FolDir = Nothing
        PicCollection = False
        
    End Function
    
    
    
    'To operate...'adjust sheetname, foldername/path and file extension to suit
    Sub Doit()
        Dim strFolder$
        strFolder = "C:\MyPics\"
        If Not PicCollection("PicSht", strFolder, "png") Then MsgBox "Picture collection NOT created!"
    End Sub
    
    
    Sub TestPicsDictionary()
    
        Dim wk As Worksheet, key, i&
        Set wk = ThisWorkbook.Sheets("PicSht")
        
        i = 0
        With wk
            For Each key In PicCollect.Keys
                MsgBox key
                .Shapes.AddPicture PicCollect(key), msoFalse, msoTrue, 50 + i, 50, PicCollect(key).Width, PicCollect(key).Height    ' GET ERROR HERE ALSO AND PicCollect loses images.
                i = i + 50
            Next key
        End With
    End Sub
    Also, when it works,a single IPicture does not show any properties populated!

    PastePictureVBA7_Error.jpg


    P.S: It seems "fnOLEError" is never called!
    Last edited by sifar786; 02-19-2020 at 08:09 AM.

  13. #13
    VBAX Expert Dave's Avatar
    Joined
    Mar 2005
    Posts
    832
    Location
    As I mentioned the code was for 32 bit office installations. The "fnOLEError" is never called as I
    removed that part of the code and in error I kept that line...whoops! Anyways, U can trial my kick at converting
    the declarations to 64 bit. The rest of the code should be the same. I think you will need an image control
    on a worksheet to diplay the images ie. Worksheets("sheet1").Image1.Picture = PicCollect(1)
    Public PicCollect As Collection
    'Declare a UDT to store a GUID for the IPicture OLE Interface
        Private Type GUID
            Data1 As Long
            Data2 As Integer
            Data3 As Integer
            Data4(0 To 7) As Byte
        End Type
        
    #If VBA7 Then
        Type uPicDesc
            Size As Long
            Type As Long
            hPic As LongPtr
            hPal As LongPtr
        End Type
        Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As Long
        Declare PtrSafe Function EmptyClipboard Lib "user32" () As Long
        Declare PtrSafe Function CloseClipboard Lib "user32" () As Long
        Declare PtrSafe Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Long) As Long
        Declare PtrSafe Function GetClipboardData Lib "user32" (ByVal wFormat As Long) As LongPtr
        Declare PtrSafe Function OleCreatePictureIndirect Lib "OleAut32.dll" (PicDesc As uPicDesc, _
                  RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
        Declare PtrSafe Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" ( _
                  ByVal hemfSrc As LongPtr, ByVal lpszFile As String) As LongPtr
        Private Declare PtrSafe 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 LongPtr
        Dim hPtr As LongPtr
    #Else
        Type uPicDesc
            Size As Long
            Type As Long
            hPic As Long
            hPal As Long
        End Type
        Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
        Declare Function EmptyClipboard Lib "user32" () As Long
        Declare Function CloseClipboard Lib "user32" () As Long
        Declare Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Integer) As Long
        Declare Function GetClipboardData Lib "user32" (ByVal wFormat As Integer) As Long
        Declare Function OleCreatePictureIndirect Lib "OleAut32.dll" (PicDesc As uPicDesc, _
                  RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
        Declare Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" _
                  (ByVal hemfSrc As Long, ByVal lpszFile As String) As Long
        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
        Dim hPtr As Long
    #End If
        
        'The API format types we're interested in
        Public Const CF_BITMAP = 2
        Private Const CF_PALETTE = 9
        Public Const CF_ENHMETAFILE = 14
        Private Const IMAGE_BITMAP = 0
        Private Const LR_COPYRETURNORG = &H4
        
        'OLE Picture types
        Private Const PICTYPE_BITMAP = 1
        Private Const PICTYPE_ENHMETAFILE = 4
    To fix the fnOLEError mistake, you can change this...
    If r <> 0 Then
        MsgBox "Create Picture Error"
        End If
    HTH. Dave

  14. #14
    Hi @Dave,

    I checked your declarations and added the missing one i.e. "EmptyClipboard".
        Declare PtrSafe Function EmptyClipboard Lib "user32" () As Long
    Also changed WFormat in both the below declarations from "Integer" to "Long".
    Declare PtrSafe Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Long) As Long
    
    Declare PtrSafe Function GetClipboardData Lib "user32" (ByVal wFormat As Long) As LongPtr
    and also changed the below from LongPtr to Long:
    fPictureOwnsHandle As Long
    in
    Declare PtrSafe Function OleCreatePictureIndirect Lib "OleAut32.dll" (PicDesc As uPicDesc, _ RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
    The code runs and i can see from the watch window that the dictionary items are populated, but show as "Nothing".

    AddPicture error.jpg


    i.e. the IPic in OleCreatePictureIndirect is not getting created correctly. Am i missing something?

    IPic Error.jpg

    How do i add an Image control to a Worksheet? Can you post a working example file demonstrating image control on worksheet?
    Last edited by sifar786; 02-19-2020 at 12:27 PM.

  15. #15
    VBAX Expert Dave's Avatar
    Joined
    Mar 2005
    Posts
    832
    Location
    Get rid of your TestPicsDictionary testing code...the pics are in a collection already so no need for dictionary code. Go to the control toolbox (developer view) and insert/select the active X image control, select your sheet and size the control. Select an Active X spinbutton control then select your sheet. Paste this code to your sheet code. Use the operating code previously posted to load the collection. The spinbutton will loop U through all your pics in the image control. Dave'**change sheet name to suit
    Dim spincnt As IntegerPrivate Sub SpinButton1_SpinDown()spincnt = spincnt - 1If spincnt < 1 Thenspincnt = 1End IfWith Sheets("Sheet1").Image1.Picture = PicCollect(spincnt)End WithEnd SubPrivate Sub SpinButton1_SpinUp()spincnt = spincnt + 1If spincnt > PicCollect.Count Thenspincnt = PicCollect.CountEnd IfWith Sheets("Sheet1").Image1.Picture = PicCollect(spincnt)End WithEnd Sub

  16. #16
    VBAX Expert Dave's Avatar
    Joined
    Mar 2005
    Posts
    832
    Location
    Hmmmmm…..
    Dim spincnt As IntegerPrivate Sub SpinButton1_SpinDown()spincnt = spincnt - 1If spincnt < 1 Thenspincnt = 1End IfWith Sheets("Sheet1").Image1.Picture = PicCollect(spincnt)End WithEnd SubPrivate Sub SpinButton1_SpinUp()spincnt = spincnt + 1If spincnt > PicCollect.Count Thenspincnt = PicCollect.CountEnd IfWith Sheets("Sheet1").Image1.Picture = PicCollect(spincnt)End WithEnd Sub

  17. #17
    Yes Dave, the picture objects seem to be loaded in the Dictionary (or Collection) but do not seem to hold Picture information. Thats what i was mentioning previously.

    So after loading the images once from the folder into Dictionary, when i use the SpinUp or SpinDown it results in an error as shown below:

    Image1 Error.JPG


    Error on
    .Image1.Picture = PicCollect(SpinCnt)Then
    :
    error 13 - TypeMismatch - Runtime error.JPG

  18. #18
    I tried even your Collection Code (just renamed PicCollection to PiczCollection and PicCollect to PiczCollect). Same issue!

    PiczCollection.JPG

    So i manually selected the Image control and then tried adding a PNG picture to the Image Control. Unfortunately, it does not show File Filters for PNG. So i selected "All Files" and then chose a PNG file. The Image control shot up a message saying "INVALID PICTURE". :-(

    EDIT:
    Later i also tried with JPG but still no images loading in Image Control! Somehow IPicture is not getting created. I am not sure what setting it is missing, but i have copied your exact code!
    Last edited by sifar786; 02-20-2020 at 03:15 AM.

  19. #19
    VBAX Expert Dave's Avatar
    Joined
    Mar 2005
    Posts
    832
    Location
    I'll try this again...
    Dim spincnt As Integer
    Private Sub SpinButton1_SpinDown()
    spincnt = spincnt - 1
    If spincnt < 1 Then
    spincnt = 1
    End If
    With Sheets("Sheet1").Image1
    .Picture = PicCollect(spincnt)
    End With
    End Sub
    Private Sub SpinButton1_SpinUp()
    spincnt = spincnt + 1
    If spincnt > PicCollect.Count Then
    spincnt = PicCollect.Count
    End If
    With Sheets("Sheet1").Image1
    .Picture = PicCollect(spincnt)
    End With
    End Sub
    I think U missed the Spincnt variable declaration at the top of the module. Apologies, the site wouldn't let me post the code correctly or edit it last pm. The code I posted works with my trialled .gif files on my 32 bit install. Dave

  20. #20
    VBAX Expert Dave's Avatar
    Joined
    Mar 2005
    Posts
    832
    Location
    Tested with gif, bmp, jpg and png files...they all work. Dave

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •