четверг, 17 мая 2012 г.

Двойная буферизация в .NET

Существует два способа двойной буферизации для отрисовки WinForms контролов: автоматический и ручной.

Для того чтобы включить автоматическую двойную буферизацию, нужно всего лишь для контрола установить свойство DoubleBuffered = true или вызвать SetStyle(ControlStyles.OptimizedDoubleBuffer, true).

Для ручного же управления двойной буферизацией Framework .NET включает 3 класса:
BufferedGraphicsManager содержит одно статическое свойство Current, которое возвращает объект BufferedGraphicsContext для текущего домена приложения.* Для приложений активно использующих анимацию может быть эффективнее создать новый экземпляр BufferedGraphicsContext вместо того чтобы использовать BufferedGraphicsManager.Current. В этом случае по завершению работы с объектом, его необходимо уничтожить явным образом:
BufferedGraphicsContext myContext = new BufferedGraphicsContext();
// использование
myContext.Dispose();
или
using(BufferedGraphicsContext myContext = 
    new BufferedGraphicsContext()) {
// использование
}
BufferedGraphicsContext имеет один метод Allocate и одно свойство MaximumBuffer
Allocate(Graphics targetGraphics, Rectangle targetRectangle) -- создает новый BufferedGraphics на основе переданного через параметр targetGraphics размером targetRectangle.

Как работает метод Allocate

Исходный код метода:
    public BufferedGraphics Allocate(Graphics targetGraphics, Rectangle targetRectangle)
    {
      if (this.ShouldUseTempManager(targetRectangle))
        return this.AllocBufferInTempManager(targetGraphics, IntPtr.Zero, targetRectangle);
      else
        return this.AllocBuffer(targetGraphics, IntPtr.Zero, targetRectangle);
    }
Внутри метода происходит проверка MaximumBuffer и targetRectangle (методом ShouldUseTempManager**). Если требуемый размер полотна превышает заданный полем MaximumBuffer , то вызывается метод AllocBufferInTempManager. Этот метод внутри себя создает новый экземпляр класса BufferedGraphicsContext, вызывает у него метод AllocBuffer и возвращает полученный от него объект класса BufferedGraphics.***
Если же targetRectangle не превышает MaximumBuffer, то тогда временный объект не создаётся, а метод AllocBuffer вызывается непосредственно у текущего объекта (т.е. у самого себя).
Метод AllocBuffer внутри себя вызывает метод CreateBuffer, для создания нового экземпляра Graphics, оборачивает его в BufferedGraphics и сохраняет в переменную объекта buffer. Эта переменная используется для того, чтобы в дальнейшем при уничтожении экземпляра BufferedGraphicsContext (методом Dispose), уничтожить связанный с ним экземпляр BufferedGraphics.

Стоит заметить что объект BufferedGraphics также хранит в себе ссылку на создавший его BufferedGraphicsContext в закрытой переменной.

---------------------------------------------------------------


*  Код класса BufferedGraphicsManager выглядит следующим образом:
  public sealed class BufferedGraphicsManager
  {
    private static BufferedGraphicsContext bufferedGraphicsContext;
    public static BufferedGraphicsContext Current
    {
      get
      {
        return BufferedGraphicsManager.bufferedGraphicsContext;
      }
    }

    static BufferedGraphicsManager()
    {
      AppDomain.CurrentDomain.ProcessExit += 
          new EventHandler(BufferedGraphicsManager.OnShutdown);

      AppDomain.CurrentDomain.DomainUnload +=
          new EventHandler(BufferedGraphicsManager.OnShutdown);
 
      // В СТАТИЧЕСКОМ КОНСТРУКТОРЕ ПРОСТО СОЗДАЕТСЯ НОВЫЙ
      // НОВЫЙ ОБЪЕКТ BufferedGraphicsContext
      BufferedGraphicsManager.bufferedGraphicsContext = 
          new BufferedGraphicsContext();
    }

    private BufferedGraphicsManager()
    {
    }

    [PrePrepareMethod]
    private static void OnShutdown(object sender, EventArgs e)
    {
      BufferedGraphicsManager.Current.Invalidate();
    }
  }

** Исходный код метода ShouldUseTempManager():
    private bool ShouldUseTempManager(Rectangle targetBounds)
    {
      return targetBounds.Width * targetBounds.Height > this.MaximumBuffer.Width * this.MaximumBuffer.Height;
    }
*** При этом созданный внутри метода AllocBufferInTempManager экземпляр BufferedGraphicsContext не уничтожается, ответственность за его уничтожение ложится на пользователя получившего объект BufferedGraphics, который хранит обратную ссылку, при уничтожении которого и уничтожается породиший его объект BufferedGraphicsContext. Как это работает? Исходный код метода AllocBufferInTempManager:
    private BufferedGraphics AllocBufferInTempManager(Graphics targetGraphics, IntPtr targetDC, Rectangle targetRectangle)
    {
      BufferedGraphicsContext bufferedGraphicsContext = (BufferedGraphicsContext) null;
      BufferedGraphics bufferedGraphics = (BufferedGraphics) null;
      try
      {
        // Создаем новый "временный" контент
        bufferedGraphicsContext = new BufferedGraphicsContext();
        if (bufferedGraphicsContext != null) //  Всегда ДА
        {
          // Создаем в нем буферизированное полотно, которое внутри себя (переменная context) хранит 
          // ссылку на него же
          bufferedGraphics = bufferedGraphicsContext.AllocBuffer(targetGraphics, targetDC, targetRectangle);

          // ВОТ, этот флаг указывает, что объект bufferedGraphics 
          // при своем уничтожении должен уничтожить породившего его
          // bufferedGraphicsContext:
          bufferedGraphics.DisposeContext = true; 
        }
      }
      finally
      {
        // В нормальной ситуации это условие не выполняется
        if (bufferedGraphicsContext != null 
            && (bufferedGraphics == null 
                || bufferedGraphics != null && !bufferedGraphics.DisposeContext))
          bufferedGraphicsContext.Dispose();
      }
      return bufferedGraphics;
    }

вторник, 15 мая 2012 г.

Выход из .NET программы

В WinForms приложениях для завершения работы программы существует метод Application.Exit(). Для того чтобы немедленно завершить работу консольного приложения, нужно выполнить следующую команду:
Environment.Exit(0);
Метод Environment.Exit(int exitCode) является аналого сишной функции exit(int code).

воскресенье, 6 мая 2012 г.

Ввод строки с консоли в Java

Запрос ввода строки с консоли в Java выполняет следующий фрагмент кода:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = in.readLine();
В C# ввод строки с консоли реализуется так:
Console.ReadLine();

Запрос .NET программой прав администратора при запуске

Для того чтобы при запуске программы на C# выводился запрос на получение прав администратора нужно:
Добавить в проект Visual Studio файл монифеста Project->New Item->Application Manifest File
В добавившемся файле заменить строку:
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
на следующую:
<requestedExecutionLevel level="requireAdministrator" uiaccess="false" />
После этого рядом с иконкой программы появится картинка щита, указывающая что запуск программы требует прав администратора.

пятница, 4 мая 2012 г.

Apache + PHP + PostgreSQL

В Windows может быть недостаточно подключить к PHP расширение php_pgsql.dll для того чтобы коннект к базе работал. В случае если при попытке подключиться в PostgreSQL, PHP выдает одну из следующих ошибок:
  • Call to undefined function pg_connect();
  • PDOException: could not find driver,
убедитесь что правильно настроили расширение php_pgsql:
  • добавили или раскоменторовали строки extension=php_pgsql.dll и extension=php_dbo_pgsql.dll в файле php.ini;
  • перезагрузке веб-сервер.
Проверьте выводит ли информацию о расширении phpinfo(), если нет - расширение не загрузилось.
В этом случае в конфигурационный файл httpd.conf нужно добавить строку:
LoadFile "C:\Program Files\PostgreSQL\9.1\bin\libpq.dll"
и перезагрузить веб-сервер.

вторник, 10 апреля 2012 г.

C#: Чтение файла без блокировки

Для того чтобы прочиталь файл без блокировки его на запись и удаление другими процессами, нужно открыть его следующим образом:
FileStream stream = new FileStream(pathName, FileMode.Open, 
FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);

суббота, 31 марта 2012 г.

Массивы PHP и SplFixedArray

Оказывается, под любой элемент массива в PHP выделяется 144 байта. Почему, написано в этой статье http://habrahabr.ru/post/141093/. Для того чтобы сэкономить оперативную память при работе с большими массивами, можно использовать класс SplFixedArray. При использовании этого класса, можно экономить 88 байт на элемент (под каждый элемент выделяется всего 56 байт:).