トップ回答者
OCXを別スレッドで呼ぶ

質問
-
いつもお世話になっております。
VS2005 c# WinXP画面のスレッドというのは1プロセスに1つしか持てないのでしょうか。
客先から提供されているOCX(VB6で作成)がよく固まるため、メインの画面とは
別スレッドで呼び出したいのですが、OCXなのでどうしても
画面と同期してしまいます。以下のようにモーダレスでやってみてもやはりForm1の方も
同時に固まってしまうようです。もしかして別プロセスにするしかないのでしょうか。
宜しくお願い致します。
public partial class Form1 : Form
{
Form2 form;
private void button1_Click( object sender, EventArgs e )
{
form = new Form2();
form.Show();
}
}
public partial class Form2 : Form
{
private void button1_Click( object sender, EventArgs e )
{
while( true )
{
}
}
}
回答
-
Formに対するInvokeは同期ですので、処理が終わるまで帰ってきません。
(「非同期デリゲート」はこの場合、関係ありません)
非同期で実行したいのであればBeginInvoke/EndInvokeを使ってください。
すぐに制御が帰ってくるので呼び出した側のスレッドは固まりませんが、処理中にもう1度、ボタンを押す等ができるようになりますので、排他を考える必要が出てくると思います。
ところで、スレッドを分けても、Ocxを貼り付けているフォームは、Ocxが処理中の時に動かなくなりますが、問題ないですよね?
(先にも書きましたが、Ocxを貼り付けるフォームを持つスレッドはSTAであるべきです。Form2に移すのであればThreadクラスのSetApartmentStateあたりで設定が必要です)
#このまま進めるとかなりリスクが高いように思えます。
すべての返信
-
Myon さんからの引用 画面のスレッドというのは1プロセスに1つしか持てないのでしょうか。
以下のようにモーダレスでやってみてもやはりForm1の方も
同時に固まってしまうようです。そもそも、上記のコードではスレッドを作っていません。
モードレス表示は同じスレッドで実行されるだけで、スレッドが自動的に作成されるわけではありません。
新たにスレッドを(Threadクラスで)作ってそのスレッドでフォームを作成、所有できなくもないですが、スレッド間の同期とか色々と検討すべき事柄はあると思います。
(細かい動作検証や裏付けは取っていません)
Myon さんからの引用 客先から提供されているOCX(VB6で作成)がよく固まるため、メインの画面とは
別スレッドで呼び出したいのですが、OCXなのでどうしても
画面と同期してしまいます。ActiveX(OCX)の多くはSTAThreadなスレッドでしか実行できないものです。
C#のデフォルトのスレッドはMTAThreadになっていますので、変更が必要です。
http://msdn2.microsoft.com/ja-jp/library/system.threading.thread.setapartmentstate(VS.80).aspx
-
ご回答ありがとうございました。
以下のようにスレッドを作ってやってみますと
Form2が一瞬表示されてすぐ消えてしまいました。
ですので画面のスレッドを2つもつことはできないのかな
と思ったのです。
マルチスレッドでOCXを呼び出したいということではありません。
呼び出しはシングルスレッドで行いたいのですが、
なんとかForm2上にOCXを置いて、
メインの画面とは切り離したいということです。
public partial class Form1 : Form
{
Form2 form;
Thread _thread;
private void button1_Click( object sender, EventArgs e )
{
_thread = new Thread( OnStart );
_thread.Start();
}
protected void OnStart()
{
form = new Form2();
form.Show();
}
} -
ご回答ありがとうございます。
Application.Runを使って以下のようにやってみますと
目的の動作になったのですが、
思いもよらぬ落とし穴というのが気になります。
やはりこのような方法は避けたほうがよろしいでしょうか。
public partial class Form1 : Form
{Thread _thread;
Form2 _form2;protected void OnStart()
{
_form2 = new Form2();
_form2.OnComplete += new Form2.DelegateOnComplete( OnComplete );
Application.Run( _form2 );
}private void button1_Click( object sender, EventArgs e )
{
_thread = new Thread( OnStart );
_thread.Start();
}private delegate void DelegateSafeOnComplete( int nData );
private void OnComplete( int nData )
{
if( this.InvokeRequired == true )
{
if( this.IsDisposed == true ) return;
DelegateSafeOnComplete d = new DelegateSafeOnComplete( OnComplete );
this.Invoke( d, new object[] { nData } );
return;
}
label1.Text = nData.ToString();
}
}
public partial class Form2 : Form
{public delegate void DelegateOnComplete( int nData );
public DelegateOnComplete OnComplete;private void button1_Click( object sender, EventArgs e )
{
for( int i = 0; i < 10; i++ )
{
System.Threading.Thread.Sleep( 1000 );
}
if( OnComplete != null )
{
OnComplete( 5 );
}
}}
-
Form1とForm2があり、お互い相手のフォームにアクセスする場合ですが、
確かに直接アクセスすると変な動作をすることを確認しました。
ですので相手にアクセスするときは
必ず自分の画面とは別スレッドでアクセスし、アクセスされたほうは
if( this.InvokeRequired == true )
{
if( this.IsDisposed == true ) return;
DelegateSafeOnComplete d = new DelegateSafeOnComplete( OnComplete );
this.Invoke( d, new object[] { nData } );
return;
}といったような処理を必ず行うようにするというルールではいかがでしょうか。
(OnCompleteも非同期デリゲートにする)
実はOCXに別スレッドでアクセスする処理は既にできているのです。
でも結局アクセスしたときに画面と同期してしまうものですから、
スレッドにした意味が無いなあと思っておりました。
(現状OCXはForm1に貼り付いています。)
-
Formに対するInvokeは同期ですので、処理が終わるまで帰ってきません。
(「非同期デリゲート」はこの場合、関係ありません)
非同期で実行したいのであればBeginInvoke/EndInvokeを使ってください。
すぐに制御が帰ってくるので呼び出した側のスレッドは固まりませんが、処理中にもう1度、ボタンを押す等ができるようになりますので、排他を考える必要が出てくると思います。
ところで、スレッドを分けても、Ocxを貼り付けているフォームは、Ocxが処理中の時に動かなくなりますが、問題ないですよね?
(先にも書きましたが、Ocxを貼り付けるフォームを持つスレッドはSTAであるべきです。Form2に移すのであればThreadクラスのSetApartmentStateあたりで設定が必要です)
#このまま進めるとかなりリスクが高いように思えます。