FreeRDP
Loading...
Searching...
No Matches
window_info_service.c
1
20#include <freerdp/config.h>
21
22#include <winpr/assert.h>
23#include <winpr/error.h>
24#include <winpr/synch.h>
25#include <winpr/thread.h>
26#include <winpr/wlog.h>
27
28#include <freerdp/log.h>
30
31#define TAG CLIENT_TAG("common.windowinfo")
32
33typedef struct
34{
35 wComBase com; /* IWTSWindowInfoService */
37 WINDOW_INFO_SOURCE source;
38 BOOL detached;
39 IWTSWindowChangedCallback* subscriber; /* a reference */
40} CWindowInfoService;
41
42WINPR_COM_IUNKNOWN_THUNKS(CWindowInfoService, &IID_IWTSWindowInfoService)
43
44
45WINPR_ATTR_NODISCARD
46static const WINDOW_INFO_SOURCE* source_locked(CWindowInfoService* s)
47{
48 return s->detached ? nullptr : &s->source;
49}
50
51static HRESULT WINPR_COM_CALL CWindowInfoService_GetWindowInfo(IWTSWindowInfoService* This,
52 UINT64 remoteHwnd,
53 WTS_WINDOW_INFO* windowInfo)
54{
55 CWindowInfoService* s = (CWindowInfoService*)This;
56 if (!windowInfo)
57 return E_POINTER;
58
59 /* the source is called with the lock held: detach() waits for running calls */
60 HRESULT hr = E_UNEXPECTED;
61 EnterCriticalSection(&s->lock);
62 const WINDOW_INFO_SOURCE* src = source_locked(s);
63 if (src)
64 {
65 memset(windowInfo, 0, sizeof(*windowInfo));
66 hr = src->GetWindowInfo(src->custom, remoteHwnd, windowInfo);
67 }
68 LeaveCriticalSection(&s->lock);
69 return hr;
70}
71
72static HRESULT WINPR_COM_CALL CWindowInfoService_GetProcessId(IWTSWindowInfoService* This,
73 DWORD* processId)
74{
75 WINPR_UNUSED(This);
76 if (!processId)
77 return E_POINTER;
78 *processId = GetCurrentProcessId();
79 return S_OK;
80}
81
82static HRESULT WINPR_COM_CALL CWindowInfoService_GetRdpSessionType(IWTSWindowInfoService* This,
83 UINT32* sessionType)
84{
85 CWindowInfoService* s = (CWindowInfoService*)This;
86 if (!sessionType)
87 return E_POINTER;
88
89 HRESULT hr = E_UNEXPECTED;
90 EnterCriticalSection(&s->lock);
91 const WINDOW_INFO_SOURCE* src = source_locked(s);
92 if (src)
93 {
94 *sessionType = src->GetSessionType(src->custom);
95 hr = S_OK;
96 }
97 LeaveCriticalSection(&s->lock);
98 return hr;
99}
100
101static HRESULT WINPR_COM_CALL CWindowInfoService_SubscribeWindowChanged(
102 IWTSWindowInfoService* This, UINT64 remoteHwnd, IWTSWindowChangedCallback* callback)
103{
104 CWindowInfoService* s = (CWindowInfoService*)This;
105 WINPR_UNUSED(remoteHwnd); /* Microsoft's client doesn't use it either */
106 if (!callback)
107 return E_POINTER;
108
109 HRESULT hr = S_OK;
110 EnterCriticalSection(&s->lock);
111 if (s->detached)
112 hr = E_UNEXPECTED;
113 else if (s->subscriber)
114 hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
115 else
116 {
117 WINPR_COM_ADDREF(callback);
118 s->subscriber = callback;
119 }
120 LeaveCriticalSection(&s->lock);
121 return hr;
122}
123
124static HRESULT WINPR_COM_CALL
125CWindowInfoService_UnsubscribeWindowChanged(IWTSWindowInfoService* This)
126{
127 CWindowInfoService* s = (CWindowInfoService*)This;
128
129 EnterCriticalSection(&s->lock);
130 IWTSWindowChangedCallback* callback = s->subscriber;
131 s->subscriber = nullptr;
132 LeaveCriticalSection(&s->lock);
133
134 if (!callback)
135 return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
136 WINPR_COM_RELEASE(callback);
137 return S_OK;
138}
139
140static const IWTSWindowInfoServiceVtbl CWindowInfoService_vtbl = {
141 .iunknown = WINPR_COM_IUNKNOWN_ENTRIES(CWindowInfoService),
142 .GetWindowInfo = CWindowInfoService_GetWindowInfo,
143 .GetProcessId = CWindowInfoService_GetProcessId,
144 .GetRdpSessionType = CWindowInfoService_GetRdpSessionType,
145 .SubscribeWindowChanged = CWindowInfoService_SubscribeWindowChanged,
146 .UnsubscribeWindowChanged = CWindowInfoService_UnsubscribeWindowChanged,
147};
148
149static void CWindowInfoService_free(wComBase* object)
150{
151 CWindowInfoService* s = (CWindowInfoService*)object;
152 if (s->subscriber)
153 WINPR_COM_RELEASE(s->subscriber);
154 DeleteCriticalSection(&s->lock);
155 free(s);
156}
157
158IWTSWindowInfoService* freerdp_client_window_info_service_new(const WINDOW_INFO_SOURCE* source)
159{
160 WINPR_ASSERT(source);
161 WINPR_ASSERT(source->GetWindowInfo);
162 WINPR_ASSERT(source->GetSessionType);
163
164 CWindowInfoService* s = calloc(1, sizeof(*s));
165 if (!s)
166 return nullptr;
167 if (!InitializeCriticalSectionAndSpinCount(&s->lock, 4000))
168 {
169 free(s);
170 return nullptr;
171 }
172 winpr_com_base_init(&s->com, "CWindowInfoService", &CWindowInfoService_vtbl.iunknown,
173 CWindowInfoService_free);
174 s->source = *source;
175 return (IWTSWindowInfoService*)s;
176}
177
178void freerdp_client_window_info_service_changed(IWTSWindowInfoService* service, UINT64 remoteHwnd)
179{
180 CWindowInfoService* s = (CWindowInfoService*)service;
181 if (!s)
182 return;
183
184 WTS_WINDOW_INFO info = WINPR_C_ARRAY_INIT;
185 IWTSWindowChangedCallback* callback = nullptr;
186 EnterCriticalSection(&s->lock);
187 const WINDOW_INFO_SOURCE* src = source_locked(s);
188 if (src && s->subscriber && SUCCEEDED(src->GetWindowInfo(src->custom, remoteHwnd, &info)))
189 {
190 callback = s->subscriber;
191 WINPR_COM_ADDREF(callback);
192 }
193 LeaveCriticalSection(&s->lock);
194
195 if (!callback)
196 return;
197 /* outside the lock: the subscriber may call the service back */
198 const HRESULT hr = callback->lpVtbl->OnWindowChanged(callback, &info);
199 if (FAILED(hr))
200 WLog_DBG(TAG, "OnWindowChanged failed: 0x%08" PRIX32, (UINT32)hr);
201 WINPR_COM_RELEASE(callback);
202}
203
204void freerdp_client_window_info_service_detach(IWTSWindowInfoService* service)
205{
206 CWindowInfoService* s = (CWindowInfoService*)service;
207 if (!s)
208 return;
209 EnterCriticalSection(&s->lock);
210 s->detached = TRUE;
211 LeaveCriticalSection(&s->lock);
212}
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_CALL
the calling convention of COM methods (STDMETHODCALLTYPE)
Definition com.h:73
what the client gives the service to answer from
UINT32(* GetSessionType)(void *custom)
a WTS_RDP_SESSION_TYPE. Called from any thread.
HRESULT(* GetWindowInfo)(void *custom, UINT64 remoteHwnd, WTS_WINDOW_INFO *info)
fills info as Microsoft's client does (see WTS_WINDOW_INFO): for a desktop session the window showing...
40 bytes on x64