/* * WmiSettingsHandler * * Copyright 2018 Raising the Floor - US * * Licensed under the New BSD license. You may not use this file except in * compliance with this License. * * You may obtain a copy of the License at * https://github.com/GPII/universal/blob/master/LICENSE.txt */ using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Linq; using System.Management; /// /// Static class to hold constants related with WMI API. /// public static class WMIConstants { /// /// Dictionary holding a map from the string types to proper C# types to use as /// return values from WMI set methods. /// /// The existence of this types is not optional because the method "InvokeMethod" used /// to call the target class method returns a "object" type. In order to properly /// recover the inner type, we need to know which is the return type of the calling function. /// public static readonly Dictionary RetTypes = new Dictionary { { "int", typeof(int) }, { "uint", typeof(uint) }, { "string", typeof(string) } }; /// /// If the desired WMI method is too complex for being call using the standard provied interface, /// or if it's return type can't be mapped to a simple value. Then we should wrap the target /// WMI function into a simpler interface that can be exposed using the common interface. /// public static readonly Dictionary> CustomQuerys = new Dictionary> { }; }; /// /// Provides an entry point to make a generic query updating some WMI setting through edge.js. /// public class Startup { public static int ExpectedArgNum = 5; /// /// Verifies that the input payload has a supported format. The valid input payload formats are: /// - /// /// /// public void checkInput(object input) { if (input == null) { throw new ArgumentException("Error: Payload was null."); } bool invalidFormat = true; bool notSupportedRetType = true; bool nonSupportedWrapper = false; try { object[] payload = ((IEnumerable)input).Cast().ToArray(); if (payload.Length == ExpectedArgNum) { var wmiNamespace = payload[0] as string; var className = payload[1] as string; var methodName = payload[2] as string; var notNullMethodArgs = payload[3] as IEnumerable != null; var notNullWmiNamespace = wmiNamespace != null; var notNullClassName = className != null; var notNullMehtodName = methodName != null; // Check that payload return type spec has the correct format. var retTypeSpec = payload[4] as object[]; var retType = retTypeSpec[0] as string; var successVal = retTypeSpec[1]; var notNullRetTypeSpec = retType != null && successVal != null; var correctFormat = notNullWmiNamespace && notNullClassName && notNullMehtodName && notNullMethodArgs && notNullRetTypeSpec; if (correctFormat) { invalidFormat = false; notSupportedRetType = WMIConstants.RetTypes.ContainsKey(retType) == false; if (className == "CustomWMIWrapper") { nonSupportedWrapper = WMIConstants.CustomQuerys.ContainsKey(methodName); } } } } catch (Exception e) { throw; } if (invalidFormat) { throw new ArgumentException("Error: Payload hasn't the proper specified format."); } if (notSupportedRetType) { throw new ArgumentException("Error: Payload specified 'resType' is not supported."); } if (nonSupportedWrapper) { throw new ArgumentException("Error: Supplied 'CustomWMIWrapper' is not supported"); } } /// /// Gets the default value for the supplied specified "Type". /// /// The 'Type' which default value is being requested /// The default value for the supplied type. public static object GetDefault(Type type) { if(type.IsValueType) { return Activator.CreateInstance(type); } return null; } /// /// Method that serves as an entry point to edge.js. /// /// /// The payload that holds the information to construct a proper WMI query to /// update a setting. /// /// The payload should be an array, holding the following values in each index: /// - 0: {String} The WMI namespace in which the class we are targetting is located. /// - 1: {String} The WMI class name which holds the desired method. /// - 2: {String} The WMI method name that is going to be called. /// - 3: {[Object]} The parameters that are going to be supplied to the selected method. /// - 4: {[Object]} Array of two objects: /// - First: The type of the return method, to recover it from the opaque type 'object' /// returned by 'InvokeMethod'. /// - Second: The value standing for success that the selected function should return. /// /// P.E: /// [ /// "className": "WmiClassName", /// "methodName": "WmiClassMethodName", /// "params": ["MethodParameters"], /// "returnVal": ["ReturnType", "ExpectedResult"] /// ] /// /// /// /// A error type of kind [result, error]. public async Task Invoke(object input) { checkInput(input); object[] payload = ((IEnumerable)input).Cast().ToArray(); var wmiNamespace = payload[0] as string; var wmiClassName = payload[1] as string; var wmiMethodName = payload[2] as string; var wmiMethodArgs = payload[3] as object[]; var returnTypeSpec = payload[4] as object[]; // Define scope (namespace) ManagementScope s = new ManagementScope(wmiNamespace); // Define query SelectQuery q = new SelectQuery(wmiClassName); ManagementObjectSearcher mos = new ManagementObjectSearcher(s, q); ManagementObjectCollection moc = mos.Get(); var retType = WMIConstants.RetTypes[returnTypeSpec[0] as string]; var firstObject = moc.OfType().FirstOrDefault(); if (wmiClassName == "CustomWMIWrapper") { int result = WMIConstants.CustomQuerys[wmiMethodName].Invoke(wmiMethodArgs); if (result != 0) { throw new SystemException("Error: Trying to set setting failed with error code - '" + result + "'"); } } else { var result = firstObject.InvokeMethod(wmiMethodName, wmiMethodArgs) ?? GetDefault(retType); var converted = Convert.ChangeType(returnTypeSpec[1], retType); if (converted.Equals(result) == false) { throw new SystemException("Error: Trying to set setting failed with error code - '" + result + "'"); } } return true; } }