sharpdx / SharpDX

SharpDX GitHub Repository
http://sharpdx.org
MIT License
1.69k stars 641 forks source link

[DWrite] Memory leak caused by bad ref count on TextLayout drawing effect. #1010

Open Jayonas opened 6 years ago

Jayonas commented 6 years ago

I've created my own managed class to use as a drawing effect with TextLayout's Get/SetDrawingEffect and my own TextRenderer implementation, and my app had a memory leak where instances of that effect class were being created and never freed. The root cause turned out to be an incorrect ref count on the CCW (COM Callable Wrapper) that wraps my effect. The count was being incremented during GetDrawingEffect with no corresponding decrement (ever), so the CCW was never deleted, so the managed effect that it was wrapping was never garbage collected.

My workaround is to retrieve the effect's CCW and manually decrement its ref count each time I call GetDrawingEffect.

I don't know the SharpDX implementation or memory management utilities very well, but my guess is that the auto-generated implementation of TextLayout.GetDrawingEffect_ is returning an IUnknown pointer with an already-incremented ref count that it expects the caller to release, because that's the general COM pattern when returning COM object pointers. However, it doesn't look like the manual implementation of TextLayout.GetDrawingEffect ever releases that ref on the pointer it retrieves by calling the auto-generated implementation, it just passes it straight to Utilities.GetObjectForIUnknown, which also doesn't release it. I also don't think that the RCW (Runtime Callable Wrapper) returned by Utilities.GetObjectForIUnknown assumes ownership of that ref; if anything, I'd guess that it adds its own ref (which it then correctly releases when disposed).

(Note that I'm basing this off my usage of SharpDX 3.1.1, which I realize is out of date, but it also doesn't look like much has happened in the DWrite code since then so I think it's likely that this is still an issue.)

Jayonas commented 5 years ago

I've recently updated SharpDX, and I can confirm that this issue still exists in 4.2.0.