I am calling CopyFileEx from a C# application with an anonymous delegate being passed into the LPPROGRESS_ROUTINE parameter in order to get notifications on the file copy progress.
My question is, does the anonymous delegate need to be pinned and why (or why not).
In addition, does the answer change if:
- CopyFileEx was not blocking.
- If I passed in a delegate that was not anonymous.
Thanks!
The delegate does not need to be pinned. A managed object is pinned if it cannot be moved by the garbage collector. If the marshalling information is correct then the marshalling layer will ensure that a pointer to something immobile is passed.
However, the comment above where you suggest that a local variable might keep the delegate alive indicates a misunderstanding of variable lifetime. I refer you to the spec, which states:
The actual lifetime of a local variable is implementation-dependent. For example, a compiler might statically determine that a local variable in a block is only used for a small portion of that block. Using this analysis, the compiler could generate code that results in the variable’s storage having a shorter lifetime than its containing block. The storage referred to by a local reference variable is reclaimed independently of the lifetime of that local reference variable
In other words, if you say:
void M()
{
Foo foo = GetAFoo();
UnmanagedLibrary.DoSomethingToFoo(foo);
}
then the jitter is allowed to say "you know, I see that no managed code ever uses foo again the moment after the unmanaged call is invoked; I can therefore aggressively reclaim the storage of that object from another thread at that time". Which means that the unmanaged call can be working on the object when suddenly it is deallocated on another thread.
This is particularly nasty if Foo has a destructor. The finalization code will possibly run on another thread while the object is in use by the unmanaged library, and heaven only knows what sort of disaster that will cause.
In this circumstance you are required to use a KeepAlive to keep the managed object alive. Do not rely on a local variable; local variables are specifically documented as not guaranteed to keep things alive.
See http://msdn.microsoft.com/en-us/library/system.gc.keepalive.aspx for more details.
You don't need to pin it, but you do need to keep a reference to it alive as long as the copy is in progress.
The thunk that is called by the unmanaged code is pinned, but you have to make sure the delegate is not garbage collected - hence the reference.
From the following msdn seems like both pinning and GC.KeepAlive are unneeded in this case since CopyFileEx is synchronous. Specifically it says:
"Usually, you won't have to worry about the lifetime of delegates. Whenever you are passing a delegate to unmanaged code, the CLR will make sure the delegate is alive during the call. However, if the native code keeps a copy of the pointer beyond the span of the call and intends to call back through that pointer later, you might need to use GCHandle to explicitly prevent the garbage collector from collecting the delegate."
Since CopyFileEx does not keep a pointer to the function beyond the span of the call, we shouldn't need to call KeepAlive.
来源:https://stackoverflow.com/questions/5465060/do-i-need-to-pin-an-anonymous-delegate