web-dev-qa-db-ja.com

C ++(win32 app)でC#クラスを使用する場合のEEFileLoadException

展開上の理由から、COM Callable Wrapperを使用する代わりに、IJWを使用してC#アセンブリをC++でラップしようとしています。

他のプロジェクトでも実行しましたが、このプロジェクトではEEFileLoadExceptionが発生します。どんな助けでもいただければ幸いです!

マネージC++ラッパーコード(これはDLLにあります):

extern "C" __declspec(dllexport) IMyObject* CreateMyObject(void)
{
    //this class references c# in the constructor
    return new CMyWrapper( );
}

extern "C" __declspec(dllexport)  void DeleteMyObject(IMyObject* pConfigFile)
{
    delete pConfigFile;
}

extern "C" __declspec(dllexport) void TestFunction(void)
{
    ::MessageBox(NULL, _T("My Message Box"), _T("Test"), MB_OK);
}

テストコード(これはEXEです):

typedef void* (*CreateObjectPtr)();
typedef void (*TestFunctionPtr)();

int _tmain testwrapper(int argc, TCHAR* argv[], TCHAR* envp[])
{
    HMODULE hModule = ::LoadLibrary(_T("MyWrapper"));
    _ASSERT(hModule != NULL);

    PVOID pFunc1 = ::GetProcAddress(hModule, "TestFunction");
    _ASSERT(pFunc1 != NULL);
    TestFunctionPtr pTest = (TestFunctionPtr)pFunc1;

    PVOID pFunc2 = ::GetProcAddress(hModule, "CreateMyObject");
    _ASSERT(pFunc2 != NULL);
    CreateObjectPtr pCreateObjectFunc = (CreateObjectPtr)pFunc2;

    (*pTest)();  //this successfully pops up a message box
    (*pCreateObjectFunc)();  //this tosses an EEFileLoadException

    return 0;
}

その価値について、イベントログは次のことを報告します。NETランタイムバージョン2.0.50727.143-致命的な実行エンジンエラー(79F97075)(80131506)

残念ながら、Microsoftはそのエラーに関する情報を持っていません。

27
Adam Tegen

問題は、DLLがどこにあるかでした。

  • c:\ dlls\managed.dll
  • c:\ dlls\wrapper.dll
  • c:\ exe\my.exe

Managed.dllをc:\ exeにコピーしてこれを確認しましたが、問題なく動作しました。どうやら、CLRはアンマネージDLLのパスでマネージDLLを検索せず、実行可能ファイルがある場所(またはGAC)でのみ検索します。

立ち入る価値のない理由で、これは私が必要とする構造です。つまり、管理されたdllを見つけてCLRに手を差し伸べる必要がありました。以下のコードを参照してください。

AssemblyResolver.h:

/// <summary>
/// Summary for AssemblyResolver
/// </summary>
public ref class AssemblyResolver
{
public:

static Assembly^ MyResolveEventHandler( Object^ sender, ResolveEventArgs^ args )
{
    Console::WriteLine( "Resolving..." );

    Assembly^ thisAssembly = Assembly::GetExecutingAssembly();
    String^ thisPath = thisAssembly->Location;
    String^ directory = Path::GetDirectoryName(thisPath);
    String^ pathToManagedAssembly = Path::Combine(directory, "managed.dll");

    Assembly^ newAssembly = Assembly::LoadFile(pathToManagedAssembly);
    return newAssembly;
}

};

Wrapper.cpp:

#include "AssemblyResolver.h"

extern "C" __declspec(dllexport) IMyObject* CreateMyObject(void)
{
    try
    {
        AppDomain^ currentDomain = AppDomain::CurrentDomain;
        currentDomain->AssemblyResolve += gcnew ResolveEventHandler( AssemblyResolver::MyResolveEventHandler );

        return new CMyWrapper( );
    }
    catch(System::Exception^ e)
    {
        System::Console::WriteLine(e->Message);

        return NULL;
    }
}
33
Adam Tegen

最初の問題は、デバッガタイプが混合に設定されていることを確認することです。次に、有用な例外が発生します。

9
Adam Tegen

混合モードdll(EXE)を使用するネイティブアプリケーションの場合は、**「デバッガタイプ」を「混合」モードに変更します。 ([プロジェクトのプロパティ]-> [構成のプロパティ]-> [デバッグ]に移動します)

他にもいくつかの点がありますが(あなたには関係がないかもしれません)、私の経験では、それらは問題を引き起こす可能性があります。 -Windows 8(セキュリティが強化されている)では、管理者としてVSを起動してみてください。 -x86構成では、x86バイナリを使用していることを確認してください。 -StrongNameの検証に注意してください。マネージC++で使用しているC#アセンブリが署名されている場合は、混合モードdllにも署名することを検討してください。

これがお役に立てば幸いです。

4
A.B.

ASP.NET MVCアプリケーションのデバッグ中に、iisexpress.exeによってC++ EEFileLoadExceptionが大量にスローされていました。コールスタックとC++例外自体は、問題を特定するのにそれほど役立ちませんでした。

C++例外で指定されたポインタアドレスを直接調べた後、最終的に、使用されなくなった古いバージョンを指しているライブラリ文字列を発見しました。これは、web.configファイルのエントリが古くなっていることが原因でした。

<runtime>
<assemblyBinding xmlns="urn:schemas-Microsoft-com:asm.v1">
<dependentAssembly>
    <assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" />
    <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
  </dependentAssembly> </assemblyBinding> </runtime>

NuGetを介してさまざまなMicrosoft.Ownセキュリティライブラリをバージョン4.0.30319にアップグレードしましたが、構成のこの行は、呼び出しをバージョン3.0.1.0にリダイレクトするようにサーバーに指示していました。これは、現在はプロジェクトの一部ではありません。設定を更新すると、問題が解決しました。

1
Gareth

他の誰かがこの質問に遭遇し、動的なアセンブリ名を使用している場合:アセンブリ名を削除していることを確認してください。アセンブリ名には、使用できないバージョン、カルチャ、およびその他のコンテンツが含まれている可能性があります。

つまり、MyResolveEventHandlerは次の形式である必要があります。

static Assembly^ MyResolveEventHandler( Object^ sender, ResolveEventArgs^ args )
{
    Console::WriteLine( "Resolving..." );

    String^ assemblyName = args->Name;

    // Strip irrelevant information, such as Assembly, version etc.
    // Example: "Acme.Foobar, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    if( assemblyName->Contains(",") ) 
    {
        assemblyName = assemblyName->Substring(0, assemblyName->IndexOf(","));
    }

    Assembly^ thisAssembly = Assembly::GetExecutingAssembly();
    String^ thisPath = thisAssembly->Location;
    String^ directory = Path::GetDirectoryName(thisPath);
    String^ pathToManagedAssembly = Path::Combine(directory, assemblyName );

    Assembly^ newAssembly = Assembly::LoadFile(pathToManagedAssembly);
    return newAssembly;
}
1
Fredrik Ullner