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 重新建置。
例如,一個包含匯出函式的程式庫以 CString 為參數,並使用 Visual C++ 6.0 建置,其將在與 Visual C++ .NET 專案連結期間,產生無法在外部之程式庫中找到或連結所需之資料型別或函式。
ATL 模組類別
ATL 3.0 提供 CComModule 類別。在 ATL 7.0 中,前一版 CComModule 提供的功能現在由許多新的類別來處理。如需詳細資訊,請參閱 ATL 模組類別。
字串轉換
在 Visual C++ 6.0 的 ATL 3.0 版及其之後版本中,使用 atlconv.h 的巨集所做的字串轉換,一律使用系統 (CP_ACP) 的 ANSI 字碼頁 (Code Page) 來執行。從 Visual C++ .NET 的 ATL 7.0 開始,字串轉換使用目前執行緒預設的 ANSI 字碼頁來執行,除非定義了 _CONVERSION_DONT_USE_THREAD_LOCALE,才會像以前一樣使用系統的 ANSI 字碼頁。
請注意字串轉換類別 (例如 CW2AEX ),讓您可以傳送轉換所要使用的字碼頁給其建構函式。如果沒有指定字碼頁,類別就會使用跟巨集一樣的字碼頁。
如需詳細資訊,請參閱 ATL 和 MFC 字串轉換巨集。
CException 現在是抽象基底類別
CException 是 MFC 程式庫中所有例外狀況的基底類別。由於 CException 現在是抽象基底類別,因此不能直接建立 CException 物件,而必須建立衍生類別 (Derived Class) 物件。若直接建立物件,則將會接收到錯誤。如需詳細資訊,請參閱 CException。
將 BSTR 轉換成 CString
在 Visual C++ 6.0 中,使用下列程式碼是可以接受的:
BSTR bstr = SysAllocString(L"Hello");
CString str = bstr;
SysFreeString(bstr);
如果 Visual C++ .NET 之下有新的專案,則會導致 ANSI 組建 (Build) 中出現下列錯誤:
error C2440: 'initializing' : cannot convert from 'BSTR' to
'ATL::CStringT<BaseType,StringTraits>'
現在有 UNICODE 和 ANSI 版本的 CString (CStringW 和 CStringA)。若要為隱含轉換所引起的不必要額外負載設立旗標,那麼接受反向型別的建構函式 (例如,CStringA 會接受 UNICODE 引數,或是 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);
從 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 中,下列的成員變數 (Member Variable) 會從 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 當做 Built-in 型別] 設定,在主要專案中關掉此選項。
布林運算式現在是 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 類別,但是目前已不再實作這兩個類別。取而代之的是使用 CLSID_StockColorPage 和 CLSID_StockFontPage GUID。這些 GUID 是透過 msstkprp.dll 進行實作,所以您必須使用應用程式轉散發該 DLL。
ON_MESSAGE 的變更
ON_MESSAGE 巨集中的函式參數,必須符合 afx_msg LRESULT (CWnd::*)(WPARAM, LPARAM) 型別。
OLE DB 樣板的變更
您可以找到有關 OLE DB 樣板的變更,請參閱知識庫 (Knowledge Base) 文件 Q321743「INFO: Porting Issues with Visual Studio .NET OLE DB Provider Template Classes」。您可以在 MSDN Library CD-ROM 或是在 https://support.microsoft.com/default.aspx?ln=zh-tw 中找到知識庫文件。
OLE DB 消費者類別和樣板
一般注意事項:存取子類別必須實作其他成員。這個動作僅在您以手動方式實作自己的存取子類別的情況下才有必要。如果您的存取子類別是衍生自 CAccessor,則不需要這項動作。
舊的行為 |
新的行為 |
---|---|
CRowset 是類別。 |
CRowset 是類別樣板,取用一個參數 TAccessor (屬於存取子類別)。 |
CBulkRowset 是類別。 |
CBulkRowset 是類別樣板。 |
CArrayRowset 的基底類別是樣板參數 (預設值為 CRowset)。 |
CArrayRowset 通常衍生自 CBulkRowset。 |
CDynamicAccessor::GetColumnInfo 會使用三個參數。 |
CDynamicAccessor::GetColumnInfo 採用新的格式,取用額外的 ppStringsBuffer 參數。使用這個參數可消弭記憶體遺漏 (Memory Leak) 的情況。已取代舊的方法。 |
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)。 |