22#include <freerdp/config.h>
26#include <winpr/assert.h>
28#include <freerdp/log.h>
29#include <freerdp/channels/drdynvc.h>
34#define TAG FREERDP_TAG("core.client")
38 freerdp_channel_handle_fkt_t fkt;
45static WINPR_TLS freerdp* g_Instance =
nullptr;
48static volatile LONG g_OpenHandleSeq = 1;
51static INIT_ONCE g_ChannelHandlesOnce = INIT_ONCE_STATIC_INIT;
52static wHashTable* g_ChannelHandles =
nullptr;
54static BOOL freerdp_channels_process_message_free(wMessage* message, DWORD type);
56static CHANNEL_OPEN_DATA* freerdp_channels_find_channel_open_data_by_name(rdpChannels* channels,
59 WINPR_ASSERT(channels);
60 for (
int index = 0; index < channels->openDataCount; index++)
64 if (strncmp(name, pChannelOpenData->stats.channelName, CHANNEL_NAME_LEN + 1) == 0)
65 return pChannelOpenData;
71#if defined(WITH_RDPEUDP)
75struct rdp_channel_bridge
78 freerdp_channels_bridge_send_fkt_t send;
79 void (*fnFree)(
void* custom);
83static void channel_bridge_unref(
struct rdp_channel_bridge* bridge)
85 if (!bridge || (InterlockedDecrement(&bridge->refs) != 0))
87 IFCALL(bridge->fnFree, bridge->custom);
94 WINPR_ASSERT(pChannelOpenData);
95 WINPR_ASSERT(pChannelOpenData->channels);
97 EnterCriticalSection(&pChannelOpenData->channels->channelsLock);
98 struct rdp_channel_bridge* old = pChannelOpenData->bridge;
99 pChannelOpenData->bridge =
nullptr;
100 LeaveCriticalSection(&pChannelOpenData->channels->channelsLock);
101 channel_bridge_unref(old);
110 WINPR_ASSERT(pChannelOpenData);
111 WINPR_ASSERT(pChannelOpenData->channels);
115 if (!InterlockedCompareExchangePointer((PVOID
volatile*)&pChannelOpenData->bridge,
nullptr,
120 rdpChannels* channels = pChannelOpenData->channels;
121 EnterCriticalSection(&channels->channelsLock);
122 struct rdp_channel_bridge* bridge = pChannelOpenData->bridge;
124 (void)InterlockedIncrement(&bridge->refs);
125 LeaveCriticalSection(&channels->channelsLock);
129 pChannelOpenData->stats.bytesOut += item->DataLength;
130 pChannelOpenData->stats.packetsOut++;
131 pChannelOpenData->stats.fragmentsOut++;
132 *ret = bridge->send(bridge->custom, item->Data, item->DataLength);
133 channel_bridge_unref(bridge);
139static rdpMcsChannel* freerdp_channels_find_channel_by_name(rdpRdp* rdp,
const char* name)
144 rdpMcs* mcs = rdp->mcs;
147 for (UINT32 index = 0; index < mcs->channelCount; index++)
149 rdpMcsChannel* channel = &mcs->channels[index];
151 if (strncmp(name, channel->Name, CHANNEL_NAME_LEN + 1) == 0)
160static rdpMcsChannel* freerdp_channels_find_channel_by_id(rdpRdp* rdp, UINT16 channel_id)
165 rdpMcs* mcs = rdp->mcs;
168 for (UINT32 index = 0; index < mcs->channelCount; index++)
170 rdpMcsChannel* channel = &mcs->channels[index];
172 if (channel->ChannelId == channel_id)
181static void channel_queue_message_free(wMessage* msg)
183 if (!msg || (msg->id != 0))
190static void channel_queue_free(
void* obj)
192 wMessage* msg = (wMessage*)obj;
193 freerdp_channels_process_message_free(msg, CHANNEL_EVENT_WRITE_CANCELLED);
194 channel_queue_message_free(msg);
197static BOOL CALLBACK init_channel_handles_table(WINPR_ATTR_UNUSED
PINIT_ONCE once,
198 WINPR_ATTR_UNUSED PVOID param,
199 WINPR_ATTR_UNUSED PVOID* context)
201 g_ChannelHandles = HashTable_New(TRUE);
205static void* channel_event_entry_clone(
const void* data)
207 const ChannelEventEntry* entry = data;
211 ChannelEventEntry* copy = calloc(1,
sizeof(ChannelEventEntry));
218rdpChannels* freerdp_channels_new(freerdp* instance)
220 if (!InitOnceExecuteOnce(&g_ChannelHandlesOnce, init_channel_handles_table,
nullptr,
nullptr))
224 rdpChannels* channels =
nullptr;
225 channels = (rdpChannels*)calloc(1,
sizeof(rdpChannels));
230 if (!g_ChannelHandles)
232 if (!InitializeCriticalSectionAndSpinCount(&channels->channelsLock, 4000))
235 channels->instance = instance;
236 channels->queue = MessageQueue_New(
nullptr);
238 if (!channels->queue)
241 obj = MessageQueue_Object(channels->queue);
244 channels->channelEvents = HashTable_New(FALSE);
245 if (!channels->channelEvents)
248 obj = HashTable_ValueObject(channels->channelEvents);
254 WINPR_PRAGMA_DIAG_PUSH
255 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
256 freerdp_channels_free(channels);
257 WINPR_PRAGMA_DIAG_POP
261void freerdp_channels_free(rdpChannels* channels)
266 HashTable_Free(channels->channelEvents);
268 DeleteCriticalSection(&channels->channelsLock);
272 MessageQueue_Free(channels->queue);
273 channels->queue =
nullptr;
284static UINT freerdp_drdynvc_on_channel_connected(DrdynvcClientContext* context,
const char* name,
287 UINT status = CHANNEL_RC_OK;
289 WINPR_ASSERT(context);
291 rdpChannels* channels = (rdpChannels*)context->custom;
292 WINPR_ASSERT(channels);
294 freerdp* instance = channels->instance;
295 WINPR_ASSERT(instance);
296 WINPR_ASSERT(instance->context);
298 ChannelConnectedEventArgs e = WINPR_C_ARRAY_INIT;
299 EventArgsInit(&e,
"freerdp");
301 e.pInterface = pInterface;
302 if (PubSub_OnChannelConnected(instance->context->pubSub, instance->context, &e) < 0)
303 return ERROR_INTERNAL_ERROR;
312static UINT freerdp_drdynvc_on_channel_disconnected(DrdynvcClientContext* context,
const char* name,
315 UINT status = CHANNEL_RC_OK;
317 WINPR_ASSERT(context);
318 rdpChannels* channels = (rdpChannels*)context->custom;
319 WINPR_ASSERT(channels);
321 freerdp* instance = channels->instance;
322 WINPR_ASSERT(instance);
323 WINPR_ASSERT(instance->context);
325 ChannelDisconnectedEventArgs e = WINPR_C_ARRAY_INIT;
326 EventArgsInit(&e,
"freerdp");
328 e.pInterface = pInterface;
329 if (PubSub_OnChannelDisconnected(instance->context->pubSub, instance->context, &e) < 0)
330 return ERROR_INTERNAL_ERROR;
334static UINT freerdp_drdynvc_on_channel_attached(DrdynvcClientContext* context,
const char* name,
337 UINT status = CHANNEL_RC_OK;
339 WINPR_ASSERT(context);
340 rdpChannels* channels = (rdpChannels*)context->custom;
341 WINPR_ASSERT(channels);
343 freerdp* instance = channels->instance;
344 WINPR_ASSERT(instance);
345 WINPR_ASSERT(instance->context);
347 ChannelAttachedEventArgs e = WINPR_C_ARRAY_INIT;
348 EventArgsInit(&e,
"freerdp");
350 e.pInterface = pInterface;
351 if (PubSub_OnChannelAttached(instance->context->pubSub, instance->context, &e) < 0)
352 return ERROR_INTERNAL_ERROR;
356static UINT freerdp_drdynvc_on_channel_detached(DrdynvcClientContext* context,
const char* name,
359 UINT status = CHANNEL_RC_OK;
361 WINPR_ASSERT(context);
362 rdpChannels* channels = (rdpChannels*)context->custom;
363 WINPR_ASSERT(channels);
365 freerdp* instance = channels->instance;
366 WINPR_ASSERT(instance);
367 WINPR_ASSERT(instance->context);
369 ChannelDetachedEventArgs e = WINPR_C_ARRAY_INIT;
370 EventArgsInit(&e,
"freerdp");
372 e.pInterface = pInterface;
373 if (PubSub_OnChannelDetached(instance->context->pubSub, instance->context, &e) < 0)
374 return ERROR_INTERNAL_ERROR;
378void freerdp_channels_register_instance(WINPR_ATTR_UNUSED rdpChannels* channels, freerdp* instance)
380 WINPR_ASSERT(channels);
381 WINPR_ASSERT(instance);
383 g_Instance = instance;
390UINT freerdp_channels_pre_connect(rdpChannels* channels, freerdp* instance)
392 UINT error = CHANNEL_RC_OK;
394 WINPR_ASSERT(channels);
395 WINPR_ASSERT(instance);
397 MessageQueue_Clear(channels->queue);
399 for (
int index = 0; index < channels->clientDataCount; index++)
403 if (pChannelClientData->pChannelInitEventProc)
405 pChannelClientData->pChannelInitEventProc(pChannelClientData->pInitHandle,
406 CHANNEL_EVENT_INITIALIZED,
nullptr, 0);
408 else if (pChannelClientData->pChannelInitEventProcEx)
410 pChannelClientData->pChannelInitEventProcEx(pChannelClientData->lpUserParam,
411 pChannelClientData->pInitHandle,
412 CHANNEL_EVENT_INITIALIZED,
nullptr, 0);
415 error = getChannelError(instance->context);
416 if (CHANNEL_RC_OK != error)
421 ChannelInitializedEventArgs e = WINPR_C_ARRAY_INIT;
422 EventArgsInit(&e,
"freerdp");
423 e.name = pChannelOpenData->stats.channelName;
424 e.pInterface = pChannelInitData->pInterface;
425 if (PubSub_OnChannelInitialized(instance->context->pubSub, instance->context, &e) < 0)
427 error = ERROR_INTERNAL_ERROR;
435UINT freerdp_channels_attach(freerdp* instance)
437 UINT error = CHANNEL_RC_OK;
439 WINPR_ASSERT(instance);
440 WINPR_ASSERT(instance->context);
441 WINPR_ASSERT(instance->context->settings);
443 rdpChannels* channels = instance->context->channels;
444 const char* hostname =
446 WINPR_ASSERT(hostname);
447 const size_t hostnameLength = strnlen(hostname, MAX_PATH);
449 for (
int index = 0; index < channels->clientDataCount; index++)
456 ChannelAttachedEventArgs e = WINPR_C_ARRAY_INIT;
461 if (pChannelClientData->pChannelInitEventProc)
464 pChannelClientData->pChannelInitEventProc(pChannelClientData->pInitHandle,
465 CHANNEL_EVENT_ATTACHED, cnv.pv,
466 (UINT)hostnameLength);
468 else if (pChannelClientData->pChannelInitEventProcEx)
470 pChannelClientData->pChannelInitEventProcEx(
471 pChannelClientData->lpUserParam, pChannelClientData->pInitHandle,
472 CHANNEL_EVENT_ATTACHED, cnv.pv, (UINT)hostnameLength);
475 error = getChannelError(instance->context);
476 if (error != CHANNEL_RC_OK)
480 EventArgsInit(&e,
"freerdp");
481 e.name = pChannelOpenData->stats.channelName;
482 e.pInterface = pChannelOpenData->pInterface;
483 if (PubSub_OnChannelAttached(instance->context->pubSub, instance->context, &e) < 0)
485 error = ERROR_INTERNAL_ERROR;
494UINT freerdp_channels_detach(freerdp* instance)
496 UINT error = CHANNEL_RC_OK;
498 WINPR_ASSERT(instance);
500 rdpContext* context = instance->context;
501 WINPR_ASSERT(context);
503 rdpChannels* channels = context->channels;
504 WINPR_ASSERT(channels);
506 WINPR_ASSERT(context->settings);
508 WINPR_ASSERT(hostname);
509 const size_t hostnameLength = strnlen(hostname, MAX_PATH);
511 for (
int index = 0; index < channels->clientDataCount; index++)
519 ChannelDetachedEventArgs e = WINPR_C_ARRAY_INIT;
524 if (pChannelClientData->pChannelInitEventProc)
526 pChannelClientData->pChannelInitEventProc(pChannelClientData->pInitHandle,
527 CHANNEL_EVENT_DETACHED, cnv.pv,
528 (UINT)hostnameLength);
530 else if (pChannelClientData->pChannelInitEventProcEx)
532 pChannelClientData->pChannelInitEventProcEx(
533 pChannelClientData->lpUserParam, pChannelClientData->pInitHandle,
534 CHANNEL_EVENT_DETACHED, cnv.pv, (UINT)hostnameLength);
537 error = getChannelError(context);
538 if (error != CHANNEL_RC_OK)
542 EventArgsInit(&e,
"freerdp");
543 e.name = pChannelOpenData->stats.channelName;
544 e.pInterface = pChannelOpenData->pInterface;
545 if (PubSub_OnChannelDetached(context->pubSub, context, &e) < 0)
547 error = ERROR_INTERNAL_ERROR;
561UINT freerdp_channels_post_connect(rdpChannels* channels, freerdp* instance)
563 UINT error = CHANNEL_RC_OK;
565 WINPR_ASSERT(channels);
566 WINPR_ASSERT(instance);
567 WINPR_ASSERT(instance->context);
568 WINPR_ASSERT(instance->context->settings);
570 channels->connected = TRUE;
571 const char* hostname =
573 WINPR_ASSERT(hostname);
574 const size_t hostnameLength = strnlen(hostname, MAX_PATH);
576 for (
int index = 0; index < channels->clientDataCount; index++)
588 if (pChannelClientData->pChannelInitEventProc)
590 pChannelClientData->pChannelInitEventProc(pChannelClientData->pInitHandle,
591 CHANNEL_EVENT_CONNECTED, cnv.pb,
592 (UINT)hostnameLength);
594 else if (pChannelClientData->pChannelInitEventProcEx)
596 pChannelClientData->pChannelInitEventProcEx(
597 pChannelClientData->lpUserParam, pChannelClientData->pInitHandle,
598 CHANNEL_EVENT_CONNECTED, cnv.pb, (UINT)hostnameLength);
601 error = getChannelError(instance->context);
602 if (error != CHANNEL_RC_OK)
605 ChannelConnectedEventArgs e = WINPR_C_ARRAY_INIT;
606 EventArgsInit(&e,
"freerdp");
607 e.name = pChannelOpenData->stats.channelName;
608 e.pInterface = pChannelOpenData->pInterface;
609 if (PubSub_OnChannelConnected(instance->context->pubSub, instance->context, &e) < 0)
611 error = ERROR_INTERNAL_ERROR;
616 channels->drdynvc = (DrdynvcClientContext*)freerdp_channels_get_static_channel_interface(
617 channels, DRDYNVC_SVC_CHANNEL_NAME);
619 if (channels->drdynvc)
621 channels->drdynvc->custom = (
void*)channels;
622 channels->drdynvc->OnChannelConnected = freerdp_drdynvc_on_channel_connected;
623 channels->drdynvc->OnChannelDisconnected = freerdp_drdynvc_on_channel_disconnected;
624 channels->drdynvc->OnChannelAttached = freerdp_drdynvc_on_channel_attached;
625 channels->drdynvc->OnChannelDetached = freerdp_drdynvc_on_channel_detached;
632BOOL freerdp_channels_data(freerdp* instance, UINT16 channelId,
const BYTE* cdata,
size_t dataSize,
633 UINT32 flags,
size_t totalSize)
642 if (!instance || !data.pcb)
644 WLog_ERR(TAG,
"(%p, %" PRIu16
", %p, 0x%08x): Invalid arguments",
645 WINPR_CXX_COMPAT_CAST(
const void*, instance), channelId,
646 WINPR_CXX_COMPAT_CAST(
const void*, data.pcb), flags);
650 WINPR_ASSERT(instance->context);
651 WINPR_ASSERT(instance->context->rdp);
653 rdpMcs* mcs = instance->context->rdp->mcs;
656 rdpChannels* channels = instance->context->channels;
657 WINPR_ASSERT(channels);
659 if (!channels || !mcs)
664 rdpMcsChannel* channel =
nullptr;
665 for (UINT32 index = 0; index < mcs->channelCount; index++)
667 rdpMcsChannel* cur = &mcs->channels[index];
669 if (cur->ChannelId == channelId)
682 freerdp_channels_find_channel_open_data_by_name(channels, channel->Name);
684 if (!pChannelOpenData)
689 pChannelOpenData->stats.bytesIn += dataSize;
690 pChannelOpenData->stats.fragmentsIn++;
691 if ((flags & CHANNEL_FLAG_LAST) != 0)
692 pChannelOpenData->stats.packetsIn++;
694 if (pChannelOpenData->pChannelOpenEventProc)
696 pChannelOpenData->pChannelOpenEventProc(pChannelOpenData->stats.channelId,
697 CHANNEL_EVENT_DATA_RECEIVED, data.pb,
698 (UINT32)dataSize, (UINT32)totalSize, flags);
700 else if (pChannelOpenData->pChannelOpenEventProcEx)
702 pChannelOpenData->pChannelOpenEventProcEx(
703 pChannelOpenData->lpUserParam, pChannelOpenData->stats.channelId,
704 CHANNEL_EVENT_DATA_RECEIVED, data.pb, (UINT32)dataSize, (UINT32)totalSize, flags);
710#if !defined(WITH_RDPEUDP)
712BOOL freerdp_channels_set_static_bridge(WINPR_ATTR_UNUSED freerdp* instance,
713 WINPR_ATTR_UNUSED
const char* name,
714 WINPR_ATTR_UNUSED freerdp_channels_bridge_send_fkt_t send,
715 WINPR_ATTR_UNUSED
void (*fnFree)(
void* custom),
716 WINPR_ATTR_UNUSED
void* custom)
721BOOL freerdp_channels_set_static_bridge(freerdp* instance,
const char* name,
722 freerdp_channels_bridge_send_fkt_t send,
723 void (*fnFree)(
void* custom),
void* custom)
725 if (!instance || !name)
728 WINPR_ASSERT(instance->context);
729 rdpChannels* channels = instance->context->channels;
733 struct rdp_channel_bridge* bridge =
nullptr;
736 bridge = calloc(1,
sizeof(
struct rdp_channel_bridge));
741 bridge->fnFree = fnFree;
742 bridge->custom = custom;
746 struct rdp_channel_bridge* old =
nullptr;
748 EnterCriticalSection(&channels->channelsLock);
750 freerdp_channels_find_channel_open_data_by_name(channels, name);
751 if (pChannelOpenData)
754 old = pChannelOpenData->bridge;
755 if (!send && custom && old && (old->custom != custom))
758 pChannelOpenData->bridge = bridge;
760 LeaveCriticalSection(&channels->channelsLock);
767 channel_bridge_unref(old);
772UINT16 freerdp_channels_get_id_by_name(freerdp* instance,
const char* channel_name)
774 if (!instance || !channel_name)
777 WINPR_ASSERT(instance->context);
778 WINPR_ASSERT(instance->context->rdp);
779 rdpMcsChannel* mcsChannel =
780 freerdp_channels_find_channel_by_name(instance->context->rdp, channel_name);
784 return mcsChannel->ChannelId;
787const char* freerdp_channels_get_name_by_id(freerdp* instance, UINT16 channelId)
792 WINPR_ASSERT(instance->context);
793 WINPR_ASSERT(instance->context->rdp);
794 rdpMcsChannel* mcsChannel =
795 freerdp_channels_find_channel_by_id(instance->context->rdp, channelId);
799 return mcsChannel->Name;
802BOOL freerdp_channels_process_message_free(wMessage* message, DWORD type)
804 WINPR_ASSERT(message);
806 if (message->id == WMQ_QUIT)
811 if (message->id == 0)
820 if (pChannelOpenData->pChannelOpenEventProc)
822 pChannelOpenData->pChannelOpenEventProc(pChannelOpenData->stats.channelId, type,
823 item->UserData, item->DataLength,
824 item->DataLength, 0);
826 else if (pChannelOpenData->pChannelOpenEventProcEx)
828 pChannelOpenData->pChannelOpenEventProcEx(
829 pChannelOpenData->lpUserParam, pChannelOpenData->stats.channelId, type,
830 item->UserData, item->DataLength, item->DataLength, 0);
837static BOOL freerdp_channels_process_message(freerdp* instance, wMessage* message)
842 WINPR_ASSERT(instance);
843 WINPR_ASSERT(instance->context);
844 WINPR_ASSERT(message);
846 if (message->id == WMQ_QUIT)
848 else if (message->id == 0)
857 if (pChannelOpenData->flags != 2)
859 freerdp_channels_process_message_free(message, CHANNEL_EVENT_WRITE_CANCELLED);
862 rdpMcsChannel* channel = freerdp_channels_find_channel_by_name(
863 instance->context->rdp, pChannelOpenData->stats.channelName);
865#if defined(WITH_RDPEUDP)
867 if (channel_bridge_send(pChannelOpenData, item, &ret))
873 pChannelOpenData->stats.bytesOut += item->DataLength;
874 pChannelOpenData->stats.packetsOut++;
876 const UINT32 VCChunkSize =
878 if (VCChunkSize == 0)
880 const uint64_t frags = (1ull * item->DataLength + VCChunkSize - 1ull) / VCChunkSize;
881 pChannelOpenData->stats.fragmentsOut += frags;
882 ret = instance->SendChannelData(instance, channel->ChannelId, item->Data,
887 if (!freerdp_channels_process_message_free(message, CHANNEL_EVENT_WRITE_COMPLETE))
893 IFCALL(message->Free, message);
900static BOOL freerdp_channels_process_sync(rdpChannels* channels, freerdp* instance)
903 wMessage message = WINPR_C_ARRAY_INIT;
905 WINPR_ASSERT(channels);
907 while (MessageQueue_Peek(channels->queue, &message, TRUE))
909 if (!freerdp_channels_process_message(instance, &message))
919#if defined(WITH_FREERDP_DEPRECATED)
920BOOL freerdp_channels_get_fds(rdpChannels* channels, WINPR_ATTR_UNUSED freerdp* instance,
921 void** read_fds,
int* read_count, WINPR_ATTR_UNUSED
void** write_fds,
922 WINPR_ATTR_UNUSED
int* write_count)
925 pfd = GetEventWaitObject(MessageQueue_Event(channels->queue));
929 read_fds[*read_count] = pfd;
937void* freerdp_channels_get_static_channel_interface(rdpChannels* channels,
const char* name)
939 void* pInterface =
nullptr;
941 freerdp_channels_find_channel_open_data_by_name(channels, name);
943 if (pChannelOpenData)
944 pInterface = pChannelOpenData->pInterface;
949HANDLE freerdp_channels_get_event_handle(freerdp* instance)
952 return INVALID_HANDLE_VALUE;
954 WINPR_ASSERT(instance->context);
956 rdpChannels* channels = instance->context->channels;
957 WINPR_ASSERT(channels);
959 return MessageQueue_Event(channels->queue);
962static BOOL channels_process(
const void* key,
void* value,
void* arg)
964 ChannelEventEntry* entry = value;
965 rdpContext* context = arg;
972 return entry->fkt(context, entry->userdata);
975int freerdp_channels_process_pending_messages(freerdp* instance)
980 WINPR_ASSERT(instance->context);
982 rdpChannels* channels = instance->context->channels;
983 WINPR_ASSERT(channels);
985 const DWORD status = WaitForSingleObject(MessageQueue_Event(channels->queue), 0);
986 if (status == WAIT_OBJECT_0)
988 if (!freerdp_channels_process_sync(channels, instance))
992 if (!HashTable_Foreach(channels->channelEvents, channels_process, instance->context))
1001BOOL freerdp_channels_check_fds(rdpChannels* channels, freerdp* instance)
1003 WINPR_ASSERT(channels);
1004 WINPR_UNUSED(channels);
1006 const int rc = freerdp_channels_process_pending_messages(instance);
1010BOOL freerdp_client_channel_register(rdpChannels* channels, HANDLE handle,
1011 freerdp_channel_handle_fkt_t fkt,
void* userdata)
1013 if (!channels || (handle == INVALID_HANDLE_VALUE) || !fkt)
1015 WLog_ERR(TAG,
"Invalid function arguments (channels=%p, handle=%p, fkt=%p, userdata=%p",
1016 WINPR_FUNC_PTR_CAST(channels,
const void*), handle,
1017 WINPR_FUNC_PTR_CAST(fkt,
const void*), userdata);
1021 ChannelEventEntry entry = { .fkt = fkt, .userdata = userdata };
1022 return HashTable_Insert(channels->channelEvents, handle, &entry);
1025BOOL freerdp_client_channel_unregister(rdpChannels* channels, HANDLE handle)
1027 if (!channels || (handle == INVALID_HANDLE_VALUE))
1029 WLog_ERR(TAG,
"Invalid function arguments (channels=%p, handle=%p",
1030 WINPR_CXX_COMPAT_CAST(
const void*, channels), handle);
1034 return HashTable_Remove(channels->channelEvents, handle);
1037SSIZE_T freerdp_client_channel_get_registered_event_handles(rdpChannels* channels, HANDLE* events,
1042 WINPR_ASSERT(channels);
1043 WINPR_ASSERT(events || (count == 0));
1045 HashTable_Lock(channels->channelEvents);
1046 size_t len = HashTable_Count(channels->channelEvents);
1049 ULONG_PTR* keys =
nullptr;
1050 const size_t nrKeys = HashTable_GetKeys(channels->channelEvents, &keys);
1051 if ((nrKeys <= SSIZE_MAX) && (nrKeys == len))
1053 for (
size_t x = 0; x < nrKeys; x++)
1055 HANDLE cur = (HANDLE)keys[x];
1058 rc = (SSIZE_T)nrKeys;
1062 HashTable_Unlock(channels->channelEvents);
1066UINT freerdp_channels_disconnect(rdpChannels* channels, freerdp* instance)
1068 UINT error = CHANNEL_RC_OK;
1070 WINPR_ASSERT(channels);
1072 if (!channels->connected)
1075 freerdp_channels_check_fds(channels, instance);
1078 for (
int index = 0; index < channels->clientDataCount; index++)
1080 ChannelDisconnectedEventArgs e = WINPR_C_ARRAY_INIT;
1083 if (pChannelClientData->pChannelInitEventProc)
1085 pChannelClientData->pChannelInitEventProc(pChannelClientData->pInitHandle,
1086 CHANNEL_EVENT_DISCONNECTED,
nullptr, 0);
1088 else if (pChannelClientData->pChannelInitEventProcEx)
1090 pChannelClientData->pChannelInitEventProcEx(pChannelClientData->lpUserParam,
1091 pChannelClientData->pInitHandle,
1092 CHANNEL_EVENT_DISCONNECTED,
nullptr, 0);
1096 EventArgsInit(&e,
"freerdp");
1097 e.name = pChannelOpenData->stats.channelName;
1098 e.pInterface = pChannelOpenData->pInterface;
1099 if (PubSub_OnChannelDisconnected(instance->context->pubSub, instance->context, &e) < 0)
1100 error = ERROR_INTERNAL_ERROR;
1103 channels->connected = FALSE;
1106 freerdp_channels_check_fds(channels, instance);
1110void freerdp_channels_close(rdpChannels* channels, freerdp* instance)
1112 WINPR_ASSERT(channels);
1113 WINPR_ASSERT(instance);
1115 MessageQueue_PostQuit(channels->queue, 0);
1116 freerdp_channels_check_fds(channels, instance);
1119 for (
int index = 0; index < channels->clientDataCount; index++)
1123 if (pChannelClientData->pChannelInitEventProc)
1125 pChannelClientData->pChannelInitEventProc(pChannelClientData->pInitHandle,
1126 CHANNEL_EVENT_TERMINATED,
nullptr, 0);
1128 else if (pChannelClientData->pChannelInitEventProcEx)
1130 pChannelClientData->pChannelInitEventProcEx(pChannelClientData->lpUserParam,
1131 pChannelClientData->pInitHandle,
1132 CHANNEL_EVENT_TERMINATED,
nullptr, 0);
1137 ChannelTerminatedEventArgs e = WINPR_C_ARRAY_INIT;
1138 EventArgsInit(&e,
"freerdp");
1139 e.name = pChannelOpenData->stats.channelName;
1140 e.pInterface = pChannelInitData->pInterface;
1141 if (PubSub_OnChannelTerminated(instance->context->pubSub, instance->context, &e) < 0)
1143 WLog_WARN(TAG,
"PubSub_OnChannelTerminated(%s) failed",
1144 pChannelOpenData->stats.channelName);
1148 for (
int index = 0; index < channels->openDataCount; index++)
1151 HashTable_Remove(g_ChannelHandles, (
void*)(UINT_PTR)pChannelOpenData->stats.channelId);
1152#if defined(WITH_RDPEUDP)
1153 channel_clear_bridge(pChannelOpenData);
1157 channels->openDataCount = 0;
1158 channels->initDataCount = 0;
1159 channels->clientDataCount = 0;
1161 WINPR_ASSERT(instance->context);
1162 WINPR_ASSERT(instance->context->settings);
1163 instance->context->settings->ChannelCount = 0;
1164 g_Instance =
nullptr;
1167static UINT VCAPITYPE FreeRDP_VirtualChannelInitEx(
1168 LPVOID lpUserParam, LPVOID clientContext, LPVOID pInitHandle,
PCHANNEL_DEF pChannel,
1169 INT channelCount, ULONG versionRequested, PCHANNEL_INIT_EVENT_EX_FN pChannelInitEventProcEx)
1172 return CHANNEL_RC_BAD_INIT_HANDLE;
1175 return CHANNEL_RC_BAD_CHANNEL;
1177 if ((channelCount <= 0) || !pChannelInitEventProcEx)
1178 return CHANNEL_RC_INITIALIZATION_ERROR;
1181 WINPR_ASSERT(pChannelInitData);
1183 rdpChannels* channels = pChannelInitData->channels;
1184 WINPR_ASSERT(channels);
1186 if (!channels->can_call_init)
1187 return CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY;
1189 if ((channels->openDataCount + channelCount) > CHANNEL_MAX_COUNT)
1190 return CHANNEL_RC_TOO_MANY_CHANNELS;
1192 if (channels->connected)
1193 return CHANNEL_RC_ALREADY_CONNECTED;
1195 if (versionRequested != VIRTUAL_CHANNEL_VERSION_WIN2000)
1199 for (
int index = 0; index < channelCount; index++)
1203 if (freerdp_channels_find_channel_open_data_by_name(channels, pChannelDef->name) !=
nullptr)
1205 return CHANNEL_RC_BAD_CHANNEL;
1209 pChannelInitData->pInterface = clientContext;
1211 CHANNEL_CLIENT_DATA* pChannelClientData = &channels->clientDataList[channels->clientDataCount];
1212 pChannelClientData->pChannelInitEventProcEx = pChannelInitEventProcEx;
1213 pChannelClientData->pInitHandle = pInitHandle;
1214 pChannelClientData->lpUserParam = lpUserParam;
1215 channels->clientDataCount++;
1217 WINPR_ASSERT(channels->instance);
1218 WINPR_ASSERT(channels->instance->context);
1220 rdpSettings* settings = channels->instance->context->settings;
1221 WINPR_ASSERT(settings);
1223 for (
int index = 0; index < channelCount; index++)
1226 CHANNEL_OPEN_DATA* pChannelOpenData = &channels->openDataList[channels->openDataCount];
1228 WINPR_ASSERT(pChannelOpenData);
1230 const LONG hdl = InterlockedIncrement(&g_OpenHandleSeq);
1231 pChannelOpenData->stats.channelId = WINPR_ASSERTING_INT_CAST(uint32_t, hdl);
1232 pChannelOpenData->channels = channels;
1233 pChannelOpenData->lpUserParam = lpUserParam;
1234 if (!HashTable_Insert(g_ChannelHandles, (
void*)(UINT_PTR)pChannelOpenData->stats.channelId,
1235 (
void*)pChannelOpenData))
1237 pChannelInitData->pInterface =
nullptr;
1238 return CHANNEL_RC_INITIALIZATION_ERROR;
1240 pChannelOpenData->flags = 1;
1241 strncpy(pChannelOpenData->stats.channelName, pChannelDef->name, CHANNEL_NAME_LEN);
1242 pChannelOpenData->options = pChannelDef->options;
1245 WINPR_ASSERT(max >= CHANNEL_MAX_COUNT);
1246 if (settings->ChannelCount < max)
1248 CHANNEL_DEF* channel = freerdp_settings_get_pointer_array_writable(
1249 settings, FreeRDP_ChannelDefArray, settings->ChannelCount);
1252 strncpy(channel->name, pChannelDef->name, CHANNEL_NAME_LEN);
1253 channel->options = pChannelDef->options;
1254 settings->ChannelCount++;
1257 channels->openDataCount++;
1260 return CHANNEL_RC_OK;
1263static UINT VCAPITYPE FreeRDP_VirtualChannelInit(LPVOID* ppInitHandle,
PCHANNEL_DEF pChannel,
1264 INT channelCount, ULONG versionRequested,
1265 PCHANNEL_INIT_EVENT_FN pChannelInitEventProc)
1269 if (!g_Instance || !g_Instance->context)
1270 return CHANNEL_RC_NOT_INITIALIZED;
1272 rdpChannels* channels = g_Instance->context->channels;
1274 if (!ppInitHandle || !channels)
1275 return CHANNEL_RC_BAD_INIT_HANDLE;
1278 return CHANNEL_RC_BAD_CHANNEL;
1280 if ((channelCount <= 0) || !pChannelInitEventProc)
1281 return CHANNEL_RC_INITIALIZATION_ERROR;
1283 CHANNEL_INIT_DATA* pChannelInitData = &(channels->initDataList[channels->initDataCount]);
1284 *ppInitHandle = pChannelInitData;
1285 channels->initDataCount++;
1286 pChannelInitData->channels = channels;
1287 pChannelInitData->pInterface =
nullptr;
1289 if (!channels->can_call_init)
1290 return CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY;
1292 if (channels->openDataCount + channelCount > CHANNEL_MAX_COUNT)
1293 return CHANNEL_RC_TOO_MANY_CHANNELS;
1295 if (channels->connected)
1296 return CHANNEL_RC_ALREADY_CONNECTED;
1298 if (versionRequested != VIRTUAL_CHANNEL_VERSION_WIN2000)
1303 for (
int index = 0; index < channelCount; index++)
1305 pChannelDef = &pChannel[index];
1307 if (freerdp_channels_find_channel_open_data_by_name(channels, pChannelDef->name) !=
nullptr)
1309 return CHANNEL_RC_BAD_CHANNEL;
1313 CHANNEL_CLIENT_DATA* pChannelClientData = &channels->clientDataList[channels->clientDataCount];
1314 pChannelClientData->pChannelInitEventProc = pChannelInitEventProc;
1315 pChannelClientData->pInitHandle = *ppInitHandle;
1316 channels->clientDataCount++;
1318 rdpSettings* settings = channels->instance->context->settings;
1320 for (
int index = 0; index < channelCount; index++)
1324 CHANNEL_OPEN_DATA* pChannelOpenData = &channels->openDataList[channels->openDataCount];
1326 const LONG hdl = InterlockedIncrement(&g_OpenHandleSeq);
1327 pChannelOpenData->stats.channelId = WINPR_ASSERTING_INT_CAST(uint32_t, hdl);
1328 pChannelOpenData->channels = channels;
1329 if (!HashTable_Insert(g_ChannelHandles, (
void*)(UINT_PTR)pChannelOpenData->stats.channelId,
1330 (
void*)pChannelOpenData))
1331 return CHANNEL_RC_INITIALIZATION_ERROR;
1332 pChannelOpenData->flags = 1;
1333 strncpy(pChannelOpenData->stats.channelName, pChannelDef->name, CHANNEL_NAME_LEN);
1334 pChannelOpenData->options = pChannelDef->options;
1336 if (ChannelCount < CHANNEL_MAX_COUNT)
1338 CHANNEL_DEF* channel = freerdp_settings_get_pointer_array_writable(
1339 settings, FreeRDP_ChannelDefArray, ChannelCount++);
1340 strncpy(channel->name, pChannelDef->name, CHANNEL_NAME_LEN);
1341 channel->options = pChannelDef->options;
1343 return ERROR_INTERNAL_ERROR;
1346 channels->openDataCount++;
1349 return CHANNEL_RC_OK;
1352static UINT VCAPITYPE
1353FreeRDP_VirtualChannelOpenEx(LPVOID pInitHandle, LPDWORD pOpenHandle, PCHAR pChannelName,
1354 PCHANNEL_OPEN_EVENT_EX_FN pChannelOpenEventProcEx)
1357 WINPR_ASSERT(pChannelInitData);
1359 rdpChannels* channels = pChannelInitData->channels;
1360 WINPR_ASSERT(channels);
1362 void* pInterface = pChannelInitData->pInterface;
1365 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1367 if (!pChannelOpenEventProcEx)
1368 return CHANNEL_RC_BAD_PROC;
1370 if (!channels->connected)
1371 return CHANNEL_RC_NOT_CONNECTED;
1374 freerdp_channels_find_channel_open_data_by_name(channels, pChannelName);
1376 if (!pChannelOpenData)
1377 return CHANNEL_RC_UNKNOWN_CHANNEL_NAME;
1379 if (pChannelOpenData->flags == 2)
1380 return CHANNEL_RC_ALREADY_OPEN;
1382 pChannelOpenData->flags = 2;
1383 pChannelOpenData->pInterface = pInterface;
1384 pChannelOpenData->pChannelOpenEventProcEx = pChannelOpenEventProcEx;
1385 *pOpenHandle = pChannelOpenData->stats.channelId;
1386 return CHANNEL_RC_OK;
1389static UINT VCAPITYPE FreeRDP_VirtualChannelOpen(LPVOID pInitHandle, LPDWORD pOpenHandle,
1391 PCHANNEL_OPEN_EVENT_FN pChannelOpenEventProc)
1394 WINPR_ASSERT(pChannelInitData);
1396 rdpChannels* channels = pChannelInitData->channels;
1397 void* pInterface = pChannelInitData->pInterface;
1400 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1402 if (!pChannelOpenEventProc)
1403 return CHANNEL_RC_BAD_PROC;
1405 if (!channels->connected)
1406 return CHANNEL_RC_NOT_CONNECTED;
1409 freerdp_channels_find_channel_open_data_by_name(channels, pChannelName);
1411 if (!pChannelOpenData)
1412 return CHANNEL_RC_UNKNOWN_CHANNEL_NAME;
1414 if (pChannelOpenData->flags == 2)
1415 return CHANNEL_RC_ALREADY_OPEN;
1417 pChannelOpenData->flags = 2;
1418 pChannelOpenData->pInterface = pInterface;
1419 pChannelOpenData->pChannelOpenEventProc = pChannelOpenEventProc;
1420 *pOpenHandle = pChannelOpenData->stats.channelId;
1421 return CHANNEL_RC_OK;
1424static UINT VCAPITYPE FreeRDP_VirtualChannelCloseEx(LPVOID pInitHandle, DWORD openHandle)
1427 return CHANNEL_RC_BAD_INIT_HANDLE;
1430 HashTable_GetItemValue(g_ChannelHandles, (
void*)(UINT_PTR)openHandle);
1432 if (!pChannelOpenData)
1433 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1435 if (pChannelOpenData->flags != 2)
1436 return CHANNEL_RC_NOT_OPEN;
1438 pChannelOpenData->flags = 0;
1439 return CHANNEL_RC_OK;
1442static UINT VCAPITYPE FreeRDP_VirtualChannelClose(DWORD openHandle)
1445 HashTable_GetItemValue(g_ChannelHandles, (
void*)(UINT_PTR)openHandle);
1447 if (!pChannelOpenData)
1448 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1450 if (pChannelOpenData->flags != 2)
1451 return CHANNEL_RC_NOT_OPEN;
1453 pChannelOpenData->flags = 0;
1454 return CHANNEL_RC_OK;
1457static UINT VCAPITYPE FreeRDP_VirtualChannelWriteEx(LPVOID pInitHandle, DWORD openHandle,
1458 LPVOID pData, ULONG dataLength,
1462 return CHANNEL_RC_BAD_INIT_HANDLE;
1465 rdpChannels* channels = pChannelInitData->channels;
1468 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1471 HashTable_GetItemValue(g_ChannelHandles, (
void*)(UINT_PTR)openHandle);
1473 if (!pChannelOpenData)
1474 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1476 if (!channels->connected)
1477 return CHANNEL_RC_NOT_CONNECTED;
1480 return CHANNEL_RC_NULL_DATA;
1483 return CHANNEL_RC_ZERO_LENGTH;
1485 if (pChannelOpenData->flags != 2)
1486 return CHANNEL_RC_NOT_OPEN;
1490 if (!pChannelOpenEvent)
1491 return CHANNEL_RC_NO_MEMORY;
1493 pChannelOpenEvent->Data = pData;
1494 pChannelOpenEvent->DataLength = dataLength;
1495 pChannelOpenEvent->UserData = pUserData;
1496 pChannelOpenEvent->pChannelOpenData = pChannelOpenData;
1498 const wMessage message = { .context = channels,
1500 .wParam = pChannelOpenEvent,
1502 .Free = channel_queue_message_free };
1504 if (!MessageQueue_Dispatch(channels->queue, &message))
1506 free(pChannelOpenEvent);
1507 return CHANNEL_RC_NO_MEMORY;
1510 return CHANNEL_RC_OK;
1513static UINT VCAPITYPE FreeRDP_VirtualChannelWrite(DWORD openHandle, LPVOID pData, ULONG dataLength,
1517 HashTable_GetItemValue(g_ChannelHandles, (
void*)(UINT_PTR)openHandle);
1519 if (!pChannelOpenData)
1520 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1522 rdpChannels* channels = pChannelOpenData->channels;
1524 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1526 if (!channels->connected)
1527 return CHANNEL_RC_NOT_CONNECTED;
1530 return CHANNEL_RC_NULL_DATA;
1533 return CHANNEL_RC_ZERO_LENGTH;
1535 if (pChannelOpenData->flags != 2)
1536 return CHANNEL_RC_NOT_OPEN;
1540 if (!pChannelOpenEvent)
1541 return CHANNEL_RC_NO_MEMORY;
1543 pChannelOpenEvent->Data = pData;
1544 pChannelOpenEvent->DataLength = dataLength;
1545 pChannelOpenEvent->UserData = pUserData;
1546 pChannelOpenEvent->pChannelOpenData = pChannelOpenData;
1548 const wMessage message = { .context = channels,
1550 .wParam = pChannelOpenEvent,
1552 .Free = channel_queue_message_free };
1554 if (!MessageQueue_Dispatch(channels->queue, &message))
1556 free(pChannelOpenEvent);
1557 return CHANNEL_RC_NO_MEMORY;
1560 return CHANNEL_RC_OK;
1563static BOOL freerdp_channels_is_loaded(rdpChannels* channels, PVIRTUALCHANNELENTRY entry)
1565 WINPR_ASSERT(channels);
1566 for (
int i = 0; i < channels->clientDataCount; i++)
1570 if (pChannelClientData->entry == entry)
1577static BOOL freerdp_channels_is_loaded_ex(rdpChannels* channels, PVIRTUALCHANNELENTRYEX entryEx)
1579 WINPR_ASSERT(channels);
1580 for (
int i = 0; i < channels->clientDataCount; i++)
1584 if (pChannelClientData->entryEx == entryEx)
1600 WINPR_ASSERT(channels);
1602 if (channels->openDataCount <= 0)
1605 const size_t odc = WINPR_ASSERTING_INT_CAST(
size_t, channels->openDataCount);
1610 for (
int i = 0; i < channels->openDataCount; i++)
1615 *stat = pChannelClientData->stats;
1624 statsw->count = odc;
1625 statsw->stats = stats;
1629int freerdp_channels_client_load(rdpChannels* channels, WINPR_ATTR_UNUSED rdpSettings* settings,
1630 PVIRTUALCHANNELENTRY entry,
void* data)
1632 WINPR_ASSERT(channels);
1633 WINPR_ASSERT(channels->instance);
1634 WINPR_ASSERT(channels->instance->context);
1635 WINPR_ASSERT(entry);
1637 if (channels->clientDataCount + 1 > CHANNEL_MAX_COUNT)
1639 WLog_ERR(TAG,
"error: too many channels");
1643 if (freerdp_channels_is_loaded(channels, entry))
1645 WLog_WARN(TAG,
"Skipping, channel already loaded");
1649 CHANNEL_CLIENT_DATA* pChannelClientData = &channels->clientDataList[channels->clientDataCount];
1650 pChannelClientData->entry = entry;
1653 .protocolVersion = VIRTUAL_CHANNEL_VERSION_WIN2000,
1654 .pVirtualChannelInit = FreeRDP_VirtualChannelInit,
1655 .pVirtualChannelOpen = FreeRDP_VirtualChannelOpen,
1656 .pVirtualChannelClose =
1657 FreeRDP_VirtualChannelClose,
1658 .pVirtualChannelWrite =
1659 FreeRDP_VirtualChannelWrite,
1660 .MagicNumber = FREERDP_CHANNEL_MAGIC_NUMBER,
1661 .pExtendedData = data,
1662 .context = channels->instance->context };
1667 PCHANNEL_ENTRY_POINTS px;
1670 ptr.pfx = &EntryPoints;
1672 channels->can_call_init = TRUE;
1673 EnterCriticalSection(&channels->channelsLock);
1674 const BOOL status = pChannelClientData->entry(ptr.px);
1675 LeaveCriticalSection(&channels->channelsLock);
1677 channels->can_call_init = FALSE;
1681 WLog_ERR(TAG,
"error: channel export function call failed");
1688int freerdp_channels_client_load_ex(rdpChannels* channels, WINPR_ATTR_UNUSED rdpSettings* settings,
1689 PVIRTUALCHANNELENTRYEX entryEx,
void* data)
1691 WINPR_ASSERT(channels);
1692 WINPR_ASSERT(channels->instance);
1693 WINPR_ASSERT(channels->instance->context);
1694 WINPR_ASSERT(entryEx);
1696 if (channels->clientDataCount + 1 > CHANNEL_MAX_COUNT)
1698 WLog_ERR(TAG,
"error: too many channels");
1702 if (freerdp_channels_is_loaded_ex(channels, entryEx))
1704 WLog_WARN(TAG,
"Skipping, channel already loaded");
1708 CHANNEL_CLIENT_DATA* pChannelClientData = &channels->clientDataList[channels->clientDataCount];
1709 pChannelClientData->entryEx = entryEx;
1711 CHANNEL_INIT_DATA* pChannelInitData = &(channels->initDataList[channels->initDataCount++]);
1712 void* pInitHandle = pChannelInitData;
1713 pChannelInitData->channels = channels;
1716 .cbSize =
sizeof(EntryPointsEx),
1717 .protocolVersion = VIRTUAL_CHANNEL_VERSION_WIN2000,
1718 .pVirtualChannelInitEx = FreeRDP_VirtualChannelInitEx,
1719 .pVirtualChannelOpenEx = FreeRDP_VirtualChannelOpenEx,
1720 .pVirtualChannelCloseEx = FreeRDP_VirtualChannelCloseEx,
1721 .pVirtualChannelWriteEx = FreeRDP_VirtualChannelWriteEx,
1722 .MagicNumber = FREERDP_CHANNEL_MAGIC_NUMBER,
1723 .pExtendedData = data,
1724 .context = channels->instance->context
1730 PCHANNEL_ENTRY_POINTS_EX px;
1733 ptr.pfx = &EntryPointsEx;
1736 channels->can_call_init = TRUE;
1737 EnterCriticalSection(&channels->channelsLock);
1738 const BOOL status = pChannelClientData->entryEx(ptr.px, pInitHandle);
1739 LeaveCriticalSection(&channels->channelsLock);
1741 channels->can_call_init = FALSE;
1745 WLog_ERR(TAG,
"error: channel export function call failed");
1756int freerdp_channels_load_plugin(rdpChannels* channels, rdpSettings* settings,
const char* name,
1759 PVIRTUALCHANNELENTRY entry =
1760 freerdp_load_channel_addin_entry(name,
nullptr,
nullptr, FREERDP_ADDIN_CHANNEL_STATIC);
1765 return freerdp_channels_client_load(channels, settings, entry, data);
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
This struct contains function pointer to initialize/free objects.
OBJECT_FREE_FN fnObjectFree
WINPR_ATTR_NODISCARD OBJECT_NEW_FN fnObjectNew