-
Mar 15th, 2021, 02:06 PM
#1
Thread Starter
Addicted Member
Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-2021
Oh, this is a very annoying one.
With the latest Windows 10 update (start of 03-2021) MS has screwed up something with printers as I see also a lot of problems with BSOD when printing to certain printers since that update.
But one other problem seems to be with the Device Context.
When I try to BitBlt/StretchBlt a stdPicture based bitmap to an EnhancedMetafile Device Context (EMDC), which was created based on a Printer.hDC, it doesn't render the bitmap anymore. It worked flawlessly before the windows update for over 19 years.
As a test I saved the bitmap image as an EMF file (with photoshop/paintshop/whatever) and then used PlayEnhMetaFile into the EMDC and that still works.
If I create the EMDC for instance based on the Form.hDC the old code still works, so it seems to be a problem with the DeviceContext in relation to being created based on the Printer.hDC.
I have added an example, just start a default Exe project with only Form1 and paste the following into the Code module:
Make sure you have an image "C:\Temp\MyTestBitmap.bmp" (my original test image has a resolution of 876x600) or replace the string in the code with your location (can also be a JPG).
For the sake of simplicity I have hardcoded widths/Heights etc
What the code does is:
-Load the image into a stdPicture object
-Render the image to the Form (so you know the image is properly loaded)
-Set the base DC which is used to create the EnhancedMetaFileDeviceContext (Printer.hDC doesn't render the image (but worked before the windows update), Me.hDC does render the image)
-Create a new EnhancedMetaFileDeviceContext
-Render the image to the EnhancedMetaFileDeviceContext
-Draw an Ellipse so we have something else on the EnhancedMetaFileDeviceContext
-Close the EnhancedMetafileDeviceContext
-Render the EnancedMetaFile on top of the Form
-Save the EnhancedMetafile to a file "C:\Temp\MyMetafile<currenttime:yyyymmdd_HH_MM_SS>.emf"
Code:
Option Explicit
Private Const SRCCOPY As Long = &HCC0020
Private Const MM_ANISOTROPIC As Long = 8
Private Const OPAQUE As Long = 2
Private Const TRANSPARENT As Long = 1
Private Type BITMAP
bmType As Long
bmWidth As Long
bmHeight As Long
bmWidthBytes As Long
bmPlanes As Integer
bmBitsPixel As Integer
bmBits As Long
End Type
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function CloseEnhMetaFile Lib "gdi32.dll" (ByVal hDC As Long) As Long
Private Declare Function CopyEnhMetaFile Lib "gdi32.dll" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As Long, ByVal lpszFile As String) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hDC As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
Private Declare Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As Long, lpInitData As Long) As Long
Private Declare Function CreateEnhMetaFile Lib "gdi32.dll" Alias "CreateEnhMetaFileA" (ByVal hdcRef As Long, ByVal lpFileName As String, lpRect As Any, ByVal lpDescription As String) As Long
Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
Private Declare Function DeleteEnhMetaFile Lib "gdi32.dll" (ByVal hemf As Long) As Long
Private Declare Function Ellipse Lib "gdi32.dll" (ByVal hDC As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function GetGDIObject Lib "gdi32.dll" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any) As Long
Private Declare Function PlayEnhMetaFile Lib "gdi32.dll" (ByVal hDC As Long, ByVal hemf As Long, lpRect As RECT) As Long
Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hDC As Long, ByVal hObject As Long) As Long
Private Declare Function SetBkMode Lib "gdi32.dll" (ByVal hDC As Long, ByVal nBkMode As Long) As Long
Private Declare Function SetMapMode Lib "gdi32.dll" (ByVal hDC As Long, ByVal nMapMode As Long) As Long
Private Declare Function SetViewportExtEx Lib "gdi32.dll" (ByVal hDC As Long, ByVal nX As Long, ByVal nY As Long, lpSize As Any) As Long
Private Declare Function SetWindowExtEx Lib "gdi32.dll" (ByVal hDC As Long, ByVal nX As Long, ByVal nY As Long, lpSize As Any) As Long
Private Declare Function StretchBlt Lib "gdi32.dll" (ByVal hDC As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, ByVal dwRop As Long) As Long
Private Sub pCreateMetaFile()
Dim bm As BITMAP
Dim hdcBase As Long
Dim hdcBitmap As Long
Dim hdcMetaFile As Long
Dim hMetaFile As Long
Dim lPrintableArea_Height As Long
Dim lPrintableArea_Width As Long
Dim lReturn As Long
Dim picTest As StdPicture
Dim tRect As RECT
'--------------------------------------------------------------
'Get Picture (info)
Set picTest = LoadPicture("C:\Temp\MyTestBitmap.bmp")
Call picTest.Render(Me.hDC, 0, 0, Me.ScaleWidth / Screen.TwipsPerPixelX, Me.ScaleHeight / Screen.TwipsPerPixelY, 0, 0, picTest.Width, picTest.Height, ByVal 0&)
'--------------------------------------------------------------
'Setup Enhanced Metafile
lPrintableArea_Width = 20989 'Himetric
lPrintableArea_Height = 29705 'Himetric
With tRect
.Left = 0
.Right = lPrintableArea_Width
.Top = 0
.Bottom = lPrintableArea_Height
End With
hdcBase = Printer.hDC
'hdcBase = Me.hDC
hdcMetaFile = CreateEnhMetaFile(hdcBase, vbNullString, tRect, _
App.EXEName & vbNullChar & "MyExtraTest" & vbNullChar & vbNullChar)
If hdcMetaFile = 0 Then
Call MsgBox("No DeviceContext available for Metafile.", vbError)
Else 'If hdcMetaFile=
'-------------------------------------------------------
'Set some Default settings
lReturn = SetMapMode(hdcMetaFile, MM_ANISOTROPIC)
lReturn = SetWindowExtEx(hdcMetaFile, _
lPrintableArea_Width, lPrintableArea_Height, _
ByVal 0&)
lReturn = SetViewportExtEx(hdcMetaFile, _
lPrintableArea_Width, _
lPrintableArea_Height, _
ByVal 0&)
lReturn = SetBkMode(hdcMetaFile, TRANSPARENT)
'--------------------------------------------------------------
'Draw picture to metafile
lReturn = GetGDIObject(picTest.Handle, Len(bm), bm)
hdcBitmap = CreateCompatibleDC(hdcMetaFile)
lReturn = SelectObject(hdcBitmap, picTest.Handle)
lReturn = StretchBlt(hdcMetaFile, 10, 10, 200, 100, _
hdcBitmap, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY)
lReturn = DeleteDC(hdcBitmap)
'--------------------------------------------------------------
'Draw ellipse
lReturn = Ellipse(hdcMetaFile, 100, 100, 300, 200)
'--------------------------------------------------------------
'Close metafileDC
hMetaFile = CloseEnhMetaFile(hdcMetaFile)
'--------------------------------------------------------------
'Draw to form
tRect.Left = 0
tRect.Top = 0
tRect.Right = Me.ScaleWidth \ 2 'Me.ScaleX(Me.ScaleWidth, vbTwips, vbHimetric)
tRect.Bottom = Me.ScaleHeight \ 2 ' Me.ScaleX(Me.ScaleHeight, vbTwips, vbHimetric)
lReturn = PlayEnhMetaFile(Me.hDC, hMetaFile, tRect)
'--------------------------------------------------------------
'Save metafile
lReturn = CopyEnhMetaFile(hMetaFile, "C:\Temp\MyMetafile" & Format(Now, "yyyymmdd_HH_MM_SS") & ".emf")
If lReturn <> 0 Then Call DeleteEnhMetaFile(lReturn)
End If
End Sub
I hope someone has found a workaround (without having to resort to not using the Printer.hDC) of rendering the image to the EnhancedMetaFileDeviceContext.
Only workaround I can think of is actually just creating a new EnhancedMetaFileDeviceContext for the image alone, not based on the Printer.hDC and use PlayEnhMetaFile to the EnhancedMetaFileDeviceContext based on the Printer.hDC. But that's not really ideal.
Edited March 18 2021
The following is (thanx to Olaf for pointing me to StretchDIBits) a solution for my problem for now:
Code:
Option Explicit
Private Const SRCCOPY As Long = &HCC0020
Private Const MM_ANISOTROPIC As Long = 8
Private Const OPAQUE As Long = 2
Private Const TRANSPARENT As Long = 1
Private Const BI_RGB As Long = 0
Private Const DIB_RGB_COLORS As Long = 0
Private Type BITMAP
bmType As Long
bmWidth As Long
bmHeight As Long
bmWidthBytes As Long
bmPlanes As Integer
bmBitsPixel As Integer
bmBits As Long
End Type
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(1) As RGBQUAD
End Type
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function CloseEnhMetaFile Lib "gdi32.dll" (ByVal hDC As Long) As Long
Private Declare Function CopyEnhMetaFile Lib "gdi32.dll" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As Long, ByVal lpszFile As String) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hDC As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
Private Declare Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As Long, lpInitData As Long) As Long
Private Declare Function CreateEnhMetaFile Lib "gdi32.dll" Alias "CreateEnhMetaFileA" (ByVal hdcRef As Long, ByVal lpFileName As String, lpRect As Any, ByVal lpDescription As String) As Long
Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
Private Declare Function DeleteEnhMetaFile Lib "gdi32.dll" (ByVal hemf As Long) As Long
Private Declare Function Ellipse Lib "gdi32.dll" (ByVal hDC As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function GetDIBits Lib "gdi32.dll" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, ByRef lpBits As Any, ByRef lpbi As BITMAPINFO, ByVal wUsage As Long) As Long
Private Declare Function GetGDIObject Lib "gdi32.dll" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any) As Long
Private Declare Function PlayEnhMetaFile Lib "gdi32.dll" (ByVal hDC As Long, ByVal hemf As Long, lpRect As RECT) As Long
Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hDC As Long, ByVal hObject As Long) As Long
Private Declare Function SetBkMode Lib "gdi32.dll" (ByVal hDC As Long, ByVal nBkMode As Long) As Long
Private Declare Function SetMapMode Lib "gdi32.dll" (ByVal hDC As Long, ByVal nMapMode As Long) As Long
Private Declare Function SetViewportExtEx Lib "gdi32.dll" (ByVal hDC As Long, ByVal nX As Long, ByVal nY As Long, lpSize As Any) As Long
Private Declare Function SetWindowExtEx Lib "gdi32.dll" (ByVal hDC As Long, ByVal nX As Long, ByVal nY As Long, lpSize As Any) As Long
Private Declare Function StretchBlt Lib "gdi32.dll" (ByVal hDC As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, ByVal dwRop As Long) As Long
Private Declare Function StretchDIBits Lib "gdi32.dll" (ByVal hDC As Long, ByVal X As Long, ByVal Y As Long, ByVal dX As Long, ByVal dY As Long, ByVal SrcX As Long, ByVal SrcY As Long, ByVal wSrcWidth As Long, ByVal wSrcHeight As Long, ByRef lpBits As Any, ByRef lpBitsInfo As BITMAPINFO, ByVal wUsage As Long, ByVal dwRop As Long) As Long
Private Sub pCreateMetaFile()
Dim abBuffer() As Byte
'Dim bm As BITMAP
Dim bmi As BITMAPINFO
Dim hdcBase As Long
Dim hdcBitmap As Long
Dim hdcMetaFile As Long
Dim hMetaFile As Long
Dim lPrintableArea_Height As Long
Dim lPrintableArea_Width As Long
Dim lReturn As Long
Dim picTest As StdPicture
Dim tRect As RECT
'--------------------------------------------------------------
'Get Picture (info)
Set picTest = LoadPicture("C:\Temp\MyTestBitmap.bmp")
Call picTest.Render(Me.hDC, 0, 0, Me.ScaleWidth / Screen.TwipsPerPixelX, Me.ScaleHeight / Screen.TwipsPerPixelY, 0, 0, picTest.Width, picTest.Height, ByVal 0&)
'--------------------------------------------------------------
'Setup Enhanced Metafile
lPrintableArea_Width = 20989 'Himetric
lPrintableArea_Height = 29705 'Himetric
With tRect
.Left = 0
.Right = lPrintableArea_Width
.Top = 0
.Bottom = lPrintableArea_Height
End With
hdcBase = Printer.hDC
'hdcBase = Me.hDC
hdcMetaFile = CreateEnhMetaFile(hdcBase, vbNullString, tRect, _
App.EXEName & vbNullChar & "MyExtraTest" & vbNullChar & vbNullChar)
If hdcMetaFile = 0 Then
Call MsgBox("No DeviceContext available for Metafile.", vbError)
Else 'If hdcMetaFile=
'-------------------------------------------------------
'Set some Default settings
lReturn = SetMapMode(hdcMetaFile, MM_ANISOTROPIC)
lReturn = SetWindowExtEx(hdcMetaFile, _
lPrintableArea_Width, lPrintableArea_Height, _
ByVal 0&)
lReturn = SetViewportExtEx(hdcMetaFile, _
lPrintableArea_Width, _
lPrintableArea_Height, _
ByVal 0&)
lReturn = SetBkMode(hdcMetaFile, TRANSPARENT)
''--------------------------------------------------------------
''Draw picture to metafile
'lReturn = GetGDIObject(picTest.Handle, Len(bm), bm)
'hdcBitmap = CreateCompatibleDC(hdcMetaFile)
'lReturn = SelectObject(hdcBitmap, picTest.Handle)
'lReturn = StretchBlt(hdcMetaFile, 10, 10, 200, 100, _
' hdcBitmap, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY)
'lReturn = DeleteDC(hdcBitmap)
''--------------------------------------------------------------
''Draw picture to metafile using Render
'Call picTest.Render((hdcMetaFile), 10, 10, 200, 100, 0, 0, picTest.Width, picTest.Height, ByVal 0&)
''--------------------------------------------------------------
''Draw picture to metafile using combination of BITMAP/BITMAPINFO without need for a separate buffer
''Following only works for 16/24/32bit (8bit and lower requires extra work because of indexed palette)
'lReturn = GetGDIObject(picTest.Handle, Len(bm), bm)
'bmi.bmiHeader.biSize = Len(bmi.bmiHeader)
'lReturn = GetDIBits(hdcMetaFile, picTest.Handle, 0, 0, ByVal 0&, bmi, DIB_RGB_COLORS)
'lReturn = StretchDIBits(hdcMetaFile, _
' 0, 0, Me.ScaleWidth / Screen.TwipsPerPixelX, Me.Height / Screen.TwipsPerPixelY, _
' 0, 0, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight, _
' ByVal bm.bmBits, _
' bmi, _
' DIB_RGB_COLORS, _
' SRCCOPY)
'--------------------------------------------------------------
'Draw picture to metafile using combination of BITMAPINFO
'--------------------------------------------------------------
'Get Info needed for creating the Buffer
bmi.bmiHeader.biSize = Len(bmi.bmiHeader)
lReturn = GetDIBits(hdcMetaFile, picTest.Handle, 0, 0, ByVal 0&, bmi, DIB_RGB_COLORS)
With bmi.bmiHeader
.biBitCount = 32
.biCompression = BI_RGB
.biClrUsed = 0
.biClrImportant = 0
.biSizeImage = 0
End With
'--------------------------------------------------------------
'Create buffer for data
ReDim abBuffer(0 To (bmi.bmiHeader.biWidth * bmi.bmiHeader.biHeight * (bmi.bmiHeader.biBitCount \ 8)) - 1)
'--------------------------------------------------------------
'Get data
lReturn = GetDIBits(hdcMetaFile, picTest.Handle, 0, bmi.bmiHeader.biHeight, abBuffer(0), bmi, DIB_RGB_COLORS)
'--------------------------------------------------------------
'Draw DIB
lReturn = StretchDIBits(hdcMetaFile, _
10, 10, 200, 100, _
0, 0, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight, _
abBuffer(0), _
bmi, _
DIB_RGB_COLORS, _
SRCCOPY)
'--------------------------------------------------------------
'Draw ellipse
lReturn = Ellipse(hdcMetaFile, 100, 100, 300, 200)
'--------------------------------------------------------------
'Close metafileDC
hMetaFile = CloseEnhMetaFile(hdcMetaFile)
'--------------------------------------------------------------
'Draw to form
tRect.Left = 0
tRect.Top = 0
tRect.Right = Me.ScaleWidth \ 2 'Me.ScaleX(Me.ScaleWidth, vbTwips, vbHimetric)
tRect.Bottom = Me.ScaleHeight \ 2 ' Me.ScaleX(Me.ScaleHeight, vbTwips, vbHimetric)
lReturn = PlayEnhMetaFile(Me.hDC, hMetaFile, tRect)
'--------------------------------------------------------------
'Save metafile
lReturn = CopyEnhMetaFile(hMetaFile, "C:\Temp\MyMetafile" & Format(Now, "yyyymmdd_HH_MM_SS") & ".emf")
If lReturn <> 0 Then Call DeleteEnhMetaFile(lReturn)
End If
End Sub
Edited March 22 2021
Micorosoft released an out-of-band update KB for the original problem created by their March 2021 update, so by applying the hotfix the original problem is solved. So above solutions are just an alternative way for doing the same as the original code.
Thanx to Arnoutdv for the following link to the hotfix (which is also available as an optional update with your Windows update).
March 18, 2021—KB5001649 (OS Builds 19041.870 and 19042.870) Out-of-band
Link to the KB5001649 hotfix for different OSses
Last edited by SuperDre; Mar 22nd, 2021 at 01:40 PM.
Reason: Added solution
-
Mar 15th, 2021, 02:30 PM
#2
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Microsoft botched one of their latest updates that affected printing. It may or may not have anything to do with the issue you are having, but it couldn't hurt to try removing the offending update.
See this link:
https://discussions.virtualdr.com/sh...ng-bluescreens
-
Mar 15th, 2021, 05:24 PM
#3
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
I checked one of my own test programs that prints text and "composed as EMF" images and it had problems too. Font metrics were screwed up resulting in wrong sizes and the EMF metrics were also screwed up which led to a runtime error.
I say "were" because after researching this and not finding anything relevant in the March Patch WhingeFest on the web... I opened the Project in the IDE and ran it. Everything worked fine! Then I tried running the old compiled EXE again and it also worked fine!
No clue what changed.
I think the BSOD patch is something else entirely, and probably a real concern. But I doubt it has any bearing on this weird EMF printing issue. I have no idea how it "healed itself" either.
-
Mar 16th, 2021, 02:11 AM
#4
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by dilettante
I have no idea how it "healed itself" either.
Changed default printer to something innocuous like Microsoft Print to PDF?
cheers,
</wqw>
-
Mar 16th, 2021, 05:30 AM
#5
Thread Starter
Addicted Member
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by dilettante
I say "were" because after researching this and not finding anything relevant in the March Patch WhingeFest on the web... I opened the Project in the IDE and ran it. Everything worked fine! Then I tried running the old compiled EXE again and it also worked fine!
No clue what changed.
I think the BSOD patch is something else entirely, and probably a real concern. But I doubt it has any bearing on this weird EMF printing issue. I have no idea how it "healed itself" either.
Are you using the Printer Device Context as a base for your EMF when creating the EMF or are you using a form or something like that as a base?
So in following example, are you using something like Printer.hDC or Form.hDC as hdcBase?
Code:
hdcMetaFile = CreateEnhMetaFile(hdcBase, vbNullString, tRect, _
App.EXEName & vbNullChar & "MyExtraTest" & vbNullChar & vbNullChar)
If I use the Form.hDC it works perfectly, but if I use Printer.hDC, the only thing that doesn't work is drawing the bitmap (any other API drawing call seems to work, so our reports look the same as before, except when a bitmap is drawn (using the method as I described in the example code in my initial post).
Originally Posted by wqweto
Changed default printer to something innocuous like Microsoft Print to PDF?
For me it doesn't matter which printer I choose, PDF or a directly/network connected printer.
-
Mar 16th, 2021, 08:39 AM
#6
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
I had the PDF driver set as default already.
But I create a memory DC compatible with the screen, so yeah there's that. I hadn't checked that earlier.
-
Mar 16th, 2021, 08:49 AM
#7
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by SuperDre
For me it doesn't matter which printer I choose, PDF or a directly/network connected printer.
FWIW, at the time I've used EMF (instead of PDF) for my own Report-generating/printing -
I've found the usage of BitBlt and StretchBlt extremely unreliable ...
The only thing that worked reliable when providing these (later Printed) EMFs with ImageContent,
was the StretchDIBits-call (in 24bpp ColorDepth, 32bpp was making problems on some Printers as well).
There's a high probability (in case you can still reproduce the "weird behaviour"), that changing your:
- current StretchBlt-call
- to a StretchDIBits-one (ImageContent in a 24bpp-DIB)
will not cause any problems.
Olaf
-
Mar 17th, 2021, 02:12 AM
#8
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Another way to avoid the problem is to use the Render method of the stdPicture
-
Mar 18th, 2021, 01:31 PM
#9
Thread Starter
Addicted Member
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by Schmidt
FWIW, at the time I've used EMF (instead of PDF) for my own Report-generating/printing -
I've found the usage of BitBlt and StretchBlt extremely unreliable ...
The only thing that worked reliable when providing these (later Printed) EMFs with ImageContent,
was the StretchDIBits-call (in 24bpp ColorDepth, 32bpp was making problems on some Printers as well).
There's a high probability (in case you can still reproduce the "weird behaviour"), that changing your:
- current StretchBlt-call
- to a StretchDIBits-one (ImageContent in a 24bpp-DIB)
will not cause any problems.
Olaf
Thanx Olaf for pointing me in the direction of StretchDIBits, that seems to be the solution (for now).
I've been heavily experimenting with it now and the following is what I've came up with that seems to work (I've also edited my original post with the complete code):
Code:
'--------------------------------------------------------------
'Get Info needed for creating the Buffer
bmi.bmiHeader.biSize = Len(bmi.bmiHeader)
lReturn = GetDIBits(hdcMetaFile, picTest.Handle, 0, 0, ByVal 0&, bmi, DIB_RGB_COLORS)
With bmi.bmiHeader
.biBitCount = 32
.biCompression = BI_RGB
.biClrUsed = 0
.biClrImportant = 0
.biSizeImage = 0
End With
'--------------------------------------------------------------
'Create buffer for data
ReDim abBuffer(0 To (bmi.bmiHeader.biWidth * bmi.bmiHeader.biHeight * (bmi.bmiHeader.biBitCount \ 8)) - 1)
'--------------------------------------------------------------
'Get data
lReturn = GetDIBits(hdcMetaFile, picTest.Handle, 0, bmi.bmiHeader.biHeight, abBuffer(0), bmi, DIB_RGB_COLORS)
'--------------------------------------------------------------
'Draw DIB
lReturn = StretchDIBits(hdcMetaFile, _
10, 10, 200, 100, _
0, 0, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight, _
abBuffer(0), _
bmi, _
DIB_RGB_COLORS, _
SRCCOPY)
I've never really had a problem for the last 19 years with using StretchBlt until MS released the March 2021 update, grrrr.. But I can't wait for MS to fix it (if they fix it at all), our customers have to be able to print their logo's on the payslips, haha (well the workaround for now is we told our customers to convert their bitmaps to .EMF as if the stdPicture is an EMF the PlayEMF doesn't have problems with the incorporated image)
Originally Posted by Eduardo-
Another way to avoid the problem is to use the Render method of the stdPicture
Well, I've tried the Render method of the stdPicture, and BY GOD what a hell it is to use that method, passing a long as hdc give me a 'Type Mismatch' (and ofcourse with other parameters as well), the method only worked on a form if I passed it Me.hDC or if I passed Me.hDC to a variable of datatype Double (probably also Variant), if I pass it to a variable of datatype Long and use that variable it just bombs..
BUT after I fixed all those problems and the Render method worked with the Form I tried it with my EnhancedMetaFile handle and it worked in the saved EMF, but it didn't render on the form when I played the EMF back (I could see the Ellipse just fine), so without it properly rendering the EMF to the screen I'm not able to use the Render Method. (I've commented out the Render part in my 'solution' in the original post)
-
Mar 18th, 2021, 03:37 PM
#10
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Anyway I think that the solution must come from Microsoft.
-
Mar 18th, 2021, 03:55 PM
#11
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by SuperDre
Well, I've tried the Render method of the stdPicture, and BY GOD what a hell it is to use that method, passing a long as hdc give me a 'Type Mismatch' (and ofcourse with other parameters as well), the method only worked on a form if I passed it Me.hDC or if I passed Me.hDC to a variable of datatype Double (probably also Variant), if I pass it to a variable of datatype Long and use that variable it just bombs..
The issues with Render have been discussed here previously.
There are two Render methods -- first on StdPicture (aliasing IPictureDisp) and second is directly early-bound on IPicture dual interface. The second one does not have any troubles with lvalues getting converted to Variants with VT_BYREF when calling IDispatch::Invoke on the IPictureDisp dispinterface, so passing local variable hDC is fine, no need to use expressions like hDC + 0 (arith expression) or Me.hDC (calling property get) to workaround the IDispatch::Invoke bugs in StdPicture's Render implementation.
cheers,
</wqw>
-
Mar 19th, 2021, 05:52 AM
#12
Thread Starter
Addicted Member
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by wqweto
The issues with Render have been discussed here previously.
There are two Render methods -- first on StdPicture (aliasing IPictureDisp) and second is directly early-bound on IPicture dual interface. The second one does not have any troubles with lvalues getting converted to Variants with VT_BYREF when calling IDispatch::Invoke on the IPictureDisp dispinterface, so passing local variable hDC is fine, no need to use expressions like hDC + 0 (arith expression) or Me.hDC (calling property get) to workaround the IDispatch::Invoke bugs in StdPicture's Render implementation.
cheers,
</wqw>
Yeah I saw that topic, but I hadn't tested the IPicture version, so maybe I'll look into that one as well.
The only problem I have now with my implementation based on StretchDIBits is with very large resolution images, people tend to use small JPG's with very large resolutions like 11.000x6.000 which are mostly just designed as vectorbased logo's but converted to JPG's.
We advice them to export them from Illustrator or Inkscape directly to EMF instead of JPGs.
But the large resolutions make it a problem to create a memorybuffer (without going into 'out of memory' at Width*Height*4), also tried GlobalAlloc instead of Redim but it also gave me out of memory.
As at the moment my Display object only accepts stdPicture for drawing the image, and it'll take some extra work to have it pass my imageclass which does have the original filedata also stored in a buffer which I would be able to pass (I think) to the StretchDIBits function. But it would have been nice if I could have just directly used the Bitmap/data itself stored in the stdPicture for usage with StretchDIBis instead of first having to create another buffer (I'm still gonna look into that).
And I can't wait for Microsoft to fix the problem (as I even highly doubt they will as they already fixed the problem with the BSOD with printers but that hotfix didn't fix the problem of not rendering with StretchBlt, at least not for me), so I have to have a quickfix for now and try to create a better fix for later. For the better fix I'll probably will also look into GDI+ so previewing the printout with some logo's will also look better, just like it does as it's in the final PDF itself (we use EnhancedMetaFile to 'record' the page and play it on a preview screen and use DynaPDF for the creating the actual PDF and use their InsertMetafileFromHandle function to convert the EMF to a page).
-
Mar 19th, 2021, 06:15 AM
#13
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by SuperDre
And I can't wait for Microsoft to fix the problem (as I even highly doubt they will as they already fixed the problem with the BSOD with printers but that hotfix didn't fix the problem of not rendering with StretchBlt, at least not for me)
Is there any information that they are working on the problem or at least that they know about it?
BTW: I didn't know of the difference in the Render method between StdPicture and IPicture, I actually tested with IPicture. But not saving a file to disk in the middle of the process.
Last edited by Eduardo-; Mar 19th, 2021 at 06:26 AM.
-
Mar 19th, 2021, 08:32 AM
#14
Thread Starter
Addicted Member
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by Eduardo-
Is there any information that they are working on the problem or at least that they know about it?
I wouldn't even know where to start reporting it as just posting it in some microsoft forum is not something I think they would notice.
Originally Posted by Eduardo-
BTW: I didn't know of the difference in the Render method between StdPicture and IPicture, I actually tested with IPicture. But not saving a file to disk in the middle of the process.
It's also not apparent there would be a difference, haha. The advantage of IPicture is also having the Attributes property for IF you want to do anything with transparency (which I don't bother with at the moment or at least I never heard a customer have a complaint about that, maybe they just solve it themselves with putting a white background in the image).
I'm now looking into combining BITMAP (as it has the bmBits pointer to the actual imagedata) and BITMAPINFO (which lacks a pointer to the original data) to see if I can just use the original BITMAP.bmBits pointer so I don't have to create another copy into memory.
It seems to be hopeful (at least already with Form.hDC) for 24bit images (and i guess 32bit), but ofcourse with 265 colors it has a separate palette, but I think I can solve that too, I just ran into my BITMAPINFO having the bmiColors field being defined for a specific situation (bmiColors(1) As RGBQUAD) , so I need to fix that into a real pointer, not a biggy.
-
Mar 19th, 2021, 08:55 AM
#15
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by SuperDre
I wouldn't even know where to start reporting it as just posting it in some microsoft forum is not something I think they would notice.
I mean, it must have broken lot of programs.
But strangely, the search KB5000802 printer "metafiles" returns nothing useful.
Originally Posted by SuperDre
I'm now looking into combining BITMAP (as it has the bmBits pointer to the actual imagedata) and BITMAPINFO (which lacks a pointer to the original data) to see if I can just use the original BITMAP.bmBits pointer so I don't have to create another copy into memory.
It seems to be hopeful (at least already with Form.hDC) for 24bit images (and i guess 32bit), but ofcourse with 265 colors it has a separate palette, but I think I can solve that too, I just ran into my BITMAPINFO having the bmiColors field being defined for a specific situation (bmiColors(1) As RGBQUAD) , so I need to fix that into a real pointer, not a biggy.
I'm thinking that perhaps a solution is to make a temporary screen based DC, resize the picture there using StertchBlt and then paste it into the printer based metafile with StretchDIBits (without stretching anything).
But I don't have now the update installed to experiment, I reverted to a restore point before the update and paused updates for now.
(To restore a restore point takes like half an hour to complete, so I'm not willing to do it is I can avoid it.)
Last edited by Eduardo-; Mar 19th, 2021 at 11:00 AM.
-
Mar 19th, 2021, 11:22 AM
#16
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by SuperDre
The only problem I have now with my implementation based on StretchDIBits is with very large resolution images, people tend to use small JPG's with very large resolutions like 11.000x6.000 which are mostly just designed as vectorbased logo's but converted to JPG's.
I'd stick with the StretchDIBits-approach in either case, because as said -
I've encountered problems "playing pre-produced Metafiles" to certain Printers with any other GDI-method
(already back in the XP and Win7 era)...
If you think about it, with the other GDI-methods (like StretchBlt for example),
you force the EMF-interpreters to "jump through hoops", to somehow manage to "embed"
not only the ImageData, but also the "Source-hDCs" into the EMF itself.
And the only Stretch-capable GDI-call, which does *not* need any Source-HDC in its Parameter-List,
is the StretchDIBits-call -> then causing "much less stress" for the EMF-interpreter,
who now has only to "embed" the Source-DIB-Data.
As for "super-high-res Image-Inputs" like from the JPGs you mentioned (11000x6000) ...
Why not reduce those beforehand in an intermediate step?
A Logo will look relatively sharp, when the EMF-stored DIB-Image-Data has a resolution of about 200DPI.
Here is a function, which can do that efficiently (for JPGs) - keeping the Aspect-Ratio of the Input-Image:
Code:
Function GetScaledDownJpg(JPG As cJPG, JpgBytes() As Byte, ByVal DesiredWidthInches#, _
Optional ByVal DesiredDPI& = 200) As StdPicture
Dim W&, H&
'first, read out the current JPG-dimensions without decoding the JPG into memory
JPG.GetJPGDimensions VarPtr(JpgBytes(0)), UBound(JpgBytes) + 1, W, H
If DesiredWidthInches <= 0 Or W = 0 Or H = 0 Then Exit Function
H = DesiredWidthInches * DesiredDPI / W * H
W = DesiredWidthInches * DesiredDPI
'the Cairo.ImageList supports a high-performance scaledown from highres-JPGs via libJpgTurbo
Set GetScaledDownJpg = Cairo.ImageList.AddImage("", JpgBytes, W, H).Picture
End Function
The above needs RC5 or RC6... and will return a scaled down StdPicture (with the Desired-DPI).
And FWIW, here's another Helper, which is able to Stretch from any Bitmap-Source-StdPicture(Handle) via StretchDIBits in 24BPP:
Code:
'this routine is not dependent on RC5/RC6 - it only needs a StdPicture.Handle as Input
Sub StretchWith24bppFromHdl(ByVal Hdl&, ByVal DstDC&, ByVal x&, ByVal y&, ByVal dx&, ByVal dy&)
Dim TmpDC&: TmpDC = GetDC(0)
ReDim BI(9) As Long
BI(0) = 40
GetDIBits TmpDC, Hdl, 0, 0, ByVal 0&, BI(0), 0
Dim W&: W = BI(1)
Dim H&: H = BI(2)
BI(2) = -H: BI(3) = 24 * 65536 + 1: BI(4) = 0
ReDim P(4 * (W * 24 \ 32 + IIf(W * 24 Mod 32, 1, 0)) - 1, H - 1) As Byte
GetDIBits TmpDC, Hdl, 0, H, P(0, 0), BI(0), 0
ReleaseDC 0, TmpDC
StretchDIBits DstDC, x, y, dx, dy, 0, 0, W, H, P(0, 0), BI(0), 0, vbSrcCopy
End Sub
Ok, here complete Form-TestCode with the 4 needed API-Declares (needs a RC5 or RC6 Project-reference):
Code:
Option Explicit
Private Declare Function GetDC Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hWnd As Long, ByVal hDC As Long) As Long
Private Declare Function GetDIBits& Lib "gdi32" (ByVal aHDC&, ByVal hBM&, ByVal nStartSL&, ByVal nNumSL&, lpBits As Any, lpBI As Any, ByVal wUsage&)
Private Declare Function StretchDIBits Lib "gdi32" (ByVal hDC As Long, ByVal x As Long, ByVal y As Long, ByVal dx As Long, ByVal dy As Long, ByVal SrcX As Long, ByVal SrcY As Long, ByVal wSrcWidth As Long, ByVal wSrcHeight As Long, lpBits As Any, lpBitsInfo As Any, ByVal wUsage As Long, ByVal dwRop As Long) As Long
Private oPic As StdPicture
Private Sub Form_Load()
Dim JpgBytes() As Byte
JpgBytes = New_c.FSO.ReadByteContent("c:\temp\large.jpg")
Set oPic = GetScaledDownJpg(New_c.JPG, JpgBytes, 2, 200)
End Sub
Private Sub Form_Click()
StretchWith24bppFromHdl oPic, hDC, 10, 10, ScaleX(oPic.Width, vbHimetric, vbPixels), _
ScaleY(oPic.Height, vbHimetric, vbPixels)
End Sub
Function GetScaledDownJpg(JPG As cJPG, JpgBytes() As Byte, ByVal DesiredWidthInches#, _
Optional ByVal DesiredDPI& = 200) As StdPicture
Dim W&, H&
'first, read out the current JPG-dimensions without decoding the JPG into memory
JPG.GetJPGDimensions VarPtr(JpgBytes(0)), UBound(JpgBytes) + 1, W, H
If DesiredWidthInches <= 0 Or W = 0 Or H = 0 Then Exit Function
H = DesiredWidthInches * DesiredDPI / W * H
W = DesiredWidthInches * DesiredDPI
'the Cairo.ImageList supports a high-performance scaledown from highres-JPGs via libJpgTurbo
Set GetScaledDownJpg = Cairo.ImageList.AddImage("", JpgBytes, W, H).Picture
End Function
Sub StretchWith24bppFromHdl(ByVal Hdl&, ByVal DstDC&, ByVal x&, ByVal y&, ByVal dx&, ByVal dy&)
Dim TmpDC&: TmpDC = GetDC(0)
ReDim BI(9) As Long
BI(0) = 40
GetDIBits TmpDC, Hdl, 0, 0, ByVal 0&, BI(0), 0
Dim W&: W = BI(1)
Dim H&: H = BI(2)
BI(2) = -H: BI(3) = 24 * 65536 + 1: BI(4) = 0
ReDim P(4 * (W * 24 \ 32 + IIf(W * 24 Mod 32, 1, 0)) - 1, H - 1) As Byte
GetDIBits TmpDC, Hdl, 0, H, P(0, 0), BI(0), 0
ReleaseDC 0, TmpDC
StretchDIBits DstDC, x, y, dx, dy, 0, 0, W, H, P(0, 0), BI(0), 0, vbSrcCopy
End Sub
HTH
Olaf
-
Mar 19th, 2021, 11:52 AM
#17
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by Schmidt
As for "super-high-res Image-Inputs" like from the JPGs you mentioned (11000x6000) ...
Why not reduce those beforehand in an intermediate step?
Or OP can tile these in 1024x1024 tiles or similar before blitting to output DC in ultra high DPI.
Btw, for pre-printing purposes 600 DPI images are the norm to feed enough data to high-quality image setters for magazines etc.
cheers,
</wqw>
-
Mar 19th, 2021, 02:13 PM
#18
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by wqweto
Or OP can tile these in 1024x1024 tiles or similar before blitting to output DC in ultra high DPI.
Btw, for pre-printing purposes 600 DPI images are the norm to feed enough data to high-quality image setters for magazines etc.
Of course - but for TrueColor-Image-Content the human capability to detect "pixelation",
already starts to vanish from 200ppi onwards (300ppi is better of course, but 400ppi is IMO completely sufficient for TrueColor-images).
E.g. on my 5.2" SmartPhone (with 1920x1080) I have about 400ppi - and can for the hell of it not see any pixelation anymore.
So, even when the Printing-System is capable to render in 600DPI (which might be important,
to make pixelation-detection impossible even for mono-colored edges as in Font-Printing or skewed Lines in a CAD-Printout),
I'd say 400ppi/dpi Input-res for TrueColor-images (as e.g. Logos) is entirely sufficient
(that's also, because many 600DPI Printers can achieve this res only for monochrome output,
for TrueColor-output, the resolution is often lower, because of "combining a few Pixels" to produce a single Color-Pixel).
But to give an example, for the large JPG-Dimensions the OP gave (11000x6000) ...
E.g. in case it is planned to render that into a 3inch wide Logo on a Printer-Page with 600ppi resolution,
the reduced Image as calculated by my routine (when giving ..., 3, 600 as the last two params) would be:
- 1800 x 982 Pixel as the "required DIB-input-size - needed, to achieve 600ppi over 3inch width"
- and this size requires only about 2.7% of the needed memory 11000x6000 would require
Olaf
-
Mar 19th, 2021, 02:30 PM
#19
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by Schmidt
1800 x 982 Pixel as the "required DIB-input-size - needed, to achieve 600ppi over 3inch width"
True!
But OP might need this image for a poster, a 30x30 inches one and 11000 pixels would not be enough for 600 DPI image resolution in this case.
A 30x30 inches poster would need 100 times more pixels that a 3x3 inches picture and image-setters deal regularly with such beasts in 2400-4000 DPI output resolution.
cheers,
</wqw>
-
Mar 19th, 2021, 02:48 PM
#20
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by wqweto
True!
But OP might need this image for a poster, a 30x30 inches one and 11000 pixels would not be enough for 600 DPI image resolution in this case.
True as well... though not sure whether the OPs customers really require Printouts in that size...
Olaf
-
Mar 19th, 2021, 09:47 PM
#21
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by SuperDre
our customers have to be able to print their logo's on the payslips
The final print is just a logo, not a huge image.
What sometimes is huge is the source image:
Originally Posted by SuperDre
The only problem I have now with my implementation based on StretchDIBits is with very large resolution images, people tend to use small JPG's with very large resolutions like 11.000x6.000 which are mostly just designed as vectorbased logo's but converted to JPG's.
And it seems to cause out of memory errors when trying to handle a very large bitmap in VB6's memory.
That's why I suggested to resize it before reading its bits:
Originally Posted by Eduardo-
make a temporary screen based DC, resize the picture there using StertchBlt and then paste it into the printer based metafile with StretchDIBits (without stretching anything).
Last edited by Eduardo-; Mar 20th, 2021 at 03:01 AM.
-
Mar 21st, 2021, 11:02 AM
#22
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
-
Mar 22nd, 2021, 08:09 AM
#23
Thread Starter
Addicted Member
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Originally Posted by Eduardo-
The final print is just a logo, not a huge image.
What sometimes is huge is the source image:
And it seems to cause out of memory errors when trying to handle a very large bitmap in VB6's memory.
That's why I suggested to resize it before reading its bits:
That's right, in my example there isn't a need to maintain the very large resolution for printing to a poster or something like that, it's being printed in a 74mm by 47mm block (in aspect ratio of the original image), so it's nonsense to have such a large resolution.
I already had built in a warning if the imagefile itself would exceed 5MB (which could be anything from a BMP, JPG, GIF, EMF, WMF, ICO) but I'm also gonna add a warning if it will exceed like 6000 pixels in width or something like that, unless maybe if it's an EMF, but the problem there is, it's possible that it's just a bitmap encapsulated in an EMF and not a 'vector based' version, but then again, it'll probably have a filesize larger than 5MB.
And the out of memory error was something I got when I first would be using GetDIBits with a separate buffer, but I circumvented that by actually using the BITMAP.bmBits handle to the data for 16bit and up, but was still trying to figure out how to do it for 8bit with a palette.
Originally Posted by Arnoutdv
Yep, that's what I wanted to post here, Microsoft actually fixed the problem they created with the original March updatedate by releasing the KB5001649 out-of-band update.
Which means I'll probably gonna ditch the new method as the old method has served us well for 19 years.
BUT I was going to disconnect my Display class from it's reliance on the VB6 Printer object for some parts so I might use the code/research done here for that anyway.
I would like to especially thank Schmidt and Eduardo for their extensive advice, help and insight as it made my research into the problem so much easier.
(If I finish the workaround with using BITMAP.bmBits which would also work with 8bit/palette images I'll update the original post with that aswell)
So thanx to all for trying to help me with finding a workaround for a problem created by Microsoft with their crappy March 2021 Windows update.
-
Oct 29th, 2021, 11:44 PM
#24
Fanatic Member
Re: Drawing bitmap to printer based Enhanced Metafile after latest windows update 03-
Last edited by DaveDavis; Oct 29th, 2021 at 11:55 PM.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|