Exit for missing Windows plugin projects (#51838)

Exit, rather than crash, if a Windows plugin is missing its project.

Fixes #51743
diff --git a/packages/flutter_tools/lib/src/windows/visual_studio_solution_utils.dart b/packages/flutter_tools/lib/src/windows/visual_studio_solution_utils.dart
index c01d89a..66560ef 100644
--- a/packages/flutter_tools/lib/src/windows/visual_studio_solution_utils.dart
+++ b/packages/flutter_tools/lib/src/windows/visual_studio_solution_utils.dart
@@ -32,10 +32,14 @@
   }) {
     name = plugin.name;
     final File projectFile = fileSystem.directory(plugin.path).childDirectory('windows').childFile('plugin.vcxproj');
+    try {
     guid = VisualStudioProject(projectFile, fileSystem: fileSystem).guid;
-    if (guid == null) {
+    } on FileSystemException {
       throwToolExit('Unable to find a plugin.vcxproj for plugin "$name"');
     }
+    if (guid == null) {
+      throwToolExit('Unable to find a plugin.vcxproj ID for plugin "$name"');
+    }
   }
 
   // The name of the plugin, which is also the name of the symlink folder.
diff --git a/packages/flutter_tools/test/general.shard/windows/visual_studio_solution_utils_test.dart b/packages/flutter_tools/test/general.shard/windows/visual_studio_solution_utils_test.dart
index ee7b8c0..5300225 100644
--- a/packages/flutter_tools/test/general.shard/windows/visual_studio_solution_utils_test.dart
+++ b/packages/flutter_tools/test/general.shard/windows/visual_studio_solution_utils_test.dart
@@ -222,12 +222,14 @@
 
     // Configures and returns a mock plugin with the given name and GUID in the
     // project's plugin symlink directory.
-    Plugin getMockPlugin(String name, String guid) {
+    Plugin getMockPlugin(String name, String guid, {bool createProject = true}) {
       final MockPlugin plugin = MockPlugin();
       when(plugin.platforms).thenReturn(<String, PluginPlatform>{project.pluginConfigKey: null});
       when(plugin.name).thenReturn(name);
       when(plugin.path).thenReturn(project.pluginSymlinkDirectory.childDirectory(name).path);
-      writeDummyPluginProject(name, guid);
+      if (createProject) {
+        writeDummyPluginProject(name, guid);
+      }
       return plugin;
     }
 
@@ -422,6 +424,16 @@
       expect(RegExp(r'^([ \t]+\t|\t[ \t]+)GlobalSection\(\s*$', multiLine: true).hasMatch(newSolutionContents), false);
       expect(RegExp(r'^([ \t]+\t|\t[ \t]+)EndGlobalSection\s*$', multiLine: true).hasMatch(newSolutionContents), false);
     });
+
+    test('A plugin without a project exits without crashing', () async {
+      writeSolutionWithoutPlugins();
+
+      final List<Plugin> plugins = <Plugin>[
+        getMockPlugin('plugin_a', pluginAGuid, createProject: false),
+      ];
+      expect(() => VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins),
+        throwsToolExit());
+    });
   });
 }