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
|
||||
|
||||
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 |
|
||||
|---------------------|------------------------------------------------|-------------------------------|
|
||||
| `rdpw32.dll` | Build output of `src-x86-x64-Fusix/` (x86) | Always (32-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) |
|
||||
| `rdpclip6032.exe` | Original Stas'M redistributable (x86) | Vista x86 install |
|
||||
| `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 |
|
||||
| File | Source | Used when |
|
||||
|---------------|--------------------------------------------------|-----------------------------|
|
||||
| `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 |
|
||||
| `rdpwrap.ini` | `msi/rdpwrap.ini` from repo | Always — offline fallback |
|
||||
| `license.txt` | Repo `LICENSE` (copied as plain text) | `RDPWInst -l` flag |
|
||||
|
||||
Binary files in this folder are intentionally excluded from version control
|
||||
via `.gitignore`. The CI pipeline copies them from the native build output
|
||||
before invoking `dotnet publish`.
|
||||
## Optional legacy files (not in VCS, not staged by CI)
|
||||
|
||||
| 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;
|
||||
|
||||
namespace RDPWrap.Common;
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2024 sjackson0109 — Apache License 2.0
|
||||
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace RDPWrap.Common;
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2024 sjackson0109 — Apache License 2.0
|
||||
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||
|
||||
namespace RDPWrap.Common;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2024 sjackson0109 — Apache License 2.0
|
||||
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||
|
||||
namespace RDPWrap.Common;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2024 sjackson0109
|
||||
// Copyright 2026 sjackson0109
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (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;
|
||||
|
||||
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;
|
||||
|
||||
namespace RDPWrap.Common;
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2024 sjackson0109 — Apache License 2.0
|
||||
// Copyright 2026 sjackson0109 — Apache License 2.0
|
||||
using System.Reflection;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||