MFCでソフトを作ったときに、連続処理をするには?

解決


ヨシ  2006-06-16 20:12:40  No: 62195

どなたか助けてください!
初心者ですがよろしくお願いします。

今、VisualC++.NETでMFCを使った開発を行っています。

ダイアログベースで全体を作成して
ソースファイルには
Sample.cpp
SampleDlg.cpp
stdafx.cpp
が作成されます。

ダイアログにはボタンを貼り付け、そのボタンのイベントハンドラを
SampleDlg.cpp内に記述しました。

そこで実行すると、ボタンの処理は実行されるのですが、
繰り返して同じボタンを押したときに、2度目以降の処理が実行されません。

何か原因は考えられるでしょうか?

ご回答よろしくお願いします。


Blue  2006-06-16 20:17:09  No: 62196

> 繰り返して同じボタンを押したときに
ボタンは押せる状態になっていますか?

スレッドで動かしていないのであれば、ボタンを押して呼ばれる作業が
終るまで何もできないようになっています。


ヨシ  2006-06-16 20:56:07  No: 62197

ご回答ありがとうございます

> ボタンは押せる状態になっていますか?

ボタンは押せる状態になっています。
スレッドにする必要はなく、処理が終わってから次の処理に移るように
作成したいと考えています。

どうでしょうか。。


Blue  2006-06-16 21:00:32  No: 62198

どういう状況になっているかよくわかりません。

クリックで呼ばれる処理の中にブレイクポイントを設定して、
デバッグ実行して、ちゃんと呼ばれているか確認してみてはどうでしょうか?


ヨシ  2006-06-16 21:08:21  No: 62199

デバッグ実行もしてみたのですが、見たこともない奥の方のプログラムに行きついてしまいました。
今からもう一度試して再度報告いたします。

よろしくお願いします。


ヨシ  2006-06-16 22:24:10  No: 62200

デバッグ実行すると
cmdtarg.cpp

dlgcore.cpp

wincore.cpp

afxstate.cpp
とプログラムをたどって、
「現在の場所のソース コードを表示できません。」
というポップアップが出てきました。

私はC言語での開発を少々かじったことのある人間です。
そこで、疑問に思っていることがあります。
SampleDlg.cpp内では、Testボタンに対して
以下のような関数を割り当てたのですが、
void CsampleDlg::OnBnClickedTest(){}

そもそもこの関数はどこから呼ばれているのでしょうか?

呼び出し元がループをかけていないから繰り返し処理が行われていないのでは?
と考えたのですが呼び出し元が特定できません。

わかる方宜しくお願いしますm(__)m


Blue  2006-06-16 22:29:19  No: 62201

> そもそもこの関数はどこから呼ばれているのでしょうか?
MFCの内部のメッセージを切り分けるところです。
メッセージループするので、1行ずつのステップ実行では効率が悪いです。

ですので、ステップ実行ではなく、ブレイクポイントを使って、
F5実行(かな?)で確認してみてはどうでしょうか?


よし  2006-06-16 23:51:08  No: 62202

Blueさんありがとうございます。

私の知識が浅く、指摘の内容が少しわかりませんでした。
今やろうとしていることを、最初からもう少し詳しく書こうと思います。

やろうとしたいこと→[ハイパーターミナルを使って、別のPCに文字列を転送する。]

・MFCを使いダイアログボックスベースでアプリを作成します。(VisualC++.NET)
  Sample.cpp
  SampleDlg.cpp
  stdafx.cpp
  以上、3つのソースファイルが自動作成されました。

・次にdialogには「test」というボタンを作成します。
  testにイベントを割り当て、以下の関数がSampleDlg.cppに作成されます。
  そこで、自作の関数comset()、send()を2回呼び出す記述をします。
  
void Csample7Dlg::OnBnClickedTest()
{
  // TODO : ここにコントロール通知ハンドラ コードを追加します。
  comset();
  send("AAA");
  send("BBB");
}

  comset()        →     転送を準備する関数。
  send("文字列")  →     文字列を転送する関数。

・これで転送先のPCでは"AAA","BBB"の文字列を受け取ることができます。

  しかし、もう一度ボタンを押したときに何も反応がありません。
  (ここで、ボタンを押した回数だけ文字列を転送したいんです。。)

以上が現象です。

デバッグの件ですが、ブレークポイントをOnBnClickedTest()の文頭に入れたパターンと
文末に入れたパターンを試してみたのですが、やはり
afxstate.cpp
「現在の場所のソース コードを表示できません。」

に行きついてしまいました。

#あとステップイン、ステップアウト、ステップオーバーすべてでやってみたのですが、
結果は全部一緒です。どのデバッグを使えばよいのでしょうか?

これから何かわかりますでしょうか?

長文失礼しました。

ご回答宜しくお願いします。


Blue  2006-06-16 23:55:32  No: 62203

問題は、ダイアログの方ではなく、

> comset();
> send("AAA");
> send("BBB");
という関数にあるのではないでしょうか?
その関数が実際どうなっているのかわからないのでこれ以上解答できないような。

推測ですが、comset()は2回も呼んでいいのでしょうか?
1回しか読んではいけない気がします。


ヨシ  2006-06-17 01:15:16  No: 62204

comset()は1回しか呼んでいません。

comset(),send()の内容は以下の通りです。
1度はsendできているので、このソースに誤りはないと思っているんですが、
いかがでしょうか??

void Csample7Dlg::comset(void)
{
  hComm = CreateFile("COM1", GENERIC_READ|GENERIC_WRITE, 
                0, NULL, OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL, NULL);

  //COM通信パラメータを格納しているDCB構造体を設定する。
  DCB lpDCB;
  GetCommState(hComm, &lpDCB);
//if(GetCommState(hComm, &lpDCB)==0)return;

  lpDCB.DCBlength = sizeof(DCB);
  lpDCB.BaudRate = 9600;
  lpDCB.Parity = NOPARITY;
  lpDCB.StopBits = TWOSTOPBITS;
  lpDCB.fParity = FALSE;
  lpDCB.fBinary = TRUE; 
  lpDCB.ByteSize = 8;

  SetCommState(hComm, &lpDCB);

  //読み書きできない時のタイムアウト設定
  COMMTIMEOUTS timeout;
  GetCommTimeouts(hComm, &timeout);

  timeout.WriteTotalTimeoutConstant=1100;
  timeout.ReadTotalTimeoutConstant=1100;

  SetCommTimeouts(hComm, &timeout);

}

void Csample7Dlg::send(string s)
{
  DWORD dw;
  unsigned char *p;

  int len = s.size();
  p = (unsigned char *)malloc(sizeof(unsigned char) * len+2);
  
  for(int i = 0;i < len;i++){
    p[i] = s[i];
  }
  p[len] = 0x0D;//CR
  p[len+1] = 0x0A;//LF

  WriteFile(hComm,p,len+2,&dw,0);

  free(p);

  return;  
}


Blue  2006-06-17 01:24:30  No: 62205

> comset()は1回しか呼んでいません。

void Csample7Dlg::OnBnClickedTest()
{
// TODO : ここにコントロール通知ハンドラ コードを追加します。
comset();
send("AAA");
send("BBB");
}

このようにかいていると、2回目にボタンを押すと、呼ばれますけど。


よし  2006-06-17 01:59:50  No: 62206

解決しました!

おっしゃるとおりでした。
「test2」というボタンを作って関数は以下のようにしました。
void Csample7Dlg::OnBnClickedTest()
{
// TODO : ここにコントロール通知ハンドラ コードを追加します。
comset();
send("AAA");
send("BBB");
}

void Csample7Dlg::OnBnClickedTest2()
{
// TODO : ここにコントロール通知ハンドラ コードを追加します。
send("AAA");
send("BBB");
}

一度目のクリックで1を押し、二度目以降は2をクリックすれば連続処理が可能でした。
ありがとうございました。
要は、comsetは最初の1回のみ使用すべきだということですね。

と、そこで新たな課題が出てきたのでわかったら教えてくださいm(__)m

アプリ立ち上げ時に無条件でcomset()を実行するにはどうすればよいのでしょうか?
本来アプリには複数のボタンを配置するつもりなのですが、
どのボタンが最初に押されるかわからないのでアプリ立ち上げ時にcomset()を呼び出すのが
一番よい方法だと思いました。
いかがでしょうか?

ご意見お聞かせください。

宜しくお願いいたします。


スナナ  2006-06-17 02:00:55  No: 62207

Blueさんのご指摘の通り、ボタンを押す度に毎回シリアルポートをオープンしてしまっています。
CloseHandle()していないので、おそらく二度目のcomset()内のCreateFile()でINVALID_HANDLE_VALUE(-1)が返って来ているのではないでしょうか?
だとしたらcomset()をOnInitDialog()等で呼ぶようにすれば解決します。


スナナ  2006-06-17 02:02:42  No: 62208

書き込みがかぶってしまいましたね(^^;
先ほど書いたようにダイアログの初期化時ならばダイアログクラスのOnInitDialog()、アプリケーションの初期化時ならアプリケーションクラスのInitInstance()でcomset()を呼ぶようにしてみてください。


Blue  2006-06-17 02:04:58  No: 62209

> どのボタンが最初に押されるかわからないのでアプリ立ち上げ時にcomset()を呼び出すのが
> 一番よい方法だと思いました。
多分そうでしょう。
タイミング的には、InitDialogあたりでしょうかね。

また、他に「接続」みたいなボタンをつくるとか。

ところで、開いたハンドル(hComm)は閉じていますか?
多分、一回一回閉じれば今までのヤツでもうまくいくかも。(効率悪いですが)


ヨシ  2006-06-17 02:50:05  No: 62210

Blueさん、スナナさん、ありがとうございました。

comset()をOnInitDialog()内で処理することで解決しました。
また「接続」ボタンを使ってみる方法も試してみてうまくいきました。

開いたハンドルは閉じていなかったので、閉じるように対応します。

また何かありましたらお力貸してください。
宜しくお願いします

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


ヨシ  2006-06-17 02:50:37  No: 62211

解決にチェックを付け忘れました。。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加