kernel32: GlobalMemoryStatusEx: return the size of physical memory + swapsize in...
[wine] / dlls / localspl / localmon.c
1 /*
2  * Implementation of the Local Printmonitor
3  *
4  * Copyright 2006 Detlef Riekenberg
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25
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"
33
34 #include "winspool.h"
35 #include "ddk/winsplp.h"
36 #include "localspl_private.h"
37
38 #include "wine/debug.h"
39 #include "wine/list.h"
40 #include "wine/unicode.h"
41
42
43 WINE_DEFAULT_DEBUG_CHANNEL(localspl);
44
45 /*****************************************************/
46
47 static CRITICAL_SECTION xcv_handles_cs;
48 static CRITICAL_SECTION_DEBUG xcv_handles_cs_debug =
49 {
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") }
53 };
54 static CRITICAL_SECTION xcv_handles_cs = { &xcv_handles_cs_debug, -1, 0, 0, 0, 0 };
55
56 /* ############################### */
57
58 typedef struct {
59     struct list entry;
60     ACCESS_MASK GrantedAccess;
61     WCHAR       nameW[1];
62 } xcv_t;
63
64 static struct list xcv_handles = LIST_INIT( xcv_handles );
65
66 /* ############################### */
67
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};
72
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};
76
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};
80
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};
86
87 static const WCHAR dllnameuiW[] = {'l','o','c','a','l','u','i','.','d','l','l',0};
88
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};
94
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};
97
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};
103
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};
109
110 /******************************************************************
111  * display the Dialog "Nothing to configure"
112  * 
113  */
114
115 static void dlg_nothingtoconfig(HWND hWnd)
116 {
117     WCHAR res_PortW[IDS_LOCALPORT_MAXLEN];
118     WCHAR res_nothingW[IDS_NOTHINGTOCONFIG_MAXLEN];
119
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);  
124
125     MessageBoxW(hWnd, res_nothingW, res_PortW, MB_OK | MB_ICONINFORMATION);
126 }
127
128 /******************************************************************
129  * enumerate the local Ports from the Registry (internal)  
130  *
131  * See localmon_EnumPortsW.
132  *
133  * NOTES
134  *  returns the needed size (in bytes) for pPorts
135  *  and *lpreturned is set to number of entries returned in pPorts
136  *
137  */
138
139 static DWORD get_ports_from_reg(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
140 {
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;
155
156     TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
157
158     entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
159
160     numentries = *lpreturned;           /* this is 0, when we scan the registry */
161     needed = entrysize * numentries;
162     ptr = (LPWSTR) &pPorts[needed];
163
164     if (needed > cbBuf) pPorts = NULL;  /* No buffer for the structs */
165
166     numentries = 0;
167     needed = 0;
168
169     /* we do not check more parameters as done in windows */
170     if ((level < 1) || (level > 2)) {
171         goto getports_cleanup;
172     }
173
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;  
177
178     res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
179     if (res == ERROR_SUCCESS) {
180
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);
186
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);
194                 }
195
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;
208
209                         out->pDescription = ptr;
210                         lstrcpyW(ptr, res_PortW);       /* Port Description */
211                         ptr += reslen_PortW;
212
213                         out->fPortType = PORT_TYPE_WRITE;
214                         out->Reserved = 0;
215                     }
216                 }
217                 id++;
218             }
219         }
220         RegCloseKey(hroot);
221     }
222     else
223     {
224         ERR("failed with %d for %s\n", res, debugstr_w(WinNT_CV_PortsW));
225         SetLastError(res);
226     }
227
228 getports_cleanup:
229     *lpreturned = numentries;
230     TRACE("need %d byte for %d entries (%d)\n", needed, numentries, GetLastError());
231     return needed;
232 }
233
234 /*****************************************************
235  * get_type_from_name (internal)
236  * 
237  */
238
239 static DWORD get_type_from_name(LPCWSTR name)
240 {
241     HANDLE  hfile;
242
243     if (!strncmpW(name, portname_LPT, sizeof(portname_LPT) / sizeof(WCHAR) -1))
244         return PORT_IS_LPT;
245
246     if (!strncmpW(name, portname_COM, sizeof(portname_COM) / sizeof(WCHAR) -1))
247         return PORT_IS_COM;
248
249     if (!strcmpW(name, portname_FILE))
250         return PORT_IS_FILE;
251
252     if (name[0] == '/')
253         return PORT_IS_UNIXNAME;
254
255     if (name[0] == '|')
256         return PORT_IS_PIPE;
257
258     if (!strncmpW(name, portname_CUPS, sizeof(portname_CUPS) / sizeof(WCHAR) -1))
259         return PORT_IS_CUPS;
260
261     if (!strncmpW(name, portname_LPR, sizeof(portname_LPR) / sizeof(WCHAR) -1))
262         return PORT_IS_LPR;
263
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);
271     }
272     if (hfile != INVALID_HANDLE_VALUE) {
273         CloseHandle(hfile);
274         return PORT_IS_FILENAME;
275     }
276     /* We can't use the name. use GetLastError() for the reason */
277     return PORT_IS_UNKNOWN;
278 }
279
280 /*****************************************************
281  *   localmon_ConfigurePortW [exported through MONITOREX]
282  *
283  * Display the Configuration-Dialog for a specific Port
284  *
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
289  *
290  * RETURNS
291  *  Success: TRUE
292  *  Failure: FALSE
293  *
294  */
295 BOOL WINAPI localmon_ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
296 {
297     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
298     /* ToDo: Dialogs by Portname ("LPTx:", "COMx:") */
299
300     dlg_nothingtoconfig(hWnd);
301     return ROUTER_SUCCESS;
302 }
303
304 /*****************************************************
305  *   localmon_DeletePortW [exported through MONITOREX]
306  *
307  * Delete a specific Port
308  *
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
313  *
314  * RETURNS
315  *  Success: TRUE
316  *  Failure: FALSE
317  *
318  */
319 BOOL WINAPI localmon_DeletePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
320 {
321     DWORD   res;
322     HKEY    hroot;
323
324     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
325
326     if ((!pPortName) || (!pPortName[0])) return FALSE;
327
328     res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
329     if (res == ERROR_SUCCESS) {
330         res = RegDeleteValueW(hroot, pPortName);
331         RegCloseKey(hroot);
332     }
333     TRACE("=> %d\n", (res == ERROR_SUCCESS));
334     return (res == ERROR_SUCCESS);
335 }
336
337 /*****************************************************
338  *   localmon_EnumPortsW [exported through MONITOREX]
339  *
340  * Enumerate all local Ports
341  *
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
349  *
350  * RETURNS
351  *  Success: TRUE
352  *  Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
353  *
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)
358  *
359  * ToDo
360  *   "HCU\Software\Wine\Spooler\<portname>" - redirection
361  *
362  */
363 BOOL WINAPI localmon_EnumPortsW(LPWSTR pName, DWORD level, LPBYTE pPorts,
364                             DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
365 {
366     BOOL    res = FALSE;
367     DWORD   needed;
368     DWORD   numentries;
369
370     TRACE("(%s, %d, %p, %d, %p, %p)\n",
371           debugstr_w(pName), level, pPorts, cbBuf, pcbNeeded, pcReturned);
372
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;
379     }
380
381     /* fill the buffer with the Port-Names */
382     needed = get_ports_from_reg(level, pPorts, cbBuf, &numentries);
383     res = TRUE;
384
385     if (pcReturned) *pcReturned = numentries;
386
387 cleanup:
388     if (pcbNeeded)  *pcbNeeded = needed;
389
390     TRACE("returning %d with %d (%d byte for %d entries)\n", 
391             res, GetLastError(), needed, numentries);
392
393     return (res);
394 }
395
396 /*****************************************************
397  * localmon_XcvClosePort [exported through MONITOREX]
398  *
399  * Close a Communication-Channel
400  *
401  * PARAMS
402  *  hXcv  [i] The Handle to close
403  *
404  * RETURNS
405  *  Success: TRUE
406  *  Failure: FALSE
407  *
408  */
409 BOOL WINAPI localmon_XcvClosePort(HANDLE hXcv)
410 {
411     xcv_t * xcv = (xcv_t *) hXcv;
412
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;
420 }
421
422 /*****************************************************
423  * localmon_XcvDataPort [exported through MONITOREX]
424  *
425  * Execute command through a Communication-Channel
426  *
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
435  *
436  * RETURNS
437  *  Success: ERROR_SUCCESS
438  *  Failure: win32 error code (same value is returned by GetLastError) 
439  *
440  * NOTES
441  *
442  *  Minimal List of commands, that every Printmonitor DLL should support:
443  *
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)
447  *
448  *
449  */
450 DWORD WINAPI localmon_XcvDataPort(HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData,
451                 PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
452 {
453     WCHAR   buffer[16];     /* buffer for a decimal number */
454     LPWSTR  ptr;
455     DWORD   res;
456     DWORD   needed;
457     HKEY    hroot;
458
459     TRACE("(%p, %s, %p, %d, %p, %d, %p)\n", hXcv, debugstr_w(pszDataName),
460           pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
461
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);
468         }
469         return res;
470     }
471
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;
480         }
481         return ERROR_FILE_NOT_FOUND;
482     }
483
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();
490     }
491
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;
497
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);
504                 }
505                 RegCloseKey(hroot);
506             }
507             return ERROR_SUCCESS;
508         }
509         return ERROR_INSUFFICIENT_BUFFER;
510     }
511
512
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;
518         }
519         return ERROR_INSUFFICIENT_BUFFER;
520     }
521
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;
528
529         /* ERROR_ACCESS_DENIED, ERROR_PATH_NOT_FOUND ore something else */
530         return GetLastError();
531     }
532
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 */
538         }
539         else
540         {
541             ptr =  ((xcv_t *)hXcv)->nameW;
542         }
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();
548     }
549
550     FIXME("command not supported: %s\n", debugstr_w(pszDataName));
551     return ERROR_INVALID_PARAMETER;
552 }
553
554 /*****************************************************
555  * localmon_XcvOpenPort [exported through MONITOREX]
556  *
557  * Open a Communication-Channel
558  *
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
563  *
564  * RETURNS
565  *  Success: TRUE
566  *  Failure: FALSE
567  *
568  */
569 BOOL WINAPI localmon_XcvOpenPort(LPCWSTR pName, ACCESS_MASK GrantedAccess, PHANDLE phXcv)
570 {
571     DWORD   len;
572     xcv_t * xcv;
573
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;
587     }
588     else
589     {
590         *phXcv = NULL;
591         return FALSE;
592     }
593 }
594
595 /*****************************************************
596  *      InitializePrintMonitor  (LOCALSPL.@)
597  *
598  * Initialize the Monitor for the Local Ports
599  *
600  * PARAMS
601  *  regroot [I] Registry-Path, where the settings are stored
602  *
603  * RETURNS
604  *  Success: Pointer to a MONITOREX Structure
605  *  Failure: NULL
606  *
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.
611  *
612  */
613
614 LPMONITOREX WINAPI InitializePrintMonitor(LPWSTR regroot)
615 {
616     static MONITOREX mymonitorex =
617     {
618         sizeof(MONITOREX) - sizeof(DWORD),
619         {
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
637         }
638     };
639
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;
645     }
646     TRACE("=> %p\n", &mymonitorex);
647     /* Native windows returns always the same pointer on success */
648     return &mymonitorex;
649 }