Skip to content

Commit 9949843

Browse files
committed
Introduce Serialize method to the weak COM Safe class.
1 parent 89f2cb8 commit 9949843

File tree

1 file changed

+61
-5
lines changed

1 file changed

+61
-5
lines changed

Rubberduck.VBEEditor/ComManagement/WeakComSafe.cs

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,26 @@
33
using System.Runtime.CompilerServices;
44
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
55

6+
#if DEBUG
7+
using System.Linq;
8+
using System.Runtime.InteropServices;
9+
#endif
10+
611
namespace Rubberduck.VBEditor.ComManagement
712
{
813
public class WeakComSafe : IComSafe
914
{
1015
//We use weak references to allow the GC to reclaim RCWs earlier if possible.
11-
private readonly ConcurrentDictionary<int, WeakReference<ISafeComWrapper>> _comWrapperCache = new ConcurrentDictionary<int, WeakReference<ISafeComWrapper>>();
12-
16+
private readonly ConcurrentDictionary<int, (DateTime insertTime, WeakReference<ISafeComWrapper> weakRef)> _comWrapperCache = new ConcurrentDictionary<int, (DateTime, WeakReference<ISafeComWrapper>)>();
1317

1418
public void Add(ISafeComWrapper comWrapper)
1519
{
1620
if (comWrapper != null)
1721
{
1822
_comWrapperCache.AddOrUpdate(
1923
GetComWrapperObjectHashCode(comWrapper),
20-
key => new WeakReference<ISafeComWrapper>(comWrapper),
21-
(key, value) => new WeakReference<ISafeComWrapper>(comWrapper));
24+
key => (DateTime.UtcNow, new WeakReference<ISafeComWrapper>(comWrapper)),
25+
(key, value) => (DateTime.UtcNow, new WeakReference<ISafeComWrapper>(comWrapper)));
2226
}
2327

2428
}
@@ -47,13 +51,65 @@ public void Dispose()
4751

4852
foreach (var weakReference in _comWrapperCache.Values)
4953
{
50-
if(weakReference.TryGetTarget(out var comWrapper))
54+
if(weakReference.weakRef.TryGetTarget(out var comWrapper))
5155
{
5256
comWrapper.Dispose();
5357
}
5458
}
5559

5660
_comWrapperCache.Clear();
5761
}
62+
63+
#if DEBUG
64+
/// <summary>
65+
/// Provide a serialized list of the COM Safe
66+
/// to make it easy to analyze what is inside
67+
/// the COM Safe at the different points of
68+
/// the session's lifetime.
69+
/// </summary>
70+
public void Serialize()
71+
{
72+
using (var stream = System.IO.File.AppendText($"comSafeOutput {DateTime.UtcNow:yyyyMMddhhmmss}.csv"))
73+
{
74+
stream.WriteLine("Ordinal\tKey\tCOM Wrapper Type\tWrapping Null?\tIUnknown Pointer Address");
75+
var i = 0;
76+
foreach (var kvp in _comWrapperCache.OrderBy(kvp => kvp.Value.insertTime))
77+
{
78+
var line = kvp.Value.weakRef.TryGetTarget(out var target)
79+
? $"{i++}\t{kvp.Key}\t\"{target.GetType().FullName}\"\t\"{target.IsWrappingNullReference}\"\t\"{(target.IsWrappingNullReference ? "null" : GetPtrAddress(target.Target))}\""
80+
: $"{i++}\t{kvp.Key}\t\"null\"\t\"null\"\t\"null\"";
81+
stream.WriteLine(line);
82+
}
83+
}
84+
}
85+
86+
private static string GetPtrAddress(object target)
87+
{
88+
if (target == null)
89+
{
90+
return IntPtr.Zero.ToString();
91+
}
92+
93+
if (!Marshal.IsComObject(target))
94+
{
95+
return "Not a COM object";
96+
}
97+
98+
var pointer = IntPtr.Zero;
99+
try
100+
{
101+
pointer = Marshal.GetIUnknownForObject(target);
102+
}
103+
finally
104+
{
105+
if (pointer != IntPtr.Zero)
106+
{
107+
Marshal.Release(pointer);
108+
}
109+
}
110+
111+
return pointer.ToString();
112+
}
113+
#endif
58114
}
59115
}

0 commit comments

Comments
 (0)