[Class] Printers

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

[Class] Printers

21 Mar 2019, 10:29

Class Printers
AutoHotkey wrapper for Print Spooler API Functions (msdn-docs)


Source
Mirror: Release on GitHub

Code: Select all

; ===============================================================================================================================
; AutoHotkey wrapper for Print Spooler API Functions
;
; Author ....: jNizM
; Released ..: 2020-07-19
; Modified ..: 2021-04-12
; Github ....: https://github.com/jNizM/Class_Printers
; Forum .....: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=62955
; ===============================================================================================================================


class Printers
{

	; ===== PUBLIC METHODS ======================================================================================================

	AddConnection(PrinterName)
	{
		if (DllCall("winspool.drv\AddConnection", "str", PrinterName))
			return true
		return false
	}


	Delete(PrinterName)
	{
		static PRINTER_ALL_ACCESS := 0x000F000C

		if (hPrinter := this.OpenHandle(PrinterName, PRINTER_ALL_ACCESS))
		{
			if (DllCall("winspool.drv\DeletePrinter", "ptr", hPrinter))
			{
				this.CloseHandle(hPrinter)
				return true
			}
			this.CloseHandle(hPrinter)
		}
		return false
	}


	DeleteConnection(PrinterName)
	{
		if (DllCall("winspool.drv\DeletePrinterConnection", "str", PrinterName))
			return true
		return false
	}


	Enum(flags := "")
	{
		flags := (flags) ? flags : 0x2|0x4
		if !(DllCall("winspool.drv\EnumPrinters", "uint", flags, "ptr", 0, "uint", 2, "ptr", 0, "uint", 0, "uint*", size, "uint*", 0))
		{
			size := VarSetCapacity(buf, size << 1, 0)
			if (DllCall("winspool.drv\EnumPrinters", "uint", flags, "ptr", 0, "uint", 2, "ptr", &buf, "uint", size, "uint*", 0, "uint*", enum))
			{
				addr := &buf, PRINTER_INFO := []
				loop % enum
				{
					PRINTER_INFO[A_Index, "ServerName"]  := StrGet(NumGet(addr + 0, "ptr"))
					PRINTER_INFO[A_Index, "PrinterName"] := StrGet(NumGet(addr + A_PtrSize, "ptr"))
					PRINTER_INFO[A_Index, "ShareName"]   := StrGet(NumGet(addr + A_PtrSize * 2, "ptr"))
					PRINTER_INFO[A_Index, "PortName"]    := StrGet(NumGet(addr + A_PtrSize * 3, "ptr"))
					PRINTER_INFO[A_Index, "DriverName"]  := StrGet(NumGet(addr + A_PtrSize * 4, "ptr"))
					PRINTER_INFO[A_Index, "Comment"]     := StrGet(NumGet(addr + A_PtrSize * 5, "ptr"))
					PRINTER_INFO[A_Index, "Location"]    := StrGet(NumGet(addr + A_PtrSize * 6, "ptr"))
					addr += A_PtrSize * 13 + 32
				}
				return PRINTER_INFO
			}
		}
		return false
	}


	GetDefault()
	{
		if !(DllCall("winspool.drv\GetDefaultPrinter", "ptr", 0, "uint*", size))
		{
			size := VarSetCapacity(buf, size << 1, 0)
			if (DllCall("winspool.drv\GetDefaultPrinter", "str", buf, "uint*", size))
				return buf
		}
		return false
	}


	GetInfo(PrinterName)
	{
		static PRINTER_ACCESS_USE := 0x00000008

		if (hPrinter := this.OpenHandle(PrinterName, PRINTER_ACCESS_USE))
		{
			if !(DllCall("winspool.drv\GetPrinter", "ptr", hPrinter, "uint", 2, "ptr", 0, "uint", 0, "uint*", size))
			{
				size := VarSetCapacity(buf, size << 1, 0)
				if (DllCall("winspool.drv\GetPrinter", "ptr", hPrinter, "uint", 2, "ptr", &buf, "uint", size, "uint*", 0))
				{
					addr := &buf, PRINTER_INFO := []
					PRINTER_INFO["ServerName"]  := StrGet(NumGet(addr + 0, "ptr"))
					PRINTER_INFO["PrinterName"] := StrGet(NumGet(addr + A_PtrSize, "ptr"))
					PRINTER_INFO["ShareName"]   := StrGet(NumGet(addr + A_PtrSize * 2, "ptr"))
					PRINTER_INFO["PortName"]    := StrGet(NumGet(addr + A_PtrSize * 3, "ptr"))
					PRINTER_INFO["DriverName"]  := StrGet(NumGet(addr + A_PtrSize * 4, "ptr"))
					PRINTER_INFO["Comment"]     := StrGet(NumGet(addr + A_PtrSize * 5, "ptr"))
					PRINTER_INFO["Location"]    := StrGet(NumGet(addr + A_PtrSize * 6, "ptr"))
					this.CloseHandle(hPrinter)
					return PRINTER_INFO
				}
			}
			this.CloseHandle(hPrinter)
		}
		return false
	}


	Properties(PrinterName, hWindow := 0)
	{
		static PRINTER_ACCESS_USE := 0x00000008

		if (hPrinter := this.OpenHandle(PrinterName, PRINTER_ACCESS_USE))
		{
			if (DllCall("winspool.drv\PrinterProperties", "ptr", hWindow, "ptr", hPrinter))
			{
				this.CloseHandle(hPrinter)
				return true
			}
			this.CloseHandle(hPrinter)
		}
		return false
	}


	SetDefault(Printer)
	{
		if (DllCall("winspool.drv\SetDefaultPrinter", "str", Printer))
			return true
		return false
	}


	; ===== PRIVATE METHODS =====================================================================================================

	CloseHandle(hPrinter)
	{
		if (DllCall("winspool.drv\ClosePrinter", "ptr", hPrinter))
			return true
		return false
	}


	OpenHandle(PrinterName, DesiredAccess)
	{
		VarSetCapacity(PRINTER_DEFAULTS, A_PtrSize * 3, 0)
		NumPut(DesiredAccess, PRINTER_DEFAULTS, A_PtrSize * 2, "ptr")
		if (DllCall("winspool.drv\OpenPrinter", "str", PrinterName, "ptr*", hPrinter, "ptr", &PRINTER_DEFAULTS))
			return hPrinter
		return false
	}

}

; ===============================================================================================================================

Questions / Bugs / Issues
If you notice any kind of bugs or issues, report them here. Same for any kind of questions.


Copyright and License
The Unlicense
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Class Printers

21 Mar 2019, 10:29

Examples
Retrieves the printer name of the default printer for the current user on the local computer.

Code: Select all

MsgBox % Printers.GetDefault()

Sets the printer name of the default printer for the current user on the local computer.

Code: Select all

Printers.SetDefault("\\192.168.10.1\PRINTER_A")

Adds a connection to the specified printer for the current user.

Code: Select all

Printers.AddConnection("\\192.168.10.1\PRINTER_A")

Deletes a connection to a printer that was established by a call to AddPrinterConnection or ConnectToPrinterDlg.

Code: Select all

Printers.DeleteConnection("\\192.168.10.1\PRINTER_A")

Deletes the specified printer object.

Code: Select all

Printers.Delete("HP DeskJet")

Enumerates available printers, print servers, domains, or print providers.

Code: Select all

; 0x2 -> LOCAL | 0x4 -> CONNECTIONS
for k, v in Printers.Enum(0x2|0x4)
    MsgBox % v.PrinterName
Retrieves information about a specified printer.

Code: Select all

MsgBox % Printers.GetInfo("\\192.168.10.1\PRINTER_A").Location

Todo
- tba
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
gregster
Posts: 8940
Joined: 30 Sep 2013, 06:48

Re: Class Printers

21 Mar 2019, 10:53

Cool! Thank you. I will definitely use that.
DRocks
Posts: 565
Joined: 08 May 2018, 10:20

Re: Class Printers

21 Mar 2019, 13:19

Does that mean that you can Print with AHK scripts, sorry my ignorance

E.g. : Print content of a Gui window ?
iPhilip
Posts: 803
Joined: 02 Oct 2013, 12:21

Re: Class Printers

21 Mar 2019, 17:03

For reference:

Code: Select all

PRINTER_ENUM_DEFAULT     = 0x00000001
PRINTER_ENUM_LOCAL       = 0x00000002
PRINTER_ENUM_CONNECTIONS = 0x00000004
PRINTER_ENUM_FAVORITE    = 0x00000004
PRINTER_ENUM_NAME        = 0x00000008
PRINTER_ENUM_REMOTE      = 0x00000010
PRINTER_ENUM_SHARED      = 0x00000020
PRINTER_ENUM_NETWORK     = 0x00000040
PRINTER_ENUM_EXPAND      = 0x00004000
PRINTER_ENUM_CONTAINER   = 0x00008000
PRINTER_ENUM_ICONMASK    = 0x00ff0000
PRINTER_ENUM_ICON1       = 0x00010000
PRINTER_ENUM_ICON2       = 0x00020000
PRINTER_ENUM_ICON3       = 0x00040000
PRINTER_ENUM_ICON4       = 0x00080000
PRINTER_ENUM_ICON5       = 0x00100000
PRINTER_ENUM_ICON6       = 0x00200000
PRINTER_ENUM_ICON7       = 0x00400000
PRINTER_ENUM_ICON8       = 0x00800000
PRINTER_ENUM_HIDE        = 0x01000000
PRINTER_ENUM_CATEGORY_ALL= 0x02000000
PRINTER_ENUM_CATEGORY_3D = 0x04000000
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Class Printers

22 Mar 2019, 03:14

DRocks wrote:
21 Mar 2019, 13:19
Does that mean that you can Print with AHK scripts, sorry my ignorance

E.g. : Print content of a Gui window ?
Yes it is possible to print directly from Gui content like an Edit-Field.
Will add an example later.
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
DRocks
Posts: 565
Joined: 08 May 2018, 10:20

Re: Class Printers

22 Mar 2019, 06:26

jNizM wrote:
22 Mar 2019, 03:14
Yes it is possible to print directly from Gui content like an Edit-Field.
Will add an example later.
Wow. This is going to fill a hole! Thank you very much for this. I cant wait to have free time to experiment with the class and see your example.

I was going to need to learn html and css to print but maybe this will make it ! :D
Elendiar
Posts: 17
Joined: 03 Jul 2018, 05:44

Re: Class Printers

02 Jul 2020, 07:12

jNizM wrote:
22 Mar 2019, 03:14
Yes it is possible to print directly from Gui content like an Edit-Field.
Will add an example later.
No examples? :( Is it possible to send zpl command for Zerba thermoprinters with this ?
ostius
Posts: 5
Joined: 22 Mar 2019, 10:14

Re: Class Printers

02 Jul 2020, 11:04

I use AHK to interface with a retail POS system and would also be interested in sending control commands to printers (Star and Zebra printers). Thanks!
Elendiar
Posts: 17
Joined: 03 Jul 2018, 05:44

Re: Class Printers

02 Jul 2020, 13:37

I tried to add from winspool StartDocPrinter > StartPagePrinter > WritePrinter > EndPagePrinter> EndDocPrinter - with this sequence it works with win win32print python module. But its too hard for me make correct DllCall in ahk with all that bytes etc...
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Class Printers

03 Jul 2020, 01:02

For Thermoprinters you can use Socket Class

Code: Select all

#Include Class_Socket.ahk

; open to TCP-Socket to Printer via IP + 9100 Port
myTCP := new SocketTCP()
myTCP.connect("192.168.5.10", 9100)

; get raw data of your printers file
File := FileOpen("D:\printers\thermo.print", "r")
DataRead := File.RawRead(RawData, File.Length)

; send raw data
myTCP.send(&RawData, DataRead)

; close connection
File.Close()
myTCP.disconnect()


ExitApp
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
Elendiar
Posts: 17
Joined: 03 Jul 2018, 05:44

Re: Class Printers

07 Jul 2020, 05:09

jNizM wrote:
03 Jul 2020, 01:02
For Thermoprinters you can use Socket Class
I try this, but without RawPrintServer this doesnt works. I need work with default printer
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Class Printers

21 Jul 2020, 04:06

I rewrote the whole class and examples. And uploaded them to GitHub.

But remember, it's just a wrapper for the Print Spooler API Functions.
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
cyruz
Posts: 346
Joined: 30 Sep 2013, 13:31

Re: [Class] Printers

21 Jul 2020, 20:44

Hi jNizM,

I didn't try it yet (i'll do soon), but I have a couple of questions.

I don't know this API, does it handle printing jobs too?
In some situations the spooler is know to misbehave and needs a restart. Is there any internal call that allows to perform a reset/restart or do we have to rely on the service handler?
ABCza on the old forum.
My GitHub.
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: [Class] Printers

22 Jul 2020, 02:54

Hi cyruz,

1) Yes. See Print Job Functions
2) Not that I know of. The spooler would still need to be restarted via services.
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: [Class] Printers

22 Jul 2020, 08:13

@DRocks sorry for the late response
In this thread you will find an example how to print from an edit control or a string -> https://www.autohotkey.com/boards/viewtopic.php?p=78205#p78205
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
cyruz
Posts: 346
Joined: 30 Sep 2013, 13:31

Re: [Class] Printers

15 Mar 2021, 20:40

Ciao jNizM,

the API function used in the "DeleteConnection" method needs to be corrected to "DeletePrinterConnection".
ABCza on the old forum.
My GitHub.
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: [Class] Printers

12 Apr 2021, 03:12

thank you. is fixed
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
Bkid
Posts: 31
Joined: 13 Jun 2014, 12:19

Re: [Class] Printers

17 May 2021, 11:53

Forgive any errors or terrible code, I just learned a lot about DLLCalls and structs over the past few days :P

I made these functions to add into the class, that I used to print directly to a printer (sending ZPL code directly to a Zebra printer):

Code: Select all

StartDocPrinter(pHandle, dName, dType)
	{
		VarSetCapacity(DocInfo, A_PtrSize * 3, 0)
		NumPut(&dName, DocInfo, "Ptr")
		NumPut(0, DocInfo, A_PtrSize, "Ptr")
		NumPut(&dType, DocInfo, A_PtrSize * 2, "Ptr")
		if(pJob := DllCall("winspool.drv\StartDocPrinter", "ptr", pHandle, "uint", 1, "ptr", &DocInfo))
			return pJob
		return false
	}
	
StartPagePrinter(pHandle)
	{
		if (DllCall("winspool.drv\StartPagePrinter", "ptr", pHandle))
			return true
		return false
	}
	
WritePrinter(pHandle, Data, dCount)
	{
		if (DllCall("winspool.drv\WritePrinter", "ptr", pHandle, "ptr", &Data, "uint", dCount, "ptr*", bytesWritten))
			return bytesWritten
		return false
	}
	
EndPagePrinter(pHandle)
	{
		if (DllCall("winspool.drv\EndPagePrinter", "ptr", pHandle))
			return true
		return false
	}
	
EndDocPrinter(pHandle)
	{
		if (DllCall("winspool.drv\EndDocPrinter", "ptr", pHandle))
			return true
		return false
	}

Here is the code I used to print to the printer (include the above functions + class_printers):

Code: Select all

PrinterName := "ZDesigner ZD410-203dpi ZPL"

DocName := "test"
DataType := "RAW"
File := FileOpen("C:\Users\Bkid\Desktop\2x3test.txt", "r")
dLen := File.Length
File.RawRead(Data, dLen)

pHandle := Printers.OpenHandle(PrinterName, 0) ;Returns printer handle on success
if pHandle {
	pStart := StartDocPrinter(pHandle, DocName, DataType) ;Returns job number on success
	if (pStart > 0) {
		pSPP := StartPagePrinter(pHandle)
		if pSPP {
			dWritten := WritePrinter(pHandle, Data, dLen)
			EndPagePrinter(pHandle)
		}
		EndDocPrinter(pHandle)
	}
	Printers.CloseHandle(pHandle)
}
It was basically converting the C++ example here to AHK, with some minor changes. Also, I didn't do the check at the end to verify all the bytes were written. Oh well. :P

Hopefully this helps someone as much as it has helped me.
ostius
Posts: 5
Joined: 22 Mar 2019, 10:14

Re: [Class] Printers

09 Jul 2021, 10:04

Hi Bkid, thank you for the code! I can now send ZPL to a Zebra ZD410 printer. Do you know of a way to query the printer status? I can send an ~HS command using the Zebra communication tool but would like to integrate that functionality into an AHK program.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 67 guests