Изменения в библиотеках ATL и MFC. ATL 7.0 и MFC 7.0
Обновлен: Ноябрь 2007
Примечание Некоторые возможности, упомянутые в данном разделе, могут пока не поддерживаться в текущей версии Visual C++.
Со времени выпуска Visual C++ 6.0 в библиотеках ATL и MFC было произведено много усовершенствований. Некоторые из них могут нарушить существующий код.
Несовместимости в библиотеках DLL
DLL-файлы библиотек ATL и MFC, поставляемые вместе с Visual C++ .NET 2002, переименованы как ATL70.dll и MFC70.dll.
Классы библиотек ATL и MFC в Visual C++ .NET несовместимы на уровне машинных кодов с теми же классами из предыдущих версий, следовательно каждый исходный код, построенный с использованием mfc42.dll, необходимо заново построить в Visual Studio .NET. Необходимо также заново построить в Visual Studio .NET все DLL- и LIB-файлы, используемые приложением.
Например, построенная в Visual C++ 6.0 библиотека, в которой содержится экспортированная функция, принимающая CString в качестве параметра, при связывании с проектом Visual C++ .NET будет давать неразрешенный внешний ресурс.
Классы модуля библиотеки ATL
В библиотеке ATL 3.0 предоставлялся класс CComModule. В библиотеке ATL 7.0 возможности, ранее предоставлявшиеся классом CComModule, теперь распределены на несколько новых классов. Дополнительные сведения см. в разделе Классы модуля библиотеки ATL.
Преобразования строк
В предыдущих версиях библиотеки ATL вплоть до версии 3.0 в Visual C++ 6.0 преобразования строк с использованием макросов из atlconv.h всегда выполнялись с помощью системной кодовой страницы ANSI (CP_ACP). Начиная с версии библиотеки ATL 7.0 в Visual C++ .NET, пока не задан параметр _CONVERSION_DONT_USE_THREAD_LOCALE, преобразования строк выполняются с помощью кодовой страницы ANSI, заданной по умолчанию для текущего потока; если же этот параметр задан, то используется системная кодовая страница ANSI, как и раньше.
Обратите внимание, что классы преобразования строк, например CW2AEX, позволяют передавать кодовую страницу в их конструкторы для использования в целях преобразования. Если кодовая страница не задана, то классы используют ту же кодовую страницу, что и макросы.
Дополнительные сведения см. в разделе Макросы преобразования строк библиотек ATL и MFC.
Класс CException теперь является абстрактным базовым классом
Базовым классом для всех исключений в библиотеке Microsoft Foundation Class является класс CException. Так как класс CException теперь является абстрактным базовым классом, невозможно создавать объекты непосредственно класса CException, необходимо создавать объекты производных классов. При создании объекта напрямую возникает ошибка. Дополнительные сведения см. в описании класса CException.
Преобразование из BSTR в CString
В Visual C++ 6.0 было допустимо использовать следующий код:
BSTR bstr = SysAllocString(L"Hello");
CString str = bstr;
SysFreeString(bstr);
В новых проектах Visual C++ .NET этот код приведет к следующей ошибке построений ANSI:
error C2440: 'initializing' : cannot convert from 'BSTR' to
'ATL::CStringT<BaseType,StringTraits>'
Теперь имеются версии CString для Юникода и ANSI (CStringW и CStringA соответственно). Чтобы пометить любые ненужные служебные данные, внесенные неявными преобразованиями, конструкторы, принимающие обратный тип (например CStringA, принимающий аргумент Юникод, или CStringW, принимающий ANSI-аргумент), теперь помечаются как явные с помощью следующей записи в stdafx.h:
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS
Чтобы избежать этой ошибки, необходимо выполнить одно из перечисленных действий:
С помощью CStringW избежать преобразования:
BSTR bstr = SysAllocString(L"Hello"); CStringW str = bstr; SysFreeString(bstr);
Явно вызвать конструктор:
BSTR bstr = SysAllocString(L"Hello"); CString str = CString(bstr); SysFreeString(bstr);
Удалить строку #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS из файла stdafx.h.
Изменения класса CTime
CTime Class использует основной тип данных __time64_t. В MFC 6.0 класс CTime использовал тип данных time_t, который тогда имел 32-разрядный тип. Изменение вызвано необходимостью поддержки времени после 3:14:07 19 января 2038.
Изменения метода CComEnumImpl::Skip
В версиях ATL до 7.0 метод CComEnumImpl::Skip не возвращал правильный код ошибки для введенного значения 0. Кроме того, этот метод выполнял обработку больших введенных значений несогласованно. В ATL 7.0 эти ошибки были устранены.
Утверждения CWnd::DestroyWindow
При отображении подсказки в окне CWnd::DestroyWindow возникало сообщение об ошибке. В результате в MFC 7.0 из AFX_THREAD_STATE в AFX_MODULE_THREAD_STATE были перемещены следующие переменные-члены:
CToolTipCtrl* m_pToolTip.
CWnd* m_pLastHit.
int m_nLastHit.
TOOLINFO m_lastInfo.
int m_nLastStatus.
CControlBar* m_pLastStatus.
Ошибка LNK2001: неразрешенный внешний символ
При вызове в статической библиотеке или библиотеке DLL функции, которая принимает тип wchar_t (обратите внимание, что BSTR и LPWSTR установлены в wchar_t*), можно было получить ошибку с номером LNK2001 и сообщением о неразрешенном внешнем символе.
Эта ошибка вызывалась параметром компиляции /Zc:wchar_t, который в новых проектах MFC по умолчанию имеет значение "on". Этот параметр заставляет компилятор трактовать тип wchar_t как машинный. До Visual C++ .NET тип wchar_t трактовался как unsigned short (короткое значение без знака).
Если в главном проекте и в библиотеке используются разные установки параметра /Zc:wchar_t, то это приводит к несоответствию в подписях функций. Чтобы исключить эту проблему, следует заново построить библиотеку с параметром компиляции /Zc:wchar_t compiler option или отключить этот параметр в главном проекте с помощью параметра Считать wchar_t встроенным типом на странице свойств Язык диалогового окна Страницы свойств.
Логические выражения теперь имеют тип bool, а не BOOL
Рассмотрим следующий класс.
class CMyClass : public CObject
{
BOOL bFlag;
void Serialize (CArchive& ar))
{
if (ar.IsStoring())
ar << (bFlag != FALSE); // breaking change
else
ar >> bFlag;
}
};
До Visual C++ .NET выражение bFlag != FALSE оценивалось как BOOL, и записывались четыре байта; в Visual C++ .NET это выражение оценивается как bool, и записывается один байт. Это означает, что программы, компилировавшиеся разными версиями компилятора, могут создавать взаимно несовместимые файлы данных.
Чтобы исключить эту проблему, следует привести это выражение к типу BOOL:
ar << (BOOL)(bFlag != FALSE);
CColorPropPage и CFontPropPage удалены
В предыдущих версиях библиотеки MFC элемент управления ActiveX отображал страницы свойств для свойств шрифта и цвета согласно GUID, заданному как CLSID_CColorPropPage и CLSID_CFontPropPage соответственно. Эти GUID указывали на классы CColorPropPage и CFontPropPage, которые больше не реализуются. Теперь следует использовать для GUID значения CLSID_StockColorPage и CLSID_StockFontPage. Эти установки реализуются библиотекой msstkprp.dll, так что необходимо поставлять эту библиотеку вместе с приложением.
Изменения ON_MESSAGE
Параметр функции в макросе ON_MESSAGE должен соответствовать типу afx_msg LRESULT (CWnd::*)(WPARAM, LPARAM).
Изменения шаблонов OLE DB
Сведения об изменениях шаблонов OLE DB см. в статье базы знаний "INFO: вопросы переноса в Visual Studio .NET классов шаблонов поставщика OLE DB" (номер Q321743). Статьи базы знаний можно найти на компакт-диске библиотеки MSDN или по ссылке https://support.microsoft.com/.
Классы и шаблоны объектов-получателей OLE DB
Общее замечание: класс методов доступа должен реализовывать дополнительные члены. Это необходимо только в том случае, если собственный класс методов доступа реализуется вручную. Если класс методов доступа является производным от CAccessor, то эти действия не нужны.
Старое поведение |
Новое поведение |
---|---|
CRowset является классом. |
CRowset является шаблоном класса и принимает один параметр, класс методов доступа TAccessor. |
CBulkRowset является классом. |
CBulkRowset является шаблоном класса. |
Базовый класс CArrayRowset был параметром шаблона (значение по умолчанию было CRowset). |
CArrayRowset всегда является производным от CBulkRowset. |
CDynamicAccessor::GetColumnInfo принимал три параметра. |
CDynamicAccessor::GetColumnInfo имеет новую форму, принимающую дополнительный параметр ppStringsBuffer. Использование этого параметра исключает утечку памяти. Старый метод не рекомендуется. |
Rowset, второй параметр шаблона CAccessorRowset, является классом набора строк. |
TRowset, второй параметр шаблона CAccessorRowset, является шаблоном класса набора строк. |
Rowset, второй параметр шаблона CTable, является классом набора строк. |
TRowset, второй параметр шаблона CTable, является шаблоном класса набора строк. |
Rowset, второй параметр шаблона CCommand, является классом набора строк. |
TRowset, второй параметр шаблона CCommand, является шаблоном класса набора строк. |
Макрос DEFINE_COMMAND |
Макрос DEFINE_COMMAND не рекомендуется. Используйте вместо него DEFINE_COMMAND_EX. |
Классы и шаблоны поставщика OLE DB
Со времени выпуска Visual C++ 6.0 внутренняя реализация многих интерфейсов и методов изменилась. Это может привести к возникновению проблем совместимости, в зависимости от того, переопределяет ли приложение эти методы.
Старое поведение |
Новое поведение |
---|---|
Реализация набора строк/метода использовала классы CSimpleMap/CSimpleArray. Предоставляемые пользователю классы коллекций должны были быть совместимыми с CSimpleMap/CSimpleArray. |
Реализация набора строк/метода использует классы CAtlMap/CAtlArray. Предоставляемые пользователю классы коллекций должны быть совместимыми с CAtlMap/CAtlArray. Кроме того, код, который вызывает методы этих классов коллекций, должен быть пересмотрен, так как есть существенная разница между классами CAtl* и CSimple* (параметры, возвращаемые значения и т. п.), которая может привести к ошибкам времени выполнения. |
ICommandImpl был производным от ICommand. |
ICommandImpl является шаблоном, производным от аргумента CommandBase шаблона (значение по умолчанию – ICommand). |
ICommandTextImpl был производным от ICommandImpl<ICommandImpl<T>. |
ICommandTextImpl является производным от ICommandImpl<ICommandImpl<T, ICommandText>. Обратите внимание, что здесь ICommandImpl является производным от ICommandText (а не от значения по умолчанию ICommand). |