I obtain a pointer to a 0-terminated Unicode string in the pwcsName field of the STATSTG structure, by using the IEnumSTATSTG::Next method.
The memory for it was allocated by OLE, but it is the caller's duty to free it.
I assume the memory was allocated using the unmanaged COM task memory allocator, but I could be wrong here. If the assumption is correct, I'd use Marshal.FreeCoTaskMem(PtrToString) to release that memory.
However, the argument is an IntPtr. In VB I tried to obtain an IntPtr with
PtrToString = CType(pwcsName, IntPtr)
The syntax is accepted, but the string is of wrong type (Input string is not in a correct format.)
Thus my questions:
How would I correctly obtain an IntPtr from the structure record's field?
Or more generally, how can I be the good citizen and prevent that potential memory leak?
This is the relevant code:
Dim oElements As IEnumSTATSTG = Nothing
oStorage.EnumElements(0, IntPtr.Zero, 0, oElements)
Dim oElement(0) As Microsoft.VisualStudio.OLE.Interop.STATSTG
Dim uiFetched As UInt32 = 0
oElements.Next(1, oElement, uiFetched)
Do While uiFetched > 0
'Work with oElement(0).pwcsName
'Attempt to free the memory occupied by the name.
Dim pName As IntPtr
pName = CType(.pwcsName, IntPtr)
Marshal.FreeCoTaskMem(pName)
Yield ...
oElements.Next(1, oElement, uiFetched)
Loop
If you take a look at the definition of
STATSTGyou'll read:So that memory must be freed by
CoTaskMemFree()(Marshal.CoTaskMemFree()in .NET).Depending on how you are marshaling the struct to .NET, it is possible that .NET will automatically free it. I say "depending" because you are referencing a version of
STATSTGthat has thepwcsNamethat is astring: in that case the .NET marshaller will free theOLESTR. If you use aSTATSTGstruct that uses anIntPtrthen you'll have to callCoTaskMemFree.To obtain a .NET
stringyou can useMarshal.PtrToStringUni().Now... In your specific case, you are in a corner case. While on parameters of methods you can specify if they are
[In]or[Out]to let the .NET Marshaller know when it has to "work" to Marshal the data (just before calling the method, or after calling the method, or both ways) (but I don't see them in yourNext()definition), on single fields of structs I don't think you can (and then you can't surely modifySTATSTG). So what happens here is that after a call toNext(), on the way C->.NET the .NET Marshaller will create a newStringfrom theOLESTRand free theOLESTR. And we are happy. Then on the next call toNext(), before calling the actual API, the .NET Marshaller will see that there is a beautifulStringin your struct and marshal it to anOLESTR, thinking it is data you want to pass toNext(). This is clearly useless. And now the problem: we don't know if the implementation ofNext()will actually free thisOLESTRbefore creating a new one for the next result (we could do some tests, but we don't want to). The easiest (and surest) solution is to setpwcsNametoNothing(nullon C#) BEFORE EACH CALL TO.Next(). In this way the .NET Marshaller will see a beautifulNothingand will marshal it tonull. Less work done by the marshaller (marshalling strings is expensive), more safety for us that don't have to think if theOLESTRwill be freed or not.Post addendum: I've nugetted the Microsoft.VisualStudio.OLE.Interop 16.7.30328.74 and I've done a Go To Definition from a C# program. The method definition I see is different (and more detailed):
I see that the
STATSTG[] rgeltis marked as[Out], so the marshalling will be done only in the C->.NET direction. You don't need to set toNothing,