После перехода на 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'ы — мои ли, майкросовтовские — просто не создаются. Никаких сообщений про ошибки при этом нету. Что же не так?
Здравствуйте, 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>}
Кстати, выставление флажка NoFailCreate в положение True ничего не меняет. Может кому-то это что-то скажет? Так же пытался откомпилить это проект на другом компьютере (Win2000,VC NET) — то же самое.
Здравствуйте, 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
Здравствуйте, 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 тоже нет.