MessageBox


祐香  2003-12-02 20:56:09  No: 52696  IP: [192.*.*.*]

祐香と申します。

MessageBox()を表示すると、
ここで処理が中断しますが、
表示しまたまま処理が次のステップへ
進むようなオプションはないでしょうか?

編集 削除
ぴのきよ  2003-12-03 00:36:50  No: 52697  IP: [192.*.*.*]

親ウィンドウのハンドルにNULLを指定するというのはどうでしょう。

編集 削除
YuO  2003-12-03 14:52:59  No: 52698  IP: [192.*.*.*]

MessageBox APIはModal Dialog Boxを作成します。
Modeless Dialog Boxのように作りたいなら,
別途スレッドを作成する必要があります。

編集 削除
なーめ  2003-12-03 23:02:52  No: 52699  IP: [192.*.*.*]

祐香さん、
マルチスレッドの使い方ってわかりますか?
 (1) Yes -> YuO さんの言うとおりです。
     CWinThread 派生クラスを作り、AfxBeginThread() してください
     メッセージボックスを消さずにアプリを終了するとメモリリークする
     かもしれません。ご注意まで。
 (2) No 次の質問
     できるだけ簡単に済ませたいという要求がありますか?
     (2-1) No -> モードレスダイアログを構築してください。
     (2-2) Yes
         引数を MessageBox に表示する別プログラムを作成します
         それを WinExec() で呼び出します。
         (引数:CWinApp の m_lpCmdLine )

(2-2) について
あまり賢い方法とはいえませんが、(^^;;
ひとつ作っておけば使いまわしができるので便利かと思います。
ループの中で使わないでくださいね。
メッセージボックスがどどどーっと、出てきたりしますから。
ちなみにデバッグ目的なら TRACE() を使ったほうがよいですよ。
目的によっては EventLog に記録する方法もあります。

編集 削除
ぴのきよ  2003-12-04 16:33:55  No: 52700  IP: [192.*.*.*]

>親ウィンドウのハンドルにNULLを指定するというのはどうでしょう。
的外れなこといってました。
すみません。m(__)m

編集 削除
なーめ  2003-12-04 18:45:51  No: 52701  IP: [192.*.*.*]

>> >親ウィンドウのハンドルにNULLを指定するというのはどうでしょう。
>> 的外れなこといってました。
>> すみません。m(__)m
ある別の問題の解決のヒントになりうる場合があるはずです。
だから、悪いことではないとおもいます。
あなたも何かの障害でこの種の問題に気づいたと思います。
だからこの意見を発言した気持ち、よくわかります。

ところで、
CWinThread 派生クラスから モードレスダイアログを立ち上げたとき、
CMainApp と同じように、
m_pMainWnd をちゃんと設定しましょう。
これをしないと、スレッドを立ち上げた意味がないような動きをします。

編集 削除
祐香  2003-12-08 15:27:34  No: 52702  IP: [192.*.*.*]

お返事遅くなりました<m(__)m>

>マルチスレッドの使い方ってわかりますか?

いえ、分りません。

でも、これが良い機会ですので勉強したいと思います。

> (1) Yes -> YuO さんの言うとおりです。
>     CWinThread 派生クラスを作り、AfxBeginThread() してください
>     メッセージボックスを消さずにアプリを終了するとメモリリークする
>     かもしれません。ご注意まで。

AfxBeginThread、これってMFCですよね?

実は、MFCは全く知りません(^^;

全てAPIで書いていますので
この場合ですとどうなりますでしょうか?

> (2) No 次の質問
>     できるだけ簡単に済ませたいという要求がありますか?
>     (2-1) No -> モードレスダイアログを構築してください。

モードレスダイアログを使えば簡単ですね。

>     (2-2) Yes
>         引数を MessageBox に表示する別プログラムを作成します
>         それを WinExec() で呼び出します。
>         (引数:CWinApp の m_lpCmdLine )

WinExecですか?

WinExecって使ったことないですが、
これって、Win32 APIではCreateProcessを使うようになっている
アレのことでしょうか?

先ずは、基本的なマルチスレッドプログラミングの方法あたりから
勉強してみたいと思います。

もし、簡単なサンプルなどございましたら、
ぜひ参考にさせて頂きたいので、宜しくお願いします。

編集 削除
YuO  2003-12-08 17:51:52  No: 52703  IP: [192.*.*.*]

> 全てAPIで書いていますので
> この場合ですとどうなりますでしょうか?

CreateThreadを利用することになります。
ただし,スレッド関数の内部で標準ライブラリの関数を呼び出すのであれば,
標準ライブラリが提供するスレッド作成関数(VC++なら_beginthreadや_beginthreadex)を利用することになります。

あと,スレッド関数に引き渡すポインタが,autoオブジェクトを指さないようにする必要があります。
理由は,
char * p (void)
{
    char c;
    return &c;
}
が問題になるのとほぼ同じ理由です。
#解決策も同じ。

ちなみに,引き渡すことができるのはポインタ一つですが,
構造体を利用すれば複数の情報を渡すことができます。


> WinExecって使ったことないですが、
> これって、Win32 APIではCreateProcessを使うようになっている
> アレのことでしょうか?

そうです。
http://msdn.microsoft.com/library/en-us/dllproc/base/winexec.asp
32bitプログラムではCreateProcess使えって書いてあります。


> もし、簡単なサンプルなどございましたら、
> ぜひ参考にさせて頂きたいので、宜しくお願いします。

サンプルはネット上に大量にありますよ。
ちなみに,MSDNのサンプル。
http://msdn.microsoft.com/library/en-us/dllproc/base/creating_threads.asp
これはこれでどうか,と思うのですけどね……。
#auto変数をポイントしている。

編集 削除
なーめ  2003-12-09 02:33:42  No: 52704  IP: [192.*.*.*]

>> 勉強したいと思います。
メモリリークとの熾烈な戦いに参戦されますか。
書籍を紹介:

『Win32 マルチスレッドプログラミング』
アスキー出版局
JAMES E. BEVERIDGE/ROBERT WIENER 共著
有限会社インフォビジョン訳
5100円

結構な値段ですが、得られるスキルを考えると安い。

>> CreateThread   -> pp46. 
>> _beginthread,_beginthreadex -> pp191

_beginthread関数を避けるべき理由 --> pp214 

というのがあるので、
どのような場合に使えないかは
確認はしておきましょう。

#  一度長い文章を書いたのに神の怒りにふれたのかさっくり消えてしまった。
# (; ;)

編集 削除
なーめ  2003-12-09 03:06:31  No: 52705  IP: [192.*.*.*]

WinExec() を調べてみました。
ブレークポイントを張り、混合モードでステップ実行(F11)です。
スタック上にSTATUPINFO と PROCESS_INFORMATION をとり、
STARTUPINFO を全クリアした後、メンバ cb に構造体サイズ 0x44 を設定、
wShowWindow メンバに引数 SW_SHOW を設定しています。
そして、CreateProcess() を呼び出すわけですが、
lpCommandLine  は WinExec() に与えた文字列。
lpStartupInfo とlpProcessInformation はスタックに作成した領域を
渡しますが、ほかのパラメタは全て0になっているようです。

  // WinExec と等価な呼び出し
  STARTUPINFO sStartupInfo;
  PROCESS_INFORMATION sProcInfo;
  BOOL bResult;
  ZeroMemory( &sStartupInfo, sizeof(sStartupInfo));
  sStartupInfo.cb = sizeof(sStartupInfo);
  sStartupInfo.wShowWindow = SW_SHOW;
  bResult = CreateProcess( NULL,cmdline,NULL,NULL,FALSE,0,NULL,NULL,&sStartupInfo,&sProcInfo );
  if( bResult == FALSE )
  {
    DWORD dw = GetLastError();
  // エラー処理等
  }

まあ、このような呼び出しを行う場合は WinExec() と同じだよということですね。
実際 WinExec() を使っても大丈夫でした。
しかし、将来のバージョンで使えなくなるかも知れないことを考えると、
CreateProcess() を使うべき。

自分用に次のようなのを作っておけば引数指定が楽ですよね。
simple.cpp

  CreateSimpleProcess( LPSTR cmdline )
  {
     ... 上のルーチン
  }

WinExec() の欠点とされていることを気にするなら

  CreateSimpleProcessEx( LPSTR cmd, LPSTR cmdargs )
  {
     char scmd[272];
#if 0
   sprintf( scmd, "\"%s\"", cmd ); // おっとこれはまずいかな。
#else
     char * p = scmd;
   char * q = cmd;
   *p++ = 0x22;   //  '\"' を打つのが面倒なので 
   while( *q ) *p++ = *q++;
     *p++ = 0x22;
     *p = 0;
#endif
     ...
   CreateProcess( scmd, cmdars, ... );
  }

にしても、同じようなことをするのに似たような関数がいっぱいあるというのは
Windows の最大の欠点ですね。
#これを長所という方おられますか?

編集 削除
祐香  2003-12-09 11:01:33  No: 52706  IP: [192.*.*.*]

お世話になります、祐香です。

皆様、貴重なアドバイスありがとうございます。

「通常、Win32のスレッドはCreateThreadを使用するが、
スレッド内でCのライブラリを使用する場合は_beginthreadexを使用する。」
とありますね。

メッセージボックスを出すだけならば、
CreateThreadでOKという事が言えますね。

>『Win32 マルチスレッドプログラミング』

推薦図書ありがとうございます。

値段にいとめはつけませんが、
版年度が古いのだけが少々気になりますが、
Amazonでも読書評価が高いようですし、
是非、参考にさせて頂きます。

基本的なところなのですが、
どういう場合にマルチスレッド処理が必要になるのか、
というところがまだよく理解できていません。

Googleで検索してみると、サンプルは多々みられるのですが、
入門的な解説ということで単に、
スレッドを生成してそこで処理しているもの?が見られます。

つまり、マルチスレッドを使わなければならない必然性みたいな
ものがあって、同時処理をしているのだというのがよく分りません。

同時処理みたいなものを体感したいのですが。

そこで、簡単なところで、どのようなものを作る時にこれを体感できるでしょうか?

目に見えるもので実際に確認できれば良いのですが。

タイトルにもありますように、
ウインドウ上のコマンドボタンクリックから
CreateThreadでスレッドを作成して、ここで
MesageBoxを表示させ、CreateThreadした後で、
1〜1000をカウントアップして、ウインドウに表示
してみるとか?

ただ、Sleep(100)でメインのスレッドを
中断させながらにしておかないと、
カウントアップは一瞬で終わってしまいそうです。

通常であれば、MesageBoxを呼び出した時点で
ユーザの判断待ちになってしまうので、カウント
アップはできないはずをマルチスレッドで可能に
しているのだというあたりを見たいと思います。

もう少し、簡単に作れて気の利いたものがあればよいのですが。

若干、タイトルとは内容が違う部分もありますが、
ご参考にお聞きできれば幸いと思っております。

こういうものを勉強しだすと、一般保護違反なんて
一日に何十回と繰り返しています、メモリリークも然りでしょうか、
もしOSが壊れたら再セットアップすれば良いですし、
私のような人間は楽観的過ぎますでしょうか(^^;

編集 削除
なーめ  2003-12-09 20:35:08  No: 52707  IP: [192.*.*.*]

>> 私のような人間は楽観的過ぎますでしょうか
私も同じ (^^;;
>>もう少し、簡単に作れて気の利いたものがあればよいのですが。
激しく同意(^^;;
なんで、MFCは覚えたほうがいいね。
おき楽にちょちょいのぱ
とやりたいならMFCですよ、やっぱり。
>> 値段にいとめはつけません
プログラムサイズにも糸目はつけませんよね。(^^;;

>> 版年度が古いのだけが少々気になりますが、
VisuaC++ 4.0 の頃ですが、
基礎技術は変わらないとおもいます。

>> どういう場合にマルチスレッド処理が必要になるのか、
>> というところがまだよく理解できていません。
>> ....
あなたの疑問にまさに答えてくれる良書だと思います。
著者の苦労話がためになります。

基本は、処理のためにUIが固まらないように組むことですね。

>> こういうものを勉強しだすと、一般保護違反なんて
>> 一日に何十回と繰り返しています、メモリリークも然りでしょうか、
>> もしOSが壊れたら再セットアップすれば良いですし、

頼もしいかぎりですねー。
回答者側に回りませんか。

それでは私からひとつ、
「ハンガリー記法」について語ってください。
m_pDlg とか  g_sData とか nCnt とかってやつです。

編集 削除
なーめ  2003-12-09 21:09:44  No: 52708  IP: [192.*.*.*]

>> ウインドウ上のコマンドボタンクリックから
>> CreateThreadでスレッドを作成して、ここで
>> MesageBoxを表示させ、CreateThreadした後で、
>> 1〜1000をカウントアップして、ウインドウに表示
>> してみるとか?

無限ループで数字をカウント、表示し、
MessageBox で OK を押したら、
無限ループを脱出するようにすると、
マルチスレッドの様子がわかると思います。
(なんか、プライマリとセカンダリの使い方が逆ですが)

int bContinue = 1, nCount = 0;
makethread( func, &bContinue ); // スレッドを作る機能をまとめた関数。
while( bContinue )
{
  fprintf( stderr, "%d\r", nCount ++ );
}
closethread(); // waitforsingleobject(), closehandle() などを行う関数

ワーカ(セカンダリ)スレッドでは OK を押すと bContinue を 0 にします。

念のためタスクマネージャでプロセスを表示しておく。

編集 削除
なーめ  2003-12-09 21:27:23  No: 52709  IP: [192.*.*.*]

話題ごとにレス立てていきます。

メモリリーク測定について簡単な方法。
(そんなの聞いてないって...(^^;; )
タスクマネージャで、[プロセス]を選択。
[表示][列の選択]
メモリ使用量、仮想メモリサイズ、ハンドルの数、スレッドの数など
関係ありそうなチェックをつける。

対象アプリで
ボタンを押す度に、いつもの100倍くらい、スレッドを立ち上げるようにする。
スレッドは何らかの処理を終えて終了するものとする。

ボタンを押す前のメモリ量を
[ALT+PrintScrn] で MSPaint に貼り付け保存。

ボタンを押して、メモリの増加を見る。
[ALT+PrintScrn] で MSPaint に貼り付け保存。

全てのスレッドの処理を終えたのを見計らって、
[ALT+PrintScrn] で MSPaint に貼り付け保存。

上記を数回繰り返し、使用されているメモリサイズやハンドル数が
ずりずり増えていればメモリリークです。

ほかに win2k/xp なら、
[マイコンピュータ][管理(G)]
[コンピュータの管理(ローカル)]
   [システムツール]
      [パフォーマンスログと警告]
を使う手もあります。
アプリ側の操作は同じですが。

編集 削除
なーめ  2003-12-09 22:12:51  No: 52710  IP: [192.*.*.*]

>> 一般保護違反

デバッグ時で検出される 0xC0000005 Access Violation.
のことではないですよね。
いわゆるブルー画面。
フリーズとかね。
Win98 以前ならよくあったような。

あ、マシンの掃除ってしてますか(年1くらい)。
ファンに埃が固まっていたりすると結構やばいですよ。

# 自作マシンでケースファン付け忘れた人がいて、落ちまくっていた。(^^;;
# CPU は Athron あたりだったかな。

掃除機とか使ってて外付け HDD の電源がふらつくなんてのも
落ちる原因になるし、HDDの寿命も短くなるようですね。
PC周りは結構たこ足になるので、一度テスターで
100Vかどうか、確認したほうがよいかも。
割と数ボルト下がってますよ。
テープルタップにもオームの法則は適用されますからね。
V = I*R
ついでに
P = I * V で発熱。だからたこ足は危ない。
# テスター友達に貸したら、中の回路黒焦げになって返ってきたことがある。
# 電流計にして100Vを計らないこと。

win2k (P4 1.9G VC++6.0開発機,IISローカル稼動) あまり落ちないな。
winME (Cel 1.4G) も持っているが、ここでは開発してないし(ゲーム機状態)。
winXP (P3 1G) Visual Studio .NET Professional, IIS ... サボりがちだな。
Linux (P1 225M) ... nmbd,samba,Apache,X11で XCreateSimpleWindow() 。
Win98 (Cel 300A) 休眠中。
Linux は落ちないからいいよね。... ってLinuxまで落とす人もいるかな?

P4 3.2G HT がほしいなっと。

で、なんの話だっけ。(^^;;

編集 削除
YuO  2003-12-09 23:57:05  No: 52711  IP: [192.*.*.*]

> メモリリークとの熾烈な戦いに参戦されますか。

マルチスレッドと(メモリ)リークの間に,何の関係があるのですか?
オブジェクトの所有などの考えがない人はシングルスレッドでもリークさせるでしょうし,
作成したオブジェクトをどのオブジェクトが所有しているかをちゃんと考えていれば,
マルチスレッドであってもリークさせないと思いますが。

そして,「資源確保は初期設定」というC++の基礎を押さえれば,
リークの可能性は相当減ると思いますが。
see) http://www.research.att.com/~bs/glossary.html#Gresource-acquisition-is-initialization

編集 削除
なーめ  2003-12-10 05:47:10  No: 52712  IP: [192.*.*.*]

>> マルチスレッドと(メモリ)リークの間に,何の関係があるのですか?
政治的経済的な関係。
あるいは経験則的に、事実上の相関性があると考えます。
マルチスレッドにおいては経験不足・思慮不足による予期せぬ出来事のために
生成、消滅の対称性が崩れる可能性が高くなりがちなわけで。

以下、ギャラリー向けモードで。

>> 基礎を押さえれば

基礎を押えない人が多すぎて困ります。
...って私も押えてない基礎が他人から見ればあるのでしょう多分。
でもそれは自分にはわかりません。
あったら指摘してください。...よろしく。
# 直せないことがあるかもしれません。(^^;;

>> そして,「資源確保は初期設定」というC++の基礎を押さえれば,
>> リークの可能性は相当減ると思いますが。
そう。
しかし、全てのデベロッパがそうしてくれるわけではないので。

具体例:
デストラクタで資源解放 -> try-throw-catch の不正な使用でリーク。
CoTaskMemFree() し忘れ。
Detach() と Empty() の混同。
FindClose() 忘れで、ディレクトリを消せない。
リークする既成の dll を呼び出し、自分のリークだと思い込む。
同じ dll を何度も load
ハンドルの上書き作成
エラーハンドリングの書き忘れ。

#ふう、枚挙にいとまがない。

API仕様をちゃんと読まずにコーディングする。
仕様書にうそが書いてある。
そもそも言語仕様をわかっていない。
仕様書があとからできる。
仕様が気分しだしでころころ変えられる。
コーディング担当者がころころ変わる。
プロジェクトマネージャがころころかわる。
いいかげんな引継ぎ。
他人のコードの誤読。
他人に誤読させかねないトリッキーな記述。
標準的な高速化コードにけちをつけてかえって変なコードにする。
飛ばしたメッセージが何段ものクラスを経て目的のクラスに到着する。
デバッグ中のコードを他人が持ち出し。
自身過剰な人がプロジェクトをひっかきまわす。
協調性のないひとが暴走する。
別プロジェクトでテストして組み込むべきものを主プロジェクトでテストを行う。
現場にテストを持ち込む。
再現性がない。
納期後デバッグ。
後日差し替え。

英語は読まない。
過去の技術に固執する。<- .... ひと事じゃないか ....

聞きたくないよね。こんな話。
_( O )_

★メモリリークとの熾烈な戦い
とは
マルチスレッドができるとなると、
マルチスレッドのリークデバッグの仕事がくるかもしれません。
ということで。
人月100万はふっかけてやりましょう。
それも、納期不定で。

# 趣味ならいいのさ、お気楽で。
体悪くするよ、ほんとに。
(鬱);;

編集 削除
なーめ  2003-12-10 06:57:49  No: 52713  IP: [192.*.*.*]

>> 実は、MFCは全く知りません(^^;
>>もう少し、簡単に作れて気の利いたものがあればよいのですが。

ある意味気の毒なので。
すでにご存知かもしれませんが、
ギャラリーとして初心者も見てることを意識したうえで、
お手軽テストプログラム作成法です。

Visual C++ 6.0 を起動します。
[ファイル][新規作成][プロジェクト]
位置と、プロジェクト名(XXXと仮定)を適切に入力。
MFC AppWizard (exe) を選択。
[OK]
   ◎[ダイアログベース]  を選択
[次へ(N)>]
[次へ(N)>]
[次へ(N)>]
[終了(F)]
[OK]

#ここまでの操作を何ミリ秒でできるか計測しておきましょう(不要).

ここまでで最初にダイアログエディタが表示されるので、
編集中のダイアログの[OK]をダブルクリック。
OnOKを書き換えるように促されますが、書き換えません。
[OK]
すると自動的にソースコードに CXXXDlg::OnOK() が追加される。
(XXX - 仮定したプロジェクト名)
ソースコードの CXXXDlg::OnOK() が表示されるので、
ここにテストプログラムを書く。
ここで、

void CXXXDlg::OnOK() 
{
  // TODO: この位置にその他の検証用のコードを追加してください
  
  // CDialog::OnOK();
}

のように
CDialog::OnOK();
をコメントにします。
コメントにしないと、追加したコードを実行したら(今のところ)プログラムは終了してしまいます。
終了するのであれば、そのまま残しておいてかまいません。

とにかく OnOK() の中にテストしたいコードを書きます。
このとき、コンパイルモードは Win32 Debug となっています。

コンソールに printf で文字列を出すようなことをしたい場合はTRACE() を使います。

TRACE() は Visual Studio のデバッグ窓に出力されます。

テスト2を追加したいときは。
ダイアログエディタに戻り、ボタンを追加。
そのボタンをダブルクリックするとコマンドハンドラが追加
されるので、同様に作業できます。

OnButton1()
OnButton2()
....

編集 削除
祐香  2003-12-10 15:16:56  No: 52714  IP: [192.*.*.*]

こんにちは。

頼もしくなんてないですよ(^^;
下手なコード書いてOSを落としまくっているだけですから。

>あなたの疑問にまさに答えてくれる良書だと思います。
>著者の苦労話がためになります。

>基本は、処理のためにUIが固まらないように組むことですね。

それじゃ、早速購入して勉強してみますね。

それに、回答者だなんて滅相もない、
私はまだVCを始めたばかりの初心者ですので(^^;

>無限ループで数字をカウント、表示し、
>MessageBox で OK を押したら、
>無限ループを脱出するようにすると、
>マルチスレッドの様子がわかると思います。
>(なんか、プライマリとセカンダリの使い方が逆ですが)

これ、良いかもしれませんね。

つまり、ウインドウ上のコマンドボタンクリック
あたりから、MessageBoxを出すと共に
カウントアップを始め、OKで終了とするという事ですね。

>プライマリとセカンダリの使い方が逆ですが

というのは?

その場合、MessageBoxがセカンダリスレッドでいいですよね?
そして、カウントアップがプライマリでいいですよね?

これが、普通は逆ですか?

>int bContinue = 1, nCount = 0;
>makethread( func, &bContinue ); // スレッドを作る機能をまとめた関数。
>while( bContinue )
>{
>  fprintf( stderr, "%d\r", nCount ++ );
>}
>closethread(); // waitforsingleobject(), closehandle() などを行う関数

>ワーカ(セカンダリ)スレッドでは OK を押すと bContinue を 0 にします。

これがちょっと理解できませんで(^^;

while( bContinue )
{
  fprintf( stderr, "%d\r", nCount ++ );
}

これが無限ループで、bContinue =0で
ループを脱出というのは理解できます。

makethread( func, &bContinue ); 
こっちが問題ですね。

int bContinue = 1, nCount = 0;

CreateThread(NULL , 0 , ThreadFunc , (LPVOID)&bContinue , 0 , &dwID);

while( bContinue )
{
  fprintf( stderr, "%d\r", nCount ++ );
}

closethread();

DWORD WINAPI
ThreadFunc(LPVOID    pParam)
{

    if MessageBox() == MB_OK {
         pParam == 0;
    }
    return 0;
}

おおよそ、こんな感じでしょうか?

編集 削除
なーめ  2003-12-11 00:59:20  No: 52715  IP: [192.*.*.*]

>プライマリとセカンダリの使い方が逆ですが

ワーカスレッドは子供ですから、
親であるプライマリスレッドが監視してやらねばなりません。

という表現で理解していただけるでしょうか。
たとえばワーカースレッドがIEのように通信を行って
データを取得するものとしましょう。
タイムアウトといった仕掛けが必要になります。
この時間計測をするのがプライマリスレッドです。

ところでYuO さんの、
>> ただし,スレッド関数の内部で標準ライブラリの関数を呼び出すのであれば,
>> 標準ライブラリが提供するスレッド作成関数(VC++なら_beginthreadや>> >> >> _beginthreadex)を利用することになります。
は重要ですよ。
確かに "_" で始まって、全部小文字の関数はかっこ悪く見えるかもしれませんが、
Cランタイム(標準ライブラリ)のためのスタートアップが入っているものですから。
このことを確認するために、
リンクするライブラリの設定からlibcmt.lib をとってみてください。
設定場所:
[プロジェクト][設定][リンク][インプット]
(デフォルトライブラリを全て無視)

リンクに失敗するはずです。
もし、リンクに失敗しなければ CreateThread でも大丈夫だとは思いますが。
デフォルトライブラリを全て無視にして libcmt.lib を追加するとリンクに
成功します。

というか、そもそも 
[プロジェクト][設定][C/C++][コード生成]

[設定の対象(S)] | [使用するランタイムライブラリ(L)]
---------------+----------------------------------
Win32 Debug    |  マルチスレッド(デバッグ)
Win32 Release  |  マルチスレッド

ちゃんと設定してますか。

編集 削除
なーめ  2003-12-11 01:11:14  No: 52716  IP: [192.*.*.*]

DWORD WINAPI
ThreadFunc(LPVOID    pParam)
{
    if MessageBox() == MB_OK {
         pParam == 0;
    }
    return 0;
}

おいおい(^^;; ... 単なるケアレスミスか。
とりあえず問題点は3点、Cの基礎なんで、自分で気づいてみて。
1つはコンパイラが指摘してくれますが。
# MessageBox の引数のことではありません。

編集 削除
なーめ  2003-12-11 01:24:34  No: 52717  IP: [192.*.*.*]

>> リンクに失敗するはずです。
すみません、
スレッドの中だけに限定した判定方法ではないですよね。
プライマリスレッドでランタイムライブラリ使っていても大丈夫なので、
別の判定方法を考えるべきでした。
うーん、スレッドのプログラムを取り出して Win32 Dynamic-link library
のプロジェクトを仮作成してみて上記を行うとか。
面倒だねー。
結果から判断できるような、なんかいい方法はないかな。

編集 削除
祐香  2003-12-11 11:30:41  No: 52718  IP: [192.*.*.*]

こんにちは。

VBばかりやっていると、ポインタの基本も忘れてしまっていました。

CreateThread(NULL , 0 , ThreadFunc , (LPVOID)bContinue , 0 , &dwID);

DWORD WINAPI
ThreadFunc(LPVOID *pParam)
{
    if MessageBox() == MB_OK {
         pParam = 0;
    }
    return 0;
}

会社にVCの環境が無いので、基本動作の確認もせずに
申し訳ありませんでした<m(__)m>

編集 削除
なーめ  2003-12-11 23:37:27  No: 52719  IP: [192.*.*.*]

このスレッドも長くなりすぎたかな。
内容も MessageBox から スレッドの使い方になってるし。
質問スレッド立て直したほうがよいかも。
# H"32K なんでダウンロードが重いだけ(^^;

>> CreateThread(NULL,0,ThreadFunc,(LPVOID)bContinue,0,&dwID);
改悪しちゃだめですよ。
(LPVOID)&bContinue;

DWORD WINAPI
ThreadFunc(LPVOID *pParam)
{
    // >> VBばかりやっていると
    // もろ VB ですねー、括弧がない。
    //>> if MessageBox() == MB_OK
    if( MessageBox() == MB_OK )
    {
         // 1つだけ改善されているけど
         //>> pParam = 0;
         *pParam = 0;
    }
    return 0;
}

私が書くと次のようになりました。
///////////////////////////////////////////////////////
typedef struct tagSem ssem;
struct tagSem
{
  BOOL m_bContinue;
  void * m_pVoid1;
  void * m_pVoid2;
}
g_Sem;

DWORD WINAPI ThreadProcMessageBox(
  LPVOID lpParameter   // スレッドのデータ
)
{
  ssem * pSem = (ssem *)lpParameter;
  if(( pSem ) && ( pSem->m_pVoid1 == (void *)pSem ))
  {
    pSem->m_pVoid2 = pSem;
    //  ちょっとした実験をした。(アセンブラによる割り込み禁止)
    //  例外処理 (初回) は p03.exe にあります: 0xC0000096: Privileged Instruction。
    //  __asm
    //  {
    //    cli
    //  }
    MessageBox( NULL, "ここを押して", "なーめ", MB_ICONINFORMATION );
    pSem->m_bContinue = FALSE;
    //  いらないかも知れないが...
    ExitThread( 0 );
  }
  else
  {  //  渡されたアドレスがNULLまたは渡したアドレスと違う場合。
    __asm
    {
      int 3;  //  Debug Break;
    }
  }
  return( 0 );
}

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow )
{
   // TODO: この位置にコードを記述してください。
  MSG msg;
  HACCEL hAccelTable;

  // グローバル ストリングを初期化します
  LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
  LoadString(hInstance, IDC_P03, szWindowClass, MAX_LOADSTRING);
  MyRegisterClass( hInstance );

  // アプリケーションの初期化を行います:
  if( !InitInstance( hInstance, nCmdShow ) ) 
  {
    return FALSE;
  }

  //////////////////////////////////////////////////

  DWORD dwThID;
  g_Sem.m_pVoid1 = &g_Sem; // アドレスチェック用
  g_Sem.m_pVoid2 = NULL;
  g_Sem.m_bContinue = TRUE;
  int nCnt = 0;
  char s[1024];
  HANDLE hTh = CreateThread( NULL,0,ThreadProcMessageBox, &g_Sem, 0, &dwThID );
  OutputDebugString( "Running.\n");
  Sleep( 1 );  //  これを入れないと MessageBox の出現が遅くなる。
  if( hTh )
  {
    while( g_Sem.m_bContinue )
    {
      //  デバッグ窓にカウントを出力
      sprintf( s, "COUNT= %d\n", nCnt++ );
      OutputDebugString( s );
    }
  }
  WaitForSingleObject( hTh, INFINITE );
  CloseHandle( hTh );
  //  念のためアドレス確認
         if( g_Sem.m_pVoid1 != g_Sem.m_pVoid2 )
         {
    sprintf( s, "GSEM: %08X,PSEM: %08X.\n", g_Sem.m_pVoid1, g_Sem.m_pVoid2 );
    OutputDebugString( s );
    __asm
    {
      int 3;
    }
  }

  //////////////////////////////////////////////////
あと省略(自動生成されたまま)。

編集 削除
ブタゴリラ  2003-12-13 00:10:18  No: 52720  IP: [192.*.*.*]

すごい話の内容が飛んでおりますが(^^;

>MessageBox()を表示すると、
>ここで処理が中断しますが、
>表示しまたまま処理が次のステップへ
>進むようなオプションはないでしょうか?

もういっその事CreateDialog関数で実装すればよろしいのでは?
そっちの方が楽だと思います。



後、なーめさんにちょっと突っ込み

>WinExec() を調べてみました。
>ブレークポイントを張り、混合モードでステップ実行(F11)です。
>スタック上にSTATUPINFO と PROCESS_INFORMATION をとり、
>STARTUPINFO を全クリアした後、メンバ cb に構造体サイズ 0x44 を設定、
>wShowWindow メンバに引数 SW_SHOW を設定しています。
>そして、CreateProcess() を呼び出すわけですが、
>lpCommandLine  は WinExec() に与えた文字列。
>lpStartupInfo とlpProcessInformation はスタックに作成した領域を
>渡しますが、ほかのパラメタは全て0になっているようです。

>まあ、このような呼び出しを行う場合は WinExec() と同じだよということですね。

*私の話は、あくまでもVC++6.0SP5での話だと思ってください。(^^;

これは、あくまでも、一定のOS(Win2000ならWin2000)での話で、
Win9x等では、別の実装をしている可能性があります。
特に、逆アセ掛けて見る場合は、コールされているアドレスがポインタ参照である場合はお気をつけ下さい。
こういう発言はどうかと思いますけど・・・。


>にしても、同じようなことをするのに似たような関数がいっぱいあるというのは
>Windows の最大の欠点ですね。

まあそうですよね。
苦肉の意見としてはいちいち設定するのがめんどくさい人用でしょうね。

編集 削除
なーめ  2003-12-13 03:09:38  No: 52721  IP: [192.*.*.*]

>> Win9x等では、別の実装をしている可能性があります。
そうですね。
この結果だけ、持ち出されると困ったことになりかねません。
_( O )_
私としてはただ、ひとつのアプローチを示したかったのでした。

# というか、テスト/追認せずに採用するなんて信じたくない話ですが。
# 実際そういう人はいるかもしれませんね。(^^;;

私の認識としては、あるOS上で開発したプログラムは
ほかの、OS上でも動くことがある、といった程度です。
リンケージオプションを変えるだけでも、異なった結果なりますね。
そもそも、単純にDebug/Release を変えただけで違う可能性があります。

そのためにプログラムのエラーレポート部分は煩雑になります。(^^;;

...それに後で考えると、リバースEng.を公表したようなものですね。
良くないことでした。_( O )_

# 方法がある、ということと、方法が使えるということは別のこと。

編集 削除