Properties" section, I can install sever...">

How can I programmatically determine the effect of each StartUp project in a solution?

In the "Solutions-> Properties" section, I can install several startup projects: Solution properties

I know that I can get a list of projects marked with "Start" (using EnvDTE: solution.SolutionBuild.StartupProjects ), but how can I get a list of projects whose action is "Start without debugging"? They do not appear in the list.

+6
source share
1 answer

I don't think this is documented and available officially, but here is some information:

  • This is stored in the solution .SUO file using the built-in Visual Studio package. The SUO file has an OLE storage format. You can view it with a tool such as OpenMCDF (it has a sample explorer). In this file, you will see a stream called " SolutionConfiguration " that contains the dwStartupOpt token, and then the information you are looking for. The stream itself has its own binary format.

  • The same information is available from VS through the IVsPersistSolutionProps interface . You need to get a pointer to it from one of the downloaded packages (for example, listing the packages using the IVsShell.GetPackageEnum method . One package will support the IVsPersistSolutionProps interface with the SolutionConfiguration stream.

However, whichever method you choose, you, in turn, will parse the "SolutionConfiguration" stream manually. I present here a method that simply opens the SUO file and breaks the bits "manually", so it works outside of VS.

Here is a utility class that parses the "SolutionConfiguration" stream:

 public sealed class StartupOptions { private StartupOptions() { } public static IDictionary<Guid, int> ReadStartupOptions(string filePath) { if (filePath == null) throw new ArgumentNullException("filePath"); // look for this token in the file const string token = "dwStartupOpt\0="; byte[] tokenBytes = Encoding.Unicode.GetBytes(token); Dictionary<Guid, int> dic = new Dictionary<Guid, int>(); byte[] bytes; using (MemoryStream stream = new MemoryStream()) { CompoundFileUtilities.ExtractStream(filePath, "SolutionConfiguration", stream); bytes = stream.ToArray(); } int i = 0; do { bool found = true; for (int j = 0; j < tokenBytes.Length; j++) { if (bytes[i + j] != tokenBytes[j]) { found = false; break; } } if (found) { // back read the corresponding project guid // guid is formatted as {guid} // len to read is Guid length* 2 and there are two offset bytes between guid and startup options token byte[] guidBytes = new byte[38 * 2]; Array.Copy(bytes, i - guidBytes.Length - 2, guidBytes, 0, guidBytes.Length); Guid guid = new Guid(Encoding.Unicode.GetString(guidBytes)); // skip VT_I4 int options = BitConverter.ToInt32(bytes, i + tokenBytes.Length + 2); dic[guid] = options; } i++; } while (i < bytes.Length); return dic; } } 

The following is a small utility for reading a composite stream (there is no need for an external library):

 public static class CompoundFileUtilities { public static void ExtractStream(string filePath, string streamName, string streamPath) { if (filePath == null) throw new ArgumentNullException("filePath"); if (streamName == null) throw new ArgumentNullException("streamName"); if (streamPath == null) throw new ArgumentNullException("streamPath"); using (FileStream output = new FileStream(streamPath, FileMode.Create)) { ExtractStream(filePath, streamName, output); } } public static void ExtractStream(string filePath, string streamName, Stream output) { if (filePath == null) throw new ArgumentNullException("filePath"); if (streamName == null) throw new ArgumentNullException("streamName"); if (output == null) throw new ArgumentNullException("output"); IStorage storage; int hr = StgOpenStorage(filePath, null, STGM.READ | STGM.SHARE_DENY_WRITE, IntPtr.Zero, 0, out storage); if (hr != 0) throw new Win32Exception(hr); try { IStream stream; hr = storage.OpenStream(streamName, IntPtr.Zero, STGM.READ | STGM.SHARE_EXCLUSIVE, 0, out stream); if (hr != 0) throw new Win32Exception(hr); int read = 0; IntPtr readPtr = Marshal.AllocHGlobal(Marshal.SizeOf(read)); try { byte[] bytes = new byte[0x1000]; do { stream.Read(bytes, bytes.Length, readPtr); read = Marshal.ReadInt32(readPtr); if (read == 0) break; output.Write(bytes, 0, read); } while(true); } finally { Marshal.FreeHGlobal(readPtr); Marshal.ReleaseComObject(stream); } } finally { Marshal.ReleaseComObject(storage); } } [ComImport, Guid("0000000b-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IStorage { void Unimplemented0(); [PreserveSig] int OpenStream([MarshalAs(UnmanagedType.LPWStr)] string pwcsName, IntPtr reserved1, STGM grfMode, uint reserved2, out IStream ppstm); // other methods not declared for simplicity } [Flags] private enum STGM { READ = 0x00000000, SHARE_DENY_WRITE = 0x00000020, SHARE_EXCLUSIVE = 0x00000010, // other values not declared for simplicity } [DllImport("ole32.dll")] private static extern int StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)] string pwcsName, IStorage pstgPriority, STGM grfMode, IntPtr snbExclude, uint reserved, out IStorage ppstgOpen); } 

And a sample showing project hints related to launch options:

 static void SafeMain(string[] args) { foreach (var kvp in StartupOptions.ReadStartupOptions("mySample.suo")) { if ((kvp.Value & 1) != 0) { Console.WriteLine("Project " + kvp.Key + " has option Start"); } if ((kvp.Value & 2) != 0) { Console.WriteLine("Project " + kvp.Key + " has option Start with debugging"); } } } 
+4
source

Source: https://habr.com/ru/post/905697/


All Articles