msacm32.drv: Constify some variables.
[wine/multimedia.git] / dlls / localspl / localmon.c
blob57cdd7ad8d861eeeaa1e4ccfa550b7b0bbe95d3f
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"
32 #include "winspool.h"
33 #include "ddk/winsplp.h"
34 #include "localspl_private.h"
36 #include "wine/debug.h"
37 #include "wine/list.h"
38 #include "wine/unicode.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(localspl);
43 /*****************************************************/
45 static CRITICAL_SECTION xcv_handles_cs;
46 static CRITICAL_SECTION_DEBUG xcv_handles_cs_debug =
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") }
52 static CRITICAL_SECTION xcv_handles_cs = { &xcv_handles_cs_debug, -1, 0, 0, 0, 0 };
54 /* ############################### */
56 typedef struct {
57 struct list entry;
58 ACCESS_MASK GrantedAccess;
59 WCHAR nameW[1];
60 } xcv_t;
62 static struct list xcv_handles = LIST_INIT( xcv_handles );
64 /* ############################### */
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};
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};
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};
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};
86 static const WCHAR dllnameuiW[] = {'l','o','c','a','l','u','i','.','d','l','l',0};
87 static const WCHAR emptyW[] = {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};
111 /******************************************************************
112 * does_port_exist (internal)
114 * returns TRUE, when the Port already exists
117 static BOOL does_port_exist(LPCWSTR myname)
120 LPPORT_INFO_1W pi;
121 DWORD needed = 0;
122 DWORD returned;
123 DWORD id;
125 TRACE("(%s)\n", debugstr_w(myname));
127 id = EnumPortsW(NULL, 1, NULL, 0, &needed, &returned);
128 pi = spl_alloc(needed);
129 returned = 0;
130 if (pi)
131 id = EnumPortsW(NULL, 1, (LPBYTE) pi, needed, &needed, &returned);
133 if (id && returned > 0) {
134 /* we got a number of valid names. */
135 for (id = 0; id < returned; id++)
137 if (lstrcmpiW(myname, pi[id].pName) == 0) {
138 TRACE("(%u) found %s\n", id, debugstr_w(pi[id].pName));
139 spl_free(pi);
140 return TRUE;
145 spl_free(pi);
146 return FALSE;
149 /******************************************************************
150 * enumerate the local Ports from the Registry (internal)
152 * See localmon_EnumPortsW.
154 * NOTES
155 * returns the needed size (in bytes) for pPorts
156 * and *lpreturned is set to number of entries returned in pPorts
160 static DWORD get_ports_from_reg(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
162 HKEY hroot = 0;
163 LPWSTR ptr;
164 LPPORT_INFO_2W out;
165 WCHAR portname[MAX_PATH];
166 WCHAR res_PortW[IDS_LOCALPORT_MAXLEN];
167 WCHAR res_MonitorW[IDS_LOCALMONITOR_MAXLEN];
168 INT reslen_PortW;
169 INT reslen_MonitorW;
170 DWORD len;
171 DWORD res;
172 DWORD needed = 0;
173 DWORD numentries;
174 DWORD entrysize;
175 DWORD id = 0;
177 TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
179 entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
181 numentries = *lpreturned; /* this is 0, when we scan the registry */
182 needed = entrysize * numentries;
183 ptr = (LPWSTR) &pPorts[needed];
185 if (needed > cbBuf) pPorts = NULL; /* No buffer for the structs */
187 numentries = 0;
188 needed = 0;
190 /* we do not check more parameters as done in windows */
191 if ((level < 1) || (level > 2)) {
192 goto getports_cleanup;
195 /* "+1" for '\0' */
196 reslen_MonitorW = LoadStringW(LOCALSPL_hInstance, IDS_LOCALMONITOR, res_MonitorW, IDS_LOCALMONITOR_MAXLEN) + 1;
197 reslen_PortW = LoadStringW(LOCALSPL_hInstance, IDS_LOCALPORT, res_PortW, IDS_LOCALPORT_MAXLEN) + 1;
199 res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
200 if (res == ERROR_SUCCESS) {
202 /* Scan all Port-Names */
203 while (res == ERROR_SUCCESS) {
204 len = MAX_PATH;
205 portname[0] = '\0';
206 res = RegEnumValueW(hroot, id, portname, &len, NULL, NULL, NULL, NULL);
208 if ((res == ERROR_SUCCESS) && (portname[0])) {
209 numentries++;
210 /* calsulate the required size */
211 needed += entrysize;
212 needed += (len + 1) * sizeof(WCHAR);
213 if (level > 1) {
214 needed += (reslen_MonitorW + reslen_PortW) * sizeof(WCHAR);
217 /* Now fill the user-buffer, if available */
218 if (pPorts && (cbBuf >= needed)){
219 out = (LPPORT_INFO_2W) pPorts;
220 pPorts += entrysize;
221 TRACE("%p: writing PORT_INFO_%dW #%d (%s)\n", out, level, numentries, debugstr_w(portname));
222 out->pPortName = ptr;
223 lstrcpyW(ptr, portname); /* Name of the Port */
224 ptr += (len + 1);
225 if (level > 1) {
226 out->pMonitorName = ptr;
227 lstrcpyW(ptr, res_MonitorW); /* Name of the Monitor */
228 ptr += reslen_MonitorW;
230 out->pDescription = ptr;
231 lstrcpyW(ptr, res_PortW); /* Port Description */
232 ptr += reslen_PortW;
234 out->fPortType = PORT_TYPE_WRITE;
235 out->Reserved = 0;
238 id++;
241 RegCloseKey(hroot);
243 else
245 ERR("failed with %d for %s\n", res, debugstr_w(WinNT_CV_PortsW));
246 SetLastError(res);
249 getports_cleanup:
250 *lpreturned = numentries;
251 TRACE("need %d byte for %d entries (%d)\n", needed, numentries, GetLastError());
252 return needed;
255 /*****************************************************
256 * get_type_from_name (internal)
260 static DWORD get_type_from_name(LPCWSTR name)
262 HANDLE hfile;
264 if (!strncmpW(name, portname_LPT, sizeof(portname_LPT) / sizeof(WCHAR) -1))
265 return PORT_IS_LPT;
267 if (!strncmpW(name, portname_COM, sizeof(portname_COM) / sizeof(WCHAR) -1))
268 return PORT_IS_COM;
270 if (!strcmpW(name, portname_FILE))
271 return PORT_IS_FILE;
273 if (name[0] == '/')
274 return PORT_IS_UNIXNAME;
276 if (name[0] == '|')
277 return PORT_IS_PIPE;
279 if (!strncmpW(name, portname_CUPS, sizeof(portname_CUPS) / sizeof(WCHAR) -1))
280 return PORT_IS_CUPS;
282 if (!strncmpW(name, portname_LPR, sizeof(portname_LPR) / sizeof(WCHAR) -1))
283 return PORT_IS_LPR;
285 /* Must be a file or a directory. Does the file exist ? */
286 hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
287 TRACE("%p for OPEN_EXISTING on %s\n", hfile, debugstr_w(name));
288 if (hfile == INVALID_HANDLE_VALUE) {
289 /* Can we create the file? */
290 hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
291 TRACE("%p for OPEN_ALWAYS\n", hfile);
293 if (hfile != INVALID_HANDLE_VALUE) {
294 CloseHandle(hfile);
295 return PORT_IS_FILENAME;
297 /* We can't use the name. use GetLastError() for the reason */
298 return PORT_IS_UNKNOWN;
301 /*****************************************************
302 * localmon_EnumPortsW [exported through MONITOREX]
304 * Enumerate all local Ports
306 * PARAMS
307 * pName [I] Servername (ignored)
308 * level [I] Structure-Level (1 or 2)
309 * pPorts [O] PTR to Buffer that receives the Result
310 * cbBuf [I] Size of Buffer at pPorts
311 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
312 * pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
314 * RETURNS
315 * Success: TRUE
316 * Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
318 * NOTES
319 *| Windows ignores pName
320 *| Windows crash the app, when pPorts, pcbNeeded or pcReturned are NULL
321 *| Windows >NT4.0 does not check for illegal levels (TRUE is returned)
323 * ToDo
324 * "HCU\Software\Wine\Spooler\<portname>" - redirection
327 BOOL WINAPI localmon_EnumPortsW(LPWSTR pName, DWORD level, LPBYTE pPorts,
328 DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
330 BOOL res = FALSE;
331 DWORD needed;
332 DWORD numentries;
334 TRACE("(%s, %d, %p, %d, %p, %p)\n",
335 debugstr_w(pName), level, pPorts, cbBuf, pcbNeeded, pcReturned);
337 numentries = 0;
338 needed = get_ports_from_reg(level, NULL, 0, &numentries);
339 /* we calculated the needed buffersize. now do the error-checks */
340 if (cbBuf < needed) {
341 SetLastError(ERROR_INSUFFICIENT_BUFFER);
342 goto cleanup;
345 /* fill the buffer with the Port-Names */
346 needed = get_ports_from_reg(level, pPorts, cbBuf, &numentries);
347 res = TRUE;
349 if (pcReturned) *pcReturned = numentries;
351 cleanup:
352 if (pcbNeeded) *pcbNeeded = needed;
354 TRACE("returning %d with %d (%d byte for %d entries)\n",
355 res, GetLastError(), needed, numentries);
357 return (res);
360 /*****************************************************
361 * localmon_XcvClosePort [exported through MONITOREX]
363 * Close a Communication-Channel
365 * PARAMS
366 * hXcv [i] The Handle to close
368 * RETURNS
369 * Success: TRUE
370 * Failure: FALSE
373 BOOL WINAPI localmon_XcvClosePort(HANDLE hXcv)
375 xcv_t * xcv = (xcv_t *) hXcv;
377 TRACE("(%p)\n", xcv);
378 /* No checks are done in Windows */
379 EnterCriticalSection(&xcv_handles_cs);
380 list_remove(&xcv->entry);
381 LeaveCriticalSection(&xcv_handles_cs);
382 spl_free(xcv);
383 return TRUE;
386 /*****************************************************
387 * localmon_XcvDataPort [exported through MONITOREX]
389 * Execute command through a Communication-Channel
391 * PARAMS
392 * hXcv [i] The Handle to work with
393 * pszDataName [i] Name of the command to execute
394 * pInputData [i] Buffer for extra Input Data (needed only for some commands)
395 * cbInputData [i] Size in Bytes of Buffer at pInputData
396 * pOutputData [o] Buffer to receive additional Data (needed only for some commands)
397 * cbOutputData [i] Size in Bytes of Buffer at pOutputData
398 * pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
400 * RETURNS
401 * Success: ERROR_SUCCESS
402 * Failure: win32 error code
404 * NOTES
406 * Minimal List of commands, that every Printmonitor DLL should support:
408 *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
409 *| "AddPort" : Add a Port (Name as WSTR in pInputData)
410 *| "DeletePort": Delete a Port (Name as WSTR in pInputData)
414 DWORD WINAPI localmon_XcvDataPort(HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData,
415 PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
417 WCHAR buffer[16]; /* buffer for a decimal number */
418 LPWSTR ptr;
419 DWORD res;
420 DWORD needed;
421 HKEY hroot;
423 TRACE("(%p, %s, %p, %d, %p, %d, %p)\n", hXcv, debugstr_w(pszDataName),
424 pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
426 if (!lstrcmpW(pszDataName, cmd_AddPortW)) {
427 TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
428 res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
429 if (res == ERROR_SUCCESS) {
430 if (does_port_exist((LPWSTR) pInputData)) {
431 RegCloseKey(hroot);
432 TRACE("=> %u\n", ERROR_ALREADY_EXISTS);
433 return ERROR_ALREADY_EXISTS;
435 res = RegSetValueExW(hroot, (LPWSTR) pInputData, 0, REG_SZ, (const BYTE *) emptyW, sizeof(emptyW));
436 RegCloseKey(hroot);
438 TRACE("=> %u\n", res);
439 return res;
443 if (!lstrcmpW(pszDataName, cmd_ConfigureLPTPortCommandOKW)) {
444 TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
445 res = RegCreateKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_WindowsW, &hroot);
446 if (res == ERROR_SUCCESS) {
447 res = RegSetValueExW(hroot, TransmissionRetryTimeoutW, 0, REG_SZ, pInputData, cbInputData);
448 RegCloseKey(hroot);
450 return res;
453 if (!lstrcmpW(pszDataName, cmd_DeletePortW)) {
454 TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
455 res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
456 if (res == ERROR_SUCCESS) {
457 res = RegDeleteValueW(hroot, (LPWSTR) pInputData);
458 RegCloseKey(hroot);
459 TRACE("=> %u with %u\n", res, GetLastError() );
460 return res;
462 return ERROR_FILE_NOT_FOUND;
465 if (!lstrcmpW(pszDataName, cmd_GetDefaultCommConfigW)) {
466 TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
467 *pcbOutputNeeded = cbOutputData;
468 res = GetDefaultCommConfigW((LPWSTR) pInputData, (LPCOMMCONFIG) pOutputData, pcbOutputNeeded);
469 TRACE("got %u with %u\n", res, GetLastError() );
470 return res ? ERROR_SUCCESS : GetLastError();
473 if (!lstrcmpW(pszDataName, cmd_GetTransmissionRetryTimeoutW)) {
474 * pcbOutputNeeded = sizeof(DWORD);
475 if (cbOutputData >= sizeof(DWORD)) {
476 /* the w2k resource kit documented a default of 90, but that's wrong */
477 *((LPDWORD) pOutputData) = 45;
479 res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_WindowsW, &hroot);
480 if (res == ERROR_SUCCESS) {
481 needed = sizeof(buffer) - sizeof(WCHAR);
482 res = RegQueryValueExW(hroot, TransmissionRetryTimeoutW, NULL, NULL, (LPBYTE) buffer, &needed);
483 if ((res == ERROR_SUCCESS) && (buffer[0])) {
484 *((LPDWORD) pOutputData) = strtoulW(buffer, NULL, 0);
486 RegCloseKey(hroot);
488 return ERROR_SUCCESS;
490 return ERROR_INSUFFICIENT_BUFFER;
494 if (!lstrcmpW(pszDataName, cmd_MonitorUIW)) {
495 * pcbOutputNeeded = sizeof(dllnameuiW);
496 if (cbOutputData >= sizeof(dllnameuiW)) {
497 memcpy(pOutputData, dllnameuiW, sizeof(dllnameuiW));
498 return ERROR_SUCCESS;
500 return ERROR_INSUFFICIENT_BUFFER;
503 if (!lstrcmpW(pszDataName, cmd_PortIsValidW)) {
504 TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
505 res = get_type_from_name((LPCWSTR) pInputData);
506 TRACE("detected as %u\n", res);
507 /* names, that we have recognized, are valid */
508 if (res) return ERROR_SUCCESS;
510 /* ERROR_ACCESS_DENIED, ERROR_PATH_NOT_FOUND or something else */
511 TRACE("=> %u\n", GetLastError());
512 return GetLastError();
515 if (!lstrcmpW(pszDataName, cmd_SetDefaultCommConfigW)) {
516 /* get the portname from the Handle */
517 ptr = strchrW(((xcv_t *)hXcv)->nameW, ' ');
518 if (ptr) {
519 ptr++; /* skip the space */
521 else
523 ptr = ((xcv_t *)hXcv)->nameW;
525 lstrcpynW(buffer, ptr, sizeof(buffer)/sizeof(WCHAR));
526 if (buffer[0]) buffer[lstrlenW(buffer)-1] = '\0'; /* remove the ':' */
527 res = SetDefaultCommConfigW(buffer, (LPCOMMCONFIG) pInputData, cbInputData);
528 TRACE("got %u with %u\n", res, GetLastError() );
529 return res ? ERROR_SUCCESS : GetLastError();
532 FIXME("command not supported: %s\n", debugstr_w(pszDataName));
533 return ERROR_INVALID_PARAMETER;
536 /*****************************************************
537 * localmon_XcvOpenPort [exported through MONITOREX]
539 * Open a Communication-Channel
541 * PARAMS
542 * pName [i] Name of selected Object
543 * GrantedAccess [i] Access-Rights to use
544 * phXcv [o] The resulting Handle is stored here
546 * RETURNS
547 * Success: TRUE
548 * Failure: FALSE
551 BOOL WINAPI localmon_XcvOpenPort(LPCWSTR pName, ACCESS_MASK GrantedAccess, PHANDLE phXcv)
553 DWORD len;
554 xcv_t * xcv;
556 TRACE("%s, 0x%x, %p)\n", debugstr_w(pName), GrantedAccess, phXcv);
557 /* No checks for any field is done in Windows */
558 len = (lstrlenW(pName) + 1) * sizeof(WCHAR);
559 xcv = spl_alloc( sizeof(xcv_t) + len);
560 if (xcv) {
561 xcv->GrantedAccess = GrantedAccess;
562 memcpy(&xcv->nameW, pName, len);
563 *phXcv = (HANDLE) xcv;
564 EnterCriticalSection(&xcv_handles_cs);
565 list_add_tail(&xcv_handles, &xcv->entry);
566 LeaveCriticalSection(&xcv_handles_cs);
567 TRACE("=> %p\n", xcv);
568 return TRUE;
570 else
572 *phXcv = NULL;
573 return FALSE;
577 /*****************************************************
578 * InitializePrintMonitor (LOCALSPL.@)
580 * Initialize the Monitor for the Local Ports
582 * PARAMS
583 * regroot [I] Registry-Path, where the settings are stored
585 * RETURNS
586 * Success: Pointer to a MONITOREX Structure
587 * Failure: NULL
589 * NOTES
590 * The fixed location "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Ports"
591 * is used to store the Ports (IniFileMapping from "win.ini", Section "Ports").
592 * Native localspl.dll fails, when no valid Port-Entry is present.
596 LPMONITOREX WINAPI InitializePrintMonitor(LPWSTR regroot)
598 static MONITOREX mymonitorex =
600 sizeof(MONITOREX) - sizeof(DWORD),
602 localmon_EnumPortsW,
603 NULL, /* localmon_OpenPortW */
604 NULL, /* localmon_OpenPortExW */
605 NULL, /* localmon_StartDocPortW */
606 NULL, /* localmon_WritePortW */
607 NULL, /* localmon_ReadPortW */
608 NULL, /* localmon_EndDocPortW */
609 NULL, /* localmon_ClosePortW */
610 NULL, /* localmon_AddPortW */
611 NULL, /* localmon_AddPortExW */
612 NULL, /* Use ConfigurePortUI in localui.dll */
613 NULL, /* Use DeletePortUI in localui.dll */
614 NULL, /* localmon_GetPrinterDataFromPort */
615 NULL, /* localmon_SetPortTimeOuts */
616 localmon_XcvOpenPort,
617 localmon_XcvDataPort,
618 localmon_XcvClosePort
622 TRACE("(%s)\n", debugstr_w(regroot));
623 /* Parameter "regroot" is ignored on NT4.0 (localmon.dll) */
624 if (!regroot || !regroot[0]) {
625 SetLastError(ERROR_INVALID_PARAMETER);
626 return NULL;
628 TRACE("=> %p\n", &mymonitorex);
629 /* Native windows returns always the same pointer on success */
630 return &mymonitorex;