Skip to content

Commit 41defa5

Browse files
committed
fixed event sink disposal and exception with protected projects; parser state still broken.
1 parent 6339c71 commit 41defa5

File tree

5 files changed

+123
-63
lines changed

5 files changed

+123
-63
lines changed

RetailCoder.VBE/App.cs

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using Rubberduck.UI.Command.MenuItems;
1818
using Infralution.Localization.Wpf;
1919
using Rubberduck.Common.Dispatch;
20+
using Rubberduck.VBEditor.Extensions;
2021

2122
namespace Rubberduck
2223
{
@@ -39,10 +40,10 @@ public class App : IDisposable
3940
private readonly IConnectionPoint _projectsEventsConnectionPoint;
4041
private readonly int _projectsEventsCookie;
4142

42-
private readonly IDictionary<VBProjectsEventsSink, Tuple<IConnectionPoint, int>> _componentsEventsConnectionPoints =
43-
new Dictionary<VBProjectsEventsSink, Tuple<IConnectionPoint, int>>();
44-
private readonly IDictionary<VBProjectsEventsSink, Tuple<IConnectionPoint, int>> _referencesEventsConnectionPoints =
45-
new Dictionary<VBProjectsEventsSink, Tuple<IConnectionPoint, int>>();
43+
private readonly IDictionary<string, Tuple<IConnectionPoint, int>> _componentsEventsConnectionPoints =
44+
new Dictionary<string, Tuple<IConnectionPoint, int>>();
45+
private readonly IDictionary<string, Tuple<IConnectionPoint, int>> _referencesEventsConnectionPoints =
46+
new Dictionary<string, Tuple<IConnectionPoint, int>>();
4647

4748
public App(VBE vbe, IMessageBox messageBox,
4849
IRubberduckParser parser,
@@ -127,11 +128,6 @@ public void Startup()
127128
{
128129
CleanReloadConfig();
129130

130-
foreach (var project in _vbe.VBProjects.Cast<VBProject>())
131-
{
132-
_parser.State.AddProject(project);
133-
}
134-
135131
_appMenus.Initialize();
136132
_appMenus.Localize();
137133

@@ -150,40 +146,51 @@ public void Startup()
150146
#region sink handlers. todo: move to another class
151147
async void sink_ProjectRemoved(object sender, DispatcherEventArgs<VBProject> e)
152148
{
153-
var sink = (VBProjectsEventsSink)sender;
154-
_componentsEventSinks.Remove(sink);
155-
_referencesEventsSinks.Remove(sink);
149+
if (e.Item.Protection == vbext_ProjectProtection.vbext_pp_locked)
150+
{
151+
Debug.WriteLine(string.Format("Locked project '{0}' was removed.", e.Item.Name));
152+
return;
153+
}
154+
155+
var projectId = e.Item.HelpFile;
156+
_componentsEventsSinks.Remove(projectId);
157+
_referencesEventsSinks.Remove(projectId);
156158
_parser.State.RemoveProject(e.Item);
157159

158160
Debug.WriteLine(string.Format("Project '{0}' was removed.", e.Item.Name));
159161
Tuple<IConnectionPoint, int> componentsTuple;
160-
if (_componentsEventsConnectionPoints.TryGetValue(sink, out componentsTuple))
162+
if (_componentsEventsConnectionPoints.TryGetValue(projectId, out componentsTuple))
161163
{
162164
componentsTuple.Item1.Unadvise(componentsTuple.Item2);
163-
_componentsEventsConnectionPoints.Remove(sink);
165+
_componentsEventsConnectionPoints.Remove(projectId);
164166
}
165167

166168
Tuple<IConnectionPoint, int> referencesTuple;
167-
if (_referencesEventsConnectionPoints.TryGetValue(sink, out referencesTuple))
169+
if (_referencesEventsConnectionPoints.TryGetValue(projectId, out referencesTuple))
168170
{
169171
referencesTuple.Item1.Unadvise(referencesTuple.Item2);
170-
_referencesEventsConnectionPoints.Remove(sink);
172+
_referencesEventsConnectionPoints.Remove(projectId);
171173
}
172-
173-
_parser.State.ClearDeclarations(e.Item);
174174
}
175175

176-
private readonly IDictionary<VBProjectsEventsSink, VBComponentsEventsSink> _componentsEventSinks =
177-
new Dictionary<VBProjectsEventsSink, VBComponentsEventsSink>();
176+
private readonly IDictionary<string,VBComponentsEventsSink> _componentsEventsSinks =
177+
new Dictionary<string,VBComponentsEventsSink>();
178178

179-
private readonly IDictionary<VBProjectsEventsSink, ReferencesEventsSink> _referencesEventsSinks =
180-
new Dictionary<VBProjectsEventsSink, ReferencesEventsSink>();
179+
private readonly IDictionary<string,ReferencesEventsSink> _referencesEventsSinks =
180+
new Dictionary<string, ReferencesEventsSink>();
181181

182182
async void sink_ProjectAdded(object sender, DispatcherEventArgs<VBProject> e)
183183
{
184-
var sink = (VBProjectsEventsSink)sender;
185-
RegisterComponentsEventSink(e, sink);
186-
_parser.State.AddProject(e.Item);
184+
Debug.WriteLine(string.Format("Project '{0}' was added.", e.Item.Name));
185+
if (e.Item.Protection == vbext_ProjectProtection.vbext_pp_locked)
186+
{
187+
Debug.WriteLine("Project is protected and will not be added to parser state.");
188+
return;
189+
}
190+
191+
_parser.State.AddProject(e.Item); // note side-effect: assigns ProjectId/HelpFile
192+
var projectId = e.Item.HelpFile;
193+
RegisterComponentsEventSink(e.Item.VBComponents, projectId);
187194

188195
if (!_parser.State.AllDeclarations.Any())
189196
{
@@ -193,13 +200,19 @@ async void sink_ProjectAdded(object sender, DispatcherEventArgs<VBProject> e)
193200
return;
194201
}
195202

196-
Debug.WriteLine(string.Format("Project '{0}' was added.", e.Item.Name));
197203
_parser.State.OnParseRequested(sender);
198204
}
199205

200-
private void RegisterComponentsEventSink(DispatcherEventArgs<VBProject> e, VBProjectsEventsSink sink)
206+
private void RegisterComponentsEventSink(VBComponents components, string projectId)
201207
{
202-
var connectionPointContainer = (IConnectionPointContainer) e.Item.VBComponents;
208+
if (_componentsEventsSinks.ContainsKey(projectId))
209+
{
210+
// already registered - this is caused by the initial load+rename of a project in the VBE
211+
Debug.WriteLine("Components sink already registered.");
212+
return;
213+
}
214+
215+
var connectionPointContainer = (IConnectionPointContainer)components;
203216
var interfaceId = typeof (_dispVBComponentsEvents).GUID;
204217

205218
IConnectionPoint connectionPoint;
@@ -212,12 +225,13 @@ private void RegisterComponentsEventSink(DispatcherEventArgs<VBProject> e, VBPro
212225
componentsSink.ComponentRemoved += sink_ComponentRemoved;
213226
componentsSink.ComponentRenamed += sink_ComponentRenamed;
214227
componentsSink.ComponentSelected += sink_ComponentSelected;
215-
_componentsEventSinks.Add(sink, componentsSink);
228+
_componentsEventsSinks.Add(projectId, componentsSink);
216229

217230
int cookie;
218231
connectionPoint.Advise(componentsSink, out cookie);
219232

220-
_componentsEventsConnectionPoints.Add(sink, Tuple.Create(connectionPoint, cookie));
233+
_componentsEventsConnectionPoints.Add(projectId, Tuple.Create(connectionPoint, cookie));
234+
Debug.WriteLine("Components sink registered and advising.");
221235
}
222236

223237
async void sink_ComponentSelected(object sender, DispatcherEventArgs<VBComponent> e)
@@ -251,7 +265,7 @@ async void sink_ComponentRemoved(object sender, DispatcherEventArgs<VBComponent>
251265
}
252266

253267
Debug.WriteLine(string.Format("Component '{0}' was removed.", e.Item.Name));
254-
_parser.State.ClearDeclarations(e.Item);
268+
_parser.State.ClearStateCache(e.Item);
255269
}
256270

257271
async void sink_ComponentReloaded(object sender, DispatcherEventArgs<VBComponent> e)
@@ -364,7 +378,10 @@ public void Dispose()
364378
{
365379
item.Value.Item1.Unadvise(item.Value.Item2);
366380
}
367-
381+
foreach (var item in _referencesEventsConnectionPoints)
382+
{
383+
item.Value.Item1.Unadvise(item.Value.Item2);
384+
}
368385
_hooks.Dispose();
369386
}
370387
}

RetailCoder.VBE/UI/Command/MenuItems/RubberduckCommandBar.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ private void _statusButton_Click(CommandBarButton Ctrl, ref bool CancelDefault)
4141

4242
public void SetStatusText(string value = null)
4343
{
44-
Debug.WriteLine(string.Format("RubberduckCommandBar status text changes to '{0}'.", value));
45-
UiDispatcher.Invoke(() => _statusButton.Caption = value ?? RubberduckUI.ResourceManager.GetString("ParserState_" + _state.Status));
44+
var text = value ?? RubberduckUI.ResourceManager.GetString("ParserState_" + _state.Status);
45+
Debug.WriteLine(string.Format("RubberduckCommandBar status text changes to '{0}'.", text));
46+
UiDispatcher.Invoke(() => _statusButton.Caption = text);
4647
}
4748

4849
public void SetSelectionText(Declaration declaration)

Rubberduck.Parsing/VBA/RubberduckParser.cs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,13 @@ public void Parse()
8989
{
9090
if (!_state.Projects.Any())
9191
{
92-
foreach (var project in _vbe.VBProjects.Cast<VBProject>())
92+
foreach (var project in _vbe.VBProjects.UnprotectedProjects())
9393
{
9494
_state.AddProject(project);
9595
}
9696
}
9797

98-
var projects = _state.Projects
99-
.Where(project => project.Protection == vbext_ProjectProtection.vbext_pp_none)
100-
.ToList();
98+
var projects = _state.Projects.ToList();
10199

102100
var components = projects.SelectMany(p => p.VBComponents.Cast<VBComponent>()).ToList();
103101
SyncComReferences(projects);
@@ -115,7 +113,7 @@ public void Parse()
115113

116114
foreach (var vbComponent in components)
117115
{
118-
while (!_state.ClearDeclarations(vbComponent)) { }
116+
while (!_state.ClearStateCache(vbComponent)) { }
119117

120118
// expects synchronous parse :/
121119
ParseComponent(vbComponent);
@@ -127,14 +125,19 @@ public void Parse()
127125
/// </summary>
128126
private void ParseAll()
129127
{
130-
var projects = _state.Projects
131-
// accessing the code of a protected VBComponent throws a COMException:
132-
.Where(project => project.Protection == vbext_ProjectProtection.vbext_pp_none)
133-
.ToList();
128+
if (!_state.Projects.Any())
129+
{
130+
foreach (var project in _vbe.VBProjects.UnprotectedProjects())
131+
{
132+
_state.AddProject(project);
133+
}
134+
}
135+
136+
var projects = _state.Projects.ToList();
134137

135138
var components = projects.SelectMany(p => p.VBComponents.Cast<VBComponent>()).ToList();
136-
var modified = components.Where(_state.IsModified).ToList();
137-
var unchanged = components.Where(c => !_state.IsModified(c)).ToList();
139+
var modified = components.Where(c => _state.IsNewOrModified(c)).ToList();
140+
var unchanged = components.Where(c => !_state.IsNewOrModified(c)).ToList();
138141

139142
SyncComReferences(projects);
140143

@@ -228,7 +231,7 @@ private void UnloadComReference(Reference reference)
228231

229232
public Task ParseAsync(VBComponent component, CancellationToken token, TokenStreamRewriter rewriter = null)
230233
{
231-
_state.ClearDeclarations(component);
234+
_state.ClearStateCache(component);
232235
_state.SetModuleState(component, ParserState.Pending); // also clears module-exceptions
233236

234237
var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_central.Token, token);
@@ -322,7 +325,7 @@ private void ResolveInternal(CancellationToken token)
322325
var qualifiedName = kvp.Key;
323326
if (true /*_state.IsModified(qualifiedName)*/)
324327
{
325-
Debug.WriteLine("Module '{0}' {1}", qualifiedName.ComponentName, _state.IsModified(qualifiedName) ? "was modified" : "was NOT modified");
328+
Debug.WriteLine("Module '{0}' {1}", qualifiedName.ComponentName, _state.IsNewOrModified(qualifiedName) ? "was modified" : "was NOT modified");
326329
// modified module; walk parse tree and re-acquire all declarations
327330
if (token.IsCancellationRequested) return;
328331
ResolveDeclarations(qualifiedName.Component, kvp.Value);

0 commit comments

Comments
 (0)