2

由于 .NET 值类型(托管 C++ 结构)存储在堆栈上,为什么(或者实际上)需要 pin_ptr 以将指针传递给非托管函数?

例如。字节 b[100];

如果我将 &b 传递给一个非托管函数而没有先固定它,堆栈可能会损坏吗?

CLR 堆栈是否会以与 GC 堆相同的方式更改?我被引导相信 CLR 堆栈使用了不寻常的优化,例如使用处理器寄存器,这使得它不适合用作非托管函数的缓冲区。关于在堆栈上固定值类型的规则似乎不清楚。

我注意到以这种方式将缓冲区数组发送到内核 NTDLL 函数 NtfsControlFile 时似乎出现了一些损坏。固定值类型解决了这个问题。但从不调用 API。

因此,将任何指向堆栈上任何值类型的指针传递给任何非托管函数,而不首先固定它们,这难道不是根本不安全的吗?

4

4 回答 4

2

您是正确的,它BYTE b[100];是在本机堆栈上创建的,因此不受托管堆移动等的影响。但是我相信您的问题是一个简单的 C++ 错误。

你说,

如果我将 &b 传递给一个非托管函数而没有先固定它,堆栈可能会损坏吗?

您不应该在数组名称 (b) 上使用地址运算符 (&),因为数组名称本身已经是数组开头的地址。使用&b将不起作用,结果行为将取决于多种因素(例如,编译器及其设置)。

简单地说,在调用函数时,您应该只传递数组名称 (b) 而不是在数组名称 (&b) 上使用地址运算符。

顺便说一句,我相信您通过询问是否可以将堆栈上的托管值类型传递给本机函数而不首先将其固定,因为您给出的示例是传递一个非托管的、基于堆栈的本机数组type,与托管值类型无关。

于 2009-12-13T12:58:28.983 回答
1

是的,内存管理能够切换地址,它只是更新自己对它们的内部引用。一旦您潜入托管层下方,您必须确保您正在使用的指针不会被移动到另一个位置。pin_ptr 的使用告诉内存管理器不要管这块内存。

于 2009-07-09T16:34:25.147 回答
0

As far as I can tell this all refers to objects on the GC Heap.

The point is I am referring to specifically STACK memory.

I have posted an example which seems to suggest that stack memory is being corrupted during an API call passing stack memory as a buffer: http://social.msdn.microsoft.com/Forums/en-US/clr/thread/3779c1ee-90b8-4a6a-9b14-f48d709cb27c

If stack memory needs to be pinned, then this seems to break the idea of "It Just Works". In unmanaged C++ we can declare a stack buffer and then pass a pointer to it to an API function. However if moving to managed code requires that to be pinned, it would seem to fundamentally undermine "It Just Works".

It is confusing how the MSDN docs for pin_ptr seem to say it is only to prevent objects moving however it is also possible to Pin value types which would seem to be on the stack and should not move anyway.

I specifically raise the question of whether the stack memory is treated the same way in managed or unmanaged code. When MSIL debugging I have found it impossible to view the stack and there is no stack viewer tool for that. I have heard, but am not sure, that there is no "real" stack in MSIL and instead the virtual machine CLR is free to optimize, for example using free processor registers instead of actual memory. It is unclear whether this is true, and, whether it would apply to stack as in parameter passing, or stack as in local variable memory.

An odd effect in the above sample project is that pin_ptr on the corrupting object seems to cure the problem. however the object is on the STACK and should not need pinning. Could it be that the /CLR interprets pin_ptr as not only "do not move this object" but also "leave this area as true memory and do not attempt register optimizations upon it" which would cause it to remain pure for the duration of the pin?

I would specifically like to know if the /CLR is clever enough to say avoid optimizations of its in-method stack memory during a call to an API, but would possibly not give me the same grace in the above example due to the direct loading of NTDLL and the way the function is declared as a typedef.

I have considered adding Marshalling attributes to the function typedef but seem unable to do so. I note there are no MarshallAs attributes on WinAPI defs.

I have managed to break into the project above using __debugbreak() immediately before the NTDLL call however this only gives me a managed debug mode which seems unable to step into native code. I cannot write "asm int 3" because x64 does not support it. I can see, however, that the errant value NumberOfPairs is being passed at a memory location pointed to by a register, not as a register itself.

于 2009-07-13T12:22:42.243 回答
0

因此,将任何指向堆栈上任何值类型的指针传递给任何非托管函数,而不首先固定它们,这难道不是根本不安全的吗?

是的。

这是由于 GC 将它们异步删除/移动到您的方法。

有关CLR GC 工作原理的说明,请参阅移动 GC 。

于 2009-07-09T16:39:48.177 回答