Visual C++ 2005 MFCです。
別スレッド内で作成したCWnd派生の独自ウィンドウのメンバ関数を
プライマリスレッドからもコールしたいと思っています。
スレッド間で同じCWndポインタを渡すのは危険だということなので、
http://msdn.microsoft.com/ja-jp/library/h14y172e(VS.80).aspx
↑の説明などを参考に、独自ウィンドウのHWNDを別に持っておき、
プライマリスレッド側でCWnd::FromHandleでWndポインタを取り直してみました。
しかし、CWnd::FromHandleで作成される一時的なオブジェクトはCWndであって、
独自ウィンドウクラスのメンバなどは取れません。
この場合、独自ウィンドウのメンバ関数を呼ぶには、
間接的にPostMessageを使うしか無いのでしょうか?
それとも、プライマリスレッドが直接CWndポインタを使ってしまっても
問題無いものなのでしょうか?
ウインドウクラスの派生でなければ良いわけですから
引き渡したいデータを単純なデータクラスに集めてしまって
それのポインターをやり取りすると言うのはどうでしょう?
どうせ、HWNDを引き渡さないといけないわけですし。
私がよくやるのはスレッド間で共有したい情報を
データクラス化してそのクラスに排他処理も入れてしまい、
そのクラスをグローバルにおいて共有メモリの代わりに
使ったりしています。
ウインドウクラスに有るべき関数を呼びたい場合は、
言われているようにPostMessageを使ってユーザー定義の
ウインドウメッセージを使うようにしてます。
CWndのポインタを使って呼ぶ場合はMSが結果を保証していない
のでうまくいかないケースが出てきても文句は言えません。
自分で検証してうまくいくことを確認したうえで使うにしても
使う側の責任になります。
細かく見て行けば、大丈夫なケースと大丈夫でないケースに
分けられるとは思いますけれど、それを一々検証してまで
使うのかと言うところでしょうね。
しかも保障できるのは検証したライブラリバージョンでの
動作だけになるのでライブラリのバージョンが変われば、
うまく動かなくても文句はいえないと言う話になります。
解説ありがとうございます。
MSDNの解説サイトの例にあるUpdateAllViewsのように、
ウィンドウの表示状態を更新するような関数で、
スレッド内部からウィンドウ自身が自分でコールすることもあるので、
やはりウィンドウクラスの中に入れておきたいです。
ウィンドウのインスタンスはあくまで別スレッドの中にあるから、
プライマリスレッドからはそのインスタンスのメンバに依存するような
関数はコールできないということなのですよね。
CWnd::FromHandleをキャストしてみても、
そのインスタンスを指していないようで、メンバはゴミが入っていました。
となるとやはり、プライマリスレッドからはPostMessageを使って
独自のメッセージを送っておき、スレッド内部のメッセージループの中で
ウィンドウ自身が自分で関数をコールするのが
適切な方法ということになるのですね。
ウィンドウハンドルもインスタンスのアドレスもわかっているのに
別スレッドからメンバ関数を直接コールできないのはとてももどかしいですし、
使ってしまっても動くこともあるというのが怖いです…。
だって、UpdateAllViewsってCDocumentクラスのメンバじゃん。
CWnd::FromHandleが解決方法って意味がわからん。
あの説明は何が解決するのかわからん。
てか解決するの?
AスレッドからBスレッドのウィンドウ操作するAPIを呼び出すときは
BスレッドでWaitForSingleObject(Aスレッドハンドル,INFINITE)
みたいなのが実行されるとデッドロックするよ。
それはメンバ関数とか関係なく、
void CMyView::func1()
{
SetWindowText(a);
}
や
::SetWindowText(Bスレッドのウィンドウハンドル,a);
で。
マップはスレッドローカルストレージだからアクセスできないっていうけど、
それはHWNDからCWndのポインタを取得するためのデータのこと。
CWndのインスタンス自体は
m_pWnd = new CMyView;
みたいにnewとかで確保したメモリである。
それにスレッドローカルストレージでも同プロセスの別スレッドからアクセスできないア
ドレスなんてない。
変な説明で煙に巻いているようにしか思えない。
int CMyView::func1(int a)
{
return a*2;
}
みたいなものならCMyViewのインスタンスを渡してfunc1を問題なく呼べる。
MFCがバージョンアップしてこれも使えなくなるとしたら迷惑だな。
但しCMyViewが別スレッドでdeleteされているなんてことはない前提で。
CWnd::FromHandleで得たCWndポインタをほかのスレッドに渡すのは駄目。
しばらくするとdeleteされてしまう一時オブジェクトの可能性がある。
同時に実行したらまずいものとか
一時オブジェクトをダウンキャストしている処理とか呼びだしたらまずいだろうね。
void CMyView::add(int a)
{
m_a = m_a + a;
}
は、m_aに排他制御が必要。
PostMessageなら無難だね。
排他制御もいらない。
なぜならメッセージはそのウィンドウを作成したスレッドで処理されるから。
常に同じスレッドでしか実行しないから排他制御いらないってこと。
質問者さんの意図がいまいちわからないのですが、
HWNDはスレッドの上位に立つ有効な値です。
従ってHWNDに対して::SendMessage()すれば
対象のHWNDのコールバックで処理できます。
わざわざCWndを使用して危険な賭けにでる必要も、
PostMessage()でいつ完了するか不安な時間をすごす
必要もありません。
前にも書いてますが、現状の動くと言う状況が保障された物でない以上は
使うべきでないと言うのが私のスタンスです。
今の実装で動いていても提供側が保障していない以上、
それは偶々動いていると見るべきです。
マイクロソフトが明言しているわけですから、
動かないような実装方法に変更したとしても文句は言えませんからね。
自分が作ったライブラリならコントロールできるでしょうけれど、
他の人が作ったライブラリを使うわけですから保障された範囲で
使うのが無難でしょう。
これは私のスタンスなので最終的な判断は御自分でどうぞ。