У меня есть приложение для точек продаж на основе MFC, которое будет отображать серию изображений на дисплее, обращенном к клиенту, пока оператор звонит.
Для обработки представления изображений я создаю окно, содержащее элемент управления CStatic
, который отображается на мониторе, обращенном к клиенту. Я использую класс CImage
для загрузки изображения из файла на диске, а затем устанавливаю изображение в элемент управления CStatic
.
Прототип с жестко закодированными именами изображений выглядит так:
CImage CImg;
HRESULT hr;
CONST TCHAR *px;
// determine the image to display based on the current OEP group number being processed.
switch (m_aszCurrentOEPGroup) {
case 16:
hr = CImg.Load( px = PifGetFilePath (_T("PlatedSandwiches.png"), "b", NULL) );
break;
case 20:
hr = CImg.Load( px = PifGetFilePath (_T("modem_after20.png"), "b", NULL) );
break;
default:
hr = CImg.Load( px = PifGetFilePath (_T("modem_after00.png"), "b", NULL) );
break;
}
if( m_OEPCustDisplayImage.GetBitmap( ) == NULL )
m_OEPCustDisplayImage.SetBitmap( HBITMAP(CImg) );
Приведенный выше код принадлежит классу, производному от CWnd
и содержит переменную-член m_OEPCustDisplayImage
, которая определена как CStatic m_OEPCustDisplayImage;
. И, глядя на это, мне действительно нужно проверить переменную результата hr
на предмет того, работает ли Load()
. И, похоже, Load()
может выдать исключение, поэтому я должен учитывать это.
Приведенный выше исходный код находится в функции члена класса, которая вызывается для вызова окна отображения изображения, обращенного к клиенту, загрузки требуемого изображения из файла в определенной папке и затем установки изображения в CStatic
для отображения изображение.
Во всплывающем окне дисплея код фактически выполняет ShowWindow(SW_SHOW)
при отображении изображения и ShowWindow(SW_HIDE)
при нажатии или удалении отображаемого изображения.
Этот исходный код отображает изображения, как и ожидалось.
Однако меня беспокоит время жизни ресурса, особенно загруженных изображений. Я не хочу создавать утечку памяти.
Поскольку окно дисплея, обращенное к клиенту, создается один раз, а затем существует до выхода из приложения, деструктор для окна не является механизмом управления жизненным циклом ресурса.
CImage
используется для загрузки изображения из файла, и, поскольку это локальный объект, при возврате функции его деструктор будет вызван для очистки.
Я пытаюсь понять фактический жизненный цикл изображения и что происходит с ресурсами, используемыми на следующих этапах:
- определяется
CImage
- изображение загружается из файла в
CImage
с использованием load()
- изображение помещается в
CStatic
с помощью SetBitmap()
- объект
CImage
выходит из области видимости, а его деструктор называется
- изображение в
CStatic
заменяется новым изображением или имеет значение NULL
Некоторые вопросы жизненного цикла, которые приходят на ум:
- когда
SetBitmap()
используется для помещения изображения в CStatic
, сделана новая копия изображения?
- когда деструктор
CImage
срабатывает, когда объект выходит из области видимости, очищаются ли ресурсы изображения? (Я бы так и ожидал)
- что мне нужно делать с изображением в
CStatic
при замене его на новое изображение?
В настоящее время в всплывающем коде окна, обращенного к клиенту, я просто устанавливаю изображение в CStatic
на NULL с m_OEPCustDisplayImage.SetBitmap(NULL);
, однако мне кажется, что это будет утечка ресурсов. Что происходит с изображением, которое отображалось в CStatic
?
Поведение, которое я наблюдаю в приложении, заключается в том, что запрашиваемое изображение загружается и отображается с помощью элемента управления CStatic
, размер которого изменяется до размера, равного CWnd
, содержащему его, с использованием ::SetWindowPos()
. Окно отображается и не отображается, как ожидалось. Изображения меняются, как и ожидалось.
Поскольку изображение отображается в CStatic
до тех пор, пока следующее действие оператора не заставит отображаемое окно не отображаться, может показаться, что метод SetBitmap()
в CStatic
сделал свою собственную копию изображения.
Нужно ли делать что-то вроде следующего, как часть окна пользователя, отображаемого и всплывающего?
CGdiObject myTempObj(m_OEPCustDisplayImage.SetBitmap(NULL));
и затем позволить этому объекту выйти из области видимости, чтобы очистить любые ресурсы?
Я смотрел на окно «Вывод» Visual Studio после выхода из приложения и не вижу никаких признаков того, что эти образы вызывают утечку ресурсов из-за жалоб на отладку среды выполнения Visual Studio C ++, однако я не уверен, что утечка ресурсов этот тип, если он существует, будет определен средой выполнения C ++.
Приложение A: Изменения в источнике для исправления утечек ресурсов, повторное тестирование
При поиске того, не были ли изображения выпущены должным образом, я не смог определить это из-за другого источника утечек ресурсов, в результате которого я увидел в диспетчере задач Windows, что количество объектов GDI увеличивалось, однако я был неуверенный относительно того, что было из-за изображений и что было из-за утечки других ресурсов, указатели кнопок не были должным образом удалены.
Я мог видеть в окне «Вывод» Visual Studio, поскольку приложение прекратило список утечек указателей кнопок, а также их расположение. Однако я не увидел никаких признаков утечки ресурсов изображения.
Я отследил причину утечки ресурса указателей кнопок и исправил ее.
Я также изменил исходный код для изображений следующим образом.
В коде, который обрабатывает отображение или всплывающее окно в окне с CStatic
отображением изображения, у меня есть следующий исходный код очистки, который перемещает владельца изображения с CStatic
на CGdiObject
, который затем переходит удаление изображения из области видимости:
CGdiObject myObj;
myObj.Attach(m_OEPCustDisplayImage.SetBitmap(NULL));
Затем в коде, который загружает сначала объект CImage
с изображением из файла, а затем загружает изображение в объект CStatic
, я внес следующие изменения, используя метод Detach()
CImage
с SetBitmap()
из CStatic
для передачи права собственности на изображение в CStatic
:
if( ! FAILED(hr)) {
if (m_OEPCustDisplayImage.GetBitmap( ) == NULL )
m_OEPCustDisplayImage.SetBitmap( CImg.Detach() );
else {
CGdiObject myObj;
myObj.Attach(m_OEPCustDisplayImage.SetBitmap(CImg.Detach()));
}
}
После этих двух попыток я смог контролировать количество объектов GDI с помощью диспетчера задач Windows, работая с приложением в режиме отладки. Я мог видеть увеличение количества объектов GDI по мере того, как отображались окна с изображениями, а затем после того, как они не отображались и выдавались, счет возвращался к счетчику до того, как функциональность была запущена.
Установка точки останова таким образом, чтобы код для передачи права собственности на изображение в CGdiObject с SetBitmap(NULL)
был заменен просто вызовом SetBitmap(NULL)
, что привело к увеличению числа объектов GDI, как и ожидалось.
Затем я перекомпилировал исправленный источник как сборку Release и применил приложение в этой области функциональности, наблюдая за отображением диспетчера задач Windows количества объектов GDI. Поведение счетчика должно было увеличиваться при отображении окон с изображениями и уменьшаться до базового значения, когда изображения не отображались.