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
2138 lines
56 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
devcon.cpp
|
|
|
|
Abstract:
|
|
|
|
Device Console
|
|
command-line interface for managing devices
|
|
|
|
@@BEGIN_DDKSPLIT
|
|
Author:
|
|
|
|
Jamie Hunter (JamieHun) Nov-30-2000
|
|
|
|
Revision History:
|
|
|
|
@@END_DDKSPLIT
|
|
--*/
|
|
|
|
#include "devcon.h"
|
|
|
|
struct GenericContext {
|
|
DWORD count;
|
|
DWORD control;
|
|
BOOL reboot;
|
|
LPCTSTR strSuccess;
|
|
LPCTSTR strReboot;
|
|
LPCTSTR strFail;
|
|
};
|
|
|
|
#define FIND_DEVICE 0x00000001 // display device
|
|
#define FIND_STATUS 0x00000002 // display status of device
|
|
#define FIND_RESOURCES 0x00000004 // display resources of device
|
|
#define FIND_DRIVERFILES 0x00000008 // display drivers used by device
|
|
#define FIND_HWIDS 0x00000010 // display hw/compat id's used by device
|
|
#define FIND_DRIVERNODES 0x00000020 // display driver nodes for a device.
|
|
#define FIND_CLASS 0x00000040 // display device's setup class
|
|
#define FIND_STACK 0x00000080 // display device's driver-stack
|
|
|
|
struct SetHwidContext {
|
|
int argc_right;
|
|
LPTSTR * argv_right;
|
|
DWORD prop;
|
|
int skipped;
|
|
int modified;
|
|
};
|
|
|
|
int cmdHelp(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
HELP command
|
|
allow HELP or HELP <command>
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - if non-NULL, remote machine (ignored)
|
|
argc/argv - remaining parameters
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
DWORD helptext = 0;
|
|
int dispIndex;
|
|
LPCTSTR cmd = NULL;
|
|
BOOL unknown = FALSE;
|
|
|
|
if(argc) {
|
|
//
|
|
// user passed in a command for help on... long help
|
|
//
|
|
for(dispIndex = 0;DispatchTable[dispIndex].cmd;dispIndex++) {
|
|
if(_tcsicmp(argv[0],DispatchTable[dispIndex].cmd)==0) {
|
|
cmd = DispatchTable[dispIndex].cmd;
|
|
helptext = DispatchTable[dispIndex].longHelp;
|
|
break;
|
|
}
|
|
}
|
|
if(!cmd) {
|
|
unknown = TRUE;
|
|
cmd = argv[0];
|
|
}
|
|
}
|
|
|
|
if(helptext) {
|
|
//
|
|
// long help
|
|
//
|
|
FormatToStream(stdout,helptext,BaseName,cmd);
|
|
} else {
|
|
//
|
|
// help help
|
|
//
|
|
FormatToStream(stdout,unknown ? MSG_HELP_OTHER : MSG_HELP_LONG,BaseName,cmd);
|
|
//
|
|
// enumerate through each command and display short help for each
|
|
//
|
|
for(dispIndex = 0;DispatchTable[dispIndex].cmd;dispIndex++) {
|
|
if(DispatchTable[dispIndex].shortHelp) {
|
|
FormatToStream(stdout,DispatchTable[dispIndex].shortHelp,DispatchTable[dispIndex].cmd);
|
|
}
|
|
}
|
|
}
|
|
return EXIT_OK;
|
|
}
|
|
|
|
int cmdClasses(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
CLASSES command
|
|
lists classes on (optionally) specified machine
|
|
format as <name>: <destination>
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - if non-NULL, remote machine
|
|
argc/argv - remaining parameters - ignored
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
DWORD reqGuids = 128;
|
|
DWORD numGuids;
|
|
LPGUID guids = NULL;
|
|
DWORD index;
|
|
int failcode = EXIT_FAIL;
|
|
|
|
guids = new GUID[reqGuids];
|
|
if(!guids) {
|
|
goto final;
|
|
}
|
|
if(!SetupDiBuildClassInfoListEx(0,guids,reqGuids,&numGuids,Machine,NULL)) {
|
|
do {
|
|
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
goto final;
|
|
}
|
|
delete [] guids;
|
|
reqGuids = numGuids;
|
|
guids = new GUID[reqGuids];
|
|
if(!guids) {
|
|
goto final;
|
|
}
|
|
} while(!SetupDiBuildClassInfoListEx(0,guids,reqGuids,&numGuids,Machine,NULL));
|
|
}
|
|
FormatToStream(stdout,Machine?MSG_CLASSES_HEADER:MSG_CLASSES_HEADER_LOCAL,numGuids,Machine);
|
|
for(index=0;index<numGuids;index++) {
|
|
TCHAR className[MAX_CLASS_NAME_LEN];
|
|
TCHAR classDesc[LINE_LEN];
|
|
if(!SetupDiClassNameFromGuidEx(&guids[index],className,MAX_CLASS_NAME_LEN,NULL,Machine,NULL)) {
|
|
lstrcpyn(className,TEXT("?"),MAX_CLASS_NAME_LEN);
|
|
}
|
|
if(!SetupDiGetClassDescriptionEx(&guids[index],classDesc,LINE_LEN,NULL,Machine,NULL)) {
|
|
lstrcpyn(classDesc,className,LINE_LEN);
|
|
}
|
|
_tprintf(TEXT("%-20s: %s\n"),className,classDesc);
|
|
}
|
|
|
|
failcode = EXIT_OK;
|
|
|
|
final:
|
|
|
|
if(guids) {
|
|
delete [] guids;
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
int cmdListClass(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
LISTCLASS <name>....
|
|
lists all devices for each specified class
|
|
there can be more than one physical class for a class name (shouldn't be
|
|
though) in such cases, list each class
|
|
if machine given, list devices for that machine
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - if non-NULL, remote machine
|
|
argc/argv - remaining parameters - list of class names
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
BOOL classListed = FALSE;
|
|
BOOL devListed = FALSE;
|
|
DWORD reqGuids = 16;
|
|
int argIndex;
|
|
int failcode = EXIT_FAIL;
|
|
LPGUID guids = NULL;
|
|
HDEVINFO devs = INVALID_HANDLE_VALUE;
|
|
|
|
if(!argc) {
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
guids = new GUID[reqGuids];
|
|
if(!guids) {
|
|
goto final;
|
|
}
|
|
|
|
for(argIndex = 0;argIndex<argc;argIndex++) {
|
|
DWORD numGuids;
|
|
DWORD index;
|
|
|
|
if(!(argv[argIndex] && argv[argIndex][0])) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// there could be one to many name to GUID mapping
|
|
//
|
|
while(!SetupDiClassGuidsFromNameEx(argv[argIndex],guids,reqGuids,&numGuids,Machine,NULL)) {
|
|
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
goto final;
|
|
}
|
|
delete [] guids;
|
|
reqGuids = numGuids;
|
|
guids = new GUID[reqGuids];
|
|
if(!guids) {
|
|
goto final;
|
|
}
|
|
}
|
|
if(numGuids == 0) {
|
|
FormatToStream(stdout,Machine?MSG_LISTCLASS_NOCLASS:MSG_LISTCLASS_NOCLASS_LOCAL,argv[argIndex],Machine);
|
|
continue;
|
|
}
|
|
for(index = 0;index<numGuids;index++) {
|
|
TCHAR className[MAX_CLASS_NAME_LEN];
|
|
TCHAR classDesc[LINE_LEN];
|
|
DWORD devCount = 0;
|
|
SP_DEVINFO_DATA devInfo;
|
|
DWORD devIndex;
|
|
|
|
devs = SetupDiGetClassDevsEx(&guids[index],NULL,NULL,DIGCF_PRESENT,NULL,Machine,NULL);
|
|
if(devs != INVALID_HANDLE_VALUE) {
|
|
//
|
|
// count number of devices
|
|
//
|
|
devInfo.cbSize = sizeof(devInfo);
|
|
while(SetupDiEnumDeviceInfo(devs,devCount,&devInfo)) {
|
|
devCount++;
|
|
}
|
|
}
|
|
|
|
if(!SetupDiClassNameFromGuidEx(&guids[index],className,MAX_CLASS_NAME_LEN,NULL,Machine,NULL)) {
|
|
lstrcpyn(className,TEXT("?"),MAX_CLASS_NAME_LEN);
|
|
}
|
|
if(!SetupDiGetClassDescriptionEx(&guids[index],classDesc,LINE_LEN,NULL,Machine,NULL)) {
|
|
lstrcpyn(classDesc,className,LINE_LEN);
|
|
}
|
|
|
|
//
|
|
// how many devices?
|
|
//
|
|
if (!devCount) {
|
|
FormatToStream(stdout,Machine?MSG_LISTCLASS_HEADER_NONE:MSG_LISTCLASS_HEADER_NONE_LOCAL,className,classDesc,Machine);
|
|
} else {
|
|
FormatToStream(stdout,Machine?MSG_LISTCLASS_HEADER:MSG_LISTCLASS_HEADER_LOCAL,devCount,className,classDesc,Machine);
|
|
for(devIndex=0;SetupDiEnumDeviceInfo(devs,devIndex,&devInfo);devIndex++) {
|
|
DumpDevice(devs,&devInfo);
|
|
}
|
|
}
|
|
if(devs != INVALID_HANDLE_VALUE) {
|
|
SetupDiDestroyDeviceInfoList(devs);
|
|
devs = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
failcode = 0;
|
|
|
|
final:
|
|
|
|
if(guids) {
|
|
delete [] guids;
|
|
}
|
|
|
|
if(devs != INVALID_HANDLE_VALUE) {
|
|
SetupDiDestroyDeviceInfoList(devs);
|
|
}
|
|
|
|
return failcode;
|
|
}
|
|
|
|
int FindCallback(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Index,LPVOID Context)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback for use by Find/FindAll
|
|
just simply display the device
|
|
|
|
Arguments:
|
|
|
|
Devs )_ uniquely identify the device
|
|
DevInfo )
|
|
Index - index of device
|
|
Context - GenericContext
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
GenericContext *pFindContext = (GenericContext*)Context;
|
|
|
|
if(!pFindContext->control) {
|
|
DumpDevice(Devs,DevInfo);
|
|
pFindContext->count++;
|
|
return EXIT_OK;
|
|
}
|
|
if(!DumpDeviceWithInfo(Devs,DevInfo,NULL)) {
|
|
return EXIT_OK;
|
|
}
|
|
if(pFindContext->control&FIND_DEVICE) {
|
|
DumpDeviceDescr(Devs,DevInfo);
|
|
}
|
|
if(pFindContext->control&FIND_CLASS) {
|
|
DumpDeviceClass(Devs,DevInfo);
|
|
}
|
|
if(pFindContext->control&FIND_STATUS) {
|
|
DumpDeviceStatus(Devs,DevInfo);
|
|
}
|
|
if(pFindContext->control&FIND_RESOURCES) {
|
|
DumpDeviceResources(Devs,DevInfo);
|
|
}
|
|
if(pFindContext->control&FIND_DRIVERFILES) {
|
|
DumpDeviceDriverFiles(Devs,DevInfo);
|
|
}
|
|
if(pFindContext->control&FIND_STACK) {
|
|
DumpDeviceStack(Devs,DevInfo);
|
|
}
|
|
if(pFindContext->control&FIND_HWIDS) {
|
|
DumpDeviceHwIds(Devs,DevInfo);
|
|
}
|
|
if (pFindContext->control&FIND_DRIVERNODES) {
|
|
DumpDeviceDriverNodes(Devs,DevInfo);
|
|
}
|
|
pFindContext->count++;
|
|
return EXIT_OK;
|
|
}
|
|
|
|
int cmdFind(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
FIND <id> ...
|
|
use EnumerateDevices to do hardwareID matching
|
|
for each match, dump to stdout
|
|
note that we only enumerate present devices
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - if non-NULL, remote machine
|
|
argc/argv - remaining parameters - passed into EnumerateDevices
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
GenericContext context;
|
|
int failcode;
|
|
|
|
if(!argc) {
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
context.count = 0;
|
|
context.control = 0;
|
|
failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
|
|
|
|
if(failcode == EXIT_OK) {
|
|
|
|
if(!context.count) {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
|
|
} else {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
|
|
}
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
int cmdFindAll(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
FINDALL <id> ...
|
|
use EnumerateDevices to do hardwareID matching
|
|
for each match, dump to stdout
|
|
like find, but also show not-present devices
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - if non-NULL, remote machine
|
|
argc/argv - remaining parameters - passed into EnumerateDevices
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
GenericContext context;
|
|
int failcode;
|
|
|
|
if(!argc) {
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
context.count = 0;
|
|
context.control = 0;
|
|
failcode = EnumerateDevices(BaseName,Machine,0,argc,argv,FindCallback,&context);
|
|
|
|
if(failcode == EXIT_OK) {
|
|
|
|
if(!context.count) {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
|
|
} else {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
|
|
}
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
int cmdStatus(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
STATUS <id> ...
|
|
use EnumerateDevices to do hardwareID matching
|
|
for each match, dump status to stdout
|
|
note that we only enumerate present devices
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - if non-NULL, remote machine
|
|
argc/argv - remaining parameters - passed into EnumerateDevices
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
GenericContext context;
|
|
int failcode;
|
|
|
|
if(!argc) {
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
context.count = 0;
|
|
context.control = FIND_DEVICE | FIND_STATUS;
|
|
failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
|
|
|
|
if(failcode == EXIT_OK) {
|
|
|
|
if(!context.count) {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
|
|
} else {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
|
|
}
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
|
|
int cmdResources(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
RESOURCES <id> ...
|
|
use EnumerateDevices to do hardwareID matching
|
|
for each match, dump resources to stdout
|
|
note that we only enumerate present devices
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - if non-NULL, remote machine
|
|
argc/argv - remaining parameters - passed into EnumerateDevices
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
GenericContext context;
|
|
int failcode;
|
|
|
|
if(!argc) {
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
context.count = 0;
|
|
context.control = FIND_DEVICE | FIND_RESOURCES;
|
|
failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
|
|
|
|
if(failcode == EXIT_OK) {
|
|
|
|
if(!context.count) {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
|
|
} else {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
|
|
}
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
|
|
int cmdDriverFiles(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
STATUS <id> ...
|
|
use EnumerateDevices to do hardwareID matching
|
|
for each match, dump driver files to stdout
|
|
note that we only enumerate present devices
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - if non-NULL, remote machine
|
|
argc/argv - remaining parameters - passed into EnumerateDevices
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
GenericContext context;
|
|
int failcode;
|
|
|
|
if(!argc) {
|
|
return EXIT_USAGE;
|
|
}
|
|
if(Machine) {
|
|
//
|
|
// must be local machine as we need to involve class/co installers (FIND_DRIVERFILES)
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
context.count = 0;
|
|
context.control = FIND_DEVICE | FIND_DRIVERFILES;
|
|
failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
|
|
|
|
if(failcode == EXIT_OK) {
|
|
|
|
if(!context.count) {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
|
|
} else {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
|
|
}
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
int cmdDriverNodes(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
STATUS <id> ...
|
|
use EnumerateDevices to do hardwareID matching
|
|
for each match, dump drivernodes to stdout
|
|
note that we only enumerate present devices
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - if non-NULL, remote machine
|
|
argc/argv - remaining parameters - passed into EnumerateDevices
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
GenericContext context;
|
|
int failcode;
|
|
|
|
if(!argc) {
|
|
return EXIT_USAGE;
|
|
}
|
|
if(Machine) {
|
|
//
|
|
// must be local machine as we need to involve class/co installers (FIND_DRIVERNODES)
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
context.count = 0;
|
|
context.control = FIND_DEVICE | FIND_DRIVERNODES;
|
|
failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
|
|
|
|
if(failcode == EXIT_OK) {
|
|
|
|
if(!context.count) {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
|
|
} else {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
|
|
}
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
int cmdHwIds(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
HWIDS <id> ...
|
|
use EnumerateDevices to do hardwareID matching
|
|
for each match, dump hw/compat id's to stdout
|
|
note that we only enumerate present devices
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - if non-NULL, remote machine
|
|
argc/argv - remaining parameters - passed into EnumerateDevices
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
GenericContext context;
|
|
int failcode;
|
|
|
|
if(!argc) {
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
context.count = 0;
|
|
context.control = FIND_DEVICE | FIND_HWIDS;
|
|
failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
|
|
|
|
if(failcode == EXIT_OK) {
|
|
|
|
if(!context.count) {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
|
|
} else {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
|
|
}
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
int cmdStack(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
STACK <id> ...
|
|
use EnumerateDevices to do hardwareID matching
|
|
for each match, dump device class and stack to stdout
|
|
note that we only enumerate present devices
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - if non-NULL, remote machine
|
|
argc/argv - remaining parameters - passed into EnumerateDevices
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
GenericContext context;
|
|
int failcode;
|
|
|
|
if(!argc) {
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
context.count = 0;
|
|
context.control = FIND_DEVICE | FIND_CLASS | FIND_STACK;
|
|
failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,FindCallback,&context);
|
|
|
|
if(failcode == EXIT_OK) {
|
|
|
|
if(!context.count) {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL_NONE:MSG_FIND_TAIL_NONE_LOCAL,Machine);
|
|
} else {
|
|
FormatToStream(stdout,Machine?MSG_FIND_TAIL:MSG_FIND_TAIL_LOCAL,context.count,Machine);
|
|
}
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
|
|
|
|
|
|
int ControlCallback(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Index,LPVOID Context)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback for use by Enable/Disable/Restart
|
|
Invokes DIF_PROPERTYCHANGE with correct parameters
|
|
uses SetupDiCallClassInstaller so cannot be done for remote devices
|
|
Don't use CM_xxx API's, they bypass class/co-installers and this is bad.
|
|
|
|
In Enable case, we try global first, and if still disabled, enable local
|
|
|
|
Arguments:
|
|
|
|
Devs )_ uniquely identify the device
|
|
DevInfo )
|
|
Index - index of device
|
|
Context - GenericContext
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
SP_PROPCHANGE_PARAMS pcp;
|
|
GenericContext *pControlContext = (GenericContext*)Context;
|
|
SP_DEVINSTALL_PARAMS devParams;
|
|
|
|
switch(pControlContext->control) {
|
|
case DICS_ENABLE:
|
|
//
|
|
// enable both on global and config-specific profile
|
|
// do global first and see if that succeeded in enabling the device
|
|
// (global enable doesn't mark reboot required if device is still
|
|
// disabled on current config whereas vice-versa isn't true)
|
|
//
|
|
pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
|
|
pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
|
|
pcp.StateChange = pControlContext->control;
|
|
pcp.Scope = DICS_FLAG_GLOBAL;
|
|
pcp.HwProfile = 0;
|
|
//
|
|
// don't worry if this fails, we'll get an error when we try config-
|
|
// specific.
|
|
if(SetupDiSetClassInstallParams(Devs,DevInfo,&pcp.ClassInstallHeader,sizeof(pcp))) {
|
|
SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,Devs,DevInfo);
|
|
}
|
|
//
|
|
// now enable on config-specific
|
|
//
|
|
pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
|
|
pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
|
|
pcp.StateChange = pControlContext->control;
|
|
pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
|
|
pcp.HwProfile = 0;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// operate on config-specific profile
|
|
//
|
|
pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
|
|
pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
|
|
pcp.StateChange = pControlContext->control;
|
|
pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
|
|
pcp.HwProfile = 0;
|
|
break;
|
|
|
|
}
|
|
|
|
if(!SetupDiSetClassInstallParams(Devs,DevInfo,&pcp.ClassInstallHeader,sizeof(pcp)) ||
|
|
!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,Devs,DevInfo)) {
|
|
//
|
|
// failed to invoke DIF_PROPERTYCHANGE
|
|
//
|
|
DumpDeviceWithInfo(Devs,DevInfo,pControlContext->strFail);
|
|
} else {
|
|
//
|
|
// see if device needs reboot
|
|
//
|
|
devParams.cbSize = sizeof(devParams);
|
|
if(SetupDiGetDeviceInstallParams(Devs,DevInfo,&devParams) && (devParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT))) {
|
|
DumpDeviceWithInfo(Devs,DevInfo,pControlContext->strReboot);
|
|
pControlContext->reboot = TRUE;
|
|
} else {
|
|
//
|
|
// appears to have succeeded
|
|
//
|
|
DumpDeviceWithInfo(Devs,DevInfo,pControlContext->strSuccess);
|
|
}
|
|
pControlContext->count++;
|
|
}
|
|
return EXIT_OK;
|
|
}
|
|
|
|
int cmdEnable(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ENABLE <id> ...
|
|
use EnumerateDevices to do hardwareID matching
|
|
for each match, attempt to enable global, and if needed, config specific
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - must be NULL (local machine only)
|
|
argc/argv - remaining parameters - passed into EnumerateDevices
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx (EXIT_REBOOT if reboot is required)
|
|
|
|
--*/
|
|
{
|
|
GenericContext context;
|
|
TCHAR strEnable[80];
|
|
TCHAR strReboot[80];
|
|
TCHAR strFail[80];
|
|
int failcode = EXIT_FAIL;
|
|
|
|
if(!argc) {
|
|
//
|
|
// arguments required
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
if(Machine) {
|
|
//
|
|
// must be local machine as we need to involve class/co installers
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
if(!LoadString(NULL,IDS_ENABLED,strEnable,ARRAYSIZE(strEnable))) {
|
|
return EXIT_FAIL;
|
|
}
|
|
if(!LoadString(NULL,IDS_ENABLED_REBOOT,strReboot,ARRAYSIZE(strReboot))) {
|
|
return EXIT_FAIL;
|
|
}
|
|
if(!LoadString(NULL,IDS_ENABLE_FAILED,strFail,ARRAYSIZE(strFail))) {
|
|
return EXIT_FAIL;
|
|
}
|
|
|
|
context.control = DICS_ENABLE; // DICS_PROPCHANGE DICS_ENABLE DICS_DISABLE
|
|
context.reboot = FALSE;
|
|
context.count = 0;
|
|
context.strReboot = strReboot;
|
|
context.strSuccess = strEnable;
|
|
context.strFail = strFail;
|
|
failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,ControlCallback,&context);
|
|
|
|
if(failcode == EXIT_OK) {
|
|
|
|
if(!context.count) {
|
|
FormatToStream(stdout,MSG_ENABLE_TAIL_NONE);
|
|
} else if(!context.reboot) {
|
|
FormatToStream(stdout,MSG_ENABLE_TAIL,context.count);
|
|
} else {
|
|
FormatToStream(stdout,MSG_ENABLE_TAIL_REBOOT,context.count);
|
|
failcode = EXIT_REBOOT;
|
|
}
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
int cmdDisable(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
DISABLE <id> ...
|
|
use EnumerateDevices to do hardwareID matching
|
|
for each match, attempt to disable global
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - must be NULL (local machine only)
|
|
argc/argv - remaining parameters - passed into EnumerateDevices
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx (EXIT_REBOOT if reboot is required)
|
|
|
|
--*/
|
|
{
|
|
GenericContext context;
|
|
TCHAR strDisable[80];
|
|
TCHAR strReboot[80];
|
|
TCHAR strFail[80];
|
|
int failcode = EXIT_FAIL;
|
|
|
|
if(!argc) {
|
|
//
|
|
// arguments required
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
if(Machine) {
|
|
//
|
|
// must be local machine as we need to involve class/co installers
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
if(!LoadString(NULL,IDS_DISABLED,strDisable,ARRAYSIZE(strDisable))) {
|
|
return EXIT_FAIL;
|
|
}
|
|
if(!LoadString(NULL,IDS_DISABLED_REBOOT,strReboot,ARRAYSIZE(strReboot))) {
|
|
return EXIT_FAIL;
|
|
}
|
|
if(!LoadString(NULL,IDS_DISABLE_FAILED,strFail,ARRAYSIZE(strFail))) {
|
|
return EXIT_FAIL;
|
|
}
|
|
|
|
context.control = DICS_DISABLE; // DICS_PROPCHANGE DICS_ENABLE DICS_DISABLE
|
|
context.reboot = FALSE;
|
|
context.count = 0;
|
|
context.strReboot = strReboot;
|
|
context.strSuccess = strDisable;
|
|
context.strFail = strFail;
|
|
failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,ControlCallback,&context);
|
|
|
|
if(failcode == EXIT_OK) {
|
|
|
|
if(!context.count) {
|
|
FormatToStream(stdout,MSG_DISABLE_TAIL_NONE);
|
|
} else if(!context.reboot) {
|
|
FormatToStream(stdout,MSG_DISABLE_TAIL,context.count);
|
|
} else {
|
|
FormatToStream(stdout,MSG_DISABLE_TAIL_REBOOT,context.count);
|
|
failcode = EXIT_REBOOT;
|
|
}
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
int cmdRestart(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
RESTART <id> ...
|
|
use EnumerateDevices to do hardwareID matching
|
|
for each match, attempt to restart by issueing a PROPCHANGE
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - must be NULL (local machine only)
|
|
argc/argv - remaining parameters - passed into EnumerateDevices
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx (EXIT_REBOOT if reboot is required)
|
|
|
|
--*/
|
|
{
|
|
GenericContext context;
|
|
TCHAR strRestarted[80];
|
|
TCHAR strReboot[80];
|
|
TCHAR strFail[80];
|
|
int failcode = EXIT_FAIL;
|
|
|
|
if(!argc) {
|
|
//
|
|
// arguments required
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
if(Machine) {
|
|
//
|
|
// must be local machine as we need to involve class/co installers
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
if(!LoadString(NULL,IDS_RESTARTED,strRestarted,ARRAYSIZE(strRestarted))) {
|
|
return EXIT_FAIL;
|
|
}
|
|
if(!LoadString(NULL,IDS_REQUIRES_REBOOT,strReboot,ARRAYSIZE(strReboot))) {
|
|
return EXIT_FAIL;
|
|
}
|
|
if(!LoadString(NULL,IDS_RESTART_FAILED,strFail,ARRAYSIZE(strFail))) {
|
|
return EXIT_FAIL;
|
|
}
|
|
|
|
context.control = DICS_PROPCHANGE;
|
|
context.reboot = FALSE;
|
|
context.count = 0;
|
|
context.strReboot = strReboot;
|
|
context.strSuccess = strRestarted;
|
|
context.strFail = strFail;
|
|
failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,ControlCallback,&context);
|
|
|
|
if(failcode == EXIT_OK) {
|
|
|
|
if(!context.count) {
|
|
FormatToStream(stdout,MSG_RESTART_TAIL_NONE);
|
|
} else if(!context.reboot) {
|
|
FormatToStream(stdout,MSG_RESTART_TAIL,context.count);
|
|
} else {
|
|
FormatToStream(stdout,MSG_RESTART_TAIL_REBOOT,context.count);
|
|
failcode = EXIT_REBOOT;
|
|
}
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
int cmdReboot(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
REBOOT
|
|
reboot local machine
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - must be NULL (local machine only)
|
|
argc/argv - remaining parameters - ignored
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
if(Machine) {
|
|
//
|
|
// must be local machine
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
FormatToStream(stdout,MSG_REBOOT);
|
|
return Reboot() ? EXIT_OK : EXIT_FAIL;
|
|
}
|
|
|
|
|
|
int cmdUpdate(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
UPDATE
|
|
update driver for existing device(s)
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - machine name, must be NULL
|
|
argc/argv - remaining parameters
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
HMODULE newdevMod = NULL;
|
|
int failcode = EXIT_FAIL;
|
|
UpdateDriverForPlugAndPlayDevicesProto UpdateFn;
|
|
BOOL reboot = FALSE;
|
|
LPCTSTR hwid = NULL;
|
|
LPCTSTR inf = NULL;
|
|
DWORD flags = 0;
|
|
DWORD res;
|
|
TCHAR InfPath[MAX_PATH];
|
|
|
|
if(Machine) {
|
|
//
|
|
// must be local machine
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
if(argc<2) {
|
|
//
|
|
// at least HWID required
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
inf = argv[0];
|
|
if(!inf[0]) {
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
hwid = argv[1];
|
|
if(!hwid[0]) {
|
|
return EXIT_USAGE;
|
|
}
|
|
//
|
|
// Inf must be a full pathname
|
|
//
|
|
res = GetFullPathName(inf,MAX_PATH,InfPath,NULL);
|
|
if((res >= MAX_PATH) || (res == 0)) {
|
|
//
|
|
// inf pathname too long
|
|
//
|
|
return EXIT_FAIL;
|
|
}
|
|
if(GetFileAttributes(InfPath)==(DWORD)(-1)) {
|
|
//
|
|
// inf doesn't exist
|
|
//
|
|
return EXIT_FAIL;
|
|
}
|
|
inf = InfPath;
|
|
flags |= INSTALLFLAG_FORCE;
|
|
|
|
//
|
|
// make use of UpdateDriverForPlugAndPlayDevices
|
|
//
|
|
newdevMod = LoadLibrary(TEXT("newdev.dll"));
|
|
if(!newdevMod) {
|
|
goto final;
|
|
}
|
|
UpdateFn = (UpdateDriverForPlugAndPlayDevicesProto)GetProcAddress(newdevMod,UPDATEDRIVERFORPLUGANDPLAYDEVICES);
|
|
if(!UpdateFn)
|
|
{
|
|
goto final;
|
|
}
|
|
|
|
FormatToStream(stdout,inf ? MSG_UPDATE_INF : MSG_UPDATE,hwid,inf);
|
|
|
|
if(!UpdateFn(NULL,hwid,inf,flags,&reboot)) {
|
|
goto final;
|
|
}
|
|
|
|
FormatToStream(stdout,MSG_UPDATE_OK);
|
|
|
|
failcode = reboot ? EXIT_REBOOT : EXIT_OK;
|
|
|
|
final:
|
|
|
|
if(newdevMod) {
|
|
FreeLibrary(newdevMod);
|
|
}
|
|
|
|
return failcode;
|
|
}
|
|
|
|
int cmdInstall(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
INSTALL
|
|
install a device manually
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - machine name, must be NULL
|
|
argc/argv - remaining parameters
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
HDEVINFO DeviceInfoSet = INVALID_HANDLE_VALUE;
|
|
SP_DEVINFO_DATA DeviceInfoData;
|
|
GUID ClassGUID;
|
|
TCHAR ClassName[MAX_CLASS_NAME_LEN];
|
|
TCHAR hwIdList[LINE_LEN+4];
|
|
TCHAR InfPath[MAX_PATH];
|
|
DWORD err;
|
|
int failcode = EXIT_FAIL;
|
|
BOOL reboot = FALSE;
|
|
LPCTSTR hwid = NULL;
|
|
LPCTSTR inf = NULL;
|
|
DWORD flags = 0;
|
|
DWORD len;
|
|
|
|
if(Machine) {
|
|
//
|
|
// must be local machine
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
if(argc<2) {
|
|
//
|
|
// at least HWID required
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
inf = argv[0];
|
|
if(!inf[0]) {
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
hwid = argv[1];
|
|
if(!hwid[0]) {
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
//
|
|
// Inf must be a full pathname
|
|
//
|
|
if(GetFullPathName(inf,MAX_PATH,InfPath,NULL) >= MAX_PATH) {
|
|
//
|
|
// inf pathname too long
|
|
//
|
|
return EXIT_FAIL;
|
|
}
|
|
|
|
//
|
|
// List of hardware ID's must be double zero-terminated
|
|
//
|
|
ZeroMemory(hwIdList,sizeof(hwIdList));
|
|
lstrcpyn(hwIdList,hwid,LINE_LEN);
|
|
|
|
//
|
|
// Use the INF File to extract the Class GUID.
|
|
//
|
|
if (!SetupDiGetINFClass(InfPath,&ClassGUID,ClassName,sizeof(ClassName)/sizeof(ClassName[0]),0))
|
|
{
|
|
goto final;
|
|
}
|
|
|
|
//
|
|
// Create the container for the to-be-created Device Information Element.
|
|
//
|
|
DeviceInfoSet = SetupDiCreateDeviceInfoList(&ClassGUID,0);
|
|
if(DeviceInfoSet == INVALID_HANDLE_VALUE)
|
|
{
|
|
goto final;
|
|
}
|
|
|
|
//
|
|
// Now create the element.
|
|
// Use the Class GUID and Name from the INF file.
|
|
//
|
|
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
if (!SetupDiCreateDeviceInfo(DeviceInfoSet,
|
|
ClassName,
|
|
&ClassGUID,
|
|
NULL,
|
|
0,
|
|
DICD_GENERATE_ID,
|
|
&DeviceInfoData))
|
|
{
|
|
goto final;
|
|
}
|
|
|
|
//
|
|
// Add the HardwareID to the Device's HardwareID property.
|
|
//
|
|
if(!SetupDiSetDeviceRegistryProperty(DeviceInfoSet,
|
|
&DeviceInfoData,
|
|
SPDRP_HARDWAREID,
|
|
(LPBYTE)hwIdList,
|
|
(lstrlen(hwIdList)+1+1)*sizeof(TCHAR)))
|
|
{
|
|
goto final;
|
|
}
|
|
|
|
//
|
|
// Transform the registry element into an actual devnode
|
|
// in the PnP HW tree.
|
|
//
|
|
if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE,
|
|
DeviceInfoSet,
|
|
&DeviceInfoData))
|
|
{
|
|
goto final;
|
|
}
|
|
|
|
FormatToStream(stdout,MSG_INSTALL_UPDATE);
|
|
//
|
|
// update the driver for the device we just created
|
|
//
|
|
failcode = cmdUpdate(BaseName,Machine,argc,argv);
|
|
|
|
final:
|
|
|
|
if (DeviceInfoSet != INVALID_HANDLE_VALUE) {
|
|
SetupDiDestroyDeviceInfoList(DeviceInfoSet);
|
|
}
|
|
|
|
return failcode;
|
|
}
|
|
|
|
int cmdUpdateNI(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
UPDATE (non interactive version)
|
|
update driver for existing device(s)
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - machine name, must be NULL
|
|
argc/argv - remaining parameters
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// turn off interactive mode while doing the update
|
|
//
|
|
HMODULE setupapiMod = NULL;
|
|
SetupSetNonInteractiveModeProto SetNIFn;
|
|
int res;
|
|
BOOL prev;
|
|
|
|
setupapiMod = LoadLibrary(TEXT("setupapi.dll"));
|
|
if(!setupapiMod) {
|
|
return cmdUpdate(BaseName,Machine,argc,argv);
|
|
}
|
|
SetNIFn = (SetupSetNonInteractiveModeProto)GetProcAddress(setupapiMod,SETUPSETNONINTERACTIVEMODE);
|
|
if(!SetNIFn)
|
|
{
|
|
FreeLibrary(setupapiMod);
|
|
return cmdUpdate(BaseName,Machine,argc,argv);
|
|
}
|
|
prev = SetNIFn(TRUE);
|
|
res = cmdUpdate(BaseName,Machine,argc,argv);
|
|
SetNIFn(prev);
|
|
FreeLibrary(setupapiMod);
|
|
return res;
|
|
}
|
|
|
|
int RemoveCallback(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Index,LPVOID Context)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback for use by Remove
|
|
Invokes DIF_REMOVE
|
|
uses SetupDiCallClassInstaller so cannot be done for remote devices
|
|
Don't use CM_xxx API's, they bypass class/co-installers and this is bad.
|
|
|
|
Arguments:
|
|
|
|
Devs )_ uniquely identify the device
|
|
DevInfo )
|
|
Index - index of device
|
|
Context - GenericContext
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
SP_REMOVEDEVICE_PARAMS rmdParams;
|
|
GenericContext *pControlContext = (GenericContext*)Context;
|
|
SP_DEVINSTALL_PARAMS devParams;
|
|
LPCTSTR action = NULL;
|
|
|
|
//
|
|
// need hardware ID before trying to remove, as we wont have it after
|
|
//
|
|
TCHAR devID[MAX_DEVICE_ID_LEN];
|
|
LPTSTR desc;
|
|
BOOL b = TRUE;
|
|
SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;
|
|
|
|
devInfoListDetail.cbSize = sizeof(devInfoListDetail);
|
|
if((!SetupDiGetDeviceInfoListDetail(Devs,&devInfoListDetail)) ||
|
|
(CM_Get_Device_ID_Ex(DevInfo->DevInst,devID,MAX_DEVICE_ID_LEN,0,devInfoListDetail.RemoteMachineHandle)!=CR_SUCCESS)) {
|
|
//
|
|
// skip this
|
|
//
|
|
return EXIT_OK;
|
|
}
|
|
|
|
rmdParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
|
|
rmdParams.ClassInstallHeader.InstallFunction = DIF_REMOVE;
|
|
rmdParams.Scope = DI_REMOVEDEVICE_GLOBAL;
|
|
rmdParams.HwProfile = 0;
|
|
if(!SetupDiSetClassInstallParams(Devs,DevInfo,&rmdParams.ClassInstallHeader,sizeof(rmdParams)) ||
|
|
!SetupDiCallClassInstaller(DIF_REMOVE,Devs,DevInfo)) {
|
|
//
|
|
// failed to invoke DIF_REMOVE
|
|
//
|
|
action = pControlContext->strFail;
|
|
} else {
|
|
//
|
|
// see if device needs reboot
|
|
//
|
|
devParams.cbSize = sizeof(devParams);
|
|
if(SetupDiGetDeviceInstallParams(Devs,DevInfo,&devParams) && (devParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT))) {
|
|
//
|
|
// reboot required
|
|
//
|
|
action = pControlContext->strReboot;
|
|
pControlContext->reboot = TRUE;
|
|
} else {
|
|
//
|
|
// appears to have succeeded
|
|
//
|
|
action = pControlContext->strSuccess;
|
|
}
|
|
pControlContext->count++;
|
|
}
|
|
_tprintf(TEXT("%-60s: %s\n"),devID,action);
|
|
|
|
return EXIT_OK;
|
|
}
|
|
|
|
int cmdRemove(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
REMOVE
|
|
remove devices
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - machine name, must be NULL
|
|
argc/argv - remaining parameters
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
GenericContext context;
|
|
TCHAR strRemove[80];
|
|
TCHAR strReboot[80];
|
|
TCHAR strFail[80];
|
|
int failcode = EXIT_FAIL;
|
|
|
|
if(!argc) {
|
|
//
|
|
// arguments required
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
if(Machine) {
|
|
//
|
|
// must be local machine as we need to involve class/co installers
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
if(!LoadString(NULL,IDS_REMOVED,strRemove,ARRAYSIZE(strRemove))) {
|
|
return EXIT_FAIL;
|
|
}
|
|
if(!LoadString(NULL,IDS_REMOVED_REBOOT,strReboot,ARRAYSIZE(strReboot))) {
|
|
return EXIT_FAIL;
|
|
}
|
|
if(!LoadString(NULL,IDS_REMOVE_FAILED,strFail,ARRAYSIZE(strFail))) {
|
|
return EXIT_FAIL;
|
|
}
|
|
|
|
context.reboot = FALSE;
|
|
context.count = 0;
|
|
context.strReboot = strReboot;
|
|
context.strSuccess = strRemove;
|
|
context.strFail = strFail;
|
|
failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,RemoveCallback,&context);
|
|
|
|
if(failcode == EXIT_OK) {
|
|
|
|
if(!context.count) {
|
|
FormatToStream(stdout,MSG_REMOVE_TAIL_NONE);
|
|
} else if(!context.reboot) {
|
|
FormatToStream(stdout,MSG_REMOVE_TAIL,context.count);
|
|
} else {
|
|
FormatToStream(stdout,MSG_REMOVE_TAIL_REBOOT,context.count);
|
|
failcode = EXIT_REBOOT;
|
|
}
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
int cmdRescan(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
RESCAN
|
|
rescan for new devices
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - machine name, must be NULL
|
|
argc/argv - remaining parameters
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// reenumerate from the root of the devnode tree
|
|
// totally CM based
|
|
//
|
|
int failcode = EXIT_FAIL;
|
|
HMACHINE machineHandle = NULL;
|
|
DEVINST devRoot;
|
|
|
|
if(Machine) {
|
|
if(CM_Connect_Machine(Machine,&machineHandle) != CR_SUCCESS) {
|
|
return failcode;
|
|
}
|
|
}
|
|
|
|
if(CM_Locate_DevNode_Ex(&devRoot,NULL,CM_LOCATE_DEVNODE_NORMAL,machineHandle) != CR_SUCCESS) {
|
|
goto final;
|
|
}
|
|
|
|
FormatToStream(stdout,Machine ? MSG_RESCAN : MSG_RESCAN_LOCAL);
|
|
|
|
if(CM_Reenumerate_DevNode_Ex(devRoot, 0, machineHandle) != CR_SUCCESS) {
|
|
goto final;
|
|
}
|
|
|
|
FormatToStream(stdout,MSG_RESCAN_OK);
|
|
|
|
failcode = EXIT_OK;
|
|
|
|
final:
|
|
if(machineHandle) {
|
|
CM_Disconnect_Machine(machineHandle);
|
|
}
|
|
|
|
return failcode;
|
|
}
|
|
|
|
int cmdClassFilter(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
CLASSFILTER <name> <type> <subcmds>
|
|
Allows tweaking of class filters
|
|
Useful for filter driver development and for Product Support
|
|
|
|
<subcmds> is a list of the following:
|
|
@service - sets 'after' to the first match of service after 'after'
|
|
(reset to -1 after any other command)
|
|
!service - deletes first match of 'service' after 'after'
|
|
-service - insert new service directly prior to 'after', or at start
|
|
+service - insert new service directly after 'after', or at end
|
|
|
|
if no <subcmds> given, list the services
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - if non-NULL, remote machine
|
|
argc/argv - remaining parameters - list of class names
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
int failcode = EXIT_FAIL;
|
|
int argIndex;
|
|
DWORD numGuids;
|
|
GUID guid;
|
|
LPTSTR regval = NULL;
|
|
HKEY hk = (HKEY)INVALID_HANDLE_VALUE;
|
|
LPTSTR * multiVal = NULL;
|
|
int after;
|
|
bool modified = false;
|
|
int span;
|
|
SC_HANDLE SCMHandle = NULL;
|
|
SC_HANDLE ServHandle = NULL;
|
|
|
|
if((argc<2) || !argv[0][0]) {
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
//
|
|
// just take the first guid for the name
|
|
//
|
|
if(!SetupDiClassGuidsFromNameEx(argv[0],&guid,1,&numGuids,Machine,NULL)) {
|
|
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
goto final;
|
|
}
|
|
}
|
|
if(numGuids == 0) {
|
|
goto final;
|
|
}
|
|
if(_tcsicmp(argv[1],TEXT("upper"))==0) {
|
|
regval = REGSTR_VAL_UPPERFILTERS;
|
|
} else if(_tcsicmp(argv[1],TEXT("lower"))==0) {
|
|
regval = REGSTR_VAL_LOWERFILTERS;
|
|
} else {
|
|
failcode = EXIT_USAGE;
|
|
goto final;
|
|
}
|
|
|
|
hk = SetupDiOpenClassRegKeyEx(&guid,
|
|
KEY_READ | (argc>2 ? KEY_WRITE : 0),
|
|
DIOCR_INSTALLER,
|
|
Machine,
|
|
NULL
|
|
);
|
|
if(hk == INVALID_HANDLE_VALUE) {
|
|
goto final;
|
|
}
|
|
multiVal = GetRegMultiSz(hk,regval);
|
|
|
|
if(argc<=2) {
|
|
//
|
|
// just display
|
|
//
|
|
FormatToStream(stdout,MSG_CLASSFILTER_UNCHANGED);
|
|
DumpArray(1,multiVal);
|
|
failcode = EXIT_OK;
|
|
goto final;
|
|
}
|
|
after = -1; // for the @service expressions
|
|
span = 1;
|
|
|
|
if(!multiVal) {
|
|
multiVal = CopyMultiSz(NULL);
|
|
if(!multiVal) {
|
|
goto final;
|
|
}
|
|
}
|
|
|
|
for(argIndex=2;argIndex<argc;argIndex++) {
|
|
if((argv[argIndex] == NULL) ||
|
|
(!argv[argIndex])) {
|
|
failcode = EXIT_USAGE;
|
|
break;
|
|
}
|
|
|
|
int op = argv[argIndex][0];
|
|
LPTSTR serv = argv[argIndex]+1;
|
|
int mark = 0;
|
|
int cnt;
|
|
int ent;
|
|
LPTSTR * tmpArray;
|
|
|
|
if(op == TEXT('=')) {
|
|
after = -1;
|
|
span = 1;
|
|
op = serv[0];
|
|
if(!op) {
|
|
continue;
|
|
}
|
|
serv++;
|
|
}
|
|
|
|
if(!serv[0]) {
|
|
failcode = EXIT_USAGE;
|
|
goto final;
|
|
}
|
|
|
|
if((op == TEXT('@')) || (op == TEXT('!'))) {
|
|
//
|
|
// need to find specified service in list
|
|
//
|
|
for(after+=span;multiVal[after];after++) {
|
|
if(_tcsicmp(multiVal[after],serv)==0) {
|
|
break;
|
|
}
|
|
}
|
|
if(!multiVal[after]) {
|
|
goto final;
|
|
}
|
|
if(op == TEXT('@')) {
|
|
//
|
|
// all we needed to do for '@'
|
|
//
|
|
span = 1; // span of 1
|
|
continue;
|
|
}
|
|
//
|
|
// we're modifying
|
|
//
|
|
int c;
|
|
for(c = after;multiVal[c];c++) {
|
|
multiVal[c] = multiVal[c+1];
|
|
}
|
|
LPTSTR * newArray = CopyMultiSz(multiVal);
|
|
if(!newArray) {
|
|
goto final;
|
|
}
|
|
DelMultiSz(multiVal);
|
|
multiVal = newArray;
|
|
span = 0; // span of 0 (deleted)
|
|
modified = true;
|
|
continue;
|
|
}
|
|
|
|
if(op == '+') {
|
|
//
|
|
// insert after
|
|
//
|
|
if(after<0) {
|
|
int c;
|
|
for(c = 0;multiVal[c];c++) {
|
|
// nothing
|
|
}
|
|
mark = c;
|
|
}
|
|
else {
|
|
mark = after+span;
|
|
}
|
|
} else if(op == '-') {
|
|
//
|
|
// insert before
|
|
//
|
|
if(after<0) {
|
|
mark = 0;
|
|
} else {
|
|
mark = after;
|
|
}
|
|
} else {
|
|
//
|
|
// not valid
|
|
//
|
|
failcode = EXIT_USAGE;
|
|
goto final;
|
|
}
|
|
//
|
|
// sanity - see if service exists
|
|
//
|
|
if(!(SCMHandle = OpenSCManager(Machine, NULL, GENERIC_READ))) {
|
|
goto final;
|
|
}
|
|
ServHandle = OpenService(SCMHandle,serv,GENERIC_READ);
|
|
if(ServHandle) {
|
|
CloseServiceHandle(ServHandle);
|
|
}
|
|
CloseServiceHandle(SCMHandle);
|
|
if(!ServHandle) {
|
|
goto final;
|
|
}
|
|
|
|
//
|
|
// need an array a little bigger
|
|
//
|
|
for(cnt = 0;multiVal[cnt];cnt++) {
|
|
// nothing
|
|
}
|
|
|
|
tmpArray = new LPTSTR[cnt+2];
|
|
if(!tmpArray) {
|
|
goto final;
|
|
}
|
|
for(ent=0;ent<mark;ent++) {
|
|
tmpArray[ent] = multiVal[ent];
|
|
}
|
|
tmpArray[ent] = serv;
|
|
for(;ent<cnt;ent++) {
|
|
tmpArray[ent+1] = multiVal[ent];
|
|
}
|
|
tmpArray[ent+1] = NULL;
|
|
LPTSTR * newArray = CopyMultiSz(tmpArray);
|
|
delete [] tmpArray;
|
|
if(!newArray) {
|
|
goto final;
|
|
}
|
|
DelMultiSz(multiVal);
|
|
multiVal = newArray;
|
|
modified = true;
|
|
span = 1;
|
|
after = mark;
|
|
}
|
|
|
|
if(modified) {
|
|
if(multiVal[0]) {
|
|
int len = 0;
|
|
LPTSTR multiSz = multiVal[-1];
|
|
LPTSTR p = multiSz;
|
|
while(*p) {
|
|
p+=lstrlen(p)+1;
|
|
}
|
|
p++; // skip past null
|
|
len = (p-multiSz)*sizeof(TCHAR);
|
|
LONG err = RegSetValueEx(hk,regval,0,REG_MULTI_SZ,(LPBYTE)multiSz,len);
|
|
if(err==NO_ERROR) {
|
|
FormatToStream(stdout,MSG_CLASSFILTER_CHANGED);
|
|
DumpArray(1,multiVal);
|
|
failcode = EXIT_REBOOT;
|
|
}
|
|
} else {
|
|
LONG err = RegDeleteValue(hk,regval);
|
|
if((err == NO_ERROR) || (err == ERROR_FILE_NOT_FOUND)) {
|
|
FormatToStream(stdout,MSG_CLASSFILTER_CHANGED);
|
|
failcode = EXIT_REBOOT;
|
|
}
|
|
}
|
|
} else {
|
|
FormatToStream(stdout,MSG_CLASSFILTER_UNCHANGED);
|
|
DumpArray(1,multiVal);
|
|
failcode = EXIT_OK;
|
|
}
|
|
|
|
final:
|
|
|
|
if(multiVal) {
|
|
DelMultiSz(multiVal);
|
|
}
|
|
if(hk != (HKEY)INVALID_HANDLE_VALUE) {
|
|
RegCloseKey(hk);
|
|
}
|
|
|
|
return failcode;
|
|
}
|
|
|
|
int SetHwidCallback(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Index,LPVOID Context)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback for use by SetHwid
|
|
|
|
Arguments:
|
|
|
|
Devs )_ uniquely identify the device
|
|
DevInfo )
|
|
Index - index of device
|
|
Context - SetHwidContext
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
SP_REMOVEDEVICE_PARAMS rmdParams;
|
|
SetHwidContext *pControlContext = (SetHwidContext*)Context;
|
|
SP_DEVINSTALL_PARAMS devParams;
|
|
ULONG status;
|
|
ULONG problem;
|
|
LPTSTR * hwlist = NULL;
|
|
bool modified = false;
|
|
int result = EXIT_FAIL;
|
|
|
|
//
|
|
// processes the sub-commands on each callback
|
|
// not most efficient way of doing things, but perf isn't important
|
|
//
|
|
TCHAR devID[MAX_DEVICE_ID_LEN];
|
|
BOOL b = TRUE;
|
|
SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;
|
|
|
|
devInfoListDetail.cbSize = sizeof(devInfoListDetail);
|
|
if((!SetupDiGetDeviceInfoListDetail(Devs,&devInfoListDetail)) ||
|
|
(CM_Get_Device_ID_Ex(DevInfo->DevInst,devID,MAX_DEVICE_ID_LEN,0,devInfoListDetail.RemoteMachineHandle)!=CR_SUCCESS) ||
|
|
(CM_Get_DevNode_Status_Ex(&status,&problem,DevInfo->DevInst,0,devInfoListDetail.RemoteMachineHandle)!=CR_SUCCESS)) {
|
|
//
|
|
// skip this
|
|
//
|
|
return EXIT_OK;
|
|
}
|
|
//
|
|
// this is how to verify it's root enumerated
|
|
//
|
|
if(!(status & DN_ROOT_ENUMERATED)) {
|
|
_tprintf(TEXT("%-60s: "),devID);
|
|
FormatToStream(stdout,MSG_SETHWID_NOTROOT);
|
|
pControlContext->skipped++;
|
|
return EXIT_OK;
|
|
}
|
|
hwlist = GetDevMultiSz(Devs,DevInfo,pControlContext->prop);
|
|
if(hwlist == NULL) {
|
|
hwlist = CopyMultiSz(NULL);
|
|
if(hwlist == NULL) {
|
|
return EXIT_FAIL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// modify hwid list (only relevent for root-enumerated devices)
|
|
//
|
|
int i;
|
|
int mark = -1;
|
|
|
|
for(i=0;i<pControlContext->argc_right;i++) {
|
|
LPTSTR op = pControlContext->argv_right[i];
|
|
if(op[0] == TEXT('=')) {
|
|
//
|
|
// clear the hwid list first
|
|
//
|
|
hwlist[0] = NULL;
|
|
mark = 0;
|
|
op++;
|
|
} else if(op[0] == TEXT('+')) {
|
|
//
|
|
// insert as better match
|
|
//
|
|
mark = 0;
|
|
op++;
|
|
} else if(op[0] == TEXT('-')) {
|
|
//
|
|
// insert as worse match
|
|
//
|
|
mark = -1;
|
|
op++;
|
|
} else if(op[0] == TEXT('!')) {
|
|
//
|
|
// delete
|
|
//
|
|
mark = -2;
|
|
op++;
|
|
} else {
|
|
//
|
|
// treat as a hardware id
|
|
//
|
|
}
|
|
if(!*op) {
|
|
result = EXIT_USAGE;
|
|
goto final;
|
|
}
|
|
int cnt;
|
|
for(cnt = 0;hwlist[cnt];cnt++) {
|
|
// nothing
|
|
}
|
|
if((mark == -1) || (mark>cnt)) {
|
|
mark = cnt;
|
|
}
|
|
LPTSTR * tmpArray = new LPTSTR[cnt+2];
|
|
if(!tmpArray) {
|
|
goto final;
|
|
}
|
|
int dst = 0;
|
|
int ent;
|
|
for(ent=0;ent<mark;ent++) {
|
|
if(_tcsicmp(hwlist[ent],op)==0) {
|
|
continue;
|
|
}
|
|
tmpArray[dst++] = hwlist[ent];
|
|
}
|
|
if(mark>=0) {
|
|
tmpArray[dst++] = op;
|
|
}
|
|
for(;ent<cnt;ent++) {
|
|
if(_tcsicmp(hwlist[ent],op)==0) {
|
|
continue;
|
|
}
|
|
tmpArray[dst++] = hwlist[ent];
|
|
}
|
|
tmpArray[dst] = NULL;
|
|
LPTSTR * newArray = CopyMultiSz(tmpArray);
|
|
delete [] tmpArray;
|
|
if(!newArray) {
|
|
goto final;
|
|
}
|
|
DelMultiSz(hwlist);
|
|
hwlist = newArray;
|
|
modified = true;
|
|
mark++;
|
|
}
|
|
|
|
//
|
|
// re-set the hwid list
|
|
//
|
|
if(modified) {
|
|
if(hwlist[0]) {
|
|
int len = 0;
|
|
LPTSTR multiSz = hwlist[-1];
|
|
LPTSTR p = multiSz;
|
|
while(*p) {
|
|
p+=lstrlen(p)+1;
|
|
}
|
|
p++; // skip past final null
|
|
len = (p-multiSz)*sizeof(TCHAR);
|
|
if(!SetupDiSetDeviceRegistryProperty(Devs,
|
|
DevInfo,
|
|
pControlContext->prop,
|
|
(LPBYTE)multiSz,
|
|
len)) {
|
|
result = EXIT_FAIL;
|
|
goto final;
|
|
}
|
|
} else {
|
|
//
|
|
// delete list
|
|
//
|
|
if(!SetupDiSetDeviceRegistryProperty(Devs,
|
|
DevInfo,
|
|
pControlContext->prop,
|
|
NULL,
|
|
0)) {
|
|
result = EXIT_FAIL;
|
|
goto final;
|
|
}
|
|
}
|
|
}
|
|
result = EXIT_OK;
|
|
pControlContext->modified++;
|
|
_tprintf(TEXT("%-60s: "),devID);
|
|
for(mark=0;hwlist[mark];mark++) {
|
|
if(mark > 0) {
|
|
_tprintf(TEXT(","));
|
|
}
|
|
_tprintf(TEXT("%s"),hwlist[mark]);
|
|
}
|
|
_tprintf(TEXT("\n"));
|
|
|
|
//
|
|
// cleanup
|
|
//
|
|
|
|
final:
|
|
|
|
if(hwlist) {
|
|
DelMultiSz(hwlist);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int cmdSetHwid(LPCTSTR BaseName,LPCTSTR Machine,int argc,TCHAR* argv[])
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
SETHWID
|
|
changes the hardware ID's of the listed root-enumerated devices
|
|
This demonstrates how to differentiate between root-enumerated and
|
|
non root-enumerated devices.
|
|
It also demonstrates how to get/set hardware ID's of root-enumerated
|
|
devices.
|
|
|
|
Arguments:
|
|
|
|
BaseName - name of executable
|
|
Machine - machine name, must be NULL
|
|
argc/argv - remaining parameters
|
|
|
|
Return Value:
|
|
|
|
EXIT_xxxx
|
|
|
|
--*/
|
|
{
|
|
SetHwidContext context;
|
|
int failcode = EXIT_FAIL;
|
|
|
|
if(!SplitCommandLine(argc,argv,context.argc_right,context.argv_right)
|
|
|| (argc == 0)
|
|
|| (context.argc_right == 0)) {
|
|
//
|
|
// arguments required both left and right of ':='
|
|
//
|
|
return EXIT_USAGE;
|
|
}
|
|
context.skipped = 0;
|
|
context.modified = 0;
|
|
context.prop = SPDRP_HARDWAREID;
|
|
|
|
failcode = EnumerateDevices(BaseName,Machine,DIGCF_PRESENT,argc,argv,SetHwidCallback,&context);
|
|
|
|
if(failcode == EXIT_OK) {
|
|
|
|
if(context.skipped) {
|
|
FormatToStream(stdout,MSG_SETHWID_TAIL_SKIPPED,context.skipped,context.modified);
|
|
} else if(context.modified) {
|
|
FormatToStream(stdout,MSG_SETHWID_TAIL_MODIFIED,context.modified);
|
|
} else {
|
|
FormatToStream(stdout,MSG_SETHWID_TAIL_NONE);
|
|
}
|
|
}
|
|
return failcode;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DispatchEntry DispatchTable[] = {
|
|
{ TEXT("classfilter"), cmdClassFilter, MSG_CLASSFILTER_SHORT, MSG_CLASSFILTER_LONG },
|
|
{ TEXT("classes"), cmdClasses, MSG_CLASSES_SHORT, MSG_CLASSES_LONG },
|
|
{ TEXT("disable"), cmdDisable, MSG_DISABLE_SHORT, MSG_DISABLE_LONG },
|
|
{ TEXT("driverfiles"), cmdDriverFiles, MSG_DRIVERFILES_SHORT, MSG_DRIVERFILES_LONG },
|
|
{ TEXT("drivernodes"), cmdDriverNodes, MSG_DRIVERNODES_SHORT, MSG_DRIVERNODES_LONG },
|
|
{ TEXT("enable"), cmdEnable, MSG_ENABLE_SHORT, MSG_ENABLE_LONG },
|
|
{ TEXT("find"), cmdFind, MSG_FIND_SHORT, MSG_FIND_LONG },
|
|
{ TEXT("findall"), cmdFindAll, MSG_FINDALL_SHORT, MSG_FINDALL_LONG },
|
|
{ TEXT("help"), cmdHelp, MSG_HELP_SHORT, 0 },
|
|
{ TEXT("hwids"), cmdHwIds, MSG_HWIDS_SHORT, MSG_HWIDS_LONG },
|
|
{ TEXT("install"), cmdInstall, MSG_INSTALL_SHORT, MSG_INSTALL_LONG },
|
|
{ TEXT("listclass"), cmdListClass, MSG_LISTCLASS_SHORT, MSG_LISTCLASS_LONG },
|
|
{ TEXT("reboot"), cmdReboot, MSG_REBOOT_SHORT, MSG_REBOOT_LONG },
|
|
{ TEXT("remove"), cmdRemove, MSG_REMOVE_SHORT, MSG_REMOVE_LONG },
|
|
{ TEXT("rescan"), cmdRescan, MSG_RESCAN_SHORT, MSG_RESCAN_LONG },
|
|
{ TEXT("resources"), cmdResources, MSG_RESOURCES_SHORT, MSG_RESOURCES_LONG },
|
|
{ TEXT("restart"), cmdRestart, MSG_RESTART_SHORT, MSG_RESTART_LONG },
|
|
{ TEXT("sethwid"), cmdSetHwid, MSG_SETHWID_SHORT, MSG_SETHWID_LONG },
|
|
{ TEXT("stack"), cmdStack, MSG_STACK_SHORT, MSG_STACK_LONG },
|
|
{ TEXT("status"), cmdStatus, MSG_STATUS_SHORT, MSG_STATUS_LONG },
|
|
{ TEXT("update"), cmdUpdate, MSG_UPDATE_SHORT, MSG_UPDATE_LONG },
|
|
{ TEXT("updateni"), cmdUpdateNI, MSG_UPDATENI_SHORT, MSG_UPDATENI_LONG },
|
|
{ TEXT("?"), cmdHelp, 0, 0 },
|
|
{ NULL,NULL }
|
|
};
|
|
|
|
|