summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/wtsapi
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--winpr/libwinpr/wtsapi/CMakeLists.txt30
-rw-r--r--winpr/libwinpr/wtsapi/ModuleOptions.cmake9
-rw-r--r--winpr/libwinpr/wtsapi/test/CMakeLists.txt74
-rw-r--r--winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateProcesses.c49
-rw-r--r--winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateSessions.c50
-rw-r--r--winpr/libwinpr/wtsapi/test/TestWtsApiExtraDisconnectSession.c22
-rw-r--r--winpr/libwinpr/wtsapi/test/TestWtsApiExtraDynamicVirtualChannel.c53
-rw-r--r--winpr/libwinpr/wtsapi/test/TestWtsApiExtraLogoffSession.c22
-rw-r--r--winpr/libwinpr/wtsapi/test/TestWtsApiExtraSendMessage.c30
-rw-r--r--winpr/libwinpr/wtsapi/test/TestWtsApiExtraStartRemoteSessionEx.c31
-rw-r--r--winpr/libwinpr/wtsapi/test/TestWtsApiExtraVirtualChannel.c53
-rw-r--r--winpr/libwinpr/wtsapi/test/TestWtsApiQuerySessionInformation.c225
-rw-r--r--winpr/libwinpr/wtsapi/test/TestWtsApiSessionNotification.c62
-rw-r--r--winpr/libwinpr/wtsapi/test/TestWtsApiShutdownSystem.c35
-rw-r--r--winpr/libwinpr/wtsapi/test/TestWtsApiWaitSystemEvent.c39
-rw-r--r--winpr/libwinpr/wtsapi/wtsapi.c802
-rw-r--r--winpr/libwinpr/wtsapi/wtsapi_win32.c808
-rw-r--r--winpr/libwinpr/wtsapi/wtsapi_win32.h27
18 files changed, 2421 insertions, 0 deletions
diff --git a/winpr/libwinpr/wtsapi/CMakeLists.txt b/winpr/libwinpr/wtsapi/CMakeLists.txt
new file mode 100644
index 0000000..793e05f
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/CMakeLists.txt
@@ -0,0 +1,30 @@
+# WinPR: Windows Portable Runtime
+# libwinpr-wtsapi cmake build script
+#
+# Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+winpr_module_add(wtsapi.c)
+
+if(WIN32)
+ winpr_module_add(wtsapi_win32.c wtsapi_win32.h)
+
+ if (MINGW)
+ winpr_library_add_private(ntdll.lib) # Only required with MINGW
+ endif()
+endif()
+
+if(BUILD_TESTING)
+ add_subdirectory(test)
+endif()
diff --git a/winpr/libwinpr/wtsapi/ModuleOptions.cmake b/winpr/libwinpr/wtsapi/ModuleOptions.cmake
new file mode 100644
index 0000000..0575367
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/ModuleOptions.cmake
@@ -0,0 +1,9 @@
+
+set(MINWIN_LAYER "0")
+set(MINWIN_GROUP "none")
+set(MINWIN_MAJOR_VERSION "0")
+set(MINWIN_MINOR_VERSION "0")
+set(MINWIN_SHORT_NAME "wtsapi")
+set(MINWIN_LONG_NAME "Windows Terminal Services API")
+set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}")
+
diff --git a/winpr/libwinpr/wtsapi/test/CMakeLists.txt b/winpr/libwinpr/wtsapi/test/CMakeLists.txt
new file mode 100644
index 0000000..d5bf76c
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/test/CMakeLists.txt
@@ -0,0 +1,74 @@
+
+set(MODULE_NAME "TestWtsApi")
+set(MODULE_PREFIX "TEST_WTSAPI")
+
+set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
+
+set(UNIX_ONLY
+ TestWtsApiShutdownSystem.c
+ TestWtsApiWaitSystemEvent.c
+ )
+
+set(${MODULE_PREFIX}_TESTS
+ TestWtsApiEnumerateProcesses.c
+ TestWtsApiEnumerateSessions.c
+ TestWtsApiQuerySessionInformation.c
+ TestWtsApiSessionNotification.c
+ )
+
+if(NOT WIN32)
+ set(${MODULE_PREFIX}_TESTS ${${MODULE_PREFIX}_TESTS} ${UNIX_ONLY})
+endif()
+
+create_test_sourcelist(${MODULE_PREFIX}_SRCS
+ ${${MODULE_PREFIX}_DRIVER}
+ ${${MODULE_PREFIX}_TESTS})
+
+add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+
+
+target_link_libraries(${MODULE_NAME} winpr)
+
+set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
+
+foreach(test ${${MODULE_PREFIX}_TESTS})
+ get_filename_component(TestName ${test} NAME_WE)
+ add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
+endforeach()
+
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
+
+if(TESTS_WTSAPI_EXTRA)
+
+set(MODULE_NAME "TestWtsApiExtra")
+set(MODULE_PREFIX "TEST_WTSAPI_EXTRA")
+
+set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
+
+set(${MODULE_PREFIX}_TESTS
+ TestWtsApiExtraDisconnectSession.c
+ TestWtsApiExtraDynamicVirtualChannel.c
+ TestWtsApiExtraLogoffSession.c
+ TestWtsApiExtraSendMessage.c
+ TestWtsApiExtraVirtualChannel.c
+ TestWtsApiExtraStartRemoteSessionEx.c
+ )
+
+create_test_sourcelist(${MODULE_PREFIX}_SRCS
+ ${${MODULE_PREFIX}_DRIVER}
+ ${${MODULE_PREFIX}_TESTS})
+
+add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+
+target_link_libraries(${MODULE_NAME} winpr)
+
+set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
+
+foreach(test ${${MODULE_PREFIX}_TESTS})
+ get_filename_component(TestName ${test} NAME_WE)
+ add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
+ set_tests_properties(${TestName} PROPERTIES LABELS "WTSAPI_EXTRA")
+endforeach()
+
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
+endif()
diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateProcesses.c b/winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateProcesses.c
new file mode 100644
index 0000000..f26646c
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateProcesses.c
@@ -0,0 +1,49 @@
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/wtsapi.h>
+#include <winpr/environment.h>
+
+int TestWtsApiEnumerateProcesses(int argc, char* argv[])
+{
+ DWORD count = 0;
+ BOOL bSuccess = 0;
+ HANDLE hServer = NULL;
+ PWTS_PROCESS_INFOA pProcessInfo = NULL;
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+#ifndef _WIN32
+ if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", NULL, 0))
+ {
+ printf("%s: No RDS environment detected, skipping test\n", __func__);
+ return 0;
+ }
+#endif
+
+ hServer = WTS_CURRENT_SERVER_HANDLE;
+
+ count = 0;
+ pProcessInfo = NULL;
+
+ bSuccess = WTSEnumerateProcessesA(hServer, 0, 1, &pProcessInfo, &count);
+
+ if (!bSuccess)
+ {
+ printf("WTSEnumerateProcesses failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+
+#if 0
+ {
+ printf("WTSEnumerateProcesses enumerated %"PRIu32" processs:\n", count);
+ for (DWORD i = 0; i < count; i++)
+ printf("\t[%"PRIu32"]: %s (%"PRIu32")\n", i, pProcessInfo[i].pProcessName, pProcessInfo[i].ProcessId);
+ }
+#endif
+
+ WTSFreeMemory(pProcessInfo);
+
+ return 0;
+}
diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateSessions.c b/winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateSessions.c
new file mode 100644
index 0000000..afe48a9
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateSessions.c
@@ -0,0 +1,50 @@
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/wtsapi.h>
+#include <winpr/environment.h>
+
+int TestWtsApiEnumerateSessions(int argc, char* argv[])
+{
+ DWORD count = 0;
+ BOOL bSuccess = 0;
+ HANDLE hServer = NULL;
+ PWTS_SESSION_INFOA pSessionInfo = NULL;
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+#ifndef _WIN32
+ if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", NULL, 0))
+ {
+ printf("%s: No RDS environment detected, skipping test\n", __func__);
+ return 0;
+ }
+#endif
+
+ hServer = WTS_CURRENT_SERVER_HANDLE;
+
+ count = 0;
+ pSessionInfo = NULL;
+
+ bSuccess = WTSEnumerateSessionsA(hServer, 0, 1, &pSessionInfo, &count);
+
+ if (!bSuccess)
+ {
+ printf("WTSEnumerateSessions failed: %" PRIu32 "\n", GetLastError());
+ return 0;
+ }
+
+ printf("WTSEnumerateSessions count: %" PRIu32 "\n", count);
+
+ for (DWORD index = 0; index < count; index++)
+ {
+ printf("[%" PRIu32 "] SessionId: %" PRIu32 " WinstationName: '%s' State: %s (%u)\n", index,
+ pSessionInfo[index].SessionId, pSessionInfo[index].pWinStationName,
+ WTSSessionStateToString(pSessionInfo[index].State), pSessionInfo[index].State);
+ }
+
+ WTSFreeMemory(pSessionInfo);
+
+ return 0;
+}
diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiExtraDisconnectSession.c b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraDisconnectSession.c
new file mode 100644
index 0000000..4d04e59
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraDisconnectSession.c
@@ -0,0 +1,22 @@
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/wtsapi.h>
+
+int TestWtsApiExtraDisconnectSession(int argc, char* argv[])
+{
+ BOOL bSuccess;
+ HANDLE hServer;
+
+ hServer = WTS_CURRENT_SERVER_HANDLE;
+
+ bSuccess = WTSDisconnectSession(hServer, WTS_CURRENT_SESSION, FALSE);
+
+ if (!bSuccess)
+ {
+ printf("WTSDisconnectSession failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiExtraDynamicVirtualChannel.c b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraDynamicVirtualChannel.c
new file mode 100644
index 0000000..d0bd87e
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraDynamicVirtualChannel.c
@@ -0,0 +1,53 @@
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/wtsapi.h>
+
+int TestWtsApiExtraDynamicVirtualChannel(int argc, char* argv[])
+{
+ BOOL bSuccess;
+ ULONG length;
+ ULONG bytesRead;
+ ULONG bytesWritten;
+ BYTE buffer[1024];
+ HANDLE hVirtualChannel;
+
+ length = sizeof(buffer);
+
+ hVirtualChannel =
+ WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, "ECHO", WTS_CHANNEL_OPTION_DYNAMIC);
+
+ if (hVirtualChannel == INVALID_HANDLE_VALUE)
+ {
+ printf("WTSVirtualChannelOpen failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+ printf("WTSVirtualChannelOpen opend");
+ bytesWritten = 0;
+ bSuccess = WTSVirtualChannelWrite(hVirtualChannel, (PCHAR)buffer, length, &bytesWritten);
+
+ if (!bSuccess)
+ {
+ printf("WTSVirtualChannelWrite failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+ printf("WTSVirtualChannelWrite written");
+
+ bytesRead = 0;
+ bSuccess = WTSVirtualChannelRead(hVirtualChannel, 5000, (PCHAR)buffer, length, &bytesRead);
+
+ if (!bSuccess)
+ {
+ printf("WTSVirtualChannelRead failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+ printf("WTSVirtualChannelRead read");
+
+ if (!WTSVirtualChannelClose(hVirtualChannel))
+ {
+ printf("WTSVirtualChannelClose failed\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiExtraLogoffSession.c b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraLogoffSession.c
new file mode 100644
index 0000000..2af5002
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraLogoffSession.c
@@ -0,0 +1,22 @@
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/wtsapi.h>
+
+int TestWtsApiExtraLogoffSession(int argc, char* argv[])
+{
+ BOOL bSuccess;
+ HANDLE hServer;
+
+ hServer = WTS_CURRENT_SERVER_HANDLE;
+
+ bSuccess = WTSLogoffSession(hServer, WTS_CURRENT_SESSION, FALSE);
+
+ if (!bSuccess)
+ {
+ printf("WTSLogoffSession failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiExtraSendMessage.c b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraSendMessage.c
new file mode 100644
index 0000000..f311af8
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraSendMessage.c
@@ -0,0 +1,30 @@
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/wtsapi.h>
+#include <winpr/user.h>
+
+#define TITLE "thats the title"
+#define MESSAGE "thats the message"
+
+int TestWtsApiExtraSendMessage(int argc, char* argv[])
+{
+ BOOL bSuccess;
+ HANDLE hServer;
+ DWORD result;
+
+ hServer = WTS_CURRENT_SERVER_HANDLE;
+
+ bSuccess = WTSSendMessageA(hServer, WTS_CURRENT_SESSION, TITLE, strlen(TITLE) + 1, MESSAGE,
+ strlen(MESSAGE) + 1, MB_CANCELTRYCONTINUE, 3, &result, TRUE);
+
+ if (!bSuccess)
+ {
+ printf("WTSSendMessage failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+
+ printf("WTSSendMessage got result: %" PRIu32 "\n", result);
+
+ return 0;
+}
diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiExtraStartRemoteSessionEx.c b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraStartRemoteSessionEx.c
new file mode 100644
index 0000000..179d33b
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraStartRemoteSessionEx.c
@@ -0,0 +1,31 @@
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/wtsapi.h>
+#include <winpr/input.h>
+#include <winpr/environment.h>
+
+int TestWtsApiExtraStartRemoteSessionEx(int argc, char* argv[])
+{
+ BOOL bSuccess;
+ ULONG logonId = 0;
+ char logonIdStr[10];
+
+ bSuccess = GetEnvironmentVariableA("TEST_SESSION_LOGON_ID", logonIdStr, 10);
+ if (bSuccess)
+ {
+ sscanf(logonIdStr, "%u\n", &logonId);
+ }
+
+ bSuccess = WTSStartRemoteControlSessionEx(
+ NULL, logonId, VK_F10, REMOTECONTROL_KBDSHIFT_HOTKEY | REMOTECONTROL_KBDCTRL_HOTKEY,
+ REMOTECONTROL_FLAG_DISABLE_INPUT);
+
+ if (!bSuccess)
+ {
+ printf("WTSStartRemoteControlSessionEx failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiExtraVirtualChannel.c b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraVirtualChannel.c
new file mode 100644
index 0000000..c528e51
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraVirtualChannel.c
@@ -0,0 +1,53 @@
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/wtsapi.h>
+
+int TestWtsApiExtraVirtualChannel(int argc, char* argv[])
+{
+ BOOL bSuccess;
+ ULONG length;
+ ULONG bytesRead;
+ ULONG bytesWritten;
+ BYTE buffer[1024];
+ HANDLE hVirtualChannel;
+
+ length = sizeof(buffer);
+
+ hVirtualChannel =
+ WTSVirtualChannelOpen(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, "sample");
+
+ if (hVirtualChannel == INVALID_HANDLE_VALUE)
+ {
+ printf("WTSVirtualChannelOpen failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+ printf("WTSVirtualChannelOpen opend");
+ bytesWritten = 0;
+ bSuccess = WTSVirtualChannelWrite(hVirtualChannel, (PCHAR)buffer, length, &bytesWritten);
+
+ if (!bSuccess)
+ {
+ printf("WTSVirtualChannelWrite failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+ printf("WTSVirtualChannelWrite written");
+
+ bytesRead = 0;
+ bSuccess = WTSVirtualChannelRead(hVirtualChannel, 5000, (PCHAR)buffer, length, &bytesRead);
+
+ if (!bSuccess)
+ {
+ printf("WTSVirtualChannelRead failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+ printf("WTSVirtualChannelRead read");
+
+ if (!WTSVirtualChannelClose(hVirtualChannel))
+ {
+ printf("WTSVirtualChannelClose failed\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiQuerySessionInformation.c b/winpr/libwinpr/wtsapi/test/TestWtsApiQuerySessionInformation.c
new file mode 100644
index 0000000..bc232f0
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/test/TestWtsApiQuerySessionInformation.c
@@ -0,0 +1,225 @@
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/wtsapi.h>
+#include <winpr/environment.h>
+
+int TestWtsApiQuerySessionInformation(int argc, char* argv[])
+{
+ DWORD count = 0;
+ BOOL bSuccess = 0;
+ HANDLE hServer = NULL;
+ LPSTR pBuffer = NULL;
+ DWORD sessionId = 0;
+ DWORD bytesReturned = 0;
+ PWTS_SESSION_INFOA pSessionInfo = NULL;
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+#ifndef _WIN32
+ if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", NULL, 0))
+ {
+ printf("%s: No RDS environment detected, skipping test\n", __func__);
+ return 0;
+ }
+#endif
+
+ hServer = WTS_CURRENT_SERVER_HANDLE;
+
+ count = 0;
+ pSessionInfo = NULL;
+
+ bSuccess = WTSEnumerateSessionsA(hServer, 0, 1, &pSessionInfo, &count);
+
+ if (!bSuccess)
+ {
+ printf("WTSEnumerateSessions failed: %" PRIu32 "\n", GetLastError());
+ return 0;
+ }
+
+ printf("WTSEnumerateSessions count: %" PRIu32 "\n", count);
+
+ for (DWORD index = 0; index < count; index++)
+ {
+ char* Username = NULL;
+ char* Domain = NULL;
+ char* ClientName = NULL;
+ ULONG ClientBuildNumber = 0;
+ USHORT ClientProductId = 0;
+ ULONG ClientHardwareId = 0;
+ USHORT ClientProtocolType = 0;
+ PWTS_CLIENT_DISPLAY ClientDisplay = NULL;
+ PWTS_CLIENT_ADDRESS ClientAddress = NULL;
+ WTS_CONNECTSTATE_CLASS ConnectState = WTSInit;
+
+ pBuffer = NULL;
+ bytesReturned = 0;
+
+ sessionId = pSessionInfo[index].SessionId;
+
+ printf("[%" PRIu32 "] SessionId: %" PRIu32 " State: %s (%u) WinstationName: '%s'\n", index,
+ pSessionInfo[index].SessionId, WTSSessionStateToString(pSessionInfo[index].State),
+ pSessionInfo[index].State, pSessionInfo[index].pWinStationName);
+
+ /* WTSUserName */
+
+ bSuccess =
+ WTSQuerySessionInformationA(hServer, sessionId, WTSUserName, &pBuffer, &bytesReturned);
+
+ if (!bSuccess)
+ {
+ printf("WTSQuerySessionInformation WTSUserName failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+
+ Username = (char*)pBuffer;
+ printf("\tWTSUserName: '%s'\n", Username);
+
+ /* WTSDomainName */
+
+ bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSDomainName, &pBuffer,
+ &bytesReturned);
+
+ if (!bSuccess)
+ {
+ printf("WTSQuerySessionInformation WTSDomainName failed: %" PRIu32 "\n",
+ GetLastError());
+ return -1;
+ }
+
+ Domain = (char*)pBuffer;
+ printf("\tWTSDomainName: '%s'\n", Domain);
+
+ /* WTSConnectState */
+
+ bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSConnectState, &pBuffer,
+ &bytesReturned);
+
+ if (!bSuccess)
+ {
+ printf("WTSQuerySessionInformation WTSConnectState failed: %" PRIu32 "\n",
+ GetLastError());
+ return -1;
+ }
+
+ ConnectState = *((WTS_CONNECTSTATE_CLASS*)pBuffer);
+ printf("\tWTSConnectState: %u (%s)\n", ConnectState, WTSSessionStateToString(ConnectState));
+
+ /* WTSClientBuildNumber */
+
+ bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientBuildNumber, &pBuffer,
+ &bytesReturned);
+
+ if (!bSuccess)
+ {
+ printf("WTSQuerySessionInformation WTSClientBuildNumber failed: %" PRIu32 "\n",
+ GetLastError());
+ return -1;
+ }
+
+ ClientBuildNumber = *((ULONG*)pBuffer);
+ printf("\tWTSClientBuildNumber: %" PRIu32 "\n", ClientBuildNumber);
+
+ /* WTSClientName */
+
+ bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientName, &pBuffer,
+ &bytesReturned);
+
+ if (!bSuccess)
+ {
+ printf("WTSQuerySessionInformation WTSClientName failed: %" PRIu32 "\n",
+ GetLastError());
+ return -1;
+ }
+
+ ClientName = (char*)pBuffer;
+ printf("\tWTSClientName: '%s'\n", ClientName);
+
+ /* WTSClientProductId */
+
+ bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientProductId, &pBuffer,
+ &bytesReturned);
+
+ if (!bSuccess)
+ {
+ printf("WTSQuerySessionInformation WTSClientProductId failed: %" PRIu32 "\n",
+ GetLastError());
+ return -1;
+ }
+
+ ClientProductId = *((USHORT*)pBuffer);
+ printf("\tWTSClientProductId: %" PRIu16 "\n", ClientProductId);
+
+ /* WTSClientHardwareId */
+
+ bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientHardwareId, &pBuffer,
+ &bytesReturned);
+
+ if (!bSuccess)
+ {
+ printf("WTSQuerySessionInformation WTSClientHardwareId failed: %" PRIu32 "\n",
+ GetLastError());
+ return -1;
+ }
+
+ ClientHardwareId = *((ULONG*)pBuffer);
+ printf("\tWTSClientHardwareId: %" PRIu32 "\n", ClientHardwareId);
+
+ /* WTSClientAddress */
+
+ bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientAddress, &pBuffer,
+ &bytesReturned);
+
+ if (!bSuccess)
+ {
+ printf("WTSQuerySessionInformation WTSClientAddress failed: %" PRIu32 "\n",
+ GetLastError());
+ return -1;
+ }
+
+ ClientAddress = (PWTS_CLIENT_ADDRESS)pBuffer;
+ printf("\tWTSClientAddress: AddressFamily: %" PRIu32 " Address: ",
+ ClientAddress->AddressFamily);
+ for (DWORD i = 0; i < sizeof(ClientAddress->Address); i++)
+ printf("%02" PRIX8 "", ClientAddress->Address[i]);
+ printf("\n");
+
+ /* WTSClientDisplay */
+
+ bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientDisplay, &pBuffer,
+ &bytesReturned);
+
+ if (!bSuccess)
+ {
+ printf("WTSQuerySessionInformation WTSClientDisplay failed: %" PRIu32 "\n",
+ GetLastError());
+ return -1;
+ }
+
+ ClientDisplay = (PWTS_CLIENT_DISPLAY)pBuffer;
+ printf("\tWTSClientDisplay: HorizontalResolution: %" PRIu32 " VerticalResolution: %" PRIu32
+ " ColorDepth: %" PRIu32 "\n",
+ ClientDisplay->HorizontalResolution, ClientDisplay->VerticalResolution,
+ ClientDisplay->ColorDepth);
+
+ /* WTSClientProtocolType */
+
+ bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientProtocolType, &pBuffer,
+ &bytesReturned);
+
+ if (!bSuccess)
+ {
+ printf("WTSQuerySessionInformation WTSClientProtocolType failed: %" PRIu32 "\n",
+ GetLastError());
+ return -1;
+ }
+
+ ClientProtocolType = *((USHORT*)pBuffer);
+ printf("\tWTSClientProtocolType: %" PRIu16 "\n", ClientProtocolType);
+ }
+
+ WTSFreeMemory(pSessionInfo);
+
+ return 0;
+}
diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiSessionNotification.c b/winpr/libwinpr/wtsapi/test/TestWtsApiSessionNotification.c
new file mode 100644
index 0000000..e83c27b
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/test/TestWtsApiSessionNotification.c
@@ -0,0 +1,62 @@
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/wtsapi.h>
+#include <winpr/environment.h>
+
+int TestWtsApiSessionNotification(int argc, char* argv[])
+{
+ HWND hWnd = NULL;
+ BOOL bSuccess = 0;
+ DWORD dwFlags = 0;
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+#ifndef _WIN32
+ if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", NULL, 0))
+ {
+ printf("%s: No RDS environment detected, skipping test\n", __func__);
+ return 0;
+ }
+#else
+ /* We create a message-only window and use the predefined class name "STATIC" for simplicity */
+ hWnd = CreateWindowA("STATIC", "TestWtsApiSessionNotification", 0, 0, 0, 0, 0, HWND_MESSAGE,
+ NULL, NULL, NULL);
+ if (!hWnd)
+ {
+ printf("%s: error creating message-only window: %" PRIu32 "\n", __func__, GetLastError());
+ return -1;
+ }
+#endif
+
+ dwFlags = NOTIFY_FOR_ALL_SESSIONS;
+
+ bSuccess = WTSRegisterSessionNotification(hWnd, dwFlags);
+
+ if (!bSuccess)
+ {
+ printf("%s: WTSRegisterSessionNotification failed: %" PRIu32 "\n", __func__,
+ GetLastError());
+ return -1;
+ }
+
+ bSuccess = WTSUnRegisterSessionNotification(hWnd);
+
+#ifdef _WIN32
+ if (hWnd)
+ {
+ DestroyWindow(hWnd);
+ hWnd = NULL;
+ }
+#endif
+
+ if (!bSuccess)
+ {
+ printf("%s: WTSUnRegisterSessionNotification failed: %" PRIu32 "\n", __func__,
+ GetLastError());
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiShutdownSystem.c b/winpr/libwinpr/wtsapi/test/TestWtsApiShutdownSystem.c
new file mode 100644
index 0000000..431424b
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/test/TestWtsApiShutdownSystem.c
@@ -0,0 +1,35 @@
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/wtsapi.h>
+#include <winpr/environment.h>
+
+int TestWtsApiShutdownSystem(int argc, char* argv[])
+{
+ BOOL bSuccess = 0;
+ HANDLE hServer = NULL;
+ DWORD ShutdownFlag = 0;
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+#ifndef _WIN32
+ if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", NULL, 0))
+ {
+ printf("%s: No RDS environment detected, skipping test\n", __func__);
+ return 0;
+ }
+#endif
+
+ hServer = WTS_CURRENT_SERVER_HANDLE;
+ ShutdownFlag = WTS_WSD_SHUTDOWN;
+
+ bSuccess = WTSShutdownSystem(hServer, ShutdownFlag);
+
+ if (!bSuccess)
+ {
+ printf("WTSShutdownSystem failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiWaitSystemEvent.c b/winpr/libwinpr/wtsapi/test/TestWtsApiWaitSystemEvent.c
new file mode 100644
index 0000000..389c0be
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/test/TestWtsApiWaitSystemEvent.c
@@ -0,0 +1,39 @@
+
+#include <winpr/crt.h>
+#include <winpr/error.h>
+#include <winpr/wtsapi.h>
+#include <winpr/environment.h>
+
+int TestWtsApiWaitSystemEvent(int argc, char* argv[])
+{
+ BOOL bSuccess = 0;
+ HANDLE hServer = NULL;
+ DWORD eventMask = 0;
+ DWORD eventFlags = 0;
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+#ifndef _WIN32
+ if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", NULL, 0))
+ {
+ printf("%s: No RDS environment detected, skipping test\n", __func__);
+ return 0;
+ }
+#endif
+
+ hServer = WTS_CURRENT_SERVER_HANDLE;
+
+ eventMask = WTS_EVENT_ALL;
+ eventFlags = 0;
+
+ bSuccess = WTSWaitSystemEvent(hServer, eventMask, &eventFlags);
+
+ if (!bSuccess)
+ {
+ printf("WTSWaitSystemEvent failed: %" PRIu32 "\n", GetLastError());
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/winpr/libwinpr/wtsapi/wtsapi.c b/winpr/libwinpr/wtsapi/wtsapi.c
new file mode 100644
index 0000000..46bf9b9
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/wtsapi.c
@@ -0,0 +1,802 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Windows Terminal Services API
+ *
+ * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
+ * Copyright 2015 Copyright 2015 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/ini.h>
+#include <winpr/path.h>
+#include <winpr/synch.h>
+#include <winpr/library.h>
+#include <winpr/environment.h>
+
+#include <winpr/wtsapi.h>
+
+#ifdef _WIN32
+#include "wtsapi_win32.h"
+#endif
+
+#include "../log.h"
+#define TAG WINPR_TAG("wtsapi")
+
+/**
+ * Remote Desktop Services API Functions:
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/aa383464/
+ */
+
+static HMODULE g_WtsApiModule = NULL;
+
+static const WtsApiFunctionTable* g_WtsApi = NULL;
+
+#if defined(_WIN32)
+static HMODULE g_WtsApi32Module = NULL;
+static WtsApiFunctionTable WtsApi32_WtsApiFunctionTable = { 0 };
+
+#ifdef __MINGW32__
+#define WTSAPI32_LOAD_PROC(NAME, TYPE) \
+ WtsApi32_WtsApiFunctionTable.p##NAME = (TYPE)GetProcAddress(g_WtsApi32Module, "WTS" #NAME);
+#else
+#define WTSAPI32_LOAD_PROC(NAME, TYPE) \
+ WtsApi32_WtsApiFunctionTable.p##NAME = (##TYPE)GetProcAddress(g_WtsApi32Module, "WTS" #NAME);
+#endif
+
+static BOOL WtsApi32_InitializeWtsApi(void)
+{
+ g_WtsApi32Module = LoadLibraryA("wtsapi32.dll");
+
+ if (!g_WtsApi32Module)
+ return FALSE;
+
+ WTSAPI32_LOAD_PROC(StopRemoteControlSession, WTS_STOP_REMOTE_CONTROL_SESSION_FN);
+ WTSAPI32_LOAD_PROC(StartRemoteControlSessionW, WTS_START_REMOTE_CONTROL_SESSION_FN_W);
+ WTSAPI32_LOAD_PROC(StartRemoteControlSessionA, WTS_START_REMOTE_CONTROL_SESSION_FN_A);
+ WTSAPI32_LOAD_PROC(ConnectSessionW, WTS_CONNECT_SESSION_FN_W);
+ WTSAPI32_LOAD_PROC(ConnectSessionA, WTS_CONNECT_SESSION_FN_A);
+ WTSAPI32_LOAD_PROC(EnumerateServersW, WTS_ENUMERATE_SERVERS_FN_W);
+ WTSAPI32_LOAD_PROC(EnumerateServersA, WTS_ENUMERATE_SERVERS_FN_A);
+ WTSAPI32_LOAD_PROC(OpenServerW, WTS_OPEN_SERVER_FN_W);
+ WTSAPI32_LOAD_PROC(OpenServerA, WTS_OPEN_SERVER_FN_A);
+ WTSAPI32_LOAD_PROC(OpenServerExW, WTS_OPEN_SERVER_EX_FN_W);
+ WTSAPI32_LOAD_PROC(OpenServerExA, WTS_OPEN_SERVER_EX_FN_A);
+ WTSAPI32_LOAD_PROC(CloseServer, WTS_CLOSE_SERVER_FN);
+ WTSAPI32_LOAD_PROC(EnumerateSessionsW, WTS_ENUMERATE_SESSIONS_FN_W);
+ WTSAPI32_LOAD_PROC(EnumerateSessionsA, WTS_ENUMERATE_SESSIONS_FN_A);
+ WTSAPI32_LOAD_PROC(EnumerateSessionsExW, WTS_ENUMERATE_SESSIONS_EX_FN_W);
+ WTSAPI32_LOAD_PROC(EnumerateSessionsExA, WTS_ENUMERATE_SESSIONS_EX_FN_A);
+ WTSAPI32_LOAD_PROC(EnumerateProcessesW, WTS_ENUMERATE_PROCESSES_FN_W);
+ WTSAPI32_LOAD_PROC(EnumerateProcessesA, WTS_ENUMERATE_PROCESSES_FN_A);
+ WTSAPI32_LOAD_PROC(TerminateProcess, WTS_TERMINATE_PROCESS_FN);
+ WTSAPI32_LOAD_PROC(QuerySessionInformationW, WTS_QUERY_SESSION_INFORMATION_FN_W);
+ WTSAPI32_LOAD_PROC(QuerySessionInformationA, WTS_QUERY_SESSION_INFORMATION_FN_A);
+ WTSAPI32_LOAD_PROC(QueryUserConfigW, WTS_QUERY_USER_CONFIG_FN_W);
+ WTSAPI32_LOAD_PROC(QueryUserConfigA, WTS_QUERY_USER_CONFIG_FN_A);
+ WTSAPI32_LOAD_PROC(SetUserConfigW, WTS_SET_USER_CONFIG_FN_W);
+ WTSAPI32_LOAD_PROC(SetUserConfigA, WTS_SET_USER_CONFIG_FN_A);
+ WTSAPI32_LOAD_PROC(SendMessageW, WTS_SEND_MESSAGE_FN_W);
+ WTSAPI32_LOAD_PROC(SendMessageA, WTS_SEND_MESSAGE_FN_A);
+ WTSAPI32_LOAD_PROC(DisconnectSession, WTS_DISCONNECT_SESSION_FN);
+ WTSAPI32_LOAD_PROC(LogoffSession, WTS_LOGOFF_SESSION_FN);
+ WTSAPI32_LOAD_PROC(ShutdownSystem, WTS_SHUTDOWN_SYSTEM_FN);
+ WTSAPI32_LOAD_PROC(WaitSystemEvent, WTS_WAIT_SYSTEM_EVENT_FN);
+ WTSAPI32_LOAD_PROC(VirtualChannelOpen, WTS_VIRTUAL_CHANNEL_OPEN_FN);
+ WTSAPI32_LOAD_PROC(VirtualChannelOpenEx, WTS_VIRTUAL_CHANNEL_OPEN_EX_FN);
+ WTSAPI32_LOAD_PROC(VirtualChannelClose, WTS_VIRTUAL_CHANNEL_CLOSE_FN);
+ WTSAPI32_LOAD_PROC(VirtualChannelRead, WTS_VIRTUAL_CHANNEL_READ_FN);
+ WTSAPI32_LOAD_PROC(VirtualChannelWrite, WTS_VIRTUAL_CHANNEL_WRITE_FN);
+ WTSAPI32_LOAD_PROC(VirtualChannelPurgeInput, WTS_VIRTUAL_CHANNEL_PURGE_INPUT_FN);
+ WTSAPI32_LOAD_PROC(VirtualChannelPurgeOutput, WTS_VIRTUAL_CHANNEL_PURGE_OUTPUT_FN);
+ WTSAPI32_LOAD_PROC(VirtualChannelQuery, WTS_VIRTUAL_CHANNEL_QUERY_FN);
+ WTSAPI32_LOAD_PROC(FreeMemory, WTS_FREE_MEMORY_FN);
+ WTSAPI32_LOAD_PROC(RegisterSessionNotification, WTS_REGISTER_SESSION_NOTIFICATION_FN);
+ WTSAPI32_LOAD_PROC(UnRegisterSessionNotification, WTS_UNREGISTER_SESSION_NOTIFICATION_FN);
+ WTSAPI32_LOAD_PROC(RegisterSessionNotificationEx, WTS_REGISTER_SESSION_NOTIFICATION_EX_FN);
+ WTSAPI32_LOAD_PROC(UnRegisterSessionNotificationEx, WTS_UNREGISTER_SESSION_NOTIFICATION_EX_FN);
+ WTSAPI32_LOAD_PROC(QueryUserToken, WTS_QUERY_USER_TOKEN_FN);
+ WTSAPI32_LOAD_PROC(FreeMemoryExW, WTS_FREE_MEMORY_EX_FN_W);
+ WTSAPI32_LOAD_PROC(FreeMemoryExA, WTS_FREE_MEMORY_EX_FN_A);
+ WTSAPI32_LOAD_PROC(EnumerateProcessesExW, WTS_ENUMERATE_PROCESSES_EX_FN_W);
+ WTSAPI32_LOAD_PROC(EnumerateProcessesExA, WTS_ENUMERATE_PROCESSES_EX_FN_A);
+ WTSAPI32_LOAD_PROC(EnumerateListenersW, WTS_ENUMERATE_LISTENERS_FN_W);
+ WTSAPI32_LOAD_PROC(EnumerateListenersA, WTS_ENUMERATE_LISTENERS_FN_A);
+ WTSAPI32_LOAD_PROC(QueryListenerConfigW, WTS_QUERY_LISTENER_CONFIG_FN_W);
+ WTSAPI32_LOAD_PROC(QueryListenerConfigA, WTS_QUERY_LISTENER_CONFIG_FN_A);
+ WTSAPI32_LOAD_PROC(CreateListenerW, WTS_CREATE_LISTENER_FN_W);
+ WTSAPI32_LOAD_PROC(CreateListenerA, WTS_CREATE_LISTENER_FN_A);
+ WTSAPI32_LOAD_PROC(SetListenerSecurityW, WTS_SET_LISTENER_SECURITY_FN_W);
+ WTSAPI32_LOAD_PROC(SetListenerSecurityA, WTS_SET_LISTENER_SECURITY_FN_A);
+ WTSAPI32_LOAD_PROC(GetListenerSecurityW, WTS_GET_LISTENER_SECURITY_FN_W);
+ WTSAPI32_LOAD_PROC(GetListenerSecurityA, WTS_GET_LISTENER_SECURITY_FN_A);
+ WTSAPI32_LOAD_PROC(EnableChildSessions, WTS_ENABLE_CHILD_SESSIONS_FN);
+ WTSAPI32_LOAD_PROC(IsChildSessionsEnabled, WTS_IS_CHILD_SESSIONS_ENABLED_FN);
+ WTSAPI32_LOAD_PROC(GetChildSessionId, WTS_GET_CHILD_SESSION_ID_FN);
+ WTSAPI32_LOAD_PROC(GetActiveConsoleSessionId, WTS_GET_ACTIVE_CONSOLE_SESSION_ID_FN);
+
+ Win32_InitializeWinSta(&WtsApi32_WtsApiFunctionTable);
+
+ g_WtsApi = &WtsApi32_WtsApiFunctionTable;
+
+ return TRUE;
+}
+#endif
+
+/* WtsApi Functions */
+
+static BOOL CALLBACK InitializeWtsApiStubs(PINIT_ONCE once, PVOID param, PVOID* context);
+static INIT_ONCE wtsapiInitOnce = INIT_ONCE_STATIC_INIT;
+
+#define WTSAPI_STUB_CALL_VOID(_name, ...) \
+ InitOnceExecuteOnce(&wtsapiInitOnce, InitializeWtsApiStubs, NULL, NULL); \
+ if (!g_WtsApi || !g_WtsApi->p##_name) \
+ return; \
+ g_WtsApi->p##_name(__VA_ARGS__)
+
+#define WTSAPI_STUB_CALL_BOOL(_name, ...) \
+ InitOnceExecuteOnce(&wtsapiInitOnce, InitializeWtsApiStubs, NULL, NULL); \
+ if (!g_WtsApi || !g_WtsApi->p##_name) \
+ return FALSE; \
+ return g_WtsApi->p##_name(__VA_ARGS__)
+
+#define WTSAPI_STUB_CALL_HANDLE(_name, ...) \
+ InitOnceExecuteOnce(&wtsapiInitOnce, InitializeWtsApiStubs, NULL, NULL); \
+ if (!g_WtsApi || !g_WtsApi->p##_name) \
+ return NULL; \
+ return g_WtsApi->p##_name(__VA_ARGS__)
+
+BOOL WINAPI WTSStartRemoteControlSessionW(LPWSTR pTargetServerName, ULONG TargetLogonId,
+ BYTE HotkeyVk, USHORT HotkeyModifiers)
+{
+ WTSAPI_STUB_CALL_BOOL(StartRemoteControlSessionW, pTargetServerName, TargetLogonId, HotkeyVk,
+ HotkeyModifiers);
+}
+
+BOOL WINAPI WTSStartRemoteControlSessionA(LPSTR pTargetServerName, ULONG TargetLogonId,
+ BYTE HotkeyVk, USHORT HotkeyModifiers)
+{
+ WTSAPI_STUB_CALL_BOOL(StartRemoteControlSessionA, pTargetServerName, TargetLogonId, HotkeyVk,
+ HotkeyModifiers);
+}
+
+BOOL WINAPI WTSStartRemoteControlSessionExW(LPWSTR pTargetServerName, ULONG TargetLogonId,
+ BYTE HotkeyVk, USHORT HotkeyModifiers, DWORD flags)
+{
+ WTSAPI_STUB_CALL_BOOL(StartRemoteControlSessionExW, pTargetServerName, TargetLogonId, HotkeyVk,
+ HotkeyModifiers, flags);
+}
+
+BOOL WINAPI WTSStartRemoteControlSessionExA(LPSTR pTargetServerName, ULONG TargetLogonId,
+ BYTE HotkeyVk, USHORT HotkeyModifiers, DWORD flags)
+{
+ WTSAPI_STUB_CALL_BOOL(StartRemoteControlSessionExA, pTargetServerName, TargetLogonId, HotkeyVk,
+ HotkeyModifiers, flags);
+}
+
+BOOL WINAPI WTSStopRemoteControlSession(ULONG LogonId)
+{
+ WTSAPI_STUB_CALL_BOOL(StopRemoteControlSession, LogonId);
+}
+
+BOOL WINAPI WTSConnectSessionW(ULONG LogonId, ULONG TargetLogonId, PWSTR pPassword, BOOL bWait)
+{
+ WTSAPI_STUB_CALL_BOOL(ConnectSessionW, LogonId, TargetLogonId, pPassword, bWait);
+}
+
+BOOL WINAPI WTSConnectSessionA(ULONG LogonId, ULONG TargetLogonId, PSTR pPassword, BOOL bWait)
+{
+ WTSAPI_STUB_CALL_BOOL(ConnectSessionA, LogonId, TargetLogonId, pPassword, bWait);
+}
+
+BOOL WINAPI WTSEnumerateServersW(LPWSTR pDomainName, DWORD Reserved, DWORD Version,
+ PWTS_SERVER_INFOW* ppServerInfo, DWORD* pCount)
+{
+ WTSAPI_STUB_CALL_BOOL(EnumerateServersW, pDomainName, Reserved, Version, ppServerInfo, pCount);
+}
+
+BOOL WINAPI WTSEnumerateServersA(LPSTR pDomainName, DWORD Reserved, DWORD Version,
+ PWTS_SERVER_INFOA* ppServerInfo, DWORD* pCount)
+{
+ WTSAPI_STUB_CALL_BOOL(EnumerateServersA, pDomainName, Reserved, Version, ppServerInfo, pCount);
+}
+
+HANDLE WINAPI WTSOpenServerW(LPWSTR pServerName)
+{
+ WTSAPI_STUB_CALL_HANDLE(OpenServerW, pServerName);
+}
+
+HANDLE WINAPI WTSOpenServerA(LPSTR pServerName)
+{
+ WTSAPI_STUB_CALL_HANDLE(OpenServerA, pServerName);
+}
+
+HANDLE WINAPI WTSOpenServerExW(LPWSTR pServerName)
+{
+ WTSAPI_STUB_CALL_HANDLE(OpenServerExW, pServerName);
+}
+
+HANDLE WINAPI WTSOpenServerExA(LPSTR pServerName)
+{
+ WTSAPI_STUB_CALL_HANDLE(OpenServerExA, pServerName);
+}
+
+VOID WINAPI WTSCloseServer(HANDLE hServer)
+{
+ WTSAPI_STUB_CALL_VOID(CloseServer, hServer);
+}
+
+BOOL WINAPI WTSEnumerateSessionsW(HANDLE hServer, DWORD Reserved, DWORD Version,
+ PWTS_SESSION_INFOW* ppSessionInfo, DWORD* pCount)
+{
+ WTSAPI_STUB_CALL_BOOL(EnumerateSessionsW, hServer, Reserved, Version, ppSessionInfo, pCount);
+}
+
+BOOL WINAPI WTSEnumerateSessionsA(HANDLE hServer, DWORD Reserved, DWORD Version,
+ PWTS_SESSION_INFOA* ppSessionInfo, DWORD* pCount)
+{
+ WTSAPI_STUB_CALL_BOOL(EnumerateSessionsA, hServer, Reserved, Version, ppSessionInfo, pCount);
+}
+
+BOOL WINAPI WTSEnumerateSessionsExW(HANDLE hServer, DWORD* pLevel, DWORD Filter,
+ PWTS_SESSION_INFO_1W* ppSessionInfo, DWORD* pCount)
+{
+ WTSAPI_STUB_CALL_BOOL(EnumerateSessionsExW, hServer, pLevel, Filter, ppSessionInfo, pCount);
+}
+
+BOOL WINAPI WTSEnumerateSessionsExA(HANDLE hServer, DWORD* pLevel, DWORD Filter,
+ PWTS_SESSION_INFO_1A* ppSessionInfo, DWORD* pCount)
+{
+ WTSAPI_STUB_CALL_BOOL(EnumerateSessionsExA, hServer, pLevel, Filter, ppSessionInfo, pCount);
+}
+
+BOOL WINAPI WTSEnumerateProcessesW(HANDLE hServer, DWORD Reserved, DWORD Version,
+ PWTS_PROCESS_INFOW* ppProcessInfo, DWORD* pCount)
+{
+ WTSAPI_STUB_CALL_BOOL(EnumerateProcessesW, hServer, Reserved, Version, ppProcessInfo, pCount);
+}
+
+BOOL WINAPI WTSEnumerateProcessesA(HANDLE hServer, DWORD Reserved, DWORD Version,
+ PWTS_PROCESS_INFOA* ppProcessInfo, DWORD* pCount)
+{
+ WTSAPI_STUB_CALL_BOOL(EnumerateProcessesA, hServer, Reserved, Version, ppProcessInfo, pCount);
+}
+
+BOOL WINAPI WTSTerminateProcess(HANDLE hServer, DWORD ProcessId, DWORD ExitCode)
+{
+ WTSAPI_STUB_CALL_BOOL(TerminateProcess, hServer, ProcessId, ExitCode);
+}
+
+BOOL WINAPI WTSQuerySessionInformationW(HANDLE hServer, DWORD SessionId,
+ WTS_INFO_CLASS WTSInfoClass, LPWSTR* ppBuffer,
+ DWORD* pBytesReturned)
+{
+ WTSAPI_STUB_CALL_BOOL(QuerySessionInformationW, hServer, SessionId, WTSInfoClass, ppBuffer,
+ pBytesReturned);
+}
+
+BOOL WINAPI WTSQuerySessionInformationA(HANDLE hServer, DWORD SessionId,
+ WTS_INFO_CLASS WTSInfoClass, LPSTR* ppBuffer,
+ DWORD* pBytesReturned)
+{
+ WTSAPI_STUB_CALL_BOOL(QuerySessionInformationA, hServer, SessionId, WTSInfoClass, ppBuffer,
+ pBytesReturned);
+}
+
+BOOL WINAPI WTSQueryUserConfigW(LPWSTR pServerName, LPWSTR pUserName,
+ WTS_CONFIG_CLASS WTSConfigClass, LPWSTR* ppBuffer,
+ DWORD* pBytesReturned)
+{
+ WTSAPI_STUB_CALL_BOOL(QueryUserConfigW, pServerName, pUserName, WTSConfigClass, ppBuffer,
+ pBytesReturned);
+}
+
+BOOL WINAPI WTSQueryUserConfigA(LPSTR pServerName, LPSTR pUserName, WTS_CONFIG_CLASS WTSConfigClass,
+ LPSTR* ppBuffer, DWORD* pBytesReturned)
+{
+ WTSAPI_STUB_CALL_BOOL(QueryUserConfigA, pServerName, pUserName, WTSConfigClass, ppBuffer,
+ pBytesReturned);
+}
+
+BOOL WINAPI WTSSetUserConfigW(LPWSTR pServerName, LPWSTR pUserName, WTS_CONFIG_CLASS WTSConfigClass,
+ LPWSTR pBuffer, DWORD DataLength)
+{
+ WTSAPI_STUB_CALL_BOOL(SetUserConfigW, pServerName, pUserName, WTSConfigClass, pBuffer,
+ DataLength);
+}
+
+BOOL WINAPI WTSSetUserConfigA(LPSTR pServerName, LPSTR pUserName, WTS_CONFIG_CLASS WTSConfigClass,
+ LPSTR pBuffer, DWORD DataLength)
+{
+ WTSAPI_STUB_CALL_BOOL(SetUserConfigA, pServerName, pUserName, WTSConfigClass, pBuffer,
+ DataLength);
+}
+
+BOOL WINAPI WTSSendMessageW(HANDLE hServer, DWORD SessionId, LPWSTR pTitle, DWORD TitleLength,
+ LPWSTR pMessage, DWORD MessageLength, DWORD Style, DWORD Timeout,
+ DWORD* pResponse, BOOL bWait)
+{
+ WTSAPI_STUB_CALL_BOOL(SendMessageW, hServer, SessionId, pTitle, TitleLength, pMessage,
+ MessageLength, Style, Timeout, pResponse, bWait);
+}
+
+BOOL WINAPI WTSSendMessageA(HANDLE hServer, DWORD SessionId, LPSTR pTitle, DWORD TitleLength,
+ LPSTR pMessage, DWORD MessageLength, DWORD Style, DWORD Timeout,
+ DWORD* pResponse, BOOL bWait)
+{
+ WTSAPI_STUB_CALL_BOOL(SendMessageA, hServer, SessionId, pTitle, TitleLength, pMessage,
+ MessageLength, Style, Timeout, pResponse, bWait);
+}
+
+BOOL WINAPI WTSDisconnectSession(HANDLE hServer, DWORD SessionId, BOOL bWait)
+{
+ WTSAPI_STUB_CALL_BOOL(DisconnectSession, hServer, SessionId, bWait);
+}
+
+BOOL WINAPI WTSLogoffSession(HANDLE hServer, DWORD SessionId, BOOL bWait)
+{
+ WTSAPI_STUB_CALL_BOOL(LogoffSession, hServer, SessionId, bWait);
+}
+
+BOOL WINAPI WTSShutdownSystem(HANDLE hServer, DWORD ShutdownFlag)
+{
+ WTSAPI_STUB_CALL_BOOL(ShutdownSystem, hServer, ShutdownFlag);
+}
+
+BOOL WINAPI WTSWaitSystemEvent(HANDLE hServer, DWORD EventMask, DWORD* pEventFlags)
+{
+ WTSAPI_STUB_CALL_BOOL(WaitSystemEvent, hServer, EventMask, pEventFlags);
+}
+
+HANDLE WINAPI WTSVirtualChannelOpen(HANDLE hServer, DWORD SessionId, LPSTR pVirtualName)
+{
+ WTSAPI_STUB_CALL_HANDLE(VirtualChannelOpen, hServer, SessionId, pVirtualName);
+}
+
+HANDLE WINAPI WTSVirtualChannelOpenEx(DWORD SessionId, LPSTR pVirtualName, DWORD flags)
+{
+ WTSAPI_STUB_CALL_HANDLE(VirtualChannelOpenEx, SessionId, pVirtualName, flags);
+}
+
+BOOL WINAPI WTSVirtualChannelClose(HANDLE hChannelHandle)
+{
+ WTSAPI_STUB_CALL_BOOL(VirtualChannelClose, hChannelHandle);
+}
+
+BOOL WINAPI WTSVirtualChannelRead(HANDLE hChannelHandle, ULONG TimeOut, PCHAR Buffer,
+ ULONG BufferSize, PULONG pBytesRead)
+{
+ WTSAPI_STUB_CALL_BOOL(VirtualChannelRead, hChannelHandle, TimeOut, Buffer, BufferSize,
+ pBytesRead);
+}
+
+BOOL WINAPI WTSVirtualChannelWrite(HANDLE hChannelHandle, PCHAR Buffer, ULONG Length,
+ PULONG pBytesWritten)
+{
+ WTSAPI_STUB_CALL_BOOL(VirtualChannelWrite, hChannelHandle, Buffer, Length, pBytesWritten);
+}
+
+BOOL WINAPI WTSVirtualChannelPurgeInput(HANDLE hChannelHandle)
+{
+ WTSAPI_STUB_CALL_BOOL(VirtualChannelPurgeInput, hChannelHandle);
+}
+
+BOOL WINAPI WTSVirtualChannelPurgeOutput(HANDLE hChannelHandle)
+{
+ WTSAPI_STUB_CALL_BOOL(VirtualChannelPurgeOutput, hChannelHandle);
+}
+
+BOOL WINAPI WTSVirtualChannelQuery(HANDLE hChannelHandle, WTS_VIRTUAL_CLASS WtsVirtualClass,
+ PVOID* ppBuffer, DWORD* pBytesReturned)
+{
+ WTSAPI_STUB_CALL_BOOL(VirtualChannelQuery, hChannelHandle, WtsVirtualClass, ppBuffer,
+ pBytesReturned);
+}
+
+VOID WINAPI WTSFreeMemory(PVOID pMemory)
+{
+ WTSAPI_STUB_CALL_VOID(FreeMemory, pMemory);
+}
+
+BOOL WINAPI WTSFreeMemoryExW(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory, ULONG NumberOfEntries)
+{
+ WTSAPI_STUB_CALL_BOOL(FreeMemoryExW, WTSTypeClass, pMemory, NumberOfEntries);
+}
+
+BOOL WINAPI WTSFreeMemoryExA(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory, ULONG NumberOfEntries)
+{
+ WTSAPI_STUB_CALL_BOOL(FreeMemoryExA, WTSTypeClass, pMemory, NumberOfEntries);
+}
+
+BOOL WINAPI WTSRegisterSessionNotification(HWND hWnd, DWORD dwFlags)
+{
+ WTSAPI_STUB_CALL_BOOL(RegisterSessionNotification, hWnd, dwFlags);
+}
+
+BOOL WINAPI WTSUnRegisterSessionNotification(HWND hWnd)
+{
+ WTSAPI_STUB_CALL_BOOL(UnRegisterSessionNotification, hWnd);
+}
+
+BOOL WINAPI WTSRegisterSessionNotificationEx(HANDLE hServer, HWND hWnd, DWORD dwFlags)
+{
+ WTSAPI_STUB_CALL_BOOL(RegisterSessionNotificationEx, hServer, hWnd, dwFlags);
+}
+
+BOOL WINAPI WTSUnRegisterSessionNotificationEx(HANDLE hServer, HWND hWnd)
+{
+ WTSAPI_STUB_CALL_BOOL(UnRegisterSessionNotificationEx, hServer, hWnd);
+}
+
+BOOL WINAPI WTSQueryUserToken(ULONG SessionId, PHANDLE phToken)
+{
+ WTSAPI_STUB_CALL_BOOL(QueryUserToken, SessionId, phToken);
+}
+
+BOOL WINAPI WTSEnumerateProcessesExW(HANDLE hServer, DWORD* pLevel, DWORD SessionId,
+ LPWSTR* ppProcessInfo, DWORD* pCount)
+{
+ WTSAPI_STUB_CALL_BOOL(EnumerateProcessesExW, hServer, pLevel, SessionId, ppProcessInfo, pCount);
+}
+
+BOOL WINAPI WTSEnumerateProcessesExA(HANDLE hServer, DWORD* pLevel, DWORD SessionId,
+ LPSTR* ppProcessInfo, DWORD* pCount)
+{
+ WTSAPI_STUB_CALL_BOOL(EnumerateProcessesExA, hServer, pLevel, SessionId, ppProcessInfo, pCount);
+}
+
+BOOL WINAPI WTSEnumerateListenersW(HANDLE hServer, PVOID pReserved, DWORD Reserved,
+ PWTSLISTENERNAMEW pListeners, DWORD* pCount)
+{
+ WTSAPI_STUB_CALL_BOOL(EnumerateListenersW, hServer, pReserved, Reserved, pListeners, pCount);
+}
+
+BOOL WINAPI WTSEnumerateListenersA(HANDLE hServer, PVOID pReserved, DWORD Reserved,
+ PWTSLISTENERNAMEA pListeners, DWORD* pCount)
+{
+ WTSAPI_STUB_CALL_BOOL(EnumerateListenersA, hServer, pReserved, Reserved, pListeners, pCount);
+}
+
+BOOL WINAPI WTSQueryListenerConfigW(HANDLE hServer, PVOID pReserved, DWORD Reserved,
+ LPWSTR pListenerName, PWTSLISTENERCONFIGW pBuffer)
+{
+ WTSAPI_STUB_CALL_BOOL(QueryListenerConfigW, hServer, pReserved, Reserved, pListenerName,
+ pBuffer);
+}
+
+BOOL WINAPI WTSQueryListenerConfigA(HANDLE hServer, PVOID pReserved, DWORD Reserved,
+ LPSTR pListenerName, PWTSLISTENERCONFIGA pBuffer)
+{
+ WTSAPI_STUB_CALL_BOOL(QueryListenerConfigA, hServer, pReserved, Reserved, pListenerName,
+ pBuffer);
+}
+
+BOOL WINAPI WTSCreateListenerW(HANDLE hServer, PVOID pReserved, DWORD Reserved,
+ LPWSTR pListenerName, PWTSLISTENERCONFIGW pBuffer, DWORD flag)
+{
+ WTSAPI_STUB_CALL_BOOL(CreateListenerW, hServer, pReserved, Reserved, pListenerName, pBuffer,
+ flag);
+}
+
+BOOL WINAPI WTSCreateListenerA(HANDLE hServer, PVOID pReserved, DWORD Reserved, LPSTR pListenerName,
+ PWTSLISTENERCONFIGA pBuffer, DWORD flag)
+{
+ WTSAPI_STUB_CALL_BOOL(CreateListenerA, hServer, pReserved, Reserved, pListenerName, pBuffer,
+ flag);
+}
+
+BOOL WINAPI WTSSetListenerSecurityW(HANDLE hServer, PVOID pReserved, DWORD Reserved,
+ LPWSTR pListenerName, SECURITY_INFORMATION SecurityInformation,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor)
+{
+ WTSAPI_STUB_CALL_BOOL(SetListenerSecurityW, hServer, pReserved, Reserved, pListenerName,
+ SecurityInformation, pSecurityDescriptor);
+}
+
+BOOL WINAPI WTSSetListenerSecurityA(HANDLE hServer, PVOID pReserved, DWORD Reserved,
+ LPSTR pListenerName, SECURITY_INFORMATION SecurityInformation,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor)
+{
+ WTSAPI_STUB_CALL_BOOL(SetListenerSecurityA, hServer, pReserved, Reserved, pListenerName,
+ SecurityInformation, pSecurityDescriptor);
+}
+
+BOOL WINAPI WTSGetListenerSecurityW(HANDLE hServer, PVOID pReserved, DWORD Reserved,
+ LPWSTR pListenerName, SECURITY_INFORMATION SecurityInformation,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD nLength,
+ LPDWORD lpnLengthNeeded)
+{
+ WTSAPI_STUB_CALL_BOOL(GetListenerSecurityW, hServer, pReserved, Reserved, pListenerName,
+ SecurityInformation, pSecurityDescriptor, nLength, lpnLengthNeeded);
+}
+
+BOOL WINAPI WTSGetListenerSecurityA(HANDLE hServer, PVOID pReserved, DWORD Reserved,
+ LPSTR pListenerName, SECURITY_INFORMATION SecurityInformation,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD nLength,
+ LPDWORD lpnLengthNeeded)
+{
+ WTSAPI_STUB_CALL_BOOL(GetListenerSecurityA, hServer, pReserved, Reserved, pListenerName,
+ SecurityInformation, pSecurityDescriptor, nLength, lpnLengthNeeded);
+}
+
+BOOL CDECL WTSEnableChildSessions(BOOL bEnable)
+{
+ WTSAPI_STUB_CALL_BOOL(EnableChildSessions, bEnable);
+}
+
+BOOL CDECL WTSIsChildSessionsEnabled(PBOOL pbEnabled)
+{
+ WTSAPI_STUB_CALL_BOOL(IsChildSessionsEnabled, pbEnabled);
+}
+
+BOOL CDECL WTSGetChildSessionId(PULONG pSessionId)
+{
+ WTSAPI_STUB_CALL_BOOL(GetChildSessionId, pSessionId);
+}
+
+BOOL CDECL WTSLogonUser(HANDLE hServer, LPCSTR username, LPCSTR password, LPCSTR domain)
+{
+ WTSAPI_STUB_CALL_BOOL(LogonUser, hServer, username, password, domain);
+}
+
+BOOL CDECL WTSLogoffUser(HANDLE hServer)
+{
+ WTSAPI_STUB_CALL_BOOL(LogoffUser, hServer);
+}
+
+#ifndef _WIN32
+
+/**
+ * WTSGetActiveConsoleSessionId is declared in WinBase.h and exported by kernel32.dll
+ */
+
+DWORD WINAPI WTSGetActiveConsoleSessionId(void)
+{
+ InitOnceExecuteOnce(&wtsapiInitOnce, InitializeWtsApiStubs, NULL, NULL);
+
+ if (!g_WtsApi || !g_WtsApi->pGetActiveConsoleSessionId)
+ return 0xFFFFFFFF;
+
+ return g_WtsApi->pGetActiveConsoleSessionId();
+}
+
+#endif
+
+const CHAR* WTSErrorToString(UINT error)
+{
+ switch (error)
+ {
+ case CHANNEL_RC_OK:
+ return "CHANNEL_RC_OK";
+
+ case CHANNEL_RC_ALREADY_INITIALIZED:
+ return "CHANNEL_RC_ALREADY_INITIALIZED";
+
+ case CHANNEL_RC_NOT_INITIALIZED:
+ return "CHANNEL_RC_NOT_INITIALIZED";
+
+ case CHANNEL_RC_ALREADY_CONNECTED:
+ return "CHANNEL_RC_ALREADY_CONNECTED";
+
+ case CHANNEL_RC_NOT_CONNECTED:
+ return "CHANNEL_RC_NOT_CONNECTED";
+
+ case CHANNEL_RC_TOO_MANY_CHANNELS:
+ return "CHANNEL_RC_TOO_MANY_CHANNELS";
+
+ case CHANNEL_RC_BAD_CHANNEL:
+ return "CHANNEL_RC_BAD_CHANNEL";
+
+ case CHANNEL_RC_BAD_CHANNEL_HANDLE:
+ return "CHANNEL_RC_BAD_CHANNEL_HANDLE";
+
+ case CHANNEL_RC_NO_BUFFER:
+ return "CHANNEL_RC_NO_BUFFER";
+
+ case CHANNEL_RC_BAD_INIT_HANDLE:
+ return "CHANNEL_RC_BAD_INIT_HANDLE";
+
+ case CHANNEL_RC_NOT_OPEN:
+ return "CHANNEL_RC_NOT_OPEN";
+
+ case CHANNEL_RC_BAD_PROC:
+ return "CHANNEL_RC_BAD_PROC";
+
+ case CHANNEL_RC_NO_MEMORY:
+ return "CHANNEL_RC_NO_MEMORY";
+
+ case CHANNEL_RC_UNKNOWN_CHANNEL_NAME:
+ return "CHANNEL_RC_UNKNOWN_CHANNEL_NAME";
+
+ case CHANNEL_RC_ALREADY_OPEN:
+ return "CHANNEL_RC_ALREADY_OPEN";
+
+ case CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY:
+ return "CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY";
+
+ case CHANNEL_RC_NULL_DATA:
+ return "CHANNEL_RC_NULL_DATA";
+
+ case CHANNEL_RC_ZERO_LENGTH:
+ return "CHANNEL_RC_ZERO_LENGTH";
+
+ case CHANNEL_RC_INVALID_INSTANCE:
+ return "CHANNEL_RC_INVALID_INSTANCE";
+
+ case CHANNEL_RC_UNSUPPORTED_VERSION:
+ return "CHANNEL_RC_UNSUPPORTED_VERSION";
+
+ case CHANNEL_RC_INITIALIZATION_ERROR:
+ return "CHANNEL_RC_INITIALIZATION_ERROR";
+
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const CHAR* WTSSessionStateToString(WTS_CONNECTSTATE_CLASS state)
+{
+ switch (state)
+ {
+ case WTSActive:
+ return "WTSActive";
+ case WTSConnected:
+ return "WTSConnected";
+ case WTSConnectQuery:
+ return "WTSConnectQuery";
+ case WTSShadow:
+ return "WTSShadow";
+ case WTSDisconnected:
+ return "WTSDisconnected";
+ case WTSIdle:
+ return "WTSIdle";
+ case WTSListen:
+ return "WTSListen";
+ case WTSReset:
+ return "WTSReset";
+ case WTSDown:
+ return "WTSDown";
+ case WTSInit:
+ return "WTSInit";
+ }
+ return "INVALID_STATE";
+}
+
+BOOL WTSRegisterWtsApiFunctionTable(const WtsApiFunctionTable* table)
+{
+ /* Use InitOnceExecuteOnce here as well - otherwise a table set with this
+ function is overriden on the first use of a WTS* API call (due to
+ wtsapiInitOnce not being set). */
+ union
+ {
+ const void* cpv;
+ void* pv;
+ } cnv;
+ cnv.cpv = table;
+ InitOnceExecuteOnce(&wtsapiInitOnce, InitializeWtsApiStubs, cnv.pv, NULL);
+ if (!g_WtsApi)
+ return FALSE;
+ return TRUE;
+}
+
+static BOOL LoadAndInitialize(char* library)
+{
+ INIT_WTSAPI_FN pInitWtsApi = NULL;
+ g_WtsApiModule = LoadLibraryX(library);
+
+ if (!g_WtsApiModule)
+ return FALSE;
+
+ pInitWtsApi = (INIT_WTSAPI_FN)GetProcAddress(g_WtsApiModule, "InitWtsApi");
+
+ if (!pInitWtsApi)
+ {
+ return FALSE;
+ }
+
+ g_WtsApi = pInitWtsApi();
+ return TRUE;
+}
+
+static void InitializeWtsApiStubs_Env(void)
+{
+ DWORD nSize = 0;
+ char* env = NULL;
+ LPCSTR wts = "WTSAPI_LIBRARY";
+
+ if (g_WtsApi)
+ return;
+
+ nSize = GetEnvironmentVariableA(wts, NULL, 0);
+
+ if (!nSize)
+ return;
+
+ env = (LPSTR)malloc(nSize);
+ if (env)
+ {
+ if (GetEnvironmentVariableA(wts, env, nSize) == nSize - 1)
+ LoadAndInitialize(env);
+ free(env);
+ }
+}
+
+#define FREERDS_LIBRARY_NAME "libfreerds-fdsapi.so"
+
+static void InitializeWtsApiStubs_FreeRDS(void)
+{
+ wIniFile* ini = NULL;
+ const char* prefix = NULL;
+ const char* libdir = NULL;
+
+ if (g_WtsApi)
+ return;
+
+ ini = IniFile_New();
+
+ if (IniFile_ReadFile(ini, "/var/run/freerds.instance") < 0)
+ {
+ IniFile_Free(ini);
+ WLog_ERR(TAG, "failed to parse freerds.instance");
+ LoadAndInitialize(FREERDS_LIBRARY_NAME);
+ return;
+ }
+
+ prefix = IniFile_GetKeyValueString(ini, "FreeRDS", "prefix");
+ libdir = IniFile_GetKeyValueString(ini, "FreeRDS", "libdir");
+ WLog_INFO(TAG, "FreeRDS (prefix / libdir): %s / %s", prefix, libdir);
+
+ if (prefix && libdir)
+ {
+ char* prefix_libdir = NULL;
+ char* wtsapi_library = NULL;
+ prefix_libdir = GetCombinedPath(prefix, libdir);
+ wtsapi_library = GetCombinedPath(prefix_libdir, FREERDS_LIBRARY_NAME);
+
+ if (wtsapi_library)
+ {
+ LoadAndInitialize(wtsapi_library);
+ }
+
+ free(prefix_libdir);
+ free(wtsapi_library);
+ }
+
+ IniFile_Free(ini);
+}
+
+static BOOL CALLBACK InitializeWtsApiStubs(PINIT_ONCE once, PVOID param, PVOID* context)
+{
+ WINPR_UNUSED(once);
+ WINPR_UNUSED(context);
+ if (param)
+ {
+ g_WtsApi = (const WtsApiFunctionTable*)param;
+ return TRUE;
+ }
+
+ InitializeWtsApiStubs_Env();
+
+#ifdef _WIN32
+ WtsApi32_InitializeWtsApi();
+#endif
+
+ if (!g_WtsApi)
+ InitializeWtsApiStubs_FreeRDS();
+
+ return TRUE;
+}
diff --git a/winpr/libwinpr/wtsapi/wtsapi_win32.c b/winpr/libwinpr/wtsapi/wtsapi_win32.c
new file mode 100644
index 0000000..cac1624
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/wtsapi_win32.c
@@ -0,0 +1,808 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Windows Terminal Services API
+ *
+ * Copyright 2013-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/io.h>
+#include <winpr/nt.h>
+#include <winpr/library.h>
+
+#include <winpr/wtsapi.h>
+
+#include "wtsapi_win32.h"
+
+#include "../log.h"
+
+#include <winternl.h>
+
+#pragma comment(lib, "ntdll.lib")
+
+#define WTSAPI_CHANNEL_MAGIC 0x44484356
+#define TAG WINPR_TAG("wtsapi")
+
+typedef struct
+{
+ UINT32 magic;
+ HANDLE hServer;
+ DWORD SessionId;
+ HANDLE hFile;
+ HANDLE hEvent;
+ char* VirtualName;
+
+ DWORD flags;
+ BYTE* chunk;
+ BOOL dynamic;
+ BOOL readSync;
+ BOOL readAsync;
+ BOOL readDone;
+ UINT32 readSize;
+ UINT32 readOffset;
+ BYTE* readBuffer;
+ BOOL showProtocol;
+ BOOL waitObjectMode;
+ OVERLAPPED overlapped;
+ CHANNEL_PDU_HEADER* header;
+} WTSAPI_CHANNEL;
+
+static BOOL g_Initialized = FALSE;
+static HMODULE g_WinStaModule = NULL;
+
+typedef HANDLE(WINAPI* fnWinStationVirtualOpen)(HANDLE hServer, DWORD SessionId,
+ LPSTR pVirtualName);
+typedef HANDLE(WINAPI* fnWinStationVirtualOpenEx)(HANDLE hServer, DWORD SessionId,
+ LPSTR pVirtualName, DWORD flags);
+
+static fnWinStationVirtualOpen pfnWinStationVirtualOpen = NULL;
+static fnWinStationVirtualOpenEx pfnWinStationVirtualOpenEx = NULL;
+
+BOOL WINAPI Win32_WTSVirtualChannelClose(HANDLE hChannel);
+
+/**
+ * NOTE !!
+ * An application using the WinPR wtsapi frees memory via WTSFreeMemory, which
+ * might be mapped to Win32_WTSFreeMemory. Latter does not know if the passed
+ * pointer was allocated by a function in wtsapi32.dll or in some internal
+ * code below. The WTSFreeMemory implementation in all Windows wtsapi32.dll
+ * versions up to Windows 10 uses LocalFree since all its allocating functions
+ * use LocalAlloc() internally.
+ * For that reason we also have to use LocalAlloc() for any memory returned by
+ * our WinPR wtsapi functions.
+ *
+ * To be safe we only use the _wts_malloc, _wts_calloc, _wts_free wrappers
+ * for memory managment the code below.
+ */
+
+static void* _wts_malloc(size_t size)
+{
+#ifdef _UWP
+ return malloc(size);
+#else
+ return (PVOID)LocalAlloc(LMEM_FIXED, size);
+#endif
+}
+
+static void* _wts_calloc(size_t nmemb, size_t size)
+{
+#ifdef _UWP
+ return calloc(nmemb, size);
+#else
+ return (PVOID)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, nmemb * size);
+#endif
+}
+
+static void _wts_free(void* ptr)
+{
+#ifdef _UWP
+ free(ptr);
+#else
+ LocalFree((HLOCAL)ptr);
+#endif
+}
+
+BOOL Win32_WTSVirtualChannelReadAsync(WTSAPI_CHANNEL* pChannel)
+{
+ BOOL status = TRUE;
+ DWORD numBytes = 0;
+
+ if (pChannel->readAsync)
+ return TRUE;
+
+ ZeroMemory(&(pChannel->overlapped), sizeof(OVERLAPPED));
+ pChannel->overlapped.hEvent = pChannel->hEvent;
+ ResetEvent(pChannel->hEvent);
+
+ if (pChannel->showProtocol)
+ {
+ ZeroMemory(pChannel->header, sizeof(CHANNEL_PDU_HEADER));
+
+ status = ReadFile(pChannel->hFile, pChannel->header, sizeof(CHANNEL_PDU_HEADER), &numBytes,
+ &(pChannel->overlapped));
+ }
+ else
+ {
+ status = ReadFile(pChannel->hFile, pChannel->chunk, CHANNEL_CHUNK_LENGTH, &numBytes,
+ &(pChannel->overlapped));
+
+ if (status)
+ {
+ pChannel->readOffset = 0;
+ pChannel->header->length = numBytes;
+
+ pChannel->readDone = TRUE;
+ SetEvent(pChannel->hEvent);
+
+ return TRUE;
+ }
+ }
+
+ if (status)
+ {
+ WLog_ERR(TAG, "Unexpected ReadFile status: %" PRId32 " numBytes: %" PRIu32 "", status,
+ numBytes);
+ return FALSE; /* ReadFile should return FALSE and set ERROR_IO_PENDING */
+ }
+
+ if (GetLastError() != ERROR_IO_PENDING)
+ {
+ WLog_ERR(TAG, "ReadFile: GetLastError() = %" PRIu32 "", GetLastError());
+ return FALSE;
+ }
+
+ pChannel->readAsync = TRUE;
+
+ return TRUE;
+}
+
+HANDLE WINAPI Win32_WTSVirtualChannelOpen_Internal(HANDLE hServer, DWORD SessionId,
+ LPSTR pVirtualName, DWORD flags)
+{
+ HANDLE hFile;
+ HANDLE hChannel;
+ WTSAPI_CHANNEL* pChannel;
+ size_t virtualNameLen;
+
+ virtualNameLen = pVirtualName ? strlen(pVirtualName) : 0;
+
+ if (!virtualNameLen)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return NULL;
+ }
+
+ if (!pfnWinStationVirtualOpenEx)
+ {
+ SetLastError(ERROR_INVALID_FUNCTION);
+ return NULL;
+ }
+
+ hFile = pfnWinStationVirtualOpenEx(hServer, SessionId, pVirtualName, flags);
+
+ if (!hFile)
+ return NULL;
+
+ pChannel = (WTSAPI_CHANNEL*)_wts_calloc(1, sizeof(WTSAPI_CHANNEL));
+
+ if (!pChannel)
+ {
+ CloseHandle(hFile);
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return NULL;
+ }
+
+ hChannel = (HANDLE)pChannel;
+ pChannel->magic = WTSAPI_CHANNEL_MAGIC;
+ pChannel->hServer = hServer;
+ pChannel->SessionId = SessionId;
+ pChannel->hFile = hFile;
+ pChannel->VirtualName = _wts_calloc(1, virtualNameLen + 1);
+ if (!pChannel->VirtualName)
+ {
+ CloseHandle(hFile);
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ _wts_free(pChannel);
+ return NULL;
+ }
+ memcpy(pChannel->VirtualName, pVirtualName, virtualNameLen);
+
+ pChannel->flags = flags;
+ pChannel->dynamic = (flags & WTS_CHANNEL_OPTION_DYNAMIC) ? TRUE : FALSE;
+
+ pChannel->showProtocol = pChannel->dynamic;
+
+ pChannel->readSize = CHANNEL_PDU_LENGTH;
+ pChannel->readBuffer = (BYTE*)_wts_malloc(pChannel->readSize);
+
+ pChannel->header = (CHANNEL_PDU_HEADER*)pChannel->readBuffer;
+ pChannel->chunk = &(pChannel->readBuffer[sizeof(CHANNEL_PDU_HEADER)]);
+
+ pChannel->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ pChannel->overlapped.hEvent = pChannel->hEvent;
+
+ if (!pChannel->hEvent || !pChannel->VirtualName || !pChannel->readBuffer)
+ {
+ Win32_WTSVirtualChannelClose(hChannel);
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return NULL;
+ }
+
+ return hChannel;
+}
+
+HANDLE WINAPI Win32_WTSVirtualChannelOpen(HANDLE hServer, DWORD SessionId, LPSTR pVirtualName)
+{
+ return Win32_WTSVirtualChannelOpen_Internal(hServer, SessionId, pVirtualName, 0);
+}
+
+HANDLE WINAPI Win32_WTSVirtualChannelOpenEx(DWORD SessionId, LPSTR pVirtualName, DWORD flags)
+{
+ return Win32_WTSVirtualChannelOpen_Internal(0, SessionId, pVirtualName, flags);
+}
+
+BOOL WINAPI Win32_WTSVirtualChannelClose(HANDLE hChannel)
+{
+ BOOL status = TRUE;
+ WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannel;
+
+ if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ if (pChannel->hFile)
+ {
+ if (pChannel->readAsync)
+ {
+ CancelIo(pChannel->hFile);
+ pChannel->readAsync = FALSE;
+ }
+
+ status = CloseHandle(pChannel->hFile);
+ pChannel->hFile = NULL;
+ }
+
+ if (pChannel->hEvent)
+ {
+ CloseHandle(pChannel->hEvent);
+ pChannel->hEvent = NULL;
+ }
+
+ if (pChannel->VirtualName)
+ {
+ _wts_free(pChannel->VirtualName);
+ pChannel->VirtualName = NULL;
+ }
+
+ if (pChannel->readBuffer)
+ {
+ _wts_free(pChannel->readBuffer);
+ pChannel->readBuffer = NULL;
+ }
+
+ pChannel->magic = 0;
+ _wts_free(pChannel);
+
+ return status;
+}
+
+BOOL WINAPI Win32_WTSVirtualChannelRead_Static(WTSAPI_CHANNEL* pChannel, DWORD dwMilliseconds,
+ LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
+ LPDWORD lpNumberOfBytesTransferred)
+{
+ if (pChannel->readDone)
+ {
+ DWORD numBytesRead = 0;
+ DWORD numBytesToRead = 0;
+
+ *lpNumberOfBytesTransferred = 0;
+
+ numBytesToRead = nNumberOfBytesToRead;
+
+ if (numBytesToRead > (pChannel->header->length - pChannel->readOffset))
+ numBytesToRead = (pChannel->header->length - pChannel->readOffset);
+
+ CopyMemory(lpBuffer, &(pChannel->chunk[pChannel->readOffset]), numBytesToRead);
+ *lpNumberOfBytesTransferred += numBytesToRead;
+ pChannel->readOffset += numBytesToRead;
+
+ if (pChannel->readOffset != pChannel->header->length)
+ {
+ SetLastError(ERROR_MORE_DATA);
+ return FALSE;
+ }
+ else
+ {
+ pChannel->readDone = FALSE;
+ Win32_WTSVirtualChannelReadAsync(pChannel);
+ }
+
+ return TRUE;
+ }
+ else if (pChannel->readSync)
+ {
+ BOOL bSuccess;
+ OVERLAPPED overlapped = { 0 };
+ DWORD numBytesRead = 0;
+ DWORD numBytesToRead = 0;
+
+ *lpNumberOfBytesTransferred = 0;
+
+ numBytesToRead = nNumberOfBytesToRead;
+
+ if (numBytesToRead > (pChannel->header->length - pChannel->readOffset))
+ numBytesToRead = (pChannel->header->length - pChannel->readOffset);
+
+ if (ReadFile(pChannel->hFile, lpBuffer, numBytesToRead, &numBytesRead, &overlapped))
+ {
+ *lpNumberOfBytesTransferred += numBytesRead;
+ pChannel->readOffset += numBytesRead;
+
+ if (pChannel->readOffset != pChannel->header->length)
+ {
+ SetLastError(ERROR_MORE_DATA);
+ return FALSE;
+ }
+
+ pChannel->readSync = FALSE;
+ Win32_WTSVirtualChannelReadAsync(pChannel);
+
+ return TRUE;
+ }
+
+ if (GetLastError() != ERROR_IO_PENDING)
+ return FALSE;
+
+ bSuccess = GetOverlappedResult(pChannel->hFile, &overlapped, &numBytesRead, TRUE);
+
+ if (!bSuccess)
+ return FALSE;
+
+ *lpNumberOfBytesTransferred += numBytesRead;
+ pChannel->readOffset += numBytesRead;
+
+ if (pChannel->readOffset != pChannel->header->length)
+ {
+ SetLastError(ERROR_MORE_DATA);
+ return FALSE;
+ }
+
+ pChannel->readSync = FALSE;
+ Win32_WTSVirtualChannelReadAsync(pChannel);
+
+ return TRUE;
+ }
+ else if (pChannel->readAsync)
+ {
+ BOOL bSuccess;
+ DWORD numBytesRead = 0;
+ DWORD numBytesToRead = 0;
+
+ *lpNumberOfBytesTransferred = 0;
+
+ if (WaitForSingleObject(pChannel->hEvent, dwMilliseconds) != WAIT_TIMEOUT)
+ {
+ bSuccess =
+ GetOverlappedResult(pChannel->hFile, &(pChannel->overlapped), &numBytesRead, TRUE);
+
+ pChannel->readOffset = 0;
+ pChannel->header->length = numBytesRead;
+
+ if (!bSuccess && (GetLastError() != ERROR_MORE_DATA))
+ return FALSE;
+
+ numBytesToRead = nNumberOfBytesToRead;
+
+ if (numBytesRead < numBytesToRead)
+ {
+ numBytesToRead = numBytesRead;
+ nNumberOfBytesToRead = numBytesRead;
+ }
+
+ CopyMemory(lpBuffer, pChannel->chunk, numBytesToRead);
+ *lpNumberOfBytesTransferred += numBytesToRead;
+ lpBuffer = (BYTE*)lpBuffer + numBytesToRead;
+ nNumberOfBytesToRead -= numBytesToRead;
+ pChannel->readOffset += numBytesToRead;
+
+ pChannel->readAsync = FALSE;
+
+ if (!nNumberOfBytesToRead)
+ {
+ Win32_WTSVirtualChannelReadAsync(pChannel);
+ return TRUE;
+ }
+
+ pChannel->readSync = TRUE;
+
+ numBytesRead = 0;
+
+ bSuccess = Win32_WTSVirtualChannelRead_Static(pChannel, dwMilliseconds, lpBuffer,
+ nNumberOfBytesToRead, &numBytesRead);
+
+ *lpNumberOfBytesTransferred += numBytesRead;
+ return bSuccess;
+ }
+ else
+ {
+ SetLastError(ERROR_IO_INCOMPLETE);
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL WINAPI Win32_WTSVirtualChannelRead_Dynamic(WTSAPI_CHANNEL* pChannel, DWORD dwMilliseconds,
+ LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
+ LPDWORD lpNumberOfBytesTransferred)
+{
+ if (pChannel->readSync)
+ {
+ BOOL bSuccess;
+ OVERLAPPED overlapped = { 0 };
+ DWORD numBytesRead = 0;
+ DWORD numBytesToRead = 0;
+
+ *lpNumberOfBytesTransferred = 0;
+
+ numBytesToRead = nNumberOfBytesToRead;
+
+ if (numBytesToRead > (pChannel->header->length - pChannel->readOffset))
+ numBytesToRead = (pChannel->header->length - pChannel->readOffset);
+
+ if (ReadFile(pChannel->hFile, lpBuffer, numBytesToRead, &numBytesRead, &overlapped))
+ {
+ *lpNumberOfBytesTransferred += numBytesRead;
+ pChannel->readOffset += numBytesRead;
+
+ if (pChannel->readOffset != pChannel->header->length)
+ {
+ SetLastError(ERROR_MORE_DATA);
+ return FALSE;
+ }
+
+ pChannel->readSync = FALSE;
+ Win32_WTSVirtualChannelReadAsync(pChannel);
+
+ return TRUE;
+ }
+
+ if (GetLastError() != ERROR_IO_PENDING)
+ return FALSE;
+
+ bSuccess = GetOverlappedResult(pChannel->hFile, &overlapped, &numBytesRead, TRUE);
+
+ if (!bSuccess)
+ return FALSE;
+
+ *lpNumberOfBytesTransferred += numBytesRead;
+ pChannel->readOffset += numBytesRead;
+
+ if (pChannel->readOffset != pChannel->header->length)
+ {
+ SetLastError(ERROR_MORE_DATA);
+ return FALSE;
+ }
+
+ pChannel->readSync = FALSE;
+ Win32_WTSVirtualChannelReadAsync(pChannel);
+
+ return TRUE;
+ }
+ else if (pChannel->readAsync)
+ {
+ BOOL bSuccess;
+ DWORD numBytesRead = 0;
+
+ *lpNumberOfBytesTransferred = 0;
+
+ if (WaitForSingleObject(pChannel->hEvent, dwMilliseconds) != WAIT_TIMEOUT)
+ {
+ bSuccess =
+ GetOverlappedResult(pChannel->hFile, &(pChannel->overlapped), &numBytesRead, TRUE);
+
+ if (pChannel->showProtocol)
+ {
+ if (numBytesRead != sizeof(CHANNEL_PDU_HEADER))
+ return FALSE;
+
+ if (!bSuccess && (GetLastError() != ERROR_MORE_DATA))
+ return FALSE;
+
+ CopyMemory(lpBuffer, pChannel->header, numBytesRead);
+ *lpNumberOfBytesTransferred += numBytesRead;
+ lpBuffer = (BYTE*)lpBuffer + numBytesRead;
+ nNumberOfBytesToRead -= numBytesRead;
+ }
+
+ pChannel->readAsync = FALSE;
+
+ if (!pChannel->header->length)
+ {
+ Win32_WTSVirtualChannelReadAsync(pChannel);
+ return TRUE;
+ }
+
+ pChannel->readSync = TRUE;
+ pChannel->readOffset = 0;
+
+ if (!nNumberOfBytesToRead)
+ {
+ SetLastError(ERROR_MORE_DATA);
+ return FALSE;
+ }
+
+ numBytesRead = 0;
+
+ bSuccess = Win32_WTSVirtualChannelRead_Dynamic(pChannel, dwMilliseconds, lpBuffer,
+ nNumberOfBytesToRead, &numBytesRead);
+
+ *lpNumberOfBytesTransferred += numBytesRead;
+ return bSuccess;
+ }
+ else
+ {
+ SetLastError(ERROR_IO_INCOMPLETE);
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL WINAPI Win32_WTSVirtualChannelRead(HANDLE hChannel, DWORD dwMilliseconds, LPVOID lpBuffer,
+ DWORD nNumberOfBytesToRead,
+ LPDWORD lpNumberOfBytesTransferred)
+{
+ WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannel;
+
+ if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ if (!pChannel->waitObjectMode)
+ {
+ OVERLAPPED overlapped = { 0 };
+
+ if (ReadFile(pChannel->hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesTransferred,
+ &overlapped))
+ return TRUE;
+
+ if (GetLastError() != ERROR_IO_PENDING)
+ return FALSE;
+
+ if (!dwMilliseconds)
+ {
+ CancelIo(pChannel->hFile);
+ *lpNumberOfBytesTransferred = 0;
+ return TRUE;
+ }
+
+ if (WaitForSingleObject(pChannel->hFile, dwMilliseconds) != WAIT_TIMEOUT)
+ return GetOverlappedResult(pChannel->hFile, &overlapped, lpNumberOfBytesTransferred,
+ FALSE);
+
+ CancelIo(pChannel->hFile);
+ SetLastError(ERROR_IO_INCOMPLETE);
+
+ return FALSE;
+ }
+ else
+ {
+ if (pChannel->dynamic)
+ {
+ return Win32_WTSVirtualChannelRead_Dynamic(pChannel, dwMilliseconds, lpBuffer,
+ nNumberOfBytesToRead,
+ lpNumberOfBytesTransferred);
+ }
+ else
+ {
+ return Win32_WTSVirtualChannelRead_Static(pChannel, dwMilliseconds, lpBuffer,
+ nNumberOfBytesToRead,
+ lpNumberOfBytesTransferred);
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL WINAPI Win32_WTSVirtualChannelWrite(HANDLE hChannel, LPCVOID lpBuffer,
+ DWORD nNumberOfBytesToWrite,
+ LPDWORD lpNumberOfBytesTransferred)
+{
+ OVERLAPPED overlapped = { 0 };
+ WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannel;
+
+ if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ if (WriteFile(pChannel->hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesTransferred,
+ &overlapped))
+ return TRUE;
+
+ if (GetLastError() == ERROR_IO_PENDING)
+ return GetOverlappedResult(pChannel->hFile, &overlapped, lpNumberOfBytesTransferred, TRUE);
+
+ return FALSE;
+}
+
+#ifndef FILE_DEVICE_TERMSRV
+#define FILE_DEVICE_TERMSRV 0x00000038
+#endif
+
+BOOL Win32_WTSVirtualChannelPurge_Internal(HANDLE hChannelHandle, ULONG IoControlCode)
+{
+ DWORD error;
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK ioStatusBlock;
+ WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannelHandle;
+
+ if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ ntstatus =
+ NtDeviceIoControlFile(pChannel->hFile, 0, 0, 0, &ioStatusBlock, IoControlCode, 0, 0, 0, 0);
+
+ if (ntstatus == STATUS_PENDING)
+ {
+ ntstatus = NtWaitForSingleObject(pChannel->hFile, 0, 0);
+
+ if (ntstatus >= 0)
+ ntstatus = ioStatusBlock.Status;
+ }
+
+ if (ntstatus == STATUS_BUFFER_OVERFLOW)
+ {
+ ntstatus = STATUS_BUFFER_TOO_SMALL;
+ error = RtlNtStatusToDosError(ntstatus);
+ SetLastError(error);
+ return FALSE;
+ }
+
+ if (ntstatus < 0)
+ {
+ error = RtlNtStatusToDosError(ntstatus);
+ SetLastError(error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL WINAPI Win32_WTSVirtualChannelPurgeInput(HANDLE hChannelHandle)
+{
+ return Win32_WTSVirtualChannelPurge_Internal(hChannelHandle,
+ (FILE_DEVICE_TERMSRV << 16) | 0x0107);
+}
+
+BOOL WINAPI Win32_WTSVirtualChannelPurgeOutput(HANDLE hChannelHandle)
+{
+ return Win32_WTSVirtualChannelPurge_Internal(hChannelHandle,
+ (FILE_DEVICE_TERMSRV << 16) | 0x010B);
+}
+
+BOOL WINAPI Win32_WTSVirtualChannelQuery(HANDLE hChannelHandle, WTS_VIRTUAL_CLASS WtsVirtualClass,
+ PVOID* ppBuffer, DWORD* pBytesReturned)
+{
+ WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannelHandle;
+
+ if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ if (WtsVirtualClass == WTSVirtualClientData)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ else if (WtsVirtualClass == WTSVirtualFileHandle)
+ {
+ *pBytesReturned = sizeof(HANDLE);
+ *ppBuffer = _wts_calloc(1, *pBytesReturned);
+
+ if (*ppBuffer == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ CopyMemory(*ppBuffer, &(pChannel->hFile), *pBytesReturned);
+ }
+ else if (WtsVirtualClass == WTSVirtualEventHandle)
+ {
+ *pBytesReturned = sizeof(HANDLE);
+ *ppBuffer = _wts_calloc(1, *pBytesReturned);
+
+ if (*ppBuffer == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ CopyMemory(*ppBuffer, &(pChannel->hEvent), *pBytesReturned);
+
+ Win32_WTSVirtualChannelReadAsync(pChannel);
+ pChannel->waitObjectMode = TRUE;
+ }
+ else
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+VOID WINAPI Win32_WTSFreeMemory(PVOID pMemory)
+{
+ _wts_free(pMemory);
+}
+
+BOOL WINAPI Win32_WTSFreeMemoryExW(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory,
+ ULONG NumberOfEntries)
+{
+ return FALSE;
+}
+
+BOOL WINAPI Win32_WTSFreeMemoryExA(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory,
+ ULONG NumberOfEntries)
+{
+ return WTSFreeMemoryExW(WTSTypeClass, pMemory, NumberOfEntries);
+}
+
+BOOL Win32_InitializeWinSta(PWtsApiFunctionTable pWtsApi)
+{
+ g_WinStaModule = LoadLibraryA("winsta.dll");
+
+ if (!g_WinStaModule)
+ return FALSE;
+
+ pfnWinStationVirtualOpen =
+ (fnWinStationVirtualOpen)GetProcAddress(g_WinStaModule, "WinStationVirtualOpen");
+ pfnWinStationVirtualOpenEx =
+ (fnWinStationVirtualOpenEx)GetProcAddress(g_WinStaModule, "WinStationVirtualOpenEx");
+
+ if (!pfnWinStationVirtualOpen | !pfnWinStationVirtualOpenEx)
+ return FALSE;
+
+ pWtsApi->pVirtualChannelOpen = Win32_WTSVirtualChannelOpen;
+ pWtsApi->pVirtualChannelOpenEx = Win32_WTSVirtualChannelOpenEx;
+ pWtsApi->pVirtualChannelClose = Win32_WTSVirtualChannelClose;
+ pWtsApi->pVirtualChannelRead = Win32_WTSVirtualChannelRead;
+ pWtsApi->pVirtualChannelWrite = Win32_WTSVirtualChannelWrite;
+ pWtsApi->pVirtualChannelPurgeInput = Win32_WTSVirtualChannelPurgeInput;
+ pWtsApi->pVirtualChannelPurgeOutput = Win32_WTSVirtualChannelPurgeOutput;
+ pWtsApi->pVirtualChannelQuery = Win32_WTSVirtualChannelQuery;
+ pWtsApi->pFreeMemory = Win32_WTSFreeMemory;
+ // pWtsApi->pFreeMemoryExW = Win32_WTSFreeMemoryExW;
+ // pWtsApi->pFreeMemoryExA = Win32_WTSFreeMemoryExA;
+
+ return TRUE;
+}
diff --git a/winpr/libwinpr/wtsapi/wtsapi_win32.h b/winpr/libwinpr/wtsapi/wtsapi_win32.h
new file mode 100644
index 0000000..7d43165
--- /dev/null
+++ b/winpr/libwinpr/wtsapi/wtsapi_win32.h
@@ -0,0 +1,27 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Windows Terminal Services API
+ *
+ * Copyright 2013-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WINPR_WTSAPI_WIN32_PRIVATE_H
+#define WINPR_WTSAPI_WIN32_PRIVATE_H
+
+#include <winpr/wtsapi.h>
+
+BOOL Win32_InitializeWinSta(PWtsApiFunctionTable pWtsApi);
+
+#endif /* WINPR_WTSAPI_WIN32_PRIVATE_H */