Wednesday, June 15, 2011

PowerShell GUI Errors Referencing STA or Single Thread Apartment

So I've been working with Powershell GUIs for a bit now and run into some errors while running some of my test forms. Whenever I'd click a certain drop-down list, I get a large error message with the following text:

The following exception occurred in the DataGridView:
System.Threading.ThreadStateException: Current thread must be set to
single thread apartment (STA) mode before OLE calls can be made.
Ensure that your Main function has STAThreadAttribute marked on it.
at
System.Windows.Forms.ComboBox.set_AutoCompleteSource(AutoCo
mpleteSource value)
at
System.Windows.Forms.DataGridViewComboBoxCellJnitializeEditingCo
ntrol(1nt32 rowlndeƧ Object initialFormattedValue
DataGridViewCellStyle dataGridViewCellStyle)
at
System.Windows.Forms.DataGridViewJnitializeEditingControlValue(Dat
aGridViewCellStyle& dataGridViewCellStyle. DataGridViewCell
dataGridViewCell)

I found through a PowerShell forum that this is because PowerShell, by default, runs in multi-threaded apartment mode (MTA). For some GUI aspects, it is required to run in single-threaded aparetment mode (STA).

A quirk of working with different editors is knowing the difference and which mode loads by default. As an example, when working with PowerShell ISE, the integrated shell loads in STA mode. However, by default, PowerShell (itself) loads in MTA mode unless specified otherwise.

You can execute powershell in STA mode simply by adding a -STA switch when calling the powershell executable.

You can check which mode you're running in by running the following:

[threading.thread]::CurrentThread.GetApartmentState()

This will either return STA or MTA depending on what context powershell is being run in.

I haven't figured out how to programmatically set the apartment state inside of a script but I will repost that information if/when I find it.

A workaround for this problem is to add this snippet of code at the start of your script:

if ([threading.thread]::CurrentThread.GetApartmentState() -eq "MTA") {
   & $env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe -sta $MyInvocation.ScriptName
   Exit
}


3 comments:

  1. I have the same problems but -MTA switch not work in PS1.

    ReplyDelete
  2. Are you sure your state is in Single and not Multi? Have you tried to run your code through the ISE or through PowerShell natively? What OS version and PowerShell version?

    ReplyDelete
  3. Also, it would appear that placement of the STA switch on the command line is important.

    $env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe -sta $MyInvocation.ScriptName
    [threading.thread]::CurrentThread.GetApartmentState() = STA

    $env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe $MyInvocation.ScriptName -sta
    [threading.thread]::CurrentThread.GetApartmentState() = MTA

    ReplyDelete