diff options
| author | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
| commit | 97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch) | |
| tree | 46f3701832d70b420eb0fc0eb93261f9da45db3f /setup/devcon/devcon.cpp | |
| parent | ef1905bf1e8825bb31120dfb27e0daf3154d859a (diff) | |
Initial publish
Diffstat (limited to 'setup/devcon/devcon.cpp')
| -rw-r--r-- | setup/devcon/devcon.cpp | 1162 |
1 files changed, 1162 insertions, 0 deletions
diff --git a/setup/devcon/devcon.cpp b/setup/devcon/devcon.cpp new file mode 100644 index 00000000..d501dc1e --- /dev/null +++ b/setup/devcon/devcon.cpp @@ -0,0 +1,1162 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + devcon.cpp + +Abstract: + + Device Console + command-line interface for managing devices + +--*/ + +#include "devcon.h" + +struct IdEntry { + LPCTSTR String; // string looking for + LPCTSTR Wild; // first wild character if any + BOOL InstanceId; +}; + +void FormatToStream(_In_ FILE * stream, _In_ 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) { + int c; + int back = 0; + // + // strip any trailing "\r\n"s and replace by a single "\n" + // + while(((c = *CharPrev(locbuffer,locbuffer+count)) == TEXT('\r')) || + (c == TEXT('\n'))) { + count--; + back++; + } + if(back) { + locbuffer[count++] = TEXT('\n'); + locbuffer[count] = TEXT('\0'); + } + // + // now write to apropriate stream + // + _fputts(locbuffer,stream); + } + LocalFree(locbuffer); + } +} + +void Padding(_In_ 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(_In_ LPCTSTR BaseName) +/*++ + +Routine Description: + + Display simple usage text + +Arguments: + + BaseName - name of executable + +Return Value: + + none + +--*/ +{ + FormatToStream(stderr,MSG_USAGE,BaseName); +} + +void CommandUsage(_In_ LPCTSTR BaseName, _In_ 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(_In_ LPCTSTR BaseName, _In_ 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; + 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 + // + // warning 28159 is a warning to rearchitect to avoid rebooting. However, + // sometimes during device installation, a reboot is needed, so this warning + // is being suppressed for the call to InitiateSystemShutdownEx. + // +#pragma warning( suppress: 28159) + return InitiateSystemShutdownEx(NULL, + NULL, + 0, + FALSE, + TRUE, + REASON_PLANNED_FLAG | REASON_HWINSTALL); + +} + +LPTSTR GetDeviceStringProperty(_In_ HDEVINFO Devs, _In_ PSP_DEVINFO_DATA DevInfo, _In_ 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(_In_ HDEVINFO Devs, _In_ 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(_In_ 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; +} + +__drv_allocatesMem(object) +LPTSTR * GetMultiSzIndexArray(_In_ __drv_aliasesMem 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; +} + +__drv_allocatesMem(object) +LPTSTR * CopyMultiSz(_In_opt_ PZPWSTR Array) +/*++ + +Routine Description: + + Creates a new array from old + old array need not have been allocated by GetMultiSzIndexArray + +Arguments: + + Array - array of strings, last entry is NULL + +Return Value: + + MultiSz array allocated by GetMultiSzIndexArray + +--*/ +{ + LPTSTR multiSz = NULL; + HRESULT hr; + int cchMultiSz = 0; + int c; + if(Array) { + for(c=0;Array[c];c++) { + cchMultiSz+=lstrlen(Array[c])+1; + } + } + cchMultiSz+=1; // final Null + multiSz = new TCHAR[cchMultiSz]; + if(!multiSz) { + return NULL; + } + int len = 0; + if(Array) { + for(c=0;Array[c];c++) { + hr = StringCchCopy(multiSz+len,cchMultiSz-len,Array[c]); + if(FAILED(hr)){ + if(multiSz) + delete [] multiSz; + return NULL; + } + +#pragma prefast(suppress:__WARNING_BUFFER_OVERFLOW, "ESP:732") + len+=lstrlen(multiSz+len)+1; + } + } + + if( len < cchMultiSz ){ + multiSz[len] = TEXT('\0'); + } else { + // This should never happen! + multiSz[cchMultiSz-1] = TEXT('\0'); + } + + LPTSTR * pRes = GetMultiSzIndexArray(multiSz); + if(pRes) { + return pRes; + } + delete [] multiSz; + + return NULL; +} + +void DelMultiSz(_In_opt_ __drv_freesMem(object) PZPWSTR 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; + } +} + +__drv_allocatesMem(object) +LPTSTR * GetDevMultiSz(_In_ HDEVINFO Devs, _In_ PSP_DEVINFO_DATA DevInfo, _In_ 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; +} + +__drv_allocatesMem(object) +LPTSTR * GetRegMultiSz(_In_ HKEY hKey, _In_ 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; + regErr = RegQueryValueEx(hKey,Val,NULL,&dataType,(PBYTE)buffer,&reqSize); + while((regErr != 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; + } + regErr = RegQueryValueEx(hKey,Val,NULL,&dataType,(PBYTE)buffer,&reqSize); + } + 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(_In_ LPCTSTR Item, _In_ 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 + // + + // + // the code suppresses the warning 28193 for the calls to _totupper + // and _totlower. This suppression is done because those functions + // have a check return annotation on them. However, they don't return + // error codes and the check return annotation is really being used + // to indicate that the return value of the function should be looked + // at and/or assigned to a variable. The check return annotation means + // the return value should always be checked in all code paths. + // We assign the return values to variables but the while loop does not + // examine both values in all code paths (e.g. when scanItem[0] == 0, + // neither u nor l will be examined) and it doesn't need to examine + // the values in all code paths. + // +#pragma warning( suppress: 28193) + TCHAR u = _totupper(wildMark[0]); +#pragma warning( suppress: 28193) + 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(_In_ PZPWSTR Array, _In_ 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; +} + +bool SplitCommandLine( + _In_ int & argc, + _In_reads_(argc) LPTSTR * & argv, + _Out_ int & argc_right, + _Outref_result_buffer_(argc_right) LPTSTR * & argv_right) +/*++ + +Routine Description: + + Splits a command line into left and right of := + this is used for some of the more complex commands + +Arguments: + + argc/argv - in/out + - in, specifies the existing argc/argv + - out, specifies the argc/argv to left of := + arc_right/argv_right - out + - specifies the argc/argv to right of := + +Return Value: + + true - ":=" appears in line, false otherwise + +--*/ +{ + int i; + for(i = 0;i<argc;i++) { + if(_tcsicmp(argv[i],SPLIT_COMMAND_SEP)==0) { + argc_right = argc-(i+1); + argv_right = argv+(i+1); + argc = i; + return true; + } + } + argc_right = 0; + argv_right = argv+argc; + return false; +} + +int EnumerateDevices(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PWSTR* argv, _In_ CallbackFunc Callback, _In_ 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; + 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; + + UNREFERENCED_PARAMETER(BaseName); + + 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(_In_ int argc, _In_reads_(argc) PWSTR* 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; + DWORD flags = 0; + + // + // syntax: + // + // [options] [-]command [<arg> [<arg>]] + // + // options: + // -m:<machine> - remote + // -r - auto reboot + // -f - force operation + // + 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 if((argv[firstArg][1]==TEXT('f')) || (argv[firstArg][1]==TEXT('F'))) { + if((argv[firstArg][2]!=TEXT('\0')) ) { + // + // don't recognize this switch + // + break; + } else { + flags |= DEVCON_FLAG_FORCE; + } + } 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(_tcsicmp(cmd,DispatchTable[dispIndex].cmd)==0) { + retval = DispatchTable[dispIndex].func(baseName,machine,flags,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; +} + + |
