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.

1527 lines
38 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. int cmdHelp(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  16. /*++
  17. Routine Description:
  18. HELP command
  19. allow HELP or HELP <command>
  20. Arguments:
  21. BaseName - name of executable
  22. Machine - if non-NULL, remote machine (ignored)
  23. argc/argv - remaining parameters
  24. Return Value:
  25. EXIT_xxxx
  26. --*/
  27. {
  28. DWORD helptext = 0;
  29. int dispIndex;
  30. LPCTSTR cmd = NULL;
  31. BOOL unknown = FALSE;
  32. if(argc) {
  33. //
  34. // user passed in a command for help on... long help
  35. //
  36. for(dispIndex = 0;DispatchTable[dispIndex].cmd;dispIndex++) {
  37. if(lstrcmpi(argv[0],DispatchTable[dispIndex].cmd)==0) {
  38. cmd = DispatchTable[dispIndex].cmd;
  39. helptext = DispatchTable[dispIndex].longHelp;
  40. break;
  41. }
  42. }
  43. if(!cmd) {
  44. unknown = TRUE;
  45. }
  46. }
  47. if(helptext) {
  48. //
  49. // long help
  50. //
  51. FormatToStream(stdout,helptext,BaseName,cmd);
  52. } else {
  53. //
  54. // help help
  55. //
  56. FormatToStream(stdout,unknown ? MSG_HELP_OTHER : MSG_HELP_LONG,BaseName);
  57. //
  58. // enumerate through each command and display short help for each
  59. //
  60. for(dispIndex = 0;DispatchTable[dispIndex].cmd;dispIndex++) {
  61. if(DispatchTable[dispIndex].shortHelp) {
  62. FormatToStream(stdout,DispatchTable[dispIndex].shortHelp,DispatchTable[dispIndex].cmd);
  63. }
  64. }
  65. }
  66. return EXIT_OK;
  67. }
  68. int cmdClasses(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  69. /*++
  70. Routine Description:
  71. CLASSES command
  72. lists classes on (optionally) specified machine
  73. format as <name>: <destination>
  74. Arguments:
  75. BaseName - name of executable
  76. Machine - if non-NULL, remote machine
  77. argc/argv - remaining parameters - ignored
  78. Return Value:
  79. EXIT_xxxx
  80. --*/
  81. {
  82. DWORD reqGuids = 128;
  83. DWORD numGuids;
  84. LPGUID guids = NULL;
  85. DWORD index;
  86. int failcode = EXIT_FAIL;
  87. guids = new GUID[reqGuids];
  88. if(!guids) {
  89. goto final;
  90. }
  91. if(!SetupDiBuildClassInfoListEx(0,guids,reqGuids,&numGuids,Machine,NULL)) {
  92. do {
  93. if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  94. goto final;
  95. }
  96. delete [] guids;
  97. reqGuids = numGuids;
  98. guids = new GUID[reqGuids];
  99. if(!guids) {
  100. goto final;
  101. }
  102. } while(!SetupDiBuildClassInfoListEx(0,guids,reqGuids,&numGuids,Machine,NULL));
  103. }
  104. FormatToStream(stdout,Machine?MSG_CLASSES_HEADER:MSG_CLASSES_HEADER_LOCAL,numGuids,Machine);
  105. for(index=0;index<numGuids;index++) {
  106. TCHAR className[MAX_CLASS_NAME_LEN];
  107. TCHAR classDesc[LINE_LEN];
  108. if(!SetupDiClassNameFromGuidEx(&guids[index],className,MAX_CLASS_NAME_LEN,NULL,Machine,NULL)) {
  109. lstrcpyn(className,TEXT("?"),MAX_CLASS_NAME_LEN);
  110. }
  111. if(!SetupDiGetClassDescriptionEx(&guids[index],classDesc,LINE_LEN,NULL,Machine,NULL)) {
  112. lstrcpyn(classDesc,className,LINE_LEN);
  113. }
  114. _tprintf(TEXT("%-20s: %s\n"),className,classDesc);
  115. }
  116. failcode = EXIT_OK;
  117. final:
  118. delete [] guids;
  119. return failcode;
  120. }
  121. int cmdListClass(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  122. /*++
  123. Routine Description:
  124. LISTCLASS <name>....
  125. lists all devices for each specified class
  126. there can be more than one physical class for a class name (shouldn't be
  127. though) in such cases, list each class
  128. if machine given, list devices for that machine
  129. Arguments:
  130. BaseName - name of executable
  131. Machine - if non-NULL, remote machine
  132. argc/argv - remaining parameters - list of class names
  133. Return Value:
  134. EXIT_xxxx
  135. --*/
  136. {
  137. BOOL classListed = FALSE;
  138. BOOL devListed = FALSE;
  139. DWORD reqGuids = 16;
  140. int argIndex;
  141. int failcode = EXIT_FAIL;
  142. LPGUID guids = NULL;
  143. HDEVINFO devs = INVALID_HANDLE_VALUE;
  144. if(!argc) {
  145. return EXIT_USAGE;
  146. }
  147. guids = new GUID[reqGuids];
  148. if(!guids) {
  149. goto final;
  150. }
  151. for(argIndex = 0;argIndex<argc;argIndex++) {
  152. DWORD numGuids;
  153. DWORD index;
  154. if(!(argv[argIndex] && argv[argIndex][0])) {
  155. continue;
  156. }
  157. //
  158. // there could be one to many name to GUID mapping
  159. //
  160. while(!SetupDiClassGuidsFromNameEx(argv[argIndex],guids,reqGuids,&numGuids,Machine,NULL)) {
  161. if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  162. goto final;
  163. }
  164. delete [] guids;
  165. reqGuids = numGuids;
  166. guids = new GUID[reqGuids];
  167. if(!guids) {
  168. goto final;
  169. }
  170. }
  171. if(numGuids == 0) {
  172. FormatToStream(stdout,Machine?MSG_LISTCLASS_NOCLASS:MSG_LISTCLASS_NOCLASS_LOCAL,argv[argIndex],Machine);
  173. continue;
  174. }
  175. for(index = 0;index<numGuids;index++) {
  176. TCHAR className[MAX_CLASS_NAME_LEN];
  177. TCHAR classDesc[LINE_LEN];
  178. DWORD devCount = 0;
  179. SP_DEVINFO_DATA devInfo;
  180. DWORD devIndex;
  181. devs = SetupDiGetClassDevsEx(&guids[index],NULL,NULL,DIGCF_PRESENT,NULL,Machine,NULL);
  182. if(devs != INVALID_HANDLE_VALUE) {
  183. //
  184. // count number of devices
  185. //
  186. devInfo.cbSize = sizeof(devInfo);
  187. while(SetupDiEnumDeviceInfo(devs,devCount,&devInfo)) {
  188. devCount++;
  189. }
  190. }
  191. if(!SetupDiClassNameFromGuidEx(&guids[index],className,MAX_CLASS_NAME_LEN,NULL,Machine,NULL)) {
  192. lstrcpyn(className,TEXT("?"),MAX_CLASS_NAME_LEN);
  193. }
  194. if(!SetupDiGetClassDescriptionEx(&guids[index],classDesc,LINE_LEN,NULL,Machine,NULL)) {
  195. lstrcpyn(classDesc,className,LINE_LEN);
  196. }
  197. //
  198. // how many devices?
  199. //
  200. if (!devCount) {
  201. FormatToStream(stdout,Machine?MSG_LISTCLASS_HEADER_NONE:MSG_LISTCLASS_HEADER_NONE_LOCAL,className,classDesc,Machine);
  202. } else {
  203. FormatToStream(stdout,Machine?MSG_LISTCLASS_HEADER:MSG_LISTCLASS_HEADER_LOCAL,devCount,className,classDesc,Machine);
  204. for(devIndex=0;SetupDiEnumDeviceInfo(devs,devIndex,&devInfo);devIndex++) {
  205. DumpDevice(devs,&devInfo);
  206. }
  207. }
  208. if(devs != INVALID_HANDLE_VALUE) {
  209. SetupDiDestroyDeviceInfoList(devs);
  210. devs = INVALID_HANDLE_VALUE;
  211. }
  212. }
  213. }
  214. failcode = 0;
  215. final:
  216. delete [] guids;
  217. if(devs != INVALID_HANDLE_VALUE) {
  218. SetupDiDestroyDeviceInfoList(devs);
  219. }
  220. return failcode;
  221. }
  222. typedef struct {
  223. DWORD count;
  224. DWORD control;
  225. BOOL reboot;
  226. LPCTSTR strSuccess;
  227. LPCTSTR strReboot;
  228. LPCTSTR strFail;
  229. }
  230. GenericContext;
  231. #define FIND_DEVICE 0x00000001 // display device
  232. #define FIND_STATUS 0x00000002 // display status of device
  233. #define FIND_RESOURCES 0x00000004 // display resources of device
  234. #define FIND_DRIVERFILES 0x00000008 // display drivers used by device
  235. #define FIND_HWIDS 0x00000010 // display hw/compat id's used by device
  236. #define FIND_DRIVERNODES 0x00000020 // display driver nodes for a device.
  237. #define FIND_CLASS 0x00000040 // display device's setup class
  238. #define FIND_STACK 0x00000080 // display device's driver-stack
  239. int FindCallback(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Index,LPVOID Context)
  240. /*++
  241. Routine Description:
  242. Callback for use by Find/FindAll
  243. just simply display the device
  244. Arguments:
  245. Devs )_ uniquely identify the device
  246. DevInfo )
  247. Index - index of device
  248. Context - GenericContext
  249. Return Value:
  250. EXIT_xxxx
  251. --*/
  252. {
  253. GenericContext *pFindContext = (GenericContext*)Context;
  254. if(!pFindContext->control) {
  255. DumpDevice(Devs,DevInfo);
  256. pFindContext->count++;
  257. return EXIT_OK;
  258. }
  259. if(!DumpDeviceWithInfo(Devs,DevInfo,NULL)) {
  260. return EXIT_OK;
  261. }
  262. if(pFindContext->control&FIND_DEVICE) {
  263. DumpDeviceDescr(Devs,DevInfo);
  264. }
  265. if(pFindContext->control&FIND_CLASS) {
  266. DumpDeviceClass(Devs,DevInfo);
  267. }
  268. if(pFindContext->control&FIND_STATUS) {
  269. DumpDeviceStatus(Devs,DevInfo);
  270. }
  271. if(pFindContext->control&FIND_RESOURCES) {
  272. DumpDeviceResources(Devs,DevInfo);
  273. }
  274. if(pFindContext->control&FIND_DRIVERFILES) {
  275. DumpDeviceDriverFiles(Devs,DevInfo);
  276. }
  277. if(pFindContext->control&FIND_STACK) {
  278. DumpDeviceStack(Devs,DevInfo);
  279. }
  280. if(pFindContext->control&FIND_HWIDS) {
  281. DumpDeviceHwIds(Devs,DevInfo);
  282. }
  283. if (pFindContext->control&FIND_DRIVERNODES) {
  284. DumpDeviceDriverNodes(Devs,DevInfo);
  285. }
  286. pFindContext->count++;
  287. return EXIT_OK;
  288. }
  289. int cmdFind(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  290. /*++
  291. Routine Description:
  292. FIND <id> ...
  293. use EnumerateDevices to do hardwareID matching
  294. for each match, dump to stdout
  295. note that we only enumerate present devices
  296. Arguments:
  297. BaseName - name of executable
  298. Machine - if non-NULL, remote machine
  299. argc/argv - remaining parameters - passed into EnumerateDevices
  300. Return Value:
  301. EXIT_xxxx
  302. --*/
  303. {
  304. GenericContext context;
  305. int failcode;
  306. if(!argc) {
  307. return EXIT_USAGE;
  308. }
  309. context.count = 0;
  310. context.control = 0;
  311. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  312. if(failcode == EXIT_OK) {
  313. if(!context.count) {
  314. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  315. } else {
  316. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  317. }
  318. }
  319. return failcode;
  320. }
  321. int cmdFindAll(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  322. /*++
  323. Routine Description:
  324. FINDALL <id> ...
  325. use EnumerateDevices to do hardwareID matching
  326. for each match, dump to stdout
  327. like find, but also show not-present devices
  328. Arguments:
  329. BaseName - name of executable
  330. Machine - if non-NULL, remote machine
  331. argc/argv - remaining parameters - passed into EnumerateDevices
  332. Return Value:
  333. EXIT_xxxx
  334. --*/
  335. {
  336. GenericContext context;
  337. int failcode;
  338. if(!argc) {
  339. return EXIT_USAGE;
  340. }
  341. context.count = 0;
  342. context.control = 0;
  343. failcode = EnumerateDevices(BaseName,Machine,0,argc,argv,FindCallback,&context);
  344. if(failcode == EXIT_OK) {
  345. if(!context.count) {
  346. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  347. } else {
  348. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  349. }
  350. }
  351. return failcode;
  352. }
  353. int cmdStatus(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  354. /*++
  355. Routine Description:
  356. STATUS <id> ...
  357. use EnumerateDevices to do hardwareID matching
  358. for each match, dump status to stdout
  359. note that we only enumerate present devices
  360. Arguments:
  361. BaseName - name of executable
  362. Machine - if non-NULL, remote machine
  363. argc/argv - remaining parameters - passed into EnumerateDevices
  364. Return Value:
  365. EXIT_xxxx
  366. --*/
  367. {
  368. GenericContext context;
  369. int failcode;
  370. if(!argc) {
  371. return EXIT_USAGE;
  372. }
  373. context.count = 0;
  374. context.control = FIND_DEVICE | FIND_STATUS;
  375. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  376. if(failcode == EXIT_OK) {
  377. if(!context.count) {
  378. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  379. } else {
  380. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  381. }
  382. }
  383. return failcode;
  384. }
  385. int cmdResources(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  386. /*++
  387. Routine Description:
  388. RESOURCES <id> ...
  389. use EnumerateDevices to do hardwareID matching
  390. for each match, dump resources to stdout
  391. note that we only enumerate present devices
  392. Arguments:
  393. BaseName - name of executable
  394. Machine - if non-NULL, remote machine
  395. argc/argv - remaining parameters - passed into EnumerateDevices
  396. Return Value:
  397. EXIT_xxxx
  398. --*/
  399. {
  400. GenericContext context;
  401. int failcode;
  402. if(!argc) {
  403. return EXIT_USAGE;
  404. }
  405. context.count = 0;
  406. context.control = FIND_DEVICE | FIND_RESOURCES;
  407. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  408. if(failcode == EXIT_OK) {
  409. if(!context.count) {
  410. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  411. } else {
  412. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  413. }
  414. }
  415. return failcode;
  416. }
  417. int cmdDriverFiles(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  418. /*++
  419. Routine Description:
  420. STATUS <id> ...
  421. use EnumerateDevices to do hardwareID matching
  422. for each match, dump driver files to stdout
  423. note that we only enumerate present devices
  424. Arguments:
  425. BaseName - name of executable
  426. Machine - if non-NULL, remote machine
  427. argc/argv - remaining parameters - passed into EnumerateDevices
  428. Return Value:
  429. EXIT_xxxx
  430. --*/
  431. {
  432. GenericContext context;
  433. int failcode;
  434. if(!argc) {
  435. return EXIT_USAGE;
  436. }
  437. if(Machine) {
  438. //
  439. // must be local machine as we need to involve class/co installers (FIND_DRIVERFILES)
  440. //
  441. return EXIT_USAGE;
  442. }
  443. context.count = 0;
  444. context.control = FIND_DEVICE | FIND_DRIVERFILES;
  445. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  446. if(failcode == EXIT_OK) {
  447. if(!context.count) {
  448. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  449. } else {
  450. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  451. }
  452. }
  453. return failcode;
  454. }
  455. int cmdDriverNodes(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  456. /*++
  457. Routine Description:
  458. STATUS <id> ...
  459. use EnumerateDevices to do hardwareID matching
  460. for each match, dump drivernodes to stdout
  461. note that we only enumerate present devices
  462. Arguments:
  463. BaseName - name of executable
  464. Machine - if non-NULL, remote machine
  465. argc/argv - remaining parameters - passed into EnumerateDevices
  466. Return Value:
  467. EXIT_xxxx
  468. --*/
  469. {
  470. GenericContext context;
  471. int failcode;
  472. if(!argc) {
  473. return EXIT_USAGE;
  474. }
  475. if(Machine) {
  476. //
  477. // must be local machine as we need to involve class/co installers (FIND_DRIVERNODES)
  478. //
  479. return EXIT_USAGE;
  480. }
  481. context.count = 0;
  482. context.control = FIND_DEVICE | FIND_DRIVERNODES;
  483. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  484. if(failcode == EXIT_OK) {
  485. if(!context.count) {
  486. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  487. } else {
  488. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  489. }
  490. }
  491. return failcode;
  492. }
  493. int cmdHwIds(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  494. /*++
  495. Routine Description:
  496. HWIDS <id> ...
  497. use EnumerateDevices to do hardwareID matching
  498. for each match, dump hw/compat id's to stdout
  499. note that we only enumerate present devices
  500. Arguments:
  501. BaseName - name of executable
  502. Machine - if non-NULL, remote machine
  503. argc/argv - remaining parameters - passed into EnumerateDevices
  504. Return Value:
  505. EXIT_xxxx
  506. --*/
  507. {
  508. GenericContext context;
  509. int failcode;
  510. if(!argc) {
  511. return EXIT_USAGE;
  512. }
  513. context.count = 0;
  514. context.control = FIND_DEVICE | FIND_HWIDS;
  515. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  516. if(failcode == EXIT_OK) {
  517. if(!context.count) {
  518. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  519. } else {
  520. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  521. }
  522. }
  523. return failcode;
  524. }
  525. int cmdStack(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  526. /*++
  527. Routine Description:
  528. STACK <id> ...
  529. use EnumerateDevices to do hardwareID matching
  530. for each match, dump device class and stack to stdout
  531. note that we only enumerate present devices
  532. Arguments:
  533. BaseName - name of executable
  534. Machine - if non-NULL, remote machine
  535. argc/argv - remaining parameters - passed into EnumerateDevices
  536. Return Value:
  537. EXIT_xxxx
  538. --*/
  539. {
  540. GenericContext context;
  541. int failcode;
  542. if(!argc) {
  543. return EXIT_USAGE;
  544. }
  545. context.count = 0;
  546. context.control = FIND_DEVICE | FIND_CLASS | FIND_STACK;
  547. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  548. if(failcode == EXIT_OK) {
  549. if(!context.count) {
  550. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  551. } else {
  552. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  553. }
  554. }
  555. return failcode;
  556. }
  557. int ControlCallback(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Index,LPVOID Context)
  558. /*++
  559. Routine Description:
  560. Callback for use by Enable/Disable/Restart
  561. Invokes DIF_PROPERTYCHANGE with correct parameters
  562. uses SetupDiCallClassInstaller so cannot be done for remote devices
  563. Don't use CM_xxx API's, they bypass class/co-installers and this is bad.
  564. In Enable case, we try global first, and if still disabled, enable local
  565. Arguments:
  566. Devs )_ uniquely identify the device
  567. DevInfo )
  568. Index - index of device
  569. Context - GenericContext
  570. Return Value:
  571. EXIT_xxxx
  572. --*/
  573. {
  574. SP_PROPCHANGE_PARAMS pcp;
  575. GenericContext *pControlContext = (GenericContext*)Context;
  576. SP_DEVINSTALL_PARAMS devParams;
  577. switch(pControlContext->control) {
  578. case DICS_ENABLE:
  579. //
  580. // enable both on global and config-specific profile
  581. // do global first and see if that succeeded in enabling the device
  582. // (global enable doesn't mark reboot required if device is still
  583. // disabled on current config whereas vice-versa isn't true)
  584. //
  585. pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
  586. pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
  587. pcp.StateChange = pControlContext->control;
  588. pcp.Scope = DICS_FLAG_GLOBAL;
  589. pcp.HwProfile = 0;
  590. //
  591. // don't worry if this fails, we'll get an error when we try config-
  592. // specific.
  593. if(SetupDiSetClassInstallParams(Devs,DevInfo,&pcp.ClassInstallHeader,sizeof(pcp))) {
  594. SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,Devs,DevInfo);
  595. }
  596. //
  597. // now enable on config-specific
  598. //
  599. pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
  600. pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
  601. pcp.StateChange = pControlContext->control;
  602. pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
  603. pcp.HwProfile = 0;
  604. break;
  605. default:
  606. //
  607. // operate on config-specific profile
  608. //
  609. pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
  610. pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
  611. pcp.StateChange = pControlContext->control;
  612. pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
  613. pcp.HwProfile = 0;
  614. break;
  615. }
  616. if(!SetupDiSetClassInstallParams(Devs,DevInfo,&pcp.ClassInstallHeader,sizeof(pcp)) ||
  617. !SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,Devs,DevInfo)) {
  618. //
  619. // failed to invoke DIF_PROPERTYCHANGE
  620. //
  621. DumpDeviceWithInfo(Devs,DevInfo,pControlContext->strFail);
  622. } else {
  623. //
  624. // see if device needs reboot
  625. //
  626. devParams.cbSize = sizeof(devParams);
  627. if(SetupDiGetDeviceInstallParams(Devs,DevInfo,&devParams) && (devParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT))) {
  628. DumpDeviceWithInfo(Devs,DevInfo,pControlContext->strReboot);
  629. pControlContext->reboot = TRUE;
  630. } else {
  631. //
  632. // appears to have succeeded
  633. //
  634. DumpDeviceWithInfo(Devs,DevInfo,pControlContext->strSuccess);
  635. }
  636. pControlContext->count++;
  637. }
  638. return EXIT_OK;
  639. }
  640. int cmdEnable(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  641. /*++
  642. Routine Description:
  643. ENABLE <id> ...
  644. use EnumerateDevices to do hardwareID matching
  645. for each match, attempt to enable global, and if needed, config specific
  646. Arguments:
  647. BaseName - name of executable
  648. Machine - must be NULL (local machine only)
  649. argc/argv - remaining parameters - passed into EnumerateDevices
  650. Return Value:
  651. EXIT_xxxx (EXIT_REBOOT if reboot is required)
  652. --*/
  653. {
  654. GenericContext context;
  655. TCHAR strEnable[80];
  656. TCHAR strReboot[80];
  657. TCHAR strFail[80];
  658. int failcode = EXIT_FAIL;
  659. if(!argc) {
  660. //
  661. // arguments required
  662. //
  663. return EXIT_USAGE;
  664. }
  665. if(Machine) {
  666. //
  667. // must be local machine as we need to involve class/co installers
  668. //
  669. return EXIT_USAGE;
  670. }
  671. if(!LoadString(NULL,IDS_ENABLED,strEnable,ARRAYSIZE(strEnable))) {
  672. return EXIT_FAIL;
  673. }
  674. if(!LoadString(NULL,IDS_ENABLED_REBOOT,strReboot,ARRAYSIZE(strReboot))) {
  675. return EXIT_FAIL;
  676. }
  677. if(!LoadString(NULL,IDS_ENABLE_FAILED,strFail,ARRAYSIZE(strFail))) {
  678. return EXIT_FAIL;
  679. }
  680. context.control = DICS_ENABLE; // DICS_PROPCHANGE DICS_ENABLE DICS_DISABLE
  681. context.reboot = FALSE;
  682. context.count = 0;
  683. context.strReboot = strReboot;
  684. context.strSuccess = strEnable;
  685. context.strFail = strFail;
  686. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,ControlCallback,&context);
  687. if(failcode == EXIT_OK) {
  688. if(!context.count) {
  689. FormatToStream(stdout,MSG_ENABLE_TAIL_NONE);
  690. } else if(!context.reboot) {
  691. FormatToStream(stdout,MSG_ENABLE_TAIL,context.count);
  692. } else {
  693. FormatToStream(stdout,MSG_ENABLE_TAIL_REBOOT,context.count);
  694. failcode = EXIT_REBOOT;
  695. }
  696. }
  697. return failcode;
  698. }
  699. int cmdDisable(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  700. /*++
  701. Routine Description:
  702. DISABLE <id> ...
  703. use EnumerateDevices to do hardwareID matching
  704. for each match, attempt to disable global
  705. Arguments:
  706. BaseName - name of executable
  707. Machine - must be NULL (local machine only)
  708. argc/argv - remaining parameters - passed into EnumerateDevices
  709. Return Value:
  710. EXIT_xxxx (EXIT_REBOOT if reboot is required)
  711. --*/
  712. {
  713. GenericContext context;
  714. TCHAR strDisable[80];
  715. TCHAR strReboot[80];
  716. TCHAR strFail[80];
  717. int failcode = EXIT_FAIL;
  718. if(!argc) {
  719. //
  720. // arguments required
  721. //
  722. return EXIT_USAGE;
  723. }
  724. if(Machine) {
  725. //
  726. // must be local machine as we need to involve class/co installers
  727. //
  728. return EXIT_USAGE;
  729. }
  730. if(!LoadString(NULL,IDS_DISABLED,strDisable,ARRAYSIZE(strDisable))) {
  731. return EXIT_FAIL;
  732. }
  733. if(!LoadString(NULL,IDS_DISABLED_REBOOT,strReboot,ARRAYSIZE(strReboot))) {
  734. return EXIT_FAIL;
  735. }
  736. if(!LoadString(NULL,IDS_DISABLE_FAILED,strFail,ARRAYSIZE(strFail))) {
  737. return EXIT_FAIL;
  738. }
  739. context.control = DICS_DISABLE; // DICS_PROPCHANGE DICS_ENABLE DICS_DISABLE
  740. context.reboot = FALSE;
  741. context.count = 0;
  742. context.strReboot = strReboot;
  743. context.strSuccess = strDisable;
  744. context.strFail = strFail;
  745. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,ControlCallback,&context);
  746. if(failcode == EXIT_OK) {
  747. if(!context.count) {
  748. FormatToStream(stdout,MSG_DISABLE_TAIL_NONE);
  749. } else if(!context.reboot) {
  750. FormatToStream(stdout,MSG_DISABLE_TAIL,context.count);
  751. } else {
  752. FormatToStream(stdout,MSG_DISABLE_TAIL_REBOOT,context.count);
  753. failcode = EXIT_REBOOT;
  754. }
  755. }
  756. return failcode;
  757. }
  758. int cmdRestart(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  759. /*++
  760. Routine Description:
  761. RESTART <id> ...
  762. use EnumerateDevices to do hardwareID matching
  763. for each match, attempt to restart by issueing a PROPCHANGE
  764. Arguments:
  765. BaseName - name of executable
  766. Machine - must be NULL (local machine only)
  767. argc/argv - remaining parameters - passed into EnumerateDevices
  768. Return Value:
  769. EXIT_xxxx (EXIT_REBOOT if reboot is required)
  770. --*/
  771. {
  772. GenericContext context;
  773. TCHAR strRestarted[80];
  774. TCHAR strReboot[80];
  775. TCHAR strFail[80];
  776. int failcode = EXIT_FAIL;
  777. if(!argc) {
  778. //
  779. // arguments required
  780. //
  781. return EXIT_USAGE;
  782. }
  783. if(Machine) {
  784. //
  785. // must be local machine as we need to involve class/co installers
  786. //
  787. return EXIT_USAGE;
  788. }
  789. if(!LoadString(NULL,IDS_RESTARTED,strRestarted,ARRAYSIZE(strRestarted))) {
  790. return EXIT_FAIL;
  791. }
  792. if(!LoadString(NULL,IDS_REQUIRES_REBOOT,strReboot,ARRAYSIZE(strReboot))) {
  793. return EXIT_FAIL;
  794. }
  795. if(!LoadString(NULL,IDS_RESTART_FAILED,strFail,ARRAYSIZE(strFail))) {
  796. return EXIT_FAIL;
  797. }
  798. context.control = DICS_PROPCHANGE;
  799. context.reboot = FALSE;
  800. context.count = 0;
  801. context.strReboot = strReboot;
  802. context.strSuccess = strRestarted;
  803. context.strFail = strFail;
  804. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,ControlCallback,&context);
  805. if(failcode == EXIT_OK) {
  806. if(!context.count) {
  807. FormatToStream(stdout,MSG_RESTART_TAIL_NONE);
  808. } else if(!context.reboot) {
  809. FormatToStream(stdout,MSG_RESTART_TAIL,context.count);
  810. } else {
  811. FormatToStream(stdout,MSG_RESTART_TAIL_REBOOT,context.count);
  812. failcode = EXIT_REBOOT;
  813. }
  814. }
  815. return failcode;
  816. }
  817. int cmdReboot(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  818. /*++
  819. Routine Description:
  820. REBOOT
  821. reboot local machine
  822. Arguments:
  823. BaseName - name of executable
  824. Machine - must be NULL (local machine only)
  825. argc/argv - remaining parameters - ignored
  826. Return Value:
  827. EXIT_xxxx
  828. --*/
  829. {
  830. if(Machine) {
  831. //
  832. // must be local machine
  833. //
  834. return EXIT_USAGE;
  835. }
  836. FormatToStream(stdout,MSG_REBOOT);
  837. return Reboot() ? EXIT_OK : EXIT_FAIL;
  838. }
  839. int cmdUpdate(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  840. /*++
  841. Routine Description:
  842. UPDATE
  843. update driver for existing device(s)
  844. Arguments:
  845. BaseName - name of executable
  846. Machine - machine name, must be NULL
  847. argc/argv - remaining parameters
  848. Return Value:
  849. EXIT_xxxx
  850. --*/
  851. {
  852. HMODULE newdevMod = NULL;
  853. int failcode = EXIT_FAIL;
  854. UpdateDriverForPlugAndPlayDevicesProto UpdateFn;
  855. BOOL reboot = FALSE;
  856. LPCTSTR hwid = NULL;
  857. LPCTSTR inf = NULL;
  858. DWORD flags = 0;
  859. TCHAR InfPath[MAX_PATH];
  860. if(Machine) {
  861. //
  862. // must be local machine
  863. //
  864. return EXIT_USAGE;
  865. }
  866. if(argc<2) {
  867. //
  868. // at least HWID required
  869. //
  870. return EXIT_USAGE;
  871. }
  872. inf = argv[0];
  873. if(!inf[0]) {
  874. return EXIT_USAGE;
  875. }
  876. hwid = argv[1];
  877. if(!hwid[0]) {
  878. return EXIT_USAGE;
  879. }
  880. //
  881. // Inf must be a full pathname
  882. //
  883. if(GetFullPathName(inf,MAX_PATH,InfPath,NULL) >= MAX_PATH) {
  884. //
  885. // inf pathname too long
  886. //
  887. return EXIT_FAIL;
  888. }
  889. inf = InfPath;
  890. flags |= INSTALLFLAG_FORCE;
  891. //
  892. // make use of UpdateDriverForPlugAndPlayDevices
  893. //
  894. newdevMod = LoadLibrary(TEXT("newdev.dll"));
  895. if(!newdevMod) {
  896. goto final;
  897. }
  898. UpdateFn = (UpdateDriverForPlugAndPlayDevicesProto)GetProcAddress(newdevMod,UPDATEDRIVERFORPLUGANDPLAYDEVICES);
  899. if(!UpdateFn)
  900. {
  901. goto final;
  902. }
  903. FormatToStream(stdout,inf ? MSG_UPDATE_INF : MSG_UPDATE,hwid,inf);
  904. if(!UpdateFn(NULL,hwid,inf,flags,&reboot)) {
  905. goto final;
  906. }
  907. failcode = reboot ? EXIT_REBOOT : EXIT_OK;
  908. final:
  909. if(newdevMod) {
  910. FreeLibrary(newdevMod);
  911. }
  912. return EXIT_OK;
  913. }
  914. int cmdInstall(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  915. /*++
  916. Routine Description:
  917. INSTALL
  918. install a device manually
  919. Arguments:
  920. BaseName - name of executable
  921. Machine - machine name, must be NULL
  922. argc/argv - remaining parameters
  923. Return Value:
  924. EXIT_xxxx
  925. --*/
  926. {
  927. HDEVINFO DeviceInfoSet = INVALID_HANDLE_VALUE;
  928. SP_DEVINFO_DATA DeviceInfoData;
  929. GUID ClassGUID;
  930. TCHAR ClassName[MAX_CLASS_NAME_LEN];
  931. TCHAR hwIdList[LINE_LEN+4];
  932. TCHAR InfPath[MAX_PATH];
  933. DWORD err;
  934. int failcode = EXIT_FAIL;
  935. BOOL reboot = FALSE;
  936. LPCTSTR hwid = NULL;
  937. LPCTSTR inf = NULL;
  938. DWORD flags = 0;
  939. DWORD len;
  940. if(Machine) {
  941. //
  942. // must be local machine
  943. //
  944. return EXIT_USAGE;
  945. }
  946. if(argc<2) {
  947. //
  948. // at least HWID required
  949. //
  950. return EXIT_USAGE;
  951. }
  952. inf = argv[0];
  953. if(!inf[0]) {
  954. return EXIT_USAGE;
  955. }
  956. hwid = argv[1];
  957. if(!hwid[0]) {
  958. return EXIT_USAGE;
  959. }
  960. //
  961. // Inf must be a full pathname
  962. //
  963. if(GetFullPathName(inf,MAX_PATH,InfPath,NULL) >= MAX_PATH) {
  964. //
  965. // inf pathname too long
  966. //
  967. return EXIT_FAIL;
  968. }
  969. //
  970. // List of hardware ID's must be double zero-terminated
  971. //
  972. ZeroMemory(hwIdList,sizeof(hwIdList));
  973. lstrcpyn(hwIdList,hwid,LINE_LEN);
  974. //
  975. // Use the INF File to extract the Class GUID.
  976. //
  977. if (!SetupDiGetINFClass(InfPath,&ClassGUID,ClassName,sizeof(ClassName),0))
  978. {
  979. goto final;
  980. }
  981. //
  982. // Create the container for the to-be-created Device Information Element.
  983. //
  984. DeviceInfoSet = SetupDiCreateDeviceInfoList(&ClassGUID,0);
  985. if(DeviceInfoSet == INVALID_HANDLE_VALUE)
  986. {
  987. goto final;
  988. }
  989. //
  990. // Now create the element.
  991. // Use the Class GUID and Name from the INF file.
  992. //
  993. DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  994. if (!SetupDiCreateDeviceInfo(DeviceInfoSet,
  995. ClassName,
  996. &ClassGUID,
  997. NULL,
  998. 0,
  999. DICD_GENERATE_ID,
  1000. &DeviceInfoData))
  1001. {
  1002. goto final;
  1003. }
  1004. //
  1005. // Add the HardwareID to the Device's HardwareID property.
  1006. //
  1007. if(!SetupDiSetDeviceRegistryProperty(DeviceInfoSet,
  1008. &DeviceInfoData,
  1009. SPDRP_HARDWAREID,
  1010. (LPBYTE)hwIdList,
  1011. (lstrlen(hwIdList)+1+1)*sizeof(TCHAR)))
  1012. {
  1013. goto final;
  1014. }
  1015. //
  1016. // Transform the registry element into an actual devnode
  1017. // in the PnP HW tree.
  1018. //
  1019. if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE,
  1020. DeviceInfoSet,
  1021. &DeviceInfoData))
  1022. {
  1023. goto final;
  1024. }
  1025. //
  1026. // update the driver for the device we just created
  1027. //
  1028. failcode = cmdUpdate(BaseName,Machine,argc,argv);
  1029. final:
  1030. if (DeviceInfoSet != INVALID_HANDLE_VALUE) {
  1031. SetupDiDestroyDeviceInfoList(DeviceInfoSet);
  1032. }
  1033. return failcode;
  1034. }
  1035. int RemoveCallback(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Index,LPVOID Context)
  1036. /*++
  1037. Routine Description:
  1038. Callback for use by Remove
  1039. Invokes DIF_REMOVE
  1040. uses SetupDiCallClassInstaller so cannot be done for remote devices
  1041. Don't use CM_xxx API's, they bypass class/co-installers and this is bad.
  1042. Arguments:
  1043. Devs )_ uniquely identify the device
  1044. DevInfo )
  1045. Index - index of device
  1046. Context - GenericContext
  1047. Return Value:
  1048. EXIT_xxxx
  1049. --*/
  1050. {
  1051. SP_REMOVEDEVICE_PARAMS rmdParams;
  1052. GenericContext *pControlContext = (GenericContext*)Context;
  1053. SP_DEVINSTALL_PARAMS devParams;
  1054. LPCTSTR action = NULL;
  1055. //
  1056. // need hardware ID before trying to remove, as we wont have it after
  1057. //
  1058. TCHAR devID[MAX_DEVICE_ID_LEN];
  1059. LPTSTR desc;
  1060. BOOL b = TRUE;
  1061. SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;
  1062. devInfoListDetail.cbSize = sizeof(devInfoListDetail);
  1063. if((!SetupDiGetDeviceInfoListDetail(Devs,&devInfoListDetail)) ||
  1064. (CM_Get_Device_ID_Ex(DevInfo->DevInst,devID,MAX_DEVICE_ID_LEN,0,devInfoListDetail.RemoteMachineHandle)!=CR_SUCCESS)) {
  1065. //
  1066. // skip this
  1067. //
  1068. return NO_ERROR;
  1069. }
  1070. rmdParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
  1071. rmdParams.ClassInstallHeader.InstallFunction = DIF_REMOVE;
  1072. rmdParams.Scope = DI_REMOVEDEVICE_GLOBAL;
  1073. rmdParams.HwProfile = 0;
  1074. if(!SetupDiSetClassInstallParams(Devs,DevInfo,&rmdParams.ClassInstallHeader,sizeof(rmdParams)) ||
  1075. !SetupDiCallClassInstaller(DIF_REMOVE,Devs,DevInfo)) {
  1076. //
  1077. // failed to invoke DIF_REMOVE
  1078. //
  1079. action = pControlContext->strFail;
  1080. } else {
  1081. //
  1082. // see if device needs reboot
  1083. //
  1084. devParams.cbSize = sizeof(devParams);
  1085. if(SetupDiGetDeviceInstallParams(Devs,DevInfo,&devParams) && (devParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT))) {
  1086. //
  1087. // reboot required
  1088. //
  1089. action = pControlContext->strReboot;
  1090. pControlContext->reboot = TRUE;
  1091. } else {
  1092. //
  1093. // appears to have succeeded
  1094. //
  1095. action = pControlContext->strSuccess;
  1096. }
  1097. pControlContext->count++;
  1098. }
  1099. _tprintf(TEXT("%-60s: %s\n"),devID,action);
  1100. return EXIT_OK;
  1101. }
  1102. int cmdRemove(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  1103. /*++
  1104. Routine Description:
  1105. REMOVE
  1106. remove devices
  1107. Arguments:
  1108. BaseName - name of executable
  1109. Machine - machine name, must be NULL
  1110. argc/argv - remaining parameters
  1111. Return Value:
  1112. EXIT_xxxx
  1113. --*/
  1114. {
  1115. GenericContext context;
  1116. TCHAR strRemove[80];
  1117. TCHAR strReboot[80];
  1118. TCHAR strFail[80];
  1119. int failcode = EXIT_FAIL;
  1120. if(!argc) {
  1121. //
  1122. // arguments required
  1123. //
  1124. return EXIT_USAGE;
  1125. }
  1126. if(Machine) {
  1127. //
  1128. // must be local machine as we need to involve class/co installers
  1129. //
  1130. return EXIT_USAGE;
  1131. }
  1132. if(!LoadString(NULL,IDS_REMOVED,strRemove,ARRAYSIZE(strRemove))) {
  1133. return EXIT_FAIL;
  1134. }
  1135. if(!LoadString(NULL,IDS_REMOVED_REBOOT,strReboot,ARRAYSIZE(strReboot))) {
  1136. return EXIT_FAIL;
  1137. }
  1138. if(!LoadString(NULL,IDS_REMOVE_FAILED,strFail,ARRAYSIZE(strFail))) {
  1139. return EXIT_FAIL;
  1140. }
  1141. context.reboot = FALSE;
  1142. context.count = 0;
  1143. context.strReboot = strReboot;
  1144. context.strSuccess = strRemove;
  1145. context.strFail = strFail;
  1146. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,RemoveCallback,&context);
  1147. if(failcode == EXIT_OK) {
  1148. if(!context.count) {
  1149. FormatToStream(stdout,MSG_REMOVE_TAIL_NONE);
  1150. } else if(!context.reboot) {
  1151. FormatToStream(stdout,MSG_REMOVE_TAIL,context.count);
  1152. } else {
  1153. FormatToStream(stdout,MSG_REMOVE_TAIL_REBOOT,context.count);
  1154. failcode = EXIT_REBOOT;
  1155. }
  1156. }
  1157. return failcode;
  1158. }
  1159. int cmdRescan(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  1160. /*++
  1161. Routine Description:
  1162. RESCAN
  1163. rescan for new devices
  1164. Arguments:
  1165. BaseName - name of executable
  1166. Machine - machine name, must be NULL
  1167. argc/argv - remaining parameters
  1168. Return Value:
  1169. EXIT_xxxx
  1170. --*/
  1171. {
  1172. //
  1173. // reenumerate from the root of the devnode tree
  1174. // totally CM based
  1175. //
  1176. int failcode = EXIT_FAIL;
  1177. HMACHINE machineHandle = NULL;
  1178. DEVINST devRoot;
  1179. if(Machine) {
  1180. if(CM_Connect_Machine(Machine,&machineHandle) != CR_SUCCESS) {
  1181. return failcode;
  1182. }
  1183. }
  1184. if(CM_Locate_DevNode_Ex(&devRoot,NULL,CM_LOCATE_DEVNODE_NORMAL,machineHandle) != CR_SUCCESS) {
  1185. goto final;
  1186. }
  1187. FormatToStream(stdout,Machine ? MSG_RESCAN : MSG_RESCAN_LOCAL);
  1188. if(CM_Reenumerate_DevNode_Ex(devRoot, 0, machineHandle) != CR_SUCCESS) {
  1189. goto final;
  1190. }
  1191. failcode = EXIT_OK;
  1192. final:
  1193. if(machineHandle) {
  1194. CM_Disconnect_Machine(machineHandle);
  1195. }
  1196. return failcode;
  1197. }
  1198. DispatchEntry DispatchTable[] = {
  1199. { TEXT("classes"), cmdClasses, MSG_CLASSES_SHORT, MSG_CLASSES_LONG },
  1200. { TEXT("disable"), cmdDisable, MSG_DISABLE_SHORT, MSG_DISABLE_LONG },
  1201. { TEXT("driverfiles"), cmdDriverFiles, MSG_DRIVERFILES_SHORT, MSG_DRIVERFILES_LONG },
  1202. { TEXT("drivernodes"), cmdDriverNodes, MSG_DRIVERNODES_SHORT, MSG_DRIVERNODES_LONG },
  1203. { TEXT("enable"), cmdEnable, MSG_ENABLE_SHORT, MSG_ENABLE_LONG },
  1204. { TEXT("find"), cmdFind, MSG_FIND_SHORT, MSG_FIND_LONG },
  1205. { TEXT("findall"), cmdFindAll, MSG_FINDALL_SHORT, MSG_FINDALL_LONG },
  1206. { TEXT("help"), cmdHelp, MSG_HELP_SHORT, 0 },
  1207. { TEXT("hwids"), cmdHwIds, MSG_HWIDS_SHORT, MSG_HWIDS_LONG },
  1208. { TEXT("install"), cmdInstall, MSG_INSTALL_SHORT, MSG_INSTALL_LONG },
  1209. { TEXT("listclass"), cmdListClass, MSG_LISTCLASS_SHORT, MSG_LISTCLASS_LONG },
  1210. { TEXT("reboot"), cmdReboot, MSG_REBOOT_SHORT, MSG_REBOOT_LONG },
  1211. { TEXT("remove"), cmdRemove, MSG_REMOVE_SHORT, MSG_REMOVE_LONG },
  1212. { TEXT("rescan"), cmdRescan, MSG_RESCAN_SHORT, MSG_RESCAN_LONG },
  1213. { TEXT("resources"), cmdResources, MSG_RESOURCES_SHORT, MSG_RESOURCES_LONG },
  1214. { TEXT("restart"), cmdRestart, MSG_RESTART_SHORT, MSG_RESTART_LONG },
  1215. { TEXT("stack"), cmdStack, MSG_STACK_SHORT, MSG_STACK_LONG },
  1216. { TEXT("status"), cmdStatus, MSG_STATUS_SHORT, MSG_STATUS_LONG },
  1217. { TEXT("update"), cmdUpdate, MSG_UPDATE_SHORT, MSG_UPDATE_LONG },
  1218. { TEXT("?"), cmdHelp, 0, 0 },
  1219. { NULL,NULL }
  1220. };