Che
Che

Reputation: 95

After using a struct from PInvoke, do I need to release the memory?

Do I need to do something like this:

TEXTMETRIC tm;
bool isTrueType = false;
if (NativeMethods.GetTextMetrics(hDC, out tm))
{
    isTrueType = ((PitchAndFamily)tm.tmPitchAndFamily & PitchAndFamily.TMPF_TRUETYPE) == PitchAndFamily.TMPF_TRUETYPE;
    IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<TEXTMETRIC>(tm));
    Marshal.StructureToPtr<TEXTMETRIC>(tm, ptr, true);
    Marshal.FreeHGlobal(ptr);
}

or will the allocated memeory be automatically cleaned up once the function exits? I think (from what I've read) it's the latter, but I'm not sure!

Any elucidation appreciated!

Upvotes: 3

Views: 580

Answers (4)

Hans Passant
Hans Passant

Reputation: 941932

Marshal.StructureToPtr<TEXTMETRIC>(tm, ptr, true);

Using true here is very wrong. The memory you allocated with AllocHGlobal() is not initialized and contains random bytes. It does not contain a previous version of the structure that needs to be released before it is overwritten by the method.

This can technically cause very hard to diagnose random crashes, depending on the random byte values. You got away with it because TEXTMETRIC does not contain any members that require cleanup. The FreeHGlobal() call is sufficient, no need for Marshal.DestroyStructure(), you ought to put it in a finally block so it is exception-safe. Which answers your question.

To be complete, cleanup is only required if the structure contains a BSTR, SAFEARRAY or a COM interface pointer. Resources that have explicit release calls and require the [MarshalAs] attribute in the structure declaration. That's exceedingly rare when you use pinvoke. Not rare at all when you use COM interop, also uses StructureToPtr() under the hood, but the CLR makes the call automatically.

Upvotes: 6

David Heffernan
David Heffernan

Reputation: 613192

The function you call, GetTextMetrics, expects the caller to allocate and deallocate the memory for the structure. If you allocate with AllocHGlobal you must deallocate with FreeHGlobal.

However all this is unnecessary. You are allocating the structure when you declare tm. There's nothing more needed. Avoid the need to call FreeHGlobal by never calling AllocHGlobal.

TEXTMETRIC tm;
bool isTrueType = false;
if (NativeMethods.GetTextMetrics(hDC, out tm))
{
    isTrueType = ((PitchAndFamily)tm.tmPitchAndFamily & PitchAndFamily.TMPF_TRUETYPE) == PitchAndFamily.TMPF_TRUETYPE;
}

Avoiding manual allocation would also allow you to avoid making the broken call, or indeed any call, to StructureToPtr that other answers describe.

Upvotes: 5

Sriram Sakthivel
Sriram Sakthivel

Reputation: 73492

No, if you allocate memory using AllocHGlobal you must free it yourself. otherwise that memory will be leaked.

Here is the relevant part from documentation

A pointer to the newly allocated memory. This memory must be released using the Marshal.FreeHGlobal method.

Upvotes: 3

usr
usr

Reputation: 171206

If you manually allocate unmanaged memory (and you are doing that) you need to manually free it. Add a finally block to make sure that no exception can interfere with freeing memory.

You example doesn't make much sense, though, because you copy tm to a memory block that you never use.

Upvotes: 2

Related Questions