Странность MFC

 
BG Реконструктор #23.08.2001 12:48
+
-
edit
 
Тут вот такая шутка получилась:
Если выполнить CDialog::EndDialog в thread (не знаю как по русски, позор! Может "нить"?) различной от той, в которой выполнен CDialog: :DoModal, то софт крашится.
Я нашел и причину:
void CDialog::EndDialog(int nResult)
{
	ASSERT(::IsWindow(m_hWnd));

	if (m_nFlags & (WF_MODALLOOP|WF_CONTINUEMODAL))
		EndModalLoop(nResult);

	::EndDialog(m_hWnd, nResult);
}

[/html_font]

После выполнения EndModalLoop(nResult) m_hWnd зануляется, т.к. внутри этого EndModalLoop-а выполняется DestroyWindow. Получается такая последовательность: DestroyWindow, m_hWnd = NULL, ::EndDialog(m_hWnd, nResult), краш.
Попробовал заменить CDialog::EndDialog на CDialog::EndModalLoop (EndModalLoop ф-я CWnd, но CDialog налследник CWnd). Это работает пару раз, потом крашится в wincore.cpp, в десктрукторе CWnd на строке
[html_font size=+0]
	// cleanup control site
	if (m_pCtrlSite != NULL && m_pCtrlSite->m_pWndCtrl == this)
		m_pCtrlSite->m_pWndCtrl = NULL;

Причина: m_pCtrlSite содержит значение 0xdddddddd, т.е. неинициализированна.

Так как, черт возьми, создавать диалоговое окно в одной thread и уничтожать его в другой???
 

Igor

втянувшийся
>thread (не знаю как по русски, позор! Может "нить"?)

Никакого позора :) . "Нить", "поток".

Обязательно прочитай в MSDN "TN003: Mapping of Windows Handles to Objects"!

Дело в том, что каждое окно принадлежит конкретному потоку (thread). Причем под окном понимается именно объект Windows (HWND), а не ассоциированный с ним объект класса MFC. Для доступа к объектам Windows, созданным в другом потоке, следует использовать дескрипторы (

!

). Т.е. у тебя есть два варианта:
1. Хранить во втором потоке дескриптор окна диалога и передавать ему соответствующее сообщение;
2. Передавать из второго потока в первый THREAD_MESSAGE, обработчик которого будет работать с экземпляром класса MFC, которым он же и владеет.

Извини, глубоко вникать в твою проблему не стал. Если то, что я написал (обязательно посмотри MSDN!) не поможет - пиши, посмотрю еще раз.
 

Igor

втянувшийся
>Это работает пару раз, потом крашится

Потому что MFC не удается ущучить тебя с первого раза :) . Эта вещь должна всегда падать на ASSERT, потому что она must die! :) Так что это feature, а не странность.
 
BG Реконструктор #24.08.2001 06:41
+
-
edit
 
Нда, я понял, НО! :)
Скажем, что в потоке имеется следущий код:
*pMyDlg = (CMyDialog *)lpParameter;
*pMyDlg2 = (CMyDialog *)CWnd::FromHandle(pMyDlg->m_hWnd);
pMyDlg2->EndDialog(0);

[/html_font]

В этом случае, переменная m_nFlags в pMyDlg2 содержит ноль, и поетому при выполнении EndDialog очевидно не выполняется EndModalLoop, только ::EndDialog. Тесты показали, что можно безпроблемно выполнить EndModalLoop после ::EndDialog, никаких проблем нет. В этом случае верхний код можно заменить порсто следующим:

[html_font size=+0]
*pMyDlg = (CMyDialog *)lpParameter;
::EndDialog(pMyDlg->m_hWnd, 0);
pMyDlg->EndModalLoop(0);

Это, конечно, большой хрен, но лучше не придумал.
 

Igor

втянувшийся
Это некрасиво, как и любой акт насилия.

>CWnd::FromHandle(pMyDlg->m_hWnd)

которая возвращает объект класса CTempWnd, который "не знает" ничего ни о каких петлях ;) , потому и m_nFlags == NULL (IMHO).

Откуда (CMyDialog*) берешь?
ASSERT(это окно было создано тем потоком, в котором сидит приведенный тобой обработчик);
// Мой постинг здесь не крашится? ;)


А как ведет себя m_nFlags в случае "нижнего" кода?

IMHO, ты можешь хранить во втором потоке и передавать в первый все-таки дескриптор окна диалога. Просто нужно посылать окну сообщение типа WM_CLOSE "на общих основаниях", т.е. обойтись без EndDialog, EndModalLoop и т.п. Причем мессагу можно посылать прямо из второй нити.
 

MiG
Реконструктор

опытный

>CWnd::FromHandle(pMyDlg->>m_hWnd)
Igor>которая возвращает объект класса CTempWnd, который "не знает" ничего ни о каких петлях ;) , потому и m_nFlags == NULL (IMHO).

Именно. FromHandlePermanent возворащает NULL.

Igor>А как ведет себя m_nFlags в случае "нижнего" кода?

Не изучал, так как небыло необходимо. Работает и не крашится и никаких побочных эффектов, т.е. то, что надо каждому программисту :)

Igor>IMHO, ты можешь хранить во втором потоке и передавать в первый все-таки дескриптор окна диалога. Просто нужно посылать окну сообщение типа WM_CLOSE "на общих основаниях", т.е. обойтись без EndDialog, EndModalLoop и т.п. Причем мессагу можно посылать прямо из второй нити.

С WM_CLOSE не пробывал, так как:
Это сообщение (как и нажатие кнопки "Cancel") генерирует вызов на OnCancel в диалоговом классе. А там у меня один симпатичный флажок сидит. Продолжить? :)
 

в начало страницы | новое
 
Поиск
Настройки
Твиттер сайта
Статистика
Рейтинг@Mail.ru