【问题标题】:Visual Studio - Get the startup project programmaticallyVisual Studio - 以编程方式获取启动项目
【发布时间】:2020-02-11 04:50:01
【问题描述】:

我正在构建一个 .Net 应用程序,它应该导入 VS2010/2008 解决方案并通过读取 .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``

【问题讨论】:

标签: c# .net


【解决方案1】:

这是解决方案。它主要基于@SimonMourier post,但不是搜索“dwStartupOpt\0=”,而是搜索“StartupProject\0=&\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    

【讨论】:

  • 对于 VS2017,令牌必须是:"StartupProject\0=" 并且 'Array.Copy' 需要移位 4:Array.Copy(bytes, i2 +4+ tokenBytes.Length + 2 , guidBytes, 0, guidBytes.Length);至少在我的情况下它开始与这个模块一起工作:) 出色的工作顺便说一句
猜你喜欢
  • 1970-01-01
  • 2012-02-06
  • 2013-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-24
相关资源
最近更新 更多