在解决方案->属性下,我可以设置多个启动项目:

我知道我可以获取标记为“开始”的项目列表(通过使用EnvDTE:solution.SolutionBuild.StartupProjects),但是如何获取其 Action 为“无需调试即可开始”的项目列表?它们没有出现在列表中。

最佳答案

我不认为这已被记录并正式提供,但是这里有一些信息:

  • 这是由Visual Studio内置程序包存储在solution's .SUO file中的。 SUO文件具有OLE复合存储格式。您可以使用诸如OpenMCDF之类的工具(具有浏览器示例)对其进行浏览。在此文件中,您将看到一个名为SolutionConfiguration的流,其中包含dwStartupOpt token 以及所需的信息。流本身具有自定义二进制格式。
  • 在VS中,可以通过IVsPersistSolutionProps Interface获得相同的信息。您需要从已加载的软件包之一获取指向它的指针(例如,使用IVsShell.GetPackageEnum Method枚举软件包列表。一个软件包将通过'SolutionConfiguration'流支持IVsPersistSolutionProps接口(interface)。

  • 但是,无论您选择哪种方法,我都将最终手动解析“SolutionConfiguration”流。我在这里介绍一种方法,它可以简单地打开SUO文件并“手动”破解这些位,因此它在VS之外可以工作。

    这是解析“SolutionConfiguration”流的实用程序类:
    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;
        }
    }
    

    后面是一个小的复合流读取实用程序(不需要外部库):
    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);
    }
    

    以及显示与启动选项相关联的项目指导的示例:
    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");
            }
        }
    }
    

    关于visual-studio - 如何以编程方式找出解决方案中每个Startup Project的操作?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8817693/

    10-11 22:49