Under Solution->Properties, I can set multiple start-up projects:
I know that I can get the list of projects marked with "Start" (by using EnvDTE: solution.SolutionBuild.StartupProjects
), but how do I get the list of projects whose action is "Start without debugging"? They do not appear in the list.
You can configure a solution to have multiple startup projects - Right click the solution in Solution Explorer, choose "Set StartUp projects", choose "Multiple Startup Projects", and choose the ones you wish to have start. They'll all be started when you hit F5.
On the Properties page, select Common Properties > Startup Project. Select Current selection, Single startup project and a project file, or Multiple startup projects. If you select Multiple startup projects, you can change the startup order and action to take for each project: Start, Start without debugging, or None.
Right-click on your Solution within the Solution Explorer Select the Set Startup Projects option. Select the Projects that you want to run under the Multiple Startup Projects area or select a single project from the Single Startup Project sections. Click Apply to apply your changes.
I don't think this is documented and available officially, but here are some information:
This is stored in the solution's .SUO file by the Visual Studio built-in package. The SUO file has the OLE compound storage format. You can browse it using a tool such as OpenMCDF (it has an explorer sample). In this file, you will see a stream named 'SolutionConfiguration
' that contains a dwStartupOpt token followed by the information you're looking for. The stream itself has a custom binary format.
The same information is available from within VS through the IVsPersistSolutionProps Interface. You need to get a pointer to it from one of the loaded packages (for example enumerating the list of packages using the IVsShell.GetPackageEnum Method. One package will support the IVsPersistSolutionProps interface with the 'SolutionConfiguration
' stream.
However, whatever method you choose, you will end up parsing the 'SolutionConfiguration' stream manually I believe. I present here a method that does it simply opening the SUO file and hacking the bits out 'manually', so it works outside of VS.
Here is the 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;
}
}
Followed by a small compound stream read utility (no need for 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 the sample that display project guids associated with startup 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");
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With