3

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.

1 Answer 1

7

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);
    }
}
Sign up to request clarification or add additional context in comments.

10 Comments

You should be abe to use this to read the non terminating error you are most likely receiving when calling the Add-AppxPackage function.
Thanks for the code, but it looks like this is a terminating error; powerShell.Invoke() throws a CommandNotFoundException, so it never hits any code after that (unless I wrap it in a try-catch). If I do wrap it in a try-catch then powerShell.HadErrors is true, but powerShell.Streams.Error is empty. The problem is not that the .appx file does not exist, but that it does not recognize the Add-AppxPackage command, even though it is using PowerShell v4.0 which does have this command.
In your Snippet 4 you mention that no exception was thrown but no application was installed. That was the snippet I based my sample on. Has something changed; are you receiving an exception in that snippet too now?
Ah, ok. I just ran it again using the code from Snippet 4 and you are right; when I run it this way it is a non-terminating error (i.e. no exception is thrown). powerShell.Streams.Error contains the same error message though that Add-AppxPackage is not a recognized cmdlet.
If it does exist normally, then perhaps the problem is that you don't have the correct environment variables available? Could you compare the results of (Get-Item Env:\PSModulePath).Value -Split ";"? And also, could you try loading the AppX module using an absolute path (I think it should be at Import-Module C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\Appx\Appx.psd1, the path can be found by doing (get-module appx -ListAvailable).Path in the PowerShell command prompt where you could find the module in question) and see if that works?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.