Sample Code

Windows Driver Samples/ Device Console (DevCon) Tool/ C++/ cmds.cpp/

/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    devcon.cpp

Abstract:

    Device Console
    command-line interface for managing devices

--*/

#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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Machine);
    UNREFERENCED_PARAMETER(Flags);

    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);
                fputs("\n",stdout);
            }
        }
    }
    return EXIT_OK;
}

int cmdClasses(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(BaseName);
    UNREFERENCED_PARAMETER(Flags);
    UNREFERENCED_PARAMETER(argc);
    UNREFERENCED_PARAMETER(argv);

    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)) {
            if (FAILED(StringCchCopy(className,MAX_CLASS_NAME_LEN,TEXT("?")))) {
                goto final;
            }
        }
        if(!SetupDiGetClassDescriptionEx(&guids[index],classDesc,LINE_LEN,NULL,Machine,NULL)) {
            if (FAILED(StringCchCopy(classDesc,LINE_LEN,className))) {
                goto final;
            }
        }
        _tprintf(TEXT("%-20s: %s\n"),className,classDesc);
    }

    failcode = EXIT_OK;

final:

    if(guids) {
        delete [] guids;
    }
    return failcode;
}

int cmdListClass(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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

--*/
{
    DWORD reqGuids = 16;
    int argIndex;
    int failcode = EXIT_FAIL;
    LPGUID guids = NULL;
    HDEVINFO devs = INVALID_HANDLE_VALUE;

    UNREFERENCED_PARAMETER(BaseName);
    UNREFERENCED_PARAMETER(Flags);

    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)) {
                if (FAILED(StringCchCopy(className,MAX_CLASS_NAME_LEN,TEXT("?")))) {
                    goto final;
                }
            }
            if(!SetupDiGetClassDescriptionEx(&guids[index],classDesc,LINE_LEN,NULL,Machine,NULL)) {
                if (FAILED(StringCchCopy(classDesc,LINE_LEN,className))) {
                    goto final;
                }
            }

            //
            // 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(_In_ HDEVINFO Devs, _In_ PSP_DEVINFO_DATA DevInfo, _In_ DWORD Index, _In_ 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;

    UNREFERENCED_PARAMETER(Index);

    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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Flags);

    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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Flags);

    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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Flags);

    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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Flags);

    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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Flags);

    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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Flags);

    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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Flags);

    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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Flags);

    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(_In_ HDEVINFO Devs, _In_ PSP_DEVINFO_DATA DevInfo, _In_ DWORD Index, _In_ 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;

    UNREFERENCED_PARAMETER(Index);

    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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Flags);

    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_FIND_TAIL_NONE_LOCAL);
        } 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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Flags);

    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_FIND_TAIL_NONE_LOCAL);
        } 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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Flags);

    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_FIND_TAIL_NONE_LOCAL);
        } 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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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

--*/
{
    UNREFERENCED_PARAMETER(BaseName);
    UNREFERENCED_PARAMETER(Flags);
    UNREFERENCED_PARAMETER(argc);
    UNREFERENCED_PARAMETER(argv);

    if(Machine) {
        //
        // must be local machine
        //
        return EXIT_USAGE;
    }
    FormatToStream(stdout,MSG_REBOOT);
    return Reboot() ? EXIT_OK : EXIT_FAIL;
}


int cmdUpdate(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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];

    UNREFERENCED_PARAMETER(BaseName);
    UNREFERENCED_PARAMETER(Flags);

    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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR argv[])
/*++

Routine Description:

    CREATE
    Creates a root enumerated devnode and installs drivers on it

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];
    int failcode = EXIT_FAIL;
    LPCTSTR hwid = NULL;
    LPCTSTR inf = NULL;

    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));
    if (FAILED(StringCchCopy(hwIdList,LINE_LEN,hwid))) {
        goto final;
    }

    //
    // 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,Flags,argc,argv);

final:

    if (DeviceInfoSet != INVALID_HANDLE_VALUE) {
        SetupDiDestroyDeviceInfoList(DeviceInfoSet);
    }

    return failcode;
}

int cmdUpdateNI(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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,Flags,argc,argv);
    }
    SetNIFn = (SetupSetNonInteractiveModeProto)GetProcAddress(setupapiMod,SETUPSETNONINTERACTIVEMODE);
    if(!SetNIFn)
    {
        FreeLibrary(setupapiMod);
        return cmdUpdate(BaseName,Machine,Flags,argc,argv);
    }
    prev = SetNIFn(TRUE);
    res = cmdUpdate(BaseName,Machine,Flags,argc,argv);
    SetNIFn(prev);
    FreeLibrary(setupapiMod);
    return res;
}

int RemoveCallback(_In_ HDEVINFO Devs, _In_ PSP_DEVINFO_DATA DevInfo, _In_ DWORD Index, _In_ 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];
    SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;

    UNREFERENCED_PARAMETER(Index);

    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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Flags);

    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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(BaseName);
    UNREFERENCED_PARAMETER(Flags);
    UNREFERENCED_PARAMETER(argc);
    UNREFERENCED_PARAMETER(argv);

    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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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 = 0;
    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;

    UNREFERENCED_PARAMETER(BaseName);
    UNREFERENCED_PARAMETER(Flags);

    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
        //
        SCMHandle = OpenSCManager(Machine, NULL, GENERIC_READ);
        if(!SCMHandle) {
            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;
        }
        _Analysis_assume_(mark < cnt);
        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]) {
            size_t len = 0;
            LPTSTR multiSz = multiVal[-1];
            LPTSTR p = multiSz;
            while(*p) {
                p+=lstrlen(p)+1;
            }
            p++; // skip past null
            len = (p-multiSz)*sizeof(TCHAR);
            if(len > DWORD_MAX) {
                goto final;
            }
            LONG err = RegSetValueEx(hk,regval,0,REG_MULTI_SZ,(LPBYTE)multiSz,(DWORD)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(_In_ HDEVINFO Devs, _In_ PSP_DEVINFO_DATA DevInfo, _In_ DWORD Index, _In_ 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

--*/
{
    SetHwidContext *pControlContext = (SetHwidContext*)Context;
    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];
    SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;

    UNREFERENCED_PARAMETER(Index);

    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]) {
            size_t 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(len > DWORD_MAX) {
                result = EXIT_FAIL;
                goto final;
            }
            if(!SetupDiSetDeviceRegistryProperty(Devs,
                                                 DevInfo,
                                                 pControlContext->prop,
                                                 (LPBYTE)multiSz,
                                                 (DWORD)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(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR 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;

    UNREFERENCED_PARAMETER(Flags);

    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;
}

int cmdDPAdd(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR argv[])
/*++

Routine Description:
    dp_add
    Add a driver package to the machine.

Arguments:

    BaseName  - name of executable
    Machine   - machine name, must be NULL
    argc/argv - remaining parameters

Return Value:

    EXIT_xxxx

--*/
{
    int failcode = EXIT_FAIL;
    DWORD res;
    TCHAR SourceInfFileName[MAX_PATH];
    TCHAR DestinationInfFileName[MAX_PATH];
    PTSTR DestinationInfFileNameComponent = NULL;
    PTSTR FilePart = NULL;

    UNREFERENCED_PARAMETER(BaseName);
    UNREFERENCED_PARAMETER(Machine);
    UNREFERENCED_PARAMETER(Flags);

    if(!argc) {
        return EXIT_USAGE;
    }

    res = GetFullPathName(argv[0],
                          ARRAYSIZE(SourceInfFileName),
                          SourceInfFileName,
                          &FilePart);
    if ((!res) || (res >= ARRAYSIZE(SourceInfFileName))) {
        FormatToStream(stdout,MSG_DPADD_INVALID_INF);
        goto final;
    }

    if (!SetupCopyOEMInf(SourceInfFileName,
                         NULL,
                         SPOST_PATH,
                         0,
                         DestinationInfFileName,
                         ARRAYSIZE(DestinationInfFileName),
                         NULL,
                         &DestinationInfFileNameComponent)) {
        FormatToStream(stdout,MSG_DPADD_FAILED);
        goto final;
    }

    //
    // Successfully added the driver package to the machine.
    //
    FormatToStream(stdout,MSG_DPADD_SUCCESS,DestinationInfFileNameComponent);
    failcode = EXIT_OK;

final:

    return failcode;
}

int cmdDPDelete(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR argv[])
/*++

Routine Description:
    dp_delete
    Deletes a driver package to the machine.

Arguments:

    BaseName  - name of executable
    Machine   - machine name, must be NULL
    argc/argv - remaining parameters

Return Value:

    EXIT_xxxx

--*/
{
    int failcode = EXIT_FAIL;
    DWORD res;
    TCHAR InfFileName[MAX_PATH];
    PTSTR FilePart = NULL;
    HMODULE setupapiMod = NULL;
    SetupUninstallOEMInfProto SUOIFn;

    UNREFERENCED_PARAMETER(BaseName);
    UNREFERENCED_PARAMETER(Machine);

    if(!argc) {
        return EXIT_USAGE;
    }

    res = GetFullPathName(argv[0],
                          ARRAYSIZE(InfFileName),
                          InfFileName,
                          &FilePart);
    if ((!res) || (!FilePart)) {
        FormatToStream(stdout,MSG_DPADD_INVALID_INF);
        goto final;
    }

    setupapiMod = LoadLibrary(TEXT("setupapi.dll"));
    if(!setupapiMod) {
        goto final;
    }
    SUOIFn = (SetupUninstallOEMInfProto)GetProcAddress(setupapiMod,SETUPUNINSTALLOEMINF);
    if(!SUOIFn)
    {
        goto final;
    }

    if (!SUOIFn(FilePart,
                ((Flags & DEVCON_FLAG_FORCE) ? 1 : 0),
                NULL)) {
        if (GetLastError() == ERROR_INF_IN_USE_BY_DEVICES) {
            FormatToStream(stdout,MSG_DPDELETE_FAILED_IN_USE);
        } else if (GetLastError() == ERROR_NOT_AN_INSTALLED_OEM_INF) {
            FormatToStream(stdout,MSG_DPDELETE_FAILED_NOT_OEM_INF);
        } else {
            FormatToStream(stdout,MSG_DPDELETE_FAILED);
        }
        goto final;
    }

    //
    // Successfully added the driver package to the machine.
    //
    FormatToStream(stdout,MSG_DPDELETE_SUCCESS,FilePart);
    failcode = EXIT_OK;

final:
    if (setupapiMod) {
        FreeLibrary(setupapiMod);
    }

    return failcode;
}

int cmdDPEnumLegacy(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR argv[])
/*++

Routine Description:
    dp_enumLegacy
    Enumerates installed Driver Packages on the machine pre Windows Longhorn

Arguments:

    BaseName  - name of executable
    Machine   - machine name, must be NULL
    argc/argv - remaining parameters

Return Value:

    EXIT_xxxx

--*/
{
    int failcode = EXIT_FAIL;
    TCHAR FindName[MAX_PATH];
    HANDLE hFind = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATA wfd;

    UNREFERENCED_PARAMETER(BaseName);
    UNREFERENCED_PARAMETER(Machine);
    UNREFERENCED_PARAMETER(Flags);
    UNREFERENCED_PARAMETER(argv);

    if(argc) {
        return EXIT_USAGE;
    }

    if (!GetWindowsDirectory(FindName, ARRAYSIZE(FindName)) ||
        FAILED(StringCchCat(FindName, ARRAYSIZE(FindName), TEXT("\\INF\\OEM*.INF")))) {
        goto final;
    }

    hFind = FindFirstFile(FindName, &wfd);
    if (hFind == INVALID_HANDLE_VALUE) {
        //
        // No OEM driver packages on this machine.
        //
        FormatToStream(stdout,MSG_DPENUM_NO_OEM_INF);
        failcode = EXIT_OK;
        goto final;
    }

    FormatToStream(stdout,MSG_DPENUM_LIST_HEADER);

    do {
        FormatToStream(stdout,MSG_DPENUM_LIST_ENTRY,wfd.cFileName);
        DumpDriverPackageData(wfd.cFileName);
    } while (FindNextFile(hFind, &wfd));

    FindClose(hFind);

    failcode = EXIT_OK;

final:

    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("dp_add"),       cmdDPAdd,       MSG_DPADD_SHORT,       MSG_DPADD_LONG },
    { TEXT("dp_delete"),    cmdDPDelete,    MSG_DPDELETE_SHORT,    MSG_DPDELETE_LONG },
    { TEXT("dp_enum"),      cmdDPEnumLegacy,MSG_DPENUM_SHORT,      MSG_DPENUM_LONG },
    { TEXT("?"),            cmdHelp,        0,                     0 },
    { NULL,NULL }
};



Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS