Powershell Invoke method neither throwing exception nor returning result - powershell

I am trying to build an ASP.Net, c# application to expose few IIS management related activities through web interface for a distributed Admin group.
I am making use of System.Management.Automation V3.0 library to wrap power shell commands. As a first step I wanted to list all Web Applications that are currently up and running on local IIS by invoking Get-WebApplication command.
This is where I am facing the issue. Method call is neither throwing any exception nor its returning the result. Does anyone know the root cause of this issue? Please share your experience of building such interface using System.Management.Automation.dll.
var shell = PowerShell.Create();
shell.Commands.AddScript("Get-WebApplication | Out-String");
try
{
var results = shell.Invoke();
if (results.Count > 0)
{
var builder = new StringBuilder();
foreach (var psObject in results)
{
builder.Append(psObject.BaseObject.ToString() + "\r\n");
}
}
}
catch(Exception ex)
{
throw;
}
PS: Get-Service in place of Get-WebApplication works absolutely fine by returning list of services available on the machine.

PowerShell.Create() does not create new PowerShell process. If you does not specify Runspace for it, then it will create new in-process Runspace. Since it run in your process, it will match your process bitness and you can not change that. To create Runspace with different bitness you need to create out of process Runspace. Here is a sample console application, which demonstrate how you can do that:
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
public static class TestApplication {
public static void Main() {
Console.WriteLine(Environment.Is64BitProcess);
using(PowerShellProcessInstance pspi = new PowerShellProcessInstance()) {
string psfn = pspi.Process.StartInfo.FileName;
psfn=psfn.ToLowerInvariant().Replace("\\syswow64\\", "\\sysnative\\");
pspi.Process.StartInfo.FileName=psfn;
using(Runspace r = RunspaceFactory.CreateOutOfProcessRunspace(null, pspi)) {
r.Open();
using(PowerShell ps = PowerShell.Create()) {
ps.Runspace=r;
ps.AddScript("[Environment]::Is64BitProcess");
foreach(PSObject pso in ps.Invoke()) {
Console.WriteLine(pso);
}
}
}
}
}
}
If you compile this application as x32, it still will use x64 out of process Runspace on x64 operation system.

Related

How to extend Powershell runspace with RestrictedLanguage to allow usage of additional types

I'm creating a Powershell host application where I create a runspace with the following code:
var iss = InitialSessionState.Create();
iss.LanguageMode = PSLanguageMode.RestrictedLanguage;
iss.ThrowOnRunspaceOpenError = true; // otherwise silently fails in loading incorrect module
// See http://www.nivot.org/blog/post/2012/02/10/Bypassing-Restricted-Execution-Policy-in-Code-or-in-Script
iss.AuthorizationManager = new System.Management.Automation.AuthorizationManager(null); // no execution policy
// Create the runspace and stuff.
using (var runspace = RunspaceFactory.CreateRunspace(host, iss))
{
runspace.Open();
:
}
This is perfect, I can extend the set of supported cmdlets with iis.Commands.Add(), and preload existing modules using iss.ImportPSModulesFromPath().
But how can I add additional types that can be used as well in the runspace? I tried:
iss.Assemblies.Add(new List<SessionStateAssemblyEntry>()
{
new SessionStateAssemblyEntry("Microsoft.SqlServer.SMO"),
new SessionStateAssemblyEntry("Microsoft.SqlServer.SMOExtended"),
};
And then access methods on these assemblies.But I get the error that I can only call methods on core types.
If I use language mode FullLanguage it works, but then I can also execute things like:
[System.IO.File]::WriteAllText('c:\test.txt', 'hello world!')

Running Selenium via NUnit and Cruise Control .NET

I'm having an issue running Selenium tests in NUnit from Cruise Control .NET. I have a simple test that runs fine when I run from the NUnit GUI on our continuous integration server. However when the NUnit test is run from Cruise Control .NET on the same server, the test always fails. Tests that don't use Selenium run fine from both the NUnit GUI and from Cruise Control.
[SetUp]
public void SetupTest()
{
Driver = new InternetExplorerDriver();
}
[TearDown]
public void TeardownTest()
{
Driver.Quit();
}
/// <summary>
/// Test basic Selenium functionality.
/// </summary>
[Test]
public void SeleniumTest()
{
Driver.Navigate().GoToUrl(TestConfig.TestURL);
IWebElement testEle = WaitForElement(Driver, By.Id, "body", TestConfig.TestWaitMS);
Assert.IsTrue(true);
}
private static IWebElement WaitForElement(IWebDriver driver, ByFunc byFunc, string elementId, int waitMs,
string waitOutput = null, int pause = 50)
{
bool elementFound = false;
int i = 0;
IWebElement webElement = null;
while (!elementFound && (i * pause) < waitMs)
{
try
{
webElement = driver.FindElement(byFunc(elementId));
elementFound = true;
}
catch (NoSuchElementException)
{
i++;
Thread.Sleep(pause);
if (waitOutput != null)
Console.Write(waitOutput);
}
}
if (elementFound)
return webElement;
else
throw new NoSuchElementException(string.Format("Could not find element {0} after waiting {1}ms.", elementId, waitMs));
}
WaitForElement is just a helper function that allows me to assign specific waits for certain elements rather than have a blanket waiting time for the entire test run.
The test fails when the NoSuchElementException is raised from the WaitForElement function.
I've found some links on Google saying that you need to run SeleniumRC as a service to get it to run from Cruise Control. I don't think that applies here as I'm using the WebDriver version. Please correct me if I'm wrong.
IE version 8
Cruise Control .NET 1.8.3.0
NUnit 2.6
Selenium 2.0.0
Thanks for the pointers #Arran. Switching to a Firefox driver fixed the issue. I guess the fault must lie somewhere in the Internet Explorer driver.
[SetUp]
public void SetupTest()
{
Driver = new FirefoxDriver();
}

How to handle close event of PowerShell window if user clicks on Close('X') button

I want to run some code before PowerShell 2.0 window is closed. For this I tried:
PS > register-engineevent PowerShell.Exiting -action {get-process | out-file c:\work\powershellexiteventcalled.txt}
It works fine if user closes PowerShell window using exit command (i.e. exit ). But it does not work if user closes it by clicking on Close ('X') button on top right.
I could not find any way to handle this. I also tried to do it the following way, but this does not work either:
PS > $query = "Select * from __InstanceDeletionEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.Name='powershell.exe'"
PS > Register-WmiEvent -Query $query -Action {get-process | out-file c:\work\powershellexiteventcalled.txt}
Please guide how I can achieve this task.
UPDATE: With some useful input from a helpful professional online I also tried the following:
$appCurrentDomain = [System.AppDomain]::CurrentDomain
Register-ObjectEvent -Action {get-process | out-file c:\work\powershellexiteventcalled.txt} -InputObject $appCurrentDomain -EventName DomainUnload
But again, it does not work. As per Microsoft "DomainUnload event occurs when an AppDomain is about to be unloaded.". But it does not work when I close the window ( or even type exit for that matter).
UPDATE:
With some help from other professionals online & a little effort I could achieve this with following code.
PS..> $code = #"
using System;
using System.Runtime.InteropServices;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace MyNamespace
{
public static class MyClass
{
public static void SetHandler()
{
SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);
}
private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{
switch (ctrlType)
{
case CtrlTypes.CTRL_C_EVENT:
Console.WriteLine("CTRL+C received!");
return false;
case CtrlTypes.CTRL_CLOSE_EVENT:
Console.WriteLine("CTRL_CLOSE_EVENT received!");
return true;
case CtrlTypes.CTRL_BREAK_EVENT:
Console.WriteLine("CTRL+BREAK received!");
return false;
case CtrlTypes.CTRL_LOGOFF_EVENT:
Console.WriteLine("User is logging off!");
return false;
case CtrlTypes.CTRL_SHUTDOWN_EVENT:
Console.WriteLine("User is shutting down!");
return false;
}
return false;
}
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
// A delegate type to be used as the handler routine
// for SetConsoleCtrlHandler.
public delegate bool HandlerRoutine(CtrlTypes CtrlType);
// An enumerated type for the control messages
// sent to the handler routine.
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
}
}"#
PS..> $text = Add-Type -TypeDefinition $code -Language CSharp
PS..> $rs = [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace
PS..> [MyNamespace.MyClass]::SetHandler()
BUT THERES AN ISSUE I AM FACING.... If I run any cmdlet on console after registering this handler (e.g. get-date, get-process). Then the application will crash whenever an event occurs (e.g. Ctrl+C, close). Can someone please help me with this?
Unfortunately, you can't do this. The only time an exiting event will get called is if you type "exit" at the PowerShell prompt.
This is how I hook up my exiting event:
$null = Register-EngineEvent -SourceIdentifier `
([System.Management.Automation.PsEngineEvent]::Exiting) -Action { # Put code to run here }
The unhandled exception occurs because garbage collection has already disposed your handler by the time the unmanaged method is called. You can get around that by storing it in a static field:
private static HandlerRoutine s_rou;
public static void SetHandler()
{
if (s_rou == null)
{
s_rou = new HandlerRoutine(ConsoleCtrlCheck);
SetConsoleCtrlHandler(s_rou, true);
}
}
When you click the X to close you are closing the application hosting PowerShell. This application would need to handle the exit situation. I believe the default PS host is the Windows console which is obviously not doing what you need. You could host PowerShell in a custom host and handle the exit events. I'm on a Mac right now but maybe running under the PS ISE would handle this for you?

Eclipse plugin - how to run external class

I want to make a plugin for Eclipse. The thing is that I looked into the API, and examples, and I managed to make a button on main bar, with a specific icon, and when I click it, open up an InputDialog.
The hard part, is that I want to start an aplication from this button, but not with Runtime as it was a new process. I simply want to start a class inside plugin, which will log in to a server and get some output from it. I want it to be opened in a console, like launching a normal application, or a separate console.
The best example of this kind is a Tomcat plugin which starts Tomcat, and then outputs the console to the Eclipse console. I want to do that too. I've looked at the Tomcat source plugin, but I got stuck there too. They use their own launcher.
I am not sure what you mean by "I want to simply start a class". I assume there is a command line tool that you want to execute and redirect its output to the console window.
To be able to do that without spawning a new process, you have to be able to control the output stream of the tool. If it cannot be controlled, then you have no choice but to start a new process to properly capture the tool's output.
It is technically possible to call System.setOut instead, but it will redirect output from all threads to your console which is not what you want.
Nevertheless you start by creating a console:
// function findConsole copied from:
// http://wiki.eclipse.org/FAQ_How_do_I_write_to_the_console_from_a_plug-in%3F
private MessageConsole findConsole(String name) {
ConsolePlugin plugin = ConsolePlugin.getDefault();
IConsoleManager conMan = plugin.getConsoleManager();
IConsole[] existing = conMan.getConsoles();
for (int i = 0; i < existing.length; i++)
if (name.equals(existing[i].getName()))
return (MessageConsole) existing[i];
//No console found, so create a new one.
MessageConsole myConsole = new MessageConsole(name, null);
conMan.addConsoles(new IConsole[]{myConsole});
return myConsole;
}
// Find my console
MessageConsole cons = findConsole("MyTool Console");
MessageConsoleStream out = cons.newMessageStream();
// Optionally get it's input stream so user can interact with my tool
IOConsoleInputStream in = cons.getInputStream();
// Optionally make a differently coloured error stream
MessageConsoleStream err = cons.newMessageStream();
err.setColor(display.getSystemColor(SWT.COLOR_RED));
// Display the console.
// Obtain the active page. See: http://wiki.eclipse.org/FAQ_How_do_I_find_the_active_workbench_page%3F
IWorkbenchPage page = ...;
String id = IConsoleConstants.ID_CONSOLE_VIEW;
IConsoleView view = (IConsoleView) page.showView(id);
view.display(cons);
Then set the input and output streams of my tool and start processing in a different thread so the UI will not block.
// Create my tool and redirect its output
final MyTool myTool = new MyTool();
myTool.setOutputStream(out);
myTool.setErrorStream(err);
myTool.setInputStream(in);
// Start it in another thread
Thread t = new Thread(new Runnable() {
public void run() {
myTool.startExecuting();
}
});
t.start();
If your tool does not support I/O redirection, you have no choice but to start it in another process with the ProcessBuilder and use a number of threads to move data between console and process streams See: Process.getInputStream(), Process.getOutputStream() and Process.getErrorStream().
The following links have additional useful details:
Executing a Java application in a separate process
FAQ How do I write to the console from a plug-in?
FAQ How do I find the active workbench page?
This is the code for running a new console with controls, like stop delete, and deleteAll! This is what I asked for in the beginning, but the message console is good to know!
ILaunchConfigurationType launchType = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurationType("org.eclipse.jdt.launching.localJavaApplication");
ILaunchConfigurationWorkingCopy config = null;
try {
config = launchType.newInstance(null, "My Plugin working");
} catch (CoreException e) {
System.err.println(e.getMessage());
}
config.setAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_ID, "org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector");
String[] classpath = new String[] { "C:\\Users\\Administrator\\Documents\\myjr.jar" };
ArrayList classpathMementos = new ArrayList();
for (int i = 0; i < classpath.length; i++) {
IRuntimeClasspathEntry cpEntry = JavaRuntime.newArchiveRuntimeClasspathEntry(new Path(classpath[i]));
cpEntry.setClasspathProperty(IRuntimeClasspathEntry.USER_CLASSES);
try {
classpathMementos.add(cpEntry.getMemento());
} catch (CoreException e) {
System.err.println(e.getMessage());
}
}
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false);
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, classpathMementos);
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, "collectorlog.handlers.MyClass");
try {
ILAUNCH = config.launch(ILaunchManager.RUN_MODE, null);
} catch (CoreException e) {
System.err.println(e.getMessage());
}

When is a started service not a started service? (SQL Express)

We require programmatic access to a SQL Server Express service as part of our application. Depending on what the user is trying to do, we may have to attach a database, detach a database, back one up, etc. Sometimes the service might not be started before we attempt these operations. So we need to ensure the service is started. Here is where we are running into problems. Apparently the ServiceController.WaitForStatus(ServiceControllerStatus.Running) returns prematurely for SQL Server Express. What is really puzzling is that the master database seems to be immediately available, but not other databases. Here is a console application to demonstrate what I am talking about:
namespace ServiceTest
{
using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;
class Program
{
private static readonly ServiceController controller = new ServiceController("MSSQL$SQLEXPRESS");
private static readonly Stopwatch stopWatch = new Stopwatch();
static void Main(string[] args)
{
stopWatch.Start();
EnsureStop();
Start();
OpenAndClose("master");
EnsureStop();
Start();
OpenAndClose("AdventureWorksLT");
Console.ReadLine();
}
private static void EnsureStop()
{
Console.WriteLine("EnsureStop enter, {0:N0}", stopWatch.ElapsedMilliseconds);
if (controller.Status != ServiceControllerStatus.Stopped)
{
controller.Stop();
controller.WaitForStatus(ServiceControllerStatus.Stopped);
Thread.Sleep(5000); // really, really make sure it stopped ... this has a problem too.
}
Console.WriteLine("EnsureStop exit, {0:N0}", stopWatch.ElapsedMilliseconds);
}
private static void Start()
{
Console.WriteLine("Start enter, {0:N0}", stopWatch.ElapsedMilliseconds);
controller.Start();
controller.WaitForStatus(ServiceControllerStatus.Running);
// Thread.Sleep(5000);
Console.WriteLine("Start exit, {0:N0}", stopWatch.ElapsedMilliseconds);
}
private static void OpenAndClose(string database)
{
Console.WriteLine("OpenAndClose enter, {0:N0}", stopWatch.ElapsedMilliseconds);
var connection = new SqlConnection(string.Format(#"Data Source=.\SQLEXPRESS;initial catalog={0};integrated security=SSPI", database));
connection.Open();
connection.Close();
Console.WriteLine("OpenAndClose exit, {0:N0}", stopWatch.ElapsedMilliseconds);
}
}
}
On my machine, this will consistently fail as written. Notice that the connection to "master" has no problems; only the connection to the other database. (You can reverse the order of the connections to verify this.) If you uncomment the Thread.Sleep in the Start() method, it will work fine.
Obviously I want to avoid an arbitrary Thread.Sleep(). Besides the rank code smell, what arbitary value would I put there? The only thing we can think of is to put some dummy connections to our target database in a while loop, catching the SqlException thrown and trying again until it works. But I'm thinking there must be a more elegant solution out there to know when the service is really ready to be used. Any ideas?
EDIT: Based on feedback provided below, I added a check on the status of the database. However, it is still failing. It looks like even the state is not reliable. Here is the function I am calling before OpenAndClose(string):
private static void WaitForOnline(string database)
{
Console.WriteLine("WaitForOnline start, {0:N0}", stopWatch.ElapsedMilliseconds);
using (var connection = new SqlConnection(string.Format(#"Data Source=.\SQLEXPRESS;initial catal
using (var command = connection.CreateCommand())
{
connection.Open();
try
{
command.CommandText = "SELECT [state] FROM sys.databases WHERE [name] = #DatabaseName";
command.Parameters.AddWithValue("#DatabaseName", database);
byte databaseState = (byte)command.ExecuteScalar();
Console.WriteLine("databaseState = {0}", databaseState);
while (databaseState != OnlineState)
{
Thread.Sleep(500);
databaseState = (byte)command.ExecuteScalar();
Console.WriteLine("databaseState = {0}", databaseState);
}
}
finally
{
connection.Close();
}
}
Console.WriteLine("WaitForOnline exit, {0:N0}", stopWatch.ElapsedMilliseconds);
}
I found another discussion dealing with a similar problem. Apparently the solution is to check the sys.database_files of the database in question. But that, of course, is a chicken-and-egg problem. Any other ideas?
Service start != database start.
Service is started when the SQL Server process is running and responded to the SCM that is 'alive'. After that the server will start putting user databases online. As part of this process, it runs the recovery process on each database, to ensure transactional consistency. Recovery of a database can last anywhere from microseconds to whole days, it depends on the ammount of log to be redone and the speed of the disk(s).
After the SCM returns that the service is running, you should connect to 'master' and check your database status in sys.databases. Only when the status is ONLINE can you proceed to open it.