2 * Implementation of the Local Printmonitor
4 * Copyright 2006 Detlef Riekenberg
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #define NONAMELESSUNION
35 #include "ddk/winsplp.h"
36 #include "localspl_private.h"
38 #include "wine/debug.h"
39 #include "wine/list.h"
40 #include "wine/unicode.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(localspl
);
45 /*****************************************************/
47 static CRITICAL_SECTION xcv_handles_cs
;
48 static CRITICAL_SECTION_DEBUG xcv_handles_cs_debug
=
50 0, 0, &xcv_handles_cs
,
51 { &xcv_handles_cs_debug
.ProcessLocksList
, &xcv_handles_cs_debug
.ProcessLocksList
},
52 0, 0, { (DWORD_PTR
)(__FILE__
": xcv_handles_cs") }
54 static CRITICAL_SECTION xcv_handles_cs
= { &xcv_handles_cs_debug
, -1, 0, 0, 0, 0 };
56 /* ############################### */
60 ACCESS_MASK GrantedAccess
;
64 static struct list xcv_handles
= LIST_INIT( xcv_handles
);
66 /* ############################### */
68 static const WCHAR cmd_DeletePortW
[] = {'D','e','l','e','t','e','P','o','r','t',0};
69 static const WCHAR cmd_ConfigureLPTPortCommandOKW
[] = {'C','o','n','f','i','g','u','r','e',
70 'L','P','T','P','o','r','t',
71 'C','o','m','m','a','n','d','O','K',0};
73 static const WCHAR cmd_GetDefaultCommConfigW
[] = {'G','e','t',
74 'D','e','f','a','u','l','t',
75 'C','o','m','m','C','o','n','f','i','g',0};
77 static const WCHAR cmd_GetTransmissionRetryTimeoutW
[] = {'G','e','t',
78 'T','r','a','n','s','m','i','s','s','i','o','n',
79 'R','e','t','r','y','T','i','m','e','o','u','t',0};
81 static const WCHAR cmd_MonitorUIW
[] = {'M','o','n','i','t','o','r','U','I',0};
82 static const WCHAR cmd_PortIsValidW
[] = {'P','o','r','t','I','s','V','a','l','i','d',0};
83 static const WCHAR cmd_SetDefaultCommConfigW
[] = {'S','e','t',
84 'D','e','f','a','u','l','t',
85 'C','o','m','m','C','o','n','f','i','g',0};
87 static const WCHAR dllnameuiW
[] = {'l','o','c','a','l','u','i','.','d','l','l',0};
89 static const WCHAR portname_LPT
[] = {'L','P','T',0};
90 static const WCHAR portname_COM
[] = {'C','O','M',0};
91 static const WCHAR portname_FILE
[] = {'F','I','L','E',':',0};
92 static const WCHAR portname_CUPS
[] = {'C','U','P','S',':',0};
93 static const WCHAR portname_LPR
[] = {'L','P','R',':',0};
95 static const WCHAR TransmissionRetryTimeoutW
[] = {'T','r','a','n','s','m','i','s','s','i','o','n',
96 'R','e','t','r','y','T','i','m','e','o','u','t',0};
98 static const WCHAR WinNT_CV_PortsW
[] = {'S','o','f','t','w','a','r','e','\\',
99 'M','i','c','r','o','s','o','f','t','\\',
100 'W','i','n','d','o','w','s',' ','N','T','\\',
101 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
102 'P','o','r','t','s',0};
104 static const WCHAR WinNT_CV_WindowsW
[] = {'S','o','f','t','w','a','r','e','\\',
105 'M','i','c','r','o','s','o','f','t','\\',
106 'W','i','n','d','o','w','s',' ','N','T','\\',
107 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
108 'W','i','n','d','o','w','s',0};
110 /******************************************************************
111 * display the Dialog "Nothing to configure"
115 static void dlg_nothingtoconfig(HWND hWnd
)
117 WCHAR res_PortW
[IDS_LOCALPORT_MAXLEN
];
118 WCHAR res_nothingW
[IDS_NOTHINGTOCONFIG_MAXLEN
];
121 res_nothingW
[0] = '\0';
122 LoadStringW(LOCALSPL_hInstance
, IDS_LOCALPORT
, res_PortW
, IDS_LOCALPORT_MAXLEN
);
123 LoadStringW(LOCALSPL_hInstance
, IDS_NOTHINGTOCONFIG
, res_nothingW
, IDS_NOTHINGTOCONFIG_MAXLEN
);
125 MessageBoxW(hWnd
, res_nothingW
, res_PortW
, MB_OK
| MB_ICONINFORMATION
);
128 /******************************************************************
129 * enumerate the local Ports from the Registry (internal)
131 * See localmon_EnumPortsW.
134 * returns the needed size (in bytes) for pPorts
135 * and *lpreturned is set to number of entries returned in pPorts
139 static DWORD
get_ports_from_reg(DWORD level
, LPBYTE pPorts
, DWORD cbBuf
, LPDWORD lpreturned
)
144 WCHAR portname
[MAX_PATH
];
145 WCHAR res_PortW
[IDS_LOCALPORT_MAXLEN
];
146 WCHAR res_MonitorW
[IDS_LOCALMONITOR_MAXLEN
];
156 TRACE("(%d, %p, %d, %p)\n", level
, pPorts
, cbBuf
, lpreturned
);
158 entrysize
= (level
== 1) ? sizeof(PORT_INFO_1W
) : sizeof(PORT_INFO_2W
);
160 numentries
= *lpreturned
; /* this is 0, when we scan the registry */
161 needed
= entrysize
* numentries
;
162 ptr
= (LPWSTR
) &pPorts
[needed
];
164 if (needed
> cbBuf
) pPorts
= NULL
; /* No buffer for the structs */
169 /* we do not check more parameters as done in windows */
170 if ((level
< 1) || (level
> 2)) {
171 goto getports_cleanup
;
175 reslen_MonitorW
= LoadStringW(LOCALSPL_hInstance
, IDS_LOCALMONITOR
, res_MonitorW
, IDS_LOCALMONITOR_MAXLEN
) + 1;
176 reslen_PortW
= LoadStringW(LOCALSPL_hInstance
, IDS_LOCALPORT
, res_PortW
, IDS_LOCALPORT_MAXLEN
) + 1;
178 res
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, WinNT_CV_PortsW
, &hroot
);
179 if (res
== ERROR_SUCCESS
) {
181 /* Scan all Port-Names */
182 while (res
== ERROR_SUCCESS
) {
185 res
= RegEnumValueW(hroot
, id
, portname
, &len
, NULL
, NULL
, NULL
, NULL
);
187 if ((res
== ERROR_SUCCESS
) && (portname
[0])) {
189 /* calsulate the required size */
191 needed
+= (len
+ 1) * sizeof(WCHAR
);
193 needed
+= (reslen_MonitorW
+ reslen_PortW
) * sizeof(WCHAR
);
196 /* Now fill the user-buffer, if available */
197 if (pPorts
&& (cbBuf
>= needed
)){
198 out
= (LPPORT_INFO_2W
) pPorts
;
200 TRACE("%p: writing PORT_INFO_%dW #%d (%s)\n", out
, level
, numentries
, debugstr_w(portname
));
201 out
->pPortName
= ptr
;
202 lstrcpyW(ptr
, portname
); /* Name of the Port */
205 out
->pMonitorName
= ptr
;
206 lstrcpyW(ptr
, res_MonitorW
); /* Name of the Monitor */
207 ptr
+= reslen_MonitorW
;
209 out
->pDescription
= ptr
;
210 lstrcpyW(ptr
, res_PortW
); /* Port Description */
213 out
->fPortType
= PORT_TYPE_WRITE
;
224 ERR("failed with %d for %s\n", res
, debugstr_w(WinNT_CV_PortsW
));
229 *lpreturned
= numentries
;
230 TRACE("need %d byte for %d entries (%d)\n", needed
, numentries
, GetLastError());
234 /*****************************************************
235 * get_type_from_name (internal)
239 static DWORD
get_type_from_name(LPCWSTR name
)
243 if (!strncmpW(name
, portname_LPT
, sizeof(portname_LPT
) / sizeof(WCHAR
) -1))
246 if (!strncmpW(name
, portname_COM
, sizeof(portname_COM
) / sizeof(WCHAR
) -1))
249 if (!strcmpW(name
, portname_FILE
))
253 return PORT_IS_UNIXNAME
;
258 if (!strncmpW(name
, portname_CUPS
, sizeof(portname_CUPS
) / sizeof(WCHAR
) -1))
261 if (!strncmpW(name
, portname_LPR
, sizeof(portname_LPR
) / sizeof(WCHAR
) -1))
264 /* Must be a file or a directory. Does the file exist ? */
265 hfile
= CreateFileW(name
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
266 TRACE("%p for OPEN_EXISTING on %s\n", hfile
, debugstr_w(name
));
267 if (hfile
== INVALID_HANDLE_VALUE
) {
268 /* Can we create the file? */
269 hfile
= CreateFileW(name
, GENERIC_WRITE
, 0, NULL
, OPEN_ALWAYS
, FILE_FLAG_DELETE_ON_CLOSE
, NULL
);
270 TRACE("%p for OPEN_ALWAYS\n", hfile
);
272 if (hfile
!= INVALID_HANDLE_VALUE
) {
274 return PORT_IS_FILENAME
;
276 /* We can't use the name. use GetLastError() for the reason */
277 return PORT_IS_UNKNOWN
;
280 /*****************************************************
281 * localmon_ConfigurePortW [exported through MONITOREX]
283 * Display the Configuration-Dialog for a specific Port
286 * pName [I] Servername or NULL (local Computer)
287 * hWnd [I] Handle to parent Window for the Dialog-Box
288 * pPortName [I] Name of the Port, that should be configured
295 BOOL WINAPI
localmon_ConfigurePortW(LPWSTR pName
, HWND hWnd
, LPWSTR pPortName
)
297 TRACE("(%s, %p, %s)\n", debugstr_w(pName
), hWnd
, debugstr_w(pPortName
));
298 /* ToDo: Dialogs by Portname ("LPTx:", "COMx:") */
300 dlg_nothingtoconfig(hWnd
);
301 return ROUTER_SUCCESS
;
304 /*****************************************************
305 * localmon_DeletePortW [exported through MONITOREX]
307 * Delete a specific Port
310 * pName [I] Servername or NULL (local Computer)
311 * hWnd [I] Handle to parent Window
312 * pPortName [I] Name of the Port, that should be deleted
319 BOOL WINAPI
localmon_DeletePortW(LPWSTR pName
, HWND hWnd
, LPWSTR pPortName
)
324 TRACE("(%s, %p, %s)\n", debugstr_w(pName
), hWnd
, debugstr_w(pPortName
));
326 if ((!pPortName
) || (!pPortName
[0])) return FALSE
;
328 res
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, WinNT_CV_PortsW
, &hroot
);
329 if (res
== ERROR_SUCCESS
) {
330 res
= RegDeleteValueW(hroot
, pPortName
);
333 TRACE("=> %d\n", (res
== ERROR_SUCCESS
));
334 return (res
== ERROR_SUCCESS
);
337 /*****************************************************
338 * localmon_EnumPortsW [exported through MONITOREX]
340 * Enumerate all local Ports
343 * pName [I] Servername (ignored)
344 * level [I] Structure-Level (1 or 2)
345 * pPorts [O] PTR to Buffer that receives the Result
346 * cbBuf [I] Size of Buffer at pPorts
347 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
348 * pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
352 * Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
355 *| Windows ignores pName
356 *| Windows crash the app, when pPorts, pcbNeeded or pcReturned are NULL
357 *| Windows >NT4.0 does not check for illegal levels (TRUE is returned)
360 * "HCU\Software\Wine\Spooler\<portname>" - redirection
363 BOOL WINAPI
localmon_EnumPortsW(LPWSTR pName
, DWORD level
, LPBYTE pPorts
,
364 DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
370 TRACE("(%s, %d, %p, %d, %p, %p)\n",
371 debugstr_w(pName
), level
, pPorts
, cbBuf
, pcbNeeded
, pcReturned
);
374 needed
= get_ports_from_reg(level
, NULL
, 0, &numentries
);
375 /* we calculated the needed buffersize. now do the error-checks */
376 if (cbBuf
< needed
) {
377 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
381 /* fill the buffer with the Port-Names */
382 needed
= get_ports_from_reg(level
, pPorts
, cbBuf
, &numentries
);
385 if (pcReturned
) *pcReturned
= numentries
;
388 if (pcbNeeded
) *pcbNeeded
= needed
;
390 TRACE("returning %d with %d (%d byte for %d entries)\n",
391 res
, GetLastError(), needed
, numentries
);
396 /*****************************************************
397 * localmon_XcvClosePort [exported through MONITOREX]
399 * Close a Communication-Channel
402 * hXcv [i] The Handle to close
409 BOOL WINAPI
localmon_XcvClosePort(HANDLE hXcv
)
411 xcv_t
* xcv
= (xcv_t
*) hXcv
;
413 TRACE("(%p)\n", xcv
);
414 /* No checks are done in Windows */
415 EnterCriticalSection(&xcv_handles_cs
);
416 list_remove(&xcv
->entry
);
417 LeaveCriticalSection(&xcv_handles_cs
);
422 /*****************************************************
423 * localmon_XcvDataPort [exported through MONITOREX]
425 * Execute command through a Communication-Channel
428 * hXcv [i] The Handle to work with
429 * pszDataName [i] Name of the command to execute
430 * pInputData [i] Buffer for extra Input Data (needed only for some commands)
431 * cbInputData [i] Size in Bytes of Buffer at pInputData
432 * pOutputData [o] Buffer to receive additional Data (needed only for some commands)
433 * cbOutputData [i] Size in Bytes of Buffer at pOutputData
434 * pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
437 * Success: ERROR_SUCCESS
438 * Failure: win32 error code (same value is returned by GetLastError)
442 * Minimal List of commands, that every Printmonitor DLL should support:
444 *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
445 *| "AddPort" : Add a Port (Name as WSTR in pInputData)
446 *| "DeletePort": Delete a Port (Name as WSTR in pInputData)
450 DWORD WINAPI
localmon_XcvDataPort(HANDLE hXcv
, LPCWSTR pszDataName
, PBYTE pInputData
, DWORD cbInputData
,
451 PBYTE pOutputData
, DWORD cbOutputData
, PDWORD pcbOutputNeeded
)
453 WCHAR buffer
[16]; /* buffer for a decimal number */
459 TRACE("(%p, %s, %p, %d, %p, %d, %p)\n", hXcv
, debugstr_w(pszDataName
),
460 pInputData
, cbInputData
, pOutputData
, cbOutputData
, pcbOutputNeeded
);
462 if (!lstrcmpW(pszDataName
, cmd_ConfigureLPTPortCommandOKW
)) {
463 TRACE("InputData (%d): %s\n", cbInputData
, debugstr_w( (LPWSTR
) pInputData
));
464 res
= RegCreateKeyW(HKEY_LOCAL_MACHINE
, WinNT_CV_WindowsW
, &hroot
);
465 if (res
== ERROR_SUCCESS
) {
466 res
= RegSetValueExW(hroot
, TransmissionRetryTimeoutW
, 0, REG_SZ
, pInputData
, cbInputData
);
472 if (!lstrcmpW(pszDataName
, cmd_DeletePortW
)) {
473 TRACE("InputData (%d): %s\n", cbInputData
, debugstr_w( (LPWSTR
) pInputData
));
474 res
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, WinNT_CV_PortsW
, &hroot
);
475 if (res
== ERROR_SUCCESS
) {
476 res
= RegDeleteValueW(hroot
, (LPWSTR
) pInputData
);
478 TRACE("=> %u with %u\n", res
, GetLastError() );
481 return ERROR_FILE_NOT_FOUND
;
484 if (!lstrcmpW(pszDataName
, cmd_GetDefaultCommConfigW
)) {
485 TRACE("InputData (%d): %s\n", cbInputData
, debugstr_w( (LPWSTR
) pInputData
));
486 *pcbOutputNeeded
= cbOutputData
;
487 res
= GetDefaultCommConfigW((LPWSTR
) pInputData
, (LPCOMMCONFIG
) pOutputData
, pcbOutputNeeded
);
488 TRACE("got %u with %u\n", res
, GetLastError() );
489 return res
? ERROR_SUCCESS
: GetLastError();
492 if (!lstrcmpW(pszDataName
, cmd_GetTransmissionRetryTimeoutW
)) {
493 * pcbOutputNeeded
= sizeof(DWORD
);
494 if (cbOutputData
>= sizeof(DWORD
)) {
495 /* the w2k resource kit documented a default of 90, but that's wrong */
496 *((LPDWORD
) pOutputData
) = 45;
498 res
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, WinNT_CV_WindowsW
, &hroot
);
499 if (res
== ERROR_SUCCESS
) {
500 needed
= sizeof(buffer
) - sizeof(WCHAR
);
501 res
= RegQueryValueExW(hroot
, TransmissionRetryTimeoutW
, NULL
, NULL
, (LPBYTE
) buffer
, &needed
);
502 if ((res
== ERROR_SUCCESS
) && (buffer
[0])) {
503 *((LPDWORD
) pOutputData
) = strtoulW(buffer
, NULL
, 0);
507 return ERROR_SUCCESS
;
509 return ERROR_INSUFFICIENT_BUFFER
;
513 if (!lstrcmpW(pszDataName
, cmd_MonitorUIW
)) {
514 * pcbOutputNeeded
= sizeof(dllnameuiW
);
515 if (cbOutputData
>= sizeof(dllnameuiW
)) {
516 memcpy(pOutputData
, dllnameuiW
, sizeof(dllnameuiW
));
517 return ERROR_SUCCESS
;
519 return ERROR_INSUFFICIENT_BUFFER
;
522 if (!lstrcmpW(pszDataName
, cmd_PortIsValidW
)) {
523 TRACE("InputData (%d): %s\n", cbInputData
, debugstr_w( (LPWSTR
) pInputData
));
524 res
= get_type_from_name((LPCWSTR
) pInputData
);
525 TRACE("detected as %u\n", res
);
526 /* names, that we have recognized, are valid */
527 if (res
) return ERROR_SUCCESS
;
529 /* ERROR_ACCESS_DENIED, ERROR_PATH_NOT_FOUND ore something else */
530 return GetLastError();
533 if (!lstrcmpW(pszDataName
, cmd_SetDefaultCommConfigW
)) {
534 /* get the portname from the Handle */
535 ptr
= strchrW(((xcv_t
*)hXcv
)->nameW
, ' ');
537 ptr
++; /* skip the space */
541 ptr
= ((xcv_t
*)hXcv
)->nameW
;
543 lstrcpynW(buffer
, ptr
, sizeof(buffer
)/sizeof(WCHAR
));
544 if (buffer
[0]) buffer
[lstrlenW(buffer
)-1] = '\0'; /* remove the ':' */
545 res
= SetDefaultCommConfigW(buffer
, (LPCOMMCONFIG
) pInputData
, cbInputData
);
546 TRACE("got %u with %u\n", res
, GetLastError() );
547 return res
? ERROR_SUCCESS
: GetLastError();
550 FIXME("command not supported: %s\n", debugstr_w(pszDataName
));
551 return ERROR_INVALID_PARAMETER
;
554 /*****************************************************
555 * localmon_XcvOpenPort [exported through MONITOREX]
557 * Open a Communication-Channel
560 * pName [i] Name of selected Object
561 * GrantedAccess [i] Access-Rights to use
562 * phXcv [o] The resulting Handle is stored here
569 BOOL WINAPI
localmon_XcvOpenPort(LPCWSTR pName
, ACCESS_MASK GrantedAccess
, PHANDLE phXcv
)
574 TRACE("%s, 0x%x, %p)\n", debugstr_w(pName
), GrantedAccess
, phXcv
);
575 /* No checks for any field is done in Windows */
576 len
= (lstrlenW(pName
) + 1) * sizeof(WCHAR
);
577 xcv
= spl_alloc( sizeof(xcv_t
) + len
);
579 xcv
->GrantedAccess
= GrantedAccess
;
580 memcpy(&xcv
->nameW
, pName
, len
);
581 *phXcv
= (HANDLE
) xcv
;
582 EnterCriticalSection(&xcv_handles_cs
);
583 list_add_tail(&xcv_handles
, &xcv
->entry
);
584 LeaveCriticalSection(&xcv_handles_cs
);
585 TRACE("=> %p\n", xcv
);
595 /*****************************************************
596 * InitializePrintMonitor (LOCALSPL.@)
598 * Initialize the Monitor for the Local Ports
601 * regroot [I] Registry-Path, where the settings are stored
604 * Success: Pointer to a MONITOREX Structure
608 * The fixed location "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Ports"
609 * is used to store the Ports (IniFileMapping from "win.ini", Section "Ports").
610 * Native localspl.dll fails, when no valid Port-Entry is present.
614 LPMONITOREX WINAPI
InitializePrintMonitor(LPWSTR regroot
)
616 static MONITOREX mymonitorex
=
618 sizeof(MONITOREX
) - sizeof(DWORD
),
621 NULL
, /* localmon_OpenPortW */
622 NULL
, /* localmon_OpenPortExW */
623 NULL
, /* localmon_StartDocPortW */
624 NULL
, /* localmon_WritePortW */
625 NULL
, /* localmon_ReadPortW */
626 NULL
, /* localmon_EndDocPortW */
627 NULL
, /* localmon_ClosePortW */
628 NULL
, /* localmon_AddPortW */
629 NULL
, /* localmon_AddPortExW */
630 localmon_ConfigurePortW
,
631 localmon_DeletePortW
,
632 NULL
, /* localmon_GetPrinterDataFromPort */
633 NULL
, /* localmon_SetPortTimeOuts */
634 localmon_XcvOpenPort
,
635 localmon_XcvDataPort
,
636 localmon_XcvClosePort
640 TRACE("(%s)\n", debugstr_w(regroot
));
641 /* Parameter "regroot" is ignored on NT4.0 (localmon.dll) */
642 if (!regroot
|| !regroot
[0]) {
643 SetLastError(ERROR_INVALID_PARAMETER
);
646 TRACE("=> %p\n", &mymonitorex
);
647 /* Native windows returns always the same pointer on success */