Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.118.9.7] |
|
Сообщ.
#1
,
|
|
|
Здравствуйте!
Хочу, на C++ сделать класс обёртку для значка. Не собираюсь реализовывать весь функционал, лишь необходимый мне минимум. Со значками, я раньше не связывался. Знакомство весьма поверхностное. Есть функция LoadIcon(), которую рекомендуется заменить LoadImage(). Функция LoadImage() загружает значок и возвращает мне хэндл. Который почему-то типа HANDLE., а иконка должна быть типа HICON. Имею ли я право использовать приведение типов? Как сказано в мане, если задать флаг LR_SHARED, хэндл копируется. А значок будет уничтожен, когда он перестанет быть нужным. Ещё в мане сказано, что значок нужно разрушать, когда он не нужен, функцией DestroyIcon(). Так вот, я не очень уверен: в каких случаях мне надо вызывать DestroyIcon()? и есть ли возможность проверить действительность значка? class icon { public: // конструктор icon( HMODULE hModule, LPCTSTR strName, INT cx, INT cy, UINT flags ): _handle( NULL ) { _handle = ::LoadImage( hModule, strName, IMAGE_ICON, cx, cy, flags ); } // деструктор ~icon() { ::DestroyIcon( _handle ); } // возвращает хэндл значка HICON handle() const { return reinterpret_cast< HICON >( _handle ); } private: // хэндл HANDLE _handle; }; Подскажите пожалуйста, что именно нужно делать в деструкторе? Когда значок разрушать? Всегда, или только в особых случаях? А если я загружу стандартный значок, с флагом LR_SHARED, потом его хэндл задам в оконном классе, всё равно звонить в DestroyIcon()? |
Сообщ.
#2
,
|
|
|
Цитата Функция LoadImage() загружает значок и возвращает мне хэндл. Который почему-то типа HANDLE., а иконка должна быть типа HICON. Имею ли я право использовать приведение типов? Да, но есть один момент, иногда результат при LoadImage получается не совсем тот что ты хочешь видеть, так как нужно задавать параметры явно. Ну на соответствующие вызовы IMAGE_BITMAP, IMAGE_CURSOR, IMAGE_BITMAP нужно вызывать соответствующие DeleteObject, DestroyCursor, DestroyIcon. Цитата Подскажите пожалуйста, что именно нужно делать в деструкторе? Когда значок разрушать? Всегда, или в особых случаях? Нет не всегда, если ты создал локальный класс в функции и значек этого класса присвоил какой нибудь кнопке, то при разрушении объекта в деструкторе удалил значек, то долго будешь гадать почему он не отображается на кнопке. Цитата Как сказано в мане, если задать флаг LR_SHARED, хэндл копируется. А значок будет уничтожен, когда он перестанет быть нужным. Нет там говориться, что значек будет загружен один раз, а при повторной загрузке, создастся новый хэндл ссылающийся уже на загруженный значек. Ну соответственно если ты удалишь значек по первому хэндлу то второй хэндл станет не валидным. |
Сообщ.
#3
,
|
|
|
Цитата Dem_max @ Нет там говориться, что значек будет загружен один раз, а при повторной загрузке, создастся новый хэндл ссылающийся уже на загруженный значек. Ну соответственно если ты удалишь значек по первому хэндлу то второй хэндл станет не валидным. А если быть ещё более точным, то при использовании флага LR_SHARED все вызовы LoadImage() будут возвращать один и тот же хендл, а система будет увеличивать внутренний счётчик ссылок. При удалении такого значка, нужно чтобы DestroyIcon() была вызвана столько же раз, сколько и LoadImage(), чтобы значок был действительно удалён. |
Сообщ.
#4
,
|
|
|
Цитата artsb @ А если быть ещё более точным, то при использовании флага LR_SHARED все вызовы LoadImage() будут возвращать один и тот же хендл, а система будет увеличивать внутренний счётчик ссылок. При удалении такого значка, нужно чтобы DestroyIcon() была вызвана столько же раз, сколько и LoadImage(), чтобы значок был действительно удалён. Значит, без зависимости от флага DestroyIcon() нужно вызывать всегда, на каждый вызов LoadImage()? Добавлено Цитата Dem_max @ Ну на соответствующие вызовы IMAGE_BITMAP, IMAGE_CURSOR, IMAGE_BITMAP нужно вызывать соответствующие DeleteObject, DestroyCursor, DestroyIcon. Это-то понятно. Я сразу, в примере обозначил, что класс для значка и тип определён жестко - IMAGE_ICON Добавлено Цитата Dem_max @ Нет не всегда, если ты создал локальный класс в функции и значек этого класса присвоил какой нибудь кнопке, то при разрушении объекта в деструкторе удалил значек, то долго будешь гадать почему он не отображается на кнопке. Я собираюсь данный объект, держать, пока работает окно. К примеру передать хэндл значка оконному классу и значку в системном трее. А когда программа будет завершатся, деструктор автоматически разрушит значок. Но я очень часто встречал в примерах, код: wcex.hIcon = LoadIcon( MAKEINTRESOURCE( RES_ICON ) ); auto atom = RegisterClassEx( &wcex ); И всё. Типа значок должен будет автоматически разрушен. |
Сообщ.
#5
,
|
|
|
сделай в своём классе метод Detach(), который укажет деструктору класса не прибивать хендл
|
Сообщ.
#6
,
|
|
|
Цитата ElcnU @ сделай в своём классе метод Detach(), который укажет деструктору класса не прибивать хендл Это понятно и сделать можно. Мне как раз не понятно., в каких именно случаях, нужно, а в каких нет, прибивать хэндл значка. И можно ли это определить автоматически? К примеру, в классе окна, я сделал что-то вроде: // деструктор window::~window() { if( NULL != _handle ) { if( ::IsWindow( _handle ) ) { // разрушить окно ::DestroyWindow( _handle ); } } } |
Сообщ.
#7
,
|
|
|
Цитата Eric-S @ К примеру, в классе окна, я сделал что-то вроде: а зачем аж 2 проверки предварительно? DestroyWindow и без тебя всё узнает.... |
Сообщ.
#8
,
|
|
|
Цитата Eric-S @ Но я очень часто встречал в примерах, код: wcex.hIcon = LoadIcon( MAKEINTRESOURCE( RES_ICON ) ); auto atom = RegisterClassEx( &wcex ); И всё. Типа значок должен будет автоматически разрушен. MSDN: Цитата Т.к. окно будет жить на всём протяжении работы программы, то можно и не удалять этот значок вручную. Об этом позаботится система. The system automatically deletes these resources when the process that created them terminates; however, calling the appropriate function saves memory and decreases the size of the process's working set. |
Сообщ.
#9
,
|
|
|
Цитата ElcnU @ а зачем аж 2 проверки предварительно? DestroyWindow и без тебя всё узнает.... Но почему-то не узнаёт. Да, одну проверку можно бы и сократить. Осталась по историческим причинам. А вот если проверки убрать, то дестроер, возбуждает исключения. У меня же на системные функции, сделаны обёртки с проверкой ошибки. В деструкторе вызывается метод destroy(). Который с большой вероятностью стрельнёт исключением. Вот отсюда и проверки. Пусть даже излишние. Подумаешь, несколько тактов потеряю. Но получу более стабильный и надёжный код. class window: public std::enable_shared_from_this< window > { public: // деструктор virtual ~window() try { dispose(); } catch( ... ) { std::terminate(); } // освобождает ресурсы void dispose() { if( is_create() ) { destroy(); } } // подтверждает, что окно создано bool is_create() const { return IsWindow( _handle )? true: false; } // разрушает void destroy() if( FALSE == DestroyWindow( _handle ) ) { throw std::system_error( GetLastError(), std::system_category(), "DestroyWindow" ); } _handle = NULL; } private: HWND _handle; }; Ну и как-то так юзаю. auto form1 = std::make_shared< window >(); form1->create( ... ); // application::run(); form1->dispose(); Тут всё пока нормально. А вот если закоментировать строчку с вызовом create() или добавить цикл обработки сообщений, где в оконной процедуре, так же вызывается DestroyWindow. То всё, метод destroy() обязательно, стрельнёт исключением. Поскольку хэндл не действителен. И окно уже давно разрушено или даже не создано. Добавлено Цитата artsb @ будет жить на всём протяжении работы программы, то можно и не удалять этот значок вручную. Об этом позаботится система. Система освобождает хэндл, когда программа завершается. А ведь система и память из кучи освобождает. Зачем вызывать HeapFree() после того как блок полученный HeapAlloc(), был использован? Да, в частном случае, на лишние функции можно забить. А по мне, если есть неуправляемый ресурс. Его нужно засунуть в класс. И пользовать соответствующие объекты. Пускай за своевременным освобождением ресурсов, забодится объект. |
Сообщ.
#10
,
|
|
|
Цитата Eric-S @ Тут всё пока нормально. А вот если закоментировать строчку с вызовом create() или добавить цикл обработки сообщений, где в оконной процедуре, так же вызывается DestroyWindow. То всё, метод destroy() обязательно, стрельнёт исключением. Поскольку хэндл не действителен. И окно уже давно разрушено или даже не создано. то что DestroyWindow вернула FALSE еще не означает конец света.... ну вернула, да вернула, просто внутри проверка сказала, что хендл кривой.... какая разница где это скажется, у тебя в классе или в где то там в user32.dll, факт тот, что даже без твоего участия эта проверка есть, так нафига тебе еще что то проверять, да исключения генерить? Что бы необоснованно отхавать процессорного времени? Ну дело личное конечно |
Сообщ.
#11
,
|
|
|
Цитата ElcnU @ то что DestroyWindow вернула FALSE еще не означает конец света.... ну вернула, да вернула, просто внутри проверка сказала, что хендл кривой.... Я вообщем-то согласен, что моя проверка дублирует, внутреннюю проверку из функции DestroyWindow. Но, когда я вызываю метод destroy, моей проверки нет. По сути, это просто обёртка для DestroyWindow() и проверка ошибки. Проверка есть в методе dispose(). Но он, реализует подстраховочное освобождение. Предполагается, что окно было уже разрушено явным вызовом метода destroy(). Соответственно, в генеральной линии, я наоборот экономлю ресурсы машины. Ну, а ежели что-то пошло наперекосяк, последней линией становится деструктор. Который выполняет дополнительный контрольный вызов метода dispose(). Не вижу смысла, особо заниматся оптимизацией, резервного механизма освобождения ресурсов. Кроме того, моя проверка, использует задокументированные функции. Ежели я попытаюсь использовать внутреннюю проверку, вшитую в DestroyWindow(), которая не задокументирована. То могу огрести дополнительные проблемы. По этому, если DestroyWindow() сообщает о некой внутренней ошибке, проще бросить соответствующее исключение. Конечно, я в деструкторе, вместо вызова dispose() могу вызывать напрямую DestroyWindow(). Забив на все проверки, исключения, перехват исключений, обнуление хэндла. И прочее, что там наверчено. Но в этом случае, я буду уже дублировать свои механизмы. А может я захочу в будущем, в метод destroy(), воткнуть ещё вызов on_destroy_handle(). А в деструкторе забуду про это? Этот механизм, у меня, реализован уже давно, и я не замечал за ним, проблем, в момент разрушения окна. И даже наоборот. Он сформировался таким из-за внешних обстоятельств. К примеру UnregisterClass() раньше ругалась, что окно не разрушено и класс всё ещё используется. Но это всё офтоп. Я просто привёл пример, как проверяю действительность хэндла окна. И для чего мне это нужно. Хотелось бы, сделать нечто подобное, для хэндла значка. Может быть не совсем так. Но думаю, будет неприятно, если я лишний раз вызову DestroyIcon(). |
Сообщ.
#12
,
|
|
|
Цитата Eric-S @ Проверка есть в методе dispose(). Но он, реализует подстраховочное освобождение. Предполагается, что окно было уже разрушено явным вызовом метода destroy(). Соответственно, в генеральной линии, я наоборот экономлю ресурсы машины. Цитата Eric-S @ return IsWindow( _handle )? true: false; так будет экономичней return (_handle!=NULL); все равно хендл обнуляется Цитата Eric-S @ К примеру UnregisterClass() раньше ругалась, что окно не разрушено и класс всё ещё используется. не вижу смысла в вызове этой функции... соответстенно все её ругательства тоже бессмыслены Цитата Eric-S @ Но думаю, будет неприятно, если я лишний раз вызову DestroyIcon(). если объект окна и обект используемой им иконки будут объявлены на одном уровне, то и деструкторы их будут вызывать ориентировочно в одно время.... если же ты будешь использовать объект иконки локально то нужен будет механизм Detach().... |