首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Assembly.Load*中的奇怪问题

Assembly.Load*中的奇怪问题
EN

Stack Overflow用户
提问于 2010-02-11 12:06:46
回答 1查看 1.3K关注 0票数 2

我在从文件中加载程序集时遇到了奇怪的问题,不幸的是,我无法用简单易用的代码来再现它。我在下面描述我在做什么。

我有4份申请:

  1. “复杂调用者”--外国的(不是我的)开源应用程序,实际上是电脑游戏,我正在为它编写一个插件。此应用程序可以从dll调用多个函数(请参阅下面的代理定义)。简单地说,它调用3个函数init();functions ();DoSomething();实际名称有点不同,但这并不重要。复杂调用程序用纯非托管c\c++编写,并使用MSVC编译。
  2. “简单调用程序”--我从零开始编写的应用程序,用来测试问题(见下文)是否以任何方式发生。它所做的同样-调用3个函数在复杂调用程序中。简单调用程序是用纯非托管c\c++编写的,并使用MSVC编译。
  3. “代理”-由两个调用方调用的dll。它导出函数init();for ();DoSomething();用于调用者调用它们。这里的另一部分是由init()函数调用的托管(CLR)部分。实际的托管类用于调用Assembly.Load(Byte[],Byte[]);此函数调用从文件加载程序集(参见下面),以便从该程序集实例化类。程序集中的类实现了接口"SomeInterface“,该接口也在”代理“中定义(”程序集“有对”代理“的引用)。代理在MSVC中以混合模式(managed+unmanaged) C++编写,并带有/clr标志。
  4. “程序集”是带有单个类的dll(托管程序集),它从代理中实现"SomeInterface“。它非常简单,并且是用c#编写的。

现在我的目标是。我希望调用者(特别是复杂的调用者)调用代理,代理反过来在类(程序集)实例中加载程序集和调用函数。关键的要求是能够根据需要“重新加载”程序集,而不需要重新执行调用程序。调用方具有将需要重新加载到代理的信号的机制,而代理则为Assembly.Load(Byte[],Byte[])提供Assembly.dll。

现在的问题也是如此。它与“简单调用者”很好地工作,并且不能与“复杂调用者”一起工作!使用简单调用程序,我能够“重新加载”(实际加载程序集的数量)--程序集超过50次;对于“复杂调用程序集”,Assembly.Load()方法在第一次尝试重新加载程序集时失败,即代理第一次加载程序集,并在需要时无法重新加载它。有趣的是,简单和复杂的加载程序执行完全相同的函数调用流,即LoadLibraryA("Pathto\Proxy.dll");init的GetProcAddress,function,handleEvent;调用这些函数;然后是FreeLibrary()。我看到了复杂调用者和.NET库之间的互操作(不知道是哪种)的问题。复杂的调用者在代码中有一些东西破坏了MS .NET的正确性。我99%肯定这不是我作为一个编码器的错。

在深入了解我获得的异常和试图克服这些问题的方法(比如使用AppDomains)之前,我想澄清一下这个论坛上的某个人是否有能力、时间和意志深入System.Load()内部(也就是说,这应该是有系统和System.Reflection源的)。这将需要安装“复杂的调用程序”以及代理和程序集(不要认为这是一项过于艰巨的任务)。

我在这里张贴相关的代码片段。

InvokerSimple

代码语言:javascript
运行
复制
DWORD WINAPI DoSomething( LPVOID lpParam )
{
    HMODULE h=LoadLibraryA("PathTo\\CSProxyInterface.dll"); //this is Proxy!


    initType init=(initType)GetProcAddress(h,"init");
    releaseType release=(releaseType)GetProcAddress(h,"release");
    handleEventType handleEvent=(handleEventType)GetProcAddress(h,"handleEvent");    



    init(0,NULL);

    int r;

    for (int i=0;i<50;i++)
    {
        r=handleEvent(0,0,NULL); //causes just Assembly.SomeFunction invoke
        r=handleEvent(0,0,NULL); //causes just Assembly.SomeFunction invoke
        r=handleEvent(0,0,NULL); //causes just Assembly.SomeFunction invoke
        r=handleEvent(0,4,NULL); //causes Assembly reload (inside the Proxy)
    }

    release(0);

    FreeLibrary(h);

    return 0;
}


int _tmain(int argc, _TCHAR* argv[])
{
    bool Threaded=true; //tried both Threaded and unThreaded. They work fine!

    if (Threaded)
    {
        DWORD threadID;
        HANDLE threadHNDL;
        threadHNDL=CreateThread(NULL,0,&DoSomething,NULL,0, &threadID);
        WaitForSingleObject(threadHNDL,INFINITE);
    }
    else
    {
        DoSomething(NULL);
    }

    return 0;
}

CSProxyInterface (代理)

stdafx.h

代码语言:javascript
运行
复制
int Safe_init(void);
int Safe_release(void);

extern "C" __declspec(dllexport) int __cdecl init(int teamId, const void* callback);
extern "C" __declspec(dllexport) int __cdecl release(int teamId);
extern "C" __declspec(dllexport) int __cdecl handleEvent(int teamId, int topic, const void* data);

stdafx.cpp

代码语言:javascript
运行
复制
#include "stdafx.h"
#include "WrapperClass.h"

#include <vcclr.h>

using namespace System;
using namespace System::Diagnostics;
using namespace System::Threading;

// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

#define CSMAXPATHLEN 8192

static gcroot<WrapperClass^> wc;
static char* my_filename="PathTo\\Assembly.dll";

int __cdecl init( int teamId, const void* callback )
{
    return Safe_init();
}

int Safe_init( void )
{
    Safe_release();
    wc=gcnew WrapperClass(gcnew String(my_filename));
    return 0;
}

int __cdecl release( int teamId )
{
    return Safe_release();
}

int Safe_release( void )
{
    if (static_cast<WrapperClass^>(wc)!=nullptr)
    {
        delete wc;
        wc=nullptr;
    }
    return 0;
}

int __cdecl handleEvent( int teamId, int topic, const void* data )
{

    if (topic!=4)
    {
        int r=wc->Do(topic);
        return r;
    }
    else
    {           
        Safe_init();
        return 0;
    }

}

WrapperClass.h

代码语言:javascript
运行
复制
#pragma once

using namespace System;
using namespace System::Reflection;

#include "SomeInterface.h"

ref class WrapperClass
{
private:
    ResolveEventHandler^ re;
    Assembly^ assembly;
    static Assembly^ assembly_interface;
    SomeInterface^ instance;
private:
    Assembly^ LoadAssembly(String^ filename_dll);
    static Assembly^ MyResolveEventHandler(Object^ sender, ResolveEventArgs^ args);
    static bool MyInterfaceFilter(Type^ typeObj, Object^ criteriaObj);
public:
    int Do(int i);
public:
    WrapperClass(String^ dll_path);
    !WrapperClass(void);
    ~WrapperClass(void) {this->!WrapperClass();};
};

WrapperClass.cpp

代码语言:javascript
运行
复制
#include "StdAfx.h"
#include "WrapperClass.h"    
WrapperClass::WrapperClass(String^ dll_path)
    {
        re=gcnew ResolveEventHandler(&MyResolveEventHandler);
        AppDomain::CurrentDomain->AssemblyResolve +=re;

        assembly=LoadAssembly(dll_path);

        array<System::Type^>^ types;

        try
        {       
            types=assembly->GetExportedTypes(); 
        }
        catch (Exception^ e)
        {
            throw e;        
        }

        for (int i=0;i<types->Length;i++)
        {
            Type^ type=types[i];

            if ((type->IsClass))
            {
                String^ InterfaceName = "SomeInterface";

                TypeFilter^ myFilter = gcnew TypeFilter(MyInterfaceFilter);
                array<Type^>^ myInterfaces = type->FindInterfaces(myFilter, InterfaceName);

                if (myInterfaces->Length==1) //founded the correct interface                
                {
                    Object^ tmpObj=Activator::CreateInstance(type);
                    instance = safe_cast<SomeInterface^>(tmpObj);
                }

            }
        }
    }

    bool WrapperClass::MyInterfaceFilter(Type^ typeObj, Object^ criteriaObj)
    {
        return (typeObj->ToString() == criteriaObj->ToString());
    }

    WrapperClass::!WrapperClass(void)
    {
        AppDomain::CurrentDomain->AssemblyResolve -=re;
        instance=nullptr;
        assembly=nullptr;
    }

    int WrapperClass::Do( int i )
    {
        return instance->Do();
    }

    Assembly^ WrapperClass::MyResolveEventHandler(Object^ sender, ResolveEventArgs^ args)
    {
        Assembly^ return_=nullptr;

        array<Assembly^>^ assemblies=AppDomain::CurrentDomain->GetAssemblies();

        for (int i=0;i<assemblies->Length;i++)
        {
            if (args->Name==assemblies[i]->FullName)
            {
                return_=assemblies[i];
                break;
            }
        }


        return return_;
    }


    Assembly^ WrapperClass::LoadAssembly(String^ filename_dll)
    {
        Assembly^ return_=nullptr;

        String^ filename_pdb=IO::Path::ChangeExtension(filename_dll, ".pdb");

        if (IO::File::Exists(filename_dll))
        {
            IO::FileStream^ dll_stream = gcnew IO::FileStream(filename_dll, IO::FileMode::Open, IO::FileAccess::Read);
            IO::BinaryReader^ dll_stream_bytereader = gcnew IO::BinaryReader(dll_stream);
            array<System::Byte>^ dll_stream_bytes = dll_stream_bytereader->ReadBytes((System::Int32)dll_stream->Length);
            dll_stream_bytereader->Close();
            dll_stream->Close();

            if (IO::File::Exists(filename_pdb))
            {
                IO::FileStream^ pdb_stream = gcnew IO::FileStream(filename_pdb, IO::FileMode::Open, IO::FileAccess::Read);
                IO::BinaryReader^ pdb_stream_bytereader = gcnew IO::BinaryReader(pdb_stream);
                array<System::Byte>^ pdb_stream_bytes = pdb_stream_bytereader->ReadBytes((System::Int32)pdb_stream->Length);
                pdb_stream_bytereader->Close();
                pdb_stream->Close();

                try
                {
                    //array<Assembly^>^ asses1=AppDomain::CurrentDomain->GetAssemblies();
                    return_=Assembly::Load(dll_stream_bytes,pdb_stream_bytes);
                    //array<Assembly^>^ asses2=AppDomain::CurrentDomain->GetAssemblies();
                }
                catch (Exception^ e)
                {   
                    //array<Assembly^>^ asses3=AppDomain::CurrentDomain->GetAssemblies();
                    throw e;
                }           
            }
            else
            {
                try
                {
                    return_=Assembly::Load(dll_stream_bytes);
                }
                catch (Exception^ e)
                {
                    throw e;
                }
            }
        } 
        return return_;

    }

最后是Assembly.dll

代码语言:javascript
运行
复制
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Assembly
{
    public class Class1:SomeInterface
    {
        private int i;

        #region SomeInterface Members

        public int Do()
        {

            return i--;
        }

        #endregion
    }
}

如果一个人设法编译所有的3个项目,它将使它工作。这是一个简单的调用者场景。

此外,我有开源游戏,它做的完全一样,在简单调用。但是在请求重新加载(调用handleEvent(0,4,NULL)之后),我在程序集中得到一个错误::Load(Byte[],Byte[]) <--您可以在代理代码中找到它

例外情况如下:

代码语言:javascript
运行
复制
"Could not load file or assembly '4096 bytes loaded from CSProxyInterface, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. Exception from HRESULT: 0x800703E6"

InnerException:

代码语言:javascript
运行
复制
0x1a158a78 { "Could not load file or assembly 'sorttbls.nlp' or one of its dependencies. Exception from HRESULT: 0x800703E6"}

为了尽可能的精确,这段代码确实适用于简单的调用场景,并且在复杂的调用场景中只工作一次(第一次Init)。

不幸的是,我似乎不像我应该做的那样清楚。我再试一次。

假设您有一个“黑匣子”,它正在做一些事情--这是我的proxy.dll。它加载assembly.dll,从程序集中实例化类的对象并运行它。

黑匣子有一个外部接口,这些都是函数init,release,DoSomething。如果我用简单的应用程序来触摸这个接口(没有线程,没有网络代码,没有互斥,关键部分等等)整个建筑都能用。它的意思是黑匣子做得好。在我的例子中,我能够多次“重新加载”程序集(50次),以便更具体。另一方面,我有一个复杂的应用程序,它执行完全相同的调用流。但不知何故,它干扰了CLR的工作方式:黑匣子中的代码停止工作。这个复杂的应用程序有线程、tcp/ip代码、互斥、boost等。很难说出到底是什么阻止了应用程序和Proxy.dll之间的正确行为。这就是为什么我问是否有人看到了Assembly.Load内部发生的事情,因为当我调用这个函数时,我得到了奇怪的异常。

EN

回答 1

Stack Overflow用户

发布于 2010-02-11 14:17:01

您可能遇到了一个负载上下文问题。这个博客条目是这方面工作原理的最好总结。我认为,使用Assembly.Load(byte[])可能会导致同一个程序集的多个实例在同一个AppDomain中结束。虽然这看起来像你想要的,但我认为这是一个灾难的配方。考虑创建多个AppDomains。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2244257

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档