我需要固定匿名代表吗?

时间:2011-03-28 21:03:52

标签: c# winapi interop kernel32 pinning

我从C#应用程序调用CopyFileEx,并将匿名委托传递给LPPROGRESS_ROUTINE参数,以便获取有关文件复制进度的通知。

我的问题是,匿名代表是否需要固定以及为什么(或为什么不)。

此外,如果出现以下情况,答案会改变:

  1. CopyFileEx没有阻止。
  2. 如果我传了一个不是匿名的代表。
  3. 谢谢!

3 个答案:

答案 0 :(得分:6)

委托不需要固定。如果无法通过垃圾收集器移动,则托管对象固定。如果编组信息正确,那么编组层将确保指向不动的东西。

但是,上面的注释中您建议局部变量可能使代理保持活动表示对变量生命周期的误解。我推荐你的规范,其中说明:

  

局部变量的实际生命周期取决于实现。例如,编译器可能静态地确定块中的局部变量仅用于该块的一小部分。使用此分析,编译器可以生成导致变量存储的生命周期比其包含块短的代码。   由本地引用变量引用的存储被回收,与该本地引用变量的生存期无关

换句话说,如果你说:

void M()
{
    Foo foo = GetAFoo();
    UnmanagedLibrary.DoSomethingToFoo(foo);
}

然后允许抖动说“你知道,我发现在调用非托管调用之后,没有托管代码再次使用foo;因此我可以从另一个线程中积极地回收该对象的存储< / em>当时“。这意味着非托管调用可以在突然在另一个线程上释放时对该对象起作用。

如果Foo有析构函数,这尤其令人讨厌。当对象被非托管库使用时,终结代码可能会在另一个线程上运行,而天堂只知道将导致什么样的灾难。

在这种情况下,您需要使用KeepAlive来保持托管对象的活动。不要依赖局部变量;局部变量具体记录为保证保持活力。

有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/system.gc.keepalive.aspx

答案 1 :(得分:3)

您不需要固定它,但只要副本正在进行中,您确实需要保持对它的引用。

非托管代码调用的thunk是固定的,但你必须确保委托不是垃圾收集 - 因此是引用。

答案 2 :(得分:2)

从以下msdn看来,在这种情况下,由于CopyFileEx是同步的,因此不需要固定和GC.KeepAlive。具体来说它说:

“通常,您不必担心委托的生命周期。每当您将委托传递给非托管代码时,CLR将确保委托在调用期间处于活动状态。但是,如果本机代码保留了超出调用范围的指针的副本,并打算稍后通过该指针回调,您可能需要使用GCHandle显式阻止垃圾收集器收集委托。“

由于CopyFileEx没有在调用范围之外保留指向函数的指针,因此我们不需要调用KeepAlive。