CComPtr 和 CComQIPtr

问题

CComPtr 和 CComQIPtr 长得这么像,有啥关系和区别?

分析

  1. 看代码 CComQIPtr 继承自 CComPtr,CComPtr<IUnknown> 没问题,但 CComQIPtr<IUnknown> 报错,应该使用 CComQIPtr<IUnknown, &IID_IUnknown>。

  2. 不同类型 CComPtr<> 不能直接互相构造;CComQIPtr<> 则可以,因为 CComQIPtr 会进行目标类型的 QueryInterface。

1
2
3
4
5
CComPtr<IUnknown> u;
// ...
CComPtr<IDispatch> d(u); // error

CComQIPtr<IDispatch> d(u); // right, will call QueryInterface
  1. 两者构造/赋值时,都会进行 AddRef,如果不想 AddRef,可以使用裸指针(必须十分清楚自己在干嘛!)。
1
2
3
4
5
6
7
CComPtr<IUnknown> u;
// ...
CComPtr<IUnknown> u1(u); // will call AddRef

CComQIPtr<IDispatch> d(u); // will call QueryInterface(call AddRef impliedly)

auto raw = static_cast<IDispatch*>(u.p); // won't call AddRef
  1. 两者赋值时,小部分行为不同。以下模板使得,当等号两边类型不同时,CComPtr 为左值和 CComQIPtr 为左值,表现不同。
1
2
3
4
5
6
7
8
9
10
// CComPtr
template <typename Q>
T* operator=(_Inout_ const CComPtr<Q>& lp) throw()
{
if(!this->IsEqualObject(lp) )
{
AtlComQIPtrAssign2((IUnknown**)&this->p, lp, __uuidof(T));
}
return *this;
}

总结

  • 您可以忘记 CComPtr,只使用 CComQIPtr;

  • 或者,尽量使用 CComPtr,只在必要时使用 CComQIPtr。

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
#include <iostream>

#include <atlbase.h>
#include <atlcom.h>
#include <atlstr.h>

MIDL_INTERFACE("00554d55-0000-0000-C000-000000000041")
IA : public IUnknown {
public:
virtual HRESULT STDMETHODCALLTYPE FuncA() = 0;
};

MIDL_INTERFACE("00554d55-0000-0000-C000-000000000042")
IB : public IA {
public:
virtual HRESULT STDMETHODCALLTYPE FuncB() = 0;
};

class A : public IA {
public:
~A() { std::cout << __FUNCTION__ << "\n"; }

HRESULT STDMETHODCALLTYPE QueryInterface(
/* [in] */ REFIID riid,
/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) {
if (__uuidof(IA) == riid || __uuidof(IUnknown) == riid) {
*ppvObject = this;
std::cout << __FUNCTION__ << "\n";
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}

ULONG STDMETHODCALLTYPE AddRef(void) {
++ref_;
std::cout << __FUNCTION__ << ": " << ref_ << "\n";
return ref_;
}

ULONG STDMETHODCALLTYPE Release(void) {
--ref_;
std::cout << __FUNCTION__ << ": " << ref_ << "\n";
if (0 == ref_) {
delete this;
}
return ref_;
}

HRESULT STDMETHODCALLTYPE FuncA() {
std::cout << __FUNCTION__ << "\n";
return S_OK;
}

private:
int ref_ = 0;
};

class B : public IB {
public:
~B() { std::cout << __FUNCTION__ << "\n"; }

HRESULT STDMETHODCALLTYPE QueryInterface(
/* [in] */ REFIID riid,
/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) {
if (__uuidof(IB) == riid || __uuidof(IUnknown) == riid) {
*ppvObject = this;
std::cout << __FUNCTION__ << "\n";
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}

ULONG STDMETHODCALLTYPE AddRef(void) {
++ref_;
std::cout << __FUNCTION__ << ": " << ref_ << "\n";
return ref_;
}

ULONG STDMETHODCALLTYPE Release(void) {
--ref_;
std::cout << __FUNCTION__ << ": " << ref_ << "\n";
if (0 == ref_) {
delete this;
}
return ref_;
}

HRESULT STDMETHODCALLTYPE FuncA() {
std::cout << __FUNCTION__ << "\n";
return S_OK;
}

HRESULT STDMETHODCALLTYPE FuncB() {
std::cout << __FUNCTION__ << "\n";
return S_OK;
}

private:
int ref_ = 0;
};

HRESULT CreateObject(REFIID riid,
_COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) {
if (__uuidof(IA) == riid || __uuidof(IUnknown) == riid) {
auto p = new A;
p->QueryInterface(riid, ppvObject);
return S_OK;
} else if (__uuidof(IB) == riid) {
auto p = new B;
p->QueryInterface(riid, ppvObject);
return S_OK;
}
return E_NOINTERFACE;
}

int main() {
std::cout << "---- Raw pointer:\n";
{
IUnknown* u;
HRESULT hr = CreateObject(__uuidof(IA), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
auto a = static_cast<IA*>(u);
a->FuncA();
a->Release();
}
std::cout << "---- ctor\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IUnknown), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
CComPtr<IUnknown> u2(u); // will call AddRef
CComQIPtr<IA> a(u); // will call QueryInterface(call AddRef impliedly)
CComQIPtr<IB> b(a); // will call QueryInterface(call AddRef impliedly)
a->FuncA();
}
std::cout << "---- CComPtr A=B:\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IB), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
CComPtr<IA> a;
a = u; // will call QueryInterface(call AddRef impliedly)
if (a) {
a->FuncA();
}

CComPtr<IB> b;
b = u;
if (b) {
b->FuncA();
b->FuncB();
}

// template<T, Q>
a = b; // failed
if (a) {
std::cout << "failed!\n";
a->FuncA();
}
}
std::cout << "---- CComQIPtr A=CComPtr<B>:\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IB), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
CComQIPtr<IA> a;
a = u; // will call QueryInterface(call AddRef impliedly)
if (a) {
a->FuncA();
}

CComPtr<IB> b;
b = u; // will call QueryInterface(call AddRef impliedly)
if (b) {
b->FuncA();
b->FuncB();
}

a = b; // will call AddRef
if (a) {
std::cout << "OK! B is-a A\n";
a->FuncA();
}
}
std::cout << "---- CComQIPtr A=CComQIPtr<B>:\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IB), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
CComQIPtr<IA> a;
a = u; // will call QueryInterface(call AddRef impliedly)
if (a) {
a->FuncA();
}

CComQIPtr<IB> b;
b = u; // will call QueryInterface(call AddRef impliedly)
if (b) {
b->FuncA();
b->FuncB();
}

a = b; // will call AddRef
if (a) {
std::cout << "OK! B is-a A\n";
a->FuncA();
}
}
std::cout << "---- CComPtr B=A:\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IUnknown), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
CComPtr<IA> a;
a = u; // will call QueryInterface(call AddRef impliedly)
if (a) {
a->FuncA();
}

CComPtr<IB> b;
b = u; // failed
if (b) {
b->FuncA();
b->FuncB();
}
}
std::cout << "---- CComQIPtr B=A:\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IUnknown), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
CComQIPtr<IA> a;
a = u; // will call QueryInterface(call AddRef impliedly)
if (a) {
a->FuncA();
}

CComQIPtr<IB> b;
b = u; // failed
if (b) {
b->FuncA();
b->FuncB();
}
}
std::cout << "---- CComQIPtr<IB>:\n";
{
CComQIPtr<IB> b;
HRESULT hr = CreateObject(__uuidof(IB), reinterpret_cast<void**>(&b));
std::cout << hr << ", " << b << "\n";
b->FuncA();
b->FuncB();
}
std::cout << "---- CComPtr<IA>:\n";
{
CComPtr<IA> a;
HRESULT hr = CreateObject(__uuidof(IB), reinterpret_cast<void**>(&a));
std::cout << hr << ", " << a << "\n";
a->FuncA();
}
std::cout << "---- CComPtr<IUnknown>:\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IA), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
auto a = static_cast<IA*>(u.p);
a->FuncA();
}

return 0;
}