“Current thread must be set to single thread apartment (STA) mode before OLE calls can be made”
While calling a OpenFileDialog (https://msdn.microsoft.com/en-us/library/system.windows.forms.openfiledialog.aspx) via Installer class as a Custom Action in the Visual Studio Setup project, it just hangs there.
When we attach a debugger we receive an error: "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. This exception is only raised if a debugger is attached to the process."
The same code works fine on Win XP and 2003. It only occurs on Win Vista and higher version of Operating Systems
The sample code is mentioned below:
MessageBox.Show("Cannot Locate Config File");
MessageBox.Show("Pid : " + System.Diagnostics.Process.GetCurrentProcess().Id.ToString());
String Location = String.Empty;
OpenFileDialog frm = new OpenFileDialog();
frm.InitializeLifetimeService();
frm.Filter = "Config Files (*.config)|*.config| (*.xml)|*.xml";
frm.Title = "Browse Config file";
DialogResult ret = frm.ShowDialog();
if (ret == DialogResult.OK)
Location = frm.FileName;
InitializeComponent();
The problem is that the MSI thread is running as an MTA thread, but the FileDialog.ShowDialog requires an STA thread. To achieve this you will need to start a STA background thread and call the dialog from that thread. Basically I did the following:
- Added the DialogState class. This keeps track of the input and output for the thread.
- Added the STAShowDialog function. This function takes a FileDialog, calls ShowDialog on a background STA thread, and then returns the results.
- Changed the call from DialogResult ret = frm.ShowDialog(); to DialogResult ret = STAShowDialog(frm);
You may need to add exception handling separately.
Here is the complete code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.Windows.Forms;
namespaceWindowsFormsApplication1
{
[RunInstaller(true)]
public partial class Installer1 : Installer
{
publicInstaller1()
{
MessageBox.Show("Cannot Locate Config File");
MessageBox.Show("Pid : " + System.Diagnostics.Process.GetCurrentProcess().Id.ToString());
String Location = String.Empty;
OpenFileDialog frm = new OpenFileDialog();
frm.InitializeLifetimeService();
frm.Filter = "Config Files (*.config)|*.config| (*.xml)|*.xml";
frm.Title = "Browse Config file";
DialogResultret = STAShowDialog(frm);
if (ret == DialogResult.OK)
Location = frm.FileName;
MessageBox.Show(Location);
InitializeComponent();
}
/* STAShowDialog takes a FileDialog and shows it on a background STA thread and returns the results.
* Usage:
* OpenFileDialog d = new OpenFileDialog();
* DialogResult ret = STAShowDialog(d);
* if (ret == DialogResult.OK)
* MessageBox.Show(d.FileName);
*/
private DialogResult STAShowDialog(FileDialogdialog)
{
DialogState state = new DialogState();
state.dialog = dialog;
System.Threading.Thread t = new System.Threading.Thread(state.ThreadProcShowDialog);
t.SetApartmentState(System.Threading.ApartmentState.STA);
t.Start();
t.Join();
returnstate.result;
}
}
/* Helper class to hold state and return value in order to call FileDialog.ShowDialog on a background thread.
* Usage:
* DialogState state = new DialogState();
* state.dialog = // <any class that derives from FileDialog>
* System.Threading.Thread t = new System.Threading.Thread(state.ThreadProcShowDialog);
* t.SetApartmentState(System.Threading.ApartmentState.STA);
* t.Start();
* t.Join();
* return state.result;
*/
public class DialogState
{
public DialogResultresult;
public FileDialogdialog;
public voidThreadProcShowDialog()
{
result = dialog.ShowDialog();
}
}
}
Comments
Anonymous
March 23, 2011
Thank you so much for this post. Life savers!Anonymous
May 10, 2011
Thank you!Anonymous
February 25, 2012
This is Awh some, many many thanks :)Anonymous
March 14, 2012
i am getting at DialogState state = new DialogState(); is there any namespace fro that we have to useAnonymous
March 18, 2012
You would need to define it. public class DialogState { public DialogResult result; public FileDialog dialog; public void ThreadProcShowDialog() { result = dialog.ShowDialog(); } }Anonymous
April 29, 2012
That's owesome! I just love internet!Anonymous
June 27, 2012
The comment has been removedAnonymous
December 30, 2012
Thanks, nice codeAnonymous
July 08, 2013
Thank a lot my problem resolved!Anonymous
September 17, 2013
Thank you very much! You solved my problem! :-)Anonymous
December 02, 2013
great!! article. it helped me alot.Anonymous
January 15, 2014
Thank you, After reading to many blogs,My problem resolved here Thanks again for wonderful post..Anonymous
March 14, 2014
ExcellentAnonymous
April 17, 2014
Thanks, Great code - I love internetAnonymous
July 10, 2014
Excellent post , while reading it self , my problem got resolved.Anonymous
July 15, 2014
Thank you, is working nowAnonymous
September 02, 2014
Thanks man!!! Very very good!!Anonymous
September 11, 2014
Thanks, very helpful and neat!Anonymous
January 06, 2015
Thank YouAnonymous
January 27, 2015
Thanks Man, you saved my time....Anonymous
February 05, 2015
Thank you!!Anonymous
February 10, 2015
Great workAnonymous
May 21, 2015
Thank youAnonymous
May 21, 2015
Extremly relievingAnonymous
May 21, 2015
Extremly usefulAnonymous
July 19, 2015
More thanks 2 u.Anonymous
November 11, 2015
Many thanks!! You saved my day today.Anonymous
November 12, 2015
Thanks for this post, also saved my day!Anonymous
September 21, 2016
Thank you so much. All these years later and it is still saving people!Anonymous
October 12, 2016
Thanks.. Problem Solved.Anonymous
October 18, 2016
Thank You ! Really Helpful.