When you export a shape as image in powerpoint sometimes it generates a white space. As can be seen in this image (with the white font of this webpage cannot be seen but if you download it and open you will see). I need to position this image in a exact place but with this margins I cannot control it well. I need a way to measure those margins to take them in count or it would be even better if I have a way to remove them.
Right now I am measuring how big it is using the following code lines:
#If VBA7 Then
Declare PtrSafe Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Declare PtrSafe Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hdc As Long) As Long
Declare PtrSafe Function GetDeviceCaps Lib "gdi32" (ByVal hdc As Long, ByVal nIndex As Long) As Long
#Else
Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, _
ByVal hdc As Long) As Long
Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As Long, _
ByVal nIndex As Long) As Long
#End If
'here I store the height and width
Hght = GetImageDimensions("c:\dink_template\dinkFile\sizeimage.jpg")(0)
wdt = GetImageDimensions("c:\dink_template\dinkFile\sizeimage.jpg")(1)
Public Function GetImageDimensions(filePath As String) As Variant
'Function returns an array of (Height, Width) from a specific image file path
Dim ImgSize(0 To 1) As Long
'returning picture.width in OLE_YSIZE_HIMETRIC/OLE_XSIZE_HIMETRIC
' these are representing 0.01 mm
With LoadPicture(filePath)
ImgSize(0) = .height / 2540 * (1440 / TwipsPerPixelY())
ImgSize(1) = .width / 2540 * (1440 / TwipsPerPixelX())
End With
GetImageDimensions = ImgSize
End Function
Const HWND_DESKTOP As Long = 0
Const LOGPIXELSX As Long = 88
Const LOGPIXELSY As Long = 90
'--------------------------------------------------
Public Function TwipsPerPixelX() As Single
'--------------------------------------------------
'Returns the width of a pixel, in twips.
'--------------------------------------------------
Dim lngDC As Long
lngDC = GetDC(HWND_DESKTOP)
TwipsPerPixelX = 1440& / GetDeviceCaps(lngDC, LOGPIXELSX)
ReleaseDC HWND_DESKTOP, lngDC
End Function
'--------------------------------------------------
Public Function TwipsPerPixelY() As Single
'--------------------------------------------------
'Returns the height of a pixel, in twips.
'--------------------------------------------------
Dim lngDC As Long
lngDC = GetDC(HWND_DESKTOP)
TwipsPerPixelY = 1440& / GetDeviceCaps(lngDC, LOGPIXELSY)
ReleaseDC HWND_DESKTOP, lngDC
End Function
I compare the height and width that I get from this values with the ones that I get with shape.height and shape.width and get how big is the margin. But I need to know the margin that I have in the left and in the right, in the top and in the bottom.
Related
I have a User Form on Excel with several controls and nested controls that I need to adjust depending on the resolution of the screen.
However after trying several codes to readjust the .Top .Left .Height .Width properties and even the .Font.Size so that the texts in the different controls would keep the same aspect ratio, I was unsuccessful.
After researching this and looking for answers and codes from several different sources I finally was able to write the necessary code to readjust the ratios.
I'm sorry but I'm really unable to cite the different sources because I also got them through a prolonged period of time and on different occasions.
The following code should be on a module of it's own.
Option Explicit
' This module includes Private declarations for certain Windows API functions
' plus code for Public Function Screen, which returns metrics for the screen displaying ActiveWindow
' This module requires VBA7 (Office 2010 or later)
' DEVELOPER: J. Woolley (for wellsr.com)
Private Declare PtrSafe Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) As Long
Private Declare PtrSafe Function GetDeviceCaps Lib "gdi32" (ByVal hDC As LongPtr, ByVal nIndex As Long) 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 GetActiveWindow Lib "user32" () As LongPtr
Private Declare PtrSafe Function MonitorFromWindow Lib "user32" _
(ByVal hWnd As LongPtr, ByVal dwFlags As Long) As LongPtr
Private Declare PtrSafe Function GetMonitorInfo Lib "user32" Alias "GetMonitorInfoA" _
(ByVal hMonitor As LongPtr, ByRef lpMI As MONITORINFOEX) As Boolean
Private Declare PtrSafe Function CreateDC Lib "gdi32" Alias "CreateDCA" _
(ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As LongPtr) As LongPtr
Private Declare PtrSafe Function DeleteDC Lib "gdi32" (ByVal hDC As LongPtr) As Long
Private Const SM_CMONITORS As Long = 80 ' number of display monitors
Private Const MONITOR_CCHDEVICENAME As Long = 32 ' device name fixed length
Private Const MONITOR_PRIMARY As Long = 1
Private Const MONITOR_DEFAULTTONULL As Long = 0
Private Const MONITOR_DEFAULTTOPRIMARY As Long = 1
Private Const MONITOR_DEFAULTTONEAREST As Long = 2
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type MONITORINFOEX
cbSize As Long
rcMonitor As RECT
rcWork As RECT
dwFlags As Long
szDevice As String * MONITOR_CCHDEVICENAME
End Type
Private Enum DevCap ' GetDeviceCaps nIndex (video displays)
HORZSIZE = 4 ' width in millimeters
VERTSIZE = 6 ' height in millimeters
HORZRES = 8 ' width in pixels
VERTRES = 10 ' height in pixels
BITSPIXEL = 12 ' color bits per pixel
LOGPIXELSX = 88 ' horizontal DPI (assumed by Windows)
LOGPIXELSY = 90 ' vertical DPI (assumed by Windows)
COLORRES = 108 ' actual color resolution (bits per pixel)
VREFRESH = 116 ' vertical refresh rate (Hz)
End Enum
'Addition made to this module for UserForm resize through windows API
Private Const GWL_STYLE = -16
Private Const WS_CAPTION = &HC00000
Private Const WS_THICKFRAME = &H40000
#If VBA7 Then
Private Declare PtrSafe Function GetWindowLong _
Lib "user32" Alias "GetWindowLongA" ( _
ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function SetWindowLong _
Lib "user32" Alias "SetWindowLongA" ( _
ByVal hWnd As Long, ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long
Private Declare PtrSafe Function DrawMenuBar _
Lib "user32" (ByVal hWnd As Long) As Long
Private Declare PtrSafe Function FindWindowA _
Lib "user32" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
#Else
Private Declare PtrSafe Function GetWindowLong _
Lib "user32" Alias "GetWindowLongA" ( _
ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function SetWindowLong _
Lib "user32" Alias "SetWindowLongA" ( _
ByVal hWnd As Long, ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long
Private Declare PtrSafe Function DrawMenuBar _
Lib "user32" (ByVal hWnd As Long) As Long
Private Declare PtrSafe Function FindWindowA _
Lib "user32" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
#End If
Public Function Screen(Item As String) As Variant
' Return display screen Item for monitor displaying ActiveWindow
' Patterned after Excel's built-in information functions CELL and INFO
' Supported Item values (each must be a string, but alphabetic case is ignored):
' HorizontalResolution or pixelsX
' VerticalResolution or pixelsY
' WidthInches or inchesX
' HeightInches or inchesY
' DiagonalInches or inchesDiag
' PixelsPerInchX or ppiX
' PixelsPerInchY or ppiY
' PixelsPerInch or ppiDiag
' WinDotsPerInchX or dpiX
' WinDotsPerInchY or dpiY
' WinDotsPerInch or dpiWin ' DPI assumed by Windows
' AdjustmentFactor or zoomFac ' adjustment to match actual size (ppiDiag/dpiWin)
' IsPrimary ' True if primary display
' DisplayName ' name recognized by CreateDC
' Update ' update cells referencing this UDF and return date/time
' Help ' display all recognized Item string values
' EXAMPLE: =Screen("pixelsX")
' Function Returns #VALUE! for invalid Item
Dim xHSizeSq As Double, xVSizeSq As Double, xPix As Double, xDot As Double
Dim hWnd As LongPtr, hDC As LongPtr, hMonitor As LongPtr
Dim tMonitorInfo As MONITORINFOEX
Dim nMonitors As Integer
Dim vResult As Variant
Dim sItem As String
Application.Volatile
nMonitors = GetSystemMetrics(SM_CMONITORS)
If nMonitors < 2 Then
nMonitors = 1 ' in case GetSystemMetrics failed
hWnd = 0
Else
hWnd = GetActiveWindow()
hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL)
If hMonitor = 0 Then
Debug.Print "ActiveWindow does not intersect a monitor"
hWnd = 0
Else
tMonitorInfo.cbSize = Len(tMonitorInfo)
If GetMonitorInfo(hMonitor, tMonitorInfo) = False Then
Debug.Print "GetMonitorInfo failed"
hWnd = 0
Else
hDC = CreateDC(tMonitorInfo.szDevice, 0, 0, 0)
If hDC = 0 Then
Debug.Print "CreateDC failed"
hWnd = 0
End If
End If
End If
End If
If hWnd = 0 Then
hDC = GetDC(hWnd)
tMonitorInfo.dwFlags = MONITOR_PRIMARY
tMonitorInfo.szDevice = "PRIMARY" & vbNullChar
End If
sItem = Trim(LCase(Item))
Select Case sItem
Case "horizontalresolution", "pixelsx" ' HorizontalResolution (pixelsX)
vResult = GetDeviceCaps(hDC, DevCap.HORZRES)
Case "verticalresolution", "pixelsy" ' VerticalResolution (pixelsY)
vResult = GetDeviceCaps(hDC, DevCap.VERTRES)
Case "widthinches", "inchesx" ' WidthInches (inchesX)
vResult = GetDeviceCaps(hDC, DevCap.HORZSIZE) / 25.4
Case "heightinches", "inchesy" ' HeightInches (inchesY)
vResult = GetDeviceCaps(hDC, DevCap.VERTSIZE) / 25.4
Case "diagonalinches", "inchesdiag" ' DiagonalInches (inchesDiag)
vResult = Sqr(GetDeviceCaps(hDC, DevCap.HORZSIZE) ^ 2 + GetDeviceCaps(hDC, DevCap.VERTSIZE) ^ 2) / 25.4
Case "pixelsperinchx", "ppix" ' PixelsPerInchX (ppiX)
vResult = 25.4 * GetDeviceCaps(hDC, DevCap.HORZRES) / GetDeviceCaps(hDC, DevCap.HORZSIZE)
Case "pixelsperinchy", "ppiy" ' PixelsPerInchY (ppiY)
vResult = 25.4 * GetDeviceCaps(hDC, DevCap.VERTRES) / GetDeviceCaps(hDC, DevCap.VERTSIZE)
Case "pixelsperinch", "ppidiag" ' PixelsPerInch (ppiDiag)
xHSizeSq = GetDeviceCaps(hDC, DevCap.HORZSIZE) ^ 2
xVSizeSq = GetDeviceCaps(hDC, DevCap.VERTSIZE) ^ 2
xPix = GetDeviceCaps(hDC, DevCap.HORZRES) ^ 2 + GetDeviceCaps(hDC, DevCap.VERTRES) ^ 2
vResult = 25.4 * Sqr(xPix / (xHSizeSq + xVSizeSq))
Case "windotsperinchx", "dpix" ' WinDotsPerInchX (dpiX)
vResult = GetDeviceCaps(hDC, DevCap.LOGPIXELSX)
Case "windotsperinchy", "dpiy" ' WinDotsPerInchY (dpiY)
vResult = GetDeviceCaps(hDC, DevCap.LOGPIXELSY)
Case "windotsperinch", "dpiwin" ' WinDotsPerInch (dpiWin)
xHSizeSq = GetDeviceCaps(hDC, DevCap.HORZSIZE) ^ 2
xVSizeSq = GetDeviceCaps(hDC, DevCap.VERTSIZE) ^ 2
xDot = GetDeviceCaps(hDC, DevCap.LOGPIXELSX) ^ 2 * xHSizeSq + GetDeviceCaps(hDC, DevCap.LOGPIXELSY) ^ 2 * xVSizeSq
vResult = Sqr(xDot / (xHSizeSq + xVSizeSq))
Case "adjustmentfactor", "zoomfac" ' AdjustmentFactor (zoomFac)
xHSizeSq = GetDeviceCaps(hDC, DevCap.HORZSIZE) ^ 2
xVSizeSq = GetDeviceCaps(hDC, DevCap.VERTSIZE) ^ 2
xPix = GetDeviceCaps(hDC, DevCap.HORZRES) ^ 2 + GetDeviceCaps(hDC, DevCap.VERTRES) ^ 2
xDot = GetDeviceCaps(hDC, DevCap.LOGPIXELSX) ^ 2 * xHSizeSq + GetDeviceCaps(hDC, DevCap.LOGPIXELSY) ^ 2 * xVSizeSq
vResult = 25.4 * Sqr(xPix / xDot)
Case "isprimary" ' IsPrimary
vResult = CBool(tMonitorInfo.dwFlags And MONITOR_PRIMARY)
Case "displayname" ' DisplayName
vResult = tMonitorInfo.szDevice & vbNullChar
vResult = Left(vResult, (InStr(1, vResult, vbNullChar) - 1))
Case "update" ' Update
vResult = Now
Case "help" ' Help
vResult = "HorizontalResolution (pixelsX), VerticalResolution (pixelsY), " _
& "WidthInches (inchesX), HeightInches (inchesY), DiagonalInches (inchesDiag), " _
& "PixelsPerInchX (ppiX), PixelsPerInchY (ppiY), PixelsPerInch (ppiDiag), " _
& "WinDotsPerInchX (dpiX), WinDotsPerInchY (dpiY), WinDotsPerInch (dpiWin), " _
& "AdjustmentFactor (zoomFac), IsPrimary, DisplayName, Update, Help"
Case Else ' Else
vResult = CVErr(xlErrValue) ' return #VALUE! error (2015)
End Select
If hWnd = 0 Then
ReleaseDC hWnd, hDC
Else
DeleteDC hDC
End If
Screen = vResult
End Function
Public Function adjustToRes(UserForm As Object, designScreenWidthPixels As Single, designScreenHeightPixels As Single, _
Optional lowerLimitHeight As Single = 768, Optional lowerLimitWidth As Single = 1024) As Boolean
Dim rateWidth As Double, rateHeight As Double
Dim currentScreenWidth As Single, currentScreenHeight As Single
currentScreenWidth = Screen("pixelsX")
currentScreenHeight = Screen("pixelsY")
If currentScreenHeight < lowerLimitHeight Or currentScreenWidth < lowerLimitWidth Then
adjustToRes = False
Exit Function
End If
rateWidth = currentScreenWidth / designScreenWidthPixels
rateHeight = currentScreenHeight / designScreenHeightPixels
If rateWidth = 1 And rateHeight = 1 Then
adjustToRes = True
Exit Function
End If
With UserForm
If rateHeight > rateWidth Then
.Zoom = .Zoom * rateHeight
Else
.Zoom = .Zoom * rateWidth
End If
.Height = .Height * rateHeight
.Width = .Width * rateWidth
' .ScrollHeight = .ScrollHeight * rateHeight
' .ScrollWidth = .ScrollWidth * rateWidth
End With
adjustToRes = True
End Function
Afterwards you need to use the adjustToRes function on the initialize event of the UserForm.
Private Sub UserForm_Initialize()
Dim adjusted As Boolean
adjusted = adjustToRes(Me, 1920, 1080)
End Sub
The adjustToRes function needs 3 required arguments and has 2 optional ones.
Public Function adjustToRes(UserForm As Object, designScreenWidthPixels As Single, designScreenHeightPixels As Single, _
Optional lowerLimitHeight As Single = 768, Optional lowerLimitWidth As Single = 1024) As Boolean
UserForm is obviously the UserForm object that needs resizing.
designScreenWidthPixels has to be the number of horizontal pixels of the screen for which the UserForm was designed.
For example if the UserForm was created using a screen with resolution of 1920*1080 then
designScreenWidthPixels = 1920
designScreenHeightPixels would then be the number of vertical pixels of the screen for which the UserForm was designed.
In the case of this example that would be 1080.
The optional argument lowerLimitHeight is used to exit the function without any resizing if the vertical resolution of the current screen is less than lowerLimitHeight.
If no argument is provided then by default lowerLimitHeight = 768.
The optional argument lowerLimitWidth does the same thing as lowerLimitHeight but concerning the horizontal resolution of the screen.
If no argument is provided then by default lowerLimitHeight = 1024.
You can of course change this default values if it doesn't suit you.
The function adjustToRes returns False if no resizing was done, otherwise if no resizing was needed or the resizing was successfull then it returns True.
I have an Access 365 64-bit problem I am trying to solve and I need some guidance.
I have a byte array with image data, retrieved from a Base64 encoded string. The decoding is working just fine and I can produce the array as expected. I have had this code from an older 32 bit version of VBA applications and it remains fully functional. So far so good.
The issue comes into play when I am trying to place the image data into a forms image control directly - no saving to a file. I have had this working in the 32 bit applications but now that the office 365 subscription I am working with is 64 bit (as is the rest of the company) I am having trouble converting the API calls and subsequent code across to 64-bit compatible versions. The images are largely PNG and JPG images (when extracted).
The main issue seems to be coming from the need to replace OLEPRO32 with OLEAUT32. The code runs (seemingly) without error however I am not getting any output. I am sure that I am missing something simple but I just cannot see it. My code is below as well as the API declarations I am using, along with the old OLEPRO32 declaration which is commented out.
Option Explicit
Option Compare Database
Declare PtrSafe Function CreateStreamOnHGlobal Lib "ole32" (ByVal hGlobal As LongPtr, ByVal fDeleteOnRelease As Long, ppstm As Any) As LongPtr
Declare PtrSafe Function GlobalAlloc Lib "kernel32" (ByVal uFlags As Long, ByVal dwBytes As Long) As LongPtr
Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
'Declare PtrSafe Function OleLoadPicture Lib "olepro32" (pStream As Any, ByVal lSize As Long, ByVal fRunmode As Long, riid As Any, ppvObj As Any) As LongPtr
Declare PtrSafe Function OleLoadPicture Lib "oleaut32" (pStream As Any, ByVal lSize As Long, ByVal fRunmode As Long, riid As Any, ppvObj As Any) As LongPtr
Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
And here is the routine that uses the declarations:
Public Function ArrayToPicture(inArray() As Byte, Offset As Long, Size As Long) As IPicture
' function creates a stdPicture from the passed array
' Offset is first item in array: 0 for 0 bound arrays
' Size is how many bytes comprise the image
Dim o_hMem As LongPtr
Dim o_lpMem As LongPtr
Dim aGUID(0 To 3) As Long
Dim IIStream As IUnknown
aGUID(0) = &H7BF80980 ' GUID for stdPicture
aGUID(1) = &H101ABF32
aGUID(2) = &HAA00BB8B
aGUID(3) = &HAB0C3000
o_hMem = GlobalAlloc(&H2&, Size)
If Not o_hMem = 0& Then
o_lpMem = GlobalLock(o_hMem)
If Not o_lpMem = 0& Then
CopyMemory ByVal o_lpMem, inArray(Offset), Size
Call GlobalUnlock(o_hMem)
If CreateStreamOnHGlobal(o_hMem, 1&, IIStream) = 0& Then
Call OleLoadPicture(ByVal ObjPtr(IIStream), 0&, 0&, aGUID(0), ArrayToPicture)
End If
End If
End If
End Function
If anyone has any ideas please let me know. The output of this function seems to be nothing at all whereas in the past I could expect a valid iPicture object that could be assigned directly to the form image controls .PictureData.
Any guidance greatly appreciated.
Cheers
The Frog
UPDATE:
I have worked through a large portion of the code and can now specifically isolate the locations where the crashes are happening. Code is below
Option Compare Database
' API declarations
Private Declare PtrSafe Function CreateStreamOnHGlobal Lib "Ole32.dll" (ByRef hGlobal As LongPtr, ByVal fDeleteOnRelease As Long, ByRef ppstm As LongPtr) As Long
Private Declare PtrSafe Function CLSIDFromString Lib "Ole32" (ByVal lpsz As LongPtr, pclsid As Any) As Long
Private Declare PtrSafe Function OLELoadPicture Lib "OleAut32.lib" (ByRef lpStream As LongPtr, ByVal lSize As Long, ByVal fRunMode As Long, ByRef RIID As GUID, ByRef lplpObj As LongPtr) As Long
Private Declare PtrSafe Function GetObject Lib "gdi32" Alias "GetObjectA" (ByVal hObject As LongPtr, ByVal nCount As Long, lpObject As Any) As Long
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 SelectObject Lib "gdi32" (ByVal hdc As LongPtr, ByVal hObject As LongPtr) As LongPtr
Private Declare PtrSafe Function GetDIBits Lib "gdi32" (ByVal aHDC As LongPtr, ByVal hBitmap As LongPtr, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long
Private Declare PtrSafe Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As LongPtr) As LongPtr
Private Declare PtrSafe Function DeleteDC Lib "gdi32" (ByVal hdc As LongPtr) As Long
Private Declare PtrSafe Function GetWindowDC Lib "user32" (ByVal hWnd As LongPtr) As LongPtr
Private Declare PtrSafe Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As LongPtr) As LongPtr
Private Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Private Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As LongPtr) As Long
Private Declare PtrSafe Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
'Necessary Types
Private Type BITMAPINFOHEADER '40 Bytes
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
Private Type RGBQUAD
rgbBlue As Byte
rgbGreen As Byte
rgbRed As Byte
rgbReserved As Byte
End Type
Private Type BITMAPINFO
bmiHeader As BITMAPINFOHEADER
bmiColors As RGBQUAD
End Type
Private Type DIBHEADER '14 magical bytes
BmpIdentification(1) As Byte
BmpSize(3) As Byte
BmpCreator As Integer
BmpCreator2 As Integer
BmpDataOffset(3) As Byte
End Type
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Public Function StdPictureToDibImage(ByRef Picture As StdPicture, Optional PadColor As Integer = -1) As Byte()
' Performs StdPicture to DIB compliant Byte Array
' Adopted based on : http://www.vbforums.com/showthread.php?833125-How-to-convert-StdPicture-into-pixel-array
' The Byte Arrays to Hold the Initial PictureData along with the Final One
Dim ImageData() As Byte
Dim buffer() As Byte
Dim tmp() As Byte
' Type Instances
Dim BMI As BITMAPINFO
Dim DIB As DIBHEADER
Dim PaddingColor As Integer
' API handle
Dim hdc As LongPtr
Dim hpic As LongPtr
hdc = CreateCompatibleDC(0) 'Create a temporary in-memory device context
BMI.bmiHeader.biSize = Len(BMI.bmiHeader) 'Initialize BitmapInfoHeader with header size
'Get the header Info of the Image based on the StdPicture handle provided
GetDIBits hdc, Picture.handle, 0, 0, ByVal 0&, BMI, 0 'Get Information about the image
'Normally here we would setup the header for BMI header but i found out that simply is not working as it should
' Only the DIB header needs manual handling
With DIB
.BmpIdentification(0) = 66
.BmpIdentification(1) = 77
tmp = LongToByteArray(BMI.bmiHeader.biSizeImage + Len(DIB) + Len(BMI.bmiHeader))
.BmpSize(0) = tmp(0)
.BmpSize(1) = tmp(1)
.BmpSize(2) = tmp(2)
.BmpSize(3) = tmp(3)
.BmpCreator = 0
.BmpCreator2 = 0
tmp = LongToByteArray(Len(DIB) + Len(BMI.bmiHeader))
.BmpDataOffset(0) = tmp(0)
.BmpDataOffset(1) = tmp(1)
.BmpDataOffset(2) = tmp(2)
.BmpDataOffset(3) = tmp(3)
End With
'Byte Arrays Initialization
ReDim ImageData(3, BMI.bmiHeader.biWidth - 1, BMI.bmiHeader.biHeight - 1) 'Initialize array for holding pixel data
ReDim buffer(0 To BMI.bmiHeader.biSizeImage + (Len(DIB) + Len(BMI.bmiHeader)) - 1)
'Here we get the actual Image Data from the StdPicture
'This was the most troubled part of the whole process as it kept truncating the image to around 3/4
' no matter what....until i noticed that the info "feeded" to the BMI header was wrong
hpic = CLngPtr(Picture.handle)
GetDIBits hdc, hpic, 0, Abs(BMI.bmiHeader.biHeight), ImageData(0, 0, 0), BMI, 0 'Get pixel data
'GetDIBits hdc, Picture.handle, 0, Abs(BMI.bmiHeader.biHeight), ImageData(0, 0, 0), BMI, 0 'Get pixel data
'Constructing the Final Image Data
'1st the DIB header ***** CRUCIAL ******* , without this everything fails and burns
CopyMemory buffer(0), DIB, Len(DIB)
'2nd the BMP header, this was done in all other cases
CopyMemory buffer(Len(DIB)), BMI.bmiHeader, Len(BMI.bmiHeader)
'3rd the actual image data
CopyMemory buffer(Len(DIB) + Len(BMI.bmiHeader)), ImageData(0, 0, 0), 3 * (BMI.bmiHeader.biWidth - 1) * (BMI.bmiHeader.biHeight - 1)
'Cleaning up
DeleteDC hdc 'Get rid of temporary in-memory device context
'Some Padding to remove the "dead" space because Images Dimensions are "resized" to multiple of 4s
'so if the either of the dimensions is not exactly a multiple of 4 then padding is applied which
'results is "dead" pixels
If PadColor < 0 Then
PaddingColor = 255
Else
PaddingColor = PadColor
End If
For I = UBound(buffer) To LBound(buffer) Step -1
If buffer(I) = 0 Then
buffer(I) = PaddingColor
Else
Exit For
End If
Next
'The final magical byte array...no more temp files,links,extra controls...whatever..everything in memory
StdPictureToDibImage = buffer()
End Function
Public Function PictureFromByteStream(ByRef B() As Byte) As IPicture
Dim LowerBound As Long
Dim ByteCount As Long
Dim hMem As Long
Dim lpMem As Long
Dim IID_IPicture As GUID
Dim istm As stdole.IUnknown
On Error GoTo Err_Init
If UBound(B, 1) < 0 Then
Exit Function
End If
LowerBound = LBound(B)
ByteCount = (UBound(B) - LowerBound) + 1
hMem = GlobalAlloc(&H2, ByteCount)
If hMem <> 0 Then
lpMem = GlobalLock(hMem)
If lpMem <> 0 Then
MoveMemory ByVal lpMem, B(LowerBound), ByteCount
Call GlobalUnlock(hMem)
If CreateStreamOnHGlobal(hMem, 1, istm) = 0 Then
If CLSIDFromString(StrPtr("{7BF80980-BF32-101A-8BBB-00AA00300CAB}"), IID_IPicture) = 0 Then
Call OLELoadPicture(ByVal ObjPtr(istm), ByteCount, 0, IID_IPicture, PictureFromByteStream)
End If
End If
End If
End If
Exit Function
Err_Init:
If err.Number = 9 Then
Debug.Print err.Number & " - " & err.Description
End Function
Function ArrayToStdPicture(imageBytes() As Byte) As StdPicture
Dim W As WIA.Vector
Dim s As StdPicture
Set W = New WIA.Vector
W.BinaryData = imageBytes
Set ArrayToStdPicture = W.Picture
If Not W Is Nothing Then Set W = Nothing
End Function
The process is as follows:
ArrayToStdPicture -> StdPictureToDIBImage
The crash occurs on the line:
GetDIBits hdc, hpic, 0, Abs(BMI.bmiHeader.biHeight), ImageData(0, 0, 0), BMI, 0 'Get pixel data
The crash totally crashes MS Access and kills the application.
Hi I am new to VBA and this seems like a simple problem.
I am trying to get the dimensions of an image in excel and using the GetDeviceCaps function as a result.
I will get a variable not defined compile error regarding the VERTES parameter.
The code I use is the following:
Option Explicit
Private Type POINTAPI
x As Long
y As Long
End Type
Private Declare Function GetPixel Lib "gdi32" (ByVal hDC As Long, _
ByVal x As Long, ByVal y As Long) As Long
Private Declare Function GetCursorPos Lib "user32" _
(lpPoint As POINTAPI) As Long
Private Declare Function GetWindowDC Lib "user32" (ByVal hwnd As Long) _
As Long
Private Declare Function GetDeviceCaps Lib "gdi32" ( _
ByVal hDC As LongPtr _
, ByVal nIndex As Long _
) As LongPtr
Public Sub Test()
Dim tPOS As POINTAPI
Dim AColor As Long
Dim ADC As Long
Dim width As Integer
ADC = GetWindowDC(0)
width = GetDeviceCaps(ADC, VERTRES)
Call GetCursorPos(tPOS)
AColor = GetPixel(ADC, tPOS.x, tPOS.y)
ActiveWindow.Selection.ShapeRange(1).Fill.ForeColor.RGB = 14588691
Debug.Print "width"
Debug.Print width
End Sub
Questions appears to be so basic there isn't anything specific about this online.
I want to get the size of the monitor where the cursor is currently. To do that I took the MonitorFromPoint function to get the monitor from the cursor point.
I have my code down below but it doesn't work properly, it returns 0 when getting the lRight property...
Private Type POINTAPI
X As Long
Y As Long
End Type
Private Type RECT
lLeft As Long
lTop As Long
lRight As Long
lBottom As Long
End Type
Private Type MONITORINFO
cbSize As Integer
rcMonitor As RECT
rcWork As RECT
dwFlags As Integer
End Type
Private Declare PtrSafe Function MonitorFromPoint Lib "user32.dll" (pt As POINTAPI, ByVal dwFlags As LongPtr) As Long
Private Declare PtrSafe Function GetMonitorInfoA Lib "user32.dll" (hMonitor As LongPtr, lpmi As MONITORINFO) As Boolean
Private Declare PtrSafe Function GetCursorPos Lib "user32.dll" (lpPoint As POINTAPI) As LongPtr
Private mPoint As POINTAPI
Sub test()
GetCursorPos mPoint ' Get point from cursor current position
Dim hMonitor As LongPtr, MI As MONITORINFO
hMonitor = MonitorFromPoint(mPoint, 2)
MI.cbSize = Len(MI)
GetMonitorInfoA hMonitor, MI
MsgBox MI.rcMonitor.lRight ' returns 0
End Sub
In some computers I get errors when I use byVal modifier. I had in my code some of them but I manage to remove all of them but I need it in the following code:
Public Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Public Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, _
ByVal hdc As Long) As Long
Public Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As Long, _
ByVal nIndex As Long) As Long
Const HWND_DESKTOP As Long = 0
Const LOGPIXELSX As Long = 88
Const LOGPIXELSY As Long = 90
'--------------------------------------------------
Public Function TwipsPerPixelX() As Single
'--------------------------------------------------
'Returns the width of a pixel, in twips.
'--------------------------------------------------
Dim lngDC As Long
lngDC = GetDC(HWND_DESKTOP)
TwipsPerPixelX = 1440& / GetDeviceCaps(lngDC, LOGPIXELSX)
ReleaseDC HWND_DESKTOP, lngDC
End Function
'--------------------------------------------------
Public Function TwipsPerPixelY() As Single
'--------------------------------------------------
'Returns the height of a pixel, in twips.
'--------------------------------------------------
Dim lngDC As Long
lngDC = GetDC(HWND_DESKTOP)
TwipsPerPixelY = 1440& / GetDeviceCaps(lngDC, LOGPIXELSY)
ReleaseDC HWND_DESKTOP, lngDC
End Function
I am sure that the error is because of ByVal. I was using ByVal in more functions and they were not working but when I do it in another way (not using ByVal) they work and if I just delete this functions all the others works perfectly. I cannot understand the reason.
Another weird thing is that it happens only in some computers (Win8, office 2013). But I have the same OS, office and security settings. The error that it gives is compilation error:
"compilation error in hidden module: Module4"