FreeRDP
Loading...
Searching...
No Matches
TestClientWindowInfoService.c
1#include <stdio.h>
2
3#include <winpr/error.h>
4#include <winpr/thread.h>
5
6#include <freerdp/freerdp.h>
8
9/* the client helper (freerdp/client/window_info_service.h), on its own and behind the context's
10 * plugin service provider, as a channel uses it */
11
12#define CHECK(cond) \
13 do \
14 { \
15 if (!(cond)) \
16 { \
17 (void)fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, #cond); \
18 goto fail; \
19 } \
20 } while (0)
21
22typedef struct
23{
24 INT32 width;
25 UINT32 sessionType;
26 int calls;
27} Source;
28
29static HRESULT source_get(void* custom, UINT64 remoteHwnd, WTS_WINDOW_INFO* info)
30{
31 Source* s = custom;
32 s->calls++;
33 if ((s->sessionType == WTS_RDP_SESSION_TYPE_REMOTEAPP) && (remoteHwnd != 7))
34 return E_INVALIDARG;
35 info->hwnd = (HWND)(UINT_PTR)0xabc;
36 info->remoteWidth = info->viewWidth = s->width;
37 info->remoteHeight = info->viewHeight = 600;
38 info->scale = 1.0f;
39 return S_OK;
40}
41
42static UINT32 source_type(void* custom)
43{
44 return ((Source*)custom)->sessionType;
45}
46
47typedef struct
48{
49 wComBase com; /* IWTSWindowChangedCallback */
50 int calls;
51 INT32 lastWidth;
52} Listener;
53
54WINPR_COM_IUNKNOWN_THUNKS(Listener, nullptr)
55
56static HRESULT WINPR_COM_CALL Listener_OnWindowChanged(IWTSWindowChangedCallback* This,
57 const WTS_WINDOW_INFO* info)
58{
59 Listener* l = (Listener*)This;
60 l->calls++;
61 l->lastWidth = info->viewWidth;
62 return S_OK;
63}
64
65static const IWTSWindowChangedCallbackVtbl Listener_vtbl = {
66 .iunknown = WINPR_COM_IUNKNOWN_ENTRIES(Listener),
67 .OnWindowChanged = Listener_OnWindowChanged,
68};
69
70static void Listener_free(wComBase* object)
71{
72 WINPR_UNUSED(object);
73}
74
75static Source source = { 800, WTS_RDP_SESSION_TYPE_DESKTOP, 0 };
76
77static void client_context_free(freerdp* instance, rdpContext* context)
78{
79 WINPR_UNUSED(instance);
80 freerdp_client_window_info_service_detach(context->windowInfoService);
81 WINPR_COM_RELEASE(context->windowInfoService);
82 context->windowInfoService = nullptr;
83}
84
85int TestClientWindowInfoService(int argc, char* argv[])
86{
87 WINPR_UNUSED(argc);
88 WINPR_UNUSED(argv);
89
90 int rc = -1;
91 IWTSWindowInfoService* svc = nullptr;
92 IWTSWindowInfoService* channel = nullptr;
93 freerdp* instance = nullptr;
94 Listener l1 = WINPR_C_ARRAY_INIT;
95 Listener l2 = WINPR_C_ARRAY_INIT;
96 winpr_com_base_init(&l1.com, "L1", &Listener_vtbl.iunknown, Listener_free);
97 winpr_com_base_init(&l2.com, "L2", &Listener_vtbl.iunknown, Listener_free);
100 const WINDOW_INFO_SOURCE src = { &source, source_get, source_type };
101
102 /* ---- the helper on its own: Microsoft's behaviour ---- */
103 svc = freerdp_client_window_info_service_new(&src);
104 CHECK(svc);
105 WTS_WINDOW_INFO info = WINPR_C_ARRAY_INIT;
106 CHECK(SUCCEEDED(svc->lpVtbl->GetWindowInfo(svc, 0, &info)));
107 CHECK(info.hwnd == (HWND)(UINT_PTR)0xabc && info.viewWidth == 800);
108 DWORD pid = 0;
109 CHECK(SUCCEEDED(svc->lpVtbl->GetProcessId(svc, &pid)) && pid == GetCurrentProcessId());
110 UINT32 type = 99;
111 CHECK(SUCCEEDED(svc->lpVtbl->GetRdpSessionType(svc, &type)) && type == 0);
112
113 /* nobody subscribed: changes go nowhere */
114 freerdp_client_window_info_service_changed(svc, 0);
115 CHECK(svc->lpVtbl->UnsubscribeWindowChanged(svc) == HRESULT_FROM_WIN32(ERROR_NOT_FOUND));
116 CHECK(SUCCEEDED(svc->lpVtbl->SubscribeWindowChanged(svc, 0, cb1)));
117 CHECK(svc->lpVtbl->SubscribeWindowChanged(svc, 0, cb2) ==
118 HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS));
119 source.width = 1024;
120 freerdp_client_window_info_service_changed(svc, 0);
121 CHECK(l1.calls == 1 && l1.lastWidth == 1024 && l2.calls == 0);
122 CHECK(SUCCEEDED(svc->lpVtbl->UnsubscribeWindowChanged(svc)));
123 freerdp_client_window_info_service_changed(svc, 0);
124 CHECK(l1.calls == 1);
125
126 /* detached: the client's state is gone */
127 freerdp_client_window_info_service_detach(svc);
128 const int calls = source.calls;
129 CHECK(svc->lpVtbl->GetWindowInfo(svc, 0, &info) == E_UNEXPECTED);
130 CHECK(svc->lpVtbl->GetRdpSessionType(svc, &type) == E_UNEXPECTED);
131 CHECK(svc->lpVtbl->SubscribeWindowChanged(svc, 0, cb1) == E_UNEXPECTED);
132 freerdp_client_window_info_service_changed(svc, 0);
133 CHECK(source.calls == calls);
134 WINPR_COM_RELEASE(svc);
135 svc = nullptr;
136
137 /* ---- in the context: what a channel gets ---- */
138 instance = freerdp_new();
139 CHECK(instance);
140 instance->ContextFree = client_context_free;
141 CHECK(freerdp_context_new(instance));
142 rdpContext* context = instance->context;
143 context->windowInfoService = freerdp_client_window_info_service_new(&src);
144 CHECK(context->windowInfoService);
145
146 IWTSPluginServiceProvider* sp = context->pluginServiceProvider;
147 wIUnknown* unk = nullptr;
148 CHECK(SUCCEEDED(sp->lpVtbl->GetService(sp, RDCLIENT_WINDOW_INFO_SERVICE, &unk)));
149 CHECK(SUCCEEDED(
150 WINPR_COM_QUERY_INTERFACE((wIUnknown*)unk, &IID_IWTSWindowInfoService, &channel)));
151 WINPR_COM_RELEASE((wIUnknown*)unk);
152 CHECK(SUCCEEDED(channel->lpVtbl->SubscribeWindowChanged(channel, 0, cb2)));
153 source.width = 1280;
154 freerdp_client_window_info_service_changed(context->windowInfoService, 0);
155 CHECK(l2.calls == 1 && l2.lastWidth == 1280);
156
157 /* RemoteApp: the remote window's handle */
158 source.sessionType = WTS_RDP_SESSION_TYPE_REMOTEAPP;
159 CHECK(SUCCEEDED(channel->lpVtbl->GetRdpSessionType(channel, &type)) && type == 1);
160 CHECK(channel->lpVtbl->GetWindowInfo(channel, 0, &info) == E_INVALIDARG);
161 CHECK(SUCCEEDED(channel->lpVtbl->GetWindowInfo(channel, 7, &info)));
162 freerdp_client_window_info_service_changed(context->windowInfoService, 7);
163 CHECK(l2.calls == 2);
164
165 /* the client goes first, the channel after (channels are freed after ContextFree) */
166 freerdp_context_free(instance);
167 freerdp_free(instance);
168 instance = nullptr;
169 CHECK(channel->lpVtbl->GetWindowInfo(channel, 7, &info) == E_UNEXPECTED);
170 CHECK(SUCCEEDED(channel->lpVtbl->UnsubscribeWindowChanged(channel)));
171 WINPR_COM_RELEASE(channel);
172 channel = nullptr;
173
174 CHECK(l1.com.refCount == 1 && l2.com.refCount == 1);
175 rc = 0;
176fail:
177 if (svc)
178 WINPR_COM_RELEASE(svc);
179 if (channel)
180 WINPR_COM_RELEASE(channel);
181 if (instance)
182 {
183 freerdp_context_free(instance);
184 freerdp_free(instance);
185 }
186 return rc;
187}
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
what the client gives the service to answer from
40 bytes on x64