30 августа 2006

Контролы от Microsoft снова рулят

Оказывается, нельзя менять стиль стандартного Windows editbox-а. Хотя так естественно было менять стиль multi-line/single-line при обработке WM_SIZE. А не получится.

Такими темпами придется заиспользовтаь что-то типа Scintilla или еще хуже - написать собственный editbox. Давно думаю, что дойдет именно до написания собственного.

Экономия на мелочах?

Некоротые товарищи пытаются сэкономить несколько байт, используя вместо int тип signed char или unsigned char. Их мотивация такая: если у нас числа не больше 127 (255), то зачем использовать четырехбайтный int (на 32 битной платформе), когда можно обойтись всего одним байтом? Экономия - 400%!

Но на деле есть а) выравнивание данных компилятором, б) размер процессорного слова.

Выравнивание может быть настроено на 4 или 8 байт и структура
struct { signed char x, y; } займет 8 или 16 байт, а не 2.

Процессору удобнее общаться с числами определенного размера. 32-битному процессору как правило легче обрабатывать 32-битные данные, для обработки данных меньшего размера скорее всего понадобится столько же такотов, но вероятно потребуются дополнительные префиксы к инструкциям. Например, при вызове функции на стек будет запихиваться 32-разрядное число, хоть значимый там будет всего один байт.

То есть никакой экономии памяти при переводе единичных переменных (не массивов) с int на char скорее всего не будет. Зато возможно будут некоторые проблемы.

Ситуация - чтение данных из текстового потока:
int value;
textstream >> value;


При входном потоке "0" мы получим value == 0. А при замене int на char получим value == 48. Сэкономили несколько байт, а получили лишнюю головную боль.

27 августа 2006

Откуда растут руки у программистов Windows?

Интересно посмотреть в глаза людям, которые писали стандартные контролы в Windows.

Стандартный button позволяет рисовать на кнопке иконку (BS_ICON window style + BM_SETIMAGE message). Все замечательно, пока у вас Windows 2000. Когда у вас Windows XP, то вы понимаете, что кнопка с иконкой не поддерживает темы XP. И поддержку тем можно сделать только вручную. А уж если что-то делать вручную, то лучше уж тогда полностью свою кнопку написать. Чтобы не ожидать очередных подстав со стороны Microsoft.

09 августа 2006

Как закрыть форточку?

WinAPI - вещь для посвященных. Если нужно что-то сделать, мало прочитать документацию (хорошо что хоть она легко доступна). Чтобы что-то сделать правильно, нужно либо долго помучаться, либо перелопатить форумы. Либо и то, и другое.

Задача: закрыть MDI-child окно.
Решение "в лоб": казалось бы, чего проще: DestroyWindow(hWnd)
И даже окно закрывается, на первый взгляд. Но фокус на другое MDI-child окно не переходит, а должен бы. Но это еще не беда. Беда начинается тогда, когда это окно (child) максимизировано. Тогда происходят вообще чудеса: окно закрывается, но от окна в строке меню остаются значки maximize-minimize-close и значок системного меню! Красота, да и только. Если так закрыть несколько окон, то будет полнейший сюрреализм:



Hint: У MDI-child-а обработчик WM_CLOSE по умолчанию закрывает окно (причем делает это грамотно).

Простой вызов SendMessage(hWnd, WM_CLOSE) подходит, но не во всех случаях. Представьте себе, вы пишете обработчик WM_CLOSE, в котором пишете такую конструкцию ;) Да мы просто зациклимся.

В результате у меня получилась подобная конструкция:
bool CMDIFormImpl::Destroy()
{
// This is temporary solution!!!
// todo: replace it by something more suitable

HWND hWnd(m_hWnd);
// call default MDI-child WM_CLOSE handler: it does MDI-destroying better than us
DefWindowProc(WM_CLOSE, 0, 0);
return !::IsWindow(hWnd);
}

01 августа 2006

Убей родителя, и он убъет тебя

В ATL/WTL конструкция delete this; - дело довольно обычное. Так можно в OnFinalMessage() удалить собственный объект.

А теперь представьте ситуацию: WTL-контролы (кнопки, edit box-ы и т.п.) хранятся в контейнере указателей у их Parent-а (диалога, etc.). В своем OnFinalMessage() родитель делает delete this, а его деструктор грохает все объекты WTL-контролов.

Все хорошо до тех пор, пока мы не захотим по нажатию кнопки мыши на контроле закрыть окно родителя. В обработчике контрола по WM_LBUTTONDOWN (скажем, в CControl::OnLButton()) мы вызываем Parent->DestroyWindow() и... падаем по assert-у, так как parent удаляет наш объект, а мы еще не вышли из CControl::OnLButton(), и наш объект еще может понадобится (не в OnLButton(), а в вызвавшем его коде).

Можно, конечно, сделать PostMessage(Parent, WM_CLOSE или WM_DESTROY), и тогда родитель будет удален уже потом, после выхода из CControl::OnLButton(), так как WM_DESTROY дойдет до него не сразу, а просто встанет в очередь. Но иногда нужно грохнуть родителя здесь и сейчас.

Выход из такой ситуации: удалять свой объект должен сам контрол в своем OnFinalMessage(). Правда, родителю не помешает "подчищать" за своими детьми - если контрол был создан как объект, но не успел создасться как окно (в терминах WinAPI) - мало ли по каким причинам - то убить объект контрола должен родитель.