Majority of PowerShell’s execution happens through Pipeline class. PowerShell’sConsoleHostconstructs a pipeline with user requested command +input and invokes the pipeline. This is true for any host built on top of PowerShell. Pipeline’s Invoke method creates a child thread and delegates execution to this child thread. Unfortunately in v1.0 release, there is no way to set the ApartmentState of this child thread. By default CLR initializes new threads as “MTA” and there is no way to change the ApartmentState once the thread is started. As a result of this a cmdlet always runs in MTA thread when invoked using Pipeline.

There is no easy solution to this problem. If you want to execute your cmdlet in “STA” apartment, your cmdlet should create a new thread + change apartment state to “STA” -> then execute the task in this child thread. One way of achieving this is creating a new cmdlet with the same functionality as invoke-expression cmdlet to take “Apartment” as the parameter (Invoke-Expression is a sealed class, so we cannot extend from this class).I am calling this cmdlet “Invoke-Apartment”. Here is how it works:

The cmdlet takes an “ApartmentState” and executes “Expression” in that apartment. It creates a new Thread with the user requested ApartmentState -> constructs a ScriptBlock with the expression supplied by the user -> executes the ScriptBlock in this child thread.

2.A new thread is created and ApartmentState is set to the one requested by the user (if current apartment state is not what user requested).

3.ScriptBlock is invoked in the new thread while the parent thread waits for task completion.

4.ScriptBlock.InvokeReturnAsIs() needs a runspace to execute the script and it looks for runspace in the Thread-Local storage (TLS)

5.Since we are executing in a different thread, we need to set Runspace.DefaultRunspace on this thread as well. [Runspace.DefaultRunspace = somerunspace actually updates the TLS]

Note: It is not recommended to execute cmdlets out of the pipeline context as described here as Runspace cannot handle multiple execuitons at the same time like this. Also there is a potential risk of compromising global variables state.

Thanks

Krishna Vutukuri

Microsoft PowerShell Development

This posting is provided “AS IS” with no rights and warranties.

/*

The code here is provided AS IS with no rights or warranties of any kind. Use it at your own discretion.

The Windows Live ID Client SDK documentation shows you quite well how to sign in silently or with the

10 years ago

oOgerryOo

Hey,

I’m Gerry.

Just saying hey – I’m new.

10 years ago

xztheericzx

i’m eric. joining a couple boards and looking

forward to participating. hehe unless i get

too distracted!

eric

11 years ago

Scott Jones

Thanks Krishna, for the useful technique. But how about addressing more interactive shell scenarios? I would like to use PS to test my WPF-based controls. I could wrap entire test scripts in Invoke-Apartment, but that does not allow me to do ad hoc interactive testing. The problem is that multiple Invoke-Apartment invocations result in distinct threads, and I’m back to MTA/STA exceptions again. What I think I need is some kind of PSObject wrapper for my WPF types, which can proxy all method/prop accesses through a persistent Invoke-Apartment instance (same thread). Anyone out there done something like that?

11 years ago

DarthHack

Bless you Krishna Vutukuri! You are the MAN!

I pulled my hair out for a couple of days fighting an interaction between PowerShell and some older InstallShield installers. Specifically I have mostly seen the interaction with installers that use _ISDEL.EXE, though I have had one installer hang intermittently that doesn’t use this binary. Hopefully this interaction is limited to installers, so the general community isn’t being frustrated by this issue.

Some older applications freeze when run from PowerShell because of a negative interaction with PowerShell’s MTA ApartmentState. Running the same applications through Invoke-Apartment ‘STA’ avoids this problem allows them to run without freezing.

ScriptBlock.Invoke needs a runspace. It looks for runspace in Thread Local Storage (TLS). The TLS for runspace can be set using Runspace.DefaultRunspace. So you can invoke a ScriptBlock outside of the context of Runspace Thread. Runspaces are not thread safe, so dont use the same runspace in 2 different threads. So to invoke a scriptblock outside of the context of Runspace thread, you need to create a new runspace which defeats the purpose as custom variables / functions / aliases and other state information may not be available directly in this new Runspace.

i had thought of this, but it would only work for doing C# stuff , but it seems you can invoke a scriptblock like this, outside the context of the runspace thread.. Is it possible to invoke a scriptblock outside the context of a runspace? or being inside a cmdlet, is it still running in the context of the runspace?

11 years ago

Joris van Lier

Interesting post, it’s good to know a little about the inner workings of powershell, please keep posting this kind of "Programming with PowerShell" info..