diff --git a/HidUtil/HidUtil.vcxproj b/HidUtil/HidUtil.vcxproj index a7c56586..802848ad 100644 --- a/HidUtil/HidUtil.vcxproj +++ b/HidUtil/HidUtil.vcxproj @@ -1,5 +1,6 @@ + Debug @@ -120,12 +121,26 @@ + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/HidUtil/HidUtil.vcxproj.filters b/HidUtil/HidUtil.vcxproj.filters index b75e5b0d..aa873169 100644 --- a/HidUtil/HidUtil.vcxproj.filters +++ b/HidUtil/HidUtil.vcxproj.filters @@ -2,9 +2,15 @@ + + + + + + \ No newline at end of file diff --git a/HidUtil/Main.cpp b/HidUtil/Main.cpp index 9bbb8890..24b59662 100644 --- a/HidUtil/Main.cpp +++ b/HidUtil/Main.cpp @@ -1,18 +1,46 @@ #include "HID.hpp" #include "TailLight.hpp" +#include "Wbem.h" +#define SWITCH_BIST "/bist" +#define CB_SWITCH_BIST_ONLY (sizeof(SWITCH_BIST) - 1) + +void PrintUsage() { + wprintf(L"IntelliMouse tail-light shifter .\n"); + wprintf(L"Usage:\n\"HidUtil.exe [/bist] [ \"" + " (example: \"HidUtil.exe 0 0 255\" or\n \"HidUtil.exe /bist\"]).\n"); +} int main(int argc, char* argv[]) { - if (argc < 4) { - wprintf(L"IntelliMouse tail-light shifter.\n"); - wprintf(L"Usage: \"HidUtil.exe \" (example: \"HidUtil.exe 0 0 255\").\n"); + + BYTE red = 0; + BYTE green = 0; + BYTE blue = 0; + + if (argc == 2 && lstrlenA(argv[1]) == CB_SWITCH_BIST_ONLY) { + if (memcmp(argv[1], SWITCH_BIST, CB_SWITCH_BIST_ONLY) == 0) { + if (SUCCEEDED(ConnectToWbem())) { + return 0; + } + else { + return -4; + } + } + else { + PrintUsage(); + return -1; + } + } + else if (argc == 4) { + red = (BYTE)atoi(argv[1]); + green = (BYTE)atoi(argv[2]); + blue = (BYTE)atoi(argv[3]); + } + else { + PrintUsage(); return -1; } - auto red = (BYTE)atoi(argv[1]); - auto green = (BYTE)atoi(argv[2]); - auto blue = (BYTE)atoi(argv[3]); - HID::Query query; query.VendorID = 0x045E; // Microsoft query.ProductID = 0x082A; // Pro IntelliMouse diff --git a/HidUtil/Wbem.h b/HidUtil/Wbem.h new file mode 100644 index 00000000..90dcdc9f --- /dev/null +++ b/HidUtil/Wbem.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include +#include +#include + +HRESULT +ConnectToWbem(); + +HRESULT +SetInterfaceSecurity( + _In_ IUnknown* InterfaceObj, + _In_opt_ PWSTR UserId, + _In_opt_ PWSTR Password, + _In_opt_ PWSTR DomainName +); + +HRESULT +ExecuteMethod_NoArgs_ReturnsValue( + _In_ winrt::com_ptr* pWbemServices, + _In_ winrt::com_ptr* pClassObj, + _In_ const BSTR InstancePath, + _In_ const OLECHAR* psz, + _Out_ HRESULT& wmiMethodRet +); + +HRESULT +ExecuteBISTOnAllDevices( + _In_ winrt::com_ptr* pWbemServices, + _In_opt_ PWSTR UserId, + _In_opt_ PWSTR Password, + _In_opt_ PWSTR DomainName +); \ No newline at end of file diff --git a/HidUtil/WbemCore.cpp b/HidUtil/WbemCore.cpp new file mode 100644 index 00000000..cabbb7f1 --- /dev/null +++ b/HidUtil/WbemCore.cpp @@ -0,0 +1,322 @@ +#include "Wbem.h" +#include "roapi.h" +#include + +#pragma comment(lib, "comsupp") +#pragma comment(lib, "wbemuuid") + +// Blatantly stolen from the Toaster sample + +HRESULT ConnectToWbem() { + HRESULT status = S_OK; + BOOLEAN initialized = FALSE; + + BSTR temp = NULL; + BSTR wmiRoot = NULL; + BSTR userIdString = NULL; + BSTR passwordString = NULL; + + PWSTR ComputerName = NULL; + PWSTR userId = NULL; + PWSTR password = NULL; + PWSTR domain = NULL; + + winrt::com_ptr wbemLocator; + winrt::com_ptr wbemServices; + + // + // Initialize COM environment for multi-threaded concurrency. + // + status = Windows::Foundation::Initialize(RO_INIT_MULTITHREADED); + if (FAILED(status)) { + return status; + } + + initialized = TRUE; + + // + // Initialize the security layer and set the specified values as the + // security default for the process. + // + status = CoInitializeSecurity(NULL, + -1, + NULL, + NULL, + RPC_C_AUTHN_LEVEL_PKT, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_NONE, + 0); + + if (FAILED(status)) { + goto exit; + } + + // + // Create a single uninitialized object associated with the class id + // CLSID_WbemLocator. + // + status = CoCreateInstance(CLSID_WbemLocator, + NULL, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, + wbemLocator.put_void()); + + if (FAILED(status)) { + goto exit; + } + + // + // Construct the object path for the WMI namespace. For local access to the + // WMI namespace, use a simple object path: "\\.\root\WMI". For access to + // the WMI namespace on a remote computer, include the computer name in the + // object path: "\\myserver\root\WMI". + // + if (ComputerName != NULL) { + + status = VarBstrCat(_bstr_t(L"\\\\"), _bstr_t(ComputerName), &temp); + if (FAILED(status)) { + goto exit; + } + + } + else { + + status = VarBstrCat(_bstr_t(L"\\\\"), _bstr_t(L"."), &temp); + if (FAILED(status)) { + goto exit; + } + } + + status = VarBstrCat(temp, _bstr_t(L"\\root\\WMI"), &wmiRoot); + if (FAILED(status)) { + goto exit; + } + + SysFreeString(temp); + temp = NULL; + + // + // Construct the user id and password strings. + // + if (userId != NULL) { + + if (domain != NULL) { + + status = VarBstrCat(_bstr_t(domain), _bstr_t(L"\\"), &temp); + if (FAILED(status)) { + goto exit; + } + + status = VarBstrCat(temp, _bstr_t(userId), &userIdString); + if (FAILED(status)) { + goto exit; + } + + SysFreeString(temp); + temp = NULL; + + } + else { + + userIdString = SysAllocString(userId); + if (userIdString == NULL) { + status = E_OUTOFMEMORY; + goto exit; + } + } + + passwordString = SysAllocString(password); + if (passwordString == NULL) { + status = E_OUTOFMEMORY; + goto exit; + } + } + + // + // Connect to the WMI server on this computer and, possibly, through it to another system. + // + status = wbemLocator->ConnectServer(wmiRoot, + userIdString, + passwordString, + NULL, + 0, + NULL, + NULL, + wbemServices.put()); + if (FAILED(status)) { + if (status != WBEM_E_LOCAL_CREDENTIALS) { + goto exit; + } + + // + // Use the identity inherited from the current process. + // + status = wbemLocator->ConnectServer(wmiRoot, + NULL, + NULL, + NULL, + 0, + NULL, + NULL, + wbemServices.put()); + if (FAILED(status)) { + goto exit; + } + } + + // + // Set authentication information for the interface. + // + status = SetInterfaceSecurity(wbemServices.get(), userId, password, domain); + if (FAILED(status)) { + goto exit; + } + + // + // Execute the methods in each instance of the desired class. + // + printf("\n1. Execute Methods in class ...\n"); + status = ExecuteBISTOnAllDevices(&wbemServices, + userId, + password, + domain); + if (FAILED(status)) { + goto exit; + } + +exit: + + if (temp != NULL) { + SysFreeString(temp); + } + + if (userIdString != NULL) { + SysFreeString(userIdString); + } + + if (passwordString != NULL) { + SysFreeString(passwordString); + } + + if (wmiRoot != NULL) { + SysFreeString(wmiRoot); + } + + if (initialized == TRUE) { + CoUninitialize(); + } + + if (FAILED(status)) { + printf("FAILED with Status = 0x%08x\n", status); + } + return status; +} + +HRESULT +SetInterfaceSecurity( + _In_ IUnknown* InterfaceObj, + _In_opt_ PWSTR UserId, + _In_opt_ PWSTR Password, + _In_opt_ PWSTR DomainName +) + +/*++ + +Routine Description: + + Set the interface security to allow the server to impersonate the specified + user. + +Arguments: + + InterfaceObj - Pointer to interface for which the security settings need + to be applied. + + UserId - Pointer to the user id information or NULL. + + Password - Pointer to password or NULL. If the user id is not specified, + this parameter is ignored. + + DomainName - Pointer to domain name or NULL. If the user id is not specified, + this parameter is ignored. + +Return Value: + + HRESULT Status code. + +--*/ + +{ + HRESULT hr; + + COAUTHIDENTITY AuthIdentity; + DWORD AuthnSvc; + DWORD AuthzSvc; + DWORD AuthnLevel; + DWORD ImpLevel; + DWORD Capabilities; + PWSTR pServerPrinName = NULL; + RPC_AUTH_IDENTITY_HANDLE pAuthHndl = NULL; + + // + // Get current authentication information for interface. + // + hr = CoQueryProxyBlanket(InterfaceObj, + &AuthnSvc, + &AuthzSvc, + &pServerPrinName, + &AuthnLevel, + &ImpLevel, + &pAuthHndl, + &Capabilities); + + if (FAILED(hr)) { + goto exit; + } + + if (UserId == NULL) { + + AuthIdentity.User = NULL; + AuthIdentity.UserLength = 0; + AuthIdentity.Password = NULL; + AuthIdentity.PasswordLength = 0; + AuthIdentity.Domain = NULL; + AuthIdentity.DomainLength = 0; + + } + else { + + AuthIdentity.User = (USHORT*)UserId; +#pragma prefast(suppress:6387, "0 length UserId is valid") + AuthIdentity.UserLength = (ULONG)wcslen(UserId); + AuthIdentity.Password = (USHORT*)Password; +#pragma prefast(suppress:6387, "0 length Password is valid") + AuthIdentity.PasswordLength = (ULONG)wcslen(Password); + AuthIdentity.Domain = (USHORT*)DomainName; + AuthIdentity.DomainLength = (DomainName == NULL) ? 0 : (ULONG)wcslen(DomainName); + + } + + AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; + + // + // Change authentication information for interface, providing the identity + // information and "echoing back" everything else. + // + hr = CoSetProxyBlanket(InterfaceObj, + AuthnSvc, + AuthzSvc, + pServerPrinName, + AuthnLevel, + ImpLevel, + &AuthIdentity, + Capabilities); + + if (FAILED(hr)) { + goto exit; + } + +exit: + return hr; +} \ No newline at end of file diff --git a/HidUtil/WbemExecute.cpp b/HidUtil/WbemExecute.cpp new file mode 100644 index 00000000..3da148d4 --- /dev/null +++ b/HidUtil/WbemExecute.cpp @@ -0,0 +1,185 @@ +#include "wbem.h" +#include + +#define TAILLIGHT_WMI_BIST_CLASS L"TailLightBIST" +#define TAILLIGHT_WMI_BIST_METHOD L"BIST" + +// Blatantly stolen from the Toaster samples. +// Just like most other driver code. +// From WmiExecute.cpp +// Updated to use WinRT and RAII + +using namespace winrt; + +HRESULT +ExecuteMethod_NoArgs_ReturnsValue( + _In_ com_ptr* pWbemServices, + _In_ com_ptr* pClassObj, + _In_ const BSTR InstancePath, + _In_ const OLECHAR* psz, + _Out_ HRESULT& wmiMethodRet) +{ + HRESULT status; + + com_ptr inputParamsObj; + com_ptr resultControlObj; + + const BSTR methodName = SysAllocString(psz); + VARIANT retVal; + + // + // Get the input parameters class objects for the method. + // + status = (*pClassObj)->GetMethod(methodName, 0, inputParamsObj.put(), NULL); + if (FAILED(status)) { + goto exit; + } + + // + // Set the output variables values (i.e., return value for BIST). + // + retVal.vt = VT_I4; + retVal.ulVal = wmiMethodRet; + + // + // Call the method. + // + printf("\n"); + printf(" Start waiting for method : %ws to finish.\n", (wchar_t*)methodName); + status = (*pWbemServices)->ExecMethod(InstancePath, + methodName, + 0, + NULL, + NULL, + NULL, + resultControlObj.put()); + + if (FAILED(status) || NULL == resultControlObj) { + goto exit; + } + + // + // Get the result of the method call. + // + status = resultControlObj->GetCallStatus(4 * 1000, &wmiMethodRet); + printf(" Return value...: 0x%x\n", wmiMethodRet); +exit: + + if (methodName != NULL) { + SysFreeString(methodName); + } + + return status; +} + + +HRESULT +ExecuteBISTOnAllDevices( + _In_ com_ptr* pWbemServices, + _In_opt_ PWSTR UserId, + _In_opt_ PWSTR Password, + _In_opt_ PWSTR DomainName +) +{ + HRESULT status = S_OK; + + com_ptr enumerator; + com_ptr classObj; + com_ptr instanceObj; + + const BSTR className = SysAllocString(TAILLIGHT_WMI_BIST_CLASS); + + VARIANT pathVariable; + _bstr_t instancePath; + ULONG nbrObjsSought = 1; + ULONG nbrObjsReturned; + + VariantInit(&pathVariable); + + // + // Create an Enumeration object to enumerate the instances of the given class. + // + status = (*pWbemServices)->CreateInstanceEnum(className, + WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, + NULL, + enumerator.put()); + if (FAILED(status)) { + goto exit; + } + + // + // Set authentication information for the interface. + // + status = SetInterfaceSecurity(enumerator.get(), UserId, Password, DomainName); + if (FAILED(status)) { + goto exit; + } + + // + // Get the class object for the method definition. + // + status = (*pWbemServices)->GetObject(className, 0, NULL, classObj.put(), NULL); + if (FAILED(status) || NULL == classObj) { + goto exit; + } + + do { + + // + // Get the instance object for each instance of the class. + // + status = enumerator->Next(WBEM_INFINITE, + nbrObjsSought, + instanceObj.put(), + &nbrObjsReturned); + + if (status == WBEM_S_FALSE) { + status = S_OK; + break; + } + + if (FAILED(status)) { + if (status == WBEM_E_INVALID_CLASS) { + printf("ERROR: TailLight driver may not be active on the system.\n"); + } + goto exit; + } + + // + // To obtain the object path of the object for which the method has to be + // executed, query the "__PATH" property of the WMI instance object. + // + + + status = instanceObj->Get(_bstr_t(L"__PATH"), 0, &pathVariable, NULL, NULL); + if (FAILED(status)) { + goto exit; + } + + instancePath = pathVariable.bstrVal; + printf("Instance Path .: %ws\n", (wchar_t*)instancePath); + + HRESULT hr = E_FAIL; + + // + // Execute the methods in this instance of the class. + // + status = ExecuteMethod_NoArgs_ReturnsValue(pWbemServices, + &classObj, + instancePath, + TAILLIGHT_WMI_BIST_METHOD, + hr); + if (FAILED(status)) { + goto exit; + } + + } while (!FAILED(status)); + +exit: + + if (className != NULL) { + SysFreeString(className); + } + + return status; +} \ No newline at end of file diff --git a/HidUtil/packages.config b/HidUtil/packages.config new file mode 100644 index 00000000..7a9561b2 --- /dev/null +++ b/HidUtil/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file