diff options
Diffstat (limited to 'windows/printing.c')
-rw-r--r-- | windows/printing.c | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/windows/printing.c b/windows/printing.c new file mode 100644 index 00000000..2286c236 --- /dev/null +++ b/windows/printing.c @@ -0,0 +1,228 @@ +/* + * Printing interface for PuTTY. + */ + +#include "putty.h" +#include <winspool.h> + +struct printer_enum_tag { + int nprinters; + DWORD enum_level; + union { + LPPRINTER_INFO_4 i4; + LPPRINTER_INFO_5 i5; + } info; +}; + +struct printer_job_tag { + HANDLE hprinter; +}; + +DECL_WINDOWS_FUNCTION(static, BOOL, EnumPrinters, + (DWORD, LPTSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD)); +DECL_WINDOWS_FUNCTION(static, BOOL, OpenPrinter, + (LPTSTR, LPHANDLE, LPPRINTER_DEFAULTS)); +DECL_WINDOWS_FUNCTION(static, BOOL, ClosePrinter, (HANDLE)); +DECL_WINDOWS_FUNCTION(static, DWORD, StartDocPrinter, (HANDLE, DWORD, LPBYTE)); +DECL_WINDOWS_FUNCTION(static, BOOL, EndDocPrinter, (HANDLE)); +DECL_WINDOWS_FUNCTION(static, BOOL, StartPagePrinter, (HANDLE)); +DECL_WINDOWS_FUNCTION(static, BOOL, EndPagePrinter, (HANDLE)); +DECL_WINDOWS_FUNCTION(static, BOOL, WritePrinter, + (HANDLE, LPVOID, DWORD, LPDWORD)); + +static void init_winfuncs(void) +{ + static bool initialised = false; + if (initialised) + return; + { + HMODULE winspool_module = load_system32_dll("winspool.drv"); + /* Some MSDN documentation claims that some of the below functions + * should be loaded from spoolss.dll, but this doesn't seem to + * be reliable in practice. + * Nevertheless, we load spoolss.dll ourselves using our safe + * loading method, against the possibility that winspool.drv + * later loads it unsafely. */ + (void) load_system32_dll("spoolss.dll"); + GET_WINDOWS_FUNCTION_PP(winspool_module, EnumPrinters); + GET_WINDOWS_FUNCTION_PP(winspool_module, OpenPrinter); + GET_WINDOWS_FUNCTION_PP(winspool_module, ClosePrinter); + GET_WINDOWS_FUNCTION_PP(winspool_module, StartDocPrinter); + GET_WINDOWS_FUNCTION_PP(winspool_module, EndDocPrinter); + GET_WINDOWS_FUNCTION_PP(winspool_module, StartPagePrinter); + GET_WINDOWS_FUNCTION_PP(winspool_module, EndPagePrinter); + GET_WINDOWS_FUNCTION_PP(winspool_module, WritePrinter); + } + initialised = true; +} + +static bool printer_add_enum(int param, DWORD level, char **buffer, + int offset, int *nprinters_ptr) +{ + DWORD needed = 0, nprinters = 0; + + init_winfuncs(); + + *buffer = sresize(*buffer, offset+512, char); + + /* + * Exploratory call to EnumPrinters to determine how much space + * we'll need for the output. + * + * If we get ERROR_INSUFFICIENT_BUFFER, that's fine, we're + * prepared to deal with it. Any other error, we return failure. + */ + if (p_EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset), 512, + &needed, &nprinters) == 0 && + GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return false; + + if (needed < 512) + needed = 512; + + *buffer = sresize(*buffer, offset+needed, char); + + if (p_EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset), + needed, &needed, &nprinters) == 0) + return false; + + *nprinters_ptr += nprinters; + + return true; +} + +printer_enum *printer_start_enum(int *nprinters_ptr) +{ + printer_enum *ret = snew(printer_enum); + char *buffer = NULL; + + *nprinters_ptr = 0; /* default return value */ + buffer = snewn(512, char); + + /* + * Determine what enumeration level to use. + * When enumerating printers, we need to use PRINTER_INFO_4 on + * NT-class systems to avoid Windows looking too hard for them and + * slowing things down; and we need to avoid PRINTER_INFO_5 as + * we've seen network printers not show up. + * On 9x-class systems, PRINTER_INFO_4 isn't available and + * PRINTER_INFO_5 is recommended. + * Bletch. + */ + if (osPlatformId != VER_PLATFORM_WIN32_NT) { + ret->enum_level = 5; + } else { + ret->enum_level = 4; + } + + if (!printer_add_enum(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, + ret->enum_level, &buffer, 0, nprinters_ptr)) + goto error; + + switch (ret->enum_level) { + case 4: + ret->info.i4 = (LPPRINTER_INFO_4)buffer; + break; + case 5: + ret->info.i5 = (LPPRINTER_INFO_5)buffer; + break; + } + ret->nprinters = *nprinters_ptr; + + return ret; + + error: + sfree(buffer); + sfree(ret); + *nprinters_ptr = 0; + return NULL; +} + +char *printer_get_name(printer_enum *pe, int i) +{ + if (!pe) + return NULL; + if (i < 0 || i >= pe->nprinters) + return NULL; + switch (pe->enum_level) { + case 4: + return pe->info.i4[i].pPrinterName; + case 5: + return pe->info.i5[i].pPrinterName; + default: + return NULL; + } +} + +void printer_finish_enum(printer_enum *pe) +{ + if (!pe) + return; + switch (pe->enum_level) { + case 4: + sfree(pe->info.i4); + break; + case 5: + sfree(pe->info.i5); + break; + } + sfree(pe); +} + +printer_job *printer_start_job(char *printer) +{ + printer_job *ret = snew(printer_job); + DOC_INFO_1 docinfo; + bool jobstarted = false, pagestarted = false; + + init_winfuncs(); + + ret->hprinter = NULL; + if (!p_OpenPrinter(printer, &ret->hprinter, NULL)) + goto error; + + docinfo.pDocName = "PuTTY remote printer output"; + docinfo.pOutputFile = NULL; + docinfo.pDatatype = "RAW"; + + if (!p_StartDocPrinter(ret->hprinter, 1, (LPBYTE)&docinfo)) + goto error; + jobstarted = true; + + if (!p_StartPagePrinter(ret->hprinter)) + goto error; + pagestarted = true; + + return ret; + + error: + if (pagestarted) + p_EndPagePrinter(ret->hprinter); + if (jobstarted) + p_EndDocPrinter(ret->hprinter); + if (ret->hprinter) + p_ClosePrinter(ret->hprinter); + sfree(ret); + return NULL; +} + +void printer_job_data(printer_job *pj, const void *data, size_t len) +{ + DWORD written; + + if (!pj) + return; + + p_WritePrinter(pj->hprinter, (void *)data, len, &written); +} + +void printer_finish_job(printer_job *pj) +{ + if (!pj) + return; + + p_EndPagePrinter(pj->hprinter); + p_EndDocPrinter(pj->hprinter); + p_ClosePrinter(pj->hprinter); + sfree(pj); +} |