Пожалуйста, объясните почему этот код не работает?
От: Xenia США  
Дата: 23.03.02 18:19
Оценка:
Имеется такой вот простенький код:


void CBimapsView::OnButton32774() 
{
    CRgn rg;
//запомнили первоначальный
    GetWindowRgn(oldRegion);
    rg.CreateEllipticRgn(10,10,200,200);
//установили эллипсовидный
    SetWindowRgn(rg,TRUE);
}

void CBimapsView::OnButton32776() 
{
    //восстановили первоначальный
    SetWindowRgn(oldRegion,TRUE);
}

Смысл — по щелчку одной кнопки окно становится круглым, а по щелчку на другой — восстанавливает свою форму. И не работает. А вот если пользоваться аналогичными функциями API и сохранять состояние не в переменной-члене класса C*View, а C*App то работает. Почему?
Re: Пожалуйста, объясните почему этот код не работает?
От: Alex Fedotov США  
Дата: 23.03.02 18:35
Оценка:
Здравствуйте Xenia, Вы писали:

X>Имеется такой вот простенький код:


X>void CBimapsView::OnButton32774() 
X>{
X>    CRgn rg;
X>//запомнили первоначальный
X>    GetWindowRgn(oldRegion);
X>    rg.CreateEllipticRgn(10,10,200,200);
X>//установили эллипсовидный
X>    SetWindowRgn(rg,TRUE);

Не работает, потому что вот в этот момент переменная rg 
уничтожается, а вместе с ней и регион, назначенный окну.
Очевидно, нужно сделать rg членом класса, как и oldRegion.

X>}

X>void CBimapsView::OnButton32776() 
X>{
X>    //восстановили первоначальный
X>    SetWindowRgn(oldRegion,TRUE);
X>}
-- Alex Fedotov
Re[2]: Пожалуйста, объясните почему этот код не работает?
От: Xenia США  
Дата: 24.03.02 09:20
Оценка:
Здравствуйте Alex Fedotov, Вы писали:

AF>Здравствуйте Xenia, Вы писали:


X>>Имеется такой вот простенький код:


AF>
X>>void CBimapsView::OnButton32774() 
X>>{
X>>    CRgn rg;
X>>//запомнили первоначальный
X>>    GetWindowRgn(oldRegion);
X>>    rg.CreateEllipticRgn(10,10,200,200);
X>>//установили эллипсовидный
X>>    SetWindowRgn(rg,TRUE);

AF>Не работает, потому что вот в этот момент переменная rg 
AF>уничтожается, а вместе с ней и регион, назначенный окну.
AF>Очевидно, нужно сделать rg членом класса, как и oldRegion.
AF>

Делаю rg членом класса и все равно не работает, а вот между прочим если написать

AF>
X>>void CBimapsView::OnButton32774() 
X>>{
X>>    CRgn rg;
X>>//запомнили первоначальный
X>>    GetWindowRgn(oldRegion);
X>>    rg.CreateEllipticRgn(10,10,200,200);
X>>//установили эллипсовидный
X>>    ::SetWindowRgn(AfxGetMainWnd()->m_hWnd,rg,TRUE); т.е. используем API, то работает, хотя rg по прежнему локальная переменная. Но вот обратно окно все равно не восстанавливает
AF>
Re[3]: Пожалуйста, объясните почему этот код не работает?
От: Alex Fedotov США  
Дата: 24.03.02 09:26
Оценка:
Здравствуйте Xenia, Вы писали:

X>>>Имеется такой вот простенький код:


X>>>void CBimapsView::OnButton32774() 
X>>>{
X>>>    CRgn rg;
X>>>//запомнили первоначальный
X>>>    GetWindowRgn(oldRegion);
X>>>    rg.CreateEllipticRgn(10,10,200,200);
X>>>//установили эллипсовидный
X>>>    SetWindowRgn(rg,TRUE);

AF>>Не работает, потому что вот в этот момент переменная rg 
AF>>уничтожается, а вместе с ней и регион, назначенный окну.
AF>>Очевидно, нужно сделать rg членом класса, как и oldRegion.


X>Делаю rg членом класса и все равно не работает, а вот между прочим если написать


X>>>void CBimapsView::OnButton32774() 
X>>>{
X>>>    CRgn rg;
X>>>//запомнили первоначальный
X>>>    GetWindowRgn(oldRegion);
X>>>    rg.CreateEllipticRgn(10,10,200,200);
X>>>//установили эллипсовидный
X>>>    ::SetWindowRgn(AfxGetMainWnd()->m_hWnd,rg,TRUE); т.е. используем API, то работает, хотя rg по прежнему локальная переменная. Но вот обратно окно все равно не восстанавливает


Так это разные окна. Одно окно, судя по названию — view, а другое — главное окно программы, m.е. frame.

Это MDI, SDI-программа или нечто другое?
-- Alex Fedotov
Re[4]: Пожалуйста, объясните почему этот код не работает?
От: Xenia США  
Дата: 24.03.02 15:06
Оценка:
Согласна, стормозила. Это не то окно. (Само приложени SDI)
Ну вот я это исправляю таким образом:


void CBimapsView::OnButton32774() 
{
    
    CRgn rg;    
//запомнили первоначальный
    AfxGetMainWnd()->GetWindowRgn(oldRegion);
    rg.CreateEllipticRgn(10,10,200,200);
//установили эллипсовидный
//::SetWindowRgn(AfxGetMainWnd()->m_hWnd,rg,TRUE);
    AfxGetMainWnd()->SetWindowRgn(rg,TRUE);

    
}

void CBimapsView::OnButton32776() 
{
    //восстановили первоначальный
    AfxGetMainWnd()->SetWindowRgn(oldRegion,TRUE);
}

И окно таки сворачивается, но обратно не восстаналивается. Вот почему оно не восстанавливается, меня очень интересует. Ведь если запомнтиь первоначальный регион в функции C*App.InitInstance() то оно восстанавливается. Какая разница где его запоминать?
Окна и регионы
От: Alex Fedotov США  
Дата: 25.03.02 02:30
Оценка: 26 (4)
#Имя: FAQ.mfc.regions
Здравствуйте Xenia, Вы писали:

X>
X>void CBimapsView::OnButton32774() 
X>{
X>    
X>    CRgn rg;    
X>//запомнили первоначальный
X>    AfxGetMainWnd()->GetWindowRgn(oldRegion);
X>    rg.CreateEllipticRgn(10,10,200,200);
X>//установили эллипсовидный
//::SetWindowRgn(AfxGetMainWnd()->>m_hWnd,rg,TRUE);
X>    AfxGetMainWnd()->SetWindowRgn(rg,TRUE);

X>    
X>}

X>void CBimapsView::OnButton32776() 
X>{
X>    //восстановили первоначальный
X>    AfxGetMainWnd()->SetWindowRgn(oldRegion,TRUE);
X>}

X>


X>Смысл — по щелчку одной кнопки окно становится круглым, а по щелчку на другой — восстанавливает свою форму. И не работает. А вот если пользоваться аналогичными функциями API и сохранять состояние не в переменной-члене класса C*View, а C*App то работает. Почему?


Я набросал примерчик, лежит на http://www.alexfedotov.com/samples/winrgn.zip

Вот замечания:

1. У меня сложилось впечатление, что ты пытаешься отлаживать программу в Release-конфигурации. Во всяком случае, ты ничего не говорила про debug assertions, а они должны быть

2. Команды установки и снятия региона относятся к главному окну, так почему бы их туда и не поместить? В моем примере они находятся в классе CMainFrame.

Теперь по существу:

void CMainFrame::OnSetRgn() 
{
    RECT rcWindow;
    GetWindowRect(&rcWindow);
    
    rcWindow.right  -= rcWindow.left;
    rcWindow.bottom -= rcWindow.top;

    CRgn rgn;
    VERIFY(rgn.CreateRoundRectRgn(0, 0, rcWindow.right, rcWindow.bottom, 80, 80));

    m_OldRgn.CreateRectRgn(0, 0, 0, 0);
    if (!GetWindowRgn(m_OldRgn))
        m_OldRgn.DeleteObject();

    VERIFY(SetWindowRgn((HRGN)rgn.Detach(), TRUE));
}


3. Функция GetWindowRgn ожидает на вход действительный HRGN, поэтому прежде чем давать m_OldRgn на вход этой функции, его надо инициализировать. В показанном тобой коде непонятно, где это происходит, и происходит ли вообще.

4. Нормальные прямоугольные окна не имеют региона. Если вызвать GetWindowRgn для такого окна, она просто возвращает FALSE (а ведь в книжках учат — проверяйте возвращаемое значение функций, хотя бы с помощью VERIFY). Поэтому логика такая: a) создаем регион, б) вызываем GetWindowRgn; в) теперь если функция завершилась успешно, мы получили регион окна, в противном случае г) уничтожаем регион. В результате мы имеет m_OldRgn.m_hObject != NULL, если у окна есть регион, и m_OldRgn.m_hObject == NULL, если его нет.

5. Документация на функцию SetWindowRgn гласит:

<!-- msdn -->
After a successful call to SetWindowRgn, the system owns the region specified by the region handle hRgn. The system does not make a copy of the region. Thus, you should not make any further function calls with this region handle. In particular, do not delete this region handle. The system deletes the region handle when it no longer needed.
<!-- /msdn -->

Поэтому ни сохранять этот регион (как я изначально советовал), ни удалять его (что произойдет в деструкторе CRgn, если не предпринять дополнительных действий) нельзя. Чтобы HRGN не удалился в деструкторе CRgn, его нужно отсоединить от объекта с помощью метода Detach(), как показано в примере.

void CMainFrame::OnRestoreRgn() 
{
    SetWindowRgn((HRGN)m_OldRgn.Detach(), TRUE);
}


6. Восстановление региона просто: если у нас есть сохраненный регион, мы отсоединяем его от объекта и назначаем окну. В противном случае мы вызываем SetWindowRgn c HRGN, равным NULL, что приводит к восстановлению нормальной прямоугольной формы окна.
-- Alex Fedotov
Re[6]: <jkmijt cgfcb,j
От: Xenia США  
Дата: 25.03.02 15:43
Оценка:
Большое спасибо за подробные разъяснения и пример!
Кстати, работала я в Debug версии и никаких assert' ов не было
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.