Workflow changes: - Rename publish-ini.yml -> build-and-release.yml; all refs updated - Split build-and-release.yml into 6 parallel jobs: build-dll / build-offsetfinder / download-sergiye (parallel), build-csharp (needs build-dll), build-msi (needs build-csharp), release (needs all) with environment gate - Add build-csharp.yml, build-cpp.yml, build-offsetfinder.yml PR checks - Add build-msi-check.yml: WiX syntax check on msi/** PRs - Add actions/cache@v4 NuGet step to all workflows using dotnet - Pin softprops/action-gh-release to SHA 153bb8e (v2) - Add concurrency guard, msi/** path filter, source path triggers - Extended INI validation: check LocalOnlyPatch+SLInitHook per section - Delete update-finder-tools.yml (superseded) Automation & tooling: - Add .github/CODEOWNERS (maintainer review on all PRs) - Add .github/dependabot.yml (github-actions, nuget, gitsubmodule) - Add tools/build-local.ps1, make-icons.ps1, sergiye-hashes.json, update-sergiye-hashes.ps1 - Add docs/BUILDING.md, CODE-SIGNING.md, SUBMODULE-UPDATE.md - Add msi/ WiX v5 project (RDPWInst.wxs, RDPWInst.wixproj, global.json) C# source: - Migrate all projects from RDPWrap.Common -> RDPWrap library - Add RDPCheck, RDPConf, RDPOffsetFinder C# projects - Dynamic version via Directory.Build.props MSBuild expression - Enable RestorePackagesWithLockFile; generate packages.lock.json for all 4 projects - Dynamic banner version read from assembly at runtime - .gitignore: ignore staged resources and msi build artifacts Legacy cleanup: - Remove Delphi source (src-installer, src-rdpcheck, src-rdpconfig, src-x86-binarymaster), old res/ directory, bin/*.bat, technical.txt, tools/RDPWrapOffsetFinder/ prebuilt binaries (now built from submodule)pull/4062/head
@ -0,0 +1,25 @@
|
|||||||
|
# CODEOWNERS
|
||||||
|
# GitHub automatically requests a review from the listed owners whenever a PR
|
||||||
|
# modifies files matching the pattern. More specific rules override less specific
|
||||||
|
# ones (last match wins), so list broad patterns first.
|
||||||
|
#
|
||||||
|
# Docs: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
|
||||||
|
|
||||||
|
# Catch-all: maintainer reviews everything
|
||||||
|
* @sjackson0109
|
||||||
|
|
||||||
|
# C++ DLL source
|
||||||
|
src-x86-x64-Fusix/ @sjackson0109
|
||||||
|
|
||||||
|
# C# tools (installer, configurator, checker, offset-finder)
|
||||||
|
src-csharp/ @sjackson0109
|
||||||
|
|
||||||
|
# WiX MSI packaging
|
||||||
|
msi/ @sjackson0109
|
||||||
|
|
||||||
|
# CI/CD pipelines
|
||||||
|
.github/ @sjackson0109
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
docs/ @sjackson0109
|
||||||
|
README.md @sjackson0109
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
updates:
|
||||||
|
# Keep GitHub Actions pinned to their latest minor/patch releases
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
day: monday
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
groups:
|
||||||
|
actions-minor:
|
||||||
|
patterns: ["*"]
|
||||||
|
|
||||||
|
# Keep .NET SDK NuGet dependencies up to date (for src-csharp/)
|
||||||
|
- package-ecosystem: nuget
|
||||||
|
directory: /src-csharp
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
day: monday
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
|
||||||
|
# Monitor git submodules for upstream releases
|
||||||
|
# Covers: src-csharp/RDPOffsetFinder (llccd/RDPWrapOffsetFinder)
|
||||||
|
# src-csharp/RDPOffsetFinder/zydis (zyantific/zydis)
|
||||||
|
# Note: gitsubmodule ecosystem is currently in Dependabot beta.
|
||||||
|
# Enable once the feature is available for the repository, or track
|
||||||
|
# submodule versions manually via the pinned SHA in .gitmodules.
|
||||||
|
- package-ecosystem: gitsubmodule
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
day: monday
|
||||||
|
open-pull-requests-limit: 5
|
||||||
@ -0,0 +1,760 @@
|
|||||||
|
name: Build and Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, master]
|
||||||
|
paths:
|
||||||
|
- 'msi/rdpwrap.ini'
|
||||||
|
- 'msi/rdpwrap-arm-kb.ini'
|
||||||
|
- 'msi/**'
|
||||||
|
- 'src-x86-x64-Fusix/**'
|
||||||
|
- 'src-csharp/**'
|
||||||
|
- '.github/workflows/build-and-release.yml'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: release
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
# ────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Job dependency / parallelism map
|
||||||
|
#
|
||||||
|
# build-dll ───────────────► build-csharp ──────────► build-msi ──┐
|
||||||
|
# build-offsetfinder ───────────────────────────────────────────► release
|
||||||
|
# download-sergiye ───────────────────────────────────────────► release
|
||||||
|
# build-dll ─┘
|
||||||
|
# ────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
# ── Job 1: Build rdpwrap.dll (x64, x86, ARM64) ────────────────────────────
|
||||||
|
# No job dependencies — starts immediately.
|
||||||
|
build-dll:
|
||||||
|
name: Build rdpwrap DLL
|
||||||
|
runs-on: windows-2022
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Setup MSBuild
|
||||||
|
uses: microsoft/setup-msbuild@v3
|
||||||
|
|
||||||
|
- name: Build rdpwrap.dll (x64, Win32, ARM64)
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
foreach ($platform in @('x64', 'Win32', 'ARM64')) {
|
||||||
|
msbuild src-x86-x64-Fusix/RDPWrap.vcxproj `
|
||||||
|
/p:Configuration=Release `
|
||||||
|
/p:Platform="$platform" `
|
||||||
|
/p:PlatformToolset=v143 `
|
||||||
|
/p:WindowsTargetPlatformVersion=10.0 `
|
||||||
|
/v:minimal
|
||||||
|
}
|
||||||
|
Copy-Item src-x86-x64-Fusix/Release/x64/rdpwrap.dll .\rdpwrap_x64.dll
|
||||||
|
Copy-Item src-x86-x64-Fusix/Release/x86/rdpwrap.dll .\rdpwrap_x86.dll
|
||||||
|
Copy-Item src-x86-x64-Fusix/Release/arm64/rdpwrap.dll .\rdpwrap_arm64.dll
|
||||||
|
Write-Host "Built DLLs:"
|
||||||
|
Get-Item .\rdpwrap_x64.dll, .\rdpwrap_x86.dll, .\rdpwrap_arm64.dll | Format-Table Name, Length
|
||||||
|
|
||||||
|
- name: Upload rdpwrap DLLs
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rdpwrap-dlls
|
||||||
|
path: |
|
||||||
|
rdpwrap_x64.dll
|
||||||
|
rdpwrap_x86.dll
|
||||||
|
rdpwrap_arm64.dll
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
||||||
|
# ── Job 2: Build RDPWrapOffsetFinder (x64, x86) — parallel to build-dll ───
|
||||||
|
# No job dependencies — starts immediately.
|
||||||
|
build-offsetfinder:
|
||||||
|
name: Build OffsetFinder
|
||||||
|
runs-on: windows-2022
|
||||||
|
outputs:
|
||||||
|
finder_ver: ${{ steps.finder.outputs.finder_ver }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository (with submodules)
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Setup MSBuild
|
||||||
|
uses: microsoft/setup-msbuild@v3
|
||||||
|
|
||||||
|
- name: Build RDPWrapOffsetFinder from submodule
|
||||||
|
id: finder
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$version = (git -C src-csharp/RDPOffsetFinder describe --tags --always).Trim()
|
||||||
|
Write-Host "Submodule version: $version"
|
||||||
|
|
||||||
|
foreach ($cfg in @(
|
||||||
|
@{ Platform = 'x64'; ZydisCfg = 'Release MD DLL'; ZydisBin = 'ReleaseX64'; Arch = 'x64' }
|
||||||
|
@{ Platform = 'Win32'; ZydisCfg = 'Release MD DLL'; ZydisBin = 'ReleaseX86'; Arch = 'x86' }
|
||||||
|
)) {
|
||||||
|
# Build Zydis DLL
|
||||||
|
msbuild src-csharp/RDPOffsetFinder/zydis/msvc/zydis/Zydis.vcxproj `
|
||||||
|
/p:Configuration="$($cfg.ZydisCfg)" `
|
||||||
|
/p:Platform="$($cfg.Platform)" `
|
||||||
|
/p:PlatformToolset=v143 `
|
||||||
|
/v:minimal
|
||||||
|
|
||||||
|
# Build RDPWrapOffsetFinder
|
||||||
|
msbuild src-csharp/RDPOffsetFinder/RDPWrapOffsetFinder/RDPWrapOffsetFinder.vcxproj `
|
||||||
|
/p:Configuration=Release `
|
||||||
|
/p:Platform="$($cfg.Platform)" `
|
||||||
|
/p:PlatformToolset=v143 `
|
||||||
|
/v:minimal
|
||||||
|
|
||||||
|
$arch = $cfg.Arch
|
||||||
|
$exeSrc = "src-csharp/RDPOffsetFinder/RDPWrapOffsetFinder/$($cfg.Platform)/Release/RDPWrapOffsetFinder.exe"
|
||||||
|
$dllSrc = "src-csharp/RDPOffsetFinder/zydis/msvc/bin/$($cfg.ZydisBin)/Zydis.dll"
|
||||||
|
Copy-Item $exeSrc ".\RDPWrapOffsetFinder_$arch.exe"
|
||||||
|
Copy-Item $dllSrc ".\Zydis_$arch.dll"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Built finder tools (version $version):"
|
||||||
|
Get-Item .\RDPWrapOffsetFinder_*.exe, .\Zydis_*.dll | Format-Table Name, Length
|
||||||
|
|
||||||
|
echo "finder_ver=$version" >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Upload OffsetFinder binaries
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: offsetfinder-binaries
|
||||||
|
path: |
|
||||||
|
RDPWrapOffsetFinder_x64.exe
|
||||||
|
RDPWrapOffsetFinder_x86.exe
|
||||||
|
Zydis_x64.dll
|
||||||
|
Zydis_x86.dll
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
||||||
|
# ── Job 3: Download rdpWrapper GUI from sergiye — parallel to build-dll ────
|
||||||
|
# No job dependencies — starts immediately.
|
||||||
|
download-sergiye:
|
||||||
|
name: Download sergiye rdpWrapper
|
||||||
|
runs-on: windows-2022
|
||||||
|
outputs:
|
||||||
|
wrapper_ver: ${{ steps.wrapper.outputs.wrapper_ver }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Download rdpWrapper (sergiye)
|
||||||
|
id: wrapper
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$apiUrl = "https://api.github.com/repos/sergiye/rdpWrapper/releases/latest"
|
||||||
|
$release = Invoke-RestMethod -Uri $apiUrl -Headers @{ "User-Agent" = "rdpwrap-ci" }
|
||||||
|
|
||||||
|
foreach ($arch in @('x64', 'x86')) {
|
||||||
|
$asset = $release.assets |
|
||||||
|
Where-Object { $_.name -eq "rdpWrapper_$arch.exe" } |
|
||||||
|
Select-Object -First 1
|
||||||
|
if (-not $asset) {
|
||||||
|
throw "Could not find rdpWrapper_$arch.exe in sergiye release $($release.tag_name)"
|
||||||
|
}
|
||||||
|
Write-Host "Downloading $($asset.browser_download_url)"
|
||||||
|
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile ".\rdpWrapper_$arch.exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Wrapper exes:"
|
||||||
|
Get-Item .\rdpWrapper_x64.exe, .\rdpWrapper_x86.exe | Format-Table Name, Length
|
||||||
|
echo "wrapper_ver=$($release.tag_name)" >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# ── SHA-256 audit log for third-party binaries ──────────────────────────
|
||||||
|
# Records the hash of every downloaded sergiye binary in the workflow log.
|
||||||
|
# This provides a tamper-evident paper trail without blocking the release.
|
||||||
|
- name: Audit SHA-256 of downloaded binaries
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$files = Get-Item .\rdpWrapper_x64.exe, .\rdpWrapper_x86.exe
|
||||||
|
Write-Host "SHA-256 audit (third-party downloads from sergiye/rdpWrapper):"
|
||||||
|
foreach ($f in $files) {
|
||||||
|
$hash = (Get-FileHash $f.FullName -Algorithm SHA256).Hash
|
||||||
|
Write-Host " $($f.Name) $hash"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Verify hashes against pinned values in tools/sergiye-hashes.json ────
|
||||||
|
# If the file contains a non-empty 'release' key, hashes must match exactly.
|
||||||
|
# If 'release' is empty this step is skipped (bootstrap scenario).
|
||||||
|
- name: Verify sergiye binary hashes
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$pinFile = 'tools/sergiye-hashes.json'
|
||||||
|
$pin = Get-Content $pinFile -Raw | ConvertFrom-Json
|
||||||
|
if ([string]::IsNullOrEmpty($pin.release)) {
|
||||||
|
Write-Host "Hash pinning not yet configured (tools/sergiye-hashes.json 'release' is empty) — skipping."
|
||||||
|
Write-Host "To enable pinning: download the binaries, compute hashes with Get-FileHash, and update $pinFile."
|
||||||
|
} else {
|
||||||
|
Write-Host "Verifying against pinned release: $($pin.release)"
|
||||||
|
$pass = $true
|
||||||
|
foreach ($name in @('rdpWrapper_x64.exe', 'rdpWrapper_x86.exe')) {
|
||||||
|
$expected = $pin.$name
|
||||||
|
$actual = (Get-FileHash ".\$name" -Algorithm SHA256).Hash
|
||||||
|
if ($actual -ne $expected) {
|
||||||
|
Write-Error "Hash mismatch for $name!"
|
||||||
|
Write-Error " Expected: $expected"
|
||||||
|
Write-Error " Actual: $actual"
|
||||||
|
$pass = $false
|
||||||
|
} else {
|
||||||
|
Write-Host " $name OK ($actual)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $pass) { throw "One or more sergiye binary hashes did not match the pinned values." }
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Upload sergiye wrappers
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sergiye-wrappers
|
||||||
|
path: |
|
||||||
|
rdpWrapper_x64.exe
|
||||||
|
rdpWrapper_x86.exe
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
||||||
|
# ── Job 4: Build C# tools (FD + SC) — waits for build-dll ────────────────
|
||||||
|
# Waits for build-dll so the DLLs can be embedded into single-file EXEs.
|
||||||
|
build-csharp:
|
||||||
|
name: Build C# Tools
|
||||||
|
runs-on: windows-2022
|
||||||
|
needs: [build-dll]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Download rdpwrap DLLs
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rdpwrap-dlls
|
||||||
|
path: .
|
||||||
|
|
||||||
|
# Stage embedded resources for RDPWInst offline/bundled mode.
|
||||||
|
# RDPWInst.csproj embeds these files when present (Condition="Exists(...)").
|
||||||
|
# Copying them here ensures the single-file EXE can install without network.
|
||||||
|
- name: Stage RDPWInst embedded resources
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$res = 'src-csharp/RDPWInst/Resources'
|
||||||
|
New-Item -ItemType Directory -Path $res -Force | Out-Null
|
||||||
|
Copy-Item .\rdpwrap_x86.dll "$res\rdpw32.dll" -Force
|
||||||
|
Copy-Item .\rdpwrap_x64.dll "$res\rdpw64.dll" -Force
|
||||||
|
Copy-Item msi\rdpwrap.ini "$res\rdpwrap.ini" -Force
|
||||||
|
Copy-Item LICENSE "$res\license.txt" -Force
|
||||||
|
Write-Host "Staged resources:"
|
||||||
|
Get-ChildItem $res | Format-Table Name, Length
|
||||||
|
|
||||||
|
- name: Setup .NET SDK
|
||||||
|
uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: '10.x'
|
||||||
|
|
||||||
|
- name: Cache NuGet packages
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.nuget/packages
|
||||||
|
key: nuget-${{ runner.os }}-${{ hashFiles('**/Directory.Build.props', '**/*.csproj') }}
|
||||||
|
restore-keys: |
|
||||||
|
nuget-${{ runner.os }}-
|
||||||
|
|
||||||
|
# Compute version once so it's consistent across all publish and sign steps
|
||||||
|
- name: Get build version
|
||||||
|
id: build_ver
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$v = Get-Date -Format "yyyy.M.d"
|
||||||
|
echo "version=$v" >> $env:GITHUB_OUTPUT
|
||||||
|
Write-Host "Build version: $v"
|
||||||
|
|
||||||
|
# ── Build framework-dependent variants ──────────────────────────────────
|
||||||
|
- name: Publish C# tools (x64, x86, arm64)
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
DOTNET_NOLOGO: true
|
||||||
|
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||||
|
run: |
|
||||||
|
$projects = @('RDPWInst', 'RDPConf', 'RDPCheck')
|
||||||
|
$rids = [ordered]@{ x64 = 'win-x64'; x86 = 'win-x86'; arm64 = 'win-arm64' }
|
||||||
|
$ver = '${{ steps.build_ver.outputs.version }}'
|
||||||
|
foreach ($proj in $projects) {
|
||||||
|
foreach ($plat in $rids.Keys) {
|
||||||
|
$rid = $rids[$plat]
|
||||||
|
dotnet publish "src-csharp/$proj/$proj.csproj" `
|
||||||
|
-c Release `
|
||||||
|
-r $rid `
|
||||||
|
-p:Platform=$plat `
|
||||||
|
-p:PublishSingleFile=true `
|
||||||
|
-p:SelfContained=false `
|
||||||
|
-p:Version=$ver `
|
||||||
|
-p:AssemblyVersion="$ver.0" `
|
||||||
|
-p:FileVersion="$ver.0" `
|
||||||
|
--output "dist_cs\$plat\$proj"
|
||||||
|
Copy-Item "dist_cs\$plat\$proj\$proj.exe" ".\${proj}_${plat}.exe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host "C# tool executables:"
|
||||||
|
Get-Item .\RDPWInst_*.exe, .\RDPConf_*.exe, .\RDPCheck_*.exe | Format-Table Name, Length
|
||||||
|
|
||||||
|
# ── Build self-contained variants (no .NET runtime required) ─────────────
|
||||||
|
# Self-contained exes are larger (~60 MB) but work without a .NET install.
|
||||||
|
# Suffixed with _sc to distinguish from the framework-dependent versions.
|
||||||
|
- name: Publish self-contained C# tools (x64, x86, arm64)
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
DOTNET_NOLOGO: true
|
||||||
|
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||||
|
run: |
|
||||||
|
$projects = @('RDPWInst', 'RDPConf', 'RDPCheck')
|
||||||
|
$rids = [ordered]@{ x64 = 'win-x64'; x86 = 'win-x86'; arm64 = 'win-arm64' }
|
||||||
|
$ver = '${{ steps.build_ver.outputs.version }}'
|
||||||
|
foreach ($proj in $projects) {
|
||||||
|
foreach ($plat in $rids.Keys) {
|
||||||
|
$rid = $rids[$plat]
|
||||||
|
dotnet publish "src-csharp/$proj/$proj.csproj" `
|
||||||
|
-c Release `
|
||||||
|
-r $rid `
|
||||||
|
-p:Platform=$plat `
|
||||||
|
-p:PublishSingleFile=true `
|
||||||
|
-p:SelfContained=true `
|
||||||
|
-p:Version=$ver `
|
||||||
|
-p:AssemblyVersion="$ver.0" `
|
||||||
|
-p:FileVersion="$ver.0" `
|
||||||
|
--output "dist_cs_sc\$plat\$proj"
|
||||||
|
Copy-Item "dist_cs_sc\$plat\$proj\$proj.exe" ".\${proj}_${plat}_sc.exe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host "Self-contained executables:"
|
||||||
|
Get-Item .\RDPWInst_*_sc.exe, .\RDPConf_*_sc.exe, .\RDPCheck_*_sc.exe | Format-Table Name, Length
|
||||||
|
|
||||||
|
# ── Optional: code-sign all C# executables ──────────────────────────────
|
||||||
|
# Enable by setting repository variable USE_CERT_SIGNING=true and adding
|
||||||
|
# secrets CODESIGN_CERT_BASE64 (PFX as base64) and CODESIGN_CERT_PASSWORD.
|
||||||
|
- name: Sign C# executables
|
||||||
|
if: vars.USE_CERT_SIGNING == 'true'
|
||||||
|
env:
|
||||||
|
CODESIGN_CERT_BASE64: ${{ secrets.CODESIGN_CERT_BASE64 }}
|
||||||
|
CODESIGN_CERT_PASSWORD: ${{ secrets.CODESIGN_CERT_PASSWORD }}
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$pfxPath = "$env:RUNNER_TEMP\codesign.pfx"
|
||||||
|
[IO.File]::WriteAllBytes($pfxPath,
|
||||||
|
[Convert]::FromBase64String($env:CODESIGN_CERT_BASE64))
|
||||||
|
$signtool = (Resolve-Path "${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x64\signtool.exe" |
|
||||||
|
Sort-Object | Select-Object -Last 1).Path
|
||||||
|
# Sign both framework-dependent and self-contained variants (glob picks up all _*.exe files)
|
||||||
|
$exes = Get-Item .\RDPWInst_*.exe, .\RDPConf_*.exe, .\RDPCheck_*.exe
|
||||||
|
foreach ($exe in $exes) {
|
||||||
|
& $signtool sign /fd SHA256 /f $pfxPath `
|
||||||
|
/p $env:CODESIGN_CERT_PASSWORD `
|
||||||
|
/tr http://timestamp.digicert.com /td SHA256 $exe.FullName
|
||||||
|
}
|
||||||
|
Remove-Item $pfxPath -Force
|
||||||
|
|
||||||
|
- name: Upload C# tools
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: csharp-tools
|
||||||
|
path: |
|
||||||
|
RDPWInst_*.exe
|
||||||
|
RDPConf_*.exe
|
||||||
|
RDPCheck_*.exe
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
||||||
|
# ── Job 5: Build MSI packages — waits for build-csharp (implies build-dll) ─
|
||||||
|
build-msi:
|
||||||
|
name: Build MSI Packages
|
||||||
|
runs-on: windows-2022
|
||||||
|
needs: [build-csharp]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Download rdpwrap DLLs
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rdpwrap-dlls
|
||||||
|
path: .
|
||||||
|
|
||||||
|
- name: Download C# tools
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: csharp-tools
|
||||||
|
path: .
|
||||||
|
|
||||||
|
- name: Setup .NET SDK
|
||||||
|
uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: '10.x'
|
||||||
|
|
||||||
|
- name: Cache NuGet packages (WiX restore)
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.nuget/packages
|
||||||
|
key: nuget-${{ runner.os }}-${{ hashFiles('msi/global.json', 'msi/*.wixproj') }}
|
||||||
|
restore-keys: |
|
||||||
|
nuget-${{ runner.os }}-
|
||||||
|
|
||||||
|
# Get version (same formula as build-csharp for filename consistency)
|
||||||
|
- name: Get build version
|
||||||
|
id: build_ver
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$v = Get-Date -Format "yyyy.M.d"
|
||||||
|
echo "version=$v" >> $env:GITHUB_OUTPUT
|
||||||
|
Write-Host "Build version: $v"
|
||||||
|
|
||||||
|
# ── Build MSI installers (WiX v5, x64 / x86 / arm64) ──────────────────
|
||||||
|
- name: Build MSI packages
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ver = '${{ steps.build_ver.outputs.version }}'
|
||||||
|
foreach ($arch in @('x64', 'x86', 'arm64')) {
|
||||||
|
# Stage arch-specific inputs next to the .wixproj
|
||||||
|
Copy-Item ".\rdpwrap_$arch.dll" "msi\rdpwrap_$arch.dll" -Force
|
||||||
|
Copy-Item ".\RDPWInst_$arch.exe" "msi\RDPWInst_$arch.exe" -Force
|
||||||
|
Copy-Item ".\RDPConf_$arch.exe" "msi\RDPConf_$arch.exe" -Force
|
||||||
|
Copy-Item ".\RDPCheck_$arch.exe" "msi\RDPCheck_$arch.exe" -Force
|
||||||
|
|
||||||
|
dotnet build msi/RDPWInst.wixproj `
|
||||||
|
-c Release `
|
||||||
|
/p:Platform=$arch `
|
||||||
|
/p:OutputName="RDPWrapper-$arch" `
|
||||||
|
/p:PackageVersion=$ver `
|
||||||
|
/p:OutputPath="$PWD/msi_out/$arch"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "WiX build failed for $arch" }
|
||||||
|
|
||||||
|
$msiSrc = Get-Item "msi_out\$arch\RDPWrapper-$arch.msi" -ErrorAction SilentlyContinue
|
||||||
|
if (-not $msiSrc) {
|
||||||
|
$msiSrc = Get-ChildItem "msi\bin" -Recurse -Filter "RDPWrapper-$arch.msi" |
|
||||||
|
Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||||
|
}
|
||||||
|
if (-not $msiSrc) { throw "Could not locate RDPWrapper-$arch.msi after WiX build" }
|
||||||
|
Copy-Item $msiSrc.FullName ".\RDPWrapper-${ver}-$arch.msi"
|
||||||
|
Write-Host "Produced: RDPWrapper-${ver}-$arch.msi ($([math]::Round($msiSrc.Length/1MB,1)) MB)"
|
||||||
|
}
|
||||||
|
Write-Host "MSI packages:"
|
||||||
|
Get-Item .\RDPWrapper-*-*.msi | Format-Table Name, Length
|
||||||
|
|
||||||
|
# ── Optional: sign the MSI packages ────────────────────────────────────
|
||||||
|
# Enable by setting repository variable USE_CERT_SIGNING=true and adding
|
||||||
|
# secrets CODESIGN_CERT_BASE64 (PFX as base64) and CODESIGN_CERT_PASSWORD.
|
||||||
|
- name: Sign MSI packages
|
||||||
|
if: vars.USE_CERT_SIGNING == 'true'
|
||||||
|
env:
|
||||||
|
CODESIGN_CERT_BASE64: ${{ secrets.CODESIGN_CERT_BASE64 }}
|
||||||
|
CODESIGN_CERT_PASSWORD: ${{ secrets.CODESIGN_CERT_PASSWORD }}
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$pfxPath = "$env:RUNNER_TEMP\codesign.pfx"
|
||||||
|
[IO.File]::WriteAllBytes($pfxPath,
|
||||||
|
[Convert]::FromBase64String($env:CODESIGN_CERT_BASE64))
|
||||||
|
$signtool = (Resolve-Path "${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x64\signtool.exe" |
|
||||||
|
Sort-Object | Select-Object -Last 1).Path
|
||||||
|
foreach ($msi in (Get-Item .\RDPWrapper-*-*.msi)) {
|
||||||
|
& $signtool sign /fd SHA256 /f $pfxPath `
|
||||||
|
/p $env:CODESIGN_CERT_PASSWORD `
|
||||||
|
/tr http://timestamp.digicert.com /td SHA256 `
|
||||||
|
$msi.FullName
|
||||||
|
}
|
||||||
|
Remove-Item $pfxPath -Force
|
||||||
|
|
||||||
|
- name: Upload MSI packages
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: msi-packages
|
||||||
|
path: RDPWrapper-*-*.msi
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
||||||
|
# ── Job 6: Publish GitHub Release — waits for all build jobs ──────────────
|
||||||
|
release:
|
||||||
|
name: Publish Release
|
||||||
|
runs-on: windows-2022
|
||||||
|
needs: [build-dll, build-csharp, build-offsetfinder, download-sergiye, build-msi]
|
||||||
|
# Attach to the 'release' GitHub Environment so repository admins can add
|
||||||
|
# required-reviewer gates in Settings → Environments → release.
|
||||||
|
# If the environment doesn't exist yet it will be created without gates.
|
||||||
|
environment: release
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Download rdpwrap DLLs
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rdpwrap-dlls
|
||||||
|
path: .
|
||||||
|
|
||||||
|
- name: Download C# tools
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: csharp-tools
|
||||||
|
path: .
|
||||||
|
|
||||||
|
- name: Download OffsetFinder binaries
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: offsetfinder-binaries
|
||||||
|
path: .
|
||||||
|
|
||||||
|
- name: Download sergiye wrappers
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sergiye-wrappers
|
||||||
|
path: .
|
||||||
|
|
||||||
|
- name: Download MSI packages
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: msi-packages
|
||||||
|
path: .
|
||||||
|
|
||||||
|
- name: Verify all release assets are present
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$required = @(
|
||||||
|
'rdpwrap_x64.dll', 'rdpwrap_x86.dll', 'rdpwrap_arm64.dll',
|
||||||
|
'RDPWInst_x64.exe', 'RDPWInst_x86.exe', 'RDPWInst_arm64.exe',
|
||||||
|
'RDPConf_x64.exe', 'RDPConf_x86.exe', 'RDPConf_arm64.exe',
|
||||||
|
'RDPCheck_x64.exe', 'RDPCheck_x86.exe', 'RDPCheck_arm64.exe',
|
||||||
|
'RDPWrapOffsetFinder_x64.exe', 'RDPWrapOffsetFinder_x86.exe',
|
||||||
|
'Zydis_x64.dll', 'Zydis_x86.dll',
|
||||||
|
'rdpWrapper_x64.exe', 'rdpWrapper_x86.exe'
|
||||||
|
)
|
||||||
|
$missing = $required | Where-Object { -not (Test-Path $_) }
|
||||||
|
if ($missing) {
|
||||||
|
throw "Missing required release assets: $($missing -join ', ')"
|
||||||
|
}
|
||||||
|
Write-Host "All required assets present."
|
||||||
|
Get-ChildItem *.exe, *.dll, *.msi | Format-Table Name, Length
|
||||||
|
|
||||||
|
# ── Extract the date string used for tagging and the updated field ───────
|
||||||
|
- name: Get release metadata
|
||||||
|
id: meta
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$date = Get-Date -Format "yyyy.MM.dd.HHmm"
|
||||||
|
$stamp = Get-Date -Format "yyyy-MM-dd HH:mm UTC"
|
||||||
|
|
||||||
|
# Pull the Updated= line out of the INI to surface in release notes
|
||||||
|
$iniContent = Get-Content msi/rdpwrap.ini -Raw
|
||||||
|
$iniDate = if ($iniContent -match 'Updated=([^\r\n]+)') { $Matches[1] } else { 'unknown' }
|
||||||
|
|
||||||
|
echo "date=$date" >> $env:GITHUB_OUTPUT
|
||||||
|
echo "stamp=$stamp" >> $env:GITHUB_OUTPUT
|
||||||
|
echo "inidate=$iniDate" >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# ── Validate the INI has required sections and mandatory per-version keys ─
|
||||||
|
- name: Validate INI
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ini = Get-Content msi/rdpwrap.ini -Raw
|
||||||
|
$lines = Get-Content msi/rdpwrap.ini
|
||||||
|
|
||||||
|
# 1) Check mandatory top-level sections exist
|
||||||
|
foreach ($section in @('[Main]', '[SLPolicy]', '[PatchCodes]')) {
|
||||||
|
if ($ini -notmatch [regex]::Escape($section)) {
|
||||||
|
throw "INI validation failed: missing required section $section"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2) Parse every Windows-version section and verify it contains the
|
||||||
|
# mandatory keys LocalOnlyPatch and SLInitHook.
|
||||||
|
$sectionKeys = [System.Collections.Generic.Dictionary[string, System.Collections.Generic.List[string]]]::new()
|
||||||
|
$currentSection = $null
|
||||||
|
|
||||||
|
foreach ($line in $lines) {
|
||||||
|
if ($line -match '^\[(\d[\d\.]+)\]') {
|
||||||
|
$currentSection = $Matches[1]
|
||||||
|
$sectionKeys[$currentSection] = [System.Collections.Generic.List[string]]::new()
|
||||||
|
} elseif ($null -ne $currentSection -and $line -match '^([A-Za-z][A-Za-z0-9_]*)[\s]*=') {
|
||||||
|
$sectionKeys[$currentSection].Add($Matches[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$mandatoryKeys = @('LocalOnlyPatch', 'SLInitHook')
|
||||||
|
$errors = [System.Collections.Generic.List[string]]::new()
|
||||||
|
|
||||||
|
foreach ($sec in $sectionKeys.Keys) {
|
||||||
|
foreach ($key in $mandatoryKeys) {
|
||||||
|
if (-not $sectionKeys[$sec].Contains($key)) {
|
||||||
|
$errors.Add(" Section [$sec] is missing mandatory key: $key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errors.Count -gt 0) {
|
||||||
|
Write-Error "INI mandatory-key check failed ($($errors.Count) issue(s)):"
|
||||||
|
$errors | ForEach-Object { Write-Error $_ }
|
||||||
|
throw "INI validation failed — see errors above."
|
||||||
|
}
|
||||||
|
|
||||||
|
$sectionCount = $sectionKeys.Count
|
||||||
|
Write-Host "INI is valid. Windows-version sections: $sectionCount (all contain LocalOnlyPatch + SLInitHook)"
|
||||||
|
|
||||||
|
# ── Auto-generate changelog from merged PRs since last release ───────────
|
||||||
|
- name: Generate changelog
|
||||||
|
id: changelog
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
# Get the tag of the previous release so we can scope the PR list
|
||||||
|
$prevTag = (gh release list --limit 1 --json tagName --jq '.[0].tagName' 2>$null) ?? ''
|
||||||
|
if ($prevTag) {
|
||||||
|
Write-Host "Generating changelog since: $prevTag"
|
||||||
|
$since = gh release view $prevTag --json publishedAt --jq .publishedAt
|
||||||
|
$prs = gh pr list --state merged --base main --search "merged:>=$since" `
|
||||||
|
--json number,title,author --limit 50 | ConvertFrom-Json
|
||||||
|
} else {
|
||||||
|
Write-Host "No previous release found; skipping per-PR changelog."
|
||||||
|
$prs = @()
|
||||||
|
}
|
||||||
|
if ($prs.Count -gt 0) {
|
||||||
|
$lines = $prs | ForEach-Object { "- #$($_.number) $($_.title) (@$($_.author.login))" }
|
||||||
|
$body = $lines -join "`n"
|
||||||
|
} else {
|
||||||
|
$body = "_No merged pull requests found since the previous release._"
|
||||||
|
}
|
||||||
|
# Write multi-line output safely
|
||||||
|
$delim = [guid]::NewGuid().ToString('N')
|
||||||
|
echo "prs<<$delim" >> $env:GITHUB_OUTPUT
|
||||||
|
echo $body >> $env:GITHUB_OUTPUT
|
||||||
|
echo $delim >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# ── Assemble the three user-facing distribution bundles ──────────────────
|
||||||
|
- name: Create distribution bundles
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
# RDPWrapper.zip - complete install package (all arches, framework-dependent)
|
||||||
|
$d = ".\bundle_wrapper"
|
||||||
|
New-Item -ItemType Directory -Path $d -Force | Out-Null
|
||||||
|
Copy-Item .\rdpwrap_x64.dll "$d\rdpwrap_x64.dll"
|
||||||
|
Copy-Item .\rdpwrap_x86.dll "$d\rdpwrap_x86.dll"
|
||||||
|
Copy-Item .\rdpwrap_arm64.dll "$d\rdpwrap_arm64.dll"
|
||||||
|
Copy-Item .\rdpWrapper_x64.exe "$d\rdpWrapper_x64.exe"
|
||||||
|
Copy-Item .\rdpWrapper_x86.exe "$d\rdpWrapper_x86.exe"
|
||||||
|
# Framework-dependent C# tools (require .NET 10 Desktop Runtime)
|
||||||
|
foreach ($proj in @('RDPWInst', 'RDPConf', 'RDPCheck')) {
|
||||||
|
foreach ($arch in @('x64', 'x86', 'arm64')) {
|
||||||
|
$src = ".\${proj}_${arch}.exe"
|
||||||
|
if (Test-Path $src) { Copy-Item $src "$d\${proj}_${arch}.exe" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Copy-Item msi\rdpwrap.ini "$d\rdpwrap.ini"
|
||||||
|
Compress-Archive -Path "$d\*" -DestinationPath ".\RDPWrapper.zip" -Force
|
||||||
|
Remove-Item $d -Recurse -Force
|
||||||
|
|
||||||
|
# RDPWrapper-SelfContained.zip - apps bundled with .NET runtime (~60 MB each)
|
||||||
|
$sc = ".\bundle_sc"
|
||||||
|
New-Item -ItemType Directory -Path $sc -Force | Out-Null
|
||||||
|
foreach ($proj in @('RDPWInst', 'RDPConf', 'RDPCheck')) {
|
||||||
|
foreach ($arch in @('x64', 'x86', 'arm64')) {
|
||||||
|
$src = ".\${proj}_${arch}_sc.exe"
|
||||||
|
if (Test-Path $src) { Copy-Item $src "$sc\${proj}_${arch}_sc.exe" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Copy-Item msi\rdpwrap.ini "$sc\rdpwrap.ini"
|
||||||
|
Compress-Archive -Path "$sc\*" -DestinationPath ".\RDPWrapper-SelfContained.zip" -Force
|
||||||
|
Remove-Item $sc -Recurse -Force
|
||||||
|
|
||||||
|
# RDPWrapOffsetFinder.zip - offset finder with per-arch subfolders
|
||||||
|
$f = ".\bundle_finder"
|
||||||
|
foreach ($arch in @('x64', 'x86')) {
|
||||||
|
$dir = "$f\$arch"
|
||||||
|
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
||||||
|
Copy-Item ".\RDPWrapOffsetFinder_$arch.exe" "$dir\RDPWrapOffsetFinder.exe"
|
||||||
|
Copy-Item ".\Zydis_$arch.dll" "$dir\Zydis.dll"
|
||||||
|
}
|
||||||
|
Compress-Archive -Path "$f\*" -DestinationPath ".\RDPWrapOffsetFinder.zip" -Force
|
||||||
|
Remove-Item $f -Recurse -Force
|
||||||
|
|
||||||
|
Write-Host "Distribution bundles:"
|
||||||
|
Get-Item .\RDPWrapper.zip, .\RDPWrapper-SelfContained.zip, .\RDPWrapOffsetFinder.zip | Format-Table Name, Length
|
||||||
|
|
||||||
|
# ── Create/update a versioned GitHub Release with all assets ─────────────
|
||||||
|
- name: Publish release
|
||||||
|
# Pinned to v2 tag SHA for supply-chain safety.
|
||||||
|
# Bump this SHA when upgrading: https://github.com/softprops/action-gh-release/releases
|
||||||
|
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2
|
||||||
|
with:
|
||||||
|
tag_name: "ini-${{ steps.meta.outputs.date }}"
|
||||||
|
name: "INI Update ${{ steps.meta.outputs.date }}"
|
||||||
|
prerelease: false
|
||||||
|
make_latest: true
|
||||||
|
body: |
|
||||||
|
## RDP Wrapper - Automated Release
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| INI `Updated` date | `${{ steps.meta.outputs.inidate }}` |
|
||||||
|
| Published | ${{ steps.meta.outputs.stamp }} |
|
||||||
|
| rdpWrapper (sergiye) | ${{ needs.download-sergiye.outputs.wrapper_ver }} |
|
||||||
|
| RDPWrapOffsetFinder (llccd) | ${{ needs.build-offsetfinder.outputs.finder_ver }} |
|
||||||
|
|
||||||
|
> **Framework-dependent tools** (default) require **.NET 10 Desktop Runtime** on the target machine.
|
||||||
|
> Download: https://dotnet.microsoft.com/download/dotnet/10.0
|
||||||
|
> For environments without a runtime install, use the `*_sc.exe` files from `RDPWrapper-SelfContained.zip`.
|
||||||
|
|
||||||
|
### Downloads
|
||||||
|
|
||||||
|
| File | Contents | Use |
|
||||||
|
|---|---|---|
|
||||||
|
| `RDPWrapper.zip` | rdpwrap DLLs, C# tools (x64/x86/arm64), rdpWrapper GUI, rdpwrap.ini | **Main install package** — extract and run `RDPWInst_x64.exe -i -o` (or matching arch) |
|
||||||
|
| `RDPWrapper-SelfContained.zip` | Same C# tools bundled with .NET runtime (no separate runtime install needed) | Use when .NET 10 Desktop Runtime cannot be deployed |
|
||||||
|
| `RDPWrapper-<ver>-x64.msi` / `x86` / `arm64` | Windows Installer packages — GUI-friendly, self-registering uninstall | Preferred for managed/enterprise deployments |
|
||||||
|
| `RDPWrapOffsetFinder.zip` | RDPWrapOffsetFinder.exe + Zydis.dll for x64 and x86 | Generate offsets for an unknown termsrv.dll version manually |
|
||||||
|
|
||||||
|
### Merged pull requests
|
||||||
|
|
||||||
|
${{ steps.changelog.outputs.prs }}
|
||||||
|
|
||||||
|
### Individual loose files (used by the automated installer)
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `rdpwrap.ini` | Offset database fetched automatically by the installer |
|
||||||
|
| `RDPWrapOffsetFinder_x64.exe` / `Zydis_x64.dll` | Loose x64 binaries downloaded by `TryAutoGenerateOffsets` |
|
||||||
|
| `RDPWrapOffsetFinder_x86.exe` / `Zydis_x86.dll` | Loose x86 binaries downloaded by `TryAutoGenerateOffsets` |
|
||||||
|
files: |
|
||||||
|
RDPWrapper.zip
|
||||||
|
RDPWrapper-SelfContained.zip
|
||||||
|
RDPWrapper-*-x64.msi
|
||||||
|
RDPWrapper-*-x86.msi
|
||||||
|
RDPWrapper-*-arm64.msi
|
||||||
|
RDPWrapOffsetFinder.zip
|
||||||
|
msi/rdpwrap.ini
|
||||||
|
msi/rdpwrap-arm-kb.ini
|
||||||
|
rdpwrap_x64.dll
|
||||||
|
rdpwrap_x86.dll
|
||||||
|
rdpwrap_arm64.dll
|
||||||
|
RDPWInst_x64.exe
|
||||||
|
RDPWInst_x86.exe
|
||||||
|
RDPWInst_arm64.exe
|
||||||
|
RDPConf_x64.exe
|
||||||
|
RDPConf_x86.exe
|
||||||
|
RDPConf_arm64.exe
|
||||||
|
RDPCheck_x64.exe
|
||||||
|
RDPCheck_x86.exe
|
||||||
|
RDPCheck_arm64.exe
|
||||||
|
RDPWrapOffsetFinder_x64.exe
|
||||||
|
RDPWrapOffsetFinder_x86.exe
|
||||||
|
Zydis_x64.dll
|
||||||
|
Zydis_x86.dll
|
||||||
@ -0,0 +1,141 @@
|
|||||||
|
name: Build C# Tools
|
||||||
|
|
||||||
|
# Trigger on:
|
||||||
|
# - PRs targeting main/master that touch C# source (compile check before merge)
|
||||||
|
# - Version tag pushes (e.g. v1.7.0)
|
||||||
|
# - Manual dispatch
|
||||||
|
# Produces self-contained, single-file RDPWInst / RDPConf / RDPCheck
|
||||||
|
# executables for x64 and x86.
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main, master]
|
||||||
|
paths:
|
||||||
|
- 'src-csharp/**'
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
DOTNET_NOLOGO: true
|
||||||
|
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ── Publish self-contained executables ────────────────────────────────────────
|
||||||
|
build:
|
||||||
|
runs-on: windows-2022
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
platform: [x64, x86, arm64]
|
||||||
|
include:
|
||||||
|
- platform: x64
|
||||||
|
rid: win-x64
|
||||||
|
- platform: x86
|
||||||
|
rid: win-x86
|
||||||
|
- platform: arm64
|
||||||
|
rid: win-arm64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Setup .NET SDK
|
||||||
|
uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: '10.x'
|
||||||
|
|
||||||
|
- name: Cache NuGet packages
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.nuget/packages
|
||||||
|
key: nuget-${{ runner.os }}-${{ hashFiles('**/Directory.Build.props', '**/*.csproj') }}
|
||||||
|
restore-keys: |
|
||||||
|
nuget-${{ runner.os }}-
|
||||||
|
|
||||||
|
- name: Publish RDPWInst (${{ matrix.platform }})
|
||||||
|
run: |
|
||||||
|
dotnet publish src-csharp/RDPWInst/RDPWInst.csproj `
|
||||||
|
-c Release `
|
||||||
|
-r ${{ matrix.rid }} `
|
||||||
|
-p:Platform=${{ matrix.platform }} `
|
||||||
|
-p:PublishSingleFile=true `
|
||||||
|
-p:SelfContained=false `
|
||||||
|
--output dist/${{ matrix.platform }}/RDPWInst
|
||||||
|
|
||||||
|
- name: Publish RDPConf (${{ matrix.platform }})
|
||||||
|
run: |
|
||||||
|
dotnet publish src-csharp/RDPConf/RDPConf.csproj `
|
||||||
|
-c Release `
|
||||||
|
-r ${{ matrix.rid }} `
|
||||||
|
-p:Platform=${{ matrix.platform }} `
|
||||||
|
-p:PublishSingleFile=true `
|
||||||
|
-p:SelfContained=false `
|
||||||
|
--output dist/${{ matrix.platform }}/RDPConf
|
||||||
|
|
||||||
|
- name: Publish RDPCheck (${{ matrix.platform }})
|
||||||
|
run: |
|
||||||
|
dotnet publish src-csharp/RDPCheck/RDPCheck.csproj `
|
||||||
|
-c Release `
|
||||||
|
-r ${{ matrix.rid }} `
|
||||||
|
-p:Platform=${{ matrix.platform }} `
|
||||||
|
-p:PublishSingleFile=true `
|
||||||
|
-p:SelfContained=false `
|
||||||
|
--output dist/${{ matrix.platform }}/RDPCheck
|
||||||
|
|
||||||
|
# ── Optional: code-sign all three executables ─────────────────────────────
|
||||||
|
# Enable by setting repository variable USE_CERT_SIGNING=true and adding
|
||||||
|
# secrets CODESIGN_CERT_BASE64 (PFX as base64) and CODESIGN_CERT_PASSWORD.
|
||||||
|
- name: Sign executables (${{ matrix.platform }})
|
||||||
|
if: vars.USE_CERT_SIGNING == 'true'
|
||||||
|
env:
|
||||||
|
CODESIGN_CERT_BASE64: ${{ secrets.CODESIGN_CERT_BASE64 }}
|
||||||
|
CODESIGN_CERT_PASSWORD: ${{ secrets.CODESIGN_CERT_PASSWORD }}
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$pfxPath = "$env:RUNNER_TEMP\codesign.pfx"
|
||||||
|
[IO.File]::WriteAllBytes($pfxPath,
|
||||||
|
[Convert]::FromBase64String($env:CODESIGN_CERT_BASE64))
|
||||||
|
|
||||||
|
$exes = @(
|
||||||
|
"dist\${{ matrix.platform }}\RDPWInst\RDPWInst.exe",
|
||||||
|
"dist\${{ matrix.platform }}\RDPConf\RDPConf.exe",
|
||||||
|
"dist\${{ matrix.platform }}\RDPCheck\RDPCheck.exe"
|
||||||
|
)
|
||||||
|
|
||||||
|
$signtool = (Resolve-Path "${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x64\signtool.exe" |
|
||||||
|
Sort-Object | Select-Object -Last 1).Path
|
||||||
|
foreach ($exe in $exes) {
|
||||||
|
& $signtool sign `
|
||||||
|
/fd SHA256 `
|
||||||
|
/f $pfxPath `
|
||||||
|
/p $env:CODESIGN_CERT_PASSWORD `
|
||||||
|
/tr http://timestamp.digicert.com `
|
||||||
|
/td SHA256 `
|
||||||
|
$exe
|
||||||
|
}
|
||||||
|
Remove-Item $pfxPath -Force
|
||||||
|
|
||||||
|
- name: Collect artifacts (${{ matrix.platform }})
|
||||||
|
run: |
|
||||||
|
Copy-Item dist/${{ matrix.platform }}/RDPWInst/RDPWInst.exe RDPWInst_${{ matrix.platform }}.exe
|
||||||
|
Copy-Item dist/${{ matrix.platform }}/RDPConf/RDPConf.exe RDPConf_${{ matrix.platform }}.exe
|
||||||
|
Copy-Item dist/${{ matrix.platform }}/RDPCheck/RDPCheck.exe RDPCheck_${{ matrix.platform }}.exe
|
||||||
|
|
||||||
|
- name: Upload ${{ matrix.platform }} artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: csharp-tools-${{ matrix.platform }}
|
||||||
|
path: |
|
||||||
|
RDPWInst_${{ matrix.platform }}.exe
|
||||||
|
RDPConf_${{ matrix.platform }}.exe
|
||||||
|
RDPCheck_${{ matrix.platform }}.exe
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
# NOTE: Full GitHub Releases (including C# tools) are published by publish-ini.yml.
|
||||||
|
# This workflow intentionally stops at the artifact upload step so that partial
|
||||||
|
# releases are never created on the tag. Run publish-ini.yml manually or let it
|
||||||
|
# fire from an INI push to produce the canonical release.
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
name: Build MSI Check
|
||||||
|
|
||||||
|
# Lightweight PR check that validates msi/RDPWInst.wxs compiles cleanly.
|
||||||
|
# We stub the binary inputs (DLL/EXE) with placeholder files so WiX can
|
||||||
|
# validate the package structure without needing a full C++ / C# build.
|
||||||
|
# For the real release build with actual binaries, see build-and-release.yml.
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main, master]
|
||||||
|
paths:
|
||||||
|
- 'msi/**'
|
||||||
|
- '.github/workflows/build-msi-check.yml'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
wix-check:
|
||||||
|
runs-on: windows-2022
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Setup .NET SDK
|
||||||
|
uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: '10.x'
|
||||||
|
|
||||||
|
- name: Cache NuGet packages
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.nuget/packages
|
||||||
|
key: nuget-${{ runner.os }}-${{ hashFiles('**/Directory.Build.props', '**/*.csproj', '**/global.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
nuget-${{ runner.os }}-
|
||||||
|
|
||||||
|
# Create minimal placeholder files so WiX can resolve all File references
|
||||||
|
# in RDPWInst.wxs without requiring an actual C++ or C# build.
|
||||||
|
# WiX v5 harvests file size at build time but does not validate binary content.
|
||||||
|
- name: Create placeholder binary inputs
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$arch = 'x64'
|
||||||
|
# Placeholder DLL and EXEs — WiX only needs the files to exist
|
||||||
|
foreach ($name in @("rdpwrap_$arch.dll", "RDPWInst_$arch.exe", "RDPConf_$arch.exe", "RDPCheck_$arch.exe")) {
|
||||||
|
$dest = "msi\$name"
|
||||||
|
if (-not (Test-Path $dest)) {
|
||||||
|
[System.IO.File]::WriteAllBytes($dest, [byte[]](0x4D, 0x5A)) # MZ stub
|
||||||
|
Write-Host "Created stub: $dest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host "Placeholder files in msi\:"
|
||||||
|
Get-ChildItem msi\ -File | Format-Table Name, Length
|
||||||
|
|
||||||
|
# Build only the x64 MSI — enough to validate WXS structure and WiX rules.
|
||||||
|
# A failure here means a real WiX schema/syntax/reference error that would
|
||||||
|
# also break the release build.
|
||||||
|
- name: Build MSI (x64 WiX check)
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
dotnet build msi/RDPWInst.wixproj `
|
||||||
|
-c Release `
|
||||||
|
/p:Platform=x64 `
|
||||||
|
/p:PackageVersion=0.0.1 `
|
||||||
|
/p:OutputPath="$PWD/msi_check_out/x64"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "WiX build failed — review errors above" }
|
||||||
|
Write-Host "WiX check passed."
|
||||||
|
Get-ChildItem msi_check_out -Recurse -Filter "*.msi" | Format-Table FullName, Length
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
name: Build RDPWrapOffsetFinder
|
||||||
|
|
||||||
|
# Trigger on:
|
||||||
|
# - PRs targeting main/master that touch the OffsetFinder submodule or Zydis
|
||||||
|
# - Version tag pushes (e.g. v1.7.0)
|
||||||
|
# - Manual dispatch
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main, master]
|
||||||
|
paths:
|
||||||
|
- 'src-csharp/RDPOffsetFinder/**'
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ── Build x64 and Win32 release exes ─────────────────────────────────────────
|
||||||
|
build:
|
||||||
|
runs-on: windows-2022
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- platform: x64
|
||||||
|
zydis_cfg: "Release MD DLL"
|
||||||
|
zydis_bin: ReleaseX64
|
||||||
|
finder_plat: x64
|
||||||
|
- platform: Win32
|
||||||
|
zydis_cfg: "Release MD DLL"
|
||||||
|
zydis_bin: ReleaseX86
|
||||||
|
finder_plat: Win32
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository (with submodules)
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Setup MSBuild
|
||||||
|
uses: microsoft/setup-msbuild@v3
|
||||||
|
|
||||||
|
# Build Zydis as a shared DLL so we can ship Zydis.dll alongside the exe
|
||||||
|
- name: Build Zydis (${{ matrix.platform }})
|
||||||
|
working-directory: src-csharp/RDPOffsetFinder
|
||||||
|
run: |
|
||||||
|
msbuild zydis\msvc\zydis\Zydis.vcxproj `
|
||||||
|
/p:Configuration="${{ matrix.zydis_cfg }}" `
|
||||||
|
/p:Platform="${{ matrix.platform }}" `
|
||||||
|
/p:PlatformToolset=v143 `
|
||||||
|
/v:minimal
|
||||||
|
|
||||||
|
# Build the offset finder (links against the Zydis import lib just produced)
|
||||||
|
- name: Build RDPWrapOffsetFinder (${{ matrix.platform }})
|
||||||
|
working-directory: src-csharp/RDPOffsetFinder
|
||||||
|
run: |
|
||||||
|
msbuild RDPWrapOffsetFinder\RDPWrapOffsetFinder.vcxproj `
|
||||||
|
/p:Configuration=Release `
|
||||||
|
/p:Platform="${{ matrix.platform }}" `
|
||||||
|
/p:PlatformToolset=v143 `
|
||||||
|
/v:minimal
|
||||||
|
|
||||||
|
- name: Collect outputs
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$arch = if ("${{ matrix.platform }}" -eq "Win32") { "x86" } else { "x64" }
|
||||||
|
$root = "src-csharp/RDPOffsetFinder"
|
||||||
|
$exe = "$root/RDPWrapOffsetFinder/${{ matrix.finder_plat }}/Release/RDPWrapOffsetFinder.exe"
|
||||||
|
$dll = "$root/zydis/msvc/bin/${{ matrix.zydis_bin }}/Zydis.dll"
|
||||||
|
|
||||||
|
Write-Host "Exe: $(Get-Item $exe | Select-Object -Exp Length) bytes"
|
||||||
|
Write-Host "Dll: $(Get-Item $dll | Select-Object -Exp Length) bytes"
|
||||||
|
|
||||||
|
Copy-Item $exe ".\RDPWrapOffsetFinder_$arch.exe"
|
||||||
|
Copy-Item $dll ".\Zydis_$arch.dll"
|
||||||
|
|
||||||
|
- name: Upload ${{ matrix.platform }} artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: finder-${{ matrix.platform }}
|
||||||
|
path: |
|
||||||
|
RDPWrapOffsetFinder_*.exe
|
||||||
|
Zydis_*.dll
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
# NOTE: Full GitHub Releases (including OffsetFinder binaries) are published by build-and-release.yml.
|
||||||
|
# This workflow stops at the artifact upload step so that partial releases are never
|
||||||
|
# created on tag pushes. Run build-and-release.yml to produce the canonical release.
|
||||||
@ -1,188 +0,0 @@
|
|||||||
name: Publish INI and Offset Finder Tools
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main, master]
|
|
||||||
paths:
|
|
||||||
- 'res/rdpwrap.ini'
|
|
||||||
- 'tools/RDPWrapOffsetFinder/**'
|
|
||||||
- '.github/workflows/publish-ini.yml'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
runs-on: windows-2022
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
# ── Build rdpwrap.dll for both x64 and Win32 ────────────────────────────
|
|
||||||
- name: Setup MSBuild
|
|
||||||
uses: microsoft/setup-msbuild@v3
|
|
||||||
|
|
||||||
- name: Build rdpwrap.dll (x64 and Win32)
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
foreach ($platform in @('x64', 'Win32')) {
|
|
||||||
msbuild src-x86-x64-Fusix/RDPWrap.vcxproj `
|
|
||||||
/p:Configuration=Release `
|
|
||||||
/p:Platform="$platform" `
|
|
||||||
/p:PlatformToolset=v143 `
|
|
||||||
/p:WindowsTargetPlatformVersion=10.0 `
|
|
||||||
/v:minimal
|
|
||||||
}
|
|
||||||
Copy-Item src-x86-x64-Fusix/x64/Release/rdpwrap.dll .\rdpwrap_x64.dll
|
|
||||||
Copy-Item src-x86-x64-Fusix/Win32/Release/rdpwrap.dll .\rdpwrap_x86.dll
|
|
||||||
Write-Host "Built DLLs:"
|
|
||||||
Get-Item .\rdpwrap_x64.dll, .\rdpwrap_x86.dll | Format-Table Name, Length
|
|
||||||
|
|
||||||
# ── Extract the date string used for tagging and the updated field ──────
|
|
||||||
- name: Get release metadata
|
|
||||||
id: meta
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
$date = Get-Date -Format "yyyy.MM.dd.HHmm"
|
|
||||||
$stamp = Get-Date -Format "yyyy-MM-dd HH:mm UTC"
|
|
||||||
|
|
||||||
# Pull the Updated= line out of the INI to surface in release notes
|
|
||||||
$iniContent = Get-Content res/rdpwrap.ini -Raw
|
|
||||||
$iniDate = if ($iniContent -match 'Updated=([^\r\n]+)') { $Matches[1] } else { 'unknown' }
|
|
||||||
|
|
||||||
echo "date=$date" >> $env:GITHUB_OUTPUT
|
|
||||||
echo "stamp=$stamp" >> $env:GITHUB_OUTPUT
|
|
||||||
echo "inidate=$iniDate" >> $env:GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# ── Stage RDPWrapOffsetFinder from committed tools/ ──────────────────────
|
|
||||||
- name: Stage RDPWrapOffsetFinder from tools/
|
|
||||||
id: finder
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
$version = (Get-Content tools/RDPWrapOffsetFinder/VERSION -Raw).Trim()
|
|
||||||
|
|
||||||
Copy-Item tools/RDPWrapOffsetFinder/x64/RDPWrapOffsetFinder.exe .\RDPWrapOffsetFinder_x64.exe
|
|
||||||
Copy-Item tools/RDPWrapOffsetFinder/x64/Zydis.dll .\Zydis_x64.dll
|
|
||||||
Copy-Item tools/RDPWrapOffsetFinder/x86/RDPWrapOffsetFinder.exe .\RDPWrapOffsetFinder_x86.exe
|
|
||||||
Copy-Item tools/RDPWrapOffsetFinder/x86/Zydis.dll .\Zydis_x86.dll
|
|
||||||
|
|
||||||
Write-Host "Staged finder tools (version $version):"
|
|
||||||
Get-Item .\RDPWrapOffsetFinder_*.exe, .\Zydis_*.dll | Format-Table Name, Length
|
|
||||||
|
|
||||||
echo "finder_ver=$version" >> $env:GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# ── Download the latest rdpWrapper GUI app from sergiye ─────────────────
|
|
||||||
- name: Download rdpWrapper (sergiye)
|
|
||||||
id: wrapper
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
$apiUrl = "https://api.github.com/repos/sergiye/rdpWrapper/releases/latest"
|
|
||||||
$release = Invoke-RestMethod -Uri $apiUrl -Headers @{ "User-Agent" = "rdpwrap-ci" }
|
|
||||||
|
|
||||||
foreach ($arch in @('x64', 'x86')) {
|
|
||||||
$asset = $release.assets |
|
|
||||||
Where-Object { $_.name -eq "rdpWrapper_$arch.exe" } |
|
|
||||||
Select-Object -First 1
|
|
||||||
if (-not $asset) {
|
|
||||||
throw "Could not find rdpWrapper_$arch.exe in sergiye release $($release.tag_name)"
|
|
||||||
}
|
|
||||||
Write-Host "Downloading $($asset.browser_download_url)"
|
|
||||||
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile ".\rdpWrapper_$arch.exe"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Wrapper exes:"
|
|
||||||
Get-Item .\rdpWrapper_x64.exe, .\rdpWrapper_x86.exe | Format-Table Name, Length
|
|
||||||
echo "wrapper_ver=$($release.tag_name)" >> $env:GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# ── Validate the INI has required sections ───────────────────────────────
|
|
||||||
- name: Validate INI
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
$ini = Get-Content res/rdpwrap.ini -Raw
|
|
||||||
foreach ($section in @('[Main]', '[SLPolicy]', '[PatchCodes]')) {
|
|
||||||
if ($ini -notmatch [regex]::Escape($section)) {
|
|
||||||
throw "INI validation failed: missing required section $section"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$sectionCount = ([regex]::Matches($ini, '^\[[\d\.]+\]', 'Multiline')).Count
|
|
||||||
Write-Host "INI is valid. Windows-version sections: $sectionCount"
|
|
||||||
|
|
||||||
# ── Assemble the two user-facing distribution bundles ────────────────────
|
|
||||||
- name: Create distribution bundles
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
# RDPWrapper.zip - complete install package
|
|
||||||
$d = ".\bundle_wrapper"
|
|
||||||
New-Item -ItemType Directory -Path $d -Force | Out-Null
|
|
||||||
Copy-Item .\rdpwrap_x64.dll "$d\rdpwrap_x64.dll"
|
|
||||||
Copy-Item .\rdpwrap_x86.dll "$d\rdpwrap_x86.dll"
|
|
||||||
Copy-Item .\rdpWrapper_x64.exe "$d\rdpWrapper_x64.exe"
|
|
||||||
Copy-Item .\rdpWrapper_x86.exe "$d\rdpWrapper_x86.exe"
|
|
||||||
Copy-Item res\rdpwrap.ini "$d\rdpwrap.ini"
|
|
||||||
Copy-Item bin\install.bat "$d\install.bat"
|
|
||||||
Copy-Item bin\uninstall.bat "$d\uninstall.bat"
|
|
||||||
Copy-Item bin\update.bat "$d\update.bat"
|
|
||||||
Compress-Archive -Path "$d\*" -DestinationPath ".\RDPWrapper.zip" -Force
|
|
||||||
Remove-Item $d -Recurse -Force
|
|
||||||
|
|
||||||
# RDPWrapOffsetFinder.zip - offset finder with per-arch subfolders
|
|
||||||
$f = ".\bundle_finder"
|
|
||||||
foreach ($arch in @('x64', 'x86')) {
|
|
||||||
$dir = "$f\$arch"
|
|
||||||
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
|
||||||
Copy-Item ".\RDPWrapOffsetFinder_$arch.exe" "$dir\RDPWrapOffsetFinder.exe"
|
|
||||||
Copy-Item ".\Zydis_$arch.dll" "$dir\Zydis.dll"
|
|
||||||
}
|
|
||||||
Compress-Archive -Path "$f\*" -DestinationPath ".\RDPWrapOffsetFinder.zip" -Force
|
|
||||||
Remove-Item $f -Recurse -Force
|
|
||||||
|
|
||||||
Write-Host "Distribution bundles:"
|
|
||||||
Get-Item .\RDPWrapper.zip, .\RDPWrapOffsetFinder.zip | Format-Table Name, Length
|
|
||||||
|
|
||||||
# ── Create/update a versioned GitHub Release with all assets ─────────────
|
|
||||||
- name: Publish release
|
|
||||||
uses: softprops/action-gh-release@v2
|
|
||||||
with:
|
|
||||||
tag_name: "ini-${{ steps.meta.outputs.date }}"
|
|
||||||
name: "INI Update ${{ steps.meta.outputs.date }}"
|
|
||||||
prerelease: false
|
|
||||||
make_latest: true
|
|
||||||
body: |
|
|
||||||
## RDP Wrapper - Automated Release
|
|
||||||
|
|
||||||
| Field | Value |
|
|
||||||
|---|---|
|
|
||||||
| INI `Updated` date | `${{ steps.meta.outputs.inidate }}` |
|
|
||||||
| Published | ${{ steps.meta.outputs.stamp }} |
|
|
||||||
| rdpWrapper (sergiye) | ${{ steps.wrapper.outputs.wrapper_ver }} |
|
|
||||||
| RDPWrapOffsetFinder (llccd) | ${{ steps.finder.outputs.finder_ver }} |
|
|
||||||
|
|
||||||
### Downloads
|
|
||||||
|
|
||||||
| File | Contents | Use |
|
|
||||||
|---|---|---|
|
|
||||||
| `RDPWrapper.zip` | rdpwrap_x64.dll, rdpwrap_x86.dll, rdpWrapper_x64.exe, rdpWrapper_x86.exe, rdpwrap.ini, install/uninstall/update.bat | Main install package - extract and run `rdpWrapper_x64.exe` |
|
|
||||||
| `RDPWrapOffsetFinder.zip` | x64\RDPWrapOffsetFinder.exe + Zydis.dll, x86\RDPWrapOffsetFinder.exe + Zydis.dll | Generate offsets for an unknown termsrv.dll version manually |
|
|
||||||
|
|
||||||
### Individual files (used by the automated installer)
|
|
||||||
|
|
||||||
| File | Purpose |
|
|
||||||
|---|---|
|
|
||||||
| `rdpwrap.ini` | Offset database fetched automatically by the installer |
|
|
||||||
| `RDPWrapOffsetFinder_x64.exe` / `Zydis_x64.dll` | Loose x64 binaries downloaded by `TryAutoGenerateOffsets` |
|
|
||||||
| `RDPWrapOffsetFinder_x86.exe` / `Zydis_x86.dll` | Loose x86 binaries downloaded by `TryAutoGenerateOffsets` |
|
|
||||||
files: |
|
|
||||||
RDPWrapper.zip
|
|
||||||
RDPWrapOffsetFinder.zip
|
|
||||||
res/rdpwrap.ini
|
|
||||||
rdpwrap_x64.dll
|
|
||||||
rdpwrap_x86.dll
|
|
||||||
RDPWrapOffsetFinder_x64.exe
|
|
||||||
RDPWrapOffsetFinder_x86.exe
|
|
||||||
Zydis_x64.dll
|
|
||||||
Zydis_x86.dll
|
|
||||||
@ -1,120 +0,0 @@
|
|||||||
name: Update RDPWrapOffsetFinder tools
|
|
||||||
|
|
||||||
# Run manually to pull a new version of llccd/RDPWrapOffsetFinder into tools/.
|
|
||||||
# The workflow commits the updated binaries and opens a pull request.
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
tag:
|
|
||||||
description: "llccd release tag to fetch (leave blank for latest)"
|
|
||||||
required: false
|
|
||||||
default: ""
|
|
||||||
|
|
||||||
env:
|
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update:
|
|
||||||
runs-on: windows-2022
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Download and extract RDPWrapOffsetFinder
|
|
||||||
id: fetch
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
$tag = "${{ github.event.inputs.tag }}"
|
|
||||||
if ($tag -eq "") {
|
|
||||||
$apiUrl = "https://api.github.com/repos/llccd/RDPWrapOffsetFinder/releases/latest"
|
|
||||||
} else {
|
|
||||||
$apiUrl = "https://api.github.com/repos/llccd/RDPWrapOffsetFinder/releases/tags/$tag"
|
|
||||||
}
|
|
||||||
|
|
||||||
$release = Invoke-RestMethod -Uri $apiUrl -Headers @{ "User-Agent" = "rdpwrap-ci" }
|
|
||||||
$version = $release.tag_name
|
|
||||||
Write-Host "Fetching version $version"
|
|
||||||
|
|
||||||
$asset = $release.assets | Where-Object { $_.name -like "*.zip" } | Select-Object -First 1
|
|
||||||
if (-not $asset) { throw "No zip asset found in release $version" }
|
|
||||||
|
|
||||||
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile finder.zip -UseBasicParsing
|
|
||||||
Expand-Archive -Path finder.zip -DestinationPath .\finder -Force
|
|
||||||
|
|
||||||
Write-Host "Zip contents:"
|
|
||||||
Get-ChildItem -Recurse .\finder | Select-Object FullName, Length
|
|
||||||
|
|
||||||
# The llccd zip uses "64bit/" and "32bit/" subfolder names.
|
|
||||||
# We pick the symbol-enabled exe (not the _nosymbol variant) from each arch folder.
|
|
||||||
function Get-Bin($filter, $archHint) {
|
|
||||||
$hits = Get-ChildItem -Recurse .\finder -Filter $filter |
|
|
||||||
Where-Object { $_.FullName -match $archHint } |
|
|
||||||
Where-Object { $_.Name -notmatch "nosymbol" }
|
|
||||||
return ($hits | Select-Object -First 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
$x64exe = Get-Bin "RDPWrapOffsetFinder*.exe" "64bit"
|
|
||||||
$x64dll = Get-Bin "Zydis*.dll" "64bit"
|
|
||||||
$x86exe = Get-Bin "RDPWrapOffsetFinder*.exe" "32bit"
|
|
||||||
$x86dll = Get-Bin "Zydis*.dll" "32bit"
|
|
||||||
|
|
||||||
# Fall back to x64 if the release only ships one arch
|
|
||||||
if (-not $x86exe) { $x86exe = $x64exe }
|
|
||||||
if (-not $x86dll) { $x86dll = $x64dll }
|
|
||||||
if (-not $x64exe) { throw "Could not locate x64 exe" }
|
|
||||||
if (-not $x64dll) { throw "Could not locate x64 Zydis.dll" }
|
|
||||||
|
|
||||||
New-Item -ItemType Directory -Path tools\RDPWrapOffsetFinder\x64 -Force | Out-Null
|
|
||||||
New-Item -ItemType Directory -Path tools\RDPWrapOffsetFinder\x86 -Force | Out-Null
|
|
||||||
|
|
||||||
Copy-Item $x64exe.FullName tools\RDPWrapOffsetFinder\x64\RDPWrapOffsetFinder.exe -Force
|
|
||||||
Copy-Item $x64dll.FullName tools\RDPWrapOffsetFinder\x64\Zydis.dll -Force
|
|
||||||
Copy-Item $x86exe.FullName tools\RDPWrapOffsetFinder\x86\RDPWrapOffsetFinder.exe -Force
|
|
||||||
Copy-Item $x86dll.FullName tools\RDPWrapOffsetFinder\x86\Zydis.dll -Force
|
|
||||||
Set-Content -Path tools\RDPWrapOffsetFinder\VERSION -Value $version
|
|
||||||
|
|
||||||
Write-Host "Staged tools:"
|
|
||||||
Get-ChildItem -Recurse tools\RDPWrapOffsetFinder | Select-Object FullName, Length
|
|
||||||
|
|
||||||
echo "version=$version" >> $env:GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Commit and push to a new branch
|
|
||||||
id: push
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
$version = "${{ steps.fetch.outputs.version }}"
|
|
||||||
$branch = "chore/update-finder-tools-$version"
|
|
||||||
|
|
||||||
git config user.name "github-actions[bot]"
|
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
git checkout -b $branch
|
|
||||||
git add tools/RDPWrapOffsetFinder
|
|
||||||
if ((git diff --cached --name-only) -eq "") {
|
|
||||||
Write-Host "No changes - tools already up to date"
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
git commit -m "chore: update RDPWrapOffsetFinder tools to $version"
|
|
||||||
git push origin $branch
|
|
||||||
echo "branch=$branch" >> $env:GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Create pull request
|
|
||||||
if: steps.push.outputs.branch != ''
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
$version = "${{ steps.fetch.outputs.version }}"
|
|
||||||
$branch = "${{ steps.push.outputs.branch }}"
|
|
||||||
gh pr create `
|
|
||||||
--title "chore: update RDPWrapOffsetFinder tools to $version" `
|
|
||||||
--body "Automated update of the committed RDPWrapOffsetFinder binaries in ``tools/RDPWrapOffsetFinder/`` to upstream release $version.
|
|
||||||
|
|
||||||
Triggered manually via the **Update RDPWrapOffsetFinder tools** workflow.
|
|
||||||
Merge to include the updated binaries in the next INI release." `
|
|
||||||
--base master `
|
|
||||||
--head $branch
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
[submodule "src-csharp/RDPOffsetFinder"]
|
||||||
|
path = src-csharp/RDPOffsetFinder
|
||||||
|
url = https://github.com/llccd/RDPWrapOffsetFinder
|
||||||
|
shallow = true
|
||||||
@ -1,14 +0,0 @@
|
|||||||
@echo off
|
|
||||||
if not exist "%~dp0RDPWInst.exe" goto :error
|
|
||||||
"%~dp0RDPWInst" -i -o
|
|
||||||
echo ______________________________________________________________
|
|
||||||
echo.
|
|
||||||
echo You can check RDP functionality with RDPCheck program.
|
|
||||||
echo Also you can configure advanced settings with RDPConf program.
|
|
||||||
echo.
|
|
||||||
goto :anykey
|
|
||||||
:error
|
|
||||||
echo [-] Installer executable not found.
|
|
||||||
echo Please extract all files from the downloaded package or check your anti-virus.
|
|
||||||
:anykey
|
|
||||||
pause
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
@echo off
|
|
||||||
if not exist "%~dp0RDPWInst.exe" goto :error
|
|
||||||
"%~dp0RDPWInst" -u
|
|
||||||
echo.
|
|
||||||
goto :anykey
|
|
||||||
:error
|
|
||||||
echo [-] Installer executable not found.
|
|
||||||
echo Please extract all files from the downloaded package or check your anti-virus.
|
|
||||||
:anykey
|
|
||||||
pause
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
@echo off
|
|
||||||
if not exist "%~dp0RDPWInst.exe" goto :error
|
|
||||||
"%~dp0RDPWInst" -w
|
|
||||||
echo.
|
|
||||||
goto :anykey
|
|
||||||
:error
|
|
||||||
echo [-] Installer executable not found.
|
|
||||||
echo Please extract all files from the downloaded package or check your anti-virus.
|
|
||||||
:anykey
|
|
||||||
pause
|
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
# Code Signing Guide
|
||||||
|
|
||||||
|
Both `build-and-release.yml` and `build-csharp.yml` include a code-signing step that is already wired up and waiting. The step fires automatically as soon as the two secrets below are present in the repository — no workflow edits are needed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What gets signed
|
||||||
|
|
||||||
|
All six framework-dependent executables and all six self-contained executables produced per release:
|
||||||
|
|
||||||
|
| File | Contents |
|
||||||
|
|---|---|
|
||||||
|
| `RDPWInst_x64.exe`, `RDPWInst_x86.exe`, `RDPWInst_arm64.exe` | CLI installer |
|
||||||
|
| `RDPConf_x64.exe`, `RDPConf_x86.exe`, `RDPConf_arm64.exe` | GUI configuration tool |
|
||||||
|
| `RDPCheck_x64.exe`, `RDPCheck_x86.exe`, `RDPCheck_arm64.exe` | GUI connection tester |
|
||||||
|
| `*_sc.exe` variants | Self-contained copies of the above |
|
||||||
|
|
||||||
|
The signing step runs on every `build-and-release.yml` and `build-csharp.yml` triggered build (tag push and manual dispatch), and is **skipped silently** when the secrets are absent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Obtaining a code-signing certificate
|
||||||
|
|
||||||
|
### Option A — Commercial certificate (recommended for public distribution)
|
||||||
|
|
||||||
|
Purchase an **EV (Extended Validation) Code Signing Certificate** from a trusted CA:
|
||||||
|
|
||||||
|
- [DigiCert Code Signing](https://www.digicert.com/signing/code-signing-certificates)
|
||||||
|
- [Sectigo (Comodo)](https://sectigo.com/ssl-certificates-tls/code-signing)
|
||||||
|
- [GlobalSign](https://www.globalsign.com/en/code-signing-certificate/)
|
||||||
|
|
||||||
|
> **Windows SmartScreen** initially blocks unsigned or low-reputation executables. A commercial EV certificate builds reputation immediately; a standard OV certificate requires time to accumulate reputation through user downloads. Self-signed certificates (Option B) will always trigger SmartScreen warnings.
|
||||||
|
|
||||||
|
### Option B — Self-signed certificate (testing / internal use only)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Run in an elevated PowerShell session
|
||||||
|
$cert = New-SelfSignedCertificate `
|
||||||
|
-Type CodeSigningCert `
|
||||||
|
-Subject "CN=RDP Wrapper" `
|
||||||
|
-KeySpec Signature `
|
||||||
|
-KeyAlgorithm RSA `
|
||||||
|
-KeyLength 4096 `
|
||||||
|
-HashAlgorithm SHA256 `
|
||||||
|
-CertStoreLocation "Cert:\CurrentUser\My" `
|
||||||
|
-NotAfter (Get-Date).AddYears(3)
|
||||||
|
|
||||||
|
# Export to PFX (set a strong password)
|
||||||
|
$pw = Read-Host "PFX password" -AsSecureString
|
||||||
|
Export-PfxCertificate -Cert $cert -FilePath "rdpwrap-codesign.pfx" -Password $pw
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Preparing the PFX for GitHub Actions
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Base64-encode the PFX so it can be stored as a secret
|
||||||
|
$b64 = [Convert]::ToBase64String([IO.File]::ReadAllBytes(".\rdpwrap-codesign.pfx"))
|
||||||
|
$b64 | Set-Clipboard
|
||||||
|
Write-Host "Base64 PFX copied to clipboard"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adding the secrets to GitHub
|
||||||
|
|
||||||
|
Navigate to: **Settings → Secrets and variables → Actions → New repository secret**
|
||||||
|
|
||||||
|
| Secret name | Value |
|
||||||
|
|---|---|
|
||||||
|
| `CODESIGN_CERT_BASE64` | Paste the base64-encoded PFX string |
|
||||||
|
| `CODESIGN_CERT_PASSWORD` | The password chosen when exporting the PFX |
|
||||||
|
|
||||||
|
> ⚠️ **Security:** Never commit the `.pfx` file or raw base64 string to the repository. Revoke and reissue the certificate if it is accidentally exposed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verifying a signed executable
|
||||||
|
|
||||||
|
After a signed release is published:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Check signature status
|
||||||
|
Get-AuthenticodeSignature .\RDPWInst_x64.exe | Format-List
|
||||||
|
|
||||||
|
# Expected output (commercial cert):
|
||||||
|
# Status : Valid
|
||||||
|
# SignerCertificate : [CN=Your Name, O=Your Org, ...]
|
||||||
|
# TimeStamperCertificate : [CN=DigiCert Timestamp 2023, ...]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Renewing / rotating the certificate
|
||||||
|
|
||||||
|
1. Export the new PFX and base64-encode it (see above).
|
||||||
|
2. Update both `CODESIGN_CERT_BASE64` and `CODESIGN_CERT_PASSWORD` via **Settings → Secrets**.
|
||||||
|
3. No workflow changes are needed — the `Sign C# executables` step always reads from secrets at runtime.
|
||||||
|
4. Remove the old PFX from your local machine and revoke the old certificate with the CA if it has not expired.
|
||||||
@ -0,0 +1,101 @@
|
|||||||
|
# Submodule Update Guide
|
||||||
|
|
||||||
|
This repository includes one git submodule (with nested sub-submodules):
|
||||||
|
|
||||||
|
| Submodule | Upstream | Current tag |
|
||||||
|
|---|---|---|
|
||||||
|
| `src-csharp/RDPOffsetFinder` | [llccd/RDPWrapOffsetFinder](https://github.com/llccd/RDPWrapOffsetFinder) | `v0.9` |
|
||||||
|
| `src-csharp/RDPOffsetFinder/zydis` | [zyantific/zydis](https://github.com/zyantific/zydis) | (pinned by upstream) |
|
||||||
|
| `src-csharp/RDPOffsetFinder/zydis/dependencies/zycore` | [zyantific/zycore-c](https://github.com/zyantific/zycore-c) | (pinned by upstream) |
|
||||||
|
|
||||||
|
The nested `zydis` and `zycore` submodule commits are controlled by llccd's repository — update only the outer submodule and the inner ones follow automatically.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cloning with submodules
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Full clone (recommended for builds)
|
||||||
|
git clone --recurse-submodules https://github.com/sjackson0109/rdpwrap
|
||||||
|
|
||||||
|
# If you already cloned without --recurse-submodules:
|
||||||
|
git submodule update --init --recursive
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checking current submodule version
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git submodule status --recursive
|
||||||
|
# Expected output (one line per submodule):
|
||||||
|
# 68da37acab6593c329776644944f55695a131731 src-csharp/RDPOffsetFinder (v0.9)
|
||||||
|
# 5a68f639e4f01604cc7bfc8d313f583a8137e3d3 src-csharp/RDPOffsetFinder/zydis (...)
|
||||||
|
# fb69402566a15a719e5df7a64a3db95105590b7e src-csharp/RDPOffsetFinder/zydis/dependencies/zycore (...)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Updating to a new upstream release
|
||||||
|
|
||||||
|
> **Do this only when a new tagged release of [llccd/RDPWrapOffsetFinder](https://github.com/llccd/RDPWrapOffsetFinder) is published** and you have verified that the new version produces correct INI sections for a known Windows build.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 1. Fetch all tags from upstream
|
||||||
|
cd src-csharp/RDPOffsetFinder
|
||||||
|
git fetch --tags origin
|
||||||
|
|
||||||
|
# 2. List available tags to find the new release
|
||||||
|
git tag --list --sort=-version:refname | Select-Object -First 10
|
||||||
|
|
||||||
|
# 3. Check out the desired tag
|
||||||
|
git checkout <new-tag> # e.g. v1.0
|
||||||
|
|
||||||
|
# 4. Update nested submodules to match the new tag's references
|
||||||
|
git submodule update --init --recursive
|
||||||
|
|
||||||
|
# 5. Return to repo root and record the new pointer
|
||||||
|
cd ../..
|
||||||
|
git add src-csharp/RDPOffsetFinder
|
||||||
|
git commit -m "chore: update RDPOffsetFinder submodule to <new-tag>"
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verifying the updated submodule builds
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# From repo root — builds x64 and x86 offset finder binaries
|
||||||
|
cd src-csharp/RDPOffsetFinder
|
||||||
|
|
||||||
|
cmake -B build-x64 -A x64 .
|
||||||
|
cmake --build build-x64 --config Release
|
||||||
|
|
||||||
|
cmake -B build-x86 -A Win32 .
|
||||||
|
cmake --build build-x86 --config Release
|
||||||
|
```
|
||||||
|
|
||||||
|
Or trigger the `build-offsetfinder.yml` workflow on your push branch to let CI validate it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
If the update causes build failures, revert the submodule pointer:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git revert HEAD # creates a revert commit
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
Or manually:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd src-csharp/RDPOffsetFinder
|
||||||
|
git checkout v0.9 # previous known-good tag
|
||||||
|
cd ../..
|
||||||
|
git add src-csharp/RDPOffsetFinder
|
||||||
|
git commit -m "chore: rollback RDPOffsetFinder submodule to v0.9"
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,51 @@
|
|||||||
|
# Screenshots — `docs/images/`
|
||||||
|
|
||||||
|
This directory holds in-repo screenshot assets referenced by the project README.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files present
|
||||||
|
|
||||||
|
The following PNG screenshots are committed and referenced by the README:
|
||||||
|
|
||||||
|
| Filename | Contents |
|
||||||
|
|---|---|
|
||||||
|
| `RDPWrapperConfig.png` | RDPConf.exe configuration window |
|
||||||
|
| `RDPWrapperCheck.png` | RDPCheck.exe showing green "Supported" status |
|
||||||
|
| `RDPWrapperCheckWarning.png` | RDPCheck.exe showing "Warning" / not-yet-supported state |
|
||||||
|
| `RDPWrapperMSI1.png` | MSI installer welcome / licence screen |
|
||||||
|
| `RDPWrapperMSI2.png` | MSI installer completion screen |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files that would still be welcome
|
||||||
|
|
||||||
|
The following screenshots would improve documentation coverage but are not blocking any CI step:
|
||||||
|
|
||||||
|
| Filename | What to capture |
|
||||||
|
|---|---|
|
||||||
|
| `RDPWrapperConfig-advanced.png` | RDPConf.exe Advanced / License tab — SL policy and licensing status fields |
|
||||||
|
| `rdpwrapper-gui.png` | sergiye/rdpWrapper GUI — main window with connection status |
|
||||||
|
| `install-success.png` | Terminal output of `RDPWInst_x64.exe -i -o` completing successfully |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Capture tips
|
||||||
|
|
||||||
|
- Use **1:1 scaling** (100 % DPI) so button labels are legible at small display size.
|
||||||
|
- Crop to exclude desktop wallpaper — show only the tool window, with a 4 px neutral border all round.
|
||||||
|
- Use PNG format (lossless); aim for < 200 KB each (resize if needed — 900 px wide is more than enough).
|
||||||
|
- Redact any username, hostname, or licence strings visible in the window before committing.
|
||||||
|
- Filename convention: `tool-state.png` — all lowercase, hyphen-delimited.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adding a new screenshot
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# After copying the PNG here:
|
||||||
|
git add docs/images/<filename>.png
|
||||||
|
git commit -m "docs: add <description> screenshot"
|
||||||
|
```
|
||||||
|
|
||||||
|
The README references each file with a relative path, so the image will render automatically on GitHub once committed.
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="WixToolset.Sdk/5.0.2">
|
||||||
|
<!--
|
||||||
|
RDP Wrapper MSI project — WiX v5 (dotnet build)
|
||||||
|
|
||||||
|
Build once per architecture:
|
||||||
|
dotnet build msi/RDPWInst.wixproj -c Release /p:Platform=x64 /p:PackageVersion=yy.M.d
|
||||||
|
dotnet build msi/RDPWInst.wixproj -c Release /p:Platform=x86 /p:PackageVersion=yy.M.d
|
||||||
|
dotnet build msi/RDPWInst.wixproj -c Release /p:Platform=arm64 /p:PackageVersion=yy.M.d
|
||||||
|
|
||||||
|
Each build expects arch-specific inputs alongside this .wixproj:
|
||||||
|
RDPWInst_<arch>.exe RDPConf_<arch>.exe RDPCheck_<arch>.exe
|
||||||
|
rdpwrap_<arch>.dll rdpwrap.ini
|
||||||
|
-->
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- OutputName includes arch so all three MSIs can coexist in the same output dir -->
|
||||||
|
<OutputName>RDPWrapper-$(Platform)</OutputName>
|
||||||
|
<!-- Version is overridden on the CLI: /p:PackageVersion=yy.M.d (e.g. 26.4.1) -->
|
||||||
|
<PackageVersion>26.3.31</PackageVersion>
|
||||||
|
<!-- Expose PackageVersion to the WiX preprocessor as $(var.PackageVersion) -->
|
||||||
|
<DefineConstants>PackageVersion=$(PackageVersion)</DefineConstants>
|
||||||
|
<!-- Suppress ICE warnings that do not apply to this package type -->
|
||||||
|
<SuppressIces>ICE61</SuppressIces>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@ -0,0 +1,127 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
RDP Wrapper — MSI installer definition (WiX v5 / schema v4)
|
||||||
|
Parameterised by MSBuild Platform: build once per arch.
|
||||||
|
|
||||||
|
Build via:
|
||||||
|
dotnet build msi/RDPWInst.wixproj -c Release /p:Platform=x64 /p:PackageVersion=yy.M.d
|
||||||
|
dotnet build msi/RDPWInst.wixproj -c Release /p:Platform=x86 /p:PackageVersion=yy.M.d
|
||||||
|
dotnet build msi/RDPWInst.wixproj -c Release /p:Platform=arm64 /p:PackageVersion=yy.M.d
|
||||||
|
|
||||||
|
Required inputs alongside this file (produced by CI / build-local.ps1):
|
||||||
|
RDPWInst_<arch>.exe RDPConf_<arch>.exe RDPCheck_<arch>.exe
|
||||||
|
rdpwrap_<arch>.dll
|
||||||
|
rdpwrap.ini (shared — same file for all arches)
|
||||||
|
-->
|
||||||
|
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
$(var.Platform) is automatically set by WiX from the MSBuild Platform property.
|
||||||
|
x86 installs to Program Files (x86); x64/arm64 install to Program Files.
|
||||||
|
-->
|
||||||
|
<?if $(var.Platform) = "x64" ?>
|
||||||
|
<?define Arch = "x64" ?>
|
||||||
|
<?define UpgradeCode = "6623f60c-e84f-41e7-a55b-f42116500064" ?>
|
||||||
|
<?define ProgramFilesDir = "ProgramFiles6432Folder" ?>
|
||||||
|
<?elseif $(var.Platform) = "x86" ?>
|
||||||
|
<?define Arch = "x86" ?>
|
||||||
|
<?define UpgradeCode = "6623f60c-e84f-41e7-a55b-f42116500032" ?>
|
||||||
|
<?define ProgramFilesDir = "ProgramFilesFolder" ?>
|
||||||
|
<?else ?>
|
||||||
|
<?define Arch = "arm64" ?>
|
||||||
|
<?define UpgradeCode = "6623f60c-e84f-41e7-a55b-f4211650aa64" ?>
|
||||||
|
<?define ProgramFilesDir = "ProgramFiles6432Folder" ?>
|
||||||
|
<?endif ?>
|
||||||
|
|
||||||
|
<Package
|
||||||
|
Name="RDP Wrapper Library ($(var.Arch))"
|
||||||
|
Version="$(var.PackageVersion)"
|
||||||
|
Manufacturer="Simon Jackson (@sjackson0109)"
|
||||||
|
UpgradeCode="$(var.UpgradeCode)"
|
||||||
|
Scope="perMachine"
|
||||||
|
InstallerVersion="500"
|
||||||
|
Compressed="yes">
|
||||||
|
|
||||||
|
<!-- Allow upgrades, downgrades, and same-version reinstalls without blocking.
|
||||||
|
Schedule=afterInstallInitialize ensures the old product is fully removed
|
||||||
|
before new files are written, preventing component-ownership conflicts. -->
|
||||||
|
<MajorUpgrade
|
||||||
|
Schedule="afterInstallInitialize"
|
||||||
|
AllowDowngrades="yes"
|
||||||
|
IgnoreRemoveFailure="yes" />
|
||||||
|
|
||||||
|
<MediaTemplate EmbedCab="yes" />
|
||||||
|
|
||||||
|
<!-- ── Installation directory ─────────────────────────────────────────── -->
|
||||||
|
<StandardDirectory Id="$(var.ProgramFilesDir)">
|
||||||
|
<Directory Id="INSTALLFOLDER" Name="RDP Wrapper" />
|
||||||
|
</StandardDirectory>
|
||||||
|
|
||||||
|
<!-- ── Feature ────────────────────────────────────────────────────────── -->
|
||||||
|
<Feature Id="ProductFeature" Title="RDP Wrapper" Level="1">
|
||||||
|
<ComponentGroupRef Id="ProductComponents" />
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
<!-- ── Custom actions: run arch-native RDPWInst ──────────────────────── -->
|
||||||
|
<!--
|
||||||
|
-f (force): silently uninstalls any existing installation (bat- or
|
||||||
|
MSI-managed) before re-installing. Prevents the "already installed"
|
||||||
|
error when upgrading from a previous MSI or a manual install.
|
||||||
|
-->
|
||||||
|
<CustomAction
|
||||||
|
Id="InstallWrap"
|
||||||
|
FileRef="RDPWInstExe"
|
||||||
|
ExeCommand="-i -o -f"
|
||||||
|
Execute="deferred"
|
||||||
|
Impersonate="no"
|
||||||
|
Return="check" />
|
||||||
|
|
||||||
|
<CustomAction
|
||||||
|
Id="UninstallWrap"
|
||||||
|
FileRef="RDPWInstExe"
|
||||||
|
ExeCommand="-u"
|
||||||
|
Execute="deferred"
|
||||||
|
Impersonate="no"
|
||||||
|
Return="check" />
|
||||||
|
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<!-- Run install wrapper after files land; skip only when THIS product is
|
||||||
|
being removed as the OLD side of an upgrade (UPGRADINGPRODUCTCODE set). -->
|
||||||
|
<Custom Action="InstallWrap" After="InstallFiles" Condition="NOT UPGRADINGPRODUCTCODE" />
|
||||||
|
<Custom Action="UninstallWrap" Before="RemoveFiles" Condition="REMOVE AND NOT UPGRADINGPRODUCTCODE" />
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
</Package>
|
||||||
|
|
||||||
|
<!-- ── Component group ─────────────────────────────────────────────────── -->
|
||||||
|
<Fragment>
|
||||||
|
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
|
||||||
|
|
||||||
|
<!-- RDPWInst (installer / uninstaller tool) -->
|
||||||
|
<Component Id="RDPWInstExe_Comp" Guid="*">
|
||||||
|
<File Id="RDPWInstExe" Source="RDPWInst_$(var.Arch).exe" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
|
||||||
|
<!-- RDPConf (configuration GUI) -->
|
||||||
|
<Component Id="RDPConfExe_Comp" Guid="*">
|
||||||
|
<File Id="RDPConfExe" Source="RDPConf_$(var.Arch).exe" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
|
||||||
|
<!-- RDPCheck (RDP loopback tester) -->
|
||||||
|
<Component Id="RDPCheckExe_Comp" Guid="*">
|
||||||
|
<File Id="RDPCheckExe" Source="RDPCheck_$(var.Arch).exe" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
|
||||||
|
<!-- rdpwrap.dll (core DLL) -->
|
||||||
|
<Component Id="RdpwrapDll_Comp" Guid="*">
|
||||||
|
<File Source="rdpwrap_$(var.Arch).dll" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
|
||||||
|
<!-- rdpwrap.ini (offset database — same file for all arches) -->
|
||||||
|
<Component Id="RdpwrapIni_Comp" Guid="*">
|
||||||
|
<File Source="rdpwrap.ini" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
|
||||||
|
</ComponentGroup>
|
||||||
|
</Fragment>
|
||||||
|
|
||||||
|
</Wix>
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"msbuild-sdks": {
|
||||||
|
"WixToolset.Sdk": "5.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,49 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
|
||||||
<?define ProductName="RDP Wrapper Library" ?>
|
|
||||||
<?define ProductVersion="1.6.2" ?>
|
|
||||||
<?define ProductCode="37ea5771-3352-4a52-9fac-9297331daebd"?>
|
|
||||||
<?define UpgradeCode="6623f60c-e84f-41e7-a55b-f421165deeb5"?>
|
|
||||||
<?define Manufacturer="Stas'M Corp. and contributors"?>
|
|
||||||
|
|
||||||
<Product Id="$(var.ProductCode)" Name="$(var.ProductName)" Language="1033" Version="$(var.ProductVersion)" Manufacturer="$(var.Manufacturer)" UpgradeCode="$(var.UpgradeCode)">
|
|
||||||
<Package InstallerVersion="200" Compressed="yes"/>
|
|
||||||
<Property Id="MSIFASTINSTALL" Value="1" />
|
|
||||||
<Property Id="DISABLEROLLBACK" Value="1" />
|
|
||||||
<MajorUpgrade AllowDowngrades="yes"/>
|
|
||||||
|
|
||||||
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes"/>
|
|
||||||
|
|
||||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
|
||||||
<Directory Id="CommonAppDataFolder">
|
|
||||||
<Directory Id="PACKAGECACHE" Name="Package Cache">
|
|
||||||
<Directory Id="INSTALLLOCATION" Name="{$(var.ProductCode)}">
|
|
||||||
<Component Id="RDPWrap" Guid="affd77d1-b35c-46f3-a97f-1686dc57b8b8">
|
|
||||||
<File Id='RDPWInst' DiskId='1' Source='RDPWInst.exe'/>
|
|
||||||
</Component>
|
|
||||||
</Directory>
|
|
||||||
</Directory>
|
|
||||||
</Directory>
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
<Feature Id="RDPWrapInstall" Title="RDPWrapSetup" Level="1">
|
|
||||||
<ComponentRef Id="RDPWrap" />
|
|
||||||
</Feature>
|
|
||||||
|
|
||||||
<CustomAction Id='InstallAction' FileKey='RDPWInst' ExeCommand='-i -o' Execute='immediate' Return='check'/>
|
|
||||||
<CustomAction Id='UninstallAction' FileKey='RDPWInst' ExeCommand='-u' Execute='immediate' Return='check'/>
|
|
||||||
<CustomAction Id='UpdateAction' FileKey='RDPWInst' ExeCommand='-w' Execute='immediate' Return='check'/>
|
|
||||||
<!-- <CustomAction Id='ChangeAction' Directory='ProgramFilesFolder' ExeCommand='RDP Wrapper\RDPConf' Execute='immediate' Return='check'/>
|
|
||||||
<CustomAction Id='RepairAction' Directory='ProgramFilesFolder' ExeCommand='RDP Wrapper\RDPCheck' Execute='immediate' Return='check'/> -->
|
|
||||||
|
|
||||||
<InstallExecuteSequence>
|
|
||||||
<Custom Action='InstallAction' After='InstallFinalize'>NOT Installed AND NOT WIX_UPGRADE_DETECTED</Custom>
|
|
||||||
<Custom Action='UninstallAction' Before="RemoveFiles">REMOVE AND NOT UPGRADINGPRODUCTCODE</Custom>
|
|
||||||
<Custom Action='UpdateAction' Before="RemoveFiles">UPGRADINGPRODUCTCODE</Custom>
|
|
||||||
<!-- <Custom Action='ChangeAction' After='InstallFinalize'>Installed AND NOT REINSTALL AND NOT UPGRADINGPRODUCTCODE AND NOT REMOVE</Custom>
|
|
||||||
<Custom Action='RepairAction' After='InstallFinalize'>REINSTALL</Custom> -->
|
|
||||||
</InstallExecuteSequence>
|
|
||||||
|
|
||||||
</Product>
|
|
||||||
</Wix>
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
@echo off
|
|
||||||
"%ProgramFiles%\WiX Toolset v3.11\bin\candle" RDPWInst.wxs
|
|
||||||
"%ProgramFiles%\WiX Toolset v3.11\bin\light" RDPWInst.wixobj
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
@echo off
|
|
||||||
echo [FILENAMES]> clearres.txt
|
|
||||||
echo Exe=%1>> clearres.txt
|
|
||||||
echo SaveAs=%1>> clearres.txt
|
|
||||||
echo Log=>> clearres.txt
|
|
||||||
echo.>> clearres.txt
|
|
||||||
echo [COMMANDS]>> clearres.txt
|
|
||||||
echo -delete RCDATA,CHARTABLE,>> clearres.txt
|
|
||||||
echo -delete RCDATA,DVCLAL,>> clearres.txt
|
|
||||||
echo -delete RCDATA,PACKAGEINFO,>> clearres.txt
|
|
||||||
echo -delete CURSORGROUP,32761,>> clearres.txt
|
|
||||||
echo -delete CURSORGROUP,32762,>> clearres.txt
|
|
||||||
echo -delete CURSORGROUP,32763,>> clearres.txt
|
|
||||||
echo -delete CURSORGROUP,32764,>> clearres.txt
|
|
||||||
echo -delete CURSORGROUP,32765,>> clearres.txt
|
|
||||||
echo -delete CURSORGROUP,32766,>> clearres.txt
|
|
||||||
echo -delete CURSORGROUP,32767,>> clearres.txt
|
|
||||||
"C:\Program Files\Resource Hacker\ResHacker.exe" -script clearres.txt
|
|
||||||
del clearres.txt
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
@echo off
|
|
||||||
setlocal EnableDelayedExpansion
|
|
||||||
echo RDP Wrapper Library Installer v1.0
|
|
||||||
echo Copyright (C) Stas'M Corp. 2013
|
|
||||||
echo.
|
|
||||||
|
|
||||||
set PROCESSOR_ARCHITECTURE | find "x86" > nul
|
|
||||||
if !errorlevel!==0 (
|
|
||||||
goto WOW64CHK
|
|
||||||
) else (
|
|
||||||
goto UNSUPPORTED
|
|
||||||
)
|
|
||||||
|
|
||||||
:WOW64CHK
|
|
||||||
echo [*] Check if running WOW64 subsystem...
|
|
||||||
set PROCESSOR_ARCHITEW6432 > nul
|
|
||||||
if !errorlevel!==0 (
|
|
||||||
goto UNSUPPORTED
|
|
||||||
) else (
|
|
||||||
goto SUPPORTED
|
|
||||||
)
|
|
||||||
|
|
||||||
:SUPPORTED
|
|
||||||
echo [+] Processor architecture is Intel x86 [supported]
|
|
||||||
goto INSTALL
|
|
||||||
|
|
||||||
:UNSUPPORTED
|
|
||||||
echo [-] Unsupported processor architecture
|
|
||||||
goto END
|
|
||||||
|
|
||||||
:INSTALL
|
|
||||||
echo [*] Installing...
|
|
||||||
if not exist rdpwrap.dll (
|
|
||||||
echo [-] Error: rdpwrap.dll file not found
|
|
||||||
goto END
|
|
||||||
)
|
|
||||||
echo [*] Copying file to Program Files...
|
|
||||||
md "%ProgramFiles%\RDP Wrapper"
|
|
||||||
xcopy /y rdpwrap.dll "%ProgramFiles%\RDP Wrapper\"
|
|
||||||
if not !errorlevel!==0 (
|
|
||||||
echo [-] Failed to copy rdpwrap.dll to Program Files folder
|
|
||||||
goto END
|
|
||||||
)
|
|
||||||
echo [*] Modifying registry...
|
|
||||||
reg add "HKLM\SYSTEM\CurrentControlSet\Services\TermService\Parameters" /v ServiceDll /t REG_EXPAND_SZ /d "%ProgramFiles%\RDP Wrapper\rdpwrap.dll" /f
|
|
||||||
if not !errorlevel!==0 (
|
|
||||||
echo [-] Failed to modify registry
|
|
||||||
goto END
|
|
||||||
)
|
|
||||||
echo [*] Setting firewall configuration...
|
|
||||||
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f
|
|
||||||
netsh advfirewall firewall add rule name="Remote Desktop" dir=in protocol=tcp localport=3389 profile=any action=allow
|
|
||||||
netsh advfirewall firewall add rule name="Remote Desktop" dir=in protocol=udp localport=3389 profile=any action=allow
|
|
||||||
echo [*] Looking for TermService PID...
|
|
||||||
tasklist /SVC /FI "SERVICES eq TermService" | find "PID" /V
|
|
||||||
echo.
|
|
||||||
if !errorlevel!==0 (
|
|
||||||
goto DONE
|
|
||||||
) else (
|
|
||||||
goto SVCSTART
|
|
||||||
)
|
|
||||||
|
|
||||||
:SVCSTART
|
|
||||||
echo [*] TermService is stopped. Starting it...
|
|
||||||
sc config TermService start= auto | find "1060" > nul
|
|
||||||
if !errorlevel!==0 (
|
|
||||||
echo [-] TermService is not installed. You need to install it manually.
|
|
||||||
goto END
|
|
||||||
) else (
|
|
||||||
net start TermService
|
|
||||||
goto DONE
|
|
||||||
)
|
|
||||||
|
|
||||||
:DONE
|
|
||||||
echo [+] Installation complete!
|
|
||||||
echo Now reboot or restart service.
|
|
||||||
echo.
|
|
||||||
echo To reboot computer type:
|
|
||||||
echo shutdown /r
|
|
||||||
echo.
|
|
||||||
echo To restart TermService type:
|
|
||||||
echo taskkill /f /pid 1234 ^(replace 1234 with real PID which is shown above^)
|
|
||||||
echo net start TermService
|
|
||||||
echo.
|
|
||||||
echo If second method is used, and there are another services sharing svchost.exe,
|
|
||||||
echo you must start it too:
|
|
||||||
echo net start Service1
|
|
||||||
echo net start Service2
|
|
||||||
echo etc.
|
|
||||||
goto END
|
|
||||||
|
|
||||||
:END
|
|
||||||
@ -0,0 +1,160 @@
|
|||||||
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
|
//
|
||||||
|
// Hosts the MsRdpClient2 ActiveX control, connects to 127.0.0.2, and
|
||||||
|
// reports the disconnect reason, mirroring the Delphi RDPDisconnected handler.
|
||||||
|
|
||||||
|
using RDPWrap.Common;
|
||||||
|
|
||||||
|
namespace RDPCheck;
|
||||||
|
|
||||||
|
internal sealed class MainForm : Form
|
||||||
|
{
|
||||||
|
private readonly AxRdpClient2 _rdp;
|
||||||
|
|
||||||
|
// Registry values saved on load — restored on disconnect
|
||||||
|
private int _savedSecurityLayer;
|
||||||
|
private int _savedUserAuthentication;
|
||||||
|
|
||||||
|
// ── Constructor / Layout ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public MainForm()
|
||||||
|
{
|
||||||
|
SuspendLayout();
|
||||||
|
|
||||||
|
Text = "RDP Wrapper Check";
|
||||||
|
ClientSize = new Size(800, 600);
|
||||||
|
StartPosition = FormStartPosition.CenterScreen;
|
||||||
|
FormBorderStyle = FormBorderStyle.Sizable;
|
||||||
|
|
||||||
|
_rdp = new AxRdpClient2
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Fill
|
||||||
|
};
|
||||||
|
|
||||||
|
Controls.Add(_rdp);
|
||||||
|
ResumeLayout(false);
|
||||||
|
|
||||||
|
_rdp.Disconnected += OnRdpDisconnected;
|
||||||
|
Load += OnFormLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── FormLoad — mirrors TFrm.FormCreate ────────────────────────────────────
|
||||||
|
|
||||||
|
private void OnFormLoad(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
const string rdpTcpKey =
|
||||||
|
@"SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp";
|
||||||
|
|
||||||
|
_rdp.DisconnectedText = "Disconnected.";
|
||||||
|
_rdp.ConnectingText = "Connecting...";
|
||||||
|
_rdp.ConnectedStatusText = "Connected.";
|
||||||
|
_rdp.UserName = string.Empty;
|
||||||
|
_rdp.Server = "127.0.0.2";
|
||||||
|
|
||||||
|
// Read, then zero-out SecurityLayer / UserAuthentication
|
||||||
|
_savedSecurityLayer = RegistryHelper.ReadInt(rdpTcpKey, "SecurityLayer", 0);
|
||||||
|
_savedUserAuthentication = RegistryHelper.ReadInt(rdpTcpKey, "UserAuthentication", 0);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RegistryHelper.WriteInt(rdpTcpKey, "SecurityLayer", 0);
|
||||||
|
RegistryHelper.WriteInt(rdpTcpKey, "UserAuthentication", 0);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[RDP] Registry write failed: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read port
|
||||||
|
int port = RegistryHelper.ReadInt(rdpTcpKey, "PortNumber", 3389);
|
||||||
|
_rdp.SetPort(port);
|
||||||
|
|
||||||
|
// Subscribe to COM events now that the handle exists
|
||||||
|
_rdp.SubscribeEvents();
|
||||||
|
|
||||||
|
// Brief delay then connect — matches Delphi Sleep(1000); RDP.Connect;
|
||||||
|
Task.Delay(1000).ContinueWith(_ => Invoke((Action)_rdp.Connect));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── OnDisconnected — mirrors TFrm.RDPDisconnected ─────────────────────────
|
||||||
|
|
||||||
|
private void OnRdpDisconnected(object? sender, int discReason)
|
||||||
|
{
|
||||||
|
string errStr = discReason switch
|
||||||
|
{
|
||||||
|
0x001 => "Local disconnection.",
|
||||||
|
0x002 => "Disconnected by user.",
|
||||||
|
0x003 => "Disconnected by server.",
|
||||||
|
0x904 => "Socket closed.",
|
||||||
|
0xC08 => "Decompress error.",
|
||||||
|
0x108 => "Connection timed out.",
|
||||||
|
0xC06 => "Decryption error.",
|
||||||
|
0x104 => "DNS name lookup failure.",
|
||||||
|
0x508 => "DNS lookup failed.",
|
||||||
|
0xB06 => "Encryption error.",
|
||||||
|
0x604 => "Windows Sockets gethostbyname() call failed.",
|
||||||
|
0x208 => "Host not found error.",
|
||||||
|
0x408 => "Internal error.",
|
||||||
|
0x906 => "Internal security error.",
|
||||||
|
0xA06 => "Internal security error.",
|
||||||
|
0x506 => "The encryption method specified is not valid.",
|
||||||
|
0x804 => "Bad IP address specified.",
|
||||||
|
0x606 => "Server security data is not valid.",
|
||||||
|
0x406 => "Security data is not valid.",
|
||||||
|
0x308 => "The IP address specified is not valid.",
|
||||||
|
0x808 => "License negotiation failed.",
|
||||||
|
0x908 => "Licensing time-out.",
|
||||||
|
0x106 => "Out of memory.",
|
||||||
|
0x206 => "Out of memory.",
|
||||||
|
0x306 => "Out of memory.",
|
||||||
|
0x706 => "Failed to unpack server certificate.",
|
||||||
|
0x204 => "Socket connection failed.",
|
||||||
|
0x404 => "Windows Sockets recv() call failed.",
|
||||||
|
0x704 => "Time-out occurred.",
|
||||||
|
0x608 => "Internal timer error.",
|
||||||
|
0x304 => "Windows Sockets send() call failed.",
|
||||||
|
0xB07 => "The account is disabled.",
|
||||||
|
0xE07 => "The account is expired.",
|
||||||
|
0xD07 => "The account is locked out.",
|
||||||
|
0xC07 => "The account is restricted.",
|
||||||
|
0x1B07 => "The received certificate is expired.",
|
||||||
|
0x1607 => "The policy does not support delegation of credentials to the target server.",
|
||||||
|
0x2107 => "The server authentication policy does not allow connection requests using saved credentials. The user must enter new credentials.",
|
||||||
|
0x807 => "Login failed.",
|
||||||
|
0x1807 => "No authority could be contacted for authentication. The domain name of the authenticating party could be wrong, the domain could be unreachable, or there might have been a trust relationship failure.",
|
||||||
|
0xA07 => "The specified user has no account.",
|
||||||
|
0xF07 => "The password is expired.",
|
||||||
|
0x1207 => "The user password must be changed before logging on for the first time.",
|
||||||
|
0x1707 => "Delegation of credentials to the target server is not allowed unless mutual authentication has been achieved.",
|
||||||
|
0x2207 => "The smart card is blocked.",
|
||||||
|
0x1C07 => "An incorrect PIN was presented to the smart card.",
|
||||||
|
0xB09 => "Network Level Authentication is required, run RDPCheck as administrator.",
|
||||||
|
0x708 => "RDP is working, but the client doesn't allow loopback connections. Try to connect to your PC from another device in the network.",
|
||||||
|
_ => $"Unknown code 0x{discReason:X}"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (discReason > 2)
|
||||||
|
{
|
||||||
|
MessageBox.Show(errStr, "Disconnected",
|
||||||
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore registry
|
||||||
|
const string rdpTcpKey =
|
||||||
|
@"SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RegistryHelper.WriteInt(rdpTcpKey, "SecurityLayer", _savedSecurityLayer);
|
||||||
|
RegistryHelper.WriteInt(rdpTcpKey, "UserAuthentication", _savedUserAuthentication);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
Application.Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing) _rdp.Dispose();
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
|
//
|
||||||
|
// RDPCheck — WinForms RDP tester entry point.
|
||||||
|
|
||||||
|
namespace RDPCheck;
|
||||||
|
|
||||||
|
internal static class Program
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
internal static void Main()
|
||||||
|
{
|
||||||
|
ApplicationConfiguration.Initialize();
|
||||||
|
Application.Run(new MainForm());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,147 @@
|
|||||||
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
|
//
|
||||||
|
// COM interop helpers for hosting the MsRdpClient2 ActiveX control and
|
||||||
|
// receiving its disconnection event without requiring pre-generated TLB wrappers.
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace RDPCheck;
|
||||||
|
|
||||||
|
// ── COM interfaces needed for connection-point event subscription ────────────
|
||||||
|
|
||||||
|
[ComImport]
|
||||||
|
[Guid("B196B284-BAB4-101A-B69C-00AA00341D07")]
|
||||||
|
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||||
|
internal interface IConnectionPointContainer
|
||||||
|
{
|
||||||
|
void EnumConnectionPoints(out IntPtr ppEnum);
|
||||||
|
void FindConnectionPoint(ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IConnectionPoint ppCP);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ComImport]
|
||||||
|
[Guid("B196B287-BAB4-101A-B69C-00AA00341D07")]
|
||||||
|
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||||
|
internal interface IConnectionPoint
|
||||||
|
{
|
||||||
|
void GetConnectionInterface(out Guid pIID);
|
||||||
|
void GetConnectionPointContainer([MarshalAs(UnmanagedType.Interface)] out IConnectionPointContainer ppCPC);
|
||||||
|
void Advise([MarshalAs(UnmanagedType.Interface)] object pUnkSink, out uint pdwCookie);
|
||||||
|
void Unadvise(uint dwCookie);
|
||||||
|
void EnumConnections(out IntPtr ppEnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── IDispatch-based event interface (dispinterface) ──────────────────────────
|
||||||
|
// Matching the DIID of IMsTscAxEvents so the RDP control can route events here.
|
||||||
|
|
||||||
|
[ComVisible(true)]
|
||||||
|
[Guid("336D5562-EBA6-11D0-B0B0-00C04FD610D0")]
|
||||||
|
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
|
||||||
|
internal interface IRdpEvents
|
||||||
|
{
|
||||||
|
[DispId(1)] void OnConnecting();
|
||||||
|
[DispId(2)] void OnConnected();
|
||||||
|
[DispId(3)] void OnLoginComplete();
|
||||||
|
[DispId(4)] void OnDisconnected(int discReason);
|
||||||
|
// Additional high-DISPID events are ignored (not listed here)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Managed event sink ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
[ComVisible(true)]
|
||||||
|
[ClassInterface(ClassInterfaceType.None)]
|
||||||
|
internal sealed class RdpEventSink : IRdpEvents
|
||||||
|
{
|
||||||
|
private readonly Action<int> _onDisconnected;
|
||||||
|
|
||||||
|
public RdpEventSink(Action<int> onDisconnected) => _onDisconnected = onDisconnected;
|
||||||
|
|
||||||
|
public void OnConnecting() { }
|
||||||
|
public void OnConnected() { }
|
||||||
|
public void OnLoginComplete() { }
|
||||||
|
public void OnDisconnected(int discReason) => _onDisconnected(discReason);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── AxHost wrapper for MsRdpClient2 ──────────────────────────────────────────
|
||||||
|
|
||||||
|
internal sealed class AxRdpClient2 : AxHost
|
||||||
|
{
|
||||||
|
// CLSID for MsRdpClient2 (msrdp.ocx / mstscax.dll)
|
||||||
|
private static readonly Guid Clsid = new("9059F30F-4EB1-4BD2-9FDC-36F43A218F4A");
|
||||||
|
// DIID for the default source interface (IMsTscAxEvents / DIMsTscAxEvents)
|
||||||
|
private static readonly Guid DiidEvents = new("336D5562-EBA6-11D0-B0B0-00C04FD610D0");
|
||||||
|
|
||||||
|
private dynamic? _ocx;
|
||||||
|
private IConnectionPoint? _cp;
|
||||||
|
private uint _cookie;
|
||||||
|
private RdpEventSink? _sink;
|
||||||
|
|
||||||
|
/// <summary>Raised on the UI thread when the RDP control disconnects.</summary>
|
||||||
|
public event EventHandler<int>? Disconnected;
|
||||||
|
|
||||||
|
public AxRdpClient2() : base(Clsid.ToString("B")) { }
|
||||||
|
|
||||||
|
protected override void AttachInterfaces()
|
||||||
|
{
|
||||||
|
_ocx = GetOcx() as dynamic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wires up the COM event sink. Call once after the handle has been created
|
||||||
|
/// (i.e. after the form is shown).
|
||||||
|
/// </summary>
|
||||||
|
public void SubscribeEvents()
|
||||||
|
{
|
||||||
|
if (_ocx is null) return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cpContainer = (IConnectionPointContainer)_ocx;
|
||||||
|
var eventsIid = DiidEvents;
|
||||||
|
cpContainer.FindConnectionPoint(ref eventsIid, out var cp);
|
||||||
|
_sink = new RdpEventSink(reason => Invoke(
|
||||||
|
(Action)(() => Disconnected?.Invoke(this, reason))));
|
||||||
|
cp.Advise(_sink, out _cookie);
|
||||||
|
_cp = cp;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[RDP] SubscribeEvents failed: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Unsubscribes the event sink.</summary>
|
||||||
|
public void UnsubscribeEvents()
|
||||||
|
{
|
||||||
|
try { _cp?.Unadvise(_cookie); } catch { }
|
||||||
|
_cp = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Typed property / method wrappers ────────────────────────────────────
|
||||||
|
|
||||||
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||||
|
public string Server { set { if (_ocx != null) _ocx.Server = value; } }
|
||||||
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||||
|
public string UserName { set { if (_ocx != null) _ocx.UserName = value; } }
|
||||||
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||||
|
public string DisconnectedText { set { if (_ocx != null) _ocx.DisconnectedText = value; } }
|
||||||
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||||
|
public string ConnectingText { set { if (_ocx != null) _ocx.ConnectingText = value; } }
|
||||||
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||||
|
public string ConnectedStatusText { set { if (_ocx != null) _ocx.ConnectedStatusText = value; } }
|
||||||
|
|
||||||
|
public void SetPort(int port)
|
||||||
|
{
|
||||||
|
try { if (_ocx != null) _ocx.AdvancedSettings2.RDPPort = port; } catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Connect()
|
||||||
|
{
|
||||||
|
try { _ocx?.Connect(); } catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing) UnsubscribeEvents();
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 445 B |
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="RDPCheck.app"/>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||||
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
</assembly>
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"net10.0-windows7.0": {
|
||||||
|
"rdpwrap": {
|
||||||
|
"type": "Project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
|
//
|
||||||
|
// LicenseForm — mirrors LicenseUnit.pas (TLicenseForm).
|
||||||
|
// Shows a readonly multiline license text with Accept / Decline buttons.
|
||||||
|
|
||||||
|
namespace RDPConf;
|
||||||
|
|
||||||
|
internal sealed class LicenseForm : Form
|
||||||
|
{
|
||||||
|
public LicenseForm(string licenseText)
|
||||||
|
{
|
||||||
|
Text = "License Agreement";
|
||||||
|
ClientSize = new Size(600, 440);
|
||||||
|
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||||
|
MaximizeBox = false;
|
||||||
|
MinimizeBox = false;
|
||||||
|
StartPosition = FormStartPosition.CenterParent;
|
||||||
|
Font = new Font("Segoe UI", 9f);
|
||||||
|
|
||||||
|
var mText = new TextBox
|
||||||
|
{
|
||||||
|
Multiline = true,
|
||||||
|
ReadOnly = true,
|
||||||
|
ScrollBars = ScrollBars.Vertical,
|
||||||
|
Text = licenseText,
|
||||||
|
Location = new Point(8, 8),
|
||||||
|
Size = new Size(576, 380),
|
||||||
|
Font = new Font("Courier New", 8.5f),
|
||||||
|
BackColor = SystemColors.Window
|
||||||
|
};
|
||||||
|
|
||||||
|
var bAccept = new Button
|
||||||
|
{
|
||||||
|
Text = "Accept",
|
||||||
|
DialogResult = DialogResult.OK,
|
||||||
|
Location = new Point(428, 398),
|
||||||
|
Size = new Size(75, 26)
|
||||||
|
};
|
||||||
|
|
||||||
|
var bDecline = new Button
|
||||||
|
{
|
||||||
|
Text = "Decline",
|
||||||
|
DialogResult = DialogResult.Cancel,
|
||||||
|
Location = new Point(509, 398),
|
||||||
|
Size = new Size(75, 26)
|
||||||
|
};
|
||||||
|
|
||||||
|
AcceptButton = bAccept;
|
||||||
|
CancelButton = bDecline;
|
||||||
|
|
||||||
|
Controls.AddRange(new Control[] { mText, bAccept, bDecline });
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,540 @@
|
|||||||
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
|
//
|
||||||
|
// All logic, registry keys, and label text match the Delphi original.
|
||||||
|
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using RDPWrap.Common;
|
||||||
|
|
||||||
|
namespace RDPConf;
|
||||||
|
|
||||||
|
internal sealed class MainForm : Form
|
||||||
|
{
|
||||||
|
// ── Controls ──────────────────────────────────────────────────────────────
|
||||||
|
private readonly CheckBox cbAllowTSConnections = new();
|
||||||
|
private readonly CheckBox cbSingleSessionPerUser = new();
|
||||||
|
private readonly CheckBox cbHideUsers = new();
|
||||||
|
private readonly CheckBox cbCustomPrg = new();
|
||||||
|
private readonly NumericUpDown seRDPPort = new();
|
||||||
|
private readonly Label lRDPPort = new();
|
||||||
|
private readonly GroupBox gbGeneral = new();
|
||||||
|
|
||||||
|
// NLA radio group
|
||||||
|
private readonly GroupBox gbNLA = new();
|
||||||
|
private readonly RadioButton rbNLA0 = new();
|
||||||
|
private readonly RadioButton rbNLA1 = new();
|
||||||
|
private readonly RadioButton rbNLA2 = new();
|
||||||
|
|
||||||
|
// Shadow radio group
|
||||||
|
private readonly GroupBox gbShadow = new();
|
||||||
|
private readonly RadioButton rbShadow0 = new();
|
||||||
|
private readonly RadioButton rbShadow1 = new();
|
||||||
|
private readonly RadioButton rbShadow2 = new();
|
||||||
|
private readonly RadioButton rbShadow3 = new();
|
||||||
|
private readonly RadioButton rbShadow4 = new();
|
||||||
|
|
||||||
|
// Diagnostics
|
||||||
|
private readonly GroupBox gbDiag = new();
|
||||||
|
private readonly Label lService = new();
|
||||||
|
private readonly Label lsService = new();
|
||||||
|
private readonly Label lListener = new();
|
||||||
|
private readonly Label lsListener = new();
|
||||||
|
private readonly Label lWrapper = new();
|
||||||
|
private readonly Label lsWrapper = new();
|
||||||
|
private readonly Label lTSVer = new();
|
||||||
|
private readonly Label lsTSVer = new();
|
||||||
|
private readonly Label lWrapVer = new();
|
||||||
|
private readonly Label lsWrapVer = new();
|
||||||
|
private readonly Label lsSuppVer = new();
|
||||||
|
|
||||||
|
// Buttons / Timer
|
||||||
|
private readonly Button bOK = new();
|
||||||
|
private readonly Button bCancel = new();
|
||||||
|
private readonly Button bApply = new();
|
||||||
|
private readonly Button bLicense = new();
|
||||||
|
private readonly System.Windows.Forms.Timer timer = new();
|
||||||
|
|
||||||
|
// State (mirrors Delphi globals)
|
||||||
|
private bool _ready;
|
||||||
|
private int _oldPort;
|
||||||
|
|
||||||
|
// ── Constructor ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public MainForm()
|
||||||
|
{
|
||||||
|
SuspendLayout();
|
||||||
|
BuildLayout();
|
||||||
|
ResumeLayout(false);
|
||||||
|
PerformLayout();
|
||||||
|
|
||||||
|
// Wire events
|
||||||
|
cbAllowTSConnections.CheckedChanged += OnAnyChange;
|
||||||
|
cbSingleSessionPerUser.CheckedChanged += OnAnyChange;
|
||||||
|
cbHideUsers.CheckedChanged += OnAnyChange;
|
||||||
|
cbCustomPrg.CheckedChanged += OnAnyChange;
|
||||||
|
seRDPPort.ValueChanged += OnAnyChange;
|
||||||
|
rbNLA0.CheckedChanged += OnAnyChange;
|
||||||
|
rbNLA1.CheckedChanged += OnAnyChange;
|
||||||
|
rbNLA2.CheckedChanged += OnAnyChange;
|
||||||
|
rbShadow0.CheckedChanged += OnAnyChange;
|
||||||
|
rbShadow1.CheckedChanged += OnAnyChange;
|
||||||
|
rbShadow2.CheckedChanged += OnAnyChange;
|
||||||
|
rbShadow3.CheckedChanged += OnAnyChange;
|
||||||
|
rbShadow4.CheckedChanged += OnAnyChange;
|
||||||
|
bApply.Click += (_, _) => { WriteSettings(); bApply.Enabled = false; };
|
||||||
|
bOK.Click += (_, _) => { if (bApply.Enabled) { WriteSettings(); bApply.Enabled = false; } Close(); };
|
||||||
|
bCancel.Click += (_, _) => Close();
|
||||||
|
bLicense.Click += OnLicenseClick;
|
||||||
|
timer.Interval = 1000;
|
||||||
|
timer.Tick += TimerTimer;
|
||||||
|
FormClosing += OnFormClosing;
|
||||||
|
Shown += OnShown;
|
||||||
|
FormClosed += (_, _) => { if (ArchHelper.Is64Bit) ArchHelper.RevertWow64Redirection(); timer.Stop(); };
|
||||||
|
|
||||||
|
if (ArchHelper.Is64Bit) ArchHelper.DisableWow64Redirection();
|
||||||
|
ReadSettings();
|
||||||
|
_ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShown(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
TimerTimer(sender, e); // immediate first tick
|
||||||
|
timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Layout ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void BuildLayout()
|
||||||
|
{
|
||||||
|
Text = "RDP Wrapper Configuration";
|
||||||
|
ClientSize = new Size(540, 383);
|
||||||
|
FormBorderStyle = FormBorderStyle.FixedSingle;
|
||||||
|
MaximizeBox = false;
|
||||||
|
StartPosition = FormStartPosition.CenterScreen;
|
||||||
|
Font = new Font("Segoe UI", 9f);
|
||||||
|
|
||||||
|
// ── General GroupBox ──────────────────────────────────────────────────
|
||||||
|
gbGeneral.Text = "General";
|
||||||
|
gbGeneral.Location = new Point(8, 8);
|
||||||
|
gbGeneral.Size = new Size(260, 175);
|
||||||
|
|
||||||
|
cbAllowTSConnections.Text = "Allow Remote Desktop connections";
|
||||||
|
cbAllowTSConnections.Location = new Point(10, 22);
|
||||||
|
cbAllowTSConnections.AutoSize = true;
|
||||||
|
|
||||||
|
cbSingleSessionPerUser.Text = "Single session per user";
|
||||||
|
cbSingleSessionPerUser.Location = new Point(10, 46);
|
||||||
|
cbSingleSessionPerUser.AutoSize = true;
|
||||||
|
|
||||||
|
cbHideUsers.Text = "Hide users on logon screen";
|
||||||
|
cbHideUsers.Location = new Point(10, 70);
|
||||||
|
cbHideUsers.AutoSize = true;
|
||||||
|
|
||||||
|
cbCustomPrg.Text = "Custom program support (HonorLegacySettings)";
|
||||||
|
cbCustomPrg.Location = new Point(10, 94);
|
||||||
|
cbCustomPrg.AutoSize = true;
|
||||||
|
|
||||||
|
lRDPPort.Text = "RDP Port:";
|
||||||
|
lRDPPort.Location = new Point(10, 124);
|
||||||
|
lRDPPort.AutoSize = true;
|
||||||
|
|
||||||
|
seRDPPort.Location = new Point(80, 121);
|
||||||
|
seRDPPort.Minimum = 1;
|
||||||
|
seRDPPort.Maximum = 65535;
|
||||||
|
seRDPPort.Value = 3389;
|
||||||
|
seRDPPort.Width = 70;
|
||||||
|
|
||||||
|
gbGeneral.Controls.AddRange(new Control[]
|
||||||
|
{
|
||||||
|
cbAllowTSConnections, cbSingleSessionPerUser, cbHideUsers,
|
||||||
|
cbCustomPrg, lRDPPort, seRDPPort
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── NLA GroupBox ──────────────────────────────────────────────────────
|
||||||
|
gbNLA.Text = "Network Level Authentication";
|
||||||
|
gbNLA.Location = new Point(276, 8);
|
||||||
|
gbNLA.Size = new Size(254, 88);
|
||||||
|
|
||||||
|
rbNLA0.Text = "No NLA";
|
||||||
|
rbNLA0.Location = new Point(10, 20);
|
||||||
|
rbNLA0.AutoSize = true;
|
||||||
|
|
||||||
|
rbNLA1.Text = "Negotiate (server-side NLA)";
|
||||||
|
rbNLA1.Location = new Point(10, 42);
|
||||||
|
rbNLA1.AutoSize = true;
|
||||||
|
|
||||||
|
rbNLA2.Text = "Require NLA (client + server)";
|
||||||
|
rbNLA2.Location = new Point(10, 64);
|
||||||
|
rbNLA2.AutoSize = true;
|
||||||
|
|
||||||
|
gbNLA.Controls.AddRange(new Control[] { rbNLA0, rbNLA1, rbNLA2 });
|
||||||
|
|
||||||
|
// ── Shadow GroupBox ───────────────────────────────────────────────────
|
||||||
|
gbShadow.Text = "Shadowing";
|
||||||
|
gbShadow.Location = new Point(276, 100);
|
||||||
|
gbShadow.Size = new Size(254, 135);
|
||||||
|
|
||||||
|
rbShadow0.Text = "Disabled";
|
||||||
|
rbShadow0.Location = new Point(10, 20); rbShadow0.AutoSize = true;
|
||||||
|
rbShadow1.Text = "Full Access (with confirmation)";
|
||||||
|
rbShadow1.Location = new Point(10, 42); rbShadow1.AutoSize = true;
|
||||||
|
rbShadow2.Text = "Full Access (no confirmation)";
|
||||||
|
rbShadow2.Location = new Point(10, 62); rbShadow2.AutoSize = true;
|
||||||
|
rbShadow3.Text = "View Only (with confirmation)";
|
||||||
|
rbShadow3.Location = new Point(10, 82); rbShadow3.AutoSize = true;
|
||||||
|
rbShadow4.Text = "View Only (no confirmation)";
|
||||||
|
rbShadow4.Location = new Point(10, 102); rbShadow4.AutoSize = true;
|
||||||
|
|
||||||
|
gbShadow.Controls.AddRange(new Control[]
|
||||||
|
{ rbShadow0, rbShadow1, rbShadow2, rbShadow3, rbShadow4 });
|
||||||
|
|
||||||
|
// ── Diagnostics GroupBox ──────────────────────────────────────────────
|
||||||
|
gbDiag.Text = "Diagnostics";
|
||||||
|
gbDiag.Location = new Point(8, 190);
|
||||||
|
gbDiag.Size = new Size(522, 140);
|
||||||
|
|
||||||
|
AddDiagRow(gbDiag, "Service:", lService, lsService, new Point(10, 22));
|
||||||
|
AddDiagRow(gbDiag, "Listener:", lListener, lsListener, new Point(10, 44));
|
||||||
|
AddDiagRow(gbDiag, "Wrapper:", lWrapper, lsWrapper, new Point(10, 66));
|
||||||
|
AddDiagRow(gbDiag, "TS version:", lTSVer, lsTSVer, new Point(10, 88));
|
||||||
|
AddDiagRow(gbDiag, "Wrapper version:", lWrapVer, lsWrapVer, new Point(10, 110));
|
||||||
|
|
||||||
|
lsSuppVer.Location = new Point(280, 88);
|
||||||
|
lsSuppVer.AutoSize = true;
|
||||||
|
lsSuppVer.Visible = false;
|
||||||
|
gbDiag.Controls.Add(lsSuppVer);
|
||||||
|
|
||||||
|
// ── Buttons ───────────────────────────────────────────────────────────
|
||||||
|
bLicense.Text = "License";
|
||||||
|
bLicense.Size = new Size(80, 26);
|
||||||
|
bLicense.Location = new Point(8, 345);
|
||||||
|
|
||||||
|
bApply.Text = "Apply";
|
||||||
|
bApply.Enabled = false;
|
||||||
|
bApply.Size = new Size(80, 26);
|
||||||
|
bApply.Location = new Point(276, 345);
|
||||||
|
|
||||||
|
bOK.Text = "OK";
|
||||||
|
bOK.Size = new Size(80, 26);
|
||||||
|
bOK.Location = new Point(364, 345);
|
||||||
|
|
||||||
|
bCancel.Text = "Cancel";
|
||||||
|
bCancel.Size = new Size(80, 26);
|
||||||
|
bCancel.Location = new Point(452, 345);
|
||||||
|
|
||||||
|
Controls.AddRange(new Control[]
|
||||||
|
{
|
||||||
|
gbGeneral, gbNLA, gbShadow, gbDiag,
|
||||||
|
bLicense, bApply, bOK, bCancel
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddDiagRow(GroupBox parent, string labelText,
|
||||||
|
Label lbl, Label status, Point origin)
|
||||||
|
{
|
||||||
|
lbl.Text = labelText;
|
||||||
|
lbl.Location = new Point(origin.X, origin.Y);
|
||||||
|
lbl.AutoSize = true;
|
||||||
|
|
||||||
|
status.Text = "...";
|
||||||
|
status.Location = new Point(origin.X + 120, origin.Y);
|
||||||
|
status.AutoSize = true;
|
||||||
|
status.ForeColor = SystemColors.GrayText;
|
||||||
|
|
||||||
|
parent.Controls.Add(lbl);
|
||||||
|
parent.Controls.Add(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAnyChange(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (_ready) bApply.Enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ReadSettings (mirrors Delphi ReadSettings) ────────────────────────────
|
||||||
|
|
||||||
|
private void ReadSettings()
|
||||||
|
{
|
||||||
|
const string tsKey = @"SYSTEM\CurrentControlSet\Control\Terminal Server";
|
||||||
|
const string rdpTcp = @"SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp";
|
||||||
|
const string policies = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System";
|
||||||
|
|
||||||
|
cbAllowTSConnections.Checked = !RegistryHelper.ReadBool(tsKey, "fDenyTSConnections", false);
|
||||||
|
cbSingleSessionPerUser.Checked = RegistryHelper.ReadBool(tsKey, "fSingleSessionPerUser", false);
|
||||||
|
cbCustomPrg.Checked = RegistryHelper.ReadBool(tsKey, "HonorLegacySettings", false);
|
||||||
|
|
||||||
|
int port = RegistryHelper.ReadInt(rdpTcp, "PortNumber", 3389);
|
||||||
|
seRDPPort.Value = Math.Clamp(port, 1, 65535);
|
||||||
|
_oldPort = port;
|
||||||
|
|
||||||
|
int secLayer = RegistryHelper.ReadInt(rdpTcp, "SecurityLayer", 0);
|
||||||
|
int userAuth = RegistryHelper.ReadInt(rdpTcp, "UserAuthentication", 0);
|
||||||
|
int shadow = RegistryHelper.ReadInt(rdpTcp, "Shadow", -1);
|
||||||
|
|
||||||
|
_ = (secLayer, userAuth) switch
|
||||||
|
{
|
||||||
|
(0, 0) => rbNLA0.Checked = true,
|
||||||
|
(1, 0) => rbNLA1.Checked = true,
|
||||||
|
(2, 1) => rbNLA2.Checked = true,
|
||||||
|
_ => rbNLA0.Checked = true
|
||||||
|
};
|
||||||
|
|
||||||
|
SetShadowRadio(shadow);
|
||||||
|
|
||||||
|
cbHideUsers.Checked = RegistryHelper.ReadBool(policies, "dontdisplaylastusername", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetShadowRadio(int idx)
|
||||||
|
{
|
||||||
|
RadioButton[] rbs = { rbShadow0, rbShadow1, rbShadow2, rbShadow3, rbShadow4 };
|
||||||
|
if (idx >= 0 && idx < rbs.Length)
|
||||||
|
rbs[idx].Checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetShadowIndex()
|
||||||
|
{
|
||||||
|
RadioButton[] rbs = { rbShadow0, rbShadow1, rbShadow2, rbShadow3, rbShadow4 };
|
||||||
|
for (int i = 0; i < rbs.Length; i++)
|
||||||
|
if (rbs[i].Checked) return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── WriteSettings (mirrors Delphi WriteSettings) ──────────────────────────
|
||||||
|
|
||||||
|
private void WriteSettings()
|
||||||
|
{
|
||||||
|
const string tsKey = @"SYSTEM\CurrentControlSet\Control\Terminal Server";
|
||||||
|
const string rdpTcp = @"SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp";
|
||||||
|
const string policiesRdp = @"SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services";
|
||||||
|
const string policies = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System";
|
||||||
|
|
||||||
|
RegistryHelper.WriteBool(tsKey, "fDenyTSConnections", !cbAllowTSConnections.Checked);
|
||||||
|
RegistryHelper.WriteBool(tsKey, "fSingleSessionPerUser", cbSingleSessionPerUser.Checked);
|
||||||
|
RegistryHelper.WriteBool(tsKey, "HonorLegacySettings", cbCustomPrg.Checked);
|
||||||
|
|
||||||
|
int newPort = (int)seRDPPort.Value;
|
||||||
|
RegistryHelper.WriteInt(rdpTcp, "PortNumber", newPort);
|
||||||
|
if (_oldPort != newPort)
|
||||||
|
{
|
||||||
|
_oldPort = newPort;
|
||||||
|
ProcessHelper.ExecWait(
|
||||||
|
$"netsh advfirewall firewall set rule name=\"Remote Desktop\" new localport={newPort}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// NLA
|
||||||
|
(int sl, int ua) = (rbNLA0.Checked, rbNLA1.Checked, rbNLA2.Checked) switch
|
||||||
|
{
|
||||||
|
(true, _, _ ) => (0, 0),
|
||||||
|
(_, true, _ ) => (1, 0),
|
||||||
|
(_, _, true) => (2, 1),
|
||||||
|
_ => (-1, -1)
|
||||||
|
};
|
||||||
|
if (sl >= 0)
|
||||||
|
{
|
||||||
|
RegistryHelper.WriteInt(rdpTcp, "SecurityLayer", sl);
|
||||||
|
RegistryHelper.WriteInt(rdpTcp, "UserAuthentication", ua);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shadow
|
||||||
|
int shadowIdx = GetShadowIndex();
|
||||||
|
if (shadowIdx >= 0)
|
||||||
|
{
|
||||||
|
RegistryHelper.WriteInt(rdpTcp, "Shadow", shadowIdx);
|
||||||
|
RegistryHelper.WriteInt(policiesRdp, "Shadow", shadowIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
RegistryHelper.WriteBool(policies, "dontdisplaylastusername", cbHideUsers.Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── TimerTimer (mirrors Delphi TimerTimer) ────────────────────────────────
|
||||||
|
|
||||||
|
private void TimerTimer(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// ── Wrapper state ──
|
||||||
|
string wrapperPath = string.Empty;
|
||||||
|
int wrapState = IsWrapperInstalled(ref wrapperPath);
|
||||||
|
bool checkSupp = false;
|
||||||
|
string iniPath = string.Empty;
|
||||||
|
|
||||||
|
switch (wrapState)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
SetStatus(lsWrapper, "Unknown", SystemColors.GrayText);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
SetStatus(lsWrapper, "Not installed", SystemColors.GrayText);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
SetStatus(lsWrapper, "Installed", Color.Green);
|
||||||
|
iniPath = Path.Combine(
|
||||||
|
Path.GetDirectoryName(ArchHelper.ExpandPath(wrapperPath))!,
|
||||||
|
"rdpwrap.ini");
|
||||||
|
checkSupp = File.Exists(iniPath);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
SetStatus(lsWrapper, "3rd-party", Color.Red);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Service state ──
|
||||||
|
int svcState = GetTermSrvState();
|
||||||
|
// dwCurrentState constants (mirrors NativeMethods SERVICE_* constants)
|
||||||
|
string svcText = svcState switch
|
||||||
|
{
|
||||||
|
1 => "Stopped",
|
||||||
|
2 => "Starting...",
|
||||||
|
3 => "Stopping...",
|
||||||
|
4 => "Running",
|
||||||
|
5 => "Resuming...",
|
||||||
|
6 => "Suspending...",
|
||||||
|
7 => "Suspended",
|
||||||
|
_ => "Unknown"
|
||||||
|
};
|
||||||
|
Color svcColor = svcState == 4 ? Color.Green // SERVICE_RUNNING
|
||||||
|
: svcState == 1 ? Color.Red // SERVICE_STOPPED
|
||||||
|
: SystemColors.GrayText;
|
||||||
|
SetStatus(lsService, svcText, svcColor);
|
||||||
|
|
||||||
|
// ── Listener ──
|
||||||
|
bool listening = IsListenerWorking();
|
||||||
|
SetStatus(lsListener,
|
||||||
|
listening ? "Listening" : "Not listening",
|
||||||
|
listening ? Color.Green : Color.Red);
|
||||||
|
|
||||||
|
// ── Wrapper version ──
|
||||||
|
string wrapExp = ArchHelper.ExpandPath(wrapperPath);
|
||||||
|
var wrapVer = string.IsNullOrEmpty(wrapperPath) ? null
|
||||||
|
: FileVersionHelper.GetVersion(wrapExp);
|
||||||
|
if (wrapVer is null)
|
||||||
|
SetStatus(lsWrapVer, "N/A", SystemColors.GrayText);
|
||||||
|
else
|
||||||
|
SetStatus(lsWrapVer, wrapVer.ToString(), SystemColors.WindowText);
|
||||||
|
|
||||||
|
// ── TS version ──
|
||||||
|
var tsVer = FileVersionHelper.GetVersionExpanded(@"%SystemRoot%\System32\termsrv.dll");
|
||||||
|
if (tsVer is null)
|
||||||
|
{
|
||||||
|
SetStatus(lsTSVer, "N/A", SystemColors.GrayText);
|
||||||
|
lsSuppVer.Visible = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetStatus(lsTSVer, tsVer.ToString(), SystemColors.WindowText);
|
||||||
|
lsSuppVer.Visible = checkSupp;
|
||||||
|
|
||||||
|
if (checkSupp)
|
||||||
|
{
|
||||||
|
string iniContent = IniHelper.LoadText(iniPath);
|
||||||
|
int level = IniHelper.CheckSupportLevel(iniContent, tsVer);
|
||||||
|
(string suppText, Color suppColor) = level switch
|
||||||
|
{
|
||||||
|
0 => ("[not supported]", Color.Red),
|
||||||
|
1 => ("[supported partially]", Color.Olive),
|
||||||
|
_ => ("[fully supported]", Color.Green)
|
||||||
|
};
|
||||||
|
lsSuppVer.Text = suppText;
|
||||||
|
lsSuppVer.ForeColor = suppColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Helper: IsWrapperInstalled ────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <returns>-1=error, 0=not installed, 1=installed, 2=3rd-party</returns>
|
||||||
|
private static int IsWrapperInstalled(ref string wrapperPath)
|
||||||
|
{
|
||||||
|
wrapperPath = string.Empty;
|
||||||
|
var host = RegistryHelper.ReadString(
|
||||||
|
@"SYSTEM\CurrentControlSet\Services\TermService", "ImagePath") ?? string.Empty;
|
||||||
|
|
||||||
|
if (!host.Contains("svchost.exe", StringComparison.OrdinalIgnoreCase)) return 2;
|
||||||
|
|
||||||
|
var svcDll = RegistryHelper.ReadString(
|
||||||
|
@"SYSTEM\CurrentControlSet\Services\TermService\Parameters", "ServiceDll") ?? string.Empty;
|
||||||
|
|
||||||
|
if (svcDll.Length == 0) return -1;
|
||||||
|
|
||||||
|
if (!svcDll.Contains("termsrv.dll", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!svcDll.Contains("rdpwrap.dll", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
if (svcDll.Contains("rdpwrap.dll", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
wrapperPath = svcDll;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Helper: GetTermSrvState (via SCM) ─────────────────────────────────────
|
||||||
|
|
||||||
|
private static int GetTermSrvState()
|
||||||
|
=> ServiceHelper.GetCurrentState("TermService");
|
||||||
|
|
||||||
|
// ── Helper: IsListenerWorking (via WinStationEnumerateW) ──────────────────
|
||||||
|
|
||||||
|
private static bool IsListenerWorking()
|
||||||
|
{
|
||||||
|
if (!NativeMethods.WinStationEnumerate(IntPtr.Zero,
|
||||||
|
out IntPtr pSessions, out uint count)) return false;
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Each entry is { DWORD SessionId; WCHAR[34] Name; DWORD State }
|
||||||
|
// = 4 + 68 + 4 = 76 bytes on x64 (with natural alignment the struct is 76 bytes)
|
||||||
|
const int stride = 76; // sizeof(WTS_SESSION_INFOW) — matches Delphi packed array
|
||||||
|
for (uint i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
IntPtr entry = pSessions + (int)(i * stride);
|
||||||
|
// Name is at offset 4, WCHAR[34]
|
||||||
|
string name = Marshal.PtrToStringUni(entry + 4, 34).TrimEnd('\0');
|
||||||
|
if (name.Equals("RDP-Tcp", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
NativeMethods.WinStationFreeMemory(pSessions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Helper: SetStatus ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private static void SetStatus(Label lbl, string text, Color color)
|
||||||
|
{
|
||||||
|
lbl.Text = text;
|
||||||
|
lbl.ForeColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── OnFormClosing — unsaved-changes guard ─────────────────────────────────
|
||||||
|
|
||||||
|
private void OnFormClosing(object? sender, FormClosingEventArgs e)
|
||||||
|
{
|
||||||
|
if (bApply.Enabled)
|
||||||
|
{
|
||||||
|
var result = MessageBox.Show(
|
||||||
|
"Settings are not saved. Do you want to exit?",
|
||||||
|
"Warning",
|
||||||
|
MessageBoxButtons.YesNo,
|
||||||
|
MessageBoxIcon.Warning);
|
||||||
|
e.Cancel = result != DialogResult.Yes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── License button ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void OnLicenseClick(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var text = ResourceHelper.ReadText(
|
||||||
|
"RDPConf.Resources.license.txt",
|
||||||
|
System.Reflection.Assembly.GetExecutingAssembly());
|
||||||
|
text ??= ResourceHelper.ReadText("RDPWInst.Resources.license.txt") ?? "(license not found)";
|
||||||
|
|
||||||
|
using var dlg = new LicenseForm(text);
|
||||||
|
if (dlg.ShowDialog(this) != DialogResult.OK)
|
||||||
|
Application.Exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
|
//
|
||||||
|
// RDPConf — WinForms configuration GUI entry point.
|
||||||
|
|
||||||
|
namespace RDPConf;
|
||||||
|
|
||||||
|
internal static class Program
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
internal static void Main()
|
||||||
|
{
|
||||||
|
ApplicationConfiguration.Initialize();
|
||||||
|
Application.Run(new MainForm());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 418 B |
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="RDPConf.app"/>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||||
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
</assembly>
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"net10.0-windows7.0": {
|
||||||
|
"rdpwrap": {
|
||||||
|
"type": "Project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 68da37acab6593c329776644944f55695a131731
|
||||||
@ -1,23 +1,31 @@
|
|||||||
# RDPWInst/Resources
|
# RDPWInst/Resources
|
||||||
|
|
||||||
Place the compiled binary payloads here before building RDPWInst.exe.
|
Place the compiled binary payloads here before building RDPWInst.exe.
|
||||||
These files are embedded as manifest resources at build time.
|
These files are embedded as manifest resources at build time via `<EmbeddedResource Condition="Exists(...)">` entries in `RDPWInst.csproj` — if a file is absent the resource is simply omitted and the build still succeeds (online install mode is used as fallback).
|
||||||
|
|
||||||
## Required files
|
## CI-staged files (automatically copied by `build-and-release.yml`)
|
||||||
|
|
||||||
| File | Source | Used when |
|
| File | Source | Used when |
|
||||||
|---------------------|------------------------------------------------|-------------------------------|
|
|---------------|--------------------------------------------------|-----------------------------|
|
||||||
| `rdpw32.dll` | Build output of `src-x86-x64-Fusix/` (x86) | Always (32-bit install) |
|
| `rdpw32.dll` | Build output of `src-x86-x64-Fusix/` (Win32) | Always — 32-bit install |
|
||||||
| `rdpw64.dll` | Build output of `src-x86-x64-Fusix/` (x64) | Always (64-bit install) |
|
| `rdpw64.dll` | Build output of `src-x86-x64-Fusix/` (x64) | Always — 64-bit install |
|
||||||
| `rdpwrap.ini` | `res/rdpwrap.ini` (bundled baseline) | Always (fallback / offline) |
|
| `rdpwrap.ini` | `msi/rdpwrap.ini` from repo | Always — offline fallback |
|
||||||
| `rdpclip6032.exe` | Original Stas'M redistributable (x86) | Vista x86 install |
|
| `license.txt` | Repo `LICENSE` (copied as plain text) | `RDPWInst -l` flag |
|
||||||
| `rdpclip6064.exe` | Original Stas'M redistributable (x64) | Vista x64 install |
|
|
||||||
| `rdpclip6132.exe` | Original Stas'M redistributable (x86) | Win7 x86 install |
|
|
||||||
| `rdpclip6164.exe` | Original Stas'M redistributable (x64) | Win7 x64 install |
|
|
||||||
| `rfxvmt32.dll` | Original Stas'M redistributable (x86) | Win10 x86 install |
|
|
||||||
| `rfxvmt64.dll` | Original Stas'M redistributable (x64) | Win10 x64 install |
|
|
||||||
| `license.txt` | Repo `LICENSE` file (copy here as text) | `-l` flag |
|
|
||||||
|
|
||||||
Binary files in this folder are intentionally excluded from version control
|
## Optional legacy files (not in VCS, not staged by CI)
|
||||||
via `.gitignore`. The CI pipeline copies them from the native build output
|
|
||||||
before invoking `dotnet publish`.
|
| File | Purpose | Status / action required |
|
||||||
|
|-------------------|--------------------------------------------|--------------------------------------------------------------------------------------|
|
||||||
|
| `rdpclip6032.exe` | Updated rdpclip for Vista x86 | Not redistributable by this project — obtain from original stascorp release if needed |
|
||||||
|
| `rdpclip6064.exe` | Updated rdpclip for Vista x64 | Same as above |
|
||||||
|
| `rdpclip6132.exe` | Updated rdpclip for Win7 x86 | Same as above |
|
||||||
|
| `rdpclip6164.exe` | Updated rdpclip for Win7 x64 | Same as above |
|
||||||
|
| `rfxvmt32.dll` | RemoteFX codec for Win10 Home x86 | **Not redistributable** — must be extracted from a Windows 10 Home installation at `C:\Windows\System32\rfxvmt.dll`. See [#194](https://github.com/stascorp/rdpwrap/issues/194) for context. |
|
||||||
|
| `rfxvmt64.dll` | RemoteFX codec for Win10 Home x64 | Same as above |
|
||||||
|
|
||||||
|
> **Decision note:** `rfxvmt.dll` is a Microsoft-owned component and cannot be legally bundled.
|
||||||
|
> `InstallerEngine.cs` handles the missing-rfxvmt case at runtime: if the file is absent from
|
||||||
|
> the embedded resources and absent from the install directory, a warning is printed and the
|
||||||
|
> user is directed to copy it manually. No CI step attempts to source or stage these files.
|
||||||
|
|
||||||
|
All binary files in this folder are excluded from version control via `.gitignore`.
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"net10.0-windows7.0": {
|
||||||
|
"rdpwrap": {
|
||||||
|
"type": "Project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
<RootNamespace>RDPWrap.Common</RootNamespace>
|
|
||||||
<AssemblyName>RDPWrap.Common</AssemblyName>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 sjackson0109 — Apache License 2.0
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace RDPWrap.Common;
|
namespace RDPWrap.Common;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 sjackson0109 — Apache License 2.0
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace RDPWrap.Common;
|
namespace RDPWrap.Common;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 sjackson0109 — Apache License 2.0
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
|
|
||||||
namespace RDPWrap.Common;
|
namespace RDPWrap.Common;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 sjackson0109 — Apache License 2.0
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
|
|
||||||
namespace RDPWrap.Common;
|
namespace RDPWrap.Common;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 sjackson0109
|
// Copyright 2026 sjackson0109
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 sjackson0109 — Apache License 2.0
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace RDPWrap.Common;
|
namespace RDPWrap.Common;
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<RootNamespace>RDPWrap.Common</RootNamespace>
|
||||||
|
<AssemblyName>RDPWrap</AssemblyName>
|
||||||
|
<Description>Shared helper library (P/Invoke, registry, service, security helpers) for the RDP Wrapper tool suite.</Description>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Grant all three consumer assemblies access to internal members (e.g. NativeMethods) -->
|
||||||
|
<ItemGroup>
|
||||||
|
<InternalsVisibleTo Include="RDPWInst" />
|
||||||
|
<InternalsVisibleTo Include="RDPConf" />
|
||||||
|
<InternalsVisibleTo Include="RDPCheck" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 sjackson0109 — Apache License 2.0
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
|
||||||
namespace RDPWrap.Common;
|
namespace RDPWrap.Common;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 sjackson0109 — Apache License 2.0
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace RDPWrap.Common;
|
namespace RDPWrap.Common;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 sjackson0109 — Apache License 2.0
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace RDPWrap.Common;
|
namespace RDPWrap.Common;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 sjackson0109 — Apache License 2.0
|
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace RDPWrap.Common;
|
namespace RDPWrap.Common;
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"net10.0-windows7.0": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,106 +0,0 @@
|
|||||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<ProjectGuid>{AF9BEAA3-99CD-4B2E-BE67-3F3BD27B961A}</ProjectGuid>
|
|
||||||
<ProjectVersion>12.0</ProjectVersion>
|
|
||||||
<MainSource>RDPWInst.dpr</MainSource>
|
|
||||||
<Config Condition="'$(Config)'==''">Release</Config>
|
|
||||||
<DCC_DCCCompiler>DCC32</DCC_DCCCompiler>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
|
|
||||||
<Base>true</Base>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_1)'!=''">
|
|
||||||
<Cfg_1>true</Cfg_1>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
<Base>true</Base>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''">
|
|
||||||
<Cfg_2>true</Cfg_2>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
<Base>true</Base>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Base)'!=''">
|
|
||||||
<DCC_ExeOutput>..\bin\</DCC_ExeOutput>
|
|
||||||
<DCC_UnitAlias>WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;$(DCC_UnitAlias)</DCC_UnitAlias>
|
|
||||||
<DCC_DependencyCheckOutputName>..\bin\RDPWInst.exe</DCC_DependencyCheckOutputName>
|
|
||||||
<DCC_ImageBase>00400000</DCC_ImageBase>
|
|
||||||
<DCC_Platform>x86</DCC_Platform>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Cfg_1)'!=''">
|
|
||||||
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
|
|
||||||
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
|
|
||||||
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
|
|
||||||
<DCC_DebugInformation>false</DCC_DebugInformation>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Cfg_2)'!=''">
|
|
||||||
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<DelphiCompile Include="RDPWInst.dpr">
|
|
||||||
<MainSource>MainSource</MainSource>
|
|
||||||
</DelphiCompile>
|
|
||||||
<BuildConfiguration Include="Base">
|
|
||||||
<Key>Base</Key>
|
|
||||||
</BuildConfiguration>
|
|
||||||
<BuildConfiguration Include="Release">
|
|
||||||
<Key>Cfg_1</Key>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
</BuildConfiguration>
|
|
||||||
<BuildConfiguration Include="Debug">
|
|
||||||
<Key>Cfg_2</Key>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
</BuildConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
|
|
||||||
<ProjectExtensions>
|
|
||||||
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
|
|
||||||
<Borland.ProjectType/>
|
|
||||||
<BorlandProject>
|
|
||||||
<Delphi.Personality>
|
|
||||||
<Parameters>
|
|
||||||
<Parameters Name="UseLauncher">False</Parameters>
|
|
||||||
<Parameters Name="LoadAllSymbols">True</Parameters>
|
|
||||||
<Parameters Name="LoadUnspecifiedSymbols">False</Parameters>
|
|
||||||
<Parameters Name="RunParams">-w</Parameters>
|
|
||||||
</Parameters>
|
|
||||||
<VersionInfo>
|
|
||||||
<VersionInfo Name="IncludeVerInfo">False</VersionInfo>
|
|
||||||
<VersionInfo Name="AutoIncBuild">False</VersionInfo>
|
|
||||||
<VersionInfo Name="MajorVer">1</VersionInfo>
|
|
||||||
<VersionInfo Name="MinorVer">0</VersionInfo>
|
|
||||||
<VersionInfo Name="Release">0</VersionInfo>
|
|
||||||
<VersionInfo Name="Build">0</VersionInfo>
|
|
||||||
<VersionInfo Name="Debug">False</VersionInfo>
|
|
||||||
<VersionInfo Name="PreRelease">False</VersionInfo>
|
|
||||||
<VersionInfo Name="Special">False</VersionInfo>
|
|
||||||
<VersionInfo Name="Private">False</VersionInfo>
|
|
||||||
<VersionInfo Name="DLL">False</VersionInfo>
|
|
||||||
<VersionInfo Name="Locale">1049</VersionInfo>
|
|
||||||
<VersionInfo Name="CodePage">1251</VersionInfo>
|
|
||||||
</VersionInfo>
|
|
||||||
<VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="CompanyName"/>
|
|
||||||
<VersionInfoKeys Name="FileDescription"/>
|
|
||||||
<VersionInfoKeys Name="FileVersion">1.0.0.0</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="InternalName"/>
|
|
||||||
<VersionInfoKeys Name="LegalCopyright"/>
|
|
||||||
<VersionInfoKeys Name="LegalTrademarks"/>
|
|
||||||
<VersionInfoKeys Name="OriginalFilename"/>
|
|
||||||
<VersionInfoKeys Name="ProductName"/>
|
|
||||||
<VersionInfoKeys Name="ProductVersion">1.0.0.0</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="Comments"/>
|
|
||||||
</VersionInfoKeys>
|
|
||||||
<Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\bcboffice2k140.bpl">Embarcadero C++Builder Office 2000 Servers Package</Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\bcbofficexp140.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\dcloffice2k140.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\dclofficexp140.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
|
|
||||||
</Excluded_Packages>
|
|
||||||
<Source>
|
|
||||||
<Source Name="MainSource">RDPWInst.dpr</Source>
|
|
||||||
</Source>
|
|
||||||
</Delphi.Personality>
|
|
||||||
</BorlandProject>
|
|
||||||
<ProjectFileVersion>12</ProjectFileVersion>
|
|
||||||
</ProjectExtensions>
|
|
||||||
</Project>
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
object Frm: TFrm
|
|
||||||
Left = 0
|
|
||||||
Top = 0
|
|
||||||
BorderIcons = [biSystemMenu, biMinimize]
|
|
||||||
BorderStyle = bsSingle
|
|
||||||
Caption = 'Local RDP Checker'
|
|
||||||
ClientHeight = 480
|
|
||||||
ClientWidth = 640
|
|
||||||
Color = clBtnFace
|
|
||||||
Font.Charset = DEFAULT_CHARSET
|
|
||||||
Font.Color = clWindowText
|
|
||||||
Font.Height = -11
|
|
||||||
Font.Name = 'Tahoma'
|
|
||||||
Font.Style = []
|
|
||||||
OldCreateOrder = False
|
|
||||||
Position = poDesktopCenter
|
|
||||||
OnCreate = FormCreate
|
|
||||||
PixelsPerInch = 96
|
|
||||||
TextHeight = 13
|
|
||||||
object RDP: TMsRdpClient2
|
|
||||||
Left = 0
|
|
||||||
Top = 0
|
|
||||||
Width = 640
|
|
||||||
Height = 480
|
|
||||||
TabOrder = 0
|
|
||||||
OnDisconnected = RDPDisconnected
|
|
||||||
ControlData = {0003000008000200000000000B0000000B000000}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,160 +0,0 @@
|
|||||||
{
|
|
||||||
Copyright 2015 Stas'M Corp.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
}
|
|
||||||
|
|
||||||
unit MainUnit;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
|
|
||||||
Dialogs, OleServer, MSTSCLib_TLB, OleCtrls, Registry;
|
|
||||||
|
|
||||||
type
|
|
||||||
TFrm = class(TForm)
|
|
||||||
RDP: TMsRdpClient2;
|
|
||||||
procedure RDPDisconnected(ASender: TObject; discReason: Integer);
|
|
||||||
procedure FormCreate(Sender: TObject);
|
|
||||||
private
|
|
||||||
{ Private declarations }
|
|
||||||
public
|
|
||||||
{ Public declarations }
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
Frm: TFrm;
|
|
||||||
SecurityLayer, UserAuthentication: DWORD;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
{$R *.dfm}
|
|
||||||
|
|
||||||
procedure TFrm.FormCreate(Sender: TObject);
|
|
||||||
var
|
|
||||||
Reg: TRegistry;
|
|
||||||
begin
|
|
||||||
RDP.DisconnectedText := 'Disconnected.';
|
|
||||||
RDP.ConnectingText := 'Connecting...';
|
|
||||||
RDP.ConnectedStatusText := 'Connected.';
|
|
||||||
RDP.UserName := '';
|
|
||||||
RDP.Server := '127.0.0.2';
|
|
||||||
Reg := TRegistry.Create;
|
|
||||||
Reg.RootKey := HKEY_LOCAL_MACHINE;
|
|
||||||
|
|
||||||
if Reg.OpenKey('\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp', True) then
|
|
||||||
begin
|
|
||||||
try
|
|
||||||
SecurityLayer := Reg.ReadInteger('SecurityLayer');
|
|
||||||
UserAuthentication := Reg.ReadInteger('UserAuthentication');
|
|
||||||
Reg.WriteInteger('SecurityLayer', 0);
|
|
||||||
Reg.WriteInteger('UserAuthentication', 0);
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
Reg.CloseKey;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if Reg.OpenKeyReadOnly('\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp') then begin
|
|
||||||
try
|
|
||||||
RDP.AdvancedSettings2.RDPPort := Reg.ReadInteger('PortNumber');
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
Reg.CloseKey;
|
|
||||||
end;
|
|
||||||
Reg.Free;
|
|
||||||
Sleep(1000);
|
|
||||||
RDP.Connect;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TFrm.RDPDisconnected(ASender: TObject; discReason: Integer);
|
|
||||||
var
|
|
||||||
ErrStr: String;
|
|
||||||
Reg: TRegistry;
|
|
||||||
begin
|
|
||||||
case discReason of
|
|
||||||
1: ErrStr := 'Local disconnection.';
|
|
||||||
2: ErrStr := 'Disconnected by user.';
|
|
||||||
3: ErrStr := 'Disconnected by server.';
|
|
||||||
$904: ErrStr := 'Socket closed.';
|
|
||||||
$C08: ErrStr := 'Decompress error.';
|
|
||||||
$108: ErrStr := 'Connection timed out.';
|
|
||||||
$C06: ErrStr := 'Decryption error.';
|
|
||||||
$104: ErrStr := 'DNS name lookup failure.';
|
|
||||||
$508: ErrStr := 'DNS lookup failed.';
|
|
||||||
$B06: ErrStr := 'Encryption error.';
|
|
||||||
$604: ErrStr := 'Windows Sockets gethostbyname() call failed.';
|
|
||||||
$208: ErrStr := 'Host not found error.';
|
|
||||||
$408: ErrStr := 'Internal error.';
|
|
||||||
$906: ErrStr := 'Internal security error.';
|
|
||||||
$A06: ErrStr := 'Internal security error.';
|
|
||||||
$506: ErrStr := 'The encryption method specified is not valid.';
|
|
||||||
$804: ErrStr := 'Bad IP address specified.';
|
|
||||||
$606: ErrStr := 'Server security data is not valid.';
|
|
||||||
$406: ErrStr := 'Security data is not valid.';
|
|
||||||
$308: ErrStr := 'The IP address specified is not valid.';
|
|
||||||
$808: ErrStr := 'License negotiation failed.';
|
|
||||||
$908: ErrStr := 'Licensing time-out.';
|
|
||||||
$106: ErrStr := 'Out of memory.';
|
|
||||||
$206: ErrStr := 'Out of memory.';
|
|
||||||
$306: ErrStr := 'Out of memory.';
|
|
||||||
$706: ErrStr := 'Failed to unpack server certificate.';
|
|
||||||
$204: ErrStr := 'Socket connection failed.';
|
|
||||||
$404: ErrStr := 'Windows Sockets recv() call failed.';
|
|
||||||
$704: ErrStr := 'Time-out occurred.';
|
|
||||||
$608: ErrStr := 'Internal timer error.';
|
|
||||||
$304: ErrStr := 'Windows Sockets send() call failed.';
|
|
||||||
$B07: ErrStr := 'The account is disabled.';
|
|
||||||
$E07: ErrStr := 'The account is expired.';
|
|
||||||
$D07: ErrStr := 'The account is locked out.';
|
|
||||||
$C07: ErrStr := 'The account is restricted.';
|
|
||||||
$1B07: ErrStr := 'The received certificate is expired.';
|
|
||||||
$1607: ErrStr := 'The policy does not support delegation of credentials to the target server.';
|
|
||||||
$2107: ErrStr := 'The server authentication policy does not allow connection requests using saved credentials. The user must enter new credentials.';
|
|
||||||
$807: ErrStr := 'Login failed.';
|
|
||||||
$1807: ErrStr := 'No authority could be contacted for authentication. The domain name of the authenticating party could be wrong, the domain could be unreachable, or there might have been a trust relationship failure.';
|
|
||||||
$A07: ErrStr := 'The specified user has no account.';
|
|
||||||
$F07: ErrStr := 'The password is expired.';
|
|
||||||
$1207: ErrStr := 'The user password must be changed before logging on for the first time.';
|
|
||||||
$1707: ErrStr := 'Delegation of credentials to the target server is not allowed unless mutual authentication has been achieved.';
|
|
||||||
$2207: ErrStr := 'The smart card is blocked.';
|
|
||||||
$1C07: ErrStr := 'An incorrect PIN was presented to the smart card.';
|
|
||||||
$B09: ErrStr := 'Network Level Authentication is required, run RDPCheck as administrator.';
|
|
||||||
$708: ErrStr := 'RDP is working, but the client doesn''t allow loopback connections. Try to connect to your PC from another device in the network.';
|
|
||||||
else ErrStr := 'Unknown code 0x'+IntToHex(discReason, 1);
|
|
||||||
end;
|
|
||||||
if (discReason > 2) then
|
|
||||||
MessageBox(Handle, PWideChar(ErrStr), 'Disconnected', mb_Ok or mb_IconError);
|
|
||||||
|
|
||||||
Reg := TRegistry.Create;
|
|
||||||
Reg.RootKey := HKEY_LOCAL_MACHINE;
|
|
||||||
|
|
||||||
if Reg.OpenKey('\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp', True) then
|
|
||||||
begin
|
|
||||||
try
|
|
||||||
Reg.WriteInteger('SecurityLayer', SecurityLayer);
|
|
||||||
Reg.WriteInteger('UserAuthentication', UserAuthentication);
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
Reg.CloseKey;
|
|
||||||
end;
|
|
||||||
|
|
||||||
Reg.Free;
|
|
||||||
|
|
||||||
Halt(0);
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
|
Before Width: | Height: | Size: 161 KiB |
@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
Copyright 2014 Stas'M Corp.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
}
|
|
||||||
|
|
||||||
program RDPCheck;
|
|
||||||
|
|
||||||
uses
|
|
||||||
Forms,
|
|
||||||
MainUnit in 'MainUnit.pas' {Frm};
|
|
||||||
|
|
||||||
{$R *.res}
|
|
||||||
|
|
||||||
begin
|
|
||||||
Application.Initialize;
|
|
||||||
Application.MainFormOnTaskbar := True;
|
|
||||||
Application.Title := 'Local RDP Checker';
|
|
||||||
Application.CreateForm(TFrm, Frm);
|
|
||||||
Application.Run;
|
|
||||||
end.
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<ProjectGuid>{90AE83F6-26B8-45D4-92FE-CF4ACCDE9F68}</ProjectGuid>
|
|
||||||
<ProjectVersion>12.0</ProjectVersion>
|
|
||||||
<MainSource>RDPCheck.dpr</MainSource>
|
|
||||||
<Config Condition="'$(Config)'==''">Release</Config>
|
|
||||||
<DCC_DCCCompiler>DCC32</DCC_DCCCompiler>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
|
|
||||||
<Base>true</Base>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_1)'!=''">
|
|
||||||
<Cfg_1>true</Cfg_1>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
<Base>true</Base>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''">
|
|
||||||
<Cfg_2>true</Cfg_2>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
<Base>true</Base>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Base)'!=''">
|
|
||||||
<DCC_ExeOutput>..\bin\</DCC_ExeOutput>
|
|
||||||
<DCC_UnitAlias>WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;$(DCC_UnitAlias)</DCC_UnitAlias>
|
|
||||||
<DCC_DependencyCheckOutputName>..\bin\RDPCheck.exe</DCC_DependencyCheckOutputName>
|
|
||||||
<DCC_ImageBase>00400000</DCC_ImageBase>
|
|
||||||
<DCC_Platform>x86</DCC_Platform>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Cfg_1)'!=''">
|
|
||||||
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
|
|
||||||
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
|
|
||||||
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
|
|
||||||
<DCC_DebugInformation>false</DCC_DebugInformation>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Cfg_2)'!=''">
|
|
||||||
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<DelphiCompile Include="RDPCheck.dpr">
|
|
||||||
<MainSource>MainSource</MainSource>
|
|
||||||
</DelphiCompile>
|
|
||||||
<DCCReference Include="MainUnit.pas">
|
|
||||||
<Form>Frm</Form>
|
|
||||||
</DCCReference>
|
|
||||||
<BuildConfiguration Include="Base">
|
|
||||||
<Key>Base</Key>
|
|
||||||
</BuildConfiguration>
|
|
||||||
<BuildConfiguration Include="Release">
|
|
||||||
<Key>Cfg_1</Key>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
</BuildConfiguration>
|
|
||||||
<BuildConfiguration Include="Debug">
|
|
||||||
<Key>Cfg_2</Key>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
</BuildConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
|
|
||||||
<ProjectExtensions>
|
|
||||||
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
|
|
||||||
<Borland.ProjectType/>
|
|
||||||
<BorlandProject>
|
|
||||||
<Delphi.Personality>
|
|
||||||
<Parameters>
|
|
||||||
<Parameters Name="UseLauncher">False</Parameters>
|
|
||||||
<Parameters Name="LoadAllSymbols">True</Parameters>
|
|
||||||
<Parameters Name="LoadUnspecifiedSymbols">False</Parameters>
|
|
||||||
</Parameters>
|
|
||||||
<VersionInfo>
|
|
||||||
<VersionInfo Name="IncludeVerInfo">True</VersionInfo>
|
|
||||||
<VersionInfo Name="AutoIncBuild">False</VersionInfo>
|
|
||||||
<VersionInfo Name="MajorVer">2</VersionInfo>
|
|
||||||
<VersionInfo Name="MinorVer">2</VersionInfo>
|
|
||||||
<VersionInfo Name="Release">0</VersionInfo>
|
|
||||||
<VersionInfo Name="Build">0</VersionInfo>
|
|
||||||
<VersionInfo Name="Debug">False</VersionInfo>
|
|
||||||
<VersionInfo Name="PreRelease">False</VersionInfo>
|
|
||||||
<VersionInfo Name="Special">False</VersionInfo>
|
|
||||||
<VersionInfo Name="Private">False</VersionInfo>
|
|
||||||
<VersionInfo Name="DLL">False</VersionInfo>
|
|
||||||
<VersionInfo Name="Locale">1033</VersionInfo>
|
|
||||||
<VersionInfo Name="CodePage">1252</VersionInfo>
|
|
||||||
</VersionInfo>
|
|
||||||
<VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="CompanyName">Stas'M Corp.</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="FileDescription">Local RDP Checker</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="FileVersion">2.2.0.0</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="InternalName">RDPCheck</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="LegalCopyright">Copyright © Stas'M Corp. 2015</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="LegalTrademarks">Stas'M Corp.</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="OriginalFilename">RDPCheck.exe</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="ProductName">RDP Host Support</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="ProductVersion">1.6.0.0</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="Comments">http://stascorp.com</VersionInfoKeys>
|
|
||||||
</VersionInfoKeys>
|
|
||||||
<Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\bcboffice2k140.bpl">Embarcadero C++Builder Office 2000 Servers Package</Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\bcbofficexp140.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\dcloffice2k140.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\dclofficexp140.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
|
|
||||||
</Excluded_Packages>
|
|
||||||
<Source>
|
|
||||||
<Source Name="MainSource">RDPCheck.dpr</Source>
|
|
||||||
</Source>
|
|
||||||
</Delphi.Personality>
|
|
||||||
</BorlandProject>
|
|
||||||
<ProjectFileVersion>12</ProjectFileVersion>
|
|
||||||
</ProjectExtensions>
|
|
||||||
</Project>
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
object LicenseForm: TLicenseForm
|
|
||||||
Left = 0
|
|
||||||
Top = 0
|
|
||||||
BorderIcons = []
|
|
||||||
BorderStyle = bsDialog
|
|
||||||
Caption = 'License Agreement'
|
|
||||||
ClientHeight = 344
|
|
||||||
ClientWidth = 386
|
|
||||||
Color = clBtnFace
|
|
||||||
Font.Charset = DEFAULT_CHARSET
|
|
||||||
Font.Color = clWindowText
|
|
||||||
Font.Height = -11
|
|
||||||
Font.Name = 'Tahoma'
|
|
||||||
Font.Style = []
|
|
||||||
OldCreateOrder = False
|
|
||||||
Position = poOwnerFormCenter
|
|
||||||
PixelsPerInch = 96
|
|
||||||
TextHeight = 13
|
|
||||||
object mText: TMemo
|
|
||||||
Left = 8
|
|
||||||
Top = 8
|
|
||||||
Width = 370
|
|
||||||
Height = 297
|
|
||||||
ReadOnly = True
|
|
||||||
ScrollBars = ssBoth
|
|
||||||
TabOrder = 0
|
|
||||||
WordWrap = False
|
|
||||||
end
|
|
||||||
object bAccept: TButton
|
|
||||||
Left = 115
|
|
||||||
Top = 311
|
|
||||||
Width = 75
|
|
||||||
Height = 25
|
|
||||||
Caption = '&Accept'
|
|
||||||
ModalResult = 1
|
|
||||||
TabOrder = 1
|
|
||||||
end
|
|
||||||
object bDecline: TButton
|
|
||||||
Left = 196
|
|
||||||
Top = 311
|
|
||||||
Width = 75
|
|
||||||
Height = 25
|
|
||||||
Caption = '&Decline'
|
|
||||||
ModalResult = 2
|
|
||||||
TabOrder = 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
{
|
|
||||||
Copyright 2014 Stas'M Corp.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
}
|
|
||||||
|
|
||||||
unit LicenseUnit;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
|
|
||||||
Dialogs, StdCtrls;
|
|
||||||
|
|
||||||
type
|
|
||||||
TLicenseForm = class(TForm)
|
|
||||||
mText: TMemo;
|
|
||||||
bAccept: TButton;
|
|
||||||
bDecline: TButton;
|
|
||||||
private
|
|
||||||
{ Private declarations }
|
|
||||||
public
|
|
||||||
{ Public declarations }
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
LicenseForm: TLicenseForm;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
{$R *.dfm}
|
|
||||||
|
|
||||||
end.
|
|
||||||
@ -1,241 +0,0 @@
|
|||||||
object MainForm: TMainForm
|
|
||||||
Left = 0
|
|
||||||
Top = 0
|
|
||||||
BorderStyle = bsDialog
|
|
||||||
Caption = 'RDP Wrapper Configuration'
|
|
||||||
ClientHeight = 314
|
|
||||||
ClientWidth = 404
|
|
||||||
Color = clBtnFace
|
|
||||||
Font.Charset = DEFAULT_CHARSET
|
|
||||||
Font.Color = clWindowText
|
|
||||||
Font.Height = -11
|
|
||||||
Font.Name = 'Tahoma'
|
|
||||||
Font.Style = []
|
|
||||||
OldCreateOrder = False
|
|
||||||
Position = poDesktopCenter
|
|
||||||
OnCloseQuery = FormCloseQuery
|
|
||||||
OnCreate = FormCreate
|
|
||||||
OnDestroy = FormDestroy
|
|
||||||
PixelsPerInch = 96
|
|
||||||
TextHeight = 13
|
|
||||||
object bOK: TButton
|
|
||||||
Left = 40
|
|
||||||
Top = 281
|
|
||||||
Width = 75
|
|
||||||
Height = 25
|
|
||||||
Caption = 'OK'
|
|
||||||
ModalResult = 1
|
|
||||||
TabOrder = 4
|
|
||||||
OnClick = bOKClick
|
|
||||||
end
|
|
||||||
object bCancel: TButton
|
|
||||||
Left = 121
|
|
||||||
Top = 281
|
|
||||||
Width = 75
|
|
||||||
Height = 25
|
|
||||||
Caption = 'Cancel'
|
|
||||||
ModalResult = 2
|
|
||||||
TabOrder = 5
|
|
||||||
OnClick = bCancelClick
|
|
||||||
end
|
|
||||||
object bApply: TButton
|
|
||||||
Left = 202
|
|
||||||
Top = 281
|
|
||||||
Width = 75
|
|
||||||
Height = 25
|
|
||||||
Caption = 'Apply'
|
|
||||||
Enabled = False
|
|
||||||
TabOrder = 6
|
|
||||||
OnClick = bApplyClick
|
|
||||||
end
|
|
||||||
object rgNLA: TRadioGroup
|
|
||||||
Left = 202
|
|
||||||
Top = 89
|
|
||||||
Width = 194
|
|
||||||
Height = 73
|
|
||||||
Caption = 'Authentication Mode'
|
|
||||||
Items.Strings = (
|
|
||||||
'GUI Authentication Only'
|
|
||||||
'Default RDP Authentication'
|
|
||||||
'Network Level Authentication')
|
|
||||||
TabOrder = 2
|
|
||||||
OnClick = cbAllowTSConnectionsClick
|
|
||||||
end
|
|
||||||
object rgShadow: TRadioGroup
|
|
||||||
Left = 202
|
|
||||||
Top = 168
|
|
||||||
Width = 194
|
|
||||||
Height = 105
|
|
||||||
Caption = 'Session Shadowing Mode'
|
|
||||||
Items.Strings = (
|
|
||||||
'Disable Shadowing'
|
|
||||||
'Full access with user'#39's permission'
|
|
||||||
'Full access without permission'
|
|
||||||
'View only with user'#39's permission'
|
|
||||||
'View only without permission')
|
|
||||||
TabOrder = 3
|
|
||||||
OnClick = cbAllowTSConnectionsClick
|
|
||||||
end
|
|
||||||
object bLicense: TButton
|
|
||||||
Left = 283
|
|
||||||
Top = 281
|
|
||||||
Width = 87
|
|
||||||
Height = 25
|
|
||||||
Caption = 'View license...'
|
|
||||||
TabOrder = 7
|
|
||||||
OnClick = bLicenseClick
|
|
||||||
end
|
|
||||||
object gbDiag: TGroupBox
|
|
||||||
Left = 8
|
|
||||||
Top = 6
|
|
||||||
Width = 388
|
|
||||||
Height = 77
|
|
||||||
Caption = 'Diagnostics'
|
|
||||||
TabOrder = 0
|
|
||||||
object lListener: TLabel
|
|
||||||
Left = 11
|
|
||||||
Top = 55
|
|
||||||
Width = 70
|
|
||||||
Height = 13
|
|
||||||
Caption = 'Listener state:'
|
|
||||||
end
|
|
||||||
object lService: TLabel
|
|
||||||
Left = 11
|
|
||||||
Top = 36
|
|
||||||
Width = 67
|
|
||||||
Height = 13
|
|
||||||
Caption = 'Service state:'
|
|
||||||
end
|
|
||||||
object lsListener: TLabel
|
|
||||||
Left = 91
|
|
||||||
Top = 55
|
|
||||||
Width = 44
|
|
||||||
Height = 13
|
|
||||||
Caption = 'Unknown'
|
|
||||||
end
|
|
||||||
object lsService: TLabel
|
|
||||||
Left = 91
|
|
||||||
Top = 36
|
|
||||||
Width = 44
|
|
||||||
Height = 13
|
|
||||||
Caption = 'Unknown'
|
|
||||||
end
|
|
||||||
object lsTSVer: TLabel
|
|
||||||
Left = 226
|
|
||||||
Top = 36
|
|
||||||
Width = 44
|
|
||||||
Height = 13
|
|
||||||
Caption = 'Unknown'
|
|
||||||
end
|
|
||||||
object lsWrapper: TLabel
|
|
||||||
Left = 91
|
|
||||||
Top = 17
|
|
||||||
Width = 44
|
|
||||||
Height = 13
|
|
||||||
Caption = 'Unknown'
|
|
||||||
end
|
|
||||||
object lsWrapVer: TLabel
|
|
||||||
Left = 226
|
|
||||||
Top = 17
|
|
||||||
Width = 44
|
|
||||||
Height = 13
|
|
||||||
Caption = 'Unknown'
|
|
||||||
end
|
|
||||||
object lTSVer: TLabel
|
|
||||||
Left = 202
|
|
||||||
Top = 36
|
|
||||||
Width = 20
|
|
||||||
Height = 13
|
|
||||||
Caption = 'ver.'
|
|
||||||
end
|
|
||||||
object lWrapper: TLabel
|
|
||||||
Left = 11
|
|
||||||
Top = 17
|
|
||||||
Width = 74
|
|
||||||
Height = 13
|
|
||||||
Caption = 'Wrapper state:'
|
|
||||||
end
|
|
||||||
object lWrapVer: TLabel
|
|
||||||
Left = 202
|
|
||||||
Top = 17
|
|
||||||
Width = 20
|
|
||||||
Height = 13
|
|
||||||
Caption = 'ver.'
|
|
||||||
end
|
|
||||||
object lsSuppVer: TLabel
|
|
||||||
Left = 202
|
|
||||||
Top = 55
|
|
||||||
Width = 70
|
|
||||||
Height = 13
|
|
||||||
Caption = '[support level]'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
object gbGeneral: TGroupBox
|
|
||||||
Left = 8
|
|
||||||
Top = 89
|
|
||||||
Width = 188
|
|
||||||
Height = 184
|
|
||||||
Caption = 'General Settings'
|
|
||||||
TabOrder = 1
|
|
||||||
object lRDPPort: TLabel
|
|
||||||
Left = 8
|
|
||||||
Top = 44
|
|
||||||
Width = 47
|
|
||||||
Height = 13
|
|
||||||
Caption = 'RDP port:'
|
|
||||||
end
|
|
||||||
object cbAllowTSConnections: TCheckBox
|
|
||||||
Left = 8
|
|
||||||
Top = 18
|
|
||||||
Width = 132
|
|
||||||
Height = 17
|
|
||||||
Caption = 'Enable Remote Desktop'
|
|
||||||
TabOrder = 0
|
|
||||||
OnClick = cbAllowTSConnectionsClick
|
|
||||||
end
|
|
||||||
object cbSingleSessionPerUser: TCheckBox
|
|
||||||
Left = 8
|
|
||||||
Top = 69
|
|
||||||
Width = 129
|
|
||||||
Height = 17
|
|
||||||
Caption = 'Single session per user'
|
|
||||||
TabOrder = 2
|
|
||||||
OnClick = cbAllowTSConnectionsClick
|
|
||||||
end
|
|
||||||
object cbHideUsers: TCheckBox
|
|
||||||
Left = 8
|
|
||||||
Top = 92
|
|
||||||
Width = 149
|
|
||||||
Height = 17
|
|
||||||
Caption = 'Hide users on logon screen'
|
|
||||||
TabOrder = 3
|
|
||||||
OnClick = cbAllowTSConnectionsClick
|
|
||||||
end
|
|
||||||
object seRDPPort: TSpinEdit
|
|
||||||
Left = 61
|
|
||||||
Top = 41
|
|
||||||
Width = 62
|
|
||||||
Height = 22
|
|
||||||
MaxValue = 65535
|
|
||||||
MinValue = 0
|
|
||||||
TabOrder = 1
|
|
||||||
Value = 0
|
|
||||||
OnChange = seRDPPortChange
|
|
||||||
end
|
|
||||||
object cbCustomPrg: TCheckBox
|
|
||||||
Left = 8
|
|
||||||
Top = 115
|
|
||||||
Width = 169
|
|
||||||
Height = 17
|
|
||||||
Caption = 'Allow to start custom programs'
|
|
||||||
TabOrder = 4
|
|
||||||
OnClick = cbAllowTSConnectionsClick
|
|
||||||
end
|
|
||||||
end
|
|
||||||
object Timer: TTimer
|
|
||||||
Interval = 250
|
|
||||||
OnTimer = TimerTimer
|
|
||||||
Left = 352
|
|
||||||
Top = 27
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,722 +0,0 @@
|
|||||||
{
|
|
||||||
Copyright 2017 Stas'M Corp.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
}
|
|
||||||
|
|
||||||
unit MainUnit;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
|
|
||||||
Dialogs, StdCtrls, Spin, ExtCtrls, Registry, WinSvc;
|
|
||||||
|
|
||||||
type
|
|
||||||
TMainForm = class(TForm)
|
|
||||||
bOK: TButton;
|
|
||||||
bCancel: TButton;
|
|
||||||
bApply: TButton;
|
|
||||||
cbSingleSessionPerUser: TCheckBox;
|
|
||||||
rgNLA: TRadioGroup;
|
|
||||||
cbAllowTSConnections: TCheckBox;
|
|
||||||
rgShadow: TRadioGroup;
|
|
||||||
seRDPPort: TSpinEdit;
|
|
||||||
lRDPPort: TLabel;
|
|
||||||
lService: TLabel;
|
|
||||||
lListener: TLabel;
|
|
||||||
lWrapper: TLabel;
|
|
||||||
lsListener: TLabel;
|
|
||||||
lsService: TLabel;
|
|
||||||
lsWrapper: TLabel;
|
|
||||||
Timer: TTimer;
|
|
||||||
lTSVer: TLabel;
|
|
||||||
lsTSVer: TLabel;
|
|
||||||
lWrapVer: TLabel;
|
|
||||||
lsWrapVer: TLabel;
|
|
||||||
bLicense: TButton;
|
|
||||||
gbDiag: TGroupBox;
|
|
||||||
lsSuppVer: TLabel;
|
|
||||||
cbHideUsers: TCheckBox;
|
|
||||||
gbGeneral: TGroupBox;
|
|
||||||
cbCustomPrg: TCheckBox;
|
|
||||||
procedure FormCreate(Sender: TObject);
|
|
||||||
procedure cbAllowTSConnectionsClick(Sender: TObject);
|
|
||||||
procedure seRDPPortChange(Sender: TObject);
|
|
||||||
procedure bApplyClick(Sender: TObject);
|
|
||||||
procedure bCancelClick(Sender: TObject);
|
|
||||||
procedure bOKClick(Sender: TObject);
|
|
||||||
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
|
||||||
procedure bLicenseClick(Sender: TObject);
|
|
||||||
procedure TimerTimer(Sender: TObject);
|
|
||||||
procedure FormDestroy(Sender: TObject);
|
|
||||||
private
|
|
||||||
{ Private declarations }
|
|
||||||
public
|
|
||||||
{ Public declarations }
|
|
||||||
function ExecWait(Cmdline: String): Boolean;
|
|
||||||
procedure ReadSettings;
|
|
||||||
procedure WriteSettings;
|
|
||||||
end;
|
|
||||||
FILE_VERSION = record
|
|
||||||
Version: record case Boolean of
|
|
||||||
True: (dw: DWORD);
|
|
||||||
False: (w: record
|
|
||||||
Minor, Major: Word;
|
|
||||||
end;)
|
|
||||||
end;
|
|
||||||
Release, Build: Word;
|
|
||||||
bDebug, bPrerelease, bPrivate, bSpecial: Boolean;
|
|
||||||
end;
|
|
||||||
WTS_SESSION_INFOW = record
|
|
||||||
SessionId: DWORD;
|
|
||||||
Name: packed array [0..33] of WideChar;
|
|
||||||
State: DWORD;
|
|
||||||
end;
|
|
||||||
WTS_SESSION = Array[0..0] of WTS_SESSION_INFOW;
|
|
||||||
PWTS_SESSION_INFOW = ^WTS_SESSION;
|
|
||||||
|
|
||||||
const
|
|
||||||
winstadll = 'winsta.dll';
|
|
||||||
var
|
|
||||||
MainForm: TMainForm;
|
|
||||||
Ready: Boolean = False;
|
|
||||||
Arch: Byte;
|
|
||||||
OldWow64RedirectionValue: LongBool;
|
|
||||||
OldPort: Word;
|
|
||||||
INI: String;
|
|
||||||
|
|
||||||
function WinStationEnumerateW(hServer: THandle;
|
|
||||||
var ppSessionInfo: PWTS_SESSION_INFOW; var pCount: DWORD): BOOL; stdcall;
|
|
||||||
external winstadll name 'WinStationEnumerateW';
|
|
||||||
function WinStationFreeMemory(P: Pointer): BOOL; stdcall; external winstadll;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
{$R *.dfm}
|
|
||||||
{$R resource.res}
|
|
||||||
|
|
||||||
uses
|
|
||||||
LicenseUnit;
|
|
||||||
|
|
||||||
function ExpandPath(Path: String): String;
|
|
||||||
var
|
|
||||||
Str: Array[0..511] of Char;
|
|
||||||
begin
|
|
||||||
Result := '';
|
|
||||||
FillChar(Str, 512, 0);
|
|
||||||
if Arch = 64 then
|
|
||||||
Path := StringReplace(Path, '%ProgramFiles%', '%ProgramW6432%', [rfReplaceAll, rfIgnoreCase]);
|
|
||||||
if ExpandEnvironmentStrings(PWideChar(Path), Str, 512) > 0 then
|
|
||||||
Result := Str;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function DisableWowRedirection: Boolean;
|
|
||||||
type
|
|
||||||
TFunc = function(var Wow64FsEnableRedirection: LongBool): LongBool; stdcall;
|
|
||||||
var
|
|
||||||
hModule: THandle;
|
|
||||||
Wow64DisableWow64FsRedirection: TFunc;
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
hModule := GetModuleHandle(kernel32);
|
|
||||||
if hModule <> 0 then
|
|
||||||
Wow64DisableWow64FsRedirection := GetProcAddress(hModule, 'Wow64DisableWow64FsRedirection')
|
|
||||||
else
|
|
||||||
Exit;
|
|
||||||
if @Wow64DisableWow64FsRedirection <> nil then
|
|
||||||
Result := Wow64DisableWow64FsRedirection(OldWow64RedirectionValue);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function RevertWowRedirection: Boolean;
|
|
||||||
type
|
|
||||||
TFunc = function(var Wow64RevertWow64FsRedirection: LongBool): LongBool; stdcall;
|
|
||||||
var
|
|
||||||
hModule: THandle;
|
|
||||||
Wow64RevertWow64FsRedirection: TFunc;
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
hModule := GetModuleHandle(kernel32);
|
|
||||||
if hModule <> 0 then
|
|
||||||
Wow64RevertWow64FsRedirection := GetProcAddress(hModule, 'Wow64RevertWow64FsRedirection')
|
|
||||||
else
|
|
||||||
Exit;
|
|
||||||
if @Wow64RevertWow64FsRedirection <> nil then
|
|
||||||
Result := Wow64RevertWow64FsRedirection(OldWow64RedirectionValue);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function GetFileVersion(const FileName: TFileName; var FileVersion: FILE_VERSION): Boolean;
|
|
||||||
type
|
|
||||||
VS_VERSIONINFO = record
|
|
||||||
wLength, wValueLength, wType: Word;
|
|
||||||
szKey: Array[1..16] of WideChar;
|
|
||||||
Padding1: Word;
|
|
||||||
Value: VS_FIXEDFILEINFO;
|
|
||||||
Padding2, Children: Word;
|
|
||||||
end;
|
|
||||||
PVS_VERSIONINFO = ^VS_VERSIONINFO;
|
|
||||||
const
|
|
||||||
VFF_DEBUG = 1;
|
|
||||||
VFF_PRERELEASE = 2;
|
|
||||||
VFF_PRIVATE = 8;
|
|
||||||
VFF_SPECIAL = 32;
|
|
||||||
var
|
|
||||||
hFile: HMODULE;
|
|
||||||
hResourceInfo: HRSRC;
|
|
||||||
VersionInfo: PVS_VERSIONINFO;
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
|
|
||||||
hFile := LoadLibraryEx(PWideChar(FileName), 0, LOAD_LIBRARY_AS_DATAFILE);
|
|
||||||
if hFile = 0 then
|
|
||||||
Exit;
|
|
||||||
|
|
||||||
hResourceInfo := FindResource(hFile, PWideChar(1), PWideChar($10));
|
|
||||||
if hResourceInfo = 0 then
|
|
||||||
Exit;
|
|
||||||
|
|
||||||
VersionInfo := Pointer(LoadResource(hFile, hResourceInfo));
|
|
||||||
if VersionInfo = nil then
|
|
||||||
Exit;
|
|
||||||
|
|
||||||
FileVersion.Version.dw := VersionInfo.Value.dwFileVersionMS;
|
|
||||||
FileVersion.Release := Word(VersionInfo.Value.dwFileVersionLS shr 16);
|
|
||||||
FileVersion.Build := Word(VersionInfo.Value.dwFileVersionLS);
|
|
||||||
FileVersion.bDebug := (VersionInfo.Value.dwFileFlags and VFF_DEBUG) = VFF_DEBUG;
|
|
||||||
FileVersion.bPrerelease := (VersionInfo.Value.dwFileFlags and VFF_PRERELEASE) = VFF_PRERELEASE;
|
|
||||||
FileVersion.bPrivate := (VersionInfo.Value.dwFileFlags and VFF_PRIVATE) = VFF_PRIVATE;
|
|
||||||
FileVersion.bSpecial := (VersionInfo.Value.dwFileFlags and VFF_SPECIAL) = VFF_SPECIAL;
|
|
||||||
|
|
||||||
FreeLibrary(hFile);
|
|
||||||
Result := True;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function IsWrapperInstalled(var WrapperPath: String): ShortInt;
|
|
||||||
var
|
|
||||||
TermServiceHost,
|
|
||||||
TermServicePath: String;
|
|
||||||
Reg: TRegistry;
|
|
||||||
begin
|
|
||||||
Result := -1;
|
|
||||||
WrapperPath := '';
|
|
||||||
Reg := TRegistry.Create;
|
|
||||||
Reg.RootKey := HKEY_LOCAL_MACHINE;
|
|
||||||
if not Reg.OpenKeyReadOnly('\SYSTEM\CurrentControlSet\Services\TermService') then begin
|
|
||||||
Reg.Free;
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
TermServiceHost := Reg.ReadString('ImagePath');
|
|
||||||
Reg.CloseKey;
|
|
||||||
if Pos('svchost.exe', LowerCase(TermServiceHost)) = 0 then
|
|
||||||
begin
|
|
||||||
Result := 2;
|
|
||||||
Reg.Free;
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
if not Reg.OpenKeyReadOnly('\SYSTEM\CurrentControlSet\Services\TermService\Parameters') then
|
|
||||||
begin
|
|
||||||
Reg.Free;
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
TermServicePath := Reg.ReadString('ServiceDll');
|
|
||||||
Reg.CloseKey;
|
|
||||||
Reg.Free;
|
|
||||||
if (Pos('termsrv.dll', LowerCase(TermServicePath)) = 0)
|
|
||||||
and (Pos('rdpwrap.dll', LowerCase(TermServicePath)) = 0) then
|
|
||||||
begin
|
|
||||||
Result := 2;
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if Pos('rdpwrap.dll', LowerCase(TermServicePath)) > 0 then begin
|
|
||||||
WrapperPath := TermServicePath;
|
|
||||||
Result := 1;
|
|
||||||
end else
|
|
||||||
Result := 0;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function GetTermSrvState: ShortInt;
|
|
||||||
type
|
|
||||||
SERVICE_STATUS_PROCESS = record
|
|
||||||
dwServiceType,
|
|
||||||
dwCurrentState,
|
|
||||||
dwControlsAccepted,
|
|
||||||
dwWin32ExitCode,
|
|
||||||
dwServiceSpecificExitCode,
|
|
||||||
dwCheckPoint,
|
|
||||||
dwWaitHint,
|
|
||||||
dwProcessId,
|
|
||||||
dwServiceFlags: DWORD;
|
|
||||||
end;
|
|
||||||
PSERVICE_STATUS_PROCESS = ^SERVICE_STATUS_PROCESS;
|
|
||||||
const
|
|
||||||
SvcName = 'TermService';
|
|
||||||
var
|
|
||||||
hSC: SC_HANDLE;
|
|
||||||
hSvc: THandle;
|
|
||||||
lpServiceStatusProcess: PSERVICE_STATUS_PROCESS;
|
|
||||||
Buf: Pointer;
|
|
||||||
cbBufSize, pcbBytesNeeded: Cardinal;
|
|
||||||
begin
|
|
||||||
Result := -1;
|
|
||||||
hSC := OpenSCManager(nil, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
|
|
||||||
if hSC = 0 then
|
|
||||||
Exit;
|
|
||||||
|
|
||||||
hSvc := OpenService(hSC, PWideChar(SvcName), SERVICE_QUERY_STATUS);
|
|
||||||
if hSvc = 0 then
|
|
||||||
begin
|
|
||||||
CloseServiceHandle(hSC);
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if QueryServiceStatusEx(hSvc, SC_STATUS_PROCESS_INFO, nil, 0, pcbBytesNeeded) then
|
|
||||||
Exit;
|
|
||||||
|
|
||||||
cbBufSize := pcbBytesNeeded;
|
|
||||||
GetMem(Buf, cbBufSize);
|
|
||||||
|
|
||||||
if not QueryServiceStatusEx(hSvc, SC_STATUS_PROCESS_INFO, Buf, cbBufSize, pcbBytesNeeded) then begin
|
|
||||||
FreeMem(Buf, cbBufSize);
|
|
||||||
CloseServiceHandle(hSvc);
|
|
||||||
CloseServiceHandle(hSC);
|
|
||||||
Exit;
|
|
||||||
end else begin
|
|
||||||
lpServiceStatusProcess := Buf;
|
|
||||||
Result := ShortInt(lpServiceStatusProcess^.dwCurrentState);
|
|
||||||
end;
|
|
||||||
FreeMem(Buf, cbBufSize);
|
|
||||||
CloseServiceHandle(hSvc);
|
|
||||||
CloseServiceHandle(hSC);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function IsListenerWorking: Boolean;
|
|
||||||
var
|
|
||||||
pCount: DWORD;
|
|
||||||
SessionInfo: PWTS_SESSION_INFOW;
|
|
||||||
I: Integer;
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
if not WinStationEnumerateW(0, SessionInfo, pCount) then
|
|
||||||
Exit;
|
|
||||||
for I := 0 to pCount - 1 do
|
|
||||||
if SessionInfo^[I].Name = 'RDP-Tcp' then begin
|
|
||||||
Result := True;
|
|
||||||
Break;
|
|
||||||
end;
|
|
||||||
WinStationFreeMemory(SessionInfo);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function ExtractResText(ResName: String): String;
|
|
||||||
var
|
|
||||||
ResStream: TResourceStream;
|
|
||||||
Str: TStringList;
|
|
||||||
begin
|
|
||||||
ResStream := TResourceStream.Create(HInstance, ResName, RT_RCDATA);
|
|
||||||
Str := TStringList.Create;
|
|
||||||
try
|
|
||||||
Str.LoadFromStream(ResStream);
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
ResStream.Free;
|
|
||||||
Result := Str.Text;
|
|
||||||
Str.Free;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TMainForm.ExecWait(Cmdline: String): Boolean;
|
|
||||||
var
|
|
||||||
si: STARTUPINFO;
|
|
||||||
pi: PROCESS_INFORMATION;
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
ZeroMemory(@si, sizeof(si));
|
|
||||||
si.cb := sizeof(si);
|
|
||||||
si.dwFlags := STARTF_USESHOWWINDOW;
|
|
||||||
si.wShowWindow := SW_HIDE;
|
|
||||||
UniqueString(Cmdline);
|
|
||||||
if not CreateProcess(nil, PWideChar(Cmdline), nil, nil, True, 0, nil, nil, si, pi) then begin
|
|
||||||
MessageBox(Handle,
|
|
||||||
PWideChar('CreateProcess error (code: ' + IntToStr(GetLastError) + ').'),
|
|
||||||
'Error', MB_ICONERROR or MB_OK);
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
CloseHandle(pi.hThread);
|
|
||||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
||||||
CloseHandle(pi.hProcess);
|
|
||||||
Result := True;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMainForm.ReadSettings;
|
|
||||||
var
|
|
||||||
Reg: TRegistry;
|
|
||||||
SecurityLayer, UserAuthentication: Integer;
|
|
||||||
begin
|
|
||||||
Reg := TRegistry.Create;
|
|
||||||
Reg.RootKey := HKEY_LOCAL_MACHINE;
|
|
||||||
Reg.OpenKeyReadOnly('\SYSTEM\CurrentControlSet\Control\Terminal Server');
|
|
||||||
try
|
|
||||||
cbAllowTSConnections.Checked := not Reg.ReadBool('fDenyTSConnections');
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
try
|
|
||||||
cbSingleSessionPerUser.Checked := Reg.ReadBool('fSingleSessionPerUser');
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
try
|
|
||||||
cbCustomPrg.Checked := Reg.ReadBool('HonorLegacySettings');
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
Reg.CloseKey;
|
|
||||||
|
|
||||||
Reg.OpenKeyReadOnly('\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp');
|
|
||||||
seRDPPort.Value := 3389;
|
|
||||||
try
|
|
||||||
seRDPPort.Value := Reg.ReadInteger('PortNumber');
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
OldPort := seRDPPort.Value;
|
|
||||||
SecurityLayer := 0;
|
|
||||||
UserAuthentication := 0;
|
|
||||||
try
|
|
||||||
SecurityLayer := Reg.ReadInteger('SecurityLayer');
|
|
||||||
UserAuthentication := Reg.ReadInteger('UserAuthentication');
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
if (SecurityLayer = 0) and (UserAuthentication = 0) then
|
|
||||||
rgNLA.ItemIndex := 0;
|
|
||||||
if (SecurityLayer = 1) and (UserAuthentication = 0) then
|
|
||||||
rgNLA.ItemIndex := 1;
|
|
||||||
if (SecurityLayer = 2) and (UserAuthentication = 1) then
|
|
||||||
rgNLA.ItemIndex := 2;
|
|
||||||
try
|
|
||||||
rgShadow.ItemIndex := Reg.ReadInteger('Shadow');
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
Reg.CloseKey;
|
|
||||||
Reg.OpenKeyReadOnly('\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System');
|
|
||||||
try
|
|
||||||
cbHideUsers.Checked := Reg.ReadBool('dontdisplaylastusername');
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
Reg.CloseKey;
|
|
||||||
Reg.Free;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMainForm.WriteSettings;
|
|
||||||
var
|
|
||||||
Reg: TRegistry;
|
|
||||||
SecurityLayer, UserAuthentication: Integer;
|
|
||||||
begin
|
|
||||||
Reg := TRegistry.Create;
|
|
||||||
Reg.RootKey := HKEY_LOCAL_MACHINE;
|
|
||||||
Reg.OpenKey('\SYSTEM\CurrentControlSet\Control\Terminal Server', True);
|
|
||||||
try
|
|
||||||
Reg.WriteBool('fDenyTSConnections', not cbAllowTSConnections.Checked);
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
try
|
|
||||||
Reg.WriteBool('fSingleSessionPerUser', cbSingleSessionPerUser.Checked);
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
try
|
|
||||||
Reg.WriteBool('HonorLegacySettings', cbCustomPrg.Checked);
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
Reg.CloseKey;
|
|
||||||
|
|
||||||
Reg.OpenKey('\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp', True);
|
|
||||||
try
|
|
||||||
Reg.WriteInteger('PortNumber', seRDPPort.Value);
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
if OldPort <> seRDPPort.Value then
|
|
||||||
begin
|
|
||||||
OldPort := seRDPPort.Value;
|
|
||||||
ExecWait('netsh advfirewall firewall set rule name="Remote Desktop" new localport=' + IntToStr(OldPort));
|
|
||||||
end;
|
|
||||||
case rgNLA.ItemIndex of
|
|
||||||
0: begin
|
|
||||||
SecurityLayer := 0;
|
|
||||||
UserAuthentication := 0;
|
|
||||||
end;
|
|
||||||
1: begin
|
|
||||||
SecurityLayer := 1;
|
|
||||||
UserAuthentication := 0;
|
|
||||||
end;
|
|
||||||
2: begin
|
|
||||||
SecurityLayer := 2;
|
|
||||||
UserAuthentication := 1;
|
|
||||||
end;
|
|
||||||
else begin
|
|
||||||
SecurityLayer := -1;
|
|
||||||
UserAuthentication := -1;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if SecurityLayer >= 0 then begin
|
|
||||||
try
|
|
||||||
Reg.WriteInteger('SecurityLayer', SecurityLayer);
|
|
||||||
Reg.WriteInteger('UserAuthentication', UserAuthentication);
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if rgShadow.ItemIndex >= 0 then begin
|
|
||||||
try
|
|
||||||
Reg.WriteInteger('Shadow', rgShadow.ItemIndex);
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Reg.CloseKey;
|
|
||||||
Reg.OpenKey('\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services', True);
|
|
||||||
if rgShadow.ItemIndex >= 0 then begin
|
|
||||||
try
|
|
||||||
Reg.WriteInteger('Shadow', rgShadow.ItemIndex);
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Reg.CloseKey;
|
|
||||||
Reg.OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System', True);
|
|
||||||
try
|
|
||||||
Reg.WriteBool('dontdisplaylastusername', cbHideUsers.Checked);
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
Reg.CloseKey;
|
|
||||||
Reg.Free;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function CheckSupport(FV: FILE_VERSION): Byte;
|
|
||||||
var
|
|
||||||
VerTxt: String;
|
|
||||||
begin
|
|
||||||
Result := 0;
|
|
||||||
if (FV.Version.w.Major = 6) and (FV.Version.w.Minor = 0) then
|
|
||||||
Result := 1;
|
|
||||||
if (FV.Version.w.Major = 6) and (FV.Version.w.Minor = 1) then
|
|
||||||
Result := 1;
|
|
||||||
VerTxt := Format('%d.%d.%d.%d',
|
|
||||||
[FV.Version.w.Major, FV.Version.w.Minor, FV.Release, FV.Build]);
|
|
||||||
if Pos('[' + VerTxt + ']', INI) > 0 then
|
|
||||||
Result := 2;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMainForm.TimerTimer(Sender: TObject);
|
|
||||||
var
|
|
||||||
WrapperPath, INIPath: String;
|
|
||||||
FV: FILE_VERSION;
|
|
||||||
L: TStringList;
|
|
||||||
CheckSupp: Boolean;
|
|
||||||
begin
|
|
||||||
CheckSupp := False;
|
|
||||||
case IsWrapperInstalled(WrapperPath) of
|
|
||||||
-1: begin
|
|
||||||
lsWrapper.Caption := 'Unknown';
|
|
||||||
lsWrapper.Font.Color := clGrayText;
|
|
||||||
end;
|
|
||||||
0: begin
|
|
||||||
lsWrapper.Caption := 'Not installed';
|
|
||||||
lsWrapper.Font.Color := clGrayText;
|
|
||||||
end;
|
|
||||||
1: begin
|
|
||||||
lsWrapper.Caption := 'Installed';
|
|
||||||
lsWrapper.Font.Color := clGreen;
|
|
||||||
CheckSupp := True;
|
|
||||||
INIPath := ExtractFilePath(ExpandPath(WrapperPath)) + 'rdpwrap.ini';
|
|
||||||
if not FileExists(INIPath) then
|
|
||||||
CheckSupp := False;
|
|
||||||
end;
|
|
||||||
2: begin
|
|
||||||
lsWrapper.Caption := '3rd-party';
|
|
||||||
lsWrapper.Font.Color := clRed;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
case GetTermSrvState of
|
|
||||||
-1, 0: begin
|
|
||||||
lsService.Caption := 'Unknown';
|
|
||||||
lsService.Font.Color := clGrayText;
|
|
||||||
end;
|
|
||||||
SERVICE_STOPPED: begin
|
|
||||||
lsService.Caption := 'Stopped';
|
|
||||||
lsService.Font.Color := clRed;
|
|
||||||
end;
|
|
||||||
SERVICE_START_PENDING: begin
|
|
||||||
lsService.Caption := 'Starting...';
|
|
||||||
lsService.Font.Color := clGrayText;
|
|
||||||
end;
|
|
||||||
SERVICE_STOP_PENDING: begin
|
|
||||||
lsService.Caption := 'Stopping...';
|
|
||||||
lsService.Font.Color := clGrayText;
|
|
||||||
end;
|
|
||||||
SERVICE_RUNNING: begin
|
|
||||||
lsService.Caption := 'Running';
|
|
||||||
lsService.Font.Color := clGreen;
|
|
||||||
end;
|
|
||||||
SERVICE_CONTINUE_PENDING: begin
|
|
||||||
lsService.Caption := 'Resuming...';
|
|
||||||
lsService.Font.Color := clGrayText;
|
|
||||||
end;
|
|
||||||
SERVICE_PAUSE_PENDING: begin
|
|
||||||
lsService.Caption := 'Suspending...';
|
|
||||||
lsService.Font.Color := clGrayText;
|
|
||||||
end;
|
|
||||||
SERVICE_PAUSED: begin
|
|
||||||
lsService.Caption := 'Suspended';
|
|
||||||
lsService.Font.Color := clWindowText;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if IsListenerWorking then begin
|
|
||||||
lsListener.Caption := 'Listening';
|
|
||||||
lsListener.Font.Color := clGreen;
|
|
||||||
end else begin
|
|
||||||
lsListener.Caption := 'Not listening';
|
|
||||||
lsListener.Font.Color := clRed;
|
|
||||||
end;
|
|
||||||
if WrapperPath = '' then begin
|
|
||||||
lsWrapVer.Caption := 'N/A';
|
|
||||||
lsWrapVer.Font.Color := clGrayText;
|
|
||||||
end else
|
|
||||||
if not GetFileVersion(ExpandPath(WrapperPath), FV) then begin
|
|
||||||
lsWrapVer.Caption := 'N/A';
|
|
||||||
lsWrapVer.Font.Color := clGrayText;
|
|
||||||
end else begin
|
|
||||||
lsWrapVer.Caption :=
|
|
||||||
IntToStr(FV.Version.w.Major)+'.'+
|
|
||||||
IntToStr(FV.Version.w.Minor)+'.'+
|
|
||||||
IntToStr(FV.Release)+'.'+
|
|
||||||
IntToStr(FV.Build);
|
|
||||||
lsWrapVer.Font.Color := clWindowText;
|
|
||||||
end;
|
|
||||||
if not GetFileVersion('termsrv.dll', FV) then begin
|
|
||||||
lsTSVer.Caption := 'N/A';
|
|
||||||
lsTSVer.Font.Color := clGrayText;
|
|
||||||
end else begin
|
|
||||||
lsTSVer.Caption :=
|
|
||||||
IntToStr(FV.Version.w.Major)+'.'+
|
|
||||||
IntToStr(FV.Version.w.Minor)+'.'+
|
|
||||||
IntToStr(FV.Release)+'.'+
|
|
||||||
IntToStr(FV.Build);
|
|
||||||
lsTSVer.Font.Color := clWindowText;
|
|
||||||
lsSuppVer.Visible := CheckSupp;
|
|
||||||
if CheckSupp then begin
|
|
||||||
if INI = '' then begin
|
|
||||||
L := TStringList.Create;
|
|
||||||
try
|
|
||||||
L.LoadFromFile(INIPath);
|
|
||||||
except
|
|
||||||
|
|
||||||
end;
|
|
||||||
INI := L.Text;
|
|
||||||
L.Free;
|
|
||||||
end;
|
|
||||||
case CheckSupport(FV) of
|
|
||||||
0: begin
|
|
||||||
lsSuppVer.Caption := '[not supported]';
|
|
||||||
lsSuppVer.Font.Color := clRed;
|
|
||||||
end;
|
|
||||||
1: begin
|
|
||||||
lsSuppVer.Caption := '[supported partially]';
|
|
||||||
lsSuppVer.Font.Color := clOlive;
|
|
||||||
end;
|
|
||||||
2: begin
|
|
||||||
lsSuppVer.Caption := '[fully supported]';
|
|
||||||
lsSuppVer.Font.Color := clGreen;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMainForm.bLicenseClick(Sender: TObject);
|
|
||||||
begin
|
|
||||||
LicenseForm.mText.Text := ExtractResText('LICENSE');
|
|
||||||
if LicenseForm.ShowModal <> mrOk then
|
|
||||||
Halt(0);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMainForm.cbAllowTSConnectionsClick(Sender: TObject);
|
|
||||||
begin
|
|
||||||
if Ready then
|
|
||||||
bApply.Enabled := True;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMainForm.seRDPPortChange(Sender: TObject);
|
|
||||||
begin
|
|
||||||
if Ready then
|
|
||||||
bApply.Enabled := True;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMainForm.FormCreate(Sender: TObject);
|
|
||||||
var
|
|
||||||
SI: TSystemInfo;
|
|
||||||
begin
|
|
||||||
GetNativeSystemInfo(SI);
|
|
||||||
case SI.wProcessorArchitecture of
|
|
||||||
0: Arch := 32;
|
|
||||||
6: Arch := 64; // Itanium-based x64
|
|
||||||
9: Arch := 64; // Intel/AMD x64
|
|
||||||
else Arch := 0;
|
|
||||||
end;
|
|
||||||
if Arch = 64 then
|
|
||||||
DisableWowRedirection;
|
|
||||||
ReadSettings;
|
|
||||||
Ready := True;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMainForm.FormDestroy(Sender: TObject);
|
|
||||||
begin
|
|
||||||
if Arch = 64 then
|
|
||||||
RevertWowRedirection;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
|
||||||
begin
|
|
||||||
if bApply.Enabled then
|
|
||||||
CanClose := MessageBox(Handle, 'Settings are not saved. Do you want to exit?',
|
|
||||||
'Warning', mb_IconWarning or mb_YesNo) = mrYes;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMainForm.bOKClick(Sender: TObject);
|
|
||||||
begin
|
|
||||||
if bApply.Enabled then begin
|
|
||||||
WriteSettings;
|
|
||||||
bApply.Enabled := False;
|
|
||||||
end;
|
|
||||||
Close;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMainForm.bCancelClick(Sender: TObject);
|
|
||||||
begin
|
|
||||||
Close;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMainForm.bApplyClick(Sender: TObject);
|
|
||||||
begin
|
|
||||||
WriteSettings;
|
|
||||||
bApply.Enabled := False;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
{
|
|
||||||
Copyright 2014 Stas'M Corp.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
}
|
|
||||||
|
|
||||||
program RDPConf;
|
|
||||||
|
|
||||||
uses
|
|
||||||
Forms,
|
|
||||||
MainUnit in 'MainUnit.pas' {MainForm},
|
|
||||||
LicenseUnit in 'LicenseUnit.pas' {LicenseForm};
|
|
||||||
|
|
||||||
{$R *.res}
|
|
||||||
|
|
||||||
begin
|
|
||||||
Application.Initialize;
|
|
||||||
Application.MainFormOnTaskbar := True;
|
|
||||||
Application.Title := 'Remote Desktop Protocol Configuration';
|
|
||||||
Application.CreateForm(TMainForm, MainForm);
|
|
||||||
Application.CreateForm(TLicenseForm, LicenseForm);
|
|
||||||
Application.Run;
|
|
||||||
end.
|
|
||||||
@ -1,111 +0,0 @@
|
|||||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<ProjectGuid>{A7CB4C30-85F5-4D96-B510-6F0CDCF7C2DA}</ProjectGuid>
|
|
||||||
<ProjectVersion>12.0</ProjectVersion>
|
|
||||||
<MainSource>RDPConf.dpr</MainSource>
|
|
||||||
<Config Condition="'$(Config)'==''">Debug</Config>
|
|
||||||
<DCC_DCCCompiler>DCC32</DCC_DCCCompiler>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
|
|
||||||
<Base>true</Base>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_1)'!=''">
|
|
||||||
<Cfg_1>true</Cfg_1>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
<Base>true</Base>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''">
|
|
||||||
<Cfg_2>true</Cfg_2>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
<Base>true</Base>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Base)'!=''">
|
|
||||||
<DCC_ExeOutput>..\bin\</DCC_ExeOutput>
|
|
||||||
<DCC_UnitAlias>WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;$(DCC_UnitAlias)</DCC_UnitAlias>
|
|
||||||
<DCC_DependencyCheckOutputName>..\bin\RDPConf.exe</DCC_DependencyCheckOutputName>
|
|
||||||
<DCC_ImageBase>00400000</DCC_ImageBase>
|
|
||||||
<DCC_Platform>x86</DCC_Platform>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Cfg_1)'!=''">
|
|
||||||
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
|
|
||||||
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
|
|
||||||
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
|
|
||||||
<DCC_DebugInformation>false</DCC_DebugInformation>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Cfg_2)'!=''">
|
|
||||||
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<DelphiCompile Include="RDPConf.dpr">
|
|
||||||
<MainSource>MainSource</MainSource>
|
|
||||||
</DelphiCompile>
|
|
||||||
<DCCReference Include="MainUnit.pas">
|
|
||||||
<Form>MainForm</Form>
|
|
||||||
</DCCReference>
|
|
||||||
<DCCReference Include="LicenseUnit.pas">
|
|
||||||
<Form>LicenseForm</Form>
|
|
||||||
</DCCReference>
|
|
||||||
<BuildConfiguration Include="Base">
|
|
||||||
<Key>Base</Key>
|
|
||||||
</BuildConfiguration>
|
|
||||||
<BuildConfiguration Include="Release">
|
|
||||||
<Key>Cfg_1</Key>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
</BuildConfiguration>
|
|
||||||
<BuildConfiguration Include="Debug">
|
|
||||||
<Key>Cfg_2</Key>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
</BuildConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
|
|
||||||
<ProjectExtensions>
|
|
||||||
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
|
|
||||||
<Borland.ProjectType/>
|
|
||||||
<BorlandProject>
|
|
||||||
<Delphi.Personality>
|
|
||||||
<Source>
|
|
||||||
<Source Name="MainSource">RDPConf.dpr</Source>
|
|
||||||
</Source>
|
|
||||||
<Parameters>
|
|
||||||
<Parameters Name="UseLauncher">False</Parameters>
|
|
||||||
<Parameters Name="LoadAllSymbols">True</Parameters>
|
|
||||||
<Parameters Name="LoadUnspecifiedSymbols">False</Parameters>
|
|
||||||
</Parameters>
|
|
||||||
<VersionInfo>
|
|
||||||
<VersionInfo Name="IncludeVerInfo">False</VersionInfo>
|
|
||||||
<VersionInfo Name="AutoIncBuild">False</VersionInfo>
|
|
||||||
<VersionInfo Name="MajorVer">1</VersionInfo>
|
|
||||||
<VersionInfo Name="MinorVer">0</VersionInfo>
|
|
||||||
<VersionInfo Name="Release">0</VersionInfo>
|
|
||||||
<VersionInfo Name="Build">0</VersionInfo>
|
|
||||||
<VersionInfo Name="Debug">False</VersionInfo>
|
|
||||||
<VersionInfo Name="PreRelease">False</VersionInfo>
|
|
||||||
<VersionInfo Name="Special">False</VersionInfo>
|
|
||||||
<VersionInfo Name="Private">False</VersionInfo>
|
|
||||||
<VersionInfo Name="DLL">False</VersionInfo>
|
|
||||||
<VersionInfo Name="Locale">1033</VersionInfo>
|
|
||||||
<VersionInfo Name="CodePage">1252</VersionInfo>
|
|
||||||
</VersionInfo>
|
|
||||||
<VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="CompanyName">Stas'M Corp.</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="FileDescription">RDP Configuration Program</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="FileVersion">1.0.0.0</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="InternalName">RDPConf</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="LegalCopyright">Copyright © Stas'M Corp. 2014</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="LegalTrademarks">Stas'M Corp.</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="OriginalFilename">RDPConf.exe</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="ProductName">RDP Host Support</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="ProductVersion">1.4.0.0</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="Comments">http://stascorp.com</VersionInfoKeys>
|
|
||||||
</VersionInfoKeys>
|
|
||||||
<Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\bcboffice2k140.bpl">Embarcadero C++Builder Office 2000 Servers Package</Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\bcbofficexp140.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\dcloffice2k140.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\dclofficexp140.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
|
|
||||||
</Excluded_Packages>
|
|
||||||
</Delphi.Personality>
|
|
||||||
</BorlandProject>
|
|
||||||
<ProjectFileVersion>12</ProjectFileVersion>
|
|
||||||
</ProjectExtensions>
|
|
||||||
</Project>
|
|
||||||
@ -1,375 +0,0 @@
|
|||||||
{
|
|
||||||
Copyright 2014 Stas'M Corp.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
}
|
|
||||||
|
|
||||||
unit LiteINI;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
SysUtils;
|
|
||||||
|
|
||||||
type
|
|
||||||
SList = Array of String;
|
|
||||||
INIValue = record
|
|
||||||
Name: String;
|
|
||||||
Value: String;
|
|
||||||
end;
|
|
||||||
INISection = record
|
|
||||||
Name: String;
|
|
||||||
Values: Array of INIValue;
|
|
||||||
end;
|
|
||||||
INIFile = Array of INISection;
|
|
||||||
|
|
||||||
procedure SListClear(var List: SList);
|
|
||||||
function SListAppend(var List: SList; S: String): Integer;
|
|
||||||
function SListFind(List: SList; Value: String): Integer;
|
|
||||||
function INIFindSection(INI: INIFile; Section: String): Integer;
|
|
||||||
function INIFindValue(INI: INIFile; Section: Integer; Value: String): Integer;
|
|
||||||
function INIAddSection(var INI: INIFile; Section: String): Integer;
|
|
||||||
function INIAddValue(var INI: INIFile; Section: Integer; ValueName, Value: String): Integer;
|
|
||||||
procedure INIUnload(var INI: INIFile);
|
|
||||||
procedure INILoad(var INI: INIFile; FileName: String);
|
|
||||||
function INISectionExists(INI: INIFile; Section: String): Boolean;
|
|
||||||
function INIValueExists(INI: INIFile; Section: String; Value: String): Boolean;
|
|
||||||
function INIReadSectionLowAPI(INI: INIFile; Section: Integer; var List: SList): Boolean;
|
|
||||||
function INIReadSection(INI: INIFile; Section: String): SList;
|
|
||||||
function INIReadStringLowAPI(INI: INIFile; Section, Value: Integer; var Str: String): Boolean;
|
|
||||||
function INIReadString(INI: INIFile; Section, Value, Default: String): String;
|
|
||||||
function INIReadInt(INI: INIFile; Section, Value: String; Default: Integer): Integer;
|
|
||||||
function INIReadDWord(INI: INIFile; Section, Value: String; Default: Cardinal): Cardinal;
|
|
||||||
function INIReadIntHex(INI: INIFile; Section, Value: String; Default: Integer): Integer;
|
|
||||||
function INIReadDWordHex(INI: INIFile; Section, Value: String; Default: Cardinal): Cardinal;
|
|
||||||
function INIReadBool(INI: INIFile; Section, Value: String; Default: Boolean): Boolean;
|
|
||||||
function INIReadBytes(INI: INIFile; Section, Value: String): TBytes;
|
|
||||||
function INIReadBytesDef(INI: INIFile; Section, Value: String; Default: TBytes): TBytes;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
procedure SListClear(var List: SList);
|
|
||||||
begin
|
|
||||||
SetLength(List, 0);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function SListAppend(var List: SList; S: String): Integer;
|
|
||||||
begin
|
|
||||||
SetLength(List, Length(List) + 1);
|
|
||||||
List[Length(List) - 1] := S;
|
|
||||||
Result := Length(List) - 1;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function SListFind(List: SList; Value: String): Integer;
|
|
||||||
var
|
|
||||||
I: Integer;
|
|
||||||
begin
|
|
||||||
Result := -1;
|
|
||||||
for I := 0 to Length(List) - 1 do
|
|
||||||
if List[I] = Value then begin
|
|
||||||
Result := I;
|
|
||||||
Break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIFindSection(INI: INIFile; Section: String): Integer;
|
|
||||||
var
|
|
||||||
I: Integer;
|
|
||||||
begin
|
|
||||||
Result := -1;
|
|
||||||
for I := 0 to Length(INI) - 1 do
|
|
||||||
if INI[I].Name = Section then begin
|
|
||||||
Result := I;
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIFindValue(INI: INIFile; Section: Integer; Value: String): Integer;
|
|
||||||
var
|
|
||||||
I: Integer;
|
|
||||||
begin
|
|
||||||
Result := -1;
|
|
||||||
if (Section < 0) or (Section >= Length(INI)) then
|
|
||||||
Exit;
|
|
||||||
for I := 0 to Length(INI[Section].Values) - 1 do
|
|
||||||
if INI[Section].Values[I].Name = Value then begin
|
|
||||||
Result := I;
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIAddSection(var INI: INIFile; Section: String): Integer;
|
|
||||||
begin
|
|
||||||
Result := INIFindSection(INI, Section);
|
|
||||||
if Result >= 0 then
|
|
||||||
Exit;
|
|
||||||
Result := Length(INI);
|
|
||||||
SetLength(INI, Result + 1);
|
|
||||||
INI[Result].Name := Section;
|
|
||||||
SetLength(INI[Result].Values, 0);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIAddValue(var INI: INIFile; Section: Integer; ValueName, Value: String): Integer;
|
|
||||||
var
|
|
||||||
I: Integer;
|
|
||||||
begin
|
|
||||||
Result := -1;
|
|
||||||
if (Section < 0) or (Section >= Length(INI)) then
|
|
||||||
Exit;
|
|
||||||
I := INIFindValue(INI, Section, ValueName);
|
|
||||||
if I = -1 then begin
|
|
||||||
Result := Length(INI[Section].Values);
|
|
||||||
SetLength(INI[Section].Values, Result + 1);
|
|
||||||
INI[Section].Values[Result].Name := ValueName;
|
|
||||||
INI[Section].Values[Result].Value := Value;
|
|
||||||
end else begin
|
|
||||||
INI[Section].Values[I].Value := Value;
|
|
||||||
Result := I;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure INIUnload(var INI: INIFile);
|
|
||||||
begin
|
|
||||||
SetLength(INI, 0);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure INILoad(var INI: INIFile; FileName: String);
|
|
||||||
var
|
|
||||||
F: TextFile;
|
|
||||||
S, ValueName, Value: String;
|
|
||||||
INIList: SList;
|
|
||||||
I, Sect: Integer;
|
|
||||||
begin
|
|
||||||
INIUnload(INI);
|
|
||||||
if not FileExists(FileName) then
|
|
||||||
Exit;
|
|
||||||
AssignFile(F, FileName);
|
|
||||||
Reset(F);
|
|
||||||
// Read and filter lines
|
|
||||||
while not EOF(F) do begin
|
|
||||||
Readln(F, S);
|
|
||||||
if (Pos(';', S) <> 1)
|
|
||||||
and (Pos('#', S) <> 1)
|
|
||||||
and (
|
|
||||||
((Pos('[', S) > 0) and (Pos(']', S) > 0)) or
|
|
||||||
(Pos('=', S) > 0)
|
|
||||||
)
|
|
||||||
then
|
|
||||||
SListAppend(INIList, S);
|
|
||||||
end;
|
|
||||||
CloseFile(F);
|
|
||||||
// Parse 2 (parse format)
|
|
||||||
Sect := -1;
|
|
||||||
for I := 0 to Length(INIList) - 1 do begin
|
|
||||||
S := Trim(INIList[I]);
|
|
||||||
if Length(S) >= 2 then
|
|
||||||
if (S[1] = '[') and (S[Length(S)] = ']') then begin
|
|
||||||
S := Trim(Copy(S, 2, Length(S) - 2));
|
|
||||||
Sect := INIAddSection(INI, S);
|
|
||||||
Continue;
|
|
||||||
end;
|
|
||||||
S := INIList[I];
|
|
||||||
if Pos('=', S) > 0 then begin
|
|
||||||
ValueName := Trim(Copy(S, 1, Pos('=', S) - 1));
|
|
||||||
Value := Copy(S, Pos('=', S) + 1, Length(S) - Pos('=', S));
|
|
||||||
if Sect = -1 then
|
|
||||||
Sect := INIAddSection(INI, '');
|
|
||||||
INIAddValue(INI, Sect, ValueName, Value);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INISectionExists(INI: INIFile; Section: String): Boolean;
|
|
||||||
begin
|
|
||||||
Result := INIFindSection(INI, Section) > -1;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIValueExists(INI: INIFile; Section: String; Value: String): Boolean;
|
|
||||||
var
|
|
||||||
Sect: Integer;
|
|
||||||
begin
|
|
||||||
Sect := INIFindSection(INI, Section);
|
|
||||||
Result := INIFindValue(INI, Sect, Value) > -1;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIReadSectionLowAPI(INI: INIFile; Section: Integer; var List: SList): Boolean;
|
|
||||||
var
|
|
||||||
I: Integer;
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
SetLength(List, 0);
|
|
||||||
if (Section < 0) or (Section >= Length(INI)) then
|
|
||||||
Exit;
|
|
||||||
for I := 0 to Length(INI[Section].Values) - 1 do
|
|
||||||
SListAppend(List, INI[Section].Values[I].Name);
|
|
||||||
Result := True;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIReadSection(INI: INIFile; Section: String): SList;
|
|
||||||
var
|
|
||||||
Sect: Integer;
|
|
||||||
begin
|
|
||||||
Sect := INIFindSection(INI, Section);
|
|
||||||
INIReadSectionLowAPI(INI, Sect, Result);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIReadStringLowAPI(INI: INIFile; Section, Value: Integer; var Str: String): Boolean;
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
if (Section < 0) or (Section >= Length(INI)) then
|
|
||||||
Exit;
|
|
||||||
if (Value < 0) or (Value >= Length(INI[Section].Values)) then
|
|
||||||
Exit;
|
|
||||||
Str := INI[Section].Values[Value].Value;
|
|
||||||
Result := True;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIReadString(INI: INIFile; Section, Value, Default: String): String;
|
|
||||||
var
|
|
||||||
Sect, Val: Integer;
|
|
||||||
begin
|
|
||||||
Sect := INIFindSection(INI, Section);
|
|
||||||
Val := INIFindValue(INI, Sect, Value);
|
|
||||||
if not INIReadStringLowAPI(INI, Sect, Val, Result) then
|
|
||||||
Result := Default;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIReadInt(INI: INIFile; Section, Value: String; Default: Integer): Integer;
|
|
||||||
var
|
|
||||||
S: String;
|
|
||||||
E: Integer;
|
|
||||||
begin
|
|
||||||
S := INIReadString(INI, Section, Value, '');
|
|
||||||
Val(S, Result, E);
|
|
||||||
if E <> 0 then
|
|
||||||
Result := Default;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIReadDWord(INI: INIFile; Section, Value: String; Default: Cardinal): Cardinal;
|
|
||||||
var
|
|
||||||
S: String;
|
|
||||||
E: Integer;
|
|
||||||
begin
|
|
||||||
S := INIReadString(INI, Section, Value, '');
|
|
||||||
Val(S, Result, E);
|
|
||||||
if E <> 0 then
|
|
||||||
Result := Default;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIReadIntHex(INI: INIFile; Section, Value: String; Default: Integer): Integer;
|
|
||||||
var
|
|
||||||
S: String;
|
|
||||||
E: Integer;
|
|
||||||
begin
|
|
||||||
S := INIReadString(INI, Section, Value, '');
|
|
||||||
Val('$'+S, Result, E);
|
|
||||||
if E <> 0 then
|
|
||||||
Result := Default;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIReadDWordHex(INI: INIFile; Section, Value: String; Default: Cardinal): Cardinal;
|
|
||||||
var
|
|
||||||
S: String;
|
|
||||||
E: Integer;
|
|
||||||
begin
|
|
||||||
S := INIReadString(INI, Section, Value, '');
|
|
||||||
Val('$'+S, Result, E);
|
|
||||||
if E <> 0 then
|
|
||||||
Result := Default;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIReadBool(INI: INIFile; Section, Value: String; Default: Boolean): Boolean;
|
|
||||||
var
|
|
||||||
S: String;
|
|
||||||
I: Cardinal;
|
|
||||||
E: Integer;
|
|
||||||
begin
|
|
||||||
S := INIReadString(INI, Section, Value, '');
|
|
||||||
Val(S, I, E);
|
|
||||||
if E <> 0 then
|
|
||||||
Result := Default
|
|
||||||
else
|
|
||||||
Result := I > 0;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function StringToBytes(S: String; var B: TBytes): Boolean;
|
|
||||||
var
|
|
||||||
I: Integer;
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
if Odd(Length(S)) then
|
|
||||||
Exit;
|
|
||||||
SetLength(B, Length(S) div 2);
|
|
||||||
for I := 0 to Length(B) - 1 do begin
|
|
||||||
B[I] := 0;
|
|
||||||
case S[(I*2)+2] of
|
|
||||||
'0': ;
|
|
||||||
'1': B[I] := B[I] or $1;
|
|
||||||
'2': B[I] := B[I] or $2;
|
|
||||||
'3': B[I] := B[I] or $3;
|
|
||||||
'4': B[I] := B[I] or $4;
|
|
||||||
'5': B[I] := B[I] or $5;
|
|
||||||
'6': B[I] := B[I] or $6;
|
|
||||||
'7': B[I] := B[I] or $7;
|
|
||||||
'8': B[I] := B[I] or $8;
|
|
||||||
'9': B[I] := B[I] or $9;
|
|
||||||
'A','a': B[I] := B[I] or $A;
|
|
||||||
'B','b': B[I] := B[I] or $B;
|
|
||||||
'C','c': B[I] := B[I] or $C;
|
|
||||||
'D','d': B[I] := B[I] or $D;
|
|
||||||
'E','e': B[I] := B[I] or $E;
|
|
||||||
'F','f': B[I] := B[I] or $F;
|
|
||||||
else Exit;
|
|
||||||
end;
|
|
||||||
case S[(I*2)+1] of
|
|
||||||
'0': ;
|
|
||||||
'1': B[I] := B[I] or $10;
|
|
||||||
'2': B[I] := B[I] or $20;
|
|
||||||
'3': B[I] := B[I] or $30;
|
|
||||||
'4': B[I] := B[I] or $40;
|
|
||||||
'5': B[I] := B[I] or $50;
|
|
||||||
'6': B[I] := B[I] or $60;
|
|
||||||
'7': B[I] := B[I] or $70;
|
|
||||||
'8': B[I] := B[I] or $80;
|
|
||||||
'9': B[I] := B[I] or $90;
|
|
||||||
'A','a': B[I] := B[I] or $A0;
|
|
||||||
'B','b': B[I] := B[I] or $B0;
|
|
||||||
'C','c': B[I] := B[I] or $C0;
|
|
||||||
'D','d': B[I] := B[I] or $D0;
|
|
||||||
'E','e': B[I] := B[I] or $E0;
|
|
||||||
'F','f': B[I] := B[I] or $F0;
|
|
||||||
else Exit;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Result := True;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIReadBytes(INI: INIFile; Section, Value: String): TBytes;
|
|
||||||
var
|
|
||||||
S: String;
|
|
||||||
begin
|
|
||||||
S := INIReadString(INI, Section, Value, '');
|
|
||||||
if not StringToBytes(S, Result) then
|
|
||||||
SetLength(Result, 0);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function INIReadBytesDef(INI: INIFile; Section, Value: String; Default: TBytes): TBytes;
|
|
||||||
var
|
|
||||||
S: String;
|
|
||||||
begin
|
|
||||||
S := INIReadString(INI, Section, Value, '');
|
|
||||||
if not StringToBytes(S, Result) then
|
|
||||||
Result := Default;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@ -1,737 +0,0 @@
|
|||||||
{
|
|
||||||
Copyright 2014 Stas'M Corp.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
}
|
|
||||||
|
|
||||||
library rdpwrap;
|
|
||||||
|
|
||||||
uses
|
|
||||||
SysUtils,
|
|
||||||
Windows,
|
|
||||||
TlHelp32,
|
|
||||||
LiteINI;
|
|
||||||
|
|
||||||
{$R rdpwrap.res}
|
|
||||||
|
|
||||||
// Hook core definitions
|
|
||||||
|
|
||||||
type
|
|
||||||
OldCode = packed record
|
|
||||||
One: DWORD;
|
|
||||||
two: Word;
|
|
||||||
end;
|
|
||||||
|
|
||||||
far_jmp = packed record
|
|
||||||
PushOp: Byte;
|
|
||||||
PushArg: Pointer;
|
|
||||||
RetOp: Byte;
|
|
||||||
end;
|
|
||||||
|
|
||||||
mov_far_jmp = packed record
|
|
||||||
MovOp: Byte;
|
|
||||||
MovArg: Byte;
|
|
||||||
PushOp: Byte;
|
|
||||||
PushArg: Pointer;
|
|
||||||
RetOp: Byte;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TTHREADENTRY32 = packed record
|
|
||||||
dwSize: DWORD;
|
|
||||||
cntUsage: DWORD;
|
|
||||||
th32ThreadID: DWORD;
|
|
||||||
th32OwnerProcessID: DWORD;
|
|
||||||
tpBasePri: LongInt;
|
|
||||||
tpDeltaPri: LongInt;
|
|
||||||
dwFlags: DWORD;
|
|
||||||
end;
|
|
||||||
//IntArray = Array of Integer;
|
|
||||||
FILE_VERSION = record
|
|
||||||
Version: record case Boolean of
|
|
||||||
True: (dw: DWORD);
|
|
||||||
False: (w: record
|
|
||||||
Minor, Major: Word;
|
|
||||||
end;)
|
|
||||||
end;
|
|
||||||
Release, Build: Word;
|
|
||||||
bDebug, bPrerelease, bPrivate, bSpecial: Boolean;
|
|
||||||
end;
|
|
||||||
|
|
||||||
const
|
|
||||||
THREAD_SUSPEND_RESUME = 2;
|
|
||||||
TH32CS_SNAPTHREAD = 4;
|
|
||||||
var
|
|
||||||
INI: INIFile;
|
|
||||||
LogFile: String = '\rdpwrap.txt';
|
|
||||||
bw: {$if CompilerVersion>=16} NativeUInt {$else} DWORD {$endif};
|
|
||||||
IsHooked: Boolean = False;
|
|
||||||
|
|
||||||
// Unhooked import
|
|
||||||
|
|
||||||
function OpenThread(dwDesiredAccess: DWORD; bInheritHandle: BOOL;
|
|
||||||
dwThreadId: DWORD): DWORD; stdcall; external kernel32;
|
|
||||||
|
|
||||||
function CreateToolhelp32Snapshot(dwFlags, th32ProcessID: DWORD): DWORD;
|
|
||||||
stdcall; external kernel32;
|
|
||||||
|
|
||||||
function Thread32First(hSnapshot: THandle; var lpte: TTHREADENTRY32): bool;
|
|
||||||
stdcall; external kernel32;
|
|
||||||
|
|
||||||
function Thread32Next(hSnapshot: THandle; var lpte: TTHREADENTRY32): bool;
|
|
||||||
stdcall; external kernel32;
|
|
||||||
|
|
||||||
// Wrapped import
|
|
||||||
|
|
||||||
var
|
|
||||||
TSMain: function(dwArgc: DWORD; lpszArgv: PWideChar): DWORD; stdcall;
|
|
||||||
TSGlobals: function(lpGlobalData: Pointer): DWORD; stdcall;
|
|
||||||
|
|
||||||
// Hooked import and vars
|
|
||||||
|
|
||||||
var
|
|
||||||
SLGetWindowsInformationDWORD: function(pwszValueName: PWideChar;
|
|
||||||
pdwValue: PDWORD): HRESULT; stdcall;
|
|
||||||
TermSrvBase: Pointer;
|
|
||||||
FV: FILE_VERSION;
|
|
||||||
|
|
||||||
var
|
|
||||||
Stub_SLGetWindowsInformationDWORD: far_jmp;
|
|
||||||
Old_SLGetWindowsInformationDWORD: OldCode;
|
|
||||||
|
|
||||||
// Main code
|
|
||||||
|
|
||||||
procedure WriteLog(S: AnsiString);
|
|
||||||
var
|
|
||||||
F: TextFile;
|
|
||||||
begin
|
|
||||||
if not FileExists(LogFile) then
|
|
||||||
Exit;
|
|
||||||
AssignFile(F, LogFile);
|
|
||||||
Append(F);
|
|
||||||
Write(F, S+#13#10);
|
|
||||||
CloseFile(F);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function GetModuleHandleEx(dwFlags: DWORD; lpModuleName: PWideChar;
|
|
||||||
var phModule: HMODULE): BOOL; stdcall; external kernel32 name 'GetModuleHandleExW';
|
|
||||||
|
|
||||||
function GetCurrentModule: HMODULE;
|
|
||||||
const
|
|
||||||
GET_MODULE_HANDLE_EX_FLAG_PIN = 1;
|
|
||||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 2;
|
|
||||||
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 4;
|
|
||||||
begin
|
|
||||||
Result := 0;
|
|
||||||
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, @GetCurrentModule, Result);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function GetBinaryPath: String;
|
|
||||||
var
|
|
||||||
Buf: Array[0..511] of Byte;
|
|
||||||
begin
|
|
||||||
ZeroMemory(@Buf[0], Length(Buf));
|
|
||||||
GetModuleFileName(GetCurrentModule, PWideChar(@Buf[0]), Length(Buf));
|
|
||||||
Result := PWideChar(@Buf[0]);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure StopThreads;
|
|
||||||
var
|
|
||||||
h, CurrTh, ThrHandle, CurrPr: DWORD;
|
|
||||||
Thread: TTHREADENTRY32;
|
|
||||||
begin
|
|
||||||
CurrTh := GetCurrentThreadId;
|
|
||||||
CurrPr := GetCurrentProcessId;
|
|
||||||
h := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
|
|
||||||
if h <> INVALID_HANDLE_VALUE then
|
|
||||||
begin
|
|
||||||
Thread.dwSize := SizeOf(TTHREADENTRY32);
|
|
||||||
if Thread32First(h, Thread) then
|
|
||||||
repeat
|
|
||||||
if (Thread.th32ThreadID <> CurrTh) and
|
|
||||||
(Thread.th32OwnerProcessID = CurrPr) then
|
|
||||||
begin
|
|
||||||
ThrHandle := OpenThread(THREAD_SUSPEND_RESUME, false,
|
|
||||||
Thread.th32ThreadID);
|
|
||||||
if ThrHandle > 0 then
|
|
||||||
begin
|
|
||||||
SuspendThread(ThrHandle);
|
|
||||||
CloseHandle(ThrHandle);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
until not Thread32Next(h, Thread);
|
|
||||||
CloseHandle(h);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure RunThreads;
|
|
||||||
var
|
|
||||||
h, CurrTh, ThrHandle, CurrPr: DWORD;
|
|
||||||
Thread: TTHREADENTRY32;
|
|
||||||
begin
|
|
||||||
CurrTh := GetCurrentThreadId;
|
|
||||||
CurrPr := GetCurrentProcessId;
|
|
||||||
h := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
|
|
||||||
if h <> INVALID_HANDLE_VALUE then
|
|
||||||
begin
|
|
||||||
Thread.dwSize := SizeOf(TTHREADENTRY32);
|
|
||||||
if Thread32First(h, Thread) then
|
|
||||||
repeat
|
|
||||||
if (Thread.th32ThreadID <> CurrTh) and
|
|
||||||
(Thread.th32OwnerProcessID = CurrPr) then
|
|
||||||
begin
|
|
||||||
ThrHandle := OpenThread(THREAD_SUSPEND_RESUME, false,
|
|
||||||
Thread.th32ThreadID);
|
|
||||||
if ThrHandle > 0 then
|
|
||||||
begin
|
|
||||||
ResumeThread(ThrHandle);
|
|
||||||
CloseHandle(ThrHandle);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
until not Thread32Next(h, Thread);
|
|
||||||
CloseHandle(h);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function GetModuleAddress(ModuleName: String; ProcessId: DWORD; var BaseAddr: Pointer; var BaseSize: DWORD): Boolean;
|
|
||||||
var
|
|
||||||
hSnap: THandle;
|
|
||||||
md: MODULEENTRY32;
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId);
|
|
||||||
if hSnap = INVALID_HANDLE_VALUE Then
|
|
||||||
Exit;
|
|
||||||
md.dwSize := SizeOf(MODULEENTRY32);
|
|
||||||
if Module32First(hSnap, md) then
|
|
||||||
begin
|
|
||||||
if LowerCase(ExtractFileName(md.szExePath)) = LowerCase(ModuleName) then
|
|
||||||
begin
|
|
||||||
Result := True;
|
|
||||||
BaseAddr := Pointer(md.modBaseAddr);
|
|
||||||
BaseSize := md.modBaseSize;
|
|
||||||
CloseHandle(hSnap);
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
while Module32Next(hSnap, md) Do
|
|
||||||
begin
|
|
||||||
if LowerCase(ExtractFileName(md.szExePath)) = LowerCase(ModuleName) then
|
|
||||||
begin
|
|
||||||
Result := True;
|
|
||||||
BaseAddr := Pointer(md.modBaseAddr);
|
|
||||||
BaseSize := md.modBaseSize;
|
|
||||||
Break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
CloseHandle(hSnap);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{procedure FindMem(Mem: Pointer; MemSz: DWORD; Buf: Pointer; BufSz: DWORD;
|
|
||||||
From: DWORD; var A: IntArray);
|
|
||||||
var
|
|
||||||
I: Integer;
|
|
||||||
begin
|
|
||||||
SetLength(A, 0);
|
|
||||||
I:=From;
|
|
||||||
if From>0 then
|
|
||||||
Inc(PByte(Mem), From);
|
|
||||||
while I < MemSz - BufSz + 1 do
|
|
||||||
begin
|
|
||||||
if (not IsBadReadPtr(Mem, BufSz)) and (CompareMem(Mem, Buf, BufSz)) then
|
|
||||||
begin
|
|
||||||
SetLength(A, Length(A)+1);
|
|
||||||
A[Length(A)-1] := I;
|
|
||||||
end;
|
|
||||||
Inc(I);
|
|
||||||
Inc(PByte(Mem));
|
|
||||||
end;
|
|
||||||
end;}
|
|
||||||
|
|
||||||
function GetModuleVersion(const ModuleName: String; var FileVersion: FILE_VERSION): Boolean;
|
|
||||||
type
|
|
||||||
VS_VERSIONINFO = record
|
|
||||||
wLength, wValueLength, wType: Word;
|
|
||||||
szKey: Array[1..16] of WideChar;
|
|
||||||
Padding1: Word;
|
|
||||||
Value: VS_FIXEDFILEINFO;
|
|
||||||
Padding2, Children: Word;
|
|
||||||
end;
|
|
||||||
PVS_VERSIONINFO = ^VS_VERSIONINFO;
|
|
||||||
const
|
|
||||||
VFF_DEBUG = 1;
|
|
||||||
VFF_PRERELEASE = 2;
|
|
||||||
VFF_PRIVATE = 8;
|
|
||||||
VFF_SPECIAL = 32;
|
|
||||||
var
|
|
||||||
hMod: HMODULE;
|
|
||||||
hResourceInfo: HRSRC;
|
|
||||||
VersionInfo: PVS_VERSIONINFO;
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
|
|
||||||
if ModuleName = '' then
|
|
||||||
hMod := GetModuleHandle(nil)
|
|
||||||
else
|
|
||||||
hMod := GetModuleHandle(PWideChar(ModuleName));
|
|
||||||
if hMod = 0 then
|
|
||||||
Exit;
|
|
||||||
|
|
||||||
hResourceInfo := FindResource(hMod, PWideChar(1), PWideChar($10));
|
|
||||||
if hResourceInfo = 0 then
|
|
||||||
Exit;
|
|
||||||
|
|
||||||
VersionInfo := Pointer(LoadResource(hMod, hResourceInfo));
|
|
||||||
if VersionInfo = nil then
|
|
||||||
Exit;
|
|
||||||
|
|
||||||
FileVersion.Version.dw := VersionInfo.Value.dwFileVersionMS;
|
|
||||||
FileVersion.Release := Word(VersionInfo.Value.dwFileVersionLS shr 16);
|
|
||||||
FileVersion.Build := Word(VersionInfo.Value.dwFileVersionLS);
|
|
||||||
FileVersion.bDebug := (VersionInfo.Value.dwFileFlags and VFF_DEBUG) = VFF_DEBUG;
|
|
||||||
FileVersion.bPrerelease := (VersionInfo.Value.dwFileFlags and VFF_PRERELEASE) = VFF_PRERELEASE;
|
|
||||||
FileVersion.bPrivate := (VersionInfo.Value.dwFileFlags and VFF_PRIVATE) = VFF_PRIVATE;
|
|
||||||
FileVersion.bSpecial := (VersionInfo.Value.dwFileFlags and VFF_SPECIAL) = VFF_SPECIAL;
|
|
||||||
|
|
||||||
Result := True;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function GetFileVersion(const FileName: String; var FileVersion: FILE_VERSION): Boolean;
|
|
||||||
type
|
|
||||||
VS_VERSIONINFO = record
|
|
||||||
wLength, wValueLength, wType: Word;
|
|
||||||
szKey: Array[1..16] of WideChar;
|
|
||||||
Padding1: Word;
|
|
||||||
Value: VS_FIXEDFILEINFO;
|
|
||||||
Padding2, Children: Word;
|
|
||||||
end;
|
|
||||||
PVS_VERSIONINFO = ^VS_VERSIONINFO;
|
|
||||||
const
|
|
||||||
VFF_DEBUG = 1;
|
|
||||||
VFF_PRERELEASE = 2;
|
|
||||||
VFF_PRIVATE = 8;
|
|
||||||
VFF_SPECIAL = 32;
|
|
||||||
var
|
|
||||||
hFile: HMODULE;
|
|
||||||
hResourceInfo: HRSRC;
|
|
||||||
VersionInfo: PVS_VERSIONINFO;
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
|
|
||||||
hFile := LoadLibraryEx(PWideChar(FileName), 0, LOAD_LIBRARY_AS_DATAFILE);
|
|
||||||
if hFile = 0 then
|
|
||||||
Exit;
|
|
||||||
|
|
||||||
hResourceInfo := FindResource(hFile, PWideChar(1), PWideChar($10));
|
|
||||||
if hResourceInfo = 0 then
|
|
||||||
Exit;
|
|
||||||
|
|
||||||
VersionInfo := Pointer(LoadResource(hFile, hResourceInfo));
|
|
||||||
if VersionInfo = nil then
|
|
||||||
Exit;
|
|
||||||
|
|
||||||
FileVersion.Version.dw := VersionInfo.Value.dwFileVersionMS;
|
|
||||||
FileVersion.Release := Word(VersionInfo.Value.dwFileVersionLS shr 16);
|
|
||||||
FileVersion.Build := Word(VersionInfo.Value.dwFileVersionLS);
|
|
||||||
FileVersion.bDebug := (VersionInfo.Value.dwFileFlags and VFF_DEBUG) = VFF_DEBUG;
|
|
||||||
FileVersion.bPrerelease := (VersionInfo.Value.dwFileFlags and VFF_PRERELEASE) = VFF_PRERELEASE;
|
|
||||||
FileVersion.bPrivate := (VersionInfo.Value.dwFileFlags and VFF_PRIVATE) = VFF_PRIVATE;
|
|
||||||
FileVersion.bSpecial := (VersionInfo.Value.dwFileFlags and VFF_SPECIAL) = VFF_SPECIAL;
|
|
||||||
|
|
||||||
Result := True;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function OverrideSL(ValueName: String; var Value: DWORD): Boolean;
|
|
||||||
begin
|
|
||||||
Result := True;
|
|
||||||
if INIValueExists(INI, 'SLPolicy', ValueName) then begin
|
|
||||||
Value := INIReadDWord(INI, 'SLPolicy', ValueName, 0);
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
Result := False;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function New_SLGetWindowsInformationDWORD(pwszValueName: PWideChar;
|
|
||||||
pdwValue: PDWORD): HRESULT; stdcall;
|
|
||||||
var
|
|
||||||
dw: DWORD;
|
|
||||||
begin
|
|
||||||
// wrapped SLGetWindowsInformationDWORD function
|
|
||||||
// termsrv.dll will call this function instead of original SLC.dll
|
|
||||||
|
|
||||||
// Override SL Policy
|
|
||||||
|
|
||||||
WriteLog('Policy query: ' + pwszValueName);
|
|
||||||
if OverrideSL(pwszValueName, dw) then begin
|
|
||||||
pdwValue^ := dw;
|
|
||||||
Result := S_OK;
|
|
||||||
WriteLog('Policy rewrite: ' + IntToStr(pdwValue^));
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// If the requested value name is not defined above
|
|
||||||
|
|
||||||
// revert to original SL Policy function
|
|
||||||
WriteProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
|
|
||||||
@Old_SLGetWindowsInformationDWORD, SizeOf(OldCode), bw);
|
|
||||||
|
|
||||||
// get result
|
|
||||||
Result := SLGetWindowsInformationDWORD(pwszValueName, pdwValue);
|
|
||||||
if Result = S_OK then
|
|
||||||
WriteLog('Policy result: ' + IntToStr(pdwValue^))
|
|
||||||
else
|
|
||||||
WriteLog('Policy request failed');
|
|
||||||
// wrap it back
|
|
||||||
WriteProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
|
|
||||||
@Stub_SLGetWindowsInformationDWORD, SizeOf(far_jmp), bw);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function New_Win8SL(pwszValueName: PWideChar; pdwValue: PDWORD): HRESULT; register;
|
|
||||||
var
|
|
||||||
dw: DWORD;
|
|
||||||
begin
|
|
||||||
// wrapped unexported function SLGetWindowsInformationDWORDWrapper in termsrv.dll
|
|
||||||
// for Windows 8 support
|
|
||||||
|
|
||||||
// Override SL Policy
|
|
||||||
|
|
||||||
WriteLog('Policy query: ' + pwszValueName);
|
|
||||||
if OverrideSL(pwszValueName, dw) then begin
|
|
||||||
pdwValue^ := dw;
|
|
||||||
Result := S_OK;
|
|
||||||
WriteLog('Policy rewrite: ' + IntToStr(pdwValue^));
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// If the requested value name is not defined above
|
|
||||||
// use function from SLC.dll
|
|
||||||
|
|
||||||
Result := SLGetWindowsInformationDWORD(pwszValueName, pdwValue);
|
|
||||||
if Result = S_OK then
|
|
||||||
WriteLog('Policy result: ' + IntToStr(pdwValue^))
|
|
||||||
else
|
|
||||||
WriteLog('Policy request failed');
|
|
||||||
end;
|
|
||||||
|
|
||||||
function New_Win8SL_CP(eax: DWORD; pdwValue: PDWORD; ecx: DWORD; pwszValueName: PWideChar): HRESULT; register;
|
|
||||||
begin
|
|
||||||
// wrapped unexported function SLGetWindowsInformationDWORDWrapper in termsrv.dll
|
|
||||||
// for Windows 8 Consumer Preview support
|
|
||||||
|
|
||||||
Result := New_Win8SL(pwszValueName, pdwValue);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function New_CSLQuery_Initialize: HRESULT; stdcall;
|
|
||||||
var
|
|
||||||
Sect: String;
|
|
||||||
bServerSku,
|
|
||||||
bRemoteConnAllowed,
|
|
||||||
bFUSEnabled,
|
|
||||||
bAppServerAllowed,
|
|
||||||
bMultimonAllowed,
|
|
||||||
lMaxUserSessions,
|
|
||||||
ulMaxDebugSessions,
|
|
||||||
bInitialized: PDWORD;
|
|
||||||
begin
|
|
||||||
bServerSku := nil;
|
|
||||||
bRemoteConnAllowed := nil;
|
|
||||||
bFUSEnabled := nil;
|
|
||||||
bAppServerAllowed := nil;
|
|
||||||
bMultimonAllowed := nil;
|
|
||||||
lMaxUserSessions := nil;
|
|
||||||
ulMaxDebugSessions := nil;
|
|
||||||
bInitialized := nil;
|
|
||||||
WriteLog('>>> CSLQuery::Initialize');
|
|
||||||
Sect := IntToStr(FV.Version.w.Major)+'.'+IntToStr(FV.Version.w.Minor)+'.'+
|
|
||||||
IntToStr(FV.Release)+'.'+IntToStr(FV.Build)+'-SLInit';
|
|
||||||
if INISectionExists(INI, Sect) then begin
|
|
||||||
bServerSku := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bServerSku.x86', 0));
|
|
||||||
bRemoteConnAllowed := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bRemoteConnAllowed.x86', 0));
|
|
||||||
bFUSEnabled := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bFUSEnabled.x86', 0));
|
|
||||||
bAppServerAllowed := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bAppServerAllowed.x86', 0));
|
|
||||||
bMultimonAllowed := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bMultimonAllowed.x86', 0));
|
|
||||||
lMaxUserSessions := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'lMaxUserSessions.x86', 0));
|
|
||||||
ulMaxDebugSessions := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'ulMaxDebugSessions.x86', 0));
|
|
||||||
bInitialized := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'bInitialized.x86', 0));
|
|
||||||
end;
|
|
||||||
|
|
||||||
if bServerSku <> nil then begin
|
|
||||||
bServerSku^ := INIReadDWord(INI, 'SLInit', 'bServerSku', 1);
|
|
||||||
WriteLog('SLInit [0x'+IntToHex(DWORD(bServerSku), 1)+'] bServerSku = ' + IntToStr(bServerSku^));
|
|
||||||
end;
|
|
||||||
if bRemoteConnAllowed <> nil then begin
|
|
||||||
bRemoteConnAllowed^ := INIReadDWord(INI, 'SLInit', 'bRemoteConnAllowed', 1);
|
|
||||||
WriteLog('SLInit [0x'+IntToHex(DWORD(bRemoteConnAllowed), 1)+'] bRemoteConnAllowed = ' + IntToStr(bRemoteConnAllowed^));
|
|
||||||
end;
|
|
||||||
if bFUSEnabled <> nil then begin
|
|
||||||
bFUSEnabled^ := INIReadDWord(INI, 'SLInit', 'bFUSEnabled', 1);
|
|
||||||
WriteLog('SLInit [0x'+IntToHex(DWORD(bFUSEnabled), 1)+'] bFUSEnabled = ' + IntToStr(bFUSEnabled^));
|
|
||||||
end;
|
|
||||||
if bAppServerAllowed <> nil then begin
|
|
||||||
bAppServerAllowed^ := INIReadDWord(INI, 'SLInit', 'bAppServerAllowed', 1);
|
|
||||||
WriteLog('SLInit [0x'+IntToHex(DWORD(bAppServerAllowed), 1)+'] bAppServerAllowed = ' + IntToStr(bAppServerAllowed^));
|
|
||||||
end;
|
|
||||||
if bMultimonAllowed <> nil then begin
|
|
||||||
bMultimonAllowed^ := INIReadDWord(INI, 'SLInit', 'bMultimonAllowed', 1);
|
|
||||||
WriteLog('SLInit [0x'+IntToHex(DWORD(bMultimonAllowed), 1)+'] bMultimonAllowed = ' + IntToStr(bMultimonAllowed^));
|
|
||||||
end;
|
|
||||||
if lMaxUserSessions <> nil then begin
|
|
||||||
lMaxUserSessions^ := INIReadDWord(INI, 'SLInit', 'lMaxUserSessions', 0);
|
|
||||||
WriteLog('SLInit [0x'+IntToHex(DWORD(lMaxUserSessions), 1)+'] lMaxUserSessions = ' + IntToStr(lMaxUserSessions^));
|
|
||||||
end;
|
|
||||||
if ulMaxDebugSessions <> nil then begin
|
|
||||||
ulMaxDebugSessions^ := INIReadDWord(INI, 'SLInit', 'ulMaxDebugSessions', 0);
|
|
||||||
WriteLog('SLInit [0x'+IntToHex(DWORD(ulMaxDebugSessions), 1)+'] ulMaxDebugSessions = ' + IntToStr(ulMaxDebugSessions^));
|
|
||||||
end;
|
|
||||||
if bInitialized <> nil then begin
|
|
||||||
bInitialized^ := INIReadDWord(INI, 'SLInit', 'bInitialized', 1);
|
|
||||||
WriteLog('SLInit [0x'+IntToHex(DWORD(bInitialized), 1)+'] bInitialized = ' + IntToStr(bInitialized^));
|
|
||||||
end;
|
|
||||||
Result := S_OK;
|
|
||||||
WriteLog('<<< CSLQuery::Initialize');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure HookFunctions;
|
|
||||||
var
|
|
||||||
ConfigFile, Sect, FuncName: String;
|
|
||||||
V: DWORD;
|
|
||||||
TS_Handle, SLC_Handle: THandle;
|
|
||||||
TermSrvSize: DWORD;
|
|
||||||
SignPtr: Pointer;
|
|
||||||
I: Integer;
|
|
||||||
PatchList: SList;
|
|
||||||
Patch: Array of TBytes;
|
|
||||||
Jump: far_jmp;
|
|
||||||
MovJump: mov_far_jmp;
|
|
||||||
begin
|
|
||||||
{ hook function ^^
|
|
||||||
(called once) }
|
|
||||||
IsHooked := True;
|
|
||||||
TSMain := nil;
|
|
||||||
TSGlobals := nil;
|
|
||||||
SLGetWindowsInformationDWORD := nil;
|
|
||||||
|
|
||||||
WriteLog('Loading configuration...');
|
|
||||||
ConfigFile := ExtractFilePath(GetBinaryPath) + 'rdpwrap.ini';
|
|
||||||
WriteLog('Configuration file: ' + ConfigFile);
|
|
||||||
INILoad(INI, ConfigFile);
|
|
||||||
if Length(INI) = 0 then begin
|
|
||||||
WriteLog('Error: Failed to load configuration');
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
LogFile := INIReadString(INI, 'Main', 'LogFile', ExtractFilePath(GetBinaryPath) + 'rdpwrap.txt');
|
|
||||||
WriteLog('Initializing RDP Wrapper...');
|
|
||||||
|
|
||||||
// load termsrv.dll and get functions
|
|
||||||
TS_Handle := LoadLibrary('termsrv.dll');
|
|
||||||
if TS_Handle = 0 then begin
|
|
||||||
WriteLog('Error: Failed to load Terminal Services library');
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
TSMain := GetProcAddress(TS_Handle, 'ServiceMain');
|
|
||||||
TSGlobals := GetProcAddress(TS_Handle, 'SvchostPushServiceGlobals');
|
|
||||||
WriteLog(
|
|
||||||
'Base addr: 0x' + IntToHex(TS_Handle, 8) + #13#10 +
|
|
||||||
'SvcMain: termsrv.dll+0x' + IntToHex(Cardinal(@TSMain) - TS_Handle, 1) + #13#10 +
|
|
||||||
'SvcGlobals: termsrv.dll+0x' + IntToHex(Cardinal(@TSGlobals) - TS_Handle, 1)
|
|
||||||
);
|
|
||||||
|
|
||||||
V := 0;
|
|
||||||
// check termsrv version
|
|
||||||
if GetModuleVersion('termsrv.dll', FV) then
|
|
||||||
V := Byte(FV.Version.w.Minor) or (Byte(FV.Version.w.Major) shl 8)
|
|
||||||
else begin
|
|
||||||
// check NT version
|
|
||||||
// V := GetVersion; // deprecated
|
|
||||||
// V := ((V and $FF) shl 8) or ((V and $FF00) shr 8);
|
|
||||||
end;
|
|
||||||
if V = 0 then begin
|
|
||||||
WriteLog('Error: Failed to detect Terminal Services version');
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
WriteLog('Version: '+
|
|
||||||
IntToStr(FV.Version.w.Major)+'.'+
|
|
||||||
IntToStr(FV.Version.w.Minor)+'.'+
|
|
||||||
IntToStr(FV.Release)+'.'+
|
|
||||||
IntToStr(FV.Build));
|
|
||||||
|
|
||||||
// temporarily freeze threads
|
|
||||||
WriteLog('Freezing threads...');
|
|
||||||
StopThreads();
|
|
||||||
|
|
||||||
WriteLog('Caching patch codes...');
|
|
||||||
PatchList := INIReadSection(INI, 'PatchCodes');
|
|
||||||
SetLength(Patch, Length(PatchList));
|
|
||||||
for I := 0 to Length(Patch) - 1 do begin
|
|
||||||
Patch[I] := INIReadBytes(INI, 'PatchCodes', PatchList[I]);
|
|
||||||
if Length(Patch[I]) > 16 then // for security reasons
|
|
||||||
SetLength(Patch[I], 16); // not more than 16 bytes
|
|
||||||
end;
|
|
||||||
|
|
||||||
if (V = $0600) and (INIReadBool(INI, 'Main', 'SLPolicyHookNT60', True)) then begin
|
|
||||||
// Windows Vista
|
|
||||||
// uses SL Policy API (slc.dll)
|
|
||||||
|
|
||||||
// load slc.dll and hook function
|
|
||||||
SLC_Handle := LoadLibrary('slc.dll');
|
|
||||||
SLGetWindowsInformationDWORD := GetProcAddress(SLC_Handle, 'SLGetWindowsInformationDWORD');
|
|
||||||
|
|
||||||
if @SLGetWindowsInformationDWORD <> nil then
|
|
||||||
begin
|
|
||||||
// rewrite original function to call our function (make hook)
|
|
||||||
|
|
||||||
WriteLog('Hook SLGetWindowsInformationDWORD');
|
|
||||||
Stub_SLGetWindowsInformationDWORD.PushOp := $68;
|
|
||||||
Stub_SLGetWindowsInformationDWORD.PushArg := @New_SLGetWindowsInformationDWORD;
|
|
||||||
Stub_SLGetWindowsInformationDWORD.RetOp := $C3;
|
|
||||||
ReadProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
|
|
||||||
@Old_SLGetWindowsInformationDWORD, SizeOf(OldCode), bw);
|
|
||||||
WriteProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
|
|
||||||
@Stub_SLGetWindowsInformationDWORD, SizeOf(far_jmp), bw);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if (V = $0601) and (INIReadBool(INI, 'Main', 'SLPolicyHookNT61', True)) then begin
|
|
||||||
// Windows 7
|
|
||||||
// uses SL Policy API (slc.dll)
|
|
||||||
|
|
||||||
// load slc.dll and hook function
|
|
||||||
SLC_Handle := LoadLibrary('slc.dll');
|
|
||||||
SLGetWindowsInformationDWORD := GetProcAddress(SLC_Handle, 'SLGetWindowsInformationDWORD');
|
|
||||||
|
|
||||||
if @SLGetWindowsInformationDWORD <> nil then
|
|
||||||
begin
|
|
||||||
// rewrite original function to call our function (make hook)
|
|
||||||
|
|
||||||
WriteLog('Hook SLGetWindowsInformationDWORD');
|
|
||||||
Stub_SLGetWindowsInformationDWORD.PushOp := $68;
|
|
||||||
Stub_SLGetWindowsInformationDWORD.PushArg := @New_SLGetWindowsInformationDWORD;
|
|
||||||
Stub_SLGetWindowsInformationDWORD.RetOp := $C3;
|
|
||||||
ReadProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
|
|
||||||
@Old_SLGetWindowsInformationDWORD, SizeOf(OldCode), bw);
|
|
||||||
WriteProcessMemory(GetCurrentProcess, @SLGetWindowsInformationDWORD,
|
|
||||||
@Stub_SLGetWindowsInformationDWORD, SizeOf(far_jmp), bw);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if V = $0602 then begin
|
|
||||||
// Windows 8
|
|
||||||
// uses SL Policy internal unexported function
|
|
||||||
|
|
||||||
// load slc.dll and get function
|
|
||||||
// (will be used on intercepting undefined values)
|
|
||||||
SLC_Handle := LoadLibrary('slc.dll');
|
|
||||||
SLGetWindowsInformationDWORD := GetProcAddress(SLC_Handle, 'SLGetWindowsInformationDWORD');
|
|
||||||
end;
|
|
||||||
if V = $0603 then begin
|
|
||||||
// Windows 8.1
|
|
||||||
// uses SL Policy internal inline code
|
|
||||||
end;
|
|
||||||
if V = $0604 then begin
|
|
||||||
// Windows 10
|
|
||||||
// uses SL Policy internal inline code
|
|
||||||
end;
|
|
||||||
|
|
||||||
Sect := IntToStr(FV.Version.w.Major)+'.'+IntToStr(FV.Version.w.Minor)+'.'+
|
|
||||||
IntToStr(FV.Release)+'.'+IntToStr(FV.Build);
|
|
||||||
|
|
||||||
if INISectionExists(INI, Sect) then
|
|
||||||
if GetModuleAddress('termsrv.dll', GetCurrentProcessId, TermSrvBase, TermSrvSize) then begin
|
|
||||||
if INIReadBool(INI, Sect, 'LocalOnlyPatch.x86', False) then begin
|
|
||||||
WriteLog('Patch CEnforcementCore::GetInstanceOfTSLicense');
|
|
||||||
SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'LocalOnlyOffset.x86', 0));
|
|
||||||
I := SListFind(PatchList, INIReadString(INI, Sect, 'LocalOnlyCode.x86', ''));
|
|
||||||
if I >= 0 then
|
|
||||||
WriteProcessMemory(GetCurrentProcess, SignPtr, @Patch[I][0], Length(Patch[I]), bw);
|
|
||||||
end;
|
|
||||||
if INIReadBool(INI, Sect, 'SingleUserPatch.x86', False) then begin
|
|
||||||
WriteLog('Patch CSessionArbitrationHelper::IsSingleSessionPerUserEnabled');
|
|
||||||
SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'SingleUserOffset.x86', 0));
|
|
||||||
I := SListFind(PatchList, INIReadString(INI, Sect, 'SingleUserCode.x86', ''));
|
|
||||||
if I >= 0 then
|
|
||||||
WriteProcessMemory(GetCurrentProcess, SignPtr, @Patch[I][0], Length(Patch[I]), bw);
|
|
||||||
end;
|
|
||||||
if INIReadBool(INI, Sect, 'DefPolicyPatch.x86', False) then begin
|
|
||||||
WriteLog('Patch CDefPolicy::Query');
|
|
||||||
SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'DefPolicyOffset.x86', 0));
|
|
||||||
I := SListFind(PatchList, INIReadString(INI, Sect, 'DefPolicyCode.x86', ''));
|
|
||||||
if I >= 0 then
|
|
||||||
WriteProcessMemory(GetCurrentProcess, SignPtr, @Patch[I][0], Length(Patch[I]), bw);
|
|
||||||
end;
|
|
||||||
if INIReadBool(INI, Sect, 'SLPolicyInternal.x86', False) then begin
|
|
||||||
WriteLog('Hook SLGetWindowsInformationDWORDWrapper');
|
|
||||||
SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'SLPolicyOffset.x86', 0));
|
|
||||||
MovJump.MovOp := $89; // mov eax, ecx
|
|
||||||
MovJump.MovArg := $C8; // __msfastcall compatibility
|
|
||||||
MovJump.PushOp := $68;
|
|
||||||
MovJump.PushArg := @New_Win8SL;
|
|
||||||
MovJump.RetOp := $C3;
|
|
||||||
FuncName := INIReadString(INI, Sect, 'SLPolicyFunc.x86', 'New_Win8SL');
|
|
||||||
if FuncName = 'New_Win8SL' then
|
|
||||||
MovJump.PushArg := @New_Win8SL;
|
|
||||||
if FuncName = 'New_Win8SL_CP' then
|
|
||||||
MovJump.PushArg := @New_Win8SL_CP;
|
|
||||||
WriteProcessMemory(GetCurrentProcess, SignPtr,
|
|
||||||
@MovJump, SizeOf(mov_far_jmp), bw);
|
|
||||||
end;
|
|
||||||
if INIReadBool(INI, Sect, 'SLInitHook.x86', False) then begin
|
|
||||||
WriteLog('Hook CSLQuery::Initialize');
|
|
||||||
SignPtr := Pointer(Cardinal(TermSrvBase) + INIReadDWordHex(INI, Sect, 'SLInitOffset.x86', 0));
|
|
||||||
Jump.PushOp := $68;
|
|
||||||
Jump.PushArg := @New_CSLQuery_Initialize;
|
|
||||||
Jump.RetOp := $C3;
|
|
||||||
FuncName := INIReadString(INI, Sect, 'SLInitFunc.x86', 'New_CSLQuery_Initialize');
|
|
||||||
if FuncName = 'New_CSLQuery_Initialize' then
|
|
||||||
Jump.PushArg := @New_CSLQuery_Initialize;
|
|
||||||
WriteProcessMemory(GetCurrentProcess, SignPtr,
|
|
||||||
@Jump, SizeOf(far_jmp), bw);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// unfreeze threads
|
|
||||||
WriteLog('Resumimg threads...');
|
|
||||||
RunThreads();
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TermServiceMain(dwArgc: DWORD; lpszArgv: PWideChar): DWORD; stdcall;
|
|
||||||
begin
|
|
||||||
// wrap ServiceMain function
|
|
||||||
WriteLog('>>> ServiceMain');
|
|
||||||
if not IsHooked then
|
|
||||||
HookFunctions;
|
|
||||||
Result := 0;
|
|
||||||
if @TSMain <> nil then
|
|
||||||
Result := TSMain(dwArgc, lpszArgv);
|
|
||||||
WriteLog('<<< ServiceMain');
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TermServiceGlobals(lpGlobalData: Pointer): DWORD; stdcall;
|
|
||||||
begin
|
|
||||||
// wrap SvchostPushServiceGlobals function
|
|
||||||
WriteLog('>>> SvchostPushServiceGlobals');
|
|
||||||
if not IsHooked then
|
|
||||||
HookFunctions;
|
|
||||||
Result := 0;
|
|
||||||
if @TSGlobals <> nil then
|
|
||||||
Result := TSGlobals(lpGlobalData);
|
|
||||||
WriteLog('<<< SvchostPushServiceGlobals');
|
|
||||||
end;
|
|
||||||
|
|
||||||
// export section
|
|
||||||
|
|
||||||
exports
|
|
||||||
TermServiceMain index 1 name 'ServiceMain',
|
|
||||||
TermServiceGlobals index 2 name 'SvchostPushServiceGlobals';
|
|
||||||
|
|
||||||
begin
|
|
||||||
// DllMain procedure is not used
|
|
||||||
end.
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<ProjectGuid>{D6811241-D595-4809-B3B8-13BECEA56E11}</ProjectGuid>
|
|
||||||
<MainSource>rdpwrap.dpr</MainSource>
|
|
||||||
<Config Condition="'$(Config)'==''">Release</Config>
|
|
||||||
<DCC_DCCCompiler>DCC32</DCC_DCCCompiler>
|
|
||||||
<ProjectVersion>12.0</ProjectVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
|
|
||||||
<Base>true</Base>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_1)'!=''">
|
|
||||||
<Cfg_1>true</Cfg_1>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
<Base>true</Base>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''">
|
|
||||||
<Cfg_2>true</Cfg_2>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
<Base>true</Base>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Base)'!=''">
|
|
||||||
<DCC_DependencyCheckOutputName>rdpwrap.dll</DCC_DependencyCheckOutputName>
|
|
||||||
<DCC_UnitAlias>WinTypes=Windows;WinProcs=Windows;$(DCC_UnitAlias)</DCC_UnitAlias>
|
|
||||||
<GenDll>true</GenDll>
|
|
||||||
<DCC_ImageBase>00400000</DCC_ImageBase>
|
|
||||||
<DCC_Platform>x86</DCC_Platform>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Cfg_1)'!=''">
|
|
||||||
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
|
|
||||||
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
|
|
||||||
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
|
|
||||||
<DCC_DebugInformation>false</DCC_DebugInformation>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Cfg_2)'!=''">
|
|
||||||
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<DelphiCompile Include="rdpwrap.dpr">
|
|
||||||
<MainSource>MainSource</MainSource>
|
|
||||||
</DelphiCompile>
|
|
||||||
<BuildConfiguration Include="Base">
|
|
||||||
<Key>Base</Key>
|
|
||||||
</BuildConfiguration>
|
|
||||||
<BuildConfiguration Include="Release">
|
|
||||||
<Key>Cfg_1</Key>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
</BuildConfiguration>
|
|
||||||
<BuildConfiguration Include="Debug">
|
|
||||||
<Key>Cfg_2</Key>
|
|
||||||
<CfgParent>Base</CfgParent>
|
|
||||||
</BuildConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
|
|
||||||
<ProjectExtensions>
|
|
||||||
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
|
|
||||||
<Borland.ProjectType>VCLApplication</Borland.ProjectType>
|
|
||||||
<BorlandProject>
|
|
||||||
<Delphi.Personality>
|
|
||||||
<Source>
|
|
||||||
<Source Name="MainSource">rdpwrap.dpr</Source>
|
|
||||||
</Source>
|
|
||||||
<Parameters>
|
|
||||||
<Parameters Name="UseLauncher">False</Parameters>
|
|
||||||
<Parameters Name="LoadAllSymbols">True</Parameters>
|
|
||||||
<Parameters Name="LoadUnspecifiedSymbols">False</Parameters>
|
|
||||||
</Parameters>
|
|
||||||
<VersionInfo>
|
|
||||||
<VersionInfo Name="IncludeVerInfo">False</VersionInfo>
|
|
||||||
<VersionInfo Name="AutoIncBuild">False</VersionInfo>
|
|
||||||
<VersionInfo Name="MajorVer">1</VersionInfo>
|
|
||||||
<VersionInfo Name="MinorVer">0</VersionInfo>
|
|
||||||
<VersionInfo Name="Release">0</VersionInfo>
|
|
||||||
<VersionInfo Name="Build">0</VersionInfo>
|
|
||||||
<VersionInfo Name="Debug">False</VersionInfo>
|
|
||||||
<VersionInfo Name="PreRelease">False</VersionInfo>
|
|
||||||
<VersionInfo Name="Special">False</VersionInfo>
|
|
||||||
<VersionInfo Name="Private">False</VersionInfo>
|
|
||||||
<VersionInfo Name="DLL">False</VersionInfo>
|
|
||||||
<VersionInfo Name="Locale">1049</VersionInfo>
|
|
||||||
<VersionInfo Name="CodePage">1251</VersionInfo>
|
|
||||||
</VersionInfo>
|
|
||||||
<VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="CompanyName"/>
|
|
||||||
<VersionInfoKeys Name="FileDescription"/>
|
|
||||||
<VersionInfoKeys Name="FileVersion">1.0.0.0</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="InternalName"/>
|
|
||||||
<VersionInfoKeys Name="LegalCopyright"/>
|
|
||||||
<VersionInfoKeys Name="LegalTrademarks"/>
|
|
||||||
<VersionInfoKeys Name="OriginalFilename"/>
|
|
||||||
<VersionInfoKeys Name="ProductName"/>
|
|
||||||
<VersionInfoKeys Name="ProductVersion">1.0.0.0</VersionInfoKeys>
|
|
||||||
<VersionInfoKeys Name="Comments"/>
|
|
||||||
</VersionInfoKeys>
|
|
||||||
<Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\bcboffice2k140.bpl">Embarcadero C++Builder Office 2000 Servers Package</Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\bcbofficexp140.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\dcloffice2k140.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\dclofficexp140.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
|
|
||||||
</Excluded_Packages>
|
|
||||||
</Delphi.Personality>
|
|
||||||
</BorlandProject>
|
|
||||||
<ProjectFileVersion>12</ProjectFileVersion>
|
|
||||||
</ProjectExtensions>
|
|
||||||
</Project>
|
|
||||||
@ -1,561 +0,0 @@
|
|||||||
RDP Wrapper Library project by Stas'M
|
|
||||||
|
|
||||||
Terminal Services supported versions
|
|
||||||
6.0.X.X (Windows Vista, any) [policy hook only]
|
|
||||||
6.0.6000.16386 (Windows Vista) [policy hook + extended patch]
|
|
||||||
6.0.6000.20723 (Windows Vista with KB944917) [todo]
|
|
||||||
6.0.6001.18000 (Windows Vista SP1) [policy hook + extended patch]
|
|
||||||
6.0.6001.22286 (Windows Vista SP1 with KB958612) [todo]
|
|
||||||
6.0.6001.22357 (Windows Vista SP1 with KB958612 v2) [todo]
|
|
||||||
6.0.6001.22323 (Windows Vista SP1 with KB960742) [todo]
|
|
||||||
6.0.6001.22392 (Windows Vista SP1 with KB968680) [todo]
|
|
||||||
6.0.6001.22565 (Windows Vista SP1 with KB977541) [todo]
|
|
||||||
6.0.6001.22635 (Windows Vista SP1 with KB970911) [todo]
|
|
||||||
6.0.6001.22801 (Windows Vista SP1 with KB2381675) [todo]
|
|
||||||
6.0.6002.18005 (Windows Vista SP2) [policy hook + extended patch]
|
|
||||||
6.0.6002.22269 (Windows Vista SP2 with KB977541) [todo]
|
|
||||||
6.0.6002.22340 (Windows Vista SP2 with KB970911) [todo]
|
|
||||||
6.0.6002.22515 (Windows Vista SP2 with KB2381675) [todo]
|
|
||||||
6.0.6002.22641 (Windows Vista SP2 with KB2523307) [todo]
|
|
||||||
6.0.6002.22790 (Windows Vista SP2 with KB2672601) [todo]
|
|
||||||
6.0.6002.19214 (Windows Vista SP2 with KB3003743 GDR) [policy hook + extended patch]
|
|
||||||
6.0.6002.23521 (Windows Vista SP2 with KB3003743 LDR) [policy hook + extended patch]
|
|
||||||
6.1.X.X (Windows 7, any) [policy hook only]
|
|
||||||
6.1.7100.0 (Windows 7 Release Candidate) [todo]
|
|
||||||
6.1.7600.16385 (Windows 7) [policy hook + extended patch]
|
|
||||||
6.1.7600.20661 (Windows 7 with KB951422) [todo]
|
|
||||||
6.1.7600.21085 (Windows 7 with KB951422 v2) [todo]
|
|
||||||
6.1.7600.20621 (Windows 7 with KB979470) [todo]
|
|
||||||
6.1.7600.20890 (Windows 7 with KB2479710) [policy hook + extended patch]
|
|
||||||
6.1.7600.21316 (Windows 7 with KB2750090) [policy hook + extended patch]
|
|
||||||
6.1.7600.21420 (Windows 7 with KB2800789) [todo]
|
|
||||||
6.1.7601.17514 (Windows 7 SP1) [policy hook + extended patch]
|
|
||||||
6.1.7601.21855 (Windows 7 SP1 with KB951422 v2) [todo]
|
|
||||||
6.1.7601.21650 (Windows 7 SP1 with KB2479710) [policy hook + extended patch]
|
|
||||||
6.1.7601.21866 (Windows 7 SP1 with KB2647409) [policy hook + extended patch]
|
|
||||||
6.1.7601.22104 (Windows 7 SP1 with KB2750090) [policy hook + extended patch]
|
|
||||||
6.1.7601.22213 (Windows 7 SP1 with KB2800789) [todo]
|
|
||||||
6.1.7601.22476 (Windows 7 SP1 with KB2870165) [todo]
|
|
||||||
6.1.7601.22435 (Windows 7 SP1 with KB2878424) [todo]
|
|
||||||
6.1.7601.22477 (Windows 7 SP1 with KB2896256) [todo]
|
|
||||||
6.1.7601.18540 (Windows 7 SP1 with KB2984972 GDR) [policy hook + extended patch]
|
|
||||||
6.1.7601.22750 (Windows 7 SP1 with KB2984972 LDR) [policy hook + extended patch]
|
|
||||||
6.1.7601.18637 (Windows 7 SP1 with KB3003743 GDR) [policy hook + extended patch]
|
|
||||||
6.1.7601.22843 (Windows 7 SP1 with KB3003743 LDR) [policy hook + extended patch]
|
|
||||||
6.1.7601.23403 (Windows 7 SP1 with KB3125574) [policy hook + extended patch]
|
|
||||||
6.1.7601.24234 (Windows 7 SP1 with KB4462923) [policy hook + extended patch]
|
|
||||||
6.2.8102.0 (Windows 8 Developer Preview) [policy hook + extended patch]
|
|
||||||
6.2.8250.0 (Windows 8 Consumer Preview) [policy hook + extended patch]
|
|
||||||
6.2.8400.0 (Windows 8 Release Preview) [policy hook + extended patch]
|
|
||||||
6.2.9200.16384 (Windows 8) [policy hook + extended patch]
|
|
||||||
6.2.9200.17048 (Windows 8 with KB2973501 GDR) [policy hook + extended patch]
|
|
||||||
6.2.9200.21166 (Windows 8 with KB2973501 LDR) [policy hook + extended patch]
|
|
||||||
6.3.9431.0 (Windows 8.1 Preview) [init hook + extended patch]
|
|
||||||
6.3.9600.16384 (Windows 8.1) [init hook + extended patch]
|
|
||||||
6.3.9600.17095 (Windows 8.1 with KB2959626) [init hook + extended patch]
|
|
||||||
6.3.9600.17415 (Windows 8.1 with KB3000850) [init hook + extended patch]
|
|
||||||
6.3.9600.18692 (Windows 8.1 with KB4022720) [init hook + extended patch]
|
|
||||||
6.3.9600.18708 (Windows 8.1 with KB4025335) [init hook + extended patch]
|
|
||||||
6.3.9600.18928 (Windows 8.1 with KB4088876) [init hook + extended patch]
|
|
||||||
6.3.9600.19093 (Windows 8.1 with KB4343891) [init hook + extended patch]
|
|
||||||
6.4.9841.0 (Windows 10 Technical Preview) [init hook + extended patch]
|
|
||||||
6.4.9860.0 (Windows 10 Technical Preview UP1) [init hook + extended patch]
|
|
||||||
6.4.9879.0 (Windows 10 Technical Preview UP2) [init hook + extended patch]
|
|
||||||
10.0.9926.0 (Windows 10 Pro Technical Preview) [init hook + extended patch]
|
|
||||||
10.0.10041.0 (Windows 10 Pro Technical Preview UP1) [init hook + extended patch]
|
|
||||||
10.0.10049.0 (Windows 10 Pro Technical Preview UP2) [todo]
|
|
||||||
10.0.10061.0 (Windows 10 Pro Technical Preview UP3) [todo]
|
|
||||||
10.0.10240.16384 (Windows 10 RTM) [init hook + extended patch]
|
|
||||||
10.0.10525.0 (Windows 10 th2_release.150812-1658) [todo]
|
|
||||||
10.0.10532.0 (Windows 10 th2_release.150822-1406) [todo]
|
|
||||||
10.0.10547.0 (Windows 10 th2_release.150913-1511) [todo]
|
|
||||||
10.0.10586.0 (Windows 10 th2_release.151029-1700) [init hook + extended patch]
|
|
||||||
10.0.10586.589 (Windows 10 th2_release.160906-1759) [init hook + extended patch]
|
|
||||||
10.0.11082.1000 (Windows 10 rs1_release.151210-2021) [init hook + extended patch]
|
|
||||||
10.0.11102.1000 (Windows 10 rs1_release.160113-1800) [init hook + extended patch]
|
|
||||||
10.0.14251.1000 (Windows 10 rs1_release.160124-1059) [init hook + extended patch]
|
|
||||||
10.0.14271.1000 (Windows 10 rs1_release.160218-2310) [init hook + extended patch]
|
|
||||||
10.0.14279.1000 (Windows 10 rs1_release.160229-1700) [init hook + extended patch]
|
|
||||||
10.0.14295.1000 (Windows 10 rs1_release.160318-1628) [init hook + extended patch]
|
|
||||||
10.0.14300.1000 (Windows Server 2016 Technical Preview 5) [init hook + extended patch]
|
|
||||||
10.0.14316.1000 (Windows 10 rs1_release.160402-2227) [init hook + extended patch]
|
|
||||||
10.0.14328.1000 (Windows 10 rs1_release.160418-1609) [init hook + extended patch]
|
|
||||||
10.0.14332.1001 (Windows 10 rs1_release.160422-1940) [init hook + extended patch]
|
|
||||||
10.0.14342.1000 (Windows 10 rs1_release.160506-1708) [init hook + extended patch]
|
|
||||||
10.0.14352.1002 (Windows 10 rs1_release.160522-1930) [init hook + extended patch]
|
|
||||||
10.0.14366.0 (Windows 10 rs1_release.160610-1700) [init hook + extended patch]
|
|
||||||
10.0.14367.0 (Windows 10 rs1_release.160613-1700) [init hook + extended patch]
|
|
||||||
10.0.14372.0 (Windows 10 rs1_release.160620-2342) [init hook + extended patch]
|
|
||||||
10.0.14379.0 (Windows 10 rs1_release.160627-1607) [init hook + extended patch]
|
|
||||||
10.0.14383.0 (Windows 10 rs1_release.160701-1839) [init hook + extended patch]
|
|
||||||
10.0.14385.0 (Windows 10 rs1_release.160706-1700) [init hook + extended patch]
|
|
||||||
10.0.14388.0 (Windows 10 rs1_release.160709-1635) [init hook + extended patch]
|
|
||||||
10.0.14393.0 (Windows 10 rs1_release.160715-1616) [init hook + extended patch]
|
|
||||||
10.0.14393.1198 (Windows 10 rs1_release_sec.170427-1353) [init hook + extended patch]
|
|
||||||
10.0.14393.1737 (Windows 10 rs1_release_inmarket.170914-1249) [init hook + extended patch]
|
|
||||||
10.0.14393.2457 (Windows 10 rs1_release_inmarket.180822-1743) [init hook + extended patch]
|
|
||||||
10.0.14901.1000 (Windows 10 rs_prerelease.160805-1700) [init hook + extended patch]
|
|
||||||
10.0.14905.1000 (Windows 10 rs_prerelease.160811-1739) [init hook + extended patch]
|
|
||||||
10.0.14915.1000 (Windows 10 rs_prerelease.160826-1902) [init hook + extended patch]
|
|
||||||
10.0.14926.1000 (Windows 10 rs_prerelease.160910-1529) [init hook + extended patch]
|
|
||||||
10.0.14931.1000 (Windows 10 rs_prerelease.160916-1700) [init hook + extended patch]
|
|
||||||
10.0.14936.1000 (Windows 10 rs_prerelease.160923-1700) [init hook + extended patch]
|
|
||||||
10.0.14942.1000 (Windows 10 rs_prerelease.161003-1929) [init hook + extended patch]
|
|
||||||
10.0.14946.1000 (Windows 10 rs_prerelease.161007-1700) [init hook + extended patch]
|
|
||||||
10.0.14951.1000 (Windows 10 rs_prerelease.161014-1700) [init hook + extended patch]
|
|
||||||
10.0.14955.1000 (Windows 10 rs_prerelease.161020-1700) [init hook + extended patch]
|
|
||||||
10.0.14959.1000 (Windows 10 rs_prerelease.161026-1700) [init hook + extended patch]
|
|
||||||
10.0.14965.1001 (Windows 10 rs_prerelease.161104-1700) [init hook + extended patch]
|
|
||||||
10.0.14971.1000 (Windows 10 rs_prerelease.161111-1700) [init hook + extended patch]
|
|
||||||
10.0.14986.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.14997.1001 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.15002.1001 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.15007.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.15014.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.15019.1000 (Windows 10 rs_prerelease.170121-1513) [init hook + extended patch]
|
|
||||||
10.0.15025.1000 (Windows 10 rs_prerelease.170127-1750) [init hook + extended patch]
|
|
||||||
10.0.15031.0 (Windows 10 rs2_release.170204-1546) [init hook + extended patch]
|
|
||||||
10.0.15042.0 (Windows 10 rs2_release.170219-2329) [init hook + extended patch]
|
|
||||||
10.0.15046.0 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.15048.0 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.15055.0 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.15058.0 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.15061.0 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.15063.0 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.15063.296 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.15063.994 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.15063.1155 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16179.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16184.1001 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16199.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16215.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16232.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16237.1001 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16241.1001 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16251.0 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16251.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16257.1 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16257.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16273.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16275.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16278.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16281.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16288.1 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16291.0 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16294.1 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16296.0 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16299.0 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16299.15 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16353.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.16362.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.17004.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.17017.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.17025.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.17035.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.17040.1000 (Windows 10 WinBuild.160101.0800) [todo]
|
|
||||||
10.0.17046.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.17063.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.17074.1002 (Windows 10 WinBuild.160101.0800) [todo]
|
|
||||||
10.0.17083.1000 (Windows 10 WinBuild.160101.0800) [todo]
|
|
||||||
10.0.17115.1 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.17128.1 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.17133.1 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.17134.1 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.17723.1000 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
10.0.17763.1 (Windows 10 WinBuild.160101.0800) [init hook + extended patch]
|
|
||||||
|
|
||||||
Source code changelog (rdpwrap library):
|
|
||||||
|
|
||||||
2018.10.10 :
|
|
||||||
- added support for termsrv.dll 6.1.7601.24234 x86
|
|
||||||
|
|
||||||
2018.10.04 :
|
|
||||||
- added support for termsrv.dll 10.0.14393.2457 x86
|
|
||||||
|
|
||||||
2018.10.03 :
|
|
||||||
- added support for termsrv.dll 6.1.7601.24234 x64
|
|
||||||
- added support for termsrv.dll 10.0.15063.994 x64
|
|
||||||
- added support for termsrv.dll 10.0.17723.1000 x64
|
|
||||||
- added support for termsrv.dll 10.0.17763.1
|
|
||||||
|
|
||||||
2018.09.10 :
|
|
||||||
- added support for termsrv.dll 6.1.7600.20890
|
|
||||||
- added support for termsrv.dll 6.1.7600.21316
|
|
||||||
- added support for termsrv.dll 6.1.7601.21650
|
|
||||||
- added support for termsrv.dll 6.1.7601.21866
|
|
||||||
- added support for termsrv.dll 6.1.7601.22104
|
|
||||||
- added support for termsrv.dll 6.3.9600.19093
|
|
||||||
- added support for termsrv.dll 10.0.14393.2457 x64
|
|
||||||
- added support for termsrv.dll 10.0.15063.1155 x64
|
|
||||||
|
|
||||||
2018.05.16 :
|
|
||||||
- added support for termsrv.dll 10.0.17115.1
|
|
||||||
- added support for termsrv.dll 10.0.17128.1
|
|
||||||
- added support for termsrv.dll 10.0.17133.1
|
|
||||||
- added support for termsrv.dll 10.0.17134.1
|
|
||||||
|
|
||||||
2018.03.26 :
|
|
||||||
- added support for termsrv.dll 6.3.9600.18928 by 1nd1g0
|
|
||||||
|
|
||||||
2017.12.27 :
|
|
||||||
- added support for termsrv.dll 10.0.17017.1000
|
|
||||||
- added support for termsrv.dll 10.0.17025.1000
|
|
||||||
- added support for termsrv.dll 10.0.17035.1000
|
|
||||||
- added support for termsrv.dll 10.0.17046.1000
|
|
||||||
- added support for termsrv.dll 10.0.17063.1000
|
|
||||||
|
|
||||||
2017.10.13 :
|
|
||||||
- added support for termsrv.dll 10.0.14393.1737
|
|
||||||
- added support for termsrv.dll 10.0.16299.0
|
|
||||||
- added support for termsrv.dll 10.0.16299.15
|
|
||||||
- added support for termsrv.dll 10.0.17004.1000
|
|
||||||
|
|
||||||
2017.09.24 :
|
|
||||||
- added support for termsrv.dll 10.0.16291.0
|
|
||||||
- added support for termsrv.dll 10.0.16294.1
|
|
||||||
- added support for termsrv.dll 10.0.16296.0
|
|
||||||
- added support for termsrv.dll 10.0.16362.1000
|
|
||||||
|
|
||||||
2017.09.15 :
|
|
||||||
- added support for termsrv.dll 10.0.16288.1
|
|
||||||
|
|
||||||
2017.09.06 :
|
|
||||||
- added support for termsrv.dll 10.0.16273.1000
|
|
||||||
- added support for termsrv.dll 10.0.16275.1000
|
|
||||||
- added support for termsrv.dll 10.0.16278.1000
|
|
||||||
- added support for termsrv.dll 10.0.16281.1000
|
|
||||||
- added support for termsrv.dll 10.0.16353.1000
|
|
||||||
|
|
||||||
2017.08.04 :
|
|
||||||
- added support for termsrv.dll 10.0.16257.1
|
|
||||||
- added support for termsrv.dll 10.0.16257.1000
|
|
||||||
|
|
||||||
2017.07.30 :
|
|
||||||
- added support for termsrv.dll 6.3.9600.18708
|
|
||||||
- added support for termsrv.dll 10.0.16232.1000
|
|
||||||
- added support for termsrv.dll 10.0.16237.1001
|
|
||||||
- added support for termsrv.dll 10.0.16241.1001
|
|
||||||
- added support for termsrv.dll 10.0.16251.0
|
|
||||||
- added support for termsrv.dll 10.0.16251.1000
|
|
||||||
|
|
||||||
2017.06.29 :
|
|
||||||
- added support for termsrv.dll 6.3.9600.18692
|
|
||||||
|
|
||||||
2017.06.10 :
|
|
||||||
- added support for termsrv.dll 10.0.15063.296
|
|
||||||
- added support for termsrv.dll 10.0.16215.1000
|
|
||||||
|
|
||||||
2017.05.29 :
|
|
||||||
- added support for termsrv.dll 10.0.16199.1000
|
|
||||||
|
|
||||||
2017.05.17 :
|
|
||||||
- added support for termsrv.dll 10.0.14997.1001 x64
|
|
||||||
|
|
||||||
2017.05.12 :
|
|
||||||
- added support for termsrv.dll 10.0.14393.1198 x86
|
|
||||||
|
|
||||||
2017.05.03 :
|
|
||||||
- added support for termsrv.dll 10.0.16179.1000
|
|
||||||
- added support for termsrv.dll 10.0.16184.1001
|
|
||||||
|
|
||||||
2017.03.22 :
|
|
||||||
- added support for termsrv.dll 10.0.15063.0
|
|
||||||
|
|
||||||
2017.03.21 :
|
|
||||||
- added support for termsrv.dll 10.0.15061.0
|
|
||||||
|
|
||||||
2017.03.16 :
|
|
||||||
- added support for termsrv.dll 10.0.15058.0
|
|
||||||
|
|
||||||
2017.03.14 :
|
|
||||||
- added support for termsrv.dll 10.0.15055.0
|
|
||||||
|
|
||||||
2017.03.05 :
|
|
||||||
- added support for termsrv.dll 10.0.15048.0
|
|
||||||
|
|
||||||
2017.03.02 :
|
|
||||||
- added support for termsrv.dll 10.0.15046.0
|
|
||||||
|
|
||||||
2017.03.01 :
|
|
||||||
- added support for termsrv.dll 10.0.15031.0
|
|
||||||
- added support for termsrv.dll 10.0.15042.0
|
|
||||||
|
|
||||||
2017.02.03 :
|
|
||||||
- added support for termsrv.dll 10.0.15025.1000 x64
|
|
||||||
|
|
||||||
2017.01.28 :
|
|
||||||
- added support for termsrv.dll 10.0.15019.1000
|
|
||||||
|
|
||||||
2017.01.21 :
|
|
||||||
- added support for termsrv.dll 10.0.15014.1000
|
|
||||||
|
|
||||||
2017.01.15 :
|
|
||||||
- added support for termsrv.dll 10.0.15007.1000
|
|
||||||
|
|
||||||
2017.01.12 :
|
|
||||||
- added support for termsrv.dll 10.0.15002.1001
|
|
||||||
|
|
||||||
2016.12.23 :
|
|
||||||
- added support for termsrv.dll 10.0.14986.1000
|
|
||||||
|
|
||||||
2016.11.19 :
|
|
||||||
- added support for termsrv.dll 10.0.14959.1000
|
|
||||||
- added support for termsrv.dll 10.0.14965.1001
|
|
||||||
- added support for termsrv.dll 10.0.14971.1000
|
|
||||||
|
|
||||||
2016.10.28 :
|
|
||||||
- added support for termsrv.dll 10.0.14955.1000
|
|
||||||
|
|
||||||
2016.10.21 :
|
|
||||||
- added support for termsrv.dll 10.0.14951.1000
|
|
||||||
|
|
||||||
2016.10.19 :
|
|
||||||
- added support for termsrv.dll 10.0.14946.1000
|
|
||||||
|
|
||||||
2016.10.08 :
|
|
||||||
- added support for termsrv.dll 10.0.14942.1000
|
|
||||||
|
|
||||||
2016.09.30 :
|
|
||||||
- added support for termsrv.dll 10.0.14936.1000
|
|
||||||
|
|
||||||
2016.09.27 :
|
|
||||||
- added support for termsrv.dll 10.0.14931.1000
|
|
||||||
|
|
||||||
2016.09.15 :
|
|
||||||
- added support for termsrv.dll 10.0.14926.1000
|
|
||||||
|
|
||||||
2016.09.14 :
|
|
||||||
- added support for termsrv.dll 10.0.10586.589
|
|
||||||
|
|
||||||
2016.09.03 :
|
|
||||||
- added support for termsrv.dll 10.0.14915.1000
|
|
||||||
|
|
||||||
2016.08.28 :
|
|
||||||
- added support for termsrv.dll 6.1.7601.23403
|
|
||||||
- added support for termsrv.dll 10.0.14901.1000
|
|
||||||
- added support for termsrv.dll 10.0.14905.1000
|
|
||||||
|
|
||||||
2016.08.12 :
|
|
||||||
- added support for termsrv.dll 10.0.14385.0
|
|
||||||
|
|
||||||
2016.08.01 :
|
|
||||||
- preparing the release
|
|
||||||
|
|
||||||
2016.07.23 :
|
|
||||||
- added online install mode to installer
|
|
||||||
- added feature to keep settings on uninstall
|
|
||||||
- fixed update firewall rule on port change in config tool
|
|
||||||
- added feature to hide users on logon
|
|
||||||
|
|
||||||
2016.07.22 :
|
|
||||||
- added support for termsrv.dll 10.0.14393.0
|
|
||||||
|
|
||||||
2016.07.15 :
|
|
||||||
- added support for termsrv.dll 10.0.14383.0
|
|
||||||
- added support for termsrv.dll 10.0.14388.0
|
|
||||||
|
|
||||||
2016.07.06 :
|
|
||||||
- added support for termsrv.dll 10.0.14379.0
|
|
||||||
|
|
||||||
2016.06.27 :
|
|
||||||
- added support for termsrv.dll 10.0.14372.0 x86
|
|
||||||
|
|
||||||
2016.06.26 :
|
|
||||||
- added support for termsrv.dll 10.0.14372.0 x64 by kbmorris
|
|
||||||
|
|
||||||
2016.06.17 :
|
|
||||||
- fixed issue with termsrv.dll 10.0.14352.1002
|
|
||||||
- added support for termsrv.dll 10.0.14366.0
|
|
||||||
- added support for termsrv.dll 10.0.14367.0
|
|
||||||
|
|
||||||
2016.05.30 :
|
|
||||||
- added support for termsrv.dll 10.0.14352.1002
|
|
||||||
|
|
||||||
2016.05.14 :
|
|
||||||
- added support for termsrv.dll 10.0.14342.1000
|
|
||||||
|
|
||||||
2016.05.08 :
|
|
||||||
- added support for termsrv.dll 10.0.14300.1000 x64
|
|
||||||
- added support for termsrv.dll 10.0.14328.1000
|
|
||||||
|
|
||||||
2016.04.29 :
|
|
||||||
- added support for termsrv.dll 10.0.14332.1001 by maxpiva
|
|
||||||
|
|
||||||
2016.04.14 :
|
|
||||||
- added support for termsrv.dll 10.0.14316.1000
|
|
||||||
|
|
||||||
2016.04.06 :
|
|
||||||
- added support for termsrv.dll 10.0.14295.1000
|
|
||||||
|
|
||||||
2016.03.07 :
|
|
||||||
- added experimental codes for ARMv7 architecture (see rdpwrap-arm-kb.ini)
|
|
||||||
- Windows RT / termsrv.dll 6.2.9200.16384
|
|
||||||
- Windows RT 8.1 / termsrv.dll 6.3.9600.16384
|
|
||||||
- Windows RT 8.1 / termsrv.dll 6.3.9600.17095
|
|
||||||
|
|
||||||
2016.03.06 :
|
|
||||||
- added support for termsrv.dll 10.0.14279.1000
|
|
||||||
|
|
||||||
2016.02.29 :
|
|
||||||
- added support for termsrv.dll 10.0.14271.1000
|
|
||||||
|
|
||||||
2016.01.28 :
|
|
||||||
- added support for termsrv.dll 10.0.14251.1000
|
|
||||||
|
|
||||||
2016.01.26 :
|
|
||||||
- added support for termsrv.dll 10.0.11102.1000
|
|
||||||
|
|
||||||
2016.01.15 :
|
|
||||||
- updated messages in the installer
|
|
||||||
- added support for termsrv.dll 10.0.11082.1000
|
|
||||||
|
|
||||||
2015.11.14 :
|
|
||||||
- added support for termsrv.dll 10.0.10586.0
|
|
||||||
|
|
||||||
2015.08.11 :
|
|
||||||
- embed new rdpclip versions in the installer (for NT 6.0 and 6.1)
|
|
||||||
- preparing the release
|
|
||||||
|
|
||||||
2015.08.07 :
|
|
||||||
- added INI update feature to installer
|
|
||||||
|
|
||||||
2015.07.30 :
|
|
||||||
- fixed issue with Windows 10 Home x86 (wrong LocalOnly offset was specified in INI file)
|
|
||||||
|
|
||||||
2015.07.17 :
|
|
||||||
- added support for termsrv.dll 10.0.10240.16384
|
|
||||||
- added HOW TO hints to KB (so other reverse engineers can do this hard work more easier)
|
|
||||||
|
|
||||||
2015.07.16 :
|
|
||||||
- moved all comments from INI file to Knowledge Base text file
|
|
||||||
- now INI file have smaller size
|
|
||||||
- updated RDP checker: changed IP Address to 127.0.0.2 (sometimes client doesn't want to connect .1), updated text message
|
|
||||||
- updated RDP config: list all possible shadowing modes, also write group policy
|
|
||||||
- updated installer: added workaround for 1056 error
|
|
||||||
- updated copyright years in source code
|
|
||||||
- obtained files from build 10.0.10240.16384
|
|
||||||
- researching Windows 10 RTM
|
|
||||||
|
|
||||||
2015.03.23 :
|
|
||||||
- researching Windows 10 Pro Technical Preview UP1
|
|
||||||
- added support for termsrv.dll 10.0.10041.0
|
|
||||||
|
|
||||||
2015.03.20 :
|
|
||||||
- new build 10.0.10041.0 was released, obtaining files...
|
|
||||||
|
|
||||||
2015.01.26 :
|
|
||||||
- researching Windows 10 Pro Technical Preview (10.0.9926.0 x86)
|
|
||||||
- added support for termsrv.dll 10.0.9926.0 (x86)
|
|
||||||
|
|
||||||
2015.01.22 :
|
|
||||||
- v-yadli contributed offsets for version 10.0.9926.0 (x64)
|
|
||||||
|
|
||||||
2014.12.13 :
|
|
||||||
- added more policy values to INI file
|
|
||||||
|
|
||||||
2014.12.10 :
|
|
||||||
- C++ version seems to work well now!
|
|
||||||
- added support for termsrv.dll 6.4.9879.0
|
|
||||||
- preparing the new release
|
|
||||||
|
|
||||||
2014.12.09 :
|
|
||||||
- many bug fixes in C++ version, you can track it in the git history :)
|
|
||||||
- it can be compiled now :D
|
|
||||||
- we are getting closer to the finish line!
|
|
||||||
|
|
||||||
2014.12.03 :
|
|
||||||
- added INI reader by Fusix for C++ version
|
|
||||||
- asulwer also helped with the development
|
|
||||||
|
|
||||||
2014.11.25 :
|
|
||||||
- corrected some typos in INI file
|
|
||||||
- added EasyPrint policy value
|
|
||||||
|
|
||||||
2014.11.24 :
|
|
||||||
- added support for termsrv.dll 6.3.9600.17415
|
|
||||||
|
|
||||||
2014.11.21 :
|
|
||||||
- new LiteINI module to read INI files
|
|
||||||
- added support to store patch settings in INI file
|
|
||||||
- version support can be extended without recompilation
|
|
||||||
- C++ version needs to be updated
|
|
||||||
|
|
||||||
2014.11.20 :
|
|
||||||
- improved comments
|
|
||||||
- researching KB3000850
|
|
||||||
- found required files
|
|
||||||
- improving RDPWrap...
|
|
||||||
- placing signatures, offsets, values, etc in separate config file
|
|
||||||
- working with code
|
|
||||||
|
|
||||||
2014.11.13 :
|
|
||||||
- researching KB3003743
|
|
||||||
- added support for version 6.0.6002.19214
|
|
||||||
- added support for version 6.0.6002.23521
|
|
||||||
- added support for version 6.1.7601.18637
|
|
||||||
- added support for version 6.1.7601.22843
|
|
||||||
|
|
||||||
2014.11.02 :
|
|
||||||
- researching termsrv.dll 6.4.9860.0
|
|
||||||
- done
|
|
||||||
|
|
||||||
2014.10.19 :
|
|
||||||
- added support for version 6.0.6000.16386 (x64)
|
|
||||||
- added support for version 6.0.6001.18000 (x64)
|
|
||||||
- added support for version 6.1.7600.16385
|
|
||||||
|
|
||||||
2014.10.18 :
|
|
||||||
- corrected some typos in source
|
|
||||||
- simplified signature constants
|
|
||||||
- added support for version 6.0.6000.16386 (x86)
|
|
||||||
- added support for version 6.0.6001.18000 (x86)
|
|
||||||
- added support for version 6.0.6002.18005
|
|
||||||
- added support for version 6.1.7601.17514
|
|
||||||
- added support for version 6.1.7601.18540
|
|
||||||
- added support for version 6.1.7601.22750
|
|
||||||
- added support for version 6.2.9200.17048
|
|
||||||
- added support for version 6.2.9200.21166
|
|
||||||
|
|
||||||
2014.10.17 :
|
|
||||||
- collecting information about all versions of Terminal Services beginning from Vista
|
|
||||||
- added [todo] to the versions list
|
|
||||||
|
|
||||||
2014.10.16 :
|
|
||||||
- got new updates: KB2984972 for Win 7 (still works with 2 concurrent users) and KB2973501 for Win 8 (doesn't work)
|
|
||||||
|
|
||||||
2014.10.02 :
|
|
||||||
- researching Windows 10 TP Remote Desktop
|
|
||||||
- done! even without debugging symbols ^^)
|
|
||||||
|
|
||||||
2014.07.20 :
|
|
||||||
- added support for Windows 8 Release Preview
|
|
||||||
- added support for Windows 8 Consumer Preview
|
|
||||||
- added support for Windows 8 Developer Preview
|
|
||||||
|
|
||||||
2014.07.19 :
|
|
||||||
- improved patching of Windows 8
|
|
||||||
- added policy patches
|
|
||||||
- will patch CDefPolicy::Query
|
|
||||||
- will patch CSessionArbitrationHelper::IsSingleSessionPerUserEnabled
|
|
||||||
|
|
||||||
2014.07.18 :
|
|
||||||
- researched patched files from MDL forum
|
|
||||||
- CSLQuery::GetMaxSessions requires no patching
|
|
||||||
- it's better to change the default policy, so...
|
|
||||||
- will patch CDefPolicy::Query
|
|
||||||
- will patch CEnforcementCore::GetInstanceOfTSLicense
|
|
||||||
- will patch CSessionArbitrationHelper::IsSingleSessionPerUserEnabled
|
|
||||||
- the function CSLQuery::Initialize is hooked correctly
|
|
||||||
|
|
||||||
2014.07.17 :
|
|
||||||
- will hook only CSLQuery::Initialize function
|
|
||||||
- CSLQuery::GetMaxSessions will be patched
|
|
||||||
- added x86 signatures for 6.3.9431.0 (Windows 8.1 Preview)
|
|
||||||
|
|
||||||
2014.07.16 :
|
|
||||||
- changing asm opcodes is bad, will hook CSL functions
|
|
||||||
|
|
||||||
2014.07.15 :
|
|
||||||
- added x86 signatures for 6.3.9600.16384 (Windows 8.1)
|
|
||||||
2014.07.15 :
|
|
||||||
- added x86 signatures for 6.3.9600.17095 (Windows 8.1 with KB2959626)
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
# RDPWrapOffsetFinder
|
|
||||||
|
|
||||||
Pre-built binaries of [llccd/RDPWrapOffsetFinder](https://github.com/llccd/RDPWrapOffsetFinder),
|
|
||||||
committed here so that the CI pipeline and releases are self-contained and
|
|
||||||
reproducible without depending on an external release being available at build time.
|
|
||||||
|
|
||||||
## Contents
|
|
||||||
|
|
||||||
```
|
|
||||||
x64/
|
|
||||||
RDPWrapOffsetFinder.exe # x86-64 build
|
|
||||||
Zydis.dll # required runtime (x64)
|
|
||||||
x86/
|
|
||||||
RDPWrapOffsetFinder.exe # x86 32-bit build
|
|
||||||
Zydis.dll # required runtime (x86)
|
|
||||||
VERSION # version tag of the upstream release
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Extract the appropriate arch folder and run:
|
|
||||||
|
|
||||||
```
|
|
||||||
.\RDPWrapOffsetFinder.exe C:\Windows\System32\termsrv.dll
|
|
||||||
```
|
|
||||||
|
|
||||||
The output `[10.0.xxxxx.xxxxx]` section can be appended to `res/rdpwrap.ini`
|
|
||||||
and submitted as a pull request.
|
|
||||||
|
|
||||||
## Updating
|
|
||||||
|
|
||||||
To update the binaries when llccd releases a new version, trigger the
|
|
||||||
`Update RDPWrapOffsetFinder tools` workflow from the Actions tab
|
|
||||||
(`.github/workflows/update-finder-tools.yml`).
|
|
||||||
It will download the latest release, update this folder, and open a pull request.
|
|
||||||
@ -1 +0,0 @@
|
|||||||
v0.9
|
|
||||||