kernel32: Use wide-char string literals.
[wine.git] / dlls / kernel32 / comm.c
blob209a490b623056b33b8c65e6565df2cca6ad1437
1 /*
2 * DEC 93 Erik Bos <erik@xs4all.nl>
4 * Copyright 1996 Marcus Meissner
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 <stdlib.h>
22 #include <stdarg.h>
23 #include <stdio.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winnls.h"
28 #include "winerror.h"
29 #include "winioctl.h"
30 #include "ddk/ntddser.h"
32 #include "wine/server.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(comm);
38 /***********************************************************************
39 * COMM_Parse* (Internal)
41 * The following COMM_Parse* functions are used by the BuildCommDCB
42 * functions to help parse the various parts of the device control string.
44 static LPCWSTR COMM_ParseStart(LPCWSTR ptr)
46 /* The device control string may optionally start with "COMx" followed
47 by an optional ':' and spaces. */
48 if(!wcsnicmp(ptr, L"COM", 3))
50 ptr += 3;
52 /* Allow any com port above 0 as Win 9x does (NT only allows
53 values for com ports which are actually present) */
54 if(*ptr < '1' || *ptr > '9')
55 return NULL;
57 /* Advance pointer past port number */
58 while(*ptr >= '0' && *ptr <= '9') ptr++;
60 /* The com port number must be followed by a ':' or ' ' */
61 if(*ptr != ':' && *ptr != ' ')
62 return NULL;
64 /* Advance pointer to beginning of next parameter */
65 while(*ptr == ' ') ptr++;
66 if(*ptr == ':')
68 ptr++;
69 while(*ptr == ' ') ptr++;
72 /* The device control string must not start with a space. */
73 else if(*ptr == ' ')
74 return NULL;
76 return ptr;
79 static LPCWSTR COMM_ParseNumber(LPCWSTR ptr, LPDWORD lpnumber)
81 if(*ptr < '0' || *ptr > '9') return NULL;
82 *lpnumber = wcstoul(ptr, NULL, 10);
83 while(*ptr >= '0' && *ptr <= '9') ptr++;
84 return ptr;
87 static LPCWSTR COMM_ParseParity(LPCWSTR ptr, LPBYTE lpparity)
89 /* Contrary to what you might expect, Windows only sets the Parity
90 member of DCB and not fParity even when parity is specified in the
91 device control string */
93 switch(*ptr++)
95 case 'e':
96 case 'E':
97 *lpparity = EVENPARITY;
98 break;
99 case 'm':
100 case 'M':
101 *lpparity = MARKPARITY;
102 break;
103 case 'n':
104 case 'N':
105 *lpparity = NOPARITY;
106 break;
107 case 'o':
108 case 'O':
109 *lpparity = ODDPARITY;
110 break;
111 case 's':
112 case 'S':
113 *lpparity = SPACEPARITY;
114 break;
115 default:
116 return NULL;
119 return ptr;
122 static LPCWSTR COMM_ParseByteSize(LPCWSTR ptr, LPBYTE lpbytesize)
124 DWORD temp;
126 if(!(ptr = COMM_ParseNumber(ptr, &temp)))
127 return NULL;
129 if(temp >= 5 && temp <= 8)
131 *lpbytesize = temp;
132 return ptr;
134 else
135 return NULL;
138 static LPCWSTR COMM_ParseStopBits(LPCWSTR ptr, LPBYTE lpstopbits)
140 DWORD temp;
142 if(!wcsncmp(L"1.5", ptr, 3))
144 ptr += 3;
145 *lpstopbits = ONE5STOPBITS;
147 else
149 if(!(ptr = COMM_ParseNumber(ptr, &temp)))
150 return NULL;
152 if(temp == 1)
153 *lpstopbits = ONESTOPBIT;
154 else if(temp == 2)
155 *lpstopbits = TWOSTOPBITS;
156 else
157 return NULL;
160 return ptr;
163 static LPCWSTR COMM_ParseOnOff(LPCWSTR ptr, LPDWORD lponoff)
165 if(!wcsnicmp(L"on", ptr, 2))
167 ptr += 2;
168 *lponoff = 1;
170 else if(!wcsnicmp(L"off", ptr, 3))
172 ptr += 3;
173 *lponoff = 0;
175 else
176 return NULL;
178 return ptr;
181 /***********************************************************************
182 * COMM_BuildOldCommDCB (Internal)
184 * Build a DCB using the old style settings string eg: "96,n,8,1"
186 static BOOL COMM_BuildOldCommDCB(LPCWSTR device, LPDCB lpdcb)
188 WCHAR last = 0;
190 if(!(device = COMM_ParseNumber(device, &lpdcb->BaudRate)))
191 return FALSE;
193 switch(lpdcb->BaudRate)
195 case 11:
196 case 30:
197 case 60:
198 lpdcb->BaudRate *= 10;
199 break;
200 case 12:
201 case 24:
202 case 48:
203 case 96:
204 lpdcb->BaudRate *= 100;
205 break;
206 case 19:
207 lpdcb->BaudRate = 19200;
208 break;
211 while(*device == ' ') device++;
212 if(*device++ != ',') return FALSE;
213 while(*device == ' ') device++;
215 if(!(device = COMM_ParseParity(device, &lpdcb->Parity)))
216 return FALSE;
218 while(*device == ' ') device++;
219 if(*device++ != ',') return FALSE;
220 while(*device == ' ') device++;
222 if(!(device = COMM_ParseByteSize(device, &lpdcb->ByteSize)))
223 return FALSE;
225 while(*device == ' ') device++;
226 if(*device++ != ',') return FALSE;
227 while(*device == ' ') device++;
229 if(!(device = COMM_ParseStopBits(device, &lpdcb->StopBits)))
230 return FALSE;
232 /* The last parameter for flow control is optional. */
233 while(*device == ' ') device++;
234 if(*device == ',')
236 device++;
237 while(*device == ' ') device++;
238 if(*device) last = *device++;
239 while(*device == ' ') device++;
242 /* Win NT sets the flow control members based on (or lack of) the last
243 parameter. Win 9x does not set these members. */
244 switch(last)
246 case 0:
247 lpdcb->fInX = FALSE;
248 lpdcb->fOutX = FALSE;
249 lpdcb->fOutxCtsFlow = FALSE;
250 lpdcb->fOutxDsrFlow = FALSE;
251 lpdcb->fDtrControl = DTR_CONTROL_ENABLE;
252 lpdcb->fRtsControl = RTS_CONTROL_ENABLE;
253 break;
254 case 'x':
255 case 'X':
256 lpdcb->fInX = TRUE;
257 lpdcb->fOutX = TRUE;
258 lpdcb->fOutxCtsFlow = FALSE;
259 lpdcb->fOutxDsrFlow = FALSE;
260 lpdcb->fDtrControl = DTR_CONTROL_ENABLE;
261 lpdcb->fRtsControl = RTS_CONTROL_ENABLE;
262 break;
263 case 'p':
264 case 'P':
265 lpdcb->fInX = FALSE;
266 lpdcb->fOutX = FALSE;
267 lpdcb->fOutxCtsFlow = TRUE;
268 lpdcb->fOutxDsrFlow = TRUE;
269 lpdcb->fDtrControl = DTR_CONTROL_HANDSHAKE;
270 lpdcb->fRtsControl = RTS_CONTROL_HANDSHAKE;
271 break;
272 default:
273 return FALSE;
276 /* This should be the end of the string. */
277 if(*device) return FALSE;
279 return TRUE;
282 /***********************************************************************
283 * COMM_BuildNewCommDCB (Internal)
285 * Build a DCB using the new style settings string.
286 * eg: "baud=9600 parity=n data=8 stop=1 xon=on to=on"
288 static BOOL COMM_BuildNewCommDCB(LPCWSTR device, LPDCB lpdcb, LPCOMMTIMEOUTS lptimeouts)
290 DWORD temp;
291 BOOL baud = FALSE, stop = FALSE;
293 while(*device)
295 while(*device == ' ') device++;
297 if(!wcsnicmp(L"baud=", device, 5))
299 baud = TRUE;
301 if(!(device = COMM_ParseNumber(device + 5, &lpdcb->BaudRate)))
302 return FALSE;
304 else if(!wcsnicmp(L"parity=", device, 7))
306 if(!(device = COMM_ParseParity(device + 7, &lpdcb->Parity)))
307 return FALSE;
309 else if(!wcsnicmp(L"data=", device, 5))
311 if(!(device = COMM_ParseByteSize(device + 5, &lpdcb->ByteSize)))
312 return FALSE;
314 else if(!wcsnicmp(L"stop=", device, 5))
316 stop = TRUE;
318 if(!(device = COMM_ParseStopBits(device + 5, &lpdcb->StopBits)))
319 return FALSE;
321 else if(!wcsnicmp(L"to=", device, 3))
323 if(!(device = COMM_ParseOnOff(device + 3, &temp)))
324 return FALSE;
326 lptimeouts->ReadIntervalTimeout = 0;
327 lptimeouts->ReadTotalTimeoutMultiplier = 0;
328 lptimeouts->ReadTotalTimeoutConstant = 0;
329 lptimeouts->WriteTotalTimeoutMultiplier = 0;
330 lptimeouts->WriteTotalTimeoutConstant = temp ? 60000 : 0;
332 else if(!wcsnicmp(L"xon=", device, 4))
334 if(!(device = COMM_ParseOnOff(device + 4, &temp)))
335 return FALSE;
337 lpdcb->fOutX = temp;
338 lpdcb->fInX = temp;
340 else if(!wcsnicmp(L"odsr=", device, 5))
342 if(!(device = COMM_ParseOnOff(device + 5, &temp)))
343 return FALSE;
345 lpdcb->fOutxDsrFlow = temp;
347 else if(!wcsnicmp(L"octs=", device, 5))
349 if(!(device = COMM_ParseOnOff(device + 5, &temp)))
350 return FALSE;
352 lpdcb->fOutxCtsFlow = temp;
354 else if(!wcsnicmp(L"dtr=", device, 4))
356 if(!(device = COMM_ParseOnOff(device + 4, &temp)))
357 return FALSE;
359 lpdcb->fDtrControl = temp;
361 else if(!wcsnicmp(L"rts=", device, 4))
363 if(!(device = COMM_ParseOnOff(device + 4, &temp)))
364 return FALSE;
366 lpdcb->fRtsControl = temp;
368 else if(!wcsnicmp(L"idsr=", device, 5))
370 if(!(device = COMM_ParseOnOff(device + 5, &temp)))
371 return FALSE;
373 /* Win NT sets the fDsrSensitivity member based on the
374 idsr parameter. Win 9x sets fOutxDsrFlow instead. */
375 lpdcb->fDsrSensitivity = temp;
377 else
378 return FALSE;
380 /* After the above parsing, the next character (if not the end of
381 the string) should be a space */
382 if(*device && *device != ' ')
383 return FALSE;
386 /* If stop bits were not specified, a default is always supplied. */
387 if(!stop)
389 if(baud && lpdcb->BaudRate == 110)
390 lpdcb->StopBits = TWOSTOPBITS;
391 else
392 lpdcb->StopBits = ONESTOPBIT;
395 return TRUE;
398 /**************************************************************************
399 * BuildCommDCBA (KERNEL32.@)
401 * Updates a device control block data structure with values from an
402 * ascii device control string. The device control string has two forms
403 * normal and extended, it must be exclusively in one or the other form.
405 * RETURNS
407 * True on success, false on a malformed control string.
409 BOOL WINAPI BuildCommDCBA(
410 LPCSTR device, /* [in] The ascii device control string used to update the DCB. */
411 LPDCB lpdcb) /* [out] The device control block to be updated. */
413 return BuildCommDCBAndTimeoutsA(device,lpdcb,NULL);
416 /**************************************************************************
417 * BuildCommDCBAndTimeoutsA (KERNEL32.@)
419 * Updates a device control block data structure with values from an
420 * ascii device control string. Taking timeout values from a timeouts
421 * struct if desired by the control string.
423 * RETURNS
425 * True on success, false bad handles etc.
427 BOOL WINAPI BuildCommDCBAndTimeoutsA(
428 LPCSTR device, /* [in] The ascii device control string. */
429 LPDCB lpdcb, /* [out] The device control block to be updated. */
430 LPCOMMTIMEOUTS lptimeouts) /* [in] The COMMTIMEOUTS structure to be updated. */
432 BOOL ret = FALSE;
433 UNICODE_STRING deviceW;
435 TRACE("(%s,%p,%p)\n",device,lpdcb,lptimeouts);
436 if(device) RtlCreateUnicodeStringFromAsciiz(&deviceW,device);
437 else deviceW.Buffer = NULL;
439 if(deviceW.Buffer) ret = BuildCommDCBAndTimeoutsW(deviceW.Buffer,lpdcb,lptimeouts);
441 RtlFreeUnicodeString(&deviceW);
442 return ret;
445 /**************************************************************************
446 * BuildCommDCBAndTimeoutsW (KERNEL32.@)
448 * Updates a device control block data structure with values from a
449 * unicode device control string. Taking timeout values from a timeouts
450 * struct if desired by the control string.
452 * RETURNS
454 * True on success, false bad handles etc
456 BOOL WINAPI BuildCommDCBAndTimeoutsW(
457 LPCWSTR devid, /* [in] The unicode device control string. */
458 LPDCB lpdcb, /* [out] The device control block to be updated. */
459 LPCOMMTIMEOUTS lptimeouts) /* [in] The COMMTIMEOUTS structure to be updated. */
461 DCB dcb;
462 COMMTIMEOUTS timeouts;
463 BOOL result;
464 LPCWSTR ptr = devid;
466 TRACE("(%s,%p,%p)\n",debugstr_w(devid),lpdcb,lptimeouts);
468 memset(&timeouts, 0, sizeof timeouts);
470 /* Set DCBlength. (Windows NT does not do this, but 9x does) */
471 lpdcb->DCBlength = sizeof(DCB);
473 /* Make a copy of the original data structures to work with since if
474 if there is an error in the device control string the originals
475 should not be modified (except possibly DCBlength) */
476 dcb = *lpdcb;
477 if(lptimeouts) timeouts = *lptimeouts;
479 ptr = COMM_ParseStart(ptr);
481 if(ptr == NULL)
482 result = FALSE;
483 else if(wcschr(ptr, ','))
484 result = COMM_BuildOldCommDCB(ptr, &dcb);
485 else
486 result = COMM_BuildNewCommDCB(ptr, &dcb, &timeouts);
488 if(result)
490 *lpdcb = dcb;
491 if(lptimeouts) *lptimeouts = timeouts;
492 return TRUE;
494 else
496 WARN("Invalid device control string: %s\n", debugstr_w(devid));
497 SetLastError(ERROR_INVALID_PARAMETER);
498 return FALSE;
502 /**************************************************************************
503 * BuildCommDCBW (KERNEL32.@)
505 * Updates a device control block structure with values from an
506 * unicode device control string. The device control string has two forms
507 * normal and extended, it must be exclusively in one or the other form.
509 * RETURNS
511 * True on success, false on a malformed control string.
513 BOOL WINAPI BuildCommDCBW(
514 LPCWSTR devid, /* [in] The unicode device control string. */
515 LPDCB lpdcb) /* [out] The device control block to be updated. */
517 return BuildCommDCBAndTimeoutsW(devid,lpdcb,NULL);
520 /***********************************************************************
521 * FIXME:
522 * The functionality of CommConfigDialogA, GetDefaultCommConfig and
523 * SetDefaultCommConfig is implemented in a DLL (usually SERIALUI.DLL).
524 * This is dependent on the type of COMM port, but since it is doubtful
525 * anybody will get around to implementing support for fancy serial
526 * ports in WINE, this is hardcoded for the time being. The name of
527 * this DLL should be stored in and read from the system registry in
528 * the hive HKEY_LOCAL_MACHINE, key
529 * System\\CurrentControlSet\\Services\\Class\\Ports\\????
530 * where ???? is the port number... that is determined by PNP
531 * The DLL should be loaded when the COMM port is opened, and closed
532 * when the COMM port is closed. - MJM 20 June 2000
533 ***********************************************************************/
534 static const WCHAR lpszSerialUI[] = L"serialui.dll";
537 /***********************************************************************
538 * CommConfigDialogA (KERNEL32.@)
540 * Raises a dialog that allows the user to configure a comm port.
541 * Fills the COMMCONFIG struct with information specified by the user.
542 * This function should call a similar routine in the COMM driver...
544 * RETURNS
546 * TRUE on success, FALSE on failure
547 * If successful, the lpCommConfig structure will contain a new
548 * configuration for the comm port, as specified by the user.
550 * BUGS
551 * The library with the CommConfigDialog code is never unloaded.
552 * Perhaps this should be done when the comm port is closed?
554 BOOL WINAPI CommConfigDialogA(
555 LPCSTR lpszDevice, /* [in] name of communications device */
556 HWND hWnd, /* [in] parent window for the dialog */
557 LPCOMMCONFIG lpCommConfig) /* [out] pointer to struct to fill */
559 LPWSTR lpDeviceW = NULL;
560 DWORD len;
561 BOOL r;
563 TRACE("(%s, %p, %p)\n", debugstr_a(lpszDevice), hWnd, lpCommConfig);
565 if (lpszDevice)
567 len = MultiByteToWideChar( CP_ACP, 0, lpszDevice, -1, NULL, 0 );
568 lpDeviceW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
569 MultiByteToWideChar( CP_ACP, 0, lpszDevice, -1, lpDeviceW, len );
571 r = CommConfigDialogW(lpDeviceW, hWnd, lpCommConfig);
572 HeapFree( GetProcessHeap(), 0, lpDeviceW );
573 return r;
576 /***********************************************************************
577 * CommConfigDialogW (KERNEL32.@)
579 * See CommConfigDialogA.
581 BOOL WINAPI CommConfigDialogW(
582 LPCWSTR lpszDevice, /* [in] name of communications device */
583 HWND hWnd, /* [in] parent window for the dialog */
584 LPCOMMCONFIG lpCommConfig) /* [out] pointer to struct to fill */
586 DWORD (WINAPI *pCommConfigDialog)(LPCWSTR, HWND, LPCOMMCONFIG);
587 HMODULE hConfigModule;
588 DWORD res = ERROR_INVALID_PARAMETER;
590 TRACE("(%s, %p, %p)\n", debugstr_w(lpszDevice), hWnd, lpCommConfig);
591 hConfigModule = LoadLibraryW(lpszSerialUI);
593 if (hConfigModule) {
594 pCommConfigDialog = (void *)GetProcAddress(hConfigModule, "drvCommConfigDialogW");
595 if (pCommConfigDialog) {
596 res = pCommConfigDialog(lpszDevice, hWnd, lpCommConfig);
598 FreeLibrary(hConfigModule);
601 if (res) SetLastError(res);
602 return (res == ERROR_SUCCESS);
605 /***********************************************************************
606 * SetDefaultCommConfigW (KERNEL32.@)
608 * Initializes the default configuration for a communication device.
610 * PARAMS
611 * lpszDevice [I] Name of the device targeted for configuration
612 * lpCommConfig [I] PTR to a buffer with the configuration for the device
613 * dwSize [I] Number of bytes in the buffer
615 * RETURNS
616 * Failure: FALSE
617 * Success: TRUE, and default configuration saved
620 BOOL WINAPI SetDefaultCommConfigW(LPCWSTR lpszDevice, LPCOMMCONFIG lpCommConfig, DWORD dwSize)
622 BOOL (WINAPI *lpfnSetDefaultCommConfig)(LPCWSTR, LPCOMMCONFIG, DWORD);
623 HMODULE hConfigModule;
624 BOOL r = FALSE;
626 TRACE("(%s, %p, %u)\n", debugstr_w(lpszDevice), lpCommConfig, dwSize);
628 hConfigModule = LoadLibraryW(lpszSerialUI);
629 if(!hConfigModule)
630 return r;
632 lpfnSetDefaultCommConfig = (void *)GetProcAddress(hConfigModule, "drvSetDefaultCommConfigW");
633 if (lpfnSetDefaultCommConfig)
634 r = lpfnSetDefaultCommConfig(lpszDevice, lpCommConfig, dwSize);
636 FreeLibrary(hConfigModule);
638 return r;
642 /***********************************************************************
643 * SetDefaultCommConfigA (KERNEL32.@)
645 * Initializes the default configuration for a communication device.
647 * See SetDefaultCommConfigW.
650 BOOL WINAPI SetDefaultCommConfigA(LPCSTR lpszDevice, LPCOMMCONFIG lpCommConfig, DWORD dwSize)
652 BOOL r;
653 LPWSTR lpDeviceW = NULL;
654 DWORD len;
656 TRACE("(%s, %p, %u)\n", debugstr_a(lpszDevice), lpCommConfig, dwSize);
658 if (lpszDevice)
660 len = MultiByteToWideChar( CP_ACP, 0, lpszDevice, -1, NULL, 0 );
661 lpDeviceW = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
662 MultiByteToWideChar( CP_ACP, 0, lpszDevice, -1, lpDeviceW, len );
664 r = SetDefaultCommConfigW(lpDeviceW,lpCommConfig,dwSize);
665 HeapFree( GetProcessHeap(), 0, lpDeviceW );
666 return r;
670 /***********************************************************************
671 * GetDefaultCommConfigW (KERNEL32.@)
673 * Acquires the default configuration of the specified communication device. (unicode)
675 * RETURNS
677 * True on successful reading of the default configuration,
678 * if the device is not found or the buffer is too small.
680 BOOL WINAPI GetDefaultCommConfigW(
681 LPCWSTR lpszName, /* [in] The unicode name of the device targeted for configuration. */
682 LPCOMMCONFIG lpCC, /* [out] The default configuration for the device. */
683 LPDWORD lpdwSize) /* [in/out] Initially the size of the default configuration buffer,
684 afterwards the number of bytes copied to the buffer or
685 the needed size of the buffer. */
687 DWORD (WINAPI *pGetDefaultCommConfig)(LPCWSTR, LPCOMMCONFIG, LPDWORD);
688 HMODULE hConfigModule;
689 DWORD res = ERROR_INVALID_PARAMETER;
691 TRACE("(%s, %p, %p) *lpdwSize: %u\n", debugstr_w(lpszName), lpCC, lpdwSize, lpdwSize ? *lpdwSize : 0 );
692 hConfigModule = LoadLibraryW(lpszSerialUI);
694 if (hConfigModule) {
695 pGetDefaultCommConfig = (void *)GetProcAddress(hConfigModule, "drvGetDefaultCommConfigW");
696 if (pGetDefaultCommConfig) {
697 res = pGetDefaultCommConfig(lpszName, lpCC, lpdwSize);
699 FreeLibrary(hConfigModule);
702 if (res) SetLastError(res);
703 return (res == ERROR_SUCCESS);
706 /**************************************************************************
707 * GetDefaultCommConfigA (KERNEL32.@)
709 * Acquires the default configuration of the specified communication device. (ascii)
711 * RETURNS
713 * True on successful reading of the default configuration,
714 * if the device is not found or the buffer is too small.
716 BOOL WINAPI GetDefaultCommConfigA(
717 LPCSTR lpszName, /* [in] The ascii name of the device targeted for configuration. */
718 LPCOMMCONFIG lpCC, /* [out] The default configuration for the device. */
719 LPDWORD lpdwSize) /* [in/out] Initially the size of the default configuration buffer,
720 afterwards the number of bytes copied to the buffer or
721 the needed size of the buffer. */
723 BOOL ret = FALSE;
724 UNICODE_STRING lpszNameW;
726 TRACE("(%s, %p, %p) *lpdwSize: %u\n", debugstr_a(lpszName), lpCC, lpdwSize, lpdwSize ? *lpdwSize : 0 );
727 if(lpszName) RtlCreateUnicodeStringFromAsciiz(&lpszNameW,lpszName);
728 else lpszNameW.Buffer = NULL;
730 ret = GetDefaultCommConfigW(lpszNameW.Buffer,lpCC,lpdwSize);
732 RtlFreeUnicodeString(&lpszNameW);
733 return ret;