C# PowerShell ApartmentState = STA not working - c#

I want to import a module in PowerShell which need -STA option, I set the Runspace.ApartmentState to STA but when I import the module its fail on missing the -STA option, I tried to use module "StarWindX" but I do not think it is module dependent problem, here is the code:
static void Main(string[] args) {
try {
var iss = InitialSessionState.CreateDefault();
iss.ApartmentState = System.Threading.ApartmentState.STA;
using (PowerShell ps = PowerShell.Create(iss)) {
Console.WriteLine("ApartmentState: " + ps.Runspace.ApartmentState.ToString());
ps.AddScript(
"Import-Module StarWindX"
);
Collection<PSObject> PSOutput = ps.Invoke();
if (ps.Streams.Error.Count > 0) {
foreach (var error in ps.Streams.Error) {
Console.WriteLine("ps error: " + error.ToString());
Console.WriteLine("ps error: " + error.ScriptStackTrace);
}
}
foreach (PSObject outputItem in PSOutput) {
if (outputItem != null) {
Console.WriteLine("ps: " + outputItem.ToString());
}
}
}
} catch (Exception ex) {
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
Console.ReadLine();
}
which output is:
ApartmentState: STA
ps error: StarWindX doesn't support current appartment. You need to run this script with -STA switch to use StarWindX.
ps error: at , C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\StarWindX\StarWindX.psm1: line 4 at ScriptBlock, No file: line 1
Here is the first 4 line of the .psm1 file:
if ($host.Runspace.ApartmentState -ne 'STA')
{
#write-host "You need to run this script with -STA switch or inside ISE"
throw "StarWindX doesn't support current appartment. You need to run this script with -STA switch to use StarWindX."
}
How can I set the ApartmentSate correctly?

Related

c#.Net PowerShell Class

I'm trying to use a PowerShell object with a remote runspace.
The initial connection is established, the test returns back with the correct responses, all appears good. I then assign my powershell object some commands to run, and it pops up with the following error message:
System.Management.Automation.PSObjectDisposedException: 'Cannot perform operation because object "PowerShell" has already been disposed. Object name: 'PowerShell'.'
I've not actually disposed of anything (I'd rather keep things open while I'm developing, and optimise it later).
RemoteShell (works fine, no problems):
Private PowerShell RemoteShell (string Hostname, PSCredential psCred)
{
PowerShell psCon = PowerShell.Create();
WSManConnectionInfo connectionInfo = new WSManconnectionInfo
{
Port = 5985,
AuthenticationMechanism = AuthenticationMechanism.Kerberos,
ComputerName = Hostname,
Credential= psCred,
ShellUri = "http://schemas.microsoft.com/powershell/microsoft.powershell",
IdleTimeout = 99999999
}
using (Runspace remoterunspace = RunspaceFactory.CreateRunespace(connectionInfo))
{
try {
remoteRunspace.Open();
using (psCon) {
psCon.Runspace = remoteRunspace;
psCon.AddCommand("whoami");
psCon.AddStatement().AddScript("[System.Net.Dns]::GetHostByName(($env:computerName))";
Collection<PSObject> results= psCon.Invoke();
string resultsstr = "Shell created under: " + results[0].ToString() + System.Environment.NewLine + "Shell created on: " + results[1].ToString();
AddToOutput(--- not really relevant ---);
} catch (Exception e) {
AddToOutput (--- not really relevant ---);
}
}
return psCon;
}
JobAsync (dies every time it hits the *** line):
Private Boolean jobAsync (string type, List<ScriptModule>JobList, PSCredential psCred)
{
PowerShell psCon;
<type switch kind of irrelevant>
psCon = RemoteShell(targetFQDN, psCred);
foreach(ScriptModule curMod in JobList)
{
psCon.Commands.Clear();
List<String[]> ResultsArr = new List<string[]>();
int TestNum = 0;
foreach(ScriptObject curScr in curMod.moduleScripts)
{
<< some more irrelevant stuff assembling strings >>
**** psCon.AddStatement().AddScript("$param" + pC + " = '" + paramVal + "';"); ****
}
The **** line constantly generates a PowerShell disposed error exception preventing any further execution.
This same code is also used on a local shell (running from the machine the application is running on) and this works fine.
The only difference in the code is the remoteShell utilising a runspace...
For reference, here's the localShell code:
private PowerShell LocalShell()
{
PowerShell psCon = PowerShell.Create();
try {
psCon.AddCommand("whoami");
psCon.AddStatement().AddScript("[System.Net.Dns]::GetHostByName(($env:computerName))");
Collection<PSObject> results = psCon.Invoke();
<< more irrelevant string stuff >>
} catch (Exception e) {
<< more irrelevant string stuff >>
}
return psCon;
}

Exception Handling in C# for handling errors in the powershell script

I am trying to call a power shell script file from C# which automates some task.
I want to efficiently handle exception that may occur while the power shell script executes and capture it in c# code.
Below is the sample code I am currently using, but am not sure if it is correct.
Any suggestions on what needs to be changed.
I also tried pipeline.Error.Count but the value returned is 0 even if exception has occurred.
string cmdArg = "mypowerscript.ps1";
Runspace runspace = null;
Pipeline pipeline = null;
try
{
runspace = RunspaceFactory.CreateRunspace();
pipeline = runspace.CreatePipeline();
runspace.ApartmentState = System.Threading.ApartmentState.STA;
runspace.ThreadOptions = PSThreadOptions.UseCurrentThread;
runspace.Open();
pipeline.Commands.AddScript(cmdArg, true);
pipeline.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
Collection<PSObject> results = null;
results = pipeline.Invoke();
if (results.Count == 0)
MessageBox.Show(" Failed",MessageBoxButtons.OK, MessageBoxIcon.Error);
else
MessageBox.Show("Successful",MessageBoxButtons.OK, MessageBoxIcon.Information);
foreach (PSObject obj in results)
{
// Consume the results
Debug.WriteLine(obj);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
Cursor.Current = Cursors.Default;
MessageBox.Show("Failed", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
if (runspace != null)
runspace.Close();

Powershell Try/Catch not executing when invoked from C#

List item
$LOGFILE="c:\log2\crush1.txt"
function log
{
write-host $($args[0])
echo $($args[0]) >> $LOGFILE
}
log "Main Before TRY"
try {
log "Inside TRY"
}
catch {
log "Inside CATCH"
}
log "Main After TRY"
return "Powershell script completed successfully"
Here is my C# code:
namespace HostSamples {
using System;
using System.Management.Automation; // Windows PowerShell namespace.
using System.Collections.ObjectModel;
using System.Management.Automation.Runspaces;
using System.IO;
using System.Text;
/// <summary>
/// </summary>
internal class HostPS1
{
private static void Main(string[] args)
{
PowerShell ps = PowerShell.Create();
ps.AddScript(LoadScript(#"c:\\temp\\crush1.ps1"));
Console.WriteLine("Starting");
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine(result);
}
Console.WriteLine("Finished");
Console.ReadLine();
}
static string LoadScript(string filename)
{
try
{
using (StreamReader sr = new StreamReader(filename))
{
// use a string builder to get all our lines from the file
StringBuilder fileContents = new StringBuilder();
// string to hold the current line
string curLine;
while ((curLine = sr.ReadLine()) != null)
{
fileContents.Append(curLine + "\n");
}
return fileContents.ToString();
}
}
catch (Exception e)
{
string errorText = "The file could not be read:";
errorText += e.Message + "\n";
return errorText;
}
}
} // End HostPs1. }
I found the answer. For some reason having a write-host call inside the log function caused the try to trigger an exception. The catch also called the log function, so it did not write anything to the output file. The output file traces made it look like the try-catch had been skipped entirely.
I cannot explain why the try-catch would complain about a write-host call, but the rest of the script does not. Possibly a scope issue.
Once I commented out the write-host statement in the log function, everything ran normally.

Unable to execute two Office 365 commands simultaneously using different accounts in C#

I am trying to query the users of two different Office 365 accounts simultaneously using C#. It works fine when trying from two Powershell Windows or when connecting and getting users one account after other by code. But not working when doing simultaneously using code.
On checking I found that only one log file is generated when trying from C#. But two different log files are generated when trying from PowerShell window.
Log folder location: %userprofile%\appdata\Local\Microsoft\Office365\Powershell
Which implies that when running from code, it works like running with single PowerShell window even with two runspaces.
Main method code:
Thread t1 = new Thread(() => connectandExec("admin#domain1.onmicrosoft.com", "Pwdd#123"));
Thread t2 = new Thread(() => connectandExec("admin#domain2.onmicrosoft.com", "Pwdd#123"));
t1.Start();
t2.Start();
Method that connects and gets the user:
public static void connectandExec(String userName, String password) {
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new String[] { "MSOnline" });
Runspace runspace = RunspaceFactory.CreateRunspace(iss);
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
Command cmd = new Command("Connect-MsolService");
System.Security.SecureString pwd = new System.Security.SecureString();
foreach (Char c in password.ToCharArray()) {
pwd.AppendChar(c);
}
log("Connecting to : " + userName);
PSCredential pscred = new PSCredential(userName, pwd);
cmd.Parameters.Add("Credential", pscred);
ps.Commands.AddCommand(cmd);
ps.Invoke();
if (ps.Streams.Error.Count > 0) {
log("Error when connecting: " + userName);
foreach (ErrorRecord errRecord in ps.Streams.Error) {
log(userName + errRecord.ToString());
}
} else {
log("Connected to : " + userName);
}
ps.Commands.Clear();
try {
ps.Commands.AddScript("Get-MsolUser -All");
ICollection<PSObject> results = ps.Invoke();
if (ps.Streams.Error.Count > 0) {
log("Error when getting users: " + userName);
foreach (ErrorRecord errRecord in ps.Streams.Error) {
log(userName + errRecord.ToString());
}
} else {
foreach (PSObject obj in results) {
if (obj != null && obj.ToString() != "") {
Object val = obj.Members["UserPrincipalName"].Value;
if (val != null) {
log(userName + ":" + val.ToString());
}
}
}
}
} catch (Exception ex) {
log(userName + ":Exception during getUsers: " + ex.ToString());
}
}
Your code is trying to use threads to do something which should be done in different application domains: "An application domain forms an isolation boundary for security".
The Office 365 library will no doubt use the app domain of the current thread - and you're just using two threads which belong to the same app domain, hence the confusion / failure.

Unable to do waitforexit while invoking EXE

I am running EXE using below code. EXE opens up properly and runs properly. I am facing two issues.
is there anything similar to Process.WaitforExit() while invoking PowerShell.Invoke.Once user completes operations on EXE and closes the same,then the remaining execution should continue.
The output of EXE is coming as System.Management.ManagementBaseObject. It should contain the executable result.
If I run the EXE using Process.Start, I can achieve both the above results. The output also coming up properly. Please help on this.
using (Runspace runSpace = RunspaceFactory.CreateRunspace())
{
string remoteScriptPath="e:\shared\test.ex";
string parameterString="p1";
runSpace.Open();
using (Pipeline pipeline = runSpace.CreatePipeline())
{
RunspaceInvoke invoke = new RunspaceInvoke();
PowerShell ps = PowerShell.Create();
ps.Runspace = runSpace;
ps.AddCommand("invoke-wmimethod");
ps.AddParameter("class", "Win32_Process");
ps.AddParameter("name", "Create");
if (string.IsNullOrEmpty(parameterString))
{
ps.AddParameter("argumentlist", remoteScriptPath);
}
else
{
ps.AddParameter("argumentlist", remoteScriptPath + " " + parameterString);
}
Collection<PSObject> psOutput = ps.Invoke();
if (ps.Streams.Error.Count == 0)
{
string result="";
foreach (PSObject psObject in psOutput)
{
if (psObject != null)
{
result += psObject.BaseObject.ToString();
result += Environment.NewLine;
}
}
return result;
}

Categories

Resources