ATL 和 MFC 更改:ATL 7.0 和 MFC 7.0
更新:2007 年 11 月
注意 本主题中提到的某些功能在当前的 Visual C++ 版本中可能已不存在。
自从 Visual C++ 6.0 以来,已经对 ATL 和 MFC 库进行了许多改进。这些更改中的某些更改可能会导致现有代码无法运行。
DLL 不兼容性
作为 Visual C++ .NET 2002 的一部分提供的 ATL 和 MFC DLL 文件已分别被重命名为 ATL70.dll 和 MFC70.dll。
Visual C++ .NET ATL 和 MFC 类与早期版本的相同类之间没有二进制兼容性,因此任何使用 mfc42.dll 生成的源代码必须用 Visual Studio .NET 重新生成。应用程序使用的任何 DLL 或 LIB 文件也必须用 Visual Studio .NET 重新生成。
例如,使用 Visual C++ 6.0 生成的、包含导出函数(该函数将 CString 用作参数)的库在与 Visual C++ .NET 项目链接时将产生无法解析的外部链接。
ATL 模块类
ATL 3.0 提供了 CComModule 类。在 ATL 7.0 中,以前由 CComModule 提供的功能由若干新类处理。有关更多信息,请参见 ATL 模块类。
字符串转换
Visual C++ 6.0 中 ATL 3.0 和 ATL 3.0 以前的 ATL 版本中,使用 atlconv.h 中的宏的字符串转换始终是使用系统 (CP_ACP) 的 ANSI 代码页执行的。从 Visual C++ .NET 中的 ATL 7.0 开始,字符串转换将使用当前线程的默认 ANSI 代码页执行,除非定义了 _CONVERSION_DONT_USE_THREAD_LOCALE(此情况下,如以前一样使用系统的 ANSI 代码页)。
请注意,字符串转换类(如 CW2AEX)使您得以将用于转换的代码页传递给它们的构造函数。如果未指定代码页,这些类使用与宏相同的代码页。
有关更多信息,请参见 ATL 和 MFC 字符串转换宏。
CException 现在是抽象基类
CException 在 Microsoft 基础类库中是所有异常的基类。因为 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 的 UNICODE 和 ANSI 版本(CStringW 和 CStringA)。若要标记任何由隐式转换导致的不必要的系统开销,采用反向类型(如带 UNICODE 参数的 CStringA,或者带 ANSI 参数的 CStringW)的构造函数现在在 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);
从 stdafx.h 中移除 #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS 行。
CTime 更改
CTime Class 使用基础的 __time64_t 数据类型。在 MFC 6.0 中,CTime 使用 time_t 数据类型,当时是一种 32 位类型。进行这一更改的原因是为了支持 2038 年 1 月 19 日 3:14:07 以后的时间。
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 无法解析的外部符号错误
当调用采用 wchar_t 类型的静态库或 DLL 中的函数时(请注意,BSTR 和 LPWSTR 解析为 wchar_t*),可能会遇到“LNK2001 无法解析的外部符号错误”。
此错误由 /Zc:wchar_t 编译器选项引起,默认情况下此选项在新的 MFC 项目中设置为打开。此选项使编译器将 wchar_t 当作本机类型。在 Visual C++ .NET 以前的版本中,wchar_t 被当作 unsigned short。
如果主项目和库不使用相同的 /Zc:wchar_t 设置,将导致函数签名不匹配。要避免此问题,请使用 /Zc:wchar_t 编译器选项重新生成该库,或使用“属性页”对话框中**“语言”属性页上的“将 wchar_t 视为内置类型”**设置在主项目中将该选项关闭。
Boolean 表达式现在类型为 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 实现的,因此您必须用应用程序重新发布该 DLL。
ON_MESSAGE 更改
ON_MESSAGE 宏中的函数参数必须与类型 afx_msg LRESULT (CWnd::*)(WPARAM, LPARAM) 匹配。
OLE DB 模板更改
您还可以在知识库文章“INFO: Porting Issues with Visual Studio .NET OLE DB Provider Template Classes”(Q321743)(“信息:与 Visual Studio .NET OLE DB 提供程序模板类有关的移植问题”(Q321743))中找到与 OLE DB 模板中的更改有关的更多信息。知识库文章位于 MSDN Library CD-ROM 中或 https://support.microsoft.com/default.aspx?ln=zh-cn 上。
OLE DB 使用者类和模板
通常,访问器类必须实现附加成员。只有在手动实现自己的访问器类时这才是必需的。如果您的访问器类派生自 CAccessor,则不需要这样做。
旧行为 |
新行为 |
---|---|
CRowset 是一个类。 |
CRowset 是类模板并采用一个参数 TAccessor,表示访问器类。 |
CBulkRowset 是一个类。 |
CBulkRowset 是类模板。 |
CArrayRowset 的基类是模板参数(默认值为 CRowset)。 |
CArrayRowset 始终从 CBulkRowset 派生。 |
CDynamicAccessor::GetColumnInfo 带三个参数。 |
CDynamicAccessor::GetColumnInfo 具有采用附加参数 ppStringsBuffer 的新形式。使用此参数消除内存泄漏。不赞成使用旧方法。 |
CAccessorRowset 模板的第二个参数 Rowset 是行集合类。 |
CAccessorRowset 模板的第二个参数 TRowset 是行集类模板。 |
CTable 模板的第二个参数 Rowset 是行集合类。 |
CTable 模板的第二个参数 TRowset 是行集类模板。 |
CCommand 模板的第二个参数 Rowset 是行集合类。 |
CCommand 模板的第二个参数 TRowset 是行集类模板。 |
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)派生。 |