1

I have a BMP image copied to the clipboard that I will paste into a form object frame control in MS Access (2016).

What I need to know is the memory size that the image takes up on the clipboard (although it would be alright if there was a way to get the size information after it was in the object frame control if necessary). For example, is the image 76,582 bytes or 652 bytes or 942,002 bytes, etc?

Here is the code I use to paste the image from the clipboard into the object frame control:

With Me.imgGrabbedFrame 'Bound Object Frame control
    .Class = "Paintbrush Picture"
    .OLETypeAllowed = acOLEEmbedded
    .Action = acOLEPaste
End With

Is there a way to get the memory taken up by an object on the clipboard?

braX
  • 11,506
  • 5
  • 20
  • 33
DRC
  • 589
  • 2
  • 8
  • 30

2 Answers2

4

You can just calculate the length of the value property of the frame. That's including any OLE meta-information.

LenB(Me.imgGrabbedFrame.Value)

The return value is the length in bytes.

Calculating it while on the clipboard is possible, but substantially more complicated.

To do that, first, let us define several functions to work with the clipboard:

Public Declare PtrSafe Function GetClipboardFormatNameW Lib "User32" (ByVal format As Long, ByVal lpszFormatName As LongPtr, ByVal cchMaxCount As Long) As Long
Public Declare PtrSafe Function OpenClipboard Lib "User32" (Optional ByVal hWndNewOwner As LongPtr) As Boolean
Public Declare PtrSafe Function CloseClipboard Lib "User32" () As Boolean
Public Declare PtrSafe Function EnumClipboardFormats Lib "User32" (ByVal format As Long) As Long
Public Declare PtrSafe Function CountClipboardFormats Lib "User32" () As Long
Public Declare PtrSafe Function GlobalSize Lib "Kernel32" (ByVal hMem As LongPtr) As LongPtr
Public Declare PtrSafe Function GetClipboardData Lib "User32" (ByVal uFormat As Long) As LongPtr

Then, let's define a function to iterate the different formats that are on the clipboard:

Public Sub ListClipboardFormats()
    Dim l As Long
    Dim format As Long
    Dim b As String
    OpenClipboard
    b = String(255, vbNullChar)
    For l = 1 To CountClipboardFormats
        format = EnumClipboardFormats(format)
        GetClipboardFormatNameW format, StrPtr(b), 255
        If Left(b, 1) = vbNullChar Then
            Debug.Print format
        Else
            Debug.Print b
        End If
        b = String(255, vbNullChar)
    Next
    CloseClipboard
End Sub

This function will print all available clipboard formats to the immediate window. If it's a built-in format, it will print a number. You can find the list of numbers here. A common number for image data is 8, for CF_DIB, a device-independent bitmap, or 17 for CF_DIBV5, a more modern variant of the same thing.

If we want to use one of the custom formats, let's define a function to get its number. This function will return 0 in case of an error or a non-existent clipboard format:

Public Function GetClipboardFormatByName(strName As String) As Long
    Dim format As Long
    Dim b As String
    Dim l As Long
    OpenClipboard
    For l = 1 To CountClipboardFormats
        b = String(255, vbNullChar) 'Initialize string buffer
        format = EnumClipboardFormats(format) 'Get next format
        GetClipboardFormatNameW format, StrPtr(b), 255 'Copy name to buffer
        If Left(b, Len(strName)) = strName Then
             GetClipboardFormatByName = format
             Exit Function
        End If
    Next
    CloseClipboard
End Function

Then, the final function to determine the size of what's on the clipboard if we have its format:

Public Function GetClipboardLength(ClipboardFormat As Long) As Long
    OpenClipboard
    Dim hClipboardGlobal As LongPtr
    hClipboardGlobal = GetClipboardData(ClipboardFormat)
    If hClipboardGlobal <> 0 Then
        GetClipboardLength = GlobalSize(hClipboardGlobal)
    End If
    CloseClipboard
End Function

If we know a DIB is on the clipboard, this makes getting it's size as simple as GetClipboardLength(8). Note that this is the size of the DIB, and that might not actually be identical to the size of the data, when pasted, as it's saved, since additional processing might occur.

A note for size on the clipboard: size on the clipboard might be larger or smaller than the size of what you paste depending on the implementation. Programs can (and often will) put multiple representations of the same thing on the clipboard (text, rich text, image, OLE data object), and can also put it there in such a way it's only copied when accessed. That's why, when closing an Office program after copying a large object, you're often asked if you want to keep that object on the clipboard.

Erik A
  • 31,639
  • 12
  • 42
  • 67
  • Thank you @Erik A. LenB always returns a value of 4915301 no matter if I change the size of the control; change the Size Mode to Clip, Zoom, or Stretch; or change the size of the image that I paste into the control. I was expecting a different value for LenB based on the size of the image I pasted into the control. Is it possible to do that? – DRC Jun 27 '19 at 21:07
  • Eh... Do you mean the size of the image in bytes, or in pixels? Changing the size mode changes nothing about the size of the image in bytes, only changing the image will change that – Erik A Jun 28 '19 at 05:16
  • I tried changing both. That is, I changed the size of the image I copied from an image resolution of 1280x960 and then to a very small image of 120x130 pixels. For both images, after pasting them into the frame, LenB returned a value of 4915301. I also changed the size mode to see if that changed how LenB returned a value (thinking perhaps LenB would return the value for the resized image in the frame), but it still returned 4915301 no matter how the size mode was set. Not sure what I'm doing wrong. – DRC Jun 28 '19 at 13:57
  • Are you actually resizing them before pasting, and hard-coding the resize? Unless the actual image data has changed irreversibly, the size is not going to change. – Erik A Jun 28 '19 at 14:07
  • I copy an image to the clipboard in some other app (e.g., Irfanview to copy an image of specific pixel dimensions to the clipboard). I then use imgGrabbedFrame.Action = acOLEPaste to paste the clipboard image into the frame. I then use LenB(imgGrabbedFrameEnd.Value) which always returns 4915301 no matter what size the image is that I had in the clipboard and pasted to the frame. It is clear from the display of the image in the frame that the small image (120x130) expands to fill the frame while the large image (1280x960) shrinks to fit the frame. But, LenB is always the same value. – DRC Jun 28 '19 at 15:46
  • I'm open to hearing the substantially more complicated option for calculating the size on the clipboard since the LenB does not seem to be doing the trick for my purposes. – DRC Jun 28 '19 at 18:41
  • 1
    @DRC It's added now – Erik A Jun 28 '19 at 19:18
  • Thank you, @Erik A. It works for me! Only problem I had was that I initially got an error of "Sub or Function not defined" for the GetClipboardData in the GetClipboardLength function. I added the following additional function declaration and then it worked great!: Public Declare PtrSafe Function GetClipboardData Lib "User32" (ByVal wFormat As Long) As Long – DRC Jun 28 '19 at 20:28
  • 1
    Ah, sorry, forgot that one. You might note that a handle is actually a `LongPtr`, but that generally doesn't cause trouble because [you're allowed to truncate it](https://stackoverflow.com/a/29526711/7296893) – Erik A Jun 28 '19 at 20:35
0

You could use this function with the string value of the image url path in the properties section of the image control

FileLen("C:\Temp\test file.xls")
Doug Coats
  • 6,255
  • 9
  • 27
  • 49
  • Thanks for the thought, @Doug Coats. However, there is no URL path or drive path or anything like that. The BMP is on the clipboard and not a file. For example, a screenshot does not have a URL--it is just sitting in the clipboard in memory. Unless you know a way to get a path to the image on the clipboard? – DRC Jun 27 '19 at 21:08