C# 使用反射调用dll函数时尝试读取或写入受保护内存

C# 使用反射调用dll函数时尝试读取或写入受保护内存,c#,marshalling,unmanaged,system.reflection,C#,Marshalling,Unmanaged,System.reflection,我制作了一个插件系统,它使用反射来调用插件中的函数。插件必须实现要使用的IPlugin接口。 在使用插件的应用程序中,使用以下代码创建插件实例: Assembly currentAssembly = Assembly.LoadFrom(startInfo.PluginAssemblyPath); Type[] types = currentAssembly.GetTypes(); IPlugin pluginInstance = null; foreach (T

我制作了一个插件系统,它使用反射来调用插件中的函数。插件必须实现要使用的IPlugin接口。 在使用插件的应用程序中,使用以下代码创建插件实例:

    Assembly currentAssembly = Assembly.LoadFrom(startInfo.PluginAssemblyPath);
    Type[] types = currentAssembly.GetTypes();
    IPlugin pluginInstance = null;

    foreach (Type type in types)
    {
        if (type.FullName == startInfo.PluginTypeName)
        {
            pluginInstance = (IPlugin)Activator.CreateInstance(type);
        }
    }

    if (pluginInstance == null)
    {
        throw new Exception("Plugin loader error: Could not instantiate plugin: " + startInfo.ToString());        }

    return pluginInstance;
我制作了一个插件,它使用了一些未命名的dll。当我在插件解决方案的测试项目中调用IPlugin接口函数时,一切都正常。但是当我通过上面代码中的插件实例调用插件时,我得到System.AccessViolationException:调用未命名dll中的函数时,尝试读取或写入受保护内存错误

未加载的DLL是由第三方组成的C++ DLL。我尝试启用本机代码调试,但没有.pdb文件

我不知道为什么会发生这种情况,这是因为反射吗?或者还有其他原因吗

编辑: 在堆栈中,我可以看到调用了unmannaged函数:

[MarshalAs(UnmanagedType.LPStr)]
private readonly StringBuilder sb = new StringBuilder(256);

[DllImport("x.dll", EntryPoint = "xLib")]
static extern int _xLib(int a1, int a2, int a3, int a4, int a5, int a6, [Out]StringBuilder str);
调用_xLib函数时引发异常

编辑:在这个_xLib函数的某个地方调用以下函数:

handle = x_Open();
它位于其他dll中,定义为:

DllExport x_Handle *x_Open();
一旦手柄上的任何东西被使用,如:

"%s", handle->x.string
异常被抛出。
我仍然不明白为什么这在测试项目中起作用,而在我将其作为插件在应用程序中使用时却不起作用。

也许您必须固定
StringBuilder
,以允许非托管代码与其交互

固定对象是不允许移动的对象。垃圾收集器通常会压缩内存,因为它会将所有对象移动到“一个或多个集群”。这是为了创建大块的可用空间

这基本上意味着,如果其他人(外部)有一个指向对象内存地址的指针,这可能会指向随机内容——因为对象已经移动了


固定对象会告诉GC不要移动它。这通常是无用的,只有在使用指针时才有意义-比如使用PInvoke。。。我可以在
\u xlib
函数

中看到一个指向StringBuilder实例的指针。经过一些密集的调试,我发现问题在于句柄的地址有误,从而导致了冲突。地址错误的原因是x_open函数加载了另一个带有LoadLibraryA函数的dll。此dll与可执行文件不在同一目录中,因此找不到它


我通过将最后一个dll的目录添加到环境路径中解决了这个问题。

至少提供一个堆栈跟踪。以及您正在调用的本机函数的名称。您当然需要一个调试器。Project + Projts,Debug tab,勾选“启用原生代码调试”选项。@ Hans Passant,我做了,但是调试器需要一个.PDB文件,我没有。@ LePePIT不是一个本地函数,而是来自第三方C++ DLL的函数。那么,为什么在地球问我们?向编写C++代码的程序员发送一个小的RePro项目。如果没有获得帮助,则不要使用该代码。封送员知道如何处理
StringBuilder
实例。固定它是不必要的,除非非托管代码即使在调用完成后仍保留指向数据的指针——如果是这样,您希望分配一块非托管内存,而不是无限期地固定
StringBuilder
。@Jeroen我想您是对的。我试过:
gchandlesbhandle=GCHandle.Alloc(sb)但这没有帮助。