none
STAThreadAttributeについて RRS feed

  • 質問

  • おはようございます。

    VB2010+SQLServer2008Expressを使用しています。

    プログレスバーによる進捗状況を確認できるフォームを作成し、この中にバックグランドワーカーなどを含めて、
    汎用的に使用できるようにしてあります。

    処理の内容としては、テキストファイルをOpenFileDialogで選択し、その中身をDBに登録するというものです。
    下記の内容をフォームのCommadnButton_Clickイベントにセットしてあります。

        TimecardFile = Filecontrol.GetFileName      → テキストファイルを取得(OpenFileDialog使用)

            Dim MaxCount As Integer = Filecontrol.GetRows(Filecontrol.GetLines(TimecardFile))

        
            Dim pgb As New MyLibrary.frmProgressbar2( _
                        "タイムカード取込中", _
                        New DoWorkEventHandler(AddressOf DoWork_ImportTimecard), _
                        MaxCount)

            Dim result As DialogResult = pgb.ShowDialog(Me)

    こちらを実行すると

    OLE が呼び出される前に、現在のスレッドが Single Thread Apartment (STA) モードに設定されていなければなりません。Main 関数に STAThreadAttribute が設定されていることを確認してください。 この例外はデバッガーがプロセスにアタッチされている場合にのみ発生します。

    というエラーが発生します。

    以前、バックグランド処理内でOpenFileDialogを使用してはいけないとのアドバイスをいただいたのですが、今回の状況では、OpenFileDialogが終了してから
    バックグランド処理に入っているつもりです。

    STAThreadAttributeを調べてみたところ、Main関数を持つクラスを作成すればよいとのことだったので、同じプロジェクト内に下記のようなクラスを追加しました。

    Imports System.Windows.Forms
    Imports System.Threading

    Public Class clsMain
        <STAThread()> _
        Shared Sub Main()
            Dim t As New Thread

            Application.EnableVisualStyles()
            Application.SetCompatibleTextRenderingDefault(False)
            Application.Run(New Form1)


        End Sub
    End Class

    しかし、結果は変わらず同じエラーが発生します。

    プログレスバーを呼び出すフォームと、プログレスバーをセットしてあるフォームに上記のクラスを作成してみて、実行してみても結果は
    変わりませんでした。

    MSDNをみると、

    Dim t As New Thread(AddressOf ThreadProc)

    t.SetApartmentState(ApartmentState.STA)

    をどこかに記述をする必要があるようですが、どこにどのように記述をすればよいかがわかりません。

    どうか、アドバイスをお願いいたします。

    2012年10月17日 23:06

回答

  • >このThreadを開始するという処理を加えるのでしょうか。

    Thread.Start メソッド呼び出しによってスレッドの実行が開始されます。

    が、.NET Framework 4 以降を使用しているなら、Thread や ThreadPool よりも Task の使用を検討した方がよいです。

    >いまいち、ヘルプなどを見ても、このあたりのつながりが良くわからなかったので。

    Thread クラスのコンストラクタのヘルプ

    http://msdn.microsoft.com/ja-jp/library/xx3ezzs2(v=vs.80).aspx

    の「解説」の項に

    >スレッドの作成時には、スレッドは実行を開始しません。スレッドの実行をスケジュールするには、Start メソッドを呼び出します。

    という説明があり、さらにその下方で使用例が示されています。

    • 回答としてマーク TI-cb400 2012年10月20日 12:06
    2012年10月18日 22:09

すべての返信

  • 以前、バックグランド処理内でOpenFileDialogを使用してはいけないとのアドバイスをいただいたのですが、今回の状況では、OpenFileDialogが終了してから
    バックグランド処理に入っているつもりです。

    そのアドバイスをどこで得られたのかは知りませんが、「バックグラウンド処理内でOpenFileDialogだけを使用してはいけない」ではなく正確には「バックグラウンド処理内ではOpenFileDialogを含む全てのGUI処理をしてはいけない」です。今回は多分 pgb.ShowDialog(Me) が問題となっています。

    このことはBackgroundWorkerクラスでも説明されています。

    DoWork イベント ハンドラーでユーザー インターフェイス オブジェクトを操作しないように注意する必要があります。 代わりに、ProgressChanged イベントと RunWorkerCompleted イベントを通じてユーザー インターフェイスと通信します。
    • 編集済み 佐祐理 2012年10月18日 0:14
    2012年10月18日 0:11
  • ご回答ありがとうございます。

    正確なご説明有難うございます。

    以前、自分が失敗したことは理解できるのですが、今回は、OpenFileDialogを使用して、ファイルの選択をして、
    その取得したファイルをバックグランドワーカーの中で利用しているだけなのですが、このような使い方でも
    問題があるのでしょうか。

    どうしても、ここの部分がわかりません。

    また、今回のコードと似た形で、FolderBrowserDialogを使用して、フォルダー内のファイル一覧を取得して
    連続して、テキストファイルの中身をDBに取り込む処理の場合は、特にエラーが出ません。

    もう少し、調べてみたいと思います。

    2012年10月18日 0:48
  • >その取得したファイルをバックグランドワーカーの中で利用しているだけなのですが、このような使い方でも
    >問題があるのでしょうか。

    本当にそれだけなら問題はなさそうです。

    が、どこに問題があるのかを、提示されたコード片から類推するのは難しいです。

    差支えなければ、もう少し広い範囲のコードを見せてください。

    >特にエラーが出ません。

    それは「ワーカースレッド内で GUI 操作をしても問題ない」ことの証明にはなっていません。

    問題が起きる場合があることが分かっていて、問題が起きる場合と起きない場合の判別が比較的困難であるため「避けるのがベスト」なのです。

    2012年10月18日 1:05
  • 今回は、OpenFileDialogを使用して、ファイルの選択をして、
    その取得したファイルをバックグランドワーカーの中で利用しているだけなのですが、このような使い方でも
    問題があるのでしょうか。
    ファイルを使用していいか悪いかではありません。GUI処理をしてはいけないのです。 pgb.ShowDialog(Me) は紛れもなくGUI処理です。
    2012年10月18日 1:24
  • BackgroundWorkerはスレッドプールを使いますから、おそらくMTAでしか動作できません。一方、WindowsフォームはSTAモデルに基づきますからBackgroundWorkerでは動作しないのだと思います。

    Dim t As New Thread(AddressOf ThreadProc)

    t.SetApartmentState(ApartmentState.STA)

    をどこかに記述をする必要があるようですが、どこにどのように記述をすればよいかがわかりません。

    BackgroundWorkerを使わずに独自にThreadを作成し、そのThreadに対してSetApartmentState(ApartmentState.STA)を実行し、そこでShowDialogを行います。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2012年10月18日 2:10
    モデレータ
  • おはようございます。

    いろいろとアドバイスをいただき、ありがとうございました。

    自分なりに色々と試してみたのですが、どうにもうまくいかず、一晩おいて改めてコードを確認したところ
    バックグランドワーカー内で、OpenFileDialogの消し忘れが見つかりました。

    こちらを削除したところ、問題なく動作をいたしました。

    お騒がせをして大変申し訳ありませんでした。

    ところで、せっかくなので教えていただきたいのですが、trapemiya様がおっしゃっている独自にThreadを
    作成するということなのですが、ヘルプを見ると、モジュールの物しかなく、今回のようにFormに記述をする場合
    実行したい場所で下記の記述を行い、このThreadを開始するという処理を加えるのでしょうか。

    Dim t As New Thread(AddressOf ThreadProc)

    t.SetApartmentState(ApartmentState.STA)

    いまいち、ヘルプなどを見ても、このあたりのつながりが良くわからなかったので。

    アドバイスをいただければ、幸いです。

    2012年10月18日 21:16
  • >このThreadを開始するという処理を加えるのでしょうか。

    Thread.Start メソッド呼び出しによってスレッドの実行が開始されます。

    が、.NET Framework 4 以降を使用しているなら、Thread や ThreadPool よりも Task の使用を検討した方がよいです。

    >いまいち、ヘルプなどを見ても、このあたりのつながりが良くわからなかったので。

    Thread クラスのコンストラクタのヘルプ

    http://msdn.microsoft.com/ja-jp/library/xx3ezzs2(v=vs.80).aspx

    の「解説」の項に

    >スレッドの作成時には、スレッドは実行を開始しません。スレッドの実行をスケジュールするには、Start メソッドを呼び出します。

    という説明があり、さらにその下方で使用例が示されています。

    • 回答としてマーク TI-cb400 2012年10月20日 12:06
    2012年10月18日 22:09