-- v2: winspool: Add SeekPrinter implementation. localspl: Add partial SeekPrinter implementation. localspl: Use print processor in ScheduleJob.
From: Piotr Caban piotr@codeweavers.com
--- dlls/localspl/provider.c | 65 ++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 15 deletions(-)
diff --git a/dlls/localspl/provider.c b/dlls/localspl/provider.c index dd6b6b9cf45..82ab5299129 100644 --- a/dlls/localspl/provider.c +++ b/dlls/localspl/provider.c @@ -234,6 +234,8 @@ typedef struct { LONG ref;
WCHAR *port; + WCHAR *print_proc; + WCHAR *datatype;
CRITICAL_SECTION jobs_cs; struct list jobs; @@ -521,13 +523,32 @@ static printer_info_t *find_printer_info(const WCHAR *name, unsigned int len) return NULL; }
+static WCHAR * reg_query_value(HKEY key, const WCHAR *name) +{ + DWORD size, type; + WCHAR *ret; + + if (RegQueryValueExW(key, name, 0, &type, NULL, &size) != ERROR_SUCCESS + || type != REG_SZ) + return NULL; + + ret = malloc(size); + if (!ret) + return NULL; + + if (RegQueryValueExW(key, name, 0, NULL, (BYTE *)ret, &size) != ERROR_SUCCESS) + { + free(ret); + return NULL; + } + return ret; +} + static printer_info_t* get_printer_info(const WCHAR *name) { HKEY hkey, hprinter = NULL; printer_info_t *info; - WCHAR port[MAX_PATH]; LSTATUS ret; - DWORD size;
EnterCriticalSection(&printers_cs); info = find_printer_info(name, -1); @@ -541,32 +562,44 @@ static printer_info_t* get_printer_info(const WCHAR *name) if (ret == ERROR_SUCCESS) ret = RegOpenKeyW(hkey, name, &hprinter); RegCloseKey(hkey); - size = sizeof(port); - if (ret == ERROR_SUCCESS) - ret = RegQueryValueExW(hprinter, L"Port", 0, NULL, (BYTE*)port, &size); - RegCloseKey(hprinter); if (ret != ERROR_SUCCESS) { LeaveCriticalSection(&printers_cs); return NULL; }
- if ((info = calloc(1, sizeof(*info))) && (info->name = wcsdup(name)) && - (info->port = wcsdup(port))) + info = calloc(1, sizeof(*info)); + if (!info) { - info->ref = 1; - list_add_head(&printers, &info->entry); - InitializeCriticalSection(&info->jobs_cs); - list_init(&info->jobs); + LeaveCriticalSection(&printers_cs); + RegCloseKey(hprinter); + return NULL; } - else if (info) + + info->name = wcsdup(name); + info->port = reg_query_value(hprinter, L"Port"); + info->print_proc = reg_query_value(hprinter, L"Print Processor"); + info->datatype = reg_query_value(hprinter, L"Datatype"); + RegCloseKey(hprinter); + + if (!info->name || !info->port || !info->print_proc || !info->datatype) { free(info->name); + free(info->port); + free(info->print_proc); + free(info->datatype); free(info); - info = NULL; + + LeaveCriticalSection(&printers_cs); + return NULL; } - LeaveCriticalSection(&printers_cs);
+ info->ref = 1; + list_add_head(&printers, &info->entry); + InitializeCriticalSection(&info->jobs_cs); + list_init(&info->jobs); + + LeaveCriticalSection(&printers_cs); return info; }
@@ -594,6 +627,8 @@ static void release_printer_info(printer_info_t *info)
free(info->name); free(info->port); + free(info->print_proc); + free(info->datatype); DeleteCriticalSection(&info->jobs_cs); while (!list_empty(&info->jobs)) {
From: Piotr Caban piotr@codeweavers.com
--- dlls/localspl/provider.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/dlls/localspl/provider.c b/dlls/localspl/provider.c index 82ab5299129..69f01850edd 100644 --- a/dlls/localspl/provider.c +++ b/dlls/localspl/provider.c @@ -835,14 +835,8 @@ static monitor_t * monitor_load(LPCWSTR name, LPWSTR dllname) lstrcatW(regroot, name); if (RegOpenKeyW(HKEY_LOCAL_MACHINE, regroot, &hroot) == ERROR_SUCCESS) { /* Get the Driver from the Registry */ - if (driver == NULL) { - DWORD namesize; - if (RegQueryValueExW(hroot, L"Driver", NULL, NULL, NULL, - &namesize) == ERROR_SUCCESS) { - driver = malloc(namesize); - RegQueryValueExW(hroot, L"Driver", NULL, NULL, (BYTE*)driver, &namesize); - } - } + if (!driver) + driver = reg_query_value(hroot, L"Driver"); } else WARN("%s not found\n", debugstr_w(regroot));
From: Piotr Caban piotr@codeweavers.com
--- dlls/localspl/provider.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/localspl/provider.c b/dlls/localspl/provider.c index 69f01850edd..babc17afa31 100644 --- a/dlls/localspl/provider.c +++ b/dlls/localspl/provider.c @@ -223,6 +223,7 @@ typedef struct { DWORD id; WCHAR *filename; WCHAR *port; + WCHAR *datatype; WCHAR *document_title; DEVMODEW *devmode; HANDLE hf; @@ -608,6 +609,7 @@ static void free_job(job_info_t *job) list_remove(&job->entry); free(job->filename); free(job->port); + free(job->datatype); free(job->document_title); free(job->devmode); CloseHandle(job->hf); @@ -3215,6 +3217,7 @@ static job_info_t* add_job(printer_t *printer, DOC_INFO_1W *info, BOOL create) job->hf = NULL; } job->document_title = wcsdup(info->pDocName); + job->datatype = wcsdup(info->pDatatype); job->devmode = dup_devmode(printer->devmode);
EnterCriticalSection(&printer->info->jobs_cs);
From: Piotr Caban piotr@codeweavers.com
--- dlls/localspl/provider.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/dlls/localspl/provider.c b/dlls/localspl/provider.c index babc17afa31..97f77c32ba5 100644 --- a/dlls/localspl/provider.c +++ b/dlls/localspl/provider.c @@ -275,7 +275,8 @@ typedef struct { typedef struct { handle_header_t header; printer_info_t *info; - LPWSTR name; + WCHAR *name; + WCHAR *datatype; DEVMODEW *devmode; job_info_t *doc; } printer_t; @@ -1841,6 +1842,8 @@ static HANDLE printer_alloc_handle(const WCHAR *name, const WCHAR *basename, return NULL; }
+ if (def && def->pDatatype) + printer->datatype = wcsdup(def->pDatatype); if (def && def->pDevMode) printer->devmode = dup_devmode(def->pDevMode);
@@ -3772,6 +3775,7 @@ static BOOL WINAPI fpClosePrinter(HANDLE hprinter)
release_printer_info(printer->info); free(printer->name); + free(printer->datatype); free(printer->devmode); free(printer); }
From: Piotr Caban piotr@codeweavers.com
--- dlls/localspl/provider.c | 198 ++++++++++++++++++++++++++++++--------- 1 file changed, 155 insertions(+), 43 deletions(-)
diff --git a/dlls/localspl/provider.c b/dlls/localspl/provider.c index 97f77c32ba5..f4bc6fb1389 100644 --- a/dlls/localspl/provider.c +++ b/dlls/localspl/provider.c @@ -292,7 +292,7 @@ static LONG last_job_id; static const WCHAR fmt_driversW[] = L"System\CurrentControlSet\control\Print\Environments\%s\Drivers%s"; static const WCHAR fmt_printprocessorsW[] = - L"System\CurrentControlSet\Control\Print\Environments\%s\Print Processors"; + L"System\CurrentControlSet\Control\Print\Environments\%s\Print Processors\"; static const WCHAR monitorsW[] = L"System\CurrentControlSet\Control\Print\Monitors\"; static const WCHAR printersW[] = L"System\CurrentControlSet\Control\Print\Printers"; static const WCHAR winnt_cv_portsW[] = L"Software\Microsoft\Windows NT\CurrentVersion\Ports"; @@ -3557,19 +3557,127 @@ static BOOL WINAPI fpGetJob(HANDLE hprinter, DWORD job_id, DWORD level, return ret; }
+typedef struct { + HMODULE hmod; + WCHAR *name; + BOOL (WINAPI *enum_datatypes)(WCHAR *, WCHAR *, DWORD, + BYTE *, DWORD, DWORD *, DWORD *); + HANDLE (WINAPI *open)(WCHAR *, PRINTPROCESSOROPENDATA *); + BOOL (WINAPI *print)(HANDLE, WCHAR *); + BOOL (WINAPI *close)(HANDLE); +} printproc_t; + +static printproc_t * print_proc_load(const WCHAR *name) +{ + WCHAR *reg_path, path[2 * MAX_PATH]; + printproc_t *ret; + DWORD size, len; + LSTATUS status; + HKEY hkey; + + size = sizeof(fmt_printprocessorsW) + + (wcslen(env_arch.envname) + wcslen(name)) * sizeof(WCHAR); + reg_path = malloc(size); + if (!reg_path) + return NULL; + swprintf(reg_path, size / sizeof(WCHAR), fmt_printprocessorsW, env_arch.envname); + wcscat(reg_path, name); + + status = RegOpenKeyW(HKEY_LOCAL_MACHINE, reg_path, &hkey); + free(reg_path); + if (status != ERROR_SUCCESS) + return NULL; + + if (!fpGetPrintProcessorDirectory(NULL, NULL, 1, (BYTE *)path, sizeof(path), &size)) + { + RegCloseKey(hkey); + return NULL; + } + len = size / sizeof(WCHAR); + path[len - 1] = '\'; + + size = sizeof(path) - len * sizeof(WCHAR); + status = RegQueryValueExW(hkey, L"Driver", NULL, NULL, (BYTE *)(path + len), &size); + RegCloseKey(hkey); + if (status != ERROR_SUCCESS) + return NULL; + + ret = malloc(sizeof(*ret)); + if (!ret) + return NULL; + + TRACE("loading print processor: %s\n", debugstr_w(path)); + + ret->hmod = LoadLibraryW(path); + if (!ret->hmod) + { + free(ret); + return NULL; + } + + ret->enum_datatypes = (void *)GetProcAddress(ret->hmod, "EnumPrintProcessorDatatypesW"); + ret->open = (void *)GetProcAddress(ret->hmod, "OpenPrintProcessor"); + ret->print = (void *)GetProcAddress(ret->hmod, "PrintDocumentOnPrintProcessor"); + ret->close = (void *)GetProcAddress(ret->hmod, "ClosePrintProcessor"); + if (!ret->enum_datatypes || !ret->open || !ret->print || !ret->close) + { + FreeLibrary(ret->hmod); + free(ret); + return NULL; + } + + ret->name = wcsdup(name); + return ret; +} + +static BOOL print_proc_check_datatype(printproc_t *pp, const WCHAR *datatype) +{ + DATATYPES_INFO_1W *types; + DWORD size, no, i; + + if (!datatype) + return FALSE; + + pp->enum_datatypes(NULL, pp->name, 1, NULL, 0, &size, &no); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return FALSE; + + types = malloc(size); + if (!types) + return FALSE; + + if (!pp->enum_datatypes(NULL, pp->name, 1, (BYTE *)types, size, &size, &no)) + { + free(types); + return FALSE; + } + + for (i = 0; i < no; i++) + { + if (!wcscmp(types[i].pName, datatype)) + break; + } + free(types); + return i < no; +} + +static void print_proc_unload(printproc_t *pp) +{ + FreeLibrary(pp->hmod); + free(pp->name); + free(pp); +} + static BOOL WINAPI fpScheduleJob(HANDLE hprinter, DWORD job_id) { - BOOL ret_startdoc = FALSE, ret_open = FALSE, ret = TRUE; printer_t *printer = (printer_t *)hprinter; + WCHAR output[1024], name[1024], *datatype; + PRINTPROCESSOROPENDATA pp_data; const WCHAR *port_name, *port; - WCHAR output[1024]; - DOC_INFO_1W info; job_info_t *job; - monitor_t *mon; - BYTE buf[4096]; - HANDLE hport; - DWORD r, w; - HANDLE hf; + printproc_t *pp; + BOOL ret = TRUE; + HANDLE hpp; HKEY hkey;
TRACE("%p %ld\n", hprinter, job_id); @@ -3594,18 +3702,6 @@ static BOOL WINAPI fpScheduleJob(HANDLE hprinter, DWORD job_id) TRACE("need to schedule job %ld filename %s to port %s\n", job->id, debugstr_w(job->filename), debugstr_w(port));
- hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, 0, NULL); - if (hf == INVALID_HANDLE_VALUE) - { - WARN("can't open spool file: %s\n", debugstr_w(job->filename)); - DeleteFileW(job->filename); - free_job(job); - LeaveCriticalSection(&printer->info->jobs_cs); - return FALSE; - } - - /* TODO: use print processor */ port_name = port; if ((isalpha(port[0]) && port[1] == ':') || !wcsncmp(port, L"FILE:", ARRAY_SIZE(L"FILE:") - 1)) @@ -3623,37 +3719,53 @@ static BOOL WINAPI fpScheduleJob(HANDLE hprinter, DWORD job_id) RegCloseKey(hkey); }
- if (!(mon = monitor_load_by_port(port_name)) || !mon->monitor.pfnOpenPort || - !mon->monitor.pfnStartDocPort || !mon->monitor.pfnWritePort || - !mon->monitor.pfnEndDocPort || !mon->monitor.pfnClosePort) + pp = print_proc_load(printer->info->print_proc); + if (!pp) { - FIXME("port not supported: %s\n", debugstr_w(port_name)); - ret = FALSE; + WARN("failed to load %s print processor\n", debugstr_w(printer->info->print_proc)); + pp = print_proc_load(L"winprint"); } + if (!pp) + return FALSE;
- if (ret) - ret = ret_open = mon->monitor.pfnOpenPort(mon->hmon, (WCHAR *)port_name, &hport); + if (job->datatype) + datatype = job->datatype; + else if (printer->datatype) + datatype = printer->datatype; + else + datatype = printer->info->datatype;
- if (ret) + if (!print_proc_check_datatype(pp, datatype)) { - info.pDocName = job->document_title; - info.pOutputFile = (WCHAR *)port; - info.pDatatype = NULL; - ret = ret_startdoc = mon->monitor.pfnStartDocPort(hport, printer->info->name, - job_id, 1, (BYTE *)&info); + WARN("%s datatype not supported by %s\n", debugstr_w(datatype), + debugstr_w(printer->info->print_proc)); + print_proc_unload(pp); + return FALSE; }
- while (ret && ReadFile(hf, buf, sizeof(buf), &r, NULL) && r) - ret = mon->monitor.pfnWritePort(hport, buf, r, &w) && r == w; + swprintf(name, ARRAY_SIZE(name), L"%s, Port", port_name); + pp_data.pDevMode = job->devmode; + pp_data.pDatatype = datatype; + pp_data.pParameters = NULL; + pp_data.pDocumentName = job->document_title; + pp_data.JobId = job->id; + pp_data.pOutputFile = (WCHAR *)port; + pp_data.pPrinterName = printer->name; + hpp = pp->open(name, &pp_data); + if (!hpp) + { + WARN("OpenPrintProcessor failed %ld\n", GetLastError()); + print_proc_unload(pp); + return FALSE; + }
- if (ret_startdoc) - mon->monitor.pfnEndDocPort(hport); - if (ret_open) - mon->monitor.pfnClosePort(hport); - if (mon) - monitor_unload(mon); + swprintf(name, ARRAY_SIZE(name), L"%s, Job %d", printer->name, job->id); + ret = pp->print(hpp, name); + if (!ret) + WARN("PrintDocumentOnPrintProcessor failed %ld\n", GetLastError()); + pp->close(hpp); + print_proc_unload(pp);
- CloseHandle(hf); DeleteFileW(job->filename); free_job(job); LeaveCriticalSection(&printer->info->jobs_cs);
From: Piotr Caban piotr@codeweavers.com
--- dlls/localspl/provider.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/dlls/localspl/provider.c b/dlls/localspl/provider.c index f4bc6fb1389..790d9d64c00 100644 --- a/dlls/localspl/provider.c +++ b/dlls/localspl/provider.c @@ -3899,6 +3899,35 @@ static BOOL WINAPI fpClosePrinter(HANDLE hprinter) return TRUE; }
+static BOOL WINAPI fpSeekPrinter(HANDLE hprinter, LARGE_INTEGER distance, + LARGE_INTEGER *pos, DWORD method, BOOL bwrite) +{ + job_t *job = (job_t *)hprinter; + + TRACE("(%p %I64d %p %lx %x)\n", hprinter, distance.QuadPart, pos, method, bwrite); + + if (!job) + { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + if (job->header.type != HANDLE_JOB) + { + FIXME("handle %x not supported\n", job->header.type); + return FALSE; + } + + if (bwrite) + { + if (pos) + pos->QuadPart = 0; + return TRUE; + } + + return SetFilePointerEx(job->hf, distance, pos, method); +} + static const PRINTPROVIDOR backend = { fpOpenPrinter, fpSetJob, @@ -3971,7 +4000,7 @@ static const PRINTPROVIDOR backend = { NULL, /* fpEnumPrinterKey */ NULL, /* fpDeletePrinterDataEx */ NULL, /* fpDeletePrinterKey */ - NULL, /* fpSeekPrinter */ + fpSeekPrinter, NULL, /* fpDeletePrinterDriverEx */ NULL, /* fpAddPerMachineConnection */ NULL, /* fpDeletePerMachineConnection */
From: Piotr Caban piotr@codeweavers.com
--- dlls/winspool.drv/info.c | 18 ++++++++++++++++++ dlls/winspool.drv/winspool.drv.spec | 1 + 2 files changed, 19 insertions(+)
diff --git a/dlls/winspool.drv/info.c b/dlls/winspool.drv/info.c index 8abfc9b0887..602237fc5d7 100644 --- a/dlls/winspool.drv/info.c +++ b/dlls/winspool.drv/info.c @@ -7640,3 +7640,21 @@ HANDLE WINAPI GetSpoolFileHandle( HANDLE printer ) FIXME( "%p: stub\n", printer ); return INVALID_HANDLE_VALUE; } + +/***************************************************************************** + * SeekPrinter [WINSPOOL.@] + */ +BOOL WINAPI SeekPrinter(HANDLE printer, LARGE_INTEGER distance, + LARGE_INTEGER *pos, DWORD method, BOOL bwrite) +{ + HANDLE handle = get_backend_handle(printer); + + TRACE("(%p %I64d %p %lx %x)\n", printer, distance.QuadPart, pos, method, bwrite); + + if (!handle) + { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + return backend->fpSeekPrinter(handle, distance, pos, method, bwrite); +} diff --git a/dlls/winspool.drv/winspool.drv.spec b/dlls/winspool.drv/winspool.drv.spec index 39c2613a4a0..1ca94e18c40 100644 --- a/dlls/winspool.drv/winspool.drv.spec +++ b/dlls/winspool.drv/winspool.drv.spec @@ -162,6 +162,7 @@ @ stdcall ResetPrinterA(long ptr) @ stdcall ResetPrinterW(long ptr) @ stdcall ScheduleJob(long long) +@ stdcall SeekPrinter(long int64 ptr long long) @ stub SetAllocFailCount @ stdcall SetFormA(long str long ptr) @ stdcall SetFormW(long wstr long ptr)
Huw Davies (@huw) commented about dlls/localspl/provider.c:
- if (!print_proc_check_datatype(pp, datatype)) {
info.pDocName = job->document_title;
info.pOutputFile = (WCHAR *)port;
info.pDatatype = NULL;
ret = ret_startdoc = mon->monitor.pfnStartDocPort(hport, printer->info->name,
job_id, 1, (BYTE *)&info);
WARN("%s datatype not supported by %s\n", debugstr_w(datatype),
debugstr_w(printer->info->print_proc));
print_proc_unload(pp);
}return FALSE;
- while (ret && ReadFile(hf, buf, sizeof(buf), &r, NULL) && r)
ret = mon->monitor.pfnWritePort(hport, buf, r, &w) && r == w;
- swprintf(name, ARRAY_SIZE(name), L"%s, Port", port_name);
Sorry, missed this earlier, but shouldn't this be: ```suggestion:-0+0 swprintf(name, ARRAY_SIZE(name), L"%s, Port %s", printer->name, port_name); ```
On Tue Dec 6 11:59:24 2022 +0000, Huw Davies wrote:
Sorry, missed this earlier, but shouldn't this be:
swprintf(name, ARRAY_SIZE(name), L"%s, Port %s", printer->name, port_name);
Port takes only port name. I have tested it with wine's winprint print processor copied to Windows.
I was testing it with real printer and Black Ice Printer (virtual PDF printer that uses v3 driver, very convenient for testing). In case of Black Ice Printer, the port is called IcePortPUR: and OpenPrintProcessor was called with following paramters: OpenPrintProcessor L"\DESKTOP-V8RFDQ0\IcePortPUR:, Port" 0000000005CBEA90
This merge request was approved by Huw Davies.
On Tue Dec 6 12:12:03 2022 +0000, Piotr Caban wrote:
Port takes only port name. I have tested it with wine's winprint print processor copied to Windows. I was testing it with real printer and Black Ice Printer (virtual PDF printer that uses v3 driver, very convenient for testing). In case of Black Ice Printer, the port is called IcePortPUR: and OpenPrintProcessor was called with following paramters:
OpenPrintProcessor L"\\DESKTOP-V8RFDQ0\IcePortPUR:, Port" 0000000005CBEA90
Ok great, thanks!
On Tue Dec 6 12:37:48 2022 +0000, Huw Davies wrote:
Ok great, thanks!
I've tried to add tests for it but it looks like "FILE:, Port" doesn't work on Windows. I've also checked what is passed in this case to OpenPrintProcessor: ``` \DESKTOP-V8RFDQ0\C:\Users\piotr\Documents\test.mdoc.prn, Port ``` Unfortunately passing path is also not working in tests. I guess that native creates the port temporarily when writing to file so the call only succeeds after the job was scheduled (and until it's printed).
On Tue Dec 6 13:03:38 2022 +0000, Piotr Caban wrote:
I've tried to add tests for it but it looks like "FILE:, Port" doesn't work on Windows. I've also checked what is passed in this case to OpenPrintProcessor:
\\DESKTOP-V8RFDQ0\C:\Users\piotr\Documents\test.mdoc.prn, Port
Unfortunately passing path is also not working in tests. I guess that native creates the port temporarily when writing to file so the call only succeeds after the job was scheduled (and until it's printed).
It was not clear in last message. `FILE:, Port` or `c:\users\piotr\Documents\test.mdoc.prn, Port` behaves differently than `IcePortPUR:, Port` that can be always opened.
On Tue Dec 6 13:08:04 2022 +0000, Piotr Caban wrote:
It was not clear in last message. `FILE:, Port` or `c:\users\piotr\Documents\test.mdoc.prn, Port` behaves differently than `IcePortPUR:, Port` that can be always opened.
Got it. Anyway, this MR is still good to go.