Visual Studio - 以编程方式获取启动项目

时间:2016-06-03 15:44:59

标签: c# .net

我正在构建一个应该导入VS2010 / 2008解决方案的.Net应用程序,并通过读取.suo文件找到启动项目。我查看了How do I programmatically find out the Action of each StartUp Project in a solution?,但只有在“解决​​方案属性”窗口中选择“多个启动项目”时,提到的解决方案才有效。我的解决方案没有多个启动项目。当解决方案只有1个启动项目时,有没有办法找到启动项目?

这是代码

` public static class StartUpProjectHelper     {

    public static FileInfo GetStartUpProject(FileInfo solutionFile)
    {
        FileInfo startUpProject = null;

        string suoFile = solutionFile.FullName.Substring(0, solutionFile.FullName.Length - 4) + ".suo";

        string guid = null;

        bool found = false;

        foreach (var kvp in ReadStartupOptions(suoFile))
        {
            if (((kvp.Value & 1) != 0 || (kvp.Value & 2) != 0) && !found)
            {
                guid = kvp.Key.ToString();

                found = true;
            }
        }

        if (!string.IsNullOrEmpty(guid))
        {
            string projectname = GetProjectNameFromGuid(solutionFile, guid).Trim().TrimStart('\"').TrimEnd('\"');

            startUpProject = new FileInfo(Path.Combine(solutionFile.DirectoryName, projectname));
        }

        return startUpProject;
    }


    public static string GetProjectNameFromGuid(FileInfo solutionFile, string guid)
    {
        string projectName = null;

        using (var reader = new StreamReader(solutionFile.FullName))
        {
            string line;

            bool found = false;

            while ((line = reader.ReadLine()) != null && !found)
            {
                //  sample format
                //Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Saltus.digiTICKET.laptop", "digiTICKET.laptop\Saltus.digiTICKET.laptop.csproj", 
                //"{236D51A1-DEB7-41C3-A4C1-1D16D0A85382}" EndProject

                if ((line.IndexOf(guid.ToUpper()) > -1) && line.Contains(",") && line.Contains("="))
                {
                    projectName = line.Split(',')[1].Split(',')[0];

                    found = true;
                }
            }
        }

        return projectName;
    }

    ////  from https://stackoverflow.com/questions/8817693/how-do-i-programmatically-find-out-the-action-of-each-startup-project-in-a-solut
    public static IDictionary<Guid, int> ReadStartupOptions(string filePath)
    {
        if (filePath == null)
        {
            throw new InvalidOperationException("No file selected");
        }


        // look for this token in the file
        const string token = "dwStartupOpt\0=";

        byte[] tokenBytes = Encoding.Unicode.GetBytes(token);

        var dic = new Dictionary<Guid, int>();

        byte[] bytes;

        using (var stream = new MemoryStream())
        {
            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
                var guidBytes = new byte[38*2];
                Array.Copy(bytes, i - guidBytes.Length - 2, guidBytes, 0, guidBytes.Length);
                var 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 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
            {
                var 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);
        }
    }

    [DllImport("ole32.dll")]
    private static extern int StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
                                             IStorage pstgPriority, STGM grfMode, IntPtr snbExclude, uint reserved,
                                             out IStorage ppstgOpen);

    #region Nested type: IStorage

    [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
    }

    #endregion

    #region Nested type: STGM

    [Flags]
    private enum STGM
    {
        READ = 0x00000000,
        SHARE_DENY_WRITE = 0x00000020,
        SHARE_EXCLUSIVE = 0x00000010,
        // other values not declared for simplicity
    }

    #endregion
}

//// from https://stackoverflow.com/questions/8817693/how-do-i-programmatically-find-out-the-action-of-each-startup-project-in-a-solut`

1 个答案:

答案 0 :(得分:3)

这是解决方案。它主要基于@SimonMourier帖子How do I programmatically find out the Action of each StartUp Project in a solution?,但不是搜索“dwStartupOpt \ 0 =”,而是寻找“StartupProject \ 0 =&amp; \ 0”。此标记仅在.suo文件中出现一次,后跟启动项目的GUID。

`

    public static FileInfo GetStartUpProject(FileInfo solutionFile)

    {
        FileInfo startUpProject = null;

        string projectName = Path.GetFileNameWithoutExtension(solutionFile.FullName);

        FileInfo suoFileInfo = new FileInfo(Path.Combine(solutionFile.Directory.FullName, string.Format(projectName + "{0}", ".suo")));

        string guid = ReadStartupOptions(suoFileInfo.FullName).ToString();

        if (!string.IsNullOrEmpty(guid))
        {
            string projectname = GetProjectNameFromGuid(solutionFile, guid).Trim().TrimStart('\"').TrimEnd('\"');

            startUpProject = new FileInfo(Path.Combine(solutionFile.DirectoryName, projectname));
        }

        return startUpProject;
    }


    public static string GetProjectNameFromGuid(FileInfo solutionFile, string guid)
    {
        string projectName = null;

        using (var reader = new StreamReader(solutionFile.FullName))
        {
            string line;

            bool found = false;

            while ((line = reader.ReadLine()) != null && !found)
            {


                if ((line.IndexOf(guid.ToUpper()) > -1) && line.Contains(",") && line.Contains("="))
                {
                    projectName = line.Split(',')[1].Split(',')[0];

                    found = true;
                }
            }
        }

        return projectName;
    }

    ////  from https://stackoverflow.com/questions/8817693/how-do-i-programmatically-find-out-the-action-of-each-startup-project-in-a-solut
    public static Guid ReadStartupOptions(string filePath)
    {
        Guid guid = new Guid();

        if (filePath == null)
        {
            throw new InvalidOperationException("No file selected");
        }

        const string token = "StartupProject\0=&\0";

        byte[] tokenBytes = Encoding.Unicode.GetBytes(token);

        byte[] bytes;

        using (var stream = new MemoryStream())
        {
            ExtractStream(filePath, "SolutionConfiguration", stream);

            bytes = stream.ToArray();
        }

        var guidBytes = new byte[36 * 2]; 

        for (int i2 = 0; i2 < bytes.Length; i2++)
        {
            if (bytes.Skip(i2).Take(tokenBytes.Length).SequenceEqual(tokenBytes))
            {
                Array.Copy(bytes, i2 + tokenBytes.Length + 2, guidBytes, 0, guidBytes.Length);

                guid = new Guid(Encoding.Unicode.GetString(guidBytes));

                break;
            }
        }

        return guid;

    }

    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
            {
                var 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);
        }
    }

    [DllImport("ole32.dll")]
    private static extern int StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
                                             IStorage pstgPriority, STGM grfMode, IntPtr snbExclude, uint reserved,
                                             out IStorage ppstgOpen);

    #region Nested type: IStorage

    [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
    }

    #endregion

    #region Nested type: STGM

    [Flags]
    private enum STGM
    {
        READ = 0x00000000,
        SHARE_DENY_WRITE = 0x00000020,
        SHARE_EXCLUSIVE = 0x00000010,
        // other values not declared for simplicity
    }

    #endregion
}

`