channels: start working on extended virtual channel API
authorMarc-André Moreau <marcandre.moreau@gmail.com>
Mon, 14 Nov 2016 21:44:45 +0000 (16:44 -0500)
committerMarc-André Moreau <marcandre.moreau@gmail.com>
Mon, 14 Nov 2016 21:44:45 +0000 (16:44 -0500)
include/freerdp/svc.h
libfreerdp/core/client.c
libfreerdp/core/client.h

index c6843cc..d0532c8 100644 (file)
@@ -30,7 +30,8 @@
 
 #define CHANNEL_EVENT_USER     1000
 
-#define CHANNEL_EXPORT_FUNC_NAME "VirtualChannelEntry"
+#define CHANNEL_EXPORT_FUNC_NAME       "VirtualChannelEntry"
+#define CHANNEL_EXPORT_FUNC_NAME_EX    "VirtualChannelEntryEx"
 
 #define FREERDP_CHANNEL_MAGIC_NUMBER   0x46524450
 
@@ -53,4 +54,23 @@ struct _CHANNEL_ENTRY_POINTS_FREERDP
 typedef struct _CHANNEL_ENTRY_POINTS_FREERDP CHANNEL_ENTRY_POINTS_FREERDP;
 typedef CHANNEL_ENTRY_POINTS_FREERDP* PCHANNEL_ENTRY_POINTS_FREERDP;
 
+struct _CHANNEL_ENTRY_POINTS_FREERDP_EX
+{
+       UINT32 cbSize;
+       UINT32 protocolVersion;
+       PVIRTUALCHANNELINITEX  pVirtualChannelInitEx;
+       PVIRTUALCHANNELOPENEX  pVirtualChannelOpenEx;
+       PVIRTUALCHANNELCLOSEEX pVirtualChannelCloseEx;
+       PVIRTUALCHANNELWRITEEX pVirtualChannelWriteEx;
+
+       /* Extended Fields */
+       UINT32 MagicNumber; /* identifies FreeRDP */
+       void* pExtendedData; /* extended initial data */
+       void* pInterface; /* channel callback interface, use after initialization */
+       void** ppInterface; /* channel callback interface, use for initialization */
+       rdpContext* context;
+};
+typedef struct _CHANNEL_ENTRY_POINTS_FREERDP_EX CHANNEL_ENTRY_POINTS_FREERDP_EX;
+typedef CHANNEL_ENTRY_POINTS_FREERDP_EX* PCHANNEL_ENTRY_POINTS_FREERDP_EX;
+
 #endif /* FREERDP_SVC_H */
index 951b296..e7d25a8 100644 (file)
@@ -196,9 +196,9 @@ UINT freerdp_channels_pre_connect(rdpChannels* channels, freerdp* instance)
                pChannelClientData = &channels->clientDataList[index];
 
                if (pChannelClientData->pChannelInitEventProc)
-                       pChannelClientData->pChannelInitEventProc(
-                           pChannelClientData->pInitHandle,
-                           CHANNEL_EVENT_INITIALIZED, 0, 0);
+               {
+                       pChannelClientData->pChannelInitEventProc(pChannelClientData->pInitHandle, CHANNEL_EVENT_INITIALIZED, 0, 0);
+               }
 
                if (CHANNEL_RC_OK != getChannelError(instance->context))
                        break;
@@ -304,8 +304,7 @@ int freerdp_channels_data(freerdp* instance, UINT16 channelId, BYTE* data,
                return 1;
        }
 
-       pChannelOpenData = freerdp_channels_find_channel_open_data_by_name(channels,
-                          channel->Name);
+       pChannelOpenData = freerdp_channels_find_channel_open_data_by_name(channels, channel->Name);
 
        if (!pChannelOpenData)
        {
@@ -317,6 +316,11 @@ int freerdp_channels_data(freerdp* instance, UINT16 channelId, BYTE* data,
                pChannelOpenData->pChannelOpenEventProc(pChannelOpenData->OpenHandle,
                                                        CHANNEL_EVENT_DATA_RECEIVED, data, dataSize, totalSize, flags);
        }
+       else if (pChannelOpenData->pChannelOpenEventProcEx)
+       {
+               pChannelOpenData->pChannelOpenEventProcEx(pChannelOpenData->lpUserParam, pChannelOpenData->OpenHandle,
+                                                       CHANNEL_EVENT_DATA_RECEIVED, data, dataSize, totalSize, flags);
+       }
 
        return 0;
 }
@@ -349,8 +353,7 @@ static int freerdp_channels_process_sync(rdpChannels* channels,
                                break;
 
                        pChannelOpenData = item->pChannelOpenData;
-                       channel = freerdp_channels_find_channel_by_name(instance->context->rdp,
-                                 pChannelOpenData->name);
+                       channel = freerdp_channels_find_channel_by_name(instance->context->rdp, pChannelOpenData->name);
 
                        if (channel)
                                instance->SendChannelData(instance, channel->ChannelId, item->Data,
@@ -362,6 +365,12 @@ static int freerdp_channels_process_sync(rdpChannels* channels,
                                                                        CHANNEL_EVENT_WRITE_COMPLETE, item->UserData, item->DataLength,
                                                                        item->DataLength, 0);
                        }
+                       else if (pChannelOpenData->pChannelOpenEventProcEx)
+                       {
+                               pChannelOpenData->pChannelOpenEventProcEx(pChannelOpenData->lpUserParam, pChannelOpenData->OpenHandle,
+                                                                       CHANNEL_EVENT_WRITE_COMPLETE, item->UserData, item->DataLength,
+                                                                       item->DataLength, 0);
+                       }
 
                        free(item);
                }
@@ -461,9 +470,10 @@ UINT freerdp_channels_disconnect(rdpChannels* channels, freerdp* instance)
                pChannelClientData = &channels->clientDataList[index];
 
                if (pChannelClientData->pChannelInitEventProc)
+               {
                        pChannelClientData->pChannelInitEventProc(
-                           pChannelClientData->pInitHandle,
-                           CHANNEL_EVENT_DISCONNECTED, 0, 0);
+                               pChannelClientData->pInitHandle, CHANNEL_EVENT_DISCONNECTED, 0, 0);
+               }
 
                if (getChannelError(instance->context) != CHANNEL_RC_OK)
                        goto fail;
@@ -506,6 +516,92 @@ void freerdp_channels_close(rdpChannels* channels, freerdp* instance)
        MessageQueue_PostQuit(channels->queue, 0);
 }
 
+static UINT VCAPITYPE FreeRDP_VirtualChannelInitEx(LPVOID lpUserParam, LPVOID pInitHandle,
+       PCHANNEL_DEF pChannel, INT channelCount, ULONG versionRequested, PCHANNEL_INIT_EVENT_EX_FN pChannelInitEventProcEx)
+{
+       INT index;
+       void* pInterface;
+       DWORD OpenHandle;
+       CHANNEL_DEF* channel;
+       rdpSettings* settings;
+       PCHANNEL_DEF pChannelDef;
+       CHANNEL_INIT_DATA* pChannelInitData;
+       CHANNEL_OPEN_DATA* pChannelOpenData;
+       CHANNEL_CLIENT_DATA* pChannelClientData;
+       rdpChannels* channels = (rdpChannels*) pInitHandle;
+
+       if (!pInitHandle)
+               return CHANNEL_RC_BAD_INIT_HANDLE;
+
+       if (!pChannel || (channelCount <= 0) || !pChannelInitEventProcEx)
+               return CHANNEL_RC_INITIALIZATION_ERROR;
+
+       pInterface = g_pInterface;
+
+       pChannelInitData = (CHANNEL_INIT_DATA*) pInitHandle;
+       channels = pChannelInitData->channels;
+       pChannelInitData->pInterface = pInterface;
+
+       if (!channels->can_call_init)
+               return CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY;
+
+       if ((channels->openDataCount + channelCount) >= CHANNEL_MAX_COUNT)
+               return CHANNEL_RC_TOO_MANY_CHANNELS;
+
+       if (!pChannel)
+               return CHANNEL_RC_BAD_CHANNEL;
+
+       if (channels->connected)
+               return CHANNEL_RC_ALREADY_CONNECTED;
+
+       if (versionRequested != VIRTUAL_CHANNEL_VERSION_WIN2000)
+       {
+       }
+
+       for (index = 0; index < channelCount; index++)
+       {
+               pChannelDef = &pChannel[index];
+
+               if (freerdp_channels_find_channel_open_data_by_name(channels, pChannelDef->name) != 0)
+               {
+                       return CHANNEL_RC_BAD_CHANNEL;
+               }
+       }
+
+       pChannelClientData = &channels->clientDataList[channels->clientDataCount];
+       pChannelClientData->pChannelInitEventProcEx = pChannelInitEventProcEx;
+       pChannelClientData->pInitHandle = pInitHandle;
+       channels->clientDataCount++;
+       settings = channels->instance->context->settings;
+
+       for (index = 0; index < channelCount; index++)
+       {
+               pChannelDef = &pChannel[index];
+               pChannelOpenData = &channels->openDataList[channels->openDataCount];
+               OpenHandle = g_OpenHandleSeq++;
+               pChannelOpenData->OpenHandle = OpenHandle;
+               pChannelOpenData->channels = channels;
+               freerdp_channel_add_open_handle_data(&g_ChannelHandles, OpenHandle, (void*) channels);
+               HashTable_Add(channels->openHandles, (void*)(UINT_PTR) OpenHandle,
+                             (void*) pChannelOpenData);
+               pChannelOpenData->flags = 1; /* init */
+               strncpy(pChannelOpenData->name, pChannelDef->name, CHANNEL_NAME_LEN);
+               pChannelOpenData->options = pChannelDef->options;
+
+               if (settings->ChannelCount < CHANNEL_MAX_COUNT)
+               {
+                       channel = &settings->ChannelDefArray[settings->ChannelCount];
+                       strncpy(channel->name, pChannelDef->name, 7);
+                       channel->options = pChannelDef->options;
+                       settings->ChannelCount++;
+               }
+
+               channels->openDataCount++;
+       }
+
+       return CHANNEL_RC_OK;
+}
+
 static UINT VCAPITYPE FreeRDP_VirtualChannelInit(LPVOID* ppInitHandle,
         PCHANNEL_DEF pChannel,
         INT channelCount, ULONG versionRequested,
@@ -596,14 +692,51 @@ static UINT VCAPITYPE FreeRDP_VirtualChannelInit(LPVOID* ppInitHandle,
        return CHANNEL_RC_OK;
 }
 
+static UINT VCAPITYPE FreeRDP_VirtualChannelOpenEx(LPVOID pInitHandle,
+       LPDWORD pOpenHandle, PCHAR pChannelName, PCHANNEL_OPEN_EVENT_EX_FN pChannelOpenEventProcEx)
+{
+       void* pInterface;
+       rdpChannels* channels;
+       CHANNEL_INIT_DATA* pChannelInitData;
+       CHANNEL_OPEN_DATA* pChannelOpenData;
+
+       pChannelInitData = (CHANNEL_INIT_DATA*) pInitHandle;
+       channels = pChannelInitData->channels;
+       pInterface = pChannelInitData->pInterface;
+
+       if (!pOpenHandle)
+               return CHANNEL_RC_BAD_CHANNEL_HANDLE;
+
+       if (!pChannelOpenEventProcEx)
+               return CHANNEL_RC_BAD_PROC;
+
+       if (!channels->connected)
+               return CHANNEL_RC_NOT_CONNECTED;
+
+       pChannelOpenData = freerdp_channels_find_channel_open_data_by_name(channels, pChannelName);
+
+       if (!pChannelOpenData)
+               return CHANNEL_RC_UNKNOWN_CHANNEL_NAME;
+
+       if (pChannelOpenData->flags == 2)
+               return CHANNEL_RC_ALREADY_OPEN;
+
+       pChannelOpenData->flags = 2; /* open */
+       pChannelOpenData->pInterface = pInterface;
+       pChannelOpenData->pChannelOpenEventProcEx = pChannelOpenEventProcEx;
+       *pOpenHandle = pChannelOpenData->OpenHandle;
+
+       return CHANNEL_RC_OK;
+}
+
 static UINT VCAPITYPE FreeRDP_VirtualChannelOpen(LPVOID pInitHandle,
-        LPDWORD pOpenHandle,
-        PCHAR pChannelName, PCHANNEL_OPEN_EVENT_FN pChannelOpenEventProc)
+        LPDWORD pOpenHandle, PCHAR pChannelName, PCHANNEL_OPEN_EVENT_FN pChannelOpenEventProc)
 {
        void* pInterface;
        rdpChannels* channels;
        CHANNEL_INIT_DATA* pChannelInitData;
        CHANNEL_OPEN_DATA* pChannelOpenData;
+
        pChannelInitData = (CHANNEL_INIT_DATA*) pInitHandle;
        channels = pChannelInitData->channels;
        pInterface = pChannelInitData->pInterface;
@@ -617,8 +750,7 @@ static UINT VCAPITYPE FreeRDP_VirtualChannelOpen(LPVOID pInitHandle,
        if (!channels->connected)
                return CHANNEL_RC_NOT_CONNECTED;
 
-       pChannelOpenData = freerdp_channels_find_channel_open_data_by_name(channels,
-                          pChannelName);
+       pChannelOpenData = freerdp_channels_find_channel_open_data_by_name(channels, pChannelName);
 
        if (!pChannelOpenData)
                return CHANNEL_RC_UNKNOWN_CHANNEL_NAME;
@@ -630,19 +762,49 @@ static UINT VCAPITYPE FreeRDP_VirtualChannelOpen(LPVOID pInitHandle,
        pChannelOpenData->pInterface = pInterface;
        pChannelOpenData->pChannelOpenEventProc = pChannelOpenEventProc;
        *pOpenHandle = pChannelOpenData->OpenHandle;
+
+       return CHANNEL_RC_OK;
+}
+
+static UINT VCAPITYPE FreeRDP_VirtualChannelCloseEx(LPVOID pInitHandle, DWORD openHandle)
+{
+       rdpChannels* channels = NULL;
+       CHANNEL_INIT_DATA* pChannelInitData = NULL;
+       CHANNEL_OPEN_DATA* pChannelOpenData = NULL;
+
+       if (!pInitHandle)
+               return CHANNEL_RC_BAD_INIT_HANDLE;
+
+       pChannelInitData = (CHANNEL_INIT_DATA*) pInitHandle;
+       channels = pChannelInitData->channels;
+
+       if (!channels)
+               return CHANNEL_RC_BAD_CHANNEL_HANDLE;
+
+       pChannelOpenData = HashTable_GetItemValue(channels->openHandles, (void*)(UINT_PTR) openHandle);
+
+       if (!pChannelOpenData)
+               return CHANNEL_RC_BAD_CHANNEL_HANDLE;
+
+       if (pChannelOpenData->flags != 2)
+               return CHANNEL_RC_NOT_OPEN;
+
+       pChannelOpenData->flags = 0;
+
        return CHANNEL_RC_OK;
 }
 
 static UINT VCAPITYPE FreeRDP_VirtualChannelClose(DWORD openHandle)
 {
+       rdpChannels* channels;
        CHANNEL_OPEN_DATA* pChannelOpenData;
-       rdpChannels* channels = (rdpChannels*) freerdp_channel_get_open_handle_data(&g_ChannelHandles, openHandle);
+
+       channels = (rdpChannels*) freerdp_channel_get_open_handle_data(&g_ChannelHandles, openHandle);
 
        if (!channels)
                return CHANNEL_RC_BAD_CHANNEL_HANDLE;
 
-       pChannelOpenData = HashTable_GetItemValue(channels->openHandles,
-                          (void*)(UINT_PTR) openHandle);
+       pChannelOpenData = HashTable_GetItemValue(channels->openHandles, (void*)(UINT_PTR) openHandle);
 
        if (!pChannelOpenData)
                return CHANNEL_RC_BAD_CHANNEL_HANDLE;
@@ -651,12 +813,65 @@ static UINT VCAPITYPE FreeRDP_VirtualChannelClose(DWORD openHandle)
                return CHANNEL_RC_NOT_OPEN;
 
        pChannelOpenData->flags = 0;
+
+       return CHANNEL_RC_OK;
+}
+
+static UINT VCAPITYPE FreeRDP_VirtualChannelWriteEx(LPVOID pInitHandle, DWORD openHandle,
+       LPVOID pData, ULONG dataLength, LPVOID pUserData)
+{
+       rdpChannels* channels = NULL;
+       CHANNEL_INIT_DATA* pChannelInitData = NULL;
+       CHANNEL_OPEN_DATA* pChannelOpenData = NULL;
+       CHANNEL_OPEN_EVENT* pChannelOpenEvent = NULL;
+
+       if (!pInitHandle)
+               return CHANNEL_RC_BAD_INIT_HANDLE;
+
+       pChannelInitData = (CHANNEL_INIT_DATA*) pInitHandle;
+       channels = pChannelInitData->channels;
+
+       if (!channels)
+               return CHANNEL_RC_BAD_CHANNEL_HANDLE;
+
+       pChannelOpenData = HashTable_GetItemValue(channels->openHandles, (void*)(UINT_PTR) openHandle);
+
+       if (!pChannelOpenData)
+               return CHANNEL_RC_BAD_CHANNEL_HANDLE;
+
+       if (!channels->connected)
+               return CHANNEL_RC_NOT_CONNECTED;
+
+       if (!pData)
+               return CHANNEL_RC_NULL_DATA;
+
+       if (!dataLength)
+               return CHANNEL_RC_ZERO_LENGTH;
+
+       if (pChannelOpenData->flags != 2)
+               return CHANNEL_RC_NOT_OPEN;
+
+       pChannelOpenEvent = (CHANNEL_OPEN_EVENT*) malloc(sizeof(CHANNEL_OPEN_EVENT));
+
+       if (!pChannelOpenEvent)
+               return CHANNEL_RC_NO_MEMORY;
+
+       pChannelOpenEvent->Data = pData;
+       pChannelOpenEvent->DataLength = dataLength;
+       pChannelOpenEvent->UserData = pUserData;
+       pChannelOpenEvent->pChannelOpenData = pChannelOpenData;
+
+       if (!MessageQueue_Post(channels->queue, (void*) channels, 0, (void*) pChannelOpenEvent, NULL))
+       {
+               free(pChannelOpenEvent);
+               return CHANNEL_RC_NO_MEMORY;
+       }
+
        return CHANNEL_RC_OK;
 }
 
 static UINT VCAPITYPE FreeRDP_VirtualChannelWrite(DWORD openHandle,
-        LPVOID pData,
-        ULONG dataLength, LPVOID pUserData)
+        LPVOID pData, ULONG dataLength, LPVOID pUserData)
 {
        CHANNEL_OPEN_DATA* pChannelOpenData;
        CHANNEL_OPEN_EVENT* pChannelOpenEvent;
@@ -665,8 +880,7 @@ static UINT VCAPITYPE FreeRDP_VirtualChannelWrite(DWORD openHandle,
        if (!channels)
                return CHANNEL_RC_BAD_CHANNEL_HANDLE;
 
-       pChannelOpenData = HashTable_GetItemValue(channels->openHandles,
-                          (void*)(UINT_PTR) openHandle);
+       pChannelOpenData = HashTable_GetItemValue(channels->openHandles, (void*)(UINT_PTR) openHandle);
 
        if (!pChannelOpenData)
                return CHANNEL_RC_BAD_CHANNEL_HANDLE;
@@ -693,8 +907,7 @@ static UINT VCAPITYPE FreeRDP_VirtualChannelWrite(DWORD openHandle,
        pChannelOpenEvent->UserData = pUserData;
        pChannelOpenEvent->pChannelOpenData = pChannelOpenData;
 
-       if (!MessageQueue_Post(channels->queue, (void*) channels, 0,
-                              (void*) pChannelOpenEvent, NULL))
+       if (!MessageQueue_Post(channels->queue, (void*) channels, 0, (void*) pChannelOpenEvent, NULL))
        {
                free(pChannelOpenEvent);
                return CHANNEL_RC_NO_MEMORY;
@@ -703,8 +916,7 @@ static UINT VCAPITYPE FreeRDP_VirtualChannelWrite(DWORD openHandle,
        return CHANNEL_RC_OK;
 }
 
-static BOOL freerdp_channels_is_loaded(rdpChannels* channels,
-                                       PVIRTUALCHANNELENTRY entry)
+static BOOL freerdp_channels_is_loaded(rdpChannels* channels, PVIRTUALCHANNELENTRY entry)
 {
        int i;
 
@@ -719,6 +931,21 @@ static BOOL freerdp_channels_is_loaded(rdpChannels* channels,
        return FALSE;
 }
 
+static BOOL freerdp_channels_is_loaded_ex(rdpChannels* channels, PVIRTUALCHANNELENTRYEX entryEx)
+{
+       int i;
+
+       for (i = 0; i < channels->clientDataCount; i++)
+       {
+               CHANNEL_CLIENT_DATA* pChannelClientData = &channels->clientDataList[i];
+
+               if (pChannelClientData->entryEx == entryEx)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
 int freerdp_channels_client_load(rdpChannels* channels, rdpSettings* settings,
                                  PVIRTUALCHANNELENTRY entry, void* data)
 {
@@ -770,6 +997,63 @@ int freerdp_channels_client_load(rdpChannels* channels, rdpSettings* settings,
        return 0;
 }
 
+int freerdp_channels_client_load_ex(rdpChannels* channels, rdpSettings* settings,
+                                 PVIRTUALCHANNELENTRYEX entryEx, void* data)
+{
+       int status;
+       void* pInitHandle = NULL;
+       CHANNEL_ENTRY_POINTS_FREERDP_EX EntryPointsEx;
+       CHANNEL_INIT_DATA* pChannelInitData = NULL;
+       CHANNEL_CLIENT_DATA* pChannelClientData = NULL;
+
+       if (channels->clientDataCount + 1 >= CHANNEL_MAX_COUNT)
+       {
+               WLog_ERR(TAG,  "error: too many channels");
+               return 1;
+       }
+
+       if (freerdp_channels_is_loaded_ex(channels, entryEx))
+       {
+               WLog_WARN(TAG, "Skipping, channel already loaded");
+               return 0;
+       }
+
+       pChannelClientData = &channels->clientDataList[channels->clientDataCount];
+       pChannelClientData->entryEx = entryEx;
+
+       pChannelInitData = &(channels->initDataList[channels->initDataCount++]);
+       pInitHandle = pChannelInitData;
+       pChannelInitData->channels = channels;
+
+       ZeroMemory(&EntryPointsEx, sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
+       EntryPointsEx.cbSize = sizeof(EntryPointsEx);
+       EntryPointsEx.protocolVersion = VIRTUAL_CHANNEL_VERSION_WIN2000;
+       EntryPointsEx.pVirtualChannelInitEx = FreeRDP_VirtualChannelInitEx;
+       EntryPointsEx.pVirtualChannelOpenEx = FreeRDP_VirtualChannelOpenEx;
+       EntryPointsEx.pVirtualChannelCloseEx = FreeRDP_VirtualChannelCloseEx;
+       EntryPointsEx.pVirtualChannelWriteEx = FreeRDP_VirtualChannelWriteEx;
+       EntryPointsEx.MagicNumber = FREERDP_CHANNEL_MAGIC_NUMBER;
+       EntryPointsEx.ppInterface = &g_pInterface;
+       EntryPointsEx.pExtendedData = data;
+       EntryPointsEx.context = ((freerdp*) settings->instance)->context;
+       /* enable VirtualChannelInit */
+       channels->can_call_init = TRUE;
+       EnterCriticalSection(&channels->channelsLock);
+       g_pInterface = NULL;
+       status = pChannelClientData->entryEx((PCHANNEL_ENTRY_POINTS_EX) &EntryPointsEx, pInitHandle);
+       LeaveCriticalSection(&channels->channelsLock);
+       /* disable MyVirtualChannelInit */
+       channels->can_call_init = FALSE;
+
+       if (!status)
+       {
+               WLog_ERR(TAG,  "error: channel export function call failed");
+               return 1;
+       }
+
+       return 0;
+}
+
 /**
  * this is called when processing the command line parameters
  * called only from main thread
index 5f6bbef..c4ff5b8 100644 (file)
@@ -40,7 +40,9 @@
 struct rdp_channel_client_data
 {
        PVIRTUALCHANNELENTRY entry;
+       PVIRTUALCHANNELENTRYEX entryEx;
        PCHANNEL_INIT_EVENT_FN pChannelInitEventProc;
+       PCHANNEL_INIT_EVENT_EX_FN pChannelInitEventProcEx;
        void* pInitHandle;
 };
 typedef struct rdp_channel_client_data CHANNEL_CLIENT_DATA;
@@ -53,7 +55,9 @@ struct rdp_channel_open_data
        int flags;
        void* pInterface;
        rdpChannels* channels;
+       void* lpUserParam;
        PCHANNEL_OPEN_EVENT_FN pChannelOpenEventProc;
+       PCHANNEL_OPEN_EVENT_EX_FN pChannelOpenEventProcEx;
 };
 typedef struct rdp_channel_open_data CHANNEL_OPEN_DATA;