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.

2138 lines
56 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 GenericContext {
  16. DWORD count;
  17. DWORD control;
  18. BOOL reboot;
  19. LPCTSTR strSuccess;
  20. LPCTSTR strReboot;
  21. LPCTSTR strFail;
  22. };
  23. #define FIND_DEVICE 0x00000001 // display device
  24. #define FIND_STATUS 0x00000002 // display status of device
  25. #define FIND_RESOURCES 0x00000004 // display resources of device
  26. #define FIND_DRIVERFILES 0x00000008 // display drivers used by device
  27. #define FIND_HWIDS 0x00000010 // display hw/compat id's used by device
  28. #define FIND_DRIVERNODES 0x00000020 // display driver nodes for a device.
  29. #define FIND_CLASS 0x00000040 // display device's setup class
  30. #define FIND_STACK 0x00000080 // display device's driver-stack
  31. struct SetHwidContext {
  32. int argc_right;
  33. LPTSTR * argv_right;
  34. DWORD prop;
  35. int skipped;
  36. int modified;
  37. };
  38. int cmdHelp(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  39. /*++
  40. Routine Description:
  41. HELP command
  42. allow HELP or HELP <command>
  43. Arguments:
  44. BaseName - name of executable
  45. Machine - if non-NULL, remote machine (ignored)
  46. argc/argv - remaining parameters
  47. Return Value:
  48. EXIT_xxxx
  49. --*/
  50. {
  51. DWORD helptext = 0;
  52. int dispIndex;
  53. LPCTSTR cmd = NULL;
  54. BOOL unknown = FALSE;
  55. if(argc) {
  56. //
  57. // user passed in a command for help on... long help
  58. //
  59. for(dispIndex = 0;DispatchTable[dispIndex].cmd;dispIndex++) {
  60. if(_tcsicmp(argv[0],DispatchTable[dispIndex].cmd)==0) {
  61. cmd = DispatchTable[dispIndex].cmd;
  62. helptext = DispatchTable[dispIndex].longHelp;
  63. break;
  64. }
  65. }
  66. if(!cmd) {
  67. unknown = TRUE;
  68. cmd = argv[0];
  69. }
  70. }
  71. if(helptext) {
  72. //
  73. // long help
  74. //
  75. FormatToStream(stdout,helptext,BaseName,cmd);
  76. } else {
  77. //
  78. // help help
  79. //
  80. FormatToStream(stdout,unknown ? MSG_HELP_OTHER : MSG_HELP_LONG,BaseName,cmd);
  81. //
  82. // enumerate through each command and display short help for each
  83. //
  84. for(dispIndex = 0;DispatchTable[dispIndex].cmd;dispIndex++) {
  85. if(DispatchTable[dispIndex].shortHelp) {
  86. FormatToStream(stdout,DispatchTable[dispIndex].shortHelp,DispatchTable[dispIndex].cmd);
  87. }
  88. }
  89. }
  90. return EXIT_OK;
  91. }
  92. int cmdClasses(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  93. /*++
  94. Routine Description:
  95. CLASSES command
  96. lists classes on (optionally) specified machine
  97. format as <name>: <destination>
  98. Arguments:
  99. BaseName - name of executable
  100. Machine - if non-NULL, remote machine
  101. argc/argv - remaining parameters - ignored
  102. Return Value:
  103. EXIT_xxxx
  104. --*/
  105. {
  106. DWORD reqGuids = 128;
  107. DWORD numGuids;
  108. LPGUID guids = NULL;
  109. DWORD index;
  110. int failcode = EXIT_FAIL;
  111. guids = new GUID[reqGuids];
  112. if(!guids) {
  113. goto final;
  114. }
  115. if(!SetupDiBuildClassInfoListEx(0,guids,reqGuids,&numGuids,Machine,NULL)) {
  116. do {
  117. if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  118. goto final;
  119. }
  120. delete [] guids;
  121. reqGuids = numGuids;
  122. guids = new GUID[reqGuids];
  123. if(!guids) {
  124. goto final;
  125. }
  126. } while(!SetupDiBuildClassInfoListEx(0,guids,reqGuids,&numGuids,Machine,NULL));
  127. }
  128. FormatToStream(stdout,Machine?MSG_CLASSES_HEADER:MSG_CLASSES_HEADER_LOCAL,numGuids,Machine);
  129. for(index=0;index<numGuids;index++) {
  130. TCHAR className[MAX_CLASS_NAME_LEN];
  131. TCHAR classDesc[LINE_LEN];
  132. if(!SetupDiClassNameFromGuidEx(&guids[index],className,MAX_CLASS_NAME_LEN,NULL,Machine,NULL)) {
  133. lstrcpyn(className,TEXT("?"),MAX_CLASS_NAME_LEN);
  134. }
  135. if(!SetupDiGetClassDescriptionEx(&guids[index],classDesc,LINE_LEN,NULL,Machine,NULL)) {
  136. lstrcpyn(classDesc,className,LINE_LEN);
  137. }
  138. _tprintf(TEXT("%-20s: %s\n"),className,classDesc);
  139. }
  140. failcode = EXIT_OK;
  141. final:
  142. if(guids) {
  143. delete [] guids;
  144. }
  145. return failcode;
  146. }
  147. int cmdListClass(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  148. /*++
  149. Routine Description:
  150. LISTCLASS <name>....
  151. lists all devices for each specified class
  152. there can be more than one physical class for a class name (shouldn't be
  153. though) in such cases, list each class
  154. if machine given, list devices for that machine
  155. Arguments:
  156. BaseName - name of executable
  157. Machine - if non-NULL, remote machine
  158. argc/argv - remaining parameters - list of class names
  159. Return Value:
  160. EXIT_xxxx
  161. --*/
  162. {
  163. BOOL classListed = FALSE;
  164. BOOL devListed = FALSE;
  165. DWORD reqGuids = 16;
  166. int argIndex;
  167. int failcode = EXIT_FAIL;
  168. LPGUID guids = NULL;
  169. HDEVINFO devs = INVALID_HANDLE_VALUE;
  170. if(!argc) {
  171. return EXIT_USAGE;
  172. }
  173. guids = new GUID[reqGuids];
  174. if(!guids) {
  175. goto final;
  176. }
  177. for(argIndex = 0;argIndex<argc;argIndex++) {
  178. DWORD numGuids;
  179. DWORD index;
  180. if(!(argv[argIndex] && argv[argIndex][0])) {
  181. continue;
  182. }
  183. //
  184. // there could be one to many name to GUID mapping
  185. //
  186. while(!SetupDiClassGuidsFromNameEx(argv[argIndex],guids,reqGuids,&numGuids,Machine,NULL)) {
  187. if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  188. goto final;
  189. }
  190. delete [] guids;
  191. reqGuids = numGuids;
  192. guids = new GUID[reqGuids];
  193. if(!guids) {
  194. goto final;
  195. }
  196. }
  197. if(numGuids == 0) {
  198. FormatToStream(stdout,Machine?MSG_LISTCLASS_NOCLASS:MSG_LISTCLASS_NOCLASS_LOCAL,argv[argIndex],Machine);
  199. continue;
  200. }
  201. for(index = 0;index<numGuids;index++) {
  202. TCHAR className[MAX_CLASS_NAME_LEN];
  203. TCHAR classDesc[LINE_LEN];
  204. DWORD devCount = 0;
  205. SP_DEVINFO_DATA devInfo;
  206. DWORD devIndex;
  207. devs = SetupDiGetClassDevsEx(&guids[index],NULL,NULL,DIGCF_PRESENT,NULL,Machine,NULL);
  208. if(devs != INVALID_HANDLE_VALUE) {
  209. //
  210. // count number of devices
  211. //
  212. devInfo.cbSize = sizeof(devInfo);
  213. while(SetupDiEnumDeviceInfo(devs,devCount,&devInfo)) {
  214. devCount++;
  215. }
  216. }
  217. if(!SetupDiClassNameFromGuidEx(&guids[index],className,MAX_CLASS_NAME_LEN,NULL,Machine,NULL)) {
  218. lstrcpyn(className,TEXT("?"),MAX_CLASS_NAME_LEN);
  219. }
  220. if(!SetupDiGetClassDescriptionEx(&guids[index],classDesc,LINE_LEN,NULL,Machine,NULL)) {
  221. lstrcpyn(classDesc,className,LINE_LEN);
  222. }
  223. //
  224. // how many devices?
  225. //
  226. if (!devCount) {
  227. FormatToStream(stdout,Machine?MSG_LISTCLASS_HEADER_NONE:MSG_LISTCLASS_HEADER_NONE_LOCAL,className,classDesc,Machine);
  228. } else {
  229. FormatToStream(stdout,Machine?MSG_LISTCLASS_HEADER:MSG_LISTCLASS_HEADER_LOCAL,devCount,className,classDesc,Machine);
  230. for(devIndex=0;SetupDiEnumDeviceInfo(devs,devIndex,&devInfo);devIndex++) {
  231. DumpDevice(devs,&devInfo);
  232. }
  233. }
  234. if(devs != INVALID_HANDLE_VALUE) {
  235. SetupDiDestroyDeviceInfoList(devs);
  236. devs = INVALID_HANDLE_VALUE;
  237. }
  238. }
  239. }
  240. failcode = 0;
  241. final:
  242. if(guids) {
  243. delete [] guids;
  244. }
  245. if(devs != INVALID_HANDLE_VALUE) {
  246. SetupDiDestroyDeviceInfoList(devs);
  247. }
  248. return failcode;
  249. }
  250. int FindCallback(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Index,LPVOID Context)
  251. /*++
  252. Routine Description:
  253. Callback for use by Find/FindAll
  254. just simply display the device
  255. Arguments:
  256. Devs )_ uniquely identify the device
  257. DevInfo )
  258. Index - index of device
  259. Context - GenericContext
  260. Return Value:
  261. EXIT_xxxx
  262. --*/
  263. {
  264. GenericContext *pFindContext = (GenericContext*)Context;
  265. if(!pFindContext->control) {
  266. DumpDevice(Devs,DevInfo);
  267. pFindContext->count++;
  268. return EXIT_OK;
  269. }
  270. if(!DumpDeviceWithInfo(Devs,DevInfo,NULL)) {
  271. return EXIT_OK;
  272. }
  273. if(pFindContext->control&FIND_DEVICE) {
  274. DumpDeviceDescr(Devs,DevInfo);
  275. }
  276. if(pFindContext->control&FIND_CLASS) {
  277. DumpDeviceClass(Devs,DevInfo);
  278. }
  279. if(pFindContext->control&FIND_STATUS) {
  280. DumpDeviceStatus(Devs,DevInfo);
  281. }
  282. if(pFindContext->control&FIND_RESOURCES) {
  283. DumpDeviceResources(Devs,DevInfo);
  284. }
  285. if(pFindContext->control&FIND_DRIVERFILES) {
  286. DumpDeviceDriverFiles(Devs,DevInfo);
  287. }
  288. if(pFindContext->control&FIND_STACK) {
  289. DumpDeviceStack(Devs,DevInfo);
  290. }
  291. if(pFindContext->control&FIND_HWIDS) {
  292. DumpDeviceHwIds(Devs,DevInfo);
  293. }
  294. if (pFindContext->control&FIND_DRIVERNODES) {
  295. DumpDeviceDriverNodes(Devs,DevInfo);
  296. }
  297. pFindContext->count++;
  298. return EXIT_OK;
  299. }
  300. int cmdFind(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  301. /*++
  302. Routine Description:
  303. FIND <id> ...
  304. use EnumerateDevices to do hardwareID matching
  305. for each match, dump to stdout
  306. note that we only enumerate present devices
  307. Arguments:
  308. BaseName - name of executable
  309. Machine - if non-NULL, remote machine
  310. argc/argv - remaining parameters - passed into EnumerateDevices
  311. Return Value:
  312. EXIT_xxxx
  313. --*/
  314. {
  315. GenericContext context;
  316. int failcode;
  317. if(!argc) {
  318. return EXIT_USAGE;
  319. }
  320. context.count = 0;
  321. context.control = 0;
  322. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  323. if(failcode == EXIT_OK) {
  324. if(!context.count) {
  325. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  326. } else {
  327. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  328. }
  329. }
  330. return failcode;
  331. }
  332. int cmdFindAll(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  333. /*++
  334. Routine Description:
  335. FINDALL <id> ...
  336. use EnumerateDevices to do hardwareID matching
  337. for each match, dump to stdout
  338. like find, but also show not-present devices
  339. Arguments:
  340. BaseName - name of executable
  341. Machine - if non-NULL, remote machine
  342. argc/argv - remaining parameters - passed into EnumerateDevices
  343. Return Value:
  344. EXIT_xxxx
  345. --*/
  346. {
  347. GenericContext context;
  348. int failcode;
  349. if(!argc) {
  350. return EXIT_USAGE;
  351. }
  352. context.count = 0;
  353. context.control = 0;
  354. failcode = EnumerateDevices(BaseName,Machine,0,argc,argv,FindCallback,&context);
  355. if(failcode == EXIT_OK) {
  356. if(!context.count) {
  357. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  358. } else {
  359. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  360. }
  361. }
  362. return failcode;
  363. }
  364. int cmdStatus(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  365. /*++
  366. Routine Description:
  367. STATUS <id> ...
  368. use EnumerateDevices to do hardwareID matching
  369. for each match, dump status to stdout
  370. note that we only enumerate present devices
  371. Arguments:
  372. BaseName - name of executable
  373. Machine - if non-NULL, remote machine
  374. argc/argv - remaining parameters - passed into EnumerateDevices
  375. Return Value:
  376. EXIT_xxxx
  377. --*/
  378. {
  379. GenericContext context;
  380. int failcode;
  381. if(!argc) {
  382. return EXIT_USAGE;
  383. }
  384. context.count = 0;
  385. context.control = FIND_DEVICE | FIND_STATUS;
  386. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  387. if(failcode == EXIT_OK) {
  388. if(!context.count) {
  389. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  390. } else {
  391. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  392. }
  393. }
  394. return failcode;
  395. }
  396. int cmdResources(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  397. /*++
  398. Routine Description:
  399. RESOURCES <id> ...
  400. use EnumerateDevices to do hardwareID matching
  401. for each match, dump resources to stdout
  402. note that we only enumerate present devices
  403. Arguments:
  404. BaseName - name of executable
  405. Machine - if non-NULL, remote machine
  406. argc/argv - remaining parameters - passed into EnumerateDevices
  407. Return Value:
  408. EXIT_xxxx
  409. --*/
  410. {
  411. GenericContext context;
  412. int failcode;
  413. if(!argc) {
  414. return EXIT_USAGE;
  415. }
  416. context.count = 0;
  417. context.control = FIND_DEVICE | FIND_RESOURCES;
  418. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  419. if(failcode == EXIT_OK) {
  420. if(!context.count) {
  421. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  422. } else {
  423. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  424. }
  425. }
  426. return failcode;
  427. }
  428. int cmdDriverFiles(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  429. /*++
  430. Routine Description:
  431. STATUS <id> ...
  432. use EnumerateDevices to do hardwareID matching
  433. for each match, dump driver files to stdout
  434. note that we only enumerate present devices
  435. Arguments:
  436. BaseName - name of executable
  437. Machine - if non-NULL, remote machine
  438. argc/argv - remaining parameters - passed into EnumerateDevices
  439. Return Value:
  440. EXIT_xxxx
  441. --*/
  442. {
  443. GenericContext context;
  444. int failcode;
  445. if(!argc) {
  446. return EXIT_USAGE;
  447. }
  448. if(Machine) {
  449. //
  450. // must be local machine as we need to involve class/co installers (FIND_DRIVERFILES)
  451. //
  452. return EXIT_USAGE;
  453. }
  454. context.count = 0;
  455. context.control = FIND_DEVICE | FIND_DRIVERFILES;
  456. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  457. if(failcode == EXIT_OK) {
  458. if(!context.count) {
  459. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  460. } else {
  461. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  462. }
  463. }
  464. return failcode;
  465. }
  466. int cmdDriverNodes(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  467. /*++
  468. Routine Description:
  469. STATUS <id> ...
  470. use EnumerateDevices to do hardwareID matching
  471. for each match, dump drivernodes to stdout
  472. note that we only enumerate present devices
  473. Arguments:
  474. BaseName - name of executable
  475. Machine - if non-NULL, remote machine
  476. argc/argv - remaining parameters - passed into EnumerateDevices
  477. Return Value:
  478. EXIT_xxxx
  479. --*/
  480. {
  481. GenericContext context;
  482. int failcode;
  483. if(!argc) {
  484. return EXIT_USAGE;
  485. }
  486. if(Machine) {
  487. //
  488. // must be local machine as we need to involve class/co installers (FIND_DRIVERNODES)
  489. //
  490. return EXIT_USAGE;
  491. }
  492. context.count = 0;
  493. context.control = FIND_DEVICE | FIND_DRIVERNODES;
  494. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  495. if(failcode == EXIT_OK) {
  496. if(!context.count) {
  497. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  498. } else {
  499. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  500. }
  501. }
  502. return failcode;
  503. }
  504. int cmdHwIds(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  505. /*++
  506. Routine Description:
  507. HWIDS <id> ...
  508. use EnumerateDevices to do hardwareID matching
  509. for each match, dump hw/compat id's to stdout
  510. note that we only enumerate present devices
  511. Arguments:
  512. BaseName - name of executable
  513. Machine - if non-NULL, remote machine
  514. argc/argv - remaining parameters - passed into EnumerateDevices
  515. Return Value:
  516. EXIT_xxxx
  517. --*/
  518. {
  519. GenericContext context;
  520. int failcode;
  521. if(!argc) {
  522. return EXIT_USAGE;
  523. }
  524. context.count = 0;
  525. context.control = FIND_DEVICE | FIND_HWIDS;
  526. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  527. if(failcode == EXIT_OK) {
  528. if(!context.count) {
  529. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  530. } else {
  531. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  532. }
  533. }
  534. return failcode;
  535. }
  536. int cmdStack(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  537. /*++
  538. Routine Description:
  539. STACK <id> ...
  540. use EnumerateDevices to do hardwareID matching
  541. for each match, dump device class and stack to stdout
  542. note that we only enumerate present devices
  543. Arguments:
  544. BaseName - name of executable
  545. Machine - if non-NULL, remote machine
  546. argc/argv - remaining parameters - passed into EnumerateDevices
  547. Return Value:
  548. EXIT_xxxx
  549. --*/
  550. {
  551. GenericContext context;
  552. int failcode;
  553. if(!argc) {
  554. return EXIT_USAGE;
  555. }
  556. context.count = 0;
  557. context.control = FIND_DEVICE | FIND_CLASS | FIND_STACK;
  558. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
  559. if(failcode == EXIT_OK) {
  560. if(!context.count) {
  561. FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
  562. } else {
  563. FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
  564. }
  565. }
  566. return failcode;
  567. }
  568. int ControlCallback(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Index,LPVOID Context)
  569. /*++
  570. Routine Description:
  571. Callback for use by Enable/Disable/Restart
  572. Invokes DIF_PROPERTYCHANGE with correct parameters
  573. uses SetupDiCallClassInstaller so cannot be done for remote devices
  574. Don't use CM_xxx API's, they bypass class/co-installers and this is bad.
  575. In Enable case, we try global first, and if still disabled, enable local
  576. Arguments:
  577. Devs )_ uniquely identify the device
  578. DevInfo )
  579. Index - index of device
  580. Context - GenericContext
  581. Return Value:
  582. EXIT_xxxx
  583. --*/
  584. {
  585. SP_PROPCHANGE_PARAMS pcp;
  586. GenericContext *pControlContext = (GenericContext*)Context;
  587. SP_DEVINSTALL_PARAMS devParams;
  588. switch(pControlContext->control) {
  589. case DICS_ENABLE:
  590. //
  591. // enable both on global and config-specific profile
  592. // do global first and see if that succeeded in enabling the device
  593. // (global enable doesn't mark reboot required if device is still
  594. // disabled on current config whereas vice-versa isn't true)
  595. //
  596. pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
  597. pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
  598. pcp.StateChange = pControlContext->control;
  599. pcp.Scope = DICS_FLAG_GLOBAL;
  600. pcp.HwProfile = 0;
  601. //
  602. // don't worry if this fails, we'll get an error when we try config-
  603. // specific.
  604. if(SetupDiSetClassInstallParams(Devs,DevInfo,&pcp.ClassInstallHeader,sizeof(pcp))) {
  605. SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,Devs,DevInfo);
  606. }
  607. //
  608. // now enable on config-specific
  609. //
  610. pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
  611. pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
  612. pcp.StateChange = pControlContext->control;
  613. pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
  614. pcp.HwProfile = 0;
  615. break;
  616. default:
  617. //
  618. // operate on config-specific profile
  619. //
  620. pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
  621. pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
  622. pcp.StateChange = pControlContext->control;
  623. pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
  624. pcp.HwProfile = 0;
  625. break;
  626. }
  627. if(!SetupDiSetClassInstallParams(Devs,DevInfo,&pcp.ClassInstallHeader,sizeof(pcp)) ||
  628. !SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,Devs,DevInfo)) {
  629. //
  630. // failed to invoke DIF_PROPERTYCHANGE
  631. //
  632. DumpDeviceWithInfo(Devs,DevInfo,pControlContext->strFail);
  633. } else {
  634. //
  635. // see if device needs reboot
  636. //
  637. devParams.cbSize = sizeof(devParams);
  638. if(SetupDiGetDeviceInstallParams(Devs,DevInfo,&devParams) && (devParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT))) {
  639. DumpDeviceWithInfo(Devs,DevInfo,pControlContext->strReboot);
  640. pControlContext->reboot = TRUE;
  641. } else {
  642. //
  643. // appears to have succeeded
  644. //
  645. DumpDeviceWithInfo(Devs,DevInfo,pControlContext->strSuccess);
  646. }
  647. pControlContext->count++;
  648. }
  649. return EXIT_OK;
  650. }
  651. int cmdEnable(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  652. /*++
  653. Routine Description:
  654. ENABLE <id> ...
  655. use EnumerateDevices to do hardwareID matching
  656. for each match, attempt to enable global, and if needed, config specific
  657. Arguments:
  658. BaseName - name of executable
  659. Machine - must be NULL (local machine only)
  660. argc/argv - remaining parameters - passed into EnumerateDevices
  661. Return Value:
  662. EXIT_xxxx (EXIT_REBOOT if reboot is required)
  663. --*/
  664. {
  665. GenericContext context;
  666. TCHAR strEnable[80];
  667. TCHAR strReboot[80];
  668. TCHAR strFail[80];
  669. int failcode = EXIT_FAIL;
  670. if(!argc) {
  671. //
  672. // arguments required
  673. //
  674. return EXIT_USAGE;
  675. }
  676. if(Machine) {
  677. //
  678. // must be local machine as we need to involve class/co installers
  679. //
  680. return EXIT_USAGE;
  681. }
  682. if(!LoadString(NULL,IDS_ENABLED,strEnable,ARRAYSIZE(strEnable))) {
  683. return EXIT_FAIL;
  684. }
  685. if(!LoadString(NULL,IDS_ENABLED_REBOOT,strReboot,ARRAYSIZE(strReboot))) {
  686. return EXIT_FAIL;
  687. }
  688. if(!LoadString(NULL,IDS_ENABLE_FAILED,strFail,ARRAYSIZE(strFail))) {
  689. return EXIT_FAIL;
  690. }
  691. context.control = DICS_ENABLE; // DICS_PROPCHANGE DICS_ENABLE DICS_DISABLE
  692. context.reboot = FALSE;
  693. context.count = 0;
  694. context.strReboot = strReboot;
  695. context.strSuccess = strEnable;
  696. context.strFail = strFail;
  697. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,ControlCallback,&context);
  698. if(failcode == EXIT_OK) {
  699. if(!context.count) {
  700. FormatToStream(stdout,MSG_ENABLE_TAIL_NONE);
  701. } else if(!context.reboot) {
  702. FormatToStream(stdout,MSG_ENABLE_TAIL,context.count);
  703. } else {
  704. FormatToStream(stdout,MSG_ENABLE_TAIL_REBOOT,context.count);
  705. failcode = EXIT_REBOOT;
  706. }
  707. }
  708. return failcode;
  709. }
  710. int cmdDisable(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  711. /*++
  712. Routine Description:
  713. DISABLE <id> ...
  714. use EnumerateDevices to do hardwareID matching
  715. for each match, attempt to disable global
  716. Arguments:
  717. BaseName - name of executable
  718. Machine - must be NULL (local machine only)
  719. argc/argv - remaining parameters - passed into EnumerateDevices
  720. Return Value:
  721. EXIT_xxxx (EXIT_REBOOT if reboot is required)
  722. --*/
  723. {
  724. GenericContext context;
  725. TCHAR strDisable[80];
  726. TCHAR strReboot[80];
  727. TCHAR strFail[80];
  728. int failcode = EXIT_FAIL;
  729. if(!argc) {
  730. //
  731. // arguments required
  732. //
  733. return EXIT_USAGE;
  734. }
  735. if(Machine) {
  736. //
  737. // must be local machine as we need to involve class/co installers
  738. //
  739. return EXIT_USAGE;
  740. }
  741. if(!LoadString(NULL,IDS_DISABLED,strDisable,ARRAYSIZE(strDisable))) {
  742. return EXIT_FAIL;
  743. }
  744. if(!LoadString(NULL,IDS_DISABLED_REBOOT,strReboot,ARRAYSIZE(strReboot))) {
  745. return EXIT_FAIL;
  746. }
  747. if(!LoadString(NULL,IDS_DISABLE_FAILED,strFail,ARRAYSIZE(strFail))) {
  748. return EXIT_FAIL;
  749. }
  750. context.control = DICS_DISABLE; // DICS_PROPCHANGE DICS_ENABLE DICS_DISABLE
  751. context.reboot = FALSE;
  752. context.count = 0;
  753. context.strReboot = strReboot;
  754. context.strSuccess = strDisable;
  755. context.strFail = strFail;
  756. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,ControlCallback,&context);
  757. if(failcode == EXIT_OK) {
  758. if(!context.count) {
  759. FormatToStream(stdout,MSG_DISABLE_TAIL_NONE);
  760. } else if(!context.reboot) {
  761. FormatToStream(stdout,MSG_DISABLE_TAIL,context.count);
  762. } else {
  763. FormatToStream(stdout,MSG_DISABLE_TAIL_REBOOT,context.count);
  764. failcode = EXIT_REBOOT;
  765. }
  766. }
  767. return failcode;
  768. }
  769. int cmdRestart(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  770. /*++
  771. Routine Description:
  772. RESTART <id> ...
  773. use EnumerateDevices to do hardwareID matching
  774. for each match, attempt to restart by issueing a PROPCHANGE
  775. Arguments:
  776. BaseName - name of executable
  777. Machine - must be NULL (local machine only)
  778. argc/argv - remaining parameters - passed into EnumerateDevices
  779. Return Value:
  780. EXIT_xxxx (EXIT_REBOOT if reboot is required)
  781. --*/
  782. {
  783. GenericContext context;
  784. TCHAR strRestarted[80];
  785. TCHAR strReboot[80];
  786. TCHAR strFail[80];
  787. int failcode = EXIT_FAIL;
  788. if(!argc) {
  789. //
  790. // arguments required
  791. //
  792. return EXIT_USAGE;
  793. }
  794. if(Machine) {
  795. //
  796. // must be local machine as we need to involve class/co installers
  797. //
  798. return EXIT_USAGE;
  799. }
  800. if(!LoadString(NULL,IDS_RESTARTED,strRestarted,ARRAYSIZE(strRestarted))) {
  801. return EXIT_FAIL;
  802. }
  803. if(!LoadString(NULL,IDS_REQUIRES_REBOOT,strReboot,ARRAYSIZE(strReboot))) {
  804. return EXIT_FAIL;
  805. }
  806. if(!LoadString(NULL,IDS_RESTART_FAILED,strFail,ARRAYSIZE(strFail))) {
  807. return EXIT_FAIL;
  808. }
  809. context.control = DICS_PROPCHANGE;
  810. context.reboot = FALSE;
  811. context.count = 0;
  812. context.strReboot = strReboot;
  813. context.strSuccess = strRestarted;
  814. context.strFail = strFail;
  815. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,ControlCallback,&context);
  816. if(failcode == EXIT_OK) {
  817. if(!context.count) {
  818. FormatToStream(stdout,MSG_RESTART_TAIL_NONE);
  819. } else if(!context.reboot) {
  820. FormatToStream(stdout,MSG_RESTART_TAIL,context.count);
  821. } else {
  822. FormatToStream(stdout,MSG_RESTART_TAIL_REBOOT,context.count);
  823. failcode = EXIT_REBOOT;
  824. }
  825. }
  826. return failcode;
  827. }
  828. int cmdReboot(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  829. /*++
  830. Routine Description:
  831. REBOOT
  832. reboot local machine
  833. Arguments:
  834. BaseName - name of executable
  835. Machine - must be NULL (local machine only)
  836. argc/argv - remaining parameters - ignored
  837. Return Value:
  838. EXIT_xxxx
  839. --*/
  840. {
  841. if(Machine) {
  842. //
  843. // must be local machine
  844. //
  845. return EXIT_USAGE;
  846. }
  847. FormatToStream(stdout,MSG_REBOOT);
  848. return Reboot() ? EXIT_OK : EXIT_FAIL;
  849. }
  850. int cmdUpdate(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  851. /*++
  852. Routine Description:
  853. UPDATE
  854. update driver for existing device(s)
  855. Arguments:
  856. BaseName - name of executable
  857. Machine - machine name, must be NULL
  858. argc/argv - remaining parameters
  859. Return Value:
  860. EXIT_xxxx
  861. --*/
  862. {
  863. HMODULE newdevMod = NULL;
  864. int failcode = EXIT_FAIL;
  865. UpdateDriverForPlugAndPlayDevicesProto UpdateFn;
  866. BOOL reboot = FALSE;
  867. LPCTSTR hwid = NULL;
  868. LPCTSTR inf = NULL;
  869. DWORD flags = 0;
  870. DWORD res;
  871. TCHAR InfPath[MAX_PATH];
  872. if(Machine) {
  873. //
  874. // must be local machine
  875. //
  876. return EXIT_USAGE;
  877. }
  878. if(argc<2) {
  879. //
  880. // at least HWID required
  881. //
  882. return EXIT_USAGE;
  883. }
  884. inf = argv[0];
  885. if(!inf[0]) {
  886. return EXIT_USAGE;
  887. }
  888. hwid = argv[1];
  889. if(!hwid[0]) {
  890. return EXIT_USAGE;
  891. }
  892. //
  893. // Inf must be a full pathname
  894. //
  895. res = GetFullPathName(inf,MAX_PATH,InfPath,NULL);
  896. if((res >= MAX_PATH) || (res == 0)) {
  897. //
  898. // inf pathname too long
  899. //
  900. return EXIT_FAIL;
  901. }
  902. if(GetFileAttributes(InfPath)==(DWORD)(-1)) {
  903. //
  904. // inf doesn't exist
  905. //
  906. return EXIT_FAIL;
  907. }
  908. inf = InfPath;
  909. flags |= INSTALLFLAG_FORCE;
  910. //
  911. // make use of UpdateDriverForPlugAndPlayDevices
  912. //
  913. newdevMod = LoadLibrary(TEXT("newdev.dll"));
  914. if(!newdevMod) {
  915. goto final;
  916. }
  917. UpdateFn = (UpdateDriverForPlugAndPlayDevicesProto)GetProcAddress(newdevMod,UPDATEDRIVERFORPLUGANDPLAYDEVICES);
  918. if(!UpdateFn)
  919. {
  920. goto final;
  921. }
  922. FormatToStream(stdout,inf ? MSG_UPDATE_INF : MSG_UPDATE,hwid,inf);
  923. if(!UpdateFn(NULL,hwid,inf,flags,&reboot)) {
  924. goto final;
  925. }
  926. FormatToStream(stdout,MSG_UPDATE_OK);
  927. failcode = reboot ? EXIT_REBOOT : EXIT_OK;
  928. final:
  929. if(newdevMod) {
  930. FreeLibrary(newdevMod);
  931. }
  932. return failcode;
  933. }
  934. int cmdInstall(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  935. /*++
  936. Routine Description:
  937. INSTALL
  938. install a device manually
  939. Arguments:
  940. BaseName - name of executable
  941. Machine - machine name, must be NULL
  942. argc/argv - remaining parameters
  943. Return Value:
  944. EXIT_xxxx
  945. --*/
  946. {
  947. HDEVINFO DeviceInfoSet = INVALID_HANDLE_VALUE;
  948. SP_DEVINFO_DATA DeviceInfoData;
  949. GUID ClassGUID;
  950. TCHAR ClassName[MAX_CLASS_NAME_LEN];
  951. TCHAR hwIdList[LINE_LEN+4];
  952. TCHAR InfPath[MAX_PATH];
  953. DWORD err;
  954. int failcode = EXIT_FAIL;
  955. BOOL reboot = FALSE;
  956. LPCTSTR hwid = NULL;
  957. LPCTSTR inf = NULL;
  958. DWORD flags = 0;
  959. DWORD len;
  960. if(Machine) {
  961. //
  962. // must be local machine
  963. //
  964. return EXIT_USAGE;
  965. }
  966. if(argc<2) {
  967. //
  968. // at least HWID required
  969. //
  970. return EXIT_USAGE;
  971. }
  972. inf = argv[0];
  973. if(!inf[0]) {
  974. return EXIT_USAGE;
  975. }
  976. hwid = argv[1];
  977. if(!hwid[0]) {
  978. return EXIT_USAGE;
  979. }
  980. //
  981. // Inf must be a full pathname
  982. //
  983. if(GetFullPathName(inf,MAX_PATH,InfPath,NULL) >= MAX_PATH) {
  984. //
  985. // inf pathname too long
  986. //
  987. return EXIT_FAIL;
  988. }
  989. //
  990. // List of hardware ID's must be double zero-terminated
  991. //
  992. ZeroMemory(hwIdList,sizeof(hwIdList));
  993. lstrcpyn(hwIdList,hwid,LINE_LEN);
  994. //
  995. // Use the INF File to extract the Class GUID.
  996. //
  997. if (!SetupDiGetINFClass(InfPath,&ClassGUID,ClassName,sizeof(ClassName)/sizeof(ClassName[0]),0))
  998. {
  999. goto final;
  1000. }
  1001. //
  1002. // Create the container for the to-be-created Device Information Element.
  1003. //
  1004. DeviceInfoSet = SetupDiCreateDeviceInfoList(&ClassGUID,0);
  1005. if(DeviceInfoSet == INVALID_HANDLE_VALUE)
  1006. {
  1007. goto final;
  1008. }
  1009. //
  1010. // Now create the element.
  1011. // Use the Class GUID and Name from the INF file.
  1012. //
  1013. DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  1014. if (!SetupDiCreateDeviceInfo(DeviceInfoSet,
  1015. ClassName,
  1016. &ClassGUID,
  1017. NULL,
  1018. 0,
  1019. DICD_GENERATE_ID,
  1020. &DeviceInfoData))
  1021. {
  1022. goto final;
  1023. }
  1024. //
  1025. // Add the HardwareID to the Device's HardwareID property.
  1026. //
  1027. if(!SetupDiSetDeviceRegistryProperty(DeviceInfoSet,
  1028. &DeviceInfoData,
  1029. SPDRP_HARDWAREID,
  1030. (LPBYTE)hwIdList,
  1031. (lstrlen(hwIdList)+1+1)*sizeof(TCHAR)))
  1032. {
  1033. goto final;
  1034. }
  1035. //
  1036. // Transform the registry element into an actual devnode
  1037. // in the PnP HW tree.
  1038. //
  1039. if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE,
  1040. DeviceInfoSet,
  1041. &DeviceInfoData))
  1042. {
  1043. goto final;
  1044. }
  1045. FormatToStream(stdout,MSG_INSTALL_UPDATE);
  1046. //
  1047. // update the driver for the device we just created
  1048. //
  1049. failcode = cmdUpdate(BaseName,Machine,argc,argv);
  1050. final:
  1051. if (DeviceInfoSet != INVALID_HANDLE_VALUE) {
  1052. SetupDiDestroyDeviceInfoList(DeviceInfoSet);
  1053. }
  1054. return failcode;
  1055. }
  1056. int cmdUpdateNI(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  1057. /*++
  1058. Routine Description:
  1059. UPDATE (non interactive version)
  1060. update driver for existing device(s)
  1061. Arguments:
  1062. BaseName - name of executable
  1063. Machine - machine name, must be NULL
  1064. argc/argv - remaining parameters
  1065. Return Value:
  1066. EXIT_xxxx
  1067. --*/
  1068. {
  1069. //
  1070. // turn off interactive mode while doing the update
  1071. //
  1072. HMODULE setupapiMod = NULL;
  1073. SetupSetNonInteractiveModeProto SetNIFn;
  1074. int res;
  1075. BOOL prev;
  1076. setupapiMod = LoadLibrary(TEXT("setupapi.dll"));
  1077. if(!setupapiMod) {
  1078. return cmdUpdate(BaseName,Machine,argc,argv);
  1079. }
  1080. SetNIFn = (SetupSetNonInteractiveModeProto)GetProcAddress(setupapiMod,SETUPSETNONINTERACTIVEMODE);
  1081. if(!SetNIFn)
  1082. {
  1083. FreeLibrary(setupapiMod);
  1084. return cmdUpdate(BaseName,Machine,argc,argv);
  1085. }
  1086. prev = SetNIFn(TRUE);
  1087. res = cmdUpdate(BaseName,Machine,argc,argv);
  1088. SetNIFn(prev);
  1089. FreeLibrary(setupapiMod);
  1090. return res;
  1091. }
  1092. int RemoveCallback(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Index,LPVOID Context)
  1093. /*++
  1094. Routine Description:
  1095. Callback for use by Remove
  1096. Invokes DIF_REMOVE
  1097. uses SetupDiCallClassInstaller so cannot be done for remote devices
  1098. Don't use CM_xxx API's, they bypass class/co-installers and this is bad.
  1099. Arguments:
  1100. Devs )_ uniquely identify the device
  1101. DevInfo )
  1102. Index - index of device
  1103. Context - GenericContext
  1104. Return Value:
  1105. EXIT_xxxx
  1106. --*/
  1107. {
  1108. SP_REMOVEDEVICE_PARAMS rmdParams;
  1109. GenericContext *pControlContext = (GenericContext*)Context;
  1110. SP_DEVINSTALL_PARAMS devParams;
  1111. LPCTSTR action = NULL;
  1112. //
  1113. // need hardware ID before trying to remove, as we wont have it after
  1114. //
  1115. TCHAR devID[MAX_DEVICE_ID_LEN];
  1116. LPTSTR desc;
  1117. BOOL b = TRUE;
  1118. SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;
  1119. devInfoListDetail.cbSize = sizeof(devInfoListDetail);
  1120. if((!SetupDiGetDeviceInfoListDetail(Devs,&devInfoListDetail)) ||
  1121. (CM_Get_Device_ID_Ex(DevInfo->DevInst,devID,MAX_DEVICE_ID_LEN,0,devInfoListDetail.RemoteMachineHandle)!=CR_SUCCESS)) {
  1122. //
  1123. // skip this
  1124. //
  1125. return EXIT_OK;
  1126. }
  1127. rmdParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
  1128. rmdParams.ClassInstallHeader.InstallFunction = DIF_REMOVE;
  1129. rmdParams.Scope = DI_REMOVEDEVICE_GLOBAL;
  1130. rmdParams.HwProfile = 0;
  1131. if(!SetupDiSetClassInstallParams(Devs,DevInfo,&rmdParams.ClassInstallHeader,sizeof(rmdParams)) ||
  1132. !SetupDiCallClassInstaller(DIF_REMOVE,Devs,DevInfo)) {
  1133. //
  1134. // failed to invoke DIF_REMOVE
  1135. //
  1136. action = pControlContext->strFail;
  1137. } else {
  1138. //
  1139. // see if device needs reboot
  1140. //
  1141. devParams.cbSize = sizeof(devParams);
  1142. if(SetupDiGetDeviceInstallParams(Devs,DevInfo,&devParams) && (devParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT))) {
  1143. //
  1144. // reboot required
  1145. //
  1146. action = pControlContext->strReboot;
  1147. pControlContext->reboot = TRUE;
  1148. } else {
  1149. //
  1150. // appears to have succeeded
  1151. //
  1152. action = pControlContext->strSuccess;
  1153. }
  1154. pControlContext->count++;
  1155. }
  1156. _tprintf(TEXT("%-60s: %s\n"),devID,action);
  1157. return EXIT_OK;
  1158. }
  1159. int cmdRemove(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  1160. /*++
  1161. Routine Description:
  1162. REMOVE
  1163. remove 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. GenericContext context;
  1173. TCHAR strRemove[80];
  1174. TCHAR strReboot[80];
  1175. TCHAR strFail[80];
  1176. int failcode = EXIT_FAIL;
  1177. if(!argc) {
  1178. //
  1179. // arguments required
  1180. //
  1181. return EXIT_USAGE;
  1182. }
  1183. if(Machine) {
  1184. //
  1185. // must be local machine as we need to involve class/co installers
  1186. //
  1187. return EXIT_USAGE;
  1188. }
  1189. if(!LoadString(NULL,IDS_REMOVED,strRemove,ARRAYSIZE(strRemove))) {
  1190. return EXIT_FAIL;
  1191. }
  1192. if(!LoadString(NULL,IDS_REMOVED_REBOOT,strReboot,ARRAYSIZE(strReboot))) {
  1193. return EXIT_FAIL;
  1194. }
  1195. if(!LoadString(NULL,IDS_REMOVE_FAILED,strFail,ARRAYSIZE(strFail))) {
  1196. return EXIT_FAIL;
  1197. }
  1198. context.reboot = FALSE;
  1199. context.count = 0;
  1200. context.strReboot = strReboot;
  1201. context.strSuccess = strRemove;
  1202. context.strFail = strFail;
  1203. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,RemoveCallback,&context);
  1204. if(failcode == EXIT_OK) {
  1205. if(!context.count) {
  1206. FormatToStream(stdout,MSG_REMOVE_TAIL_NONE);
  1207. } else if(!context.reboot) {
  1208. FormatToStream(stdout,MSG_REMOVE_TAIL,context.count);
  1209. } else {
  1210. FormatToStream(stdout,MSG_REMOVE_TAIL_REBOOT,context.count);
  1211. failcode = EXIT_REBOOT;
  1212. }
  1213. }
  1214. return failcode;
  1215. }
  1216. int cmdRescan(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  1217. /*++
  1218. Routine Description:
  1219. RESCAN
  1220. rescan for new devices
  1221. Arguments:
  1222. BaseName - name of executable
  1223. Machine - machine name, must be NULL
  1224. argc/argv - remaining parameters
  1225. Return Value:
  1226. EXIT_xxxx
  1227. --*/
  1228. {
  1229. //
  1230. // reenumerate from the root of the devnode tree
  1231. // totally CM based
  1232. //
  1233. int failcode = EXIT_FAIL;
  1234. HMACHINE machineHandle = NULL;
  1235. DEVINST devRoot;
  1236. if(Machine) {
  1237. if(CM_Connect_Machine(Machine,&machineHandle) != CR_SUCCESS) {
  1238. return failcode;
  1239. }
  1240. }
  1241. if(CM_Locate_DevNode_Ex(&devRoot,NULL,CM_LOCATE_DEVNODE_NORMAL,machineHandle) != CR_SUCCESS) {
  1242. goto final;
  1243. }
  1244. FormatToStream(stdout,Machine ? MSG_RESCAN : MSG_RESCAN_LOCAL);
  1245. if(CM_Reenumerate_DevNode_Ex(devRoot, 0, machineHandle) != CR_SUCCESS) {
  1246. goto final;
  1247. }
  1248. FormatToStream(stdout,MSG_RESCAN_OK);
  1249. failcode = EXIT_OK;
  1250. final:
  1251. if(machineHandle) {
  1252. CM_Disconnect_Machine(machineHandle);
  1253. }
  1254. return failcode;
  1255. }
  1256. int cmdClassFilter(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  1257. /*++
  1258. Routine Description:
  1259. CLASSFILTER <name> <type> <subcmds>
  1260. Allows tweaking of class filters
  1261. Useful for filter driver development and for Product Support
  1262. <subcmds> is a list of the following:
  1263. @service - sets 'after' to the first match of service after 'after'
  1264. (reset to -1 after any other command)
  1265. !service - deletes first match of 'service' after 'after'
  1266. -service - insert new service directly prior to 'after', or at start
  1267. +service - insert new service directly after 'after', or at end
  1268. if no <subcmds> given, list the services
  1269. Arguments:
  1270. BaseName - name of executable
  1271. Machine - if non-NULL, remote machine
  1272. argc/argv - remaining parameters - list of class names
  1273. Return Value:
  1274. EXIT_xxxx
  1275. --*/
  1276. {
  1277. int failcode = EXIT_FAIL;
  1278. int argIndex;
  1279. DWORD numGuids;
  1280. GUID guid;
  1281. LPTSTR regval = NULL;
  1282. HKEY hk = (HKEY)INVALID_HANDLE_VALUE;
  1283. LPTSTR * multiVal = NULL;
  1284. int after;
  1285. bool modified = false;
  1286. int span;
  1287. SC_HANDLE SCMHandle = NULL;
  1288. SC_HANDLE ServHandle = NULL;
  1289. if((argc<2) || !argv[0][0]) {
  1290. return EXIT_USAGE;
  1291. }
  1292. //
  1293. // just take the first guid for the name
  1294. //
  1295. if(!SetupDiClassGuidsFromNameEx(argv[0],&guid,1,&numGuids,Machine,NULL)) {
  1296. if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  1297. goto final;
  1298. }
  1299. }
  1300. if(numGuids == 0) {
  1301. goto final;
  1302. }
  1303. if(_tcsicmp(argv[1],TEXT("upper"))==0) {
  1304. regval = REGSTR_VAL_UPPERFILTERS;
  1305. } else if(_tcsicmp(argv[1],TEXT("lower"))==0) {
  1306. regval = REGSTR_VAL_LOWERFILTERS;
  1307. } else {
  1308. failcode = EXIT_USAGE;
  1309. goto final;
  1310. }
  1311. hk = SetupDiOpenClassRegKeyEx(&guid,
  1312. KEY_READ | (argc>2 ? KEY_WRITE : 0),
  1313. DIOCR_INSTALLER,
  1314. Machine,
  1315. NULL
  1316. );
  1317. if(hk == INVALID_HANDLE_VALUE) {
  1318. goto final;
  1319. }
  1320. multiVal = GetRegMultiSz(hk,regval);
  1321. if(argc<=2) {
  1322. //
  1323. // just display
  1324. //
  1325. FormatToStream(stdout,MSG_CLASSFILTER_UNCHANGED);
  1326. DumpArray(1,multiVal);
  1327. failcode = EXIT_OK;
  1328. goto final;
  1329. }
  1330. after = -1; // for the @service expressions
  1331. span = 1;
  1332. if(!multiVal) {
  1333. multiVal = CopyMultiSz(NULL);
  1334. if(!multiVal) {
  1335. goto final;
  1336. }
  1337. }
  1338. for(argIndex=2;argIndex<argc;argIndex++) {
  1339. if((argv[argIndex] == NULL) ||
  1340. (!argv[argIndex])) {
  1341. failcode = EXIT_USAGE;
  1342. break;
  1343. }
  1344. int op = argv[argIndex][0];
  1345. LPTSTR serv = argv[argIndex]+1;
  1346. int mark = 0;
  1347. int cnt;
  1348. int ent;
  1349. LPTSTR * tmpArray;
  1350. if(op == TEXT('=')) {
  1351. after = -1;
  1352. span = 1;
  1353. op = serv[0];
  1354. if(!op) {
  1355. continue;
  1356. }
  1357. serv++;
  1358. }
  1359. if(!serv[0]) {
  1360. failcode = EXIT_USAGE;
  1361. goto final;
  1362. }
  1363. if((op == TEXT('@')) || (op == TEXT('!'))) {
  1364. //
  1365. // need to find specified service in list
  1366. //
  1367. for(after+=span;multiVal[after];after++) {
  1368. if(_tcsicmp(multiVal[after],serv)==0) {
  1369. break;
  1370. }
  1371. }
  1372. if(!multiVal[after]) {
  1373. goto final;
  1374. }
  1375. if(op == TEXT('@')) {
  1376. //
  1377. // all we needed to do for '@'
  1378. //
  1379. span = 1; // span of 1
  1380. continue;
  1381. }
  1382. //
  1383. // we're modifying
  1384. //
  1385. int c;
  1386. for(c = after;multiVal[c];c++) {
  1387. multiVal[c] = multiVal[c+1];
  1388. }
  1389. LPTSTR * newArray = CopyMultiSz(multiVal);
  1390. if(!newArray) {
  1391. goto final;
  1392. }
  1393. DelMultiSz(multiVal);
  1394. multiVal = newArray;
  1395. span = 0; // span of 0 (deleted)
  1396. modified = true;
  1397. continue;
  1398. }
  1399. if(op == '+') {
  1400. //
  1401. // insert after
  1402. //
  1403. if(after<0) {
  1404. int c;
  1405. for(c = 0;multiVal[c];c++) {
  1406. // nothing
  1407. }
  1408. mark = c;
  1409. }
  1410. else {
  1411. mark = after+span;
  1412. }
  1413. } else if(op == '-') {
  1414. //
  1415. // insert before
  1416. //
  1417. if(after<0) {
  1418. mark = 0;
  1419. } else {
  1420. mark = after;
  1421. }
  1422. } else {
  1423. //
  1424. // not valid
  1425. //
  1426. failcode = EXIT_USAGE;
  1427. goto final;
  1428. }
  1429. //
  1430. // sanity - see if service exists
  1431. //
  1432. if(!(SCMHandle = OpenSCManager(Machine, NULL, GENERIC_READ))) {
  1433. goto final;
  1434. }
  1435. ServHandle = OpenService(SCMHandle,serv,GENERIC_READ);
  1436. if(ServHandle) {
  1437. CloseServiceHandle(ServHandle);
  1438. }
  1439. CloseServiceHandle(SCMHandle);
  1440. if(!ServHandle) {
  1441. goto final;
  1442. }
  1443. //
  1444. // need an array a little bigger
  1445. //
  1446. for(cnt = 0;multiVal[cnt];cnt++) {
  1447. // nothing
  1448. }
  1449. tmpArray = new LPTSTR[cnt+2];
  1450. if(!tmpArray) {
  1451. goto final;
  1452. }
  1453. for(ent=0;ent<mark;ent++) {
  1454. tmpArray[ent] = multiVal[ent];
  1455. }
  1456. tmpArray[ent] = serv;
  1457. for(;ent<cnt;ent++) {
  1458. tmpArray[ent+1] = multiVal[ent];
  1459. }
  1460. tmpArray[ent+1] = NULL;
  1461. LPTSTR * newArray = CopyMultiSz(tmpArray);
  1462. delete [] tmpArray;
  1463. if(!newArray) {
  1464. goto final;
  1465. }
  1466. DelMultiSz(multiVal);
  1467. multiVal = newArray;
  1468. modified = true;
  1469. span = 1;
  1470. after = mark;
  1471. }
  1472. if(modified) {
  1473. if(multiVal[0]) {
  1474. int len = 0;
  1475. LPTSTR multiSz = multiVal[-1];
  1476. LPTSTR p = multiSz;
  1477. while(*p) {
  1478. p+=lstrlen(p)+1;
  1479. }
  1480. p++; // skip past null
  1481. len = (p-multiSz)*sizeof(TCHAR);
  1482. LONG err = RegSetValueEx(hk,regval,0,REG_MULTI_SZ,(LPBYTE)multiSz,len);
  1483. if(err==NO_ERROR) {
  1484. FormatToStream(stdout,MSG_CLASSFILTER_CHANGED);
  1485. DumpArray(1,multiVal);
  1486. failcode = EXIT_REBOOT;
  1487. }
  1488. } else {
  1489. LONG err = RegDeleteValue(hk,regval);
  1490. if((err == NO_ERROR) || (err == ERROR_FILE_NOT_FOUND)) {
  1491. FormatToStream(stdout,MSG_CLASSFILTER_CHANGED);
  1492. failcode = EXIT_REBOOT;
  1493. }
  1494. }
  1495. } else {
  1496. FormatToStream(stdout,MSG_CLASSFILTER_UNCHANGED);
  1497. DumpArray(1,multiVal);
  1498. failcode = EXIT_OK;
  1499. }
  1500. final:
  1501. if(multiVal) {
  1502. DelMultiSz(multiVal);
  1503. }
  1504. if(hk != (HKEY)INVALID_HANDLE_VALUE) {
  1505. RegCloseKey(hk);
  1506. }
  1507. return failcode;
  1508. }
  1509. int SetHwidCallback(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Index,LPVOID Context)
  1510. /*++
  1511. Routine Description:
  1512. Callback for use by SetHwid
  1513. Arguments:
  1514. Devs )_ uniquely identify the device
  1515. DevInfo )
  1516. Index - index of device
  1517. Context - SetHwidContext
  1518. Return Value:
  1519. EXIT_xxxx
  1520. --*/
  1521. {
  1522. SP_REMOVEDEVICE_PARAMS rmdParams;
  1523. SetHwidContext *pControlContext = (SetHwidContext*)Context;
  1524. SP_DEVINSTALL_PARAMS devParams;
  1525. ULONG status;
  1526. ULONG problem;
  1527. LPTSTR * hwlist = NULL;
  1528. bool modified = false;
  1529. int result = EXIT_FAIL;
  1530. //
  1531. // processes the sub-commands on each callback
  1532. // not most efficient way of doing things, but perf isn't important
  1533. //
  1534. TCHAR devID[MAX_DEVICE_ID_LEN];
  1535. BOOL b = TRUE;
  1536. SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;
  1537. devInfoListDetail.cbSize = sizeof(devInfoListDetail);
  1538. if((!SetupDiGetDeviceInfoListDetail(Devs,&devInfoListDetail)) ||
  1539. (CM_Get_Device_ID_Ex(DevInfo->DevInst,devID,MAX_DEVICE_ID_LEN,0,devInfoListDetail.RemoteMachineHandle)!=CR_SUCCESS) ||
  1540. (CM_Get_DevNode_Status_Ex(&status,&problem,DevInfo->DevInst,0,devInfoListDetail.RemoteMachineHandle)!=CR_SUCCESS)) {
  1541. //
  1542. // skip this
  1543. //
  1544. return EXIT_OK;
  1545. }
  1546. //
  1547. // this is how to verify it's root enumerated
  1548. //
  1549. if(!(status & DN_ROOT_ENUMERATED)) {
  1550. _tprintf(TEXT("%-60s: "),devID);
  1551. FormatToStream(stdout,MSG_SETHWID_NOTROOT);
  1552. pControlContext->skipped++;
  1553. return EXIT_OK;
  1554. }
  1555. hwlist = GetDevMultiSz(Devs,DevInfo,pControlContext->prop);
  1556. if(hwlist == NULL) {
  1557. hwlist = CopyMultiSz(NULL);
  1558. if(hwlist == NULL) {
  1559. return EXIT_FAIL;
  1560. }
  1561. }
  1562. //
  1563. // modify hwid list (only relevent for root-enumerated devices)
  1564. //
  1565. int i;
  1566. int mark = -1;
  1567. for(i=0;i<pControlContext->argc_right;i++) {
  1568. LPTSTR op = pControlContext->argv_right[i];
  1569. if(op[0] == TEXT('=')) {
  1570. //
  1571. // clear the hwid list first
  1572. //
  1573. hwlist[0] = NULL;
  1574. mark = 0;
  1575. op++;
  1576. } else if(op[0] == TEXT('+')) {
  1577. //
  1578. // insert as better match
  1579. //
  1580. mark = 0;
  1581. op++;
  1582. } else if(op[0] == TEXT('-')) {
  1583. //
  1584. // insert as worse match
  1585. //
  1586. mark = -1;
  1587. op++;
  1588. } else if(op[0] == TEXT('!')) {
  1589. //
  1590. // delete
  1591. //
  1592. mark = -2;
  1593. op++;
  1594. } else {
  1595. //
  1596. // treat as a hardware id
  1597. //
  1598. }
  1599. if(!*op) {
  1600. result = EXIT_USAGE;
  1601. goto final;
  1602. }
  1603. int cnt;
  1604. for(cnt = 0;hwlist[cnt];cnt++) {
  1605. // nothing
  1606. }
  1607. if((mark == -1) || (mark>cnt)) {
  1608. mark = cnt;
  1609. }
  1610. LPTSTR * tmpArray = new LPTSTR[cnt+2];
  1611. if(!tmpArray) {
  1612. goto final;
  1613. }
  1614. int dst = 0;
  1615. int ent;
  1616. for(ent=0;ent<mark;ent++) {
  1617. if(_tcsicmp(hwlist[ent],op)==0) {
  1618. continue;
  1619. }
  1620. tmpArray[dst++] = hwlist[ent];
  1621. }
  1622. if(mark>=0) {
  1623. tmpArray[dst++] = op;
  1624. }
  1625. for(;ent<cnt;ent++) {
  1626. if(_tcsicmp(hwlist[ent],op)==0) {
  1627. continue;
  1628. }
  1629. tmpArray[dst++] = hwlist[ent];
  1630. }
  1631. tmpArray[dst] = NULL;
  1632. LPTSTR * newArray = CopyMultiSz(tmpArray);
  1633. delete [] tmpArray;
  1634. if(!newArray) {
  1635. goto final;
  1636. }
  1637. DelMultiSz(hwlist);
  1638. hwlist = newArray;
  1639. modified = true;
  1640. mark++;
  1641. }
  1642. //
  1643. // re-set the hwid list
  1644. //
  1645. if(modified) {
  1646. if(hwlist[0]) {
  1647. int len = 0;
  1648. LPTSTR multiSz = hwlist[-1];
  1649. LPTSTR p = multiSz;
  1650. while(*p) {
  1651. p+=lstrlen(p)+1;
  1652. }
  1653. p++; // skip past final null
  1654. len = (p-multiSz)*sizeof(TCHAR);
  1655. if(!SetupDiSetDeviceRegistryProperty(Devs,
  1656. DevInfo,
  1657. pControlContext->prop,
  1658. (LPBYTE)multiSz,
  1659. len)) {
  1660. result = EXIT_FAIL;
  1661. goto final;
  1662. }
  1663. } else {
  1664. //
  1665. // delete list
  1666. //
  1667. if(!SetupDiSetDeviceRegistryProperty(Devs,
  1668. DevInfo,
  1669. pControlContext->prop,
  1670. NULL,
  1671. 0)) {
  1672. result = EXIT_FAIL;
  1673. goto final;
  1674. }
  1675. }
  1676. }
  1677. result = EXIT_OK;
  1678. pControlContext->modified++;
  1679. _tprintf(TEXT("%-60s: "),devID);
  1680. for(mark=0;hwlist[mark];mark++) {
  1681. if(mark > 0) {
  1682. _tprintf(TEXT(","));
  1683. }
  1684. _tprintf(TEXT("%s"),hwlist[mark]);
  1685. }
  1686. _tprintf(TEXT("\n"));
  1687. //
  1688. // cleanup
  1689. //
  1690. final:
  1691. if(hwlist) {
  1692. DelMultiSz(hwlist);
  1693. }
  1694. return result;
  1695. }
  1696. int cmdSetHwid(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
  1697. /*++
  1698. Routine Description:
  1699. SETHWID
  1700. changes the hardware ID's of the listed root-enumerated devices
  1701. This demonstrates how to differentiate between root-enumerated and
  1702. non root-enumerated devices.
  1703. It also demonstrates how to get/set hardware ID's of root-enumerated
  1704. devices.
  1705. Arguments:
  1706. BaseName - name of executable
  1707. Machine - machine name, must be NULL
  1708. argc/argv - remaining parameters
  1709. Return Value:
  1710. EXIT_xxxx
  1711. --*/
  1712. {
  1713. SetHwidContext context;
  1714. int failcode = EXIT_FAIL;
  1715. if(!SplitCommandLine(argc,argv,context.argc_right,context.argv_right)
  1716. || (argc == 0)
  1717. || (context.argc_right == 0)) {
  1718. //
  1719. // arguments required both left and right of ':='
  1720. //
  1721. return EXIT_USAGE;
  1722. }
  1723. context.skipped = 0;
  1724. context.modified = 0;
  1725. context.prop = SPDRP_HARDWAREID;
  1726. failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,SetHwidCallback,&context);
  1727. if(failcode == EXIT_OK) {
  1728. if(context.skipped) {
  1729. FormatToStream(stdout,MSG_SETHWID_TAIL_SKIPPED,context.skipped,context.modified);
  1730. } else if(context.modified) {
  1731. FormatToStream(stdout,MSG_SETHWID_TAIL_MODIFIED,context.modified);
  1732. } else {
  1733. FormatToStream(stdout,MSG_SETHWID_TAIL_NONE);
  1734. }
  1735. }
  1736. return failcode;
  1737. }
  1738. DispatchEntry DispatchTable[] = {
  1739. { TEXT("classfilter"), cmdClassFilter, MSG_CLASSFILTER_SHORT, MSG_CLASSFILTER_LONG },
  1740. { TEXT("classes"), cmdClasses, MSG_CLASSES_SHORT, MSG_CLASSES_LONG },
  1741. { TEXT("disable"), cmdDisable, MSG_DISABLE_SHORT, MSG_DISABLE_LONG },
  1742. { TEXT("driverfiles"), cmdDriverFiles, MSG_DRIVERFILES_SHORT, MSG_DRIVERFILES_LONG },
  1743. { TEXT("drivernodes"), cmdDriverNodes, MSG_DRIVERNODES_SHORT, MSG_DRIVERNODES_LONG },
  1744. { TEXT("enable"), cmdEnable, MSG_ENABLE_SHORT, MSG_ENABLE_LONG },
  1745. { TEXT("find"), cmdFind, MSG_FIND_SHORT, MSG_FIND_LONG },
  1746. { TEXT("findall"), cmdFindAll, MSG_FINDALL_SHORT, MSG_FINDALL_LONG },
  1747. { TEXT("help"), cmdHelp, MSG_HELP_SHORT, 0 },
  1748. { TEXT("hwids"), cmdHwIds, MSG_HWIDS_SHORT, MSG_HWIDS_LONG },
  1749. { TEXT("install"), cmdInstall, MSG_INSTALL_SHORT, MSG_INSTALL_LONG },
  1750. { TEXT("listclass"), cmdListClass, MSG_LISTCLASS_SHORT, MSG_LISTCLASS_LONG },
  1751. { TEXT("reboot"), cmdReboot, MSG_REBOOT_SHORT, MSG_REBOOT_LONG },
  1752. { TEXT("remove"), cmdRemove, MSG_REMOVE_SHORT, MSG_REMOVE_LONG },
  1753. { TEXT("rescan"), cmdRescan, MSG_RESCAN_SHORT, MSG_RESCAN_LONG },
  1754. { TEXT("resources"), cmdResources, MSG_RESOURCES_SHORT, MSG_RESOURCES_LONG },
  1755. { TEXT("restart"), cmdRestart, MSG_RESTART_SHORT, MSG_RESTART_LONG },
  1756. { TEXT("sethwid"), cmdSetHwid, MSG_SETHWID_SHORT, MSG_SETHWID_LONG },
  1757. { TEXT("stack"), cmdStack, MSG_STACK_SHORT, MSG_STACK_LONG },
  1758. { TEXT("status"), cmdStatus, MSG_STATUS_SHORT, MSG_STATUS_LONG },
  1759. { TEXT("update"), cmdUpdate, MSG_UPDATE_SHORT, MSG_UPDATE_LONG },
  1760. { TEXT("updateni"), cmdUpdateNI, MSG_UPDATENI_SHORT, MSG_UPDATENI_LONG },
  1761. { TEXT("?"), cmdHelp, 0, 0 },
  1762. { NULL,NULL }
  1763. };