C# -- Windows Server 2012 installing Features and Roles using Powershell - c#

I have written an application that installs Windows Roles and Features using the Powershell API. It works just fine in Windows 2008 R2, but nothing happens in Windows 2012; the program just moves on as if everything happened just fine, but nothing is installed.
I've tried making the program .NET v4.5 (it was .NET v2.0), but that didn't help. I've been all over Google about this and I can't find a solution that works. In fact, most say to use the sort of implementation that works in Windows 2008 R2. Here is my code:
public bool runPowerShell(string command, string args)
{
mLogger myLogger = mLogger.instance; //How I log stuff in my application.
bool done = false; //default Return value.
const string path = #"C:\\XMPLogs\\Roles and Features"; //Where Powershell output will go.
//Make sure Powershell log directory is there.
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
//Start a new Powershell instance.
PowerShell powershell = PowerShell.Create();
System.Collections.ObjectModel.Collection<PSObject> output = new System.Collections.ObjectModel.Collection<PSObject>();
StringBuilder strBuilder = new StringBuilder(); //Used to examine results (for testing)
powershell.AddScript(#"Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted");
powershell.AddScript(#"Import-Module ServerManager");
//powershell.Invoke();
powershell.AddScript(command + " " + args);
try
{
output = powershell.Invoke();
// Construct a StringBuilder to examine the output of Invoke()
foreach (PSObject obj in output)
strBuilder.AppendLine(obj.ToString());
// Show the StringBuilder to see results (always empty!)
MessageBox.Show(strBuilder.ToString());
done = true;
}
catch (Exception ex)
{
string test = ex.ToString();
MessageBox.Show(test);
myLogger.output("ERRO", "PowerShell command " + command
+ "failed to run with arguments \"" + args + "\". Message: " + ex.ToString());
done = false;
}
powershell.Dispose();
return done;
}
I would call the method like this:
runPowerShell("add-windowsfeature", "-name FS-FileServer -logpath \"c:\\XMPLogs\\Roles and Features\\File Services.log\"");
The "output" object never has any data in it nor does the log file. So, I have no idea what is going on. I do know if I take the two parameters in the method call and enter them into a Powershell prompt manually, the install runs flawlessly.
Can anyone see what I'm doing wrong with this implementation on Windows Server 2012?
Thank you.

It's hard to know what is the real problem here, without more information. Perhaps you're not running your application as an administrator and therefore aren't allowed to add windows features?
However, PowerShell differs between terminating errors (which would block the execution and throw an exception, which should make your code enter the catch statement) and non-terminating errors (which are just written to the error stream and will not enter your catch statement).
You can read more about this if you run Get-Help Write-Error, Get-Help about_Throw and Get-Help about_Try_Catch_Finally.
I'm guessing your powershell command results in a non-terminating error. To find out whether a non terminating error has occured or not, you could check the powershell.HadErrors property and to get the error messages you can read the powershell.Streams.Error property.
This should probably help you in finding out what errors are occuring and hopefully help you solve your problem.

Related

Executing elevated powershell scripts from C# in .NET Core 3.0

I'm calling a self-elevating powershell script from C# code. The Script resets DNS Settings.
The script works fine when called from unelevated powershell, but takes no effect when called from C# code with no exceptions thrown.
My Execution policy is temporarily set on unrestricted and I'm running Visual Studio as Admin.
Does anyone know what's wrong?
The C#:
class Program
{
static void Main(string[] args)
{
var pathToScript = #"C:\Temp\test.ps1";
Execute(pathToScript);
Console.ReadKey();
}
public static void Execute(string command)
{
using (var ps = PowerShell.Create())
{
var results = ps.AddScript(command).Invoke();
foreach (var result in results)
{
Console.WriteLine(result.ToString());
}
}
}
}
The script:
# Get the ID and security principal of the current user account
$myWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent();
$myWindowsPrincipal = New-Object System.Security.Principal.WindowsPrincipal($myWindowsID);
# Get the security principal for the administrator role
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator;
# Check to see if we are currently running as an administrator
if ($myWindowsPrincipal.IsInRole($adminRole))
{
# We are running as an administrator, so change the title and background colour to indicate this
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)";
$Host.UI.RawUI.BackgroundColor = "DarkBlue";
Clear-Host;
}
else {
# We are not running as an administrator, so relaunch as administrator
# Create a new process object that starts PowerShell
$newProcess = New-Object System.Diagnostics.ProcessStartInfo "PowerShell";
# Specify the current script path and name as a parameter with added scope and support for scripts with spaces in it's path
$newProcess.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'"
# Indicate that the process should be elevated
$newProcess.Verb = "runas";
# Start the new process
[System.Diagnostics.Process]::Start($newProcess);
# Exit from the current, unelevated, process
Exit;
}
# Run your code that needs to be elevated here...
Set-DnsClientServerAddress -InterfaceIndex 9 -ResetServerAddresses
As you've just determined yourself, the primary problem was that script execution was disabled on your system, necessitating (at least) a process-level change of PowerShell's execution policy, as the following C# code demonstrates, which calls
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass before invoking the script file (*.ps1):
For an alternative approach that uses the initial session state to set the per-process execution policy, see this answer.
The approach below can in principle be used to persistently change the execution policy for the current user, namely by replacing .AddParameter("Scope", "Process") with .AddParameter("Scope", "CurrentUser")
Caveat: When using a PowerShell (Core) 7+ SDK, persistent changes to the local machine's policy (.AddParameter("Scope", "LocalMachine")) - which require running with elevation (as admin) - are seen by that SDK project only; see this answer for details.
Caveat: If the current user's / machine's execution policy is controlled by a GPO (Group Policy Object), it can NOT be overridden programmatically - neither per process, nor persistently (except via GPO changes).
class Program
{
static void Main(string[] args)
{
var pathToScript = #"C:\Temp\test.ps1";
Execute(pathToScript);
Console.ReadKey();
}
public static void Execute(string command)
{
using (var ps = PowerShell.Create())
{
// Make sure that script execution is enabled at least for
// the current process.
// For extra safety, you could try to save and restore
// the policy previously in effect after executing your script.
ps.AddCommand("Set-ExecutionPolicy")
.AddParameter("Scope", "Process")
.AddParameter("ExecutionPolicy", "Bypass")
.Invoke();
// Now invoke the script and print its success output.
// Note: Use .AddCommand() (rather than .AddScript()) even
// for script *files*.
// .AddScript() is meant for *strings
// containing PowerShell statements*.
var results = ps.AddCommand(command).Invoke();
foreach (var result in results)
{
Console.WriteLine(result.ToString());
}
// Also report non-terminating errors, if any.
foreach (var error in ps.Streams.Error)
{
Console.Error.WriteLine("ERROR: " + error.ToString());
}
}
}
}
Note that the code also reports any non-terminating errors that the script may have reported, via stderr (the standard error output stream).
Without the Set-ExecutionPolicy call, if the execution policy didn't permit (unsigned) script execution, PowerShell would report a non-terminating error via its error stream (.Streams.Error) rather than throw an exception.
If you had checked .Streams.Error to begin with, you would have discovered the specific cause of your problem sooner.
Therefore:
When using the PowerShell SDK, in addition to relying on / catching exceptions, you must examine .Streams.Error to determine if (at least formally less severe) errors occurred.
Potential issues with your PowerShell script:
You're not waiting for the elevated process to terminate before returning from your PowerShell script.
You're not capturing the elevated process' output, which you'd have to via the .RedirectStandardInput and .RedirectStandardError properties of the System.Diagnostics.ProcessStartInfo instance, and then make your script output the results.
See this answer for how to do that.
The following, streamlined version of your code addresses the first point, and invokes the powershell.exe CLI via -ExecutionPolicy Bypass too.
If you're using the Windows PowerShell SDK, this shouldn't be necessary (because the execution policy was already changed in the C# code), but it could be if you're using the PowerShell [Core] SDK, given that the two PowerShell editions have separate execution-policy settings.
# Check to see if we are currently running as an administrator
$isElevated = & { net session *>$null; $LASTEXITCODE -eq 0 }
if ($isElevated)
{
# We are running as an administrator, so change the title and background color to indicate this
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)"
$Host.UI.RawUI.BackgroundColor = "DarkBlue"
Clear-Host
}
else {
# We are not running as an administrator, so relaunch as administrator
# Create a new process object that starts PowerShell
$psi = New-Object System.Diagnostics.ProcessStartInfo 'powershell.exe'
# Specify the current script path and name as a parameter with and support for scripts with spaces in its path
$psi.Arguments = '-ExecutionPolicy Bypass -File "{0}"' -f
$script:MyInvocation.MyCommand.Path
# Indicate that the process should be elevated.
$psi.Verb = 'RunAs'
# !! For .Verb to be honored, .UseShellExecute must be $true
# !! In .NET Framework, .UseShellExecute *defaults* to $true,
# !! but no longer in .NET Core.
$psi.UseShellExecute = $true
# Start the new process, wait for it to terminate, then
# exit from the current, unelevated process, passing the exit code through.
exit $(
try { ([System.Diagnostics.Process]::Start($psi).WaitForExit()) } catch { Throw }
)
}
# Run your code that needs to be elevated here...
Set-DnsClientServerAddress -InterfaceIndex 9 -ResetServerAddresses

What are the minimum IIS AppPool users permissions required for executing hosted Powershell via a WSManConnectionInfo WinRM 'loopback' session in C#?

I have a web service running some hosted Powershell. There is authentication at the front-end which passes credentials down to some impersonation in C#.
The application runs as the default IIS AppPool user. More or less everything ends up impersonated.
This works perfectly - although I had to tweak the .NET 'alwaysFlowImpersonationPolicy' setting as I discovered here:
Invoking Powershell via C# in a web application - issues with app pool identity
In an answer to the question above, it was also suggested I could use WinRM as an alternative method for executing the Powershell, which I didn't choose to do at the time.
For a bunch of different reasons I would like to experiment with doing away with the C# impersonation and move all of the permission elevation to WinRM. I am led to believe this is possible, although I am happy to acknowledge that it isn’t...
If I do something like this:
PSCredential psc = new PSCredential(user, password);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("https://ahostname:5986/wsman"), "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", psc);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
connectionInfo.SkipCNCheck = true;
connectionInfo.SkipCACheck = true;
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
connectionInfo.EnableNetworkAccess = true;
using (PowerShell powershell = PowerShell.Create())
{
string existingScript = #"C:\Users\Administrator\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1";
string scriptFileName = Path.GetFileName(existingScript);
string scriptText = ". " + existingScript + "; " + command;
if (command_logging == true)
{
string logMessage = "Running command via PSH wrapper: " + scriptText;
Tools.logToFile(logMessage);
}
powershell.AddScript(scriptText);
Collection<PSObject> results = new Collection<PSObject>();
try
{
results = powershell.Invoke();
}
catch (Exception ex)
{
results.Add(new PSObject((object)ex.Message));
}
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
string rawReturnValue = stringBuilder.ToString();
string returnValue = rawReturnValue.Replace(System.Environment.NewLine, "");
returnValue.Trim();
powershell.Dispose();
return returnValue;
}
I get a weird set of behaviour:
If my AppPool runs under the default credentials (ApplicationPoolIdentity), 99% of Powershell functions will not return anything meaningful.
The PSObject 'results' object in the snippet above returns 0. No error at all, it's almost as if it is not executing.
Some functions, 'Get-Process' for example, will return fine.
I thought perhaps my methodology may not transfer to using WSManConnectionInfo (i.e. dot sourcing the profile etc), however if I force the app to run under an administrator account, everything works perfectly (so much so I can swap my new and old Powershell 'wrapper' methods out seamlessly, running a bunch of other tests...)
The two 'default' cmdlets i tested:
'Get-Module'. This is in Microsoft.PowerShell.Core, works under the administrator account but does not work under ApplicationPoolIdentity.
'Get-Process'. Microsoft.PowerShell.Management, works under both ApplicationPoolIdentity and the administrator account.
I then wrapped 'Get-Process' in a test function, put it in my $profile and tried to call that. This works when the AppPool is running under the administrator account but not under the ApplicationPoolIdentity.
Almost certainly a permissions issue? Could someone please point me in the right direction?

Calling PowerShell cmdlet from C# throws CommandNotFoundException

I'm trying to call the Add-AppxPackage cmdlet from C#. I found the MSDN article on running PowerShell from C# code. I have referenced the System.Management.Automation assembly and have tried the following code snippets, all of which result in the same exception when trying to call powerShell.Invoke():
System.Management.Automation.CommandNotFoundException was unhandled
The term 'Add-AppxPackage' is not recognized as the name of a cmdlet,
function, script file, or operable program. Check the spelling of the
name, or if a path was included, verify that the path is correct and
try again.
Snippet 1:
var powerShell = PowerShell.Create();
powerShell.AddCommand(string.Format("Add-AppxPackage '{0}'", appxFilePath));
foreach (PSObject result in powerShell.Invoke())
{
Console.WriteLine(result);
}
I understand why this doesn't work, since I shouldn't be providing parameters in the AddCommand() function.
Snippet 2:
var powerShell = PowerShell.Create();
powerShell.AddCommand("Add-AppxPackage");
powerShell.AddParameter("Path", appxFilePath);
foreach (PSObject result in powerShell.Invoke())
{
Console.WriteLine(result);
}
Snippet 3:
var powerShell = PowerShell.Create();
powerShell.AddCommand("Add-AppxPackage");
powerShell.AddArgument(appxFilePath);
foreach (PSObject result in powerShell.Invoke())
{
Console.WriteLine(result);
}
My C# project targets .Net 4.5, and if I do powerShell.AddCommand("Get-Host") it works and the Version it returns back is 4.0. Add-AppxPackage was added in v3.0 of PowerShell, so the command should definitely exist, and it works fine if I manually run this command from the Windows PowerShell command prompt.
Any ideas what I am doing wrong here? Any suggestions are appreciated.
-- Update --
I found this post and this one, and realized there is a AddScript() function, so I tried this:
Snippet 4:
var powerShell = PowerShell.Create();
powerShell.AddScript(string.Format("Add-AppxPackage '{0}'", appxFilePath));
var results = powerShell.Invoke();
foreach (PSObject result in results)
{
Console.WriteLine(result);
}
And it does not throw an exception, but it also doesn't install the metro app, and the "results" returned from powerShell.Invoke() are empty, so I'm still at a loss...
-- Update 2 --
So I decided that I would try just creating a new PowerShell process to run my command, so I tried this:
Process.Start(new ProcessStartInfo("PowerShell", string.Format("-Command Add-AppxPackage '{0}'; $key = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyUp')", appxFilePath)));
but it still throws the same error that Add-AppxPackage is not a recognized cmdlet.
ANSWER
If you follow the long comment thread on robert.westerlund's answer, you will see that for some reason when running/launched from Visual Studio, PowerShell was not including all of the PSModulePaths that it does when running straight from a PowerShell command prompt, so many modules are not present. The solution was to find the absolute path of the module that I needed (the appx module in my case) using:
(Get-Module appx -ListAvailable).Path
And then import that module before trying to call one of its cmdlets. So this is the C# code that worked for me:
var powerShell = PowerShell.Create();
powerShell.AddScript(string.Format(#"Import-Module 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\Appx\Appx.psd1'; Add-AppxPackage '{0}'", appxFilePath));
var results = powerShell.Invoke();
UPDATED ANSWER
You can see from this other post I opened, that the problem was with a bug in a Visual Studio extension (in my case StudioShell) causing not all of the PSModulePaths to be loaded. After uninstalling that extension all of the modules were loaded correctly and I no longer needed to manually import the module.
In PowerShell there is a difference between terminating errors (which stops the execution) and non-terminating errors (which are just written to the error stream).
If you want to create a non-terminating error in a function of your own, just use the Write-Error cmdlet. If you want to create a terminating error, use the Throw keyword. You can read more about these concepts if you run Get-Help Write-Error, Get-Help about_Throw and Get-Help about_Try_Catch_Finally.
Using the Add-AppxPackage with a non existing package is a non terminating error and will thus be written to the error stream, but no execution halting exception will be thrown. The following code tries to add a non existing package and then writes the error to the console.
var powerShell = PowerShell.Create();
powerShell.AddScript("Add-AppxPackage NonExistingPackageName");
// Terminating errors will be thrown as exceptions when calling the Invoke method.
// If we want to handle terminating errors, we should place the Invoke call inside a try-catch block.
var results = powerShell.Invoke();
// To check if a non terminating error has occurred, test the HadErrors property
if (powerShell.HadErrors)
{
// The documentation for the Error property states that "The command invoked by the PowerShell
// object writes information to this stream whenever a nonterminating error occurs."
foreach (var error in powerShell.Streams.Error)
{
Console.WriteLine("Error: " + error);
}
}
else
{
foreach(var package in results)
{
Console.WriteLine(package);
}
}

C# running Process weird stuff

no error. no exception. Second and Third produce a file f[1]/[2]. but not first. why? I verify using debug that the command is good. and using the command I capture from debug , cut and past to command line, I can produce the file[f0].
string[] f = new string[4];
f[0] = "SNICKER.reg.txt";
f[1] = "SNDIS.reg.txt";
f[2] = "SNICS.reg.txt";
f[3] = "Ssmf.xml";
//First
Run_Process("REG", "EXPORT HKEY_LOCAL_MACHINE\\SOFTWARE\\sridge\\Snicker " + f[0] + " /y");
//Second
Run_Process("REG", "EXPORT HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\SNDIS " + f[1] + " /y");
//Third
Run_Process("REG", "EXPORT HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\SClass " + f[2] + " /y");
private static void Run_Process(string exe_name, string arg)
{
Process myProcess = new Process();
try
{
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.FileName = exe_name;
//myProcess.StartInfo.Arguments = "/C getLH.exe > feed.txt";
myProcess.StartInfo.Arguments = arg;
myProcess.StartInfo.CreateNoWindow = true;
myProcess.Start();
myProcess.WaitForExit();
}
catch (Exception ep)
{
Console.WriteLine(exe_name + " " + arg + ". Error: " + ep.Message);
}
}
When your app runs on a 64bit OS and you try to access the registry you could end up reading the values in the wrong folders.
This happens when you compile for x86 Platform, and thus your code is emitted as 32bit code.
In this scenario the registry redirector kicks in and changes your hand made registry paths that point to folders inside HKLM\SOFTWARE to HKLM\SOFTWARE\Wow6432Node.
The reasoning behind this behavior is complex and you could try to read something here
Said that, I still cannot understand really well what happen when we put in this scenario the REG program executed as a process launched from an application built with AnyCPU. This application should be executed as a 64bit code but, for some reason, the REG program executed is the 32 bit version (yes, there are two version of REG.EXE, one in system32 and one in SysWow64) and this version search your data in the wrong path. Perhaps this is related to the current value of the PATH environment variable.
As a side note. One of the worst decision ever made by Microsoft is to allow applications to store their configuration data in the registry. I really suggest to change this behavior, if possible
UPDATE
I can confirm that on a 64bit OS, a console application compiled as AnyCPU running the code that executes the REG.EXE command as above launches the 32bit version of REG.EXE from the WINDOWS\SYSWOW64. I have checked with ProcMon and I cannot explain why this happens. It is not related to the PATH env variable because I have only the path to C:\WINDOWS\SYSTEM32
Check out Process.start: how to get the output? and try to use that method to see the output of your command. I don't think there will be any exception, because it will only catch the exception of that block of code, not the exception of outside program that you attempt to run.

How to obtain DefragAnalysis using C#

I am currently developing an application in C# (.NET 4.0) that should have as a part of its functionality the ability to determine the percentage of fragmentation on a particular volume. All the other features have been tested and are working fine but I’ve hit a snag trying to access this data. I would ideally prefer to use WMI as this matches the format I’m using for the other features but at this point I’m willing to use anything that can be efficiently integrated into the application, even if I have to use RegEx to filter the data. I am currently doing the development on a Windows 7 Professional (x64) machine. I have tested the following Powershell snippet using Administrator rights and it works flawlessly.
$drive = Get-WmiObject -Class Win32_Volume -Namespace root\CIMV2 -ComputerName . | Where-Object { $_.DriveLetter -eq 'D:' }
$drive.DefragAnalysis().DefragAnalysis
This is the method I’m using in C# to accomplish the same thing, but the InvokeMethod keeps returning 11 (0xB).
public static Fragmentation GetVolumeFragmentationAnalysis(string drive)
{
//Fragmenation object initialization removed for simplicity
try
{
ConnectionOptions mgmtConnOptions = new ConnectionOptions { EnablePrivileges = true };
ManagementScope scope = new ManagementScope(new ManagementPath(string.Format(#"\\{0}\root\CIMV2", Environment.MachineName)), mgmtConnOptions);
ObjectQuery query = new ObjectQuery(string.Format(#"SELECT * FROM Win32_Volume WHERE Name = '{0}\\'", drive));
scope.Connect();
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
{
object[] outputArgs = new object[2];
foreach (ManagementObject moVolume in searcher.Get())
{
// Execution stops at this line as the result is always 11
UInt32 result = (UInt32)moVolume.InvokeMethod("DefragAnalysis", outputArgs);
if (result == 0)
{
Console.WriteLine("Defrag Needed: = {0}\n", outputArgs[0]);
ManagementBaseObject mboDefragAnalysis = outputArgs[1] as ManagementBaseObject;
if (null != mboDefragAnalysis)
{
Console.WriteLine(mboDefragAnalysis["TotalPercentFragmentation"].ToString());
}
}
else
{
Console.WriteLine("Return Code: = {0}", result);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Could not acquire fragmentation data.\n" + ex);
}
return result;
}
I have even added the following line to the app.manifest but still nothing.
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
Could somebody please tell me what I’m overlooking? Failure is not an option for me on this, so if it cannot be done using C# I don’t mind creating a DLL in another language (even if I have to learn it), that will give me the results I need. Ideally the application should be able work on any OS from XP upwards and must be totally transparent to the user.
These are the resources I have already used. I wanted to add the jeffrey_wall blog on msdn as well but as a new user I can only add 2 hyperlinks at a time. Thanks again.
http://www.codeproject.com/Messages/2901324/Re-the-result-of-DefragAnalysis-method-in-csharp.aspx
http://social.technet.microsoft.com/Forums/vi-VN/winserverfiles/thread/9d56bfad-dcf5-4258-90cf-4ba9247200da
Try building your application targeting 'Any CPU' - on the Build tab of the project properties. I suspect you're using a target of x86. I get the same error code on my Win7 x64 machine if I do that.
In fact, running your PowerShell snippet in the x86 version of PowerShell gives an empty set of results, too.
You get the same error if you run either piece of code without full Administrator privileges, as you've found, so also ensure your app.manifest is correct. A UAC prompt is a handy hint that it's taking effect!
No idea why this WMI query doesn't like running under WoW64, I'm afraid, but hopefully this will give you a head-start.
You could simply call the PowerShell command you mentioned in your post, since you said the PowerShell code works. From C# you would want to follow this workflow:
Instantiate a PowerShell RunSpace
Open the RunSpace
Add a script to the Commands property
Invoke the command list
Here is an example of how to achieve this, and process the resulting object output.
http://www.codeproject.com/Articles/18229/How-to-run-PowerShell-scripts-from-C
For Windows XP and Windows Vista, you would have to ensure that PowerShell was installed on each of the systems you want to run your program on. Not a bad prerequisite to have, but something to keep in mind as a dependency.
Hope this helps.
The 32-bit WMI provider for Win32_Volume doesn't seem to be able to start the defragsvc for whatever reason. You can force the 64-bit WMI provider even in a 32-bit client running under WOW64 by changing your code to add an additional WMI connection option:
ConnectionOptions mgmtConnOptions = new ConnectionOptions {
EnablePrivileges = true,
Context = new ManagementNamedValueCollection() {
{ "__ProviderArchitecture", 64 }
}
};

Categories