Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

783 lines
20 KiB

  1. // Copyright (c) 1998-1999 Microsoft Corporation
  2. /******************************************************************************
  3. *
  4. * CHGPORT.C
  5. *
  6. * Change serial port mapping.
  7. *
  8. *
  9. *
  10. *******************************************************************************/
  11. #include <nt.h>
  12. #include <ntrtl.h>
  13. #include <nturtl.h>
  14. #include <windows.h>
  15. #include <winstaw.h>
  16. #include <assert.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <utilsub.h>
  20. #include <string.h>
  21. #include <printfoa.h>
  22. #include <locale.h>
  23. #include "chgport.h"
  24. /*
  25. * Global Data
  26. */
  27. WCHAR user_string[MAX_IDS_LEN+1]; // parsed user input
  28. USHORT help_flag = FALSE; // User wants help
  29. USHORT fDelete = FALSE; // delete mapped port
  30. USHORT fquery = FALSE; // query the mapped ports
  31. PCOMNAME pValidNames = NULL; // list of valid com names in registry
  32. TOKMAP ptm[] = {
  33. {L" ", TMFLAG_OPTIONAL, TMFORM_STRING, MAX_IDS_LEN, user_string},
  34. {L"/d", TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &fDelete},
  35. {L"/?", TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &help_flag},
  36. {L"/QUERY", TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &fquery},
  37. {L"/Q", TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &fquery},
  38. {0, 0, 0, 0, 0}
  39. };
  40. /*
  41. * Constants
  42. */
  43. #define DOSDEVICE_STRING L"\\DosDevices"
  44. /*
  45. * Local function prototypes.
  46. */
  47. void Usage(BOOLEAN bError);
  48. BOOL DeleteMappedPort(PWCHAR user_string);
  49. BOOL GetPorts(PWCHAR user_string,
  50. PWCHAR pwcSrcPort,
  51. PWCHAR pwcDestPort,
  52. ULONG ulbufsize);
  53. BOOL MapPorts(PWCHAR pwcSrcPort,
  54. PWCHAR pwcDestPort);
  55. void ListSerialPorts();
  56. BOOL IsSerialDevice(PWCHAR pwcName);
  57. ULONG GetNTObjectName(PWCHAR pwcDOSdev,
  58. PWCHAR pwcNTObjName,
  59. ULONG ulbufsize);
  60. ULONG AddComName(PCOMNAME *pComList,
  61. PWCHAR pwcNTName,
  62. PWCHAR pwcDOSName);
  63. void DelComName(PCOMNAME pEntry);
  64. PCOMNAME FindComName(PCOMNAME pComList,
  65. PWCHAR pwcName);
  66. BOOL IsVDMdeviceName(PWCHAR pwcName);
  67. /*******************************************************************************
  68. *
  69. * main
  70. *
  71. ******************************************************************************/
  72. int __cdecl
  73. main(INT argc, CHAR **argv)
  74. {
  75. WCHAR **argvW;
  76. WCHAR wcSrcPort[MAX_PATH], wcDestPort[MAX_PATH];
  77. ULONG ulSrcPort, ulDestPort, rc;
  78. INT i;
  79. setlocale(LC_ALL, ".OCP");
  80. /*
  81. * Massage the command line.
  82. */
  83. argvW = MassageCommandLine((DWORD)argc);
  84. if (argvW == NULL) {
  85. ErrorPrintf(IDS_ERROR_MALLOC);
  86. return(FAILURE);
  87. }
  88. /*
  89. * parse the cmd line without parsing the program name (argc-1, argv+1)
  90. */
  91. rc = ParseCommandLine(argc-1, argvW+1, ptm, 0);
  92. /*
  93. * Check for error from ParseCommandLine
  94. */
  95. if ( help_flag || (rc && !(rc & PARSE_FLAG_NO_PARMS)) ) {
  96. if ( !help_flag ) {
  97. Usage(TRUE);
  98. return(FAILURE);
  99. } else {
  100. Usage(FALSE);
  101. return(SUCCESS);
  102. }
  103. }
  104. //If we are not Running under Terminal Server, Return Error
  105. if(!AreWeRunningTerminalServices())
  106. {
  107. ErrorPrintf(IDS_ERROR_NOT_TS);
  108. return (FAILURE);
  109. }
  110. if (fDelete) {
  111. DeleteMappedPort(user_string);
  112. } else if (*user_string) {
  113. GetPorts(user_string, wcSrcPort, wcDestPort, MAX_PATH);
  114. MapPorts(wcSrcPort, wcDestPort);
  115. } else { // query the mapped ports
  116. ListSerialPorts();
  117. }
  118. // Free up the list of valid port names
  119. if (pValidNames) {
  120. PCOMNAME pEntry, pPrev;
  121. pEntry = pValidNames;
  122. while (pEntry) {
  123. pPrev = pEntry;
  124. pEntry = pEntry->com_pnext;
  125. DelComName(pPrev);
  126. }
  127. }
  128. return(SUCCESS);
  129. }
  130. /*******************************************************************************
  131. *
  132. * Usage
  133. *
  134. * Output the usage message for this utility.
  135. *
  136. * ENTRY:
  137. * bError (input)
  138. * TRUE if the 'invalid parameter(s)' message should preceed the usage
  139. * message and the output go to stderr; FALSE for no such error
  140. * string and output goes to stdout.
  141. *
  142. * EXIT:
  143. *
  144. *
  145. ******************************************************************************/
  146. void
  147. Usage( BOOLEAN bError )
  148. {
  149. if ( bError ) {
  150. ErrorPrintf(IDS_ERROR_INVALID_PARAMETERS);
  151. }
  152. ErrorPrintf(IDS_HELP_USAGE1);
  153. ErrorPrintf(IDS_HELP_USAGE2);
  154. ErrorPrintf(IDS_HELP_USAGE3);
  155. ErrorPrintf(IDS_HELP_USAGE4);
  156. ErrorPrintf(IDS_HELP_USAGE5);
  157. } /* Usage() */
  158. /*******************************************************************************
  159. *
  160. * DeleteMappedPort
  161. *
  162. * This routine deletes the specified mapped port
  163. *
  164. *
  165. * ENTRY:
  166. * PWCHAR pwcport (In): Pointer to port mapping to delete
  167. *
  168. * EXIT:
  169. * TRUE: port was deleted
  170. * FALSE: error deleting port
  171. *
  172. ******************************************************************************/
  173. BOOL DeleteMappedPort(PWCHAR pwcport)
  174. {
  175. ULONG rc;
  176. PWCHAR pwch;
  177. WCHAR wcbuff[MAX_PATH];
  178. // Check if this a serial device and if it is, remove it
  179. if (!GetNTObjectName(pwcport, wcbuff, sizeof(wcbuff)/sizeof(WCHAR)) &&
  180. IsSerialDevice(wcbuff)) {
  181. if (DefineDosDevice(DDD_REMOVE_DEFINITION,
  182. pwcport,
  183. NULL)) {
  184. return(TRUE);
  185. } else {
  186. rc = GetLastError();
  187. }
  188. } else {
  189. rc = ERROR_FILE_NOT_FOUND;
  190. }
  191. StringDwordErrorPrintf(IDS_ERROR_DEL_PORT_MAPPING, pwcport, rc);
  192. return(FALSE);
  193. }
  194. /*******************************************************************************
  195. *
  196. * GetPorts
  197. *
  198. * This routine converts the string to the source and destination ports
  199. *
  200. *
  201. * ENTRY:
  202. * PWCHAR pwcstring (In): Pointer to user string
  203. * PWCHAR pwcSrcPort (Out): Pointer to return source port
  204. * PWCHAR pwcSrcPort (Out): Pointer to return destination port
  205. * ULONG ulbufsize (In): Size of return buffers
  206. *
  207. * EXIT:
  208. * TRUE: string converted to source and destination ports
  209. * FALSE: error
  210. *
  211. ******************************************************************************/
  212. BOOL GetPorts(PWCHAR pwcstring, PWCHAR pwcSrcPort, PWCHAR pwcDestPort,
  213. ULONG ulbufsize)
  214. {
  215. PWCHAR pwch;
  216. ULONG ulcnt;
  217. BOOL fSawEqual = FALSE;
  218. pwch = pwcstring;
  219. // find next non alphanumeric character
  220. for (ulcnt = 0; pwch[ulcnt] && iswalnum(pwch[ulcnt]); ulcnt++) {
  221. }
  222. // Get the source port
  223. if (pwch[ulcnt] && (ulcnt < ulbufsize)) {
  224. wcsncpy(pwcSrcPort, pwch, ulcnt);
  225. } else {
  226. return(FALSE);
  227. }
  228. pwcSrcPort[ulcnt] = L'\0';
  229. pwch += ulcnt;
  230. // get to destination port
  231. while (*pwch && !iswalnum(*pwch)) {
  232. if (*pwch == L'=') {
  233. fSawEqual = TRUE;
  234. }
  235. pwch++;
  236. }
  237. // If the syntax is OK and there's room in the buffer, copy the dest. port
  238. if (*pwch && fSawEqual && (wcslen(pwch) < ulbufsize)) {
  239. wcscpy(pwcDestPort, pwch);
  240. } else {
  241. return(FALSE);
  242. }
  243. // remove the : if they entered comn:
  244. if (pwch = wcsrchr(pwcSrcPort, L':')) {
  245. *pwch = L'\0';
  246. }
  247. if (pwch = wcsrchr(pwcDestPort, L':')) {
  248. *pwch = L'\0';
  249. }
  250. return(TRUE);
  251. }
  252. /*******************************************************************************
  253. *
  254. * MapPorts
  255. *
  256. * This routine maps the source port number to the destination port.
  257. *
  258. *
  259. * ENTRY:
  260. * PWCHAR pwcSrcPort (In): Source port
  261. * PWCHAR pwcDestPort (In): Destination port
  262. *
  263. * EXIT:
  264. * TRUE: port was mapped
  265. * FALSE: error mapping port
  266. *
  267. ******************************************************************************/
  268. BOOL MapPorts(PWCHAR pwcSrcPort, PWCHAR pwcDestPort)
  269. {
  270. ULONG rc = ERROR_FILE_NOT_FOUND;
  271. WCHAR wcdest[MAX_PATH], wcsrc[MAX_PATH];
  272. PWCHAR pFixedPort = NULL;
  273. // Get the NT name of the destination and make sure it's a serial device
  274. if (!GetNTObjectName(pwcDestPort, wcdest, sizeof(wcdest)/sizeof(WCHAR)) &&
  275. IsSerialDevice(wcdest)) {
  276. // see if this mapping already exists
  277. if (!GetNTObjectName(pwcSrcPort, wcsrc, sizeof(wcsrc)/sizeof(WCHAR)) &&
  278. !_wcsicmp(wcdest, wcsrc)) {
  279. ErrorPrintf(IDS_ERROR_PORT_MAPPING_EXISTS,
  280. pwcSrcPort,
  281. pwcDestPort);
  282. return(FALSE);
  283. }
  284. if (DefineDosDevice(DDD_RAW_TARGET_PATH,
  285. pwcSrcPort,
  286. wcdest)) {
  287. return(TRUE);
  288. } else {
  289. rc = GetLastError();
  290. }
  291. }
  292. StringDwordErrorPrintf(IDS_ERROR_CREATE_PORT_MAPPING, pwcSrcPort, rc);
  293. return(FALSE);
  294. }
  295. /*******************************************************************************
  296. *
  297. * GetNTObjectName
  298. *
  299. * This routine returns the NT object name for a DOS device.
  300. *
  301. * ENTRY:
  302. * PWCHAR pwcDOSdev (In): pointer to DOS device name
  303. * PWCHAR pwcNTObjName (Out): pointer for NT object name
  304. * ULONG ulbufsize (In): size (in wide chars) of object name buffer
  305. *
  306. * EXIT:
  307. * Success:
  308. * returns 0
  309. * Failure:
  310. * returns error code
  311. *
  312. ******************************************************************************/
  313. ULONG GetNTObjectName(PWCHAR pwcDOSdev, PWCHAR pwcNTObjName, ULONG ulbufsize)
  314. {
  315. WCHAR wcbuff[MAX_PATH];
  316. PWCHAR pwch;
  317. // Make a copy of the name passed in
  318. wcscpy(wcbuff, pwcDOSdev);
  319. // Strip off any trailing colon (comn:)
  320. if (pwch = wcsrchr(wcbuff, L':')) {
  321. *pwch = L'\0';
  322. }
  323. if (QueryDosDevice(pwcDOSdev, pwcNTObjName, ulbufsize)) {
  324. return(0);
  325. } else {
  326. return(GetLastError());
  327. }
  328. }
  329. /*******************************************************************************
  330. *
  331. * ListSerialPorts
  332. *
  333. * This routine lists all of the mapped ports.
  334. *
  335. * ENTRY:
  336. *
  337. * EXIT:
  338. *
  339. ******************************************************************************/
  340. void ListSerialPorts(void)
  341. {
  342. ULONG ulcnt, rc;
  343. // WCHAR DeviceNames[4096];
  344. WCHAR TargetPath[4096];
  345. PWCH pwch;
  346. PCOMNAME pComList = NULL;
  347. PCOMNAME pEntry, pPrev;
  348. DWORD dwBufferSize = 2048;
  349. WCHAR *DeviceNames = malloc(dwBufferSize);
  350. if (!DeviceNames) {
  351. ErrorPrintf(IDS_ERROR_MALLOC);
  352. return;
  353. }
  354. //
  355. // Get all of the defined DOS devices
  356. //
  357. //
  358. // QueryDosDevice function returns success even if buffer is too small!
  359. // Lets get around it
  360. //
  361. SetLastError(0);
  362. while (!QueryDosDevice( NULL,
  363. DeviceNames,
  364. dwBufferSize/sizeof(WCHAR)) ||
  365. GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
  366. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  367. {
  368. SetLastError(0);
  369. free(DeviceNames);
  370. dwBufferSize *= 2;
  371. DeviceNames = malloc(dwBufferSize);
  372. if (!DeviceNames)
  373. {
  374. ErrorPrintf(IDS_ERROR_MALLOC);
  375. return;
  376. }
  377. }
  378. else
  379. {
  380. ErrorPrintf(IDS_ERROR_GETTING_COMPORTS, GetLastError());
  381. }
  382. }
  383. pwch = DeviceNames;
  384. // Go through each DOS device and get it's NT object name, then check if
  385. // it's a serial device, and if so display it
  386. while (*pwch) {
  387. rc = GetNTObjectName(pwch,
  388. TargetPath,
  389. sizeof(TargetPath)/sizeof(WCHAR));
  390. if (rc) {
  391. ErrorPrintf(IDS_ERROR_GETTING_COMPORTS, rc);
  392. } else if (IsSerialDevice(TargetPath)) {
  393. AddComName(&pComList, TargetPath, pwch);
  394. }
  395. pwch += wcslen(pwch) + 1;
  396. }
  397. if (pComList) {
  398. // print out the entries
  399. pEntry = pComList;
  400. while (pEntry) {
  401. wprintf(L"%s = %s\n", pEntry->com_pwcDOSName, pEntry->com_pwcNTName);
  402. pPrev = pEntry;
  403. pEntry = pEntry->com_pnext;
  404. DelComName(pPrev);
  405. }
  406. } else {
  407. ErrorPrintf(IDS_ERROR_NO_SERIAL_PORTS);
  408. }
  409. free(DeviceNames);
  410. }
  411. /*******************************************************************************
  412. *
  413. * IsSerialDevice
  414. *
  415. * This routine checks if the NT file name is a serial device
  416. *
  417. *
  418. * ENTRY:
  419. * PWCHAR pwcName (In): Pointer to name to check
  420. *
  421. * EXIT:
  422. * TRUE: Is a serial device
  423. * FALSE: Not a serial device
  424. *
  425. ******************************************************************************/
  426. BOOL IsSerialDevice(PWCHAR pwcName)
  427. {
  428. NTSTATUS Status;
  429. HANDLE Handle;
  430. IO_STATUS_BLOCK IoStatusBlock;
  431. FILE_FS_DEVICE_INFORMATION FileFSDevInfo;
  432. OBJECT_ATTRIBUTES ObjFile;
  433. UNICODE_STRING UniFile;
  434. WCHAR wcbuff[MAX_PATH];
  435. WCHAR wcvalue[MAX_PATH];
  436. PWCHAR pwch;
  437. HKEY hKey;
  438. ULONG ulType, ulSize, ulcnt, ulValSize;
  439. BOOL fIsSerial = FALSE;
  440. if (IsVDMdeviceName(pwcName)) {
  441. return FALSE;
  442. }
  443. RtlInitUnicodeString(&UniFile, pwcName);
  444. InitializeObjectAttributes(&ObjFile,
  445. &UniFile,
  446. OBJ_CASE_INSENSITIVE,
  447. NULL,
  448. NULL);
  449. //
  450. // Open the device
  451. //
  452. Status = NtOpenFile(&Handle,
  453. (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  454. &ObjFile,
  455. &IoStatusBlock,
  456. FILE_SHARE_READ | FILE_SHARE_WRITE,
  457. FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
  458. if (NT_SUCCESS(Status)) {
  459. Status = NtQueryVolumeInformationFile(Handle,
  460. &IoStatusBlock,
  461. &FileFSDevInfo,
  462. sizeof(FileFSDevInfo),
  463. FileFsDeviceInformation);
  464. // Check if this is actually a serial device or not
  465. if (NT_SUCCESS(Status) &&
  466. (FileFSDevInfo.DeviceType == FILE_DEVICE_SERIAL_PORT)) {
  467. fIsSerial = TRUE;
  468. }
  469. // Close the file handle
  470. NtClose(Handle);
  471. } else {
  472. // If we couldn't open the device, look for the name in the registry
  473. #ifdef DEBUG
  474. wprintf(L"Error opening: %s, error = %x\n", pwcName, Status);
  475. #endif
  476. // strip off the leading \device
  477. pwch = wcschr(pwcName+2, L'\\');
  478. if (pwch != NULL)
  479. {
  480. pwch++;
  481. // If we haven't built the list of valid names from the registry,
  482. // build it.
  483. if (pValidNames == NULL) {
  484. // Open the serialcomm entry in the registry
  485. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  486. L"Hardware\\DeviceMap\\SerialComm",
  487. 0,
  488. KEY_READ,
  489. &hKey) == ERROR_SUCCESS) {
  490. ulValSize = ulSize = MAX_PATH;
  491. ulcnt = 0;
  492. // Put all of the valid entries into the valid names list
  493. while (!RegEnumValue (hKey, ulcnt++, wcvalue, &ulValSize,
  494. NULL, &ulType, (LPBYTE) wcbuff, &ulSize))
  495. {
  496. if (ulType != REG_SZ)
  497. continue;
  498. AddComName(&pValidNames, wcvalue, wcbuff);
  499. ulValSize = ulSize = MAX_PATH;
  500. }
  501. RegCloseKey(hKey);
  502. }
  503. }
  504. // look for the name in the list of valid com names
  505. if (FindComName(pValidNames, pwch)) {
  506. fIsSerial = TRUE;
  507. }
  508. }
  509. }
  510. return(fIsSerial);
  511. }
  512. /*****************************************************************************
  513. *
  514. * AddComName
  515. *
  516. * This routines adds a new node onto the specified com port names.
  517. *
  518. * ENTRY:
  519. * PCOMNAME *pComList (In) - Pointer to list to add entry to
  520. * PWCHAR pwcNTName (In) - NT name of device
  521. * PWCHAR pwcDOSName (In) - DOW name of device
  522. *
  523. * EXIT:
  524. * SUCCESS:
  525. * return ERROR_SUCCESS
  526. * FAILURE:
  527. * returns error code
  528. *
  529. ****************************************************************************/
  530. ULONG AddComName(PCOMNAME *pComList,
  531. PWCHAR pwcNTName,
  532. PWCHAR pwcDOSName)
  533. {
  534. PCOMNAME pnext, pprev, pnew;
  535. LONG rc = ERROR_SUCCESS;
  536. if (pnew = malloc(sizeof(COMNAME))) {
  537. // clear out the new entry
  538. memset(pnew, 0, sizeof(COMNAME));
  539. // Allocate and initialize the NT name
  540. if (pnew->com_pwcNTName =
  541. malloc((wcslen(pwcNTName) + 1)*sizeof(WCHAR))) {
  542. wcscpy(pnew->com_pwcNTName, pwcNTName);
  543. } else {
  544. rc = ERROR_NOT_ENOUGH_MEMORY;
  545. }
  546. // Allocate and initialize the DOS name
  547. if ((rc == ERROR_SUCCESS) && (pnew->com_pwcDOSName =
  548. malloc((wcslen(pwcDOSName) + 1)*sizeof(WCHAR)))) {
  549. wcscpy(pnew->com_pwcDOSName, pwcDOSName);
  550. } else {
  551. rc = ERROR_NOT_ENOUGH_MEMORY;
  552. }
  553. } else {
  554. rc = ERROR_NOT_ENOUGH_MEMORY;
  555. }
  556. // If we allocate everything OK, add the node into the list
  557. if (rc == ERROR_SUCCESS) {
  558. pprev = NULL;
  559. pnext = *pComList;
  560. // Insert the entry into the list in ascending order
  561. while (pnext &&
  562. ((rc = _wcsicmp(pwcDOSName, pnext->com_pwcDOSName)) > 0)) {
  563. pprev = pnext;
  564. pnext = pnext->com_pnext;
  565. }
  566. // just return if this name is already in the list
  567. if (pnext && (rc == 0)) {
  568. return(ERROR_SUCCESS);
  569. }
  570. // Insert this entry into the list
  571. pnew->com_pnext = pnext;
  572. // If this is going to the front of the list, update list pointer
  573. if (pprev == NULL) {
  574. *pComList = pnew;
  575. } else {
  576. pprev->com_pnext = pnew;
  577. }
  578. } else if (pnew) {
  579. // Didn't allocate everything, release the memory we got
  580. DelComName(pnew);
  581. }
  582. return(rc);
  583. }
  584. /*****************************************************************************
  585. *
  586. * DelComName
  587. *
  588. * This routines frees up the memory allocated to a com name node.
  589. *
  590. * ENTRY:
  591. * PCOMNAME pEntry (In) - Node to delete
  592. *
  593. * EXIT:
  594. * NONE
  595. *
  596. ****************************************************************************/
  597. void DelComName(PCOMNAME pEntry)
  598. {
  599. if (pEntry) {
  600. if (pEntry->com_pwcNTName) {
  601. free(pEntry->com_pwcNTName);
  602. }
  603. if (pEntry->com_pwcDOSName) {
  604. free(pEntry->com_pwcDOSName);
  605. }
  606. free(pEntry);
  607. }
  608. }
  609. /*****************************************************************************
  610. *
  611. * FindComName
  612. *
  613. * This routines searches for the specified name in the com port list.
  614. *
  615. * ENTRY:
  616. * PCOMNAME pComList (In) - List to search
  617. * PWCHAR pwcName (In) - Name to search for
  618. *
  619. * EXIT:
  620. * SUCCESS:
  621. * returns pointer to node containing the specified name
  622. * FAILURE:
  623. * returns NULL (name not found)
  624. *
  625. ****************************************************************************/
  626. PCOMNAME FindComName(PCOMNAME pComList,
  627. PWCHAR pwcName)
  628. {
  629. PCOMNAME pcom;
  630. pcom = pComList;
  631. while (pcom) {
  632. //Check if the name matches either the NT or DOS device name
  633. if (!_wcsicmp(pwcName, pcom->com_pwcDOSName) ||
  634. !_wcsicmp(pwcName, pcom->com_pwcNTName)) {
  635. return(pcom);
  636. }
  637. pcom = pcom->com_pnext;
  638. }
  639. return(NULL);
  640. }
  641. BOOL IsVDMdeviceName(PWCHAR pwcName)
  642. {
  643. UINT index;
  644. UINT vdmlength = wcslen(L"VDM");
  645. for (index = 0; (index+vdmlength-1) < wcslen(pwcName); index++) {
  646. if (_wcsnicmp(&pwcName[index], L"VDM", vdmlength) == 0) {
  647. return TRUE;
  648. }
  649. }
  650. return FALSE;
  651. }
  652.