понедельник, 15 июля 2013 г.

Как узнать, какие версии Framework установлены на компьютере

На днях передо мной встала задача определить программно, какие версии .Net Framework установлены на машине клиента. Покопавшись в реестре, я нашел нужную мне информацию в "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP". Однако, что бы правильно вытащить ее оттуда, пришлось немного повозиться и вылилось в следующий код:
public class NetVersions
{
   public static Collection<Version> InstalledDotNetVersions()
   {
      Collection<Version> versions = new Collection<Version>();
      RegistryKey NDPKey = Registry.LocalMachine.OpenSubKey( @"SOFTWARE\Microsoft\NET Framework Setup\NDP", true );
      if( NDPKey != null )
      {
         string[] subkeys = NDPKey.GetSubKeyNames();
         foreach( string subkey in subkeys )
         {
            GetDotNetVersion( NDPKey.OpenSubKey( subkey ), subkey, versions );
            GetDotNetVersion( NDPKey.OpenSubKey( subkey ).OpenSubKey( "Client" ), subkey, versions );
            GetDotNetVersion( NDPKey.OpenSubKey( subkey ).OpenSubKey( "Full" ), subkey, versions );
         }
      }
      return versions;
   }

   static void GetDotNetVersion( RegistryKey parentKey, string subVersionName, Collection<Version> versions )
   {
      if( parentKey != null )
      {
         string installed = Convert.ToString( parentKey.GetValue( "Install" ) );
         if( installed == "1" )
         {
            string version = Convert.ToString( parentKey.GetValue( "Version" ) );
            if( string.IsNullOrEmpty( version ) )
            {
               if( subVersionName.StartsWith( "v" ) )
                  version = subVersionName.Substring( 1 );
               else
                  version = subVersionName;
            }

            Version ver = new Version( version );

            if( !versions.Contains( ver ) )
               versions.Add( ver );
         }
      }
   }
}
Буду рад, если оно понадобится еще кому-нибудь.

понедельник, 8 июля 2013 г.

Как проверить, что мы в дизайнере (DesignMode) в Windows Form

Задача

Разрабатывая элементы управления в дизайнере Windows Form, нам часто необходимо проверить в каком режиме открыли наш элемент. Ведь в реальном режиме у нас может быть сложная логика чтения информации из БД, подключения в web-службам, взаимодействия с какими-то сторонними компонентами и т. д.. На момент редактирования визуального представления, вся эта логика должна быть отключена. А, для этого, нам необходим какой-то признак, по которому мы сможем отличить дизайнер от реального запуска программы:
if( !DesignMode )
{
    //...
}

Решения

Для начала посмотрим на то, что нам предлагают разработчики от Микрософт. В классе Component имеется свойство DesignMode. "Эврика! Это именно то, что нужно", подумал я. А, прочитав описание, я уж было совсем расслабился и начал писать свой код. Однако, первое же использование моего контроля в дизайнере, ввело меня в недоумение и растерянность - DesignMode был false. Всегда! Покурив форумы, я понял, что это свойство становится true после какого-то там обряда инициализации. Далее шли рассуждения о том, в какой момент времени это свойство показывает правильное значение, а в какой неправильное.. В общем-то рассуждения весьма бредовые, потому что код "иногда работающий правильно" - это как часы которые стоят - показывают правильное время ровно 2 раза в сутки, но толку от этого никакого. Кроме того, как я уже говорил, свойство находится в классе Component, а значит, до него невозможно добраться из вспомогательных классов, не имеющих ссылку на Component, что приводит к ограничениям в рефакторинге. А это значит, что свойство не просто бесполезно, но и вредно.
Продолжая курить форумы, я случайно наткнулся на класс LicenseManager. С его помощью проверку можно осуществить следующим образом:
if( LicenseManager.UsageMode != LicenseUsageMode.Designtime )
{
    //...
}
Однако, тут есть один маленький неприятный нюанс - этот класс писали те же "руки", что и предыдущий, а значит, работает все так же "глючно" и, в самый неожиданный момент, свойство UsageMode может выдать Runtime, не смотря на то, что мы находимся в дизайнере.
Еще одно интересное решение - это проверить название процесса:
if( Process.GetCurrentProcess().ProcessName != "devenv" )
{
    //...
}
Ограничения такого решения видны сразу - оно будет работать только из-под одной среды разработки - Microsoft Visual Studio. Однако, ничего не мешает расширить проверку, если ваша команда использует другие средства. По крайней мере, тут все прозрачно и предсказуемо.
Так как же сделать проверку правильно? Ок. Есть еще один вариант, который мне кажется самым правильным - завести где-то в общей библиотеке статическую переменную DesignMode, которая по умолчанию будет равна true. А, при запуске программы, сбрасывать ее на false. Это решение стабильно, прозрачно и надежно. Но! Это ужасное решение. Дело в том, что библиотека, содержащая такое решение, накладывает ограничения на свое использование -  подключая такую библиотеку к проекту, нам придется вспомнить о нужных строчках и вписать их, а при исключении библиотеки из проекта, строчку придется удалять. Конечно, ничего криминального в этом нет. Однако, стоит нам предположить, что подход со статистическими флажками приемлем, как мы начнем использовать его повсеместно и наши библиотеки приобретут совершенно непредсказуемое поведение - ведь очень скоро уже никто и не вспомнит какие флажки надо прописать, где, когда и сколько их вообще и какие для чего.

Выводы

Я так и не смог найти оптимального решения данной задачи. Сам я использую комбинации приведенных выше подходов, каждый раз вспоминая "добрым словом" авторов свойства DesignMode. Если кто-нибудь нашел решение лучше - буду рад получить обратную связь в комментариях.