Число созданий CompatibleBitmap под w98 сильно ограничено?
От: elich  
Дата: 21.01.04 20:29
Оценка:
Всем привет.

Обрисую ситуацию:
Есть класс списка, у которого для прорисовки каждой строки вызывается примерно такой код:
[quote]
...
CBitmap bmpSrc;
if (bmpSrc.CreateCompatibleBitmap(&dc, bitmap.bmWidth,
bitmap.bmHeight) == FALSE)
{
TRACE("ОШИБКА: CompatibleBitmap не создан");
return;
};
...
[/quote]
То есть при прорисовке строки каждый раз создается CompatibleBitmap.

Как возникает глюк:
Запускаю прогу, начинаю ездить по списку туда-сюда, взад-вперед, вверх, вниз, из конца, в начало, и снова туда-сюда, взад-вперед, ...
Ездю, ездю, и вдруг, в Output вижу эту чертову строку. Самое смешное, что первое время езденья, достаточно долгое, она не появляется.

Подробности:
Ездить приходится достаточно долго, но гораздо меньше, чем обычно ездят по спискам во время юзанья проги. По ощущениям, раз 1000 приходится перерисовывать строки в списке, то есть раз 1000 создается CompatibleBitmap. под XP никаких проблем не возникает, данная проблема возникает только под w98. Такое ощущение, что в ней ограничено число созданий CompatibleBitmap. В параметрах к Create поступает обычный контекст рисования. Когда возникает ошибка, специльно проверил: требуемая величина битмапа 14x14 пикселей (поступает в параметрах к Create). GetLastError() возвращает 0 (Это Вам не XP, а w98). Все, что сказано в MSDN — что размер битмапа не может превышать 16мб. А GetLastErorr под w98 ничерта не возвращает. Вот и не знаю, что тут делать. Может кто встречался с подобной ситуацией, разъяснит, как починить?

Делать Create в каждом рисовании есть большая необходимость. Кто-то может сказать — создай в конструкторе контрола, и не создавай каждый раз при рисовании строки. Дак вот, хотелось бы все-таки создавать каждый раз при рисовании строки.
Re: Число созданий CompatibleBitmap под w98 сильно ограничен
От: Sergey Россия  
Дата: 22.01.04 10:42
Оценка:
Hello, elich!
You wrote on Wed, 21 Jan 2004 20:29:04 GMT:

e> Всем привет.


e> Обрисую ситуацию:

e> Есть класс списка, у которого для прорисовки каждой строки вызывается
e> примерно такой код: [quote]
e> ...
e> CBitmap bmpSrc;
e> if (bmpSrc.CreateCompatibleBitmap(&dc, bitmap.bmWidth,
e> bitmap.bmHeight) == FALSE)
e> {
e> TRACE("ОШИБКА: CompatibleBitmap не создан");
e> return;
e> };
e> ...
e> [/quote]
e> То есть при прорисовке строки каждый раз создается CompatibleBitmap.

А что с ним дальше делается? Где он убивается?

e> Подробности:

e> Ездить приходится достаточно долго, но гораздо меньше, чем обычно ездят
e> по спискам во время юзанья проги. По ощущениям, раз 1000 приходится
e> перерисовывать строки в списке, то есть раз 1000 создается
e> CompatibleBitmap. под XP никаких проблем не возникает, данная проблема
e> возникает только под w98. Такое ощущение, что в ней ограничено число
e> созданий CompatibleBitmap. В параметрах к Create поступает обычный
e> контекст рисования. Когда возникает ошибка, специльно проверил:
e> требуемая величина битмапа 14x14 пикселей (поступает в параметрах к
e> Create). GetLastError() возвращает 0 (Это Вам не XP, а w98). Все, что
e> сказано в MSDN — что размер битмапа не может превышать 16мб. А
e> GetLastErorr под w98 ничерта не возвращает. Вот и не знаю, что тут
e> делать. Может кто встречался с подобной ситуацией, разъяснит, как
e> починить?

Скорее всего ты эту битмапу после использования из контекста не удаляешь. Под XP посмотри в task manager'е — количество GDI Objects для твоего процесса растет? Если да, ищи где у тебя текут ресурсы.

Best regards,
Sergey.
Posted via RSDN NNTP Server 1.8 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[2]: Число созданий CompatibleBitmap под w98 сильно ограни
От: elich  
Дата: 22.01.04 18:24
Оценка:
Здравствуйте, Sergey, Вы писали:

Спасибо за внимание, Сергей. А то уже думал, что мой вопрос как раз оказался таким, что по его примеру можно написать статью на подобии Как неправильно задавать вопросы...

S>А что с ним дальше делается? Где он убивается?


Убивается он в деструкторе CBitmap::~CBitmap(). Точнее не в его собственном, а в виртуальном деструкторе базового класса CGdiObject. Иерархия там такова: CBitmap<-CGdiObject<-CObject — все это из MFC.

S>Скорее всего ты эту битмапу после использования из контекста не удаляешь. Под XP посмотри в task manager'е — количество GDI Objects для твоего процесса растет? Если да, ищи где у тебя текут ресурсы.


Я как раз так сначала и подумал — набрел на тот самый деструктор — в нем нашел ::DeleteObject(m_hHandle).
Совсем забыл, что в XP можно столбцы менять — спасибо за это. Для отладки как раз штука не зменимая. Там число GDI объектов неизменно — остается на уровне 40 штук. Незнаю даже, что и делать. Хоть бы какую-нить зацепку получить. Мне все-таки кажется, что под w98 ограничено число созданий (возможно, где-нить про это читал, но сейчас не помню точно, так это или нет).

И еще кое-что добавлю, может будет полезно знание этих обстоятельств:
Этот код, что я приводил выше — отвечает за отрисоку изображений. Когда возинкает ошибка, в моем списке просто вместо изображений возникают "пустые места". Вдобавок, эти места возникают на тулбарах в других открытых приложениях, даже на меню ПУСК — врезка слева — и та не прорисовывается. Чтобы это убралось — приходится resatartить постоянно — что также не способствует быстрой и эффективной отладке.

Вот и совсем растерялся. Даже подумывал, до того, как обратился сюда, написать в релизе проги, что w98 не supported.
Но если ответа не найду, видимо, так и придется делать.

S>Best regards,

S> Sergey.
Re[3]: Число созданий CompatibleBitmap под w98 сильно ограни
От: elich  
Дата: 22.01.04 21:48
Оценка:
Вот пример фунции, которая у меня дает сбой. Эту функцию построил на основании своей, которую немного сократил. Поэтому, в приведенном ниже коде минимум половина может показаться просто не нужной для достижения того же результата. Эту функцию я вызываю по таймеру через каждые 30 мс. Приходится жадть от 100 — до 140 секунд на моей машине. Еще более усугубляет ситуацию то, что w98 вылетает после появления сбоя, поэтому приходится каждый раз делать restart. Еще более ее усугубляет и то, что Visual C 7.0 под w98 поставить нельзя, поэтому приходится вместо отладчика использовать MessageBoxы и прочие пакости. В общей сложности на этот баг я потратил уже 2 дня. До сих пор не разобрался, по какой причине не работает. Блин, Вы не представляете, как бы я был Вам признателен, елси бы хотя бы малюсенькую зацепочку кто подсказал. Для тех, кто проявит интерес, гораздо более обычного, я могу выслать demo-проект, Visual C ++ 7.0. В нем этот сбой наглядно наблюдается — специально для этого делал диалог, в котором стоит таймер, статус вызова функции и т.п. Код хорошо прокомментирован, писал как раз для Вас. Все, что для этого надо — послать запрос мне по мылу: elich@ezmail.ru с указанием того, нужно ли exe файл, который чуть менее 200кб, или только исходник выслать. Обязуюсь выслать, очень скоро. Спецально для этого буду постонно проверять почту в течение 23 января, в надежде, что хоть кто-нибудь ответит на мой душевный крик. ВНИМАНИЕ: баг локализован. Т.е. известна строка, на которой происходит ошибка. Не известно лишь то, ПОЧЕМУ ОНА ПРОИСХОДИТ!!!. А вот и сама фукция для тех, кому не нужны мои старания...

int TestDraw1(CDC& dc, HBITMAP hbmPaint, CRect rcPaint)
{
    // Прозрачный цвет
    COLORREF clrTrans = RGB(255, 255, 255);

    // Создаем контекст в памяти
    CDC dcMem;
    dcMem.CreateCompatibleDC(&dc);
    if (!dcMem.m_hDC) return 1;
    
    // Создаем битмап для выбора в контесте и выбираем его
    CBitmap bmpSrc;
    if (bmpSrc.CreateCompatibleBitmap(&dc, bitmap.bmWidth, 
        bitmap.bmHeight) == FALSE) return 2;
    CBitmap* pOld = dcMem.SelectObject(&bmpSrc);

    // Получаем параметры битмапа, который необходимо отрисовать
    BITMAP bitmap;
    ::ZeroMemory(&bitmap, sizeof(bitmap));
    if (::GetObject(hbmPaint, sizeof(BITMAP), &bitmap) == FALSE) return 3;

    // Рисуем на холсте в памяти сначала, получаем прозрачный цвет
    if (dcMem.DrawState(CPoint(0, 0), 
        CSize(bitmap.bmWidth, bitmap.bmHeight), hbmPaint, 0) == FALSE) 
        return 4;

    // Рассчитываем прямоугольник отрисовки
    CRect rcBitmap(rcPaint.left, rcPaint.top, 
        rcPaint.left + bitmap.bmWidth, rcPaint.top + bitmap.bmHeight);
    
    // Вывод
    if (dc.TransparentBlt(rcBitmap.left, rcBitmap.top, 
        bitmap.bmWidth, bitmap.bmHeight, &dcMem, 0, 0, bitmap.bmWidth, bitmap.bmHeight, clrTrans) == FALSE)
            return 5;

    // Освобождаем ресурсы
    dcMem.SelectObject(pOld);
    if (bmpSrc.DeleteObject() == FALSE)
        return 6;

    return 0;
}
Re[3]: Число созданий CompatibleBitmap под w98 сильно ограни
От: Аноним  
Дата: 23.01.04 00:09
Оценка:
E>... Когда возинкает ошибка, в моем списке просто вместо изображений возникают "пустые места". Вдобавок, эти места возникают на тулбарах в других открытых приложениях, даже на меню ПУСК — врезка слева — и та не прорисовывается. Чтобы это убралось — приходится resatartить постоянно — что также не способствует быстрой и эффективной отладке.

Очень похоже на утечку ресурсов GDI.
Проверить это для Win98 можно с помощью утилиты GDIUsage (Есть в MSDN в статье "Resource Leaks: Detecting, Locating, and Repairing Your Leaky GDI Code") С ее помощью можно определить какой именно ресурс "утекает" (DC, brush и т.д.). Если это действительно утечка ресурсов GDI, то нужно проверить:
— все места в программе в которых в ходе отрисовки используется такие объекты GDI (освобождаются они или нет) ;
— чтобы перед освобождением объекта GDI он не был выбран в DC.

Удачи.
Re[4]: Число созданий CompatibleBitmap под w98 сильно ограни
От: elich  
Дата: 23.01.04 07:15
Оценка:
А>Очень похоже на утечку ресурсов GDI.
А>Проверить это для Win98 можно с помощью утилиты GDIUsage (Есть в MSDN в статье "Resource Leaks: Detecting, Locating, and Repairing Your Leaky GDI Code") С ее помощью можно определить какой именно ресурс "утекает" (DC, brush и т.д.). Если это действительно утечка ресурсов GDI, то нужно проверить:
А> — все места в программе в которых в ходе отрисовки используется такие объекты GDI (освобождаются они или нет) ;
А> — чтобы перед освобождением объекта GDI он не был выбран в DC.

А>Удачи.


Действительно, очень похоже. Но я специально "отделил" "гнилую" функцию от основной программы и вставил в demo-проект, в котором, кроме нее, ничего не используется. Код этой фунции приводил выше — в нем все освобождается и удаляется (на мой взгляд). Но баг все-равно происходит. Всеръез подумываю написать гневное письмо в адрес Microsoft
Re[4]: Число созданий CompatibleBitmap под w98 сильно ограни
От: sokolprog Россия  
Дата: 23.01.04 08:16
Оценка:
Здравствуйте, elich, Вы писали:

>ВНИМАНИЕ: баг локализован. Т.е. известна строка, на которой происходит ошибка. Не >известно лишь то, ПОЧЕМУ ОНА ПРОИСХОДИТ!!!. А вот и сама фукция для тех, кому не нужны >мои старания...


Скорее всего дело не в TestDraw. Если ты запускаешь TestDraw по таймеру, значит используется что-то вроде:

CDC* pDC=GetDC();
TestDraw(*pDC, hBitmap, rect);
ReleaseDC(pDC); // эта строка у тебя присутствует?


Может ты просто не освобождаешь контекст?
Re[5]: Число созданий CompatibleBitmap под w98 сильно ограни
От: elich  
Дата: 23.01.04 09:34
Оценка:
S>Скорее всего дело не в TestDraw. Если ты запускаешь TestDraw по таймеру, значит используется что-то вроде:

S>
S>CDC* pDC=GetDC();
S>TestDraw(*pDC, hBitmap, rect);
S>ReleaseDC(pDC); // эта строка у тебя присутствует?
S>


S>Может ты просто не освобождаешь контекст?


Глядя на код TestDraw, я тоже думаю, вот уже в 10000 раз, что дело не в ней. Убедившись, что больше нигде, кроме как в ней, не используются ресурсы, снова и снова возвращаюсь к ней и опять смотрю на нее, но уже в 10001 раз. Смотрю, и плачу... Плачу, и смотрю...

Вызываю я ее по таймеру примерно так:

void CDlg::OnTimerEvent(UINT nIDEvent)
{
    RedrawWindow(CRect(0, 0, 30, 30));
}


BOOL CDlg::OnEraseBckgnd(CDC* pDC)
{
    CDlg::OnEraseBckgnd(pDc);
    TestDraw(*pDC, m_bmb, CRect(0, 0, 30, 30));
    return TRUE;
}
Re[6]: Число созданий CompatibleBitmap под w98 сильно ограни
От: .h  
Дата: 23.01.04 10:08
Оценка: 9 (1)
Здравствуйте, elich, Вы писали:
Возможно причина в этом: Q300555 — TransparentBlt Leaks Memory in Msimg32.dll
Re[6]: Число созданий CompatibleBitmap под w98 сильно ограни
От: sokolprog Россия  
Дата: 23.01.04 10:18
Оценка:
Здравствуйте, elich, Вы писали:

>Вызываю я ее по таймеру примерно так:

>BOOL CDlg::OnEraseBckgnd(CDC* pDC)
>{
> CDlg::OnEraseBckgnd(pDc);
> TestDraw(*pDC, m_bmb, CRect(0, 0, 30, 30));
> return TRUE;
>}

Прикольно!
В CDlg::OnEraseBckgnd() вызывается CDlg::OnEraseBckgnd()! Обычно в результате этого получается stack overflow.
Re[4]: Число созданий CompatibleBitmap под w98 сильно ограни
От: LehiZ  
Дата: 23.01.04 10:39
Оценка:
попробуй добавить dcMem.DeleteDC() в конец функции
Re[4]: Число созданий CompatibleBitmap под w98 сильно ограни
От: Sergey Россия  
Дата: 23.01.04 11:39
Оценка:
Hello, elich!
You wrote on Thu, 22 Jan 2004 21:48:50 GMT:


e>
 e> int TestDraw1(CDC& dc, HBITMAP hbmPaint, CRect rcPaint)
 e> {
 e>  // Прозрачный цвет
 e>  COLORREF clrTrans = RGB(255, 255, 255);

 e>  // Создаем контекст в памяти
 e>  CDC dcMem;
 e>  dcMem.CreateCompatibleDC(&dc);
 e>  if (!dcMem.m_hDC) return 1;

 e>  // Создаем битмап для выбора в контесте и выбираем его
 e>  CBitmap bmpSrc;
 e>  if (bmpSrc.CreateCompatibleBitmap(&dc, bitmap.bmWidth, 
 e>   bitmap.bmHeight) == FALSE) return 2;
 e>  CBitmap* pOld = dcMem.SelectObject(&bmpSrc);

 e>  // Получаем параметры битмапа, который необходимо отрисовать
 e>  BITMAP bitmap;
 e>  ::ZeroMemory(&bitmap, sizeof(bitmap));
 e>  if (::GetObject(hbmPaint, sizeof(BITMAP), &bitmap) == FALSE) return 3;

 e>  // Рисуем на холсте в памяти сначала, получаем прозрачный цвет
 e>  if (dcMem.DrawState(CPoint(0, 0), 
 e>   CSize(bitmap.bmWidth, bitmap.bmHeight), hbmPaint, 0) == FALSE) 
 e>   return 4;

 e>  // Рассчитываем прямоугольник отрисовки
 e>  CRect rcBitmap(rcPaint.left, rcPaint.top, 
 e>   rcPaint.left + bitmap.bmWidth, rcPaint.top + bitmap.bmHeight);

 e>  // Вывод
 e>  if (dc.TransparentBlt(rcBitmap.left, rcBitmap.top, 
 e>   bitmap.bmWidth, bitmap.bmHeight, &dcMem, 0, 0, bitmap.bmWidth,
 e> bitmap.bmHeight, clrTrans) == FALSE)
 e>    return 5;

 e>  // Освобождаем ресурсы
 e>  dcMem.SelectObject(pOld);
 e>  if (bmpSrc.DeleteObject() == FALSE)
 e>   return 6;

 e>  return 0;
 e> }
 e>


Ну, течь оно может если DrawState или TransparentBlt вернул 0. Насколько, я понял, возвращаемое функцией значение не контролируется?
Да, а то, что я говорил насчет GDI объектов в XP — полумеры, утечки pOld оно показать вроде бы не должно.

Best regards,
Sergey.
Posted via RSDN NNTP Server 1.8 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[7]: Число созданий CompatibleBitmap под w98 сильно ограни
От: elich  
Дата: 23.01.04 12:28
Оценка:
>>Вызываю я ее по таймеру примерно так:
>>BOOL CDlg::OnEraseBckgnd(CDC* pDC)
>>{
>> CDlg::OnEraseBckgnd(pDc);
>> TestDraw(*pDC, m_bmb, CRect(0, 0, 30, 30));
>> return TRUE;
>>}

S>Прикольно!

S>В CDlg::OnEraseBckgnd() вызывается CDlg::OnEraseBckgnd()! Обычно в результате этого получается stack overflow.

Конечно! Писал я это из башки, после двухдневного секса с этой функцией. Я тут и не такое писал...
Имелось ввиду __super::OnEraseDlg(pDC);, конечно.
Re[5]: Число созданий CompatibleBitmap под w98 сильно ограни
От: elich  
Дата: 23.01.04 12:34
Оценка:
Здравствуйте, Sergey, Вы писали:

S>Ну, течь оно может если DrawState или TransparentBlt вернул 0. Насколько, я понял, возвращаемое функцией значение не контролируется?

S>Да, а то, что я говорил насчет GDI объектов в XP — полумеры, утечки pOld оно показать вроде бы не должно.

Конечно, в свободное плавание эту функцию выпускать нельзя. Ее я составил из другой, которая гораздо длиннее, но с контролем всех ошибок, разумеется. Поставь тут ту длинную функцию, желающих посмотреть на нее резко бы поубавилось. Результат же этой функции контролируется в demo-проекте, который я все хочу кому-нибудь навязать Как только она возвращает не 0, фиксируется время, в течении которого она завалилась, и код ошибки. У меня он такой, что валится TransparentBlt. После этого ее вызов по таймеру прекращается. Поэтому если и возможен утек по этой вине — то в размере всего 2 хэндлов — и уже после того, как она завалилась.
Но согласен, что за образец эту функцию брать, конечно, нельзя. Повторюсь — это только для того, чтобы показать, в чем проблема.
Re[7]: Число созданий CompatibleBitmap под w98 сильно ограни
От: elich  
Дата: 23.01.04 12:37
Оценка:
Здравствуйте, .h, Вы писали:

.h>Здравствуйте, elich, Вы писали:

.h>Возможно причина в этом: Q300555 — TransparentBlt Leaks Memory in Msimg32.dll

Возможно, у меня как раз валится эта функция — error code от функции 5. Сегодня вечером обязательно посмотрю, а сейчас убегаю...
Re[7]: Число созданий CompatibleBitmap под w98 сильно ограни
От: elich  
Дата: 23.01.04 17:29
Оценка:
Здравствуйте, .h, Вы писали:

.h>Возможно причина в этом: Q300555 — TransparentBlt Leaks Memory in Msimg32.dll


Во блин, в этом причина и есть. Вот уже 12 минут работает безо всяких сбоев. Я и себе ничего не могу на сайте microsoft.com найти, а ты и другим успеваешь!
Оффиггеннейшее СПАСИБО!

Интересно, кто должен оплачивать те два дня, что я потратил на поиск ошибки по вине Microsoft?
Надо будет предложить им переименовать функцию TransparentBlt() в TransparentBug(). Вооще.
Граждане, запомните эту функцию! И не наступайте на мои грабли, это очень обидно...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.