FreeRDP
Loading...
Searching...
No Matches
TestPluginServices.c
1#include <stdio.h>
2
3#include <winpr/error.h>
4
5#include <freerdp/freerdp.h>
7
8/* rdpContext::pluginServiceProvider over a fake client window info service with Microsoft's
9 * rules (one subscriber), and a fake bitmap render service */
10
11#define CHECK(cond) \
12 do \
13 { \
14 if (!(cond)) \
15 { \
16 (void)fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, #cond); \
17 goto fail; \
18 } \
19 } while (0)
20
21/* ---- the client's window info service ---- */
22
23typedef struct
24{
25 wComBase com; /* IWTSWindowInfoService */
26 IWTSWindowChangedCallback* subscriber;
27 int subscribes;
28 int unsubscribes;
29 BOOL freed;
30} FakeWindowInfo;
31
32static FakeWindowInfo fakeWindowInfo;
33
34WINPR_COM_IUNKNOWN_THUNKS(FakeWindowInfo, &IID_IWTSWindowInfoService)
35
36static HRESULT WINPR_COM_CALL FakeWindowInfo_GetWindowInfo(IWTSWindowInfoService* This,
37 UINT64 remoteHwnd, WTS_WINDOW_INFO* info)
38{
39 WINPR_UNUSED(This);
40 WINPR_UNUSED(remoteHwnd);
41 if (!info)
42 return E_POINTER;
43 memset(info, 0, sizeof(*info));
44 info->hwnd = (HWND)(UINT_PTR)0x1234;
45 info->remoteWidth = info->viewWidth = 1920;
46 info->remoteHeight = info->viewHeight = 1080;
47 info->scale = 1.0f;
48 return S_OK;
49}
50
51static HRESULT WINPR_COM_CALL FakeWindowInfo_GetProcessId(IWTSWindowInfoService* This,
52 DWORD* processId)
53{
54 WINPR_UNUSED(This);
55 *processId = 42;
56 return S_OK;
57}
58
59static HRESULT WINPR_COM_CALL FakeWindowInfo_GetRdpSessionType(IWTSWindowInfoService* This,
60 UINT32* sessionType)
61{
62 WINPR_UNUSED(This);
63 *sessionType = WTS_RDP_SESSION_TYPE_DESKTOP;
64 return S_OK;
65}
66
67static HRESULT WINPR_COM_CALL FakeWindowInfo_Subscribe(IWTSWindowInfoService* This,
68 UINT64 remoteHwnd,
70{
71 FakeWindowInfo* f = (FakeWindowInfo*)This;
72 WINPR_UNUSED(remoteHwnd);
73 if (f->subscriber)
74 return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
75 WINPR_COM_ADDREF(callback);
76 f->subscriber = callback;
77 f->subscribes++;
78 return S_OK;
79}
80
81static HRESULT WINPR_COM_CALL FakeWindowInfo_Unsubscribe(IWTSWindowInfoService* This)
82{
83 FakeWindowInfo* f = (FakeWindowInfo*)This;
84 if (!f->subscriber)
85 return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
86 WINPR_COM_RELEASE(f->subscriber);
87 f->subscriber = nullptr;
88 f->unsubscribes++;
89 return S_OK;
90}
91
92static const IWTSWindowInfoServiceVtbl FakeWindowInfo_vtbl = {
93 .iunknown = WINPR_COM_IUNKNOWN_ENTRIES(FakeWindowInfo),
94 .GetWindowInfo = FakeWindowInfo_GetWindowInfo,
95 .GetProcessId = FakeWindowInfo_GetProcessId,
96 .GetRdpSessionType = FakeWindowInfo_GetRdpSessionType,
97 .SubscribeWindowChanged = FakeWindowInfo_Subscribe,
98 .UnsubscribeWindowChanged = FakeWindowInfo_Unsubscribe,
99};
100
101static void FakeWindowInfo_free(wComBase* object)
102{
103 ((FakeWindowInfo*)object)->freed = TRUE;
104}
105
106static void fire_window_changed(void)
107{
108 WTS_WINDOW_INFO info = WINPR_C_ARRAY_INIT;
109 IWTSWindowChangedCallback* cb = fakeWindowInfo.subscriber;
110 if (cb)
111 (void)cb->lpVtbl->OnWindowChanged(cb, &info);
112}
113
114/* ---- the client's bitmap render service (only its identity matters) ---- */
115
116typedef struct
117{
118 wComBase com; /* IWTSBitmapRenderService */
119 BOOL freed;
120} FakeBitmapRender;
121
122static FakeBitmapRender fakeBitmapRender;
123
124WINPR_COM_IUNKNOWN_THUNKS(FakeBitmapRender, &IID_IWTSBitmapRenderService)
125
126static HRESULT WINPR_COM_CALL FakeBitmapRender_GetMappedRenderer(
127 IWTSBitmapRenderService* This, UINT64 mappingId, IWTSBitmapRendererCallback* callback,
128 IWTSBitmapRenderer** renderer)
129{
130 WINPR_UNUSED(This);
131 WINPR_UNUSED(mappingId);
132 WINPR_UNUSED(callback);
133 WINPR_UNUSED(renderer);
134 return E_NOTIMPL;
135}
136
137static const IWTSBitmapRenderServiceVtbl FakeBitmapRender_vtbl = {
138 .iunknown = WINPR_COM_IUNKNOWN_ENTRIES(FakeBitmapRender),
139 .GetMappedRenderer = FakeBitmapRender_GetMappedRenderer,
140};
141
142static void FakeBitmapRender_free(wComBase* object)
143{
144 ((FakeBitmapRender*)object)->freed = TRUE;
145}
146
147/* ---- a plugin's change callback ---- */
148
149typedef struct
150{
151 wComBase com; /* IWTSWindowChangedCallback */
152 int calls;
153} Listener;
154
155WINPR_COM_IUNKNOWN_THUNKS(Listener, nullptr)
156
157static HRESULT WINPR_COM_CALL Listener_OnWindowChanged(IWTSWindowChangedCallback* This,
158 const WTS_WINDOW_INFO* info)
159{
160 WINPR_UNUSED(info);
161 ((Listener*)This)->calls++;
162 return S_OK;
163}
164
165static const IWTSWindowChangedCallbackVtbl Listener_vtbl = {
166 .iunknown = WINPR_COM_IUNKNOWN_ENTRIES(Listener),
167 .OnWindowChanged = Listener_OnWindowChanged,
168};
169
170static void Listener_free(wComBase* object)
171{
172 WINPR_UNUSED(object);
173}
174
175/* ---- the client's ContextFree: releases its services ---- */
176
177static BOOL subscribedAtContextFree = TRUE;
178
179static void client_context_free(freerdp* instance, rdpContext* context)
180{
181 WINPR_UNUSED(instance);
182 /* the core dropped its subscription before */
183 subscribedAtContextFree = (fakeWindowInfo.subscriber != nullptr);
184 WINPR_COM_RELEASE(context->windowInfoService);
185 context->windowInfoService = nullptr;
186 WINPR_COM_RELEASE(context->bitmapRenderService);
187 context->bitmapRenderService = nullptr;
188}
189
190static IWTSWindowInfoService* get_window_info(IWTSPluginServiceProvider* sp)
191{
192 wIUnknown* unk = nullptr;
193 IWTSWindowInfoService* svc = nullptr;
194 if (FAILED(sp->lpVtbl->GetService(sp, RDCLIENT_WINDOW_INFO_SERVICE, &unk)))
195 return nullptr;
196 const HRESULT hr = WINPR_COM_QUERY_INTERFACE((wIUnknown*)unk, &IID_IWTSWindowInfoService, &svc);
197 WINPR_COM_RELEASE((wIUnknown*)unk);
198 return SUCCEEDED(hr) ? svc : nullptr;
199}
200
201int TestPluginServices(int argc, char* argv[])
202{
203 WINPR_UNUSED(argc);
204 WINPR_UNUSED(argv);
205
206 int rc = -1;
207 IWTSWindowInfoService* a = nullptr;
208 IWTSWindowInfoService* b = nullptr;
209 Listener la = WINPR_C_ARRAY_INIT;
210 Listener lb = WINPR_C_ARRAY_INIT;
211 winpr_com_base_init(&la.com, "ListenerA", &Listener_vtbl.iunknown, Listener_free);
212 winpr_com_base_init(&lb.com, "ListenerB", &Listener_vtbl.iunknown, Listener_free);
215 IWTSPluginServiceProvider* sp = nullptr;
216
217 freerdp* instance = freerdp_new();
218 CHECK(instance);
219 instance->ContextFree = client_context_free;
220 CHECK(freerdp_context_new(instance));
221 rdpContext* context = instance->context;
222 /* what a channel does from its entry points */
223 sp = freerdp_get_plugin_service_provider(context);
224 CHECK(sp && (sp == context->pluginServiceProvider));
225
226 /* nothing set by the client yet */
227 wIUnknown* unk = nullptr;
228 CHECK(sp->lpVtbl->GetService(sp, RDCLIENT_WINDOW_INFO_SERVICE, &unk) == E_NOINTERFACE);
229 CHECK(sp->lpVtbl->GetService(sp, RDCLIENT_BITMAP_RENDER_SERVICE, &unk) == E_NOINTERFACE);
230 CHECK(sp->lpVtbl->GetService(sp, IID_IWTSPluginServiceProvider, &unk) == E_NOINTERFACE);
231 CHECK(!unk);
232
233 /* the client sets its services */
234 winpr_com_base_init(&fakeWindowInfo.com, "FakeWindowInfo", &FakeWindowInfo_vtbl.iunknown,
235 FakeWindowInfo_free);
236 winpr_com_base_init(&fakeBitmapRender.com, "FakeBitmapRender", &FakeBitmapRender_vtbl.iunknown,
237 FakeBitmapRender_free);
238 context->windowInfoService = (IWTSWindowInfoService*)&fakeWindowInfo;
239 context->bitmapRenderService = (IWTSBitmapRenderService*)&fakeBitmapRender;
240
241 /* the bitmap render service is the client's own */
242 CHECK(SUCCEEDED(sp->lpVtbl->GetService(sp, RDCLIENT_BITMAP_RENDER_SERVICE, &unk)));
243 CHECK(unk == (wIUnknown*)&fakeBitmapRender);
244 WINPR_COM_RELEASE((wIUnknown*)unk);
245
246 /* a window info service per caller, which passes the getters through */
247 a = get_window_info(sp);
248 b = get_window_info(sp);
249 CHECK(a && b && (a != b));
250 CHECK(a != (IWTSWindowInfoService*)&fakeWindowInfo);
251 WTS_WINDOW_INFO info = WINPR_C_ARRAY_INIT;
252 CHECK(SUCCEEDED(a->lpVtbl->GetWindowInfo(a, 0, &info)));
253 CHECK(info.hwnd == (HWND)(UINT_PTR)0x1234 && info.remoteWidth == 1920);
254 DWORD pid = 0;
255 UINT32 type = 99;
256 CHECK(SUCCEEDED(b->lpVtbl->GetProcessId(b, &pid)) && pid == 42);
257 CHECK(SUCCEEDED(b->lpVtbl->GetRdpSessionType(b, &type)) && type == 0);
258
259 /* Microsoft's rules per object, one subscription to the client */
260 CHECK(a->lpVtbl->UnsubscribeWindowChanged(a) == HRESULT_FROM_WIN32(ERROR_NOT_FOUND));
261 CHECK(a->lpVtbl->SubscribeWindowChanged(a, 0, nullptr) == E_POINTER);
262 CHECK(SUCCEEDED(a->lpVtbl->SubscribeWindowChanged(a, 0, cba)));
263 CHECK(a->lpVtbl->SubscribeWindowChanged(a, 0, cbb) == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS));
264 CHECK(SUCCEEDED(b->lpVtbl->SubscribeWindowChanged(b, 0, cbb)));
265 CHECK(fakeWindowInfo.subscribes == 1);
266
267 fire_window_changed();
268 CHECK(la.calls == 1 && lb.calls == 1);
269
270 CHECK(SUCCEEDED(a->lpVtbl->UnsubscribeWindowChanged(a)));
271 CHECK(a->lpVtbl->UnsubscribeWindowChanged(a) == HRESULT_FROM_WIN32(ERROR_NOT_FOUND));
272 CHECK(fakeWindowInfo.unsubscribes == 0);
273 fire_window_changed();
274 CHECK(la.calls == 1 && lb.calls == 2);
275
276 /* the last one out ends the client's subscription, the next one in makes a new one */
277 CHECK(SUCCEEDED(b->lpVtbl->UnsubscribeWindowChanged(b)));
278 CHECK(fakeWindowInfo.unsubscribes == 1 && !fakeWindowInfo.subscriber);
279 CHECK(SUCCEEDED(b->lpVtbl->SubscribeWindowChanged(b, 0, cbb)));
280 CHECK(fakeWindowInfo.subscribes == 2);
281
282 /* releasing a subscribed object unsubscribes it */
283 WINPR_COM_RELEASE(b);
284 b = nullptr;
285 CHECK(fakeWindowInfo.unsubscribes == 2);
286
287 /* the context goes while a plugin still holds a subscribed service */
288 CHECK(SUCCEEDED(a->lpVtbl->SubscribeWindowChanged(a, 0, cba)));
289 CHECK(fakeWindowInfo.subscribes == 3);
290 freerdp_context_free(instance);
291 freerdp_free(instance);
292 instance = nullptr;
293 CHECK(!subscribedAtContextFree);
294 CHECK(fakeWindowInfo.freed && fakeBitmapRender.freed);
295 CHECK(a->lpVtbl->GetWindowInfo(a, 0, &info) == E_UNEXPECTED);
296 CHECK(a->lpVtbl->SubscribeWindowChanged(a, 0, cbb) == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS));
297 CHECK(SUCCEEDED(a->lpVtbl->UnsubscribeWindowChanged(a)));
298 CHECK(a->lpVtbl->SubscribeWindowChanged(a, 0, cbb) == E_UNEXPECTED);
299 WINPR_COM_RELEASE(a);
300 a = nullptr;
301
302 /* the channel's provider outlives the context, and has nothing to give */
303 CHECK(sp->lpVtbl->GetService(sp, RDCLIENT_WINDOW_INFO_SERVICE, &unk) == E_NOINTERFACE);
304 CHECK(sp->lpVtbl->GetService(sp, RDCLIENT_BITMAP_RENDER_SERVICE, &unk) == E_NOINTERFACE);
305 WINPR_COM_RELEASE(sp);
306 sp = nullptr;
307
308 /* every reference given back */
309 CHECK(la.com.refCount == 1 && lb.com.refCount == 1);
310 rc = 0;
311fail:
312 if (sp)
313 WINPR_COM_RELEASE(sp);
314 if (a)
315 WINPR_COM_RELEASE(a);
316 if (b)
317 WINPR_COM_RELEASE(b);
318 if (instance)
319 {
320 freerdp_context_free(instance);
321 freerdp_free(instance);
322 }
323 return rc;
324}
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
#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
IUnknown, C form.
Definition com.h:111
40 bytes on x64