Powershell Remoting and "Running startup script threw an error"

Previously I was running a number of remote tests on a new Powershell module to ensure it performed as expected when invoked via Powershell Remoting.

To do this I had created a new Powershell Session Configuration called 'test-config' using

New-PSSessionConfiguration

And set the StartupScript property for this configuration to a one-line script which imported my test module. This ensured the cmdlets in the module were available was soon as the session was created.

However, as soon I tried to create a new remote session I received the error


Running startup script threw an error: NEGATIVE TEST 52
    + CategoryInfo          : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [], RemoteException
    + FullyQualifiedErrorId : PSSessionOpenFailed

This was a test exception thrown by my test module during import. What surprised me is that the exception is thrown inside a try/catch block and dealt with inside the module. It should not leaked outside or continue to propagate up the call stack.

I double checked the module code and decided to rewrite the simple startup script so it threw and handled a simple exception.

try {
    throw 'handled exception'
}
catch {
    Write-Warning 'caught handled exception'
}

This time when I tried to create a new remote session I got the following output:

WARNING: caught handled exception

Running startup script threw an error: handled exception
    + CategoryInfo          : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [], RemoteException
    + FullyQualifiedErrorId : PSSessionOpenFailed

This was unexpected. The exception is clearly caught, handled and not re-thrown in the startup script. So why am I am still seeing a fatal error from Powershell reporting the exception.

Lets take a deeper look at the 'RemoteException', specifically:
$_.Exception.SerializedRemoteException.StackTrace


   at System.Management.Automation.ServerRunspacePoolDriver.HandleRunspaceCreated(Object sender, RunspaceCreatedEventArgs args)
   at System.EventHandler`1.Invoke(Object sender, TEventArgs e)
   at System.Management.Automation.Runspaces.RunspacePool.OnRunspaceCreated(Object source, RunspaceCreatedEventArgs args)
   at System.Management.Automation.Runspaces.Internal.RunspacePoolInternal.CreateRunspace()
   at System.Management.Automation.Runspaces.Internal.RunspacePoolInternal.OpenHelper()

Now lets use Reflector to take a look at what's happening in the MSIL:
...
ArrayList dollarErrorVariable = (ArrayList) powershell.Runspace.GetExecutionContext.DollarErrorVariable;
        if (dollarErrorVariable.Count > 0)
        {
            string str2 = (dollarErrorVariable[0] as ErrorRecord).ToString();
            throw tracer.NewInvalidOperationException("RemotingErrorIdStrings", PSRemotingErrorId.StartupScriptThrewTerminatingError.ToString(), new object[] { str2 });
        }
...

We can see that Powershell is checking the 'DollarErrorVariable' ($ERROR) and if this is greater than 0 it throws the terminating error.
There is no provision to check whether the error was caught or handled, which explains why the startup script still fails.

I strongly suspect this is a bug. However, at least we can implemented a simple workaround. We need to ensure we clear $ERROR() after we catch the exception. Of course, its important to note we must clear the variable at global scope, which might upset some Administrators. So make sure you only clear it when you need to: when being run as a startup script via remoting

try {
    throw 'handled exception'
}
catch {
    Write-Warning 'caught handled exception'

    # remote is set if the script is being run via Remoting
    if($remote -and $startup) {
        $global:error.Clear()
    }
}




Powershell Remoting and Single Threaded Apartments (STA)

I've recently been testing a Powershell module I developed to see if it worked as expected via Powershell Remoting.


The module must run in Single Threaded Apartment mode (STA) because of a dependency on the MSBuild engine. Normally, when running locally you would need to launch Powershell with the -STA switch. However you cannot specify this switch when using remoting since Powershell is started and hosted by WSMan/WinRM (wsmprovhost.exe)
In this case the Powershell startup configuration is managed via the following family of cmdlets:

Set-PSSessionConfiguration
New-PSSessionConfiguration


You can also explore the session configuration in the WSMAN namespace:
cd WSMan::localhost\Plugin


   WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Plugin

Name                      Type                 Keys
----                      ----                 ----
Event Forwarding Plugin   Container            {Name=Event Forwarding Plugin}
microsoft.powershell      Container            {Name=microsoft.powershell}
Microsoft.PowerShell32    Container            {Name=Microsoft.PowerShell32}
microsoft.ServerManager   Container            {Name=microsoft.ServerManager}
SEL Plugin                Container            {Name=SEL Plugin}
WMI Provider              Container            {Name=WMI Provider}

The default configuration for a Remoting Session is 'microsoft.powershell', which will start Powershell in MTA.

To start a remote powershell session in STA mode you need to set the following InitializationParameters in the configuration.

pssessionthreadapartmentstate = STA
pssessionthreadoptions = UseNewThread

To test my module created a new PSSessionConfiguration, which also specified a StartupScript to automatically perform the import, and then specified the configuration when starting the remoting session:

New-PSSession -Computername SERVER1 -ConfigurationName TESTCONFIG

Incidentally, the configuration is persisted as an XML stream in the following registry location:
HKLM\Software\Microsoft\Windows\CurrentVersion\WSMAN\Plugin

Autosuggest email contacts with Active Directory Lookup (ASP.NET)

I've been working on a lot of back-end data access code lately and I needed a little change. So when I arrived in the office last Friday I boldly assigned a web-developer task to myself (its been at least year since I wrote some serious ASP.NET)
This particular (very low priority) task was based on a customer request for an email AutoSuggest TextBox. I sensed a Friday challenge.

In our web application we have a TextBox where the user can input additional email addresses of the people they would like notified when the workflow status of a particular process has changed.
For example, if this were an on-line shopping application (it is not), this field would contain email addresses of people that would be emailed when an item is now in stock. The email addresses need to be semicolon delimited and in full smtp format.
For example:
client1@contoso.com;client2@contoso.com

The Change Request was simple, the TextBox should be replaced with a control that acts like the 'To' field when composing an email in Outlook

In terms of behaviour: As a name is typed, the TextBox should 'autosuggest' matching contacts and when selected should display them in the TextBox. It should obviously be able to accept multiple contacts, in addition to contacts that do not exist (well, could not be looked up). In this case the source of the contacts for lookup was the corporate Active Directory (and not just users, but email distribution groups as well). In addition, the control should display the full name and not the underlying email address, just as outlook does.

I was pleasantly surprised how easy this was!

Since our Web Application is ASP.NET 4.0, we already use jQuery throughout. With Google to rescue, I download and configured Drew Wilsons jQuery autosuggest plugin:
http://code.drewwilson.com/entry/autosuggest-jquery-plugin

The plugin handles keydown events and will query a specified URL supplying what the user has typed so far in the query string. In response it expects a JSON formatted array of suggestions. This sounded like a perfect match for a WCF WebService (which also turned out to be surprisingly simple):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text.RegularExpressions;
using System.DirectoryServices;

namespace ExampleAutosuggest
{
    [ServiceContract(Namespace = "")]
    [ServiceKnownType(typeof(string))]
    [ServiceKnownType(typeof(string[]))]
    public interface IContactSuggestionService
    {
        [OperationContract]
        [WebGet(UriTemplate = "/Search?q={q}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
        Contact[] Suggest(string q);
    }

    public class ContactSuggestionService : IContactSuggestionService
    {

        public Contact[] Suggest(string q)
        {
            DirectoryEntry de = new DirectoryEntry();
            de.Path = "LDAP://OU=Users,DC=prod,DC=ad,DC=contoso,DC=com";
            de.AuthenticationType = AuthenticationTypes.Secure;

            DirectorySearcher deSearch = new DirectorySearcher();
            deSearch.SearchRoot = de;

            deSearch.Filter = "(&(objectClass=user) (cn=" + q + "*))";
            deSearch.SizeLimit = 10;
            SearchResultCollection results = deSearch.FindAll();

            // Get the SMTP email address for the users
            IList list = new List();
            foreach (SearchResult result in results) {
                try {
                    DirectoryEntry der = result.GetDirectoryEntry();
                    string email = der.Properties["mail"].Value.ToString();
                    string name = der.Properties["name"].Value.ToString();

                    list.Add(new Contact(name, email));
                }
                catch (Exception) {

                }
            }
            return list.ToArray();
        }
    }

    [DataContract]
    public class Contact
    {
        public Contact(string Name, string Email)
        {
            this.DisplayName = Name;
            this.Email = Email;
        }
        [DataMember(IsRequired = true, Name = "name")]
        public string DisplayName { get; set; }
        [DataMember(IsRequired = true, Name = "value")]
        public string Email { get; set; }
    }
}

Download Project

Finally, I needed to make some changes to Drew's code so the control's state would survive both partial and full postbacks with ASP.NET. Drew's implementation saves the list of values to a hidden input field created by the script, this field only contains the underlying values, which may be different to what was displayed to the user and is only designed to be retrieved by the server during the PostBack. Once the Post Back all state is lost. This was address by adding a hidden form field to the page and supplying the elements id to the autosuggest code when it was attached. The autosuggest's code was then modified to serialise and deserialise its state (via JSON) to the hidden field whenever a contact was added or removed. The ASP.NET framework takes care of maintaining the value of the hidden field between postback and so the state of the autosuggest is also maintained. In the end we stored the serialised state of the auto-suggest directly in the database, which meant we could render it very quickly without having to perform Active Directory lookups on each contact before displaying the page. Im sure our DBA's would'nt be happy about this, but at least its not XML (which drives them crazy).

Download Customised Autosuggest

Note:
This Javascript is not perfect, but let me know if you find any bugs

How to check your Powershell Script is being run Remotely

Here's a little trick to determine if the current script is being run via Remoting;



$remote = (test-path variable:PSSenderInfo) -and
          (($PSSenderInfo -ne $null) -and
          ($PSSenderInfo.GetType().Name -eq 'PSSenderInfo'))

Calling an Oracle Function Returning a Scalar with NHibernate 2.1.2GA

There appear to be a few bugs in the way HNibernate 2.1.2GA handles Oracle functions. You can find the official documentation here:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/querysql.html#sp_query

Issue 1: Calling Functions and Procedures in an Oracle Package
The first bug appears in the 2.1.2 implementation of CalleableParserNHibernate.Engine.Query.CallableParser The CalleableParser class is reponsible for parsing 'call' expressions enclosed in '{ }' in mappings, for example: The Parse(...) method relays on the following RexEx expression to match the function names:
"\{[\S\s]*call[\s]+([\w]+)[^\w]"

Unfortunately this expression will not match names which exist in an Oracle package. This is because such names require a scope, separated by using standard '.' notation.
For example:
MyOraclePackage.MyFunction

In this scenario the above RegEx expression will match only 'MyOraclePackage', which results in a failed call.
(take a look at the posts here https://forum.hibernate.org/viewtopic.php?f=25&t=968269&p=2434379#p2434379)
At the moment our team have worked around this by creating wrapper functions that call the real function in the appropriate package, far from ideal at the moment, but the project is very short on time.

Issue 2: Return Scalar from Oracle Function
The second bug, which may be more difficult to fix arises when you try to return a scalar value from an Oracle Function, as expressed in the same example mapping: As required by NHibernate the Oracle function we have developed, called: 'get_id_from_name' returns an Oracle REFCURSOR which contains the scalar value. The problem arises when we try to determine the column name to put in out nHibernate mapping file.
If the column name is incorrect NHibernate throws a 'could not execute query exception', and if we look further into the ODP.NET Trace File (you have to turn this on with a Registry Editor) you'll see:
ODP error code=-1502; ODP message=Unable to find specified column in result set
So what's happening?
After running the function, NHibernate starts processing the resultset from the DataReader and eventually calls into some code that attempts to get the ordinal value (column index) for the named column. However, since the name we made up does not exist an exception is thrown. Certainly, there doesnt appear to be a way to specify the column names in a REFCURSOR from Oracle.

So how do we get the column name?
Well I still haven't figured this out, but after looking at it in the debugger I found a private member in Oracles DataReader, sadly I cant quite recall how I found it, but in our case it was always:
:B1

So the final mapping looks like: I spoke to our Oracle DBA's who recalled the Oracle usually refer to the results as :B1, :B2, etc. So if your having this problem, try :B1 as the column name

For reference these issues were encountered with Oracle 10G and ODP.NET 2.112.1.0

Catching custom errors/exceptions from Oracle procedures in nHiberntate

I'm lucky (or unlucky) enough to work with a pretty smart Oracle DBA on my current project. Although the fact that he is smart doesn't change my somewhat negative view of Oracle (purely for a developer tools standpoint).
Interestingly, the decision to use Oracle for this system came down to the number of in-house Oracle DBA's and Unix Administrators who far outnumber the Windows engineers (and who appear to be much harder to outsource/offshore)


Anyway, we ran into a problem on Friday which took us an entire afternoon to figure out. Our Oracle DBA had developed a PL/SQL procedure that would perform some calculations and populate another table with the results. It was decided that this particular task was best suited to PL/SQL for performance reasons, since it touched on a large number of entities in the database, whilst also doing a number of inserts.


The procedure in question look a little like this:

FUNCTION xxxx(...) RETURN INTEGER
IS
....
  -- Declare bespoke error codes
  e_invalid_cmp_class      EXCEPTION;
  PRAGMA EXCEPTION_INIT(e_invalid_cmp_class, -20001);


...
BEGIN


IF (v_cmp_class_check = FALSE) THEN
RAISE e_invalid_cmp_class;
END IF;
END xxxx;




As you can see our DBA was diligently raising useful error messages in the custom error range when something unexpected occurs. In fact I was alarmed that DBA's were capable of such well structured and organised code.
In this solution, all data access from the application passes through a generic .NET based data-layer, which is largely built on the nHibernate (think Repository pattern).
The problem was that the custom/user exceptions raised in PL/SQL would simply vanish. Indeed, it was as if the exceptions were not even being raised. nHibernate would execute the procedure and continue on as if the call had been successful. Conversely, standard Oracle exceptions did appear to work - for example, if we called a procedure that did not exist. Of course, our DBA was happy to blame nHibernate (as all DBAs do).


After double checking everything, we put together the smallest sample code we could to reproduce the problem. We eliminated nHibernate and our entire DAL, and ended up with about 10 lines of C# using ODP.NET. Still the exceptions were not being caught, or propagated. At this point, I was happy to blame Oracle and ODP.NET - however, this didn't help find the solution.


In the end we stumbled across if by trial and error:


You MUST the RAISE_APPLICATION_ERROR procedure, and not RAISE for user exceptions


Rewriting the procedure (shown below) ensured that the exceptions propagated through our DAL


There appears to be no clear documentation explaining the difference between this procedure and the language keyword 'RAISE'. However, without the procedure, user exceptions don't seem to arrive at the client. In testing our DBA was able to see them on the server, but the ODP.NET client simply ignore's or doesn't even get to see them.









Fake MacBook Pro Battery

I have a love hate relationship with that online Bizarre Bazaar called eBay, sometimes you can grab a genuine bargain or a part that nobody else seems to stock...but you have to be on your guard for fakes. In fact I've stopped buying specific goods on eBay because I've had to return fakes on too many occasions (well two occasions)

Last week Windows 7 recommended I replace by MacBook Pro battery (its a late 2007 model, so I have this luxury). I went to the Apple Store in Regent Street, London who advised a £99 replacement. Now I really like my MacBook Pro, but not enough to spend that much on a new battery, besides, I lost a bet on England to beat the USA in the World Cup so my wallet was feeling a little empty.

I went home and checked a few websites and inevitably ended up on eBay. Who would have thought - a new MacBook Pro battery for only £25! Direct from Hong Kong in a few days. I knew those Apple people were ripping me off :-) Obviously this was fake, so I shopped around and found a genuine local seller who appeared to be selling genuine batteries for around £55 - or so it appeared.

Fast forward and 3 days later and im looking at yet another fake! A more expensive and certainly more convincing fake. In fact, its one of the best fakes I think I have seen.Take a look at the photos and see what you think.

Genuine Battery
Take a look at the quality of the laser etched text, the font, spacing and kerning is all regular and consistent


Fake Battery
The text on the fake battery has irregular spacing, kerning, and is poorly aligned.


...and heres one more comparing some other differences



Its very convincing, and unless you take a close look, you could be fooled!
Again, I find myself returning yet another item to its seller and forking out the GDP of an African nation to the Apple Store

Before I go, here's a link to the DNG's (Digital Negatives) from my camera if you want to take a really close look! (They are pretty big 15MB each)


England vs Germany!? Can England possibly win?