none
Assembly-related FileNotFoundException after return from function creating AppDomain with odd file extension RRS feed

  • Question

  • I'm having a problem in a function where I'm creating an AppDomain in my C++/CLI 'class library' file, which is hooked in another process. Whenever my function returns (or at least, that's what the VS debugger is making me believe, the error line is on the closing }, or on the 'return X' previously) I get an FileNotFoundException with the following message:

    Could not load file or assembly 'ScriptHook, Version=2010.3.9.1737, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

    The odd part is that ScriptHook is the exact assembly that's executing at that moment, and why it attempts to load the assembly again is something I really do not understand. This assembly is, however, due to requirements for 'plugin files' in a host application requiring all files to have a specific file extension (all these files are simply LoadLibrary()'d, and I initialize my requirements in a thread spawned from DllMain, it's how this odd plugin system works) not named X.dll nor X.exe, and as mentioned before, I have no way of changing this.

    I however, do have a AssemblyResolve event handler on both the main AppDomain (set before this function call) and as shown in my code, in the new AppDomain. However, this AppDomain is never being called.

    My code:

    	void ScriptDomain::CreateDomain() {
    		_mainDomain = AppDomain::CurrentDomain;
    
    		AppDomainSetup^ domainSetup = gcnew AppDomainSetup();
    		domainSetup->ApplicationBase = AppDomain::CurrentDomain->BaseDirectory;
    		System::Security::NamedPermissionSet^ namedPermissionSet = Policy::PolicyLevel::CreateAppDomainLevel()->GetNamedPermissionSet("FullTrust");
    
    		_scriptDomain = AppDomain::CreateDomain("ScriptHook", nullptr, domainSetup, namedPermissionSet);
    		_scriptDomain->InitializeLifetimeService();
    
    		_scriptDomain->AssemblyResolve += gcnew ResolveEventHandler(&ScriptLoader::ResolveAssembly);
    
    		//return _scriptDomain;
    	}
    I really don't know what the issue with my code can be, though this however is my first time using AppDomains in .NET. I hope somebody who has experienced something like this before can help with this issue. :)
    Tuesday, March 9, 2010 4:49 PM

Answers

  • 1) Find out who throws the exception and get full call stack (e.g. stop on first-chance exceptions in VS, step through your code, etc.).
    2) Add logging to your ScriptLoader::ResolveAssembly to see which app domains are loading which assemblies.
    3) If that still doesn't help, create a small repro (as few lines as possible) and post it here (e.g. replace your plugin host with simple executable which calls LoadLibrary on your plugin, etc.).

    -Karel

    BTW: The code you posted works just fine. So if there is a problem, it is likely somewhere else (or in combination with something else).

    // Compile: cl.exe /clr /Zi HelloWorld.cpp
    
    using namespace System;
    
    ref class ScriptLoader
    {
    public:
        static System::Reflection::Assembly ^ ResolveAssembly(
            Object ^ sender, 
            ResolveEventArgs ^ args)
        {
            return nullptr;
        }
    };
    
    void main()
    {
        AppDomainSetup ^ domainSetup = gcnew AppDomainSetup();
        domainSetup->ApplicationBase = AppDomain::CurrentDomain->BaseDirectory;
        System::Security::NamedPermissionSet ^ namedPermissionSet = 
            System::Security::Policy::PolicyLevel::CreateAppDomainLevel()->GetNamedPermissionSet("FullTrust");
        
        AppDomain ^ scriptDomain = AppDomain::CreateDomain(
            "ScriptHook", nullptr, domainSetup, namedPermissionSet);
        scriptDomain->InitializeLifetimeService();
        
        scriptDomain->AssemblyResolve += gcnew ResolveEventHandler(&ScriptLoader::ResolveAssembly);
        
        Console::WriteLine(L"Finished.");
    }
    
    • Marked as answer by NTAuthority Wednesday, March 10, 2010 7:16 PM
    Tuesday, March 9, 2010 9:37 PM
    Moderator

All replies

  • 1) Find out who throws the exception and get full call stack (e.g. stop on first-chance exceptions in VS, step through your code, etc.).
    2) Add logging to your ScriptLoader::ResolveAssembly to see which app domains are loading which assemblies.
    3) If that still doesn't help, create a small repro (as few lines as possible) and post it here (e.g. replace your plugin host with simple executable which calls LoadLibrary on your plugin, etc.).

    -Karel

    BTW: The code you posted works just fine. So if there is a problem, it is likely somewhere else (or in combination with something else).

    // Compile: cl.exe /clr /Zi HelloWorld.cpp
    
    using namespace System;
    
    ref class ScriptLoader
    {
    public:
        static System::Reflection::Assembly ^ ResolveAssembly(
            Object ^ sender, 
            ResolveEventArgs ^ args)
        {
            return nullptr;
        }
    };
    
    void main()
    {
        AppDomainSetup ^ domainSetup = gcnew AppDomainSetup();
        domainSetup->ApplicationBase = AppDomain::CurrentDomain->BaseDirectory;
        System::Security::NamedPermissionSet ^ namedPermissionSet = 
            System::Security::Policy::PolicyLevel::CreateAppDomainLevel()->GetNamedPermissionSet("FullTrust");
        
        AppDomain ^ scriptDomain = AppDomain::CreateDomain(
            "ScriptHook", nullptr, domainSetup, namedPermissionSet);
        scriptDomain->InitializeLifetimeService();
        
        scriptDomain->AssemblyResolve += gcnew ResolveEventHandler(&ScriptLoader::ResolveAssembly);
        
        Console::WriteLine(L"Finished.");
    }
    
    • Marked as answer by NTAuthority Wednesday, March 10, 2010 7:16 PM
    Tuesday, March 9, 2010 9:37 PM
    Moderator
  • 1: Stepping through the code only nets me an exception at the end of the function, not much more is shown in the call stack.
    2: It's just not being called, oddly.
    3: This test case shows a similar issue, only this time VS does not show it as being anything more than a 0xe0434f4d native exception being thrown from mscorwks.dll (non-relevant call stack is below the test case)

    host application (compile as console/native)
    #include <windows.h>
    
    void main()
    {
    	LoadLibrary(L"plugin.blah");
    
    	while (true) {
    		Sleep(1);
    	}
    }
    
    plugin library (CLR mixed DLL, named plugin.blah)
    #include <windows.h>
    
    using namespace System;
    using namespace System::Security;
    
    ref class ScriptLoader
    {
    public:
        static System::Reflection::Assembly ^ ResolveAssembly(
            Object ^ sender, 
            ResolveEventArgs ^ args)
        {
            return nullptr;
        }
    };
    
    ref class ScriptDomain {
    private:
    	static AppDomain^ _scriptDomain;
    	static AppDomain^ _mainDomain;
    public:
    	static void CreateDomain() {
    		_mainDomain = AppDomain::CurrentDomain;
    
    		AppDomainSetup^ domainSetup = gcnew AppDomainSetup();
    		domainSetup->ApplicationBase = AppDomain::CurrentDomain->BaseDirectory;
    		NamedPermissionSet^ namedPermissionSet = Policy::PolicyLevel::CreateAppDomainLevel()->GetNamedPermissionSet("FullTrust");
    
    		_scriptDomain = AppDomain::CreateDomain("ScriptHook", nullptr, domainSetup, namedPermissionSet);
    		_scriptDomain->InitializeLifetimeService();
    
    		_scriptDomain->AssemblyResolve += gcnew ResolveEventHandler(&ScriptLoader::ResolveAssembly); // removing this line fixes the example exception
    	}
    };
    
    // because GoUnmanaged can't catch the CLR exception to something meaningful.
    // this was originally in GoManaged, but that made a 0xe0434f4d 'unmanaged CLR' exception
    // added: it still does. :/
    void GoManagedDeux() {
    	AppDomain::CurrentDomain->AssemblyResolve += gcnew ResolveEventHandler(&ScriptLoader::ResolveAssembly);
    
    	Console::WriteLine(L"Before CreateDomain");
    	ScriptDomain::CreateDomain();
    	Console::WriteLine(L"After CreateDomain");
    }
    
    void GoManaged() {
    	GoManagedDeux();
    }
    
    #pragma unmanaged
    
    // function to help with some stuff that comes with CLR init
    DWORD GoUnmanaged() {
    	GoManaged();
    	return 0;
    }
    
    // DllMain, initializes the managed thread
    BOOL APIENTRY DllMain (HINSTANCE hInstance, DWORD reason, LPVOID reserved) {
    	switch (reason) {
    		case DLL_PROCESS_ATTACH:
    			DWORD threadID = 0;
    			HANDLE threadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)GoUnmanaged, 0, 0, &threadID);
    
    			if (threadHandle == 0 || threadHandle == INVALID_HANDLE_VALUE) {
    				return false;
    			}
    
    			CloseHandle(threadHandle);
    		break;
    	}
    
    	return true;
    }
    The exception call stack (according to the console, thrown in CreateDomain):

    >	KernelBase.dll!_RaiseException@16()  + 0x58 bytes	
     	mscorwks.dll!RaiseTheExceptionInternalOnly()  + 0x159 bytes	
     	mscorwks.dll!RaiseTheException()  + 0x4e bytes	
     	mscorwks.dll!RaiseTheException()  + 0xc0 bytes	
     	mscorwks.dll!RealCOMPlusThrow()  + 0x30 bytes	
     	mscorwks.dll!RealCOMPlusThrow()  + 0xd bytes	
     	mscorwks.dll!Thread::RaiseCrossContextException()  + 0x1ff bytes	
     	mscorwks.dll!MakeCallWithAppDomainTransition()  + 0x168a7e bytes	
     	mscorwks.dll!CrossDomainChannel::MarshalAndCall()  + 0x507 bytes	
     	mscorwks.dll!CrossDomainChannel::ExecuteCrossDomainCall()  + 0x59 bytes	
     	mscorwks.dll!CrossDomainChannel::CheckCrossDomainCall()  + 0xd9 bytes	
     	mscorwks.dll!CTPMethodTable::OnCall()  + 0xa8 bytes	
     	00931cf5()	
     	kernel32.dll!@BaseThreadInitThunk@12()  + 0x12 bytes	
     	ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes	
     	ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes	
    
    The exception message:
    Unhandled exception at 0x7540b727 (KernelBase.dll) in TestCaseAppDomainHost.exe: 0xE0434F4D: 0xe0434f4d.
    I'm not sure if this also happens in the full application in this case, but that one is being caught correctly as a managed exception.
    It seems to me that the runtime tries to load the currently executing assembly in the other AppDomain when adding the ResolveEventHandler (or calling anything) as it's supposed to, but since the default assembly loader cannot find the '.blah' assembly file and the ResolveEventHandler can not be added, this is a bit of a chicken-and-egg problem.
    Wednesday, March 10, 2010 8:17 AM