スポンサーリンク

| キーワード:

C++CLIでdll内のクラスを使う時の error C2011: ‘***’ : ‘class’ 型の再定義

#pragma onceを忘れたとかそんな話ではない。

すべてCLIで、testclidll.dll内にクラスClass1を作成し、それをexe側に参照を追加してClass1を使おうとしたとき、

 

error C2011: 'testclidll::Class1' : 'class' 型の再定義

 

が発生する

 

再現手順 (Visual Studio 2017 community)

問題1

1.[ファイル] - [新規作成] - [プロジェクト] から、「CLRコンソールアプリケーション」を作成(名前をexe_and_dll_testとする)

2.[ファイル] - [新規作成] - [プロジェクト] から、「クラスライブラリ」を作成(名前をtestclidllとする)

3.testclidll.hを以下のようにする

// testclidll.h
#pragma once
using namespace System;
namespace testclidll {


  public ref class Class1
  {
    int c;
  public:
    void Set(int d);
    int Get();

  };
}

 

4.testclidll.cppを以下のようにする

namespace testclidll {

  void Class1::Set(int d){
    c = d;
  }
  int Class1::Get(){
    return c;
  }
}

 

5.exe側を以下のようにする

#include "stdafx.h"
using namespace System;
#include "../testclidll/testclidll.h"
int main(array<System::String ^> ^args)
{

  testclidll::Class1^ c = gcnew testclidll::Class1;

  c->Set(10);

  Console::WriteLine(System::String::Format("{0}\n",c->Get()));
    return 0;
}

 

6.DLLをコンパイルする

7.ソリューションエクスプローラから、exe側のプロジェクトを右クリックし、[参照...]を選択

 プロパティページから、[共通プロパティ]-[Frameworkと参照]で「新しい参照の追加...」ボタンを押し、コンパイルしたdllを追加する

8.exe側をコンパイルする

 

2>e:\mycodes\exe_and_dll_test\exe_and_dll_test\../testclidll/testclidll.h(12): error C2011: 'testclidll::Class1' : 'class' 型の再定義
2> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
2>exe_and_dll_test.cpp(14): error C2027: 認識できない型 'testclidll::Class1' が使われています。
2> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
2>exe_and_dll_test.cpp(14): error C2227: '->Set' : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。
2>exe_and_dll_test.cpp(18): error C2027: 認識できない型 'testclidll::Class1' が使われています。
2> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
2>exe_and_dll_test.cpp(18): error C2227: '->Get' : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。

 

解決策1

まず、C/C++ではdllをリンクするときにも必ずヘッダファイルをincludeしなければならないが、.NETではdllが情報を持っている(?)ので、そこへさらにインクルードをかけると定義が二重に行われる(のだと思う)。従って、

#include "stdafx.h"
using namespace System;
//#include "../testclidll/testclidll.h" //このincludeはしてはいけない
int main(array<System::String ^> ^args)
{

  testclidll::Class1^ c = gcnew testclidll::Class1;

  c->Set(10);

  Console::WriteLine(System::String::Format("{0}\n",c->Get()));
    return 0;
}

 

問題2

再現手順

1. testclidll.hを、以下のように修正する

// testclidll.h
#pragma once
using namespace System;
namespace testclidll {

  enum ReturnT{ //型を追加
    True,False,
  };

  public ref class Class1
  {
    int c;
  public:
    void Set(int d);
    int Get();

  ReturnT isOdd(); //メンバが奇数かどうかを返す関数を追加。戻り値はユーザー定義型のReturnT

  };
}

 

2.testclidll.cppを以下のように修正する

// これは メイン DLL ファイルです。
#include "stdafx.h"
#include "testclidll.h"
namespace testclidll {

  void Class1::Set(int d){
    c = d;
  }
  int Class1::Get(){
    return c;
  }

  //メンバが奇数かどうかを返す関数の定義
  ReturnT Class1::isOdd(){

    if( c % 2 != 0 )
      return True;

    return False;

  }
}

 

3.exe側を以下のように修正する

#include "stdafx.h"
using namespace System;
//#include "../testclidll/testclidll.h"
int main(array<System::String ^> ^args)
{

  testclidll::Class1^ c = gcnew testclidll::Class1;

  c->Set(10);
  
  testclidll::ReturnT t = c->isOdd();//奇数かどうかの判定を追加

  Console::WriteLine(System::String::Format("{0}\n",c->Get()));
    return 0;
}

 

4.testclidllをコンパイルする

 

5.exe側をコンパイルする

 

コンバイルエラー

2>exe_and_dll_test.cpp(16): error C2039: 'ReturnT' : 'testclidll' のメンバーではありません。
2>exe_and_dll_test.cpp(16): error C2065: 'ReturnT' : 定義されていない識別子です。
2>exe_and_dll_test.cpp(16): error C2146: 構文エラー : ';' が、識別子 't' の前に必要です。
2>exe_and_dll_test.cpp(16): error C2065: 't' : 定義されていない識別子です。

 

これは、クラスの情報は参照したdllの中にあるが、enumのような定数の情報は入っていない(?)ので、定義がないと怒られる。そこで、testclidll.hのincludeを復活させてみる。

#include "stdafx.h"
using namespace System;
#include "../testclidll/testclidll.h" //やっぱりいるのか?
int main(array<System::String ^> ^args)
{

 

1>e:\mycodes\exe_and_dll_test\exe_and_dll_test\../testclidll/testclidll.h(14): error C2011: 'testclidll::Class1' : 'class' 型の再定義
1> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
1>exe_and_dll_test.cpp(14): error C2027: 認識できない型 'testclidll::Class1' が使われています。
1> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
1>exe_and_dll_test.cpp(14): error C2227: '->Set' : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。
1>exe_and_dll_test.cpp(16): error C2027: 認識できない型 'testclidll::Class1' が使われています。
1> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
1>exe_and_dll_test.cpp(16): error C2227: '->isOdd' : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。
1>exe_and_dll_test.cpp(18): error C2027: 認識できない型 'testclidll::Class1' が使われています。
1> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
1>exe_and_dll_test.cpp(18): error C2227: '->Get' : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。

 

当然のように最初のエラーが再発する。

 

解決策2

enumの定義を別のヘッダファイルへ移動する

testclidllプロジェクト内、defvalheader.hを追加

namespace testclidll {

  enum ReturnT{
    True,False,
  };
}

それに伴い、dll本体側であるtestclidll.hからenumの定義を消し、includeにする

// testclidll.h
#pragma once
#include "defvalheader.h" //ReturnTの定義を読み込む
using namespace System;
namespace testclidll {

  public ref class Class1
  {
    int c;
  public:
    void Set(int d);
    int Get();

    ReturnT isOdd();

  };
}

 

exe側では、testclidll.hではなく、defvalheader.hを読み込む

#include "stdafx.h"
using namespace System;
#include "../testclidll/defvalheader.h"
int main(array<System::String ^> ^args)
{

  testclidll::Class1^ c = gcnew testclidll::Class1;

  c->Set(10);
  
  testclidll::ReturnT t = c->isOdd();

  Console::WriteLine(System::String::Format("{0}\n",c->Get()));
    return 0;
}

 

 

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: