Code improvements

Fixed memory leak in the installer
Added diagnostics info to configuration app
This commit is contained in:
binarymaster 2014-11-22 21:17:39 +03:00
parent edbff7bedd
commit 61f6adf1f2
3 changed files with 468 additions and 25 deletions

View File

@ -676,6 +676,7 @@ begin
FileVersion.bPrivate := (VersionInfo.Value.dwFileFlags and VFF_PRIVATE) = VFF_PRIVATE; FileVersion.bPrivate := (VersionInfo.Value.dwFileFlags and VFF_PRIVATE) = VFF_PRIVATE;
FileVersion.bSpecial := (VersionInfo.Value.dwFileFlags and VFF_SPECIAL) = VFF_SPECIAL; FileVersion.bSpecial := (VersionInfo.Value.dwFileFlags and VFF_SPECIAL) = VFF_SPECIAL;
FreeLibrary(hFile);
Result := True; Result := True;
end; end;

View File

@ -3,8 +3,8 @@ object MainForm: TMainForm
Top = 0 Top = 0
BorderStyle = bsDialog BorderStyle = bsDialog
Caption = 'RDP Wrapper Configuration' Caption = 'RDP Wrapper Configuration'
ClientHeight = 245 ClientHeight = 326
ClientWidth = 326 ClientWidth = 351
Color = clBtnFace Color = clBtnFace
Font.Charset = DEFAULT_CHARSET Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
@ -15,18 +15,19 @@ object MainForm: TMainForm
Position = poDesktopCenter Position = poDesktopCenter
OnCloseQuery = FormCloseQuery OnCloseQuery = FormCloseQuery
OnCreate = FormCreate OnCreate = FormCreate
OnDestroy = FormDestroy
PixelsPerInch = 96 PixelsPerInch = 96
TextHeight = 13 TextHeight = 13
object lRDPPort: TLabel object lRDPPort: TLabel
Left = 203 Left = 225
Top = 33 Top = 103
Width = 47 Width = 47
Height = 13 Height = 13
Caption = 'RDP Port:' Caption = 'RDP Port:'
end end
object bOK: TButton object bOK: TButton
Left = 45 Left = 10
Top = 212 Top = 293
Width = 75 Width = 75
Height = 25 Height = 25
Caption = 'OK' Caption = 'OK'
@ -35,8 +36,8 @@ object MainForm: TMainForm
OnClick = bOKClick OnClick = bOKClick
end end
object bCancel: TButton object bCancel: TButton
Left = 126 Left = 91
Top = 212 Top = 293
Width = 75 Width = 75
Height = 25 Height = 25
Caption = 'Cancel' Caption = 'Cancel'
@ -45,8 +46,8 @@ object MainForm: TMainForm
OnClick = bCancelClick OnClick = bCancelClick
end end
object bApply: TButton object bApply: TButton
Left = 207 Left = 172
Top = 212 Top = 293
Width = 75 Width = 75
Height = 25 Height = 25
Caption = 'Apply' Caption = 'Apply'
@ -56,7 +57,7 @@ object MainForm: TMainForm
end end
object cbSingleSessionPerUser: TCheckBox object cbSingleSessionPerUser: TCheckBox
Left = 8 Left = 8
Top = 31 Top = 112
Width = 130 Width = 130
Height = 17 Height = 17
Caption = 'Single Session Per User' Caption = 'Single Session Per User'
@ -65,8 +66,8 @@ object MainForm: TMainForm
end end
object rgNLA: TRadioGroup object rgNLA: TRadioGroup
Left = 8 Left = 8
Top = 54 Top = 135
Width = 310 Width = 335
Height = 73 Height = 73
Caption = 'Security Mode' Caption = 'Security Mode'
Items.Strings = ( Items.Strings = (
@ -78,7 +79,7 @@ object MainForm: TMainForm
end end
object cbAllowTSConnections: TCheckBox object cbAllowTSConnections: TCheckBox
Left = 8 Left = 8
Top = 8 Top = 89
Width = 174 Width = 174
Height = 17 Height = 17
Caption = 'Enable Remote Desktop Protocol' Caption = 'Enable Remote Desktop Protocol'
@ -87,20 +88,20 @@ object MainForm: TMainForm
end end
object rgShadow: TRadioGroup object rgShadow: TRadioGroup
Left = 8 Left = 8
Top = 133 Top = 214
Width = 310 Width = 335
Height = 73 Height = 73
Caption = 'Session Shadowing Mode' Caption = 'Session Shadowing Mode'
Items.Strings = ( Items.Strings = (
'Disable Shadowing' 'Disable Shadowing'
'Shadowing will request user permission' 'Shadowing will request user'#39's permission'
'Shadowing sessions immediately') 'Shadow sessions immediately')
TabOrder = 6 TabOrder = 6
OnClick = cbAllowTSConnectionsClick OnClick = cbAllowTSConnectionsClick
end end
object seRDPPort: TSpinEdit object seRDPPort: TSpinEdit
Left = 256 Left = 278
Top = 30 Top = 100
Width = 62 Width = 62
Height = 22 Height = 22
MaxValue = 65535 MaxValue = 65535
@ -110,12 +111,96 @@ object MainForm: TMainForm
OnChange = seRDPPortChange OnChange = seRDPPortChange
end end
object bLicense: TButton object bLicense: TButton
Left = 224 Left = 253
Top = 6 Top = 293
Width = 94 Width = 87
Height = 21 Height = 25
Caption = 'View license...' Caption = 'View license...'
TabOrder = 8 TabOrder = 8
OnClick = bLicenseClick OnClick = bLicenseClick
end end
object gbDiag: TGroupBox
Left = 8
Top = 6
Width = 335
Height = 77
Caption = 'Diagnostics'
TabOrder = 9
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 = 206
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 = 206
Top = 17
Width = 44
Height = 13
Caption = 'Unknown'
end
object lTSVer: TLabel
Left = 182
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 = 182
Top = 17
Width = 20
Height = 13
Caption = 'ver.'
end
end
object Timer: TTimer
Interval = 250
OnTimer = TimerTimer
Left = 280
Top = 19
end
end end

View File

@ -20,7 +20,7 @@ interface
uses uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Spin, ExtCtrls, Registry; Dialogs, StdCtrls, Spin, ExtCtrls, Registry, WinSvc;
type type
TMainForm = class(TForm) TMainForm = class(TForm)
@ -33,7 +33,19 @@ type
rgShadow: TRadioGroup; rgShadow: TRadioGroup;
seRDPPort: TSpinEdit; seRDPPort: TSpinEdit;
lRDPPort: TLabel; 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; bLicense: TButton;
gbDiag: TGroupBox;
procedure FormCreate(Sender: TObject); procedure FormCreate(Sender: TObject);
procedure cbAllowTSConnectionsClick(Sender: TObject); procedure cbAllowTSConnectionsClick(Sender: TObject);
procedure seRDPPortChange(Sender: TObject); procedure seRDPPortChange(Sender: TObject);
@ -42,6 +54,8 @@ type
procedure bOKClick(Sender: TObject); procedure bOKClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure bLicenseClick(Sender: TObject); procedure bLicenseClick(Sender: TObject);
procedure TimerTimer(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private private
{ Private declarations } { Private declarations }
public public
@ -49,10 +63,36 @@ type
procedure ReadSettings; procedure ReadSettings;
procedure WriteSettings; procedure WriteSettings;
end; 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 var
MainForm: TMainForm; MainForm: TMainForm;
Ready: Boolean = False; Ready: Boolean = False;
Arch: Byte;
OldWow64RedirectionValue: LongBool;
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 implementation
@ -62,6 +102,214 @@ implementation
uses uses
LicenseUnit; 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; function ExtractResText(ResName: String): String;
var var
ResStream: TResourceStream; ResStream: TResourceStream;
@ -192,6 +440,98 @@ begin
Reg.Free; Reg.Free;
end; end;
procedure TMainForm.TimerTimer(Sender: TObject);
var
WrapperPath: String;
FV: FILE_VERSION;
begin
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;
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;
end;
end;
procedure TMainForm.bLicenseClick(Sender: TObject); procedure TMainForm.bLicenseClick(Sender: TObject);
begin begin
LicenseForm.mText.Text := ExtractResText('LICENSE'); LicenseForm.mText.Text := ExtractResText('LICENSE');
@ -212,11 +552,28 @@ begin
end; end;
procedure TMainForm.FormCreate(Sender: TObject); procedure TMainForm.FormCreate(Sender: TObject);
var
SI: TSystemInfo;
begin 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; ReadSettings;
Ready := True; Ready := True;
end; end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
if Arch = 64 then
RevertWowRedirection;
end;
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin begin
if bApply.Enabled then if bApply.Enabled then