1. 程式人生 > >C# OpenFileDialog.ShowDialog 打不開,程式無響應(錯誤的解決)

C# OpenFileDialog.ShowDialog 打不開,程式無響應(錯誤的解決)

1:C# OpenFileDialog.ShowDialog 打不開,程式無響應

環境:win7 .Net framework2.0
現象; c#寫的一個程式,在xp下點選檔案開啟按鈕沒有任何問題,但在我的win7下,點選則不彈出開啟檔案對話方塊,程式忙且無響應
網上搜索其他人也出現過這樣的問題,且有些計算機行有些不行。
給出解決辦法為設定openFileDialog開啟的預設目錄。
可是設定了後仍然不行,在win7下,點選開啟無法彈出開啟檔案對話方塊且程式忙,無響應
繼續搜尋,發現一個帖子也是說這個問題,大致是主執行緒必須是單執行緒單元,否則可能會導致此問題,應在主執行緒上加 [STAThread] 屬性

http://social.msdn.microsoft.com/Forums/zh-CN/vcgeneral/thread/105702a4-475d-4209-9730-c02cef7895e2
更改main函式的宣告如下,問題解決。
[STAThread]
public static void Main(string[] args)

網上找到的關於[STAThread]的一些說明:
[STAThread]
STAThread:Single     Thread     Apartment Thread.(單一執行緒單元執行緒)
[]是用來表示Attributes;

[STAThread]
是一種執行緒模型,用在程式的入口方法上(在C#和VB.NET裡是Main()方法),來指定當前執行緒的ApartmentState 是STA。用在其他方法上不產生影響。在aspx頁面上可以使用AspCompat = "true" 來達到同樣的效果。這個屬性只在  Com  Interop  有用,如果全部是  managed  code  則無用。簡單的說法:[STAThread]指示應用程式的預設執行緒模型是單執行緒單元 (STA)。啟動執行緒模型可設定為單執行緒單元或多執行緒單元。如果未對其進行設定,則該執行緒不被初始化。也就是說如果你用的.NET Framework,並且沒有使用COM Interop,一般不需要這個Attribute。其它的還有MTA(多執行緒套間)、Free  Thread(自由執行緒)。

[STAThread] attribute指示應用程式的 COM 執行緒模型是單執行緒單元。
而於此對應的多執行緒單元則是 [MTAThread] (多執行緒單元執行緒)

COM 執行緒模型只適用於使用 COM interop 的應用程式。如果將此屬性應用到不使用 COM interop 的應用程式,將沒有任何效果。

COM 執行緒模型可設定為單執行緒單元或多執行緒單元。如果應用程式執行緒實際呼叫了 COM 元件,則僅為 COM interop 初始化該執行緒。如果沒有使用 COM interop,則不初始化該執行緒。

我又在網路上找了兩篇文章或許更能說明這個問題。我沒有翻譯,文章的大意是,由於很多COM在.NET環境下如果使用多執行緒的話,會導致引用的COM不能正常執行,而如果不宣告程式為STAThread的話,.NET就會自動使用多執行緒來提高效率,這樣就會導致不可預知的後果。

Q. When I create a c# project from scratch in VS.NET, the generated code always have a [STAThread] attribute above the main routine. What does the STAThread attribute really do? Can I change it to MTAThread instead? I have searched website and books, no one seems to explain this well.

Asked by anon. Answered by the Wonk on February 17, 2003


A.

The STAThreadAttribute marks a thread to use the Single-Threaded COM Apartment if COM is needed. By default, .NET won't initialize COM at all. It's only when COM is needed, like when a COM object or COM Control is created or when drag 'n' drop is needed, that COM is initialized. When that happens, .NET calls the underlying CoInitializeEx function, which takes a flag indicating whether to join the thread to a multi-threaded or single-threaded apartment.

A multi-threaded apartment (MTA) in COM is more efficient, since any of a number of RPC threads from a pool can be used to handle a request. However, an object on the MTA thread needs to protect itself from multiple threads accessing it at the same time, so that efficiency comes at a cost.

The single-thread apartment (STA) in COM is inherently single-threaded and therefore no additional thread synchronization is needed. The STA is implemented using the thread's Windows message queue, which is how requests to objects on an STA are serialized. Because of how the STA thread is implemented, calls to objects on that thread are serialized with Windows message handling on that thread, making sure that everything, both the COM objects and the underlying windowing objects, e.g. HWNDs, are all synchronized. This is necessary for UI-oriented COM objects, like controls and drag 'n' drop, which must also be synchronized together with the UI.

When COM is needed .NET will call CoInitializeEx, picking the MTA by default because it's more efficient. However, to get the synchronization needed for controls, windows and drag 'n' drop, you need to mark a thread's entry point with the STAThreadAttribute to let .NET know to initialize the UI thread on the STA. All of the VS.NET project templates put that attribute in to make sure you don't forget:

[STAThread]
static void Main() {...}

Be very careful to leave that STAThreadAttribute just where it is, or things can go all wacky and you won't know why.

2:ShowDialog() 錯誤的解決

首先,一個類裡,有個linkLabel1

private OpenFileDialog openFileDialog1;
private DialogResult result;

private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {

            openFileDialog1 = new OpenFileDialog();
            string patch = Application.StartupPath + "\\LOG\\";
            openFileDialog1.InitialDirectory = patch;
            openFileDialog1.Filter = "xls files (*.xls)|*.xls";

            result = openFileDialog1.ShowDialog();

            if (result == DialogResult.OK)
            {
                if (openFileDialog1.FileName != "")
                {
                    Process.Start(openFileDialog1.FileName);
                }
               
            }

           
        }

就會報 
在可以呼叫 OLE 之前,必須將當前執行緒設定為單執行緒單元(STA)模式。請確保您的 Main 函式帶有 STAThreadAttribute 標記。 只有將偵錯程式附加到該程序才會引發此異常。

在測試小程式裡沒有問題,當移到大程式裡就這樣的問題了。可能是執行緒多的原因。解決辦法就是新增執行緒,程式碼如下

private Thread invokeThread;

private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            openFileDialog1 = new OpenFileDialog();
            openFileDialog1.InitialDirectory = patch;
            openFileDialog1.Filter = "xls files (*.xls)|*.xls";

            invokeThread = new Thread(new ThreadStart(InvokeMethod));
            invokeThread.SetApartmentState(ApartmentState.STA);
            invokeThread.Start();
            invokeThread.Join();

            if (result == DialogResult.OK)
            {
                if (openFileDialog1.FileName != "")
                {
                    Process.Start(openFileDialog1.FileName);
                }

            }
        }

private void InvokeMethod()
        {
            result = openFileDialog1.ShowDialog();
        }

問題得到解決