ATL浅谈(二)
由于上一篇写的太浅,这篇谈点实际的东西。
ATL和STL一样,是由一些小巧、高效和灵活的类组成的集合。但是,伴随着权利而来的是职责。和STL一样,只有有经验的C++程序员(最好的STL经验)才能有效地使用它。
ATL给我们提供了什么?那些希望在对COM一无所知的情况下建立COM对象的人,ATL不适合他们。事实上,使用ATL意味着要非常熟悉C++中的COM以及ATL本身的实现细节。
ATL给我提供了什么利器呢?
要组装一个优秀组件提供给用户首先自己必须知道自己有什么精良的装备。
1、 为维护代价很高的数据类型(例如:接口指针、VARIANT、BSTR和HWND)提供了包装类(对应的类:(CComPtr、CComQIPtr和CComDispatchDriver智能指针类)、ComVariant、CComBSTR、(CWindow、CwindowImpl、CDialogImpl、CContainedWindow、和CAxWindow))。
2、 提供一些类,他们实现了诸如IUnknown、IClassFactory、IDispath、IPersistXxx、IConnectionPointContainer和IEnumXxx这些基本的COM接口(由于这里牵涉东西太多后面会一一讲解ATL对应的类)。
3、 管理COM服务的类(CComModule类),他们用于暴露类对象、自注册和服务器的生命周期管理。
4、 节省手工录入的向导(wizard)。
这里讲一讲ATL维护的数据类型和智能指针
COM处了C语言中的数字类型之外还有一些其它的数据类型。其中最主要的三个是文本字符串(BSTR)、VARIANT数据类型和接口指针。ATL针对这些数据类型提供了有用的封装类。
在C++编程中处理文本数据类型相当痛苦。主要是因为不只是一个文本数据类型,不同的操作系统和编程语言会在自己的文本类型上引入其它的语义(eg:以NUL字符结尾或者增加一个长度前缀)。当我们操作和使用文本的时候首先需要知道的是文本由什么类型的字符组成。第二个就是,我们携程序时使用什么字符集。因为源代码使用的字符集不一定要于运行程序的操作系统首选的字符集相同。当然相同会更方便。第三就是,决定文本字符串的长度。一些语言(C和C++)和一些操作系统(Windows 9x/NT和UNIX)使用结束字符NUL。但是vb、java和pascal都是用的是前缀作字符串的长度。
因为COM是跨平台跨语言的,很显然,它就要制定一些标准的文本数据类型,并且在不同的环境下所有的COM对象都能够理解这些类型。
COM是用OLECHAR字符数据类型。COM字符串是一个以NUL字符作为结尾的OLECHAR字符数组,并且指向这种字符的指针是一个LPOLECHAR。作为一个原则,传递给COM接口方法的文本字符串参数类型必须是LPOLECHAR类型。如果一个方法不需要改变着个字符串,那么就用LPCOLECHAR类型,也就是指向OLECHAR数组的常量指针。有些人可能感觉迷茫了你不是说是BSTR吗?哈哈!是的,看看BSTR的定义就知道了(typedef OLECHAR FAR* BSTR;)。BSTR(宽的双字节,但在苹果的PowerMac是单字节的)其实是个包含长度前缀的OLECHAR数组。
我们会经常碰到BSTR类型和我们编写代码中使用的字符类型不同,这就要我们自己来转换(把宽字符转传成单字符的),不过,ATL为我们提供了一些字符转换宏(例如:A2OLE)。我们也经常更改编译选项(例如:Window NT Unicode build),可能导致以前不需要转换的字符串代码现在却需要转换,或者相反。基于window的COM组件一般混和使用四种文本类型。下面是ATL转换宏中这四种类型的缩写:
T 表示指向Win32 TCHAR字符类型的指针
W 表示指向Unicode wchar_t字符类型的指针
A 表示指向 MBCS/ANSI char 字符类型的指针
OLE 表示指向COM OLECHAR 字符类型的指针
所有宏的名字使用都是“<源类型缩写>2<目标类型缩写>”
Eg:
#include //宏的头文件
//创建一个 CString
CString myCString(_T("My String"));
//必须加上这个宏在使用ATL提供的转换宏前
USES_CONVERSION;
BSTR y = SysAllocString(T2COLE(myCString))
……
SysFreeString(y); //最后必须释放掉
下面该讲到CComBSTR类了,它是ATL对BSTR的一个包装类,文件atlbase.h包含它的定义。这个类维护的唯一状态是一个BSTR类型的共有成员变量m_str.
//CComBSTR
Class CComBSTR
{
public:
BSTR m_str;
……
};
CComBSTR对象有8个构造函数。缺省的构造函数简单的把变量m_str初始化为NULL。析构函数调用SysFreeString释放包含在m_str变量中的任何BSTR。
CComBSTR() {m_str = NULL;}
~CComBSTR() {SysFreeString(m_str);}
使用最多的构造函数是:
CComBSTR(LPCOLESTR pSrc) {m_str = ::SysAllocString(pSrc);}
//看看下面代码的调用
CComBSTR str1(OLESTR(“This is a string of OLECHARs”));
这里对其它构造的用法就不一一介绍了,可能有些人已经不厌烦了。如果那里不会用或者有疑问可以来问我,我们一起探讨。我一般都用bstr_t类,和CComBSTR一样对BSTR进行了封装,下面给出一个经典的例子关于COM中各种字符串类型的用法,如果你以前对字符串的处理还不太清楚那么你看了下面代码估计不会在为了它头疼了!哈哈!好了看看吧!
void CXXXX::OnStrings()
{
//创建一个BSTR并赋值到一个Variant
//Create a BSTR and assign it to a Variant
BSTR x = SysAllocString(L"Hello");
VARIANT myVariant;
myVariant.vt = VT_BSTR; //设置VARIANT的数据类型
myVariant.bstrVal = x; //赋值给myVariant
SysFreeString(x); //记着释放
//创建一个CString并转换成一个variant类型
//Create a CString and change it to a variant;
CString myCString(_T("My String"));
CString mySecondString;
//必须在用ATL宏之前调用这个宏
//This is required to use the T2COLE macro.
USES_CONVERSION;
BSTR y = SysAllocString(T2COLE(myCString));
myVariant.bstrVal = y;
mySecondString = y;
SysFreeString(y);
_bstr_t str2="hjljljl";
char *str1 = (char*)str2;//char*是_bstr_t类的一个重载
wchar_t *str4 = (wchar_t*)str2;//wchar_t*是_bstr_t类的一个重载
CString str3=str1;
//创建两个BSTR让他们相加
//Create two BSTRs and add them.
BSTR a = SysAllocString(L"One two ");
BSTR b = SysAllocString(L"three four.");
_bstr_t my_bstr_t(a, TRUE);
my_bstr_t += b;
myVariant.bstrVal = my_bstr_t;
// or
myVariant.bstrVal = _bstr_t(a, FALSE) + b;
//把BSTR转换成CString
//Change a bstr to a CString.
CString ANewString(b);
//or if CString already exists.
myCString = b;
//CComBSTR的一些用法
//Use of CComBSTR
CComBSTR myCComBSTR(L"Hello");
myCComBSTR.Append(L", how are you?");
VARIANT varFromCCom;
varFromCCom.vt = VT_BSTR;
varFromCCom.bstrVal = myCComBSTR;
}
好了!下篇会继续接着说智能指针,由于前几天比较忙,所以没有时间写!可能有些人会说些的很滥哈哈!不过对一些人还是有用的!我的msn在上篇已经留了。