// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
//
// This file is part of the VNC system.
//
// The VNC system is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// If the source code for the VNC system is not available from the place
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
// the authors on vnc@uk.research.att.com for information on obtaining it.
// vncService
// Implementation of service-oriented functionality of WinVNC
static const char *szAppName = "WinVNC";
#include "stdhdrs.h"
// Header
#include "vncService.h"
#include <lmcons.h>
#include "omnithread.h"
#include "WinVNC.h"
#include "vncMenu.h"
#include "vncTimedMsgBox.h"
// Error message logging
void LogErrorMsg(char *message);
// OS-SPECIFIC ROUTINES
// Create an instance of the vncService class to cause the static fields to be
// initialised properly
vncService init;
DWORD g_platform_id;
BOOL g_impersonating_user = 0;
DWORD g_version_major;
DWORD g_version_minor;
vncService::vncService()
{
OSVERSIONINFO osversioninfo;
osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
// Get the current OS version
if (!GetVersionEx(&osversioninfo))
g_platform_id = 0;
g_platform_id = osversioninfo.dwPlatformId;
g_version_major = osversioninfo.dwMajorVersion;
g_version_minor = osversioninfo.dwMinorVersion;
}
// CurrentUser - fills a buffer with the name of the current user!
BOOL
vncService::CurrentUser(char *buffer, UINT size)
{
// How to obtain the name of the current user depends upon the OS being used
if ((g_platform_id == VER_PLATFORM_WIN32_NT) && vncService::RunningAsService())
{
// Windows NT, service-mode
// -=- FIRSTLY - verify that a user is logged on
// Get the current Window station
HWINSTA station = GetProcessWindowStation();
if (station == NULL)
return FALSE;
// Get the current user SID size
DWORD usersize;
GetUserObjectInformation(station,
UOI_USER_SID, NULL, 0, &usersize);
// Check the required buffer size isn't zero
if (usersize == 0)
{
// No user is logged in - ensure we're not impersonating anyone
RevertToSelf();
g_impersonating_user = FALSE;
// Return "" as the name...
if (strlen("") >= size)
return FALSE;
strcpy(buffer, "");
return TRUE;
}
// -=- SECONDLY - a user is logged on but if we're not impersonating
// them then we can't continue!
if (!g_impersonating_user) {
// Return "" as the name...
if (strlen("") >= size)
return FALSE;
strcpy(buffer, "");
return TRUE;
}
}
// -=- When we reach here, we're either running under Win9x, or we're running
// under NT as an application or as a service impersonating a user
// Either way, we should find a suitable user name.
switch (g_platform_id)
{
case VER_PLATFORM_WIN32_WINDOWS:
case VER_PLATFORM_WIN32_NT:
{
// Just call GetCurrentUser
DWORD length = size;
if (GetUserName(buffer, &length) == 0)
{
UINT error = GetLastError();
if (error == ERROR_NOT_LOGGED_ON)
{
// No user logged on
if (strlen("") >= size)
return FALSE;
strcpy(buffer, "");
return TRUE;
}
else
{
// Genuine error...
log.Print(LL_INTERR, VNCLOG("getusername error %d\n"), GetLastError());
return FALSE;
}
}
}
return TRUE;
};
// OS was not recognised!
return FALSE;
}
// IsWin95 - returns a BOOL indicating whether the current OS is Win95
BOOL
vncService::IsWin95()
{
return (g_platform_id == VER_PLATFORM_WIN32_WINDOWS);
}
// IsWinNT - returns a bool indicating whether the current OS is WinNT
BOOL
vncService::IsWinNT()
{
return (g_platform_id == VER_PLATFORM_WIN32_NT);
}
// Version info
DWORD
vncService::VersionMajor()
{
return g_version_major;
}
DWORD
vncService::VersionMinor()
{
return g_version_minor;
}
#ifndef _UBT_BUILD_
// Internal routine to find the WinVNC menu class window and
// post a message to it!
BOOL
PostToWinVNC(UINT message, WPARAM wParam, LPARAM lParam)
{
// Locate the hidden WinVNC menu window
HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
if (hservwnd == NULL)
return FALSE;
// Post the message to WinVNC
PostMessage(hservwnd, message, wParam, lParam);
return TRUE;
}
#endif
// Static routines only used on Windows NT to ensure we're in the right desktop
// These routines are generally available to any thread at any time.
// - SelectDesktop(HDESK)
// Switches the current thread into a different desktop by deskto handle
// This call takes care of all the evil memory management involved
BOOL
vncService::SelectHDESK(HDESK new_desktop)
{
// Are we running on NT?
if (IsWinNT())
{
HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
DWORD dummy;
char new_name[256];
if (!GetUserObjectInformation(new_desktop, UOI_NAME, &new_name, 256, &dummy)) {
return FALSE;
}
log.Print(LL_INTERR, VNCLOG("SelectHDESK to %s (%x) from %x"), new_name, new_desktop, old_desktop);
// Switch the desktop
if(!SetThreadDesktop(new_desktop)) {
return FALSE;
}
// Switched successfully - destroy the old desktop
if (!CloseDesktop(old_desktop))
log.Print(LL_INTERR, VNCLOG("SelectHDESK failed to close old desktop %x (Err=%d)\n"), old_desktop, GetLastError());
return TRUE;
}
return TRUE;
}
// - SelectDesktop(char *)
// Switches the current thread into a different desktop, by name
// Calling with a valid desktop name will place the thread in that desktop.
// Calling with a NULL name will place the thread in the current input desktop.
BOOL
vncService::SelectDesktop(char *name)
{
// Are we running on NT?
if (IsWinNT())
{
HDESK desktop;
if (name != NULL)
{
// Attempt to open the named desktop
desktop = OpenDesktop(name, 0, FALSE,
DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
}
else
{
// No, so open the input desktop
desktop = OpenInputDesktop(0, FALSE,
DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
}
// Did we succeed?
if (desktop == NULL) {
return FALSE;
}
// Switch to the new desktop
if (!SelectHDESK(desktop)) {
// Failed to enter the new desktop, so free it!
if (!CloseDesktop(desktop))
log.Print(LL_INTERR, VNCLOG("SelectDesktop failed to close desktop\n"));
return FALSE;
}
// We successfully switched desktops!
return TRUE;
}
return (name == NULL);
}
// NT only function to establish whether we're on the current input desktop
BOOL
vncService::InputDesktopSelected()
{
// Are we running on NT?
if (IsWinNT())
{
// Get the input and thread desktops
HDESK threaddesktop = GetThreadDesktop(GetCurrentThreadId());
HDESK inputdesktop = OpenInputDesktop(0, FALSE,
DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
// Get the desktop names:
// *** I think this is horribly inefficient but I'm not sure.
if (inputdesktop == NULL)
return FALSE;
DWORD dummy;
char threadname[256];
char inputname[256];
if (!GetUserObjectInformation(threaddesktop, UOI_NAME, &threadname, 256, &dummy)) {
if (!CloseDesktop(inputdesktop))
log.Print(LL_INTWARN, VNCLOG("failed to close input desktop\n"));
return FALSE;
}
_ASSERT(dummy <= 256);
if (!GetUserObjectInformation(inputdesktop, UOI_NAME, &inputname, 256, &dummy)) {
if (!CloseDesktop(inputdesktop))
log.Print(LL_INTWARN, VNCLOG("failed to close input desktop\n"));
return FALSE;
}
_ASSERT(dummy <= 256);
if (!CloseDesktop(inputdesktop))
log.Print(LL_INTWARN, VNCLOG("failed to close input desktop\n"));
if (strcmp(threadname, inputname) != 0)
return FALSE;
}
return TRUE;
}
// Static routine used to fool Winlogon into thinking CtrlAltDel was pressed
void *
SimulateCtrlAltDelThreadFn(void *context)
{
HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
// Switch into the Winlogon desktop
if (!vncService::SelectDesktop("Winlogon"))
{
log.Print(LL_INTERR, VNCLOG("failed to select logon desktop\n"));
return FALSE;
}
log.Print(LL_ALL, VNCLOG("generating ctrl-alt-del\n"));
// Fake a hotkey event to any windows we find there.... :(
// Winlogon uses hotkeys to trap Ctrl-Alt-Del...
PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE));
// Switch back to our original desktop
if (old_desktop != NULL)
vncService::SelectHDESK(old_desktop);
return NULL;
}
// Static routine to simulate Ctrl-Alt-Del locally
BOOL
vncService::SimulateCtrlAltDel()
{
log.Print(LL_ALL, VNCLOG("preparing to generate ctrl-alt-del\n"));
// Are we running on NT?
if (IsWinNT())
{
log.Print(LL_ALL, VNCLOG("spawn ctrl-alt-del thread...\n"));
// *** This is an unpleasant hack. Oh dear.
// I simulate CtrAltDel by posting a WM_HOTKEY message to all
// the windows on the Winlogon desktop.
// This requires that the current thread is part of the Winlogon desktop.
// But the current thread has hooks set & a window open, so it can't
// switch desktops, so I instead spawn a new thread & let that do the work...
omni_thread *thread = omni_thread::create(SimulateCtrlAltDelThreadFn);
if (thread == NULL)
return FALSE;
thread->join(NULL);
return TRUE;
}
return TRUE;
}
// Static routine to show the Properties dialog for a currently-running
// copy of WinVNC, (usually a servicified version.)
#ifndef _UBT_BUILD_
BOOL
vncService::ShowProperties()
{
// Post to the WinVNC menu window
if (!PostToWinVNC(MENU_PROPERTIES_SHOW, 0, 0))
{
MessageBox(NULL, "No existing instance of WinVNC could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
return FALSE;
}
return TRUE;
}
// Static routine to show the Default Properties dialog for a currently-running
// copy of WinVNC, (usually a servicified version.)
BOOL
vncService::ShowDefaultProperties()
{
// Post to the WinVNC menu window
if (!PostToWinVNC(MENU_DEFAULT_PROPERTIES_SHOW, 0, 0))
{
MessageBox(NULL, "No existing instance of WinVNC could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
return FALSE;
}
return TRUE;
}
// Static routine to show the About dialog for a currently-running
// copy of WinVNC, (usually a servicified version.)
BOOL
vncService::ShowAboutBox()
{
// Post to the WinVNC menu window
if (!PostToWinVNC(MENU_ABOUTBOX_SHOW, 0, 0))
{
MessageBox(NULL, "No existing instance of WinVNC could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
return FALSE;
}
return TRUE;
}
// Static routine to tell a locally-running instance of the server
// to connect out to a new client
BOOL
vncService::PostAddNewClient(unsigned long ipaddress)
{
// Post to the WinVNC menu window
if (!PostToWinVNC(MENU_ADD_CLIENT_MSG, 0, ipaddress))
{
MessageBox(NULL, "No existing instance of WinVNC could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
return FALSE;
}
return TRUE;
}
#endif //_UBT_BUILD_
// SERVICE-MODE ROUTINES
// Service-mode defines:
// Executable name
#define VNCAPPNAME "winvnc"
// Internal service name
#define VNCSERVICENAME "winvnc"
// Displayed service name
#define VNCSERVICEDISPLAYNAME "VNC Server"
// List of other required services ("dependency 1\0dependency 2\0\0")
// *** These need filling in properly
#define VNCDEPENDENCIES ""
// Internal service state
SERVICE_STATUS g_srvstatus; // current status of the service
SERVICE_STATUS_HANDLE g_hstatus;
DWORD g_error = 0;
DWORD g_servicethread = NULL;
char* g_errortext[256];
// Forward defines of internal service functions
void WINAPI ServiceMain(DWORD argc, char **argv);
void ServiceWorkThread(void *arg);
void ServiceStop();
void WINAPI ServiceCtrl(DWORD ctrlcode);
bool WINAPI CtrlHandler (DWORD ctrltype);
BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
// ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
BOOL g_servicemode = FALSE;
BOOL
vncService::RunningAsService()
{
return g_servicemode;
}
#ifndef _UBT_BUILD_
BOOL
vncService::KillRunningCopy()
{
// Locate the hidden WinVNC menu window
HWND hservwnd;
while ((hservwnd = FindWindow(MENU_CLASS_NAME, NULL)) != NULL)
{
// Post the message to WinVNC
PostMessage(hservwnd, WM_CLOSE, 0, 0);
omni_thread::sleep(1);
}
return TRUE;
}
// ROUTINE TO POST THE HANDLE OF THE CURRENT USER TO THE RUNNING WINVNC, IN ORDER
// THAT IT CAN LOAD THE APPROPRIATE SETTINGS. THIS IS USED ONLY BY THE SVCHELPER
// OPTION, WHEN RUNNING UNDER NT
BOOL
vncService::PostUserHelperMessage()
{
// - Check the platform type
if (!IsWinNT())
return TRUE;
// - Get the current process ID
DWORD processId = GetCurrentProcessId();
// - Post it to the existing WinVNC
if (!PostToWinVNC(MENU_SERVICEHELPER_MSG, 0, (LPARAM)processId))
return FALSE;
// - Wait until it's been used
omni_thread::sleep(5);
return TRUE;
}
#endif // _UBT_BUILD_
// ROUTINE TO PROCESS AN INCOMING INSTANCE OF THE ABOVE MESSAGE
BOOL
vncService::ProcessUserHelperMessage(WPARAM wParam, LPARAM lParam) {
// - Check the platform type
if (!IsWinNT() || !vncService::RunningAsService())
return TRUE;
// - Close the HKEY_CURRENT_USER key, to force NT to reload it for the new user
// NB: Note that this is _really_ dodgy if ANY other thread is accessing the key!
if (RegCloseKey(HKEY_CURRENT_USER) != ERROR_SUCCESS) {
log.Print(LL_INTERR, VNCLOG("failed to close current registry hive\n"));
return FALSE;
}
// - Revert to our own identity
RevertToSelf();
g_impersonating_user = FALSE;
// - Open the specified process
HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)lParam);
if (processHandle == NULL) {
log.Print(LL_INTERR, VNCLOG("failed to open specified process(%d)\n"), GetLastError());
return FALSE;
}
// - Get the token for the given process
HANDLE userToken = NULL;
if (!OpenProcessToken(processHandle, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, &userToken)) {
log.Print(LL_INTERR, VNCLOG("failed to get user token(%d)\n"), GetLastError());
CloseHandle(processHandle);
return FALSE;
}
CloseHandle(processHandle);
// - Set this thread to impersonate them
if (!ImpersonateLoggedOnUser(userToken)) {
log.Print(LL_INTERR, VNCLOG("failed to impersonate user(%d)\n"), GetLastError());
CloseHandle(userToken);
return FALSE;
}
CloseHandle(userToken);
g_impersonating_user = TRUE;
log.Print(LL_INTINFO, VNCLOG("impersonating logged on user\n"));
return TRUE;
}
#ifndef _UBT_BUILD_
// SERVICE MAIN ROUTINE
int
vncService::WinVNCServiceMain()
{
// Mark that we are a service
g_servicemode = TRUE;
// How to run as a service depends upon the OS being used
switch (g_platform_id)
{
// Windows 95/98
case VER_PLATFORM_WIN32_WINDOWS:
{
// Obtain a handle to the kernel library
HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
if (kerneldll == NULL)
break;
// And find the RegisterServiceProcess function
DWORD (*RegisterService)(DWORD, DWORD);
RegisterService = (DWORD (*)(DWORD, DWORD))
GetProcAddress(kerneldll, "RegisterServiceProcess");
if (RegisterService == NULL)
break;
// Register this process with the OS as a service!
RegisterService(NULL, 1);
// Run the service itself
WinVNCAppMain();
// Then remove the service from the system service table
RegisterService(NULL, 0);
// Free the kernel library
FreeLibrary(kerneldll);
// *** If we don't kill the process directly here, then
// for some reason, WinVNC crashes...
ExitProcess(0);
}
break;
// Windows NT
case VER_PLATFORM_WIN32_NT:
{
// Create a service entry table
SERVICE_TABLE_ENTRY dispatchTable[] =
{
{VNCSERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
{NULL, NULL}
};
// Call the service control dispatcher with our entry table
if (!StartServiceCtrlDispatcher(dispatchTable))
LogErrorMsg("StartServiceCtrlDispatcher failed.");
}
break;
};
return 0;
}
// SERVICE MAIN ROUTINE
void WINAPI ServiceMain(DWORD argc, char**argv)
{
// Register the service control handler
g_hstatus = RegisterServiceCtrlHandler(VNCSERVICENAME, ServiceCtrl);
if (g_hstatus == 0)
return;
// Set up some standard service state values
g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
g_srvstatus.dwServiceSpecificExitCode = 0;
// Give this status to the SCM
if (!ReportStatus(
SERVICE_START_PENDING, // Service state
NO_ERROR, // Exit code type
15000)) // Hint as to how long WinVNC should have hung before you assume error
{
ReportStatus(
SERVICE_STOPPED,
g_error,
0);
return;
}
// Now start the service for real
omni_thread *workthread = omni_thread::create(ServiceWorkThread);
return;
}
// SERVICE START ROUTINE - thread that calls WinVNCAppMain
void ServiceWorkThread(void *arg)
{
// Save the current thread identifier
g_servicethread = GetCurrentThreadId();
// report the status to the service control manager.
//
if (!ReportStatus(
SERVICE_RUNNING, // service state
NO_ERROR, // exit code
0)) // wait hint
return;
// RUN!
WinVNCAppMain();
// Mark that we're no longer running
g_servicethread = NULL;
// Tell the service manager that we've stopped.
ReportStatus(
SERVICE_STOPPED,
g_error,
0);
}
// SERVICE STOP ROUTINE - post a quit message to the relevant thread
void ServiceStop()
{
// Post a quit message to the main service thread
if (g_servicethread != NULL)
{
PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
}
}
// SERVICE INSTALL ROUTINE
int
vncService::InstallService()
{
const int pathlength = 2048;
char path[pathlength];
char servicecmd[pathlength];
// Get the filename of this executable
if (GetModuleFileName(NULL, path, pathlength-(strlen(winvncRunService)+2)) == 0)
{
MessageBox(NULL, "Unable to install WinVNC service", szAppName, MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Append the service-start flag to the end of the path:
if (strlen(path) + 4 + strlen(winvncRunService) < pathlength)
sprintf(servicecmd, "\"%s\" %s", path, winvncRunService);
else
return 0;
// How to add the WinVNC service depends upon the OS
switch (g_platform_id)
{
// Windows 95/98
case VER_PLATFORM_WIN32_WINDOWS:
{
// Locate the RunService registry entry
HKEY runservices;
if (RegCreateKey(HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
&runservices) != ERROR_SUCCESS)
{
MessageBox(NULL, "The SCM could not be contacted - the WinVNC service was not installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
break;
}
// Attempt to add a WinVNC key
if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS)
{
RegCloseKey(runservices);
MessageBox(NULL, "The WinVNC service could not be installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
break;
}
RegCloseKey(runservices);
// We have successfully installed the service!
vncTimedMsgBox::Do(
"The WinVNC service was successfully installed\n"
"The service will start now and will automatically\n"
"be run the next time this machine is reset",
szAppName,
MB_ICONINFORMATION | MB_OK);
// Run the service...
STARTUPINFO si;
si.cb = sizeof(si);
si.cbReserved2 = 0;
si.lpReserved = NULL;
si.lpReserved2 = NULL;
si.dwFlags = 0;
si.lpTitle = NULL;
PROCESS_INFORMATION pi;
if (!CreateProcess(
NULL, servicecmd, // Program name & path
NULL, NULL, // Security attributes
FALSE, // Inherit handles?
NORMAL_PRIORITY_CLASS, // Extra startup flags
NULL, // Environment table
NULL, // Current directory
&si,
&pi
))
{
MessageBox(NULL, "The WinVNC service failed to start", szAppName, MB_ICONSTOP | MB_OK);
break;
}
}
break;
// Windows NT
case VER_PLATFORM_WIN32_NT:
{
SC_HANDLE hservice;
SC_HANDLE hsrvmanager;
// Open the default, local Service Control Manager database
hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hsrvmanager == NULL)
{
MessageBox(NULL,
"The SCM could not be contacted - the WinVNC service was not installed",
szAppName,
MB_ICONEXCLAMATION | MB_OK);
break;
}
// Create an entry for the WinVNC service
hservice = CreateService(
hsrvmanager, // SCManager database
VNCSERVICENAME, // name of service
VNCSERVICEDISPLAYNAME, // name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
// service type
SERVICE_AUTO_START, // start type
SERVICE_ERROR_NORMAL, // error control type
servicecmd, // service's binary
NULL, // no load ordering group
NULL, // no tag identifier
VNCDEPENDENCIES, // dependencies
NULL, // LocalSystem account
NULL); // no password
CloseServiceHandle(hsrvmanager);
if (hservice == NULL)
{
MessageBox(NULL,
"The WinVNC service could not be installed",
szAppName,
MB_ICONEXCLAMATION | MB_OK);
break;
}
CloseServiceHandle(hservice);
// Now install the servicehelper registry setting...
// Locate the RunService registry entry
HKEY runapps;
if (RegCreateKey(HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
&runapps) != ERROR_SUCCESS)
{
MessageBox(NULL, "WARNING:Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded", szAppName, MB_ICONEXCLAMATION | MB_OK);
} else {
char servicehelpercmd[pathlength];
// Append the service-helper-start flag to the end of the path:
if (strlen(path) + 4 + strlen(winvncRunServiceHelper) < pathlength)
sprintf(servicehelpercmd, "\"%s\" %s", path, winvncRunServiceHelper);
else
return 0;
// Add the VNCserviceHelper entry
if (RegSetValueEx(runapps, szAppName, 0, REG_SZ,
(unsigned char *)servicehelpercmd, strlen(servicehelpercmd)+1) != ERROR_SUCCESS)
{
MessageBox(NULL, "WARNING:Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded", szAppName, MB_ICONEXCLAMATION | MB_OK);
}
RegCloseKey(runapps);
}
// Everything went fine
vncTimedMsgBox::Do(
"The WinVNC service was successfully installed\n"
"The service may be started from the Control Panel, and will\n"
"automatically be run the next time this machine is reset",
szAppName,
MB_ICONINFORMATION | MB_OK);
}
break;
};
return 0;
}
// SERVICE REMOVE ROUTINE
int
vncService::RemoveService()
{
// How to remove the WinVNC service depends upon the OS
switch (g_platform_id)
{
// Windows 95/98
case VER_PLATFORM_WIN32_WINDOWS:
{
// Locate the RunService registry entry
HKEY runservices;
if (RegOpenKey(HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
&runservices) != ERROR_SUCCESS)
{
MessageBox(NULL, "The SCM could not be contacted - the WinVNC service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
}
else
{
// Attempt to delete the WinVNC key
if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS)
{
RegCloseKey(runservices);
MessageBox(NULL, "The WinVNC service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
}
RegCloseKey(runservices);
break;
}
// Try to kill any running copy of WinVNC
if (!KillRunningCopy())
{
MessageBox(NULL,
"The WinVNC service could not be contacted",
szAppName,
MB_ICONEXCLAMATION | MB_OK);
break;
}
// We have successfully removed the service!
vncTimedMsgBox::Do("The WinVNC service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
}
break;
// Windows NT
case VER_PLATFORM_WIN32_NT:
{
SC_HANDLE hservice;
SC_HANDLE hsrvmanager;
// Attempt to remove the service-helper hook
HKEY runapps;
if (RegOpenKey(HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
&runapps) == ERROR_SUCCESS)
{
// Attempt to delete the WinVNC key
if (RegDeleteValue(runapps, szAppName) != ERROR_SUCCESS)
{
MessageBox(NULL, "WARNING:The ServiceHelper hook entry could not be removed from the registry", szAppName, MB_ICONEXCLAMATION | MB_OK);
}
RegCloseKey(runapps);
}
// Open the SCM
hsrvmanager = OpenSCManager(
NULL, // machine (NULL == local)
NULL, // database (NULL == default)
SC_MANAGER_ALL_ACCESS // access required
);
if (hsrvmanager)
{
hservice = OpenService(hsrvmanager, VNCSERVICENAME, SERVICE_ALL_ACCESS);
if (hservice != NULL)
{
SERVICE_STATUS status;
// Try to stop the WinVNC service
if (ControlService(hservice, SERVICE_CONTROL_STOP, &status))
{
while(QueryServiceStatus(hservice, &status))
{
if (status.dwCurrentState == SERVICE_STOP_PENDING)
Sleep(1000);
else
break;
}
if (status.dwCurrentState != SERVICE_STOPPED)
MessageBox(NULL, "The WinVNC service could not be stopped", szAppName, MB_ICONEXCLAMATION | MB_OK);
}
// Now remove the service from the SCM
if(DeleteService(hservice))
vncTimedMsgBox::Do("The WinVNC service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
else
MessageBox(NULL, "The WinVNC service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
CloseServiceHandle(hservice);
}
else
MessageBox(NULL, "The WinVNC service could not be found", szAppName, MB_ICONEXCLAMATION | MB_OK);
CloseServiceHandle(hsrvmanager);
}
else
MessageBox(NULL, "The SCM could not be contacted - the WinVNC service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
}
break;
};
return 0;
}
// USEFUL SERVICE SUPPORT ROUTINES
// Service control routine
void WINAPI ServiceCtrl(DWORD ctrlcode)
{
// What control code have we been sent?
switch(ctrlcode)
{
case SERVICE_CONTROL_STOP:
// STOP : The service must stop
g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
ServiceStop();
break;
case SERVICE_CONTROL_INTERROGATE:
// QUERY : Service control manager just wants to know our state
break;
default:
// Control code not recognised
break;
}
// Tell the control manager what we're up to.
ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
}
// Service manager status reporting
BOOL ReportStatus(DWORD state,
DWORD exitcode,
DWORD waithint)
{
static DWORD checkpoint = 1;
BOOL result = TRUE;
// If we're in the start state then we don't want the control manager
// sending us control messages because they'll confuse us.
if (state == SERVICE_START_PENDING)
g_srvstatus.dwControlsAccepted = 0;
else
g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
// Save the new status we've been given
g_srvstatus.dwCurrentState = state;
g_srvstatus.dwWin32ExitCode = exitcode;
g_srvstatus.dwWaitHint = waithint;
// Update the checkpoint variable to let the SCM know that we
// haven't died if requests take a long time
if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED))
g_srvstatus.dwCheckPoint = 0;
else
g_srvstatus.dwCheckPoint = checkpoint++;
// Tell the SCM our new status
if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus)))
LogErrorMsg("SetServiceStatus failed");
return result;
}
// Error reporting
void LogErrorMsg(char *message)
{
char msgbuff[256];
HANDLE heventsrc;
char * strings[2];
// Save the error code
g_error = GetLastError();
// Use event logging to log the error
heventsrc = RegisterEventSource(NULL, VNCSERVICENAME);
sprintf(msgbuff, "%s error: %d", VNCSERVICENAME, g_error);
strings[0] = msgbuff;
strings[1] = message;
if (heventsrc != NULL)
{
MessageBeep(MB_OK);
ReportEvent(
heventsrc, // handle of event source
EVENTLOG_ERROR_TYPE, // event type
0, // event category
0, // event ID
NULL, // current user's SID
2, // strings in 'strings'
0, // no bytes of raw data
(const char **)strings, // array of error strings
NULL); // no raw data
DeregisterEventSource(heventsrc);
}
}
#endif