diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
commit | a9bcc81f821d7c66f623779fa5147e728eb3c388 (patch) | |
tree | 98676963bcdd537ae5908a067a8eb110b93486a6 /winpr/libwinpr/wtsapi | |
parent | Initial commit. (diff) | |
download | freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip |
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'winpr/libwinpr/wtsapi')
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 */ |