CWinThreadからダイアログを操作

[戻る]
いしだ 2012/02/17(金) 16:47:05
MSDNのサイトで「MUTEXES サンプル」というものをダウンロードし、
ソースを読んでみたのですが、

int CCounterThread::Run()
{
    CSingleLock sLock(&(m_pOwner->m_mutex));
    while (!m_bDone)
    {
        (中略)
        m_pOwner->AddToListBox(_T("Cntr: Increment"));
    }
}

int CDisplayThread::Run()
{
    CSingleLock sLock(&(m_pOwner->m_mutex));
    while (!m_bDone)
    {
        (中略)
        m_pOwner->AddToListBox(strBuffer);
    }
}

となっていて、AddToListBoxは、以下のように
ダイアログ内のリストボックスに文字列を直接追加していました。

void CMutexesDlg::AddToListBox(LPCTSTR szBuffer)
{
    CListBox* pBox = (CListBox*) GetDlgItem(IDC_DATABOX);
    ASSERT(pBox != NULL);

    if (pBox != NULL)
    {
        int x = pBox->AddString(szBuffer);
        pBox->SetCurSel(x);

        if (pBox->GetCount() > 100)
            pBox->DeleteString(0);
    }
}

この流れを見ると、ワーカスレッドから
メインスレッドのウィンドウを直接操作していると思われるのですが、
これは「ダメな例」だと思ったほうがよいのでしょうか。

http://rarara.cafe.coocan.jp/cgi-bin/lng/vc/vclng.cgi?print+201002/10020017.txt
これと同じなのではと思われるのですが。

tetrapod 2012/02/17(金) 18:09:16
うんにゃ。
CWnd* CWnd::GetDlgItem(int) const; が返すのは
(そのスレッド内だけで有効な新しい) 一時的 CWnd インスタンスへのポインタ。
http://msdn.microsoft.com/ja-jp/library/77d16yhw(v=vs.80).aspx

UI スレッド上で作成した CWnd のインスタンス を
Worker スレッドからアクセスしてはならない (スレッドまたぎはだめ)
のであって

Worker スレッド上で作成した CWnd のインスタンスを
Worker スレッドからアクセスするのは問題ない (同一スレッドなら良い)
# Worker スレッドが複数あるとき、スレッドまたぎはだめ

AddToListBox は別スレッドから起動されうるが、
・pBox に一時的 CWnd インスタンスを得て
・pBox に操作を加える
一連の処理は同一スレッド内で行われる (=またがない) 。

ある1つの HWND に対して複数スレッドから操作を行ってよい。
# 排他しないと望みどおりの結果にならないかもしれんが。

ある1つの HWND に対して別の CWnd インスタンスを作ってよい。
片方の CWnd インスタンスは UI スレッドに属し
片方の CWnd インスタンスは Worker スレッドに属し、て問題ない。

いしだ 2012/02/17(金) 19:44:08
情報ありがとうございます。

> AddToListBox は別スレッドから起動されうるが、
> ・pBox に一時的 CWnd インスタンスを得て
> ・pBox に操作を加える
> 一連の処理は同一スレッド内で行われる (=またがない) 。

AddStringなどのCListBoxのメンバ関数はSendMessageが使われているので、
最初に挙げた過去ログのように、UIスレッドがWaitForSingleObjectで待つと
デッドロックになり得るから危険と解釈していたのですが、
そのような待ちかたさえしなければ、
ワーカースレッドからUIスレッドのウィンドウに対して
SendMessageが使われるのは、問題ないということでしょうか。

また、もし問題ないとすると、もう一つ質問なのですが、
ワーカースレッドの内部で、
UIスレッドの作成したダイアログやその下のコントロールに対して、
HWNDを経由してCClientDCを使って直接描画したりするのも、
一応は問題ないということになりますでしょうか?

tetrapod 2012/02/18(土) 13:16:30
> ワーカースレッドからUIスレッドのウィンドウに対して
> SendMessageが使われるのは、問題ないということでしょうか。
問題ないっす。
(もちろん UI スレッドが待つのはだめ)

> HWNDを経由してCClientDCを使って直接描画したりするのも、
HWND を扱う限りにおいては複数スレッドから操作して問題ないよ。
排他しないと意図通りになるとは限らないけど。

いしだ 2012/02/20(月) 09:17:31
情報ありがとうごございます。

何度もすみません。
セーフ・アウトの境界を自分なりに理解しようとしているのですが、
もう一つ疑問点が出てきてしまいましたので、
追記させていただきます。

> UI スレッド上で作成した CWnd のインスタンス を
> Worker スレッドからアクセスしてはならない (スレッドまたぎはだめ)

m_pOwnerというのはCMutexesDlgというメインダイアログのポインタで、
UIスレッド側でダイアログ自身がthisを渡しているのですが、
これはなぜ問題ないのでしょうか。
thisはCWnd(CDialog)のインスタンスなのですが。

tetrapod 2012/02/20(月) 13:35:44
CWnd やその派生クラスに関してスレッドまたぎが一切ダメ、ってわけではなくて、
CWnd の TLS (Thread Local Storage) 依存部がスレッドまたぎ禁止なだけ。

ここでの m_pOwner ってのは CWnd 派生クラスで追加したメンバー変数であって
m_pOwner そのものは TLS 依存しない (m_pOwner の先の HWND 関連部は依存あり)
m_pOwner の先の m_mutex も TLS 依存しない
ので問題ない

具体的に何ができて何が禁止なのかは MFC ソースを読んでみるべし。

PATIO 2012/02/21(火) 16:30:47
スレッド跨ぎの件もさることながら、モジュールの独立性とか
モジュール間の結合度を出来るだけ弱くすると言う考え方からすると
HWNDを使ってPostMessageするとかの方がより疎結合になるかなと
思います。個人的には出来る限り疎結合にしてウインドウの組み換えを
しやすくしておきたいので、基本的にHWNDでPostMessageと言うのが
私のスタイルです。

まあ、この辺は人それぞれの部分もあるので何処までやるのかと言う話はあると思います
けれども。

いしだ 2012/02/23(木) 10:09:47
[[解決]]
情報ありがとうごございます。

最初に挙げた過去ログもそうですが、それ以外の多くの過去ログでも、
MFCのインスタンスをスレッドを跨いで渡してはダメと書かれていたので、
ダイアログがthisを渡すこと自体ダメなのだと思っていました。

MSDNのサイトの「MUTEXES サンプル」では、
メインスレッドのダイアログがワーカースレッドにthisを渡し、
ワーカースレッドがそのthisを使ってダイアログ内のリストビューを
直接操作したりしていたので、疑問に思っていたのですが、
それらはすべて問題ない使いかただったと理解しました。

ありがとうございました。

PATIO 2012/02/23(木) 13:18:45
> ワーカースレッドがそのthisを使ってダイアログ内のリストビューを
> 直接操作したりしていたので、疑問に思っていたのですが、
> それらはすべて問題ない使いかただったと理解しました。

この書き方だとちょっと言いすぎなのではと思いますけれど。
MSDNにはスレッド跨ぎでのCWndオブジェクトの受け渡しは駄目と
はっきり書いてありますので、全て問題がないという話ではないです。

この辺はなぜ駄目なのかと言う部分の理解がないと
どの操作が駄目でどの操作が良いのかと言う部分の切り分けが難しいので
その部分の判断ができないのであれば、基本的には行わないと言う方向に
倒した方が安全だと思います。

あと、仕様上の縛りでだめと言う話になっているので
絶対に動作しないと言う話ではなく、偶々動いているという状況も
ありえますし、動いていたパターンがあるバージョンで動かなくなったとしても
MSからすれば、仕様上駄目と公式に書いていますよねと言う事になるので
あまりチャレンジャーなやり方はお勧めできないと言うのが本音です。
(今、動いているから良いという話では無いという事ですね。)

基本的にはCWndオブジェクトをスレッド間でやり取りしなくても良いように
設計すべきだと思いますし、そもそもダイアログが抱えているコントロールの
制御はそのダイアログが行なうべきだと思いますので
HWNDとPostMessage(ユーザー定義メッセージ)を使ってやり取りを行い、
コントロールの制御はメッセージを受けたダイアログ(メインスレッド側)で
行った方が良いと思いますよ。

いしだ 2012/02/26(日) 13:01:50
> MSDNにはスレッド跨ぎでのCWndオブジェクトの受け渡しは駄目と
> はっきり書いてありますので

ご意見ありがとうございます。

ここの掲示板でも、たびたびこのような指摘が書かれていて、
今回のMSDNのサンプルではダイアログがワーカースレッドにthisを渡していたので、
これがまさにその駄目な使いかたなのではと疑問に思ったのですが、
tetrapodさんからは「問題ない」という見解をいただいたため、
今回のような使いかたには当てはまらないのだ解釈しました。

この自分の解釈がまた変に誤解しているということでしょうか?

それとも、PATIOさんの見解としては、
今回のMSDNのサンプルは「駄目な例」だということになりますでしょうか?

ITO 2012/02/26(日) 16:07:15
うーーん、
> tetrapodさんからは「問題ない」という見解をいただいたため、
> 今回のような使いかたには当てはまらないのだ解釈しました。
 HWNDを使ったCWndオブジェクトの処理は"HWND == NULL"の例外が発生して
異常終了します。
 
> MSDNのサイトの「MUTEXES サンプル」では、--------

> ワーカースレッドがそのthisを使ってダイアログ内のリストビューを
> 直接操作したりしていたので、疑問に思っていたのですが、

> MSDNにはスレッド跨ぎでのCWndオブジェクトの受け渡しは駄目と
> はっきり書いてありますので
 「駄目な例」となるとどこまでがCWndの処理になるか微妙ですが、
MSDNの仕様をまもるのであれば避けたい処理ですね。

ロマ 2012/02/26(日) 18:43:25
どうもよくわからないです。

> CListBox* pBox = (CListBox*) GetDlgItem(IDC_DATABOX);

このキャストで使えるCListBoxのメンバー関数は、
直にWin32APIを呼び出しているようなものに限られているように思います。

もしそうならば、ワーカースレッドはHWNDだけを使うほうがわかりやすいような気が。

PATIO 2012/02/27(月) 15:35:27
> もしそうならば、ワーカースレッドはHWNDだけを使うほうがわかりやすいような気が。

私も同意見です。
スレッド間でCWndを受け渡してはいけないと言う話の元凶が把握で来ていて
こういう使い方なら大丈夫と逐一判断出来るなら良いのですけれど。
個人的にはこのメソッドなら大丈夫とかこういう使い方なら大丈夫とか
そういう判断は結構煩雑になるので一切駄目とした方が解りやすいかなと思います。
根本の理解は必要だと思うのですが、人数を掛けて作業するときなどは
なかなかうまく回らない事が多いと思いますから。

今回のMSDNのサンプルに関しては私自身としてはこういう使い方はしないと思います。
基本的にはワーカースレッド側からユーザー定義のメッセージを使って通知し、
更新に必要な情報を引き渡してメインスレッド側で表示更新を行なう形にすると思います。
作り的にはワーカースレッドはメッセージを投げる所までで自処理の流れに戻り、
受けたメインスレッド側はワーカースレッドからの呼び出しとは切り離された所で
動作する事になります。
好みの話も入っているかもと思いますが、私個人としては切り離してしまいたいです。

gak 2012/02/27(月) 18:49:18
CWnd、CDC 等の「MFC Windowsオブジェクト」ラップクラスには”永続オブジェクト”と”一
時オブジェクト”の2種類存在する。
「MUTEXES」の「m_pOwner(==CMutexesDlg)」は CMutexesApp::InitInstance() で
> CMutexesDlg dlg;
という感じにインスタンスが明示的に作成されているオブジェクト。こういうのを”永続
オブジェクト”と呼び「dlg」が生存中はインスタンスが存在する事が確約される。

> CListBox* pBox = (CListBox*) GetDlgItem(IDC_DATABOX);
逆に、上記で作成される「pBox」は”一時オブジェクト”。IDC_DATABOX (のHWND)に対応
する MFC オブジェクトは(現在のスレッド内で)明示的に作成されていないので、MFC
が一時的な CWnd オブジェクトを作成している。
で、”一時オブジェクト”は”永続オブジェクト”とは異なり(オブジェクトを作成したス
レッドの)処理が一段落した段階で削除される(ガベージコレクションのような感じ)
# スレッドのTLSでハンドルマップを持って管理しる理由の一つはこの機能かも

> ダイアログ自身がthisを渡しているのですが、これはなぜ問題ないのでしょうか。
”一時オブジェクト”を他スレッドに渡す事は 100% NG。仕様上、ガベージコレクションに
よって意図しないタイミングで削除される事があるから(現状モーダルダイアログ表示中
なら一時オブジェクトの削除は行われない動作なので、結果大丈夫かもしんないが)
でも”永続オブジェクト”はそのオブジェクトが存在する事が保証される期間内ならば他ス
レッドに渡す事は可能。よって m_pOwner はこの点に措いては問題無いハズ。

MFCオブジェクトのもう一つの問題点は仕様上「スレッドセーフでは無い」という点。
例えば CMutexesDlg には m_strNumber という CString のメンバ変数が存在しているが

  m_pOwner->m_strNumber = CString(_T("xxxxxx"));

なんて処理を複数のスレッドから実行されるとタイミングによっては多分落ちる。上記の
様に直接メンバ変数を操作しないにしても、呼んだメンバ関数内でメンバ変数が操作され
ていた場合等にも同様の危険がある。
つまり、MFCオブジェクト(C++オブジェクトでも同様だけど)を複数スレッドから扱うな
らプログラムを組む側で同期してやらないと何時破綻しても文句は言えない。
# 「MUTEXES」では m_strNumber を同期無しで複数スレッドから操作している。
# が、m_strNumber のバッファの再確保が起こらない様に実装(LockBuffer() 等で)し
# て落ちる事は防いでいる(俺にはその場凌ぎの対応としか思えなかった…)


まぁ要すると「MFCオブジェクトをスレッド跨いで安全に使おうと思うと色々メンドイ」

> m_pOwner->m_strNumber.SetAt(0,L'\0');
> m_pOwner->m_strNumber.SetAt(nCurrentDigitPos,(TCHAR) ('0' + nNumber%10));
正直「MUTEXES」は「スレッド間の同期をミューテックスで取る」という点だけに絞って
参考にした方が良いかも。サンプルに含まれていた↑コード見ても「結果的には一緒に
なってるんだけど…う〜ん」とか思ってしまう。

ロマ 2012/02/29(水) 01:32:44
> ダイアログ自身がthisを渡しているのですが、これはなぜ問題ないのでしょうか。
どこかで、ASSERT_VALID(m_pOwner)を呼び出していれば失敗します。

あと、
VC2003のコードを追っていくと、
一時オブジェクトは、OnIdleで削除されるようです。
モーダルダイアログはOnIdleを呼ばないので、
gakさんの記述どおりの様です(未検証)。
メッセージループの無い、ワーカースレッドの場合は、
スレッドの寿命と等しいようです(実験してみました)。

bun 2012/02/29(水) 09:16:45
このあたり、私も以前からあいまいで、正確なところを知りたいのですが、
そもそもスレッドをまたいでも見えるものと見えないものの差は何なのでしょう?

MFCなんて所詮クラスライブラリです。
C++言語やWindowsAPIの枠からはみ出す物は一つも無いはずです。

C++言語やWindowsAPIの枠内でスレッドをまたぐと見えなくなるものがあるはずだと
思うのですが、それはいったい何なのでしょう?

ロマ 2012/02/29(水) 11:04:25
私もわかっていませんが、とりあえず、落書き程度です。

デバッガーで追うと、CHandleMap::FromHandleに行き、
この中に、LookupTemporary,LookupPermanentを見つけ、
これが、gakさんの ”一時オブジェクト”、”永続オブジェクト”かと思い、
実験。
さらに、grepして、CWnd::AssertValidを見つけたということです。

TlsAllocもみつけたのですが、ソースはまだ読んでいません。

subaru 2012/02/29(水) 11:22:22
>C++言語やWindowsAPIの枠内でスレッドをまたぐと見えなくなるものがあるはずだと
>思うのですが、それはいったい何なのでしょう?

すでに回答にも出てきていますが、
スレッドをまたぐと見えなくなるものとは
スレッドローカルストレージ(TLS)に格納された値のことです。
(CWndなどのMFCオブジェクトはTLSを使用しているらしい)

WindowsAPIでは以下のものが使われます。
TlsAlloc
TlsGetValue
TlsSetValue
TlsFree

bun 2012/02/29(水) 16:08:50
ロマさん、subaruさん ありがとうございます。

お二方のヒントでようやく全貌を理解した気がします。
当方の環境は VC++2005ですが、おそらく最新版も大差ないであろうと予測の
元お話しさせて頂くと、

話題になるのは、
MFCにおける CWnd* <-> HWND間の参照です。

CWnd* -> HWND の方向は、メンバ変数m_hWndの参照ですから何の問題もあり
ません。

HWND -> CWnd* の方向は、マップを用いて変換を行っています。
 マップキー:HWND
 マップ値:CWnd*
このマップ(CHandleMap)の取得は常に以下の形で行われます。
    CHandleMap* pMap = afxMapHWND();
問題は、afxMapHWND()の呼び出しの中で、TLSが使われていることです。
そのため、HWND -> CWnd* の変換マップがスレッド毎に独自のものに
なってしまうようです。

よって、別スレッドでは、HWNDに対応するCWnd*が見つからず、正常処理がで
きないようです。

CWnd*のポインタ自体は別スレッドでも有効でしょうから、
HWND -> CWnd* の変換マップさえ正常動作すればいいように思えます。

それに対する答がMicroSoftの言う、スレッド側で、
「HWND を Attach() で登録しろ」という話なのでしょう。
登録した結果、HWND -> CWnd* の逆引きが出来るようになってめでたし、
なのかな?
(この話だけちょっと自信が無い^^;)

いしだ 2012/02/29(水) 17:41:00
多くのご意見ありがとうございます。
ちょっと自分の状態を説明させていただきます。

> CWnd、CDC 等の「MFC Windowsオブジェクト」ラップクラスには”永続オブジェクト”と”一
> 時オブジェクト”の2種類存在する。

このへんの仕組みは、自分なりには理解しているつもりです。

CWnd::FromHandleなどを使ってHWNDから取得したCWndのポインタは、
そのワーカースレッドの中で一時的に作成したCWndを指していて、
UIスレッド側のCMutexesDlgは指していないため、
無理やりダウンキャストしてm_mutexなどのメンバ変数にアクセスすると、
その時点で不正アクセスになってしまいます。

そのため、このサンプルでは、HWNDではなくCMutexesDlg自体のポインタを渡し、
ワーカースレッド内でそれらを使えるようにしているのだと解釈しました。

ただ、その方法を使った時点で、
よく目にする「CWndのポインタを別スレッドに渡すな」に
MS自身が違反していることになるため、
2012/02/20(月) 09:17:31や、
2012/02/26(日) 13:01:50の質問をさせていただきました。

「駄目な例」という結論であれば、それはそれで理解できるのですが、
tetrapodさんからは「問題ない」という見解を頂いたたため、
MSも問題ないという認識で書いたサンプルなのだと解釈しました。

MSが「本当は駄目だけど、ただのサンプルだし」と書いたものなのか、
「別スレッドに渡すな」というのはこういうことではなく、
MS的にはセーフな使いかたなのか、どっちなのでしょうね。

gak 2012/03/01(木) 18:20:52
> MS的にはセーフな使いかたなのか、どっちなのでしょうね。
http://msdn.microsoft.com/ja-jp/library/h14y172e.aspx
http://msdn.microsoft.com/ja-jp/library/c251x6s1.aspx
・前提として、MFCはスレッドセーフ設計では無い
・スレッド跨いで安全に使いたい場合は同期が必要
・MFCにはスレッドアンセーフな実装をしている機能もある
・ハンドルとMFCオブジェクトのマッピング機能に関してはTLS使ってる。

MSの立ち位置としては「MFCはスレッドセーフ設計では無い。スレッド跨いで使う際の動
作保障はMSはしない。特にハンドル関係はTLS使ってる関係上其れを意識しないコード組
んで予期しない結果が起きてもしらないよ」じゃない?
ぶっちゃけ、スレッドセーフな使い方をプログラマがしないならout、するならoutでは無
い、の範疇ではなかろうか。

サンプルの m_pOwner の使われ方も C++ オブジェクトをスレッド跨いで使うレベルと差
異ない使われ方しかしていない。なのでサンプルの作者は問題無いと判断を下したのかも
しれない。
> http://msdn.microsoft.com/ja-jp/library/ms386471.aspx
若しくは、元々 C + Windows API で書かれていたコードを MFC に置き換えたコードらし
いので、良く考えずに置換しただけなのかもしれない。

> m_pOwner->AddToListBox(_T("Cntr: Increment"));
自分の意見を述べてなかったので言っとくと、俺なら HWND 渡して SendMessage する。

いしだ 2012/03/13(火) 09:52:01
ご意見ありがとうございます。
急用でPCをさわれませんでした。申し訳ありません。

> サンプルの m_pOwner の使われ方も C++ オブジェクトをスレッド跨いで使うレベルと差
> 異ない使われ方しかしていない。なのでサンプルの作者は問題無いと判断を下したのかも
> しれない。

自分も、ダイアログクラスなどがワーカースレッドを使って
自身のメンバを構築していくような処理では、
HWNDだとダイアログ自身のメンバにはアクセスできないため、
thisを渡してしまうことがよくあります。
これがすでにアウトだという意見もあるかもしれませんが。

ただ、リストボックスへの追加など、ダイアログ内のコントロールの操作まで
ワーカースレッド側ですることまではしないようにしていたので、
今回のMSのサンプルは、ある意味予想外でした。

> > m_pOwner->AddToListBox(_T("Cntr: Increment"));
> 自分の意見を述べてなかったので言っとくと、俺なら HWND 渡して SendMessage する。

これは、「AddToListBoxを呼んでください」といった趣旨の独自のメッセージで、
_T("Cntr: Increment")のポインタをLPARAMなどに入れて送るわけですよね。

gak 2012/03/14(水) 18:49:37
> これは、「AddToListBoxを呼んでください」といった趣旨の独自のメッセージで、
> _T("Cntr: Increment")のポインタをLPARAMなどに入れて送るわけですよね。
そう。今回のサンプルは「mainスレッド + subスレッドx2」という構成で、AddToListBox
がsubスレッドから同時にコールされる可能性が在る実装が気分的に引っ掛かった。なの
で俺なら(念の為) SendMessage で同期取るかなぁ、という考え。

以下蛇足。
例えば「進捗表示するダイアログをモーダルで出して裏スレッドで作業する」という実装
をする場合以下の様なコードを組んだりする(というか過去コード見ると組んでた)

class dlgProgress : public CDialog {
    void OnInitDialog() { // WM_INITDIALOG handler
        AfxBeginThread(&threadProc、this);
    }
    static UINT __cdecl threadProc(void *param) {
        dlgProgress *dlg = reinterpret_cast<dlgProgress*>(param);
        dlg->Do();
    }
    void Do() { // 作業実行
        HWND progressBar = ::GetDlgItem(m_hWnd, プログレスバーid);
        ::SendMessage(progressBar, PBM_SETPOS, 0, 0); // 0%
          :
        ::SendMessage(progressBar, PBM_SETPOS, 50, 0); // 50%
          :
        ::SendMessage(progressBar, PBM_SETPOS, 100, 0); // 100%
        PostMessage(WM_APP, IDOK);
    }
    LRESULT OnWmApp(WPARAM w, LPARAM) { // WM_APP handler
        EndDialog(w);
    }
}

俺的には特に問題無いつもりでいたのだけど実際はどうなんだろうね。
・変数へ同時アクセスする類の処理
・TLS 依存部分
・FromHandle() で返される一時オブジェクトの生存期間
辺りにさえ注意すれば、画一的に「CWnd 派生を別スレッドに渡すな」では無いと思って
たけど…これ以上に深く考えた事はなかった。

いしだ 2012/03/16(金) 09:20:24
> void Do() { // 作業実行
>     HWND progressBar = ::GetDlgItem(m_hWnd, プログレスバーid);
>     ::SendMessage(progressBar, PBM_SETPOS, 0, 0); // 0%
>       :
>     ::SendMessage(progressBar, PBM_SETPOS, 50, 0); // 50%
>       :
>     ::SendMessage(progressBar, PBM_SETPOS, 100, 0); // 100%
>     PostMessage(WM_APP, IDOK);
> }

この「:」の部分で、dlgProgressクラスが自身のメンバを
参照したり更新したりしているわけですね?

自分もこのようにAfxBeginThreadにthisを渡すことが多いのですが、
thisはCWnd派生クラスのポインタなので、
MSの見解上は「CWndのポインタを別スレッドに渡すな」に
引っかかっているのではと疑問に思っています。

なお、自分の方法としては、進捗状況を数値型のメンバ変数で持っておき、
プログレスバーの更新はメインスレッド側でタイマーを使って表示したり、
進捗状況の更新を伝えるメッセージを自身に送ったりするようにしています。

> 俺的には特に問題無いつもりでいたのだけど実際はどうなんだろうね。
> ・変数へ同時アクセスする類の処理
> ・TLS 依存部分
> ・FromHandle() で返される一時オブジェクトの生存期間
> 辺りにさえ注意すれば、画一的に「CWnd 派生を別スレッドに渡すな」では無いと思って
> たけど…これ以上に深く考えた事はなかった。

自分もこういうことなのかなとは思っているのですが、
このスレッドでも、人によって解釈が異なるようですし、
どの意見も正しく思えてしまいます。
MS自身が「サンプル」として堂々とスレッド跨ぎしちゃってますし。

古いスレッドには追加発言できません。