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
33 #include "ddk/winsplp.h"
34 #include "localspl_private.h"
36 #include "wine/debug.h"
37 #include "wine/heap.h"
38 #include "wine/list.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(localspl
);
43 /*****************************************************/
45 static CRITICAL_SECTION port_handles_cs
;
46 static CRITICAL_SECTION_DEBUG port_handles_cs_debug
=
48 0, 0, &port_handles_cs
,
49 { &port_handles_cs_debug
.ProcessLocksList
, &port_handles_cs_debug
.ProcessLocksList
},
50 0, 0, { (DWORD_PTR
)(__FILE__
": port_handles_cs") }
52 static CRITICAL_SECTION port_handles_cs
= { &port_handles_cs_debug
, -1, 0, 0, 0, 0 };
55 static CRITICAL_SECTION xcv_handles_cs
;
56 static CRITICAL_SECTION_DEBUG xcv_handles_cs_debug
=
58 0, 0, &xcv_handles_cs
,
59 { &xcv_handles_cs_debug
.ProcessLocksList
, &xcv_handles_cs_debug
.ProcessLocksList
},
60 0, 0, { (DWORD_PTR
)(__FILE__
": xcv_handles_cs") }
62 static CRITICAL_SECTION xcv_handles_cs
= { &xcv_handles_cs_debug
, -1, 0, 0, 0, 0 };
64 /* ############################### */
74 ACCESS_MASK GrantedAccess
;
78 static struct list port_handles
= LIST_INIT( port_handles
);
79 static struct list xcv_handles
= LIST_INIT( xcv_handles
);
81 static const WCHAR WinNT_CV_PortsW
[] = L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports";
82 static const WCHAR WinNT_CV_WindowsW
[] = L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
84 static HINSTANCE LOCALSPL_hInstance
;
86 /*****************************************************
89 BOOL WINAPI
DllMain(HINSTANCE hinstDLL
, DWORD fdwReason
, LPVOID lpvReserved
)
91 TRACE("(%p, %d, %p)\n",hinstDLL
, fdwReason
, lpvReserved
);
95 case DLL_PROCESS_ATTACH
:
96 DisableThreadLibraryCalls( hinstDLL
);
97 LOCALSPL_hInstance
= hinstDLL
;
103 /******************************************************************
104 * does_port_exist (internal)
106 * returns TRUE, when the Port already exists
109 static BOOL
does_port_exist(LPCWSTR myname
)
117 TRACE("(%s)\n", debugstr_w(myname
));
119 id
= EnumPortsW(NULL
, 1, NULL
, 0, &needed
, &returned
);
120 pi
= heap_alloc(needed
);
123 id
= EnumPortsW(NULL
, 1, (LPBYTE
) pi
, needed
, &needed
, &returned
);
125 if (id
&& returned
> 0) {
126 /* we got a number of valid names. */
127 for (id
= 0; id
< returned
; id
++)
129 if (lstrcmpiW(myname
, pi
[id
].pName
) == 0) {
130 TRACE("(%u) found %s\n", id
, debugstr_w(pi
[id
].pName
));
141 /******************************************************************
142 * enumerate the local Ports from the Registry (internal)
144 * See localmon_EnumPortsW.
147 * returns the needed size (in bytes) for pPorts
148 * and *lpreturned is set to number of entries returned in pPorts
152 static DWORD
get_ports_from_reg(DWORD level
, LPBYTE pPorts
, DWORD cbBuf
, LPDWORD lpreturned
)
157 WCHAR portname
[MAX_PATH
];
158 WCHAR res_PortW
[IDS_LOCALPORT_MAXLEN
];
159 WCHAR res_MonitorW
[IDS_LOCALMONITOR_MAXLEN
];
169 TRACE("(%d, %p, %d, %p)\n", level
, pPorts
, cbBuf
, lpreturned
);
171 entrysize
= (level
== 1) ? sizeof(PORT_INFO_1W
) : sizeof(PORT_INFO_2W
);
173 numentries
= *lpreturned
; /* this is 0, when we scan the registry */
174 needed
= entrysize
* numentries
;
175 ptr
= (LPWSTR
) &pPorts
[needed
];
177 if (needed
> cbBuf
) pPorts
= NULL
; /* No buffer for the structs */
182 /* we do not check more parameters as done in windows */
183 if ((level
< 1) || (level
> 2)) {
184 goto getports_cleanup
;
188 reslen_MonitorW
= LoadStringW(LOCALSPL_hInstance
, IDS_LOCALMONITOR
, res_MonitorW
, IDS_LOCALMONITOR_MAXLEN
) + 1;
189 reslen_PortW
= LoadStringW(LOCALSPL_hInstance
, IDS_LOCALPORT
, res_PortW
, IDS_LOCALPORT_MAXLEN
) + 1;
191 res
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, WinNT_CV_PortsW
, &hroot
);
192 if (res
== ERROR_SUCCESS
) {
194 /* Scan all Port-Names */
195 while (res
== ERROR_SUCCESS
) {
198 res
= RegEnumValueW(hroot
, id
, portname
, &len
, NULL
, NULL
, NULL
, NULL
);
200 if ((res
== ERROR_SUCCESS
) && (portname
[0])) {
202 /* calculate the required size */
204 needed
+= (len
+ 1) * sizeof(WCHAR
);
206 needed
+= (reslen_MonitorW
+ reslen_PortW
) * sizeof(WCHAR
);
209 /* Now fill the user-buffer, if available */
210 if (pPorts
&& (cbBuf
>= needed
)){
211 out
= (LPPORT_INFO_2W
) pPorts
;
213 TRACE("%p: writing PORT_INFO_%dW #%d (%s)\n", out
, level
, numentries
, debugstr_w(portname
));
214 out
->pPortName
= ptr
;
215 lstrcpyW(ptr
, portname
); /* Name of the Port */
218 out
->pMonitorName
= ptr
;
219 lstrcpyW(ptr
, res_MonitorW
); /* Name of the Monitor */
220 ptr
+= reslen_MonitorW
;
222 out
->pDescription
= ptr
;
223 lstrcpyW(ptr
, res_PortW
); /* Port Description */
226 out
->fPortType
= PORT_TYPE_WRITE
;
237 ERR("failed with %d for %s\n", res
, debugstr_w(WinNT_CV_PortsW
));
242 *lpreturned
= numentries
;
243 TRACE("need %d byte for %d entries (%d)\n", needed
, numentries
, GetLastError());
247 /*****************************************************
248 * get_type_from_name (internal)
252 static DWORD
get_type_from_name(LPCWSTR name
)
256 if (!wcsncmp(name
, L
"LPT", ARRAY_SIZE(L
"LPT") - 1))
259 if (!wcsncmp(name
, L
"COM", ARRAY_SIZE(L
"COM") - 1))
262 if (!lstrcmpW(name
, L
"FILE:"))
266 return PORT_IS_UNIXNAME
;
271 if (!wcsncmp(name
, L
"CUPS:", ARRAY_SIZE(L
"CUPS:") - 1))
274 if (!wcsncmp(name
, L
"LPR:", ARRAY_SIZE(L
"LPR:") - 1))
277 /* Must be a file or a directory. Does the file exist ? */
278 hfile
= CreateFileW(name
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
279 TRACE("%p for OPEN_EXISTING on %s\n", hfile
, debugstr_w(name
));
280 if (hfile
== INVALID_HANDLE_VALUE
) {
281 /* Can we create the file? */
282 hfile
= CreateFileW(name
, GENERIC_WRITE
, 0, NULL
, OPEN_ALWAYS
, FILE_FLAG_DELETE_ON_CLOSE
, NULL
);
283 TRACE("%p for OPEN_ALWAYS\n", hfile
);
285 if (hfile
!= INVALID_HANDLE_VALUE
) {
287 return PORT_IS_FILENAME
;
289 /* We can't use the name. use GetLastError() for the reason */
290 return PORT_IS_UNKNOWN
;
293 /*****************************************************
294 * get_type_from_local_name (internal)
298 static DWORD
get_type_from_local_name(LPCWSTR nameW
)
301 LPWSTR myname
= NULL
;
303 DWORD numentries
= 0;
306 TRACE("(%s)\n", debugstr_w(myname
));
308 needed
= get_ports_from_reg(1, NULL
, 0, &numentries
);
309 pi
= heap_alloc(needed
);
311 needed
= get_ports_from_reg(1, (LPBYTE
) pi
, needed
, &numentries
);
313 if (pi
&& needed
&& numentries
> 0) {
314 /* we got a number of valid ports. */
316 while ((myname
== NULL
) && (id
< numentries
))
318 if (lstrcmpiW(nameW
, pi
[id
].pName
) == 0) {
319 TRACE("(%u) found %s\n", id
, debugstr_w(pi
[id
].pName
));
320 myname
= pi
[id
].pName
;
326 id
= (myname
) ? get_type_from_name(myname
) : PORT_IS_UNKNOWN
;
332 /******************************************************************************
333 * localmon_AddPortExW [exported through MONITOREX]
335 * Add a Port, without presenting a user interface
338 * pName [I] Servername or NULL (local Computer)
339 * level [I] Structure-Level (1) for pBuffer
340 * pBuffer [I] PTR to the Input-Data (PORT_INFO_1)
341 * pMonitorName [I] Name of the Monitor that manage the Port
348 * Level 2 is documented on MSDN for Portmonitors, but not supported by the
349 * "Local Port" Portmonitor (localspl.dll / localmon.dll)
351 static BOOL WINAPI
localmon_AddPortExW(LPWSTR pName
, DWORD level
, LPBYTE pBuffer
, LPWSTR pMonitorName
)
357 pi
= (PORT_INFO_1W
*) pBuffer
;
358 TRACE("(%s, %d, %p, %s) => %s\n", debugstr_w(pName
), level
, pBuffer
,
359 debugstr_w(pMonitorName
), debugstr_w(pi
? pi
->pName
: NULL
));
362 if ((pMonitorName
== NULL
) || (lstrcmpiW(pMonitorName
, L
"Local Port") != 0 ) ||
363 (pi
== NULL
) || (pi
->pName
== NULL
) || (pi
->pName
[0] == '\0') ) {
364 SetLastError(ERROR_INVALID_PARAMETER
);
369 SetLastError(ERROR_INVALID_LEVEL
);
373 res
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, WinNT_CV_PortsW
, &hroot
);
374 if (res
== ERROR_SUCCESS
) {
375 if (does_port_exist(pi
->pName
)) {
377 TRACE("=> FALSE with %u\n", ERROR_INVALID_PARAMETER
);
378 SetLastError(ERROR_INVALID_PARAMETER
);
381 res
= RegSetValueExW(hroot
, pi
->pName
, 0, REG_SZ
, (const BYTE
*) L
"", sizeof(L
""));
384 if (res
!= ERROR_SUCCESS
) SetLastError(ERROR_INVALID_PARAMETER
);
385 TRACE("=> %u with %u\n", (res
== ERROR_SUCCESS
), GetLastError());
386 return (res
== ERROR_SUCCESS
);
389 /*****************************************************
390 * localmon_ClosePort [exported through MONITOREX]
395 * hPort [i] The Handle to close
402 static BOOL WINAPI
localmon_ClosePort(HANDLE hPort
)
404 port_t
* port
= hPort
;
406 TRACE("(%p)\n", port
);
407 EnterCriticalSection(&port_handles_cs
);
408 list_remove(&port
->entry
);
409 LeaveCriticalSection(&port_handles_cs
);
414 /*****************************************************
415 * localmon_EnumPortsW [exported through MONITOREX]
417 * Enumerate all local Ports
420 * pName [I] Servername (ignored)
421 * level [I] Structure-Level (1 or 2)
422 * pPorts [O] PTR to Buffer that receives the Result
423 * cbBuf [I] Size of Buffer at pPorts
424 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
425 * pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
429 * Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
432 *| Windows ignores pName
433 *| Windows crash the app, when pPorts, pcbNeeded or pcReturned are NULL
434 *| Windows >NT4.0 does not check for illegal levels (TRUE is returned)
437 * "HCU\Software\Wine\Spooler\<portname>" - redirection
440 static BOOL WINAPI
localmon_EnumPortsW(LPWSTR pName
, DWORD level
, LPBYTE pPorts
,
441 DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
447 TRACE("(%s, %d, %p, %d, %p, %p)\n",
448 debugstr_w(pName
), level
, pPorts
, cbBuf
, pcbNeeded
, pcReturned
);
451 needed
= get_ports_from_reg(level
, NULL
, 0, &numentries
);
452 /* we calculated the needed buffersize. now do the error-checks */
453 if (cbBuf
< needed
) {
454 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
458 /* fill the buffer with the Port-Names */
459 needed
= get_ports_from_reg(level
, pPorts
, cbBuf
, &numentries
);
462 if (pcReturned
) *pcReturned
= numentries
;
465 if (pcbNeeded
) *pcbNeeded
= needed
;
467 TRACE("returning %d with %d (%d byte for %d entries)\n",
468 res
, GetLastError(), needed
, numentries
);
473 /*****************************************************
474 * localmon_OpenPort [exported through MONITOREX]
476 * Open a Data-Channel for a Port
479 * pName [i] Name of selected Object
480 * phPort [o] The resulting Handle is stored here
487 static BOOL WINAPI
localmon_OpenPortW(LPWSTR pName
, PHANDLE phPort
)
492 TRACE("%s, %p)\n", debugstr_w(pName
), phPort
);
494 /* an empty name is invalid */
495 if (!pName
[0]) return FALSE
;
497 /* does the port exist? */
498 type
= get_type_from_local_name(pName
);
499 if (!type
) return FALSE
;
501 port
= heap_alloc(FIELD_OFFSET(port_t
, nameW
[lstrlenW(pName
) + 1]));
502 if (!port
) return FALSE
;
505 lstrcpyW(port
->nameW
, pName
);
508 EnterCriticalSection(&port_handles_cs
);
509 list_add_tail(&port_handles
, &port
->entry
);
510 LeaveCriticalSection(&port_handles_cs
);
512 TRACE("=> %p\n", port
);
516 /*****************************************************
517 * localmon_XcvClosePort [exported through MONITOREX]
519 * Close a Communication-Channel
522 * hXcv [i] The Handle to close
529 static BOOL WINAPI
localmon_XcvClosePort(HANDLE hXcv
)
533 TRACE("(%p)\n", xcv
);
534 /* No checks are done in Windows */
535 EnterCriticalSection(&xcv_handles_cs
);
536 list_remove(&xcv
->entry
);
537 LeaveCriticalSection(&xcv_handles_cs
);
542 /*****************************************************
543 * localmon_XcvDataPort [exported through MONITOREX]
545 * Execute command through a Communication-Channel
548 * hXcv [i] The Handle to work with
549 * pszDataName [i] Name of the command to execute
550 * pInputData [i] Buffer for extra Input Data (needed only for some commands)
551 * cbInputData [i] Size in Bytes of Buffer at pInputData
552 * pOutputData [o] Buffer to receive additional Data (needed only for some commands)
553 * cbOutputData [i] Size in Bytes of Buffer at pOutputData
554 * pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
557 * Success: ERROR_SUCCESS
558 * Failure: win32 error code
562 * Minimal List of commands, that every Printmonitor DLL should support:
564 *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
565 *| "AddPort" : Add a Port (Name as WSTR in pInputData)
566 *| "DeletePort": Delete a Port (Name as WSTR in pInputData)
570 static DWORD WINAPI
localmon_XcvDataPort(HANDLE hXcv
, LPCWSTR pszDataName
, PBYTE pInputData
, DWORD cbInputData
,
571 PBYTE pOutputData
, DWORD cbOutputData
, PDWORD pcbOutputNeeded
)
573 WCHAR buffer
[16]; /* buffer for a decimal number */
579 TRACE("(%p, %s, %p, %d, %p, %d, %p)\n", hXcv
, debugstr_w(pszDataName
),
580 pInputData
, cbInputData
, pOutputData
, cbOutputData
, pcbOutputNeeded
);
582 if (!lstrcmpW(pszDataName
, L
"AddPort")) {
583 TRACE("InputData (%d): %s\n", cbInputData
, debugstr_w( (LPWSTR
) pInputData
));
584 res
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, WinNT_CV_PortsW
, &hroot
);
585 if (res
== ERROR_SUCCESS
) {
586 if (does_port_exist((LPWSTR
) pInputData
)) {
588 TRACE("=> %u\n", ERROR_ALREADY_EXISTS
);
589 return ERROR_ALREADY_EXISTS
;
591 res
= RegSetValueExW(hroot
, (LPWSTR
)pInputData
, 0, REG_SZ
, (const BYTE
*)L
"", sizeof(L
""));
594 TRACE("=> %u\n", res
);
599 if (!lstrcmpW(pszDataName
, L
"ConfigureLPTPortCommandOK")) {
600 TRACE("InputData (%d): %s\n", cbInputData
, debugstr_w( (LPWSTR
) pInputData
));
601 res
= RegCreateKeyW(HKEY_LOCAL_MACHINE
, WinNT_CV_WindowsW
, &hroot
);
602 if (res
== ERROR_SUCCESS
) {
603 res
= RegSetValueExW(hroot
, L
"TransmissionRetryTimeout", 0, REG_SZ
, pInputData
, cbInputData
);
609 if (!lstrcmpW(pszDataName
, L
"DeletePort")) {
610 TRACE("InputData (%d): %s\n", cbInputData
, debugstr_w( (LPWSTR
) pInputData
));
611 res
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, WinNT_CV_PortsW
, &hroot
);
612 if (res
== ERROR_SUCCESS
) {
613 res
= RegDeleteValueW(hroot
, (LPWSTR
) pInputData
);
615 TRACE("=> %u with %u\n", res
, GetLastError() );
618 return ERROR_FILE_NOT_FOUND
;
621 if (!lstrcmpW(pszDataName
, L
"GetDefaultCommConfig")) {
622 TRACE("InputData (%d): %s\n", cbInputData
, debugstr_w( (LPWSTR
) pInputData
));
623 *pcbOutputNeeded
= cbOutputData
;
624 res
= GetDefaultCommConfigW((LPWSTR
) pInputData
, (LPCOMMCONFIG
) pOutputData
, pcbOutputNeeded
);
625 TRACE("got %u with %u\n", res
, GetLastError() );
626 return res
? ERROR_SUCCESS
: GetLastError();
629 if (!lstrcmpW(pszDataName
, L
"GetTransmissionRetryTimeout")) {
630 * pcbOutputNeeded
= sizeof(DWORD
);
631 if (cbOutputData
>= sizeof(DWORD
)) {
632 /* the w2k resource kit documented a default of 90, but that's wrong */
633 *((LPDWORD
) pOutputData
) = 45;
635 res
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, WinNT_CV_WindowsW
, &hroot
);
636 if (res
== ERROR_SUCCESS
) {
637 needed
= sizeof(buffer
) - sizeof(WCHAR
);
638 res
= RegQueryValueExW(hroot
, L
"TransmissionRetryTimeout", NULL
, NULL
, (BYTE
*)buffer
, &needed
);
639 if ((res
== ERROR_SUCCESS
) && (buffer
[0])) {
640 *((LPDWORD
) pOutputData
) = wcstoul(buffer
, NULL
, 0);
644 return ERROR_SUCCESS
;
646 return ERROR_INSUFFICIENT_BUFFER
;
650 if (!lstrcmpW(pszDataName
, L
"MonitorUI")) {
651 * pcbOutputNeeded
= sizeof(L
"localui.dll");
652 if (cbOutputData
>= sizeof(L
"localui.dll")) {
653 memcpy(pOutputData
, L
"localui.dll", sizeof(L
"localui.dll"));
654 return ERROR_SUCCESS
;
656 return ERROR_INSUFFICIENT_BUFFER
;
659 if (!lstrcmpW(pszDataName
, L
"PortIsValid")) {
660 TRACE("InputData (%d): %s\n", cbInputData
, debugstr_w( (LPWSTR
) pInputData
));
661 res
= get_type_from_name((LPCWSTR
) pInputData
);
662 TRACE("detected as %u\n", res
);
663 /* names, that we have recognized, are valid */
664 if (res
) return ERROR_SUCCESS
;
666 /* ERROR_ACCESS_DENIED, ERROR_PATH_NOT_FOUND or something else */
667 TRACE("=> %u\n", GetLastError());
668 return GetLastError();
671 if (!lstrcmpW(pszDataName
, L
"SetDefaultCommConfig")) {
672 /* get the portname from the Handle */
673 ptr
= wcschr(((xcv_t
*)hXcv
)->nameW
, ' ');
675 ptr
++; /* skip the space */
679 ptr
= ((xcv_t
*)hXcv
)->nameW
;
681 lstrcpynW(buffer
, ptr
, ARRAY_SIZE(buffer
));
682 if (buffer
[0]) buffer
[lstrlenW(buffer
)-1] = '\0'; /* remove the ':' */
683 res
= SetDefaultCommConfigW(buffer
, (LPCOMMCONFIG
) pInputData
, cbInputData
);
684 TRACE("got %u with %u\n", res
, GetLastError() );
685 return res
? ERROR_SUCCESS
: GetLastError();
688 FIXME("command not supported: %s\n", debugstr_w(pszDataName
));
689 return ERROR_INVALID_PARAMETER
;
692 /*****************************************************
693 * localmon_XcvOpenPort [exported through MONITOREX]
695 * Open a Communication-Channel
698 * pName [i] Name of selected Object
699 * GrantedAccess [i] Access-Rights to use
700 * phXcv [o] The resulting Handle is stored here
707 static BOOL WINAPI
localmon_XcvOpenPort(LPCWSTR pName
, ACCESS_MASK GrantedAccess
, PHANDLE phXcv
)
711 TRACE("%s, 0x%x, %p)\n", debugstr_w(pName
), GrantedAccess
, phXcv
);
712 /* No checks for any field is done in Windows */
713 xcv
= heap_alloc(FIELD_OFFSET(xcv_t
, nameW
[lstrlenW(pName
) + 1]));
715 xcv
->GrantedAccess
= GrantedAccess
;
716 lstrcpyW(xcv
->nameW
, pName
);
718 EnterCriticalSection(&xcv_handles_cs
);
719 list_add_tail(&xcv_handles
, &xcv
->entry
);
720 LeaveCriticalSection(&xcv_handles_cs
);
721 TRACE("=> %p\n", xcv
);
731 /*****************************************************
732 * InitializePrintMonitor (LOCALSPL.@)
734 * Initialize the Monitor for the Local Ports
737 * regroot [I] Registry-Path, where the settings are stored
740 * Success: Pointer to a MONITOREX Structure
744 * The fixed location "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Ports"
745 * is used to store the Ports (IniFileMapping from "win.ini", Section "Ports").
746 * Native localspl.dll fails, when no valid Port-Entry is present.
750 LPMONITOREX WINAPI
InitializePrintMonitor(LPWSTR regroot
)
752 static MONITOREX mymonitorex
=
758 NULL
, /* localmon_OpenPortExW */
759 NULL
, /* localmon_StartDocPortW */
760 NULL
, /* localmon_WritePortW */
761 NULL
, /* localmon_ReadPortW */
762 NULL
, /* localmon_EndDocPortW */
764 NULL
, /* Use AddPortUI in localui.dll */
766 NULL
, /* Use ConfigurePortUI in localui.dll */
767 NULL
, /* Use DeletePortUI in localui.dll */
768 NULL
, /* localmon_GetPrinterDataFromPort */
769 NULL
, /* localmon_SetPortTimeOuts */
770 localmon_XcvOpenPort
,
771 localmon_XcvDataPort
,
772 localmon_XcvClosePort
776 TRACE("(%s)\n", debugstr_w(regroot
));
777 /* Parameter "regroot" is ignored on NT4.0 (localmon.dll) */
778 if (!regroot
|| !regroot
[0]) {
779 SetLastError(ERROR_INVALID_PARAMETER
);
782 TRACE("=> %p\n", &mymonitorex
);
783 /* Native windows returns always the same pointer on success */