FreeRDP
Loading...
Searching...
No Matches
client/rail_main.c
1
26#include <freerdp/config.h>
27
28#include <winpr/crt.h>
29
30#include <freerdp/types.h>
31#include <freerdp/constants.h>
32#include <freerdp/freerdp.h>
33
34#include "rail_orders.h"
35#include "rail_main.h"
36
37#include "../../../channels/client/addin.h"
38
39RailClientContext* rail_get_client_interface(railPlugin* rail)
40{
41 RailClientContext* pInterface = nullptr;
42
43 if (!rail)
44 return nullptr;
45
46 pInterface = (RailClientContext*)rail->channelEntryPoints.pInterface;
47 return pInterface;
48}
49
55static UINT rail_send(railPlugin* rail, wStream* s)
56{
57 UINT status = 0;
58
59 if (!rail)
60 {
61 Stream_Free(s, TRUE);
62 return CHANNEL_RC_BAD_INIT_HANDLE;
63 }
64
65 status = rail->channelEntryPoints.pVirtualChannelWriteEx(
66 rail->InitHandle, rail->OpenHandle, Stream_Buffer(s), (UINT32)Stream_GetPosition(s), s);
67
68 if (status != CHANNEL_RC_OK)
69 {
70 Stream_Free(s, TRUE);
71 WLog_ERR(TAG, "pVirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
72 WTSErrorToString(status), status);
73 }
74
75 return status;
76}
77
83UINT rail_send_channel_data(railPlugin* rail, wStream* src)
84{
85 if (!rail || !src)
86 {
87 Stream_Free(src, TRUE);
88 return ERROR_INVALID_PARAMETER;
89 }
90
91 return rail_send(rail, src);
92}
93
103static UINT rail_client_execute(RailClientContext* context, const RAIL_EXEC_ORDER* exec)
104{
105 const char* exeOrFile = nullptr;
106 UINT error = 0;
107 railPlugin* rail = nullptr;
108 UINT16 flags = 0;
109 RAIL_UNICODE_STRING ruExeOrFile = WINPR_C_ARRAY_INIT;
110 RAIL_UNICODE_STRING ruWorkingDir = WINPR_C_ARRAY_INIT;
111 RAIL_UNICODE_STRING ruArguments = WINPR_C_ARRAY_INIT;
112
113 if (!context || !exec)
114 return ERROR_INVALID_PARAMETER;
115
116 rail = (railPlugin*)context->handle;
117 exeOrFile = exec->RemoteApplicationProgram;
118 flags = exec->flags;
119
120 if (!exeOrFile)
121 return ERROR_INVALID_PARAMETER;
122
123 if (!utf8_string_to_rail_string(exec->RemoteApplicationProgram,
124 &ruExeOrFile) || /* RemoteApplicationProgram */
125 !utf8_string_to_rail_string(exec->RemoteApplicationWorkingDir,
126 &ruWorkingDir) || /* ShellWorkingDirectory */
127 !utf8_string_to_rail_string(exec->RemoteApplicationArguments,
128 &ruArguments)) /* RemoteApplicationCmdLine */
129 error = ERROR_INTERNAL_ERROR;
130 else
131 error = rail_send_client_exec_order(rail, flags, &ruExeOrFile, &ruWorkingDir, &ruArguments);
132
133 rail_unicode_string_free(&ruExeOrFile);
134 rail_unicode_string_free(&ruWorkingDir);
135 rail_unicode_string_free(&ruArguments);
136 return error;
137}
138
144static UINT rail_client_activate(RailClientContext* context, const RAIL_ACTIVATE_ORDER* activate)
145{
146 railPlugin* rail = nullptr;
147
148 if (!context || !activate)
149 return ERROR_INVALID_PARAMETER;
150
151 rail = (railPlugin*)context->handle;
152 return rail_send_client_activate_order(rail, activate);
153}
154
160static UINT rail_send_client_sysparam(RailClientContext* context,
161 const RAIL_SYSPARAM_ORDER* sysparam)
162{
163 size_t length = RAIL_SYSPARAM_ORDER_LENGTH;
164
165 if (!context || !sysparam)
166 return ERROR_INVALID_PARAMETER;
167
168 railPlugin* rail = (railPlugin*)context->handle;
169
170 switch (sysparam->param)
171 {
172 case SPI_SET_DRAG_FULL_WINDOWS:
173 case SPI_SET_KEYBOARD_CUES:
174 case SPI_SET_KEYBOARD_PREF:
175 case SPI_SET_MOUSE_BUTTON_SWAP:
176 length += 1;
177 break;
178
179 case SPI_SET_WORK_AREA:
180 case SPI_DISPLAY_CHANGE:
181 case SPI_TASKBAR_POS:
182 length += 8;
183 break;
184
185 case SPI_SET_HIGH_CONTRAST:
186 length += sysparam->highContrast.colorSchemeLength + 10;
187 break;
188
189 case SPI_SETFILTERKEYS:
190 length += 20;
191 break;
192
193 case SPI_SETSTICKYKEYS:
194 case SPI_SETCARETWIDTH:
195 case SPI_SETTOGGLEKEYS:
196 length += 4;
197 break;
198
199 default:
200 return ERROR_BAD_ARGUMENTS;
201 }
202
203 wStream* s = rail_pdu_init(length);
204
205 if (!s)
206 {
207 WLog_ERR(TAG, "rail_pdu_init failed!");
208 return CHANNEL_RC_NO_MEMORY;
209 }
210
211 const BOOL extendedSpiSupported = rail_is_extended_spi_supported(rail->channelFlags);
212 const UINT error = rail_write_sysparam_order(s, sysparam, extendedSpiSupported);
213 if (error)
214 {
215 WLog_ERR(TAG, "rail_write_client_sysparam_order failed with error %" PRIu32 "!", error);
216 Stream_Free(s, TRUE);
217 return error;
218 }
219
220 return rail_send_pdu(rail, s, TS_RAIL_ORDER_SYSPARAM);
221}
222
228static UINT rail_client_system_param(RailClientContext* context,
229 const RAIL_SYSPARAM_ORDER* sysInParam)
230{
231 UINT error = CHANNEL_RC_OK;
232
233 if (!context || !sysInParam)
234 return ERROR_INVALID_PARAMETER;
235
236 RAIL_SYSPARAM_ORDER sysparam = *sysInParam;
237
238 if (sysparam.params & SPI_MASK_SET_HIGH_CONTRAST)
239 {
240 sysparam.param = SPI_SET_HIGH_CONTRAST;
241
242 if ((error = rail_send_client_sysparam(context, &sysparam)))
243 {
244 WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
245 return error;
246 }
247 }
248
249 if (sysparam.params & SPI_MASK_TASKBAR_POS)
250 {
251 sysparam.param = SPI_TASKBAR_POS;
252
253 if ((error = rail_send_client_sysparam(context, &sysparam)))
254 {
255 WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
256 return error;
257 }
258 }
259
260 if (sysparam.params & SPI_MASK_SET_MOUSE_BUTTON_SWAP)
261 {
262 sysparam.param = SPI_SET_MOUSE_BUTTON_SWAP;
263
264 if ((error = rail_send_client_sysparam(context, &sysparam)))
265 {
266 WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
267 return error;
268 }
269 }
270
271 if (sysparam.params & SPI_MASK_SET_KEYBOARD_PREF)
272 {
273 sysparam.param = SPI_SET_KEYBOARD_PREF;
274
275 if ((error = rail_send_client_sysparam(context, &sysparam)))
276 {
277 WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
278 return error;
279 }
280 }
281
282 if (sysparam.params & SPI_MASK_SET_DRAG_FULL_WINDOWS)
283 {
284 sysparam.param = SPI_SET_DRAG_FULL_WINDOWS;
285
286 if ((error = rail_send_client_sysparam(context, &sysparam)))
287 {
288 WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
289 return error;
290 }
291 }
292
293 if (sysparam.params & SPI_MASK_SET_KEYBOARD_CUES)
294 {
295 sysparam.param = SPI_SET_KEYBOARD_CUES;
296
297 if ((error = rail_send_client_sysparam(context, &sysparam)))
298 {
299 WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
300 return error;
301 }
302 }
303
304 if (sysparam.params & SPI_MASK_SET_WORK_AREA)
305 {
306 sysparam.param = SPI_SET_WORK_AREA;
307
308 if ((error = rail_send_client_sysparam(context, &sysparam)))
309 {
310 WLog_ERR(TAG, "rail_send_client_sysparam failed with error %" PRIu32 "!", error);
311 return error;
312 }
313 }
314
315 return error;
316}
317
323static UINT rail_client_system_command(RailClientContext* context,
324 const RAIL_SYSCOMMAND_ORDER* syscommand)
325{
326 railPlugin* rail = nullptr;
327
328 if (!context || !syscommand)
329 return ERROR_INVALID_PARAMETER;
330
331 rail = (railPlugin*)context->handle;
332 return rail_send_client_syscommand_order(rail, syscommand);
333}
334
340static UINT rail_client_handshake(RailClientContext* context, const RAIL_HANDSHAKE_ORDER* handshake)
341{
342 if (!context || !handshake)
343 return ERROR_INVALID_PARAMETER;
344
345 railPlugin* rail = (railPlugin*)context->handle;
346 WINPR_ASSERT(rail);
347 WLog_Print(rail->log, WLOG_DEBUG, "build=0x%08" PRIx32, handshake->buildNumber);
348 return rail_send_handshake_order(rail, handshake);
349}
350
351static UINT rail_server_handshake(RailClientContext* context, const RAIL_HANDSHAKE_ORDER* handshake)
352{
353 WINPR_ASSERT(context);
354 WINPR_ASSERT(handshake);
355
356 railPlugin* rail = (railPlugin*)context->handle;
357 WINPR_ASSERT(rail);
358
359 WLog_Print(rail->log, WLOG_DEBUG, "build=0x%08" PRIx32, handshake->buildNumber);
360
361 const UINT32 buildnumber =
362 freerdp_settings_get_uint32(rail->rdpcontext->settings, FreeRDP_ClientBuild);
363 const RAIL_HANDSHAKE_ORDER clientHandshake = { buildnumber };
364 const UINT status = context->ClientHandshake(context, &clientHandshake);
365 if (status != CHANNEL_RC_OK)
366 return status;
367 /* Advertise client status before waiting for monitored-desktop orders. */
368 return rail_send_client_status(context);
369}
370
371static UINT rail_server_handshake_ex(RailClientContext* context,
372 const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
373{
374 WINPR_ASSERT(context);
375 WINPR_ASSERT(handshakeEx);
376
377 railPlugin* rail = (railPlugin*)context->handle;
378 WINPR_ASSERT(rail);
379
380 const RAIL_HANDSHAKE_ORDER handshake = { handshakeEx->buildNumber };
381
382 char buffer[128] = WINPR_C_ARRAY_INIT;
383 WLog_Print(
384 rail->log, WLOG_DEBUG, "build=0x%08" PRIx32 ", flags=%s", handshakeEx->buildNumber,
385 rail_handshake_ex_flags_to_string(handshakeEx->railHandshakeFlags, buffer, sizeof(buffer)));
386 return rail_server_handshake(context, &handshake);
387}
388
394static UINT rail_client_notify_event(RailClientContext* context,
395 const RAIL_NOTIFY_EVENT_ORDER* notifyEvent)
396{
397 railPlugin* rail = nullptr;
398
399 if (!context || !notifyEvent)
400 return ERROR_INVALID_PARAMETER;
401
402 rail = (railPlugin*)context->handle;
403 return rail_send_client_notify_event_order(rail, notifyEvent);
404}
405
411static UINT rail_client_window_move(RailClientContext* context,
412 const RAIL_WINDOW_MOVE_ORDER* windowMove)
413{
414 railPlugin* rail = nullptr;
415
416 if (!context || !windowMove)
417 return ERROR_INVALID_PARAMETER;
418
419 rail = (railPlugin*)context->handle;
420 return rail_send_client_window_move_order(rail, windowMove);
421}
422
428static UINT rail_client_information(RailClientContext* context,
429 const RAIL_CLIENT_STATUS_ORDER* clientStatus)
430{
431 railPlugin* rail = nullptr;
432
433 if (!context || !clientStatus)
434 return ERROR_INVALID_PARAMETER;
435
436 rail = (railPlugin*)context->handle;
437 return rail_send_client_status_order(rail, clientStatus);
438}
439
445static UINT rail_client_system_menu(RailClientContext* context, const RAIL_SYSMENU_ORDER* sysmenu)
446{
447 railPlugin* rail = nullptr;
448
449 if (!context || !sysmenu)
450 return ERROR_INVALID_PARAMETER;
451
452 rail = (railPlugin*)context->handle;
453 return rail_send_client_sysmenu_order(rail, sysmenu);
454}
455
461static UINT rail_client_language_bar_info(RailClientContext* context,
462 const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
463{
464 railPlugin* rail = nullptr;
465
466 if (!context || !langBarInfo)
467 return ERROR_INVALID_PARAMETER;
468
469 rail = (railPlugin*)context->handle;
470 return rail_send_client_langbar_info_order(rail, langBarInfo);
471}
472
473static UINT rail_client_language_ime_info(RailClientContext* context,
474 const RAIL_LANGUAGEIME_INFO_ORDER* langImeInfo)
475{
476 railPlugin* rail = nullptr;
477
478 if (!context || !langImeInfo)
479 return ERROR_INVALID_PARAMETER;
480
481 rail = (railPlugin*)context->handle;
482 return rail_send_client_languageime_info_order(rail, langImeInfo);
483}
484
490static UINT rail_client_get_appid_request(RailClientContext* context,
491 const RAIL_GET_APPID_REQ_ORDER* getAppIdReq)
492{
493 railPlugin* rail = nullptr;
494
495 if (!context || !getAppIdReq || !context->handle)
496 return ERROR_INVALID_PARAMETER;
497
498 rail = (railPlugin*)context->handle;
499 return rail_send_client_get_appid_req_order(rail, getAppIdReq);
500}
501
502static UINT rail_client_compartment_info(RailClientContext* context,
503 const RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo)
504{
505 railPlugin* rail = nullptr;
506
507 if (!context || !compartmentInfo || !context->handle)
508 return ERROR_INVALID_PARAMETER;
509
510 rail = (railPlugin*)context->handle;
511 return rail_send_client_compartment_info_order(rail, compartmentInfo);
512}
513
514static UINT rail_client_cloak(RailClientContext* context, const RAIL_CLOAK* cloak)
515{
516 railPlugin* rail = nullptr;
517
518 if (!context || !cloak || !context->handle)
519 return ERROR_INVALID_PARAMETER;
520
521 rail = (railPlugin*)context->handle;
522 return rail_send_client_cloak_order(rail, cloak);
523}
524
525static UINT rail_client_snap_arrange(RailClientContext* context, const RAIL_SNAP_ARRANGE* snap)
526{
527 railPlugin* rail = nullptr;
528
529 if (!context || !snap || !context->handle)
530 return ERROR_INVALID_PARAMETER;
531
532 rail = (railPlugin*)context->handle;
533 return rail_send_client_snap_arrange_order(rail, snap);
534}
535
536static UINT rail_client_text_scale(RailClientContext* context, UINT32 textScale)
537{
538 if (!context || !context->handle)
539 return ERROR_INVALID_PARAMETER;
540
541 railPlugin* rail = (railPlugin*)context->handle;
542 return rail_send_client_text_scale_order(rail, textScale);
543}
544
545static UINT rail_client_caret_blink_rate(RailClientContext* context, UINT32 rate)
546{
547 if (!context || !context->handle)
548 return ERROR_INVALID_PARAMETER;
549
550 railPlugin* rail = (railPlugin*)context->handle;
551 return rail_send_client_caret_blink_rate_order(rail, rate);
552}
553
554static VOID VCAPITYPE rail_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
555 UINT event, LPVOID pData,
556 UINT32 dataLength, UINT32 totalLength,
557 UINT32 dataFlags)
558{
559 UINT error = CHANNEL_RC_OK;
560 railPlugin* rail = (railPlugin*)lpUserParam;
561
562 switch (event)
563 {
564 case CHANNEL_EVENT_DATA_RECEIVED:
565 if (!rail || (rail->OpenHandle != openHandle))
566 {
567 WLog_ERR(TAG, "error no match");
568 return;
569 }
570
571 if ((error = channel_client_post_message(rail->MsgsHandle, pData, dataLength,
572 totalLength, dataFlags)))
573 {
574 WLog_ERR(TAG,
575 "rail_virtual_channel_event_data_received"
576 " failed with error %" PRIu32 "!",
577 error);
578 }
579
580 break;
581
582 case CHANNEL_EVENT_WRITE_CANCELLED:
583 case CHANNEL_EVENT_WRITE_COMPLETE:
584 {
585 wStream* s = (wStream*)pData;
586 Stream_Free(s, TRUE);
587 }
588 break;
589
590 case CHANNEL_EVENT_USER:
591 break;
592 default:
593 break;
594 }
595
596 if (error && rail && rail->rdpcontext)
597 setChannelError(rail->rdpcontext, error,
598 "rail_virtual_channel_open_event reported an error");
599}
600
606static UINT rail_virtual_channel_event_connected(railPlugin* rail, WINPR_ATTR_UNUSED LPVOID pData,
607 WINPR_ATTR_UNUSED UINT32 dataLength)
608{
609 WINPR_ASSERT(rail);
610
611 rail->MsgsHandle = channel_client_create_handler(rail->rdpcontext, rail, rail_order_recv,
612 RAIL_SVC_CHANNEL_NAME);
613 if (!rail->MsgsHandle)
614 return ERROR_INTERNAL_ERROR;
615
616 return rail->channelEntryPoints.pVirtualChannelOpenEx(rail->InitHandle, &rail->OpenHandle,
617 rail->channelDef.name,
618 rail_virtual_channel_open_event_ex);
619}
620
626static UINT rail_virtual_channel_event_disconnected(railPlugin* rail)
627{
628 UINT rc = 0;
629
630 channel_client_quit_handler(rail->MsgsHandle);
631 if (rail->OpenHandle == 0)
632 return CHANNEL_RC_OK;
633
634 WINPR_ASSERT(rail->channelEntryPoints.pVirtualChannelCloseEx);
635 rc = rail->channelEntryPoints.pVirtualChannelCloseEx(rail->InitHandle, rail->OpenHandle);
636
637 if (CHANNEL_RC_OK != rc)
638 {
639 WLog_ERR(TAG, "pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]", WTSErrorToString(rc),
640 rc);
641 return rc;
642 }
643
644 rail->OpenHandle = 0;
645
646 return CHANNEL_RC_OK;
647}
648
649static void rail_virtual_channel_event_terminated(railPlugin* rail)
650{
651 rail->InitHandle = nullptr;
652 free(rail->context);
653 free(rail);
654}
655
656static VOID VCAPITYPE rail_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
657 UINT event, LPVOID pData, UINT dataLength)
658{
659 UINT error = CHANNEL_RC_OK;
660 railPlugin* rail = (railPlugin*)lpUserParam;
661
662 if (!rail || (rail->InitHandle != pInitHandle))
663 {
664 WLog_ERR(TAG, "error no match");
665 return;
666 }
667
668 switch (event)
669 {
670 case CHANNEL_EVENT_CONNECTED:
671 if ((error = rail_virtual_channel_event_connected(rail, pData, dataLength)))
672 WLog_ERR(TAG, "rail_virtual_channel_event_connected failed with error %" PRIu32 "!",
673 error);
674
675 break;
676
677 case CHANNEL_EVENT_DISCONNECTED:
678 if ((error = rail_virtual_channel_event_disconnected(rail)))
679 WLog_ERR(TAG,
680 "rail_virtual_channel_event_disconnected failed with error %" PRIu32 "!",
681 error);
682
683 break;
684
685 case CHANNEL_EVENT_TERMINATED:
686 rail_virtual_channel_event_terminated(rail);
687 break;
688
689 case CHANNEL_EVENT_ATTACHED:
690 case CHANNEL_EVENT_DETACHED:
691 default:
692 break;
693 }
694
695 if (error && rail->rdpcontext)
696 setChannelError(rail->rdpcontext, error,
697 "rail_virtual_channel_init_event_ex reported an error");
698}
699
700/* rail is always built-in */
701#define VirtualChannelEntryEx rail_VirtualChannelEntryEx
702
703FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,
704 PVOID pInitHandle))
705{
706 UINT rc = 0;
707 railPlugin* rail = nullptr;
708 RailClientContext* context = nullptr;
709 CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx = nullptr;
710 BOOL isFreerdp = FALSE;
711 rail = (railPlugin*)calloc(1, sizeof(railPlugin));
712
713 if (!rail)
714 {
715 WLog_ERR(TAG, "calloc failed!");
716 return FALSE;
717 }
718
719 rail->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
720 CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL;
721 (void)sprintf_s(rail->channelDef.name, ARRAYSIZE(rail->channelDef.name), RAIL_SVC_CHANNEL_NAME);
722 pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
723
724 if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
725 (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
726 {
727 context = (RailClientContext*)calloc(1, sizeof(RailClientContext));
728
729 if (!context)
730 {
731 WLog_ERR(TAG, "calloc failed!");
732 free(rail);
733 return FALSE;
734 }
735
736 context->handle = (void*)rail;
737 context->custom = nullptr;
738 context->ClientExecute = rail_client_execute;
739 context->ClientActivate = rail_client_activate;
740 context->ClientSystemParam = rail_client_system_param;
741 context->ClientSystemCommand = rail_client_system_command;
742 context->ClientHandshake = rail_client_handshake;
743 context->ServerHandshake = rail_server_handshake;
744 context->ServerHandshakeEx = rail_server_handshake_ex;
745 context->ClientNotifyEvent = rail_client_notify_event;
746 context->ClientWindowMove = rail_client_window_move;
747 context->ClientInformation = rail_client_information;
748 context->ClientSystemMenu = rail_client_system_menu;
749 context->ClientLanguageBarInfo = rail_client_language_bar_info;
750 context->ClientLanguageIMEInfo = rail_client_language_ime_info;
751 context->ClientGetAppIdRequest = rail_client_get_appid_request;
752 context->ClientSnapArrange = rail_client_snap_arrange;
753 context->ClientCloak = rail_client_cloak;
754 context->ClientCompartmentInfo = rail_client_compartment_info;
755 context->ClientTextScale = rail_client_text_scale;
756 context->ClientCaretBlinkRate = rail_client_caret_blink_rate;
757 rail->rdpcontext = pEntryPointsEx->context;
758 rail->context = context;
759 isFreerdp = TRUE;
760 }
761
762 rail->log = WLog_Get("com.freerdp.channels.rail.client");
763 WLog_Print(rail->log, WLOG_DEBUG, "VirtualChannelEntryEx");
764 CopyMemory(&(rail->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
765 rail->InitHandle = pInitHandle;
766 rc = rail->channelEntryPoints.pVirtualChannelInitEx(
767 rail, context, pInitHandle, &rail->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
768 rail_virtual_channel_init_event_ex);
769
770 if (CHANNEL_RC_OK != rc)
771 {
772 WLog_ERR(TAG, "failed with %s [%08" PRIX32 "]", WTSErrorToString(rc), rc);
773 goto error_out;
774 }
775
776 rail->channelEntryPoints.pInterface = context;
777 return TRUE;
778error_out:
779
780 if (isFreerdp)
781 free(rail->context);
782
783 free(rail);
784 return FALSE;
785}
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
Definition svc.h:60