C# · 12月 27, 2021

c# – 非管理组件的内存管理由CLR

我有点混乱,可能这个问题很傻.

分配给非托管组件的内存在哪里?

在我的.net代码中,如果我启动了一个非托管组件,这个组件将被加载并且内存被分配?

如何在Managed和Unmanaged堆之间调用CLR marshall?

编辑

谢谢你的回复,但我问的是,假设我做一个DLLIMPORT的User32.Dll,这显然是一个非托管的dll,我在User32.DLL中调用一些函数现在我的问题,CLR如何调用我的这个unmanged的dll?

解决方法 它开始很简单. pinvoke编组器首先调用LoadLibrary并传递您指定的DLL名称,即DllImportAttribute.Value属性.在您的情况下,user32.dll已被加载,因为它由.NET引导程序加载,其引用计数只是增加.但通常,Windows加载器将DLL映射到进程的地址空间中,因此可以调用导出的函数.

接下来是GetProcAddress获取要调用的函数的地址,DllImportAttribute.EntryPoint属性.除非你使用ExactSpelling,否则编组者会尝试几次尝试.一个函数名称,如“foo”,测试了几种可能的方式,foo和fooW或fooA. Win32的执行细节与Unicode和Ansi字符的区别有关. CharSet属性在这里重要.

现在我需要挥手一点,因为它变得棘手.编组器构造堆栈框架,设置需要传递给导出函数的参数.这需要低级代码,仔细排除在窥探之外.以面值表示,它执行Marshal类支持在托管类型和非托管类型之间转换的那种翻译. DllImportAttribute.CallingConvention属性在这里,因为它决定了什么参数值需要放在哪里,以便被调用的函数可以正确读取它.

接下来,它设置一个SEH异常处理程序,以便被调用代码引发的硬件异常可以被捕获并转换为受管异常.一个生成更常见的一个,AccessViolationException.和别的.

接下来,它会在堆栈上推送一个特殊的cookie,以指示非托管代码即将开始使用堆栈.这样可以防止垃圾收集器发生错误的非托管堆栈框架,并将其找到的指针解释为托管对象引用.您可以在调试器的调用堆栈[管理到本机过渡]中看到此cookie.

接下来,只需使用GetProcAddress()找到函数地址的间接调用.这使得非托管代码运行.

调用之后,可能需要执行清理以释放分配用于传递非托管参数的内存.返回值可能需要转换回管理值.就是这样,假设没有讨厌的事情,下一个托管代码语句的执行继续.