简体   繁体   中英

How can I programmatically turn off or on 'Windows Features'

When I try to update Windows features; When I update UseShellExecute to "true"; "The Process object must have the UseShellExecute property set to false in order to redirect IO streams." I get an error. When I set it to False; Unable to update. How can I do it? Do you have any other suggestions?

 static void InstallIISSetupFeature()
        {
            var featureNames = new List<string>() {

                 "IIS-WebServerRole",
                "IIS-WebServer",
                "IIS-CommonHttpFeatures",
                "IIS-HttpErrors",
                "IIS-HttpRedirect",
                "IIS-ApplicationDevelopment",
                "IIS-Security",
                "IIS-RequestFiltering",
                "IIS-NetFxExtensibility",
                "IIS-NetFxExtensibility45",
                "IIS-HealthAndDiagnostics",
                "IIS-HttpLogging",
                "IIS-LoggingLibraries",
                "IIS-RequestMonitor",
                "IIS-HttpTracing",
                "IIS-URLAuthorization",
                "IIS-IPSecurity",
                "IIS-Performance",
                "IIS-HttpCompressionDynamic",
                "IIS-WebServerManagementTools",
                "IIS-ManagementScriptingTools",
                "IIS-IIS6ManagementCompatibility",
                "IIS-Metabase",
                "IIS-HostableWebCore","IIS-StaticContent", 
                "IIS-DefaultDocument",
                "IIS-DirectoryBrowsing",
                "IIS-WebDAV",
                "IIS-WebSockets",
                "IIS-ApplicationInit",
                "IIS-ASPNET",
                "IIS-ASPNET45",
                "IIS-ASP",
                "IIS-CGI",
                "IIS-ISAPIExtensions",
                "IIS-ISAPIFilter",
                "IIS-ServerSideIncludes",
                "IIS-CustomLogging",
                "IIS-BasicAuthentication",
                "IIS-HttpCompressionStatic",
                "IIS-ManagementConsole",
                "IIS-ManagementService",
                "IIS-WMICompatibility",
                "IIS-LegacyScripts",
                "IIS-LegacySnapIn",
                "IIS-FTPServer",
                "IIS-FTPSvc",
                "IIS-FTPExtensibility",
                "IIS-CertProvider",
                "IIS-WindowsAuthentication",
                "IIS-DigestAuthentication",
                "IIS-ClientCertificateMappingAuthentication",
                "IIS-IISCertificateMappingAuthentication",
                "IIS-ODBCLogging",
                "NetFx4-AdvSrvs",
                "NetFx4Extended-ASPNET45",
                "NetFx3",
                "WAS-WindowsActivationService",
                "WCF-HTTP-Activation",
                "WCF-HTTP-Activation45",
                "WCF-MSMQ-Activation45",
                "WCF-NonHTTP-Activation",
                "WCF-Pipe-Activation45",
                "WCF-TCP-Activation45",
                "WCF-TCP-PortSharing45",
                "WCF-Services45",
            };
               ManagementObjectSearcher obj = new ManagementObjectSearcher("select * from Win32_OperatingSystem");
                foreach (ManagementObject wmi in obj.Get())
                {
                    string Name = wmi.GetPropertyValue("Caption").ToString();
                    Name = Regex.Replace(Name.ToString(), "[^A-Za-z0-9 ]", "");
                    if (Name.Contains("Server 2008 R2") || Name.Contains("Windows 7"))
                    {
                        featureNames.Add("IIS-ASPNET");
                        featureNames.Add("IIS-NetFxExtensibility");
                        featureNames.Add("WCF-HTTP-Activation");
                        featureNames.Add("WCF-MSMQ-Activation");
                        featureNames.Add("WCF-Pipe-Activation");
                        featureNames.Add("WCF-TCP-Activation");
                        featureNames.Add("WCF-TCP-Activation");
                    }
                    string Version = (string)wmi["Version"];
                    string Architecture = (string)wmi["OSArchitecture"];
                }         
            foreach (var featureName in featureNames)
            {
                Run(string.Format("dism/online/Enable-Feature:{0}", featureName));
            }
         
        }  
        static void Run(string arguments)
        {
            try
            {
                string systemPath = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32");

                var dism = new Process();
                dism.StartInfo.WorkingDirectory = systemPath;
                dism.StartInfo.Arguments = arguments;
                dism.StartInfo.FileName = "dism.exe";
                dism.StartInfo.Verb = "runas";

                dism.StartInfo.UseShellExecute = true;
                dism.StartInfo.RedirectStandardOutput = true;
                dism.Start();
                var result = dism.StandardOutput.ReadToEnd();
                dism.WaitForExit();
            }
            catch (Exception ex)
            {
            }

        }`

I tried to update the feature with dism.exe and cmd.exe, when it gave an authorization error, I used the Verb property `

  • Since the use of .Verb = "RunAs" requires .UseShellExecute = true , and since the latter cannot be combined with RedirectStandardOutput = true , you cannot directly capture the elevated process' output in memory.

    • It seems that the system itself, by security-minded design, prevents a non-elevated process from directly capturing an elevated process' output .
  • The workaround is to launch the target executable ( dism.exe , in your case) indirectly , via a shell , and then use the latter's redirection feature ( > ) to capture the target executable's output (invariably) in a file , as shown below.

string systemPath = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32");

// Create a temp. file to capture the elevated process' output in.
string tempOutFile = Path.GetTempFileName();

var dism = new Process();
dism.StartInfo.WorkingDirectory = systemPath;
// Use cmd.exe as the executable, and pass it a command line via /c
dism.StartInfo.FileName = "cmd.exe" ;
// Use a ">" redirection to capture the elevated process' output.
// Use "2> ..." to also capture *stderr* output.
// Append "2>&1" to capture *both* stdout and stderr in the file targeted with ">"
dism.StartInfo.Arguments = 
  String.Format(
    "/c {0} {1} > \"{2}\"", 
    "dism.exe", arguments, tempOutFile
  );
dism.StartInfo.Verb = "RunAs";
dism.StartInfo.UseShellExecute = true;

dism.Start();
dism.WaitForExit();
// Read the temp. file in which the output was captured...
var result = File.ReadAllText(tempOutFile);
// ... and delete it.
File.Delete(tempOutFile);

First, you can use WindowsPrincipal::IsInRole() to check if you're running elevated. See Microsoft Learn for details.

Second, this may be one of those cases where using native PS is easier than the cmdlet approach (admittedly, still not great).

If the script is supposed to run on clients as well as server operating systems: use Get-WmiObject or Get-CimInstance to get a reference to what you're running on. ActiveDirectory also has that information (in operatingSystem attribute).

For servers use Get-WindowsFeature in ServerManager module. For clients use Get-WindowsOptionalFeature with switch -Online in DISM module which, if you indeed need to support OSes older than 6.3.xxxx, can be copied over from a machine that has it and added to $Env:Path before C:\Windows and C:\Windows\System32.

For either platform just pass the list of features to configure.

If in a (binary) cmdlet you have to call external tools then the advantage of them is mostly gone. It may be possible to access Windows CBS using a managed API to avoid this but even then the script based approach gets more results faster, especially since you can just just put together a quick wrapper around dism.exe .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM