Page 1 of 1

[Class] Printers

Posted: 21 Mar 2019, 10:29
by jNizM
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 ..: 2020-07-22
; 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\DeleteConnection", "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

Re: Class Printers

Posted: 21 Mar 2019, 10:29
by jNizM
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

Re: Class Printers

Posted: 21 Mar 2019, 10:53
by gregster
Cool! Thank you. I will definitely use that.

Re: Class Printers

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

E.g. : Print content of a Gui window ?

Re: Class Printers

Posted: 21 Mar 2019, 17:03
by iPhilip
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

Re: Class Printers

Posted: 22 Mar 2019, 03:14
by jNizM
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.

Re: Class Printers

Posted: 22 Mar 2019, 06:26
by DRocks
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

Re: Class Printers

Posted: 02 Jul 2020, 07:12
by Elendiar
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 ?

Re: Class Printers

Posted: 02 Jul 2020, 11:04
by ostius
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!

Re: Class Printers

Posted: 02 Jul 2020, 13:37
by Elendiar
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...

Re: Class Printers

Posted: 03 Jul 2020, 01:02
by jNizM
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

Re: Class Printers

Posted: 07 Jul 2020, 05:09
by Elendiar
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

Re: Class Printers

Posted: 21 Jul 2020, 04:06
by jNizM
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.

Re: [Class] Printers

Posted: 21 Jul 2020, 20:44
by cyruz
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?

Re: [Class] Printers

Posted: 22 Jul 2020, 02:54
by jNizM
Hi cyruz,

1) Yes. See Print Job Functions
2) Not that I know of. The spooler would still need to be restarted via services.

Re: [Class] Printers

Posted: 22 Jul 2020, 08:13
by jNizM
@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