2012-01-11 4 views
6

Под решение-> Свойства, можно установить несколько проектов пуску: Solution PropertiesКак программно узнать действие каждого проекта StartUp в решении?

Я знаю, что я могу получить список проектов со значком «Старт» (с помощью EnvDTE: solution.SolutionBuild.StartupProjects), но как я получить список проектов, действие которых «Начать без отладки»? Они не отображаются в списке.

+0

что ваш язык ? вы разрабатываете пакет VS? VS Addin? или какой-то внешний-VS-инструмент? –

+0

@SimonMourier Написание VS-пакета в C# –

+0

Я создал [предложение для публикации, чтобы добавить это в официальный API] (http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/7555128-extend-visual -studio-api-with-a-method-to-set-star) –

ответ

4

Я не думаю, что это документально и доступно официально, но вот некоторая информация:

  • Это хранится в solution's .SUO file при помощи встроенного в пакет Visual Studio. Файл SUO имеет формат хранения OLE. Вы можете просмотреть его с помощью такого инструмента, как OpenMCDF (у него есть образец проводника). В этом файле вы увидите поток с именем «SolutionConfiguration», который содержит токен dwStartupOpt, а затем информацию, которую вы ищете. Сам поток имеет собственный двоичный формат.

  • Такая же информация доступна в пределах ВС через IVsPersistSolutionProps Interface. Вам нужно получить указатель на него одного из загруженных пакетов (например, перечисляя список пакетов с помощью. Один пакет IVsShell.GetPackageEnum Method будет поддерживать интерфейс IVsPersistSolutionProps с «SolutionConfiguration» потока.

Однако, независимо от метод, который вы выберете, в конечном итоге вы проанализируете поток «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? –

+0

Вам необходимо сохранить решение, чтобы убедиться, что изменения в suo совершают –

+0

Это решение отлично подходит для полной структуры, но, похоже, не работает на компактной основе. 'BitConverter.ToInt32 (байты, i + tokenBytes.Length + 2)' равно 0 для всех команд. любое обходное решение для этой работы для компактных рамок? – user678229