407 lines
12 KiB
C++
407 lines
12 KiB
C++
#include "shsrvice.h"
|
|
|
|
#include "mischlpr.h"
|
|
|
|
#include "dbg.h"
|
|
#include "tfids.h"
|
|
|
|
#define ARRAYSIZE(a) (sizeof((a))/sizeof((a)[0]))
|
|
|
|
#define SC_PAUSE 0
|
|
#define SC_CONTINUE 1
|
|
#define SC_STOP 2
|
|
#define SC_SHUTDOWN 3
|
|
#define SC_INTERROGATE 4
|
|
|
|
DWORD rgdwControlCodes[] =
|
|
{
|
|
SERVICE_CONTROL_PAUSE, // 0x00000002
|
|
SERVICE_CONTROL_CONTINUE, // 0x00000003
|
|
SERVICE_CONTROL_STOP, // 0x00000001
|
|
SERVICE_CONTROL_SHUTDOWN, // 0x00000005
|
|
SERVICE_CONTROL_INTERROGATE, // 0x00000004
|
|
};
|
|
|
|
static SERVICEENTRY* g_pseWantsDeviceEvents = NULL;
|
|
static SERVICEENTRY** g_ppse = NULL;
|
|
static HWND g_hwnd = NULL;
|
|
static HANDLE* g_phEvent = NULL;
|
|
|
|
LRESULT _FakeWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
DWORD WINAPI _ServiceMainCaller(PVOID pvParam)
|
|
{
|
|
LPWSTR* ppsz = (LPWSTR*)pvParam;
|
|
|
|
TRACE(TF_SERVICEASPROCESS, TEXT("%s: _ServiceMainCaller called"),
|
|
(LPWSTR)*ppsz);
|
|
|
|
CGenericServiceManager::_ServiceMain(1, (LPWSTR*)pvParam);
|
|
|
|
return 0;
|
|
}
|
|
|
|
DWORD _ServiceIndexFromServiceName(SERVICE_TABLE_ENTRY* pste,
|
|
LPCWSTR pszServiceName)
|
|
{
|
|
DWORD dw = 0;
|
|
|
|
while (pste[dw].lpServiceName)
|
|
{
|
|
if (!lstrcmp(pste[dw].lpServiceName, pszServiceName))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
++dw;
|
|
}
|
|
}
|
|
|
|
return dw;
|
|
}
|
|
|
|
//static
|
|
HRESULT CGenericServiceManager::_RegisterServiceCtrlHandler(
|
|
LPCWSTR pszServiceName, SERVICEENTRY* pse)
|
|
{
|
|
ASSERT(pse);
|
|
|
|
TRACE(TF_SERVICEASPROCESS, TEXT("%s: _RegisterServiceCtrlHandler called"),
|
|
pszServiceName);
|
|
|
|
g_ppse[_ServiceIndexFromServiceName(_rgste, pszServiceName)] = pse;
|
|
|
|
#ifdef DEBUG
|
|
lstrcpy(pse->_szServiceName, pszServiceName);
|
|
#endif
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// static
|
|
HRESULT CGenericServiceManager::_HandleWantsDeviceEvents(
|
|
LPCWSTR pszServiceName, BOOL UNREF_PARAM(fWantsDeviceEvents))
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
DWORD dwWait;
|
|
DWORD dwService = _ServiceIndexFromServiceName(_rgste, pszServiceName);
|
|
|
|
TRACE(TF_SERVICEASPROCESS, TEXT("%s: _HandleWantsDeviceEvents (1)"),
|
|
pszServiceName);
|
|
|
|
// We need to release the main service thread, so that it can create
|
|
// the fake wnd that will receive the WM_DEVICECHANGE msgs to fake
|
|
// the SERVICE_CONTROL_DEVICEEVENT.
|
|
SetEvent(g_phEvent[dwService]);
|
|
|
|
TRACE(TF_SERVICEASPROCESS, TEXT("%s: _HandleWantsDeviceEvents (2)"),
|
|
pszServiceName);
|
|
// Let's relinquish our remaining time slice. Or else we won't wait on
|
|
// the following line.
|
|
// That's not fool proof (might not work) but this is test code...
|
|
Sleep(0);
|
|
|
|
TRACE(TF_SERVICEASPROCESS, TEXT("%s: _HandleWantsDeviceEvents (3)"),
|
|
pszServiceName);
|
|
// We wait until the main service thread is done with the window creation.
|
|
// Then g_hwnd wil lbe set and we'll be able to use it to register for
|
|
// notif.
|
|
dwWait = WaitForSingleObject(g_phEvent[dwService], INFINITE);
|
|
|
|
TRACE(TF_SERVICEASPROCESS, TEXT("%s: _HandleWantsDeviceEvents (4)"),
|
|
pszServiceName);
|
|
if (WAIT_OBJECT_0 == dwWait)
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
|
|
CloseHandle(g_phEvent[dwService]);
|
|
|
|
g_phEvent[dwService] = NULL;
|
|
|
|
return hres;
|
|
}
|
|
|
|
// static
|
|
HRESULT CGenericServiceManager::StartServiceCtrlDispatcher()
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
HANDLE* phEvents;
|
|
|
|
g_ppse = (SERVICEENTRY**)LocalAlloc(LPTR, sizeof(SERVICEENTRY*) * _cste);
|
|
|
|
phEvents = (HANDLE*)LocalAlloc(LPTR, sizeof(HANDLE) * _cste * 5);
|
|
|
|
g_phEvent = (HANDLE*)LocalAlloc(LPTR, sizeof(HANDLE) * _cste);
|
|
|
|
if (g_ppse && phEvents && g_phEvent)
|
|
{
|
|
for (DWORD dwService = 0; dwService < _cste; ++dwService)
|
|
{
|
|
WCHAR szEventName[256];
|
|
LPWSTR pszServiceName = _rgste[dwService].lpServiceName;
|
|
|
|
hres = S_OK;
|
|
|
|
lstrcpy(szEventName, _rgste[dwService].lpServiceName);
|
|
lstrcat(szEventName, TEXT(".SC_PAUSE"));
|
|
|
|
phEvents[dwService * 5 + SC_PAUSE] = CreateEvent(NULL, TRUE,
|
|
FALSE, szEventName);
|
|
|
|
lstrcpy(szEventName, _rgste[dwService].lpServiceName);
|
|
lstrcat(szEventName, TEXT(".SC_CONTINUE"));
|
|
|
|
phEvents[dwService * 5 + SC_CONTINUE] = CreateEvent(NULL, TRUE,
|
|
FALSE, szEventName);
|
|
|
|
lstrcpy(szEventName, _rgste[dwService].lpServiceName);
|
|
lstrcat(szEventName, TEXT(".SC_STOP"));
|
|
|
|
phEvents[dwService * 5 + SC_STOP] = CreateEvent(NULL, TRUE,
|
|
FALSE, szEventName);
|
|
|
|
lstrcpy(szEventName, _rgste[dwService].lpServiceName);
|
|
lstrcat(szEventName, TEXT(".SC_SHUTDOWN"));
|
|
|
|
phEvents[dwService * 5 + SC_SHUTDOWN] = CreateEvent(NULL, TRUE,
|
|
FALSE, szEventName);
|
|
|
|
lstrcpy(szEventName, _rgste[dwService].lpServiceName);
|
|
lstrcat(szEventName, TEXT(".SC_INTERROGATE"));
|
|
|
|
phEvents[dwService * 5 + SC_INTERROGATE] = CreateEvent(NULL, TRUE,
|
|
FALSE, szEventName);
|
|
|
|
for (DWORD dwEvent = SC_PAUSE; SUCCEEDED(hres) &&
|
|
(dwEvent <= SC_INTERROGATE); ++dwEvent)
|
|
{
|
|
if (!phEvents[(dwService * 5) + dwEvent])
|
|
{
|
|
hres = E_FAIL;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
g_phEvent[dwService] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (g_phEvent[dwService])
|
|
{
|
|
CreateThread(NULL, 0, _ServiceMainCaller,
|
|
(LPWSTR*)&(_rgste[dwService].lpServiceName), 0, NULL);
|
|
|
|
// We have to wait for the IService impl to be CoCreated and
|
|
// queried for fWantsDeviceEvents. So we block here.
|
|
// _HandleWantsDeviceEvents will unblock us when
|
|
// fWantsDeviceEvents will be known.
|
|
TRACE(TF_SERVICEASPROCESS,
|
|
TEXT("%s: StartServiceCtrlDispatcher (1)"),
|
|
pszServiceName);
|
|
|
|
DWORD dwWait = WaitForSingleObject(g_phEvent[dwService],
|
|
INFINITE);
|
|
|
|
TRACE(TF_SERVICEASPROCESS,
|
|
TEXT("%s: StartServiceCtrlDispatcher (2)"),
|
|
pszServiceName);
|
|
|
|
if (WAIT_OBJECT_0 == dwWait)
|
|
{
|
|
if (g_ppse[dwService]->_fWantsDeviceEvents)
|
|
{
|
|
WNDCLASSEX wndclass;
|
|
HINSTANCE hinst = GetModuleHandle(NULL);
|
|
|
|
g_pseWantsDeviceEvents = g_ppse[dwService];
|
|
|
|
if (hinst)
|
|
{
|
|
wndclass.cbSize = sizeof(wndclass);
|
|
wndclass.style = NULL;
|
|
wndclass.lpfnWndProc = _FakeWndProc;
|
|
wndclass.cbClsExtra = 0;
|
|
wndclass.cbWndExtra = 0;
|
|
wndclass.hInstance = hinst;
|
|
wndclass.hIcon = NULL;
|
|
wndclass.hCursor = NULL;
|
|
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
wndclass.lpszMenuName = NULL;
|
|
wndclass.lpszClassName = TEXT("FakeWnd");
|
|
wndclass.hIconSm = NULL;
|
|
|
|
if (RegisterClassEx(&wndclass))
|
|
{
|
|
g_hwnd = CreateWindow(TEXT("FakeWnd"), TEXT("FakeWnd"),
|
|
WS_POPUPWINDOW, 0, 0, 100, 200, NULL, NULL, hinst, NULL);
|
|
|
|
if (g_hwnd)
|
|
{
|
|
g_pseWantsDeviceEvents->_ssh =
|
|
(SERVICE_STATUS_HANDLE)g_hwnd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We're done with the window creation, release the IService
|
|
// impl thread.
|
|
SetEvent(g_phEvent[dwService]);
|
|
}
|
|
}
|
|
}
|
|
TRACE(TF_SERVICEASPROCESS,
|
|
TEXT("%s: StartServiceCtrlDispatcher (3)"),
|
|
pszServiceName);
|
|
}
|
|
|
|
while (SUCCEEDED(hres))
|
|
{
|
|
DWORD dw = MsgWaitForMultipleObjects(_cste * 5,
|
|
phEvents, FALSE, INFINITE, QS_ALLINPUT);
|
|
|
|
if (WAIT_FAILED != dw)
|
|
{
|
|
if ((_cste * 5) == (dw - WAIT_OBJECT_0))
|
|
{
|
|
MSG msg;
|
|
|
|
if (GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
if (WM_DEVICECHANGE != msg.message)
|
|
{
|
|
DispatchMessage(&msg);
|
|
}
|
|
else
|
|
{
|
|
// To mimic the true service behavior, deliver
|
|
// these messages only if the "service" is
|
|
// running
|
|
if (SERVICE_RUNNING ==
|
|
g_pseWantsDeviceEvents->_servicestatus.dwCurrentState)
|
|
{
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DWORD dwService2 = ((dw - WAIT_OBJECT_0 + 1) / 5);
|
|
|
|
if (NO_ERROR != _ServiceHandler(
|
|
rgdwControlCodes[(dw - WAIT_OBJECT_0) -
|
|
(dwService2 * 5)], 0, NULL, (PVOID)g_ppse[dwService2]))
|
|
{
|
|
hres = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
BOOL CGenericServiceManager::_SetServiceStatus(SERVICEENTRY* pse)
|
|
{
|
|
WCHAR sz[256];
|
|
|
|
lstrcpy(sz, pse->_szServiceName);
|
|
|
|
switch (pse->_servicestatus.dwCurrentState)
|
|
{
|
|
case SERVICE_STOPPED:
|
|
lstrcat(sz, TEXT(": SERVICE_STOPPED"));
|
|
break;
|
|
case SERVICE_START_PENDING:
|
|
lstrcat(sz, TEXT(": SERVICE_START_PENDING"));
|
|
break;
|
|
case SERVICE_STOP_PENDING:
|
|
lstrcat(sz, TEXT(": SERVICE_STOP_PENDING"));
|
|
break;
|
|
case SERVICE_RUNNING:
|
|
lstrcat(sz, TEXT(": SERVICE_RUNNING"));
|
|
break;
|
|
case SERVICE_CONTINUE_PENDING:
|
|
lstrcat(sz, TEXT(": SERVICE_CONTINUE_PENDING"));
|
|
break;
|
|
case SERVICE_PAUSE_PENDING:
|
|
lstrcat(sz, TEXT(": SERVICE_PAUSE_PENDING"));
|
|
break;
|
|
case SERVICE_PAUSED:
|
|
lstrcat(sz, TEXT(": SERVICE_PAUSED"));
|
|
break;
|
|
default:
|
|
lstrcat(sz, TEXT(": Unknown state"));
|
|
break;
|
|
}
|
|
|
|
TRACE(TF_SERVICE, sz);
|
|
|
|
return TRUE;
|
|
}
|
|
#else
|
|
BOOL CGenericServiceManager::_SetServiceStatus(SERVICEENTRY*)
|
|
{
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Wnd stuff
|
|
LRESULT _FakeWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT lRes = 0;
|
|
BOOL fProcessed = FALSE;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_DEVICECHANGE:
|
|
{
|
|
fProcessed = TRUE;
|
|
|
|
lRes = CGenericServiceManager::_ServiceHandler(
|
|
SERVICE_CONTROL_DEVICEEVENT, (DWORD)wParam, (PVOID)lParam,
|
|
(PVOID)g_pseWantsDeviceEvents);
|
|
|
|
if ((NO_ERROR != lRes) && (TRUE != lRes))
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case WM_DESTROY:
|
|
|
|
// Should cleanup here
|
|
|
|
fProcessed = FALSE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fProcessed = FALSE;
|
|
break;
|
|
|
|
}
|
|
|
|
if (!fProcessed)
|
|
{
|
|
lRes = DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
return lRes;
|
|
}
|