Reputation: 376
I would like to copy a picture that has been inserted in an Excel spreadsheet to an Image object using VBA. Here is the code I tried:
Dim logo As Image
Set logo = New Image
logo.Picture = ThisWorkbook.Sheets("Sheet1").Pictures("Picture1")
The last line fails with a Type Mismatch error. When I look at logo.Picture in a watch window, it is listed as type Picture; when I assign an Object variable to the expression on the right of the equals sign, it is listed as type Picture/Picture. I'm not familiar enough with the VBA object hierarchy to know whether or not these types are related, nor how to convert from one to the other, and have not been able to find anything about that despite diligent Google searches.
If I replace the last line with this:
logo.Picture = LoadPicture(ThisWorkbook.Path & "\Logo.bmp")
the file loads and the rest of my program works. I have searched many postings here and elsewhere, and have not found anything useful, other than suggestions to export the image to a file and then import it to the Image object using LoadPicture
. Any suggestions of how to get the picture from the worksheet instead of a file?
In case it matters, the rest of the code uses logo.Picture.Handle as a GDI bitmap HANDLE, and passes it to an external library to display the image on an external device. If there is a way to get a GDI bitmap HANDLE from a different object, that would work too.
Upvotes: 5
Views: 12582
Reputation: 1766
This method worked for me:
First, create a new module and paste the PicturesAndShapes code (check below). Then to copy the image from a shape to your Image control, simply use this:
Dim shp As Shape
On Error Resume Next
Set shp = Worksheets("SHEET NAME").Shapes("SHAPE NAME")
If Err.Number = 0 Then
On Error GoTo -1
Logo.Picture = PictureFromShape(shp)
End If
On Error GoTo -1
PicturesAndShapes module:
Option Explicit
Private Const SRCCOPY As Long = &HCC0020
Private Const RC_PALETTE As Long = &H100
Private Const SIZEPALETTE As Long = 104
Private Const RASTERCAPS As Long = 38
Private Type PALETTEENTRY
peRed As Byte
peGreen As Byte
peBlue As Byte
peFlags As Byte
End Type
Private Type LOGPALETTE
palVersion As Integer
palNumEntries As Integer
palPalEntry(255) As PALETTEENTRY ' Enough for 256 colors
End Type
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Private Type PICTDESC
Size As Long
Typ As Long
#If Win64 Then
hPic As LongPtr
hPal As LongPtr
#Else
hPic As Long
hPal As Long
#End If
End Type
#If VBA7 Then
Private Declare PtrSafe Function OleCreatePictureIndirect Lib "oleaut32" ( _
PICDESC As PICTDESC, RefIID As GUID, ByVal fPictureOwnsHandle As Long, _
IPic As IPicture) As Long
#Else
Private Declare Function OleCreatePictureIndirect Lib "oleaut32" ( _
PICDESC As PICTDESC, RefIID As GUID, ByVal fPictureOwnsHandle As Long, _
IPic As IPicture) As Long
#End If
Private Enum PictureType
CF_BITMAP = 2
CF_ENHMETAFILE = 14
End Enum
#If Win64 Then
Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long
Private Declare PtrSafe Function OpenClipboard Lib "user32" ( _
ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function GetClipboardData Lib "user32" ( _
ByVal wFormat As Long) As LongPtr
Private Declare PtrSafe Function IsClipboardFormatAvailable Lib "user32" ( _
ByVal wFormat As Long) As Long
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private 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 LongPtr, ByVal imageType As Long, ByVal NewWidth As Long, _
ByVal NewHeight As Long, ByVal lFlags As Long) As LongPtr
#Else
Private Declare Function CloseClipboard Lib "user32" () As Long
Private Declare Function OpenClipboard Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function GetClipboardData Lib "user32" (ByVal wFormat As Long) As Long
Private Declare Function IsClipboardFormatAvailable Lib "user32" ( _
ByVal wFormat As Long) As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Declare Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" ( _
ByVal hemfSrc As Long, ByVal lpszFile As String) As Long
Private Declare Function CopyImage Lib "user32" ( _
ByVal Handle As Long, ByVal imageType As Long, ByVal NewWidth As Long, _
ByVal NewHeight As Long, ByVal lFlags As Long) As Long
#End If
Public Function PictureFromShape(ByVal s As Shape) As IPicture
If s Is Nothing Then Exit Function
s.CopyPicture xlScreen, xlBitmap
Set PictureFromShape = PictureFromClipboard
End Function
Public Function PictureFromClipboard() As IPicture
'Return a bitmap or metafile picture from clipboard (type is auto detected)
Const IMAGE_BITMAP = 0
Const LR_COPYRETURNORG = &H4
#If VBA7 Then
Dim hPic As LongPtr, hCopy As LongPtr
#Else
Dim hPic As Long, hCopy As Long
#End If
Dim Result As Long, PicType As PictureType
Dim Count As Integer
'Check if the clipboard contains a possible format
If IsClipboardFormatAvailable(CF_BITMAP) <> 0 Then
PicType = CF_BITMAP
ElseIf IsClipboardFormatAvailable(CF_ENHMETAFILE) <> 0 Then
PicType = CF_ENHMETAFILE
End If
If PicType = 0 Then Err.Raise 70, "PictureFromClipboard", "No valid picture in " & _
"clipboard"
'Get access to the clipboard
Do
Result = OpenClipboard(0&)
If Result <> 1 Then
CloseClipboard
DoEvents
Sleep 10
End If
Count = Count + 1
Loop Until Count = 10 Or Result = 1
If Result <> 1 Then Err.Raise 70, "PictureFromClipboard", "Can not open the clipboard"
'Get a handle to the image data
hPic = GetClipboardData(PicType)
If hPic = 0 Then
CloseClipboard
Err.Raise Err.LastDllError, "PictureFromClipboard"
End If
'Create our own copy of the image on the clipboard, in the appropriate format.
If PicType = CF_BITMAP Then
hCopy = CopyImage(hPic, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)
Else
hCopy = CopyEnhMetaFile(hPic, vbNullString)
End If
If hCopy = 0 Then Err.Raise Err.LastDllError, "PictureFromClipboard"
'Release the clipboard to other programs
CloseClipboard
'Convert it into a Picture object and return it
Set PictureFromClipboard = CreatePicture(hCopy, 0, PicType)
End Function
#If VBA7 Then
Private Function CreatePicture(ByVal hPic As LongPtr, ByVal hPal As LongPtr, _
Optional ByVal PicType As PictureType = CF_BITMAP) As IPicture
#Else
Private Function CreatePicture(ByVal hPic As Long, ByVal hPal As Long, _
Optional ByVal PicType As PictureType = CF_BITMAP) As IPicture
#End If
Const PICTYPE_BITMAP As Long = 1
Const PICTYPE_ENHMETAFILE As Long = 4
Dim IPictureIID As GUID
Dim IPic As IPicture
Dim tagPic As PICTDESC
'Fill in the IPicture GUID {7BF80980-BF32-101A-8BBB-00AA00300CAB}
With IPictureIID
.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
'Set the properties on the picture object
With tagPic
.Size = Len(tagPic)
.hPic = hPic
Select Case PicType
Case CF_BITMAP
.Typ = PICTYPE_BITMAP
.hPal = hPal
Case CF_ENHMETAFILE
.Typ = PICTYPE_ENHMETAFILE
.hPal = 0
Case Else
Err.Raise 51, "CreatePicture", "Invalid picture type"
End Select
End With
'Create a picture that will delete it's bitmap when it is finished with it
OleCreatePictureIndirect tagPic, IPictureIID, 1, IPic
If IPic Is Nothing Then Err.Raise Err.LastDllError, "CreatePicture"
Set CreatePicture = IPic
End Function
Upvotes: 1
Reputation: 2825
There is no Image object in the Excel object model, but there is a Picture object (undocumented).
Dim logo As Picture
Set logo = ThisWorkbook.Sheets("Sheet1").Pictures("Picture1")
Upvotes: 0
Reputation: 51
Have you tried inserting it as an OLEObject
?
Me.logo.Picture = ThisWorkbook.Worksheets("Sheet1").OLEObjects("Picture1").Object.Picture
The images you want to copy should be inserted into the worksheet as Image (ActiveX Control).
Upvotes: 1
Reputation: 3206
Define it as an Object
instead and assign your image to it using Set
:
Dim Logo As Object
Set Logo = ThisWorkbook.ActiveSheet.Pictures("Picture 1")
Upvotes: 0