/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: misc.c Abstract: This module contains miscellaneous Configuration Manager API routines. CM_Get_Version CM_Is_Version_Available CM_Connect_Machine CM_Disconnect_Machine CM_Get_Global_State CM_Run_Detection CM_Query_Arbitrator_Free_Data CM_Query_Resource_Conflicts CM_Query_Arbitrator_Free_Size CMP_Report_LogOn CMP_Init_Detection CMP_WaitServicesAvailable CMP_WaitNoPendingInstallEvents CMP_GetBlockedDriverInfo Author: Paula Tomlinson (paulat) 6-20-1995 Environment: User mode only. Revision History: 20-Jun-1995 paulat Creation and initial implementation. --*/ // // includes // #include "precomp.h" #pragma hdrstop #include "cfgi.h" #include "pnpipc.h" // // global data // extern PVOID hLocalStringTable; // NOT MODIFIED BY THESE PROCEDURES extern WCHAR LocalMachineNameNetBIOS[]; // NOT MODIFIED BY THESE PROCEDURES extern WCHAR LocalMachineNameDnsFullyQualified[];// NOT MODIFIED BY THESE PROCEDURES #define NUM_LOGON_RETRIES 30 // // Private prototypes // CONFIGRET IsRemoteServiceRunning( IN LPCWSTR UNCServerName, IN LPCWSTR ServiceName ); WORD CM_Get_Version_Ex( IN HMACHINE hMachine ) /*++ Routine Description: This routine retrieves the version number of the Configuration Manager APIs. Arguments: hMachine - Machine handle returned from CM_Connect_Machine or NULL. Return value: The function returns the major revision number in the high byte and the minor revision number in the low byte. For example, version 4.0 of Configuration Manager returns 0x0400. --*/ { CONFIGRET Status = CR_SUCCESS; WORD wVersion = (WORD)CFGMGR32_VERSION; handle_t hBinding = NULL; // // setup rpc binding handle // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return wVersion = 0; } // // No special privileges are required by the server // RpcTryExcept { // // call rpc service entry point // Status = PNP_GetVersion( hBinding, // rpc machine name &wVersion); // server side version } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_WARNINGS, "PNP_GetVersion caused an exception (%d)\n", RpcExceptionCode())); SetLastError(RpcExceptionCode()); wVersion = 0; } RpcEndExcept return wVersion; } // CM_Get_Version_Ex BOOL CM_Is_Version_Available_Ex( IN WORD wVersion, IN HMACHINE hMachine ) /*++ Routine Description: This routine returns whether a specific version of the Configuration Manager APIs are available. Arguments: wVersion - Version to query. hMachine - Machine to connect to. Return value: The function returns TRUE if the version of the Configuration Manager APIs is equal to or greater than the specified version. --*/ { handle_t hBinding = NULL; WORD wVersionInternal; // // version 0x0400 is available on all servers, by definition. // if (wVersion <= (WORD)0x0400) { return TRUE; } // // setup rpc binding handle // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { return FALSE; } // // retrieve the internal server version. // if (!PnPGetVersion(hMachine, &wVersionInternal)) { return FALSE; } // // versions up to and including the internal server version are available. // return (wVersion <= wVersionInternal); } // CM_Is_Version_Available_Ex CONFIGRET CM_Connect_MachineW( IN PCWSTR UNCServerName, OUT PHMACHINE phMachine ) /*++ Routine Description: This routine connects to the machine specified and returns a handle that is then passed to future calls to the Ex versions of the CM routines. This allows callers to get device information on remote machines. Arguments: UNCServerName - Specifies the UNC name of the remote machine to connect to. phMachine - Specifies the address of a variable to receive a handle to the connected machine. Return value: If the function succeeds, it returns CR_SUCCESS, otherwise it returns one of the CR_* error codes. --*/ { CONFIGRET Status = CR_SUCCESS; WORD wVersion = 0, wVersionInternal; PPNP_MACHINE pMachine = NULL; size_t UNCServerNameLen; try { // // validate parameters // if (!ARGUMENT_PRESENT(phMachine)) { Status = CR_INVALID_POINTER; goto Clean0; } *phMachine = NULL; // // if machine name specified, check for UNC format // if ((ARGUMENT_PRESENT(UNCServerName)) && (UNCServerName[0] != L'\0')) { // // Check that the length in not any longer than the longest possible // name we can possibly save, including the NULL terminating // character. // if (FAILED(StringCchLength( UNCServerName, MAX_PATH + 3, &UNCServerNameLen))) { Status = CR_INVALID_MACHINENAME; goto Clean0; } ASSERT(UNCServerNameLen < (MAX_PATH + 3)); // // Check that the machine name is a UNC name. // if ((UNCServerNameLen < 3) || (UNCServerName[0] != L'\\') || (UNCServerName[1] != L'\\')) { Status = CR_INVALID_MACHINENAME; goto Clean0; } } // // allocate memory for the machine structure and initialize it // pMachine = (PPNP_MACHINE)pSetupMalloc(sizeof(PNP_MACHINE)); if(!pMachine) { Status = CR_OUT_OF_MEMORY; goto Clean0; } if ((!ARGUMENT_PRESENT(UNCServerName)) || (UNCServerName[0] == L'\0') || (!lstrcmpi(UNCServerName, LocalMachineNameNetBIOS)) || (!lstrcmpi(UNCServerName, LocalMachineNameDnsFullyQualified))) { //---------------------------------------------------------- // If no machine name was passed in or the machine name // matches the local name, use local machine info rather // than creating a new binding. //---------------------------------------------------------- PnPGetGlobalHandles(NULL, &pMachine->hStringTable, &pMachine->hBindingHandle); if (!ARGUMENT_PRESENT(UNCServerName)) { if (FAILED(StringCchCopy( pMachine->szMachineName, SIZECHARS(pMachine->szMachineName), LocalMachineNameNetBIOS))) { Status = CR_INVALID_MACHINENAME; goto Clean0; } } else { if (FAILED(StringCchCopy( pMachine->szMachineName, SIZECHARS(pMachine->szMachineName), UNCServerName))) { Status = CR_INVALID_MACHINENAME; goto Clean0; } } } else { // // First, make sure that the RemoteRegistry service is running on // the remote machine, since RemoteRegistry is required for several // cfgmgr32/setupapi services. // Status = IsRemoteServiceRunning(UNCServerName, L"RemoteRegistry"); if (Status != CR_SUCCESS) { goto Clean0; } //------------------------------------------------------------- // A remote machine name was specified so explicitly force a // new binding for this machine. //------------------------------------------------------------- pMachine->hBindingHandle = (PVOID)PNP_HANDLE_bind((PNP_HANDLE)UNCServerName); if (pMachine->hBindingHandle == NULL) { if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) { Status = CR_OUT_OF_MEMORY; } else if (GetLastError() == ERROR_INVALID_COMPUTERNAME) { Status = CR_INVALID_MACHINENAME; } else { Status = CR_FAILURE; } goto Clean0; } // // initialize a string table for use with this connection to // the remote machine // pMachine->hStringTable = pSetupStringTableInitialize(); if (pMachine->hStringTable == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } // // Add a priming string (see dll entrypt in main.c for details) // pSetupStringTableAddString(pMachine->hStringTable, PRIMING_STRING, STRTAB_CASE_SENSITIVE); // // save the machine name // if (FAILED(StringCchCopy( pMachine->szMachineName, SIZECHARS(pMachine->szMachineName), UNCServerName))) { Status = CR_INVALID_MACHINENAME; goto Clean0; } } // // test the binding by calling the simplest RPC call (good way // for the caller to know whether the service is actually // running) // // // No special privileges are required by the server // RpcTryExcept { // // call rpc service entry point // Status = PNP_GetVersion( pMachine->hBindingHandle, &wVersion); } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_WARNINGS, "PNP_GetVersion caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept if (Status == CR_SUCCESS) { // // we got the standard version alright, now try to determine the // internal version of the server. initialize the version supplied // to the internal version of the client. // wVersionInternal = (WORD)CFGMGR32_VERSION_INTERNAL; // // No special privileges are required by the server // RpcTryExcept { // // call rpc service entry point // Status = PNP_GetVersionInternal( pMachine->hBindingHandle, &wVersionInternal); } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { // // rpc exception may occur if the interface does not exist on // the server, indicating a server version prior to NT 5.1. // KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_WARNINGS, "PNP_GetVersionInternal caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept if (Status == CR_SUCCESS) { // // PNP_GetVersionInternal exists on NT 5.1 and later. // ASSERT(wVersionInternal >= (WORD)0x0501); // // use the real internal version of the server instead of the // static version returned by PNP_GetVersion. // wVersion = wVersionInternal; } // // no matter what happened while trying to retrieve the internal // version of the server, we were successful before this. // Status = CR_SUCCESS; } if (Status == CR_SUCCESS) { pMachine->ulSignature = (ULONG)MACHINE_HANDLE_SIGNATURE; pMachine->wVersion = wVersion; *phMachine = (HMACHINE)pMachine; } Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; // // Reference the following variables so the compiler will respect // statement ordering w.r.t. their assignment. // pMachine = pMachine; } if ((Status != CR_SUCCESS) && (pMachine != NULL)) { pSetupFree(pMachine); } return Status; } // CM_Connect_MachineW CONFIGRET CM_Disconnect_Machine( IN HMACHINE hMachine ) /*++ Routine Description: This routine disconnects from a machine that was previously connected to with the CM_Connect_Machine call. Arguments: hMachine - Specifies a machine handle previously returned by a call to CM_Connect_Machine. Return value: If the function succeeds, it returns CR_SUCCESS, otherwise it returns one of the CR_* error codes. --*/ { CONFIGRET Status = CR_SUCCESS; PPNP_MACHINE pMachine = NULL; try { // // validate parameters // if (hMachine == NULL) { Status = CR_INVALID_POINTER; goto Clean0; } pMachine = (PPNP_MACHINE)hMachine; if (pMachine->ulSignature != (ULONG)MACHINE_HANDLE_SIGNATURE) { Status = CR_INVALID_POINTER; goto Clean0; } // // only free the machine info if it's not the local machine // if (pMachine->hStringTable != hLocalStringTable) { // // free the rpc binding for this remote machine // PNP_HANDLE_unbind((PNP_HANDLE)pMachine->szMachineName, (handle_t)pMachine->hBindingHandle); // // release the string table // pSetupStringTableDestroy(pMachine->hStringTable); } // // invalidate the signature so we never try to free it again. // pMachine->ulSignature = 0; // // free the memory for the PNP_MACHINE struct // pSetupFree(pMachine); Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Disconnect_Machine CONFIGRET CM_Get_Global_State_Ex( OUT PULONG pulState, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine retrieves the global state of the configuration manager. Parameters: pulState Supplies the address of the variable that receives the Configuration Manager’s state. May be a combination of the following values: Configuration Manager Global State Flags: CM_GLOBAL_STATE_CAN_DO_UI Can UI be initiated? [TBD: On NT, this may relate to whether anyone is logged in] CM_GLOBAL_STATE_SERVICES_AVAILABLE Are the CM APIs available? (on Windows NT this is always set) CM_GLOBAL_STATE_SHUTTING_DOWN The Configuration Manager is shutting down. [TBD: Does this only happen at shutdown/restart time?] CM_GLOBAL_STATE_DETECTION_PENDING The Configuration Manager is about to initiate some sort of detection. Windows 95 also defines the following additional flag: CM_GLOBAL_STATE_ON_BIG_STACK [TBD: What should this be defaulted to for NT?] ulFlags Not used, must be zero. Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is a CR error code. --*/ { CONFIGRET Status = CR_SUCCESS; handle_t hBinding = NULL; try { // // validate parameters // if (!ARGUMENT_PRESENT(pulState)) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, 0)) { Status = CR_INVALID_FLAG; goto Clean0; } // // setup rpc binding handle (don't need string table handle) // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // No special privileges are required by the server // RpcTryExcept { // // call rpc service entry point // Status = PNP_GetGlobalState( hBinding, // rpc binding handle pulState, // returns global state ulFlags); // not used } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_GetGlobalState caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Get_Global_State_Ex CONFIGRET CM_Run_Detection_Ex( IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine loads and executes a detection module. Parameters: ulFlags - Specifies the reason for the detection. Can be one of the following values: Detection Flags: CM_DETECT_NEW_PROFILE - Run detection for a new hardware profile. CM_DETECT_CRASHED - Previously attempted detection crashed. (Windows 95 defines the following two unused flags as well: CM_DETECT_HWPROF_FIRST_BOOT and CM_DETECT_RUN.) Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is CR_INVALID_FLAG. --*/ { CONFIGRET Status = CR_SUCCESS; handle_t hBinding = NULL; HANDLE hToken; ULONG ulPrivilege; try { // // validate parameters // if (INVALID_FLAGS(ulFlags, CM_DETECT_BITS)) { Status = CR_INVALID_FLAG; goto Clean0; } // // setup rpc binding handle (don't need string table handle) // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // Enable privileges required by the server // ulPrivilege = SE_LOAD_DRIVER_PRIVILEGE; hToken = PnPEnablePrivileges(&ulPrivilege, 1); RpcTryExcept { // // call rpc service entry point // Status = PNP_RunDetection( hBinding, ulFlags); // not used } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_RunDetection caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept // // Restore previous privileges // PnPRestorePrivileges(hToken); Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Run_Detection_Ex CONFIGRET CM_Query_Arbitrator_Free_Data_Ex( OUT PVOID pData, IN ULONG DataLen, IN DEVINST dnDevInst, IN RESOURCEID ResourceID, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine returns information about available resources of a particular type. If the given size is not large enough, this API truncates the data and returns CR_BUFFER_SMALL. To determine the buffer size needed to receive all the available resource information, use the CM_Query_Arbitrator_Free_Size API. Parameters: pData Supplies the address of the buffer that receives information on the available resources for the resource type specified by ResourceID. DataLen Supplies the size, in bytes, of the data buffer. dnDevNode Supplies the handle of the device instance associated with the arbitrator. This is only meaningful for local arbitrators--for global arbitrators, specify the root device instance or NULL. On Windows NT, this parameter must specify either the Root device instance or NULL. ResourceID Supplies the type of the resource. Can be one of the ResType values listed in Section 2.1.2.1.. (This API returns CR_INVALID_RESOURCEID if this value is ResType_All or ResType_None.) ulFlags Must be zero. Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is one of the following: CR_BUFFER_SMALL, CR_FAILURE, CR_INVALID_DEVNODE, CR_INVALID_FLAG, CR_INVALID_POINTER, or CR_INVALID_RESOURCEID. (Windows 95 may also return CR_NO_ARBITRATOR.) --*/ { CONFIGRET Status = CR_SUCCESS; WCHAR pDeviceID [MAX_DEVICE_ID_LEN]; PVOID hStringTable = NULL; handle_t hBinding = NULL; ULONG ulLen = MAX_DEVICE_ID_LEN; BOOL Success; try { // // validate parameters // if (dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } if ((!ARGUMENT_PRESENT(pData)) || (DataLen == 0)) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, CM_QUERY_ARBITRATOR_BITS)) { Status = CR_INVALID_FLAG; goto Clean0; } if (ResourceID > ResType_MAX && ResourceID != ResType_ClassSpecific) { Status = CR_INVALID_RESOURCEID; goto Clean0; } // // setup rpc binding handle (don't need string table handle) // if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // retrieve the device instance ID string associated with the devinst // Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,pDeviceID,&ulLen); if (Success == FALSE || INVALID_DEVINST(pDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } // // No special privileges are required by the server // RpcTryExcept { // // call rpc service entry point // Status = PNP_QueryArbitratorFreeData( hBinding, pData, DataLen, pDeviceID, ResourceID, ulFlags); // not used } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_QueryArbitratorFreeData caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Query_Arbitrator_Free_Data_Ex #if 0 CONFIGRET WINAPI CM_Query_Resource_Conflicts_Ex( IN DEVINST dnDevInst, IN RESOURCEID ResourceID, IN PCVOID ResourceData, IN ULONG ResourceLen, IN OUT PVOID pData, IN OUT PULONG pulDataLen, IN ULONG ulFlags ) /*++ Routine Description: This routine returns a list of devnodes that own resources that would conflict with the specified resource. If there are no conflicts, the returned list is NULL. If the caller supplied buffer is not large enough, CR_BUFFER_SMALL is returned and pulDataLen contains the required buffer size. Parameters: dnDevInst Supplies the handle of the device instance associated with the arbitrator. This is only meaningful for local arbitrators--for global arbitrators, specify the root device instance or NULL. On Windows NT, this parameter must specify either the Root device instance or NULL. ??? ResourceID Supplies the type of the resource. Can be one of the ResType values listed in Section 2.1.2.1.. (This API returns CR_INVALID_RESOURCEID if this value is ResType_All or ResType_None.) ResourceData Supplies the adress of an IO_DES, MEM_DES, DMA_DES, IRQ_DES, etc, structure, depending on the given resource type. ResourceLen Supplies the size, in bytes, of the structure pointed to by ResourceData. pData Supplies the address of the buffer that receives information on the available resources for the resource type specified by ResourceID. pulDataLen Supplies the size, in bytes, of the data buffer. ulFlags Must be zero. Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is one of the following: CR_BUFFER_SMALL, CR_FAILURE, CR_INVALID_DEVNODE, CR_INVALID_FLAG, CR_INVALID_POINTER, or CR_INVALID_RESOURCEID. (Windows 95 may also return CR_NO_ARBITRATOR.) --*/ { CONFIGRET Status = CR_SUCCESS; WCHAR pDeviceID [MAX_DEVICE_ID_LEN]; PVOID hStringTable = NULL; handle_t hBinding = NULL; ULONG ulLen = MAX_DEVICE_ID_LEN; BOOL Success; try { // // validate parameters // if (dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } if ((!ARGUMENT_PRESENT(pData)) || (!ARGUMENT_PRESENT(pulDataLen)) || (*pulDataLen == 0)) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, CM_QUERY_ARBITRATOR_BITS)) { Status = CR_INVALID_FLAG; goto Clean0; } if (ResourceID > ResType_MAX && ResourceID != ResType_ClassSpecific) { Status = CR_INVALID_RESOURCEID; goto Clean0; } // // setup rpc binding handle (don't need string table handle) // if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // retrieve the device instance ID string associated with the devinst // Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,pDeviceID,&ulLen); if (Success == FALSE || INVALID_DEVINST(pDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } // // No special privileges are required by the server // RpcTryExcept { // // call rpc service entry point // Status = PNP_QueryArbitratorFreeData( hBinding, pData, DataLen, pDeviceID, ResourceID, ulFlags); // not used } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_QueryArbitratorFreeData caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Query_Resource_Conflicts_Ex #endif CONFIGRET CM_Query_Arbitrator_Free_Size_Ex( OUT PULONG pulSize, IN DEVINST dnDevInst, IN RESOURCEID ResourceID, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine retrieves the size of the available resource information that would be returned in a call to the CM_Query_Arbitrator_Free_Data API. Parameters: pulSize Supplies the address of the variable that receives the size, in bytes, that is required to hold the available resource information. dnDevNode Supplies the handle of the device instance associated with the arbitrator. This is only meaningful for local arbitrators--for global arbitrators, specify the root device instance or NULL. On Windows NT, this parameter must specify either the Root device instance or NULL. ResourceID Supplies the type of the resource. Can be one of the ResType values listed in Section 2.1.2.1.. (This API returns CR_INVALID_RESOURCEID if this value is ResType_All or ResType_None.) ulFlags CM_QUERY_ARBITRATOR_BITS. Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is one of the following: CR_FAILURE, CR_INVALID_DEVNODE, CR_INVALID_FLAG, CR_INVALID_POINTER, or CR_INVALID_RESOURCEID. (Windows 95 may also return CR_NO_ARBITRATOR.) --*/ { CONFIGRET Status = CR_SUCCESS; WCHAR pDeviceID [MAX_DEVICE_ID_LEN]; PVOID hStringTable = NULL; handle_t hBinding = NULL; ULONG ulLen = MAX_DEVICE_ID_LEN; BOOL Success; try { // // validate parameters // if (dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } if (!ARGUMENT_PRESENT(pulSize)) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, CM_QUERY_ARBITRATOR_BITS)) { Status = CR_INVALID_FLAG; goto Clean0; } if (ResourceID > ResType_MAX && ResourceID != ResType_ClassSpecific) { Status = CR_INVALID_RESOURCEID; goto Clean0; } // // setup rpc binding handle (don't need string table handle) // if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // retrieve the device instance ID string associated with the devinst // Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,pDeviceID,&ulLen); if (Success == FALSE || INVALID_DEVINST(pDeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } // // No special privileges are required by the server // RpcTryExcept { // // call rpc service entry point // Status = PNP_QueryArbitratorFreeSize( hBinding, pulSize, pDeviceID, ResourceID, ulFlags); // not used } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_QueryArbitratorFreeSize caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Query_Arbitrator_Free_Size_Ex //------------------------------------------------------------------- // Private CM routines //------------------------------------------------------------------- CONFIGRET CMP_Report_LogOn( IN ULONG ulPrivateID, IN DWORD ProcessID ) /*++ Routine Description: This is a private routine to notify the PlugPlay service that an interactive user has logged in. It is currently only called by userinit.exe. Parameters: ulPrivateID - Specifies the value of the private id. ProcessID - Specifies the process id of the calling process. Currently, this routine is only called by userinit.exe. Return Value: If the function succeeds, the return value is CR_SUCCESS. If the function fails, the return value is one of the following: CR_FAILURE, CR_INVALID_DATA --*/ { CONFIGRET Status = CR_SUCCESS; handle_t hBinding = NULL; DWORD Retries = 0; UNREFERENCED_PARAMETER(ProcessID); try { // // validate parameters // if (ulPrivateID != 0x07020420) { Status = CR_INVALID_DATA; goto Clean0; } // // setup rpc binding handle (don't need string table handle) // this is always to the local server, by definition // if (!PnPGetGlobalHandles(NULL, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } for (Retries = 0; Retries < NUM_LOGON_RETRIES; Retries++) { RpcTryExcept { // // call rpc service entry point // Status = PNP_ReportLogOn( hBinding, // rpc binding handle FALSE, // Is Admin? (not used by server) GetCurrentProcessId()); // calling process id } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_ReportLogOn caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept if ((Status == CR_NO_CM_SERVICES) || (Status == CR_REMOTE_COMM_FAILURE)) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PlugPlay services not available (%d), retrying...\n", Status)); // // This is some error related to the service not being // available, wait and try again. // Sleep(5000); continue; } else { // // Some other error, the service may never be available so bail // out now. // break; } } Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CMP_Report_LogOn CONFIGRET CMP_Init_Detection( IN ULONG ulPrivateID ) { CONFIGRET Status = CR_SUCCESS; handle_t hBinding = NULL; HANDLE hToken; ULONG ulPrivilege; try { // // validate parameters // if (ulPrivateID != 0x07020420) { Status = CR_INVALID_DATA; goto Clean0; } // // setup rpc binding handle (don't need string table handle) // this is always to the local server, by definition // if (!PnPGetGlobalHandles(NULL, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // Enable privileges required by the server // ulPrivilege = SE_LOAD_DRIVER_PRIVILEGE; hToken = PnPEnablePrivileges(&ulPrivilege, 1); RpcTryExcept { // // call rpc service entry point // Status = PNP_InitDetection( hBinding); // rpc binding handle } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_InitDetection caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept // // Restore previous privileges // PnPRestorePrivileges(hToken); Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CMP_Init_Detection CONFIGRET CMP_WaitServicesAvailable( IN HMACHINE hMachine ) /*++ Routine Description: This routine determines whether the user-mode pnp manager server side (umpnpmgr) is up and running yet (providing the PNP_Xxx side of the config manager apis). Parameters: hMachine - private (opaque) cm machine handle. If NULL, the call refers to the local machine. Return Value: If the service is avialable upon return then CR_SUCCESS is returned. If some other failure occurs, CR_FAILURE is returned. --*/ { CONFIGRET Status = CR_NO_CM_SERVICES; handle_t hBinding = NULL; WORD wVersion; try { // // setup rpc binding handle (don't need string table handle) // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } while ((Status == CR_NO_CM_SERVICES) || (Status == CR_MACHINE_UNAVAILABLE) || (Status == CR_REMOTE_COMM_FAILURE)) { // // No special privileges are required by the server // RpcTryExcept { // // call rpc service entry point // Status = PNP_GetVersion( hBinding, // rpc machine name &wVersion); // server size version } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_WARNINGS, "PNP_GetVersion caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept if (Status == CR_SUCCESS) { // // Service is avilable now, return success. // goto Clean0; } if ((Status == CR_NO_CM_SERVICES) || (Status == CR_MACHINE_UNAVAILABLE) || (Status == CR_REMOTE_COMM_FAILURE)) { // // This is some error related to the service not being // available, wait and try again. // Sleep(5000); } else { // // Some other error, the service may never be avaiable // so bail out now. // Status = CR_FAILURE; goto Clean0; } } Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CMP_WaitServicesAvailable DWORD CMP_WaitNoPendingInstallEvents( IN DWORD dwTimeout ) /*++ Routine Description: This routine waits until there are no pending device install events. If a timeout value is specified then it will return either when no install events are pending or when the timeout period has expired, whichever comes first. This routine is intended to be called after user-logon, only. NOTE: New install events can occur at anytime, this routine just indicates that there are no install events at this moment. Parameters: dwTimeout - Specifies the time-out interval, in milliseconds. The function returns if the interval elapses, even if there are still pending install events. If dwTimeout is zero, the function just tests whether there are pending install events and returns immediately. If dwTimeout is INFINITE, the function's time-out interval never elapses. Return Value: If the function succeeds, the return value indicates the event that caused the function to return. If the function fails, the return value is WAIT_FAILED. To get extended error information, call GetLastError. The return value on success is one of the following values: WAIT_ABANDONED The specified object is a mutex object that was not released by the thread that owned the mutex object before the owning thread terminated. Ownership of the mutex object is granted to the calling thread, and the mutex is set to nonsignaled. WAIT_OBJECT_0 The state of the specified object is signaled. WAIT_TIMEOUT The time-out interval elapsed, and the object's state is nonsignaled. --*/ { DWORD Status = WAIT_FAILED; HANDLE hEvent = NULL; try { hEvent = OpenEvent(SYNCHRONIZE, FALSE, PNP_NO_INSTALL_EVENTS); if (hEvent == NULL) { Status = WAIT_FAILED; } else { Status = WaitForSingleObject(hEvent, dwTimeout); } } except(EXCEPTION_EXECUTE_HANDLER) { Status = WAIT_FAILED; } if (hEvent) { CloseHandle(hEvent); } return Status; } // CMP_WaitNoPendingInstallEvents CONFIGRET CMP_GetBlockedDriverInfo( OUT LPBYTE Buffer, IN OUT PULONG pulLength, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine retrieves the list of drivers that have been blocked from loading on the system since boot. Arguments: Buffer - Supplies the address of the buffer that receives the list of drivers that have been blocked from loading on the system. Can be NULL when simply retrieving data size. pulLength - Supplies the address of the variable that contains the size, in bytes, of the buffer. If the variable is initially zero, the API replaces it with the buffer size needed to receive all the data. In this case, the Buffer parameter is ignored. ulFlags - Must be zero. hMachine - Machine handle returned from CM_Connect_Machine or NULL. Return value: If the function succeeds, it returns CR_SUCCESS, otherwise it returns one of the CR_* error codes. --*/ { CONFIGRET Status; ULONG ulTransferLen; BYTE NullBuffer = 0; handle_t hBinding = NULL; try { // // validate parameters // if (!ARGUMENT_PRESENT(pulLength)) { Status = CR_INVALID_POINTER; goto Clean0; } if ((!ARGUMENT_PRESENT(Buffer)) && (*pulLength != 0)) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, 0)) { Status = CR_INVALID_FLAG; goto Clean0; } // // setup rpc binding handle (don't need string table handle) // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // NOTE: The ulTransferLen variable is just used to control // how much data is marshalled via rpc between address spaces. // ulTransferLen should be set on entry to the size of the Buffer. // The last parameter should also be the size of the Buffer on entry // and on exit contains either the amount transferred (if a transfer // occured) or the amount required, this value should be passed back // in the callers pulLength parameter. // ulTransferLen = *pulLength; if (!ARGUMENT_PRESENT(Buffer)) { Buffer = &NullBuffer; } // // No special privileges are required by the server // RpcTryExcept { // // call rpc service entry point // Status = PNP_GetBlockedDriverInfo( hBinding, // rpc binding handle Buffer, // receives blocked driver info &ulTransferLen, // input/output buffer size pulLength, // bytes copied (or bytes required) ulFlags); // not used } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_WARNINGS, "PNP_GetBlockedDriverInfo caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CMP_GetBlockedDriverInfo CONFIGRET CMP_GetServerSideDeviceInstallFlags( IN PULONG pulSSDIFlags, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: This routine retrieves the server side device install flags. Arguments: pulSSDIFlags - Pointer to a ULONG that receives the server side device install flags. ulFlags - Must be zero. hMachine - Machine handle returned from CM_Connect_Machine or NULL. Return value: If the function succeeds, it returns CR_SUCCESS, otherwise it returns one of the CR_* error codes. --*/ { CONFIGRET Status; handle_t hBinding = NULL; try { // // validate parameters // if (!ARGUMENT_PRESENT(pulSSDIFlags)) { Status = CR_INVALID_POINTER; goto Clean0; } if (INVALID_FLAGS(ulFlags, 0)) { Status = CR_INVALID_FLAG; goto Clean0; } // // setup rpc binding handle (don't need string table handle) // if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // No special privileges are required by the server // RpcTryExcept { // // call rpc service entry point // Status = PNP_GetServerSideDeviceInstallFlags( hBinding, // rpc binding handle pulSSDIFlags, // receives server side device install flags ulFlags); // not used } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_WARNINGS, "PNP_GetServerSideDeviceInstallFlags caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CMP_GetServerSideDeviceInstallFlags //------------------------------------------------------------------- // Local Stubs //------------------------------------------------------------------- WORD CM_Get_Version( VOID ) { return CM_Get_Version_Ex(NULL); } BOOL CM_Is_Version_Available( IN WORD wVersion ) { return CM_Is_Version_Available_Ex(wVersion, NULL); } CONFIGRET CM_Get_Global_State( OUT PULONG pulState, IN ULONG ulFlags ) { return CM_Get_Global_State_Ex(pulState, ulFlags, NULL); } CONFIGRET CM_Query_Arbitrator_Free_Data( OUT PVOID pData, IN ULONG DataLen, IN DEVINST dnDevInst, IN RESOURCEID ResourceID, IN ULONG ulFlags ) { return CM_Query_Arbitrator_Free_Data_Ex(pData, DataLen, dnDevInst, ResourceID, ulFlags, NULL); } CONFIGRET CM_Query_Arbitrator_Free_Size( OUT PULONG pulSize, IN DEVINST dnDevInst, IN RESOURCEID ResourceID, IN ULONG ulFlags ) { return CM_Query_Arbitrator_Free_Size_Ex(pulSize, dnDevInst, ResourceID, ulFlags, NULL); } CONFIGRET CM_Run_Detection( IN ULONG ulFlags ) { return CM_Run_Detection_Ex(ulFlags, NULL); } //------------------------------------------------------------------- // ANSI Stubs //------------------------------------------------------------------- CONFIGRET CM_Connect_MachineA( IN PCSTR UNCServerName, OUT PHMACHINE phMachine ) { CONFIGRET Status = CR_SUCCESS; PWSTR pUniName = NULL; if ((!ARGUMENT_PRESENT(UNCServerName)) || (UNCServerName[0] == '\0')) { // // no explicit name specified, so assume local machine and // nothing to translate // Status = CM_Connect_MachineW(pUniName, phMachine); } else if (pSetupCaptureAndConvertAnsiArg(UNCServerName, &pUniName) == NO_ERROR) { Status = CM_Connect_MachineW(pUniName, phMachine); pSetupFree(pUniName); } else { Status = CR_INVALID_DATA; } return Status; } // CM_Connect_MachineA