Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Wanet Telecoms Ltd on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Non-Standard Printer Properties 2

Status
Not open for further replies.
Oct 5, 1999
105
GB
I have a program that creates despatch notes etc. from a database using a printer object.

I have no problem setting standard properties like PapeSize, PaperBin and Duplex etc. but my printer driver has some options that don't seem to be available in VB6.

In particular, my printer driver, like a lot of recent printers, can print different layous, eg "Multiple Pages per Side" and "Booklet Printing" etc.

Does anyone know how to access these settings?
Thanks
 
Without more specifics, add a reference to the printer and take a look at object browser. If the model is exposed, all the props & methods will be there.

Always remember that you're unique. Just like everyone else.
 
Sorry if I'm being thick, but what do you mean by "add a reference to the Printer"?

I use
Code:
 Dim mPrint as Printer
at module level, but I'm not sure how to get object browser to expose it!

Paul
 




Tools > References

then scroll to find the object library for your printer.

Skip,

[glasses] When a diminutive clarvoyant had disappeared from detention, headlines read...
Small Medium at Large[tongue]
 
Tools > References

Presumably in VB6 pro that should be Project > References ?

I can't find any reference to my printer (Samsung CLP-500) on the list, and I can't find any obvious .dll file anywhere either.

Any ideas
 
You'll want to look into your printers control language. For example, HP PCL (printer control language) allows you to send the escape character to the printer, followed by any of a large number of strings that do things like select trays, change fonts, or the like. I looked up your printer, and apparently it supports neither PCL nor PostScript. However, it almost certainly has its own set of control strings. You would send them to the printer the same way as any printable strings, by using Printer.Print.

I find a bit of information here: Samsuhg has its own proprietary printer language called SPL, and it would seem that it is basically undocumented, at least on the internet. You'll probably have better luck contacting Samsung about it.

This is pretty much the reason why VB doesn't support methods of the Printer object to do more than basic stuff; everybody has their own way of doing things. Even a given printer of a given brand might be set up to support any of a few printer languages in a given instance. For example, HP printers can be configured to support PCL or PostScript.

So, I would get in touch with Samsung and see what I could find.

HTH

Bob
 
I'm afraid that I don't think it'll be as simple as 'printing' basic control sequences to the printer.
 
<I'm afraid that...

Well, perhaps it isn't. However, I've been able to do some pretty interesting things in the past with printing PCL escape codes, which is why I thought it might be worth a shot to look into it.
 
Sure. But even with PCL there are limits - and what we are actually talking about here is Job control and/or page control- and even HP printers have a different language for that (JCL). ANd the commands get sent atr the start of a print job (via the print driver) and are difficult to include in the page stream (ask yourself how, once a page has started printing, you can tell it that actually we are supposed to be printing multiple pages on the paper ...)
 
<ask yourself how, once a page has started printing, you can tell it that actually we are supposed to be printing multiple pages on the paper

Well, I asked myself that quite some time ago, when I had to automate the printing of an EPA form for a car manufacturer. In those days I had to exactly duplicate the form using PCL, by sending PCL escape sequences to the printer to position the cursor where I wanted to put the next printed item. It was a long and tedious project, but I may say that I know PCL pretty well now.

While it is indeed the case that if you tell a page to start printing, it's going to be tough to then tell it to reformat how it prints, you simply don't tell the page to print. You position the cursor, print whatever you want to print, position the cursor again, and output a page feed when the page is done. So, since I can select whatever font I want, position the cursor at any dot location that I want, at any point in time relative to when a page has started printing, and output a physical page feed as soon as I'm done printing exactly what I want to print, wherever I want to print it, it follows that I can do multiple pages per sheet, albeit in a very tedious way. I have to believe that there's a simple escape code that will allow you to specify the number of logical pages on a physical page, and while I haven't found the smoking gun, a few things I've looked at suggest that there is such a thing somewhere.

To position the cursor on a page at whatever location you want, check the cursor positioning chapter starting on page 95.

As for the two different languages, (PCL and PJL), they are both accessed using the same escape sequence methodology, so what I'm arguing for PCL holds for PJL as well.

Bob
 
As one who has been coding escape sequences for HP PCL since version 1 with the original HP Laserjet (it improved vastly with the Laserjet 2), that sort of thing does not worry me. (e.g. position cursor at top left = <esc>*p0x0Y ).

I suspect I would be more taxed these days trying to bypass the printer driver to get the raw escape sequence to actually reach the printer! (We always did it from DOS in those days, as windows made the whole idea redundant).

No, my first problem is that I can't source any Samsung SPL details, and anyway, it seems a rather desperate way to solve my problem.

I suppose the simplest solution if I want multiple pages per sheet is to manually program that in.

Thanks anyway guys.
 
I'm sorry, Bob, but I'm afraid we're going to have to agree to disagree.

I still don't think it'll be that easy. I did have a point-by-point debate lined up, but decided against it

Suffice it to say that the Samsung Printer Language is, as the link you yourself provided indicates, just a new(ish) term for a GDI printer.

Now, this means that your PC does all the rendering of your print job, using Windows GDI commands, into a memory DC and then simply transfers the resulting bitmap to the printer (this meant the printer needs almost no intelligence whatsoever, which keeps the cost down). There is some header info that the driver privides (which in this case is indeed PJL compatible), but there's no way to inject printer control sequences into that bitmap ...


Some key terms: GDI Printer, SPL2, PrinThru, QPDL
 
Yep, I'm now happily configuring Multiple Pages per side, Booklet printing, poster layout, and a number of other 'advanced' settings ...

Just need to tidy up the code a bit.
 
OK, here's the illustrative module. It looks a trifle daunting, but well over half of it is simply declarations, and oanother quarter or so is just Rem statements:

Code:
[blue]Option Explicit

Public Declare Function OpenPrinter Lib &quot;winspool.drv&quot; Alias &quot;OpenPrinterA&quot; (ByVal pPrinterName As String, phPrinter As Long, pDefault As PRINTER_DEFAULTS) As Long
Public Declare Function ClosePrinter Lib &quot;winspool.drv&quot; (ByVal hPrinter As Long) As Long
Public Declare Function DocumentProperties Lib &quot;winspool.drv&quot; Alias &quot;DocumentPropertiesA&quot; (ByVal hwnd As Long, ByVal hPrinter As Long, ByVal pDeviceName As String, pDevModeOutput As Any, pDevModeInput As Any, ByVal fMode As Long) As Long
Public Declare Function GetPrinter Lib &quot;winspool.drv&quot; Alias &quot;GetPrinterA&quot; (ByVal hPrinter As Long, ByVal Level As Long, pPrinter As Any, ByVal cbBuf As Long, pcbNeeded As Long) As Long
Public Declare Function SetPrinter Lib &quot;winspool.drv&quot; Alias &quot;SetPrinterA&quot; (ByVal hPrinter As Long, ByVal Level As Long, pPrinter As Byte, ByVal Command As Long) As Long
Public Declare Sub CopyMemory Lib &quot;kernel32&quot; Alias &quot;RtlMoveMemory&quot; (Destination As Any, Source As Any, ByVal Length As Long)

Public Const CCHDEVICENAME = 32
Public Const CCHFORMNAME = 32

Public Type DEVMODE
        dmDeviceName As String * CCHDEVICENAME
        dmSpecVersion As Integer
        dmDriverVersion As Integer
        dmSize As Integer
        dmDriverExtra As Integer
        dmFields As Long
        dmOrientation As Integer
        dmPaperSize As Integer
        dmPaperLength As Integer
        dmPaperWidth As Integer
        dmScale As Integer
        dmCopies As Integer
        dmDefaultSource As Integer
        dmPrintQuality As Integer
        dmColor As Integer
        dmDuplex As Integer
        dmYResolution As Integer
        dmTTOption As Integer
        dmCollate As Integer
        dmFormName As String * CCHFORMNAME
        dmUnusedPadding As Integer
        dmBitsPerPel As Long
        dmPelsWidth As Long
        dmPelsHeight As Long
        dmDisplayFlags As Long
        dmDisplayFrequency As Long
End Type

Public Type PRINTER_DEFAULTS
        pDatatype As String
        pDevMode As DEVMODE
        DesiredAccess As Long
End Type

Public Type PRINTER_INFO_2
        pServerName As String
        pPrinterName As String
        pShareName As String
        pPortName As String
        pDriverName As String
        pComment As String
        pLocation As String
        pDevMode As Long 'DEVMODE
        pSepFile As String
        pPrintProcessor As String
        pDatatype As String
        pParameters As String
        pSecurityDescriptor As Long 'SECURITY_DESCRIPTOR
        Attributes As Long
        Priority As Long
        DefaultPriority As Long
        StartTime As Long
        UntilTime As Long
        Status As Long
        cJobs As Long
        AveragePPM As Long
End Type


Public Const PRINTER_ACCESS_ADMINISTER = &amp;H4
Public Const PRINTER_ACCESS_USE = &amp;H8
Public Const STANDARD_RIGHTS_REQUIRED = &amp;HF0000
Public Const PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or PRINTER_ACCESS_ADMINISTER Or PRINTER_ACCESS_USE)

Public Const DM_MODIFY = 8
Public Const DM_PROMPT = 4
Public Const DM_COPY = 2
Public Const DM_IN_BUFFER = DM_MODIFY
Public Const DM_IN_PROMPT = DM_PROMPT
Public Const DM_OUT_BUFFER = DM_COPY

Public Enum CLP_SETTINGS_LOCATION
    PagesPerSide = 478
    PosterLayout = 492
    LayoutType = 504
End Enum

' Simplistic version. We assume we only want to change one byte at a time in the
' DriverExtra memory
Public Function SetDriverExtraByte(lLocation As CLP_SETTINGS_LOCATION, bValue As Byte)
    'Dim PrintDevIn As DEVMODE
    Dim PrintDevOut As DEVMODE ' Just needed for sizing purposes
    Dim phPrinter As Long
    Dim printerDefaults As PRINTER_DEFAULTS
    Dim pBuffer() As Byte
    Dim pcbNeeded As Long
    Dim pinfo As PRINTER_INFO_2
    
    'Dim ExtraBuffer As String
    
    Dim result As Long
    
    'Dim BufferIn() As Byte
    Dim BufferOut() As Byte
    
    'Dim PrivateBuffer() As Byte
    'Dim CompareBuffer() As Byte
    
    ' Get required access to the printer
    printerDefaults.DesiredAccess = PRINTER_ALL_ACCESS
    If OpenPrinter(Printer.DeviceName, phPrinter, printerDefaults) Then ' Note we are working with whatever VB thinks is the default printer in this example
        ' See how much data there is for this printer (DEVMODE + private data that comes after it) ...
        result = DocumentProperties(0&amp;, phPrinter, Printer.DeviceName, 0&amp;, 0&amp;, 0)
        '... and size our own buffer appropriately
        'ReDim BufferIn(result - 1) As Byte
        ReDim BufferOut(result - 1) As Byte
        ' Now it is safe to read the data, which is basically the current printer settings that you would
        ' normally access through the print setup dialogs including any special features
        result = DocumentProperties(0&amp;, phPrinter, Printer.DeviceName, BufferOut(0), 0&amp;, DM_OUT_BUFFER)
        ' Ok, modify the private data (advanced features of the specific printer)
        BufferOut(lLocation + Len(PrintDevOut)) = bValue

        ' In theory it is suggested we ensure BufferOut is legit by another call to DocumentProperties as shown below. In practice,
        ' for this example it does not seem to be necessary
        'result = DocumentProperties(0&amp;, phPrinter, Printer.DeviceName, BufferOut(0), BufferOut(0), DM_IN_BUFFER Or DM_OUT_BUFFER)
        
        ' Now we need to drop our modified DEVMODE+private data into a legit PRINTER_INFO_2 structure to reconfigure the driver
        GetPrinter phPrinter, 2, 0&amp;, 0&amp;, pcbNeeded ' find out how big buffer needs to be
        ReDim pBuffer(pcbNeeded) As Byte ' Ok, make sure buffer is big enough
        GetPrinter phPrinter, 2, pBuffer(0), pcbNeeded, pcbNeeded ' get printer info into buffer
        CopyMemory pinfo, pBuffer(0), Len(pinfo)  'First part of buffer is basic PRINTER_INFO_2 structure
        pinfo.pDevMode = VarPtr(BufferOut(0)) ' and we need that so we can set the DevMode member to point to our modified DevMode
        CopyMemory pBuffer(0), pinfo, Len(pinfo) 'then copy it back into the buffer
        ' And here's were we change it ...
        SetPrinter phPrinter, 2, pBuffer(0), 0&amp; ' and set the printer using our updated info
        
    End If
    ClosePrinter phPrinter
    'Ta da - all done
End Function[/blue]
And here's how we might call it from a Form
Code:
[blue]Option Explicit

Private Sub Command1_Click()
    ' Demonstrate this is working by bringing up the Print Dialog so we can review current settings
    ' after making each change
    ' Note: this example is specifically for the Samsung CLP-500
    SetDriverExtraByte PagesPerSide, 4   ' 4 pages per side (values can be 1,2,4,9,16)
    CommonDialog1.ShowPrinter
    SetDriverExtraByte PagesPerSide, 9 ' 9 pages per side (values can be 1,2,4,9,16)
    CommonDialog1.ShowPrinter
    SetDriverExtraByte LayoutType, 4 ' Booklet (0=Normal, 3= Poster, 4=Booklet)
    CommonDialog1.ShowPrinter
    SetDriverExtraByte LayoutType, 3 ' Poster (0=Normal, 3= Poster, 4=Booklet)
    SetDriverExtraByte PosterLayout, 2 ' 3x3 layout (1=2x2, 2=3x3, 3=4x4)
    CommonDialog1.ShowPrinter
    ' And back to defaults
    SetDriverExtraByte LayoutType, 0 ' Normal  (0=Normal, 3= Poster, 4=Booklet)
    SetDriverExtraByte PagesPerSide, 1 ' 1 page per side (values can be 1,2,4,9,16)
    CommonDialog1.ShowPrinter
End Sub[/blue]

I have code that'll display the printer dialogs without having to use the common dialog control, but I didn't want to complicate things...
 
Wow - that's just what I was looking for.

I gave your code a test and it does indeed do what it says on the tin! That definitely deserves a star.

One query, you say this works on the default printer which is presumably why you set it back at the end of your test. Is there any problem in supplying a printer object as one of the parameters or is it always best to use the default set-up?

Thanks again

Paul

 
Shouldn't matter. Was just easiest for the sake of the example to use the default. And the returning to defaults was a cheap way of doing it; more properly we should probably take a copy of the very first BufferOut (i.e the initial DEVMODE of the printer) and then SetPrinter the printer back to this at the end.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top