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 free(ruExeOrFile.string);
134 free(ruWorkingDir.string);
135 free(ruArguments.string);
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 return context->ClientHandshake(context, &clientHandshake);
365}
366
367static UINT rail_server_handshake_ex(RailClientContext* context,
368 const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
369{
370 WINPR_ASSERT(context);
371 WINPR_ASSERT(handshakeEx);
372
373 railPlugin* rail = (railPlugin*)context->handle;
374 WINPR_ASSERT(rail);
375
376 const RAIL_HANDSHAKE_ORDER handshake = { handshakeEx->buildNumber };
377
378 char buffer[128] = WINPR_C_ARRAY_INIT;
379 WLog_Print(
380 rail->log, WLOG_DEBUG, "build=0x%08" PRIx32 ", flags=%s", handshakeEx->buildNumber,
381 rail_handshake_ex_flags_to_string(handshakeEx->railHandshakeFlags, buffer, sizeof(buffer)));
382 return rail_server_handshake(context, &handshake);
383}
384
390static UINT rail_client_notify_event(RailClientContext* context,
391 const RAIL_NOTIFY_EVENT_ORDER* notifyEvent)
392{
393 railPlugin* rail = nullptr;
394
395 if (!context || !notifyEvent)
396 return ERROR_INVALID_PARAMETER;
397
398 rail = (railPlugin*)context->handle;
399 return rail_send_client_notify_event_order(rail, notifyEvent);
400}
401
407static UINT rail_client_window_move(RailClientContext* context,
408 const RAIL_WINDOW_MOVE_ORDER* windowMove)
409{
410 railPlugin* rail = nullptr;
411
412 if (!context || !windowMove)
413 return ERROR_INVALID_PARAMETER;
414
415 rail = (railPlugin*)context->handle;
416 return rail_send_client_window_move_order(rail, windowMove);
417}
418
424static UINT rail_client_information(RailClientContext* context,
425 const RAIL_CLIENT_STATUS_ORDER* clientStatus)
426{
427 railPlugin* rail = nullptr;
428
429 if (!context || !clientStatus)
430 return ERROR_INVALID_PARAMETER;
431
432 rail = (railPlugin*)context->handle;
433 return rail_send_client_status_order(rail, clientStatus);
434}
435
441static UINT rail_client_system_menu(RailClientContext* context, const RAIL_SYSMENU_ORDER* sysmenu)
442{
443 railPlugin* rail = nullptr;
444
445 if (!context || !sysmenu)
446 return ERROR_INVALID_PARAMETER;
447
448 rail = (railPlugin*)context->handle;
449 return rail_send_client_sysmenu_order(rail, sysmenu);
450}
451
457static UINT rail_client_language_bar_info(RailClientContext* context,
458 const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
459{
460 railPlugin* rail = nullptr;
461
462 if (!context || !langBarInfo)
463 return ERROR_INVALID_PARAMETER;
464
465 rail = (railPlugin*)context->handle;
466 return rail_send_client_langbar_info_order(rail, langBarInfo);
467}
468
469static UINT rail_client_language_ime_info(RailClientContext* context,
470 const RAIL_LANGUAGEIME_INFO_ORDER* langImeInfo)
471{
472 railPlugin* rail = nullptr;
473
474 if (!context || !langImeInfo)
475 return ERROR_INVALID_PARAMETER;
476
477 rail = (railPlugin*)context->handle;
478 return rail_send_client_languageime_info_order(rail, langImeInfo);
479}
480
486static UINT rail_client_get_appid_request(RailClientContext* context,
487 const RAIL_GET_APPID_REQ_ORDER* getAppIdReq)
488{
489 railPlugin* rail = nullptr;
490
491 if (!context || !getAppIdReq || !context->handle)
492 return ERROR_INVALID_PARAMETER;
493
494 rail = (railPlugin*)context->handle;
495 return rail_send_client_get_appid_req_order(rail, getAppIdReq);
496}
497
498static UINT rail_client_compartment_info(RailClientContext* context,
499 const RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo)
500{
501 railPlugin* rail = nullptr;
502
503 if (!context || !compartmentInfo || !context->handle)
504 return ERROR_INVALID_PARAMETER;
505
506 rail = (railPlugin*)context->handle;
507 return rail_send_client_compartment_info_order(rail, compartmentInfo);
508}
509
510static UINT rail_client_cloak(RailClientContext* context, const RAIL_CLOAK* cloak)
511{
512 railPlugin* rail = nullptr;
513
514 if (!context || !cloak || !context->handle)
515 return ERROR_INVALID_PARAMETER;
516
517 rail = (railPlugin*)context->handle;
518 return rail_send_client_cloak_order(rail, cloak);
519}
520
521static UINT rail_client_snap_arrange(RailClientContext* context, const RAIL_SNAP_ARRANGE* snap)
522{
523 railPlugin* rail = nullptr;
524
525 if (!context || !snap || !context->handle)
526 return ERROR_INVALID_PARAMETER;
527
528 rail = (railPlugin*)context->handle;
529 return rail_send_client_snap_arrange_order(rail, snap);
530}
531
532static UINT rail_client_text_scale(RailClientContext* context, UINT32 textScale)
533{
534 if (!context || !context->handle)
535 return ERROR_INVALID_PARAMETER;
536
537 railPlugin* rail = (railPlugin*)context->handle;
538 return rail_send_client_text_scale_order(rail, textScale);
539}
540
541static UINT rail_client_caret_blink_rate(RailClientContext* context, UINT32 rate)
542{
543 if (!context || !context->handle)
544 return ERROR_INVALID_PARAMETER;
545
546 railPlugin* rail = (railPlugin*)context->handle;
547 return rail_send_client_caret_blink_rate_order(rail, rate);
548}
549
550static VOID VCAPITYPE rail_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
551 UINT event, LPVOID pData,
552 UINT32 dataLength, UINT32 totalLength,
553 UINT32 dataFlags)
554{
555 UINT error = CHANNEL_RC_OK;
556 railPlugin* rail = (railPlugin*)lpUserParam;
557
558 switch (event)
559 {
560 case CHANNEL_EVENT_DATA_RECEIVED:
561 if (!rail || (rail->OpenHandle != openHandle))
562 {
563 WLog_ERR(TAG, "error no match");
564 return;
565 }
566
567 if ((error = channel_client_post_message(rail->MsgsHandle, pData, dataLength,
568 totalLength, dataFlags)))
569 {
570 WLog_ERR(TAG,
571 "rail_virtual_channel_event_data_received"
572 " failed with error %" PRIu32 "!",
573 error);
574 }
575
576 break;
577
578 case CHANNEL_EVENT_WRITE_CANCELLED:
579 case CHANNEL_EVENT_WRITE_COMPLETE:
580 {
581 wStream* s = (wStream*)pData;
582 Stream_Free(s, TRUE);
583 }
584 break;
585
586 case CHANNEL_EVENT_USER:
587 break;
588 default:
589 break;
590 }
591
592 if (error && rail && rail->rdpcontext)
593 setChannelError(rail->rdpcontext, error,
594 "rail_virtual_channel_open_event reported an error");
595}
596
602static UINT rail_virtual_channel_event_connected(railPlugin* rail, WINPR_ATTR_UNUSED LPVOID pData,
603 WINPR_ATTR_UNUSED UINT32 dataLength)
604{
605 WINPR_ASSERT(rail);
606
607 rail->MsgsHandle = channel_client_create_handler(rail->rdpcontext, rail, rail_order_recv,
608 RAIL_SVC_CHANNEL_NAME);
609 if (!rail->MsgsHandle)
610 return ERROR_INTERNAL_ERROR;
611
612 return rail->channelEntryPoints.pVirtualChannelOpenEx(rail->InitHandle, &rail->OpenHandle,
613 rail->channelDef.name,
614 rail_virtual_channel_open_event_ex);
615}
616
622static UINT rail_virtual_channel_event_disconnected(railPlugin* rail)
623{
624 UINT rc = 0;
625
626 channel_client_quit_handler(rail->MsgsHandle);
627 if (rail->OpenHandle == 0)
628 return CHANNEL_RC_OK;
629
630 WINPR_ASSERT(rail->channelEntryPoints.pVirtualChannelCloseEx);
631 rc = rail->channelEntryPoints.pVirtualChannelCloseEx(rail->InitHandle, rail->OpenHandle);
632
633 if (CHANNEL_RC_OK != rc)
634 {
635 WLog_ERR(TAG, "pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]", WTSErrorToString(rc),
636 rc);
637 return rc;
638 }
639
640 rail->OpenHandle = 0;
641
642 return CHANNEL_RC_OK;
643}
644
645static void rail_virtual_channel_event_terminated(railPlugin* rail)
646{
647 rail->InitHandle = nullptr;
648 free(rail->context);
649 free(rail);
650}
651
652static VOID VCAPITYPE rail_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
653 UINT event, LPVOID pData, UINT dataLength)
654{
655 UINT error = CHANNEL_RC_OK;
656 railPlugin* rail = (railPlugin*)lpUserParam;
657
658 if (!rail || (rail->InitHandle != pInitHandle))
659 {
660 WLog_ERR(TAG, "error no match");
661 return;
662 }
663
664 switch (event)
665 {
666 case CHANNEL_EVENT_CONNECTED:
667 if ((error = rail_virtual_channel_event_connected(rail, pData, dataLength)))
668 WLog_ERR(TAG, "rail_virtual_channel_event_connected failed with error %" PRIu32 "!",
669 error);
670
671 break;
672
673 case CHANNEL_EVENT_DISCONNECTED:
674 if ((error = rail_virtual_channel_event_disconnected(rail)))
675 WLog_ERR(TAG,
676 "rail_virtual_channel_event_disconnected failed with error %" PRIu32 "!",
677 error);
678
679 break;
680
681 case CHANNEL_EVENT_TERMINATED:
682 rail_virtual_channel_event_terminated(rail);
683 break;
684
685 case CHANNEL_EVENT_ATTACHED:
686 case CHANNEL_EVENT_DETACHED:
687 default:
688 break;
689 }
690
691 if (error && rail->rdpcontext)
692 setChannelError(rail->rdpcontext, error,
693 "rail_virtual_channel_init_event_ex reported an error");
694}
695
696/* rail is always built-in */
697#define VirtualChannelEntryEx rail_VirtualChannelEntryEx
698
699FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,
700 PVOID pInitHandle))
701{
702 UINT rc = 0;
703 railPlugin* rail = nullptr;
704 RailClientContext* context = nullptr;
705 CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx = nullptr;
706 BOOL isFreerdp = FALSE;
707 rail = (railPlugin*)calloc(1, sizeof(railPlugin));
708
709 if (!rail)
710 {
711 WLog_ERR(TAG, "calloc failed!");
712 return FALSE;
713 }
714
715 rail->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
716 CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL;
717 (void)sprintf_s(rail->channelDef.name, ARRAYSIZE(rail->channelDef.name), RAIL_SVC_CHANNEL_NAME);
718 pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
719
720 if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
721 (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
722 {
723 context = (RailClientContext*)calloc(1, sizeof(RailClientContext));
724
725 if (!context)
726 {
727 WLog_ERR(TAG, "calloc failed!");
728 free(rail);
729 return FALSE;
730 }
731
732 context->handle = (void*)rail;
733 context->custom = nullptr;
734 context->ClientExecute = rail_client_execute;
735 context->ClientActivate = rail_client_activate;
736 context->ClientSystemParam = rail_client_system_param;
737 context->ClientSystemCommand = rail_client_system_command;
738 context->ClientHandshake = rail_client_handshake;
739 context->ServerHandshake = rail_server_handshake;
740 context->ServerHandshakeEx = rail_server_handshake_ex;
741 context->ClientNotifyEvent = rail_client_notify_event;
742 context->ClientWindowMove = rail_client_window_move;
743 context->ClientInformation = rail_client_information;
744 context->ClientSystemMenu = rail_client_system_menu;
745 context->ClientLanguageBarInfo = rail_client_language_bar_info;
746 context->ClientLanguageIMEInfo = rail_client_language_ime_info;
747 context->ClientGetAppIdRequest = rail_client_get_appid_request;
748 context->ClientSnapArrange = rail_client_snap_arrange;
749 context->ClientCloak = rail_client_cloak;
750 context->ClientCompartmentInfo = rail_client_compartment_info;
751 context->ClientTextScale = rail_client_text_scale;
752 context->ClientCaretBlinkRate = rail_client_caret_blink_rate;
753 rail->rdpcontext = pEntryPointsEx->context;
754 rail->context = context;
755 isFreerdp = TRUE;
756 }
757
758 rail->log = WLog_Get("com.freerdp.channels.rail.client");
759 WLog_Print(rail->log, WLOG_DEBUG, "VirtualChannelEntryEx");
760 CopyMemory(&(rail->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
761 rail->InitHandle = pInitHandle;
762 rc = rail->channelEntryPoints.pVirtualChannelInitEx(
763 rail, context, pInitHandle, &rail->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
764 rail_virtual_channel_init_event_ex);
765
766 if (CHANNEL_RC_OK != rc)
767 {
768 WLog_ERR(TAG, "failed with %s [%08" PRIX32 "]", WTSErrorToString(rc), rc);
769 goto error_out;
770 }
771
772 rail->channelEntryPoints.pInterface = context;
773 return TRUE;
774error_out:
775
776 if (isFreerdp)
777 free(rail->context);
778
779 free(rail);
780 return FALSE;
781}
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