localspl: Implement DeletePort for XcvDataPort.
[wine/wine64.git] / dlls / localspl / localmon.c
blobc98611d2758211a5cbbacfc9ec8079eaf36102bd
1 /*
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
21 #include <stdarg.h>
23 #define COBJMACROS
24 #define NONAMELESSUNION
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "winreg.h"
31 #include "winver.h"
32 #include "winnls.h"
34 #include "winspool.h"
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 /* ############################### */
58 typedef struct {
59 struct list entry;
60 ACCESS_MASK GrantedAccess;
61 WCHAR nameW[1];
62 } xcv_t;
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];
120 res_PortW[0] = '\0';
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.
133 * NOTES
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)
141 HKEY hroot = 0;
142 LPWSTR ptr;
143 LPPORT_INFO_2W out;
144 WCHAR portname[MAX_PATH];
145 WCHAR res_PortW[IDS_LOCALPORT_MAXLEN];
146 WCHAR res_MonitorW[IDS_LOCALMONITOR_MAXLEN];
147 INT reslen_PortW;
148 INT reslen_MonitorW;
149 DWORD len;
150 DWORD res;
151 DWORD needed = 0;
152 DWORD numentries;
153 DWORD entrysize;
154 DWORD id = 0;
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 */
166 numentries = 0;
167 needed = 0;
169 /* we do not check more parameters as done in windows */
170 if ((level < 1) || (level > 2)) {
171 goto getports_cleanup;
174 /* "+1" for '\0' */
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) {
183 len = MAX_PATH;
184 portname[0] = '\0';
185 res = RegEnumValueW(hroot, id, portname, &len, NULL, NULL, NULL, NULL);
187 if ((res == ERROR_SUCCESS) && (portname[0])) {
188 numentries++;
189 /* calsulate the required size */
190 needed += entrysize;
191 needed += (len + 1) * sizeof(WCHAR);
192 if (level > 1) {
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;
199 pPorts += entrysize;
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 */
203 ptr += (len + 1);
204 if (level > 1) {
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 */
211 ptr += reslen_PortW;
213 out->fPortType = PORT_TYPE_WRITE;
214 out->Reserved = 0;
217 id++;
220 RegCloseKey(hroot);
222 else
224 ERR("failed with %d for %s\n", res, debugstr_w(WinNT_CV_PortsW));
225 SetLastError(res);
228 getports_cleanup:
229 *lpreturned = numentries;
230 TRACE("need %d byte for %d entries (%d)\n", needed, numentries, GetLastError());
231 return needed;
234 /*****************************************************
235 * get_type_from_name (internal)
239 static DWORD get_type_from_name(LPCWSTR name)
241 HANDLE hfile;
243 if (!strncmpW(name, portname_LPT, sizeof(portname_LPT) / sizeof(WCHAR) -1))
244 return PORT_IS_LPT;
246 if (!strncmpW(name, portname_COM, sizeof(portname_COM) / sizeof(WCHAR) -1))
247 return PORT_IS_COM;
249 if (!strcmpW(name, portname_FILE))
250 return PORT_IS_FILE;
252 if (name[0] == '/')
253 return PORT_IS_UNIXNAME;
255 if (name[0] == '|')
256 return PORT_IS_PIPE;
258 if (!strncmpW(name, portname_CUPS, sizeof(portname_CUPS) / sizeof(WCHAR) -1))
259 return PORT_IS_CUPS;
261 if (!strncmpW(name, portname_LPR, sizeof(portname_LPR) / sizeof(WCHAR) -1))
262 return PORT_IS_LPR;
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) {
273 CloseHandle(hfile);
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
285 * PARAMS
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
290 * RETURNS
291 * Success: TRUE
292 * Failure: FALSE
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
309 * PARAMS
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
314 * RETURNS
315 * Success: TRUE
316 * Failure: FALSE
319 BOOL WINAPI localmon_DeletePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
321 DWORD res;
322 HKEY hroot;
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);
331 RegCloseKey(hroot);
333 TRACE("=> %d\n", (res == ERROR_SUCCESS));
334 return (res == ERROR_SUCCESS);
337 /*****************************************************
338 * localmon_EnumPortsW [exported through MONITOREX]
340 * Enumerate all local Ports
342 * PARAMS
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
350 * RETURNS
351 * Success: TRUE
352 * Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
354 * NOTES
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)
359 * ToDo
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)
366 BOOL res = FALSE;
367 DWORD needed;
368 DWORD numentries;
370 TRACE("(%s, %d, %p, %d, %p, %p)\n",
371 debugstr_w(pName), level, pPorts, cbBuf, pcbNeeded, pcReturned);
373 numentries = 0;
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);
378 goto cleanup;
381 /* fill the buffer with the Port-Names */
382 needed = get_ports_from_reg(level, pPorts, cbBuf, &numentries);
383 res = TRUE;
385 if (pcReturned) *pcReturned = numentries;
387 cleanup:
388 if (pcbNeeded) *pcbNeeded = needed;
390 TRACE("returning %d with %d (%d byte for %d entries)\n",
391 res, GetLastError(), needed, numentries);
393 return (res);
396 /*****************************************************
397 * localmon_XcvClosePort [exported through MONITOREX]
399 * Close a Communication-Channel
401 * PARAMS
402 * hXcv [i] The Handle to close
404 * RETURNS
405 * Success: TRUE
406 * Failure: FALSE
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);
418 spl_free(xcv);
419 return TRUE;
422 /*****************************************************
423 * localmon_XcvDataPort [exported through MONITOREX]
425 * Execute command through a Communication-Channel
427 * PARAMS
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
436 * RETURNS
437 * Success: ERROR_SUCCESS
438 * Failure: win32 error code (same value is returned by GetLastError)
440 * NOTES
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 */
454 LPWSTR ptr;
455 DWORD res;
456 DWORD needed;
457 HKEY hroot;
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);
467 RegCloseKey(hroot);
469 return res;
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);
477 RegCloseKey(hroot);
478 TRACE("=> %u with %u\n", res, GetLastError() );
479 return res;
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);
505 RegCloseKey(hroot);
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, ' ');
536 if (ptr) {
537 ptr++; /* skip the space */
539 else
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
559 * PARAMS
560 * pName [i] Name of selected Object
561 * GrantedAccess [i] Access-Rights to use
562 * phXcv [o] The resulting Handle is stored here
564 * RETURNS
565 * Success: TRUE
566 * Failure: FALSE
569 BOOL WINAPI localmon_XcvOpenPort(LPCWSTR pName, ACCESS_MASK GrantedAccess, PHANDLE phXcv)
571 DWORD len;
572 xcv_t * xcv;
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);
578 if (xcv) {
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);
586 return TRUE;
588 else
590 *phXcv = NULL;
591 return FALSE;
595 /*****************************************************
596 * InitializePrintMonitor (LOCALSPL.@)
598 * Initialize the Monitor for the Local Ports
600 * PARAMS
601 * regroot [I] Registry-Path, where the settings are stored
603 * RETURNS
604 * Success: Pointer to a MONITOREX Structure
605 * Failure: NULL
607 * NOTES
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),
620 localmon_EnumPortsW,
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);
644 return NULL;
646 TRACE("=> %p\n", &mymonitorex);
647 /* Native windows returns always the same pointer on success */
648 return &mymonitorex;