3

私の別の SO 投稿で説明されているように、VS 2008 (.net 3.5) から VS 2013 (および 4.5 ではなく .net 4.0 を使用) に移行した後、アプリケーションの奇妙な動作を見ました。クラスの静的コンストラクター (cctor) が呼び出されなくなっていることがわかりました。したがって、アプリケーションを小さなテスト プログラムに分割しました。

DLL testAssembly_2-0 および testAssembly_4-0
(同様の内容; testAssembly_4-0 の名前は40ではなく20)

namespace testAssembly_20
{
public ref class Class20
{
public:
  Class20 ()
  { Console::WriteLine (__FUNCTION__"()"); }

  static Class20 ()
  { Console::WriteLine (__FUNCTION__"()" + " ms_iValue=" + ms_iValue);
    ms_iValue = 2;
    Console::WriteLine (__FUNCTION__"()" + " ms_iValue=" + ms_iValue); }

  void func20 ()
  { Console::WriteLine (__FUNCTION__"()" + " ms_iValue=" + ms_iValue); }

protected:
  static int ms_iValue = 1;
};
}

main VS2008
コンパイル時testAssembly_2-0およびmainVS 2008 で (.net 2.0 アセンブリとそのアプリケーションを作成)、両方の実行方法で期待どおりに実行されます (IDE でデバッグ モードを開始し、exe を直接開始します):

int main ()
{
  testAssembly_20::Class20^ oC20 = gcnew testAssembly_20::Class20;
  oC20->func20 ();
}
// output:
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=1
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=2
// testAssembly_20::Class20::Class20()
// testAssembly_20::Class20::func20() ms_iValue=2

main VS2013
VS 2013 で コンパイルtestAssembly_4-0mainて (.net 4.0 アセンブリとアプリケーションを作成)、既存のものを含める ( app.config.net 2.0 testAssembly_2-0を使用、リンクされた投稿を参照) 場合でも動作しますが、IDE のデバッグと exe の開始とでは​​動作が異なります。
IDE のデバッグでは、上記の結果が生成されます ( で 1 回、Class20で 1 回Class40)。
exe start は、cctorクラスのインスタンス化時ではなく、静的メンバーが初めてアクセスされたときに呼び出します。これは、.net 4.0 で導入された、いわゆる遅延初期化が原因であるに違いありません。ここ数時間の調査でわかっている限りです。

int main ()
{
  testAssembly_40::Class40^ oC40 = gcnew testAssembly_40::Class40;
  oC40->func40 ();
  testAssembly_20::Class20^ oC20 = gcnew testAssembly_20::Class20;
  oC20->func20 ();
}
// output of exe start:
// testAssembly_40::Class40::Class40 (static class constructor)() ms_iValue=1
// testAssembly_40::Class40::Class40 (static class constructor)() ms_iValue=2
// testAssembly_40::Class40::Class40()
// testAssembly_40::Class40::func40() ms_iValue=2
// testAssembly_20::Class20::Class20()
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=1
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=2
// testAssembly_20::Class20::func20() ms_iValue=2

拡張された DLL
これはまだ私の失敗を再現していないので、元のアプリケーションでも行っているように、静的メンバーにアクセスするためのプロパティをクラスに追加しました。でこのプロパティをクエリmain()すると、関数呼び出しの順序が異なります (Class20 cctorが最初に呼び出され、 の先頭で直接呼び出されるようになりましたmain())。しかし、その行動は正しかった。

したがって、元のアプリケーションにさらに一歩進み、両方のアセンブリに派生クラスを追加しました。

public ref class Class20derived : Class20
{
public:
  Class20derived ()
  { Console::WriteLine (__FUNCTION__"()"); }

  static Class20derived ()
  { Console::WriteLine (__FUNCTION__"()" + " ms_iValue=" + ms_iValue);
    ms_iValue = 3;
    Console::WriteLine (__FUNCTION__"()" + " ms_iValue=" + ms_iValue); }

  void func20derived ()
  { Console::WriteLine (__FUNCTION__"()" + " ms_iValue=" + ms_iValue); }
};

Class40derived is similar again.

main VS2008 new
テスト プログラムは、派生クラスのオブジェクトを作成するようになりました。両方の実行方法 (IDE、exe 直接) で期待どおりに実行されます。

int main ()
{
  testAssembly_20::Class20derived^ oC20D = gcnew testAssembly_20::Class20derived;
  oC20D->func20 ();
}
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=1
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=2
// testAssembly_20::Class20derived::Class20derived (static class constructor)() ms_iValue=2
// testAssembly_20::Class20derived::Class20derived (static class constructor)() ms_iValue=3
// testAssembly_20::Class20::Class20()
// testAssembly_20::Class20derived::Class20derived()
// testAssembly_20::Class20::func20() ms_iValue=3

main VS2013 new
テスト プログラムは、両方の派生クラスのオブジェクトを作成するようになりました。IDE から起動すると、期待どおりに実行されます (VS2008 new と同じ結果で、Class40 で 1 回、Class20 で 1 回)。
しかし、exe を起動すると、結果が正しくありません。

int main ()
{
  testAssembly_40::Class40derived^ oC40D = gcnew testAssembly_40::Class40derived;
  oC40D->func40 ();
  testAssembly_20::Class20derived^ oC20D = gcnew testAssembly_20::Class20derived;
  oC20D->func20 ();
}
// testAssembly_40::Class40::Class40 (static class constructor)() ms_iValue=1
// testAssembly_40::Class40::Class40 (static class constructor)() ms_iValue=2
// testAssembly_40::Class40derived::Class40derived (static class constructor)() ms_iValue=2
// testAssembly_40::Class40derived::Class40derived (static class constructor)() ms_iValue=3
// testAssembly_40::Class40::Class40()
// testAssembly_40::Class40derived::Class40derived()
// testAssembly_40::Class40::func40() ms_iValue=3
// testAssembly_20::Class20::Class20()
// testAssembly_20::Class20derived::Class20derived()
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=1
// testAssembly_20::Class20::Class20 (static class constructor)() ms_iValue=2
--> where is the Class20derived cctor??
// testAssembly_20::Class20::func20() ms_iValue=2

.net 2.0 アセンブリの派生 cctor() が呼び出されないのはなぜですか?
これは .net 4.0 の遅延初期化の意図した動作ですか、それともコンパイラのバグであると思いますか? ここで奇妙なことに、.net 4.0 アセンブリは正しく使用されていますが、.net 2.0 アセンブリは正しく使用されていません。

また、上部の基本クラス:
.net 4.0 cctor はクラスのインスタンス化で呼び出されるのに、.net2.0 cctor はオンデマンドで呼び出されるのはなぜですか?

編集 1

app.exe.config を使用する場合と使用しない場合で、同じアプリケーション (VS2008、DLL 拡張) が exe として実行されると、動作が異なることがわかりました。
app.config が存在する場合、アプリケーションは VS2013 でコンパイルされたものとして動作します。つまり、アプリケーションに問題があります。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>

しかし、app.config を削除するとすぐに、アプリケーションは正常に動作します。
したがって、バグはVS C ++ / CLIコンパイラ内ではなく、.net 4.0 CLR自体内にあると思います...

4

1 に答える 1