Filename: "{app}\winvnc4.exe"; Parameters: "-register"; Tasks: installservice
Filename: "net"; Parameters: "start winvnc4"; Tasks: startservice
#endif
+
+#ifdef BUILD_WINVNC
+[Code]
+
+{--- IShellLink ---}
+
+const
+ CLSID_ShellLink = '{00021401-0000-0000-C000-000000000046}';
+ SLDF_RUNAS_USER = $2000;
+
+type
+ IShellLinkW = interface(IUnknown)
+ '{000214F9-0000-0000-C000-000000000046}'
+ procedure Dummy;
+ procedure Dummy2;
+ procedure Dummy3;
+ function GetDescription(pszName: String; cchMaxName: Integer): HResult;
+ function SetDescription(pszName: String): HResult;
+ function GetWorkingDirectory(pszDir: String; cchMaxPath: Integer): HResult;
+ function SetWorkingDirectory(pszDir: String): HResult;
+ function GetArguments(pszArgs: String; cchMaxPath: Integer): HResult;
+ function SetArguments(pszArgs: String): HResult;
+ function GetHotkey(var pwHotkey: Word): HResult;
+ function SetHotkey(wHotkey: Word): HResult;
+ function GetShowCmd(out piShowCmd: Integer): HResult;
+ function SetShowCmd(iShowCmd: Integer): HResult;
+ function GetIconLocation(pszIconPath: String; cchIconPath: Integer;
+ out piIcon: Integer): HResult;
+ function SetIconLocation(pszIconPath: String; iIcon: Integer): HResult;
+ function SetRelativePath(pszPathRel: String; dwReserved: DWORD): HResult;
+ function Resolve(Wnd: HWND; fFlags: DWORD): HResult;
+ function SetPath(pszFile: String): HResult;
+ end;
+
+ IShellLinkDataList = interface(IUnknown)
+ '{45E2B4AE-B1C3-11D0-B92F-00A0C90312E1}'
+ function AddDataBlock(pDataBlock : DWORD) : HResult;
+ function CopyDataBlock(dwSig : DWORD; var ppDataBlock : DWORD) : HResult;
+ function RemoveDataBlock(dwSig : DWORD) : HResult;
+ function GetFlags(var pdwFlags : DWORD) : HResult;
+ function SetFlags(dwFlags : DWORD) : HResult;
+ end;
+
+ IPersist = interface(IUnknown)
+ '{0000010C-0000-0000-C000-000000000046}'
+ function GetClassID(var classID: TGUID): HResult;
+ end;
+
+ IPersistFile = interface(IPersist)
+ '{0000010B-0000-0000-C000-000000000046}'
+ function IsDirty: HResult;
+ function Load(pszFileName: String; dwMode: Longint): HResult;
+ function Save(pszFileName: String; fRemember: BOOL): HResult;
+ function SaveCompleted(pszFileName: String): HResult;
+ function GetCurFile(out pszFileName: String): HResult;
+ end;
+
+var
+ OSVersion: TWindowsVersion;
+
+function InitializeSetup: Boolean;
+begin
+ GetWindowsVersionEx(OSVersion);
+ Result := True;
+end;
+
+procedure SetRunAsUserFlag(Path: String);
+var
+ Obj: IUnknown;
+ SL: IShellLinkW;
+ SDL: IShellLinkDataList;
+ PF: IPersistFile;
+ Flags: DWord;
+begin
+ Obj := CreateComObject(StringToGuid(CLSID_ShellLink));
+ SL := IShellLinkW(Obj);
+ PF := IPersistFile(Obj);
+ SDL := IShellLinkDataList(Obj);
+ Path := ExpandConstant(Path);
+ OleCheck(PF.Load(Path, 0));
+ OleCheck(SDL.GetFlags(Flags));
+ OleCheck(SDL.SetFlags(Flags or SLDF_RUNAS_USER));
+ OleCheck(PF.Save(Path, True));
+end;
+
+procedure CurStepChanged(CurStep: TSetupStep);
+var
+ Flags: DWord;
+begin
+ { Post-install actions on Windows Vista and higher:
+ o Modify Service-Mode start menu commands so they run as administrator.
+ o Set up the SoftwareSASGeneration system policy so as to allow services to simulate Ctrl+Alt+Del. }
+ if (CurStep = ssPostInstall) and (OSVersion.Major >= 6) then begin
+ SetRunAsUserFlag('{group}\VNC Server (Service-Mode)\Configure VNC Service.lnk');
+ SetRunAsUserFlag('{group}\VNC Server (Service-Mode)\Register VNC Service.lnk');
+ SetRunAsUserFlag('{group}\VNC Server (Service-Mode)\Unregister VNC Service.lnk');
+ SetRunAsUserFlag('{group}\VNC Server (Service-Mode)\Start VNC Service.lnk');
+ SetRunAsUserFlag('{group}\VNC Server (Service-Mode)\Stop VNC Service.lnk');
+ if not RegQueryDWordValue(
+ HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System',
+ 'SoftwareSASGeneration', Flags
+ ) then Flags := 0;
+ RegWriteDWordValue(
+ HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System',
+ 'SoftwareSASGeneration', Flags or 1
+ );
+ end;
+end;
+#endif
await();
returnCode = STILL_ACTIVE;
+ DWORD size;
+ char desktopName[256];
+ char buf[256];
+ HDESK desktop = GetThreadDesktop(GetCurrentThreadId());
+ if (!GetUserObjectInformation(desktop, UOI_NAME, buf, 256, &size))
+ throw rdr::SystemException("unable to launch process", GetLastError());
+
+ snprintf(desktopName, 256, "WinSta0\\%s", buf);
+
// - Create storage for the process startup information
STARTUPINFO sinfo;
memset(&sinfo, 0, sizeof(sinfo));
sinfo.cb = sizeof(sinfo);
+ sinfo.lpDesktop = desktopName;
// - Concoct a suitable command-line
TCharArray exePath;
// - Internal service implementation functions
Service* service = 0;
+bool runAsService = false;
VOID WINAPI serviceHandler(DWORD control) {
switch (control) {
if (!osVersion.isPlatformNT)
return false;
+ if (osVersion.dwMajorVersion >= 6) {
+ rfb::win32::Handle sessionEventCad =
+ CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNCCad");
+ SetEvent(sessionEventCad);
+ return true;
+ }
+
CADThread* cad_thread = new CADThread();
vlog.debug("emulate Ctrl-Alt-Del");
if (cad_thread) {
bool rfb::win32::isServiceProcess() {
- return service != 0;
+ return runAsService;
}
}
break;
case ID_OPTIONS:
- {
- CurrentUserToken token;
- if (token.canImpersonate())
- vncConfig.start(isServiceProcess() ? (HANDLE)token : INVALID_HANDLE_VALUE);
- else
- vlog.error("Options: unknown current user");
- }
+ vncConfig.start(INVALID_HANDLE_VALUE);
break;
case ID_CONNECT:
- {
- CurrentUserToken token;
- if (token.canImpersonate())
- vncConnect.start(isServiceProcess() ? (HANDLE)token : INVALID_HANDLE_VALUE);
- else
- vlog.error("Options: unknown current user");
- }
+ vncConnect.start(INVALID_HANDLE_VALUE);
break;
case ID_DISCONNECT:
thread.server.disconnectClients("tray menu disconnect");
if (MsgBox(0, _T("Are you sure you want to close the server?"),
MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) == IDYES) {
if (isServiceProcess()) {
- ImpersonateCurrentUser icu;
try {
rfb::win32::stopService(VNCServerService::Name);
} catch (rdr::Exception& e) {
#include <winvnc/VNCServerService.h>
#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/TsSessions.h>
+#include <rfb_win32/ModuleFileName.h>
+#include <wtsapi32.h>
+#include <tlhelp32.h>
using namespace winvnc;
using namespace rfb;
const TCHAR* winvnc::VNCServerService::Name = _T("WinVNC4");
-
-VNCServerService::VNCServerService(VNCServerWin32& s)
- : Service(Name), server(s) {
+VNCServerService::VNCServerService()
+ : Service(Name)
+ , SendSas(_T("sas.dll"), "SendSAS")
+ , stopServiceEvent(CreateEvent(0, FALSE, FALSE, 0))
+ , sessionEvent(CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNC"))
+ , sessionEventCad(CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNCCad")) {
// - Set the service-mode logging defaults
// These will be overridden by the Log option in the
// registry, if present.
}
-DWORD VNCServerService::serviceMain(int argc, TCHAR* argv[]) {
- setStatus(SERVICE_RUNNING);
- int result = server.run();
- setStatus(SERVICE_STOP_PENDING);
- return result;
+//////////////////////////////////////////////////////////////////////////////
+
+DWORD GetLogonPid(DWORD dwSessionId)
+{
+ DWORD dwLogonPid = 0;
+ HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (hSnap != INVALID_HANDLE_VALUE)
+ {
+ PROCESSENTRY32 procEntry;
+ procEntry.dwSize = sizeof procEntry;
+
+ if (Process32First(hSnap, &procEntry)) do
+ {
+ DWORD dwLogonSessionId = 0;
+ if (_stricmp(procEntry.szExeFile, "winlogon.exe") == 0 &&
+ ProcessIdToSessionId(procEntry.th32ProcessID, &dwLogonSessionId) &&
+ dwLogonSessionId == dwSessionId)
+ {
+ dwLogonPid = procEntry.th32ProcessID;
+ break;
+ }
+ } while (Process32Next(hSnap, &procEntry));
+ CloseHandle(hSnap);
+ }
+ return dwLogonPid;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+BOOL GetSessionUserTokenWin(OUT LPHANDLE lphUserToken)
+{
+ BOOL bResult = FALSE;
+ ConsoleSessionId ID_session;
+ DWORD Id = GetLogonPid(ID_session.id);
+ if (HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Id))
+ {
+ bResult = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, lphUserToken);
+ CloseHandle(hProcess);
+ }
+ return bResult;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// START the app as system
+HANDLE LaunchProcessWin(DWORD dwSessionId)
+{
+ HANDLE hProcess = NULL;
+ HANDLE hToken = NULL;
+ if (GetSessionUserTokenWin(&hToken))
+ {
+ ModuleFileName filename;
+ static const char cmdLineFmt[] = "\"%s\" -noconsole -service_run";
+ TCharArray cmdLine(_tcslen(filename.buf) + sizeof(cmdLineFmt)/sizeof(cmdLineFmt[0]));
+ _stprintf(cmdLine.buf, cmdLineFmt, filename.buf);
+ STARTUPINFO si;
+ ZeroMemory(&si, sizeof si);
+ si.cb = sizeof si;
+ si.dwFlags = STARTF_USESHOWWINDOW;
+ PROCESS_INFORMATION pi;
+ if (CreateProcessAsUser(hToken, NULL, cmdLine.buf, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi))
+ {
+ CloseHandle(pi.hThread);
+ hProcess = pi.hProcess;
+ }
+ CloseHandle(hToken);
+ }
+ return hProcess;
+}
+
+DWORD VNCServerService::serviceMain(int argc, TCHAR* argv[])
+{
+ ConsoleSessionId OlddwSessionId;
+
+ HANDLE hProcess = NULL;
+ //We use this event to notify the program that the session has changed
+ //The program need to end so the service can restart the program in the correct session
+ //wait_for_existing_process();
+ HANDLE testevent[2] = { stopServiceEvent, sessionEventCad };
+ setStatus(SERVICE_RUNNING);
+ while (status.dwCurrentState == SERVICE_RUNNING)
+ {
+ DWORD dwEvent = WaitForMultipleObjects(2, testevent, FALSE, 1000);
+ switch (dwEvent)
+ {
+ //stopServiceEvent, exit while loop
+ case WAIT_OBJECT_0 + 0:
+ setStatus(SERVICE_STOP_PENDING);
+ break;
+
+ //cad request
+ case WAIT_OBJECT_0 + 1:
+ if (SendSas.isValid())
+ (*SendSas)(FALSE);
+ break;
+
+ case WAIT_TIMEOUT:
+ {
+ ConsoleSessionId dwSessionId;
+ if (OlddwSessionId.id != dwSessionId.id)
+ {
+ OlddwSessionId.id = dwSessionId.id;
+ SetEvent(sessionEvent);
+ }
+ DWORD dwExitCode = 0;
+ if (hProcess == NULL ||
+ (GetExitCodeProcess(hProcess, &dwExitCode) &&
+ dwExitCode != STILL_ACTIVE &&
+ CloseHandle(hProcess)))
+ {
+ hProcess = LaunchProcessWin(dwSessionId.id);
+ }
+ }
+ break;
+ }
+ }
+
+ SetEvent(sessionEvent);
+
+ if (hProcess)
+ {
+ WaitForSingleObject(hProcess, 15000);
+ CloseHandle(hProcess);
+ }
+ return 0;
}
void VNCServerService::stop() {
- server.stop();
+ SetEvent(stopServiceEvent);
+ SetEvent(sessionEvent);
}
#include <winvnc/VNCServerWin32.h>
#include <rfb_win32/Service.h>
+#include <rfb_win32/DynamicFn.h>
namespace winvnc {
class VNCServerService : public rfb::win32::Service {
public:
- VNCServerService(VNCServerWin32& s);
+ VNCServerService();
DWORD serviceMain(int argc, TCHAR* argv[]);
void stop();
static const TCHAR* Name;
protected:
- VNCServerWin32& server;
+ rfb::win32::DynamicFn<void (WINAPI *)(BOOL)> SendSas;
+ rfb::win32::Handle stopServiceEvent;
+ rfb::win32::Handle sessionEvent;
+ rfb::win32::Handle sessionEventCad;
};
};
VNCServerWin32::VNCServerWin32()
: command(NoCommand), commandSig(commandLock),
commandEvent(CreateEvent(0, TRUE, FALSE, 0)),
+ sessionEvent(isServiceProcess() ?
+ CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNC") : 0),
vncServer(CStr(ComputerName().buf), &desktop),
hostThread(0), runServer(false), isDesktopStarted(false),
httpServer(&vncServer), config(&sockMgr), trayIcon(0),
// Register the queued command event to be handled
sockMgr.addEvent(commandEvent, this);
+ if (sessionEvent)
+ sockMgr.addEvent(sessionEvent, this);
}
VNCServerWin32::~VNCServerWin32() {
command = NoCommand;
commandSig.signal();
}
+ } else if (event_ == sessionEvent.h) {
+ stop();
}
}
rfb::Mutex commandLock;
rfb::Condition commandSig;
rfb::win32::Handle commandEvent;
+ rfb::win32::Handle sessionEvent;
// VNCServerWin32 Server-internal state
rfb::win32::SDisplay desktop;
TStr rfb::win32::AppName("VNC Server");
-static bool runAsService = false;
+extern bool runAsService;
static bool runServer = true;
static bool close_console = false;
sprintf(result.buf, stateMsg, (const char*)CStr(VNCServerService::Name), stateStr.buf);
MsgBoxOrLog(result.buf);
} else if (strcasecmp(argv[i], "-service") == 0) {
+ printf("Run in service mode\n");
+ runServer = false;
+ runAsService = true;
+
+ } else if (strcasecmp(argv[i], "-service_run") == 0) {
printf("Run in service mode\n");
runAsService = true;
if (runServer) {
// Start the network subsystem and run the server
VNCServerWin32 server;
-
- if (runAsService) {
- printf("Starting Service-Mode VNC Server.\n");
- VNCServerService service(server);
- service.start();
- result = service.getStatus().dwWin32ExitCode;
- } else {
- printf("Starting User-Mode VNC Server.\n");
- result = server.run();
- }
+ result = server.run();
+ } else if (runAsService) {
+ VNCServerService service;
+ service.start();
+ result = service.getStatus().dwWin32ExitCode;
}
vlog.debug("WinVNC service destroyed");