It's easier to explain this in an example. Let’s say you have a export function in your native DLL:
void WINAPI MyFunction(_com_ptr_t<IMyInterface> ptr)
{
// ... do something with ptr
ptr->Func();
}
And the DllImport would be something like this:
[DllImport("mydll.dll")]
void MyFunction(IMyInterface ptr);
Everything seems to be perfectly valid, except that there is a extra release, and you'll end up crashing later when the RCW finalizes.
The problem here is the _com_ptr<IMyInterface> argument. This is a C++ smart COM pointer, and it AddRef the pointer when it is created, and Release the pointer when it goes out of scope. Normally, when you are calling it in C++, C++ will take care of everything, making sure the contruction of _com_ptr is done correctly, making sure the AddRef is done. For example, when you are calling MyFunction with a IMyInterface* in C++, the actual code generated will look like this:
IMyInterface *p;
_com_ptr_t<IMyInterface> ptr(p); // -------> C++ inserts this code for you
MyFunction(ptr);
However, when you are calling this as a P/invoke, the construction of ptr is not done at all - CLR is not C++, and has absolutely no knowledge about your _com_ptr_t. Whatever CLR passes as the interface pointer, becomes the _com_ptr_t. This happens to work (except for the extra release part), because _com_ptr_t only has one pointer field. As a result, AddRef is not done, and _com_ptr_t does an extra Release on the interface pointer. Then when CLR is trying to release the COM object, CLR will crash because the COM object will be gone/deleted before the last release from CLR. And people typically blame CLR for that because CLR dll shows up on the stack...
The underlying problem here, is that C++ types come with additional semantics that is not part of the P/invoke signature and is not understood by CLR, and there is no way CLR could follow C++ semantics and make sure they are done correctly. Some common mistakes that people make are:
- Assume constructors are called
- Don't realize that C++ classes might come with v-tables that change the layout of other fields
- Assume C++ 'standard' type like std::string is understood by CLR
Read more: Yi Zhang's MSDN Blog
QR:
0 comments:
Post a Comment