Существует MDI приложение, необходимо запустить копию фрейма основного фрейма приложения в отдельном потоке, по сути получается что надо запустить копию приложения из самого приложения в отдельном потоке. Каким образом я это попытался сделать: завел два новых класса
class CPreviewFrame : public CMDIFrameWnd - класс копия основного фрейма,
class CPreviewApp : public CWinThread - наследник от CWinThread чтобы можно было в потоке манипулировать графикой
Программа ругается, а затем падает на функции LoadFrame(IDR_PREVIEWFRAME) с помощью которой я создаю фрейм. В чем может быть дело?
Класс самого приложения CWinApp, проблема была бы исчерпана если бы можно было завести в приложении еще один экземпляр класса CWinApp, но как известно MFC этого не позволяет.
Что я не так делаю?
05.09.08 17:21: Перенесено модератором из 'C/C++' — Кодт
Здравствуйте, TatarDozor, Вы писали:
TD>Существует MDI приложение, необходимо запустить копию фрейма основного фрейма приложения в отдельном потоке, по сути получается что надо запустить копию приложения из самого приложения в отдельном потоке. Каким образом я это попытался сделать: завел два новых класса TD>
TD>class CPreviewFrame : public CMDIFrameWnd - класс копия основного фрейма,
TD>class CPreviewApp : public CWinThread - наследник от CWinThread чтобы можно было в потоке манипулировать графикой
TD>
TD>и в обработчике запускаю поток следующим образом
TD>
TD>Программа ругается, а затем падает на функции LoadFrame(IDR_PREVIEWFRAME) с помощью которой я создаю фрейм. В чем может быть дело? TD>Класс самого приложения CWinApp, проблема была бы исчерпана если бы можно было завести в приложении еще один экземпляр класса CWinApp, но как известно MFC этого не позволяет. TD>Что я не так делаю?
Я такое делал, все хорошо работает.
Правда у меня в потоках создавались окна одинакового класса. Создаются по команде типа "Открыть в новом окне".
CWinApp тебе не нужен, цикл прокачки сообщений со всеми idle и т.п. есть в CWinThread.
В MFC есть мутный код при создании окна рамки (что-то там с поддержкой старых версий, дефаултных меню и др.).
Если не путаю, то надо перекрыть OnCreateClient для CMDIFrameWnd, чтобы это безобразие не вызывалось.
Здравствуйте, AstroMan, Вы писали:
AM>Я такое делал, все хорошо работает. AM>Правда у меня в потоках создавались окна одинакового класса. Создаются по команде типа "Открыть в новом окне". AM>CWinApp тебе не нужен, цикл прокачки сообщений со всеми idle и т.п. есть в CWinThread. AM>В MFC есть мутный код при создании окна рамки (что-то там с поддержкой старых версий, дефаултных меню и др.). AM>Если не путаю, то надо перекрыть OnCreateClient для CMDIFrameWnd, чтобы это безобразие не вызывалось.
Что значит перекрыть? Каким образом можно это сделать?
В данной функции, как я понял, создается вид для документов. Я переопределил функцию и оставил реализацию пустой, все равно падает на функции
Я так сделал и действительно приложение запустило еще один фрейм в отдельном потоке, только вот проблема осталось: при тыке мышью на любой документ приложение падает, а падает оно на функциях проверки
CDocument::AssertValid();
//эту функция в свою очередь вызывает проверку у вида документа
void CView::AssertValid() const
{//а тут вызывется функция
CWnd::AssertValid();//здесь собственно все и падает!!!
}
Приложение отображает документы в одном виде, условно назову его шаблон 1 (CDocTemplate).
Дело в том что в отдельном потоке с отдельным фреймом необходимо отображать документы с другим видом, а это значит что надо использовать другой шаблон, условно назову его шаблон 2, который в свою очередь я попытался привязать к классу CPreviewApp (наследнику от CWinThread), но че-то видимо не особо получилось. При работе в отдельном потоке приложение все равно обращается и использует шаблон 1 привязаннный к основному классу приложения CWinApp. По моему в этом и заключается проблема.
Как можно привязать другой шаблон для использования в отдельном потоке? и к какому классу его надо привязывать?
Здравствуйте, TatarDozor, Вы писали:
TD>Приложение отображает документы в одном виде, условно назову его шаблон 1 (CDocTemplate). TD>Дело в том что в отдельном потоке с отдельным фреймом необходимо отображать документы с другим видом, а это значит что надо использовать другой шаблон, условно назову его шаблон 2, который в свою очередь я попытался привязать к классу CPreviewApp (наследнику от CWinThread), но че-то видимо не особо получилось. При работе в отдельном потоке приложение все равно обращается и использует шаблон 1 привязаннный к основному классу приложения CWinApp. По моему в этом и заключается проблема. TD>Как можно привязать другой шаблон для использования в отдельном потоке? и к какому классу его надо привязывать?
Создание документа, как я понимаю, происходит в обработчике ID_FILE_NEW. По умолчанию обработчик этой команды
определен в классе приложения. Если надо по особому обрабатывать в дополнительном фрейме, то сделайте соответствующий
ON_COMMAND в CWinThread или в его CMDIFrameWnd. При этом явно обращайтесь к нужному шаблону:
Здравствуйте, AstroMan, Вы писали:
AM>Создание документа, как я понимаю, происходит в обработчике ID_FILE_NEW. По умолчанию обработчик этой команды AM>определен в классе приложения. Если надо по особому обрабатывать в дополнительном фрейме, то сделайте соответствующий AM>ON_COMMAND в CWinThread или в его CMDIFrameWnd. При этом явно обращайтесь к нужному шаблону:
AM>
Я так и делаю, обращаюсь явно к нужному шаблону, но дело в том что программа ругается до того как я создам новый вид и фрейм для документа.
Прога выдает сообщение об ошибке при создании отдельного MDI фрейма, если в основном фрейме открыт хотя бы один документ.
Подробнее:
CMainFrame - класс основного MDI фрейма проги.
CZnzApp - класс приложения
CPreviewFrame - класс дополнительного MDI фрейма для запуска в отдельном потоке.
CPreviewApp - класс-наследник от CWinThread
//Есть обработчик
CMainFrame::MyCreateNewFrame() //в котором я запускаю поток
{
CWinThread *p = AfxBeginThread(RUNTIME_CLASS(CPreviewApp),THREAD_PRIORITY_NORMAL);
}
//AfxBeginThread() в свою очередь запускает CPreviewApp::InitInstance
//в этой функции я создаю дополнительный фрейм
BOOL CPreviewApp::InitInstance()
{
CPreviewFrame *pPF = new CPreviewFrame();
m_pMainWnd = pPF;
//загрузка фрейма из ресурсов
BOOL b = pPF->LoadFrame(IDR_PREVIEWFRAME);
}
//Так вот, если в CMainFrame открыт хотя бы один документ, то pPF->LoadFrame(IDR_PREVIEWFRAME) выдает ошибку!!
В чем может быть дело?
А если в CMainFrame не открыт не один документ, то pPF->LoadFrame(IDR_PREVIEWFRAME) прекрасно срабатывает!!
Здравствуйте, TatarDozor, Вы писали:
TD>//в этой функции я создаю дополнительный фрейм TD>BOOL CPreviewApp::InitInstance() TD>{ TD> CPreviewFrame *pPF = new CPreviewFrame(); TD> m_pMainWnd = pPF; TD> //загрузка фрейма из ресурсов TD> BOOL b = pPF->LoadFrame(IDR_PREVIEWFRAME); TD>}
TD>//Так вот, если в CMainFrame открыт хотя бы один документ, то pPF->LoadFrame(IDR_PREVIEWFRAME) выдает ошибку!! TD>[/code]
TD>В чем может быть дело?
TD>А если в CMainFrame не открыт не один документ, то pPF->LoadFrame(IDR_PREVIEWFRAME) прекрасно срабатывает!!
LoadFrame в основном зовет Create и посылает WM_INITIALUPDATE дочерним окнам.
Ничего зацепляющего другой фрейм там вроде нет.
Может в коде CPreviewFrame где-то явно зацепляется AfxGetApp()->... (в OnCreate, например).
С моим кодом отличие в том, что я не вызываю LoadFrame, а просто CMDIFrameWnd::Create.
Что в стеке-то при вываливании assert'a? Кто зовет CDocument::AssertValid() и в котором потоке?
Здравствуйте, AstroMan, Вы писали:
AM>LoadFrame в основном зовет Create и посылает WM_INITIALUPDATE дочерним окнам. AM>Ничего зацепляющего другой фрейм там вроде нет. AM>Может в коде CPreviewFrame где-то явно зацепляется AfxGetApp()->... (в OnCreate, например). AM>С моим кодом отличие в том, что я не вызываю LoadFrame, а просто CMDIFrameWnd::Create.
AM>Что в стеке-то при вываливании assert'a? Кто зовет CDocument::AssertValid() и в котором потоке?
Вот Call Stack, все происходит в дополнительном потоке
znz32_d.exe!CWnd::AssertValid() Line 892 + 0x43 bytes C++ //здесь вываливается Assert
znz32_d.exe!CView::AssertValid() Line 500 C++ //
znz32_d.exe!CZnzView::AssertValid() Line 795 C++ //мой пользовательский класс вида используется
//в шаблоне 1, в дополнительном потоке вообще
//надо использовать класс CZnzPreview (вид для шаблона 2)
//который я добавлял к CPreviewApp в InitInstance()
znz32_d.exe!AfxAssertValidObject(const CObject * pOb=0x0237dc98, const char * lpszFileName=0x010040ec, int nLine=882) Line 107 C++
znz32_d.exe!CDocument::AssertValid() Line 883 C++
znz32_d.exe!CZnzDoc::AssertValid() Line 570 C++ //класс документа
znz32_d.exe!AfxAssertValidObject(const CObject * pOb=0x02384218, const char * lpszFileName=0x0102d90c, int nLine=423) Line 107 C++
znz32_d.exe!CDocTemplate::AssertValid() Line 424 C++
znz32_d.exe!CMultiDocTemplate::AssertValid() Line 213 C++ //вот он и зовет CDocument::AssertValid(), хотя если логично
//рассуждать, то не должен, ведь я еще не добавил ни одного документа,
//стало быть эта сволочь обращается к документам которые были открыты
//в основном фрейме, почему?
znz32_d.exe!AfxAssertValidObject(const CObject * pOb=0x0186cab8, const char * lpszFileName=0x01123c88, int nLine=253) Line 107 C++
znz32_d.exe!CBCGKeyboardManager::LoadState() + 0xd0 bytes
znz32_d.exe!CBCGWorkspace::LoadState() + 0x4de bytes
znz32_d.exe!CBCGFrameImpl::OnLoadFrame() + 0x47 bytes
znz32_d.exe!CBCGMDIFrameWnd::LoadFrame() + 0x5d bytes //начало создания фрейма
znz32_d.exe!CPreviewApp::InitInstance() Line 50 + 0x1e bytes C++
znz32_d.exe!_AfxThreadEntry(void * pParam=0x0012f034) Line 113 + 0xd //запуск потока
znz32_d.exe!_callthreadstartex() Line 348 + 0xf bytes C //запуск потока
znz32_d.exe!_threadstartex(void * ptd=0x02b7abb8) Line 331 C //запуск потока
Кстати, если запускать Release версию проги, то она создает фрейм, добавляет документ, с ним даже можно поработать немного!!! После нескольких манипуляций
прога зависает
Здравствуйте, TatarDozor, Вы писали:
TD>Вот Call Stack, все происходит в дополнительном потоке
TD>
TD> znz32_d.exe!CWnd::AssertValid() Line 892 + 0x43 bytes C++ //здесь вываливается Assert
TD> znz32_d.exe!CView::AssertValid() Line 500 C++ //
TD> znz32_d.exe!CZnzView::AssertValid() Line 795 C++ //мой пользовательский класс вида используется
TD> //в шаблоне 1, в дополнительном потоке вообще
TD> //надо использовать класс CZnzPreview (вид для шаблона 2)
TD> //который я добавлял к CPreviewApp в InitInstance()
TD> znz32_d.exe!AfxAssertValidObject(const CObject * pOb=0x0237dc98, const char * lpszFileName=0x010040ec, int nLine=882) Line 107 C++
TD> znz32_d.exe!CDocument::AssertValid() Line 883 C++
TD> znz32_d.exe!CZnzDoc::AssertValid() Line 570 C++ //класс документа
TD> znz32_d.exe!AfxAssertValidObject(const CObject * pOb=0x02384218, const char * lpszFileName=0x0102d90c, int nLine=423) Line 107 C++
TD> znz32_d.exe!CDocTemplate::AssertValid() Line 424 C++
TD> znz32_d.exe!CMultiDocTemplate::AssertValid() Line 213 C++ //вот он и зовет CDocument::AssertValid(), хотя если логично
TD> //рассуждать, то не должен, ведь я еще не добавил ни одного документа,
TD> //стало быть эта сволочь обращается к документам которые были открыты
TD> //в основном фрейме, почему?
TD> znz32_d.exe!AfxAssertValidObject(const CObject * pOb=0x0186cab8, const char * lpszFileName=0x01123c88, int nLine=253) Line 107 C++
TD> znz32_d.exe!CBCGKeyboardManager::LoadState() + 0xd0 bytes
TD> znz32_d.exe!CBCGWorkspace::LoadState() + 0x4de bytes
TD> znz32_d.exe!CBCGFrameImpl::OnLoadFrame() + 0x47 bytes
TD> znz32_d.exe!CBCGMDIFrameWnd::LoadFrame() + 0x5d bytes //начало создания фрейма
TD> znz32_d.exe!CPreviewApp::InitInstance() Line 50 + 0x1e bytes C++
TD> znz32_d.exe!_AfxThreadEntry(void * pParam=0x0012f034) Line 113 + 0xd //запуск потока
TD> znz32_d.exe!_callthreadstartex() Line 348 + 0xf bytes C //запуск потока
TD> znz32_d.exe!_threadstartex(void * ptd=0x02b7abb8) Line 331 C //запуск потока
TD>
Очевидно, что CBCGMDIFrameWnd имеет другую реализацию LoadFrame, чем CMDIFrameWnd.
Классы CMultiDocTemplate регистрируются в CWinApp, странно, что доступ к ним возникает из LoadFrame.
Может это косяк BCG? У меня старый MFC, в LoadFrame никаких KeyboardManager нет и все работает.
Здравствуйте, AstroMan, Вы писали:
AM>Очевидно, что CBCGMDIFrameWnd имеет другую реализацию LoadFrame, чем CMDIFrameWnd. AM>Классы CMultiDocTemplate регистрируются в CWinApp, странно, что доступ к ним возникает из LoadFrame. AM>Может это косяк BCG? У меня старый MFC, в LoadFrame никаких KeyboardManager нет и все работает.
Действительно, я передал в дополнительный поток не CBCGMDIFrameWnd, а CMDIFrameWnd и фрейм создался, только вот все равно есть какая-то проблема при создании ChildFrame дочернего окна в дополнительном потоке, все равно какой-то Assert вываливается и кажется где-то при прорисовке.
Если ваш проект не коммерческий, про который вы говорили в качестве примера, то пожалуйста дайте мне исходники.
Здравствуйте, TatarDozor, Вы писали:
TD>Если ваш проект не коммерческий, про который вы говорили в качестве примера, то пожалуйста дайте мне исходники.
Тогда может дадите кусок кода где вызывается функция создания фрейма, мне интересно заполнение параметров? Вы тоже в отдельном потоке запускаете? И если можно кусок запуска потока?