msi: Unpublish the product when it is entirely removed.
[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
32 #include "winspool.h"
33 #include "ddk/winsplp.h"
34 #include "localspl_private.h"
35
36 #include "wine/debug.h"
37 #include "wine/list.h"
38 #include "wine/unicode.h"
39
40
41 WINE_DEFAULT_DEBUG_CHANNEL(localspl);
42
43 /*****************************************************/
44
45 static CRITICAL_SECTION xcv_handles_cs;
46 static CRITICAL_SECTION_DEBUG xcv_handles_cs_debug =
47 {
48     0, 0, &xcv_handles_cs,
49     { &xcv_handles_cs_debug.ProcessLocksList, &xcv_handles_cs_debug.ProcessLocksList },
50       0, 0, { (DWORD_PTR)(__FILE__ ": xcv_handles_cs") }
51 };
52 static CRITICAL_SECTION xcv_handles_cs = { &xcv_handles_cs_debug, -1, 0, 0, 0, 0 };
53
54 /* ############################### */
55
56 typedef struct {
57     struct list entry;
58     ACCESS_MASK GrantedAccess;
59     WCHAR       nameW[1];
60 } xcv_t;
61
62 static struct list xcv_handles = LIST_INIT( xcv_handles );
63
64 /* ############################### */
65
66 static const WCHAR cmd_AddPortW[] = {'A','d','d','P','o','r','t',0};
67 static const WCHAR cmd_DeletePortW[] = {'D','e','l','e','t','e','P','o','r','t',0};
68 static const WCHAR cmd_ConfigureLPTPortCommandOKW[] = {'C','o','n','f','i','g','u','r','e',
69                                     'L','P','T','P','o','r','t',
70                                     'C','o','m','m','a','n','d','O','K',0};
71
72 static const WCHAR cmd_GetDefaultCommConfigW[] = {'G','e','t',
73                                     'D','e','f','a','u','l','t',
74                                     'C','o','m','m','C','o','n','f','i','g',0};
75
76 static const WCHAR cmd_GetTransmissionRetryTimeoutW[] = {'G','e','t',
77                                     'T','r','a','n','s','m','i','s','s','i','o','n',
78                                     'R','e','t','r','y','T','i','m','e','o','u','t',0};
79
80 static const WCHAR cmd_MonitorUIW[] = {'M','o','n','i','t','o','r','U','I',0};
81 static const WCHAR cmd_PortIsValidW[] = {'P','o','r','t','I','s','V','a','l','i','d',0};
82 static const WCHAR cmd_SetDefaultCommConfigW[] = {'S','e','t',
83                                     'D','e','f','a','u','l','t',
84                                     'C','o','m','m','C','o','n','f','i','g',0};
85
86 static const WCHAR dllnameuiW[] = {'l','o','c','a','l','u','i','.','d','l','l',0};
87 static const WCHAR emptyW[] = {0};
88 static const WCHAR LocalPortW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
89
90 static const WCHAR portname_LPT[]  = {'L','P','T',0};
91 static const WCHAR portname_COM[]  = {'C','O','M',0};
92 static const WCHAR portname_FILE[] = {'F','I','L','E',':',0};
93 static const WCHAR portname_CUPS[] = {'C','U','P','S',':',0};
94 static const WCHAR portname_LPR[]  = {'L','P','R',':',0};
95
96 static const WCHAR TransmissionRetryTimeoutW[] = {'T','r','a','n','s','m','i','s','s','i','o','n',
97                                     'R','e','t','r','y','T','i','m','e','o','u','t',0};
98
99 static const WCHAR WinNT_CV_PortsW[] = {'S','o','f','t','w','a','r','e','\\',
100                                         'M','i','c','r','o','s','o','f','t','\\',
101                                         'W','i','n','d','o','w','s',' ','N','T','\\',
102                                         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
103                                         'P','o','r','t','s',0};
104
105 static const WCHAR WinNT_CV_WindowsW[] = {'S','o','f','t','w','a','r','e','\\',
106                                         'M','i','c','r','o','s','o','f','t','\\',
107                                         'W','i','n','d','o','w','s',' ','N','T','\\',
108                                         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
109                                         'W','i','n','d','o','w','s',0};
110
111
112 /******************************************************************
113  * does_port_exist (internal)
114  *
115  * returns TRUE, when the Port already exists
116  *
117  */
118 static BOOL does_port_exist(LPCWSTR myname)
119 {
120
121     LPPORT_INFO_1W  pi;
122     DWORD   needed = 0;
123     DWORD   returned;
124     DWORD   id;
125
126     TRACE("(%s)\n", debugstr_w(myname));
127
128     id = EnumPortsW(NULL, 1, NULL, 0, &needed, &returned);
129     pi = spl_alloc(needed);
130     returned = 0;
131     if (pi)
132         id = EnumPortsW(NULL, 1, (LPBYTE) pi, needed, &needed, &returned);
133
134     if (id && returned > 0) {
135         /* we got a number of valid names. */
136         for (id = 0; id < returned; id++)
137         {
138             if (lstrcmpiW(myname, pi[id].pName) == 0) {
139                 TRACE("(%u) found %s\n", id, debugstr_w(pi[id].pName));
140                 spl_free(pi);
141                 return TRUE;
142             }
143         }
144     }
145
146     spl_free(pi);
147     return FALSE;
148 }
149
150 /******************************************************************
151  * enumerate the local Ports from the Registry (internal)  
152  *
153  * See localmon_EnumPortsW.
154  *
155  * NOTES
156  *  returns the needed size (in bytes) for pPorts
157  *  and *lpreturned is set to number of entries returned in pPorts
158  *
159  */
160
161 static DWORD get_ports_from_reg(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
162 {
163     HKEY    hroot = 0;
164     LPWSTR  ptr;
165     LPPORT_INFO_2W out;
166     WCHAR   portname[MAX_PATH];
167     WCHAR   res_PortW[IDS_LOCALPORT_MAXLEN];
168     WCHAR   res_MonitorW[IDS_LOCALMONITOR_MAXLEN];
169     INT     reslen_PortW;
170     INT     reslen_MonitorW;
171     DWORD   len;
172     DWORD   res;
173     DWORD   needed = 0;
174     DWORD   numentries;
175     DWORD   entrysize;
176     DWORD   id = 0;
177
178     TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
179
180     entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
181
182     numentries = *lpreturned;           /* this is 0, when we scan the registry */
183     needed = entrysize * numentries;
184     ptr = (LPWSTR) &pPorts[needed];
185
186     if (needed > cbBuf) pPorts = NULL;  /* No buffer for the structs */
187
188     numentries = 0;
189     needed = 0;
190
191     /* we do not check more parameters as done in windows */
192     if ((level < 1) || (level > 2)) {
193         goto getports_cleanup;
194     }
195
196     /* "+1" for '\0' */
197     reslen_MonitorW = LoadStringW(LOCALSPL_hInstance, IDS_LOCALMONITOR, res_MonitorW, IDS_LOCALMONITOR_MAXLEN) + 1;  
198     reslen_PortW = LoadStringW(LOCALSPL_hInstance, IDS_LOCALPORT, res_PortW, IDS_LOCALPORT_MAXLEN) + 1;  
199
200     res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
201     if (res == ERROR_SUCCESS) {
202
203         /* Scan all Port-Names */
204         while (res == ERROR_SUCCESS) {
205             len = MAX_PATH;
206             portname[0] = '\0';
207             res = RegEnumValueW(hroot, id, portname, &len, NULL, NULL, NULL, NULL);
208
209             if ((res == ERROR_SUCCESS) && (portname[0])) {
210                 numentries++;
211                 /* calsulate the required size */
212                 needed += entrysize;
213                 needed += (len + 1) * sizeof(WCHAR);
214                 if (level > 1) {
215                     needed += (reslen_MonitorW + reslen_PortW) * sizeof(WCHAR);
216                 }
217
218                 /* Now fill the user-buffer, if available */
219                 if (pPorts && (cbBuf >= needed)){
220                     out = (LPPORT_INFO_2W) pPorts;
221                     pPorts += entrysize;
222                     TRACE("%p: writing PORT_INFO_%dW #%d (%s)\n", out, level, numentries, debugstr_w(portname));
223                     out->pPortName = ptr;
224                     lstrcpyW(ptr, portname);            /* Name of the Port */
225                     ptr += (len + 1);
226                     if (level > 1) {
227                         out->pMonitorName = ptr;
228                         lstrcpyW(ptr, res_MonitorW);    /* Name of the Monitor */
229                         ptr += reslen_MonitorW;
230
231                         out->pDescription = ptr;
232                         lstrcpyW(ptr, res_PortW);       /* Port Description */
233                         ptr += reslen_PortW;
234
235                         out->fPortType = PORT_TYPE_WRITE;
236                         out->Reserved = 0;
237                     }
238                 }
239                 id++;
240             }
241         }
242         RegCloseKey(hroot);
243     }
244     else
245     {
246         ERR("failed with %d for %s\n", res, debugstr_w(WinNT_CV_PortsW));
247         SetLastError(res);
248     }
249
250 getports_cleanup:
251     *lpreturned = numentries;
252     TRACE("need %d byte for %d entries (%d)\n", needed, numentries, GetLastError());
253     return needed;
254 }
255
256 /*****************************************************
257  * get_type_from_name (internal)
258  * 
259  */
260
261 static DWORD get_type_from_name(LPCWSTR name)
262 {
263     HANDLE  hfile;
264
265     if (!strncmpW(name, portname_LPT, sizeof(portname_LPT) / sizeof(WCHAR) -1))
266         return PORT_IS_LPT;
267
268     if (!strncmpW(name, portname_COM, sizeof(portname_COM) / sizeof(WCHAR) -1))
269         return PORT_IS_COM;
270
271     if (!strcmpW(name, portname_FILE))
272         return PORT_IS_FILE;
273
274     if (name[0] == '/')
275         return PORT_IS_UNIXNAME;
276
277     if (name[0] == '|')
278         return PORT_IS_PIPE;
279
280     if (!strncmpW(name, portname_CUPS, sizeof(portname_CUPS) / sizeof(WCHAR) -1))
281         return PORT_IS_CUPS;
282
283     if (!strncmpW(name, portname_LPR, sizeof(portname_LPR) / sizeof(WCHAR) -1))
284         return PORT_IS_LPR;
285
286     /* Must be a file or a directory. Does the file exist ? */
287     hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
288     TRACE("%p for OPEN_EXISTING on %s\n", hfile, debugstr_w(name));
289     if (hfile == INVALID_HANDLE_VALUE) {
290         /* Can we create the file? */
291         hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
292         TRACE("%p for OPEN_ALWAYS\n", hfile);
293     }
294     if (hfile != INVALID_HANDLE_VALUE) {
295         CloseHandle(hfile);
296         return PORT_IS_FILENAME;
297     }
298     /* We can't use the name. use GetLastError() for the reason */
299     return PORT_IS_UNKNOWN;
300 }
301
302 /******************************************************************************
303  *   localmon_AddPortExW [exported through MONITOREX]
304  *
305  * Add a Port, without presenting a user interface
306  *
307  * PARAMS
308  *  pName         [I] Servername or NULL (local Computer)
309  *  level         [I] Structure-Level (1) for pBuffer
310  *  pBuffer       [I] PTR to the Input-Data (PORT_INFO_1)
311  *  pMonitorName  [I] Name of the Monitor that manage the Port
312  *
313  * RETURNS
314  *  Success: TRUE
315  *  Failure: FALSE
316  *
317  * NOTES
318  *  Level 2 is documented on MSDN for Portmonitors, but not supported by the
319  *  "Local Port" Portmonitor (localspl.dll / localmon.dll)
320  */
321 static BOOL WINAPI localmon_AddPortExW(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
322 {
323     PORT_INFO_1W * pi;
324     HKEY  hroot;
325     DWORD res;
326
327     pi = (PORT_INFO_1W *) pBuffer;
328     TRACE("(%s, %d, %p, %s) => %s\n", debugstr_w(pName), level, pBuffer,
329             debugstr_w(pMonitorName), debugstr_w(pi ? pi->pName : NULL));
330
331
332     if ((pMonitorName == NULL) || (lstrcmpiW(pMonitorName, LocalPortW) != 0 ) ||
333         (pi == NULL) || (pi->pName == NULL) || (pi->pName[0] == '\0') ) {
334         SetLastError(ERROR_INVALID_PARAMETER);
335         return FALSE;
336     }
337
338     if (level != 1) {
339         SetLastError(ERROR_INVALID_LEVEL);
340         return FALSE;
341     }
342
343     res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
344     if (res == ERROR_SUCCESS) {
345         if (does_port_exist(pi->pName)) {
346             RegCloseKey(hroot);
347             TRACE("=> FALSE with %u\n", ERROR_INVALID_PARAMETER);
348             SetLastError(ERROR_INVALID_PARAMETER);
349             return FALSE;
350         }
351         res = RegSetValueExW(hroot, pi->pName, 0, REG_SZ, (const BYTE *) emptyW, sizeof(emptyW));
352         RegCloseKey(hroot);
353     }
354     if (res != ERROR_SUCCESS) SetLastError(ERROR_INVALID_PARAMETER);
355     TRACE("=> %u with %u\n", (res == ERROR_SUCCESS), GetLastError());
356     return (res == ERROR_SUCCESS);
357 }
358
359 /*****************************************************
360  *   localmon_EnumPortsW [exported through MONITOREX]
361  *
362  * Enumerate all local Ports
363  *
364  * PARAMS
365  *  pName       [I] Servername (ignored)
366  *  level       [I] Structure-Level (1 or 2)
367  *  pPorts      [O] PTR to Buffer that receives the Result
368  *  cbBuf       [I] Size of Buffer at pPorts
369  *  pcbNeeded   [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
370  *  pcReturned  [O] PTR to DWORD that receives the number of Ports in pPorts
371  *
372  * RETURNS
373  *  Success: TRUE
374  *  Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
375  *
376  * NOTES
377  *|  Windows ignores pName
378  *|  Windows crash the app, when pPorts, pcbNeeded or pcReturned are NULL
379  *|  Windows >NT4.0 does not check for illegal levels (TRUE is returned)
380  *
381  * ToDo
382  *   "HCU\Software\Wine\Spooler\<portname>" - redirection
383  *
384  */
385 static BOOL WINAPI localmon_EnumPortsW(LPWSTR pName, DWORD level, LPBYTE pPorts,
386                                        DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
387 {
388     BOOL    res = FALSE;
389     DWORD   needed;
390     DWORD   numentries;
391
392     TRACE("(%s, %d, %p, %d, %p, %p)\n",
393           debugstr_w(pName), level, pPorts, cbBuf, pcbNeeded, pcReturned);
394
395     numentries = 0;
396     needed = get_ports_from_reg(level, NULL, 0, &numentries);
397     /* we calculated the needed buffersize. now do the error-checks */
398     if (cbBuf < needed) {
399         SetLastError(ERROR_INSUFFICIENT_BUFFER);
400         goto cleanup;
401     }
402
403     /* fill the buffer with the Port-Names */
404     needed = get_ports_from_reg(level, pPorts, cbBuf, &numentries);
405     res = TRUE;
406
407     if (pcReturned) *pcReturned = numentries;
408
409 cleanup:
410     if (pcbNeeded)  *pcbNeeded = needed;
411
412     TRACE("returning %d with %d (%d byte for %d entries)\n", 
413             res, GetLastError(), needed, numentries);
414
415     return (res);
416 }
417
418 /*****************************************************
419  * localmon_XcvClosePort [exported through MONITOREX]
420  *
421  * Close a Communication-Channel
422  *
423  * PARAMS
424  *  hXcv  [i] The Handle to close
425  *
426  * RETURNS
427  *  Success: TRUE
428  *  Failure: FALSE
429  *
430  */
431 static BOOL WINAPI localmon_XcvClosePort(HANDLE hXcv)
432 {
433     xcv_t * xcv = (xcv_t *) hXcv;
434
435     TRACE("(%p)\n", xcv);
436     /* No checks are done in Windows */
437     EnterCriticalSection(&xcv_handles_cs);
438     list_remove(&xcv->entry);
439     LeaveCriticalSection(&xcv_handles_cs);
440     spl_free(xcv);
441     return TRUE;
442 }
443
444 /*****************************************************
445  * localmon_XcvDataPort [exported through MONITOREX]
446  *
447  * Execute command through a Communication-Channel
448  *
449  * PARAMS
450  *  hXcv            [i] The Handle to work with
451  *  pszDataName     [i] Name of the command to execute
452  *  pInputData      [i] Buffer for extra Input Data (needed only for some commands)
453  *  cbInputData     [i] Size in Bytes of Buffer at pInputData
454  *  pOutputData     [o] Buffer to receive additional Data (needed only for some commands)
455  *  cbOutputData    [i] Size in Bytes of Buffer at pOutputData
456  *  pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
457  *
458  * RETURNS
459  *  Success: ERROR_SUCCESS
460  *  Failure: win32 error code
461  *
462  * NOTES
463  *
464  *  Minimal List of commands, that every Printmonitor DLL should support:
465  *
466  *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
467  *| "AddPort"   : Add a Port (Name as WSTR in pInputData)
468  *| "DeletePort": Delete a Port (Name as WSTR in pInputData)
469  *
470  *
471  */
472 static DWORD WINAPI localmon_XcvDataPort(HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData,
473                                          PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
474 {
475     WCHAR   buffer[16];     /* buffer for a decimal number */
476     LPWSTR  ptr;
477     DWORD   res;
478     DWORD   needed;
479     HKEY    hroot;
480
481     TRACE("(%p, %s, %p, %d, %p, %d, %p)\n", hXcv, debugstr_w(pszDataName),
482           pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
483
484     if (!lstrcmpW(pszDataName, cmd_AddPortW)) {
485         TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
486         res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
487         if (res == ERROR_SUCCESS) {
488             if (does_port_exist((LPWSTR) pInputData)) {
489                 RegCloseKey(hroot);
490                 TRACE("=> %u\n", ERROR_ALREADY_EXISTS);
491                 return ERROR_ALREADY_EXISTS;
492             }
493             res = RegSetValueExW(hroot, (LPWSTR) pInputData, 0, REG_SZ, (const BYTE *) emptyW, sizeof(emptyW));
494             RegCloseKey(hroot);
495         }
496         TRACE("=> %u\n", res);
497         return res;
498     }
499
500
501     if (!lstrcmpW(pszDataName, cmd_ConfigureLPTPortCommandOKW)) {
502         TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
503         res = RegCreateKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_WindowsW, &hroot);
504         if (res == ERROR_SUCCESS) {
505             res = RegSetValueExW(hroot, TransmissionRetryTimeoutW, 0, REG_SZ, pInputData, cbInputData);
506             RegCloseKey(hroot);
507         }
508         return res;
509     }
510
511     if (!lstrcmpW(pszDataName, cmd_DeletePortW)) {
512         TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
513         res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
514         if (res == ERROR_SUCCESS) {
515             res = RegDeleteValueW(hroot, (LPWSTR) pInputData);
516             RegCloseKey(hroot);
517             TRACE("=> %u with %u\n", res, GetLastError() );
518             return res;
519         }
520         return ERROR_FILE_NOT_FOUND;
521     }
522
523     if (!lstrcmpW(pszDataName, cmd_GetDefaultCommConfigW)) {
524         TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
525         *pcbOutputNeeded = cbOutputData;
526         res = GetDefaultCommConfigW((LPWSTR) pInputData, (LPCOMMCONFIG) pOutputData, pcbOutputNeeded);
527         TRACE("got %u with %u\n", res, GetLastError() );
528         return res ? ERROR_SUCCESS : GetLastError();
529     }
530
531     if (!lstrcmpW(pszDataName, cmd_GetTransmissionRetryTimeoutW)) {
532         * pcbOutputNeeded = sizeof(DWORD);
533         if (cbOutputData >= sizeof(DWORD)) {
534             /* the w2k resource kit documented a default of 90, but that's wrong */
535             *((LPDWORD) pOutputData) = 45;
536
537             res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_WindowsW, &hroot);
538             if (res == ERROR_SUCCESS) {
539                 needed = sizeof(buffer) - sizeof(WCHAR);
540                 res = RegQueryValueExW(hroot, TransmissionRetryTimeoutW, NULL, NULL, (LPBYTE) buffer, &needed);
541                 if ((res == ERROR_SUCCESS) && (buffer[0])) {
542                     *((LPDWORD) pOutputData) = strtoulW(buffer, NULL, 0);
543                 }
544                 RegCloseKey(hroot);
545             }
546             return ERROR_SUCCESS;
547         }
548         return ERROR_INSUFFICIENT_BUFFER;
549     }
550
551
552     if (!lstrcmpW(pszDataName, cmd_MonitorUIW)) {
553         * pcbOutputNeeded = sizeof(dllnameuiW);
554         if (cbOutputData >= sizeof(dllnameuiW)) {
555             memcpy(pOutputData, dllnameuiW, sizeof(dllnameuiW));
556             return ERROR_SUCCESS;
557         }
558         return ERROR_INSUFFICIENT_BUFFER;
559     }
560
561     if (!lstrcmpW(pszDataName, cmd_PortIsValidW)) {
562         TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
563         res = get_type_from_name((LPCWSTR) pInputData);
564         TRACE("detected as %u\n",  res);
565         /* names, that we have recognized, are valid */
566         if (res) return ERROR_SUCCESS;
567
568         /* ERROR_ACCESS_DENIED, ERROR_PATH_NOT_FOUND or something else */
569         TRACE("=> %u\n", GetLastError());
570         return GetLastError();
571     }
572
573     if (!lstrcmpW(pszDataName, cmd_SetDefaultCommConfigW)) {
574         /* get the portname from the Handle */
575         ptr =  strchrW(((xcv_t *)hXcv)->nameW, ' ');
576         if (ptr) {
577             ptr++;  /* skip the space */
578         }
579         else
580         {
581             ptr =  ((xcv_t *)hXcv)->nameW;
582         }
583         lstrcpynW(buffer, ptr, sizeof(buffer)/sizeof(WCHAR));
584         if (buffer[0]) buffer[lstrlenW(buffer)-1] = '\0';  /* remove the ':' */
585         res = SetDefaultCommConfigW(buffer, (LPCOMMCONFIG) pInputData, cbInputData);
586         TRACE("got %u with %u\n", res, GetLastError() );
587         return res ? ERROR_SUCCESS : GetLastError();
588     }
589
590     FIXME("command not supported: %s\n", debugstr_w(pszDataName));
591     return ERROR_INVALID_PARAMETER;
592 }
593
594 /*****************************************************
595  * localmon_XcvOpenPort [exported through MONITOREX]
596  *
597  * Open a Communication-Channel
598  *
599  * PARAMS
600  *  pName         [i] Name of selected Object
601  *  GrantedAccess [i] Access-Rights to use
602  *  phXcv         [o] The resulting Handle is stored here
603  *
604  * RETURNS
605  *  Success: TRUE
606  *  Failure: FALSE
607  *
608  */
609 static BOOL WINAPI localmon_XcvOpenPort(LPCWSTR pName, ACCESS_MASK GrantedAccess, PHANDLE phXcv)
610 {
611     DWORD   len;
612     xcv_t * xcv;
613
614     TRACE("%s, 0x%x, %p)\n", debugstr_w(pName), GrantedAccess, phXcv);
615     /* No checks for any field is done in Windows */
616     len = (lstrlenW(pName) + 1) * sizeof(WCHAR);
617     xcv = spl_alloc( sizeof(xcv_t) + len);
618     if (xcv) {
619         xcv->GrantedAccess = GrantedAccess;
620         memcpy(&xcv->nameW, pName, len);
621         *phXcv = (HANDLE) xcv;
622         EnterCriticalSection(&xcv_handles_cs);
623         list_add_tail(&xcv_handles, &xcv->entry);
624         LeaveCriticalSection(&xcv_handles_cs);
625         TRACE("=> %p\n", xcv);
626         return TRUE;
627     }
628     else
629     {
630         *phXcv = NULL;
631         return FALSE;
632     }
633 }
634
635 /*****************************************************
636  *      InitializePrintMonitor  (LOCALSPL.@)
637  *
638  * Initialize the Monitor for the Local Ports
639  *
640  * PARAMS
641  *  regroot [I] Registry-Path, where the settings are stored
642  *
643  * RETURNS
644  *  Success: Pointer to a MONITOREX Structure
645  *  Failure: NULL
646  *
647  * NOTES
648  *  The fixed location "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Ports"
649  *  is used to store the Ports (IniFileMapping from "win.ini", Section "Ports").
650  *  Native localspl.dll fails, when no valid Port-Entry is present.
651  *
652  */
653
654 LPMONITOREX WINAPI InitializePrintMonitor(LPWSTR regroot)
655 {
656     static MONITOREX mymonitorex =
657     {
658         sizeof(MONITOREX) - sizeof(DWORD),
659         {
660             localmon_EnumPortsW,
661             NULL,       /* localmon_OpenPortW */ 
662             NULL,       /* localmon_OpenPortExW */ 
663             NULL,       /* localmon_StartDocPortW */
664             NULL,       /* localmon_WritePortW */
665             NULL,       /* localmon_ReadPortW */
666             NULL,       /* localmon_EndDocPortW */
667             NULL,       /* localmon_ClosePortW */
668             NULL,       /* Use AddPortUI in localui.dll */
669             localmon_AddPortExW,
670             NULL,       /* Use ConfigurePortUI in localui.dll */
671             NULL,       /* Use DeletePortUI in localui.dll */
672             NULL,       /* localmon_GetPrinterDataFromPort */
673             NULL,       /* localmon_SetPortTimeOuts */
674             localmon_XcvOpenPort,
675             localmon_XcvDataPort,
676             localmon_XcvClosePort
677         }
678     };
679
680     TRACE("(%s)\n", debugstr_w(regroot));
681     /* Parameter "regroot" is ignored on NT4.0 (localmon.dll) */
682     if (!regroot || !regroot[0]) {
683         SetLastError(ERROR_INVALID_PARAMETER);
684         return NULL;
685     }
686     TRACE("=> %p\n", &mymonitorex);
687     /* Native windows returns always the same pointer on success */
688     return &mymonitorex;
689 }