こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

Windowsフォームの表示

Visual C++ 2005(Express Edition)で、windowsフォームを3個作りました。
1個目をForm1.h、2個目をForm2.h、3個目をForm3.h、メインプロジェクトは、Sample.cpp,
ファイルとします。
Form1にボタンbutton1を作成し、ボタンをクリックしたイベントが発生したとき、Form2を表示させます。
Form2には、ボタンbutton2があります。
Form1と同様に、Form2のボタンをクリックするとForm3の画面が表示されるようにします。

ところが、以下のソースファイルで書いたところ、Form2とForm3は、名前空間に存在しませんという意味のエラーが生じます。
Form2とForm3をインスタンス化し、Show()メッソドで、画面を表示させる方法です。

しかし、Form1とForm2だけのファイルですと、先のソースの書き方で上手くいきます。
何か、書き方が異なるのでしょうか?正しい書き方を教えてくださいますとありがたいです。
なお、C++/CLIの文法に沿った、ソースコードでお願いします。


<ソースファイル>
----Form1.h-----------------------------
#pragma once
#include "stdafx.h"
#include "Form2.h"
#include "Form3.h"


namespace Sample {

using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;

/// <summary>
/// Form1 の概要
///
/// 警告: このクラスの名前を変更する場合、このクラスが依存するすべての .resx ファイルに関連付けられた
/// マネージ リソース コンパイラ ツールに対して 'Resource File Name' プロパティを
/// 変更する必要があります。この変更を行わないと、
/// デザイナと、このフォームに関連付けられたローカライズ済みリソースとが、
/// 正しく相互に利用できなくなります。
/// </summary>
public ref class Form1 : public System::Windows::Forms::Form
{

//button1を作成
//省略
#pragma endregion


private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
Form2^ a = gcnew Form2();
a->Show();
}
};
}
[EOF]
---------------------------------------

----Form2.h----------------------------
#pragma once
#include "Form1.h"
#include "Form3.h"

using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;


namespace Sample {

/// <summary>
/// IsotopeInput の概要
///
/// 警告: このクラスの名前を変更する場合、このクラスが依存するすべての .resx ファイルに関連付けられた
/// マネージ リソース コンパイラ ツールに対して 'Resource File Name' プロパティを
/// 変更する必要があります。この変更を行わないと、
/// デザイナと、このフォームに関連付けられたローカライズ済みリソースとが、
/// 正しく相互に利用できなくなります。
/// </summary>
public ref class Form2 : public System::Windows::Forms::Form
{
//button2を作成
//省略

}
#pragma endregion

private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) {
Form3^ b = gcnew Form3();
b->Show();
}
};
}

[EOF]
---------------------------------------

-------Form3.h--------------------------

#pragma once
#include "stdafx.h"
#include "Form1.h"
#include "Form2.h"

using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;


namespace Sample {

/// <summary>
/// Result の概要
///
/// 警告: このクラスの名前を変更する場合、このクラスが依存するすべての .resx ファイルに関連付けられた
/// マネージ リソース コンパイラ ツールに対して 'Resource File Name' プロパティを
/// 変更する必要があります。この変更を行わないと、
/// デザイナと、このフォームに関連付けられたローカライズ済みリソースとが、
/// 正しく相互に利用できなくなります。
/// </summary>
public ref class Form3 : public System::Windows::Forms::Form
{
//省略
}

#pragma endregion

};
}
[EOF]
---------------------------------------------------------

Sample.cpp-----------------------------------------------
// Sample.cpp : メイン プロジェクト ファイルです。

#include "stdafx.h"
#include "Form1.h"
#include "Form2.h"
#include "Form3.h"

using namespace Sample;

[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
// コントロールが作成される前に、Windows XP ビジュアル効果を有効にします
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);

// メイン ウィンドウを作成して、実行します
Application::Run(gcnew Form1());
return 0;
}
[EOF]
------------------------------------------------------------

投稿日時 - 2013-02-06 10:53:39

QNo.7930755

困ってます

質問者が選んだベストアンサー

おそらく、メインとなる sample.cpp のコンパイルはうまくいくかもしれませんが、Form2 と Form3 の追加時に一緒にプロジェクトに追加される Form2.cpp, Form3.cpp のコンパイルに失敗しているのではないでしょうか。

このソースの問題点は、Form1.h, Form2.h, Form3.h がそれぞれ相互に #include をしている点です。

それぞれのヘッダファイルには多重インクルードを防止する #pragma once が含まれており、いったん読まれたファイルは2度目は無視する、というようになっています。
Form2.cpp には(プリコンパイル済みヘッダの stdafx.h を除けば)

#include "Form2.h"

のみが記載されていると思いますが、コンパイル時にどのように解析されていくかを辿ってみましょう。

まず、Form2.h の中が参照されます。この中ではさらに先頭に

#include "Form1.h"

が書かれているので、(Form2クラスの定義を見るよりも前に) Form1.h を読みに行きます。Form1.h では先頭で再び

#include "Form2.h"

が記載されていますが、Form2.h はすでに読み込まれている(より正確には、読み込みを開始している)ので #pragma once の効果により無視されます。次に

#include "Form3"

が書かれているので Form3.h を読みに行きます。Form3.h の先頭でまたまた

#include "Form1.h"
#include "Form2.h"

が書かれていますが、どちらも読み込み開始しているので無視されます。その後、最初のクラス定義である

class Form3 {...};

を読みます。Form3の読み込みが完了すると、Form1.h の中の 「#include "Form3.h"」直後から読み込み再開し、(まだForm2クラスの定義が出現していない状態で)

class Form1 {...};

を読むことになります。この中で、まだ定義されていない Form2 を使用するため、コンパイルエラーとなります。


解決方法としては、ヘッダファイルの相互参照を無くすことです。今回の場合、Form1.h からは Form2.h のみをインクルードし、Form2.h からは Form3.h のみをインクルードするようにします。

投稿日時 - 2013-02-06 21:35:10

お礼

回答ありがとうございます。
fresh homepieさんの方法でやりましたら成功しました。
ヘッダーファイルの事も詳しく説明されていて、こちらの方も、新たな知識を得たと感じています。
私は、独学でVc++プログラミングをやっています。
こういうかゆい点まで説明している、VC++の本がほとんど無くて、困っています。

投稿日時 - 2013-02-08 11:38:07

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(1)