How do I detect that my application is running as service or in an interactive session?
I'm writing an application that is able to run as a service or s开发者_JAVA技巧tandalone but I want to detect if the application was executed as a service or in a normal user session.
If this is a C++ application, somewhere in your startup code you have to call StartServiceCtrlDispatcher. If it fails and GetLastError()
returns ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
, the app has not been started as a service.
Another option would be to use System.Environment.UserInteractive http://msdn.microsoft.com/en-us/library/system.environment.userinteractive.aspx
Update: To make up for posting a .NET answer to a C++ topic, I provide a C implementation based on the .NET implementation.
BOOL IsUserInteractive()
{
BOOL bIsUserInteractive = TRUE;
HWINSTA hWinStation = GetProcessWindowStation();
if (hWinStation != NULL)
{
USEROBJECTFLAGS uof = {0};
if (GetUserObjectInformation(hWinStation, UOI_FLAGS, &uof, sizeof(USEROBJECTFLAGS), NULL) && ((uof.dwFlags & WSF_VISIBLE) == 0))
{
bIsUserInteractive = FALSE;
}
}
return bIsUserInteractive;
}
I think you can query the process token for membership in the Interactive group.
From http://support.microsoft.com/kb/243330:
SID: S-1-5-4
Name: Interactive
Description: A group that includes all users that have logged on interactively. Membership is controlled by the operating system.
Call GetTokenInformation with TokenGroups to get the groups associated with the account under which the process is running, then iterate over the sids looking for the Interactive sid.
I found a nice chunk of code at http://marc.info/?l=openssl-dev&m=104401851331452&w=2
I think you can base your detection on the fact that services are running with SessionID 0 and user accounts do have other values (like 1).
bServiceMode = false;
SessionID=-1;
Size=0;
hToken = NULL;
(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
GetLastError();
if (!GetTokenInformation(hToken, TokenSessionId, &SessionID, sizeof(SessionID), &Size) || !Size)
return FALSE;
if(SessionID==0)
bServiceMode = true;
All of the above methods are unreliable. Session Id is not necessarily 0 (at least not in previous Windows versions), Window Station is only WinSta0 if "If the service is running in the LocalSystem account and is interacting with the desktop". See KB171890 for more details.
One method for detecting if a process is running as service is following:
Please note: Only services installed in services database will be detected with this method, but not child processes started by a service process that are not registered in the database. In this case, it would not also be a system service. *1.
bool IsRunningAsService(unsigned int Pid) {
bool Result = false;
SC_HANDLE hScm = OpenSCManager(
0,
SERVICES_ACTIVE_DATABASE,
SC_MANAGER_ENUMERATE_SERVICE
);
if (hScm == 0) {
return Result;
}
DWORD ServicesBufferRequired = 0;
DWORD ResumeHandle = 0;
DWORD ServicesBufferSize = 0;
DWORD ServicesCount = 0;
ENUM_SERVICE_STATUS_PROCESS* ServicesBuffer = 0;
EnumServicesStatusEx(hScm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32,
SERVICE_ACTIVE, 0, 0, &ServicesBufferRequired, &ServicesCount, &ResumeHandle, 0);
// Todo: Error handling (GetLastError() results are currently bogus?)
ServicesBuffer = (ENUM_SERVICE_STATUS_PROCESS*) new
char[ServicesBufferRequired];
ServicesBufferSize = ServicesBufferRequired;
EnumServicesStatusEx(hScm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32,
SERVICE_ACTIVE, (LPBYTE) ServicesBuffer, ServicesBufferSize,
&ServicesBufferRequired, &ServicesCount, &ResumeHandle, 0);
ENUM_SERVICE_STATUS_PROCESS* ServicesBufferPtr = ServicesBuffer;
while (ServicesCount--) {
if (ServicesBufferPtr->ServiceStatusProcess.dwProcessId == Pid) {
Result = true;
break;
}
ServicesBufferPtr++;
}
delete [] ServicesBuffer;
CloseServiceHandle(hScm);
return Result;
}
Please note, the code above should contain additional error handling, especially it should be called in a loop until EnumServicesStatusEx returns nonzero. But unfortunetaly as I found out, GetLastError()
always returns 1 (ERROR_INVALID_FUNCTION) even if the buffer is correctly filled with data.
*1: Testing if a process was started by a service: In this case you could use a combination of other solutions. One could test, if the process has a parent (grandparent...) process that is a registered as a service. You could use CreateToolhelp32Snapshot
API for this purpose. However if the parent process is already killed, things getting difficult. I'm sure there are any undocumented settings which can determine whether a process is running as a service apart from the usual suspects like SessionId = 0, WindowStation = 0, WSF_VISIBLE, No Interactive Group membership...
There is a simple way to detect whether the application is started as a service. When you create a service with CreateService, pass in lpBinaryPathName parameter some additional argument, say -s which would indicate that your application is started as a service. Then in the application you can check for this argument. It can also possibly help when debugging, because you can test your service functionality without actually running as a service. If StartServiceCtrlDispatcher fails with ERROR_FAILED_SERVICE_CONTROLLER_CONNECT, you can set a flag indicating the program is running as a console application simulating a service mode, so you can skip service related API calls using this flag.
Process in normal user session always has a window station called WinSta0.
wchar_t buffer[256] = {0};
DWORD length = 0;
GetUserObjectInformation(GetProcessWindowStation(), UOI_NAME, buffer, 256, &length);
if (!lstricmp(buffer, "WinSta0")) {
// normal user session
} else {
// service session
}
精彩评论