BackgroundWorkerで「ソースは利用できません。」となることについて

タグの編集
投稿者 ねぼすけ  (社会人) 投稿日時 2016/12/12 09:51:06
時間のかかる処理を、バックグランドで処理させようとしています。
For Each f As String In files
    Dim dir As String = IO.Path.GetDirectoryName(f)
    Dim file As String = IO.Path.GetFileName(f)
    Dim shell As New Shell
    Dim fldr As Folder = shell.NameSpace(dir)
    Dim item As FolderItem = fldr.ParseName(file)
    Dim artist As String = StrConv(fldr.GetDetailsOf(item, 13), VbStrConv.Narrow)
    Dim title As String = StrConv(fldr.GetDetailsOf(item, 21), VbStrConv.Narrow)
    Dim cdTitle As String = StrConv(fldr.GetDetailsOf(item, 14), VbStrConv.Narrow)
    data.Add(f & "," & artist & "," & title & "," & cdTitle)
Next

の部分に来ると
「ソースは利用できません。
……
TargetInvocationExceptionはハンドルされませんでした。
型 'System.Reflection.TargetInvocationException' のハンドルされていない例外が
mscorlib.dll で発生しました。」
となって目的を達せられないでいます。
どういうことなのか教えていただけないでしょうか。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2016/12/12 11:09:14
> 時間のかかる処理を、バックグランドで処理させようとしています。

COM コンポーネントの Shell オブジェクトを利用していますが、
これの ThreadingModel は Apartment となっています。
すなわち、シングルスレッドアパートメント(STA)からしか呼び出せない仕様です。

一方、BackgroundWorker で生成されるスレッドは、常に
マルチスレッドアパートメント(MTA)です。そのため、
BackgroundWorker 内から Shell オブジェクトを使うことはできません。


ひとまず、
Debug.Print( System.Threading.Thread.CurrentThread.GetApartmentState().ToString() )
の内容を確認してください。Shell を呼びたいなら、これが STA でなければなりません。

そして STA なスレッドを用意したいのであれば、BackgroundWorker は使えません。
代わりに、New Thread で生成したスレッドを SetApartmentState することになります。
投稿者 ねぼすけ  (社会人) 投稿日時 2016/12/12 17:01:10
>Debug.Print( System.Threading.Thread.CurrentThread.GetApartmentState().ToString() )
の内容は「MTA」でした。
色々調べて、
Dim th As New Threading.Thread(New Threading.ThreadStart(AddressOf GetCsv))
th.SetApartmentState(Threading.ApartmentState.STA)
th.Start()
で、目的は達せられました。課題もあります。
①BackgroundWorkerでしたら、引数を渡せました。Threadではどうするのか。
②スレッドの終了をどう把握するのか。
です。今、「Delegate」や「Invoke」について調べています。
何か参考になることがあれば教えてください。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2016/12/12 18:27:44
> 引数を渡せました。Threadではどうするのか。
こんな感じです。
http://www.atmarkit.co.jp/fdotnet/dotnettips/434paramthread/paramthread.html


> スレッドの終了をどう把握するのか。
終了通知用のデリゲート インスタンスを事前に渡しておき、
終了時にワーカースレッドから呼び出すようにしては如何でしょう。

BackgroundWorker.RunWorkerCompleted のように、イベント通知でも良いですけど。
投稿者 ねぼすけ  (社会人) 投稿日時 2016/12/14 13:41:27
①引数の渡し方2種類覚えました。
・Classを利用し、Newコンストラクタで渡す
・ParameterizedThreadStart
②スレッドの終了では
・Joinメソッド
>終了通知用のデリゲート インスタンスを事前に渡しておき、
>終了時にワーカースレッドから呼び出すようにしては如何でしょう。
だと思うんだけど、DelegateとInvokeを利用して
色々と試してみて、大変勉強になりました。
魔界の仮面弁士さん、どうもありがとうございます。