├── .github
├── CODEOWNERS
└── workflows
│ └── build-package.yml
├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── PowerShell
├── ConnectRdpSession.cs
├── Devolutions.MsRdpEx.PowerShell.csproj
├── RdpContext.cs
├── RdpMain.cs
├── RdpView.cs
├── StartRdpClient.cs
└── StartRdpProcess.cs
├── README.md
├── channels
├── CMakeLists.txt
└── DvcServer.cpp
├── cmake
├── MSVCRuntime.cmake
└── WindowsRC.cmake
├── com
├── .gitignore
├── MSTSCLib.dll
├── README.md
├── mstscax.h
├── mstscax.idl
├── mstscax.tlh
├── mstscax.tli
└── mstscax_i.c
├── detours.ps1
├── dll
├── ApiHooks.cpp
├── ArrayList.c
├── AxDll.cpp
├── AxHost
│ ├── RdpAxHostWnd.cpp
│ ├── RdpAxHostWnd.h
│ ├── RdpComBase.h
│ ├── RdpEventSink.cpp
│ ├── RdpEventSink.h
│ ├── RdpOleSite.cpp
│ ├── RdpOleSite.h
│ ├── RdpWinMain.cpp
│ └── RdpWinMain.h
├── Bitmap.c
├── CMakeLists.txt
├── ComHelpers.h
├── Detours.cpp
├── DpiHelper.cpp
├── DpiHelper.h
├── Environment.c
├── File.c
├── HashTable.c
├── KeyMaps.cpp
├── Log.c
├── Memory.c
├── MsRdpClient.cpp
├── MsRdpClient.h
├── MsRdpEx.cpp
├── MsRdpEx.def
├── MsRdpEx.h
├── MsRdpEx.rc
├── NameResolver.c
├── NamedPipe.c
├── OutputMirror.c
├── Paths.c
├── Pcap.cpp
├── RdpCoreApi.cpp
├── RdpDvcClient.cpp
├── RdpDvcClient.h
├── RdpFile.c
├── RdpInstance.cpp
├── RdpProcess.cpp
├── RdpSettings.cpp
├── RecordingManifest.c
├── Sspi.cpp
├── Stopwatch.c
├── Stream.cpp
├── String.c
├── TSObjects.cpp
├── TSObjects.h
├── VideoRecorder.c
└── WinMsg.c
├── dotnet
├── .gitignore
├── AxInterop.MSTSCLib
│ ├── AxInterop.MSTSCLib.csproj
│ ├── AxMSTSCLib.cs
│ └── RdpAxHost.cs
├── CMakeLists.txt
├── Devolutions.MsRdpEx
│ ├── Bindings.cs
│ ├── Devolutions.MsRdpEx.csproj
│ ├── Devolutions.MsRdpEx.targets
│ ├── LoadBalanceInfo.cs
│ ├── MarshalHelpers.cs
│ ├── RdpCoreApi.cs
│ ├── RdpInstance.cs
│ └── RdpProcess.cs
├── Directory.Build.props.in
├── Interop.MSTSCLib
│ └── Interop.MSTSCLib.dll
├── MsRdpEx_App
│ ├── App.config
│ ├── DvcClientLib.cs
│ ├── MainDlg.Designer.cs
│ ├── MainDlg.cs
│ ├── MainDlg.resx
│ ├── MsRdpEx_App.csproj
│ ├── NowProtoPipeTransport.cs
│ ├── Program.cs
│ ├── Properties
│ │ ├── Resources.Designer.cs
│ │ ├── Resources.resx
│ │ ├── Settings.Designer.cs
│ │ └── Settings.settings
│ ├── RdpChannel.cs
│ ├── RdpManager.cs
│ ├── RdpView.cs
│ └── RdpView.resx
└── common.build.pre.props
├── exe
├── CMakeLists.txt
├── msrdcex
│ ├── CMakeLists.txt
│ ├── msrdcex.cpp
│ ├── msrdcex.ico
│ └── msrdcex.rc
├── mstscex
│ ├── CMakeLists.txt
│ ├── mstscex.cpp
│ ├── mstscex.ico
│ └── mstscex.rc
└── vmconnectex
│ ├── CMakeLists.txt
│ ├── vmconnectex.cpp
│ ├── vmconnectex.ico
│ └── vmconnectex.rc
├── images
└── MsRdpEx_installed.png
├── include
└── MsRdpEx
│ ├── ArrayList.h
│ ├── Detours.h
│ ├── Environment.h
│ ├── HashTable.h
│ ├── KeyMaps.h
│ ├── Memory.h
│ ├── MsRdpEx.h
│ ├── NameResolver.h
│ ├── NamedPipe.h
│ ├── OutputMirror.h
│ ├── Pcap.h
│ ├── RdpCoreApi.h
│ ├── RdpFile.h
│ ├── RdpInstance.h
│ ├── RdpProcess.h
│ ├── RdpSettings.h
│ ├── RecordingManifest.h
│ ├── Sspi.h
│ ├── Stopwatch.h
│ ├── Stream.h
│ └── VideoRecorder.h
├── installer
├── Folders.wxs
├── MsRdpEx.sln
├── MsRdpEx.wixproj
├── MsRdpEx.wxs
├── Package.en-us.wxl
├── Package.wxs
└── Variables.wxi
└── scripts
└── SetAssemblyTargetFramework.ps1
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # File auto-generated and managed by Devops
2 | /.github/ @devolutions/devops
3 | /.github/dependabot.yml @devolutions/security-managers
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build*/
2 | bin/
3 | obj/
4 | .vscode/
5 | .nupkg
6 | dependencies/
7 | package/
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8)
2 |
3 | set(CMAKE_CONFIGURATION_TYPES "Debug;Release")
4 |
5 | project(MsRdpEx C CXX)
6 |
7 | set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
8 |
9 | include(CSharpUtilities)
10 | include(CMakePackageConfigHelpers)
11 | include(WindowsRC)
12 |
13 | file(STRINGS "${CMAKE_SOURCE_DIR}/dotnet/Devolutions.MsRdpEx/Devolutions.MsRdpEx.csproj" CSPROJ_LINES)
14 | foreach(CSPROJ_LINE ${CSPROJ_LINES})
15 | if(CSPROJ_LINE MATCHES ".*")
16 | string(REGEX REPLACE "(.*)" "\\1" VERSION_STRING ${CSPROJ_LINE})
17 | string(STRIP ${VERSION_STRING} VERSION_STRING)
18 | break()
19 | endif()
20 | endforeach()
21 |
22 | string(REGEX REPLACE "([0-9]+).[0-9]+.[0-9]+.[0-9]+" "\\1" MSRDPEX_VERSION_MAJOR ${VERSION_STRING})
23 | string(REGEX REPLACE "[0-9]+.([0-9]+).[0-9]+.[0-9]+" "\\1" MSRDPEX_VERSION_MINOR ${VERSION_STRING})
24 | string(REGEX REPLACE "[0-9]+.[0-9]+.([0-9]+).[0-9]+" "\\1" MSRDPEX_VERSION_PATCH ${VERSION_STRING})
25 | string(REGEX REPLACE "[0-9]+.[0-9]+.[0-9]+.([0-9]+)" "\\1" MSRDPEX_VERSION_BUILD ${VERSION_STRING})
26 | set(MSRDPEX_VERSION "${MSRDPEX_VERSION_MAJOR}.${MSRDPEX_VERSION_MINOR}.${MSRDPEX_VERSION_PATCH}.${MSRDPEX_VERSION_BUILD}")
27 |
28 | message(STATUS "VERSION: ${MSRDPEX_VERSION}")
29 |
30 | string(TIMESTAMP CURRENT_YEAR "%Y")
31 |
32 | set(MSRDPEX_NAME "MsRdpEx")
33 | set(MSRDPEX_VENDOR "Devolutions Inc.")
34 | set(MSRDPEX_COPYRIGHT "Copyright 2021-${CURRENT_YEAR} ${MSRDPEX_VENDOR}")
35 |
36 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
37 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
38 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
39 |
40 | if(NOT DEFINED WITH_NATIVE)
41 | set(WITH_NATIVE TRUE)
42 | endif()
43 |
44 | if(NOT DEFINED WITH_DOTNET)
45 | set(WITH_DOTNET TRUE)
46 | endif()
47 |
48 | if(MSVC)
49 | include(MSVCRuntime)
50 |
51 | if(NOT DEFINED MSVC_RUNTIME)
52 | set(MSVC_RUNTIME "static")
53 | endif()
54 |
55 | configure_msvc_runtime()
56 | endif()
57 |
58 | if(WIN32)
59 | set(C_FLAGS "")
60 | set(C_FLAGS "${C_FLAGS} -D_UNICODE")
61 | set(C_FLAGS "${C_FLAGS} -D_CRT_SECURE_NO_WARNINGS")
62 | set(C_FLAGS "${C_FLAGS} -DWIN32_LEAN_AND_MEAN")
63 | set(C_FLAGS "${C_FLAGS} -D_WINSOCK_DEPRECATED_NO_WARNINGS")
64 | set(C_FLAGS "${C_FLAGS} -DWINVER=0x0602 -D_WIN32_WINNT=0x0602")
65 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_FLAGS}")
66 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${C_FLAGS}")
67 |
68 | set(C_FLAGS_RELEASE "/Zi /GF") # produce debug symbols in release builds
69 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${C_FLAGS_RELEASE}")
70 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${C_FLAGS_RELEASE}")
71 |
72 | # http://devcenter.wintellect.com/jrobbins/correctly-creating-native-c-release-build-pdbs
73 | set(LINKER_FLAGS_RELEASE "/DEBUG")
74 | set(LINKER_FLAGS_RELEASE "${LINKER_FLAGS_RELEASE} /OPT:REF /OPT:ICF")
75 | set(LINKER_FLAGS_RELEASE "${LINKER_FLAGS_RELEASE} /MAP /MAPINFO:EXPORTS")
76 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELEASE}" CACHE STRING "" FORCE)
77 | set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELEASE}" CACHE STRING "" FORCE)
78 | set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELEASE}" CACHE STRING "" FORCE)
79 | endif()
80 |
81 | set(DEPENDENCIES_DIR "${CMAKE_SOURCE_DIR}/dependencies")
82 | set(DETOURS_ROOT_DIR "${DEPENDENCIES_DIR}/detours")
83 |
84 | include_directories("${DETOURS_ROOT_DIR}/include")
85 |
86 | add_library(detours STATIC IMPORTED)
87 |
88 | if(CMAKE_GENERATOR_PLATFORM MATCHES "ARM64")
89 | set(MSVC_PLATFORM_TARGET "arm64")
90 | elseif(CMAKE_GENERATOR_PLATFORM MATCHES "Win32")
91 | set(MSVC_PLATFORM_TARGET "x86")
92 | else()
93 | set(MSVC_PLATFORM_TARGET "x64")
94 | endif()
95 |
96 | message(STATUS "MSVC_PLATFORM_TARGET: ${MSVC_PLATFORM_TARGET}")
97 |
98 | set_property(TARGET detours PROPERTY
99 | IMPORTED_LOCATION "${DETOURS_ROOT_DIR}/lib/${MSVC_PLATFORM_TARGET}/Release/detours.lib")
100 | set_property(TARGET detours PROPERTY
101 | IMPORTED_LOCATION_DEBUG "${DETOURS_ROOT_DIR}/lib/${MSVC_PLATFORM_TARGET}/Debug/detours.lib")
102 |
103 | include_directories("${CMAKE_SOURCE_DIR}/include")
104 | include_directories("${CMAKE_SOURCE_DIR}/com")
105 |
106 | if(WITH_NATIVE)
107 | add_subdirectory(dll)
108 | add_subdirectory(exe)
109 | add_subdirectory(channels)
110 | endif()
111 |
112 | if(WITH_DOTNET)
113 | add_subdirectory(dotnet)
114 | endif()
115 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Permission is hereby granted, free of charge, to any
2 | person obtaining a copy of this software and associated
3 | documentation files (the "Software"), to deal in the
4 | Software without restriction, including without
5 | limitation the rights to use, copy, modify, merge,
6 | publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software
8 | is furnished to do so, subject to the following
9 | conditions:
10 |
11 | The above copyright notice and this permission notice
12 | shall be included in all copies or substantial portions
13 | of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/PowerShell/ConnectRdpSession.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Drawing;
4 | using System.Management.Automation;
5 | using System.Management.Automation.Runspaces;
6 | using System.Windows.Forms;
7 |
8 | using MSTSCLib;
9 |
10 | using MsRdpEx;
11 |
12 | namespace MsRdpEx.PowerShell
13 | {
14 | [Cmdlet(VerbsCommunications.Connect,"RdpSession")]
15 |
16 | public class ConnectRdpSessionCommand : PSCmdlet
17 | {
18 | [Parameter(Position = 0, Mandatory = true)]
19 | public string Hostname;
20 |
21 | [Parameter(Position = 1, Mandatory = true)]
22 | public string Username;
23 |
24 | [Parameter(Position = 2, Mandatory = true)]
25 | public string Password;
26 |
27 | protected override void ProcessRecord()
28 | {
29 | RdpContext rdpContext = RdpContext.Instance();
30 | rdpContext.Connect(Hostname, Username, Password);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/PowerShell/Devolutions.MsRdpEx.PowerShell.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | true
6 | Devolutions.MsRdpEx.PowerShell
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/PowerShell/RdpContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Drawing;
4 | using System.Threading;
5 | using System.Management.Automation;
6 | using System.Management.Automation.Runspaces;
7 | using System.Windows.Forms;
8 |
9 | using MSTSCLib;
10 |
11 | using MsRdpEx;
12 |
13 | namespace MsRdpEx.PowerShell
14 | {
15 | public class RdpContext
16 | {
17 | static RdpContext instance = null;
18 | static Thread thread = null;
19 |
20 | static RdpMain rdpMain = null;
21 |
22 | public static RdpContext Instance() {
23 | if (instance == null) {
24 | instance = new RdpContext();
25 | }
26 | return instance;
27 | }
28 |
29 | public RdpContext()
30 | {
31 | RdpContext.StartThread();
32 | System.Threading.Thread.Sleep(250);
33 | }
34 |
35 | public static Thread StartThread()
36 | {
37 | thread = new Thread(new ThreadStart(MainThread));
38 | thread.SetApartmentState(ApartmentState.STA);
39 | thread.Start();
40 | return thread;
41 | }
42 |
43 | static void MainThread()
44 | {
45 | Application.EnableVisualStyles();
46 | Application.SetCompatibleTextRenderingDefault(false);
47 | rdpMain = new RdpMain();
48 | Application.Run(rdpMain);
49 | }
50 |
51 | public void Connect(string hostname, string username, string password)
52 | {
53 | rdpMain.Connect(hostname, username, password);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/PowerShell/RdpMain.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Data;
6 | using System.Drawing;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 | using System.Windows.Forms;
11 | using System.Diagnostics;
12 | using System.Runtime.InteropServices;
13 |
14 | using MSTSCLib;
15 |
16 | using MsRdpEx;
17 |
18 | namespace MsRdpEx.PowerShell
19 | {
20 | public partial class RdpMain : Form
21 | {
22 | public RdpCoreApi coreApi;
23 | public RdpInstance rdpInstance;
24 | public RdpView rdpView;
25 | public List rdpViews = new List();
26 |
27 | public RdpMain()
28 | {
29 | InitializeComponent();
30 |
31 | this.coreApi = new RdpCoreApi();
32 | coreApi.Load();
33 | }
34 |
35 | public void Connect(string hostname, string username, string password)
36 | {
37 | if (this.InvokeRequired) {
38 | Action safe = delegate { Connect(hostname, username, password); };
39 | this.Invoke(safe);
40 | return;
41 | }
42 |
43 | RdpContext context = RdpContext.Instance();
44 | string rdpExDll = coreApi.MsRdpExDllPath;
45 |
46 | string axName = "mstsc";
47 | string appName = axName;
48 |
49 | RdpView rdpView = new RdpView(axName, rdpExDll);
50 | AxMSTSCLib.AxMsRdpClient9NotSafeForScripting rdp = rdpView.rdpClient;
51 |
52 | this.rdpInstance = new RdpInstance((IMsRdpExInstance)rdp.GetOcx());
53 | rdpInstance.OutputMirrorEnabled = true;
54 | rdpInstance.VideoRecordingEnabled = true;
55 |
56 | rdp.Server = hostname;
57 | rdp.UserName = username;
58 | rdp.AdvancedSettings9.EnableCredSspSupport = true;
59 | IMsTscNonScriptable secured = (IMsTscNonScriptable)rdp.GetOcx();
60 | secured.ClearTextPassword = password;
61 | IMsRdpExtendedSettings extendedSettings = (IMsRdpExtendedSettings)rdp.GetOcx();
62 | object boolValue = false;
63 | extendedSettings.set_Property("EnableHardwareMode", ref boolValue);
64 | Size DesktopSize = new Size(1024, 768);
65 | rdp.DesktopWidth = DesktopSize.Width;
66 | rdp.DesktopHeight = DesktopSize.Height;
67 | rdpView.ClientSize = DesktopSize;
68 | rdpView.Text = String.Format("{0} ({1})", rdp.Server, axName);
69 |
70 | rdp.Connect();
71 | rdpView.Show();
72 |
73 | this.rdpViews.Add(rdpView);
74 | }
75 |
76 | protected override void OnLoad(EventArgs e)
77 | {
78 | //Visible = false;
79 | //ShowInTaskbar = false;
80 | //WindowState = FormWindowState.Minimized;
81 | base.OnLoad(e);
82 | }
83 |
84 | private void InitializeComponent()
85 | {
86 | this.SuspendLayout();
87 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
88 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
89 | this.ClientSize = new System.Drawing.Size(256, 256);
90 | this.Name = "RdpMain";
91 | this.Text = "Remote Desktop Client";
92 | this.ResumeLayout(false);
93 | this.Hide();
94 | }
95 |
96 | protected override void Dispose(bool disposing)
97 | {
98 | base.Dispose(disposing);
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/PowerShell/StartRdpClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Drawing;
4 | using System.Management.Automation;
5 | using System.Management.Automation.Runspaces;
6 | using System.Windows.Forms;
7 |
8 | using MSTSCLib;
9 |
10 | using MsRdpEx;
11 |
12 | namespace MsRdpEx.PowerShell
13 | {
14 | [Cmdlet(VerbsLifecycle.Start,"RdpClient")]
15 | public class StartRdpClientCommand : PSCmdlet
16 | {
17 | protected override void ProcessRecord()
18 | {
19 | RdpContext rdpContext = RdpContext.Instance();
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/PowerShell/StartRdpProcess.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Management.Automation;
3 | using System.Management.Automation.Runspaces;
4 |
5 | using MsRdpEx;
6 |
7 | namespace MsRdpEx.PowerShell
8 | {
9 | [Cmdlet(VerbsLifecycle.Start,"RdpProcess")]
10 | public class StartRdpProcessCommand : PSCmdlet
11 | {
12 | protected override void ProcessRecord()
13 | {
14 | RdpCoreApi coreApi = new RdpCoreApi();
15 | string rdpExDll = coreApi.MsRdpExDllPath;
16 |
17 | coreApi.Load();
18 |
19 | string axName = "mstsc";
20 | string appName = axName;
21 | string[] args = new string[0];
22 | RdpProcess rdpProcess = new RdpProcess(args, appName, axName);
23 |
24 | WriteObject(rdpProcess);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Microsoft RDP Extensions (MsRdpEx)
2 |
3 | ## Installation
4 |
5 | Download and install the latest MsRdpEx MSI package [GitHub releases](https://github.com/Devolutions/MsRdpEx/releases).
6 |
7 | After installation, the launcher executables and API hooking DLL can be found in "%ProgramFiles%\Devolutions\MsRdpEx":
8 |
9 | 
10 |
11 | The installer automatically associates .RDP files with mstscex, and .RDPW files with msrdcex, so you can get started right away. Simply revert the file type association to use the original Microsoft Remote Desktop Clients without the extensions, or launch mstsc.exe/msrdc.exe manually.
12 |
13 | This repository also contains a C# [nuget package](https://www.nuget.org/packages/Devolutions.MsRdpEx) that can be used to consume the RDP ActiveX interface with or without API hooking, along with launching mstsc.exe or msrdc.exe as external processes using MsRdpEx.dll.
14 |
15 | ## Extended .RDP File Options
16 |
17 | MsRdpEx processes additional .RDP file options that are not normally supported by mstsc.exe:
18 |
19 | | RDP setting | Description | Values | Default value |
20 | |------------------------------------|------------------------|------------------------|:----------------------:|
21 | | KDCProxyURL:s:value | Kerberos KDC Proxy HTTPS URL | KDC Proxy HTTPS *URL*, not using error-prone KDCProxyName format, and unrestricted in length, like https://:443/KdcProxy | - |
22 | | UserSpecifiedServerName:s:value | Server name used for TLS and Kerberos server validation | explicit server name (usually the machine FQDN) | same as DNS hostname used for RDP server |
23 | | EnableMouseJiggler:i:value | Enable RDP mouse jiggler | 0/1 | 0 |
24 | | MouseJigglerInterval:i:value | RDP mouse jiggler interval in seconds | Interval in seconds | 60 |
25 | | MouseJigglerMethod:i:value | RDP mouse jiggler method | 0/1 | 0 |
26 | | AllowBackgroundInput:i:value | Allow background input events when window is not in focus | 0/1 | 0 |
27 | | EnableRelativeMouse:i:value | Enable relative mouse mode | 0/1 | 0 |
28 | | DisableCredentialsDelegation:i:value | Disable CredSSP credential delegation | 0/1 | 0 |
29 | | RedirectedAuthentication:i:value | Enable Remote Credential Guard | 0/1 | 0 |
30 | | RestrictedLogon:i:value | Enable Restricted Admin Mode | 0/1 | 0 |
31 | | DisableUDPTransport:i:value | Disable RDP UDP transport (TCP only) | 0/1 | 0 |
32 | | ConnectToChildSession:i:value | Connect to child session | 0/1 | 0 |
33 | | EnableHardwareMode:i:value | Disable DirectX client presenter (force GDI client presenter) | 0/1 | 1 |
34 | | ClearTextPassword:s:value | Target RDP server password - use for testing only | Insecure password | - |
35 | | GatewayPassword:s:value | RD Gateway server password - use for testing only | Insecure password | - |
36 |
37 | ## Extended RDP client logs
38 |
39 | MsRdpEx also supports extended logging controlled by environment variables:
40 |
41 | ```powershell
42 | $Env:MSRDPEX_LOG_ENABLED="1"
43 | $Env:MSRDPEX_LOG_LEVEL="DEBUG"
44 | .\mstscex.exe
45 | ```
46 |
47 | If you don't pass a .RDP file, the mstsc.exe GUI will launch normally, but you won't be able to leverage any of the extended MsRdpEx .RDP file options. The default log file path location is in "%LocalAppData%\MsRdpEx\MsRdpEx.log". You can override log settings using the MSRDPEX_LOG_LEVEL and MSRDPEX_LOG_FILE_PATH environment variables:
48 |
49 | ```powershell
50 | $Env:MSRDPEX_LOG_ENABLED="1"
51 | $Env:MSRDPEX_LOG_LEVEL="TRACE"
52 | $Env:MSRDPEX_LOG_FILE_PATH="C:\Windows\Temp\MsRdpEx.log"
53 | .\mstscex.exe
54 | ```
55 |
56 | The trace log level is extremely verbose, so it should only be used when necessary. The MsRdpEx logging is very helpful in understanding the Microsoft RDP client internals.
57 |
58 | ## Building from source
59 |
60 | Using a [Visual Studio developer shell](https://www.powershellgallery.com/packages/VsDevShell), build the [Detours](https://github.com/Microsoft/Detours) library:
61 |
62 | ```powershell
63 | Enter-VsDevShell x64
64 | .\detours.ps1
65 | ```
66 |
67 | Generate the Visual Studio project files for your target platform:
68 |
69 | ```powershell
70 | mkdir build-x64 && cd build-x64
71 | cmake -G "Visual Studio 17 2022" -A x64 ..
72 | ```
73 |
74 | Open the Visual Studio solution or build it from the command-line:
75 |
76 | ```powershell
77 | cmake --build . --config Release
78 | ```
79 |
80 | You should now have mstscex.exe and MsRdpEx.dll.
81 |
--------------------------------------------------------------------------------
/channels/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | add_executable(DvcServer
3 | DvcServer.cpp)
4 |
5 | target_link_libraries(DvcServer wtsapi32.lib)
6 |
--------------------------------------------------------------------------------
/channels/DvcServer.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | DWORD OpenVirtualChannel(const char* channelName, HANDLE* phFile)
8 | {
9 | HANDLE hWTSHandle = NULL;
10 | HANDLE hWTSFileHandle;
11 | PVOID vcFileHandlePtr = NULL;
12 | DWORD len;
13 | DWORD rc = ERROR_SUCCESS;
14 |
15 | hWTSHandle = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, (LPSTR)channelName, WTS_CHANNEL_OPTION_DYNAMIC);
16 |
17 | if (!hWTSHandle)
18 | {
19 | rc = GetLastError();
20 | printf("WTSVirtualChannelOpenEx API Call Failed: GetLastError() = %d\n", GetLastError());
21 | goto exitpt;
22 | }
23 |
24 | BOOL bSuccess = WTSVirtualChannelQuery(hWTSHandle, WTSVirtualFileHandle, &vcFileHandlePtr, &len);
25 |
26 | if (!bSuccess)
27 | {
28 | rc = GetLastError();
29 | goto exitpt;
30 | }
31 |
32 | if (len != sizeof(HANDLE))
33 | {
34 | rc = ERROR_INVALID_PARAMETER;
35 | goto exitpt;
36 | }
37 |
38 | hWTSFileHandle = *(HANDLE*)vcFileHandlePtr;
39 |
40 | bSuccess = DuplicateHandle(GetCurrentProcess(),
41 | hWTSFileHandle, GetCurrentProcess(), phFile, 0, FALSE, DUPLICATE_SAME_ACCESS);
42 |
43 | if (!bSuccess)
44 | {
45 | rc = GetLastError();
46 | goto exitpt;
47 | }
48 |
49 | rc = ERROR_SUCCESS;
50 | exitpt:
51 | if (vcFileHandlePtr)
52 | {
53 | WTSFreeMemory(vcFileHandlePtr);
54 | }
55 | if (hWTSHandle)
56 | {
57 | WTSVirtualChannelClose(hWTSHandle);
58 | }
59 | return rc;
60 | }
61 |
62 | DWORD WriteVirtualChannelMessage(HANDLE hFile, ULONG cbSize, BYTE* pBuffer)
63 | {
64 | BYTE WriteBuffer[1024];
65 | DWORD dwWritten;
66 | BOOL bSuccess;
67 | HANDLE hEvent;
68 |
69 | hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
70 |
71 | OVERLAPPED overlapped = { 0 };
72 | overlapped.hEvent = hEvent;
73 |
74 | bSuccess = WriteFile(hFile, pBuffer, cbSize, &dwWritten, &overlapped);
75 |
76 | if (!bSuccess)
77 | {
78 | if (GetLastError() == ERROR_IO_PENDING)
79 | {
80 | DWORD dwStatus = WaitForSingleObject(overlapped.hEvent, 10000);
81 | bSuccess = GetOverlappedResult(hFile, &overlapped, &dwWritten, FALSE);
82 | }
83 | }
84 |
85 | if (!bSuccess)
86 | {
87 | DWORD error = GetLastError();
88 | return error;
89 | }
90 |
91 | return 0;
92 | }
93 |
94 | DWORD HandleVirtualChannel(HANDLE hFile)
95 | {
96 | BYTE ReadBuffer[CHANNEL_PDU_LENGTH];
97 | DWORD dwRead;
98 | BYTE b = 0;
99 | CHANNEL_PDU_HEADER* pHdr = (CHANNEL_PDU_HEADER*)ReadBuffer;
100 | BOOL bSuccess;
101 | HANDLE hEvent;
102 |
103 | const char* cmd = "whoami";
104 | ULONG cbSize = strlen(cmd) + 1;
105 | BYTE* pBuffer = (BYTE*)cmd;
106 | WriteVirtualChannelMessage(hFile, cbSize, pBuffer);
107 |
108 | hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
109 |
110 | do
111 | {
112 | OVERLAPPED overlapped = { 0 };
113 | DWORD TotalRead = 0;
114 |
115 | do {
116 | overlapped.hEvent = hEvent;
117 | bSuccess = ReadFile(hFile, ReadBuffer, sizeof(ReadBuffer), &dwRead, &overlapped);
118 |
119 | if (!bSuccess)
120 | {
121 | if (GetLastError() == ERROR_IO_PENDING)
122 | {
123 | DWORD dwStatus = WaitForSingleObject(overlapped.hEvent, INFINITE);
124 | bSuccess = GetOverlappedResult(hFile, &overlapped, &dwRead, FALSE);
125 | }
126 | }
127 |
128 | if (!bSuccess)
129 | {
130 | DWORD error = GetLastError();
131 | return error;
132 | }
133 |
134 | printf("read %d bytes\n", dwRead);
135 |
136 | ULONG packetSize = dwRead - sizeof(*pHdr);
137 | TotalRead += packetSize;
138 | PBYTE pData = (PBYTE)(pHdr + 1);
139 |
140 | printf(">> %s\n", (const char*)pData);
141 |
142 | } while (0 == (pHdr->flags & CHANNEL_FLAG_LAST));
143 |
144 | } while (true);
145 |
146 | return 0;
147 | }
148 |
149 | INT _cdecl wmain(INT argc, __in_ecount(argc) WCHAR** argv)
150 | {
151 | DWORD rc;
152 | HANDLE hFile;
153 | const char* channelName = "DvcSample";
154 |
155 | printf("Opening %s dynamic virtual channel\n", channelName);
156 | rc = OpenVirtualChannel(channelName, &hFile);
157 |
158 | if (ERROR_SUCCESS != rc)
159 | {
160 | printf("Failed to open %s dynamic virtual channel\n", channelName);
161 | return 0;
162 | }
163 | else
164 | {
165 | printf("%s dynamic virtual channel is opened\n", channelName);
166 | }
167 |
168 | HandleVirtualChannel(hFile);
169 |
170 | CloseHandle(hFile);
171 |
172 | return 0;
173 | }
174 |
--------------------------------------------------------------------------------
/cmake/MSVCRuntime.cmake:
--------------------------------------------------------------------------------
1 |
2 | macro(configure_msvc_runtime)
3 | if(MSVC)
4 | # Default to statically-linked runtime.
5 | if("${MSVC_RUNTIME}" STREQUAL "")
6 | set(MSVC_RUNTIME "dynamic")
7 | endif()
8 |
9 | # Set compiler options.
10 | set(variables
11 | CMAKE_C_FLAGS
12 | CMAKE_C_FLAGS_DEBUG
13 | CMAKE_C_FLAGS_MINSIZEREL
14 | CMAKE_C_FLAGS_RELEASE
15 | CMAKE_C_FLAGS_RELWITHDEBINFO
16 | CMAKE_CXX_FLAGS
17 | CMAKE_CXX_FLAGS_DEBUG
18 | CMAKE_CXX_FLAGS_MINSIZEREL
19 | CMAKE_CXX_FLAGS_RELEASE
20 | CMAKE_CXX_FLAGS_RELWITHDEBINFO)
21 |
22 | if(${MSVC_RUNTIME} STREQUAL "static")
23 | message(STATUS "MSVC: using statically-linked runtime (/MT and /MTd).")
24 | foreach(variable ${variables})
25 | if(${variable} MATCHES "/MD")
26 | string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}")
27 | endif()
28 | endforeach()
29 | else()
30 | message(STATUS "MSVC: using dynamically-linked runtime (/MD and /MDd).")
31 | foreach(variable ${variables})
32 | if(${variable} MATCHES "/MT")
33 | string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}")
34 | endif()
35 | endforeach()
36 | endif()
37 |
38 | foreach(variable ${variables})
39 | if(${variable} MATCHES "/Ob0")
40 | string(REGEX REPLACE "/Ob0" "/Ob2" ${variable} "${${variable}}")
41 | endif()
42 | endforeach()
43 |
44 | foreach(variable ${variables})
45 | if(${variable} MATCHES "/W3")
46 | string(REGEX REPLACE "/W3" "/W2" ${variable} "${${variable}}")
47 | endif()
48 | endforeach()
49 |
50 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")
51 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
52 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
53 | set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}")
54 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
55 |
56 | foreach(variable ${variables})
57 | set(${variable} "${${variable}}" CACHE STRING "MSVC_${variable}" FORCE)
58 | endforeach()
59 | endif()
60 | endmacro(configure_msvc_runtime)
61 |
--------------------------------------------------------------------------------
/cmake/WindowsRC.cmake:
--------------------------------------------------------------------------------
1 |
2 | function(windows_rc_generate_version_info)
3 |
4 | set(RC_ARGS_OPTIONS "")
5 | set(RC_ARGS_SINGLE "NAME;TYPE;VERSION;VENDOR;FILENAME;COPYRIGHT;OUTPUT")
6 | set(RC_ARGS_MULTI "")
7 |
8 | cmake_parse_arguments(RC "${RC_ARGS_OPTIONS}" "${RC_ARGS_SINGLE}" "${RC_ARGS_MULTI}" ${ARGN})
9 |
10 | if(NOT DEFINED RC_TYPE OR "${RC_TYPE}" STREQUAL "")
11 | set(RC_TYPE "APP")
12 | endif()
13 |
14 | if(NOT DEFINED RC_VERSION OR "${RC_VERSION}" STREQUAL "")
15 | set(RC_VERSION "1.0.0")
16 | endif()
17 |
18 | # File Version
19 |
20 | if(NOT DEFINED RC_FILE_VERSION OR "${RC_FILE_VERSION}" STREQUAL "")
21 | set(RC_FILE_VERSION ${RC_VERSION})
22 | endif()
23 |
24 | string(REPLACE "." ";" RC_FILE_VERSION_NUMBERS ${RC_FILE_VERSION})
25 | list(LENGTH RC_FILE_VERSION_NUMBERS RC_FILE_VERSION_LENGTH)
26 |
27 | if(RC_FILE_VERSION_LENGTH LESS 4)
28 | list(APPEND RC_FILE_VERSION_NUMBERS "0")
29 | endif()
30 |
31 | string(REPLACE ";" "," RC_FILE_VERSION_NUMBER "${RC_FILE_VERSION_NUMBERS}")
32 | string(REPLACE ";" "." RC_FILE_VERSION_STRING "${RC_FILE_VERSION_NUMBERS}")
33 |
34 | # Product Version
35 |
36 | if(NOT DEFINED RC_PRODUCT_VERSION OR "${RC_PRODUCT_VERSION}" STREQUAL "")
37 | set(RC_PRODUCT_VERSION ${RC_VERSION})
38 | endif()
39 |
40 | string(REPLACE "." ";" RC_PRODUCT_VERSION_NUMBERS ${RC_PRODUCT_VERSION})
41 | list(LENGTH RC_PRODUCT_VERSION_NUMBERS RC_PRODUCT_VERSION_LENGTH)
42 |
43 | if(RC_PRODUCT_VERSION_LENGTH LESS 4)
44 | list(APPEND RC_PRODUCT_VERSION_NUMBERS "0")
45 | endif()
46 |
47 | string(REPLACE ";" "," RC_PRODUCT_VERSION_NUMBER "${RC_PRODUCT_VERSION_NUMBERS}")
48 | string(REPLACE ";" "." RC_PRODUCT_VERSION_STRING "${RC_PRODUCT_VERSION_NUMBERS}")
49 |
50 | set(RC_FILEOS "0x40004L")
51 |
52 | if(RC_TYPE STREQUAL "APP")
53 | set(RC_FILETYPE "0x1L") # VFT_APP
54 | elseif(RC_TYPE STREQUAL "DLL")
55 | set(RC_FILETYPE "0x2L") # VFT_DLL
56 | elseif(RC_TYPE STREQUAL "DRV")
57 | set(RC_FILETYPE "0x3L") # VFT_DRV
58 | else()
59 | set(RC_FILETYPE "0x0L") # VFT_UNKNOWN
60 | endif()
61 |
62 | set(RC_FILESUBTYPE "0x0L")
63 |
64 | set(RC_COMPANY_NAME "${RC_VENDOR}")
65 | set(RC_FILE_DESCRIPTION "${RC_NAME}")
66 | set(RC_PRODUCT_NAME "${RC_NAME}")
67 | set(RC_INTERNAL_NAME ${RC_FILENAME})
68 | set(RC_ORIGINAL_FILENAME ${RC_FILENAME})
69 | set(RC_LEGAL_COPYRIGHT ${RC_COPYRIGHT})
70 |
71 | # VERSIONINFO resource
72 | # https://msdn.microsoft.com/en-us/library/windows/desktop/aa381058
73 |
74 | set(LINES "")
75 | list(APPEND LINES "")
76 | list(APPEND LINES "#include ")
77 | list(APPEND LINES "")
78 | list(APPEND LINES "VS_VERSION_INFO VERSIONINFO")
79 | list(APPEND LINES " FILEVERSION ${RC_FILE_VERSION_NUMBER}")
80 | list(APPEND LINES " PRODUCTVERSION ${RC_PRODUCT_VERSION_NUMBER}")
81 | list(APPEND LINES " FILEFLAGSMASK 0x3fL")
82 | list(APPEND LINES "#ifdef _DEBUG")
83 | list(APPEND LINES " FILEFLAGS 0x1L")
84 | list(APPEND LINES "#else")
85 | list(APPEND LINES " FILEFLAGS 0x0L")
86 | list(APPEND LINES "#endif")
87 | list(APPEND LINES " FILEOS ${RC_FILEOS}")
88 | list(APPEND LINES " FILETYPE ${RC_FILETYPE}")
89 | list(APPEND LINES " FILESUBTYPE ${RC_FILESUBTYPE}")
90 | list(APPEND LINES "BEGIN")
91 | list(APPEND LINES " BLOCK \"StringFileInfo\"")
92 | list(APPEND LINES " BEGIN")
93 | list(APPEND LINES " BLOCK \"040904b0\"")
94 | list(APPEND LINES " BEGIN")
95 | list(APPEND LINES " VALUE \"CompanyName\", \"${RC_COMPANY_NAME}\"")
96 | list(APPEND LINES " VALUE \"FileDescription\", \"${RC_FILE_DESCRIPTION}\"")
97 | list(APPEND LINES " VALUE \"FileVersion\", \"${RC_FILE_VERSION_STRING}\"")
98 | list(APPEND LINES " VALUE \"InternalName\", \"${RC_INTERNAL_NAME}\"")
99 | list(APPEND LINES " VALUE \"LegalCopyright\", \"${RC_LEGAL_COPYRIGHT}\"")
100 | list(APPEND LINES " VALUE \"OriginalFilename\", \"${RC_ORIGINAL_FILENAME}\"")
101 | list(APPEND LINES " VALUE \"ProductName\", \"${RC_PRODUCT_NAME}\"")
102 | list(APPEND LINES " VALUE \"ProductVersion\", \"${RC_PRODUCT_VERSION_STRING}\"")
103 | list(APPEND LINES " END")
104 | list(APPEND LINES " END")
105 | list(APPEND LINES " BLOCK \"VarFileInfo\"")
106 | list(APPEND LINES " BEGIN")
107 | list(APPEND LINES " VALUE \"Translation\", 0x409, 1200")
108 | list(APPEND LINES " END")
109 | list(APPEND LINES "END")
110 | list(APPEND LINES "")
111 |
112 | set(RC_FILE "")
113 | foreach(LINE ${LINES})
114 | set(RC_FILE "${RC_FILE}${LINE}\n")
115 | endforeach()
116 |
117 | file(WRITE "${RC_OUTPUT}" ${RC_FILE})
118 | endfunction()
119 |
--------------------------------------------------------------------------------
/com/.gitignore:
--------------------------------------------------------------------------------
1 | mstscax.dll
2 | mstscax.tlb
3 | rdclientax.dll
4 | AxMSTSCLib.dll
5 | AxMSTSCLib.pdb
6 | AxMSTSCLib.cs
--------------------------------------------------------------------------------
/com/MSTSCLib.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devolutions/MsRdpEx/c66219fb32d03d2fdadd6b631efb0376ffe8db88/com/MSTSCLib.dll
--------------------------------------------------------------------------------
/com/README.md:
--------------------------------------------------------------------------------
1 |
2 | Open a Visual Studio Command Prompt with PowerShell.
3 |
4 | Copy mstscax.dll (or rdclientax.dll) in the current directory:
5 |
6 | ```powershell
7 | cp "$Env:SystemRoot\System32\mstscax.dll" .
8 | ```
9 |
10 | Use the MSVC compiler built-in type library importer:
11 |
12 | ```powershell
13 | echo '#import "mstscax.dll" named_guids' > import.cpp
14 | cl.exe /c /nologo .\import.cpp && rm .\import.*
15 | (Get-Content mstscax.tlh | Select-Object -Skip 7) | Set-Content mstscax.tlh
16 | (Get-Content mstscax.tli | Select-Object -Skip 7) | Set-Content mstscax.tli
17 | ```
18 |
19 | Generate C# import library:
20 |
21 | ```powershell
22 | tlbimp.exe /machine:Agnostic /out:Interop.MSTSCLib.dll /namespace:MSTSCLib .\mstscax.dll
23 | aximp.exe /source /rcw:Interop.MSTSCLib.dll /out:AxInterop.MSTSCLib.dll .\mstscax.dll
24 | (Get-Content AxInterop.MSTSCLib.cs | Select-Object -Skip 10) | Set-Content AxInterop.MSTSCLib.cs
25 | ```
26 |
27 | Generate interface definition file (IDL) from type library:
28 |
29 | * Launch OleView.exe from an elevated command prompt
30 | * File -> View TypeLib, then select mstscax.dll
31 | * File -> Save As..., use file name mstscax.idl
32 |
33 | The resulting .IDL file needs to be manually edited to fix type declaration ordering before midl.exe can compile it again into a .tlb file.
34 |
35 | ```powershell
36 | midl.exe .\mstscax.idl /notlb /header mstscax.h /iid mstscax_i.c
37 | ```
38 |
39 | In 32-bit mode, the generated headers define UINT_PTR and LONG_PTR types that can cause conflicts, use this code snippet to comment them out:
40 |
41 | ```powershell
42 | 'mstscax.tlh', 'mstscax.h' | ForEach-Object {
43 | $file = $_
44 | (Get-Content $file) | ForEach-Object {
45 | if ($_ -match '^\s*typedef\s+.*?_PTR;') {
46 | '// ' + $_
47 | } else {
48 | $_
49 | }
50 | } | Set-Content $file
51 | }
52 | ```
53 |
--------------------------------------------------------------------------------
/com/mstscax.idl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devolutions/MsRdpEx/c66219fb32d03d2fdadd6b631efb0376ffe8db88/com/mstscax.idl
--------------------------------------------------------------------------------
/detours.ps1:
--------------------------------------------------------------------------------
1 | param(
2 | [string] $GitRepo = 'https://github.com/microsoft/Detours',
3 | [string] $GitCommit = '4b8c659' # July 24, 2023
4 | )
5 |
6 | $RepoRoot = $PSScriptRoot
7 |
8 | $SourcesDir = "$RepoRoot/dependencies/sources"
9 | New-Item -ItemType Directory -Path $SourcesDir -ErrorAction SilentlyContinue | Out-Null
10 |
11 | Push-Location
12 | Set-Location $SourcesDir
13 |
14 | if (-Not (Test-Path -Path "detours")) {
15 | & 'git' 'clone' $GitRepo 'detours'
16 | }
17 |
18 | Set-Location "detours/src"
19 |
20 | & 'git' 'checkout' $GitCommit
21 |
22 | if (-Not (Test-Path Env:VSCMD_ARG_TGT_ARCH)) {
23 | throw "VSCMD_ARG_TGT_ARCH is not set, use a Visual Studio developer shell"
24 | }
25 |
26 | Write-Host "Building Detours for $Env:VSCMD_ARG_TGT_ARCH"
27 |
28 | $TargetArchs = @($Env:VSCMD_ARG_TGT_ARCH)
29 | $TargetConfs = @('Release', 'Debug')
30 |
31 | foreach ($TargetArch in $TargetArchs) {
32 | $Env:VSCMD_ARG_TGT_ARCH="$TargetArch"
33 | $Env:DETOURS_TARGET_PROCESSOR="$($TargetArch.ToUpper())"
34 | & nmake clean
35 | foreach ($TargetConf in $TargetConfs) {
36 | $Env:DETOURS_CONFIG="$TargetConf"
37 | & nmake
38 | }
39 | }
40 |
41 | Set-Location ".."
42 |
43 | $PkgDir = "$RepoRoot/dependencies/detours"
44 | New-Item -ItemType Directory -Path $PkgDir -ErrorAction SilentlyContinue | Out-Null
45 | New-Item -ItemType Directory -Path "$PkgDir/lib" -ErrorAction SilentlyContinue | Out-Null
46 |
47 | Remove-Item -Path "$PkgDir/include" -Recurse -ErrorAction SilentlyContinue | Out-Null
48 | Copy-Item "include" -Destination "$PkgDir/include" -Exclude @("syelog.h") -Recurse
49 |
50 | foreach ($TargetArch in $TargetArchs) {
51 | Remove-Item -Path "$PkgDir/lib/$TargetArch" -Recurse -ErrorAction SilentlyContinue | Out-Null
52 | New-Item -ItemType Directory -Path "$PkgDir/lib/$TargetArch" -ErrorAction SilentlyContinue | Out-Null
53 | foreach ($TargetConf in $TargetConfs) {
54 | $LibDir = "lib." + $TargetArch.ToUpper() + $TargetConf
55 | Write-Host "Copying $LibDir to "$PkgDir/lib/$TargetArch/$TargetConf""
56 | New-Item -ItemType Directory -Path "$PkgDir/lib/$TargetArch/$TargetConf" | Out-Null
57 | Copy-Item "$LibDir/*" -Destination "$PkgDir/lib/$TargetArch/$TargetConf" -Recurse
58 | }
59 | }
60 |
61 | Pop-Location
62 |
--------------------------------------------------------------------------------
/dll/AxDll.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "MsRdpEx.h"
3 |
4 | #include "MsRdpClient.h"
5 |
6 | #include
7 |
8 | #include
9 |
10 | extern bool g_AxHookEnabled;
11 |
12 | HRESULT CDECL MsRdpEx_AxDll_DllGetClassObject(fnDllGetClassObject pfnDllGetClassObject, REFCLSID rclsid, REFIID riid, LPVOID* ppv)
13 | {
14 | HRESULT hr = pfnDllGetClassObject(rclsid, riid, ppv);
15 |
16 | if (!MsRdpEx_GetAxHookEnabled())
17 | return hr;
18 |
19 | if (riid == IID_IClassFactory)
20 | {
21 | if (hr == S_OK)
22 | {
23 | *ppv = MsRdpEx_CClassFactory_New(rclsid, (IClassFactory*) *ppv);
24 | }
25 | }
26 |
27 | return hr;
28 | }
29 |
30 | bool CDECL MsRdpEx_mstscax_Load(MsRdpEx_mstscax* dll, const char* filename)
31 | {
32 | bool success = false;
33 |
34 | if (dll->initialized)
35 | return true;
36 |
37 | ZeroMemory(dll, sizeof(MsRdpEx_mstscax));
38 |
39 | dll->hModule = MsRdpEx_LoadLibrary(filename);
40 |
41 | MsRdpEx_LogPrint(DEBUG, "mstscax_load(%s): %p", filename, dll->hModule);
42 |
43 | if (!dll->hModule)
44 | goto exit;
45 |
46 | dll->DllCanUnloadNow = (fnDllCanUnloadNow)GetProcAddress(dll->hModule, "DllCanUnloadNow");
47 | dll->DllGetClassObject = (fnDllGetClassObject)GetProcAddress(dll->hModule, "DllGetClassObject");
48 | dll->DllRegisterServer = (fnDllRegisterServer)GetProcAddress(dll->hModule, "DllRegisterServer");
49 | dll->DllUnregisterServer = (fnDllUnregisterServer)GetProcAddress(dll->hModule, "DllUnregisterServer");
50 | dll->DllGetTscCtlVer = (fnDllGetTscCtlVer)GetProcAddress(dll->hModule, "DllGetTscCtlVer");
51 | dll->DllSetAuthProperties = (fnDllSetAuthProperties)GetProcAddress(dll->hModule, "DllSetAuthProperties");
52 | dll->DllGetClaimsToken = (fnDllGetClaimsToken9)GetProcAddress(dll->hModule, "DllGetClaimsToken");
53 | dll->DllSetClaimsToken = (fnDllSetClaimsToken)GetProcAddress(dll->hModule, "DllSetClaimsToken");
54 | dll->DllLogoffClaimsToken = (fnDllLogoffClaimsToken1)GetProcAddress(dll->hModule, "DllLogoffClaimsToken");
55 | dll->DllCancelAuthentication = (fnDllCancelAuthentication)GetProcAddress(dll->hModule, "DllCancelAuthentication");
56 | dll->DllDeleteSavedCreds = (fnDllDeleteSavedCreds)GetProcAddress(dll->hModule, "DllDeleteSavedCreds");
57 |
58 | dll->tscCtlVer = dll->DllGetTscCtlVer();
59 |
60 | success = true;
61 | exit:
62 | dll->initialized = true;
63 | return success;
64 | }
65 |
66 | bool CDECL MsRdpEx_mstscax_Init(MsRdpEx_mstscax* dll)
67 | {
68 | bool success = false;
69 | char* axPath = NULL;
70 |
71 | if (dll->initialized)
72 | return true;
73 |
74 | axPath = MsRdpEx_GetEnv("MSRDPEX_MSTSCAX_DLL");
75 |
76 | if (!axPath)
77 | axPath = _strdup(MsRdpEx_GetPath(MSRDPEX_MSTSCAX_DLL_PATH));
78 |
79 | if (!axPath)
80 | goto exit;
81 |
82 | success = MsRdpEx_mstscax_Load(dll, axPath);
83 | exit:
84 | free(axPath);
85 | return success;
86 | }
87 |
88 | void CDECL MsRdpEx_mstscax_Uninit(MsRdpEx_mstscax* dll)
89 | {
90 | if (dll->hModule) {
91 | FreeLibrary(dll->hModule);
92 | dll->hModule = NULL;
93 | }
94 |
95 | ZeroMemory(dll, sizeof(MsRdpEx_mstscax));
96 | }
97 |
98 | bool CDECL MsRdpEx_rdclientax_Load(MsRdpEx_rdclientax* dll, const char* filename)
99 | {
100 | bool success = false;
101 |
102 | if (dll->initialized)
103 | return true;
104 |
105 | ZeroMemory(dll, sizeof(MsRdpEx_rdclientax));
106 |
107 | dll->hModule = MsRdpEx_LoadLibrary(filename);
108 |
109 | MsRdpEx_LogPrint(DEBUG, "rdclientax_load(%s): %p", filename, dll->hModule);
110 |
111 | if (!dll->hModule)
112 | goto exit;
113 |
114 | dll->DllCanUnloadNow = (fnDllCanUnloadNow)GetProcAddress(dll->hModule, "DllCanUnloadNow");
115 | dll->DllGetClassObject = (fnDllGetClassObject)GetProcAddress(dll->hModule, "DllGetClassObject");
116 | dll->DllRegisterServer = (fnDllRegisterServer)GetProcAddress(dll->hModule, "DllRegisterServer");
117 | dll->DllUnregisterServer = (fnDllUnregisterServer)GetProcAddress(dll->hModule, "DllUnregisterServer");
118 | dll->DllGetTscCtlVer = (fnDllGetTscCtlVer)GetProcAddress(dll->hModule, "DllGetTscCtlVer");
119 | dll->DllGetNewActivityId = (fnDllGetNewActivityId)GetProcAddress(dll->hModule, "DllGetNewActivityId");
120 | dll->DllSetAuthProperties = (fnDllSetAuthProperties)GetProcAddress(dll->hModule, "DllSetAuthProperties");
121 | dll->DllGetClaimsToken = (fnDllGetClaimsToken19)GetProcAddress(dll->hModule, "DllGetClaimsToken");
122 | dll->DllSetClaimsToken = (fnDllSetClaimsToken)GetProcAddress(dll->hModule, "DllSetClaimsToken");
123 | dll->DllLogoffClaimsToken = (fnDllLogoffClaimsToken3)GetProcAddress(dll->hModule, "DllLogoffClaimsToken");
124 | dll->DllCancelAuthentication = (fnDllCancelAuthentication)GetProcAddress(dll->hModule, "DllCancelAuthentication");
125 | dll->DllDeleteSavedCreds = (fnDllDeleteSavedCreds)GetProcAddress(dll->hModule, "DllDeleteSavedCreds");
126 | dll->DllPreCleanUp = (fnDllPreCleanUp)GetProcAddress(dll->hModule, "DllPreCleanUp");
127 |
128 | dll->tscCtlVer = dll->DllGetTscCtlVer();
129 |
130 | success = true;
131 | exit:
132 | dll->initialized = true;
133 | return success;
134 | }
135 |
136 | bool CDECL MsRdpEx_rdclientax_Init(MsRdpEx_rdclientax* dll)
137 | {
138 | bool success = false;
139 | char* axPath = NULL;
140 |
141 | if (dll->initialized)
142 | return true;
143 |
144 | axPath = MsRdpEx_GetEnv("MSRDPEX_RDCLIENTAX_DLL");
145 |
146 | if (!axPath)
147 | axPath = _strdup(MsRdpEx_GetPath(MSRDPEX_RDCLIENTAX_DLL_PATH));
148 |
149 | if (!axPath)
150 | goto exit;
151 |
152 | success = MsRdpEx_rdclientax_Load(dll, axPath);
153 | exit:
154 | free(axPath);
155 | return success;
156 | }
157 |
158 | void CDECL MsRdpEx_rdclientax_Uninit(MsRdpEx_rdclientax* dll)
159 | {
160 | if (dll->hModule) {
161 | FreeLibrary(dll->hModule);
162 | dll->hModule = NULL;
163 | }
164 |
165 | ZeroMemory(dll, sizeof(MsRdpEx_rdclientax));
166 | }
167 |
--------------------------------------------------------------------------------
/dll/AxHost/RdpAxHostWnd.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_AX_HOST_H
2 | #define MSRDPEX_AX_HOST_H
3 |
4 | #include
5 |
6 | int MsRdpEx_AxHost_WinMain(
7 | HINSTANCE hInstance,
8 | HINSTANCE hPrevInstance,
9 | LPWSTR lpCmdLine,
10 | int nCmdShow);
11 |
12 | #endif /* MSRDPEX_AX_HOST_H */
--------------------------------------------------------------------------------
/dll/AxHost/RdpComBase.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_COM_BASE_H
2 | #define MSRDPEX_COM_BASE_H
3 |
4 | #include "../ComHelpers.h"
5 |
6 | #include "../com/mstscax.tlh"
7 | using namespace MSTSCLib;
8 |
9 | #endif /* MSRDPEX_COM_BASE_H */
10 |
--------------------------------------------------------------------------------
/dll/AxHost/RdpEventSink.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "RdpEventSink.h"
3 |
4 | // IUnknown methods
5 | STDMETHODIMP CRdpEventSink::QueryInterface(REFIID riid, void** ppv)
6 | {
7 | HRESULT hr = S_OK;
8 |
9 | if (!ppv)
10 | return E_INVALIDARG;
11 |
12 | *ppv = NULL;
13 |
14 | if (riid == IID_IUnknown) {
15 | *ppv = this;
16 | }
17 | else if (riid == IID_IDispatch) {
18 | *ppv = this;
19 | }
20 | else if (riid == DIID_IMsTscAxEvents) {
21 | *ppv = this;
22 | }
23 |
24 | if (nullptr != *ppv) {
25 | ((IUnknown*)*ppv)->AddRef();
26 | }
27 | else {
28 | hr = E_NOINTERFACE;
29 | }
30 |
31 | return hr;
32 | }
33 |
34 | STDMETHODIMP_(ULONG) CRdpEventSink::AddRef(void)
35 | {
36 | return InterlockedIncrement(&m_refCount);
37 | }
38 |
39 | STDMETHODIMP_(ULONG) CRdpEventSink::Release(void)
40 | {
41 | ULONG refCount = InterlockedDecrement(&m_refCount);
42 |
43 | if (refCount != 0) {
44 | return refCount;
45 | }
46 |
47 | delete this;
48 | return 0;
49 | }
50 |
51 | // IDispatch methods
52 | STDMETHODIMP CRdpEventSink::GetTypeInfoCount(UINT* pctinfo)
53 | {
54 | *pctinfo = 0;
55 | return S_OK;
56 | }
57 |
58 | STDMETHODIMP CRdpEventSink::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
59 | {
60 | return E_NOTIMPL;
61 | }
62 |
63 | STDMETHODIMP CRdpEventSink::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
64 | LCID lcid, DISPID* rgDispId)
65 | {
66 | return E_NOTIMPL;
67 | }
68 |
69 | STDMETHODIMP CRdpEventSink::Invoke(DISPID dispIdMember,
70 | REFIID riid, LCID lcid, WORD wFlags,
71 | DISPPARAMS* pDispParams, VARIANT* pVarResult,
72 | EXCEPINFO* pExcepInfo, UINT* puArgErr)
73 | {
74 | HRESULT hr = E_NOTIMPL;
75 |
76 | switch (dispIdMember)
77 | {
78 | case IMsTscAxEvents_OnConnectingId:
79 | hr = OnConnecting();
80 | break;
81 |
82 | case IMsTscAxEvents_OnConnectedId:
83 | hr = OnConnected();
84 | break;
85 |
86 | case IMsTscAxEvents_OnLoginCompleteId:
87 | hr = OnLoginComplete();
88 | break;
89 |
90 | case IMsTscAxEvents_OnDisconnectedId:
91 | hr = OnDisconnected(pDispParams->rgvarg->lVal);
92 | break;
93 |
94 | case IMsTscAxEvents_OnEnterFullScreenModeId:
95 | hr = OnEnterFullScreenMode();
96 | break;
97 |
98 | case IMsTscAxEvents_OnLeaveFullScreenModeId:
99 | hr = OnLeaveFullScreenMode();
100 | break;
101 |
102 | case IMsTscAxEvents_OnRemoteDesktopSizeChangeId:
103 | hr = OnRemoteDesktopSizeChange(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0].lVal);
104 | break;
105 |
106 | case IMsTscAxEvents_OnRequestContainerMinimizeId:
107 | hr = OnRequestContainerMinimize();
108 | break;
109 |
110 | case IMsTscAxEvents_OnConfirmCloseId:
111 | hr = OnConfirmClose(pDispParams->rgvarg[0].pboolVal);
112 | break;
113 | }
114 |
115 | return hr;
116 | }
117 |
118 | // IMsTscAxEvents methods
119 | STDMETHODIMP CRdpEventSink::OnConnecting()
120 | {
121 | return S_OK;
122 | }
123 |
124 | STDMETHODIMP CRdpEventSink::OnConnected()
125 | {
126 | return S_OK;
127 | }
128 |
129 | STDMETHODIMP CRdpEventSink::OnLoginComplete()
130 | {
131 | return S_OK;
132 | }
133 |
134 | STDMETHODIMP CRdpEventSink::OnDisconnected(long discReason)
135 | {
136 | return S_OK;
137 | }
138 |
139 | STDMETHODIMP CRdpEventSink::OnEnterFullScreenMode()
140 | {
141 | return S_OK;
142 | }
143 |
144 | STDMETHODIMP CRdpEventSink::OnLeaveFullScreenMode()
145 | {
146 | return S_OK;
147 | }
148 |
149 | STDMETHODIMP CRdpEventSink::OnRemoteDesktopSizeChange(long width, long height)
150 | {
151 | return S_OK;
152 | }
153 |
154 | STDMETHODIMP CRdpEventSink::OnRequestContainerMinimize()
155 | {
156 | return S_OK;
157 | }
158 |
159 | STDMETHODIMP CRdpEventSink::OnConfirmClose(VARIANT_BOOL* pfAllowClose)
160 | {
161 | *pfAllowClose = VARIANT_TRUE;
162 | return S_OK;
163 | }
164 |
165 | CRdpEventSink::CRdpEventSink(HWND hWndParent)
166 | {
167 | m_refCount = 0;
168 | m_hWndParent = hWndParent;
169 | }
170 |
171 | CRdpEventSink::~CRdpEventSink()
172 | {
173 |
174 | }
175 |
--------------------------------------------------------------------------------
/dll/AxHost/RdpEventSink.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_EVENT_SINK_H
2 | #define MSRDPEX_EVENT_SINK_H
3 |
4 | #include
5 |
6 | #include "RdpComBase.h"
7 |
8 | #define IMsTscAxEvents_OnConnectingId 0x00000001
9 | #define IMsTscAxEvents_OnConnectedId 0x00000002
10 | #define IMsTscAxEvents_OnLoginCompleteId 0x00000003
11 | #define IMsTscAxEvents_OnDisconnectedId 0x00000004
12 | #define IMsTscAxEvents_OnEnterFullScreenModeId 0x00000005
13 | #define IMsTscAxEvents_OnLeaveFullScreenModeId 0x00000006
14 | #define IMsTscAxEvents_OnChannelReceivedDataId 0x00000007
15 | #define IMsTscAxEvents_OnRequestGoFullScreenId 0x00000008
16 | #define IMsTscAxEvents_OnRequestLeaveFullScreenId 0x00000009
17 | #define IMsTscAxEvents_OnFatalErrorId 0x0000000a
18 | #define IMsTscAxEvents_OnWarningId 0x0000000b
19 | #define IMsTscAxEvents_OnRemoteDesktopSizeChangeId 0x0000000c
20 | #define IMsTscAxEvents_OnIdleTimeoutNotificationId 0x0000000d
21 | #define IMsTscAxEvents_OnRequestContainerMinimizeId 0x0000000e
22 | #define IMsTscAxEvents_OnConfirmCloseId 0x0000000f
23 | #define IMsTscAxEvents_OnReceivedTSPublicKeyId 0x00000010
24 | #define IMsTscAxEvents_OnAutoReconnectingId 0x00000011
25 | #define IMsTscAxEvents_OnAuthenticationWarningDisplayedId 0x00000012
26 | #define IMsTscAxEvents_OnAuthenticationWarningDismissedId 0x00000013
27 | #define IMsTscAxEvents_OnRemoteProgramResultId 0x00000014
28 | #define IMsTscAxEvents_OnRemoteProgramDisplayedId 0x00000015
29 | #define IMsTscAxEvents_OnRemoteWindowDisplayedId 0x00000016
30 | #define IMsTscAxEvents_OnLogonErrorId 0x00000017
31 | #define IMsTscAxEvents_OnFocusReleasedId 0x00000018
32 | #define IMsTscAxEvents_OnUserNameAcquiredId 0x00000019
33 | #define IMsTscAxEvents_OnMouseInputModeChangedId 0x0000001a
34 | #define IMsTscAxEvents_OnServiceMessageReceivedId 0x0000001b
35 | #define IMsTscAxEvents_OnConnectionBarPullDownId 0x0000001c
36 | #define IMsTscAxEvents_OnNetworkStatusChangedId 0x0000001d
37 | #define IMsTscAxEvents_OnDevicesButtonPressedId 0x0000001e
38 | #define IMsTscAxEvents_OnAutoReconnectedId 0x0000001f
39 | #define IMsTscAxEvents_OnAutoReconnecting2Id 0x00000020
40 |
41 | class CRdpEventSink : public IMsTscAxEvents
42 | {
43 | public:
44 | // Constructor and Destructor
45 | CRdpEventSink(HWND hWndParent);
46 | virtual ~CRdpEventSink();
47 |
48 | // IUnknown methods
49 | STDMETHOD(QueryInterface)(REFIID riid, void** ppv) override;
50 | STDMETHOD_(ULONG, AddRef)(void) override;
51 | STDMETHOD_(ULONG, Release)(void) override;
52 |
53 | // IDispatch methods
54 | STDMETHOD(GetTypeInfoCount)(UINT* pctinfo) override;
55 | STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) override;
56 | STDMETHOD(GetIDsOfNames)(REFIID riid,
57 | LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) override;
58 | STDMETHOD(Invoke)(DISPID dispIdMember,
59 | REFIID riid, LCID lcid, WORD wFlags,
60 | DISPPARAMS* pDispParams, VARIANT* pVarResult,
61 | EXCEPINFO* pExcepInfo, UINT* puArgErr) override;
62 |
63 | // IMsTscAxEvents methods
64 | STDMETHOD(OnConnecting)(void);
65 | STDMETHOD(OnConnected)(void);
66 | STDMETHOD(OnLoginComplete)(void);
67 | STDMETHOD(OnDisconnected)(long discReason);
68 | STDMETHOD(OnEnterFullScreenMode)(void);
69 | STDMETHOD(OnLeaveFullScreenMode)(void);
70 | STDMETHOD(OnRemoteDesktopSizeChange)(long width, long height);
71 | STDMETHOD(OnRequestContainerMinimize)(void);
72 | STDMETHOD(OnConfirmClose)(VARIANT_BOOL* pfAllowClose);
73 |
74 | private:
75 | ULONG m_refCount;
76 | HWND m_hWndParent;
77 | };
78 |
79 | #endif /* MSRDPEX_EVENT_SINK_H */
--------------------------------------------------------------------------------
/dll/AxHost/RdpOleSite.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "RdpOleSite.h"
3 |
4 | CRdpOleClientSite::CRdpOleClientSite(IUnknown* pUnkOuter)
5 | {
6 | m_refCount = 0;
7 | m_pUnkOuter = pUnkOuter;
8 | m_pUnkOuter->AddRef();
9 | }
10 |
11 | CRdpOleClientSite::~CRdpOleClientSite()
12 | {
13 | SafeRelease(m_pUnkOuter);
14 | }
15 |
16 | // IUnknown methods
17 | STDMETHODIMP CRdpOleClientSite::QueryInterface(REFIID riid, void** ppv)
18 | {
19 | HRESULT hr = S_OK;
20 |
21 | if (!ppv)
22 | return E_INVALIDARG;
23 |
24 | *ppv = NULL;
25 |
26 | if (riid == IID_IUnknown) {
27 | *ppv = static_cast(this);
28 | }
29 | else if (riid == IID_IOleClientSite) {
30 | *ppv = static_cast(this);
31 | }
32 | else if (m_pUnkOuter) {
33 | return m_pUnkOuter->QueryInterface(riid, ppv);
34 | }
35 | else {
36 | hr = E_NOINTERFACE;
37 | }
38 |
39 | if (*ppv) {
40 | AddRef();
41 | }
42 |
43 | return hr;
44 | }
45 |
46 | STDMETHODIMP_(ULONG) CRdpOleClientSite::AddRef()
47 | {
48 | return InterlockedIncrement(&m_refCount);
49 | }
50 |
51 | STDMETHODIMP_(ULONG) CRdpOleClientSite::Release()
52 | {
53 | ULONG refCount = InterlockedDecrement(&m_refCount);
54 | if (refCount == 0) {
55 | delete this;
56 | }
57 | return refCount;
58 | }
59 |
60 | // IOleClientSite methods
61 | STDMETHODIMP CRdpOleClientSite::SaveObject()
62 | {
63 | return S_OK;
64 | }
65 |
66 | STDMETHODIMP CRdpOleClientSite::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk)
67 | {
68 | *ppmk = NULL;
69 | return E_NOTIMPL;
70 | }
71 |
72 | STDMETHODIMP CRdpOleClientSite::GetContainer(IOleContainer** ppContainer)
73 | {
74 | *ppContainer = NULL;
75 | return E_NOTIMPL;
76 | }
77 |
78 | STDMETHODIMP CRdpOleClientSite::ShowObject()
79 | {
80 | return S_OK;
81 | }
82 |
83 | STDMETHODIMP CRdpOleClientSite::OnShowWindow(BOOL fShow)
84 | {
85 | return S_OK;
86 | }
87 |
88 | STDMETHODIMP CRdpOleClientSite::RequestNewObjectLayout()
89 | {
90 | return E_NOTIMPL;
91 | }
92 |
93 | CRdpOleInPlaceSiteEx::CRdpOleInPlaceSiteEx(IUnknown* pUnkOuter)
94 | : m_refCount(0), m_hWnd(0)
95 | {
96 | m_pUnkOuter = pUnkOuter;
97 | m_pUnkOuter->AddRef();
98 | }
99 |
100 | CRdpOleInPlaceSiteEx::~CRdpOleInPlaceSiteEx()
101 | {
102 | SafeRelease(m_pUnkOuter);
103 | }
104 |
105 | // IUnknown methods
106 | STDMETHODIMP CRdpOleInPlaceSiteEx::QueryInterface(REFIID riid, void** ppv)
107 | {
108 | HRESULT hr = S_OK;
109 |
110 | if (!ppv) return E_INVALIDARG;
111 | *ppv = NULL;
112 |
113 | if (riid == IID_IUnknown) {
114 | *ppv = static_cast(this);
115 | }
116 | else if (riid == IID_IOleWindow || riid == IID_IOleInPlaceSite || riid == IID_IOleInPlaceSiteEx) {
117 | *ppv = static_cast(this);
118 | }
119 | else if (m_pUnkOuter) {
120 | return m_pUnkOuter->QueryInterface(riid, ppv);
121 | }
122 | else {
123 | hr = E_NOINTERFACE;
124 | }
125 |
126 | if (*ppv) {
127 | AddRef();
128 | }
129 |
130 | return hr;
131 | }
132 |
133 | STDMETHODIMP_(ULONG) CRdpOleInPlaceSiteEx::AddRef()
134 | {
135 | return InterlockedIncrement(&m_refCount);
136 | }
137 |
138 | STDMETHODIMP_(ULONG) CRdpOleInPlaceSiteEx::Release()
139 | {
140 | ULONG refCount = InterlockedDecrement(&m_refCount);
141 | if (refCount == 0) {
142 | delete this;
143 | }
144 | return refCount;
145 | }
146 |
147 | // IOleWindow methods
148 | STDMETHODIMP CRdpOleInPlaceSiteEx::GetWindow(HWND* phwnd)
149 | {
150 | *phwnd = m_hWnd;
151 | return S_OK;
152 | }
153 |
154 | STDMETHODIMP CRdpOleInPlaceSiteEx::ContextSensitiveHelp(BOOL fEnterMode)
155 | {
156 | return S_OK;
157 | }
158 |
159 | // IOleInPlaceSite methods
160 | STDMETHODIMP CRdpOleInPlaceSiteEx::CanInPlaceActivate()
161 | {
162 | return S_OK;
163 | }
164 |
165 | STDMETHODIMP CRdpOleInPlaceSiteEx::OnInPlaceActivate()
166 | {
167 | return S_OK;
168 | }
169 |
170 | STDMETHODIMP CRdpOleInPlaceSiteEx::OnUIActivate()
171 | {
172 | return S_OK;
173 | }
174 |
175 | STDMETHODIMP CRdpOleInPlaceSiteEx::GetWindowContext(IOleInPlaceFrame** ppFrame, IOleInPlaceUIWindow** ppDoc,
176 | LPRECT lprcPosRect, LPRECT lprcClipRect,
177 | LPOLEINPLACEFRAMEINFO lpFrameInfo)
178 | {
179 | RECT rect;
180 |
181 | *ppFrame = NULL;
182 | *ppDoc = NULL;
183 | lpFrameInfo = NULL;
184 |
185 | if (GetClientRect(m_hWnd, &rect))
186 | {
187 | int width = rect.right - rect.left;
188 | int height = rect.bottom - rect.top;
189 | SetRect(lprcClipRect, 0, 0, width, height);
190 | SetRect(lprcPosRect, 0, 0, width, height);
191 | }
192 |
193 | return S_OK;
194 | }
195 |
196 | STDMETHODIMP CRdpOleInPlaceSiteEx::Scroll(SIZE scrollExtant)
197 | {
198 | return S_OK;
199 | }
200 |
201 | STDMETHODIMP CRdpOleInPlaceSiteEx::OnUIDeactivate(BOOL fUndoable)
202 | {
203 | return S_OK;
204 | }
205 |
206 | STDMETHODIMP CRdpOleInPlaceSiteEx::OnInPlaceDeactivate()
207 | {
208 | return S_OK;
209 | }
210 |
211 | STDMETHODIMP CRdpOleInPlaceSiteEx::DiscardUndoState()
212 | {
213 | return S_OK;
214 | }
215 |
216 | STDMETHODIMP CRdpOleInPlaceSiteEx::DeactivateAndUndo()
217 | {
218 | return S_OK;
219 | }
220 |
221 | STDMETHODIMP CRdpOleInPlaceSiteEx::OnPosRectChange(LPCRECT lprcPosRect)
222 | {
223 | return S_OK;
224 | }
225 |
226 | // IOleInPlaceSiteEx methods
227 | STDMETHODIMP CRdpOleInPlaceSiteEx::OnInPlaceActivateEx(BOOL* pfNoRedraw, DWORD dwFlags)
228 | {
229 | *pfNoRedraw = TRUE;
230 | return S_OK;
231 | }
232 |
233 | STDMETHODIMP CRdpOleInPlaceSiteEx::OnInPlaceDeactivateEx(BOOL fNoRedraw)
234 | {
235 | return S_OK;
236 | }
237 |
238 | STDMETHODIMP CRdpOleInPlaceSiteEx::RequestUIActivate()
239 | {
240 | return S_OK;
241 | }
242 |
243 | // additional methods
244 | STDMETHODIMP CRdpOleInPlaceSiteEx::SetWindow(HWND hWnd)
245 | {
246 | m_hWnd = hWnd;
247 | return S_OK;
248 | }
249 |
--------------------------------------------------------------------------------
/dll/AxHost/RdpOleSite.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_OLE_SITE_H
2 | #define MSRDPEX_OLE_SITE_H
3 |
4 | #include
5 |
6 | #include "RdpComBase.h"
7 |
8 | class CRdpOleClientSite : public IOleClientSite {
9 | public:
10 | // Constructor and Destructor
11 | CRdpOleClientSite(IUnknown* pUnkOuter);
12 | virtual ~CRdpOleClientSite();
13 |
14 | // IUnknown methods
15 | STDMETHOD(QueryInterface)(REFIID riid, void** ppv) override;
16 | STDMETHOD_(ULONG, AddRef)() override;
17 | STDMETHOD_(ULONG, Release)() override;
18 |
19 | // IOleClientSite methods
20 | STDMETHOD(SaveObject)() override;
21 | STDMETHOD(GetMoniker)(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk) override;
22 | STDMETHOD(GetContainer)(IOleContainer** ppContainer) override;
23 | STDMETHOD(ShowObject)() override;
24 | STDMETHOD(OnShowWindow)(BOOL fShow) override;
25 | STDMETHOD(RequestNewObjectLayout)() override;
26 |
27 | private:
28 | ULONG m_refCount;
29 | IUnknown* m_pUnkOuter;
30 | };
31 |
32 | class CRdpOleInPlaceSiteEx : public IOleInPlaceSiteEx {
33 | public:
34 | // Constructor and Destructor
35 | CRdpOleInPlaceSiteEx(IUnknown* pUnkOuter);
36 | virtual ~CRdpOleInPlaceSiteEx();
37 |
38 | // IUnknown methods
39 | STDMETHOD(QueryInterface)(REFIID riid, void** ppv) override;
40 | STDMETHOD_(ULONG, AddRef)() override;
41 | STDMETHOD_(ULONG, Release)() override;
42 |
43 | // IOleWindow methods
44 | STDMETHOD(GetWindow)(HWND* phwnd) override;
45 | STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode) override;
46 |
47 | // IOleInPlaceSite methods
48 | STDMETHOD(CanInPlaceActivate)() override;
49 | STDMETHOD(OnInPlaceActivate)() override;
50 | STDMETHOD(OnUIActivate)() override;
51 | STDMETHOD(GetWindowContext)(IOleInPlaceFrame** ppFrame,
52 | IOleInPlaceUIWindow** ppDoc,
53 | LPRECT lprcPosRect, LPRECT lprcClipRect,
54 | LPOLEINPLACEFRAMEINFO lpFrameInfo) override;
55 | STDMETHOD(Scroll)(SIZE scrollExtant) override;
56 | STDMETHOD(OnUIDeactivate)(BOOL fUndoable) override;
57 | STDMETHOD(OnInPlaceDeactivate)() override;
58 | STDMETHOD(DiscardUndoState)() override;
59 | STDMETHOD(DeactivateAndUndo)() override;
60 | STDMETHOD(OnPosRectChange)(LPCRECT lprcPosRect) override;
61 |
62 | // IOleInPlaceSiteEx methods
63 | STDMETHOD(OnInPlaceActivateEx)(BOOL* pfNoRedraw, DWORD dwFlags) override;
64 | STDMETHOD(OnInPlaceDeactivateEx)(BOOL fNoRedraw) override;
65 | STDMETHOD(RequestUIActivate)() override;
66 |
67 | // Additional methods specific to your implementation
68 | STDMETHOD(SetWindow)(HWND hWnd);
69 |
70 | private:
71 | ULONG m_refCount;
72 | HWND m_hWnd;
73 | IUnknown* m_pUnkOuter;
74 | };
75 |
76 | #endif /* MSRDPEX_OLE_SITE_H */
--------------------------------------------------------------------------------
/dll/AxHost/RdpWinMain.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "RdpAxHostWnd.h"
5 | #include "RdpWinMain.h"
6 |
7 | static LPWSTR ProcessAxHostCommandLine(LPWSTR lpCmdLine, bool* axHost)
8 | {
9 | *axHost = false;
10 | LPCWSTR cmdArg = L"/axhost";
11 | WCHAR* cmdLine = _wcsdup(lpCmdLine);
12 | WCHAR* found = wcsstr(cmdLine, cmdArg);
13 |
14 | if (found != NULL) {
15 | *axHost = true;
16 | memmove(found, found + wcslen(cmdArg),
17 | (wcslen(found + wcslen(cmdArg)) + 1) * sizeof(WCHAR));
18 | }
19 |
20 | return cmdLine;
21 | }
22 |
23 | int MsRdpEx_WinMain(
24 | _In_ HINSTANCE hInstance,
25 | _In_opt_ HINSTANCE hPrevInstance,
26 | _In_ LPWSTR lpCmdLine,
27 | _In_ int nShowCmd,
28 | const char* axName)
29 | {
30 | HRESULT hr;
31 | int exitCode = 0;
32 | bool axHost = false;
33 |
34 | LPWSTR cmdLine = ProcessAxHostCommandLine(lpCmdLine, &axHost);
35 |
36 | if (axHost)
37 | {
38 | MsRdpEx_SetEnv("MSRDPEX_AXNAME", axName);
39 | exitCode = MsRdpEx_AxHost_WinMain(hInstance, hPrevInstance, cmdLine, nShowCmd);
40 | }
41 | else
42 | {
43 | hr = MsRdpEx_LaunchProcess(-1, NULL, NULL, axName);
44 |
45 | if (FAILED(hr)) {
46 | exitCode = -1;
47 | }
48 | }
49 |
50 | free(cmdLine);
51 |
52 | return exitCode;
53 | }
54 |
--------------------------------------------------------------------------------
/dll/AxHost/RdpWinMain.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_WIN_MAIN_H
2 | #define MSRDPEX_WIN_MAIN_H
3 |
4 | #include
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | int MsRdpEx_WinMain(
11 | HINSTANCE hInstance,
12 | HINSTANCE hPrevInstance,
13 | LPWSTR lpCmdLine,
14 | int nCmdShow,
15 | const char* axName);
16 |
17 | #ifdef __cplusplus
18 | }
19 | #endif
20 |
21 | #endif /* MSRDPEX_WIN_MAIN_H */
22 |
--------------------------------------------------------------------------------
/dll/Bitmap.c:
--------------------------------------------------------------------------------
1 |
2 | #include "MsRdpEx.h"
3 |
4 | #include
5 |
6 | #pragma pack(push, 1)
7 |
8 | struct _MSRDPEX_BITMAP_FILE_HEADER
9 | {
10 | uint8_t bfType[2];
11 | uint32_t bfSize;
12 | uint16_t bfReserved1;
13 | uint16_t bfReserved2;
14 | uint32_t bfOffBits;
15 | };
16 | typedef struct _MSRDPEX_BITMAP_FILE_HEADER MSRDPEX_BITMAP_FILE_HEADER;
17 |
18 | struct _MSRDPEX_BITMAP_INFO_HEADER
19 | {
20 | uint32_t biSize;
21 | int32_t biWidth;
22 | int32_t biHeight;
23 | uint16_t biPlanes;
24 | uint16_t biBitCount;
25 | uint32_t biCompression;
26 | uint32_t biSizeImage;
27 | int32_t biXPelsPerMeter;
28 | int32_t biYPelsPerMeter;
29 | uint32_t biClrUsed;
30 | uint32_t biClrImportant;
31 | };
32 | typedef struct _MSRDPEX_BITMAP_INFO_HEADER MSRDPEX_BITMAP_INFO_HEADER;
33 |
34 | struct _MSRDPEX_BITMAP_CORE_HEADER
35 | {
36 | uint32_t bcSize;
37 | uint16_t bcWidth;
38 | uint16_t bcHeight;
39 | uint16_t bcPlanes;
40 | uint16_t bcBitCount;
41 | };
42 | typedef struct _MSRDPEX_BITMAP_CORE_HEADER MSRDPEX_BITMAP_CORE_HEADER;
43 |
44 | #pragma pack(pop)
45 |
46 | bool MsRdpEx_WriteBitmapFile(const char* filename, uint8_t* data, int width, int height, int bpp)
47 | {
48 | FILE* fp;
49 | bool success = true;
50 | MSRDPEX_BITMAP_FILE_HEADER bf;
51 | MSRDPEX_BITMAP_INFO_HEADER bi;
52 |
53 | ZeroMemory(&bf, sizeof(MSRDPEX_BITMAP_FILE_HEADER));
54 | ZeroMemory(&bi, sizeof(MSRDPEX_BITMAP_INFO_HEADER));
55 |
56 | fp = fopen(filename, "w+b");
57 |
58 | if (!fp)
59 | return false;
60 |
61 | bf.bfType[0] = 'B';
62 | bf.bfType[1] = 'M';
63 | bf.bfReserved1 = 0;
64 | bf.bfReserved2 = 0;
65 | bf.bfOffBits = sizeof(MSRDPEX_BITMAP_FILE_HEADER) + sizeof(MSRDPEX_BITMAP_INFO_HEADER);
66 | bi.biSizeImage = width * height * (bpp / 8);
67 | bf.bfSize = bf.bfOffBits + bi.biSizeImage;
68 |
69 | bi.biWidth = width;
70 | bi.biHeight = -1 * height;
71 | bi.biPlanes = 1;
72 | bi.biBitCount = bpp;
73 | bi.biCompression = 0;
74 | bi.biXPelsPerMeter = 0;
75 | bi.biYPelsPerMeter = 0;
76 | bi.biClrUsed = 0;
77 | bi.biClrImportant = 0;
78 | bi.biSize = sizeof(MSRDPEX_BITMAP_INFO_HEADER);
79 |
80 | if (fwrite(&bf, sizeof(MSRDPEX_BITMAP_FILE_HEADER), 1, fp) != 1 ||
81 | fwrite(&bi, sizeof(MSRDPEX_BITMAP_INFO_HEADER), 1, fp) != 1 ||
82 | fwrite(data, bi.biSizeImage, 1, fp) != 1)
83 | {
84 | success = false;
85 | }
86 |
87 | fclose(fp);
88 |
89 | return success;
90 | }
91 |
92 | HBITMAP MsRdpEx_CreateDIBSection(HDC hDC, int width, int height, int bpp, uint8_t** ppPixelData)
93 | {
94 | BITMAPINFO bitmapInfo;
95 | ZeroMemory(&bitmapInfo, sizeof(BITMAPINFO));
96 | bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFO);
97 | bitmapInfo.bmiHeader.biWidth = width;
98 | bitmapInfo.bmiHeader.biHeight = -1 * height;
99 | bitmapInfo.bmiHeader.biPlanes = 1;
100 | bitmapInfo.bmiHeader.biBitCount = bpp;
101 | bitmapInfo.bmiHeader.biCompression = BI_RGB;
102 | return CreateDIBSection(hDC, &bitmapInfo, DIB_RGB_COLORS, (void**) ppPixelData, NULL, 0);
103 | }
104 |
--------------------------------------------------------------------------------
/dll/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | set(MSRDPEX_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include/MsRdpEx")
3 |
4 | set(MSRDPEX_RESOURCES
5 | MsRdpEx.rc)
6 |
7 | windows_rc_generate_version_info(
8 | NAME "MsRdpEx" TYPE "DLL"
9 | VERSION "${MSRDPEX_VERSION}"
10 | FILENAME "MsRdpEx.dll"
11 | VENDOR "${MSRDPEX_VENDOR}"
12 | COPYRIGHT "${MSRDPEX_COPYRIGHT}"
13 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
14 |
15 | source_group("Resources" FILES ${MSRDPEX_RESOURCES})
16 |
17 | set(MSRDPEX_HEADERS
18 | "${MSRDPEX_INCLUDE_DIR}/Sspi.h"
19 | "${MSRDPEX_INCLUDE_DIR}/Stopwatch.h"
20 | "${MSRDPEX_INCLUDE_DIR}/Stream.h"
21 | "${MSRDPEX_INCLUDE_DIR}/Detours.h"
22 | "${MSRDPEX_INCLUDE_DIR}/Memory.h"
23 | "${MSRDPEX_INCLUDE_DIR}/Pcap.h"
24 | "${MSRDPEX_INCLUDE_DIR}/ArrayList.h"
25 | "${MSRDPEX_INCLUDE_DIR}/HashTable.h"
26 | "${MSRDPEX_INCLUDE_DIR}/KeyMaps.h"
27 | "${MSRDPEX_INCLUDE_DIR}/Environment.h"
28 | "${MSRDPEX_INCLUDE_DIR}/NameResolver.h"
29 | "${MSRDPEX_INCLUDE_DIR}/RdpFile.h"
30 | "${MSRDPEX_INCLUDE_DIR}/RdpCoreApi.h"
31 | "${MSRDPEX_INCLUDE_DIR}/RdpProcess.h"
32 | "${MSRDPEX_INCLUDE_DIR}/RdpInstance.h"
33 | "${MSRDPEX_INCLUDE_DIR}/RdpSettings.h"
34 | "${MSRDPEX_INCLUDE_DIR}/OutputMirror.h"
35 | "${MSRDPEX_INCLUDE_DIR}/VideoRecorder.h"
36 | "${MSRDPEX_INCLUDE_DIR}/RecordingManifest.h"
37 | "${MSRDPEX_INCLUDE_DIR}/NamedPipe.h"
38 | "${MSRDPEX_INCLUDE_DIR}/MsRdpEx.h")
39 |
40 | set(MSRDPEX_SOURCES
41 | ArrayList.c
42 | HashTable.c
43 | KeyMaps.cpp
44 | Environment.c
45 | NameResolver.c
46 | Stopwatch.c
47 | DpiHelper.cpp
48 | DpiHelper.h
49 | Sspi.cpp
50 | Stream.cpp
51 | Detours.cpp
52 | Memory.c
53 | File.c
54 | Log.c
55 | Paths.c
56 | String.c
57 | MsRdpClient.cpp
58 | MsRdpClient.h
59 | ApiHooks.cpp
60 | OutputMirror.c
61 | VideoRecorder.c
62 | RecordingManifest.c
63 | NamedPipe.c
64 | Pcap.cpp
65 | Bitmap.c
66 | WinMsg.c
67 | RdpFile.c
68 | RdpCoreApi.cpp
69 | RdpProcess.cpp
70 | RdpInstance.cpp
71 | RdpSettings.cpp
72 | RdpDvcClient.cpp
73 | RdpDvcClient.h
74 | TSObjects.cpp
75 | TSObjects.h
76 | MsRdpEx.cpp
77 | MsRdpEx.h
78 | AxDll.cpp)
79 |
80 | set(MSRDPEX_AXHOST_SOURCE_DIR "${CMAKE_SOURCE_DIR}/dll/AxHost")
81 | include_directories(${AXHOST_SOURCE_DIR})
82 |
83 | set(MSRDPEX_AXHOST_SOURCES
84 | "${MSRDPEX_AXHOST_SOURCE_DIR}/RdpAxHostWnd.cpp"
85 | "${MSRDPEX_AXHOST_SOURCE_DIR}/RdpAxHostWnd.h"
86 | "${MSRDPEX_AXHOST_SOURCE_DIR}/RdpEventSink.cpp"
87 | "${MSRDPEX_AXHOST_SOURCE_DIR}/RdpEventSink.h"
88 | "${MSRDPEX_AXHOST_SOURCE_DIR}/RdpOleSite.cpp"
89 | "${MSRDPEX_AXHOST_SOURCE_DIR}/RdpOleSite.h"
90 | "${MSRDPEX_AXHOST_SOURCE_DIR}/RdpComBase.h"
91 | "${MSRDPEX_AXHOST_SOURCE_DIR}/RdpWinMain.cpp"
92 | "${MSRDPEX_AXHOST_SOURCE_DIR}/RdpWinMain.h")
93 |
94 | add_library(MsRdpEx_Dll SHARED
95 | ${MSRDPEX_SOURCES}
96 | ${MSRDPEX_HEADERS}
97 | ${MSRDPEX_RESOURCES}
98 | ${MSRDPEX_AXHOST_SOURCES}
99 | MsRdpEx.def)
100 |
101 | set(MSRDPEX_LIBS
102 | detours
103 | version.lib
104 | userenv.lib
105 | user32.lib
106 | rpcrt4.lib
107 | ws2_32.lib
108 | secur32.lib
109 | credui.lib
110 | advapi32.lib
111 | comctl32.lib)
112 |
113 | target_compile_options(MsRdpEx_Dll PRIVATE /W4)
114 |
115 | target_link_libraries(MsRdpEx_Dll ${MSRDPEX_LIBS})
116 |
117 | set_target_properties(MsRdpEx_Dll PROPERTIES OUTPUT_NAME "MsRdpEx")
118 |
--------------------------------------------------------------------------------
/dll/ComHelpers.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_COM_HELPERS_H
2 | #define MSRDPEX_COM_HELPERS_H
3 |
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | #ifndef SafeRelease
11 | #define SafeRelease(_x) { if ((_x) != nullptr) { (_x)->Release(); (_x) = nullptr; } }
12 | #endif
13 |
14 | #ifndef ToVariantBool
15 | #define ToVariantBool(_b) ((_b) ? VARIANT_TRUE : VARIANT_FALSE)
16 | #endif
17 |
18 | #define VariantInitBool(pv, b) \
19 | do { \
20 | VariantInit((pv)); \
21 | (pv)->vt = VT_BOOL; \
22 | (pv)->boolVal = ((b) ? VARIANT_TRUE : VARIANT_FALSE); \
23 | } while (0)
24 |
25 | #endif /* MSRDPEX_COM_HELPERS_H */
26 |
--------------------------------------------------------------------------------
/dll/Detours.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 |
4 |
--------------------------------------------------------------------------------
/dll/DpiHelper.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_DPI_HELPER_H
2 | #define MSRDPEX_DPI_HELPER_H
3 |
4 | #include
5 |
6 | #include
7 |
8 | #ifndef SCALING_ENUMS_DECLARED
9 |
10 | typedef enum
11 | {
12 | DEVICE_PRIMARY = 0,
13 | DEVICE_IMMERSIVE = 1,
14 | } DISPLAY_DEVICE_TYPE;
15 |
16 | typedef enum
17 | {
18 | SCF_VALUE_NONE = 0x00,
19 | SCF_SCALE = 0x01,
20 | SCF_PHYSICAL = 0x02,
21 | } SCALE_CHANGE_FLAGS;
22 |
23 | DEFINE_ENUM_FLAG_OPERATORS(SCALE_CHANGE_FLAGS);
24 |
25 | #define SCALING_ENUMS_DECLARED
26 | #endif
27 |
28 | #ifndef DPI_ENUMS_DECLARED
29 |
30 | typedef enum PROCESS_DPI_AWARENESS
31 | {
32 | PROCESS_DPI_UNAWARE = 0,
33 | PROCESS_SYSTEM_DPI_AWARE = 1,
34 | PROCESS_PER_MONITOR_DPI_AWARE = 2
35 | } PROCESS_DPI_AWARENESS;
36 |
37 | typedef enum MONITOR_DPI_TYPE
38 | {
39 | MDT_EFFECTIVE_DPI = 0,
40 | MDT_ANGULAR_DPI = 1,
41 | MDT_RAW_DPI = 2,
42 | MDT_DEFAULT = MDT_EFFECTIVE_DPI
43 | } MONITOR_DPI_TYPE;
44 |
45 | #define DPI_ENUMS_DECLARED
46 | #endif
47 |
48 | #ifndef SHELL_UI_COMPONENT_ENUMS_DECLARED
49 |
50 | typedef enum
51 | {
52 | SHELL_UI_COMPONENT_TASKBARS = 0,
53 | SHELL_UI_COMPONENT_NOTIFICATIONAREA = 1,
54 | SHELL_UI_COMPONENT_DESKBAND = 2,
55 | } SHELL_UI_COMPONENT;
56 |
57 | #define SHELL_UI_COMPONENT_ENUMS_DECLARED
58 | #endif
59 |
60 | #define WM_DPICHANGED 0x02E0
61 | #define WM_GETDPISCALEDSIZE 0x02E4
62 |
63 | typedef DEVICE_SCALE_FACTOR(WINAPI * fnGetScaleFactorForDevice)(DISPLAY_DEVICE_TYPE deviceType);
64 | typedef HRESULT(WINAPI * fnRegisterScaleChangeNotifications)(DISPLAY_DEVICE_TYPE displayDevice, HWND hwndNotify,
65 | uint32_t uMsgNotify, DWORD* pdwCookie);
66 | typedef HRESULT(WINAPI * fnRevokeScaleChangeNotifications)(DISPLAY_DEVICE_TYPE displayDevice, DWORD dwCookie);
67 | typedef HRESULT(WINAPI * fnGetScaleFactorForMonitor)(HMONITOR hMonitor, DEVICE_SCALE_FACTOR* pScale);
68 | typedef HRESULT(WINAPI * fnRegisterScaleChangeEvent)(HANDLE hEvent, DWORD_PTR* pdwCookie);
69 | typedef HRESULT(WINAPI * fnUnregisterScaleChangeEvent)(DWORD_PTR dwCookie);
70 | typedef HRESULT(WINAPI * fnSetProcessDpiAwareness)(PROCESS_DPI_AWARENESS value);
71 | typedef HRESULT(WINAPI * fnGetProcessDpiAwareness)(HANDLE hProcess, PROCESS_DPI_AWARENESS* value);
72 | typedef HRESULT(WINAPI * fnGetDpiForMonitor)(HMONITOR hMonitor, MONITOR_DPI_TYPE dpiType, uint32_t* dpiX,
73 | uint32_t* dpiY);
74 | typedef uint32_t(WINAPI * fnGetDpiForShellUIComponent)(SHELL_UI_COMPONENT component);
75 | typedef BOOL(WINAPI * fnAdjustWindowRectExForDpi)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle,
76 | UINT dpi);
77 | typedef BOOL(WINAPI * fnEnableNonClientDpiScaling)(HWND hwnd);
78 |
79 | struct dpi_helper
80 | {
81 | HMODULE hShCore;
82 | HMODULE hUser32;
83 | fnGetScaleFactorForDevice GetScaleFactorForDevice;
84 | fnRegisterScaleChangeNotifications RegisterScaleChangeNotifications;
85 | fnRevokeScaleChangeNotifications RevokeScaleChangeNotifications;
86 | fnGetScaleFactorForMonitor GetScaleFactorForMonitor;
87 | fnRegisterScaleChangeEvent RegisterScaleChangeEvent;
88 | fnUnregisterScaleChangeEvent UnregisterScaleChangeEvent;
89 | fnSetProcessDpiAwareness SetProcessDpiAwareness;
90 | fnGetProcessDpiAwareness GetProcessDpiAwareness;
91 | fnGetDpiForMonitor GetDpiForMonitor;
92 | fnGetDpiForShellUIComponent GetDpiForShellUIComponent;
93 | fnAdjustWindowRectExForDpi AdjustWindowRectExForDpi;
94 | fnEnableNonClientDpiScaling EnableNonClientDpiScaling;
95 | };
96 | typedef struct dpi_helper DpiHelper;
97 |
98 | #ifdef __cplusplus
99 | extern "C" {
100 | #endif
101 |
102 | DpiHelper* DpiHelper_Get();
103 | void DpiHelper_Release();
104 |
105 | float DpiHelper_GetScaleFactor(int dpi);
106 | int DpiHelper_ScaleValue(int value, int dpi);
107 |
108 | DEVICE_SCALE_FACTOR MsRdpEx_GetScaleFactorForDevice(DISPLAY_DEVICE_TYPE deviceType);
109 | HRESULT MsRdpEx_RegisterScaleChangeNotifications(DISPLAY_DEVICE_TYPE displayDevice, HWND hwndNotify, uint32_t uMsgNotify,
110 | DWORD* pdwCookie);
111 | HRESULT MsRdpEx_RevokeScaleChangeNotifications(DISPLAY_DEVICE_TYPE displayDevice, DWORD dwCookie);
112 | HRESULT MsRdpEx_GetScaleFactorForMonitor(HMONITOR hMonitor, DEVICE_SCALE_FACTOR* pScale);
113 | HRESULT MsRdpEx_RegisterScaleChangeEvent(HANDLE hEvent, DWORD_PTR* pdwCookie);
114 | HRESULT MsRdpEx_UnregisterScaleChangeEvent(DWORD_PTR dwCookie);
115 | HRESULT MsRdpEx_SetProcessDpiAwareness(PROCESS_DPI_AWARENESS value);
116 | HRESULT MsRdpEx_GetProcessDpiAwareness(HANDLE hProcess, PROCESS_DPI_AWARENESS* value);
117 | HRESULT MsRdpEx_GetDpiForMonitor(HMONITOR hMonitor, MONITOR_DPI_TYPE dpiType, uint32_t* dpiX, uint32_t* dpiY);
118 | uint32_t MsRdpEx_GetDpiForShellUIComponent(SHELL_UI_COMPONENT component);
119 | BOOL MsRdpEx_AdjustWindowRectExForDpi(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi);
120 | BOOL MsRdpEx_EnableNonClientDpiScaling(HWND hwnd);
121 |
122 | #ifdef __cplusplus
123 | }
124 | #endif
125 |
126 | #endif /* MSRDPEX_DPI_HELPER_H */
127 |
--------------------------------------------------------------------------------
/dll/Environment.c:
--------------------------------------------------------------------------------
1 |
2 | #include
3 |
4 | #include
5 |
6 | bool MsRdpEx_SetEnv(const char* name, const char* value)
7 | {
8 | bool result = false;
9 | WCHAR* nameW = NULL;
10 | WCHAR* valueW = NULL;
11 |
12 | if (!name)
13 | {
14 | goto exit;
15 | }
16 |
17 | if (MsRdpEx_ConvertToUnicode(CP_UTF8, 0, name, -1, &nameW, 0) < 1) {
18 | goto exit;
19 | }
20 |
21 | if (value) { // optional
22 | if (MsRdpEx_ConvertToUnicode(CP_UTF8, 0, value, -1, &valueW, 0) < 1) {
23 | goto exit;
24 | }
25 | }
26 |
27 | result = SetEnvironmentVariableW(nameW, valueW) ? true : false;
28 |
29 | exit:
30 | free(nameW);
31 | free(valueW);
32 | return result;
33 | }
34 |
35 | char* MsRdpEx_GetEnv(const char* name)
36 | {
37 | DWORD nSizeW;
38 | WCHAR* nameW = NULL;
39 | WCHAR* valueW = NULL;
40 | char* value = NULL;
41 |
42 | if (!name)
43 | {
44 | goto exit;
45 | }
46 |
47 | if (MsRdpEx_ConvertToUnicode(CP_UTF8, 0, name, -1, &nameW, 0) < 1) {
48 | goto exit;
49 | }
50 |
51 | nSizeW = GetEnvironmentVariableW(nameW, NULL, 0);
52 |
53 | if (!nSizeW)
54 | return NULL;
55 |
56 | valueW = (WCHAR*) malloc((nSizeW + 1) * 2);
57 |
58 | if (!valueW) {
59 | goto exit;
60 | }
61 |
62 | nSizeW = GetEnvironmentVariableW(nameW, valueW, nSizeW);
63 |
64 | valueW[nSizeW] = '\0';
65 |
66 | if (MsRdpEx_ConvertFromUnicode(CP_UTF8, 0, valueW, -1, &value, 0, NULL, NULL) < 0) {
67 | goto exit;
68 | }
69 |
70 | exit:
71 | free(nameW);
72 | free(valueW);
73 | return value;
74 | }
75 |
76 | bool MsRdpEx_EnvExists(const char* name)
77 | {
78 | if (!name)
79 | return false;
80 |
81 | return GetEnvironmentVariableA(name, NULL, 0) ? true : false;
82 | }
83 |
84 | bool MsRdpEx_GetEnvBool(const char* name, bool defaultValue)
85 | {
86 | char* env;
87 | bool value = defaultValue;
88 |
89 | env = MsRdpEx_GetEnv(name);
90 |
91 | if (!env)
92 | return value;
93 |
94 | if ((strcmp(env, "1") == 0) || (_stricmp(env, "TRUE") == 0))
95 | value = true;
96 | else if ((strcmp(env, "0") == 0) || (_stricmp(env, "FALSE") == 0))
97 | value = false;
98 |
99 | free(env);
100 |
101 | return value;
102 | }
103 |
104 | int MsRdpEx_GetEnvInt(const char* name, int defaultValue)
105 | {
106 | char* env;
107 | int value = defaultValue;
108 |
109 | env = MsRdpEx_GetEnv(name);
110 |
111 | if (!env)
112 | return value;
113 |
114 | value = atoi(env);
115 |
116 | free(env);
117 |
118 | return value;
119 | }
120 |
121 | char** MsRdpEx_GetEnvironmentVariables(int* envc)
122 | {
123 | int count;
124 | int index;
125 | size_t length;
126 | char* env = NULL;
127 | char** envs = NULL;
128 | LPCWSTR pEnvW = NULL;
129 | LPVOID lpEnvBlock = NULL;
130 |
131 | if (!CreateEnvironmentBlock(&lpEnvBlock, NULL, TRUE)) {
132 | return NULL;
133 | }
134 |
135 | count = 0;
136 | pEnvW = (WCHAR*) lpEnvBlock;
137 |
138 | while (true)
139 | {
140 | length = wcslen(pEnvW);
141 |
142 | if (length < 1)
143 | break;
144 |
145 | pEnvW = &pEnvW[length + 1];
146 | count++;
147 | }
148 |
149 | envs = (char**) calloc(count + 1, sizeof(char*));
150 |
151 | if (!envs)
152 | goto fail;
153 |
154 | pEnvW = (WCHAR*) lpEnvBlock;
155 |
156 | for (index = 0; index < count; index++)
157 | {
158 | length = wcslen(pEnvW);
159 |
160 | env = NULL;
161 |
162 | if (MsRdpEx_ConvertFromUnicode(CP_UTF8, 0, pEnvW, -1, &env, 0, NULL, NULL) < 0) {
163 | goto fail;
164 | }
165 |
166 | envs[index] = env;
167 |
168 | pEnvW = &pEnvW[length + 1];
169 | }
170 |
171 | *envc = count;
172 |
173 | DestroyEnvironmentBlock(lpEnvBlock);
174 | return envs;
175 | fail:
176 | DestroyEnvironmentBlock(lpEnvBlock);
177 | free(envs);
178 | return NULL;
179 | }
180 |
181 | void MsRdpEx_FreeEnvironmentVariables(int envc, char** envs)
182 | {
183 | int index;
184 |
185 | if (!envs)
186 | return;
187 |
188 | for (index = 0; index < envc; index++) {
189 | free(envs[index]);
190 | }
191 |
192 | free(envs);
193 | }
194 |
195 | char* MsRdpEx_ReadTextFromNamedPipe(const char* pipeName)
196 | {
197 | size_t size = 0;
198 | size_t length = 0;
199 | DWORD readBytes = 0;
200 | char* text = NULL;
201 | char* buffer = NULL;
202 | char* tempBuffer = NULL;
203 | HANDLE pipeHandle = NULL;
204 | char filename[MSRDPEX_MAX_PATH];
205 |
206 | if (!pipeName)
207 | return NULL;
208 |
209 | sprintf_s(filename, sizeof(filename) - 1, "\\\\.\\pipe\\%s", pipeName);
210 |
211 | pipeHandle = CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
212 |
213 | if (pipeHandle == INVALID_HANDLE_VALUE) {
214 | goto exit;
215 | }
216 |
217 | size = 1024;
218 | buffer = (char*) malloc(size);
219 |
220 | if (!buffer)
221 | goto exit;
222 |
223 | length = 0;
224 | while (1) {
225 | if (!ReadFile(pipeHandle, buffer + length, size - length, &readBytes, NULL) || (readBytes == 0)) {
226 | // If no bytes were read or an error occurred, break out of the loop.
227 | break;
228 | }
229 |
230 | length += readBytes;
231 | if ((size - length) <= 1) {
232 | tempBuffer = (char*) realloc(buffer, size * 2);
233 | if (!tempBuffer) {
234 | goto exit;
235 | }
236 | buffer = tempBuffer;
237 | size = size * 2;
238 | }
239 | }
240 | buffer[length] = '\0';
241 | text = _strdup(buffer);
242 |
243 | exit:
244 | if (buffer) {
245 | SecureZeroMemory(buffer, size);
246 | free(buffer);
247 | }
248 | if (pipeHandle && (pipeHandle != INVALID_HANDLE_VALUE)) {
249 | CloseHandle(pipeHandle);
250 | }
251 | return text;
252 | }
253 |
--------------------------------------------------------------------------------
/dll/KeyMaps.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | typedef struct {
4 | const char* keyName;
5 | uint32_t vkCode;
6 | } KeyCodeMapping;
7 |
8 | #define VK_UNKNOWN 0
9 |
10 | #define VK_KEY_0 0x30 /* '0' key */
11 | #define VK_KEY_1 0x31 /* '1' key */
12 | #define VK_KEY_2 0x32 /* '2' key */
13 | #define VK_KEY_3 0x33 /* '3' key */
14 | #define VK_KEY_4 0x34 /* '4' key */
15 | #define VK_KEY_5 0x35 /* '5' key */
16 | #define VK_KEY_6 0x36 /* '6' key */
17 | #define VK_KEY_7 0x37 /* '7' key */
18 | #define VK_KEY_8 0x38 /* '8' key */
19 | #define VK_KEY_9 0x39 /* '9' key */
20 |
21 | #define VK_KEY_A 0x41 /* 'A' key */
22 | #define VK_KEY_B 0x42 /* 'B' key */
23 | #define VK_KEY_C 0x43 /* 'C' key */
24 | #define VK_KEY_D 0x44 /* 'D' key */
25 | #define VK_KEY_E 0x45 /* 'E' key */
26 | #define VK_KEY_F 0x46 /* 'F' key */
27 | #define VK_KEY_G 0x47 /* 'G' key */
28 | #define VK_KEY_H 0x48 /* 'H' key */
29 | #define VK_KEY_I 0x49 /* 'I' key */
30 | #define VK_KEY_J 0x4A /* 'J' key */
31 | #define VK_KEY_K 0x4B /* 'K' key */
32 | #define VK_KEY_L 0x4C /* 'L' key */
33 | #define VK_KEY_M 0x4D /* 'M' key */
34 | #define VK_KEY_N 0x4E /* 'N' key */
35 | #define VK_KEY_O 0x4F /* 'O' key */
36 | #define VK_KEY_P 0x50 /* 'P' key */
37 | #define VK_KEY_Q 0x51 /* 'Q' key */
38 | #define VK_KEY_R 0x52 /* 'R' key */
39 | #define VK_KEY_S 0x53 /* 'S' key */
40 | #define VK_KEY_T 0x54 /* 'T' key */
41 | #define VK_KEY_U 0x55 /* 'U' key */
42 | #define VK_KEY_V 0x56 /* 'V' key */
43 | #define VK_KEY_W 0x57 /* 'W' key */
44 | #define VK_KEY_X 0x58 /* 'X' key */
45 | #define VK_KEY_Y 0x59 /* 'Y' key */
46 | #define VK_KEY_Z 0x5A /* 'Z' key */
47 |
48 | #define VK_ABNT_C1 0xC1 /* Brazilian (ABNT) Keyboard */
49 | #define VK_ABNT_C2 0xC2 /* Brazilian (ABNT) Keyboard */
50 |
51 | // https://github.com/microsoft/vscode/blob/main/src/vs/base/common/keyCodes.ts
52 |
53 | static const KeyCodeMapping keyCodeTable[] =
54 | {
55 | { "None", VK_UNKNOWN },
56 | { "Hyper", VK_UNKNOWN },
57 | { "Super", VK_UNKNOWN },
58 | { "Fn", VK_UNKNOWN },
59 | { "FnLock", VK_UNKNOWN },
60 | { "Suspend", VK_UNKNOWN },
61 | { "Resume", VK_UNKNOWN },
62 | { "Turbo", VK_UNKNOWN },
63 | { "Sleep", VK_SLEEP },
64 | { "WakeUp", VK_UNKNOWN },
65 |
66 | { "Ctrl", VK_CONTROL },
67 | { "Shift", VK_SHIFT },
68 | { "Alt", VK_MENU },
69 |
70 | { "A", VK_KEY_A },
71 | { "B", VK_KEY_B },
72 | { "C", VK_KEY_C },
73 | { "D", VK_KEY_D },
74 | { "E", VK_KEY_E },
75 | { "F", VK_KEY_F },
76 | { "G", VK_KEY_G },
77 | { "H", VK_KEY_H },
78 | { "I", VK_KEY_I },
79 | { "J", VK_KEY_J },
80 | { "K", VK_KEY_K },
81 | { "L", VK_KEY_L },
82 | { "M", VK_KEY_M },
83 | { "N", VK_KEY_N },
84 | { "O", VK_KEY_O },
85 | { "P", VK_KEY_P },
86 | { "Q", VK_KEY_Q },
87 | { "R", VK_KEY_R },
88 | { "S", VK_KEY_S },
89 | { "T", VK_KEY_T },
90 | { "U", VK_KEY_U },
91 | { "V", VK_KEY_V },
92 | { "W", VK_KEY_W },
93 | { "X", VK_KEY_X },
94 | { "Y", VK_KEY_Y },
95 | { "Z", VK_KEY_Z },
96 |
97 | { "0", VK_KEY_0 },
98 | { "1", VK_KEY_1 },
99 | { "2", VK_KEY_2 },
100 | { "3", VK_KEY_3 },
101 | { "4", VK_KEY_4 },
102 | { "5", VK_KEY_5 },
103 | { "6", VK_KEY_6 },
104 | { "7", VK_KEY_7 },
105 | { "8", VK_KEY_8 },
106 | { "9", VK_KEY_9 },
107 |
108 | { "Enter", VK_RETURN },
109 | { "Escape", VK_ESCAPE },
110 | { "Backspace", VK_BACK },
111 | { "Tab", VK_TAB },
112 | { "Space", VK_SPACE },
113 | { "-", VK_OEM_MINUS },
114 | { "=", VK_OEM_PLUS },
115 | { "[", VK_OEM_4 },
116 | { "]", VK_OEM_6 },
117 | { "\\", VK_OEM_5 },
118 | { ";", VK_OEM_1 },
119 | { "\'", VK_OEM_7 },
120 | { "`", VK_OEM_3 },
121 | { ",", VK_OEM_COMMA },
122 | { ".", VK_OEM_PERIOD },
123 | { "/", VK_OEM_2 },
124 | { "CapsLock", VK_CAPITAL },
125 |
126 | { "F1", VK_F1 },
127 | { "F2", VK_F2 },
128 | { "F3", VK_F3 },
129 | { "F4", VK_F4 },
130 | { "F5", VK_F5 },
131 | { "F6", VK_F6 },
132 | { "F7", VK_F7 },
133 | { "F8", VK_F8 },
134 | { "F9", VK_F9 },
135 | { "F10", VK_F10 },
136 | { "F11", VK_F11 },
137 | { "F12", VK_F12 },
138 |
139 | { "PrintScreen", VK_UNKNOWN },
140 | { "ScrollLock", VK_SCROLL },
141 | { "PauseBreak", VK_PAUSE },
142 | { "Insert", VK_INSERT },
143 | { "Home", VK_HOME },
144 | { "PageUp", VK_PRIOR },
145 | { "Delete", VK_DELETE },
146 | { "End", VK_END },
147 | { "PageDown", VK_NEXT },
148 | { "RightArrow", VK_RIGHT },
149 | { "LeftArrow", VK_LEFT },
150 | { "DownArrow", VK_DOWN },
151 | { "UpArrow", VK_UP },
152 |
153 | { "NumLock", VK_NUMLOCK },
154 | { "NumPad_Divide", VK_DIVIDE },
155 | { "NumPad_Subtract", VK_SUBTRACT },
156 | { "NumPad_Add", VK_ADD },
157 | { "NumPad_Enter", VK_UNKNOWN },
158 |
159 | { "Numpad0", VK_NUMPAD0 },
160 | { "Numpad1", VK_NUMPAD1 },
161 | { "Numpad2", VK_NUMPAD2 },
162 | { "Numpad3", VK_NUMPAD3 },
163 | { "Numpad4", VK_NUMPAD4 },
164 | { "Numpad5", VK_NUMPAD5 },
165 | { "Numpad6", VK_NUMPAD6 },
166 | { "Numpad7", VK_NUMPAD7 },
167 | { "Numpad8", VK_NUMPAD8 },
168 | { "Numpad9", VK_NUMPAD9 },
169 |
170 | { "NumPad_Decimal", VK_DECIMAL },
171 | { "Numpad_Equal", VK_UNKNOWN },
172 | { "Numpad_Separator", VK_SEPARATOR },
173 | { "Numpad_Clear", VK_CLEAR },
174 | };
175 |
176 | uint32_t MsRdpEx_KeyNameToVKCode(const char* keyName)
177 | {
178 | int numMappings = sizeof(keyCodeTable) / sizeof(keyCodeTable[0]);
179 |
180 | for (int i = 0; i < numMappings; i++) {
181 | if (_stricmp(keyCodeTable[i].keyName, keyName) == 0) {
182 | return keyCodeTable[i].vkCode;
183 | }
184 | }
185 |
186 | return VK_UNKNOWN;
187 | }
188 |
--------------------------------------------------------------------------------
/dll/Log.c:
--------------------------------------------------------------------------------
1 |
2 | #include
3 |
4 | #include
5 |
6 | static bool g_LogInitialized = false;
7 |
8 | static FILE* g_LogFile = NULL;
9 | static bool g_LogEnabled = false;
10 | static char g_LogFilePath[MSRDPEX_MAX_PATH] = { 0 };
11 |
12 | static uint32_t g_LogLevel = MSRDPEX_LOG_DEBUG;
13 |
14 | #define MSRDPEX_LOG_MAX_LINE 8192
15 |
16 | LPCSTR LOG_LEVELS[7] = { "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF" };
17 |
18 | bool MsRdpEx_IsLogLevelActive(uint32_t logLevel)
19 | {
20 | if (!g_LogEnabled)
21 | return false;
22 |
23 | if (g_LogLevel == MSRDPEX_LOG_OFF)
24 | return false;
25 |
26 | return logLevel >= g_LogLevel;
27 | }
28 |
29 | bool MsRdpEx_LogVA(uint32_t level, const char* format, va_list args)
30 | {
31 | if (!g_LogFile)
32 | return true;
33 |
34 | SYSTEMTIME st;
35 | GetLocalTime(&st);
36 |
37 | DWORD pid = GetCurrentProcessId();
38 | DWORD tid = GetCurrentThreadId();
39 |
40 | char message[MSRDPEX_LOG_MAX_LINE];
41 | vsnprintf_s(message, MSRDPEX_LOG_MAX_LINE - 1, _TRUNCATE, format, args);
42 |
43 | fprintf(g_LogFile, "[%s] %04d-%02d-%02d %02d:%02d:%02d.%03d PID:%lu TID:%lu - %s\n",
44 | LOG_LEVELS[level],
45 | st.wYear, st.wMonth, st.wDay,
46 | st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
47 | pid, tid,
48 | message);
49 | fflush(g_LogFile); // WARNING: performance drag
50 |
51 | return true;
52 | }
53 |
54 | bool MsRdpEx_Log(uint32_t level, const char* format, ...)
55 | {
56 | bool status;
57 | va_list args;
58 | va_start(args, format);
59 | status = MsRdpEx_LogVA(level, format, args);
60 | va_end(args);
61 | return status;
62 | }
63 |
64 | void MsRdpEx_LogHexDump(const uint8_t* data, size_t size)
65 | {
66 | int i, ln, hn;
67 | const uint8_t* p = data;
68 | size_t width = 16;
69 | size_t offset = 0;
70 | size_t chunk = 0;
71 | char line[512] = { 0 };
72 | char* bin2hex = "0123456789ABCDEF";
73 |
74 | while (offset < size) {
75 | chunk = size - offset;
76 |
77 | if (chunk >= width)
78 | chunk = width;
79 |
80 | for (i = 0; i < chunk; i++)
81 | {
82 | ln = p[i] & 0xF;
83 | hn = (p[i] >> 4) & 0xF;
84 |
85 | line[i * 2] = bin2hex[hn];
86 | line[(i * 2) + 1] = bin2hex[ln];
87 | }
88 |
89 | line[chunk * 2] = ' ';
90 |
91 | for (i = chunk; i < width; i++) {
92 | line[i * 2] = ' ';
93 | line[(i * 2) + 1] = ' ';
94 | }
95 |
96 | char* side = &line[(width * 2) + 1];
97 |
98 | for (i = 0; i < chunk; i++)
99 | {
100 | char c = ((p[i] >= 0x20) && (p[i] < 0x7F)) ? p[i] : '.';
101 | side[i] = c;
102 | }
103 | side[i] = '\n';
104 | side[i+1] = '\0';
105 |
106 | if (g_LogFile) {
107 | fwrite(line, 1, strlen(line), g_LogFile);
108 | }
109 |
110 | offset += chunk;
111 | p += chunk;
112 | }
113 | }
114 |
115 | void MsRdpEx_LogEnvInit()
116 | {
117 | char* envvar;
118 |
119 | if (g_LogInitialized)
120 | return;
121 |
122 | bool logEnabled = MsRdpEx_EnvExists("MSRDPEX_LOG_LEVEL");
123 |
124 | if (logEnabled) {
125 | // only set if true to avoid overriding current value
126 | MsRdpEx_SetLogEnabled(true);
127 | }
128 |
129 | envvar = MsRdpEx_GetEnv("MSRDPEX_LOG_LEVEL");
130 |
131 | if (envvar) {
132 |
133 | if (MsRdpEx_StringIEquals(envvar, "TRACE"))
134 | {
135 | MsRdpEx_SetLogLevel(MSRDPEX_LOG_TRACE);
136 | }
137 | else if (MsRdpEx_StringIEquals(envvar, "DEBUG"))
138 | {
139 | MsRdpEx_SetLogLevel(MSRDPEX_LOG_DEBUG);
140 | }
141 | else if (MsRdpEx_StringIEquals(envvar, "INFO"))
142 | {
143 | MsRdpEx_SetLogLevel(MSRDPEX_LOG_INFO);
144 | }
145 | else if (MsRdpEx_StringIEquals(envvar, "WARN"))
146 | {
147 | MsRdpEx_SetLogLevel(MSRDPEX_LOG_WARN);
148 | }
149 | else if (MsRdpEx_StringIEquals(envvar, "ERROR"))
150 | {
151 | MsRdpEx_SetLogLevel(MSRDPEX_LOG_ERROR);
152 | }
153 | else if (MsRdpEx_StringIEquals(envvar, "FATAL"))
154 | {
155 | MsRdpEx_SetLogLevel(MSRDPEX_LOG_FATAL);
156 | }
157 | else if (MsRdpEx_StringIEquals(envvar, "OFF"))
158 | {
159 | MsRdpEx_SetLogLevel(MSRDPEX_LOG_OFF);
160 | }
161 | else
162 | {
163 | int ival = atoi(envvar);
164 |
165 | if ((ival >= 0) && (ival <= 6))
166 | {
167 | MsRdpEx_SetLogLevel((uint32_t)ival);
168 | }
169 | }
170 | }
171 |
172 | free(envvar);
173 |
174 | envvar = MsRdpEx_GetEnv("MSRDPEX_LOG_FILE_PATH");
175 |
176 | if (envvar) {
177 | MsRdpEx_SetLogFilePath(envvar);
178 | }
179 |
180 | free(envvar);
181 |
182 | g_LogInitialized = true;
183 | }
184 |
185 | void MsRdpEx_LogOpen()
186 | {
187 | MsRdpEx_LogEnvInit();
188 |
189 | if (!g_LogEnabled)
190 | return;
191 |
192 | if (g_LogFilePath[0] == '\0') {
193 | const char* appDataPath = MsRdpEx_GetPath(MSRDPEX_APP_DATA_PATH);
194 | sprintf_s(g_LogFilePath, MSRDPEX_MAX_PATH, "%s\\MsRdpEx.log", appDataPath);
195 | }
196 |
197 | g_LogFile = MsRdpEx_FileOpen(g_LogFilePath, "wb");
198 | }
199 |
200 | void MsRdpEx_LogClose()
201 | {
202 | if (g_LogFile) {
203 | fclose(g_LogFile);
204 | g_LogFile = NULL;
205 | }
206 | }
207 |
208 | void MsRdpEx_SetLogEnabled(bool logEnabled)
209 | {
210 | g_LogEnabled = logEnabled;
211 | }
212 |
213 | void MsRdpEx_SetLogLevel(uint32_t logLevel)
214 | {
215 | g_LogLevel = logLevel;
216 | }
217 |
218 | void MsRdpEx_SetLogFilePath(const char* logFilePath)
219 | {
220 | strcpy_s(g_LogFilePath, MSRDPEX_MAX_PATH, logFilePath);
221 | }
222 |
--------------------------------------------------------------------------------
/dll/Memory.c:
--------------------------------------------------------------------------------
1 |
2 | #include
3 |
4 | bool MsRdpEx_IsBadReadPtr(void* ptr, size_t size)
5 | {
6 | bool ok;
7 | DWORD mask;
8 | BYTE* p = (BYTE*) ptr;
9 | BYTE* maxPtr = p + size;
10 | BYTE* regionEnd = NULL;
11 | MEMORY_BASIC_INFORMATION mbi;
12 |
13 | if (size < 0)
14 | return false;
15 |
16 | if (!p)
17 | return true;
18 |
19 | mask = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
20 |
21 | do
22 | {
23 | if (p == ptr || p == regionEnd)
24 | {
25 | if (VirtualQuery((LPCVOID)p, &mbi, sizeof(mbi)) == 0)
26 | {
27 | return true;
28 | }
29 | else
30 | {
31 | regionEnd = ((BYTE*) mbi.BaseAddress + mbi.RegionSize);
32 | }
33 | }
34 |
35 | ok = (mbi.Protect & mask) != 0;
36 |
37 | if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS))
38 | {
39 | ok = false;
40 | }
41 |
42 | if (!ok)
43 | {
44 | return true;
45 | }
46 |
47 | if (maxPtr <= regionEnd) /* the whole address range is inside the current memory region */
48 | {
49 | return false;
50 | }
51 | else if (maxPtr > regionEnd) /* this region is a part of (or overlaps with) the address range we are checking */
52 | {
53 | p = regionEnd; /* lets move to the next memory region */
54 | }
55 | } while (p < maxPtr);
56 |
57 | return false;
58 | }
59 |
60 | bool MsRdpEx_CanReadUnsafePtr(void* ptr, size_t size)
61 | {
62 | return MsRdpEx_IsBadReadPtr(ptr, size) ? false : true;
63 | }
64 |
65 | bool MsRdpEx_StringEqualsUnsafePtr(const char* ptr, const char* str)
66 | {
67 | size_t length = strlen(str);
68 |
69 | if (length < 1)
70 | return false;
71 |
72 | if (!MsRdpEx_CanReadUnsafePtr((void*) ptr, length + 1))
73 | return false;
74 |
75 | return (strcmp(ptr, str) == 0) ? true : false;
76 | }
77 |
78 | bool MsRdpEx_StringIEqualsUnsafePtr(const char* ptr, const char* str)
79 | {
80 | size_t length = strlen(str);
81 |
82 | if (length < 1)
83 | return false;
84 |
85 | if (!MsRdpEx_CanReadUnsafePtr((void*)ptr, length + 1))
86 | return false;
87 |
88 | return (_stricmp(ptr, str) == 0) ? true : false;
89 | }
90 |
--------------------------------------------------------------------------------
/dll/MsRdpClient.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_CLIENT_H
2 | #define MSRDPEX_CLIENT_H
3 |
4 | #include "MsRdpEx.h"
5 |
6 | #include
7 |
8 | #ifdef __cplusplus
9 | extern "C" {
10 | #endif
11 |
12 | void* MsRdpEx_CClassFactory_New(REFCLSID rclsid, IClassFactory* pDelegate);
13 |
14 | #ifdef __cplusplus
15 | }
16 | #endif
17 |
18 | #endif /* MSRDPEX_CLIENT_H */
19 |
--------------------------------------------------------------------------------
/dll/MsRdpEx.def:
--------------------------------------------------------------------------------
1 | LIBRARY "MsRdpEx"
2 | EXPORTS
3 | DllCanUnloadNow PRIVATE
4 | DllGetClassObject PRIVATE
5 | DllRegisterServer PRIVATE
6 | DllUnregisterServer PRIVATE
7 | DllGetTscCtlVer
8 | DllGetNewActivityId
9 | DllSetAuthProperties
10 | DllGetClaimsToken
11 | DllSetClaimsToken
12 | DllLogoffClaimsToken
13 | DllCancelAuthentication
14 | DllDeleteSavedCreds
15 | DllPreCleanUp
16 | MsRdpEx_GetClaimsToken
17 | MsRdpEx_LogoffClaimsToken
18 | MsRdpEx_CancelAuthentication
19 | MsRdpEx_DeleteSavedCreds
20 | MsRdpEx_PreCleanUp
21 | MsRdpEx_InitPaths
22 | MsRdpEx_GetPath
23 | MsRdpEx_CreateInstance
24 | MsRdpEx_LaunchProcess
25 | MsRdpExProcess_CreateInstance
26 | MsRdpEx_GetArgumentVector
27 | MsRdpEx_FreeArgumentVector
28 | MsRdpEx_WinMain
--------------------------------------------------------------------------------
/dll/MsRdpEx.rc:
--------------------------------------------------------------------------------
1 |
2 | #include "version.rc"
3 |
--------------------------------------------------------------------------------
/dll/NameResolver.c:
--------------------------------------------------------------------------------
1 |
2 | #include
3 |
4 | #include
5 |
6 | struct _MsRdpEx_NameResolver
7 | {
8 | MsRdpEx_HashTable* renaming;
9 | };
10 |
11 | void MsRdpEx_NameResolver_Free(MsRdpEx_NameResolver* ctx);
12 |
13 | static int g_RefCount = 0;
14 | static MsRdpEx_NameResolver* g_NameResolver = NULL;
15 |
16 | bool MsRdpEx_NameResolver_GetMapping(const char* oldName, char** pNewName)
17 | {
18 | MsRdpEx_NameResolver* ctx = g_NameResolver;
19 |
20 | if (!ctx)
21 | return false;
22 |
23 | char* newName = MsRdpEx_HashTable_GetItemValue(ctx->renaming, (void*) oldName);
24 |
25 | if (newName) {
26 | *pNewName = _strdup(newName);
27 |
28 | if (*pNewName != NULL) {
29 | return true;
30 | }
31 | }
32 |
33 | return false;
34 | }
35 |
36 | bool MsRdpEx_NameResolver_RemapName(const char* oldName, const char* newName)
37 | {
38 | MsRdpEx_NameResolver* ctx = g_NameResolver;
39 |
40 | if (!ctx)
41 | return false;
42 |
43 | MsRdpEx_LogPrint(DEBUG, "RemapName: %s -> %s", oldName, newName);
44 |
45 | MsRdpEx_HashTable_Add(ctx->renaming, (void*) oldName, (void*) newName);
46 |
47 | return true;
48 | }
49 |
50 | bool MsRdpEx_NameResolver_UnmapName(const char* name)
51 | {
52 | MsRdpEx_NameResolver* ctx = g_NameResolver;
53 |
54 | if (!ctx)
55 | return false;
56 |
57 | return MsRdpEx_HashTable_Remove(ctx->renaming, (void*) name);
58 | }
59 |
60 | MsRdpEx_NameResolver* MsRdpEx_NameResolver_New()
61 | {
62 | MsRdpEx_NameResolver* ctx;
63 |
64 | ctx = (MsRdpEx_NameResolver*) calloc(1, sizeof(MsRdpEx_NameResolver));
65 |
66 | if (!ctx)
67 | return NULL;
68 |
69 | ctx->renaming = MsRdpEx_HashTable_New(true, MSRDPEX_HASHTABLE_FLAGS_NONE);
70 |
71 | if (!ctx->renaming)
72 | goto error;
73 |
74 | MsRdpEx_HashTable_SetHashFunction(ctx->renaming, MsRdpEx_HashTable_StringHash);
75 | MsRdpEx_HashTable_SetKeyCompareFunction(ctx->renaming, MsRdpEx_StringIEquals);
76 | MsRdpEx_HashTable_SetKeyCloneFunction(ctx->renaming, MsRdpEx_HashTable_StringClone);
77 | MsRdpEx_HashTable_SetKeyFreeFunction(ctx->renaming, MsRdpEx_HashTable_StringFree);
78 | MsRdpEx_HashTable_SetValueCompareFunction(ctx->renaming, MsRdpEx_StringIEquals);
79 | MsRdpEx_HashTable_SetValueCloneFunction(ctx->renaming, MsRdpEx_HashTable_StringClone);
80 | MsRdpEx_HashTable_SetValueFreeFunction(ctx->renaming, MsRdpEx_HashTable_StringFree);
81 |
82 | return ctx;
83 | error:
84 | MsRdpEx_NameResolver_Free(ctx);
85 | return NULL;
86 | }
87 |
88 | void MsRdpEx_NameResolver_Free(MsRdpEx_NameResolver* ctx)
89 | {
90 | if (!ctx)
91 | return;
92 |
93 | if (ctx->renaming) {
94 | MsRdpEx_HashTable_Free(ctx->renaming);
95 | ctx->renaming = NULL;
96 | }
97 | }
98 |
99 | MsRdpEx_NameResolver* MsRdpEx_NameResolver_Get()
100 | {
101 | if (!g_NameResolver)
102 | g_NameResolver = MsRdpEx_NameResolver_New(true);
103 |
104 | g_RefCount++;
105 |
106 | return g_NameResolver;
107 | }
108 |
109 | void MsRdpEx_NameResolver_Release()
110 | {
111 | g_RefCount--;
112 |
113 | if (g_RefCount < 0)
114 | g_RefCount = 0;
115 |
116 | if (g_NameResolver && (g_RefCount < 1))
117 | {
118 | MsRdpEx_NameResolver_Free(g_NameResolver);
119 | g_NameResolver = NULL;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/dll/NamedPipe.c:
--------------------------------------------------------------------------------
1 |
2 | #include "MsRdpEx.h"
3 |
4 | #include
5 |
6 | #include
7 | #include
8 |
9 | #define XMF_NAMED_PIPE_BUFFER_SIZE 8192
10 |
11 | int MsRdpEx_NamedPipe_Read(HANDLE np_handle, uint8_t* data, size_t size)
12 | {
13 | DWORD cb_read = 0;
14 |
15 | if (!ReadFile(np_handle, data, size, &cb_read, NULL)) {
16 | return -1;
17 | }
18 |
19 | return (int) cb_read;
20 | }
21 |
22 | int MsRdpEx_NamedPipe_Write(HANDLE np_handle, const uint8_t* data, size_t size)
23 | {
24 | DWORD cb_write = 0;
25 |
26 | if (!WriteFile(np_handle, (void*)data, (DWORD)size, &cb_write, NULL)) {
27 | return -1;
28 | }
29 |
30 | return (int) cb_write;
31 | }
32 |
33 | HANDLE MsRdpEx_NamedPipe_Open(const char* np_name)
34 | {
35 | HANDLE np_handle;
36 | char filename[MSRDPEX_MAX_PATH];
37 |
38 | if (!np_name)
39 | return NULL;
40 |
41 | sprintf_s(filename, sizeof(filename) - 1, "\\\\.\\pipe\\%s", np_name);
42 |
43 | np_handle = CreateFileA(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
44 |
45 | if (np_handle == INVALID_HANDLE_VALUE) {
46 | return NULL;
47 | }
48 |
49 | return np_handle;
50 | }
51 |
52 | HANDLE MsRdpEx_NamedPipe_Create(const char* np_name, int max_clients)
53 | {
54 | HANDLE np_handle;
55 | char filename[MSRDPEX_MAX_PATH];
56 |
57 | if (!np_name)
58 | return NULL;
59 |
60 | sprintf_s(filename, sizeof(filename) - 1, "\\\\.\\pipe\\%s", np_name);
61 |
62 | np_handle = CreateNamedPipeA(filename,
63 | PIPE_ACCESS_DUPLEX,
64 | PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
65 | max_clients,
66 | XMF_NAMED_PIPE_BUFFER_SIZE,
67 | XMF_NAMED_PIPE_BUFFER_SIZE,
68 | 0, NULL);
69 |
70 | if (np_handle == INVALID_HANDLE_VALUE) {
71 | return NULL;
72 | }
73 |
74 | return np_handle;
75 | }
76 |
77 | HANDLE MsRdpEx_NamedPipe_Accept(HANDLE np_handle)
78 | {
79 | if (!ConnectNamedPipe(np_handle, NULL)) {
80 | return NULL;
81 | }
82 | return np_handle;
83 | }
84 |
85 |
86 | void MsRdpEx_NamedPipe_Close(HANDLE np_handle)
87 | {
88 | CloseHandle(np_handle);
89 | }
90 |
--------------------------------------------------------------------------------
/dll/RdpCoreApi.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 |
5 | #include
6 |
7 | #include "MsRdpEx.h"
8 |
9 | extern "C" const GUID IID_IMsRdpExCoreApi;
10 | extern "C" const GUID IID_IMsRdpExInstance;
11 | extern "C" const GUID IID_IMsRdpExProcess;
12 |
13 | class CMsRdpExCoreApi : public IMsRdpExCoreApi
14 | {
15 | public:
16 | CMsRdpExCoreApi()
17 | {
18 | m_refCount = 0;
19 | MsRdpEx_PathCchDetect(m_MsRdpExDllPath, MSRDPEX_MAX_PATH, MSRDPEX_CURRENT_LIBRARY_PATH);
20 | }
21 |
22 | ~CMsRdpExCoreApi()
23 | {
24 |
25 | }
26 |
27 | // IUnknown interface
28 | public:
29 | HRESULT STDMETHODCALLTYPE QueryInterface(
30 | REFIID riid,
31 | LPVOID* ppvObject
32 | )
33 | {
34 | HRESULT hr = E_NOINTERFACE;
35 | ULONG refCount = m_refCount;
36 | char iid[MSRDPEX_GUID_STRING_SIZE];
37 | MsRdpEx_GuidBinToStr((GUID*)&riid, iid, 0);
38 |
39 | if (riid == IID_IUnknown)
40 | {
41 | *ppvObject = (LPVOID)((IUnknown*)this);
42 | refCount = InterlockedIncrement(&m_refCount);
43 | hr = S_OK;
44 | }
45 | else if (riid == IID_IMsRdpExCoreApi)
46 | {
47 | *ppvObject = (LPVOID)((IUnknown*)this);
48 | refCount = InterlockedIncrement(&m_refCount);
49 | hr = S_OK;
50 | }
51 |
52 | MsRdpEx_LogPrint(DEBUG, "CMsRdpExCoreApi::QueryInterface(%s) = 0x%08X, %d", iid, hr, refCount);
53 |
54 | return hr;
55 | }
56 |
57 | ULONG STDMETHODCALLTYPE AddRef()
58 | {
59 | ULONG refCount = InterlockedIncrement(&m_refCount);
60 | MsRdpEx_LogPrint(DEBUG, "CMsRdpExCoreApi::AddRef() = %d", refCount);
61 | return refCount;
62 | }
63 |
64 | ULONG STDMETHODCALLTYPE Release()
65 | {
66 | ULONG refCount = InterlockedDecrement(&m_refCount);
67 |
68 | MsRdpEx_LogPrint(DEBUG, "CMsRdpExCoreApi::Release() = %d", refCount);
69 |
70 | if (refCount == 0)
71 | {
72 | delete this;
73 | return 0;
74 | }
75 |
76 | return refCount;
77 | }
78 |
79 | // IMsRdpExCoreApi
80 | public:
81 | HRESULT __stdcall Load()
82 | {
83 | MsRdpEx_LogPrint(DEBUG, "CMsRdpExCoreApi::Load");
84 | return S_OK;
85 | }
86 |
87 | HRESULT __stdcall Unload()
88 | {
89 | MsRdpEx_LogPrint(DEBUG, "CMsRdpExCoreApi::Unload");
90 | return S_OK;
91 | }
92 |
93 | const char* __stdcall GetMsRdpExDllPath()
94 | {
95 | return (const char*) m_MsRdpExDllPath;
96 | }
97 |
98 | void __stdcall SetLogEnabled(bool logEnabled)
99 | {
100 | MsRdpEx_SetLogEnabled(logEnabled);
101 | }
102 |
103 | void __stdcall SetLogLevel(uint32_t logLevel)
104 | {
105 | MsRdpEx_SetLogLevel(logLevel);
106 | }
107 |
108 | void __stdcall SetLogFilePath(const char* logFilePath)
109 | {
110 | MsRdpEx_SetLogFilePath(logFilePath);
111 | }
112 |
113 | void __stdcall SetPcapEnabled(bool pcapEnabled)
114 | {
115 | MsRdpEx_SetPcapEnabled(pcapEnabled);
116 | }
117 |
118 | void __stdcall SetPcapFilePath(const char* pcapFilePath)
119 | {
120 | MsRdpEx_SetPcapFilePath(pcapFilePath);
121 | }
122 |
123 | void __stdcall SetAxHookEnabled(bool axHookEnabled)
124 | {
125 | MsRdpEx_SetAxHookEnabled(axHookEnabled);
126 | }
127 |
128 | bool __stdcall QueryInstanceByWindowHandle(HWND hWnd, LPVOID* ppvObject)
129 | {
130 | HRESULT hr;
131 | IMsRdpExInstance* instance = NULL;
132 |
133 | instance = (IMsRdpExInstance*) MsRdpEx_InstanceManager_FindByOutputPresenterHwnd(hWnd);
134 |
135 | if (!instance)
136 | return false;
137 |
138 | hr = instance->QueryInterface(IID_IMsRdpExInstance, ppvObject);
139 |
140 | return (hr == S_OK) ? true : false;
141 | }
142 |
143 | bool __stdcall OpenInstanceForWindowHandle(HWND hWnd, LPVOID* ppvObject)
144 | {
145 | HRESULT hr;
146 | IMsRdpExInstance* instance = NULL;
147 |
148 | instance = (IMsRdpExInstance*) MsRdpEx_InstanceManager_FindByOutputPresenterHwnd(hWnd);
149 |
150 | if (!instance)
151 | {
152 | instance = (IMsRdpExInstance*) CMsRdpExInstance_New(NULL);
153 | instance->AttachOutputWindow(hWnd, NULL);
154 | MsRdpEx_InstanceManager_Add((CMsRdpExInstance*) instance);
155 | }
156 |
157 | hr = instance->QueryInterface(IID_IMsRdpExInstance, ppvObject);
158 |
159 | return (hr == S_OK) ? true : false;
160 | }
161 |
162 | private:
163 | ULONG m_refCount;
164 | char m_MsRdpExDllPath[MSRDPEX_MAX_PATH] = { 0 };
165 | };
166 |
167 | HRESULT CDECL MsRdpExCoreApi_CreateInstance(LPVOID* ppvObject)
168 | {
169 | CMsRdpExCoreApi* pObj = new CMsRdpExCoreApi();
170 | *ppvObject = (LPVOID) pObj;
171 | return S_OK;
172 | }
173 |
174 | HRESULT CDECL MsRdpEx_CreateInstance(REFCLSID riid, LPVOID* ppvObject)
175 | {
176 | IUnknown* pUnknown = NULL;
177 | HRESULT hr = E_NOINTERFACE;
178 |
179 | char iid[MSRDPEX_GUID_STRING_SIZE];
180 | MsRdpEx_GuidBinToStr((GUID*)&riid, iid, 0);
181 |
182 | MsRdpEx_LogPrint(DEBUG, "MsRdpEx_CreateInstance(%s)", iid);
183 |
184 | if (riid == IID_IMsRdpExCoreApi) {
185 | hr = MsRdpExCoreApi_CreateInstance((LPVOID*) &pUnknown);
186 | }
187 | else if (riid == IID_IMsRdpExProcess) {
188 | hr = MsRdpExProcess_CreateInstance((LPVOID*) &pUnknown);
189 | }
190 |
191 | if (hr == S_OK) {
192 | hr = pUnknown->QueryInterface(riid, ppvObject);
193 | }
194 |
195 | return hr;
196 | }
197 |
--------------------------------------------------------------------------------
/dll/RdpDvcClient.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_DVC_CLIENT_H
2 | #define MSRDPEX_DVC_CLIENT_H
3 |
4 | #include "MsRdpEx.h"
5 |
6 | #include
7 |
8 | class CRdpDvcClient :
9 | public IWTSVirtualChannelCallback
10 | {
11 | public:
12 | // IUnknown methods
13 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) override;
14 | ULONG STDMETHODCALLTYPE AddRef() override;
15 | ULONG STDMETHODCALLTYPE Release() override;
16 |
17 | // IWTSVirtualChannelCallback methods
18 | HRESULT STDMETHODCALLTYPE OnDataReceived(ULONG cbSize, BYTE* pBuffer) override;
19 | HRESULT STDMETHODCALLTYPE OnClose(void) override;
20 |
21 | // Additional methods
22 | void SetChannel(IWTSVirtualChannel* pChannel);
23 | void SetListener(IWTSListener* pListener);
24 |
25 | CRdpDvcClient(void);
26 | virtual ~CRdpDvcClient();
27 |
28 | private:
29 | ULONG m_refCount = 0;
30 | IWTSVirtualChannel* m_pChannel = NULL;
31 | IWTSListener* m_pListener = NULL;
32 | };
33 |
34 | class CRdpDvcListener :
35 | public IWTSListenerCallback
36 | {
37 | public:
38 | // IUnknown methods
39 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) override;
40 | ULONG STDMETHODCALLTYPE AddRef() override;
41 | ULONG STDMETHODCALLTYPE Release() override;
42 |
43 | // IWTSListenerCallback methods
44 | HRESULT STDMETHODCALLTYPE OnNewChannelConnection(IWTSVirtualChannel* pChannel,
45 | BSTR data, BOOL* pbAccept, IWTSVirtualChannelCallback** ppCallback) override;
46 |
47 | // Additional methods
48 | CRdpDvcListener(void);
49 | virtual ~CRdpDvcListener();
50 | private:
51 | ULONG m_refCount = 0;
52 | };
53 |
54 | class CRdpDvcPlugin :
55 | public IWTSPlugin
56 | {
57 | public:
58 | // IUnknown methods
59 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) override;
60 | ULONG STDMETHODCALLTYPE AddRef() override;
61 | ULONG STDMETHODCALLTYPE Release() override;
62 |
63 | // IWTSPlugin methods
64 | HRESULT STDMETHODCALLTYPE Initialize(IWTSVirtualChannelManager* pChannelMgr) override;
65 | HRESULT STDMETHODCALLTYPE Connected() override;
66 | HRESULT STDMETHODCALLTYPE Disconnected(DWORD dwDisconnectCode) override;
67 | HRESULT STDMETHODCALLTYPE Terminated() override;
68 |
69 | // Additional methods
70 | CRdpDvcPlugin(void);
71 | virtual ~CRdpDvcPlugin();
72 |
73 | private:
74 | ULONG m_refCount = 0;
75 | IWTSVirtualChannel* m_pChannel = NULL;
76 | };
77 |
78 | HRESULT STDAPICALLTYPE DllGetClassObject_DvcPlugin(REFCLSID rclsid, REFIID riid, LPVOID* ppv, void* instance);
79 |
80 | #endif /* MSRDPEX_DVC_CLIENT_H */
81 |
--------------------------------------------------------------------------------
/dll/RdpSettings.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devolutions/MsRdpEx/c66219fb32d03d2fdadd6b631efb0376ffe8db88/dll/RdpSettings.cpp
--------------------------------------------------------------------------------
/dll/Stopwatch.c:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 |
5 | static bool g_ProfilingInitialized = false;
6 | static uint32_t g_ProfilingLevel = MSRDPEX_PROF_OFF;
7 |
8 | static bool MsRdpEx_ProfilingLevelActive(uint32_t profilingLevel)
9 | {
10 | if (g_ProfilingLevel == MSRDPEX_PROF_OFF)
11 | return false;
12 |
13 | return profilingLevel >= g_ProfilingLevel;
14 | }
15 |
16 | static void MsRdpEx_SetProfilingLevel(uint32_t profilingLevel)
17 | {
18 | g_ProfilingLevel = profilingLevel;
19 | }
20 |
21 | void MsRdpEx_ProfilingInit()
22 | {
23 | char* envvar = NULL;
24 |
25 | if (g_ProfilingInitialized)
26 | return;
27 |
28 | if (!MsRdpEx_EnvExists("MSRDPEX_PROF_LEVEL"))
29 | {
30 | return;
31 | }
32 |
33 | envvar = MsRdpEx_GetEnv("MSRDPEX_PROF_LEVEL");
34 |
35 | if (envvar)
36 | {
37 | int ival = atoi(envvar);
38 |
39 | if ((ival >= 0) && (ival <= 3))
40 | {
41 | MsRdpEx_SetProfilingLevel((uint32_t)ival);
42 | }
43 | }
44 |
45 | free(envvar);
46 |
47 | g_ProfilingInitialized = true;
48 | }
49 |
50 | void MsRdpEx_Stopwatch_Init(MsRdpEx_Stopwatch* stopwatch, uint32_t profilingLevel, bool start)
51 | {
52 | MsRdpEx_Stopwatch_InitEx(stopwatch, profilingLevel, start, true);
53 | }
54 |
55 | void MsRdpEx_Stopwatch_InitEx(MsRdpEx_Stopwatch* stopwatch, uint32_t profilingLevel, bool start, bool highPrecision)
56 | {
57 | stopwatch->enabled = MsRdpEx_ProfilingLevelActive(profilingLevel);
58 | stopwatch->highPrecision = highPrecision;
59 |
60 | if (start)
61 | {
62 | MsRdpEx_Stopwatch_Start(stopwatch);
63 | }
64 | }
65 |
66 | void MsRdpEx_Stopwatch_Start(MsRdpEx_Stopwatch* stopwatch)
67 | {
68 | if (stopwatch->enabled)
69 | {
70 | if (stopwatch->highPrecision)
71 | {
72 | QueryPerformanceCounter(&stopwatch->time);
73 | }
74 | else
75 | {
76 | stopwatch->tickCount = GetTickCount64();
77 | }
78 | }
79 | }
80 |
81 | double MsRdpEx_Stopwatch_GetTime(MsRdpEx_Stopwatch* stopwatch)
82 | {
83 | if (stopwatch->enabled)
84 | {
85 | if (stopwatch->highPrecision)
86 | {
87 | LARGE_INTEGER frequency;
88 | LARGE_INTEGER stop;
89 | LARGE_INTEGER elapsed;
90 |
91 | QueryPerformanceFrequency(&frequency);
92 | QueryPerformanceCounter(&stop);
93 |
94 | elapsed.QuadPart = stop.QuadPart - stopwatch->time.QuadPart;
95 | elapsed.QuadPart *= 1000;
96 | elapsed.QuadPart /= frequency.QuadPart;
97 |
98 | return (double)elapsed.QuadPart;
99 | }
100 | else
101 | {
102 | ULONGLONG elapsed = GetTickCount64() - stopwatch->tickCount;
103 | return (double) elapsed;
104 | }
105 | }
106 |
107 | return -1;
108 | }
109 |
110 | void MsRdpEx_Stopwatch_Print(MsRdpEx_Stopwatch* stopwatch, uint32_t logLevel, const char* message)
111 | {
112 | if (!stopwatch->enabled)
113 | {
114 | return;
115 | }
116 |
117 | if (!MsRdpEx_IsLogLevelActive(logLevel))
118 | {
119 | return;
120 | }
121 |
122 | MsRdpEx_Log(logLevel, "%s [%.3fms]", message, MsRdpEx_Stopwatch_GetTime(stopwatch));
123 | }
124 |
--------------------------------------------------------------------------------
/dll/Stream.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 |
4 | bool MsRdpEx_Stream_Find(MsRdpEx_Stream* s, const uint8_t* pattern, size_t pattern_length, size_t* offset)
5 | {
6 | MsRdpEx_Stream* _s = NULL;
7 | bool result = false;
8 |
9 | if (MsRdpEx_Stream_GetRemainingLength(s) < pattern_length)
10 | return false;
11 |
12 | _s = MsRdpEx_Stream_New(s->pointer, MsRdpEx_Stream_GetRemainingLength(s), false);
13 |
14 | while (MsRdpEx_Stream_CheckSafeRead(_s, pattern_length))
15 | {
16 | if (memcmp(_s->pointer, pattern, pattern_length) == 0)
17 | {
18 | result = true;
19 | *offset = _s->pointer - s->pointer;
20 | goto exit;
21 | }
22 |
23 | MsRdpEx_StreamSeek(_s, 1);
24 | }
25 |
26 | exit:
27 | MsRdpEx_Stream_Free(_s);
28 |
29 | return result;
30 | }
31 |
32 | bool MsRdpEx_Stream_EnsureCapacity(MsRdpEx_Stream* s, size_t size)
33 | {
34 | if (!s->capacity)
35 | return false;
36 |
37 | if (s->capacity < size)
38 | {
39 | uint8_t* new_buf;
40 | size_t position;
41 | size_t old_capacity;
42 | size_t new_capacity;
43 |
44 | old_capacity = s->capacity;
45 | new_capacity = old_capacity;
46 |
47 | do
48 | {
49 | new_capacity *= 2;
50 | }
51 | while (new_capacity < size);
52 |
53 | position = MsRdpEx_Stream_GetPosition(s);
54 |
55 | if (s->owner)
56 | {
57 | new_buf = (uint8_t*) realloc(s->buffer, new_capacity);
58 |
59 | if (!new_buf)
60 | return false;
61 | }
62 | else
63 | {
64 | new_buf = (uint8_t*) malloc(new_capacity);
65 |
66 | if (!new_buf)
67 | return false;
68 |
69 | CopyMemory(new_buf, s->buffer, old_capacity);
70 |
71 | s->owner = true;
72 | }
73 |
74 | s->buffer = new_buf;
75 | s->capacity = new_capacity;
76 | s->length = new_capacity;
77 | ZeroMemory(&s->buffer[old_capacity], s->capacity - old_capacity);
78 |
79 | MsRdpEx_Stream_SetPosition(s, position);
80 | }
81 |
82 | return true;
83 | }
84 |
85 | bool MsRdpEx_Stream_EnsureRemainingCapacity(MsRdpEx_Stream* s, size_t size)
86 | {
87 | if ((MsRdpEx_Stream_GetPosition(s) + size) > MsRdpEx_Stream_Capacity(s))
88 | return MsRdpEx_Stream_EnsureCapacity(s, MsRdpEx_Stream_Capacity(s) + size);
89 |
90 | return true;
91 | }
92 |
93 | bool MsRdpEx_Stream_CheckSafeRead(MsRdpEx_Stream* s, size_t size)
94 | {
95 | size_t offset;
96 |
97 | if (s->buffer > s->pointer)
98 | return false;
99 |
100 | offset = s->pointer - s->buffer;
101 |
102 | if (s->length < offset)
103 | return false;
104 |
105 | int remaining_length = s->length - (s->pointer - s->buffer);
106 |
107 | if (remaining_length < size)
108 | return false;
109 |
110 | return true;
111 | }
112 |
113 | bool MsRdpEx_Stream_CheckSafeWrite(MsRdpEx_Stream* s, size_t size)
114 | {
115 | if ((MsRdpEx_Stream_GetPosition(s) + size) > MsRdpEx_Stream_Capacity(s))
116 | {
117 | return MsRdpEx_Stream_EnsureCapacity(s, MsRdpEx_Stream_Capacity(s) + size);
118 | }
119 |
120 | return true;
121 | }
122 |
123 | int MsRdpEx_Stream_Format(MsRdpEx_Stream* s, const char* format, ...)
124 | {
125 | int status;
126 | size_t size;
127 | va_list args;
128 |
129 | /* vsnprintf: http://www.cplusplus.com/reference/cstdio/vsnprintf/ */
130 |
131 | va_start(args, format);
132 | size = s->capacity - MsRdpEx_Stream_GetPosition(s);
133 | status = vsnprintf((char*) s->pointer, size, format, args);
134 | va_end(args);
135 |
136 | if (status < 0)
137 | return -1;
138 |
139 | if (status < size)
140 | {
141 | MsRdpEx_StreamSeek(s, (size_t) status);
142 | }
143 | else
144 | {
145 | if (!MsRdpEx_Stream_CheckSafeWrite(s, status + 1))
146 | return -1;
147 |
148 | va_start(args, format);
149 | size = s->capacity - MsRdpEx_Stream_GetPosition(s);
150 | status = vsnprintf((char*) s->pointer, size, format, args);
151 | va_end(args);
152 |
153 | if ((status < 0) || (status >= size))
154 | return -1;
155 |
156 | MsRdpEx_StreamSeek(s, (size_t) status);
157 | }
158 |
159 | return status;
160 | }
161 |
162 | MsRdpEx_Stream* MsRdpEx_Stream_Init(MsRdpEx_Stream* s, uint8_t* buffer, size_t size, bool owner)
163 | {
164 | if (!s)
165 | return NULL;
166 |
167 | s->owner = owner;
168 |
169 | if (buffer)
170 | {
171 | s->buffer = buffer;
172 | }
173 | else
174 | {
175 | s->buffer = (uint8_t*) malloc(size);
176 | s->owner = true;
177 | }
178 |
179 | if (!s->buffer)
180 | return NULL;
181 |
182 | s->pointer = s->buffer;
183 | s->capacity = size;
184 | s->length = size;
185 |
186 | return s;
187 | }
188 |
189 | void MsRdpEx_Stream_Uninit(MsRdpEx_Stream* s)
190 | {
191 | if (!s)
192 | return;
193 |
194 | if (s->owner)
195 | {
196 | free(s->buffer);
197 | s->buffer = NULL;
198 | }
199 |
200 | s->owner = false;
201 | s->pointer = NULL;
202 | s->capacity = 0;
203 | s->length = 0;
204 | }
205 |
206 | MsRdpEx_Stream* MsRdpEx_Stream_New(uint8_t* buffer, size_t size, bool owner)
207 | {
208 | MsRdpEx_Stream* s;
209 |
210 | s = (MsRdpEx_Stream*) malloc(sizeof(MsRdpEx_Stream));
211 |
212 | if (!s)
213 | return NULL;
214 |
215 | if (!MsRdpEx_Stream_Init(s, buffer, size, owner))
216 | {
217 | free(s);
218 | return NULL;
219 | }
220 |
221 | return s;
222 | }
223 |
224 | void MsRdpEx_Stream_Free(MsRdpEx_Stream* s)
225 | {
226 | if (!s)
227 | return;
228 |
229 | MsRdpEx_Stream_Uninit(s);
230 |
231 | free(s);
232 | }
233 |
--------------------------------------------------------------------------------
/dll/TSObjects.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 |
4 | #include
5 |
6 | #include "TSObjects.h"
7 |
8 | const char* GetTSPropertyTypeName(uint8_t propType)
9 | {
10 | const char* name = "none";
11 |
12 | switch (propType)
13 | {
14 | case 1: name = "ULONG"; break;
15 | case 2: name = "INT"; break;
16 | case 3: name = "BOOL"; break;
17 | case 4: name = "String"; break;
18 | case 5: name = "Binary"; break;
19 | case 6: name = "SecureString"; break;
20 | case 7: name = "IUnknown"; break;
21 | }
22 |
23 | return name;
24 | }
25 |
26 | PROPERTY_ENTRY_EX* FindTSProperty(ITSPropertySet* pTSPropertySet, const char* name)
27 | {
28 | PROPERTY_ENTRY_EX* found = NULL;
29 | uint32_t propCount = pTSPropertySet->propCount;
30 | PROPERTY_ENTRY_EX* propMap = pTSPropertySet->propMap;
31 |
32 | for (int i = 0; i < propCount; i++)
33 | {
34 | PROPERTY_ENTRY_EX* prop = &propMap[i];
35 |
36 | if (MsRdpEx_StringEquals(name, prop->propName)) {
37 | found = prop;
38 | break;
39 | }
40 | }
41 |
42 | return found;
43 | }
44 |
45 | bool GetTSPropertyType(ITSPropertySet* pTSPropertySet, const char* name, uint8_t* pPropType)
46 | {
47 | PROPERTY_ENTRY_EX* prop = FindTSProperty(pTSPropertySet, name);
48 |
49 | *pPropType = 0;
50 |
51 | if (prop) {
52 | *pPropType = prop->propType;
53 | }
54 |
55 | return prop ? true : false;
56 | }
57 |
58 | void DumpTSPropertyMap(ITSPropertySet* pTSPropertySet, const char* name)
59 | {
60 | uint32_t propCount = pTSPropertySet->propCount;
61 | PROPERTY_ENTRY_EX* propMap = pTSPropertySet->propMap;
62 |
63 | MsRdpEx_LogPrint(DEBUG, "TSPropertySet: %s (%d)", name, propCount);
64 |
65 | for (int i = 0; i < propCount; i++)
66 | {
67 | PROPERTY_ENTRY_EX* prop = &propMap[i];
68 | MsRdpEx_LogPrint(DEBUG, "%s (%s)", prop->propName, GetTSPropertyTypeName(prop->propType));
69 | }
70 | }
71 |
72 | bool TsPropertyMap_IsCoreProps(ITSPropertySet* pTSPropertySet)
73 | {
74 | uint32_t propCount;
75 | PROPERTY_ENTRY_EX* propMap;
76 |
77 | if (!MsRdpEx_CanReadUnsafePtr((void*)pTSPropertySet, sizeof(ITSPropertySet)))
78 | return false;
79 |
80 | propCount = pTSPropertySet->propCount;
81 | propMap = pTSPropertySet->propMap;
82 |
83 | if ((propCount < 100) || (propCount > 500))
84 | return false;
85 |
86 | if (!MsRdpEx_CanReadUnsafePtr((void*)propMap, propCount * sizeof(PROPERTY_ENTRY_EX)))
87 | return false;
88 |
89 | for (int i = 0; i < 10; i++)
90 | {
91 | PROPERTY_ENTRY_EX* prop = &propMap[i];
92 |
93 | if (MsRdpEx_StringIEqualsUnsafePtr(prop->propName, "ServerName")) {
94 | return true;
95 | }
96 | }
97 |
98 | return false;
99 | }
100 |
101 | bool TsPropertyMap_IsBaseProps(ITSPropertySet* pTSPropertySet)
102 | {
103 | uint32_t propCount;
104 | PROPERTY_ENTRY_EX* propMap;
105 |
106 | if (!MsRdpEx_CanReadUnsafePtr((void*)pTSPropertySet, sizeof(ITSPropertySet)))
107 | return false;
108 |
109 | propCount = pTSPropertySet->propCount;
110 | propMap = pTSPropertySet->propMap;
111 |
112 | if ((propCount < 100) || (propCount > 500))
113 | return false;
114 |
115 | if (!MsRdpEx_CanReadUnsafePtr((void*)propMap, propCount * sizeof(PROPERTY_ENTRY_EX)))
116 | return false;
117 |
118 | for (int i = 0; i < 10; i++)
119 | {
120 | PROPERTY_ENTRY_EX* prop = &propMap[i];
121 |
122 | if (MsRdpEx_StringIEqualsUnsafePtr(prop->propName, "FullScreen")) {
123 | return true;
124 | }
125 | }
126 |
127 | return false;
128 | }
129 |
130 | bool TsPropertyMap_IsTransportProps(ITSPropertySet* pTSPropertySet)
131 | {
132 | uint32_t propCount;
133 | PROPERTY_ENTRY_EX* propMap;
134 |
135 | if (!MsRdpEx_CanReadUnsafePtr((void*)pTSPropertySet, sizeof(ITSPropertySet)))
136 | return false;
137 |
138 | propCount = pTSPropertySet->propCount;
139 | propMap = pTSPropertySet->propMap;
140 |
141 | if ((propCount < 25) || (propCount > 500))
142 | return false;
143 |
144 | if (!MsRdpEx_CanReadUnsafePtr((void*)propMap, propCount * sizeof(PROPERTY_ENTRY_EX)))
145 | return false;
146 |
147 | for (int i = 0; i < 10; i++)
148 | {
149 | PROPERTY_ENTRY_EX* prop = &propMap[i];
150 |
151 | if (MsRdpEx_StringIEqualsUnsafePtr(prop->propName, "GatewayHostname")) {
152 | return true;
153 | }
154 | }
155 |
156 | return false;
157 | }
158 |
159 | typedef struct
160 | {
161 | void* param1;
162 | void* param2;
163 | void* name;
164 | void* marker;
165 | } COPWnd;
166 |
167 | void CDECL MsRdpEx_OutputWindow_OnCreate(HWND hWnd, void* pUserData)
168 | {
169 | COPWnd* pOPWnd = (COPWnd*) pUserData;
170 |
171 | MsRdpEx_LogPrint(DEBUG, "WindowCreate: %s name: %s hWnd: %p", pOPWnd->name, hWnd);
172 | }
173 |
--------------------------------------------------------------------------------
/dotnet/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | obj/
3 | .vs/
4 | *.csproj.user
5 | Directory.Build.props
6 | launchSettings.json
--------------------------------------------------------------------------------
/dotnet/AxInterop.MSTSCLib/AxInterop.MSTSCLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AxInterop.MSTSCLib
6 | AxInterop.MSTSCLib
7 | 1.0.0.0
8 | Library
9 | true
10 | disable
11 | True
12 | $(CMakeOutputPath)
13 |
14 |
15 |
16 |
17 | <_Parameter1>11/09/2021 16:17:20
18 |
19 |
20 |
21 |
22 |
23 | True
24 | ..\Interop.MSTSCLib\Interop.MSTSCLib.dll
25 | False
26 |
27 |
28 |
--------------------------------------------------------------------------------
/dotnet/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | include_external_msproject(AxInterop.MSTSCLib
3 | "${CMAKE_CURRENT_SOURCE_DIR}/AxInterop.MSTSCLib/AxInterop.MSTSCLib.csproj")
4 |
5 | include_external_msproject(Devolutions.MsRdpEx
6 | "${CMAKE_CURRENT_SOURCE_DIR}/Devolutions.MsRdpEx/Devolutions.MsRdpEx.csproj")
7 |
8 | include_external_msproject(MsRdpEx_App
9 | "${CMAKE_CURRENT_SOURCE_DIR}/MsRdpEx_App/MsRdpEx_App.csproj")
10 |
11 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Directory.Build.props.in"
12 | "${CMAKE_CURRENT_SOURCE_DIR}/Directory.Build.props" @ONLY)
13 |
--------------------------------------------------------------------------------
/dotnet/Devolutions.MsRdpEx/Devolutions.MsRdpEx.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Devolutions.MsRdpEx
6 | Devolutions.MsRdpEx
7 | 1.0.0.0
8 | mamoreau@devolutions.net
9 | Devolutions
10 | Microsoft RDP Extensions
11 | Library
12 | True
13 | $(CMakeOutputPath)
14 | enable
15 | True
16 | True
17 |
18 |
19 |
20 |
21 | False
22 | ..\Interop.MSTSCLib\Interop.MSTSCLib.dll
23 | False
24 | True
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/dotnet/Devolutions.MsRdpEx/Devolutions.MsRdpEx.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | runtimes\win-x86\native\MsRdpEx.dll
6 | Included
7 | False
8 | PreserveNewest
9 | true
10 | false
11 |
12 |
13 | runtimes\win-x64\native\MsRdpEx.dll
14 | Included
15 | False
16 | PreserveNewest
17 | true
18 | false
19 |
20 |
21 | runtimes\win-arm64\native\MsRdpEx.dll
22 | Included
23 | False
24 | PreserveNewest
25 | true
26 | false
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/dotnet/Devolutions.MsRdpEx/MarshalHelpers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Net.NetworkInformation;
4 | using System.Runtime.CompilerServices;
5 | using System.Runtime.InteropServices;
6 | using System.Text;
7 |
8 | namespace MsRdpEx
9 | {
10 | public class MarshalHelpers
11 | {
12 | // Replacement for native strlen() function
13 | public static unsafe int PtrStringLength(IntPtr ptr)
14 | {
15 | byte* p = (byte*)ptr;
16 | int length = 0;
17 |
18 | while (*p != 0)
19 | {
20 | p++;
21 | length++;
22 | }
23 |
24 | return length;
25 | }
26 |
27 | // Replacement for Marshal.PtrToStringUTF8
28 | public static string PtrToStringUTF8(IntPtr ptr)
29 | {
30 | int length = PtrStringLength(ptr);
31 | byte[] buffer = new byte[length];
32 | Marshal.Copy(ptr, buffer, 0, length);
33 | return System.Text.Encoding.UTF8.GetString(buffer);
34 | }
35 |
36 | // Replacement for Marshal.StringToCoTaskMemUTF8
37 | public static IntPtr StringToCoTaskMemUTF8(string str)
38 | {
39 | if (str == null)
40 | {
41 | return IntPtr.Zero;
42 | }
43 |
44 | byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(str);
45 | IntPtr unmanagedMemory = Marshal.AllocCoTaskMem(utf8Bytes.Length + 1);
46 | Marshal.Copy(utf8Bytes, 0, unmanagedMemory, utf8Bytes.Length);
47 | Marshal.WriteByte(unmanagedMemory, utf8Bytes.Length, 0);
48 |
49 | return unmanagedMemory;
50 | }
51 |
52 | // Replacement for [MarshalAs(UnmanagedType.LPStr)]
53 | // [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MarshalHelpers.LPUTF8Str))]
54 |
55 | public class LPUTF8Str : ICustomMarshaler
56 | {
57 | public void CleanUpManagedData(object ManagedObj)
58 | {
59 |
60 | }
61 |
62 | public void CleanUpNativeData(IntPtr pNativeData)
63 | {
64 | Marshal.FreeCoTaskMem(pNativeData);
65 | }
66 |
67 | public int GetNativeDataSize()
68 | {
69 | throw new NotImplementedException();
70 | }
71 |
72 | public IntPtr MarshalManagedToNative(object ManagedObj)
73 | {
74 | return MarshalHelpers.StringToCoTaskMemUTF8((string)ManagedObj);
75 | }
76 |
77 | public object MarshalNativeToManaged(IntPtr pNativeData)
78 | {
79 | return MarshalHelpers.PtrToStringUTF8(pNativeData);
80 | }
81 |
82 | public static ICustomMarshaler GetInstance(string cookie) => new LPUTF8Str();
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/dotnet/Devolutions.MsRdpEx/RdpCoreApi.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace MsRdpEx
5 | {
6 | public class RdpCoreApi
7 | {
8 | public IMsRdpExCoreApi iface;
9 |
10 | public RdpCoreApi()
11 | {
12 | iface = Bindings.GetCoreApi();
13 | }
14 |
15 | public void Load()
16 | {
17 | iface.Load();
18 | }
19 |
20 | public void Unload()
21 | {
22 | iface.Unload();
23 | }
24 |
25 | public string MsRdpExDllPath
26 | {
27 | get { return MarshalHelpers.PtrToStringUTF8(iface.GetMsRdpExDllPath()); }
28 | }
29 |
30 | public bool LogEnabled
31 | {
32 | set { iface.SetLogEnabled(value); }
33 | }
34 |
35 | public MsRdpEx_LogLevel LogLevel
36 | {
37 | set { iface.SetLogLevel(value); }
38 | }
39 |
40 | public string LogFilePath
41 | {
42 | set { iface.SetLogFilePath(value); }
43 | }
44 |
45 | public bool PcapEnabled
46 | {
47 | set { iface.SetPcapEnabled(value); }
48 | }
49 |
50 | public string PcapFilePath
51 | {
52 | set { iface.SetPcapFilePath(value); }
53 | }
54 |
55 | public bool AxHookEnabled
56 | {
57 | set { iface.SetAxHookEnabled(value); }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/dotnet/Devolutions.MsRdpEx/RdpInstance.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace MsRdpEx
5 | {
6 | public class RdpInstance
7 | {
8 | public IMsRdpExInstance iface;
9 |
10 | public RdpInstance(IMsRdpExInstance iface) {
11 | this.iface = iface;
12 | }
13 |
14 | public Guid SessionId
15 | {
16 | get { Guid val; iface.GetSessionId(out val); return val; }
17 | }
18 |
19 | public bool OutputMirrorEnabled {
20 | get { bool val; iface.GetOutputMirrorEnabled(out val); return val; }
21 | set { iface.SetOutputMirrorEnabled(value); }
22 | }
23 |
24 | public bool VideoRecordingEnabled
25 | {
26 | get { bool val; iface.GetVideoRecordingEnabled(out val); return val; }
27 | set { iface.SetVideoRecordingEnabled(value); }
28 | }
29 |
30 | public bool DumpBitmapUpdates
31 | {
32 | get { bool val; iface.GetDumpBitmapUpdates(out val); return val; }
33 | set { iface.SetDumpBitmapUpdates(value); }
34 | }
35 |
36 | public bool GetShadowBitmap(ref IntPtr phDC, ref IntPtr phBitmap, ref IntPtr pBitmapData,
37 | ref UInt32 pBitmapWidth, ref UInt32 pBitmapHeight, ref UInt32 pBitmapStep)
38 | {
39 | return iface.GetShadowBitmap(ref phDC, ref phBitmap, ref pBitmapData,
40 | ref pBitmapWidth, ref pBitmapHeight, ref pBitmapStep);
41 | }
42 |
43 | public object WTSPlugin
44 | {
45 | set { iface.SetWTSPluginObject(Marshal.GetIUnknownForObject(value)); }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/dotnet/Devolutions.MsRdpEx/RdpProcess.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using System.Diagnostics;
4 | using System.Collections.Generic;
5 | using System.Collections.ObjectModel;
6 |
7 | namespace MsRdpEx
8 | {
9 | public class RdpProcess
10 | {
11 | public IMsRdpExProcess iface;
12 |
13 | public RdpProcess(string[] args, string appName, string axName) {
14 | iface = Bindings.StartProcess(args, appName, axName);
15 | }
16 |
17 | public RdpProcess(ProcessStartInfo startInfo)
18 | {
19 | iface = Bindings.CreateProcess();
20 |
21 | this.FileName = startInfo.FileName;
22 | this.WorkingDirectory = startInfo.WorkingDirectory;
23 | this.SetArguments(startInfo.Arguments);
24 | SetEnvironmentBlock(startInfo.Environment);
25 | }
26 |
27 | public void SetArguments(string arguments)
28 | {
29 | iface.SetArguments(arguments);
30 | }
31 |
32 | public void SetArgumentVector(string[] args)
33 | {
34 | StringBuilder sb = new StringBuilder();
35 | foreach (string arg in args)
36 | {
37 | sb.AppendFormat("{0}\0", arg);
38 | }
39 | sb.Append("\0");
40 |
41 | string argumentBlock = sb.ToString();
42 | iface.SetArgumentBlock(argumentBlock);
43 | }
44 |
45 | private void SetEnvironmentBlock(IDictionary environment)
46 | {
47 | StringBuilder sb = new StringBuilder();
48 | foreach (KeyValuePair envvar in environment)
49 | {
50 | sb.AppendFormat("{0}={1}\0", envvar.Key, envvar.Value);
51 | }
52 | sb.Append("\0");
53 |
54 | string environmentBlock = sb.ToString();
55 | iface.SetEnvironmentBlock(environmentBlock);
56 | }
57 |
58 | public string FileName
59 | {
60 | set { iface.SetFileName(value); }
61 | }
62 |
63 | public string WorkingDirectory
64 | {
65 | set { iface.SetWorkingDirectory(value); }
66 | }
67 |
68 | public static Process StartProcess(ProcessStartInfo startInfo)
69 | {
70 | RdpProcess rdpProcess = new RdpProcess(startInfo);
71 | return rdpProcess.Start();
72 | }
73 |
74 | public Process Start()
75 | {
76 | iface.StartWithInfo();
77 | uint processId = this.GetProcessId();
78 | return Process.GetProcessById((int)processId);
79 | }
80 |
81 | public void Stop(UInt32 exitCode)
82 | {
83 | iface.Stop(exitCode);
84 | }
85 |
86 | public void Wait(UInt32 milliseconds)
87 | {
88 | iface.Wait(milliseconds);
89 | }
90 |
91 | public uint GetProcessId()
92 | {
93 | return iface.GetProcessId();
94 | }
95 |
96 | public uint GetExitCode()
97 | {
98 | return iface.GetExitCode();
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/dotnet/Directory.Build.props.in:
--------------------------------------------------------------------------------
1 |
2 |
3 | @CMAKE_SOURCE_DIR@
4 | @CMAKE_BINARY_DIR@
5 | $(CMakeBinaryDir)/$(Configuration)
6 |
7 |
--------------------------------------------------------------------------------
/dotnet/Interop.MSTSCLib/Interop.MSTSCLib.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devolutions/MsRdpEx/c66219fb32d03d2fdadd6b631efb0376ffe8db88/dotnet/Interop.MSTSCLib/Interop.MSTSCLib.dll
--------------------------------------------------------------------------------
/dotnet/MsRdpEx_App/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/dotnet/MsRdpEx_App/MainDlg.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/dotnet/MsRdpEx_App/MsRdpEx_App.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | Win32;x64;ARM64
6 | net8.0-windows
7 | True
8 | disable
9 | True
10 | $(CMakeOutputPath)
11 | True
12 | 11
13 |
14 |
15 |
16 | MsRdpEx
17 | MsRdpEx
18 | 1.0.0
19 | 1.0.0.0
20 | 1.0.0.0
21 | Always
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | True
43 | ..\Interop.MSTSCLib\Interop.MSTSCLib.dll
44 |
45 |
46 |
--------------------------------------------------------------------------------
/dotnet/MsRdpEx_App/NowProtoPipeTransport.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.Pipes;
4 | using System.Threading.Tasks;
5 |
6 | namespace MsRdpEx_App
7 | {
8 | internal class NowProtoPipeTransport: IDisposable, IAsyncDisposable
9 | {
10 | public NowProtoPipeTransport(NamedPipeServerStream pipe)
11 | {
12 | _pipe = pipe;
13 | }
14 |
15 | public async Task Write(byte[] data)
16 | {
17 | await _pipe.WriteAsync(data);
18 | await _pipe.FlushAsync();
19 | }
20 |
21 | public async Task Read()
22 | {
23 | var bytesRead = await _pipe.ReadAsync(_buffer, 0, _buffer.Length);
24 |
25 | if (bytesRead == 0)
26 | {
27 | throw new EndOfStreamException("End of stream reached (DVC)");
28 | }
29 |
30 | return _buffer[..bytesRead];
31 | }
32 |
33 | public void Dispose()
34 | {
35 | _pipe?.Dispose();
36 | }
37 |
38 | public async ValueTask DisposeAsync()
39 | {
40 | if (_pipe != null) await _pipe.DisposeAsync();
41 | }
42 |
43 | private readonly NamedPipeServerStream _pipe;
44 | // 64K message buffer
45 | private readonly byte[] _buffer = new byte[64 * 1024];
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/dotnet/MsRdpEx_App/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using System.Windows.Forms;
7 |
8 | namespace MsRdpEx_App
9 | {
10 | static class Program
11 | {
12 | ///
13 | /// The main entry point for the application.
14 | ///
15 | [STAThread]
16 | static void Main()
17 | {
18 | Application.EnableVisualStyles();
19 | Application.SetCompatibleTextRenderingDefault(false);
20 | Application.Run(new MainDlg());
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/dotnet/MsRdpEx_App/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace MsRdpEx.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MsRdpEx.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/dotnet/MsRdpEx_App/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | text/microsoft-resx
48 |
49 |
50 | 2.0
51 |
52 |
53 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
54 |
55 |
56 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
57 |
58 |
--------------------------------------------------------------------------------
/dotnet/MsRdpEx_App/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 |
12 | namespace MsRdpEx.Properties
13 | {
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
17 | {
18 |
19 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
20 |
21 | public static Settings Default
22 | {
23 | get
24 | {
25 | return defaultInstance;
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/dotnet/MsRdpEx_App/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/dotnet/MsRdpEx_App/RdpManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Collections.Generic;
4 | using System.Runtime.InteropServices;
5 |
6 | using MsRdpEx;
7 |
8 | namespace MsRdpEx_App
9 | {
10 | public class MsRdpExManager
11 | {
12 | private static readonly RdpCoreApi coreApi;
13 |
14 | private static bool axHookEnabled = true;
15 |
16 | public RdpCoreApi CoreApi { get => coreApi; }
17 |
18 | public bool AxHookEnabled { get => axHookEnabled; }
19 |
20 | private static RdpCoreApi LoadCoreApi()
21 | {
22 | RdpCoreApi coreApi = new RdpCoreApi();
23 |
24 | string logFilePath = Environment.ExpandEnvironmentVariables("%LocalAppData%\\MsRdpEx\\HostApp.log");
25 | string pcapFilePath = Environment.ExpandEnvironmentVariables("%LocalAppData%\\MsRdpEx\\capture.pcap");
26 |
27 | coreApi.LogEnabled = true;
28 | coreApi.LogLevel = MsRdpEx_LogLevel.Trace;
29 | coreApi.LogFilePath = logFilePath;
30 | coreApi.PcapEnabled = false;
31 | coreApi.PcapFilePath = pcapFilePath;
32 | coreApi.AxHookEnabled = axHookEnabled;
33 | coreApi.Load();
34 |
35 | return coreApi;
36 | }
37 |
38 | private static MsRdpExManager instance = null;
39 | private static readonly object padlock = new object();
40 |
41 | public static MsRdpExManager Instance
42 | {
43 | get
44 | {
45 | lock (padlock)
46 | {
47 | if (instance == null)
48 | {
49 | instance = new MsRdpExManager();
50 | }
51 |
52 | return instance;
53 | }
54 | }
55 | }
56 |
57 | static MsRdpExManager()
58 | {
59 | coreApi = LoadCoreApi();
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/dotnet/MsRdpEx_App/RdpView.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/dotnet/common.build.pre.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | net48;net8.0-windows
6 | True
7 | disable
8 | 11
9 | Win32;x64;ARM64
10 | AnyCPU
11 | MSIL
12 | True
13 |
14 |
15 |
16 | False
17 | Full
18 |
19 |
20 |
21 | True
22 | None
23 |
24 |
--------------------------------------------------------------------------------
/exe/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | add_subdirectory(mstscex)
3 | add_subdirectory(msrdcex)
4 | add_subdirectory(vmconnectex)
5 |
--------------------------------------------------------------------------------
/exe/msrdcex/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # msrdcex launcher executable
2 |
3 | windows_rc_generate_version_info(
4 | NAME "msrdcex" TYPE "EXE"
5 | VERSION "${MSRDPEX_VERSION}"
6 | FILENAME "msrdcex.exe"
7 | VENDOR "${MSRDPEX_VENDOR}"
8 | COPYRIGHT "${MSRDPEX_COPYRIGHT}"
9 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
10 |
11 | source_group("Resources" FILES msrdcex.rc)
12 |
13 | add_executable(msrdcex WIN32
14 | msrdcex.cpp
15 | msrdcex.rc)
16 |
17 | target_link_libraries(msrdcex MsRdpEx_Dll)
18 |
--------------------------------------------------------------------------------
/exe/msrdcex/msrdcex.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "../dll/AxHost/RdpWinMain.h"
3 |
4 | int WINAPI wWinMain(
5 | _In_ HINSTANCE hInstance,
6 | _In_opt_ HINSTANCE hPrevInstance,
7 | _In_ LPWSTR lpCmdLine,
8 | _In_ int nShowCmd)
9 | {
10 | return MsRdpEx_WinMain(hInstance, hPrevInstance, lpCmdLine, nShowCmd, "msrdc");
11 | }
12 |
--------------------------------------------------------------------------------
/exe/msrdcex/msrdcex.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devolutions/MsRdpEx/c66219fb32d03d2fdadd6b631efb0376ffe8db88/exe/msrdcex/msrdcex.ico
--------------------------------------------------------------------------------
/exe/msrdcex/msrdcex.rc:
--------------------------------------------------------------------------------
1 |
2 | IDI_APP_ICON ICON "msrdcex.ico"
3 |
4 | #include "version.rc"
5 |
--------------------------------------------------------------------------------
/exe/mstscex/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # mstscex launcher executable
2 |
3 | windows_rc_generate_version_info(
4 | NAME "mstscex" TYPE "EXE"
5 | VERSION "${MSRDPEX_VERSION}"
6 | FILENAME "mstscex.exe"
7 | VENDOR "${MSRDPEX_VENDOR}"
8 | COPYRIGHT "${MSRDPEX_COPYRIGHT}"
9 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
10 |
11 | source_group("Resources" FILES mstscex.rc)
12 |
13 | add_executable(mstscex WIN32
14 | mstscex.cpp
15 | mstscex.rc)
16 |
17 | target_link_libraries(mstscex MsRdpEx_Dll)
18 |
--------------------------------------------------------------------------------
/exe/mstscex/mstscex.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "../dll/AxHost/RdpWinMain.h"
3 |
4 | int WINAPI wWinMain(
5 | _In_ HINSTANCE hInstance,
6 | _In_opt_ HINSTANCE hPrevInstance,
7 | _In_ LPWSTR lpCmdLine,
8 | _In_ int nShowCmd)
9 | {
10 | return MsRdpEx_WinMain(hInstance, hPrevInstance, lpCmdLine, nShowCmd, "mstsc");
11 | }
12 |
--------------------------------------------------------------------------------
/exe/mstscex/mstscex.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devolutions/MsRdpEx/c66219fb32d03d2fdadd6b631efb0376ffe8db88/exe/mstscex/mstscex.ico
--------------------------------------------------------------------------------
/exe/mstscex/mstscex.rc:
--------------------------------------------------------------------------------
1 |
2 | IDI_APP_ICON ICON "mstscex.ico"
3 |
4 | #include "version.rc"
5 |
--------------------------------------------------------------------------------
/exe/vmconnectex/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # vmconnectex launcher executable
2 |
3 | windows_rc_generate_version_info(
4 | NAME "vmconnectex" TYPE "EXE"
5 | VERSION "${MSRDPEX_VERSION}"
6 | FILENAME "vmconnectex.exe"
7 | VENDOR "${MSRDPEX_VENDOR}"
8 | COPYRIGHT "${MSRDPEX_COPYRIGHT}"
9 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
10 |
11 | source_group("Resources" FILES vmconnectex.rc)
12 |
13 | add_executable(vmconnectex WIN32
14 | vmconnectex.cpp
15 | vmconnectex.rc)
16 |
17 | target_link_libraries(vmconnectex MsRdpEx_Dll)
18 |
--------------------------------------------------------------------------------
/exe/vmconnectex/vmconnectex.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "../dll/AxHost/RdpWinMain.h"
3 |
4 | int WINAPI wWinMain(
5 | _In_ HINSTANCE hInstance,
6 | _In_opt_ HINSTANCE hPrevInstance,
7 | _In_ LPWSTR lpCmdLine,
8 | _In_ int nShowCmd)
9 | {
10 | return MsRdpEx_WinMain(hInstance, hPrevInstance, lpCmdLine, nShowCmd, "vmconnect");
11 | }
12 |
--------------------------------------------------------------------------------
/exe/vmconnectex/vmconnectex.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devolutions/MsRdpEx/c66219fb32d03d2fdadd6b631efb0376ffe8db88/exe/vmconnectex/vmconnectex.ico
--------------------------------------------------------------------------------
/exe/vmconnectex/vmconnectex.rc:
--------------------------------------------------------------------------------
1 |
2 | IDI_APP_ICON ICON "vmconnectex.ico"
3 |
4 | #include "version.rc"
5 |
--------------------------------------------------------------------------------
/images/MsRdpEx_installed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devolutions/MsRdpEx/c66219fb32d03d2fdadd6b631efb0376ffe8db88/images/MsRdpEx_installed.png
--------------------------------------------------------------------------------
/include/MsRdpEx/ArrayList.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_ARRAY_LIST_H
2 | #define MSRDPEX_ARRAY_LIST_H
3 |
4 | #include
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | typedef void* (*MSRDPEX_OBJECT_NEW_FN)(void);
11 | typedef void (*MSRDPEX_OBJECT_INIT_FN)(void* obj);
12 | typedef void (*MSRDPEX_OBJECT_UNINIT_FN)(void* obj);
13 | typedef void (*MSRDPEX_OBJECT_FREE_FN)(void* obj);
14 | typedef bool (*MSRDPEX_OBJECT_EQUALS_FN)(void* objA, void* objB);
15 |
16 | typedef bool (*MSRDPEX_OBJECT_MATCH_FN)(void* obj, void* param);
17 | typedef int (*MSRDPEX_OBJECT_COMPARE_FN)(void* objA, void* objB);
18 |
19 | struct msrdpex_object
20 | {
21 | MSRDPEX_OBJECT_NEW_FN fnObjectNew;
22 | MSRDPEX_OBJECT_INIT_FN fnObjectInit;
23 | MSRDPEX_OBJECT_UNINIT_FN fnObjectUninit;
24 | MSRDPEX_OBJECT_FREE_FN fnObjectFree;
25 | MSRDPEX_OBJECT_EQUALS_FN fnObjectEquals;
26 | };
27 | typedef struct msrdpex_object MsRdpEx_Object;
28 |
29 | typedef struct msrdpex_array_list MsRdpEx_ArrayList;
30 | typedef struct msrdpex_array_list_it MsRdpEx_ArrayListIt;
31 |
32 | #define MSRDPEX_ITERATOR_FLAG_EXCLUSIVE 0x00000001
33 | #define MSRDPEX_ITERATOR_FLAG_DUPLICATE 0x00000002
34 | #define MSRDPEX_ITERATOR_FLAG_REVERSE 0x00000004
35 |
36 | int MsRdpEx_ArrayListIt_Count(MsRdpEx_ArrayListIt* it);
37 | void MsRdpEx_ArrayListIt_Reset(MsRdpEx_ArrayListIt* it);
38 | void* MsRdpEx_ArrayListIt_Current(MsRdpEx_ArrayListIt* it);
39 | bool MsRdpEx_ArrayListIt_Move(MsRdpEx_ArrayListIt* it);
40 | void* MsRdpEx_ArrayListIt_Next(MsRdpEx_ArrayListIt* it);
41 | bool MsRdpEx_ArrayListIt_Done(MsRdpEx_ArrayListIt* it);
42 |
43 | MsRdpEx_ArrayListIt* MsRdpEx_ArrayList_It(MsRdpEx_ArrayList* ctx, uint32_t flags);
44 | void MsRdpEx_ArrayListIt_Finish(MsRdpEx_ArrayListIt* it);
45 |
46 | int MsRdpEx_ArrayList_Capacity(MsRdpEx_ArrayList* ctx);
47 | int MsRdpEx_ArrayList_Count(MsRdpEx_ArrayList* ctx);
48 | bool MsRdpEx_ArrayList_IsEmpty(MsRdpEx_ArrayList* ctx);
49 | int MsRdpEx_ArrayList_Items(MsRdpEx_ArrayList* ctx, uintptr_t** ppItems);
50 | bool MsRdpEx_ArrayList_IsSynchronized(MsRdpEx_ArrayList* ctx);
51 |
52 | void MsRdpEx_ArrayList_Lock(MsRdpEx_ArrayList* ctx);
53 | void MsRdpEx_ArrayList_Unlock(MsRdpEx_ArrayList* ctx);
54 |
55 | void* MsRdpEx_ArrayList_GetItem(MsRdpEx_ArrayList* ctx, int index);
56 | bool MsRdpEx_ArrayList_SetItem(MsRdpEx_ArrayList* ctx, int index, void* obj);
57 |
58 | void* MsRdpEx_ArrayList_GetHead(MsRdpEx_ArrayList* ctx);
59 | void* MsRdpEx_ArrayList_GetTail(MsRdpEx_ArrayList* ctx);
60 |
61 | bool MsRdpEx_ArrayList_DefaultEquals(void* objA, void* objB);
62 |
63 | MsRdpEx_Object* MsRdpEx_ArrayList_Object(MsRdpEx_ArrayList* ctx);
64 |
65 | void* MsRdpEx_ArrayList_Find(MsRdpEx_ArrayList* ctx, MSRDPEX_OBJECT_MATCH_FN fnMatch, void* param);
66 |
67 | void MsRdpEx_ArrayList_Clear(MsRdpEx_ArrayList* ctx, bool free);
68 | bool MsRdpEx_ArrayList_Contains(MsRdpEx_ArrayList* ctx, void* obj);
69 |
70 | int MsRdpEx_ArrayList_Add(MsRdpEx_ArrayList* ctx, void* obj);
71 | bool MsRdpEx_ArrayList_InsertAt(MsRdpEx_ArrayList* ctx, int index, void* obj);
72 | int MsRdpEx_ArrayList_Insert(MsRdpEx_ArrayList* ctx, void* obj, MSRDPEX_OBJECT_COMPARE_FN fnCompare);
73 |
74 | void* MsRdpEx_ArrayList_Remove(MsRdpEx_ArrayList* ctx, void* obj, bool free);
75 | void* MsRdpEx_ArrayList_RemoveAt(MsRdpEx_ArrayList* ctx, int index, bool free);
76 |
77 | void* MsRdpEx_ArrayList_RemoveHead(MsRdpEx_ArrayList* ctx, bool free);
78 | void* MsRdpEx_ArrayList_RemoveTail(MsRdpEx_ArrayList* ctx, bool free);
79 |
80 | int MsRdpEx_ArrayList_RemoveAll(MsRdpEx_ArrayList* ctx, MSRDPEX_OBJECT_MATCH_FN fnMatch, void* param, bool free);
81 | int MsRdpEx_ArrayList_RemoveDuplicates(MsRdpEx_ArrayList* ctx, MSRDPEX_OBJECT_EQUALS_FN fnEquals, bool free);
82 |
83 | int MsRdpEx_ArrayList_IndexOf(MsRdpEx_ArrayList* ctx, void* obj, int startIndex, int count);
84 | int MsRdpEx_ArrayList_LastIndexOf(MsRdpEx_ArrayList* ctx, void* obj, int startIndex, int count);
85 |
86 | MsRdpEx_ArrayList* MsRdpEx_ArrayList_New(bool synchronized);
87 | void MsRdpEx_ArrayList_Free(MsRdpEx_ArrayList* ctx);
88 |
89 | #ifdef __cplusplus
90 | }
91 | #endif
92 |
93 | #endif /* MSRDPEX_ARRAY_LIST_H */
94 |
--------------------------------------------------------------------------------
/include/MsRdpEx/Detours.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_DETOURS_H
2 | #define MSRDPEX_DETOURS_H
3 |
4 | #include
5 |
6 | #include
7 |
8 | #define MSRDPEX_DETOUR_ATTACH(_realFn, _hookFn) \
9 | if (_realFn) DetourAttach((PVOID*)(&_realFn), _hookFn);
10 |
11 | #define MSRDPEX_DETOUR_DETACH(_realFn, _hookFn) \
12 | if (_realFn) DetourDetach((PVOID*)(&_realFn), _hookFn);
13 |
14 | #define MSRDPEX_GETPROCADDRESS(_funcPtr, _funcType, _hModule, _funcName) \
15 | _funcPtr = ( _funcType ) GetProcAddress(_hModule, _funcName);
16 |
17 | #ifdef __cplusplus
18 | extern "C" {
19 | #endif
20 |
21 |
22 |
23 | #ifdef __cplusplus
24 | }
25 | #endif
26 |
27 | #endif // MSRDPEX_DETOURS_H
--------------------------------------------------------------------------------
/include/MsRdpEx/Environment.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_ENVIRONMENT_H
2 | #define MSRDPEX_ENVIRONMENT_H
3 |
4 | #include
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | bool MsRdpEx_SetEnv(const char* name, const char* value);
11 | char* MsRdpEx_GetEnv(const char* name);
12 |
13 | bool MsRdpEx_EnvExists(const char* name);
14 |
15 | bool MsRdpEx_GetEnvBool(const char* name, bool defaultValue);
16 | int MsRdpEx_GetEnvInt(const char* name, int defaultValue);
17 |
18 | char** MsRdpEx_GetEnvironmentVariables(int* envc);
19 | void MsRdpEx_FreeEnvironmentVariables(int envc, char** envs);
20 |
21 | char* MsRdpEx_ReadTextFromNamedPipe(const char* pipeName);
22 |
23 | #ifdef __cplusplus
24 | }
25 | #endif
26 |
27 | #endif /* MSRDPEX_ENVIRONMENT_H */
28 |
--------------------------------------------------------------------------------
/include/MsRdpEx/HashTable.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef MSRDPEX_HASHTABLE_H
3 | #define MSRDPEX_HASHTABLE_H
4 |
5 | #include
6 |
7 | #ifdef __cplusplus
8 | extern "C" {
9 | #endif
10 |
11 | #define MSRDPEX_HASHTABLE_INT_KEY(x) (void*)(size_t)(x)
12 | #define MSRDPEX_HASHTABLE_INT_VALUE(x) (void*)(size_t)(x)
13 |
14 | #define MSRDPEX_HASHTABLE_FLAGS_NONE 0x00
15 | #define MSRDPEX_HASHTABLE_FLAGS_ACCEPT_NULL_KEYS 0x01
16 | #define MSRDPEX_HASHTABLE_FLAGS_ACCEPT_NULL_VALUES 0x02
17 |
18 | typedef uint32_t (*MSRDPEX_HASHTABLE_HASH_FN)(void* key);
19 | typedef bool (*MSRDPEX_HASHTABLE_KEY_COMPARE_FN)(void* key1, void* key2);
20 | typedef bool (*MSRDPEX_HASHTABLE_VALUE_COMPARE_FN)(void* value1, void* value2);
21 | typedef void* (*MSRDPEX_HASHTABLE_KEY_CLONE_FN)(void* key);
22 | typedef void* (*MSRDPEX_HASHTABLE_VALUE_CLONE_FN)(void* value);
23 | typedef void (*MSRDPEX_HASHTABLE_KEY_FREE_FN)(void* key);
24 | typedef void (*MSRDPEX_HASHTABLE_VALUE_FREE_FN)(void* value);
25 |
26 | typedef struct msrdpex_key_value_pair MsRdpEx_KeyValuePair;
27 | typedef struct msrdpex_hash_table MsRdpEx_HashTable;
28 |
29 | void MsRdpEx_HashTable_SetHashFunction(MsRdpEx_HashTable* table, MSRDPEX_HASHTABLE_HASH_FN hash);
30 | void MsRdpEx_HashTable_SetKeyCompareFunction(MsRdpEx_HashTable* table, MSRDPEX_HASHTABLE_KEY_COMPARE_FN keyCompare);
31 | void MsRdpEx_HashTable_SetValueCompareFunction(MsRdpEx_HashTable* table, MSRDPEX_HASHTABLE_VALUE_COMPARE_FN valueCompare);
32 | void MsRdpEx_HashTable_SetKeyCloneFunction(MsRdpEx_HashTable* table, MSRDPEX_HASHTABLE_KEY_CLONE_FN keyClone);
33 | void MsRdpEx_HashTable_SetValueCloneFunction(MsRdpEx_HashTable* table, MSRDPEX_HASHTABLE_VALUE_CLONE_FN valueClone);
34 | void MsRdpEx_HashTable_SetKeyFreeFunction(MsRdpEx_HashTable* table, MSRDPEX_HASHTABLE_KEY_FREE_FN keyFree);
35 | void MsRdpEx_HashTable_SetValueFreeFunction(MsRdpEx_HashTable* table, MSRDPEX_HASHTABLE_VALUE_FREE_FN valueFree);
36 |
37 | /**
38 | * Lock access to the hash table.
39 | */
40 | void MsRdpEx_HashTable_Lock(MsRdpEx_HashTable* table);
41 |
42 | /**
43 | * Unlock access to the hash table.
44 | */
45 | void MsRdpEx_HashTable_Unlock(MsRdpEx_HashTable* table);
46 |
47 | /**
48 | * Gets the number of key/value pairs contained in the HashTable.
49 | */
50 | int MsRdpEx_HashTable_Count(MsRdpEx_HashTable* table);
51 |
52 | /**
53 | * Adds an element with the specified key and value into the HashTable.
54 | */
55 | int MsRdpEx_HashTable_Add(MsRdpEx_HashTable* table, void* key, void* value);
56 |
57 | /**
58 | * Removes the element with the specified key from the HashTable.
59 | */
60 | bool MsRdpEx_HashTable_Remove(MsRdpEx_HashTable* table, void* key);
61 |
62 | bool MsRdpEx_HashTable_RemoveValue(MsRdpEx_HashTable* table, void* value);
63 |
64 | /**
65 | * Removes all elements from the HashTable.
66 | */
67 | void MsRdpEx_HashTable_Clear(MsRdpEx_HashTable* table);
68 |
69 | /**
70 | * Determines whether the HashTable contains a specific key.
71 | */
72 | bool MsRdpEx_HashTable_ContainsKey(MsRdpEx_HashTable* table, void* key);
73 |
74 | /**
75 | * Determines whether the HashTable contains a specific value.
76 | */
77 | bool MsRdpEx_HashTable_ContainsValue(MsRdpEx_HashTable* table, void* value);
78 |
79 | /**
80 | * Get an item value using key.
81 | */
82 | void* MsRdpEx_HashTable_GetItemValue(MsRdpEx_HashTable* table, void* key);
83 |
84 | /**
85 | * Set an item value using key.
86 | */
87 | bool MsRdpEx_HashTable_SetItemValue(MsRdpEx_HashTable* table, void* key, void* value);
88 |
89 | /**
90 | * Gets the list of keys as an array.
91 | */
92 | int MsRdpEx_HashTable_GetKeys(MsRdpEx_HashTable* table, ULONG_PTR** ppKeys);
93 |
94 | uint32_t MsRdpEx_HashTable_PointerHash(void* pointer);
95 | bool MsRdpEx_HashTable_PointerCompare(void* pointer1, void* pointer2);
96 | uint32_t MsRdpEx_HashTable_StringHash(void* key);
97 | bool MsRdpEx_HashTable_StringCompare(void* string1, void* string2);
98 | void* MsRdpEx_HashTable_StringClone(void* str);
99 | void MsRdpEx_HashTable_StringFree(void* str);
100 | bool MsRdpEx_HashTable_UInt32Compare(void* v1, void* v2);
101 | uint32_t MsRdpEx_HashTable_UInt32Hash(void* v);
102 |
103 | MsRdpEx_HashTable* MsRdpEx_HashTable_New(bool synchronized, uint8_t flags);
104 | void MsRdpEx_HashTable_Free(MsRdpEx_HashTable* table);
105 |
106 | #ifdef __cplusplus
107 | }
108 | #endif
109 |
110 | #endif /* MSRDPEX_HASHTABLE_H */
--------------------------------------------------------------------------------
/include/MsRdpEx/KeyMaps.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_KEYMAPS_H
2 | #define MSRDPEX_KEYMAPS_H
3 |
4 | #include
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | uint32_t MsRdpEx_KeyNameToVKCode(const char* keyName);
11 |
12 | #ifdef __cplusplus
13 | }
14 | #endif
15 |
16 | #endif // MSRDPEX_KEYMAPS_H
17 |
--------------------------------------------------------------------------------
/include/MsRdpEx/Memory.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_MEMORY_H
2 | #define MSRDPEX_MEMORY_H
3 |
4 | #include
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | bool MsRdpEx_CanReadUnsafePtr(void* ptr, size_t size);
11 | bool MsRdpEx_StringEqualsUnsafePtr(const char* ptr, const char* str);
12 | bool MsRdpEx_StringIEqualsUnsafePtr(const char* ptr, const char* str);
13 |
14 | #ifdef __cplusplus
15 | }
16 | #endif
17 |
18 | #endif // MSRDPEX_MEMORY_H
19 |
--------------------------------------------------------------------------------
/include/MsRdpEx/NameResolver.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_NAME_RESOLVER_H
2 | #define MSRDPEX_NAME_RESOLVER_H
3 |
4 | #include
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | typedef struct _MsRdpEx_NameResolver MsRdpEx_NameResolver;
11 |
12 | bool MsRdpEx_NameResolver_GetMapping(const char* oldName, char** pNewName);
13 |
14 | bool MsRdpEx_NameResolver_RemapName(const char* oldName, const char* newName);
15 | bool MsRdpEx_NameResolver_UnmapName(const char* name);
16 |
17 | MsRdpEx_NameResolver* MsRdpEx_NameResolver_Get();
18 | void MsRdpEx_NameResolver_Release();
19 |
20 | #ifdef __cplusplus
21 | }
22 | #endif
23 |
24 | #endif // MSRDPEX_NAME_RESOLVER_H
25 |
--------------------------------------------------------------------------------
/include/MsRdpEx/NamedPipe.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_NAMED_PIPE_H
2 | #define MSRDPEX_NAMED_PIPE_H
3 |
4 | #include "MsRdpEx.h"
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | int MsRdpEx_NamedPipe_Read(HANDLE np_handle, uint8_t* data, size_t size);
11 | int MsRdpEx_NamedPipe_Write(HANDLE np_handle, const uint8_t* data, size_t size);
12 |
13 | HANDLE MsRdpEx_NamedPipe_Open(const char* np_name);
14 | HANDLE MsRdpEx_NamedPipe_Create(const char* np_name, int max_clients);
15 | HANDLE MsRdpEx_NamedPipe_Accept(HANDLE np_handle);
16 |
17 | void MsRdpEx_NamedPipe_Close(HANDLE np_handle);
18 |
19 | #ifdef __cplusplus
20 | }
21 | #endif
22 |
23 | #endif /* MSRDPEX_NAMED_PIPE_H */
--------------------------------------------------------------------------------
/include/MsRdpEx/OutputMirror.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_OUTPUT_MIRROR_H
2 | #define MSRDPEX_OUTPUT_MIRROR_H
3 |
4 | #include
5 |
6 | #include
7 |
8 | #ifdef __cplusplus
9 | extern "C" {
10 | #endif
11 |
12 | typedef struct _MsRdpEx_OutputMirror MsRdpEx_OutputMirror;
13 |
14 | void MsRdpEx_OutputMirror_SetSourceDC(MsRdpEx_OutputMirror* ctx, HDC hSourceDC);
15 | HDC MsRdpEx_OutputMirror_GetShadowDC(MsRdpEx_OutputMirror* ctx);
16 |
17 | void MsRdpEx_OutputMirror_SetFrameSize(MsRdpEx_OutputMirror* ctx, uint32_t frameWidth, uint32_t frameHeight);
18 | void MsRdpEx_OutputMirror_GetFrameSize(MsRdpEx_OutputMirror* ctx, uint32_t* frameWidth, uint32_t* frameHeight);
19 |
20 | bool MsRdpEx_OutputMirror_DumpFrame(MsRdpEx_OutputMirror* ctx);
21 |
22 | void MsRdpEx_OutputMirror_SetDumpBitmapUpdates(MsRdpEx_OutputMirror* ctx, bool dumpBitmapUpdates);
23 | void MsRdpEx_OutputMirror_SetVideoRecordingEnabled(MsRdpEx_OutputMirror* ctx, bool videoRecordingEnabled);
24 | void MsRdpEx_OutputMirror_SetVideoQualityLevel(MsRdpEx_OutputMirror* ctx, uint32_t videoQualityLevel);
25 | void MsRdpEx_OutputMirror_SetRecordingPath(MsRdpEx_OutputMirror* ctx, const char* recordingPath);
26 | void MsRdpEx_OutputMirror_SetRecordingPipeName(MsRdpEx_OutputMirror* ctx, const char* recordingPipeName);
27 | void MsRdpEx_OutputMirror_SetSessionId(MsRdpEx_OutputMirror* ctx, const char* sessionId);
28 |
29 | bool MsRdpEx_OutputMirror_GetShadowBitmap(MsRdpEx_OutputMirror* ctx,
30 | HDC* phDC, HBITMAP* phBitmap, uint8_t** pBitmapData,
31 | uint32_t* pBitmapWidth, uint32_t* pBitmapHeight, uint32_t* pBitmapStep);
32 |
33 | void MsRdpEx_OutputMirror_Lock(MsRdpEx_OutputMirror* ctx);
34 | void MsRdpEx_OutputMirror_Unlock(MsRdpEx_OutputMirror* ctx);
35 |
36 | bool MsRdpEx_OutputMirror_Init(MsRdpEx_OutputMirror* ctx);
37 | bool MsRdpEx_OutputMirror_Uninit(MsRdpEx_OutputMirror* ctx);
38 |
39 | MsRdpEx_OutputMirror* MsRdpEx_OutputMirror_New();
40 | void MsRdpEx_OutputMirror_Free(MsRdpEx_OutputMirror* ctx);
41 |
42 | #ifdef __cplusplus
43 | }
44 | #endif
45 |
46 | #endif // MSRDPEX_OUTPUT_MIRROR_H
47 |
--------------------------------------------------------------------------------
/include/MsRdpEx/Pcap.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_PCAP_H
2 | #define MSRDPEX_PCAP_H
3 |
4 | #include
5 |
6 | struct _pcap_file_header
7 | {
8 | uint32_t magic_number; /* magic number */
9 | uint16_t version_major; /* major version number */
10 | uint16_t version_minor; /* minor version number */
11 | int32_t thiszone; /* GMT to local correction */
12 | uint32_t sigfigs; /* accuracy of timestamps */
13 | uint32_t snaplen; /* max length of captured packets, in octets */
14 | uint32_t network; /* data link type */
15 | };
16 | typedef struct _pcap_file_header PCAP_FILE_HEADER;
17 |
18 | struct _pcap_record_header
19 | {
20 | uint32_t ts_sec; /* timestamp seconds */
21 | uint32_t ts_usec; /* timestamp microseconds */
22 | uint32_t incl_len; /* number of octets of packet saved in file */
23 | uint32_t orig_len; /* actual length of packet */
24 | };
25 | typedef struct _pcap_record_header PCAP_RECORD_HEADER;
26 |
27 | typedef struct _pcap_record PCAP_RECORD;
28 |
29 | struct _pcap_record
30 | {
31 | PCAP_RECORD_HEADER header;
32 | union
33 | {
34 | void* data;
35 | const void* cdata;
36 | };
37 | uint32_t length;
38 | PCAP_RECORD* next;
39 | };
40 |
41 | typedef struct _MsRdpEx_Pcap MsRdpEx_PcapFile;
42 |
43 | #pragma pack(push, 1)
44 |
45 | struct _PCAP_ETHERNET_HEADER
46 | {
47 | uint8_t Destination[6];
48 | uint8_t Source[6];
49 | uint16_t Type;
50 | };
51 | typedef struct _PCAP_ETHERNET_HEADER PCAP_ETHERNET_HEADER;
52 |
53 | struct _PCAP_IPV4_HEADER
54 | {
55 | uint8_t Version;
56 | uint8_t InternetHeaderLength;
57 | uint8_t TypeOfService;
58 | uint16_t TotalLength;
59 | uint16_t Identification;
60 | uint8_t InternetProtocolFlags;
61 | uint16_t FragmentOffset;
62 | uint8_t TimeToLive;
63 | uint8_t Protocol;
64 | uint16_t HeaderChecksum;
65 | uint32_t SourceAddress;
66 | uint32_t DestinationAddress;
67 | };
68 | typedef struct _PCAP_IPV4_HEADER PCAP_IPV4_HEADER;
69 |
70 | struct _PCAP_TCP_HEADER
71 | {
72 | uint16_t SourcePort;
73 | uint16_t DestinationPort;
74 | uint32_t SequenceNumber;
75 | uint32_t AcknowledgementNumber;
76 | uint8_t Offset;
77 | uint8_t Reserved;
78 | uint8_t TcpFlags;
79 | uint16_t Window;
80 | uint16_t Checksum;
81 | uint16_t UrgentPointer;
82 | };
83 | typedef struct _PCAP_TCP_HEADER PCAP_TCP_HEADER;
84 |
85 | #pragma pack(pop)
86 |
87 | #define PCAP_PACKET_FLAG_INBOUND 0x00000001
88 | #define PCAP_PACKET_FLAG_OUTBOUND 0x00000002
89 |
90 | #ifdef __cplusplus
91 | extern "C" {
92 | #endif
93 |
94 | MsRdpEx_PcapFile* MsRdpEx_PcapFile_Open(const char* name, bool write);
95 | void MsRdpEx_PcapFile_Close(MsRdpEx_PcapFile* pcap);
96 |
97 | bool MsRdpEx_PcapFile_AddRecord(MsRdpEx_PcapFile* pcap, const uint8_t* data, uint32_t length);
98 | bool MsRdpEx_PcapFile_HasNextRecord(MsRdpEx_PcapFile* pcap);
99 | bool MsRdpEx_PcapFile_GetNextRecord(MsRdpEx_PcapFile* pcap, PCAP_RECORD* record);
100 | bool MsRdpEx_PcapFile_GetNextRecordHeader(MsRdpEx_PcapFile* pcap, PCAP_RECORD* record);
101 | bool MsRdpEx_PcapFile_GetNextRecordContent(MsRdpEx_PcapFile* pcap, PCAP_RECORD* record);
102 | void MsRdpEx_PcapFile_Flush(MsRdpEx_PcapFile* pcap);
103 |
104 | void MsRdpEx_PcapFile_Lock(MsRdpEx_PcapFile* pcap);
105 | void MsRdpEx_PcapFile_Unlock(MsRdpEx_PcapFile* pcap);
106 |
107 | bool MsRdpEx_PcapFile_WritePacket(MsRdpEx_PcapFile* pcap, const uint8_t* data, size_t length, uint32_t flags);
108 |
109 | #ifdef __cplusplus
110 | }
111 | #endif
112 |
113 | #endif // MSRDPEX_PCAP_H
114 |
--------------------------------------------------------------------------------
/include/MsRdpEx/RdpCoreApi.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_CORE_API_H
2 | #define MSRDPEX_CORE_API_H
3 |
4 | #include
5 |
6 | #include
7 |
8 | struct __declspec(uuid("13F6E86F-EE7D-44D1-AA94-1136B784441D")) __declspec(novtable)
9 | IMsRdpExCoreApi : public IUnknown
10 | {
11 | public:
12 | virtual HRESULT __stdcall Load(void) = 0;
13 | virtual HRESULT __stdcall Unload(void) = 0;
14 | virtual const char* __stdcall GetMsRdpExDllPath() = 0;
15 | virtual void __stdcall SetLogEnabled(bool enabled) = 0;
16 | virtual void __stdcall SetLogLevel(uint32_t logLevel) = 0;
17 | virtual void __stdcall SetLogFilePath(const char* logFilePath) = 0;
18 | virtual void __stdcall SetPcapEnabled(bool enabled) = 0;
19 | virtual void __stdcall SetPcapFilePath(const char* pcapFilePath) = 0;
20 | virtual void __stdcall SetAxHookEnabled(bool axHookEnabled) = 0;
21 | virtual bool __stdcall QueryInstanceByWindowHandle(HWND hWnd, LPVOID* ppvObject) = 0;
22 | virtual bool __stdcall OpenInstanceForWindowHandle(HWND hWnd, LPVOID* ppvObject) = 0;
23 | };
24 |
25 | #ifdef __cplusplus
26 | extern "C" {
27 | #endif
28 |
29 |
30 |
31 | #ifdef __cplusplus
32 | }
33 | #endif
34 |
35 | #endif // MSRDPEX_CORE_API_H
36 |
--------------------------------------------------------------------------------
/include/MsRdpEx/RdpFile.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_RDP_FILE_H
2 | #define MSRDPEX_RDP_FILE_H
3 |
4 | #include
5 |
6 | #include
7 |
8 | #include
9 |
10 | #ifdef __cplusplus
11 | extern "C" {
12 | #endif
13 |
14 | struct _MsRdpEx_RdpFileEntry
15 | {
16 | char type;
17 | char* name;
18 | char* value;
19 | };
20 | typedef struct _MsRdpEx_RdpFileEntry MsRdpEx_RdpFileEntry;
21 |
22 | bool MsRdpEx_RdpFileEntry_IsMatch(MsRdpEx_RdpFileEntry* entry, char type, const char* name);
23 |
24 | bool MsRdpEx_RdpFileEntry_GetBoolValue(MsRdpEx_RdpFileEntry* entry, bool* pValue);
25 | bool MsRdpEx_RdpFileEntry_GetVBoolValue(MsRdpEx_RdpFileEntry* entry, _Out_ VARIANT* pVariant);
26 | bool MsRdpEx_RdpFileEntry_GetIntValue(MsRdpEx_RdpFileEntry* entry, _Out_ VARIANT* pVariant);
27 |
28 | MsRdpEx_RdpFileEntry* MsRdpEx_RdpFileEntry_New(char type, const char* name, const char* value);
29 | void MsRdpEx_RdpFileEntry_Free(MsRdpEx_RdpFileEntry* entry);
30 |
31 | struct _MsRdpEx_RdpFile
32 | {
33 | MsRdpEx_ArrayList* entries;
34 | };
35 | typedef struct _MsRdpEx_RdpFile MsRdpEx_RdpFile;
36 |
37 | char* MsRdpEx_GetRdpFilenameFromCommandLine();
38 |
39 | bool MsRdpEx_RdpFile_Load(MsRdpEx_RdpFile* ctx, const char* filename);
40 | bool MsRdpEx_RdpFile_LoadText(MsRdpEx_RdpFile* ctx, const char* text);
41 |
42 | MsRdpEx_RdpFile* MsRdpEx_RdpFile_New();
43 | void MsRdpEx_RdpFile_Free(MsRdpEx_RdpFile* ctx);
44 |
45 | #ifdef __cplusplus
46 | }
47 | #endif
48 |
49 | #endif // MSRDPEX_RDP_FILE_H
50 |
--------------------------------------------------------------------------------
/include/MsRdpEx/RdpInstance.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_INSTANCE_H
2 | #define MSRDPEX_INSTANCE_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 |
10 | struct __declspec(novtable)
11 | IMsRdpExInstance : public IUnknown
12 | {
13 | public:
14 | virtual HRESULT __stdcall GetSessionId(GUID* pSessionId) = 0;
15 | virtual HRESULT __stdcall GetRdpClient(LPVOID* ppvObject) = 0;
16 | virtual HRESULT __stdcall GetOutputMirrorObject(LPVOID* ppvObject) = 0;
17 | virtual HRESULT __stdcall SetOutputMirrorObject(LPVOID pvObject) = 0;
18 | virtual HRESULT __stdcall GetOutputMirrorEnabled(bool* outputMirrorEnabled) = 0;
19 | virtual HRESULT __stdcall SetOutputMirrorEnabled(bool outputMirrorEnabled) = 0;
20 | virtual HRESULT __stdcall GetVideoRecordingEnabled(bool* videoRecordingEnabled) = 0;
21 | virtual HRESULT __stdcall SetVideoRecordingEnabled(bool videoRecordingEnabled) = 0;
22 | virtual HRESULT __stdcall GetDumpBitmapUpdates(bool* dumpBitmapUpdates) = 0;
23 | virtual HRESULT __stdcall SetDumpBitmapUpdates(bool dumpBitmapUpdates) = 0;
24 | virtual HRESULT __stdcall GetCorePropsRawPtr(LPVOID* ppCorePropsRaw) = 0;
25 | virtual HRESULT __stdcall SetCorePropsRawPtr(LPVOID pCorePropsRaw) = 0;
26 | virtual HRESULT __stdcall AttachInputWindow(HWND hOutputWnd, void* pUserData) = 0;
27 | virtual HRESULT __stdcall GetInputWindow(HWND* phInputCaptureWnd) = 0;
28 | virtual HRESULT __stdcall AttachOutputWindow(HWND hOutputWnd, void* pUserData) = 0;
29 | virtual HRESULT __stdcall GetOutputWindow(HWND* phOutputPresenterWnd) = 0;
30 | virtual HRESULT __stdcall AttachTscShellContainerWindow(HWND hTscShellContainerWnd) = 0;
31 | virtual HRESULT __stdcall GetTscShellContainerWindow(HWND* phTscShellContainerWnd) = 0;
32 | virtual HRESULT __stdcall AttachExtendedSettings(CMsRdpExtendedSettings* pExtendedSettings) = 0;
33 | virtual bool __stdcall GetExtendedSettings(CMsRdpExtendedSettings** ppExtendedSettings) = 0;
34 | virtual bool __stdcall GetShadowBitmap(HDC* phDC, HBITMAP* phBitmap, uint8_t** pBitmapData,
35 | uint32_t* pBitmapWidth, uint32_t* pBitmapHeight, uint32_t* pBitmapStep) = 0;
36 | virtual void __stdcall LockShadowBitmap() = 0;
37 | virtual void __stdcall UnlockShadowBitmap() = 0;
38 | virtual void __stdcall GetLastMousePosition(int32_t* posX, int32_t* posY) = 0;
39 | virtual void __stdcall SetLastMousePosition(int32_t posX, int32_t posY) = 0;
40 | virtual HRESULT __stdcall GetWTSPluginObject(LPVOID* ppvObject) = 0;
41 | virtual HRESULT __stdcall SetWTSPluginObject(LPVOID pvObject) = 0;
42 | };
43 |
44 | class CMsRdpExInstance;
45 | class CMsRdpClient;
46 |
47 | #ifdef __cplusplus
48 | extern "C" {
49 | #endif
50 |
51 | typedef struct _MsRdpEx_InstanceManager MsRdpEx_InstanceManager;
52 |
53 | bool MsRdpEx_InstanceManager_Add(CMsRdpExInstance* instance);
54 | bool MsRdpEx_InstanceManager_Remove(CMsRdpExInstance* instance);
55 |
56 | CMsRdpExInstance* MsRdpEx_InstanceManager_FindByOutputPresenterHwnd(HWND hWnd);
57 |
58 | CMsRdpExInstance* MsRdpEx_InstanceManager_AttachOutputWindow(HWND hOutputWnd, void* pUserData);
59 |
60 | CMsRdpExInstance* MsRdpEx_InstanceManager_FindByInputCaptureHwnd(HWND hWnd);
61 |
62 | CMsRdpExInstance* MsRdpEx_InstanceManager_AttachInputWindow(HWND hInputWnd, void* pUserData);
63 |
64 | CMsRdpExInstance* MsRdpEx_InstanceManager_FindByTscShellContainerWnd(HWND hWnd);
65 |
66 | CMsRdpExInstance* MsRdpEx_InstanceManager_FindBySessionId(GUID* sessionId);
67 |
68 | CMsRdpExtendedSettings* MsRdpEx_FindExtendedSettingsBySessionId(GUID* sessionId);
69 |
70 | MsRdpEx_InstanceManager* MsRdpEx_InstanceManager_Get();
71 | void MsRdpEx_InstanceManager_Release();
72 |
73 | CMsRdpExInstance* CMsRdpExInstance_New(CMsRdpClient* pMsRdpClient);
74 |
75 | #ifdef __cplusplus
76 | }
77 | #endif
78 |
79 | #endif // MSRDPEX_INSTANCE_H
80 |
--------------------------------------------------------------------------------
/include/MsRdpEx/RdpProcess.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_PROCESS_LAUNCHER_H
2 | #define MSRDPEX_PROCESS_LAUNCHER_H
3 |
4 | #include
5 |
6 | #include
7 |
8 | struct __declspec(novtable)
9 | IMsRdpExProcess : public IUnknown
10 | {
11 | public:
12 | virtual void __stdcall SetFileName(const char* filename) = 0;
13 | virtual void __stdcall SetArguments(const char* arguments) = 0;
14 | virtual void __stdcall SetArgumentBlock(const char* argumentBlock) = 0;
15 | virtual void __stdcall SetEnvironmentBlock(const char* environmentBlock) = 0;
16 | virtual void __stdcall SetWorkingDirectory(const char* workingDirectory) = 0;
17 | virtual HRESULT __stdcall StartWithInfo() = 0;
18 | virtual HRESULT __stdcall Start(int argc, char** argv, const char* appName, const char* axName) = 0;
19 | virtual HRESULT __stdcall Stop(uint32_t exitCode) = 0;
20 | virtual HRESULT __stdcall Wait(uint32_t milliseconds) = 0;
21 | virtual uint32_t __stdcall GetProcessId() = 0;
22 | virtual uint32_t __stdcall GetExitCode() = 0;
23 | };
24 |
25 | #ifdef __cplusplus
26 | extern "C" {
27 | #endif
28 |
29 | HRESULT MsRdpEx_LaunchProcess(int argc, char** argv, const char* appName, const char* axName);
30 |
31 | char** MsRdpEx_GetArgumentVector(int* argc);
32 | void MsRdpEx_FreeArgumentVector(int argc, char** argv);
33 |
34 | HRESULT MsRdpExProcess_CreateInstance(LPVOID* ppvObject);
35 |
36 | #ifdef __cplusplus
37 | }
38 | #endif
39 |
40 | #endif // MSRDPEX_PROCESS_LAUNCHER_H
41 |
--------------------------------------------------------------------------------
/include/MsRdpEx/RdpSettings.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_SETTINGS_H
2 | #define MSRDPEX_SETTINGS_H
3 |
4 | #include
5 |
6 | #include "TSObjects.h"
7 |
8 | #include
9 |
10 | #define MOUSE_JIGGLER_METHOD_MOUSE_MOVE 0
11 | #define MOUSE_JIGGLER_METHOD_SPECIAL_KEY 1
12 |
13 | class CMsRdpExtendedSettings;
14 | class CMsRdpPropertySet;
15 |
16 | class CMsRdpExtendedSettings : public IMsRdpExtendedSettings
17 | {
18 | public:
19 | CMsRdpExtendedSettings(IUnknown* pUnknown, GUID* pSessionId);
20 | ~CMsRdpExtendedSettings();
21 |
22 | // IUnknown interface
23 | public:
24 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppvObject);
25 | ULONG STDMETHODCALLTYPE AddRef();
26 | ULONG STDMETHODCALLTYPE Release();
27 |
28 | // IMsRdpExtendedSettings
29 | public:
30 | HRESULT __stdcall put_Property(BSTR bstrPropertyName, VARIANT* pValue);
31 | HRESULT __stdcall get_Property(BSTR bstrPropertyName, VARIANT* pValue);
32 |
33 | // additional stuff
34 | public:
35 | HRESULT __stdcall put_CoreProperty(BSTR bstrPropertyName, VARIANT* pValue);
36 | HRESULT __stdcall get_CoreProperty(BSTR bstrPropertyName, VARIANT* pValue);
37 | HRESULT __stdcall put_BaseProperty(BSTR bstrPropertyName, VARIANT* pValue);
38 | HRESULT __stdcall get_BaseProperty(BSTR bstrPropertyName, VARIANT* pValue);
39 | HRESULT __stdcall SetTargetPassword(const char* password);
40 | HRESULT __stdcall SetGatewayPassword(const char* password);
41 | HRESULT __stdcall SetKdcProxyUrl(const char* kdcProxyUrl);
42 | HRESULT __stdcall SetRecordingPath(const char* recordingPath);
43 | HRESULT __stdcall SetRecordingPipeName(const char* recordingPipeName);
44 | HRESULT __stdcall AttachRdpClient(IMsTscAx* pMsTscAx);
45 | HRESULT __stdcall ApplyRdpFile(void* rdpFilePtr);
46 | HRESULT __stdcall LoadRdpFile(const char* rdpFileName);
47 | HRESULT __stdcall LoadRdpFileFromNamedPipe(const char* pipeName);
48 | HRESULT __stdcall GetCorePropsRawPtr(LPVOID* ppCorePropsRaw);
49 | HRESULT __stdcall PrepareSspiSessionIdHack();
50 | HRESULT __stdcall PrepareMouseJiggler();
51 | HRESULT __stdcall PrepareVideoRecorder();
52 | HRESULT __stdcall PrepareExtraSystemMenu();
53 | char* __stdcall GetKdcProxyUrl();
54 | char* __stdcall GetKdcProxyName();
55 | bool GetMouseJigglerEnabled();
56 | uint32_t GetMouseJigglerInterval();
57 | uint32_t GetMouseJigglerMethod();
58 | bool GetKeyboardHookToggleShortcutEnabled();
59 | const char* GetKeyboardHookToggleShortcutKey();
60 | const char* GetSessionId();
61 | bool GetOutputMirrorEnabled();
62 | bool GetVideoRecordingEnabled();
63 | uint32_t GetVideoRecordingQuality();
64 | char* GetRecordingPath();
65 | char* GetRecordingPipeName();
66 | bool GetDumpBitmapUpdates();
67 | bool GetExtraSystemMenuEnabled();
68 |
69 | private:
70 | GUID m_sessionId;
71 | char m_sessionIdStr[MSRDPEX_GUID_STRING_SIZE];
72 | ULONG m_refCount = 0;
73 | IUnknown* m_pUnknown = NULL;
74 | IMsTscAx* m_pMsTscAx = NULL;
75 | IMsRdpClient7* m_pMsRdpClient7 = NULL;
76 | IMsRdpExtendedSettings* m_pMsRdpExtendedSettings = NULL;
77 | IMsRdpClientTransportSettings2* m_pMsRdpClientTransportSettings2 = NULL;
78 | ITSPropertySet* m_pCorePropsRaw = NULL;
79 | CMsRdpPropertySet* m_CoreProps = NULL;
80 | CMsRdpPropertySet* m_BaseProps = NULL;
81 | CMsRdpPropertySet* m_TransportProps = NULL;
82 | char* m_KdcProxyUrl = NULL;
83 | bool m_MouseJigglerEnabled = false;
84 | uint32_t m_MouseJigglerInterval = 60;
85 | uint32_t m_MouseJigglerMethod = 0;
86 | bool m_OutputMirrorEnabled = false;
87 | bool m_VideoRecordingEnabled = false;
88 | uint32_t m_VideoRecordingQuality = 5;
89 | char* m_RecordingPath = NULL;
90 | char* m_RecordingPipeName = NULL;
91 | bool m_DumpBitmapUpdates = false;
92 | bool m_ExtraSystemMenuEnabled = true;
93 | bool m_KeyboardHookToggleShortcutEnabled = false;
94 | char m_KeyboardHookToggleShortcutKey[32];
95 | IUnknown* m_pWTSPlugin = NULL;
96 | };
97 |
98 | #ifdef __cplusplus
99 | extern "C" {
100 | #endif
101 |
102 | CMsRdpExtendedSettings* CMsRdpExtendedSettings_New(IUnknown* pUnknown, IUnknown* pMsTscAx, GUID* pSessionId);
103 |
104 | char* MsRdpEx_KdcProxyUrlToName(const char* kdcProxyUrl);
105 |
106 | #ifdef __cplusplus
107 | }
108 | #endif
109 |
110 | #endif // MSRDPEX_SETTINGS_H
111 |
--------------------------------------------------------------------------------
/include/MsRdpEx/RecordingManifest.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_RECORDING_MANIFEST_H
2 | #define MSRDPEX_RECORDING_MANIFEST_H
3 |
4 | #include "MsRdpEx.h"
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | typedef struct _MsRdpEx_JrecFile MsRdpEx_JrecFile;
11 |
12 | typedef struct _MsRdpEx_RecordingManifest MsRdpEx_RecordingManifest;
13 |
14 | void MsRdpEx_RecordingManifest_SetSessionId(MsRdpEx_RecordingManifest* ctx, GUID* sessionId);
15 | void MsRdpEx_RecordingManifest_SetStartTime(MsRdpEx_RecordingManifest* ctx, int64_t startTime);
16 | void MsRdpEx_RecordingManifest_SetDuration(MsRdpEx_RecordingManifest* ctx, int64_t duration);
17 |
18 | bool MsRdpEx_RecordingManifest_AddFile(MsRdpEx_RecordingManifest* ctx, const char* fileName, int64_t startTime, int64_t duration);
19 | void MsRdpEx_RecordingManifest_FinalizeFile(MsRdpEx_RecordingManifest* ctx, int64_t endTime);
20 |
21 | char* MsRdpEx_RecordingManifest_WriteJsonData(MsRdpEx_RecordingManifest* ctx);
22 | bool MsRdpEx_RecordingManifest_WriteJsonFile(MsRdpEx_RecordingManifest* ctx, const char* filename);
23 |
24 | MsRdpEx_RecordingManifest* MsRdpEx_RecordingManifest_New();
25 | void MsRdpEx_RecordingManifest_Free(MsRdpEx_RecordingManifest* ctx);
26 |
27 | #ifdef __cplusplus
28 | }
29 | #endif
30 |
31 | #endif /* MSRDPEX_RECORDING_MANIFEST_H */
32 |
--------------------------------------------------------------------------------
/include/MsRdpEx/Sspi.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_SSPI_H
2 | #define MSRDPEX_SSPI_H
3 |
4 | #include
5 |
6 | #define SECURITY_WIN32
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #ifdef __cplusplus
13 | extern "C" {
14 | #endif
15 |
16 | LONG MsRdpEx_AttachSspiHooks();
17 | LONG MsRdpEx_DetachSspiHooks();
18 |
19 | #ifdef __cplusplus
20 | }
21 | #endif
22 |
23 | #endif // MSRDPEX_SSPI_H
24 |
--------------------------------------------------------------------------------
/include/MsRdpEx/Stopwatch.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_STOPWATCH_H
2 | #define MSRDPEX_STOPWATCH_H
3 |
4 | #include
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | struct msrdpex_stopwatch
11 | {
12 | LARGE_INTEGER time;
13 | ULONGLONG tickCount;
14 | bool enabled;
15 | bool highPrecision;
16 | };
17 |
18 | typedef struct msrdpex_stopwatch MsRdpEx_Stopwatch;
19 |
20 | void MsRdpEx_ProfilingInit();
21 |
22 | void MsRdpEx_Stopwatch_Init(MsRdpEx_Stopwatch* stopwatch, uint32_t profilingLevel, bool start);
23 | void MsRdpEx_Stopwatch_InitEx(MsRdpEx_Stopwatch* stopwatch, uint32_t profilingLevel, bool start, bool highPrecision);
24 | void MsRdpEx_Stopwatch_Start(MsRdpEx_Stopwatch* stopwatch);
25 | double MsRdpEx_Stopwatch_GetTime(MsRdpEx_Stopwatch* stopwatch);
26 | void MsRdpEx_Stopwatch_Print(MsRdpEx_Stopwatch* stopwatch, uint32_t logLevel, const char* message);
27 |
28 | #ifdef __cplusplus
29 | }
30 | #endif
31 |
32 | #endif /* MSRDPEX_STOPWATCH_H */
33 |
--------------------------------------------------------------------------------
/include/MsRdpEx/VideoRecorder.h:
--------------------------------------------------------------------------------
1 | #ifndef MSRDPEX_VIDEO_RECORDER_H
2 | #define MSRDPEX_VIDEO_RECORDER_H
3 |
4 | #include
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | typedef void XmfRecorder;
11 | typedef void XmfWebMMuxer;
12 | typedef void XmfBipBlock;
13 | typedef void XmfBipBuffer;
14 |
15 | typedef XmfRecorder* (CDECL * fnXmfRecorder_New)();
16 | typedef bool (CDECL* fnXmfRecorder_Init)(XmfRecorder* ctx);
17 | typedef void (CDECL* fnXmfRecorder_Uninit)(XmfRecorder* ctx);
18 | typedef void (CDECL* fnXmfRecorder_SetFrameSize)(XmfRecorder* ctx, uint32_t width, uint32_t height);
19 | typedef void (CDECL* fnXmfRecorder_SetFrameRate)(XmfRecorder* ctx, uint32_t frameRate);
20 | typedef void (CDECL* fnXmfRecorder_SetVideoQuality)(XmfRecorder* ctx, uint32_t videoQuality);
21 | typedef void (CDECL* fnXmfRecorder_SetFileName)(XmfRecorder* ctx, const char* filename);
22 | typedef void (CDECL* fnXmfRecorder_SetBipBuffer)(XmfRecorder* ctx, XmfBipBuffer* bb);
23 | typedef void (CDECL* fnXmfRecorder_UpdateFrame)(XmfRecorder* ctx, uint8_t* buffer, uint32_t updateX,
24 | uint32_t updateY, uint32_t updateWidth, uint32_t updateHeight, uint32_t surfaceStep);
25 | typedef void (CDECL* fnXmfRecorder_Timeout)(XmfRecorder* ctx);
26 | typedef uint32_t (CDECL* fnXmfRecorder_GetTimeout)(XmfRecorder* ctx);
27 | typedef void (CDECL* fnXmfRecorder_Free)(XmfRecorder* ctx);
28 |
29 | typedef XmfWebMMuxer* (CDECL* fnXmfWebMMuxer_New)();
30 | typedef int (CDECL* fnXmfWebMMuxer_Remux)(XmfWebMMuxer* ctx, const char* inputFile, const char* outputFile);
31 | typedef void (CDECL* fnXmfWebMMuxer_Free)(XmfWebMMuxer* ctx);
32 |
33 | typedef bool (CDECL* fnXmfBipBuffer_Grow)(XmfBipBuffer* ctx, size_t size);
34 | typedef void (CDECL* fnXmfBipBuffer_Clear)(XmfBipBuffer* ctx);
35 | typedef size_t(CDECL* fnXmfBipBuffer_UsedSize)(XmfBipBuffer* ctx);
36 | typedef size_t(CDECL* fnXmfBipBuffer_BufferSize)(XmfBipBuffer* ctx);
37 | typedef uint8_t* (CDECL* fnXmfBipBuffer_WriteReserve)(XmfBipBuffer* ctx, size_t size);
38 | typedef uint8_t* (CDECL* fnXmfBipBuffer_WriteTryReserve)(XmfBipBuffer* ctx, size_t size, size_t* reserved);
39 | typedef void (CDECL* fnXmfBipBuffer_WriteCommit)(XmfBipBuffer* ctx, size_t size);
40 | typedef uint8_t* (CDECL* fnXmfBipBuffer_ReadReserve)(XmfBipBuffer* ctx, size_t size);
41 | typedef uint8_t* (CDECL* fnXmfBipBuffer_ReadTryReserve)(XmfBipBuffer* ctx, size_t size, size_t* reserved);
42 | typedef void (CDECL* fnXmfBipBuffer_ReadCommit)(XmfBipBuffer* ctx, size_t size);
43 | typedef int (CDECL* fnXmfBipBuffer_Read)(XmfBipBuffer* ctx, uint8_t* data, size_t size);
44 | typedef int (CDECL* fnXmfBipBuffer_Write)(XmfBipBuffer* ctx, const uint8_t* data, size_t size);
45 | typedef void (CDECL* fnXmfBipBuffer_SetSignaledState)(XmfBipBuffer* ctx, bool signaled);
46 | typedef bool (CDECL* fnXmfBipBuffer_GetSignaledState)(XmfBipBuffer* ctx);
47 | typedef XmfBipBuffer* (CDECL* fnXmfBipBuffer_New)(size_t size);
48 | typedef void (CDECL* fnXmfBipBuffer_Free)(XmfBipBuffer* ctx);
49 |
50 | struct _MsRdpEx_VideoRecorder
51 | {
52 | HMODULE hModule;
53 | XmfRecorder* recorder;
54 | fnXmfRecorder_New XmfRecorder_New;
55 | fnXmfRecorder_Init XmfRecorder_Init;
56 | fnXmfRecorder_Uninit XmfRecorder_Uninit;
57 | fnXmfRecorder_SetFrameSize XmfRecorder_SetFrameSize;
58 | fnXmfRecorder_SetFrameRate XmfRecorder_SetFrameRate;
59 | fnXmfRecorder_SetVideoQuality XmfRecorder_SetVideoQuality;
60 | fnXmfRecorder_SetFileName XmfRecorder_SetFileName;
61 | fnXmfRecorder_SetBipBuffer XmfRecorder_SetBipBuffer;
62 | fnXmfRecorder_UpdateFrame XmfRecorder_UpdateFrame;
63 | fnXmfRecorder_Timeout XmfRecorder_Timeout;
64 | fnXmfRecorder_GetTimeout XmfRecorder_GetTimeout;
65 | fnXmfRecorder_Free XmfRecorder_Free;
66 |
67 | fnXmfWebMMuxer_New XmfWebMMuxer_New;
68 | fnXmfWebMMuxer_Remux XmfWebMMuxer_Remux;
69 | fnXmfWebMMuxer_Free XmfWebMMuxer_Free;
70 |
71 | fnXmfBipBuffer_Grow XmfBipBuffer_Grow;
72 | fnXmfBipBuffer_Clear XmfBipBuffer_Clear;
73 | fnXmfBipBuffer_UsedSize XmfBipBuffer_UsedSize;
74 | fnXmfBipBuffer_BufferSize XmfBipBuffer_BufferSize;
75 | fnXmfBipBuffer_WriteReserve XmfBipBuffer_WriteReserve;
76 | fnXmfBipBuffer_WriteTryReserve XmfBipBuffer_WriteTryReserve;
77 | fnXmfBipBuffer_WriteCommit XmfBipBuffer_WriteCommit;
78 | fnXmfBipBuffer_ReadReserve XmfBipBuffer_ReadReserve;
79 | fnXmfBipBuffer_ReadTryReserve XmfBipBuffer_ReadTryReserve;
80 | fnXmfBipBuffer_ReadCommit XmfBipBuffer_ReadCommit;
81 | fnXmfBipBuffer_Read XmfBipBuffer_Read;
82 | fnXmfBipBuffer_Write XmfBipBuffer_Write;
83 | fnXmfBipBuffer_SetSignaledState XmfBipBuffer_SetSignaledState;
84 | fnXmfBipBuffer_GetSignaledState XmfBipBuffer_GetSignaledState;
85 | fnXmfBipBuffer_New XmfBipBuffer_New;
86 | fnXmfBipBuffer_Free XmfBipBuffer_Free;
87 |
88 | FILE* fp;
89 | HANDLE np_handle;
90 | char* np_name;
91 | bool useBipBuffer;
92 | XmfBipBuffer* bb;
93 | size_t bipBufferSize;
94 | char filename[MSRDPEX_MAX_PATH];
95 | };
96 | typedef struct _MsRdpEx_VideoRecorder MsRdpEx_VideoRecorder;
97 |
98 | bool MsRdpEx_VideoRecorder_Init(MsRdpEx_VideoRecorder* ctx);
99 | void MsRdpEx_VideoRecorder_Uninit(MsRdpEx_VideoRecorder* ctx);
100 |
101 | void MsRdpEx_VideoRecorder_SetFrameSize(MsRdpEx_VideoRecorder* ctx, uint32_t width, uint32_t height);
102 | void MsRdpEx_VideoRecorder_SetFrameRate(MsRdpEx_VideoRecorder* ctx, uint32_t frameRate);
103 | void MsRdpEx_VideoRecorder_SetVideoQuality(MsRdpEx_VideoRecorder* ctx, uint32_t videoQuality);
104 |
105 | void MsRdpEx_VideoRecorder_SetFileName(MsRdpEx_VideoRecorder* ctx, const char* filename);
106 | void MsRdpEx_VideoRecorder_SetPipeName(MsRdpEx_VideoRecorder* ctx, const char* pipeName);
107 |
108 | void MsRdpEx_VideoRecorder_UpdateFrame(MsRdpEx_VideoRecorder* ctx,
109 | uint8_t* buffer, uint32_t updateX, uint32_t updateY,
110 | uint32_t updateWidth, uint32_t updateHeight, uint32_t surfaceStep);
111 |
112 | void MsRdpEx_VideoRecorder_Timeout(MsRdpEx_VideoRecorder* ctx);
113 | uint32_t MsRdpEx_VideoRecorder_GetTimeout(MsRdpEx_VideoRecorder* ctx);
114 |
115 | bool MsRdpEx_VideoRecorder_Remux(MsRdpEx_VideoRecorder* ctx, const char* filename);
116 |
117 | MsRdpEx_VideoRecorder* MsRdpEx_VideoRecorder_New();
118 | void MsRdpEx_VideoRecorder_Free(MsRdpEx_VideoRecorder* ctx);
119 |
120 | #ifdef __cplusplus
121 | }
122 | #endif
123 |
124 | #endif // MSRDPEX_VIDEO_RECORDER_H
125 |
--------------------------------------------------------------------------------
/installer/Folders.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/installer/MsRdpEx.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33122.133
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}") = "MsRdpEx", "MsRdpEx.wixproj", "{A8FFAEDD-525B-46F0-BC38-A984E659DD23}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|ARM64 = Debug|ARM64
11 | Debug|x64 = Debug|x64
12 | Debug|Win32 = Debug|Win32
13 | Release|ARM64 = Release|ARM64
14 | Release|x64 = Release|x64
15 | Release|Win32 = Release|Win32
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {A8FFAEDD-525B-46F0-BC38-A984E659DD23}.Debug|ARM64.ActiveCfg = Debug|ARM64
19 | {A8FFAEDD-525B-46F0-BC38-A984E659DD23}.Debug|ARM64.Build.0 = Debug|ARM64
20 | {A8FFAEDD-525B-46F0-BC38-A984E659DD23}.Debug|x64.ActiveCfg = Debug|x64
21 | {A8FFAEDD-525B-46F0-BC38-A984E659DD23}.Debug|x64.Build.0 = Debug|x64
22 | {A8FFAEDD-525B-46F0-BC38-A984E659DD23}.Debug|Win32.ActiveCfg = Debug|Win32
23 | {A8FFAEDD-525B-46F0-BC38-A984E659DD23}.Debug|Win32.Build.0 = Debug|Win32
24 | {A8FFAEDD-525B-46F0-BC38-A984E659DD23}.Release|ARM64.ActiveCfg = Release|ARM64
25 | {A8FFAEDD-525B-46F0-BC38-A984E659DD23}.Release|ARM64.Build.0 = Release|ARM64
26 | {A8FFAEDD-525B-46F0-BC38-A984E659DD23}.Release|x64.ActiveCfg = Release|x64
27 | {A8FFAEDD-525B-46F0-BC38-A984E659DD23}.Release|x64.Build.0 = Release|x64
28 | {A8FFAEDD-525B-46F0-BC38-A984E659DD23}.Release|Win32.ActiveCfg = Release|Win32
29 | {A8FFAEDD-525B-46F0-BC38-A984E659DD23}.Release|Win32.Build.0 = Release|Win32
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {E5B39C52-A0A1-4A89-9739-4B0326F040A0}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/installer/MsRdpEx.wixproj:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/installer/MsRdpEx.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/installer/Package.en-us.wxl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/installer/Package.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/installer/Variables.wxi:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/scripts/SetAssemblyTargetFramework.ps1:
--------------------------------------------------------------------------------
1 | param(
2 | [string] $AssemblyFilePath,
3 | [string] $FrameworkName = ".NETFramework,Version=v4.8",
4 | [string] $FrameworkDisplayName = ".NET Framework 4.8"
5 | )
6 |
7 | if (-Not (Test-Path $AssemblyFilePath)) {
8 | throw "Assembly file not found at the specified path: $AssemblyFilePath"
9 | }
10 |
11 | $ilasm = "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\ilasm.exe"
12 | $ildasm = "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64\ildasm.exe"
13 |
14 | if (-Not (Test-Path $ilasm)) {
15 | throw "ilasm.exe not found at the path: $ilasm"
16 | }
17 |
18 | if (-Not (Test-Path $ildasm)) {
19 | throw "ildasm.exe not found at the path: $ildasm"
20 | }
21 |
22 | if (-Not (Get-Command -Name 'rg' -ErrorAction SilentlyContinue)) {
23 | throw "rg (RipGrep) not found"
24 | }
25 |
26 | $AssemblyName = [System.IO.Path]::GetFileNameWithoutExtension($AssemblyFilePath)
27 | $AssemblyDllFile = $AssemblyFilePath
28 | $AssemblyIlfile = "${AssemblyName}.il"
29 | $AssemblyResfile = "${AssemblyName}.res"
30 | & $ildasm "/OUT=$AssemblyIlfile" /NOBAR $AssemblyDllFile
31 |
32 | # https://learn.microsoft.com/en-us/dotnet/api/system.runtime.versioning.targetframeworkattribute
33 |
34 | # 01 00
35 | # 1A (26)
36 | # 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B 2C 56 65 72 73 69 6F 6E 3D 76 34 2E 38 ".NETFramework,Version=v4.8"
37 | # 01 00 54 0E
38 | # 14 (20)
39 | # 46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C 61 79 4E 61 6D 65 "FrameworkDisplayName"
40 | # 12 (18)
41 | # 2E 4E 45 54 20 46 72 61 6D 65 77 6F 72 6B 20 34 2E 38 ".NET Framework 4.8"
42 |
43 | function Convert-StringToByteArray {
44 | param(
45 | [Parameter(Mandatory=$true,Position=0)]
46 | [string] $InputString
47 | )
48 |
49 | if ($InputString.Length -gt 255) {
50 | throw "String length exceeds the maximum limit for a single byte."
51 | }
52 |
53 | $lengthByte = [byte]$InputString.Length
54 | $utf8Bytes = [System.Text.Encoding]::UTF8.GetBytes($InputString)
55 | return , $lengthByte + $utf8Bytes
56 | }
57 |
58 | $ctorBytes = @(0x01, 0x00)
59 | $ctorBytes = $ctorBytes + (Convert-StringToByteArray $FrameworkName)
60 | $ctorBytes = $ctorBytes + @(0x01, 0x00, 0x54, 0x0E)
61 | $ctorBytes = $ctorBytes + (Convert-StringToByteArray "FrameworkDisplayName")
62 | $ctorBytes = $ctorBytes + (Convert-StringToByteArray $FrameworkDisplayName)
63 | $ctorHex = ($ctorBytes | ForEach-Object { $_.ToString("X2") }) -join " "
64 |
65 | $lineToAdd = " .custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( $CtorHex )"
66 | $lineAfter = " .hash algorithm"
67 | $NewContent = rg "$lineAfter" $AssemblyIlfile -r "$lineToAdd`r`n$lineAfter" -N --passthru
68 | Set-Content -Path $AssemblyIlfile -Value $NewContent -Force
69 |
70 | & $ilasm $AssemblyIlfile "/OUTPUT=$AssemblyDllfile" /DLL "/RESOURCE=$AssemblyResfile"
71 | @($AssemblyIlfile, $AssemblyResfile) | Remove-Item -Force -ErrorAction SilentlyContinue
72 |
--------------------------------------------------------------------------------