ビュ切り替えで問題に遭遇している方が多く居られます、この門愛は開発中には問題が起きませんがこれからリリ-スに切り替える時に例外のオンパレ-ドになります!こうなってからは諦める方が多く居られるようです。このデバガ⁻モ-ドから正規のモードに移行する時に起きる場合がすべてだと述べても問題は有りません。
ヒープエリヤ-にメモリ-が割り当てられる即ちリンカ-処理がこの例外を引き起こします、例えばアプリが多数起動している場合それこそ阿波の様に実態が無いメモリエリヤが生まれて行きますこの実態のない部分をアクセスするから例外が起きるのです
ビュー切り替えは何のためにやるのでしょうか?私が子供だった昔々に紙芝居が有りました毎週楽しみに見ていました。それはその都度物語が変化(シナリオですかね!)黄金バット・少年ジェット等々心弾む内容でしたその様な事とビュ-切り替えは同じだと私は思うのです、ビュ-を説明しているHPは沢山ありますがどれも一山いくらの応用できないものばかりです。これでは将来一体どうなるのか心配です!ワ-ド・エクセル・MAYA等々沢山ありますが皆VC++で開発されているのです。高級言語と言われるBASIC・C#も同じです。
下記の画面でMFSを説明します
コントロ-ラとリストコントロ-ラ!、それらとフレ-ムビュが多く存在しますがノウハウ(約束事です)を守れば例外は起きないのです、大きなヒントとしては「スタティツクエリヤ」を使いその範囲にメモリ-を割り当てて行きますこの目的はリンカ-に対して正確な指示を出します。
今回説明するプロジェクトです。(不定期更新 質問は常時受け付けます)
ビュを切り替えるとは
ビュ-を切り替えるとはシナリオを変更するのであり、必ず又元のビュ-に戻る事になりますこの場合インタ-フエ-スが切り替わる意味を持ち合わせています、現在のビュ情報を全て保持する事が絶対条件です、ですから1回目のビュ-切り替えは成功するが2回目の操作でビュ-切り替えが失敗する事がほとんどで。その場合には多くの例外が発生している事と思われます。
例外の発生するアプリ構成とは
例外をスル-する機構は色々と有ります?しかし原因を取り除く事は出来ません!この例外の多くはメモリ-関係で起きています。MFSのデバガ-は非常に素晴らしい物です、どの様なアプリでもシステムが落ちる事は有りません、でも他のの開発ツ-ルであるデルファイとかでは絶えずシステムダウンします。VC++を使う理由はこのデバッガ-を使える事だけでも価値があります。
下記のコ-ドを良く見て下さい、static形が使われています、これらの変数は昔で言う所のOMエリヤにリンカ-で配置されることになります。
/////////////////////////////////////////////////////////////////////////////
static _TCHAR* ControlPracticeColumnLabel[]={_T("------"),_T("マーク"),_T("マーク")};
static int ControlPracticeWidth[]={13*13+2,13*4+2,13*4};
static int ControlPracticeFmt[]={LVCFMT_CENTER,LVCFMT_CENTER,LVCFMT_CENTER};
/////////////////////////////////////////////////////////////////////////////
static _TCHAR* LeftFormColumnLabel[]={_T("------"),_T("マーク"),_T("マーク")};
static int LeftFormWidth[]={13*13+2,13*4+2,13*4};
static int LeftFormFmt[]={LVCFMT_CENTER,LVCFMT_CENTER,LVCFMT_CENTER};
/////////////////////////////////////////////////////////////////////////////
static _TCHAR* RightFormColumnLabel[]={_T("------"),_T("マーク"),_T("マーク")};
static int RightFormWidth[]={13*7,13*7,13*7};
static int RightFormFmt[]={LVCFMT_CENTER,LVCFMT_CENTER,LVCFMT_CENTER};
/////////////////////////////////////////////////////////////////////////////
//////////////////リストコントロ-ル様に領域を確保////////////////////////////
////////////////// 型 Char ////////////////////////////
/////////////////////////////////////////////////////////////////////////////
static _TCHAR ControlPracticeView_buffDim[100][8][MAX_PATH];
static _TCHAR LeftFormVew_buffDim[100][8][MAX_PATH] ;
static _TCHAR RightFormVew_buffDim[100][8][MAX_PATH];
/////////////////////////////////////////////////////////////////////////////
処理の流れを知る必要がある
このアプリの場合ではどの様な構成でどの様に初期化されるのかを理解してから始めてビュ-の切り替えを行う事が出来るのです。
起動されたアプリ
実装されているコントロ-ルは適当ではあるが正しく初期化されているのがお解り頂けると思う、実装されている全てのコントロ-ルの中でリストコントロ-ルを除いて最小限の理解が必要ではあるのですが、貴方は理解できていますか?
アプリで使用されているクラス構成は?
一番始めにウィンドウズを制御する他為にメッセ-ジハンドラ-で使うストリングリストを定地する必要があります、各クラスは定義されたメッセ-ジを受けたり発行したりしながら制御をして行きます。
下記メッセ-ジが最低限必要な物になります
下記の文字列は意味合いで内容が変化して行きますがメインフレ-ムからフレ-ムビュ-へ又フレ-ムビュ-からメインクラスへの更新指示を発行して行きます。それぞれのクラスから発行された内容によりメインフレ-ム及びフレ-ムビュ-等々の処理内容が順次処理が実行されて行きkます。
#define ID_VEW_CHENGER (WM_USER + 100)
/////////////////////////////////////////////////////////////////////////////
#define ID_FORM_LEFT_UPDATE_ID_OK_DIS (WM_USER + 101)
#define ID_FORM_RIGHT_UPDATE_ID_OK_DIS (WM_USER + 102)
/////////////////////////////////////////////////////////////////////////////
#define ID_FORM_LEFT_UPDATE_ID_CANCEL_DIS (WM_USER + 103)
#define ID_FORM_RIGHTUPDATE_ID_CANCSL_DIS (WM_USER + 104)
/////////////////////////////////////////////////////////////////////////////
#define ID_FORM_CONTROL_PRAVEW_ID_OK_DIS (WM_USER + 105)
#define ID_FORM_CONTROL_PRAVEWID_CANCSL_DIS (WM_USER + 106)
#define HINT_SIZE_CHENG (WM_USER + 107)
/////////////////////////////////////////////////////////////////////////////
#define LINE_COLOR RGB(195,195,202)
#define LINE_BLACK_COLOR RGB(0,0,0)
使用されているクラスの種類
class CControlPracticeApp : public CWinApp
class CControlPracticeDoc : public CDocument
class ControlPracticeView : public CFormView
class LeftFormVew : public CFormView
class CMainFrame : public CFrameWnd
class RightFormVew : public CFormView
メッセ-ジの使用例
この様にして使います!下記のコ-ドはメインクラスに実装された状況です。
/////////////////////////////////////////////////////////////////////////////
///////////////////// Vewを切り替えます ///////////////////////////
/////////////////////////////////////////////////////////////////////////////
LRESULT CMainFrame::OneVewChenger(WPARAM wParam, LPARAM lParam)
{
CDocument* pActiveDoc=EkGetActiveDocument();//現在の有効なドキュメントクラスを取得する
CControlPracticeDoc* pMyDoc;
//======================================================================================
if (pActiveDoc==NULL)
{
if(pActiveDoc->IsKindOf(RUNTIME_CLASS(CControlPracticeDoc))){
pMyDoc=static_cast<CControlPracticeDoc*>(pActiveDoc);
}
}
//======================================================================================
else if(pActiveDoc->IsKindOf(RUNTIME_CLASS(CControlPracticeDoc))){
pMyDoc=static_cast<CControlPracticeDoc*>(pActiveDoc);
}
//======================フォ-ムビュからこの位置でブレ-クが掛かります==================
if((pMyDoc->FormVewNmcer & 0x11)==0x11) {
pMyDoc->FormVewNmcer&=0xee;pMyDoc->UpdateAllViews(NULL,ID_FORM_LEFT_UPDATE_ID_OK_DIS ,this) ;}//リセット 0x11
if((pMyDoc->FormVewNmcer & 0x12)==0x12) {
pMyDoc->FormVewNmcer&=0xed;pMyDoc->UpdateAllViews(NULL,ID_FORM_LEFT_UPDATE_ID_CANCEL_DIS,this);}//リセット 0x12
//==========================向かって右側から要求が発信されて此処で停止します===========================
if((pMyDoc->FormVewNmcer & 0x21)==0x21) {
//OKボタンが押された時に此処で止まります >>直ぐに返信します
pMyDoc->FormVewNmcer&=0xde;pMyDoc->UpdateAllViews(NULL,ID_FORM_RIGHT_UPDATE_ID_OK_DIS ,this) ;}//リセット 0x21
if((pMyDoc->FormVewNmcer & 0x22)==0x22) {
//CANCELボタンが押された時に此処で止まります >>直ぐに返信します
pMyDoc->FormVewNmcer&=0xdd;pMyDoc->UpdateAllViews(NULL,ID_FORM_RIGHTUPDATE_ID_CANCSL_DIS,this);}//リセット 0x22
if((pMyDoc->FormVewNmcer & 0x44)==0x44) {
//印刷ボタンが押された時に此処で止まります >>直ぐに返信します
pMyDoc->FormVewNmcer&=0xbb;pMyDoc->FormVewNmcer&=0xdd;pMyDoc->UpdateAllViews(NULL,IDS_TO_PRINT,this);}//リセット 0x44
//======================================================================================
return TRUE;
赤い色の着いた文字列は現在のメインフレ-ムぅラスから現在起動されているドキュメントクラスへのポインタ-を取得してから、そのクラス内部を(この場合は定義変数)参照する為に使用しています、この関数は後で詳しく説明致します・
大きく拡大して処理を見て見ましょう
/////////////////////////////////////////////////////////////////////////////
///////////////////// Vewを切り替えます ///////////////////////////
/////////////////////////////////////////////////////////////////////////////
LRESULT CMainFrame::OneVewChenger(WPARAM wParam, LPARAM lParam){
CDocument* pActiveDoc=EkGetActiveDocument();
//現在の有効なドキュメントクラスを取得する
CControlPracticeDoc* pMyDoc;
//==================================================================================LRESULT CMainFrame::OneVewChenger(WPARAM wParam, LPARAM lParam)
{
/////////////////////////////////////////////////////////////////////////////
///////////////////// Vewを切り替えます ///////////////////////////
/////////////////////////////////////////////////////////////////////////////
LRESULT CMainFrame::OneVewChenger(WPARAM wParam, LPARAM lParam){
CDocument* pActiveDoc=EkGetActiveDocument();
//現在の有効なドキュメントクラスを取得する
CControlPracticeDoc* pMyDoc;
//==================================================================================LRESULT CMainFrame::OneVewChenger(WPARAM wParam, LPARAM lParam)
{
CDocument* pActiveDoc=EkGetActiveDocument();
//現在の有効なドキュメントクラスを取得する
CControlPracticeDoc* pMyDoc;
//==================================================================================
if (pActiveDoc==NULL)
{
if(pActiveDoc->IsKindOf(RUNTIME_CLASS(CControlPracticeDoc))){
pMyDoc=static_cast<CControlPracticeDoc*>(pActiveDoc);
}
}
//==================================================================================
else if(pActiveDoc->IsKindOf(RUNTIME_CLASS(CControlPracticeDoc))){
pMyDoc=static_cast<CControlPracticeDoc*>(pActiveDoc);
}
//==================フォ-ムビュからこの位置でブレ-クが掛かります==================
if((pMyDoc->FormVewNmcer & 0x11)==0x11) {
pMyDoc->FormVewNmcer&=0xee;pMyDoc->UpdateAllViews(NULL,ID_FORM_LEFT_UPDATE_ID_OK_DIS ,this) ;}//リセット 0x11
if((pMyDoc->FormVewNmcer & 0x12)==0x12) {
pMyDoc->FormVewNmcer&=0xed;pMyDoc->UpdateAllViews(NULL,ID_FORM_LEFT_UPDATE_ID_CANCEL_DIS,this);}//リセット 0x12
CDocument* pActiveDoc=EkGetActiveDocument();
CControlPracticeDoc* pMyDoc;
//==================================================================================
if (pActiveDoc==NULL)
{
if(pActiveDoc->IsKindOf(RUNTIME_CLASS(CControlPracticeDoc))){
pMyDoc=static_cast<CControlPracticeDoc*>(pActiveDoc);
}
}
//==================================================================================
else if(pActiveDoc->IsKindOf(RUNTIME_CLASS(CControlPracticeDoc))){
pMyDoc=static_cast<CControlPracticeDoc*>(pActiveDoc);
}
//==フォ-ムビュから発行されたメッセ-ジこの位置でブレ-クが掛かります===========
if((pMyDoc->FormVewNmcer & 0x11)==0x11) {
pMyDoc->FormVewNmcer&=0xee;pMyDoc->UpdateAllViews(NULL,ID_FORM_LEFT_UPDATE_ID_OK_DIS ,this) ;}//リセット 0x11
if((pMyDoc->FormVewNmcer & 0x12)==0x12) {
pMyDoc->FormVewNmcer&=0xed;pMyDoc->UpdateAllViews(NULL,ID_FORM_LEFT_UPDATE_ID_CANCEL_DIS,this);}//リセット 0x12
青い文字色の部分がドキュメントクラスで定義されている変数で本来はドキュメントクラスで隔離されています。その為に普通は参照出来ませんので注意してください。 pMyDoc->FormVewNmcer はフレ-ムビュ-からビュ-切り替え時のコードをフレ-ムビュ-又はメインフレ-ムクラスから各フレ-ムビュ-に処理を伝えるコードを挿入したりします、言わば手紙の様な機能です。
ではどこでメッセ-ジを受け取るか?
各フレ-ムビュ内にあるOnUpdate()で受け取ります、この部分がメッセ-ジハンドラ-と言われる部分ですからよく見て下さい・
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// MeikingDish メッセージ ハンドラ
void LeftFormVew::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
CControlPracticeDoc* pDoc = GetDocument();
int code=0;
//========================================================
switch(lHint){
case ID_FORM_LEFT_UPDATE_ID_OK_DIS:
code=0;
break;
case ID_FORM_LEFT_UPDATE_ID_CANCEL_DIS:
code=0;
break; //==================ビューが切り替わりが強要されました====================
case 0:
code=0;
break;
}
}
実際のビユ-切り替えチェンジャ-です
下記の実際の使用例では各フレ-ムで使用するデータを一括にして構造体にしてから各フレ-ムに渡しています、よく見て下さい。
各切り替えビュ-に使用されている又は必要な表示デ-タとかその他色々な情報も渡さなければならない、その為には構造体の使い方も非常に重要なんです上記図をご覧ください多種多様な構造体を一括で渡している事が解るでしょう!こrでこそのビュ-切り替えなんです、ただ単に画面を切り替えても何ら意味は無い物なのである、だかあこそシナリオのk理科絵が可能なのであり本物のアプリケ-ションになります、このビュ-切り替えを説明する為には多種多様な項目に触れなければなりません、応用出来てなんぼのせかいなのですから。
優先して覚えなければならない項目とは
最初は私が提示するアプリケ-ションを徹底的に覚えて下さいそれが一番になるでしょう、その後で格意味を理解して下さい。これが最良の方法です迷わず着いてきてください。私にメール「を下されば本省で使用するプロジェクトは差し上げます
使用している代表的な親クラス名
CWinApp
CDocument
CFrameWnd
CFormView(複数使用)
代表的な各クラスの主な役割
CWinApp はアプリケ-ションの本体でありこの名前でウインドウズに登録され、管理されます。
CDocument はこのアプリケ-ションで使われる全てのデータを制御します。
CFrameWnd はこの CFormVieを使いユザ-とのやり取りを適切に処理を行いシナリオを約束に従い進捗させます。
CFormViewは紙芝居の絵で有り表示されるシナリオでユザ-の操作を適切に誘導します。
変数と構造体は CDocument .h内に定義します。
後ほど詳しく説明致します。スタチックエリヤを積極的に使用しますが、そもそも スタチックエリヤ とは何でしょうか、CPUボ-ドで説明した方がわかりやすいでしょうからこちらで説明します。CPUボードはEPROMと読み書きDER可能な部分の二種類ありますEPROMは一度書き込むと変更できませんがRAMは何度でも読み書き出来ます、さて私たちのパソコンは保母すべてRAMで構成されています、パソコンではHDで呼び出されたアプリケ-ションはRAMに全てが展開されます、そこでプログラム領域とデータ領域が同一なエリヤに保持されて行きます。私たちのウインドウズはその都度泡の様に消えたり増えたりたりします、この事を動的に作成されると言いますが此処で問題が発生します。それはコードが動的にメモリ-内に増減を繰り返す為にデータエリアのアドレスも動的に変化してしまうのです。しかし他のフレ-ムビュ-はデ-タの状況変化を把握出来ないのでそのそのデ-タを参照した時指定アドレスにあるデータは変化している可能性があり、そのデータが有効であるのか把握出来ない為に多くの異常が発生して例外が頻発するのです。
MFCで初期の流れはどの様に流れるのか?
一番始めの起動順序はどの様に流れるのか知っておく必要があります、立ち上がり順序が解らなけれブレ-クも掛けて停止sる事が出来ないので重要な事柄である。
1: CWinApp はアプリケ-ションの本体。
2:CDocument は CWinApp から起動されます。
3:CFrameWnd は CC で起動されます(紙芝居の絵を差し込むステ-ジです)。
4:CFormViewは CFrameWnd で起動されます 。
上記の様に上から下に向かって処理は進行して行きます4)項目目まで進むと各フレ-ムビュ-のOnUpdate()関数がウインドウズから自動的に呼び出され、その時点で始めて各フレ-ムビュ-は初期化を行ないウィンドウズは待機状態になります。その後は各フレ-ムから送られて切るメッセ-ジに対応し処理が進みます、」ま!大きな流れは左記の様になります。
各フレ-ム内の関数・変数は他のクラスからは隠蔽されている!
なので全てドキュメントクラスを経由して或いは通じて参照する事が出来ます、メッセ-ジの発行はAPPクラスから間接的にドライバ-を取得してそこへ向けてメッセ-ジを発行しメインフレ-ムに伝える事が可能になります。