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
}


8 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
  4. I am trying to execute a Crystal Report (.rpt file). Pls review this script:

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

    [System.Reflection.Assembly]::LoadWithPartialName('CrystalDecisions.Shared')
    Write-Progress -Activity "CrystalDecisions.Shared loaded #1..."
    Start-Sleep -s 2
    [System.Reflection.Assembly]::LoadWithPartialName('CrystalDecisions.CrystalReports.Engine')
    Write-Progress -Activity "CrystalDecisions.CrystalReports.Engine loaded #2"
    Start-Sleep -s 2

    $rpt = "x.rpt"
    $exportPDF = "x.pdf"

    $report = New-Object CrystalDecisions.CrystalReports.Engine.ReportDocument
    Write-Progress -Activity "Report Object created... #3"
    Start-Sleep -Seconds 5

    $report.Load($rpt)
    Write-Progress -Activity "Report loading succeded...#4"
    Start-Sleep -Seconds 5

    $report.SetDatabaseLogon('adegool','Ad36OOl!!')
    Write-Progress -Activity "Logon Credential set...#5"
    Start-Sleep -Seconds 5

    $report.Refresh()
    $report.ExportToDisk("PortableDocFormat", $exportPDF)
    Write-Progress -Activity "Report Exported...#6"
    Start-Sleep -Seconds 5
    $report.Dispose()
    $report.close()

    PowerShell is crashing on the $report.Load($rpt) line - crashing while trying to load the report. Any thoughts as to why?

    Env: PowerShell version 1.0 & Windows 10

    ReplyDelete
  5. Yes; I have tried running through the ISE and through PowerShell natively. Crashes in both cases on the same line.

    ReplyDelete
  6. Could this be an issue of 32-bit versus 64-bit? The CrystalDecisions library that am using is 32-bit while PowerShell is 64-bit running on a 64-bit machine.

    ReplyDelete
  7. Awesome issue. Try installing EXO module v 2.0.4 or above, then run https://docs.microsoft.com/en-us/powershell/scripting/samples/selecting-items-from-a-list-box?view=powershell-5.1 which works like a charm. Now add "Connect-ExchangeOnline" at the end of the script and run it again in a new PS window. Bleaaach! And again... and... why is it efing working now?

    ReplyDelete