FreeRDP
Loading...
Searching...
No Matches
TestCom.c
1
20#include <stdio.h>
21#include <stddef.h>
22
23#include <winpr/error.h>
24#include <winpr/com.h>
25
26#define CHECK(cond) \
27 do \
28 { \
29 if (!(cond)) \
30 { \
31 (void)fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, #cond); \
32 return FALSE; \
33 } \
34 } while (0)
35
36/* two interfaces of our own, declared in C form */
37typedef struct ITestFoo ITestFoo;
38typedef struct ITestBar ITestBar;
39
40WINPR_COM_DEFINE_GUID(IID_ITestFoo, 0x01234567, 0x89ab, 0xcdef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
41 0xcd, 0xef);
42WINPR_COM_DEFINE_GUID(IID_ITestBar, 0x76543210, 0xba98, 0xfedc, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba,
43 0xdc, 0xfe);
44
45typedef struct
46{
47 wIUnknownVtbl iunknown;
48 UINT32(WINPR_COM_CALL* Foo)(ITestFoo* This);
49} ITestFooVtbl;
50
51struct ITestFoo
52{
53 const ITestFooVtbl* lpVtbl;
54};
55
56typedef struct
57{
58 wIUnknownVtbl iunknown;
59 UINT32(WINPR_COM_CALL* Bar)(ITestBar* This);
60} ITestBarVtbl;
61
62struct ITestBar
63{
64 const ITestBarVtbl* lpVtbl;
65};
66
67static int freed = 0;
68
69/* ---- an object with one interface ---- */
70
71typedef struct
72{
73 wComBase com; /* ITestFoo */
74 UINT32 value;
75} CSingle;
76
77WINPR_COM_IUNKNOWN_THUNKS(CSingle, &IID_ITestFoo)
78
79static UINT32 WINPR_COM_CALL CSingle_Foo(ITestFoo* This)
80{
81 return ((CSingle*)This)->value;
82}
83
84static const ITestFooVtbl CSingle_vtbl = {
85 .iunknown = WINPR_COM_IUNKNOWN_ENTRIES(CSingle),
86 .Foo = CSingle_Foo,
87};
88
89static void CSingle_free(wComBase* object)
90{
91 freed++;
92 free(object);
93}
94
95static BOOL test_single(void)
96{
97 /* same layout as the flat vtable MIDL generates */
98 WINPR_STATIC_ASSERT(offsetof(ITestFooVtbl, Foo) == 3 * sizeof(void*));
99
100 freed = 0;
101 CSingle* obj = calloc(1, sizeof(*obj));
102 CHECK(obj);
103 winpr_com_base_init(&obj->com, "CSingle", &CSingle_vtbl.iunknown, CSingle_free);
104 obj->value = 42;
105 ITestFoo* foo = (ITestFoo*)obj;
106 CHECK(obj->com.refCount == 1);
107 CHECK(foo->lpVtbl->Foo(foo) == 42);
108
109 /* the interface implemented and IUnknown, both on the same pointer */
110 ITestFoo* foo2 = nullptr;
111 CHECK(SUCCEEDED(WINPR_COM_QUERY_INTERFACE(foo, &IID_ITestFoo, &foo2)));
112 CHECK(foo2 == foo);
113 wIUnknown* unk = nullptr;
114 CHECK(SUCCEEDED(WINPR_COM_QUERY_INTERFACE(foo, &WINPR_IID_IUnknown, &unk)));
115 CHECK((void*)unk == (void*)foo);
116 CHECK(obj->com.refCount == 3);
117
118 /* anything else */
119 void* other = foo;
120 CHECK(WINPR_COM_QUERY_INTERFACE(foo, &IID_ITestBar, &other) == E_NOINTERFACE);
121 CHECK(other == nullptr);
122 CHECK(foo->lpVtbl->iunknown.QueryInterface((wIUnknown*)foo, &IID_ITestFoo, nullptr) ==
123 E_POINTER);
124 CHECK(WINPR_COM_QUERY_INTERFACE(foo, nullptr, &other) == E_POINTER);
125 CHECK(obj->com.refCount == 3);
126
127 CHECK(WINPR_COM_ADDREF(foo) == 4);
128 CHECK(WINPR_COM_RELEASE(foo) == 3);
129 CHECK(WINPR_COM_RELEASE(unk) == 2);
130 CHECK(WINPR_COM_RELEASE(foo2) == 1);
131 CHECK(freed == 0);
132 CHECK(WINPR_COM_RELEASE(foo) == 0);
133 CHECK(freed == 1);
134 return TRUE;
135}
136
137/* ---- an object with two interfaces and its own QueryInterface ---- */
138
139typedef struct
140{
141 wComBase com; /* ITestFoo, and IUnknown */
142 const ITestBarVtbl* lpBar; /* ITestBar */
143 UINT32 foo;
144 UINT32 bar;
145} CDouble;
146
147static CDouble* CDouble_from_bar(wIUnknown* This)
148{
149 return (CDouble*)((BYTE*)This - offsetof(CDouble, lpBar));
150}
151
152static HRESULT CDouble_query(CDouble* obj, const IID* riid, void** ppvObject)
153{
154 if (!ppvObject)
155 return E_POINTER;
156 *ppvObject = nullptr;
157 if (!riid)
158 return E_POINTER;
159
160 if (winpr_com_guid_equal(riid, &WINPR_IID_IUnknown) ||
161 winpr_com_guid_equal(riid, &IID_ITestFoo))
162 *ppvObject = &obj->com;
163 else if (winpr_com_guid_equal(riid, &IID_ITestBar))
164 *ppvObject = (void*)&obj->lpBar;
165 else
166 return E_NOINTERFACE;
167 (void)winpr_com_base_add_ref(&obj->com);
168 return S_OK;
169}
170
171/* ITestFoo, at the start of the object */
173
174static HRESULT WINPR_COM_CALL CDouble_QueryInterface(wIUnknown* This, const IID* riid,
175 void** ppvObject)
176{
177 return CDouble_query((CDouble*)This, riid, ppvObject);
178}
179
180static UINT32 WINPR_COM_CALL CDouble_Foo(ITestFoo* This)
181{
182 return ((CDouble*)This)->foo;
183}
184
185static const ITestFooVtbl CDouble_vtbl = {
186 .iunknown = WINPR_COM_IUNKNOWN_ENTRIES(CDouble),
187 .Foo = CDouble_Foo,
188};
189
190/* ITestBar, at lpBar */
191static HRESULT WINPR_COM_CALL CDoubleBar_QueryInterface(wIUnknown* This, const IID* riid,
192 void** ppvObject)
193{
194 return CDouble_query(CDouble_from_bar(This), riid, ppvObject);
195}
196
197static ULONG WINPR_COM_CALL CDoubleBar_AddRef(wIUnknown* This)
198{
199 return winpr_com_base_add_ref(&CDouble_from_bar(This)->com);
200}
201
202static ULONG WINPR_COM_CALL CDoubleBar_Release(wIUnknown* This)
203{
204 return winpr_com_base_release(&CDouble_from_bar(This)->com);
205}
206
207static UINT32 WINPR_COM_CALL CDoubleBar_Bar(ITestBar* This)
208{
209 return CDouble_from_bar((wIUnknown*)This)->bar;
210}
211
212static const ITestBarVtbl CDoubleBar_vtbl = {
213 .iunknown = WINPR_COM_IUNKNOWN_ENTRIES(CDoubleBar),
214 .Bar = CDoubleBar_Bar,
215};
216
217static void CDouble_free(wComBase* object)
218{
219 freed++;
220 free(object);
221}
222
223static BOOL test_double(void)
224{
225 freed = 0;
226 CDouble* obj = calloc(1, sizeof(*obj));
227 CHECK(obj);
228 winpr_com_base_init(&obj->com, "CDouble", &CDouble_vtbl.iunknown, CDouble_free);
229 obj->lpBar = &CDoubleBar_vtbl;
230 obj->foo = 1;
231 obj->bar = 2;
232 ITestFoo* foo = (ITestFoo*)obj;
233
234 ITestBar* bar = nullptr;
235 CHECK(SUCCEEDED(WINPR_COM_QUERY_INTERFACE(foo, &IID_ITestBar, &bar)));
236 CHECK((void*)bar != (void*)foo);
237 CHECK(bar->lpVtbl->Bar(bar) == 2);
238
239 /* and back */
240 ITestFoo* foo2 = nullptr;
241 CHECK(SUCCEEDED(WINPR_COM_QUERY_INTERFACE(bar, &IID_ITestFoo, &foo2)));
242 CHECK(foo2 == foo);
243 CHECK(foo2->lpVtbl->Foo(foo2) == 1);
244
245 /* identity: IUnknown is the same pointer from any interface */
246 wIUnknown* unk1 = nullptr;
247 wIUnknown* unk2 = nullptr;
248 CHECK(SUCCEEDED(WINPR_COM_QUERY_INTERFACE(foo, &WINPR_IID_IUnknown, &unk1)));
249 CHECK(SUCCEEDED(WINPR_COM_QUERY_INTERFACE(bar, &WINPR_IID_IUnknown, &unk2)));
250 CHECK(unk1 == unk2);
251
252 /* one reference count for the object, whatever the interface */
253 CHECK(obj->com.refCount == 5);
254 CHECK(WINPR_COM_RELEASE(unk1) == 4);
255 CHECK(WINPR_COM_RELEASE(unk2) == 3);
256 CHECK(WINPR_COM_RELEASE(foo2) == 2);
257 CHECK(WINPR_COM_RELEASE(foo) == 1);
258 CHECK(freed == 0);
259 CHECK(WINPR_COM_RELEASE(bar) == 0);
260 CHECK(freed == 1);
261 return TRUE;
262}
263
264static BOOL test_guid(void)
265{
266 CHECK(winpr_com_guid_equal(&IID_ITestFoo, &IID_ITestFoo));
267 CHECK(!winpr_com_guid_equal(&IID_ITestFoo, &IID_ITestBar));
268
269 char guid[64] = WINPR_C_ARRAY_INIT;
270 (void)snprintf(guid, sizeof(guid), WINPR_COM_GUID_FMT, WINPR_COM_GUID_ARGS(&IID_ITestFoo));
271 CHECK(strcmp(guid, "{01234567-89AB-CDEF-0123-456789ABCDEF}") == 0);
272 return TRUE;
273}
274
275int TestCom(int argc, char* argv[])
276{
277 WINPR_UNUSED(argc);
278 WINPR_UNUSED(argv);
279
280 if (!test_guid() || !test_single() || !test_double())
281 return -1;
282 return 0;
283}
#define WINPR_COM_GUID_FMT
printf a GUID as {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}: WLog_DBG(TAG, "id=" WINPR_COM_GUID_FMT,...
Definition com.h:137
static BOOL winpr_com_guid_equal(const GUID *a, const GUID *b)
IsEqualGUID.
Definition com.h:130
#define WINPR_COM_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8)
defines a GUID constant visible in the including file only (nothing to link)
Definition com.h:122
WINPR_API void winpr_com_base_init(wComBase *object, const char *className, const wIUnknownVtbl *vtbl, pfnwComBaseFree Free)
sets the vtable and the reference count to 1, the caller owns that first reference.
Definition com.c:30
WINPR_API ULONG winpr_com_base_add_ref(wComBase *object)
AddRef of an object starting with a wComBase.
Definition com.c:43
#define WINPR_COM_IUNKNOWN_THUNKS(prefix, iid)
WINPR_COM_REFCOUNT_THUNKS plus prefix_QueryInterface answering IUnknown and iid (a const IID*,...
Definition com.h:231
#define WINPR_COM_IUNKNOWN_ENTRIES(prefix)
the initializer of a wIUnknownVtbl from prefix_QueryInterface (the thunk above or the object's own),...
Definition com.h:241
#define WINPR_COM_QUERY_INTERFACE(obj, iid, ppv)
IUnknown calls on an interface in C form. In C++, the C++ form of wIUnknown is called directly: unkno...
Definition com.h:150
#define WINPR_COM_CALL
the calling convention of COM methods (STDMETHODCALLTYPE)
Definition com.h:73
#define WINPR_COM_REFCOUNT_THUNKS(prefix)
prefix_AddRef and prefix_Release, for an object starting with a wComBase
Definition com.h:219
WINPR_API ULONG winpr_com_base_release(wComBase *object)
Release of an object starting with a wComBase: calls its Free at 0.
Definition com.c:51
IUnknown, C form.
Definition com.h:111
the vtable of IUnknown, the first entries of every COM vtable
Definition com.h:85