这里两个对接口指针的封装类,也就是智能指针,使用只能指针有如下优势:
1.析够函数自动释放接口指针
2.处理异常时智能指针可以自动析够
3.赋值操作可以自动处理释放、AddRef的资源管理
4.构造函数、类型转换等自动处理了类似QI等若干问题
CComQIPtr继承了CComPtr,使用CComPtr的地方都可以使用CComQIPtr,而且使用CComQIPtr的地方多数也可以使用CComPtr,需要注意的地方只是CComQIPtr的构造函数,如果类型参数是IUnknown,必须显式指定第二个参数,否则编译出错,这是我理解的区别了。
下面的代码我展示了我能想到的这个智能指针的方方面面,注意代码只是为了展示功能,也就是可以编译通过,但是执行未必正确。(IDispath特化这段是可以提出来编译运行的,只是需要我自己写的一个com,所以还是没有运行意义的。)
//代码说明:G.IDispatch特例化单拿出来出可以编译运行的(当然需要我自己写的com),其他运行会异常,只是展示用法,但求编译通过即可。
CComPtr
punk;
CComQIPtr
pdsp;
//类型转换中,punk的QueryInterface会被调用
pdsp = punk;
//CComPtr需要一个类型信息作为模板参数,而CComPtr额外需要一个IID指针作为模板参数,不过该参数为默认参数(&__uuidof
)
CComQIPtr
pdsp1;
CComQIPtr pdsp2;
CComQIPtr pdsp3;
//A.构造函数
CComPtr
ccpt1; //构造一个NUL
IDispatch* myprDsp = NULL;
CComPtr
ccpt2(myprDsp); //这里如果prUnk非NUL,则构造函数会调用一次prUnk->AddRef()
//不同类型的接口指针,构造函数会内部调用QI方法
CComQIPtr ccpt3(ccpt2); //当使用CComQIPtr类想让构造函数调用QI方法时,不加IID参数编译不过
CComPtr
test(ccpt2); //CComPtr没有这个问题
//B.赋值 赋值操作三部曲:1.当当前指针不为NUL,释放,2.当源指针不为NUL,AddRef,3.用源指针替换当前指针
ccpt3 = ccpt1;
//C.实例化对象的方法
CComPtr
ccpt4;
ccpt4.CoCreateInstance(CLSID_MySharp); //需要CLSID,如果使用聚合,还需要pUnkOuter,当然,一如com,你也可以再传一个参数dwClsContext
//D.资源管理
//1.资源释放
ccpt3 = NULL; //赋值NUL,释放资源
ccpt1.Release(); //调用Release显式释放
ccpt2 = NULL;
ccpt4.Release();
//2.指针赋值,拷贝出一个新的生命周期完全独立的指针
IUnknown* myprUnk;
punk.CopyTo(&myprUnk);
//3.类型转换,CComPtr的类型转换没有在目标类型指针上自动调用AddRef方法
punk = (IUnknown*)ccpt4;
punk = ccpt4; //两种方式都能接受,内部都是调用了QI方法
//4.转移控制权
myprUnk = punk.Detach(); //Detach
punk.Attach(myprUnk); //Attach
//E.其他操作
IDispatch* myprDsp2;
//1.简化的查询方法
punk.QueryInterface(&myprDsp2);
//2.在两个IUnknow上查询是否是同一com对象,它会负责自动转换(自动调用QI)
if (punk.IsEqualObject(ccpt4))
{
}
//3.处理聚合
punk.SetSite(myprUnk);
//4.处理连接点关联
DWORD dwCookie;
punk.Advise(myprDsp2,IID_IDispatch,&dwCookie);
//F.比较操作,CComPtr可以用!检查NUL情况,其他operator<、operator==、operator!=都是为了适用STL而定义的
if (!punk)
{
}
//G.IDispath特化
//1.访问属性示例
CoInitialize(NULL);
CComPtr
c17ptr; //必须使用IDispach指针才可以使用这些特性(使用了模板的偏特化处理的,所以必须使用IDispach了)
c17ptr.CoCreateInstance(CLSID_MySharp,NULL);
CComVariant comvar(3,VT_I4);
//a.只通过属性名进行设置
c17ptr.PutPropertyByName(OLESTR("xpos"),&comvar);
comvar.Clear();
VARIANT rvar;
rvar.vt =VT_EMPTY;
//b.只通过属性名进行获取
c17ptr.GetPropertyByName(OLESTR("xpos"),&rvar);
VariantClear(&rvar);
//c.使用属性的DISPID
DISPID dispidXpos; //其实DISPID就是IDL文件里定义的属性/方法id,因为oleidl.h里定义:typedef LONG DISPID;
c17ptr.GetIDOfName(OLESTR("xpos"),&dispidXpos);
VARIANT rvar2;
VariantInit(&rvar2);
c17ptr.GetProperty(dispidXpos,&rvar2);
//d.有两个静态方法,可以直接使用IDispach*(而不必是CComPtr封装的指针)来通过DISPID设置或获取参数
IDispatch* rdisp = c17ptr.Detach();
rvar2.vt = VT_EMPTY;
CComPtr
::GetProperty(rdisp,1,&rvar2); //get
CComPtr
::PutProperty(rdisp,2,&rvar2); //put
c17ptr.Attach(rdisp);
//e.Invoke方法,er,真的,如果没有封装,直接使用invoke方法真的是梦魇
CComPtr
c17calc;
c17calc.CoCreateInstance(CLSID_MCalcServer);
CComVariant par1(7.0);
CComVariant par2(8.0);
c17calc.Invoke2(OLESTR("doAdd"),&par2,&par1); //使用两个参数的版本,这里要注意参数顺序是由右到左的
rvar2.vt = VT_EMPTY;
c17calc.GetPropertyByName(OLESTR("addResult"),&rvar2);
VariantClear(&rvar2);
c17calc = NULL;
c17ptr = NULL;
CoUninitialize();