2012-01-11 35 views
6

在解決方案 - >屬性,我可以設置多個啓動項目: Solution Properties如何以編程方式查找解決方案中每個啓動項目的操作?

我知道,我能得到的標有「開始」的項目列表(通過使用EnvDTE:solution.SolutionBuild.StartupProjects),但我怎麼獲取動作是「無需調試就開始」的項目列表?它們不會出現在列表中。

+0

什麼是你的語言?你正在開發一個VS包嗎?一個VS Addin?或一些外部VS工具? – 2012-02-10 10:20:16

+0

@SimonMourier在C#中編寫VS包# – 2012-02-10 15:24:17

+0

我創建了一個[uservoice建議將其添加到官方API](http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/7555128-extend-visual -studio-api-with-a-method-to-set-star) – 2015-04-13 18:22:58

回答

4

我不認爲這是記錄並提供正式,但這裏有一些信息:

  • 這是通過在Visual Studio內置包存儲在solution's .SUO file。 SUO文件具有OLE複合存儲格式。您可以使用諸如OpenMCDF之類的工具(它具有資源管理器示例)來瀏覽它。在此文件中,您將看到一個名爲'SolutionConfiguration'的流,其中包含一個dwStartupOpt標記,後跟您正在查找的信息。流本身具有自定義二進制格式。

  • 通過IVsPersistSolutionProps Interface可以從VS獲得相同的信息。你需要從裝包之一獲得一個指向它(例如枚舉使用IVsShell.GetPackageEnum Method,一個包將支持與「SolutionConfiguration」流IVsPersistSolutionProps界面包的列表。

然而,無論方法,你會最終手動解析'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); 
} 

和樣品與啓動選項相關的顯示項目的GUID:

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"); 
     } 
    } 
} 
+0

謝謝!我嘗試將它作爲一個外部應用運行,並且它工作正常,但是如果我改變設置並重新運行它,我仍然會得到舊的值 - 直到我關閉解決方案,此時它似乎VS寫入到SUO中,並且然後重新運行它給了我新的價值。將使用IVsPersistSolutionProps修復此問題,如果不是,您是否碰巧知道強制Visual Studio寫入.suo的任何方式? – 2012-02-12 20:24:04

+0

您需要保存解決方案以確保suo更改提交 – 2012-02-12 20:35:30

+0

此解決方案適用於完整框架,但似乎不適用於緊湊型框架。對於所有的GUID,「BitConverter.ToInt32(字節,i + tokenBytes.Length + 2)」爲0。任何解決方法,使這項工作的緊湊框架? – user678229 2016-06-02 14:48:23

相關問題