Основной MDIFrame в отдельном потоке
От: TatarDozor  
Дата: 05.09.08 08:01
Оценка:
Существует MDI приложение, необходимо запустить копию фрейма основного фрейма приложения в отдельном потоке, по сути получается что надо запустить копию приложения из самого приложения в отдельном потоке. Каким образом я это попытался сделать: завел два новых класса
class CPreviewFrame : public CMDIFrameWnd - класс копия основного фрейма, 
class CPreviewApp : public CWinThread - наследник от CWinThread чтобы можно было в потоке манипулировать графикой


и в обработчике запускаю поток следующим образом

CWinThread *p = AfxBeginThread(RUNTIME_CLASS(CPreviewApp),THREAD_PRIORITY_NORMAL);


Программа ругается, а затем падает на функции LoadFrame(IDR_PREVIEWFRAME) с помощью которой я создаю фрейм. В чем может быть дело?
Класс самого приложения CWinApp, проблема была бы исчерпана если бы можно было завести в приложении еще один экземпляр класса CWinApp, но как известно MFC этого не позволяет.
Что я не так делаю?

05.09.08 17:21: Перенесено модератором из 'C/C++' — Кодт
Re: Основной MDIFrame в отдельном потоке
От: AstroMan  
Дата: 07.09.08 08:57
Оценка:
Здравствуйте, TatarDozor, Вы писали:

TD>Существует MDI приложение, необходимо запустить копию фрейма основного фрейма приложения в отдельном потоке, по сути получается что надо запустить копию приложения из самого приложения в отдельном потоке. Каким образом я это попытался сделать: завел два новых класса

TD>
TD>class CPreviewFrame : public CMDIFrameWnd - класс копия основного фрейма, 
TD>class CPreviewApp : public CWinThread - наследник от CWinThread чтобы можно было в потоке манипулировать графикой
TD>


TD>и в обработчике запускаю поток следующим образом


TD>
TD>CWinThread *p = AfxBeginThread(RUNTIME_CLASS(CPreviewApp),THREAD_PRIORITY_NORMAL);
TD>


TD>Программа ругается, а затем падает на функции LoadFrame(IDR_PREVIEWFRAME) с помощью которой я создаю фрейм. В чем может быть дело?

TD>Класс самого приложения CWinApp, проблема была бы исчерпана если бы можно было завести в приложении еще один экземпляр класса CWinApp, но как известно MFC этого не позволяет.
TD>Что я не так делаю?

Я такое делал, все хорошо работает.
Правда у меня в потоках создавались окна одинакового класса. Создаются по команде типа "Открыть в новом окне".
CWinApp тебе не нужен, цикл прокачки сообщений со всеми idle и т.п. есть в CWinThread.
В MFC есть мутный код при создании окна рамки (что-то там с поддержкой старых версий, дефаултных меню и др.).
Если не путаю, то надо перекрыть OnCreateClient для CMDIFrameWnd, чтобы это безобразие не вызывалось.
Re[2]: Основной MDIFrame в отдельном потоке
От: TatarDozor  
Дата: 08.09.08 05:50
Оценка:
Здравствуйте, AstroMan, Вы писали:

AM>Я такое делал, все хорошо работает.

AM>Правда у меня в потоках создавались окна одинакового класса. Создаются по команде типа "Открыть в новом окне".
AM>CWinApp тебе не нужен, цикл прокачки сообщений со всеми idle и т.п. есть в CWinThread.
AM>В MFC есть мутный код при создании окна рамки (что-то там с поддержкой старых версий, дефаултных меню и др.).
AM>Если не путаю, то надо перекрыть OnCreateClient для CMDIFrameWnd, чтобы это безобразие не вызывалось.

Что значит перекрыть? Каким образом можно это сделать?
В данной функции, как я понял, создается вид для документов. Я переопределил функцию и оставил реализацию пустой, все равно падает на функции
    BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
    {
        ........
        при получении параметра
        CRuntimeClass* pClassThis = GetRuntimeClass();
        ........
    }
Re[3]: Основной MDIFrame в отдельном потоке
От: AstroMan  
Дата: 08.09.08 10:57
Оценка:
Здравствуйте, TatarDozor, Вы писали:

TD>Что значит перекрыть? Каким образом можно это сделать?


BOOL CMyMainFrame::OnCreateClient(LPCREATESTRUCT lpCreateStruct, CCreateContext*)
{
  // не вызываем CMDIFrameWnd::OnCreateClient!!!
  return CreateClient(lpCreateStruct, NULL);
}
Re[4]: Основной MDIFrame в отдельном потоке
От: TatarDozor  
Дата: 09.09.08 06:03
Оценка:
Здравствуйте, AstroMan, Вы писали:

TD>>Что значит перекрыть? Каким образом можно это сделать?


AM>
AM>BOOL CMyMainFrame::OnCreateClient(LPCREATESTRUCT lpCreateStruct, CCreateContext*)
AM>{
AM>  // не вызываем CMDIFrameWnd::OnCreateClient!!!
AM>  return CreateClient(lpCreateStruct, NULL);
AM>}
AM>


Я так сделал и действительно приложение запустило еще один фрейм в отдельном потоке, только вот проблема осталось: при тыке мышью на любой документ приложение падает, а падает оно на функциях проверки
    CDocument::AssertValid();
        //эту функция в свою очередь вызывает проверку у вида документа
        void CView::AssertValid() const
        {//а тут вызывется функция
            CWnd::AssertValid();//здесь собственно все и падает!!!
        }

Приложение отображает документы в одном виде, условно назову его шаблон 1 (CDocTemplate).
Дело в том что в отдельном потоке с отдельным фреймом необходимо отображать документы с другим видом, а это значит что надо использовать другой шаблон, условно назову его шаблон 2, который в свою очередь я попытался привязать к классу CPreviewApp (наследнику от CWinThread), но че-то видимо не особо получилось. При работе в отдельном потоке приложение все равно обращается и использует шаблон 1 привязаннный к основному классу приложения CWinApp. По моему в этом и заключается проблема.
Как можно привязать другой шаблон для использования в отдельном потоке? и к какому классу его надо привязывать?
Re[5]: Основной MDIFrame в отдельном потоке
От: AstroMan  
Дата: 09.09.08 08:20
Оценка:
Здравствуйте, TatarDozor, Вы писали:

TD>Приложение отображает документы в одном виде, условно назову его шаблон 1 (CDocTemplate).

TD>Дело в том что в отдельном потоке с отдельным фреймом необходимо отображать документы с другим видом, а это значит что надо использовать другой шаблон, условно назову его шаблон 2, который в свою очередь я попытался привязать к классу CPreviewApp (наследнику от CWinThread), но че-то видимо не особо получилось. При работе в отдельном потоке приложение все равно обращается и использует шаблон 1 привязаннный к основному классу приложения CWinApp. По моему в этом и заключается проблема.
TD>Как можно привязать другой шаблон для использования в отдельном потоке? и к какому классу его надо привязывать?

Создание документа, как я понимаю, происходит в обработчике ID_FILE_NEW. По умолчанию обработчик этой команды
определен в классе приложения. Если надо по особому обрабатывать в дополнительном фрейме, то сделайте соответствующий
ON_COMMAND в CWinThread или в его CMDIFrameWnd. При этом явно обращайтесь к нужному шаблону:

CDocument* pDoc = m_pMyDocTemplate->CreateNewDocument();
Re[6]: Основной MDIFrame в отдельном потоке
От: TatarDozor  
Дата: 09.09.08 09:32
Оценка:
Здравствуйте, AstroMan, Вы писали:

AM>Создание документа, как я понимаю, происходит в обработчике ID_FILE_NEW. По умолчанию обработчик этой команды

AM>определен в классе приложения. Если надо по особому обрабатывать в дополнительном фрейме, то сделайте соответствующий
AM>ON_COMMAND в CWinThread или в его CMDIFrameWnd. При этом явно обращайтесь к нужному шаблону:

AM>
AM>CDocument* pDoc = m_pMyDocTemplate->CreateNewDocument();
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) прекрасно срабатывает!!
Re[7]: Основной MDIFrame в отдельном потоке
От: AstroMan  
Дата: 09.09.08 13:31
Оценка:
Здравствуйте, 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() и в котором потоке?
Re[8]: Основной MDIFrame в отдельном потоке
От: TatarDozor  
Дата: 10.09.08 05:09
Оценка:
Здравствуйте, 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 версию проги, то она создает фрейм, добавляет документ, с ним даже можно поработать немного!!! После нескольких манипуляций
прога зависает
Re[9]: Основной MDIFrame в отдельном потоке
От: AstroMan  
Дата: 10.09.08 06:20
Оценка:
Здравствуйте, 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 нет и все работает.
Re[10]: Основной MDIFrame в отдельном потоке
От: TatarDozor  
Дата: 11.09.08 10:44
Оценка:
Здравствуйте, AstroMan, Вы писали:

AM>Очевидно, что CBCGMDIFrameWnd имеет другую реализацию LoadFrame, чем CMDIFrameWnd.

AM>Классы CMultiDocTemplate регистрируются в CWinApp, странно, что доступ к ним возникает из LoadFrame.
AM>Может это косяк BCG? У меня старый MFC, в LoadFrame никаких KeyboardManager нет и все работает.

Действительно, я передал в дополнительный поток не CBCGMDIFrameWnd, а CMDIFrameWnd и фрейм создался, только вот все равно есть какая-то проблема при создании ChildFrame дочернего окна в дополнительном потоке, все равно какой-то Assert вываливается и кажется где-то при прорисовке.
Если ваш проект не коммерческий, про который вы говорили в качестве примера, то пожалуйста дайте мне исходники.
Re[11]: Основной MDIFrame в отдельном потоке
От: AstroMan  
Дата: 11.09.08 14:24
Оценка:
Здравствуйте, TatarDozor, Вы писали:

TD>Если ваш проект не коммерческий, про который вы говорили в качестве примера, то пожалуйста дайте мне исходники.


К сожалению, коммерческий
Re[12]: Основной MDIFrame в отдельном потоке
От: TatarDozor  
Дата: 15.09.08 06:14
Оценка:
Здравствуйте, AstroMan, Вы писали:

Тогда может дадите кусок кода где вызывается функция создания фрейма, мне интересно заполнение параметров? Вы тоже в отдельном потоке запускаете? И если можно кусок запуска потока?
Re[12]: Основной MDIFrame в отдельном потоке
От: TatarDozor  
Дата: 18.09.08 10:30
Оценка:
Здравствуйте, AstroMan

Я разобрался, дело действительно было в BCG!! а также я неправильно работал с шаблоном документов в дополнительном потоке, спасибо за подсказку
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.