程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 子線程使用FolderBrowserDialog的問題延伸

子線程使用FolderBrowserDialog的問題延伸

編輯:關於.NET

Q:子線程如何使用FolderBrowserDialog

A:

private void button1_Click(object sender, EventArgs e)
    ...{
      System.Threading.Thread s = new System.Threading.Thread(new System.Threading.ThreadStart(test));
      s.ApartmentState = System.Threading.ApartmentState.STA;
      s.Start();
    }
  
    public void test()
    ...{
      System.Windows.Forms.FolderBrowserDialog dlg = new FolderBrowserDialog();
      dlg.ShowDialog();
    }

以上代碼簡單的演示了FolderBrowserDialog在子線程中的使用,其中設置線程的ApartmentState為System.Threading.ApartmentState.STA是關鍵的語句。在.net2.0中應該使用

s.SetApartmentState(System.Threading.ApartmentState.STA);

如果沒有上述設置會報如下錯誤

在可以調用 OLE 之前,必須將當前線程設置為單線程單元(STA)模式。

如果你了解com的線程模型的話,應該已經清楚上面的問題根本了。

我們先看一下 FolderBrowserDialog的實現方法,這個控件實現是通過ole的.可以用Reflector.exe看一下他的代碼,調用了幾個windows shell32的api。

[SuppressUnmanagedCodeSecurity]
internal class Shell32
...{
  // Methods
  public Shell32();
  [DllImport("shell32.dll", CharSet=CharSet.Auto)]
  public static extern IntPtr SHBrowseForFolder([In] UnsafeNativeMethods.BROWSEINFO lpbi);
  [DllImport("shell32.dll")]
  public static extern int SHCreateShellItem(IntPtr pidlParent, IntPtr psfParent, IntPtr pidl, out FileDialogNative.IShellItem ppsi);
  public static int SHGetFolderPathEx(ref Guid rfid, uint dwFlags, IntPtr hToken, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPath, uint cchPath);
  [DllImport("shell32.dll", EntryPoint="SHGetFolderPathEx")]
  private static extern int SHGetFolderPathExPrivate(ref Guid rfid, uint dwFlags, IntPtr hToken, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPath, uint cchPath);
  [DllImport("shell32.dll")]
  public static extern int SHGetMalloc([Out, MarshalAs(UnmanagedType.LPArray)] UnsafeNativeMethods.IMalloc[] ppMalloc);
  [DllImport("shell32.dll", CharSet=CharSet.Auto)]
  public static extern bool SHGetPathFromIDList(IntPtr pidl, IntPtr pszPath);
  [DllImport("shell32.dll")]
  public static extern int SHGetSpecialFolderLocation(IntPtr hwnd, int csidl, ref IntPtr ppidl);
  [DllImport("shell32.dll")]
  public static extern int SHILCreateFromPath([MarshalAs(UnmanagedType.LPWStr)] string pszPath, out IntPtr ppIdl, ref uint rgflnOut);
}

COM提供的線程模型共有三種:Single-Threaded Apartment(STA 單線程套間)、Multithreaded Apartment(MTA 多線程套間)和Neutral Apartment/Thread Neutral Apartment/Neutral Threaded Apartment(NA/TNA/NTA 中立線程套間,由COM+提供)。

STA 一個對象只能由一個線程訪問,相當於windows的消息循環,實現方式也是通過消息循環的,ActiveX控件、OLE文檔服務器等有界面的,都使用STA的套間。MTA 一個對象可以被多個線程訪問,即這個對象的代碼在自己的方法中實現了線程保護,保證可以正確改變自己的狀態。

所以創建和訪問一個activex或者ole對象時,必須設置線程模式為sta。

稍微有些多線程使用經驗的人會發現用Control.Invoke方法也可以成功調用ole對象,比如上面的例子改為

private void Form1_Load(object sender, EventArgs e)
    ...{
      System.Threading.Thread s = new System.Threading.Thread(new System.Threading.ThreadStart(test));
      //s.SetApartmentState(System.Threading.ApartmentState.STA);
      s.Start();
    }
  
    public delegate void dtest();
  
    public void test()
    ...{
      this.Invoke(new dtest(invokeTest));
    }
  
    public void invokeTest()
    ...{
      System.Windows.Forms.FolderBrowserDialog dlg = new FolderBrowserDialog();
      dlg.ShowDialog();
    }

其實使得這個調用成功的原因不是在於Invoke,還是線程模式。如果把main函數上邊的[STAThread] 去掉的話,文章開始處的錯誤仍然會發生。Invoke只是讓主線程來執行子線程的調用函數。[STAThread]在程序入口處即將主線程置為sta模式,如果沒有這句話將置為mta模式。而且線程模型一旦確定將不可以更改,所以你無法在其他地方用代碼來設置主線程的線程模型。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved