Created
March 15, 2011 16:37
-
-
Save EBNull/870992 to your computer and use it in GitHub Desktop.
Install script for https://sourceforge.net/projects/wphfforwin7 . Installs files, port monitor, port, driver, and printer.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Run this script to install system files, port monitor, port, driver, and printer | |
#You can change settings in the main() function | |
# | |
#(c) 2011 CBWhiz | |
import os | |
import sys | |
import ctypes | |
import subprocess | |
import codecs | |
import tempfile | |
import platform | |
import logging | |
from ctypes import * | |
from ctypes.wintypes import * | |
class StrictStructure(Structure): | |
"""Class to disallow setting invalid structure attributes. Helps prevent typos.""" | |
def __setattr__(self, attr, val): | |
if attr not in (x[0] for x in self._fields_): | |
raise AttributeError | |
super(StrictStructure, self).__setattr__(attr, val) | |
Structure = StrictStructure | |
#--------------------------------------- | |
#-----Begin Endless defines | |
#--------------------------------------- | |
class MONITOR_INFO_2(Structure): | |
_fields_ = ( | |
('name', LPWSTR), | |
('environ', LPWSTR), | |
('dll', LPWSTR), | |
) | |
ERROR_SUCCESS = 0 | |
MAX_PORTNAME_LEN = 64 | |
MAX_NETWORKNAME_LEN = 49 | |
MAX_SNMP_COMMUNITY_STR_LEN = 33 | |
MAX_QUEUENAME_LEN = 33 | |
MAX_IPADDR_STR_LEN = 16 | |
RESERVED_BYTE_ARRAY_SIZE = 540 | |
class PORT_DATA_1(Structure): | |
_fields_ = ( | |
('portName', WCHAR*MAX_PORTNAME_LEN), | |
('version', DWORD), | |
('protocol', DWORD), | |
('cbSize', DWORD), | |
('dwReserved', DWORD), | |
('hostAddress', WCHAR*MAX_NETWORKNAME_LEN), | |
('snmpCommunity', WCHAR*MAX_SNMP_COMMUNITY_STR_LEN), | |
('doubleSpool', DWORD), | |
('queue', WCHAR*MAX_QUEUENAME_LEN), | |
('ipAddress', WCHAR*MAX_IPADDR_STR_LEN), | |
('reserved', BYTE*RESERVED_BYTE_ARRAY_SIZE), | |
('portNumber', DWORD), | |
('snmpEnabled', DWORD), | |
('snmpDevIndex', DWORD), | |
) | |
PRINTER_ACCESS_SERVER_ADMIN = 0x1 | |
PRINTER_ACCESS_ADMINISTER = 0x4 | |
SERVER_ALL_ACCESS = 0xF0003 | |
PRINTER_ALL_ACCESS = 0xF000C | |
class PRINTER_DEFAULTS(Structure): | |
_fields_ = ( | |
('datatype', LPWSTR), | |
('devMode', c_void_p), #Actually points to a DEVMODE | |
('desiredAccess', DWORD), | |
) | |
class PRINTER_INFO_2(Structure): | |
_fields_ = ( | |
('serverName', LPWSTR), | |
('printerName', LPWSTR), | |
('shareName', LPWSTR), | |
('portName', LPWSTR), | |
('driverName', LPWSTR), | |
('comment', LPWSTR), | |
('location', LPWSTR), | |
('devMode', c_void_p), #Actually points to a DEVMODE | |
('sepFile', LPWSTR), | |
('printProcessor', LPWSTR), | |
('datatype', LPWSTR), | |
('parameters', LPWSTR), | |
('securityDescriptor', c_void_p), | |
('attributes', DWORD), | |
('priority', DWORD), | |
('defaultPriority', DWORD), | |
('startTime', DWORD), | |
('untilTime', DWORD), | |
('status', DWORD), | |
('cJobs', DWORD), | |
('averagePPM', DWORD), | |
) | |
PRINTER_ATTRIBUTE_LOCAL = 0x40 | |
PRINTER_ATTRIBUTE_NETWORK = 0x10 | |
PRINTER_ATTRIBUTE_FAX = 0x4000 | |
#--------------------------------------- | |
#-----End defines | |
#--------------------------------------- | |
def nullcatch(res, func, args): | |
"""Simple error catcher, if res=0 raise WinError()""" | |
if not res: | |
raise WinError() | |
return res | |
#The code below does no error checking of these APIs and relies on ctypes to do it for us. | |
#ctypes will call our .errcheck handler after each call, which can then raise an exception. | |
ctypes.windll['winspool.drv'].AddMonitorW.errcheck = nullcatch | |
ctypes.windll['winspool.drv'].DeleteMonitorW.errcheck = nullcatch | |
ctypes.windll['winspool.drv'].AddPrinterW.errcheck = nullcatch | |
ctypes.windll['winspool.drv'].DeletePrinter.errcheck = nullcatch | |
ctypes.windll['winspool.drv'].OpenPrinterW.errcheck = nullcatch | |
ctypes.windll['winspool.drv'].ClosePrinter.errcheck = nullcatch | |
ctypes.windll['winspool.drv'].XcvDataW.errcheck = nullcatch | |
class PrinterPorts(object): | |
"""Manages Printer Ports (LPT1:. etc)""" | |
@classmethod | |
def add(cls, portName, portType): | |
"""Creates a new printer port. portName is generally in the format 'XXXN:'""" | |
return cls._add_rm_port(portName, portType, remove=False) | |
@classmethod | |
def delete(cls, portName, portType): | |
"""Removes an existing printer port. portName is generally in the format 'XXXN:'""" | |
return cls._add_rm_port(portName, portType, remove=True) | |
@classmethod | |
def _add_rm_port(cls, portName, portType, remove): | |
printersettings = PRINTER_DEFAULTS() | |
printersettings.datatype = None | |
printersettings.devMode = None | |
printersettings.desiredAccess = PRINTER_ACCESS_SERVER_ADMIN | |
printer = HANDLE() | |
ctypes.windll['winspool.drv'].OpenPrinterW(u",XcvMonitor %s"%(portType), byref(printer), byref(printersettings)) | |
pd = PORT_DATA_1() | |
pd.version = 1 | |
pd.protocol = 1 #1 = RAW, 515 = LPR | |
pd.portNumber = 9100 #9100 default for RAW | |
pd.portName = portName | |
pd.cbSize = sizeof(pd) | |
status = DWORD() | |
outputreq = DWORD() | |
output = DWORD() | |
if not remove: | |
ctypes.windll['winspool.drv'].XcvDataW(printer, u"AddPort", byref(pd), pd.cbSize, byref(output), sizeof(output), byref(outputreq), byref(status)) | |
else: | |
ctypes.windll['winspool.drv'].XcvDataW(printer, u"DeletePort", byref(pd), pd.cbSize, byref(output), sizeof(output), byref(outputreq), byref(status)) | |
if status.value: | |
raise WinError(status.value) | |
ctypes.windll['winspool.drv'].ClosePrinter(printer) | |
class Printers(object): | |
"""Manages printers""" | |
@classmethod | |
def add(cls, name, port, driver, is_fax=False): | |
pi = PRINTER_INFO_2() | |
pi.serverName = None | |
pi.printerName = name | |
pi.shareName = None | |
pi.portName = port | |
pi.driverName = driver | |
pi.printProcessor = "WinPrint" | |
pi.securityDescriptor=None | |
pi.attributes=PRINTER_ATTRIBUTE_LOCAL | (PRINTER_ATTRIBUTE_FAX if is_fax else 0) | |
printer = ctypes.windll['winspool.drv'].AddPrinterW(None, 2, byref(pi)) | |
if printer: | |
printer = ctypes.windll['winspool.drv'].ClosePrinter(printer) | |
@classmethod | |
def delete(cls, name): | |
printersettings = PRINTER_DEFAULTS() | |
printersettings.datatype = None | |
printersettings.devMode = None | |
printersettings.desiredAccess = PRINTER_ALL_ACCESS | |
printer = HANDLE() | |
ctypes.windll['winspool.drv'].OpenPrinterW(unicode(name), byref(printer), byref(printersettings)) | |
ctypes.windll['winspool.drv'].DeletePrinter(printer) | |
if printer: | |
printer = ctypes.windll['winspool.drv'].ClosePrinter(printer) | |
class PrinterDrivers(object): | |
"""Manages Printer Drivers""" | |
@classmethod | |
def add_system_driver(cls, driver): | |
p = subprocess.Popen(r'rundll32 printui.dll,PrintUIEntry /ia /m "%s" /f "%s\inf\ntprint.inf" /u'%(driver, os.environ['windir'])) | |
p.wait() | |
class PortMonitors(object): | |
"""Manages Port Monitors (Port Types)""" | |
@classmethod | |
def add(cls, name, environ, dll): | |
moninfo = MONITOR_INFO_2() | |
moninfo.name = name | |
moninfo.environ = environ | |
moninfo.dll = dll | |
ctypes.windll['winspool.drv'].AddMonitorW(None, 2, byref(moninfo)) | |
@classmethod | |
def delete(cls, name, environ): | |
ctypes.windll['winspool.drv'].DeleteMonitorW(None, unicode(environ), unicode(name)) | |
class NoWow64Redirect(object): | |
def __enter__(self): | |
self._pold = c_void_p() | |
try: | |
ctypes.windll.kernel32.Wow64DisableWow64FsRedirection(byref(self._pold)) | |
except: | |
pass | |
def __exit__(self, e0, e1, e2): | |
try: | |
ctypes.windll.kernel32.Wow64RevertWow64FsRedirection(byref(self._pold)) | |
except: | |
pass | |
def setup_hylafax_port(port, monitor, server, username="fax", password="", email="", addresspath="", pagesize="US Letter"): | |
data = u"""Windows Registry Editor Version 5.00 | |
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Monitors\%s\Ports\%s] | |
"Description"="%s Port" | |
"Server"="%s" | |
"Username"="%s" | |
"Password"="%s" | |
"DefaultEmail"="%s" | |
"Modem"="" | |
"AddressBookPath"="%s" | |
"NotificationType"="Failure and Success" | |
"PageSize"="%s" | |
"IgnorePassiveIP"="0" | |
"AddressBookType"="" | |
"Resolution"="Fine" | |
""" | |
data = data%(monitor, port, monitor, server, username, password, email, addresspath.replace('\\', '\\\\'), pagesize) | |
outf = tempfile.NamedTemporaryFile(delete=False, suffix='.hylafax.reg') | |
outf.write(codecs.BOM_UTF16_LE + data.encode('utf-16le')) | |
outf.close() | |
p = subprocess.Popen(['regedit', '/s', outf.name]) | |
p.wait() | |
os.unlink(outf.name) | |
CSIDL_SYSTEM = 0x25 | |
CSIDL_SYSTEMX86 = 0x29 | |
def is_system_64bit(): | |
sys = create_unicode_buffer(260) | |
sys32 = create_unicode_buffer(260) | |
with NoWow64Redirect(): | |
ctypes.windll.shell32.SHGetSpecialFolderPathW(None, byref(sys), CSIDL_SYSTEM, 0) | |
ctypes.windll.shell32.SHGetSpecialFolderPathW(None, byref(sys32), CSIDL_SYSTEMX86, 0) | |
sys = sys.value | |
sys32 = sys32.value | |
if all((sys, sys32)): | |
if sys == sys32: | |
return False | |
if sys != sys32: | |
return True | |
else: | |
return False | |
def copy_hylafax_files(source=''): | |
sys = create_unicode_buffer(260) | |
with NoWow64Redirect(): | |
ctypes.windll.shell32.SHGetSpecialFolderPathW(None, byref(sys), CSIDL_SYSTEM, 0) | |
sys = sys.value | |
shutil.copy(os.path.join(source, "HylaPrintMon.dll"), sys) | |
shutil.copy(os.path.join(source, "HylaPrintUI.exe"), sys) | |
def delete_hylafax_files(): | |
sys = create_unicode_buffer(260) | |
with NoWow64Redirect(): | |
ctypes.windll.shell32.SHGetSpecialFolderPathW(None, byref(sys), CSIDL_SYSTEM, 0) | |
sys = sys.value | |
os.unlink(os.path.join(sys, "HylaPrintMon.dll")) | |
os.unlink(os.path.join(sys, "HylaPrintUI.exe")) | |
def main(argv): | |
log = logging.getLogger('main') | |
printer_name = "Fax" | |
port_name = u"HFAX1:" | |
mon_name = "Winprint Hylafax" | |
mon_dll = "HylaPrintMon.dll" | |
hylafax_server_opts = { | |
'server': '192.168.1.2', | |
'username': 'fax', | |
'password': '', | |
'email': '', | |
'addresspath': r'' | |
} | |
import platform | |
if int(platform.win32_ver()[1].split('.')[0]) < 6: | |
#pre-vista | |
driver_name = u"Apple LaserWriter 12/640 PS" | |
else: | |
driver_name = u"Xerox Phaser 6250B PS" | |
sixfour = is_system_64bit() | |
if "--32" in argv: | |
sixfour = False | |
elif "--64" in argv: | |
sixfour = True | |
mon_environ = "Windows NT x86" | |
if sixfour: | |
mon_environ = "Windows x64" | |
if "--remove" not in argv: | |
log.info("Setting up %s on port %s using %s with driver %s", printer_name, port_name, mon_name, driver_name) | |
log.info("Copying hylafax files") | |
try: | |
if sixfour: | |
copy_hylafax_files(os.path.join(__dir__, 'Only-For-Windows-64bit')) | |
else: | |
copy_hylafax_files(os.path.join(__dir__, 'Only-For-Windows-32bit')) | |
except Exception as e: | |
log.error("Could not copy hylafax files: %s", e) | |
log.info("Creating port monitor %s", mon_name) | |
try: | |
PortMonitors.add(mon_name, mon_environ, mon_dll) | |
except WindowsError as e: | |
if e.winerror == 3006: | |
log.error("Could not create monitor: %s", e) | |
else: | |
raise | |
log.info("Creating port %s of type %s", port_name, mon_name) | |
try: | |
PrinterPorts.add(port_name, mon_name) | |
setup_hylafax_port(port_name, mon_name, **hylafax_server_opts) | |
except WindowsError as e: | |
if e.winerror == 183: | |
log.error("Could not create port: %s", e) | |
else: | |
raise | |
log.info("Adding system driver %s", driver_name) | |
PrinterDrivers.add_system_driver(driver_name) | |
log.info("Creating printer %s", printer_name) | |
try: | |
Printers.add(printer_name, port_name, driver_name, is_fax=True) | |
except WindowsError as e: | |
if e.winerror == 1802: | |
log.error("Could not create printer: %s", e) | |
else: | |
raise | |
log.info("Done.") | |
else: | |
log.info("Removing printer %s", printer_name) | |
try: | |
Printers.delete(printer_name) | |
except WindowsError as e: | |
if e.winerror == 1801: | |
log.error("Could not remove printer: %s", e) | |
else: | |
raise | |
log.warn("Not removing driver %s", driver_name) | |
log.info("Removing port %s", port_name) | |
try: | |
PrinterPorts.delete(port_name, mon_name) | |
except Exception as e: | |
log.error("Could not remove port: %s", e) | |
log.info("Removing port monitor %s", mon_name) | |
try: | |
PortMonitors.delete(mon_name, mon_environ) | |
except WindowsError as e: | |
if e.winerror == 3000: | |
log.error("Could not remove port monitor: %s", e) | |
else: | |
raise | |
log.info("Removing hylafax files") | |
try: | |
delete_hylafax_files() | |
except Exception as e: | |
log.error("Could not remove hylafax files: %s", e) | |
if __name__ == '__main__': | |
logging.basicConfig(level=logging.DEBUG) | |
main(sys.argv) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment