Skip to content

Commit 0394c68

Browse files
committed
Work around COM concurrency issue when accessing VS instances
1 parent b82cb30 commit 0394c68

File tree

1 file changed

+34
-26
lines changed

1 file changed

+34
-26
lines changed

src/Cli/dotnet/commands/dotnet-workload/list/VisualStudioWorkloads.cs

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ namespace Microsoft.DotNet.Workloads.Workload
2020
#endif
2121
internal static class VisualStudioWorkloads
2222
{
23+
private static readonly object s_guard = new();
24+
2325
private const int REGDB_E_CLASSNOTREG = unchecked((int)0x80040154);
2426

2527
/// <summary>
@@ -164,44 +166,50 @@ internal static IEnumerable<WorkloadId> WriteSDKInstallRecordsForVSWorkloads(IIn
164166
/// <returns>A list of Visual Studio instances.</returns>
165167
private static List<ISetupInstance> GetVisualStudioInstances()
166168
{
167-
List<ISetupInstance> vsInstances = new();
168-
169-
try
169+
// The underlying COM API has a bug where-by it's not safe for concurrent calls. Until their
170+
// bug fix is rolled out use a lock to ensure we don't concurrently access this API.
171+
// https://dev.azure.com/devdiv/DevDiv/_workitems/edit/2241752/
172+
lock (s_guard)
170173
{
171-
SetupConfiguration setupConfiguration = new();
172-
ISetupConfiguration2 setupConfiguration2 = setupConfiguration;
173-
IEnumSetupInstances setupInstances = setupConfiguration2.EnumInstances();
174-
ISetupInstance[] instances = new ISetupInstance[1];
175-
int fetched = 0;
174+
List<ISetupInstance> vsInstances = new();
176175

177-
do
176+
try
178177
{
179-
setupInstances.Next(1, instances, out fetched);
178+
SetupConfiguration setupConfiguration = new();
179+
ISetupConfiguration2 setupConfiguration2 = setupConfiguration;
180+
IEnumSetupInstances setupInstances = setupConfiguration2.EnumInstances();
181+
ISetupInstance[] instances = new ISetupInstance[1];
182+
int fetched = 0;
180183

181-
if (fetched > 0)
184+
do
182185
{
183-
ISetupInstance2 instance = (ISetupInstance2)instances[0];
186+
setupInstances.Next(1, instances, out fetched);
184187

185-
// .NET Workloads only shipped in 17.0 and later and we should only look at IDE based SKUs
186-
// such as community, professional, and enterprise.
187-
if (Version.TryParse(instance.GetInstallationVersion(), out Version version) &&
188-
version.Major >= 17 &&
189-
s_visualStudioProducts.Contains(instance.GetProduct().GetId()))
188+
if (fetched > 0)
190189
{
191-
vsInstances.Add(instances[0]);
190+
ISetupInstance2 instance = (ISetupInstance2)instances[0];
191+
192+
// .NET Workloads only shipped in 17.0 and later and we should only look at IDE based SKUs
193+
// such as community, professional, and enterprise.
194+
if (Version.TryParse(instance.GetInstallationVersion(), out Version version) &&
195+
version.Major >= 17 &&
196+
s_visualStudioProducts.Contains(instance.GetProduct().GetId()))
197+
{
198+
vsInstances.Add(instances[0]);
199+
}
192200
}
193201
}
202+
while (fetched > 0);
203+
204+
}
205+
catch (COMException e) when (e.ErrorCode == REGDB_E_CLASSNOTREG)
206+
{
207+
// Query API not registered, good indication there are no VS installations of 15.0 or later.
208+
// Other exceptions are passed through since that likely points to a real error.
194209
}
195-
while (fetched > 0);
196210

211+
return vsInstances;
197212
}
198-
catch (COMException e) when (e.ErrorCode == REGDB_E_CLASSNOTREG)
199-
{
200-
// Query API not registered, good indication there are no VS installations of 15.0 or later.
201-
// Other exceptions are passed through since that likely points to a real error.
202-
}
203-
204-
return vsInstances;
205213
}
206214
}
207215
}

0 commit comments

Comments
 (0)