|
|
/*++
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 IdEntry { LPCTSTR String; // string looking for
LPCTSTR Wild; // first wild character if any
BOOL InstanceId; };
void FormatToStream(FILE * stream,DWORD fmt,...) /*++
Routine Description:
Format text to stream using a particular msg-id fmt Used for displaying localizable messages
Arguments:
stream - file stream to output to, stdout or stderr fmt - message id ... - parameters %1...
Return Value:
none
--*/ { va_list arglist; LPTSTR locbuffer = NULL; DWORD count;
va_start(arglist, fmt); count = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE|FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, fmt, 0, // LANGID
(LPTSTR) &locbuffer, 0, // minimum size of buffer
&arglist);
if(locbuffer) { if(count) { _fputts(locbuffer,stream); } LocalFree(locbuffer); } }
void Padding(int pad) /*++
Routine Description:
Insert padding into line before text
Arguments:
pad - number of padding tabs to insert
Return Value:
none
--*/ { int c;
for(c=0;c<pad;c++) { fputs(" ",stdout); } }
void Usage(LPCTSTR BaseName) /*++
Routine Description:
Display simple usage text
Arguments:
BaseName - name of executable
Return Value:
none
--*/ { FormatToStream(stderr,MSG_USAGE,BaseName); }
void CommandUsage(LPCTSTR BaseName,LPCTSTR Cmd) /*++
Routine Description:
Invalid command usage Display how to get help on command
Arguments:
BaseName - name of executable
Return Value:
none
--*/ { FormatToStream(stderr,MSG_COMMAND_USAGE,BaseName,Cmd); }
void Failure(LPCTSTR BaseName,LPCTSTR Cmd) /*++
Routine Description:
Display simple error text for general failure
Arguments:
BaseName - name of executable
Return Value:
none
--*/ { FormatToStream(stderr,MSG_FAILURE,BaseName,Cmd); }
BOOL Reboot() /*++
Routine Description:
Attempt to reboot computer
Arguments:
none
Return Value:
TRUE if API suceeded
--*/ { HANDLE Token; BOOL b; TOKEN_PRIVILEGES NewPrivileges; LUID Luid;
//
// we need to "turn on" reboot privilege
// if any of this fails, try reboot anyway
//
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&Token)) { goto final; }
if(!LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&Luid)) { CloseHandle(Token); goto final; }
NewPrivileges.PrivilegeCount = 1; NewPrivileges.Privileges[0].Luid = Luid; NewPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges( Token, FALSE, &NewPrivileges, 0, NULL, NULL );
CloseHandle(Token);
final:
//
// attempt reboot - inform system that this is planned hardware install
//
return ExitWindowsEx(EWX_REBOOT, REASON_PLANNED_FLAG|REASON_HWINSTALL); }
LPTSTR GetDeviceStringProperty(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Prop) /*++
Routine Description:
Return a string property for a device, otherwise NULL
Arguments:
Devs )_ uniquely identify device DevInfo ) Prop - string property to obtain
Return Value:
string containing description
--*/ { LPTSTR buffer; DWORD size; DWORD reqSize; DWORD dataType; DWORD szChars;
size = 1024; // initial guess
buffer = new TCHAR[(size/sizeof(TCHAR))+1]; if(!buffer) { return NULL; } while(!SetupDiGetDeviceRegistryProperty(Devs,DevInfo,Prop,&dataType,(LPBYTE)buffer,size,&reqSize)) { if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) { goto failed; } if(dataType != REG_SZ) { goto failed; } size = reqSize; delete [] buffer; buffer = new TCHAR[(size/sizeof(TCHAR))+1]; if(!buffer) { goto failed; } } szChars = reqSize/sizeof(TCHAR); buffer[szChars] = TEXT('\0'); return buffer;
failed: if(buffer) { delete [] buffer; } return NULL; }
LPTSTR GetDeviceDescription(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo) /*++
Routine Description:
Return a string containing a description of the device, otherwise NULL Always try friendly name first
Arguments:
Devs )_ uniquely identify device DevInfo )
Return Value:
string containing description
--*/ { LPTSTR desc; desc = GetDeviceStringProperty(Devs,DevInfo,SPDRP_FRIENDLYNAME); if(!desc) { desc = GetDeviceStringProperty(Devs,DevInfo,SPDRP_DEVICEDESC); } return desc; }
IdEntry GetIdType(LPCTSTR Id) /*++
Routine Description:
Determine if this is instance id or hardware id and if there's any wildcards instance ID is prefixed by '@' wildcards are '*'
Arguments:
Id - ptr to string to check
Return Value:
IdEntry
--*/ { IdEntry Entry;
Entry.InstanceId = FALSE; Entry.Wild = NULL; Entry.String = Id;
if(Entry.String[0] == INSTANCEID_PREFIX_CHAR) { Entry.InstanceId = TRUE; Entry.String = CharNext(Entry.String); } if(Entry.String[0] == QUOTE_PREFIX_CHAR) { //
// prefix to treat rest of string literally
//
Entry.String = CharNext(Entry.String); } else { //
// see if any wild characters exist
//
Entry.Wild = _tcschr(Entry.String,WILD_CHAR); } return Entry; }
LPTSTR * GetMultiSzIndexArray(LPTSTR MultiSz) /*++
Routine Description:
Get an index array pointing to the MultiSz passed in
Arguments:
MultiSz - well formed multi-sz string
Return Value:
array of strings. last entry+1 of array contains NULL returns NULL on failure
--*/ { LPTSTR scan; LPTSTR * array; int elements;
for(scan = MultiSz, elements = 0; scan[0] ;elements++) { scan += lstrlen(scan)+1; } array = new LPTSTR[elements+2]; if(!array) { return NULL; } array[0] = MultiSz; array++; if(elements) { for(scan = MultiSz, elements = 0; scan[0]; elements++) { array[elements] = scan; scan += lstrlen(scan)+1; } } array[elements] = NULL; return array; }
void DelMultiSz(LPTSTR * Array) /*++
Routine Description:
Deletes the string array allocated by GetDevMultiSz/GetRegMultiSz/GetMultiSzIndexArray
Arguments:
Array - pointer returned by GetMultiSzIndexArray
Return Value:
None
--*/ { if(Array) { Array--; if(Array[0]) { delete [] Array[0]; } delete [] Array; } }
LPTSTR * GetDevMultiSz(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Prop) /*++
Routine Description:
Get a multi-sz device property and return as an array of strings
Arguments:
Devs - HDEVINFO containing DevInfo DevInfo - Specific device Prop - SPDRP_HARDWAREID or SPDRP_COMPATIBLEIDS
Return Value:
array of strings. last entry+1 of array contains NULL returns NULL on failure
--*/ { LPTSTR buffer; DWORD size; DWORD reqSize; DWORD dataType; LPTSTR * array; DWORD szChars;
size = 8192; // initial guess, nothing magic about this
buffer = new TCHAR[(size/sizeof(TCHAR))+2]; if(!buffer) { return NULL; } while(!SetupDiGetDeviceRegistryProperty(Devs,DevInfo,Prop,&dataType,(LPBYTE)buffer,size,&reqSize)) { if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) { goto failed; } if(dataType != REG_MULTI_SZ) { goto failed; } size = reqSize; delete [] buffer; buffer = new TCHAR[(size/sizeof(TCHAR))+2]; if(!buffer) { goto failed; } } szChars = reqSize/sizeof(TCHAR); buffer[szChars] = TEXT('\0'); buffer[szChars+1] = TEXT('\0'); array = GetMultiSzIndexArray(buffer); if(array) { return array; }
failed: if(buffer) { delete [] buffer; } return NULL; }
LPTSTR * GetRegMultiSz(HKEY hKey,LPCTSTR Val) /*++
Routine Description:
Get a multi-sz from registry and return as an array of strings
Arguments:
hKey - Registry Key Val - Value to query
Return Value:
array of strings. last entry+1 of array contains NULL returns NULL on failure
--*/ { LPTSTR buffer; DWORD size; DWORD reqSize; DWORD dataType; LPTSTR * array; DWORD szChars; LONG regErr;
size = 8192; // initial guess, nothing magic about this
buffer = new TCHAR[(size/sizeof(TCHAR))+2]; if(!buffer) { return NULL; } reqSize = size; while((regErr = RegQueryValueEx(hKey,Val,NULL,&dataType,(PBYTE)buffer,&reqSize) != NO_ERROR)) { if(GetLastError() != ERROR_MORE_DATA) { goto failed; } if(dataType != REG_MULTI_SZ) { goto failed; } size = reqSize; delete [] buffer; buffer = new TCHAR[(size/sizeof(TCHAR))+2]; if(!buffer) { goto failed; } } szChars = reqSize/sizeof(TCHAR); buffer[szChars] = TEXT('\0'); buffer[szChars+1] = TEXT('\0');
array = GetMultiSzIndexArray(buffer); if(array) { return array; }
failed: if(buffer) { delete [] buffer; } return NULL; }
BOOL WildCardMatch(LPCTSTR Item,const IdEntry & MatchEntry) /*++
Routine Description:
Compare a single item against wildcard I'm sure there's better ways of implementing this Other than a command-line management tools it's a bad idea to use wildcards as it implies assumptions about the hardware/instance ID eg, it might be tempting to enumerate root\* to find all root devices, however there is a CfgMgr API to query status and determine if a device is root enumerated, which doesn't rely on implementation details.
Arguments:
Item - item to find match for eg a\abcd\c MatchEntry - eg *\*bc*\*
Return Value:
TRUE if any match, otherwise FALSE
--*/ { LPCTSTR scanItem; LPCTSTR wildMark; LPCTSTR nextWild; size_t matchlen;
//
// before attempting anything else
// try and compare everything up to first wild
//
if(!MatchEntry.Wild) { return _tcsicmp(Item,MatchEntry.String) ? FALSE : TRUE; } if(_tcsnicmp(Item,MatchEntry.String,MatchEntry.Wild-MatchEntry.String) != 0) { return FALSE; } wildMark = MatchEntry.Wild; scanItem = Item + (MatchEntry.Wild-MatchEntry.String);
for(;wildMark[0];) { //
// if we get here, we're either at or past a wildcard
//
if(wildMark[0] == WILD_CHAR) { //
// so skip wild chars
//
wildMark = CharNext(wildMark); continue; } //
// find next wild-card
//
nextWild = _tcschr(wildMark,WILD_CHAR); if(nextWild) { //
// substring
//
matchlen = nextWild-wildMark; } else { //
// last portion of match
//
size_t scanlen = lstrlen(scanItem); matchlen = lstrlen(wildMark); if(scanlen < matchlen) { return FALSE; } return _tcsicmp(scanItem+scanlen-matchlen,wildMark) ? FALSE : TRUE; } if(_istalpha(wildMark[0])) { //
// scan for either lower or uppercase version of first character
//
TCHAR u = _totupper(wildMark[0]); TCHAR l = _totlower(wildMark[0]); while(scanItem[0] && scanItem[0]!=u && scanItem[0]!=l) { scanItem = CharNext(scanItem); } if(!scanItem[0]) { //
// ran out of string
//
return FALSE; } } else { //
// scan for first character (no case)
//
scanItem = _tcschr(scanItem,wildMark[0]); if(!scanItem) { //
// ran out of string
//
return FALSE; } } //
// try and match the sub-string at wildMark against scanItem
//
if(_tcsnicmp(scanItem,wildMark,matchlen)!=0) { //
// nope, try again
//
scanItem = CharNext(scanItem); continue; } //
// substring matched
//
scanItem += matchlen; wildMark += matchlen; } return (wildMark[0] ? FALSE : TRUE); }
BOOL WildCompareHwIds(LPTSTR * Array,const IdEntry & MatchEntry) /*++
Routine Description:
Compares all strings in Array against Id Use WildCardMatch to do real compare
Arguments:
Array - pointer returned by GetDevMultiSz MatchEntry - string to compare against
Return Value:
TRUE if any match, otherwise FALSE
--*/ { if(Array) { while(Array[0]) { if(WildCardMatch(Array[0],MatchEntry)) { return TRUE; } Array++; } } return FALSE; }
int EnumerateDevices(LPCTSTR BaseName,LPCTSTR Machine,DWORD Flags,int argc,LPTSTR argv[],CallbackFunc Callback,LPVOID Context) /*++
Routine Description:
Generic enumerator for devices that will be passed the following arguments: <id> [<id>...] =<class> [<id>...] where <id> can either be @instance-id, or hardware-id and may contain wildcards <class> is a class name
Arguments:
BaseName - name of executable Machine - name of machine to enumerate Flags - extra enumeration flags (eg DIGCF_PRESENT) argc/argv - remaining arguments on command line Callback - function to call for each hit Context - data to pass function for each hit
Return Value:
EXIT_xxxx
--*/ { HDEVINFO devs = INVALID_HANDLE_VALUE; IdEntry * templ = NULL; DWORD err; int failcode = EXIT_FAIL; int retcode; int argIndex; DWORD devIndex; SP_DEVINFO_DATA devInfo; SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail; BOOL doSearch = FALSE; BOOL match; BOOL all = FALSE; GUID cls; DWORD numClass = 0; int skip = 0;
if(!argc) { return EXIT_USAGE; }
templ = new IdEntry[argc]; if(!templ) { goto final; }
//
// determine if a class is specified
//
if(argc>skip && argv[skip][0]==CLASS_PREFIX_CHAR && argv[skip][1]) { if(!SetupDiClassGuidsFromNameEx(argv[skip]+1,&cls,1,&numClass,Machine,NULL) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { goto final; } if(!numClass) { failcode = EXIT_OK; goto final; } skip++; } if(argc>skip && argv[skip][0]==WILD_CHAR && !argv[skip][1]) { //
// catch convinient case of specifying a single argument '*'
//
all = TRUE; skip++; } else if(argc<=skip) { //
// at least one parameter, but no <id>'s
//
all = TRUE; }
//
// determine if any instance id's were specified
//
// note, if =<class> was specified with no id's
// we'll mark it as not doSearch
// but will go ahead and add them all
//
for(argIndex=skip;argIndex<argc;argIndex++) { templ[argIndex] = GetIdType(argv[argIndex]); if(templ[argIndex].Wild || !templ[argIndex].InstanceId) { //
// anything other than simple InstanceId's require a search
//
doSearch = TRUE; } } if(doSearch || all) { //
// add all id's to list
// if there's a class, filter on specified class
//
devs = SetupDiGetClassDevsEx(numClass ? &cls : NULL, NULL, NULL, (numClass ? 0 : DIGCF_ALLCLASSES) | Flags, NULL, Machine, NULL);
} else { //
// blank list, we'll add instance id's by hand
//
devs = SetupDiCreateDeviceInfoListEx(numClass ? &cls : NULL, NULL, Machine, NULL); } if(devs == INVALID_HANDLE_VALUE) { goto final; } for(argIndex=skip;argIndex<argc;argIndex++) { //
// add explicit instances to list (even if enumerated all,
// this gets around DIGCF_PRESENT)
// do this even if wildcards appear to be detected since they
// might actually be part of the instance ID of a non-present device
//
if(templ[argIndex].InstanceId) { SetupDiOpenDeviceInfo(devs,templ[argIndex].String,NULL,0,NULL); } }
devInfoListDetail.cbSize = sizeof(devInfoListDetail); if(!SetupDiGetDeviceInfoListDetail(devs,&devInfoListDetail)) { goto final; }
//
// now enumerate them
//
if(all) { doSearch = FALSE; }
devInfo.cbSize = sizeof(devInfo); for(devIndex=0;SetupDiEnumDeviceInfo(devs,devIndex,&devInfo);devIndex++) {
if(doSearch) { for(argIndex=skip,match=FALSE;(argIndex<argc) && !match;argIndex++) { TCHAR devID[MAX_DEVICE_ID_LEN]; LPTSTR *hwIds = NULL; LPTSTR *compatIds = NULL; //
// determine instance ID
//
if(CM_Get_Device_ID_Ex(devInfo.DevInst,devID,MAX_DEVICE_ID_LEN,0,devInfoListDetail.RemoteMachineHandle)!=CR_SUCCESS) { devID[0] = TEXT('\0'); }
if(templ[argIndex].InstanceId) { //
// match on the instance ID
//
if(WildCardMatch(devID,templ[argIndex])) { match = TRUE; } } else { //
// determine hardware ID's
// and search for matches
//
hwIds = GetDevMultiSz(devs,&devInfo,SPDRP_HARDWAREID); compatIds = GetDevMultiSz(devs,&devInfo,SPDRP_COMPATIBLEIDS);
if(WildCompareHwIds(hwIds,templ[argIndex]) || WildCompareHwIds(compatIds,templ[argIndex])) { match = TRUE; } } DelMultiSz(hwIds); DelMultiSz(compatIds); } } else { match = TRUE; } if(match) { retcode = Callback(devs,&devInfo,devIndex,Context); if(retcode) { failcode = retcode; goto final; } } }
failcode = EXIT_OK;
final: if(templ) { delete [] templ; } if(devs != INVALID_HANDLE_VALUE) { SetupDiDestroyDeviceInfoList(devs); } return failcode;
}
int __cdecl _tmain(int argc, LPTSTR argv[]) /*++
Routine Description:
Main entry point interpret -m:<machine> and hand off execution to command
Arguments:
argc/argv - parameters passed to executable
Return Value:
EXIT_xxxx
--*/ { LPCTSTR cmd; LPCTSTR baseName; LPCTSTR machine = NULL; int dispIndex; int firstArg = 1; int retval = EXIT_USAGE; BOOL autoReboot = FALSE;
//
// syntax:
//
// [options] [-]command [<arg> [<arg>]]
//
// options:
// -m:<machine> - remote
// -r - auto reboot
//
baseName = _tcsrchr(argv[0],TEXT('\\')); if(!baseName) { baseName = argv[0]; } else { baseName = CharNext(baseName); } while((argc > firstArg) && ((argv[firstArg][0] == TEXT('-')) || (argv[firstArg][0] == TEXT('/')))) { if((argv[firstArg][1]==TEXT('m')) || (argv[firstArg][1]==TEXT('M'))) { if((argv[firstArg][2]!=TEXT(':')) || (argv[firstArg][3]==TEXT('\0'))) { //
// don't recognize this switch
//
break; } machine = argv[firstArg]+3; } else if((argv[firstArg][1]==TEXT('r')) || (argv[firstArg][1]==TEXT('R'))) { if((argv[firstArg][2]!=TEXT('\0')) ) { //
// don't recognize this switch
//
break; } else { autoReboot = TRUE; } } else { //
// don't recognize this switch
//
break; } firstArg++; }
if((argc-firstArg) < 1) { //
// after switches, must at least be command
//
Usage(baseName); return EXIT_USAGE; } cmd = argv[firstArg]; if((cmd[0]==TEXT('-')) || (cmd[0]==TEXT('/'))) { //
// command may begin '-' or '/'
// eg, people might do devcon -help
//
cmd = CharNext(cmd); } firstArg++; for(dispIndex = 0;DispatchTable[dispIndex].cmd;dispIndex++) { if(lstrcmpi(cmd,DispatchTable[dispIndex].cmd)==0) { retval = DispatchTable[dispIndex].func(baseName,machine,argc-firstArg,argv+firstArg); switch(retval) { case EXIT_USAGE: CommandUsage(baseName,DispatchTable[dispIndex].cmd); break; case EXIT_REBOOT: if(autoReboot) { Reboot(); } break; case EXIT_OK: break; default: Failure(baseName,DispatchTable[dispIndex].cmd); break; } return retval; } } Usage(baseName); return EXIT_USAGE; }
|