Re: CAxPropertyPage
От: Alexey Goncharov Россия  
Дата: 27.11.03 21:10
Оценка: 17 (2)
Здравствуйте, Tumypka, Вы писали:

T>После перехода на Visual Studio 7 в диалоге, основанном на CAxPropertyPage, не создаются ActiveX'ы. Никакие. Как будто их и не было. Что очень странно, т.к. всё это прекрасно компилировалось и работало под VC6 и WTL 7. После этого я попробовал создать совсем простенькое тестовое приложение


Поковырявшись в исходниках ATL я разобрался, в чем тут было дело.

В ATL 3.0 обработка ActiveX-контролов в диалогах реализована следующим образом:
CAxDialogImpl<>::DoModal вызывает AtlAxDialogBox, а тот в свою очередь _DialogSplitHelper::SplitDialogTemplate
Этот метод занимается в том числе и обработкой ActiveX-контролов в шаблоне диалога — заменяет их на вспомогательные окошки хоста (класс окна "AtlAxWin")

В ATL 7.0 из CAxDialogImpl<>::DoModal также в конце концов вызывается _DialogSplitHelper::SplitDialogTemplate
Но здесь весь код по обработке ActiveX-контролов вынесен из SplitDialogTemplate в CAxDialogImpl<>::CreateActiveXControls, который занимается собственно их созданием. Он в свою очередь вызывается при получении сообщения WM_INITDIALOG

А теперь самое интересное — как рализован CAxPropertyPageImpl<> из WTL 7.0
В его конструкторе стоит вызов _DialogSplitHelper::SplitDialogTemplate
Но в ATL 7.0 в этом методе нет обработки ActiveX-контролов, в результате чего они в страницах свойств просто не создаются.

Исправить это можно двумя способами :
1) Написать свой наследник CAxPropertyPageImpl<>, добавив аналог CreateActiveXControls из CAxDialogImpl<> и вызывать его при получении WM_INITDIALOG
2) Заменить в конструкторе CAxPropertyPageImpl<> вызов _DialogSplitHelper::SplitDialogTemplate, позаимствовав его реализацию из ATL 3.0

IMHO первый вариант лучше, поэтому привожу его реализацию:

namespace WTL
{

template <class T, class TBase = CPropertyPageWindow>
class ATL_NO_VTABLE CFixAxPropertyPageImpl : public CAxPropertyPageImpl< T, TBase >
{
  typedef CAxPropertyPageImpl< T, TBase > BaseClass ;
  typedef CFixAxPropertyPageImpl< T, TBase > ThisClass ;
public:
  CFixAxPropertyPageImpl(_U_STRINGorID title = (LPCTSTR)NULL) : BaseClass(title) {}

#if _ATL_VER >= 0x0700
  int GetIDD()
  {
    return( static_cast<T*>(this)->IDD );
  }

  virtual DLGPROC GetDialogProc()
  {
    return DialogProc;
  }

  static INT_PTR CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  {
    ThisClass* pThis = (ThisClass*)hWnd;
    if (uMsg == WM_INITDIALOG)
    {
      HRESULT hr;
      if (FAILED(hr = pThis->CreateActiveXControls(pThis->GetIDD())))
      {
        pThis->DestroyWindow();
        SetLastError(hr & 0x0000FFFF);
        return FALSE;
      }
    }
    return CDialogImplBaseT< TBase >::DialogProc(hWnd, uMsg, wParam, lParam);
  }

  virtual HRESULT CreateActiveXControls(UINT nID)
  {
    // код этого метода целиком заимствован из CAxDialogImpl<>::CreateActiveXControls
    // для краткости здесь он не приводится
  }
#endif
} ;

}; //namespace WTL


Везде в страницах свойств с ActiveX-контролами теперь необходимо использовать CFixAxPropertyPageImpl вместо CAxPropertyPageImpl:

T>class CPage1 :
T>    public CFixAxPropertyPageImpl<CPage1>
T>{
T>public:
T>    CPage1() 
T>    {
T>    }

T>    enum {IDD = IDD_PAGE1};

T>    BEGIN_MSG_MAP(CPage1)
T>        COMMAND_HANDLER(IDOK, BN_CLICKED, OnClickedOK)
T>        COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnClickedCancel)
T>        CHAIN_MSG_MAP(CFixAxPropertyPageImpl<CPage1>)
T>    END_MSG_MAP()

T>      // ...
T>};


Работоспособность класса проверялась в Visual Studio 98 и Visual Studio .NET 2002, по идее он должен работать без изменений и в Visual Studio .NET 2003

И еще небольшое дополнение.
Вам необходимо добавить в проект .idl с одной лишь пустой library, а ее LIBID передать третьим аргументом в вызове _Module.Init
Если этого не сделать — появится куча ATLASSERT'ов в atlcom.h, строка 3552
Это особенность ATL 7.0
CAxPropertyPage
От: Tumypka  
Дата: 13.11.03 13:29
Оценка:
После перехода на Visual Studio 7 в диалоге, основанном на CAxPropertyPage, не создаются ActiveX'ы. Никакие. Как будто их и не было. Что очень странно, т.к. всё это прекрасно компилировалось и работало под VC6 и WTL 7. После этого я попробовал создать совсем простенькое тестовое приложение

class CPage1 :
    public CAxPropertyPageImpl<CPage1>
{
public:
    CPage1() 
    {
    }

    enum {IDD = IDD_PAGE1};

    BEGIN_MSG_MAP(CPage1)
        COMMAND_HANDLER(IDOK, BN_CLICKED, OnClickedOK)
        COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnClickedCancel)
        CHAIN_MSG_MAP(CAxPropertyPageImpl<CPage1>)
    END_MSG_MAP()

    LRESULT OnClickedOK(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
    {
        EndDialog(hWndCtl, wID);
        return 0;
    }

    LRESULT OnClickedCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
    {
        EndDialog(hWndCtl, wID);
        return 0;
    }
};

class CMainDlg : 
    public CPropertySheetImpl<CMainDlg>
{
public:
    CMainDlg()
    {
        AddPage(m_page1);
    }

    BEGIN_MSG_MAP(CMainDlg)
        CHAIN_MSG_MAP(CPropertySheetImpl<CMainDlg>)
    END_MSG_MAP()

    CPage1 m_page1;
};

CAppModule _Module;

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
    HRESULT hRes = ::CoInitialize(NULL);

    // this resolves ATL window thunking problem when Microsoft Layer for Unicode (MSLU) is used
    ::DefWindowProc(NULL, 0, 0, 0L);

    AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES);    // add flags to support other controls

    hRes = _Module.Init(NULL, hInstance);
    ATLASSERT(SUCCEEDED(hRes));

    CMainDlg dlg;
    int nRet = dlg.DoModal();

    _Module.Term();
    ::CoUninitialize();

    return nRet;
}


— опять неудача. Любые ActiveX'ы — мои ли, майкросовтовские — просто не создаются. Никаких сообщений про ошибки при этом нету. Что же не так?
Re: CAxPropertyPage
От: SilentCat Россия  
Дата: 25.11.03 18:49
Оценка:
Здравствуйте, Tumypka, Вы писали:

Попробуйте вызвать AtlAxWinInit перед DoModal :

T>int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
T>{
T>    HRESULT hRes = ::CoInitialize(NULL);

T>    // this resolves ATL window thunking problem when Microsoft Layer for Unicode (MSLU) is used
T>    ::DefWindowProc(NULL, 0, 0, 0L);

T>    AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES);    // add flags to support other controls

T>    hRes = _Module.Init(NULL, hInstance);
T>    ATLASSERT(SUCCEEDED(hRes));

      AtlAxWinInit();

T>    CMainDlg dlg;
T>    int nRet = dlg.DoModal();

      AtlAxWinTerm(); 

T>    _Module.Term();
T>    ::CoUninitialize();

T>    return nRet;
T>}
Re[2]: CAxPropertyPage
От: Tumypka  
Дата: 26.11.03 10:50
Оценка:
Здравствуйте, SilentCat, Вы писали:

Нет, вызов этой функции ничего не меняет. К тому же она вызывается в конструкторе CAxPropertyPageImpl

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


SC>Попробуйте вызвать AtlAxWinInit перед DoModal :
Re[3]: CAxPropertyPage
От: Tumypka  
Дата: 26.11.03 11:00
Оценка:
Кстати, выставление флажка NoFailCreate в положение True ничего не меняет. Может кому-то это что-то скажет? Так же пытался откомпилить это проект на другом компьютере (Win2000,VC NET) — то же самое.
Re[2]: CAxPropertyPage
От: Tumypka  
Дата: 29.11.03 09:29
Оценка:
Здравствуйте, Alexey Goncharov.

Огромное спасибо, всё правильно, всё отлично работает.

AG>И еще небольшое дополнение.

AG>Вам необходимо добавить в проект .idl с одной лишь пустой library, а ее LIBID передать третьим аргументом в вызове _Module.Init
AG>Если этого не сделать — появится куча ATLASSERT'ов в atlcom.h, строка 3552
AG>Это особенность ATL 7.0

У меня как раз Visual Studio .Net 2003 и соответственно ATL 7.1 . Судя по всему там это уже пофиксили, во всяком случае ASSERT'ов нет и по соответствующей строке ничего похожего на ASSERT тоже нет.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.