Leaked source code of windows server 2003
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.

1106 lines
25 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. devcon.cpp
  5. Abstract:
  6. Device Console
  7. command-line interface for managing devices
  8. @@BEGIN_DDKSPLIT
  9. Author:
  10. Jamie Hunter (JamieHun) Nov-30-2000
  11. Revision History:
  12. @@END_DDKSPLIT
  13. --*/
  14. #include "devcon.h"
  15. struct IdEntry {
  16. LPCTSTR String; // string looking for
  17. LPCTSTR Wild; // first wild character if any
  18. BOOL InstanceId;
  19. };
  20. void FormatToStream(FILE * stream,DWORD fmt,...)
  21. /*++
  22. Routine Description:
  23. Format text to stream using a particular msg-id fmt
  24. Used for displaying localizable messages
  25. Arguments:
  26. stream - file stream to output to, stdout or stderr
  27. fmt - message id
  28. ... - parameters %1...
  29. Return Value:
  30. none
  31. --*/
  32. {
  33. va_list arglist;
  34. LPTSTR locbuffer = NULL;
  35. DWORD count;
  36. va_start(arglist, fmt);
  37. count = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE|FORMAT_MESSAGE_ALLOCATE_BUFFER,
  38. NULL,
  39. fmt,
  40. 0, // LANGID
  41. (LPTSTR) &locbuffer,
  42. 0, // minimum size of buffer
  43. &arglist);
  44. if(locbuffer) {
  45. if(count) {
  46. int c;
  47. int back = 0;
  48. //
  49. // strip any trailing "\r\n"s and replace by a single "\n"
  50. //
  51. while(((c = *CharPrev(locbuffer,locbuffer+count)) == TEXT('\r')) ||
  52. (c == TEXT('\n'))) {
  53. count--;
  54. back++;
  55. }
  56. if(back) {
  57. locbuffer[count++] = TEXT('\n');
  58. locbuffer[count] = TEXT('\0');
  59. }
  60. //
  61. // now write to apropriate stream
  62. //
  63. _fputts(locbuffer,stream);
  64. }
  65. LocalFree(locbuffer);
  66. }
  67. }
  68. void Padding(int pad)
  69. /*++
  70. Routine Description:
  71. Insert padding into line before text
  72. Arguments:
  73. pad - number of padding tabs to insert
  74. Return Value:
  75. none
  76. --*/
  77. {
  78. int c;
  79. for(c=0;c<pad;c++) {
  80. fputs(" ",stdout);
  81. }
  82. }
  83. void Usage(LPCTSTR BaseName)
  84. /*++
  85. Routine Description:
  86. Display simple usage text
  87. Arguments:
  88. BaseName - name of executable
  89. Return Value:
  90. none
  91. --*/
  92. {
  93. FormatToStream(stderr,MSG_USAGE,BaseName);
  94. }
  95. void CommandUsage(LPCTSTR BaseName,LPCTSTR Cmd)
  96. /*++
  97. Routine Description:
  98. Invalid command usage
  99. Display how to get help on command
  100. Arguments:
  101. BaseName - name of executable
  102. Return Value:
  103. none
  104. --*/
  105. {
  106. FormatToStream(stderr,MSG_COMMAND_USAGE,BaseName,Cmd);
  107. }
  108. void Failure(LPCTSTR BaseName,LPCTSTR Cmd)
  109. /*++
  110. Routine Description:
  111. Display simple error text for general failure
  112. Arguments:
  113. BaseName - name of executable
  114. Return Value:
  115. none
  116. --*/
  117. {
  118. FormatToStream(stderr,MSG_FAILURE,BaseName,Cmd);
  119. }
  120. BOOL Reboot()
  121. /*++
  122. Routine Description:
  123. Attempt to reboot computer
  124. Arguments:
  125. none
  126. Return Value:
  127. TRUE if API suceeded
  128. --*/
  129. {
  130. HANDLE Token;
  131. BOOL b;
  132. TOKEN_PRIVILEGES NewPrivileges;
  133. LUID Luid;
  134. //
  135. // we need to "turn on" reboot privilege
  136. // if any of this fails, try reboot anyway
  137. //
  138. if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&Token)) {
  139. goto final;
  140. }
  141. if(!LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&Luid)) {
  142. CloseHandle(Token);
  143. goto final;
  144. }
  145. NewPrivileges.PrivilegeCount = 1;
  146. NewPrivileges.Privileges[0].Luid = Luid;
  147. NewPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  148. AdjustTokenPrivileges(
  149. Token,
  150. FALSE,
  151. &NewPrivileges,
  152. 0,
  153. NULL,
  154. NULL
  155. );
  156. CloseHandle(Token);
  157. final:
  158. //
  159. // attempt reboot - inform system that this is planned hardware install
  160. //
  161. return ExitWindowsEx(EWX_REBOOT, REASON_PLANNED_FLAG|REASON_HWINSTALL);
  162. }
  163. LPTSTR GetDeviceStringProperty(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Prop)
  164. /*++
  165. Routine Description:
  166. Return a string property for a device, otherwise NULL
  167. Arguments:
  168. Devs )_ uniquely identify device
  169. DevInfo )
  170. Prop - string property to obtain
  171. Return Value:
  172. string containing description
  173. --*/
  174. {
  175. LPTSTR buffer;
  176. DWORD size;
  177. DWORD reqSize;
  178. DWORD dataType;
  179. DWORD szChars;
  180. size = 1024; // initial guess
  181. buffer = new TCHAR[(size/sizeof(TCHAR))+1];
  182. if(!buffer) {
  183. return NULL;
  184. }
  185. while(!SetupDiGetDeviceRegistryProperty(Devs,DevInfo,Prop,&dataType,(LPBYTE)buffer,size,&reqSize)) {
  186. if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  187. goto failed;
  188. }
  189. if(dataType != REG_SZ) {
  190. goto failed;
  191. }
  192. size = reqSize;
  193. delete [] buffer;
  194. buffer = new TCHAR[(size/sizeof(TCHAR))+1];
  195. if(!buffer) {
  196. goto failed;
  197. }
  198. }
  199. szChars = reqSize/sizeof(TCHAR);
  200. buffer[szChars] = TEXT('\0');
  201. return buffer;
  202. failed:
  203. if(buffer) {
  204. delete [] buffer;
  205. }
  206. return NULL;
  207. }
  208. LPTSTR GetDeviceDescription(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo)
  209. /*++
  210. Routine Description:
  211. Return a string containing a description of the device, otherwise NULL
  212. Always try friendly name first
  213. Arguments:
  214. Devs )_ uniquely identify device
  215. DevInfo )
  216. Return Value:
  217. string containing description
  218. --*/
  219. {
  220. LPTSTR desc;
  221. desc = GetDeviceStringProperty(Devs,DevInfo,SPDRP_FRIENDLYNAME);
  222. if(!desc) {
  223. desc = GetDeviceStringProperty(Devs,DevInfo,SPDRP_DEVICEDESC);
  224. }
  225. return desc;
  226. }
  227. IdEntry GetIdType(LPCTSTR Id)
  228. /*++
  229. Routine Description:
  230. Determine if this is instance id or hardware id and if there's any wildcards
  231. instance ID is prefixed by '@'
  232. wildcards are '*'
  233. Arguments:
  234. Id - ptr to string to check
  235. Return Value:
  236. IdEntry
  237. --*/
  238. {
  239. IdEntry Entry;
  240. Entry.InstanceId = FALSE;
  241. Entry.Wild = NULL;
  242. Entry.String = Id;
  243. if(Entry.String[0] == INSTANCEID_PREFIX_CHAR) {
  244. Entry.InstanceId = TRUE;
  245. Entry.String = CharNext(Entry.String);
  246. }
  247. if(Entry.String[0] == QUOTE_PREFIX_CHAR) {
  248. //
  249. // prefix to treat rest of string literally
  250. //
  251. Entry.String = CharNext(Entry.String);
  252. } else {
  253. //
  254. // see if any wild characters exist
  255. //
  256. Entry.Wild = _tcschr(Entry.String,WILD_CHAR);
  257. }
  258. return Entry;
  259. }
  260. LPTSTR * GetMultiSzIndexArray(LPTSTR MultiSz)
  261. /*++
  262. Routine Description:
  263. Get an index array pointing to the MultiSz passed in
  264. Arguments:
  265. MultiSz - well formed multi-sz string
  266. Return Value:
  267. array of strings. last entry+1 of array contains NULL
  268. returns NULL on failure
  269. --*/
  270. {
  271. LPTSTR scan;
  272. LPTSTR * array;
  273. int elements;
  274. for(scan = MultiSz, elements = 0; scan[0] ;elements++) {
  275. scan += lstrlen(scan)+1;
  276. }
  277. array = new LPTSTR[elements+2];
  278. if(!array) {
  279. return NULL;
  280. }
  281. array[0] = MultiSz;
  282. array++;
  283. if(elements) {
  284. for(scan = MultiSz, elements = 0; scan[0]; elements++) {
  285. array[elements] = scan;
  286. scan += lstrlen(scan)+1;
  287. }
  288. }
  289. array[elements] = NULL;
  290. return array;
  291. }
  292. LPTSTR * CopyMultiSz(LPTSTR * Array)
  293. /*++
  294. Routine Description:
  295. Creates a new array from old
  296. old array need not have been allocated by GetMultiSzIndexArray
  297. Arguments:
  298. Array - array of strings, last entry is NULL
  299. Return Value:
  300. MultiSz array allocated by GetMultiSzIndexArray
  301. --*/
  302. {
  303. LPTSTR multiSz = NULL;
  304. int len = 0;
  305. int c;
  306. if(Array) {
  307. for(c=0;Array[c];c++) {
  308. len+=lstrlen(Array[c])+1;
  309. }
  310. }
  311. len+=1; // final Null
  312. multiSz = new TCHAR[len];
  313. if(!multiSz) {
  314. return NULL;
  315. }
  316. len = 0;
  317. if(Array) {
  318. for(c=0;Array[c];c++) {
  319. lstrcpy(multiSz+len,Array[c]);
  320. len+=lstrlen(multiSz+len)+1;
  321. }
  322. }
  323. multiSz[len] = TEXT('\0');
  324. LPTSTR * pRes = GetMultiSzIndexArray(multiSz);
  325. if(pRes) {
  326. return pRes;
  327. }
  328. delete [] multiSz;
  329. return NULL;
  330. }
  331. void DelMultiSz(LPTSTR * Array)
  332. /*++
  333. Routine Description:
  334. Deletes the string array allocated by GetDevMultiSz/GetRegMultiSz/GetMultiSzIndexArray
  335. Arguments:
  336. Array - pointer returned by GetMultiSzIndexArray
  337. Return Value:
  338. None
  339. --*/
  340. {
  341. if(Array) {
  342. Array--;
  343. if(Array[0]) {
  344. delete [] Array[0];
  345. }
  346. delete [] Array;
  347. }
  348. }
  349. LPTSTR * GetDevMultiSz(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Prop)
  350. /*++
  351. Routine Description:
  352. Get a multi-sz device property
  353. and return as an array of strings
  354. Arguments:
  355. Devs - HDEVINFO containing DevInfo
  356. DevInfo - Specific device
  357. Prop - SPDRP_HARDWAREID or SPDRP_COMPATIBLEIDS
  358. Return Value:
  359. array of strings. last entry+1 of array contains NULL
  360. returns NULL on failure
  361. --*/
  362. {
  363. LPTSTR buffer;
  364. DWORD size;
  365. DWORD reqSize;
  366. DWORD dataType;
  367. LPTSTR * array;
  368. DWORD szChars;
  369. size = 8192; // initial guess, nothing magic about this
  370. buffer = new TCHAR[(size/sizeof(TCHAR))+2];
  371. if(!buffer) {
  372. return NULL;
  373. }
  374. while(!SetupDiGetDeviceRegistryProperty(Devs,DevInfo,Prop,&dataType,(LPBYTE)buffer,size,&reqSize)) {
  375. if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  376. goto failed;
  377. }
  378. if(dataType != REG_MULTI_SZ) {
  379. goto failed;
  380. }
  381. size = reqSize;
  382. delete [] buffer;
  383. buffer = new TCHAR[(size/sizeof(TCHAR))+2];
  384. if(!buffer) {
  385. goto failed;
  386. }
  387. }
  388. szChars = reqSize/sizeof(TCHAR);
  389. buffer[szChars] = TEXT('\0');
  390. buffer[szChars+1] = TEXT('\0');
  391. array = GetMultiSzIndexArray(buffer);
  392. if(array) {
  393. return array;
  394. }
  395. failed:
  396. if(buffer) {
  397. delete [] buffer;
  398. }
  399. return NULL;
  400. }
  401. LPTSTR * GetRegMultiSz(HKEY hKey,LPCTSTR Val)
  402. /*++
  403. Routine Description:
  404. Get a multi-sz from registry
  405. and return as an array of strings
  406. Arguments:
  407. hKey - Registry Key
  408. Val - Value to query
  409. Return Value:
  410. array of strings. last entry+1 of array contains NULL
  411. returns NULL on failure
  412. --*/
  413. {
  414. LPTSTR buffer;
  415. DWORD size;
  416. DWORD reqSize;
  417. DWORD dataType;
  418. LPTSTR * array;
  419. DWORD szChars;
  420. LONG regErr;
  421. size = 8192; // initial guess, nothing magic about this
  422. buffer = new TCHAR[(size/sizeof(TCHAR))+2];
  423. if(!buffer) {
  424. return NULL;
  425. }
  426. reqSize = size;
  427. while((regErr = RegQueryValueEx(hKey,Val,NULL,&dataType,(PBYTE)buffer,&reqSize) != NO_ERROR)) {
  428. if(GetLastError() != ERROR_MORE_DATA) {
  429. goto failed;
  430. }
  431. if(dataType != REG_MULTI_SZ) {
  432. goto failed;
  433. }
  434. size = reqSize;
  435. delete [] buffer;
  436. buffer = new TCHAR[(size/sizeof(TCHAR))+2];
  437. if(!buffer) {
  438. goto failed;
  439. }
  440. }
  441. szChars = reqSize/sizeof(TCHAR);
  442. buffer[szChars] = TEXT('\0');
  443. buffer[szChars+1] = TEXT('\0');
  444. array = GetMultiSzIndexArray(buffer);
  445. if(array) {
  446. return array;
  447. }
  448. failed:
  449. if(buffer) {
  450. delete [] buffer;
  451. }
  452. return NULL;
  453. }
  454. BOOL WildCardMatch(LPCTSTR Item,const IdEntry & MatchEntry)
  455. /*++
  456. Routine Description:
  457. Compare a single item against wildcard
  458. I'm sure there's better ways of implementing this
  459. Other than a command-line management tools
  460. it's a bad idea to use wildcards as it implies
  461. assumptions about the hardware/instance ID
  462. eg, it might be tempting to enumerate root\* to
  463. find all root devices, however there is a CfgMgr
  464. API to query status and determine if a device is
  465. root enumerated, which doesn't rely on implementation
  466. details.
  467. Arguments:
  468. Item - item to find match for eg a\abcd\c
  469. MatchEntry - eg *\*bc*\*
  470. Return Value:
  471. TRUE if any match, otherwise FALSE
  472. --*/
  473. {
  474. LPCTSTR scanItem;
  475. LPCTSTR wildMark;
  476. LPCTSTR nextWild;
  477. size_t matchlen;
  478. //
  479. // before attempting anything else
  480. // try and compare everything up to first wild
  481. //
  482. if(!MatchEntry.Wild) {
  483. return _tcsicmp(Item,MatchEntry.String) ? FALSE : TRUE;
  484. }
  485. if(_tcsnicmp(Item,MatchEntry.String,MatchEntry.Wild-MatchEntry.String) != 0) {
  486. return FALSE;
  487. }
  488. wildMark = MatchEntry.Wild;
  489. scanItem = Item + (MatchEntry.Wild-MatchEntry.String);
  490. for(;wildMark[0];) {
  491. //
  492. // if we get here, we're either at or past a wildcard
  493. //
  494. if(wildMark[0] == WILD_CHAR) {
  495. //
  496. // so skip wild chars
  497. //
  498. wildMark = CharNext(wildMark);
  499. continue;
  500. }
  501. //
  502. // find next wild-card
  503. //
  504. nextWild = _tcschr(wildMark,WILD_CHAR);
  505. if(nextWild) {
  506. //
  507. // substring
  508. //
  509. matchlen = nextWild-wildMark;
  510. } else {
  511. //
  512. // last portion of match
  513. //
  514. size_t scanlen = lstrlen(scanItem);
  515. matchlen = lstrlen(wildMark);
  516. if(scanlen < matchlen) {
  517. return FALSE;
  518. }
  519. return _tcsicmp(scanItem+scanlen-matchlen,wildMark) ? FALSE : TRUE;
  520. }
  521. if(_istalpha(wildMark[0])) {
  522. //
  523. // scan for either lower or uppercase version of first character
  524. //
  525. TCHAR u = _totupper(wildMark[0]);
  526. TCHAR l = _totlower(wildMark[0]);
  527. while(scanItem[0] && scanItem[0]!=u && scanItem[0]!=l) {
  528. scanItem = CharNext(scanItem);
  529. }
  530. if(!scanItem[0]) {
  531. //
  532. // ran out of string
  533. //
  534. return FALSE;
  535. }
  536. } else {
  537. //
  538. // scan for first character (no case)
  539. //
  540. scanItem = _tcschr(scanItem,wildMark[0]);
  541. if(!scanItem) {
  542. //
  543. // ran out of string
  544. //
  545. return FALSE;
  546. }
  547. }
  548. //
  549. // try and match the sub-string at wildMark against scanItem
  550. //
  551. if(_tcsnicmp(scanItem,wildMark,matchlen)!=0) {
  552. //
  553. // nope, try again
  554. //
  555. scanItem = CharNext(scanItem);
  556. continue;
  557. }
  558. //
  559. // substring matched
  560. //
  561. scanItem += matchlen;
  562. wildMark += matchlen;
  563. }
  564. return (wildMark[0] ? FALSE : TRUE);
  565. }
  566. BOOL WildCompareHwIds(LPTSTR * Array,const IdEntry & MatchEntry)
  567. /*++
  568. Routine Description:
  569. Compares all strings in Array against Id
  570. Use WildCardMatch to do real compare
  571. Arguments:
  572. Array - pointer returned by GetDevMultiSz
  573. MatchEntry - string to compare against
  574. Return Value:
  575. TRUE if any match, otherwise FALSE
  576. --*/
  577. {
  578. if(Array) {
  579. while(Array[0]) {
  580. if(WildCardMatch(Array[0],MatchEntry)) {
  581. return TRUE;
  582. }
  583. Array++;
  584. }
  585. }
  586. return FALSE;
  587. }
  588. bool SplitCommandLine(int & argc,LPTSTR * & argv,int & argc_right,LPTSTR * & argv_right)
  589. /*++
  590. Routine Description:
  591. Splits a command line into left and right of :=
  592. this is used for some of the more complex commands
  593. Arguments:
  594. argc/argv - in/out
  595. - in, specifies the existing argc/argv
  596. - out, specifies the argc/argv to left of :=
  597. arc_right/argv_right - out
  598. - specifies the argc/argv to right of :=
  599. Return Value:
  600. true - ":=" appears in line, false otherwise
  601. --*/
  602. {
  603. int i;
  604. for(i = 0;i<argc;i++) {
  605. if(_tcsicmp(argv[i],SPLIT_COMMAND_SEP)==0) {
  606. argc_right = argc-(i+1);
  607. argv_right = argv+(i+1);
  608. argc = i;
  609. return true;
  610. }
  611. }
  612. argc_right = 0;
  613. argv_right = argv+argc;
  614. return false;
  615. }
  616. int EnumerateDevices(LPCTSTR BaseName,LPCTSTR Machine,DWORD Flags,int argc,LPTSTR argv[],CallbackFunc Callback,LPVOID Context)
  617. /*++
  618. Routine Description:
  619. Generic enumerator for devices that will be passed the following arguments:
  620. <id> [<id>...]
  621. =<class> [<id>...]
  622. where <id> can either be @instance-id, or hardware-id and may contain wildcards
  623. <class> is a class name
  624. Arguments:
  625. BaseName - name of executable
  626. Machine - name of machine to enumerate
  627. Flags - extra enumeration flags (eg DIGCF_PRESENT)
  628. argc/argv - remaining arguments on command line
  629. Callback - function to call for each hit
  630. Context - data to pass function for each hit
  631. Return Value:
  632. EXIT_xxxx
  633. --*/
  634. {
  635. HDEVINFO devs = INVALID_HANDLE_VALUE;
  636. IdEntry * templ = NULL;
  637. DWORD err;
  638. int failcode = EXIT_FAIL;
  639. int retcode;
  640. int argIndex;
  641. DWORD devIndex;
  642. SP_DEVINFO_DATA devInfo;
  643. SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;
  644. BOOL doSearch = FALSE;
  645. BOOL match;
  646. BOOL all = FALSE;
  647. GUID cls;
  648. DWORD numClass = 0;
  649. int skip = 0;
  650. if(!argc) {
  651. return EXIT_USAGE;
  652. }
  653. templ = new IdEntry[argc];
  654. if(!templ) {
  655. goto final;
  656. }
  657. //
  658. // determine if a class is specified
  659. //
  660. if(argc>skip && argv[skip][0]==CLASS_PREFIX_CHAR && argv[skip][1]) {
  661. if(!SetupDiClassGuidsFromNameEx(argv[skip]+1,&cls,1,&numClass,Machine,NULL) &&
  662. GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  663. goto final;
  664. }
  665. if(!numClass) {
  666. failcode = EXIT_OK;
  667. goto final;
  668. }
  669. skip++;
  670. }
  671. if(argc>skip && argv[skip][0]==WILD_CHAR && !argv[skip][1]) {
  672. //
  673. // catch convinient case of specifying a single argument '*'
  674. //
  675. all = TRUE;
  676. skip++;
  677. } else if(argc<=skip) {
  678. //
  679. // at least one parameter, but no <id>'s
  680. //
  681. all = TRUE;
  682. }
  683. //
  684. // determine if any instance id's were specified
  685. //
  686. // note, if =<class> was specified with no id's
  687. // we'll mark it as not doSearch
  688. // but will go ahead and add them all
  689. //
  690. for(argIndex=skip;argIndex<argc;argIndex++) {
  691. templ[argIndex] = GetIdType(argv[argIndex]);
  692. if(templ[argIndex].Wild || !templ[argIndex].InstanceId) {
  693. //
  694. // anything other than simple InstanceId's require a search
  695. //
  696. doSearch = TRUE;
  697. }
  698. }
  699. if(doSearch || all) {
  700. //
  701. // add all id's to list
  702. // if there's a class, filter on specified class
  703. //
  704. devs = SetupDiGetClassDevsEx(numClass ? &cls : NULL,
  705. NULL,
  706. NULL,
  707. (numClass ? 0 : DIGCF_ALLCLASSES) | Flags,
  708. NULL,
  709. Machine,
  710. NULL);
  711. } else {
  712. //
  713. // blank list, we'll add instance id's by hand
  714. //
  715. devs = SetupDiCreateDeviceInfoListEx(numClass ? &cls : NULL,
  716. NULL,
  717. Machine,
  718. NULL);
  719. }
  720. if(devs == INVALID_HANDLE_VALUE) {
  721. goto final;
  722. }
  723. for(argIndex=skip;argIndex<argc;argIndex++) {
  724. //
  725. // add explicit instances to list (even if enumerated all,
  726. // this gets around DIGCF_PRESENT)
  727. // do this even if wildcards appear to be detected since they
  728. // might actually be part of the instance ID of a non-present device
  729. //
  730. if(templ[argIndex].InstanceId) {
  731. SetupDiOpenDeviceInfo(devs,templ[argIndex].String,NULL,0,NULL);
  732. }
  733. }
  734. devInfoListDetail.cbSize = sizeof(devInfoListDetail);
  735. if(!SetupDiGetDeviceInfoListDetail(devs,&devInfoListDetail)) {
  736. goto final;
  737. }
  738. //
  739. // now enumerate them
  740. //
  741. if(all) {
  742. doSearch = FALSE;
  743. }
  744. devInfo.cbSize = sizeof(devInfo);
  745. for(devIndex=0;SetupDiEnumDeviceInfo(devs,devIndex,&devInfo);devIndex++) {
  746. if(doSearch) {
  747. for(argIndex=skip,match=FALSE;(argIndex<argc) && !match;argIndex++) {
  748. TCHAR devID[MAX_DEVICE_ID_LEN];
  749. LPTSTR *hwIds = NULL;
  750. LPTSTR *compatIds = NULL;
  751. //
  752. // determine instance ID
  753. //
  754. if(CM_Get_Device_ID_Ex(devInfo.DevInst,devID,MAX_DEVICE_ID_LEN,0,devInfoListDetail.RemoteMachineHandle)!=CR_SUCCESS) {
  755. devID[0] = TEXT('\0');
  756. }
  757. if(templ[argIndex].InstanceId) {
  758. //
  759. // match on the instance ID
  760. //
  761. if(WildCardMatch(devID,templ[argIndex])) {
  762. match = TRUE;
  763. }
  764. } else {
  765. //
  766. // determine hardware ID's
  767. // and search for matches
  768. //
  769. hwIds = GetDevMultiSz(devs,&devInfo,SPDRP_HARDWAREID);
  770. compatIds = GetDevMultiSz(devs,&devInfo,SPDRP_COMPATIBLEIDS);
  771. if(WildCompareHwIds(hwIds,templ[argIndex]) ||
  772. WildCompareHwIds(compatIds,templ[argIndex])) {
  773. match = TRUE;
  774. }
  775. }
  776. DelMultiSz(hwIds);
  777. DelMultiSz(compatIds);
  778. }
  779. } else {
  780. match = TRUE;
  781. }
  782. if(match) {
  783. retcode = Callback(devs,&devInfo,devIndex,Context);
  784. if(retcode) {
  785. failcode = retcode;
  786. goto final;
  787. }
  788. }
  789. }
  790. failcode = EXIT_OK;
  791. final:
  792. if(templ) {
  793. delete [] templ;
  794. }
  795. if(devs != INVALID_HANDLE_VALUE) {
  796. SetupDiDestroyDeviceInfoList(devs);
  797. }
  798. return failcode;
  799. }
  800. int
  801. __cdecl
  802. _tmain(int argc, LPTSTR argv[])
  803. /*++
  804. Routine Description:
  805. Main entry point
  806. interpret -m:<machine>
  807. and hand off execution to command
  808. Arguments:
  809. argc/argv - parameters passed to executable
  810. Return Value:
  811. EXIT_xxxx
  812. --*/
  813. {
  814. LPCTSTR cmd;
  815. LPCTSTR baseName;
  816. LPCTSTR machine = NULL;
  817. int dispIndex;
  818. int firstArg = 1;
  819. int retval = EXIT_USAGE;
  820. BOOL autoReboot = FALSE;
  821. //
  822. // syntax:
  823. //
  824. // [options] [-]command [<arg> [<arg>]]
  825. //
  826. // options:
  827. // -m:<machine> - remote
  828. // -r - auto reboot
  829. //
  830. baseName = _tcsrchr(argv[0],TEXT('\\'));
  831. if(!baseName) {
  832. baseName = argv[0];
  833. } else {
  834. baseName = CharNext(baseName);
  835. }
  836. while((argc > firstArg) && ((argv[firstArg][0] == TEXT('-')) || (argv[firstArg][0] == TEXT('/')))) {
  837. if((argv[firstArg][1]==TEXT('m')) || (argv[firstArg][1]==TEXT('M'))) {
  838. if((argv[firstArg][2]!=TEXT(':')) || (argv[firstArg][3]==TEXT('\0'))) {
  839. //
  840. // don't recognize this switch
  841. //
  842. break;
  843. }
  844. machine = argv[firstArg]+3;
  845. } else if((argv[firstArg][1]==TEXT('r')) || (argv[firstArg][1]==TEXT('R'))) {
  846. if((argv[firstArg][2]!=TEXT('\0')) ) {
  847. //
  848. // don't recognize this switch
  849. //
  850. break;
  851. } else {
  852. autoReboot = TRUE;
  853. }
  854. } else {
  855. //
  856. // don't recognize this switch
  857. //
  858. break;
  859. }
  860. firstArg++;
  861. }
  862. if((argc-firstArg) < 1) {
  863. //
  864. // after switches, must at least be command
  865. //
  866. Usage(baseName);
  867. return EXIT_USAGE;
  868. }
  869. cmd = argv[firstArg];
  870. if((cmd[0]==TEXT('-')) || (cmd[0]==TEXT('/'))) {
  871. //
  872. // command may begin '-' or '/'
  873. // eg, people might do devcon -help
  874. //
  875. cmd = CharNext(cmd);
  876. }
  877. firstArg++;
  878. for(dispIndex = 0;DispatchTable[dispIndex].cmd;dispIndex++) {
  879. if(_tcsicmp(cmd,DispatchTable[dispIndex].cmd)==0) {
  880. retval = DispatchTable[dispIndex].func(baseName,machine,argc-firstArg,argv+firstArg);
  881. switch(retval) {
  882. case EXIT_USAGE:
  883. CommandUsage(baseName,DispatchTable[dispIndex].cmd);
  884. break;
  885. case EXIT_REBOOT:
  886. if(autoReboot) {
  887. Reboot();
  888. }
  889. break;
  890. case EXIT_OK:
  891. break;
  892. default:
  893. Failure(baseName,DispatchTable[dispIndex].cmd);
  894. break;
  895. }
  896. return retval;
  897. }
  898. }
  899. Usage(baseName);
  900. return EXIT_USAGE;
  901. }