I've recently been asked (again) about pinning pointers so I decided to blog it, so that next time I can just email the link and save everyone a little time
When you're working in C++/CLI, there are two kinds of indirect access to objects: handles and pointers. A pointer is that good old friend (fiend?) we've known for so long and it holds the address of a place on the native heap or the stack. You get a pointer by using the new operator with a type, or the address-of operator, &, with an instance:
Customer c("Microsoft");
Customer* pc1 = &c;
Customer* pc2 = new Customer("CTV");
Pointers stay still. You can cast them to ints, stick those ints into collections, do whatever you like to them, and later cast them back to pointers and dereference them and if they were pointing to something on the heap, like pc2 does, they will still be pointing to the same place on the native heap the whole time. What's more, the instance they point to will not have gone anywhere unless you explicitly delete it yourself.
Handles are the new kid on the block and they sorta-point-to a place on the managed heap. You get a handle by using the gcnew operator with a type, or the make-me-a-handle operator, %, with an instance:
Employee e("Kate", "Gregory");
Employee^ he1 = %e;
Employee^ he2 = gcnew Employee("Brian","Gregory");
Handles don't stay still. If you found a way to get the value out of them and then stuck it somewhere and let the handle itself go out of scope, the garbage collector might clear away the item on the heap, making the number meaningless. Even if you kept the handle in scope, the garbage collector might move the item to somewhere else, making the number meaningless.
Now normally this is nothing more than an interesting fact, one of the differences between handles and pointers. But what if you have some information contained inside a managed instance and you need to pass it to some native code that doesn't know about handles? Now you not only need to convert somehow, you also need to ask the garbage collector not to move that heap item while you are holding a pointer to it. This is called pinning. Let's switch to simpler types so you don't have to worry about what's in an Employee object. I need this most often with strings - I have some managed code that gives me a String^ and I need to pass it to some native code that needs a LPCWSTR or the like.
String^ title=HandyLibraryFunction();
{
pin_ptr<const WCHAR> str = PtrToStringChars(title);
NativeFunction((LPCWSTR)str);
}
PtrToStringChars is a handy function you will find in <vcclr.h>. Those brace brackets are important -- when the pinned pointer goes out of scope, the garbage collector is allowed to move the item again. You really need to minimize pinning time or you will ruin your app's performance, so I've artificially shortened its lifetime to release it as soon as possible. And whatever NativeFunction does, it better not take a copy of the pointer it's given and save it away for future use, because it will be a dangling pointer once str has gone out of scope -- even if title is still in scope.
The concept is much the same when you want a member variable from a managed instance. Grab hold of it and use the pin_ptr template to pin it and give you a pointer, which you can then work with following all the rules of pointers. Let go of it as soon as you can.
Kate