VC++ 6.0を使ってシリアル通信を行うプログラムを開発しています。
現在テストプログラムとしてダイアログベースによる開発を行っております。
シリアル通信処理はrs232c.libというシェアウェアのライブラリを使用し、このライブラ
リのメソッド呼び出しなども全てダイアログクラス上で行っています。
本番プログラムでは、SDIベースのフォームビューを用いたインターフェイスにするの
で、テストプログラムでダイアログクラス上で作成したシリアル通信機能をクラスにまと
めようと思います。
新規クラス作るためにClassWizerdを使うわけですが、その時必要になる基本クラスは一
般的に何を指定するのでしょうか?
新規クラスで実現したい主な機能は
・シリアル通信(rs232c.libによる)
・WM_TIMERメッセージ処理
です。
何を指定すればよいのか判らなかったので、試しにCCmdTargetクラスを基本クラスに設定
してみました。
そこにダイアログベースで作成した処理をメソッドにコピーしてコンパイルしてみたので
すが、基本クラスが違うためSetTimer()やOnTimer()がダイアログベースとは違うものを
オーバーロードしているらしく、コンパイルエラーが発生します。
どうも上手く説明できなくて申し訳ありません。
どなたかご教授願います。
開発環境
Windows XP SP1
Visual Studio 6.0 (VC++ 6.0)
あなたが作成したいクラスはウィンドウですか?
というよりウィンドウじゃないですよね。
MFCとは関係ないGenelicにして基本クラスなしです。
ウィンドウが無い場合はウィンドウメッセージが扱えません。
ウィンドウがなくてもSetTimerとか使えるけど面倒だな。
場合によってはってことだけど
最初に苦労したばっかりにあとあとも苦労し続けることもあるから
『上位アプリでWM_TIMERからxxx.OnTimer()を呼んでください。』
みたいに楽に済ませることもあるな。
以下の方法でいいと思いますが、
1. 親ウインドウ、CMainFrameか、CXXXViewのクラスで
WM_TIMERに対応するタイマールーチン
「void CXXX::OnTimer(UINT nIDEvent){} 」
をClassWizardで作成する。
2.「void CXXX::OnTimer(UINT nIDEvent){} 」で動かす処理をメンバ-関数
として新規クラスで作成する。
3.「void CXXX::OnTimer(UINT nIDEvent){} 」で新規クラスで作成した関数を呼び出す
タイマーに限らず、ウィンドウに関わる処理は、CMainFrameか、CXXXViewのクラス
でないと出来ないと思っていいです。(例外もあると思います。)
ダイアログベースの場合は、「CDialog」です。
WM_TIMERくらいなら、CWndでも充分できると思いますが。
逆に、Frame系やView系を使ってしまうと、ダイアログプロジェクトから使うのは困難ですし。
超初心者さんの書かれているような方法も比較的お勧めです。
>皆様
ご回答有難うございます。
新規クラス作成で基本クラスにCViewなどを指定しても良いのですが、継承してもWM_TIMERしか
使わないので必要最小限のMFC機能だけを使いたいと思っています。
GenelicはMFC機能を使わないで新規クラスを作成する場合に使用するという認識でいます。
それでも、メンバ関数内でMFCのあるクラスのインスタンスを生成する事やメッセージを使うこ
とが出来るのでしょうか?
ちょっと開発環境が手元に無いので試せないので、すぐ確認できないのがはがゆいです。
MFC機能といわれても全部できるかどうか知らない。
そこまでは約束できない。
俺rs232c.libを知らないし。
MFCが使えるプロジェクトを作成しているとして
MFCの派生クラスではないメンバ関数からCStringとか使えるよ。
クラスじゃない関数からもCStringとか使えるよ。
CStringをメンバ変数にしてメンバ関数から使えるよ。
CViewに組み込んで
後から本番ではフォームビューに変えるとか
出来なくはないが余計な手間取るね。
>GenelicはMFC機能を使わないで新規クラスを作成する場合に使用するという
>認識でいます。
VisualC 6.0MFCの「Genelic」は「Genelic CWnd」です。
基底クラスは、MFCクラスの「CWnd」です。
超初心者さんの書かれているGenericは、文脈からGeneric CWndではないGenericだと思います
が。
変にMFCにこだわるより、Genericにして、
『上位アプリでWM_TIMERからxxx.OnTimer()を呼んでください。』
とかの方が、MFCのクラスに依存せずに可搬性が高いですし。
# まぁ、MFCに固執せずともWin32でも十分といえば十分?
私もBanさんの意見に賛成。
ウインドウとしての機能は全くなく、Timerイベント為だけにCWndを規定にしても
意味が無いし、そもそもCWndを規定にすると言う事はウインドウクラスなのだから
ウインドウで無いクラスをそこから起こすのは使う側から見ても混乱の原因になると
思います。
超初心者さんの書かれている方法を採用してTimer処理は外部に任せてしまう方が
可搬性が高いのではないでしょうか。
無理にクラスにしなくても単純に使いやすいようにラップしたライブラリでも
良い様な気はしますね。クラスであることが意味があるのであれば話は別ですけれど。
あうあう。
規定じゃなくて基底でした。
また、いらんことでスレッドを伸ばしてしまった。
Banさん、PATIOさん
ご意見有難う御座います。
クラスの種類を「Genericクラス」にするということですね。
超初心者さんの言われている
『上位アプリでWM_TIMERからxxx.OnTimer()を呼んでください。』
の意味を良く理解していないのですが、基本クラスにGenericを指定した新規クラスを作
成し、引数を4つ持つSetTimer()でコールバック関数を指定することでタイマ割り込みを
発生させる事ができるようになりました。
機能的には満たしているのですが、作り方に問題が無いか心配です。
そのSetTimer(Win32API)は、実は「メッセージループが必要」という前提がありますが、
まぁありがちで無難な実装だとは思います。
>『上位アプリでWM_TIMERからxxx.OnTimer()を呼んでください。』
の方は、クラスの方ではタイマを自分で呼ばないってことです。
「ダイアログでもビューでもいいから、ウィンドウにはタイマハンドラを作って、
この関数(例えばFooBar::OnTimer();を呼んで使ってね」ってルールにする。
>そのSetTimer(Win32API)は、実は「メッセージループが必要」という前提がありますが、
>まぁありがちで無難な実装だとは思います。
そういったご意見を伺えて、ちょっと安心しました。
今日作成したクラスを使ってタイマ割り込みのテストを行ってみました。
SetTimer(Win32API)を呼び出したメソッドでは、While(1)による無限ループを行っています。
このメソッド内では割り込みを検知できますが、無限ループの中でこのクラスのPrivateなメソ
ッドを呼び出し、そのメソッドでも無限ループを行った場合の検知はできませんでした。
もともとUNIXの開発を中心にやってきたもので、signal(SIGALRM)とalarm()の関係のように何
処からでも割り込みを検知できると思っていたのですが、VC(Windows)ではちょっと違うんです
ね。
何はともあれ、無事に目的のクラスを作成できました。
ご教授いただいた皆様、どうも有難うございました。
> SetTimer(Win32API)を呼び出したメソッドでは、While(1)による無限ループを行っています。
> このメソッド内では割り込みを検知できますが、無限ループの中でこのクラスのPrivateな
> メソッドを呼び出し、そのメソッドでも無限ループを行った場合の検知はできませんでした。
えっと、よく構成が飲み込めてませんが、結果として、
SetTimerに指定したメソッドの中で無限ループしてる、or
SetTimerした方が無限ループしてる、とかですか?
そういうことをすると、メッセージループに影響が出てしまうと思いますが。
(それが、『実は「メッセージループが必要」』ってあたりなのですが)
SetTimerの場合、こんな特徴があります。
・実体はWM_TIMERメッセージで駆動するので、メッセージループと呼ばれるAPI群がスレッド内で
呼ばれていなければならない⇒むやみに無限ループしてたらダメ
・WM_TIMERは優先度の低い特殊なメッセージのため他のメッセージがたまっていると動かない。
(精度が低い/必ず呼ばれる保証がない)
その代わり、メッセージ処理と親和性が高く、負荷も低く、扱いやすいのが特徴です。
アラームに挙動が近いのは多分、こっちのAPIかと>timeSetEvent