Skip to content

Commit 8667769

Browse files
committed
Refactor to its own class, UiSynchronizer.
Update description for better clarity. Add calls to its Initialize method.
1 parent ac486b2 commit 8667769

File tree

5 files changed

+115
-84
lines changed

5 files changed

+115
-84
lines changed

RetailCoder.VBE/API/VBA/ParserState.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public sealed class ParserState : IParserState, IDisposable
5656
public ParserState()
5757
{
5858
UiDispatcher.Initialize();
59+
UiSynchronizer.Initialize();
5960
}
6061

6162
public void Initialize(Microsoft.Vbe.Interop.VBE vbe)

RetailCoder.VBE/App.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public App(IVBE vbe,
5050
_configService.SettingsChanged += _configService_SettingsChanged;
5151

5252
UiDispatcher.Initialize();
53+
UiSynchronizer.Initialize();
5354
}
5455

5556
private void _configService_SettingsChanged(object sender, ConfigurationChangedEventArgs e)

Rubberduck.Parsing/Rubberduck.Parsing.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@
176176
<Compile Include="Symbols\ParsingExceptions\MainParseSyntaxErrorException.cs" />
177177
<Compile Include="Symbols\ParsingExceptions\PreprosessorSyntaxErrorException.cs" />
178178
<Compile Include="UIContext\UiDispatcher.cs" />
179+
<Compile Include="UIContext\UiSynchronizer.cs" />
179180
<Compile Include="VBA\RepositoryProjectManager.cs" />
180181
<Compile Include="VBA\ParsePass.cs" />
181182
<Compile Include="Inspections\CannotAnnotateAttribute.cs" />

Rubberduck.Parsing/UIContext/UiDispatcher.cs

Lines changed: 1 addition & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
11
using System;
2-
using System.Runtime.InteropServices;
32
using System.Threading;
43
using System.Threading.Tasks;
54

65
namespace Rubberduck.Parsing.UIContext
76
{
87
public static class UiDispatcher
98
{
10-
static UiDispatcher()
11-
{
12-
_version = DllVersion.Unknown;
13-
}
14-
159
// thanks to Pellared on http://stackoverflow.com/a/12909070/1188513
1610

1711
private static SynchronizationContext UiContext { get; set; }
@@ -104,8 +98,7 @@ public static Task<T> StartTask<T>(Func<T> func, TaskCreationOptions options = T
10498
{
10599
return StartTask(func, CancellationToken.None, options);
106100
}
107-
108-
101+
109102
private static void CheckInitialization()
110103
{
111104
if (UiContext == null) throw new InvalidOperationException("UiDispatcher is not initialized. Invoke Initialize() from UI thread first.");
@@ -119,81 +112,5 @@ public static void Shutdown()
119112
// Dispatcher.CurrentDispatcher.InvokeShutdown();
120113
//});
121114
}
122-
123-
/// <summary>
124-
/// Used to pump any pending COM messages. This should be used only as a part of
125-
/// synchronizing or to effect a block until all other threads has finished with
126-
/// their pending COM calls. This should be used by the UI thread **ONLY**.
127-
/// </summary>
128-
/// <remarks>
129-
/// Typical use would be within a event handler for an event belonging to a COM
130-
/// object which require some synchronization with COM accesses from other threads.
131-
/// Events raised by COM are on UI thread by definition.
132-
/// </remarks>
133-
public static int DoEvents()
134-
{
135-
CheckInitialization();
136-
137-
if (UiContext != SynchronizationContext.Current)
138-
{
139-
throw new InvalidOperationException("DoEvents cannot be used in other threads");
140-
}
141-
142-
return ExecuteDoEvents();
143-
}
144-
145-
private static int ExecuteDoEvents()
146-
{
147-
switch (_version)
148-
{
149-
case DllVersion.Vbe7:
150-
return rtcDoEvents7();
151-
case DllVersion.Vbe6:
152-
return rtcDoEvents6();
153-
default:
154-
return DetermineVersionAndExecute();
155-
}
156-
}
157-
158-
private enum DllVersion
159-
{
160-
Unknown,
161-
Vbe6,
162-
Vbe7
163-
}
164-
165-
private static DllVersion _version;
166-
167-
[DllImport("vbe6.dll", EntryPoint = "rtcDoEvents")]
168-
private static extern int rtcDoEvents6();
169-
170-
[DllImport("vbe7.dll", EntryPoint = "rtcDoEvents")]
171-
private static extern int rtcDoEvents7();
172-
173-
private static int DetermineVersionAndExecute()
174-
{
175-
int result;
176-
try
177-
178-
{
179-
result = rtcDoEvents7();
180-
_version = DllVersion.Vbe7;
181-
}
182-
catch
183-
{
184-
try
185-
{
186-
result = rtcDoEvents6();
187-
_version = DllVersion.Vbe6;
188-
}
189-
catch
190-
{
191-
// we shouldn't be here....
192-
throw new InvalidOperationException("Cannot execute DoEvents; the VBE dll could not be located.");
193-
}
194-
}
195-
196-
return result;
197-
}
198115
}
199116
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
using System;
2+
using System.Runtime.InteropServices;
3+
using System.Threading;
4+
5+
namespace Rubberduck.Parsing.UIContext
6+
{
7+
public static class UiSynchronizer
8+
{
9+
private enum DllVersion
10+
{
11+
Unknown,
12+
Vbe6,
13+
Vbe7
14+
}
15+
16+
private static DllVersion _version;
17+
18+
private static SynchronizationContext UiContext { get; set; }
19+
20+
static UiSynchronizer()
21+
{
22+
_version = DllVersion.Unknown;
23+
}
24+
25+
public static void Initialize()
26+
{
27+
if (UiContext == null)
28+
{
29+
UiContext = SynchronizationContext.Current;
30+
}
31+
}
32+
33+
/// <summary>
34+
/// Used to pump any pending COM messages. This should be used only as a part of
35+
/// synchronizing or to effect a block until all other threads has finished with
36+
/// their pending COM calls. This should be used by the UI thread **ONLY**;
37+
/// otherwise execptions will be thrown.
38+
/// </summary>
39+
/// <remarks>
40+
/// Typical use would be within a event handler for an event belonging to a COM
41+
/// object which require some synchronization with COM accesses from other threads.
42+
/// Events raised by COM are on UI thread by definition so the call stack originating
43+
/// from COM objects' events can use this method.
44+
/// </remarks>
45+
/// <returns>Count of open forms which is always zero for VBA hosts but may be nonzero for VB6 projects.</returns>
46+
public static int DoEvents()
47+
{
48+
CheckContext();
49+
50+
return ExecuteDoEvents();
51+
}
52+
53+
private static int ExecuteDoEvents()
54+
{
55+
switch (_version)
56+
{
57+
case DllVersion.Vbe7:
58+
return rtcDoEvents7();
59+
case DllVersion.Vbe6:
60+
return rtcDoEvents6();
61+
default:
62+
return DetermineVersionAndExecute();
63+
}
64+
}
65+
66+
private static int DetermineVersionAndExecute()
67+
{
68+
int result;
69+
try
70+
71+
{
72+
result = rtcDoEvents7();
73+
_version = DllVersion.Vbe7;
74+
}
75+
catch
76+
{
77+
try
78+
{
79+
result = rtcDoEvents6();
80+
_version = DllVersion.Vbe6;
81+
}
82+
catch
83+
{
84+
// we shouldn't be here.... Rubberduck is a VBA add-in, so how the heck could it have loaded without a VBE dll?!?
85+
throw new InvalidOperationException("Cannot execute DoEvents; the VBE dll could not be located.");
86+
}
87+
}
88+
89+
return result;
90+
}
91+
92+
private static void CheckContext()
93+
{
94+
if (UiContext == null)
95+
{
96+
throw new InvalidOperationException("UiSynchronizer is not initialized. Invoke Initialize() from UI thread first.");
97+
}
98+
99+
if (UiContext != SynchronizationContext.Current)
100+
{
101+
throw new InvalidOperationException("UiSynchronizer cannot be used in other threads. Only the UI thread can call methods on the UiSynchronizer");
102+
}
103+
}
104+
105+
[DllImport("vbe6.dll", EntryPoint = "rtcDoEvents")]
106+
private static extern int rtcDoEvents6();
107+
108+
[DllImport("vbe7.dll", EntryPoint = "rtcDoEvents")]
109+
private static extern int rtcDoEvents7();
110+
}
111+
}

0 commit comments

Comments
 (0)