Update released.
Sorry for the ongoing updates with CommonDialog.cls
Hopefully this is now final. 

Originally Posted by
Simos
Yes. This is the perfect logic.
Almost. Now only the PropPrinterName is used to select a non-default printer on initialization.
Also the DEVNAMES structure is now always filled according to the DeviceName. (which can be GetPrinterDefault() or PropPrinterName)
DEVNAMES is important if the printer name is > 32 chars. (if less 32 then DMDeviceName will be read)
Also PropPrinterDriver and PropPrinterPort are not needed actually, so there are ignored on input.
On output (dialog function returns) of course the DEVNAMES will be read and all PropPrinterName/PropPrinterDriver/PropPrinterPort will be set.
I played around very extensively now and this logic must be the "correct" one.
In code this means following: (changes marked as red)
Code:
Dim hPrinter As Long, DeviceName As String, DMODE_B() As Byte, dwBytes As Long
If PropPrinterDefaultInit = False And Not PropPrinterName = vbNullString Then
DeviceName = PropPrinterName
dwBytes = PrepareDevModeBuffer(hPrinter, DeviceName, DMODE_B())
If dwBytes = 0 Then
' Fallback to default printer as user-defined printer name is invalid.
DeviceName = GetPrinterDefault()
dwBytes = PrepareDevModeBuffer(hPrinter, DeviceName, DMODE_B())
End If
Else
DeviceName = GetPrinterDefault()
dwBytes = PrepareDevModeBuffer(hPrinter, DeviceName, DMODE_B())
End If
If dwBytes > 0 Then
[...]
Code:
[...]
If Not DeviceName = vbNullString Then
' wDeviceOffset will only be used on input when DMDeviceName got truncated due to the 32 characters limit.
' wDriverOffset and wOutputOffset are ignored on input.
DNAMES.wDriverOffset = 4
DNAMES.wDeviceOffset = DNAMES.wDriverOffset + 1
DNAMES.wOutputOffset = DNAMES.wDeviceOffset + Len(DeviceName) + 1
DNAMES.wDefault = 0
Buffer = Left$(vbNullChar & DeviceName & vbNullChar & vbNullChar, CCHDEVNAMESEXTRA)
CopyMemory DNAMES.wExtra(0), ByVal StrPtr(Buffer), LenB(Buffer)
PDLG.hDevNames = GlobalAlloc(GMEM_MOVEABLE Or GMEM_ZEROINIT, LenB(DNAMES))
lpDevNames = GlobalLock(PDLG.hDevNames)
CopyMemory ByVal lpDevNames, DNAMES, LenB(DNAMES)
GlobalUnlock PDLG.hDevNames
End If
End If

Originally Posted by
Simos
Did you test the Generic / Text Only ?
No. Why?
EDIT:
Another gotcha fixed. (potential memory issue)
On return, when reading hDevNames, the size is flexible. So instead using LenB(DNAMES) now use GlobalSize().
I defined CCHDEVNAMESEXTRA as 200 to ensure big enough buffer. However, it may be smaller when not necessary.
And therefore using LenB(DNAMES) could CopyMemory too much, out of scope of the allocated memory block.
So GlobalSize() should be the safe way.
Code:
If PDLG.hDevNames <> 0 Then
' DEVNAMES is a variable length memory block.
Dim dwMemSize As Long
dwMemSize = GlobalSize(PDLG.hDevNames)
If dwMemSize > LenB(DNAMES) Then dwMemSize = LenB(DNAMES)
Erase DNAMES.wExtra()
lpDevNames = GlobalLock(PDLG.hDevNames)
CopyMemory DNAMES, ByVal lpDevNames, dwMemSize
GlobalUnlock PDLG.hDevNames
GlobalFree PDLG.hDevNames
[...]