Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I programmatically find out the Action of each StartUp Project in a solution?

Under Solution->Properties, I can set multiple start-up projects: Solution Properties

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.

like image 374
Omer Raviv Avatar asked Jan 11 '12 10:01

Omer Raviv


People also ask

How do you run a specific project in a solution?

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.

How do I debug multiple projects in Visual Studio?

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.

How do I run a particular project in a solution in Visual Studio?

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.


1 Answers

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");
        }
    }
}
like image 196
Simon Mourier Avatar answered Sep 20 '22 17:09

Simon Mourier