[USER32] Add support for navigating a group of radio buttons using a keyboard.
[reactos.git] / win32ss / user / user32 / misc / winsta.c
1 /*
2 * PROJECT: ReactOS user32.dll
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Window stations
5 * COPYRIGHT: Copyright 2001-2018 Casper S. Hornstrup (chorns@users.sourceforge.net)
6 * Copyright 2011-2018 Giannis Adamopoulos
7 */
8
9 #include <user32.h>
10
11 WINE_DEFAULT_DEBUG_CHANNEL(winsta);
12
13 /*
14 * @implemented
15 */
16 HWINSTA
17 WINAPI
18 CreateWindowStationA(
19 IN LPCSTR lpwinsta OPTIONAL,
20 IN DWORD dwReserved,
21 IN ACCESS_MASK dwDesiredAccess,
22 IN LPSECURITY_ATTRIBUTES lpsa OPTIONAL)
23 {
24 HWINSTA hWinSta;
25 UNICODE_STRING WindowStationNameU;
26
27 if (lpwinsta)
28 {
29 /* After conversion, the buffer is zero-terminated */
30 RtlCreateUnicodeStringFromAsciiz(&WindowStationNameU, lpwinsta);
31 }
32 else
33 {
34 RtlInitUnicodeString(&WindowStationNameU, NULL);
35 }
36
37 hWinSta = CreateWindowStationW(WindowStationNameU.Buffer,
38 dwReserved,
39 dwDesiredAccess,
40 lpsa);
41
42 /* Free the string if it was allocated */
43 if (lpwinsta) RtlFreeUnicodeString(&WindowStationNameU);
44
45 return hWinSta;
46 }
47
48
49 /*
50 * @implemented
51 */
52 HWINSTA
53 WINAPI
54 CreateWindowStationW(
55 IN LPCWSTR lpwinsta OPTIONAL,
56 IN DWORD dwReserved,
57 IN ACCESS_MASK dwDesiredAccess,
58 IN LPSECURITY_ATTRIBUTES lpsa OPTIONAL)
59 {
60 NTSTATUS Status;
61 HWINSTA hWinSta;
62 UNICODE_STRING WindowStationName;
63 // FIXME: We should cache a per-session directory (see ntuser\winsta.c!UserCreateWinstaDirectory).
64 UNICODE_STRING WindowStationsDir = RTL_CONSTANT_STRING(L"\\Windows\\WindowStations");
65 OBJECT_ATTRIBUTES ObjectAttributes;
66 HANDLE hWindowStationsDir;
67
68 /*
69 * If provided, the window station name is always relative to the
70 * current user session's WindowStations directory.
71 * Otherwise (the window station name is NULL or an empty string),
72 * pass both an empty string and no WindowStations directory handle
73 * to win32k, so that it will create a window station whose name
74 * is based on the logon session identifier for the calling process.
75 */
76 if (lpwinsta && *lpwinsta)
77 {
78 /* Open WindowStations directory */
79 InitializeObjectAttributes(&ObjectAttributes,
80 &WindowStationsDir,
81 OBJ_CASE_INSENSITIVE,
82 NULL,
83 NULL);
84
85 Status = NtOpenDirectoryObject(&hWindowStationsDir,
86 DIRECTORY_CREATE_OBJECT,
87 &ObjectAttributes);
88 if (!NT_SUCCESS(Status))
89 {
90 ERR("Failed to open WindowStations directory\n");
91 return NULL;
92 }
93
94 RtlInitUnicodeString(&WindowStationName, lpwinsta);
95 }
96 else
97 {
98 lpwinsta = NULL;
99 hWindowStationsDir = NULL;
100 }
101
102 /* Create the window station object */
103 InitializeObjectAttributes(&ObjectAttributes,
104 lpwinsta ? &WindowStationName : NULL,
105 OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
106 hWindowStationsDir,
107 NULL);
108
109 /* Check if the handle should be inheritable */
110 if (lpsa && lpsa->bInheritHandle)
111 {
112 ObjectAttributes.Attributes |= OBJ_INHERIT;
113 }
114
115 hWinSta = NtUserCreateWindowStation(&ObjectAttributes,
116 dwDesiredAccess,
117 0, 0, 0, 0, 0);
118
119 if (hWindowStationsDir)
120 NtClose(hWindowStationsDir);
121
122 return hWinSta;
123 }
124
125 /*
126 * Common code for EnumDesktopsA/W and EnumWindowStationsA/W
127 */
128 BOOL
129 FASTCALL
130 EnumNamesW(HWINSTA WindowStation,
131 NAMEENUMPROCW EnumFunc,
132 LPARAM Context,
133 BOOL Desktops)
134 {
135 CHAR Buffer[256];
136 PVOID NameList;
137 PWCHAR Name;
138 NTSTATUS Status;
139 ULONG RequiredSize;
140 ULONG CurrentEntry, EntryCount;
141 BOOL Ret;
142
143 /* Check parameters */
144 if (WindowStation == NULL && Desktops)
145 {
146 WindowStation = GetProcessWindowStation();
147 }
148
149 /* Try with fixed-size buffer */
150 Status = NtUserBuildNameList(WindowStation, sizeof(Buffer), Buffer, &RequiredSize);
151 if (NT_SUCCESS(Status))
152 {
153 /* Fixed-size buffer is large enough */
154 NameList = (PWCHAR) Buffer;
155 }
156 else if (Status == STATUS_BUFFER_TOO_SMALL)
157 {
158 /* Allocate a larger buffer */
159 NameList = HeapAlloc(GetProcessHeap(), 0, RequiredSize);
160 if (NameList == NULL)
161 return FALSE;
162
163 /* Try again */
164 Status = NtUserBuildNameList(WindowStation, RequiredSize, NameList, NULL);
165 if (!NT_SUCCESS(Status))
166 {
167 HeapFree(GetProcessHeap(), 0, NameList);
168 SetLastError(RtlNtStatusToDosError(Status));
169 return FALSE;
170 }
171 }
172 else
173 {
174 /* Some unrecognized error occured */
175 SetLastError(RtlNtStatusToDosError(Status));
176 return FALSE;
177 }
178
179 /* Enum the names one by one */
180 EntryCount = *((DWORD *) NameList);
181 Name = (PWCHAR) ((PCHAR) NameList + sizeof(DWORD));
182 Ret = TRUE;
183 for (CurrentEntry = 0; CurrentEntry < EntryCount && Ret; ++CurrentEntry)
184 {
185 Ret = (*EnumFunc)(Name, Context);
186 Name += wcslen(Name) + 1;
187 }
188
189 /* Cleanup */
190 if (NameList != Buffer)
191 {
192 HeapFree(GetProcessHeap(), 0, NameList);
193 }
194
195 return Ret;
196 }
197
198 /* For W->A conversion */
199 typedef struct tagENUMNAMESASCIICONTEXT
200 {
201 NAMEENUMPROCA UserEnumFunc;
202 LPARAM UserContext;
203 } ENUMNAMESASCIICONTEXT, *PENUMNAMESASCIICONTEXT;
204
205 /*
206 * Callback used by Ascii versions. Converts the Unicode name to
207 * Ascii and then calls the user callback
208 */
209 BOOL
210 CALLBACK
211 EnumNamesCallback(LPWSTR Name, LPARAM Param)
212 {
213 PENUMNAMESASCIICONTEXT Context = (PENUMNAMESASCIICONTEXT) Param;
214 CHAR FixedNameA[32];
215 LPSTR NameA;
216 INT Len;
217 BOOL Ret;
218
219 /*
220 * Determine required size of Ascii string and see
221 * if we can use fixed buffer.
222 */
223 Len = WideCharToMultiByte(CP_ACP, 0, Name, -1, NULL, 0, NULL, NULL);
224 if (Len <= 0)
225 {
226 /* Some strange error occured */
227 return FALSE;
228 }
229 else if (Len <= sizeof(FixedNameA))
230 {
231 /* Fixed-size buffer is large enough */
232 NameA = FixedNameA;
233 }
234 else
235 {
236 /* Allocate a larger buffer */
237 NameA = HeapAlloc(GetProcessHeap(), 0, Len);
238 if (NULL == NameA)
239 {
240 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
241 return FALSE;
242 }
243 }
244
245 /* Do the Unicode ->Ascii conversion */
246 if (0 == WideCharToMultiByte(CP_ACP, 0, Name, -1, NameA, Len, NULL, NULL))
247 {
248 /* Something went wrong, clean up */
249 if (NameA != FixedNameA)
250 {
251 HeapFree(GetProcessHeap(), 0, NameA);
252 }
253 return FALSE;
254 }
255
256 /* Call user callback */
257 Ret = Context->UserEnumFunc(NameA, Context->UserContext);
258
259 /* Cleanup */
260 if (NameA != FixedNameA)
261 {
262 HeapFree(GetProcessHeap(), 0, NameA);
263 }
264
265 return Ret;
266 }
267
268 /*
269 * Common code for EnumDesktopsA and EnumWindowStationsA
270 */
271 BOOL
272 FASTCALL
273 EnumNamesA(HWINSTA WindowStation,
274 NAMEENUMPROCA EnumFunc,
275 LPARAM Context,
276 BOOL Desktops)
277 {
278 ENUMNAMESASCIICONTEXT PrivateContext;
279
280 PrivateContext.UserEnumFunc = EnumFunc;
281 PrivateContext.UserContext = Context;
282
283 return EnumNamesW(WindowStation, EnumNamesCallback, (LPARAM) &PrivateContext, Desktops);
284 }
285
286 /*
287 * @implemented
288 */
289 BOOL
290 WINAPI
291 EnumWindowStationsA(
292 IN WINSTAENUMPROCA EnumFunc,
293 IN LPARAM Context)
294 {
295 return EnumNamesA(NULL, EnumFunc, Context, FALSE);
296 }
297
298
299 /*
300 * @implemented
301 */
302 BOOL
303 WINAPI
304 EnumWindowStationsW(
305 IN WINSTAENUMPROCW EnumFunc,
306 IN LPARAM Context)
307 {
308 return EnumNamesW(NULL, EnumFunc, Context, FALSE);
309 }
310
311
312 /*
313 * @unimplemented on Win32k side
314 */
315 BOOL
316 WINAPI
317 GetWinStationInfo(PVOID pUnknown)
318 {
319 return (BOOL)NtUserCallOneParam((DWORD_PTR)pUnknown, ONEPARAM_ROUTINE_GETWINSTAINFO);
320 }
321
322
323 /*
324 * @implemented
325 */
326 HWINSTA
327 WINAPI
328 OpenWindowStationA(
329 IN LPCSTR lpszWinSta,
330 IN BOOL fInherit,
331 IN ACCESS_MASK dwDesiredAccess)
332 {
333 HWINSTA hWinSta;
334 UNICODE_STRING WindowStationNameU;
335
336 if (lpszWinSta)
337 {
338 /* After conversion, the buffer is zero-terminated */
339 RtlCreateUnicodeStringFromAsciiz(&WindowStationNameU, lpszWinSta);
340 }
341 else
342 {
343 RtlInitUnicodeString(&WindowStationNameU, NULL);
344 }
345
346 hWinSta = OpenWindowStationW(WindowStationNameU.Buffer,
347 fInherit,
348 dwDesiredAccess);
349
350 /* Free the string if it was allocated */
351 if (lpszWinSta) RtlFreeUnicodeString(&WindowStationNameU);
352
353 return hWinSta;
354 }
355
356
357 /*
358 * @implemented
359 */
360 HWINSTA
361 WINAPI
362 OpenWindowStationW(
363 IN LPCWSTR lpszWinSta,
364 IN BOOL fInherit,
365 IN ACCESS_MASK dwDesiredAccess)
366 {
367 NTSTATUS Status;
368 HWINSTA hWinSta;
369 UNICODE_STRING WindowStationName;
370 // FIXME: We should cache a per-session directory (see ntuser\winsta.c!UserCreateWinstaDirectory).
371 UNICODE_STRING WindowStationsDir = RTL_CONSTANT_STRING(L"\\Windows\\WindowStations");
372 OBJECT_ATTRIBUTES ObjectAttributes;
373 HANDLE hWindowStationsDir;
374
375 /* Open WindowStations directory */
376 InitializeObjectAttributes(&ObjectAttributes,
377 &WindowStationsDir,
378 OBJ_CASE_INSENSITIVE,
379 NULL,
380 NULL);
381
382 Status = NtOpenDirectoryObject(&hWindowStationsDir,
383 DIRECTORY_TRAVERSE,
384 &ObjectAttributes);
385 if(!NT_SUCCESS(Status))
386 {
387 ERR("Failed to open WindowStations directory\n");
388 return NULL;
389 }
390
391 /* Open the window station object */
392 RtlInitUnicodeString(&WindowStationName, lpszWinSta);
393
394 InitializeObjectAttributes(&ObjectAttributes,
395 &WindowStationName,
396 OBJ_CASE_INSENSITIVE,
397 hWindowStationsDir,
398 NULL);
399
400 /* Check if the handle should be inheritable */
401 if (fInherit)
402 {
403 ObjectAttributes.Attributes |= OBJ_INHERIT;
404 }
405
406 hWinSta = NtUserOpenWindowStation(&ObjectAttributes, dwDesiredAccess);
407
408 NtClose(hWindowStationsDir);
409
410 return hWinSta;
411 }
412
413
414 /*
415 * @implemented
416 */
417 BOOL
418 WINAPI
419 SetWindowStationUser(
420 IN HWINSTA hWindowStation,
421 IN PLUID pluid,
422 IN PSID psid OPTIONAL,
423 IN DWORD size)
424 {
425 BOOL Success;
426
427 Success = NtUserSetWindowStationUser(hWindowStation, pluid, psid, size);
428 if (Success)
429 {
430 /* Signal log-on/off to WINSRV */
431
432 /* User is logging on if *pluid != LuidNone, otherwise it is a log-off */
433 LUID LuidNone = {0, 0};
434 BOOL IsLogon = (pluid && !RtlEqualLuid(pluid, &LuidNone));
435
436 Logon(IsLogon);
437 }
438
439 return Success;
440 }
441
442 /* EOF */