mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1914 lines
51 KiB
1914 lines
51 KiB
/*++
|
|
Copyright (c) 2000 Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
tabsrv.cpp
|
|
|
|
Abstract:
|
|
This is the main module of the Tablet PC Listener service.
|
|
|
|
Author:
|
|
Michael Tsang (MikeTs) 01-Jun-2000
|
|
|
|
Environment:
|
|
User mode
|
|
|
|
Revision History:
|
|
--*/
|
|
|
|
#ifndef INITGUID
|
|
#define INITGUID
|
|
#endif
|
|
|
|
#include "pch.h"
|
|
#include <userenv.h>
|
|
|
|
extern "C"
|
|
{
|
|
HANDLE GetCurrentUserTokenW(WCHAR Winsta[], DWORD DesiredAccess);
|
|
};
|
|
|
|
//
|
|
// Global data
|
|
//
|
|
HMODULE ghMod = NULL;
|
|
DWORD gdwfTabSrv = 0;
|
|
LIST_ENTRY glistNotifyClients = {0};
|
|
HANDLE ghDesktopSwitchEvent = NULL;
|
|
HANDLE ghmutNotifyList = NULL;
|
|
HANDLE ghHotkeyEvent = NULL;
|
|
HANDLE ghRefreshEvent = NULL;
|
|
HANDLE ghRPCServerThread = NULL;
|
|
HWND ghwndSuperTIP = NULL;
|
|
HWND ghwndMouse = NULL;
|
|
HWND ghwndSuperTIPInk = NULL;
|
|
HCURSOR ghcurPressHold = NULL;
|
|
HCURSOR ghcurNormal = NULL;
|
|
UINT guimsgSuperTIPInk = 0;
|
|
#ifdef DRAW_INK
|
|
HWND ghwndDrawInk = NULL;
|
|
#endif
|
|
ISuperTip *gpISuperTip = NULL;
|
|
ITellMe *gpITellMe = NULL;
|
|
int gcxScreen = 0, gcyScreen = 0;
|
|
int gcxPrimary = 0, gcyPrimary = 0;
|
|
LONG glVirtualDesktopLeft = 0,
|
|
glVirtualDesktopRight = 0,
|
|
glVirtualDesktopTop = 0,
|
|
glVirtualDesktopBottom = 0;
|
|
LONG glLongOffset = 0, glShortOffset = 0;
|
|
int giButtonsSwapped = 0;
|
|
ULONG gdwMinX = 0,
|
|
gdwMaxX = MAX_NORMALIZED_X,
|
|
gdwRngX = MAX_NORMALIZED_X;
|
|
ULONG gdwMinY = 0,
|
|
gdwMaxY = MAX_NORMALIZED_Y,
|
|
gdwRngY = MAX_NORMALIZED_Y;
|
|
int gixIndex = 0, giyIndex = 0;
|
|
INPUT gInput = {INPUT_MOUSE};
|
|
DWORD gdwPenState = PENSTATE_NORMAL;
|
|
DWORD gdwPenDownTime = 0;
|
|
LONG glPenDownX = 0;
|
|
LONG glPenDownY = 0;
|
|
DWORD gdwPenUpTime = 0;
|
|
LONG glPenUpX = 0;
|
|
LONG glPenUpY = 0;
|
|
WORD gwLastButtons = 0;
|
|
CONFIG gConfig = {0};
|
|
GESTURE_SETTINGS gDefGestureSettings =
|
|
{
|
|
sizeof(GESTURE_SETTINGS),
|
|
GESTURE_FEATURE_RECOG_ENABLED |
|
|
GESTURE_FEATURE_PRESSHOLD_ENABLED,
|
|
200, //iRadius
|
|
6, //iMinOutPts
|
|
800, //iMaxTimeToInspect
|
|
3, //iAspectRatio
|
|
400, //iCheckTime
|
|
4, //iPointsToExamine
|
|
50, //iStopDist
|
|
350, //iStopTime (was 200)
|
|
500, //iPressHoldTime
|
|
3, //iHoldTolerance
|
|
700, //iCancelPressHoldTime
|
|
PopupSuperTIP,
|
|
PopupMIP,
|
|
GestureNoAction,
|
|
GestureNoAction
|
|
};
|
|
BUTTON_SETTINGS gDefButtonSettings =
|
|
{
|
|
sizeof(BUTTON_SETTINGS),
|
|
Enter,
|
|
PageUp,
|
|
InvokeNoteBook,
|
|
AltEsc,
|
|
PageDown,
|
|
BUTTONSTATE_DEFHOTKEY
|
|
};
|
|
TSTHREAD gTabSrvThreads[] =
|
|
{
|
|
RPCServerThread, "RPCServer", TSF_RPCTHREAD,
|
|
THREADF_ENABLED,
|
|
0, NULL, NULL, -1, NULL,
|
|
SuperTIPThread, "SuperTIP", TSF_SUPERTIPTHREAD,
|
|
THREADF_ENABLED | THREADF_SDT_POSTMSG | THREADF_RESTARTABLE,
|
|
0, NULL, NULL, -1, NULL,
|
|
DeviceThread, "Digitizer", TSF_DIGITHREAD,
|
|
THREADF_ENABLED | THREADF_SDT_SETEVENT | THREADF_RESTARTABLE,
|
|
0, NULL, NULL, -1, (PVOID)&gdevDigitizer,
|
|
#ifdef MOUSE_THREAD
|
|
MouseThread, "Mouse", TSF_MOUSETHREAD,
|
|
THREADF_ENABLED | THREADF_SDT_POSTMSG | THREADF_RESTARTABLE,
|
|
0, NULL, NULL, -1, NULL,
|
|
#endif
|
|
DeviceThread, "Buttons", TSF_BUTTONTHREAD,
|
|
THREADF_ENABLED | THREADF_SDT_SETEVENT | THREADF_RESTARTABLE,
|
|
0, NULL, NULL, -1, (PVOID)&gdevButtons,
|
|
};
|
|
#define NUM_THREADS ARRAYSIZE(gTabSrvThreads)
|
|
|
|
TCHAR gtszTabSrvTitle[128] = {0};
|
|
TCHAR gtszTabSrvName[] = TEXT(STR_TABSRV_NAME);
|
|
TCHAR gtszGestureSettings[] = TEXT("GestureSettings");
|
|
TCHAR gtszButtonSettings[] = TEXT("ButtonSettings");
|
|
TCHAR gtszPenTilt[] = TEXT("PenTilt");
|
|
TCHAR gtszLinearityMap[] = TEXT(STR_LINEARITY_MAP);
|
|
TCHAR gtszRegPath[] = TEXT(STR_REGPATH_TABSRVPARAM);
|
|
TCHAR gtszPortraitMode2[] = TEXT("PortraitMode2");
|
|
TCHAR gtszInputDesktop[32] = {0};
|
|
|
|
SERVICE_STATUS_HANDLE ghServStatus = NULL;
|
|
SERVICE_STATUS gServStatus = {0};
|
|
DIGITIZER_DATA gLastRawDigiReport = {0};
|
|
DEVICE_DATA gdevDigitizer = {HID_USAGE_PAGE_DIGITIZER,
|
|
HID_USAGE_DIGITIZER_PEN};
|
|
DEVICE_DATA gdevButtons = {HID_USAGE_PAGE_CONSUMER,
|
|
HID_USAGE_CONSUMERCTRL};
|
|
|
|
#ifdef DEBUG
|
|
NAMETABLE ServiceControlNames[] =
|
|
{
|
|
SERVICE_CONTROL_STOP, "Stop",
|
|
SERVICE_CONTROL_PAUSE, "Pause",
|
|
SERVICE_CONTROL_CONTINUE, "Continue",
|
|
SERVICE_CONTROL_INTERROGATE, "Interrogate",
|
|
SERVICE_CONTROL_SHUTDOWN, "Shutdown",
|
|
SERVICE_CONTROL_PARAMCHANGE, "ParamChange",
|
|
SERVICE_CONTROL_NETBINDADD, "NetBindAdd",
|
|
SERVICE_CONTROL_NETBINDREMOVE, "NetBindRemove",
|
|
SERVICE_CONTROL_NETBINDENABLE, "NetBindEnable",
|
|
SERVICE_CONTROL_NETBINDDISABLE, "NetBindDisable",
|
|
0, NULL
|
|
};
|
|
NAMETABLE ConsoleControlNames[] =
|
|
{
|
|
CTRL_C_EVENT, "CtrlCEvent",
|
|
CTRL_BREAK_EVENT, "CtrlBreakEvent",
|
|
CTRL_CLOSE_EVENT, "CloseEvent",
|
|
CTRL_LOGOFF_EVENT, "LogOffEvent",
|
|
CTRL_SHUTDOWN_EVENT, "ShutDownEvent",
|
|
0, NULL
|
|
};
|
|
#endif
|
|
|
|
/*++
|
|
@doc EXTERNAL
|
|
|
|
@func int | main | Program entry point.
|
|
|
|
@parm IN int | icArgs | Specifies number of command line arguments.
|
|
@parm IN PSZ * | apszArgs | Points to the array of argument strings.
|
|
|
|
@rvalue SUCCESS | Returns 0.
|
|
@rvalue FAILURE | Returns -1.
|
|
--*/
|
|
|
|
int __cdecl
|
|
main(
|
|
IN int icArgs,
|
|
IN LPTSTR *aptszArgs
|
|
)
|
|
{
|
|
TRACEPROC("main", 1)
|
|
int rc = 0;
|
|
|
|
TRACEINIT(STR_TABSRV_NAME, 0, 0);
|
|
TRACEENTER(("(icArgs=%d,aptszArgs=%p)\n", icArgs, aptszArgs));
|
|
|
|
ghMod = GetModuleHandle(NULL);
|
|
TRACEASSERT(ghMod != NULL);
|
|
LoadString(ghMod,
|
|
IDS_TABSRV_TITLE,
|
|
gtszTabSrvTitle,
|
|
ARRAYSIZE(gtszTabSrvTitle));
|
|
|
|
if (icArgs == 1)
|
|
{
|
|
//
|
|
// No command line argument, must be SCM...
|
|
//
|
|
SERVICE_TABLE_ENTRY ServiceTable[] =
|
|
{
|
|
{gtszTabSrvName, TabSrvMain},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
if (!StartServiceCtrlDispatcher(ServiceTable))
|
|
{
|
|
TABSRVERR(("Failed to start service dispatcher (err=%d).\n",
|
|
GetLastError()));
|
|
rc = -1;
|
|
}
|
|
}
|
|
else if (icArgs == 2)
|
|
{
|
|
if ((aptszArgs[1][0] == TEXT('-')) || (aptszArgs[1][0] == TEXT('/')))
|
|
{
|
|
if (lstrcmpi(&aptszArgs[1][1], TEXT("install")) == 0)
|
|
{
|
|
InstallTabSrv();
|
|
}
|
|
#ifdef ALLOW_REMOVE
|
|
else if (lstrcmpi(&aptszArgs[1][1], TEXT("remove")) == 0)
|
|
{
|
|
RemoveTabSrv();
|
|
}
|
|
#endif
|
|
#ifdef ALLOW_START
|
|
else if (lstrcmpi(&aptszArgs[1][1], TEXT("start")) == 0)
|
|
{
|
|
StartTabSrv();
|
|
}
|
|
#endif
|
|
#ifdef ALLOW_STOP
|
|
else if (lstrcmpi(&aptszArgs[1][1], TEXT("stop")) == 0)
|
|
{
|
|
StopTabSrv();
|
|
}
|
|
#endif
|
|
#ifdef ALLOW_DEBUG
|
|
else if (lstrcmpi(&aptszArgs[1][1], TEXT("debug")) == 0)
|
|
{
|
|
gdwfTabSrv |= TSF_DEBUG_MODE;
|
|
SetConsoleCtrlHandler(TabSrvConsoleHandler, TRUE);
|
|
TabSrvMain(0, NULL);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
TABSRVERR(("Invalid command line argument \"%s\".\n",
|
|
aptszArgs[1]));
|
|
rc = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Invalid command line syntax \"%s\".\n", aptszArgs[1]));
|
|
rc = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Too many command line arguments.\n"));
|
|
rc = -1;
|
|
}
|
|
|
|
TRACEEXIT(("=%d\n", rc));
|
|
TRACETERMINATE();
|
|
return rc;
|
|
} //main
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func VOID | InstallTabSrv | Install TabSrv service.
|
|
|
|
@parm None.
|
|
|
|
@rvalue None.
|
|
--*/
|
|
|
|
VOID
|
|
InstallTabSrv(
|
|
VOID
|
|
)
|
|
{
|
|
TRACEPROC("InstallTabSrv", 2)
|
|
SC_HANDLE hSCM;
|
|
SC_HANDLE hService;
|
|
|
|
TRACEENTER(("()\n"));
|
|
|
|
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
|
TRACEASSERT(hSCM != NULL);
|
|
|
|
hService = CreateService(hSCM,
|
|
gtszTabSrvName,
|
|
gtszTabSrvTitle,
|
|
SERVICE_ALL_ACCESS | SERVICE_CHANGE_CONFIG,
|
|
SERVICE_WIN32_OWN_PROCESS,
|
|
SERVICE_AUTO_START,
|
|
SERVICE_ERROR_NORMAL,
|
|
TEXT("%SystemRoot%\\system32\\tabsrv.exe"),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (hService != NULL)
|
|
{
|
|
SetTabSrvConfig(hService);
|
|
CloseServiceHandle(hService);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to create TabSrv service (err=%d).\n",
|
|
GetLastError()));
|
|
}
|
|
|
|
CloseServiceHandle(hSCM);
|
|
|
|
//
|
|
// Go ahead and start the service for no-reboot install.
|
|
//
|
|
StartTabSrv();
|
|
|
|
TRACEEXIT(("!\n"));
|
|
return;
|
|
} //InstallTabSrv
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func VOID | SetTabSrvConfig | Set service configuration.
|
|
|
|
@parm IN SC_HANDLE | hService | Service handle.
|
|
|
|
@rvalue None.
|
|
--*/
|
|
|
|
VOID
|
|
SetTabSrvConfig(
|
|
IN SC_HANDLE hService
|
|
)
|
|
{
|
|
TRACEPROC("SetTabSrvConfig", 2)
|
|
SERVICE_FAILURE_ACTIONS FailActions;
|
|
SC_ACTION Actions = {SC_ACTION_RESTART, 10000}; //restart after 10 sec.
|
|
|
|
TRACEENTER(("(hService=%x)\n", hService));
|
|
|
|
FailActions.dwResetPeriod = 86400; //reset failure count after 1 day.
|
|
FailActions.lpRebootMsg = FailActions.lpCommand = NULL;
|
|
FailActions.cActions = 1;
|
|
FailActions.lpsaActions = &Actions;
|
|
if (!ChangeServiceConfig2(hService,
|
|
SERVICE_CONFIG_FAILURE_ACTIONS,
|
|
&FailActions))
|
|
{
|
|
TABSRVERR(("Failed to set failure actions (err=%d)\n", GetLastError()));
|
|
}
|
|
|
|
TRACEEXIT(("!\n"));
|
|
return;
|
|
} //SetTabSrvConfig
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func VOID | RemoveTabSrv | Remove TabSrv service.
|
|
|
|
@parm None.
|
|
|
|
@rvalue None.
|
|
--*/
|
|
|
|
VOID
|
|
RemoveTabSrv(
|
|
VOID
|
|
)
|
|
{
|
|
TRACEPROC("RemoveTabSrv", 2)
|
|
SC_HANDLE hSCM;
|
|
SC_HANDLE hService;
|
|
|
|
TRACEENTER(("()\n"));
|
|
|
|
//
|
|
// Stop the service first
|
|
//
|
|
StopTabSrv();
|
|
|
|
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
|
TRACEASSERT(hSCM != NULL);
|
|
|
|
hService = OpenService(hSCM, gtszTabSrvName, DELETE);
|
|
if (hService != NULL)
|
|
{
|
|
DeleteService(hService);
|
|
CloseServiceHandle(hService);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to open service for delete.\n"));
|
|
}
|
|
|
|
CloseServiceHandle(hSCM);
|
|
|
|
TRACEEXIT(("!\n"));
|
|
return;
|
|
} //RemoveTabSrv
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func VOID | StartTabSrv | Start TabSrv service.
|
|
|
|
@parm None.
|
|
|
|
@rvalue None.
|
|
--*/
|
|
|
|
VOID
|
|
StartTabSrv(
|
|
VOID
|
|
)
|
|
{
|
|
TRACEPROC("StartTabSrv", 2)
|
|
SC_HANDLE hSCM;
|
|
SC_HANDLE hService;
|
|
|
|
TRACEENTER(("()\n"));
|
|
|
|
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
|
TRACEASSERT(hSCM != NULL);
|
|
|
|
hService = OpenService(hSCM, gtszTabSrvName, SERVICE_START);
|
|
if (hService != NULL)
|
|
{
|
|
if (!StartService(hService, 0, NULL))
|
|
{
|
|
TABSRVERR(("Failed to start service (err=%d).\n", GetLastError()));
|
|
}
|
|
CloseServiceHandle(hService);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to open service for start.\n"));
|
|
}
|
|
|
|
CloseServiceHandle(hSCM);
|
|
|
|
TRACEEXIT(("!\n"));
|
|
return;
|
|
} //StartTabSrv
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func VOID | StopTabSrv | Stop TabSrv service.
|
|
|
|
@parm None.
|
|
|
|
@rvalue None.
|
|
--*/
|
|
|
|
VOID
|
|
StopTabSrv(
|
|
VOID
|
|
)
|
|
{
|
|
TRACEPROC("StopTabSrv", 2)
|
|
SC_HANDLE hSCM;
|
|
SC_HANDLE hService;
|
|
SERVICE_STATUS Status;
|
|
|
|
TRACEENTER(("()\n"));
|
|
|
|
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
|
TRACEASSERT(hSCM != NULL);
|
|
|
|
hService = OpenService(hSCM, gtszTabSrvName, SERVICE_STOP);
|
|
if (hService != NULL)
|
|
{
|
|
if (!ControlService(hService, SERVICE_CONTROL_STOP, &Status))
|
|
{
|
|
TABSRVERR(("Failed to stop service (err=%d).\n", GetLastError()));
|
|
}
|
|
CloseServiceHandle(hService);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to open service for stop.\n"));
|
|
}
|
|
|
|
CloseServiceHandle(hSCM);
|
|
|
|
TRACEEXIT(("!\n"));
|
|
return;
|
|
} //StopTabSrv
|
|
|
|
/*++
|
|
@doc EXTERNAL
|
|
|
|
@func VOID | TabSrvServiceHandler | Service handler.
|
|
|
|
@parm IN DWORD | dwControl | Control code.
|
|
|
|
@rvalue None.
|
|
--*/
|
|
|
|
VOID WINAPI
|
|
TabSrvServiceHandler(
|
|
IN DWORD dwControl
|
|
)
|
|
{
|
|
TRACEPROC("TabSrvServiceHandler", 3)
|
|
|
|
TRACEENTER(("(Control=%s)\n", LookupName(dwControl, ServiceControlNames)));
|
|
|
|
switch (dwControl)
|
|
{
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
SetServiceStatus(ghServStatus, &gServStatus);
|
|
break;
|
|
|
|
case SERVICE_CONTROL_STOP:
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
SET_SERVICE_STATUS(SERVICE_STOP_PENDING);
|
|
TabSrvTerminate(TRUE);
|
|
break;
|
|
|
|
default:
|
|
TRACEWARN(("Unhandled control code (%s).\n",
|
|
LookupName(dwControl, ServiceControlNames)));
|
|
}
|
|
|
|
TRACEEXIT(("!\n"));
|
|
return;
|
|
} //TabSrvServiceHandler
|
|
|
|
#ifdef ALLOW_DEBUG
|
|
/*++
|
|
@doc EXTERNAL
|
|
|
|
@func BOOL | TabSrvConsoleHandler | Console control handler.
|
|
|
|
@parm IN DWORD | dwControl | Control code.
|
|
|
|
@rvalue SUCCESS | Returns TRUE - handle the event.
|
|
@rvalue FAILURE | Returns FALSE - do not handle the event.
|
|
--*/
|
|
|
|
BOOL WINAPI
|
|
TabSrvConsoleHandler(
|
|
IN DWORD dwControl
|
|
)
|
|
{
|
|
TRACEPROC("TabSrvConsoleHandler", 3)
|
|
BOOL rc = FALSE;
|
|
|
|
TRACEENTER(("(Control=%s)\n", LookupName(dwControl, ConsoleControlNames)));
|
|
|
|
switch (dwControl)
|
|
{
|
|
case CTRL_C_EVENT:
|
|
case CTRL_BREAK_EVENT:
|
|
case CTRL_CLOSE_EVENT:
|
|
SET_SERVICE_STATUS(SERVICE_STOP_PENDING);
|
|
TabSrvTerminate(TRUE);
|
|
rc = TRUE;
|
|
break;
|
|
}
|
|
|
|
TRACEEXIT(("=%x\n", rc));
|
|
return rc;
|
|
} //TabSrvConsoleHandler
|
|
#endif
|
|
|
|
/*++
|
|
@doc EXTERNAL
|
|
|
|
@func VOID | TabSrvMain | Service entry point.
|
|
|
|
@parm IN int | icArgs | Specifies number of command line arguments.
|
|
@parm IN PSZ * | apszArgs | Points to the array of argument strings.
|
|
|
|
@rvalue None.
|
|
--*/
|
|
|
|
VOID WINAPI
|
|
TabSrvMain(
|
|
IN DWORD icArgs,
|
|
IN LPTSTR *aptszArgs
|
|
)
|
|
{
|
|
TRACEPROC("TabSrvMain", 2)
|
|
|
|
TRACEENTER(("(icArgs=%d,aptszArgs=%p)\n", icArgs, aptszArgs));
|
|
|
|
if ((icArgs == 2) && (lstrcmpi(aptszArgs[1], TEXT("/config")) == 0))
|
|
{
|
|
SC_HANDLE hSCM;
|
|
SC_HANDLE hService;
|
|
|
|
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
|
TRACEASSERT(hSCM != NULL);
|
|
|
|
hService = OpenService(hSCM,
|
|
gtszTabSrvName,
|
|
SERVICE_ALL_ACCESS | SERVICE_CHANGE_CONFIG);
|
|
if (hService != NULL)
|
|
{
|
|
SetTabSrvConfig(hService);
|
|
CloseServiceHandle(hService);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to open service for config.\n"));
|
|
}
|
|
CloseServiceHandle(hSCM);
|
|
}
|
|
|
|
InitializeListHead(&glistNotifyClients);
|
|
|
|
ghcurPressHold = LoadCursor(ghMod, MAKEINTRESOURCE(IDC_PRESSHOLD));
|
|
TRACEASSERT(ghcurPressHold != NULL);
|
|
ghcurNormal = LoadCursor(ghMod, MAKEINTRESOURCE(IDC_NORMAL));
|
|
TRACEASSERT(ghcurNormal != NULL);
|
|
|
|
ghDesktopSwitchEvent = OpenEvent(SYNCHRONIZE,
|
|
FALSE,
|
|
TEXT("WinSta0_DesktopSwitch"));
|
|
TRACEASSERT(ghDesktopSwitchEvent != NULL);
|
|
|
|
ghmutNotifyList = CreateMutex(NULL, FALSE, NULL);
|
|
TRACEASSERT(ghmutNotifyList != NULL);
|
|
|
|
gdevDigitizer.hStopDeviceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
TRACEASSERT(gdevDigitizer.hStopDeviceEvent != NULL);
|
|
FindThread(TSF_DIGITHREAD)->pvSDTParam = gdevDigitizer.hStopDeviceEvent;
|
|
gdevButtons.hStopDeviceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
TRACEASSERT(gdevButtons.hStopDeviceEvent != NULL);
|
|
FindThread(TSF_BUTTONTHREAD)->pvSDTParam = gdevButtons.hStopDeviceEvent;
|
|
ghHotkeyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
TRACEASSERT(ghHotkeyEvent != NULL);
|
|
ghRefreshEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
TRACEASSERT(ghRefreshEvent != NULL);
|
|
|
|
gServStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
|
gServStatus.dwCurrentState = SERVICE_START_PENDING;
|
|
gServStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
|
SERVICE_ACCEPT_SHUTDOWN;
|
|
gServStatus.dwWin32ExitCode = NO_ERROR;
|
|
gServStatus.dwServiceSpecificExitCode = NO_ERROR;
|
|
gServStatus.dwCheckPoint = 0;
|
|
gServStatus.dwWaitHint = 0;
|
|
UpdateRotation();
|
|
|
|
if (!(gdwfTabSrv & TSF_DEBUG_MODE))
|
|
{
|
|
ghServStatus = RegisterServiceCtrlHandler(gtszTabSrvName,
|
|
TabSrvServiceHandler);
|
|
TRACEASSERT(ghServStatus != NULL);
|
|
SetServiceStatus(ghServStatus, &gServStatus);
|
|
}
|
|
|
|
//
|
|
// For a noninteractive service application to interact with the user,
|
|
// it must open the user's window station ("WinSta0") and desktop
|
|
// ("Default").
|
|
//
|
|
HWINSTA hwinstaSave = GetProcessWindowStation();
|
|
HWINSTA hwinsta = OpenWindowStation(TEXT("WinSta0"),
|
|
FALSE,
|
|
MAXIMUM_ALLOWED);
|
|
TRACEASSERT(hwinstaSave != NULL);
|
|
if (hwinsta != NULL)
|
|
{
|
|
SetProcessWindowStation(hwinsta);
|
|
InitConfigFromReg();
|
|
GetInputDesktopName(gtszInputDesktop, sizeof(gtszInputDesktop));
|
|
if (InitThreads(gTabSrvThreads, NUM_THREADS))
|
|
{
|
|
SET_SERVICE_STATUS(SERVICE_RUNNING);
|
|
WaitForTermination();
|
|
SET_SERVICE_STATUS(SERVICE_STOPPED);
|
|
}
|
|
else
|
|
{
|
|
TabSrvTerminate(TRUE);
|
|
}
|
|
|
|
if (hwinstaSave != NULL)
|
|
{
|
|
SetProcessWindowStation(hwinstaSave);
|
|
}
|
|
CloseWindowStation(hwinsta);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to open window station.\n"));
|
|
}
|
|
|
|
DWORD rcWait = WaitForSingleObject(ghmutNotifyList, INFINITE);
|
|
if (rcWait == WAIT_OBJECT_0)
|
|
{
|
|
PLIST_ENTRY plist;
|
|
PNOTIFYCLIENT NotifyClient;
|
|
|
|
while (!IsListEmpty(&glistNotifyClients))
|
|
{
|
|
plist = RemoveHeadList(&glistNotifyClients);
|
|
NotifyClient = CONTAINING_RECORD(plist, NOTIFYCLIENT, list);
|
|
free(NotifyClient);
|
|
}
|
|
ReleaseMutex(ghmutNotifyList);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to wait for RawPtList Mutex (rcWait=%x,err=%d).\n",
|
|
rcWait, GetLastError()));
|
|
}
|
|
|
|
CloseHandle(ghRefreshEvent);
|
|
CloseHandle(ghHotkeyEvent);
|
|
CloseHandle(gdevDigitizer.hStopDeviceEvent);
|
|
CloseHandle(gdevButtons.hStopDeviceEvent);
|
|
ghHotkeyEvent = NULL;
|
|
gdevDigitizer.hStopDeviceEvent = NULL;
|
|
gdevButtons.hStopDeviceEvent = NULL;
|
|
CloseHandle(ghmutNotifyList);
|
|
ghmutNotifyList = NULL;
|
|
CloseHandle(ghDesktopSwitchEvent);
|
|
ghDesktopSwitchEvent = NULL;
|
|
DestroyCursor(ghcurNormal);
|
|
ghcurNormal = NULL;
|
|
DestroyCursor(ghcurPressHold);
|
|
ghcurPressHold = NULL;
|
|
gdwfTabSrv = 0;
|
|
TRACEINFO(1, ("Out of here.\n"));
|
|
|
|
TRACEEXIT(("!\n"));
|
|
return;
|
|
} //TabSrvMain
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func VOID | InitConfigFromReg | Initialize configuration from registry.
|
|
|
|
@parm None.
|
|
|
|
@rvalue None.
|
|
--*/
|
|
|
|
VOID
|
|
InitConfigFromReg(
|
|
VOID
|
|
)
|
|
{
|
|
TRACEPROC("InitConfigFromReg", 2)
|
|
LONG rcCfg;
|
|
LPTSTR lptstrPenTilt;
|
|
PTSTHREAD MouseThread;
|
|
|
|
TRACEENTER(("()\n"));
|
|
|
|
rcCfg = ReadConfig(gtszGestureSettings,
|
|
(LPBYTE)&gConfig.GestureSettings,
|
|
sizeof(gConfig.GestureSettings));
|
|
if ((rcCfg != ERROR_SUCCESS) ||
|
|
(gConfig.GestureSettings.dwcbLen != sizeof(gConfig.GestureSettings)))
|
|
{
|
|
if (rcCfg == ERROR_SUCCESS)
|
|
{
|
|
TABSRVERR(("Incorrect size on GestureSettings, use default.\n"));
|
|
}
|
|
gConfig.GestureSettings = gDefGestureSettings;
|
|
}
|
|
|
|
MouseThread = FindThread(TSF_MOUSETHREAD);
|
|
if (MouseThread != NULL)
|
|
{
|
|
if (gConfig.GestureSettings.dwfFeatures & GESTURE_FEATURE_MOUSE_ENABLED)
|
|
{
|
|
MouseThread->dwfThread |= THREADF_ENABLED;
|
|
}
|
|
else
|
|
{
|
|
MouseThread->dwfThread &= ~THREADF_ENABLED;
|
|
}
|
|
}
|
|
|
|
lptstrPenTilt = (gdwfTabSrv & TSF_PORTRAIT_MODE)?
|
|
TEXT("PenTilt_Portrait"): TEXT("PenTilt_Landscape"),
|
|
rcCfg = ReadConfig(lptstrPenTilt,
|
|
(LPBYTE)&gConfig.PenTilt,
|
|
sizeof(gConfig.PenTilt));
|
|
|
|
rcCfg = ReadConfig(gtszLinearityMap,
|
|
(LPBYTE)&gConfig.LinearityMap,
|
|
sizeof(gConfig.LinearityMap));
|
|
if ((rcCfg == ERROR_SUCCESS) &&
|
|
(gConfig.LinearityMap.dwcbLen == sizeof(gConfig.LinearityMap)))
|
|
{
|
|
gdwfTabSrv |= TSF_HAS_LINEAR_MAP;
|
|
}
|
|
|
|
rcCfg = ReadConfig(gtszButtonSettings,
|
|
(LPBYTE)&gConfig.ButtonSettings,
|
|
sizeof(gConfig.ButtonSettings));
|
|
if ((rcCfg != ERROR_SUCCESS) ||
|
|
(gConfig.ButtonSettings.dwcbLen != sizeof(gConfig.ButtonSettings)))
|
|
{
|
|
if (rcCfg == ERROR_SUCCESS)
|
|
{
|
|
TABSRVERR(("Incorrect size on ButtonSettings, use default.\n"));
|
|
}
|
|
gConfig.ButtonSettings = gDefButtonSettings;
|
|
}
|
|
|
|
BOOL fPortraitMode2 = FALSE;
|
|
rcCfg = ReadConfig(gtszPortraitMode2,
|
|
(LPBYTE)&fPortraitMode2,
|
|
sizeof(fPortraitMode2));
|
|
if ((rcCfg == ERROR_SUCCESS) && fPortraitMode2)
|
|
{
|
|
gdwfTabSrv |= TSF_PORTRAIT_MODE2;
|
|
}
|
|
|
|
TRACEEXIT(("!\n"));
|
|
return;
|
|
} //InitConfigFromReg
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func BOOL | InitThreads | Initialize various threads.
|
|
|
|
@parm PTSTHREAD | pThreads | Points to the thread array for the threads
|
|
to start.
|
|
@parm int | nThreads | Specifies the number of threads in the array.
|
|
|
|
@rvalue SUCCESS | Returns TRUE.
|
|
@rvalue FAILURE | Returns FALSE.
|
|
--*/
|
|
|
|
BOOL
|
|
InitThreads(
|
|
IN PTSTHREAD pThreads,
|
|
IN int nThreads
|
|
)
|
|
{
|
|
TRACEPROC("InitThreads", 2)
|
|
BOOL rc = TRUE;
|
|
int i;
|
|
unsigned uThreadID;
|
|
|
|
TRACEENTER(("(pThreads=%p,NumThreads=%d)\n", pThreads, nThreads));
|
|
|
|
for (i = 0; i < nThreads; ++i)
|
|
{
|
|
if (pThreads[i].dwfThread & THREADF_ENABLED)
|
|
{
|
|
pThreads[i].hThread = (HANDLE)_beginthreadex(
|
|
NULL,
|
|
0,
|
|
pThreads[i].pfnThread,
|
|
pThreads[i].pvParam?
|
|
pThreads[i].pvParam:
|
|
&pThreads[i],
|
|
0,
|
|
&uThreadID);
|
|
if (pThreads[i].hThread != NULL)
|
|
{
|
|
gdwfTabSrv |= pThreads[i].dwThreadTag;
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to create %s thread, LastError=%d.\n",
|
|
pThreads[i].pszThreadName, GetLastError()));
|
|
rc = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
SetEvent(ghRefreshEvent);
|
|
|
|
TRACEEXIT(("=%x\n", rc));
|
|
return rc;
|
|
} //InitThreads
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func PTSTHREAD | FindThread | Find a thread in the thread table.
|
|
|
|
@parm DWORD | dwThreadTag | Specifies the thread to look for.
|
|
|
|
@rvalue SUCCESS | Returns pointer to the thread entry in table.
|
|
@rvalue FAILURE | Returns NULL.
|
|
--*/
|
|
|
|
PTSTHREAD
|
|
FindThread(
|
|
IN DWORD dwThreadTag
|
|
)
|
|
{
|
|
TRACEPROC("FindThread", 2)
|
|
PTSTHREAD Thread = NULL;
|
|
int i;
|
|
|
|
TRACEENTER(("(ThreadTag=%x)\n", dwThreadTag));
|
|
|
|
for (i = 0; i < NUM_THREADS; ++i)
|
|
{
|
|
if (gTabSrvThreads[i].dwThreadTag == dwThreadTag)
|
|
{
|
|
Thread = &gTabSrvThreads[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
TRACEEXIT(("=%p\n", Thread));
|
|
return Thread;
|
|
} //FindThread
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func VOID | WaitForTermination | Wait for termination.
|
|
|
|
@parm None.
|
|
|
|
@rvalue None.
|
|
--*/
|
|
|
|
VOID
|
|
WaitForTermination(
|
|
VOID
|
|
)
|
|
{
|
|
TRACEPROC("WaitForTermination", 2)
|
|
HANDLE ahWait[NUM_THREADS + 3];
|
|
DWORD rcWait;
|
|
int i;
|
|
|
|
TRACEENTER(("()\n"));
|
|
|
|
ahWait[0] = ghDesktopSwitchEvent;
|
|
ahWait[1] = ghHotkeyEvent;
|
|
ahWait[2] = ghRefreshEvent;
|
|
|
|
//
|
|
// Wait for termination.
|
|
//
|
|
for (;;)
|
|
{
|
|
DWORD n = 3;
|
|
|
|
for (i = 0; i < NUM_THREADS; ++i)
|
|
{
|
|
if (gTabSrvThreads[i].hThread != NULL)
|
|
{
|
|
ahWait[n] = gTabSrvThreads[i].hThread;
|
|
gTabSrvThreads[i].iThreadStatus = WAIT_OBJECT_0 + n;
|
|
n++;
|
|
}
|
|
else
|
|
{
|
|
gTabSrvThreads[i].iThreadStatus = -1;
|
|
}
|
|
}
|
|
|
|
rcWait = WaitForMultipleObjects(n, ahWait, FALSE, INFINITE);
|
|
if ((rcWait == WAIT_OBJECT_0) || (rcWait == WAIT_OBJECT_0 + 1))
|
|
{
|
|
TCHAR tszDesktop[32];
|
|
BOOL fDoSwitch = TRUE;
|
|
|
|
if (rcWait == WAIT_OBJECT_0 + 1)
|
|
{
|
|
//
|
|
// Send hot key event and tell all threads to switch.
|
|
//
|
|
TRACEINFO(1, ("Send Hotkey.\n"));
|
|
SendAltCtrlDel();
|
|
if (GetInputDesktopName(tszDesktop, sizeof(tszDesktop)))
|
|
{
|
|
TRACEINFO(1, ("[CAD] New input desktop=%s\n", tszDesktop));
|
|
lstrcpyn(gtszInputDesktop,
|
|
tszDesktop,
|
|
ARRAYSIZE(gtszInputDesktop));
|
|
}
|
|
}
|
|
else if (GetInputDesktopName(tszDesktop, sizeof(tszDesktop)))
|
|
{
|
|
if (lstrcmpi(tszDesktop, gtszInputDesktop) == 0)
|
|
{
|
|
//
|
|
// It seems that when the user logs off, two desktop
|
|
// switches are signaled in quick succession. The
|
|
// desktop is still 'Default' after the first switch
|
|
// is signaled. So we don't really want to switch
|
|
// all threads to the same desktop.
|
|
//
|
|
TRACEINFO(1, ("Same desktop, don't switch (Desktop=%s).\n",
|
|
tszDesktop));
|
|
fDoSwitch = FALSE;
|
|
}
|
|
else
|
|
{
|
|
TRACEINFO(1, ("New input desktop=%s\n", tszDesktop));
|
|
lstrcpyn(gtszInputDesktop,
|
|
tszDesktop,
|
|
ARRAYSIZE(gtszInputDesktop));
|
|
}
|
|
}
|
|
|
|
if (fDoSwitch)
|
|
{
|
|
//
|
|
// We are swtiching, tell all threads that need to switch
|
|
// desktop to switch.
|
|
//
|
|
TabSrvTerminate(FALSE);
|
|
}
|
|
}
|
|
else if (rcWait != WAIT_OBJECT_0 + 2)
|
|
{
|
|
for (i = 0; i < NUM_THREADS; ++i)
|
|
{
|
|
if (rcWait == (DWORD)gTabSrvThreads[i].iThreadStatus)
|
|
{
|
|
CloseHandle(gTabSrvThreads[i].hThread);
|
|
gTabSrvThreads[i].hThread = NULL;
|
|
gdwfTabSrv &= ~gTabSrvThreads[i].dwThreadTag;
|
|
TRACEINFO(1, ("%s thread is killed.\n",
|
|
gTabSrvThreads[i].pszThreadName));
|
|
//
|
|
// If a thread died unexpectedly and it is marked
|
|
// restartable, restart it.
|
|
//
|
|
if (!(gdwfTabSrv & TSF_TERMINATE) &&
|
|
(gTabSrvThreads[i].dwfThread & THREADF_RESTARTABLE))
|
|
{
|
|
if (gTabSrvThreads[i].dwcRestartTries < MAX_RESTARTS)
|
|
{
|
|
TRACEINFO(1, ("%s thread died unexpectedly, restarting (count=%d)\n",
|
|
gTabSrvThreads[i].pszThreadName,
|
|
gTabSrvThreads[i].dwcRestartTries));
|
|
InitThreads(&gTabSrvThreads[i], 1);
|
|
gTabSrvThreads[i].dwcRestartTries++;
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("%s thread exceeds restart maximum.\n",
|
|
gTabSrvThreads[i].pszThreadName));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == NUM_THREADS)
|
|
{
|
|
TABSRVERR(("WaitForMultipleObjects failed (rcWait=%x,err=%d).\n",
|
|
rcWait, GetLastError()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!(gdwfTabSrv & TSF_ALLTHREAD))
|
|
{
|
|
//
|
|
// All threads are dead, we can quit now.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
TRACEEXIT(("!\n"));
|
|
return;
|
|
} //WaitForTermination
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func VOID | TabSrvTerminate | Do clean up.
|
|
|
|
@parm IN BOOL | fTerminate | TRUE if real termination, otherwise
|
|
switching desktop.
|
|
|
|
@rvalue None.
|
|
--*/
|
|
|
|
VOID
|
|
TabSrvTerminate(
|
|
IN BOOL fTerminate
|
|
)
|
|
{
|
|
TRACEPROC("TabSrvTerminate", 2)
|
|
int i;
|
|
|
|
TRACEENTER(("(fTerminate=%x)\n", fTerminate));
|
|
|
|
if (fTerminate)
|
|
{
|
|
gdwfTabSrv |= TSF_TERMINATE;
|
|
}
|
|
|
|
for (i = 0; i < NUM_THREADS; ++i)
|
|
{
|
|
if (gTabSrvThreads[i].dwfThread &
|
|
(THREADF_SDT_POSTMSG | THREADF_SDT_SETEVENT))
|
|
{
|
|
if (gdwfTabSrv & gTabSrvThreads[i].dwThreadTag)
|
|
{
|
|
TRACEINFO(1, (fTerminate? "Kill %s thread.\n":
|
|
"Switch %s thread desktop.\n",
|
|
gTabSrvThreads[i].pszThreadName));
|
|
TRACEASSERT(gTabSrvThreads[i].pvSDTParam != NULL);
|
|
|
|
if (gTabSrvThreads[i].dwfThread & THREADF_SDT_POSTMSG)
|
|
{
|
|
if (IsWindow((HWND)gTabSrvThreads[i].pvSDTParam))
|
|
{
|
|
PostMessage((HWND)gTabSrvThreads[i].pvSDTParam,
|
|
WM_CLOSE,
|
|
0,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
TRACEINFO(1, ("%s thread window handle (%x) is not valid.\n",
|
|
gTabSrvThreads[i].pszThreadName,
|
|
gTabSrvThreads[i].pvSDTParam));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetEvent((HANDLE)gTabSrvThreads[i].pvSDTParam);
|
|
}
|
|
}
|
|
else if (!fTerminate &&
|
|
(gTabSrvThreads[i].dwcRestartTries >= MAX_RESTARTS))
|
|
{
|
|
TRACEINFO(1, ("Retrying to start %s thread.\n",
|
|
gTabSrvThreads[i].pszThreadName));
|
|
InitThreads(&gTabSrvThreads[i], 1);
|
|
gTabSrvThreads[i].dwcRestartTries = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fTerminate && (gdwfTabSrv & TSF_RPCTHREAD))
|
|
{
|
|
RpcMgmtStopServerListening(NULL);
|
|
}
|
|
|
|
TRACEEXIT(("!\n"));
|
|
return;
|
|
} //TabSrvTerminate
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func VOID | TabSrvLogError | Log the error in the event log.
|
|
|
|
@parm IN PSZ | pszFormat | Points to the format string.
|
|
@parm ... | String arguments.
|
|
|
|
@rvalue None.
|
|
--*/
|
|
|
|
VOID
|
|
TabSrvLogError(
|
|
IN PSZ pszFormat,
|
|
...
|
|
)
|
|
{
|
|
TRACEPROC("TabSrvLogError", 5)
|
|
|
|
TRACEENTER(("(Format=%s,...)\n", pszFormat));
|
|
|
|
if (!(gdwfTabSrv & TSF_DEBUG_MODE))
|
|
{
|
|
HANDLE hEventLog;
|
|
|
|
hEventLog = RegisterEventSource(NULL, TEXT(STR_TABSRV_NAME));
|
|
if (hEventLog != NULL)
|
|
{
|
|
char szMsg[256];
|
|
LPCSTR psz = szMsg;
|
|
va_list arglist;
|
|
|
|
va_start(arglist, pszFormat);
|
|
wvsprintfA(szMsg, pszFormat, arglist);
|
|
va_end(arglist);
|
|
|
|
ReportEventA(hEventLog, //handle of event source
|
|
EVENTLOG_ERROR_TYPE, //event type
|
|
0, //event category
|
|
0, //event ID
|
|
NULL, //current user's SID
|
|
1, //number of strings
|
|
0, //size of raw data
|
|
&psz, //array of strings
|
|
NULL); //no raw data
|
|
DeregisterEventSource(hEventLog);
|
|
}
|
|
}
|
|
|
|
TRACEEXIT(("!\n"));
|
|
return;
|
|
} //TabSrvLogError
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func LONG | ReadConfig | Read TabSrv configuration from registry.
|
|
|
|
@parm IN LPCTSTR | lptstrValueName | Points to the registry value
|
|
name string.
|
|
@parm OUT LPBYTE | lpbData | Points to the buffer to hold the value
|
|
read.
|
|
@parm IN DWORD | dwcb | Specifies the size of the buffer.
|
|
|
|
@rvalue SUCCESS | Returns ERROR_SUCCESS.
|
|
@rvalue FAILURE | Returns registry error code.
|
|
--*/
|
|
|
|
LONG
|
|
ReadConfig(
|
|
IN LPCTSTR lptstrValueName,
|
|
OUT LPBYTE lpbData,
|
|
IN DWORD dwcb
|
|
)
|
|
{
|
|
TRACEPROC("ReadConfig", 3)
|
|
LONG rc;
|
|
HKEY hkey;
|
|
|
|
TRACEENTER(("(Name=%s,pData=%p,Len=%d)\n", lptstrValueName, lpbData, dwcb));
|
|
|
|
rc = RegCreateKey(HKEY_LOCAL_MACHINE,
|
|
gtszRegPath,
|
|
&hkey);
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
rc = RegQueryValueEx(hkey,
|
|
lptstrValueName,
|
|
NULL,
|
|
NULL,
|
|
lpbData,
|
|
&dwcb);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
TRACEWARN(("Failed to read \"%s\" from registry (rc=%d).\n",
|
|
lptstrValueName, rc));
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to open registry key to read configuration (rc=%d).\n",
|
|
rc));
|
|
}
|
|
|
|
TRACEEXIT(("=%d\n", rc));
|
|
return rc;
|
|
} //ReadConfig
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func LONG | WriteConfig | Write TabSrv configuration to registry.
|
|
|
|
@parm IN LPCTSTR | lptstrValueName | Points to the registry value
|
|
name string.
|
|
@parm IN DWORD | dwType | Specifies the registry value type.
|
|
name string.
|
|
@parm IN LPBYTE | lpbData | Points to the buffer to hold the value
|
|
read.
|
|
@parm IN DWORD | dwcb | Specifies the size of the buffer.
|
|
|
|
@rvalue SUCCESS | Returns ERROR_SUCCESS.
|
|
@rvalue FAILURE | Returns registry error code.
|
|
--*/
|
|
|
|
LONG
|
|
WriteConfig(
|
|
IN LPCTSTR lptstrValueName,
|
|
IN DWORD dwType,
|
|
IN LPBYTE lpbData,
|
|
IN DWORD dwcb
|
|
)
|
|
{
|
|
TRACEPROC("WriteConfig", 3)
|
|
LONG rc;
|
|
HKEY hkey;
|
|
|
|
TRACEENTER(("(Name=%s,Type=%d,pData=%p,Len=%d)\n",
|
|
lptstrValueName, dwType, lpbData, dwcb));
|
|
|
|
rc = RegCreateKey(HKEY_LOCAL_MACHINE,
|
|
gtszRegPath,
|
|
&hkey);
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
rc = RegSetValueEx(hkey,
|
|
lptstrValueName,
|
|
0,
|
|
dwType,
|
|
lpbData,
|
|
dwcb);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
TRACEWARN(("Failed to write \"%s\" to registry (rc=%d).\n",
|
|
lptstrValueName, rc));
|
|
}
|
|
RegCloseKey(hkey);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to create registry key to write configuration (rc=%d).\n",
|
|
rc));
|
|
}
|
|
|
|
TRACEEXIT(("=%d\n", rc));
|
|
return rc;
|
|
} //WriteConfig
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func HRESULT | GetRegValueString | Get registry string value.
|
|
|
|
@parm IN HKEY | hkeyTopLevel | Top level registry key.
|
|
@parm IN LPCTSTR | pszSubKey | Subkey string.
|
|
@parm IN LPCTSTR | pszValueName | Value name string.
|
|
@parm OUT LPTSTR | pszValueString | To hold value string.
|
|
@parm IN OUT LPDWORD | lpdwcb | Specify size of ValueString and to hold
|
|
result string size.
|
|
|
|
@rvalue SUCCESS | Returns ERROR_SUCCESS.
|
|
@rvalue FAILURE | Returns registry error code.
|
|
--*/
|
|
|
|
LONG
|
|
GetRegValueString(
|
|
IN HKEY hkeyTopLevel,
|
|
IN LPCTSTR pszSubKey,
|
|
IN LPCTSTR pszValueName,
|
|
OUT LPTSTR pszValueString,
|
|
IN OUT LPDWORD lpdwcb
|
|
)
|
|
{
|
|
TRACEPROC("GetRegValueString", 3)
|
|
LONG rc;
|
|
HKEY hkey;
|
|
|
|
TRACEASSERT(lpdwcb != NULL);
|
|
TRACEASSERT((pszValueString != NULL) || (*lpdwcb == 0));
|
|
TRACEENTER(("(hkTop=%x,SubKey=%s,ValueName=%s,ValueString=%p,Len=%d)\n",
|
|
hkeyTopLevel, pszSubKey, pszValueName, pszValueString,
|
|
*lpdwcb));
|
|
|
|
if (pszValueString != NULL)
|
|
{
|
|
pszValueString[0] = TEXT('\0');
|
|
}
|
|
|
|
rc = RegOpenKeyEx(hkeyTopLevel, pszSubKey, 0, KEY_READ, &hkey);
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwType = 0;
|
|
|
|
rc = RegQueryValueEx(hkey,
|
|
pszValueName,
|
|
0,
|
|
&dwType,
|
|
(LPBYTE)pszValueString,
|
|
lpdwcb);
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
if (dwType != REG_SZ)
|
|
{
|
|
TABSRVERR(("Invalid registry data type (type=%x).\n", dwType));
|
|
rc = ERROR_INVALID_DATATYPE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to read registry value %s\\%s (rc=%x).\n",
|
|
pszSubKey, pszValueName, rc));
|
|
}
|
|
RegCloseKey(hkey);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to open registry key %s (rc=%x).\n",
|
|
pszSubKey, rc));
|
|
}
|
|
|
|
TRACEEXIT(("=%d\n", rc));
|
|
return rc;
|
|
} //GetRegValueString
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func BOOL | SwitchThreadToInputDesktop | Switch thread to current
|
|
input desktop.
|
|
|
|
@parm IN PTSTHREAD | pThread | Points to the thread structure.
|
|
|
|
@rvalue SUCCESS | Returns TRUE.
|
|
@rvalue FAILURE | Returns FALSE.
|
|
--*/
|
|
|
|
BOOL
|
|
SwitchThreadToInputDesktop(
|
|
IN PTSTHREAD pThread
|
|
)
|
|
{
|
|
TRACEPROC("SwitchThreadToInputDesktop", 2)
|
|
BOOL rc = TRUE;
|
|
HDESK hdesk;
|
|
|
|
TRACEENTER(("(pThread=%p)\n", pThread));
|
|
|
|
hdesk = OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED);
|
|
if (hdesk == NULL)
|
|
{
|
|
TRACEWARN(("Failed to open input desktop (err=%d), try Winlogon ...\n",
|
|
GetLastError()));
|
|
|
|
hdesk = OpenDesktop(TEXT("Winlogon"), 0, FALSE, MAXIMUM_ALLOWED);
|
|
if (hdesk == NULL)
|
|
{
|
|
TABSRVERR(("Failed to open winlogon desktop (err=%d).\n",
|
|
GetLastError()));
|
|
rc = FALSE;
|
|
}
|
|
}
|
|
|
|
if (rc == TRUE)
|
|
{
|
|
CloseDesktop(GetThreadDesktop(GetCurrentThreadId()));
|
|
|
|
rc = SetThreadDesktop(hdesk);
|
|
if (rc)
|
|
{
|
|
TCHAR tszDesktop[32];
|
|
DWORD dwcb;
|
|
|
|
if (GetUserObjectInformation(hdesk,
|
|
UOI_NAME,
|
|
tszDesktop,
|
|
sizeof(tszDesktop),
|
|
&dwcb))
|
|
{
|
|
TRACEINFO(1, ("Switch to Input desktop %s.\n", tszDesktop));
|
|
if (lstrcmpi(tszDesktop, TEXT("Winlogon")) == 0)
|
|
{
|
|
pThread->dwfThread |= THREADF_DESKTOP_WINLOGON;
|
|
}
|
|
else
|
|
{
|
|
pThread->dwfThread &= ~THREADF_DESKTOP_WINLOGON;
|
|
}
|
|
|
|
if (lstrcmpi(tszDesktop, gtszInputDesktop) != 0)
|
|
{
|
|
TRACEINFO(1, ("Input desktop name out of sync (old=%s, new=%s).\n",
|
|
gtszInputDesktop, tszDesktop));
|
|
lstrcpyn(gtszInputDesktop,
|
|
tszDesktop,
|
|
ARRAYSIZE(gtszInputDesktop));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to set thread desktop (err=%d)\n",
|
|
GetLastError()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to open input desktop (err=%d)\n", GetLastError()));
|
|
}
|
|
|
|
TRACEEXIT(("=%x\n", rc));
|
|
return rc;
|
|
} //SwitchThreadToInputDesktop
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func BOOL | GetInputDesktopName | Get current input desktop name.
|
|
|
|
@parm OUT LPTSTR | pszDesktopName | Point to a buffer to hold the desktop
|
|
name.
|
|
@parm IN DWORD | dwcbLen | Specifies length of buffer.
|
|
|
|
@rvalue SUCCESS | Returns TRUE.
|
|
@rvalue FAILURE | Returns FALSE.
|
|
--*/
|
|
|
|
BOOL
|
|
GetInputDesktopName(
|
|
OUT LPTSTR pszDesktopName,
|
|
IN DWORD dwcbLen
|
|
)
|
|
{
|
|
TRACEPROC("GetInputDesktopName", 2)
|
|
BOOL rc = FALSE;
|
|
HDESK hdesk;
|
|
|
|
TRACEENTER(("(pszDesktopName=%p,Len=%d)\n", pszDesktopName, dwcbLen));
|
|
|
|
hdesk = OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED);
|
|
if (hdesk == NULL)
|
|
{
|
|
TRACEWARN(("Failed to open input desktop (err=%d), try Winlogon ...\n",
|
|
GetLastError()));
|
|
|
|
hdesk = OpenDesktop(TEXT("Winlogon"), 0, FALSE, MAXIMUM_ALLOWED);
|
|
if (hdesk == NULL)
|
|
{
|
|
TABSRVERR(("Failed to open winlogon desktop (err=%d).\n",
|
|
GetLastError()));
|
|
}
|
|
}
|
|
|
|
if (hdesk != NULL)
|
|
{
|
|
if (GetUserObjectInformation(hdesk,
|
|
UOI_NAME,
|
|
pszDesktopName,
|
|
dwcbLen,
|
|
&dwcbLen))
|
|
{
|
|
rc = TRUE;
|
|
}
|
|
else
|
|
{
|
|
TRACEWARN(("Failed to get desktop name (err=%d).\n",
|
|
GetLastError()));
|
|
}
|
|
CloseDesktop(hdesk);
|
|
}
|
|
else
|
|
{
|
|
TRACEWARN(("failed to open input desktop (err=%d)\n", GetLastError()));
|
|
}
|
|
|
|
TRACEEXIT(("=%x (DeskTopName=%s)\n", rc, pszDesktopName));
|
|
return rc;
|
|
} //GetInputDesktopName
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func VOID | SendAltCtrlDel | Send Alt+Ctrl+Del
|
|
|
|
@parm None.
|
|
|
|
@rvalue SUCCESS | Returns TRUE.
|
|
@rvalue FAILURE | Returns FALSE.
|
|
--*/
|
|
|
|
BOOL
|
|
SendAltCtrlDel(
|
|
VOID
|
|
)
|
|
{
|
|
TRACEPROC("SendAltCtrlDel", 2)
|
|
BOOL rc = FALSE;
|
|
HWINSTA hwinstaSave;
|
|
HDESK hdeskSave;
|
|
|
|
TRACEENTER(("()\n"));
|
|
|
|
hwinstaSave = GetProcessWindowStation();
|
|
hdeskSave = GetThreadDesktop(GetCurrentThreadId());
|
|
|
|
if ((hwinstaSave != NULL) && (hdeskSave != NULL))
|
|
{
|
|
HWINSTA hwinsta;
|
|
HDESK hdesk;
|
|
|
|
hwinsta = OpenWindowStation(TEXT("WinSta0"), FALSE, MAXIMUM_ALLOWED);
|
|
if (hwinsta != NULL)
|
|
{
|
|
SetProcessWindowStation(hwinsta);
|
|
hdesk = OpenDesktop(TEXT("Winlogon"), 0, FALSE, MAXIMUM_ALLOWED);
|
|
if (hdesk != NULL)
|
|
{
|
|
HWND hwndSAS;
|
|
|
|
SetThreadDesktop(hdesk);
|
|
hwndSAS = FindWindow(NULL, TEXT("SAS window"));
|
|
if (hwndSAS != NULL)
|
|
{
|
|
SendMessage(hwndSAS, WM_HOTKEY, 0, 0);
|
|
rc = TRUE;
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to find SAS window (err=%d).\n",
|
|
GetLastError()));
|
|
}
|
|
SetThreadDesktop(hdeskSave);
|
|
CloseDesktop(hdesk);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to open Winlogon (err=%d).\n",
|
|
GetLastError()));
|
|
}
|
|
SetProcessWindowStation(hwinsta);
|
|
CloseWindowStation(hwinsta);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to open WinSta0 (err=%d).\n", GetLastError()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("GetProcessWindowStation or GetThreadDesktop failed (err=%d,hwinsta=%x,hdesk=%x).\n",
|
|
GetLastError(), hwinstaSave, hdeskSave));
|
|
}
|
|
|
|
TRACEEXIT(("=%x\n", rc));
|
|
return rc;
|
|
} //SendAltCtrlDel
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func VOID | NotifyClient | Check if we need to notify anybody.
|
|
|
|
@parm IN EVTNOTIFY | Event | Event to broadcast.
|
|
@parm IN WPARAM | wParam | wParam to send in the message.
|
|
@parm IN LPARAM | lParam | lParam to send in the message.
|
|
|
|
@rvalue None.
|
|
--*/
|
|
|
|
VOID
|
|
NotifyClient(
|
|
IN EVTNOTIFY Event,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
{
|
|
TRACEPROC("NotifyClient", 5)
|
|
|
|
TRACEENTER(("(Event=%x,wParam=%x,lParam=%x)\n", Event, wParam, lParam));
|
|
|
|
if (!IsListEmpty(&glistNotifyClients))
|
|
{
|
|
DWORD rcWait;
|
|
PLIST_ENTRY plist;
|
|
PNOTIFYCLIENT Client;
|
|
|
|
rcWait = WaitForSingleObject(ghmutNotifyList, INFINITE);
|
|
if (rcWait == WAIT_OBJECT_0)
|
|
{
|
|
for (plist = glistNotifyClients.Flink;
|
|
plist != &glistNotifyClients; plist = plist->Flink)
|
|
{
|
|
Client = CONTAINING_RECORD(plist,
|
|
NOTIFYCLIENT,
|
|
list);
|
|
if (Client->Event == Event)
|
|
{
|
|
PostMessage(Client->hwnd,
|
|
Client->uiMsg,
|
|
wParam,
|
|
lParam);
|
|
}
|
|
}
|
|
ReleaseMutex(ghmutNotifyList);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("failed to wait for Notify list mutex (rcWait=%x,err=%d).\n",
|
|
rcWait, GetLastError()));
|
|
}
|
|
}
|
|
|
|
TRACEEXIT(("!\n"));
|
|
return;
|
|
} //NotifyClient
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func BOOL | ImpersonateCurrentUser | Impersonate current logged on user.
|
|
|
|
@parm None.
|
|
|
|
@rvalue SUCCESS | Returns TRUE.
|
|
@rvalue FAILURE | Returns FALSE.
|
|
--*/
|
|
|
|
BOOL
|
|
ImpersonateCurrentUser(
|
|
VOID
|
|
)
|
|
{
|
|
TRACEPROC("ImpersonateCurrentUser", 3)
|
|
BOOL rc = FALSE;
|
|
HANDLE hToken;
|
|
|
|
TRACEENTER(("()\n"));
|
|
|
|
hToken = GetCurrentUserTokenW(L"WinSta0", TOKEN_ALL_ACCESS);
|
|
if (hToken != NULL)
|
|
{
|
|
rc = ImpersonateLoggedOnUser(hToken);
|
|
|
|
if (rc == FALSE)
|
|
{
|
|
TABSRVERR(("Failed to impersonate logged on user (err=%d).\n",
|
|
GetLastError()));
|
|
}
|
|
CloseHandle(hToken);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
|
|
if (dwError != ERROR_NOT_LOGGED_ON)
|
|
{
|
|
TABSRVERR(("Failed to get current user token (err=%d)\n",
|
|
dwError));
|
|
}
|
|
}
|
|
|
|
TRACEEXIT(("=%x\n", rc));
|
|
return rc;
|
|
} //ImpersonateCurrentUser
|
|
|
|
/*++
|
|
@doc INTERNAL
|
|
|
|
@func BOOL | RunProcessAsUser | Run a process as the logged on user.
|
|
|
|
@parm IN LPTSTR | pszCmd | Process command.
|
|
|
|
@rvalue SUCCESS | Returns TRUE.
|
|
@rvalue FAILURE | Returns FALSE.
|
|
--*/
|
|
|
|
BOOL
|
|
RunProcessAsUser(
|
|
IN LPTSTR pszCmd
|
|
)
|
|
{
|
|
TRACEPROC("RunProcessAsUser", 3)
|
|
BOOL rc = FALSE;
|
|
HANDLE hImpersonateToken;
|
|
HANDLE hPrimaryToken;
|
|
|
|
TRACEENTER(("(Cmd=%s)\n", pszCmd));
|
|
|
|
if (OpenThreadToken(GetCurrentThread(),
|
|
TOKEN_QUERY |
|
|
TOKEN_DUPLICATE |
|
|
TOKEN_ASSIGN_PRIMARY,
|
|
TRUE,
|
|
&hImpersonateToken))
|
|
{
|
|
if (DuplicateTokenEx(hImpersonateToken,
|
|
TOKEN_IMPERSONATE |
|
|
TOKEN_READ |
|
|
TOKEN_ASSIGN_PRIMARY |
|
|
TOKEN_DUPLICATE |
|
|
TOKEN_ADJUST_PRIVILEGES,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenPrimary,
|
|
&hPrimaryToken))
|
|
{
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
LPVOID lpEnv = NULL;
|
|
|
|
if (!CreateEnvironmentBlock(&lpEnv, hImpersonateToken, TRUE))
|
|
{
|
|
TABSRVERR(("Failed to create environment block (err=%d).\n",
|
|
GetLastError()));
|
|
lpEnv = NULL;
|
|
}
|
|
|
|
memset(&si, 0, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
si.lpDesktop = TEXT("WinSta0\\Default");
|
|
|
|
if (CreateProcessAsUser(hPrimaryToken,
|
|
NULL,
|
|
pszCmd,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
NORMAL_PRIORITY_CLASS |
|
|
CREATE_UNICODE_ENVIRONMENT,
|
|
lpEnv,
|
|
NULL,
|
|
&si,
|
|
&pi))
|
|
{
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
rc = TRUE;
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to create process as user (err=%d).\n",
|
|
GetLastError()));
|
|
}
|
|
if (lpEnv != NULL)
|
|
{
|
|
DestroyEnvironmentBlock(lpEnv);
|
|
}
|
|
CloseHandle(hPrimaryToken);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to duplicate token (err=%d).\n",
|
|
GetLastError()));
|
|
}
|
|
CloseHandle(hImpersonateToken);
|
|
}
|
|
else
|
|
{
|
|
TABSRVERR(("Failed to open thread token (err=%d).\n",
|
|
GetLastError()));
|
|
}
|
|
|
|
TRACEEXIT(("=%x\n", rc));
|
|
return rc;
|
|
} //RunProcessAsUser
|
|
|
|
/*++
|
|
@doc EXTERNAL
|
|
|
|
@func void __RPC_FAR * | MIDL_user_allocate | MIDL allocate.
|
|
|
|
@parm IN size_t | len | size of allocation.
|
|
|
|
@rvalue SUCCESS | Returns the pointer to the memory allocated.
|
|
@rvalue FAILURE | Returns NULL.
|
|
--*/
|
|
|
|
void __RPC_FAR * __RPC_USER
|
|
MIDL_user_allocate(
|
|
IN size_t len
|
|
)
|
|
{
|
|
TRACEPROC("MIDL_user_allocate", 5)
|
|
void __RPC_FAR *ptr;
|
|
|
|
TRACEENTER(("(len=%d)\n", len));
|
|
|
|
ptr = malloc(len);
|
|
|
|
TRACEEXIT(("=%p\n", ptr));
|
|
return ptr;
|
|
} //MIDL_user_allocate
|
|
|
|
/*++
|
|
@doc EXTERNAL
|
|
|
|
@func void | MIDL_user_free | MIDL free.
|
|
|
|
@parm IN void __PRC_FAR * | ptr | Points to the memory to be freed.
|
|
|
|
@rvalue None.
|
|
--*/
|
|
|
|
void __RPC_USER
|
|
MIDL_user_free(
|
|
IN void __RPC_FAR *ptr
|
|
)
|
|
{
|
|
TRACEPROC("MIDL_user_free", 5)
|
|
|
|
TRACEENTER(("(ptr=%p)\n", ptr));
|
|
|
|
free(ptr);
|
|
|
|
TRACEEXIT(("!\n"));
|
|
return;
|
|
} //MIDL_user_free
|