Skip to content

Commit 0d47aa1

Browse files
committed
Work around COM concurrency issue when accessing VS instances
1 parent 6915717 commit 0d47aa1

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
@@ -17,6 +17,8 @@ namespace Microsoft.DotNet.Workloads.Workload
1717
#endif
1818
internal static class VisualStudioWorkloads
1919
{
20+
private static readonly object s_guard = new();
21+
2022
private const int REGDB_E_CLASSNOTREG = unchecked((int)0x80040154);
2123

2224
/// <summary>
@@ -125,44 +127,50 @@ internal static void GetInstalledWorkloads(IWorkloadResolver workloadResolver,
125127
/// <returns>A list of Visual Studio instances.</returns>
126128
private static List<ISetupInstance> GetVisualStudioInstances()
127129
{
128-
List<ISetupInstance> vsInstances = new();
129-
130-
try
130+
// The underlying COM API has a bug where-by it's not safe for concurrent calls. Until their
131+
// bug fix is rolled out use a lock to ensure we don't concurrently access this API.
132+
// https://dev.azure.com/devdiv/DevDiv/_workitems/edit/2241752/
133+
lock (s_guard)
131134
{
132-
SetupConfiguration setupConfiguration = new();
133-
ISetupConfiguration2 setupConfiguration2 = setupConfiguration;
134-
IEnumSetupInstances setupInstances = setupConfiguration2.EnumInstances();
135-
ISetupInstance[] instances = new ISetupInstance[1];
136-
int fetched = 0;
135+
List<ISetupInstance> vsInstances = new();
137136

138-
do
137+
try
139138
{
140-
setupInstances.Next(1, instances, out fetched);
139+
SetupConfiguration setupConfiguration = new();
140+
ISetupConfiguration2 setupConfiguration2 = setupConfiguration;
141+
IEnumSetupInstances setupInstances = setupConfiguration2.EnumInstances();
142+
ISetupInstance[] instances = new ISetupInstance[1];
143+
int fetched = 0;
141144

142-
if (fetched > 0)
145+
do
143146
{
144-
ISetupInstance2 instance = (ISetupInstance2)instances[0];
147+
setupInstances.Next(1, instances, out fetched);
145148

146-
// .NET Workloads only shipped in 17.0 and later and we should only look at IDE based SKUs
147-
// such as community, professional, and enterprise.
148-
if (Version.TryParse(instance.GetInstallationVersion(), out Version version) &&
149-
version.Major >= 17 &&
150-
s_visualStudioProducts.Contains(instance.GetProduct().GetId()))
149+
if (fetched > 0)
151150
{
152-
vsInstances.Add(instances[0]);
151+
ISetupInstance2 instance = (ISetupInstance2)instances[0];
152+
153+
// .NET Workloads only shipped in 17.0 and later and we should only look at IDE based SKUs
154+
// such as community, professional, and enterprise.
155+
if (Version.TryParse(instance.GetInstallationVersion(), out Version version) &&
156+
version.Major >= 17 &&
157+
s_visualStudioProducts.Contains(instance.GetProduct().GetId()))
158+
{
159+
vsInstances.Add(instances[0]);
160+
}
153161
}
154162
}
163+
while (fetched > 0);
164+
165+
}
166+
catch (COMException e) when (e.ErrorCode == REGDB_E_CLASSNOTREG)
167+
{
168+
// Query API not registered, good indication there are no VS installations of 15.0 or later.
169+
// Other exceptions are passed through since that likely points to a real error.
155170
}
156-
while (fetched > 0);
157171

172+
return vsInstances;
158173
}
159-
catch (COMException e) when (e.ErrorCode == REGDB_E_CLASSNOTREG)
160-
{
161-
// Query API not registered, good indication there are no VS installations of 15.0 or later.
162-
// Other exceptions are passed through since that likely points to a real error.
163-
}
164-
165-
return vsInstances;
166174
}
167175
}
168176
}

0 commit comments

Comments
 (0)