потомка CFormView в DLL
От: Аноним  
Дата: 25.05.10 04:13
Оценка:
Здравствуйте!

Имеется: обычное Doc\View приложение, View объекты в нём не от CView, а от CFormView.
Вопрос: Хотелось бы сделать потомки от CFormView во в нешних DLLках, читай плагины. Как более гарамотно\правильно вписать это в MFC и Doc\View?
Примечание: Пробовал делать ограниченный интерфес типа: несколько экспортируемых функций вида OnCreate, OnClose и т.д.,
но это довольно топорно и совершенно не покрывает функционал объекта, не вынесенного в отдельную DLLку.

25.05.10 12:03: Перенесено из 'C/C++'
Re: потомка CFormView в DLL
От: Андрей Россия  
Дата: 25.05.10 04:35
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте!


А>Имеется: обычное Doc\View приложение, View объекты в нём не от CView, а от CFormView.

А>Вопрос: Хотелось бы сделать потомки от CFormView во в нешних DLLках, читай плагины. Как более гарамотно\правильно вписать это в MFC и Doc\View?
А>Примечание: Пробовал делать ограниченный интерфес типа: несколько экспортируемых функций вида OnCreate, OnClose и т.д.,
А> но это довольно топорно и совершенно не покрывает функционал объекта, не вынесенного в отдельную DLLку.

лучше не использовать MFC-шную архитектуру Doc/View — уж больно монолитное приложение получается
это я тебе по собственному печальному опыту говорю — мы в свое время на нее завязались (это было давно и опыта у меня тогда было немного), теперь вот мучаемся

лучше какой-то велосипед с MVC изобрести (или готовую реализацию взять)
Re[2]: потомка CFormView в DLL
От: MasterZiv СССР  
Дата: 25.05.10 06:16
Оценка:
Андрей пишет:

> лучше не использовать MFC-шную архитектуру Doc/View — уж больно

> монолитное приложение получается

Это как-то обосновать можешь, или просто голословное утверждение ?
Как "монолитность" приложения измеряется, и почему это должно быть
плохо ? Может это как раз то, что нужно ?
Posted via RSDN NNTP Server 2.1 beta
Re: потомка CFormView в DLL
От: MasterZiv СССР  
Дата: 25.05.10 06:44
Оценка:
Аноним пишет:

> *Вопрос:* Хотелось бы сделать потомки от CFormView во в нешних DLLках,

> читай плагины. Как более гарамотно\правильно вписать это в MFC и Doc\View?

Как бы проблем с этим нет. Просто пишешь класс, экспортируешь его,
если надо, из DLL (declspec(dllexport)) -- и всё.

Написание View в приложении и в dll ничем почти не отличается.

Единственное в dll будет проблема с инициализацией MFC, в стандартных
визардом генерённых проектах это делается, а это не нужно, ежели
само приложение уже использует MFC и оно (MFC) уже проинициализировано.

Лучше использовать для создания проекта визард MFC Extention DLL,
а затем удалить все упоминания о AFX_extention. (но макра AFXEXT в проекте
должна остаться!)

#include "stdafx.h"
// --- УБРАТЬ !! #include <afxdllx.h> ---

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


// --- УБРАТЬ !! static AFX_EXTENSION_MODULE CtformutilsDLL = { NULL, NULL }; ---

extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
UNREFERENCED_PARAMETER(lpReserved);

if (dwReason == DLL_PROCESS_ATTACH)
{
// --- УБРАТЬ !! if (!AfxInitExtensionModule(CtformutilsDLL, hInstance))
// --- УБРАТЬ !! return 0;

// --- УБРАТЬ !! new CDynLinkLibrary(CtformutilsDLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
// --- УБРАТЬ !! AfxTermExtensionModule(CtformutilsDLL);
}
return 1;
}


Другой вариант -- создать просто Win32 DLL и добавить в проект макрос AFXEXT и
ссылки на библиотеки MFC руками.

Да, и не забудь, что в этом случае обязательно использовать MFC ТОЛЬКО в виде
DLL, и все плагины должны использовать ОДНУ И ТУ ЖЕ MFC, либо DEBUG, либо RELEASE.
Posted via RSDN NNTP Server 2.1 beta
Re[2]: потомка CFormView в DLL
От: AHOHUM  
Дата: 25.05.10 07:09
Оценка:
Здравствуйте, Андрей, Вы писали:

А>лучше не использовать MFC-шную архитектуру Doc/View — уж больно монолитное приложение получается

А>это я тебе по собственному печальному опыту говорю — мы в свое время на нее завязались (это было давно и опыта у меня тогда было немного), теперь вот мучаемся

А>лучше какой-то велосипед с MVC изобрести (или готовую реализацию взять)


К сожалению уже довольно много написано, да и Ribbon успел понравиться,
поэтому и интересуюсь в рамках Doc\View(.

1. Нельзя ли, как-нибудь, сабклассить:
SetWindowLong( ...GWL_WNDPROC...)

только в MFCшной обёртке?

2.Сделать что-то на подобии этого, в основном модуле:
CFormView *lpNewChild = reinterpret_cast<CFormView *>( Plugin->GetInstance() );


а в плагине, что-то на подобии:
class CPluginFormView: : public CFormView
{
...
}

CPluginFormView g_PluginFormView;

...
extern "C" __declspec( dllexport ) void *GetInstance()
{
  return &g_PluginFormView;
}

?
Ведь по сути мне надо подменить vtbl родительского класса — потомком, только не уверен что этого достаточно.
Re[2]: потомка CFormView в DLL
От: AHOHUM  
Дата: 25.05.10 07:12
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>Аноним пишет:


>> *Вопрос:* Хотелось бы сделать потомки от CFormView во в нешних DLLках,

>> читай плагины. Как более гарамотно\правильно вписать это в MFC и Doc\View?

MZ>Как бы проблем с этим нет. Просто пишешь класс, экспортируешь его,

MZ>если надо, из DLL (declspec(dllexport)) -- и всё.

MZ>Написание View в приложении и в dll ничем почти не отличается.


MZ>Единственное в dll будет проблема с инициализацией MFC, в стандартных

MZ>визардом генерённых проектах это делается, а это не нужно, ежели
MZ>само приложение уже использует MFC и оно (MFC) уже проинициализировано.

MZ>Лучше использовать для создания проекта визард MFC Extention DLL,

MZ>а затем удалить все упоминания о AFX_extention. (но макра AFXEXT в проекте
MZ>должна остаться!)

MZ>#include "stdafx.h"

MZ>// --- УБРАТЬ !! #include <afxdllx.h> ---

MZ>#ifdef _DEBUG

MZ>#define new DEBUG_NEW
MZ>#undef THIS_FILE
MZ>static char THIS_FILE[] = __FILE__;
MZ>#endif


MZ>// --- УБРАТЬ !! static AFX_EXTENSION_MODULE CtformutilsDLL = { NULL, NULL }; ---


MZ>extern "C" int APIENTRY

MZ>DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
MZ>{
MZ> UNREFERENCED_PARAMETER(lpReserved);

MZ> if (dwReason == DLL_PROCESS_ATTACH)

MZ> {
MZ> // --- УБРАТЬ !! if (!AfxInitExtensionModule(CtformutilsDLL, hInstance))
MZ> // --- УБРАТЬ !! return 0;

MZ> // --- УБРАТЬ !! new CDynLinkLibrary(CtformutilsDLL);

MZ> }
MZ> else if (dwReason == DLL_PROCESS_DETACH)
MZ> {
MZ> // --- УБРАТЬ !! AfxTermExtensionModule(CtformutilsDLL);
MZ> }
MZ> return 1;
MZ>}


MZ>Другой вариант -- создать просто Win32 DLL и добавить в проект макрос AFXEXT и

MZ>ссылки на библиотеки MFC руками.

MZ>Да, и не забудь, что в этом случае обязательно использовать MFC ТОЛЬКО в виде

MZ>DLL, и все плагины должны использовать ОДНУ И ТУ ЖЕ MFC, либо DEBUG, либо RELEASE.

Ого, спасибо. Сейчас попробую реализовать.
Re: потомка CFormView в DLL
От: rus blood Россия  
Дата: 25.05.10 07:14
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Имеется: обычное Doc\View приложение, View объекты в нём не от CView, а от CFormView.

А>Вопрос: Хотелось бы сделать потомки от CFormView во в нешних DLLках, читай плагины. Как более гарамотно\правильно вписать это в MFC и Doc\View?
А>Примечание: Пробовал делать ограниченный интерфес типа: несколько экспортируемых функций вида OnCreate, OnClose и т.д.,
А> но это довольно топорно и совершенно не покрывает функционал объекта, не вынесенного в отдельную DLLку.

Много-много лет назад, из спортивного интереса, делал подобную "плагинную" схему с MFC Doc/View.

В проекте приложения реализованы:
— свой класс шаблона документа на основе CMultiDocTemplate, который хранит HINSTANCE внешней библиотеки для загрузки ресурсов. В классе переопределен базовый виртуальный метод LoadTemplate.
— свой класс child-фрейма, который использует HINSTANCE своего шаблона для загрузки ресурсов. Переопределен метод LoadFrame, шаблон передается в контексте.

Проект плагина реализует классы документа и view, и экспортирует функции (plain C-функции), которые позволяют приложению получить
— кол-во наборов doc/view (если их несколько)
— указатели на runtime-классы document и view (RUNTIME_CLASS выдает указатель на статический объект, поэтому указатели "живут" пока загружен плагин).

Приложение загружает библиотеки плагинов, получает указатели на runtime-классы doc/view и регистрирует шаблоны в template manager (стандартном).


Планировалось, что это будет некая студия, где приложение предоставляет workspace, а плагины реализуют doc/view для обработки файлов различных типов. Пример работал, нормально создавались документы из плагинов. Дальше разрабатывать не стал.
Имею скафандр — готов путешествовать!
Re[3]: потомка CFormView в DLL
От: Андрей Россия  
Дата: 25.05.10 07:16
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>Андрей пишет:


>> лучше не использовать MFC-шную архитектуру Doc/View — уж больно

>> монолитное приложение получается

MZ>Это как-то обосновать можешь, или просто голословное утверждение ?

MZ>Как "монолитность" приложения измеряется, и почему это должно быть
MZ>плохо ? Может это как раз то, что нужно ?

чего-ж в этом хорошего?

Doc/View — это вырожденный случай архитектуры MVC, из которого как раз Controller и удалили
я не говорю, что это само по себе плохо
просто такой подход может очень быстро привести к тому, что View и Document будут очень тесно переплетены между собой

а архитектура Doc/View никак эти связи не ограничивает, к сожалению

все-таки более правильным мне представляется отделение мух от котлет, и использование для взаимодействия View и Document промежуточного слоя
Re[4]: потомка CFormView в DLL
От: AHOHUM  
Дата: 26.05.10 06:20
Оценка:
Хм, попробовал варианты указанные выше, это:

что касается основного модуля, в нём, как указал товарищ rus blood,
сделал свой класс производный от CMultiDocTemplate и изменил конструктор и
LoadTemplate:

class CPluginMultiDocTemplate : public CMultiDocTemplate
{
    HINSTANCE m_hPluginModule;

public:
    CPluginMultiDocTemplate( HINSTANCE hInst, UINT nIDResource, CRuntimeClass* pDocClass,
        CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass );

    virtual void LoadTemplate();
}; 

...

CPluginMultiDocTemplate::CPluginMultiDocTemplate( HINSTANCE hInst, UINT nIDResource, CRuntimeClass* pDocClass, 
                                                 CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass )
                                                 : CMultiDocTemplate( nIDResource, pDocClass, pFrameClass, pViewClass ) 
{
    m_hPluginModule = hInst;

    LoadTemplate();
}

void CPluginMultiDocTemplate::LoadTemplate()
{
    CMultiDocTemplate::LoadTemplate();

    if( m_hPluginModule != 0 && m_nIDResource != 0 && m_hMenuShared == NULL )
    {
        m_hMenuShared = ::LoadMenu( m_hPluginModule, MAKEINTRESOURCE( m_nIDResource ) );
        m_hAccelTable =    ::LoadAccelerators( m_hPluginModule, MAKEINTRESOURCE( m_nIDResource ) );
    }
}


Что касается DLL:
1. Создал MFC Extension DLL и почикал её, как указал товарищ MasterZiv.
2. Создал MFC Extension DLL и почикал её, и не менял инициализацию.
3. Создал MFC DLL статик-линкед

в ней добавил ксласс производный от CFormView,

#pragma once

#include "resource.h"

// CTestView form view

class CTestView : public CFormView
{
    DECLARE_DYNCREATE(CTestView)

protected:
    CTestView();           // protected constructor used by dynamic creation
    virtual ~CTestView();

public:
    enum { IDD = IDD_TESTVIEW };
#ifdef _DEBUG
    virtual void AssertValid() const;
#ifndef _WIN32_WCE
    virtual void Dump(CDumpContext& dc) const;
#endif
#endif

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

    DECLARE_MESSAGE_MAP()
};

...

#include "stdafx.h"
#include "resource.h"
#include "TestView.h"


// CTestView

IMPLEMENT_DYNCREATE(CTestView, CFormView)

CTestView::CTestView()
    : CFormView(CTestView::IDD)
{

}

CTestView::~CTestView()
{
}

void CTestView::DoDataExchange(CDataExchange* pDX)
{
    CFormView::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CTestView, CFormView)
END_MESSAGE_MAP()


// CTestView diagnostics

#ifdef _DEBUG
void CTestView::AssertValid() const
{
    CFormView::AssertValid();
}

#ifndef _WIN32_WCE
void CTestView::Dump(CDumpContext& dc) const
{
    CFormView::Dump(dc);
}
#endif
#endif //_DEBUG


в DllMain.cpp добавил
extern "C" __declspec( dllexport ) CRuntimeClass* GetInstance()
{
    return RUNTIME_CLASS( CTestView );
}


Итог:

в случае 1 и 2 не верно инициализируется цепочка наследия связей и происходит вылет на таком месте:

BOOL CRuntimeClass::IsDerivedFrom(const CRuntimeClass* pBaseClass) const
{
    ENSURE(this != NULL);
    ASSERT(AfxIsValidAddress(this, sizeof(CRuntimeClass), FALSE));
    ENSURE(pBaseClass != NULL);
    ASSERT(AfxIsValidAddress(pBaseClass, sizeof(CRuntimeClass), FALSE));

    // simple SI case
    const CRuntimeClass* pClassThis = this;
#ifdef _AFXDLL
    for (;;)
#else
    while (pClassThis != NULL)                      // Вот в этом цикле и ловим бед поинтеры
#endif
    {
        if (pClassThis == pBaseClass)
            return TRUE;
#ifdef _AFXDLL
        if (pClassThis->m_pfnGetBaseClass == NULL)
            break;
        pClassThis = (*pClassThis->m_pfnGetBaseClass)();
#else
        pClassThis = pClassThis->m_pBaseClass;
#endif
    }
    return FALSE;       // walked to the top, no match
}


в случае же 3 цепочка инициализируется верно, но дпже при сравнении CView с CView (вот тут — if (pClassThis == pBaseClass))
не проходит на тру.

В чём загвоздка? Или верно RUNTIME_CLASS делать в основном модуле, а не в DLL?
Re: потомка CFormView в DLL
От: AHOHUM  
Дата: 26.05.10 08:52
Оценка:
Нашёл доку с тем что мне нужно: здесь
Решение очень схоже с тем что предложил rus blood,
но обходиться проблема не совсем корректного поведения функций типа CRuntimeClass::IsDerivedFrom
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.