他のダイアログから関数を呼び出す方法

[戻る]
ちる 2002/09/10(火) 18:01:39
いつもお世話になっております。
今回も初歩的な質問かもしれませんがよろしくお願い致します。
情報不足な点がありましたらご指摘ください。

<作りたいもの>
ダイアログAに設置されているボタン(CMainDlg::OnMain())を
ダイアログB上に設置されているボタン(CSubDlg::OnCall())から
呼び出したいのですがエラーが発生してうまく呼び出せません。

<ソース>
void CMainDlg::OnMain() 
{
//色々な処理
}

void CSubDlg::OnCall() 
{
CMainDlg::OnMain();
}

<エラー>
1.CMainDlg::OnMain():静的でないメンバ関数の中で呼び出しが正しくありません。
2.'OnMain' の宣言を確認してください。

OnMainは最初protected:で宣言されていましたが
アクセスできずと言うエラーが発生するためpublic:に移動しました。

public:
afx_msg void OnMain();

<その他の情報>
関係ないかもしれませんが一応・・・
ダイアログBのCPPファイル内ではダイアログAのヘッダファイルを
インクルードしています。(ダイアログAの関数を使うため)

<知りたいこと>
エラーの原因
解決策

<環境>
言語:VC++6.0 MFC使用
OS:WINXP

君は僕の太陽暦だ! 2002/09/10(火) 19:15:19
<エラーの原因>
OnMain関数はCMainDlgクラスのメンバ関数であり、静的メンバ関数ではないから。
まず、「静的な」関数というのを理解されることをお勧めします。
静的でないメンバ関数は、クラスのインスタンス(実態)が無ければ呼び出すことは
できません。
例えば、
class A
{
public:
  A(){}    //コンストラクタ
  void func(){} //メンバ関数
}
こんな感じでくらすが合った場合、クラスA内のfunc関数を呼び出すには

  A  a;

  a.func();

上記の様にクラスAのインスタンス(実態)であるaを作ってから
呼び出さなくてはいけません。

<回避策>
具体的にやりたいことが分らないので、適切な回答ではないかもしれませんが
解決策の一つは、OnMain関数を静的関数にすることです。
調べればすぐおわかりになるかとは思いますが(MSDNを「静的」で検索したら
すぐひっかかるとおもいます)OnMainの宣言時に頭にstaticをつければよいです。
ただし、静的関数はメンバ関数にしてメンバ関数にあらずで、静的なメンバ関数
内では、メンバ変数にはアクセスできません。
というか、OnMainはボタンを押下した時の処理ということなのでおそらく、この方法は
だめでしょうね・・・。まぁ、
MainDlgのインスタンスのポインタを持ってきて、OnMainを呼ぶとか
CSubDlgがMainDlgの子供なら、GetParentで親のウインドウハンドルは取れるからそっから
処理するとか。
OnMainの処理を共通関数かしてCSubDlg::OnCallでも呼び出すとか、方法はいっぱいあると
おもいますよ!
やりたいことに応じて適切なものを選んでくださーい!

PATIO 2002/09/11(水) 09:39:32
>ダイアログAに設置されているボタン(CMainDlg::OnMain())を
>ダイアログB上に設置されているボタン(CSubDlg::OnCall())から
>呼び出したいのですがエラーが発生してうまく呼び出せません。

発想自体に問題があるような気がします。
ダイアログAのメソッドはあくまでもダイアログAのインスタンスあってのものです。
静的関数という話もありますが、静的関数はダイアログAのメンバー変数を使った
処理は出来ません。それでよければ、静的関数というのも解決策の一つではあります。
あと、ダイアログAとダイアログBの関係がきちんと説明されていませんよね。
モーダルダイアログ? モードレス? 親子関係は?
出せる情報は全て提示して下さい。
今の情報では適切な回答は出来ないですよ。

2002/09/11(水) 10:24:50
要するにクラスはサブルーチンの集まりではないってことだ。

ちる 2002/09/11(水) 10:45:18
失礼しました・・
昨日からいろいろやったのですがなかなか解決しません><

>ダイアログAとダイアログBとの関係
ダイアログAのボタンを押すとダイアログBがモードレスで呼び出されます。

やりたい事をもう少し詳しく書きます。

////////////////////////
呼び出されたAdlgのクラスにはメンバー変数とメンバー関数がいくつかあります。
Adlgのメンバ関数からBdlgがモードレスで呼び出され
BdlgはAdlgのメンバー変数やメンバー関数を使用したいと思っています。

なぜこのようなことをやりたいかと言いますと
クラスAは裏処理や他の処理からも参照される変数を一まとめにしている
中心的(ライブラリ的)なクラス。
クラスBは表示用としてクラスAの値を参照したり
他の処理でも使うような共通な関数をAクラスの中から取ってきて実行する
補助的なクラス。
といった事を最終目標にしております。
////////////////////////

なにぶんプログラム開発はこれが初めてで
使い方が間違っているかもしれませんが、アドバイスお願い致します。

ちる 2002/09/11(水) 11:09:57
大変わかりにくいので質問を変えます。
MFCダイアログベースでaaaプロジェクトを作成し
関数が記載されているライブラリmeirei.cppを作成します。

aaa.h      //
aaa.cpp    //ダイアログの生成
aaadlg.h   //
aaadlg.cpp //ダイアログクラス
meirei.cpp //関数ライブラリ

ここでaaa.cppの処理内で生成されたダイアログクラス
CaaaDlg dlg;
をmeirei.cpp内で参照することは可能でしょうか?

試しにmeirei.cppの先頭に

#include "aaaDlg.h"
#include "aaa.h"

extern CaaaDlg dlg;

test()
{
int test;
test = dlg.xxx; //dlgはメンバー変数xxxを持っているので参照
}

とやってみたんですが上手く行きません。

太陰暦 2002/09/11(水) 11:23:14
「君は僕の太陽暦だ!」さんのレスでほぼ解決と思いつつ・・・

よくあるパターンとしては
クラスAのポインタをグローバルまたは
クラスBのメンバ変数として持つ。
もってどうするの?とかは過去ログにありそうで
適切なものが見つからなかったのだけど・・・。

ボタンを押した時の処理みたいな関数は直接呼び出すより
「ボタンが押されたメッセージを投げる」のが正道かと思います。

太陰暦 2002/09/11(水) 11:38:12
うっ。いつのまにかレスついてる。

2002/09/11(水) 11:09:57 について
一見問題ないように見えるのですが、
>とやってみたんですが上手く行きません。
どううまくいかないのでしょうか?
リンクエラーでしょうか?

>ここでaaa.cppの処理内で生成されたダイアログクラス
>CaaaDlg dlg;
これはグローバル変数になってますか?
BOOL CaaaApp::InitInstance() 内のオート変数だと
うまくいきませんよ。

#AfxGetで始まる関数を調べてみるといいかも・・・

ちる 2002/09/11(水) 12:04:42
度々のご回答、ありがとうございます!

エラー内容は

20Dlg.obj : error LNK2001: 外部シンボル ""class CMy20Dlg dlg" (?
dlg@@3VCMy20Dlg@@A)" は未解決です
Debug/20.exe : fatal error LNK1120: 外部参照 1 が未解決です。

です。(ちなみにプロジェクト名がaaaから20になってます・・)

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

>BOOL CaaaApp::InitInstance() 内のオート変数だと

InitInstance() 内のオート変数です・・・
試しに、aaa.cpp内に記述されている
CaaaDlg dlg;の行を外に追い出してみました(グローバル宣言)
すると、エラーは消えましたがダイアログを起動する前に

Debug Assertion Failed!
略
Line:22

ちなみにLine22は
{ ASSERT(afxCurrentResourceHandle != NULL);
return afxCurrentResourceHandle; }

というエラーが出ました。(無視すれば一応立ち上がるみたいなんですが・・)

オート変数は参照他から参照できないのでしょうか?

ちる 2002/09/11(水) 13:21:04
[[解決]]
出来ました出来ました!
C++から一度勉強しなおすことにします。。。

解決方法はaaa.cpp内でクラスを生成する際に
グローバル変数(dlgAdd)にアドレスを取っておく
そのアドレスはどこからでも参照できるから
任意の場所に

dlgAdd->メンバー変数
dlgAdd->メンバー関数

で実行することが出来ました^-^

dairygoods 2002/09/11(水) 13:34:48
> ここでaaa.cppの処理内で生成されたダイアログクラス
> CaaaDlg dlg;
> をmeirei.cpp内で参照することは可能でしょうか?

その1: 
meirei.cpp内のすべての関数の引数にCaaaDlgへのポインタを追加する。

void doSomethig(CaaaDlg* pDlg, その他の引数) {
   ...
   pDlg->SomeMethod(...);
}

この方法は、CaaaDlgが複数現れたときにも対応できる汎用的な方法です。
ポインタを渡すのを忘れる可能性も低いので安全です。


その2:
CaaaDlg* pCaaaDlg;
というグローバル変数を作り、ダイアログを生成した箇所で
この変数を初期化する。

BOOL CaaaApp::InitInstance() 
{
  CaaaDlg dlg;
  pCaaaDlg = &dlg;
  dlg.DoModal();
  pCaaaDlg = NULL;
}

void doSomethig(その他の引数) {
   ...
   extern CaaaDlg* pDlg;
   pDlg->SomeMethod(...);
}

この方法は、CaaaDlgがアプリケーションの実行中に一度に1つしか
現れない場合のみ使えます。
pCaaaDlgの設定を忘れたり、設定前に参照したり、
とバグが発生しやすい方法です。

太陰暦 2002/09/11(水) 13:41:03
>というエラーが出ました。(無視すれば一応立ち上がるみたいなんですが・・)
失礼しました。
ダイアログだからエラーがでますね。
(もともとこういう手法はとらないもので・・・。)

>オート変数は参照他から参照できないのでしょうか?
できないというわけでもないですが
extern CaaaDlg dlg; だとグローバルじゃないと
リンクできません。

現在のやりかたでいくと・・・
 extern CaaaDlg* dlg; として
 CaaaDlg* dlg;   // グローバル変数
 InitInstance() 内の一部を以下のように変更
  dlg = new CaaaDlg;
  m_pMainWnd = dlg;
  int nResponse = dlg->DoModal();
とすればうまく良くと思いますが、

CaaaAppのm_pMainWndになってるウィンドウは(ダイアログでも)
AfxGetMainWndで取得できるのでこっちのほうが簡単ですよ。
test() {
int test = ((CaaaDlg*)AfxGetMainWnd())->xxx;
}

ちる 2002/09/12(木) 09:13:27
[[解決]]
>>dairygoodsさん

いつも丁寧な解説ありがとうございます!

その1の関数に引数を直接渡す方法ですがDLLで定義している関数なので
新たに引数を追加することができないためその2の方法を取ることにしました。
ダイアログの設定忘れや参照順には十分な注意を払ってプログラミングしていきます!

>>太陰暦さん

質問に最後まで付き合っていただきありがとうございます

>test() {
>int test = ((CaaaDlg*)AfxGetMainWnd())->xxx;}

このような方法もあるんですね・・・
今試してみます!

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

無事解決しました
今回お世話にして頂いた方々有難うございます!

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