A 3rd party com module being used from c# via interop generated interface is leaking memory
The 3rd party c++ method signature is:
somemethod(....., long** param3, long** param4)
The generated interop method is:
somemethod(...., IntPtr param3, IntPtr param4)
The last 2 params are assigned arrays by the umanaged dll, and are freed from c# Marshal.CoMemFree (dont remember exact sig atm)
开发者_StackOverflow社区Using the same method from C++ via com interface and freeing in same way does not produce leaks
Using tlbimp from the command line produces:
TlbImp : warning TI0000 : At least one of the arguments for
'Sometype.somemethod' cannot be marshaled by the runtime
marshaler. Such arguments will therefore be passed as a pointer and may
require unsafe code to manipulate.
I find it surprising long** params can't be marshalled automatically.
Have a better understanding of c++ (excl. Com black magic) than .net, but implementing the .net side...
What is the correct way to access and free the memory passed back in param3 and param4. I suspect they should be 'out IntPtr'?
This declaration is strongly incompatible with COM Automation. Arrays need to be passed as SAFEARRAYs so it is crystal clear how large they are and how their memory is managed. Passing a long** would typically indicate that the callee is responsible for allocating the array and returning a pointer to the array. Exactly how it is supposed to allocate is the problem, it isn't clear whether it should use the process heap, the COM heap or can use the CRT heap.
Tlbimp.exe throws a fit, it doesn't know how to properly translate the argument type. You would have to decompile the interop library with ildasm.exe, edit the IL to turn the argument into out IntPtr or out int[] and compile it again with ilasm.exe. The only reasonable guess you have for an allocator is Marshal.AllocCoTaskMem(). Might work, might leak badly. You need help from the component vendor or author to avoid having to guess.
Found 'a' way to get expected functionality, not sure how sound the solution is. Following works without leaking
long* arr1 = null;
long* arr2 = null;
IntPtr parr1 = new IntPtr(&arr1);
IntPtr parr2 = new IntPtr(&arr2);
somemethod(....., parr1, parr2);
Marshal.CoTaskMemFree(new IntPtr(arr1));
Marshal.CoTaskMemFree(new IntPtr(arr2));
Caveats:
Have not tried to access the arrays, I don't actually need the contents, but to do so I guess probably requires Marshall.Copy calls.
Intuitively the call should be somemethod(....., ref parr1, ref parr2); However it appears IntPtr is actually more like a void pointer, so although it's passed by value it's value is the address of arr1 so the callee is able to assign into arr1, smells wrong but it works, possibly the arr1 or parr1 should be allocated with Marshal.CoTaskMemAlloc (?)
I had tried the above before in a console application and it still leaked, however when specifying [STAThread] (as opposed to not specifying / default) the leaking stops. The fact apartment thing changes the meaning of code so subtlety stinks
精彩评论