Форум: "Прочее";
Текущий архив: 2016.05.08;
Скачать: [xml.tar.bz2];
ВнизКак понять, что приложение запустили в виде сервиса? Найти похожие ветки
← →
Masterucs © (2015-08-25 13:58) [0]Идея написать универсальное приложение, которое может работать и как сервис, и как "обычный" exe запущенный из проводника.
Соответственно, в DPR нужно определить как нас запустили - в виде сервиса или обычного приложения. И если в виде сервиса - то создать форму от TService, если нет - то создать обычную главную форму.
Есть инсайд от проверенных товарищей, что способ WinSta0 уже не валиден для новых ОС, также не валиден способ, который предполагает, что у всех сервисов SessionId = 0.
Да, можно запуск сервиса всегда делать с ключом и по этому ключу понимать, что тебя запустили как сервис. Но это способ явно обходной, должна же быть возможность "нативно" определить как тебя запустили - в виде сервиса или как стандартное приложение...
← →
Юрий Зотов © (2015-08-25 14:24) [1]По ключу проще. И, кроме /service, часто делаются ключи /install и /uninstall. Поэтому ключ - это путь не обходной, а вполне нормальный.
← →
Masterucs © (2015-08-25 14:28) [2]
> По ключу проще.
Каждая задача имеет простое, очевидное для всех, неправильное решение (c)
> Поэтому ключ - это путь не обходной, а вполне нормальный.
таки обходной. Потому что никто не мешает запустить напрямую EXE"шник с этим ключом, а также вручную поставить как сервис и "забыть" ключ указать, а потом долго думать почему сервис не стартует.
Должен же быть способ понять, что нас как сервис запускают...
← →
DVM © (2015-08-25 14:40) [3]
> Masterucs ©
Спросить у Service Control Managera состояние службы с указанным именем, если оно SERVICE_START_PENDING то приложение запускается в виде службы (или SERVICE_RUNNING если запущено).
Но это при условии, что процесс не может одновременно запускаться и как служба и не как служба, т.е. не должно быть двух экземляров одновременно.
← →
DVM © (2015-08-25 14:41) [4]QueryServiceStatus()
← →
Masterucs © (2015-08-25 14:41) [5]теоретически можно дернуть "StartServiceCtrlDispatcher" и она вернет false в пользовательском режиме (что только подтверждает, что сама windows знает в каком окружении запускается EXE).
Но решение не подходит, потому что если это действительно сервис, то после моего "ручного" вызова StartServiceCtrlDispatcher очень непонятно как всё это на TService переключить...
← →
Masterucs © (2015-08-25 14:48) [6]
> Спросить у Service Control Managera состояние службы с указанным
> именем, если оно SERVICE_START_PENDING то приложение запускается
> в виде службы (или SERVICE_RUNNING если запущено).
при этом нет никакой гарантии, что запускаемый / запущенный сервис - это ты :)
Можно немного переиначить задачу. Допустим, стоит запрет на повторный запуск приложения (неважно сервис или нет) через глобальный мьютекс. Так вот если мы сервис - то просто не стартовать, а если мы пользовательское приложение - то выдать на экран соответствующее предупреждение.
← →
DVM © (2015-08-25 14:57) [7]
> Masterucs © (25.08.15 14:48) [6]
> при этом нет никакой гарантии, что запускаемый / запущенный
> сервис - это ты :)
Гарантии нет, я об этом и сказал. Но и другого способа нет.
← →
Rouse_ © (2015-08-25 15:17) [8]Ну получи PID процесса который тебя запустил, вытащи его путь, если это services.exe - значит тебя хочет SCM.
А вообще сколько лет пишу сервисы - параметр командной строки бронебойный вариант, ни разу не подводил.
← →
Masterucs © (2015-08-25 15:34) [9]
> если это services.exe
ну а потом они возьмут и переименуют... или локализуют...
← →
NoUser © (2015-08-25 16:06) [10]>Masterucs © (25.08.15 14:41) [5]
направление правильное, а чтобы не думать как на TService переключить
пишешь API-ручками и всё хорошо.
Удачный вызов StartServiceCtrlDispatcher из сервиса "закончится" вместе с сервисом.
← →
ухты © (2015-08-25 16:13) [11]сделать 2 "стартующий" модуля, не вариант?
← →
sniknik © (2015-08-25 17:11) [12]> запуск сервиса всегда делать с ключом и по этому ключу понимать, что тебя запустили как сервис.
не, без ключей(+ /install /uninstall) - сервис, а с параметром /programma - программа.
самый простой и понятный способ.
при слишком "умной" программе люди могут даже не заподозрить, что программа может быть сервисом... запускать как получается, и где-то так через полгода "вылезти" с предложением "сделать ее сервисом т.к. нужно чтобы и на изначально при старте залоченной машине без юзера работала".
← →
DVM © (2015-08-25 17:20) [13]У меня много лет уже эксплуатируются подобные программы, которые могут запускаться как сервис, консольное приложение и безо всяких ключей в командной строке.
Запускаем двойным кликом - консольное приложение, через SCM - сервис.
Только дельфийский TServiceApplication и TService не подходит в чистом виде, нужно модифицировать его.
← →
кгшзх © (2015-08-25 17:30) [14]есть же куча неявных признаков сервис ты или нет.
юзернэйм, карентфолдер .....
← →
Rouse_ © (2015-08-25 19:02) [15]Миш, давай попробуем с азов.
Ты пишешь приложение, которое должно запускаться либо как приложение, либо как сервис - не наказуемо.
Ты спрашиваешь совета: как определить что сейчас меня стартуют именно как сервис?
Люди (посмотри список выше) тебе говорят - через командную строку, на что ты говоришь что это не правильно.
Давай рассмотрим твои возражения:
> Потому что никто не мешает запустить напрямую EXE"шник с
> этим ключом
Отлично, ну запустили его и что? Он внезапно стал сервисом?
Отвечаю: читай MSDN - получишь ошибку при старте.
Смотрим вариант два: приложение сервис без ключа - и при старте опять получим ошибку.
Почему?
Потому что: http://www.gunsmoker.ru/2008/10/x-y-z.html
Ну а с переименуют - ты сделал мой вечер :)
← →
DayGaykin © (2015-08-25 19:06) [16]
> Rouse_ © (25.08.15 19:02) [15]
А если всегда запускаться как сервис, а если получаем при этом ошибку - то запускаться как приложение?
← →
Rouse_ © (2015-08-25 19:11) [17]
> DayGaykin © (25.08.15 19:06) [16]
> А если всегда запускаться как сервис, а если получаем при
> этом ошибку - то запускаться как приложение?
Дим, ну голову то включи. Ошибка при инициализации - мошт еще и как драйвер себя заинсталируем при ошибку (ну а что, а вдруг)?
← →
ухты © (2015-08-25 19:28) [18]А надо ли в сервис совать формы и наоборот? ну да можно, будет балласт
В чем выгода, никак не пойму. Работы столько же + геморрой :)
← →
Masterucs © (2015-08-25 20:56) [19]Rouse_, а зачем освобождать глобальный мьютекс? И зачем по большому счету секции finalization в юнитах? И так далее.
Ведь при завершении процесса Винда скорее всего освободит все ресурсы?
Но почему хороший программист напишет код, который сам все освобождает?
Все таки логика а-ля Работает - и хорошо, не всегда верная.
← →
Eraser © (2015-08-25 22:03) [20]вот такой код используется уже много лет, вроде бы проблем не было )
function GetStartingAsService(const SrvName: string): Boolean;
var
Mgr, Svc: Integer;
SSBuf: PServiceStatus;
SSBufSz: Cardinal;
SvcState: Cardinal;
begin
Result := False;
// То что мы сервис - еще не значит что мы запущены с правами SYSTEM!
// Поэтому запрашиваем только право чтения.
Mgr := OpenSCManager(nil, nil, GENERIC_READ);
if (Mgr <> 0) then
begin
// Аналогично. Будем только читать.
Svc := OpenService(Mgr, PChar(SrvName), GENERIC_READ);
Result := (Svc <> 0);
if Result then
begin
JwaWinSvc.QueryServiceStatusEx(Svc, JwaWinSvc.SC_STATUS_PROCESS_INFO, nil, 0, SSBufSz);
GetMem(SSBuf, SSBufSz);
JwaWinSvc.QueryServiceStatusEx(Svc, JwaWinSvc.SC_STATUS_PROCESS_INFO, PByte(SSBuf), SSBufSz,
SSbufSz);
SvcState := SSBuf.dwCurrentState;
FreeMem(SSBuf);
Result :=
(SvcState = SERVICE_START_PENDING) and
AnsiSameText(GetProcessParentName, "services.exe");
CloseServiceHandle(Svc);
end;
CloseServiceHandle(Mgr);
end;
end;
← →
Кто б сомневался © (2015-08-25 22:06) [21]
> Идея написать универсальное приложение, которое может работать
> и как сервис, и как "обычный" exe запущенный из проводника.
>
Чет не пойму, а нафига это вообще делать? Где это может понадобиться?
← →
DVM © (2015-08-25 22:14) [22]Ну мне лично удобно в процессе тестирования и отладки пользоваться консольным вариантом программы которая в обычных условиях должна работать как сервис. И лог в консоли сразу видно и отлаживать проще. Но только консоль, никаких окон.
← →
кгшзх © (2015-08-25 22:31) [23]в таких случаях кажется проще выносить полезный код в отдельный юнит и иметь два дпр.
← →
Rouse_ © (2015-08-25 22:36) [24]
> Masterucs © (25.08.15 20:56) [19]
> Все таки логика а-ля Работает - и хорошо, не всегда верная.
Ты странный :)
У всех работает, а у тебя не всегда правильно - может стоит пересмотреть концепцию? :)
← →
Masterucs © (2015-08-25 23:41) [25]>У всех работает
У меня сейчас тоже работает. Но это не означает, что у всех работает как у тебя
← →
Masterucs © (2015-08-26 10:32) [26]
> (SvcState = SERVICE_START_PENDING) and
тоже оставил такой вариант. Мы стартуем и при этом сервис в состоянии старта - наверное, это мы и есть. Но все таки как-то опосредственно.
В services.exe я действительно не очень верю. Что стоит переделать этот механизм.
← →
sniknik © (2015-08-26 11:50) [27]> вот такой код используется уже много лет, вроде бы проблем не было )
вот такой код закоментарен уже много лет, может даже и рабочий. не понадобился, хотя идеи возникали{//проверка родительского процесса, может понадобится проверять, что запуск сервис менеджером
uses
Windows,
PsAPI,
type
PROCESS_BASIC_INFORMATION = packed record
ExitStatus: DWORD;
PebBaseAddress: Pointer;
AffinityMask: DWORD;
BasePriority: DWORD;
uUniqueProcessId: Ulong;
uInheritedFromUniqueProcessId: Ulong;
end;
function NtQueryInformationProcess(
ProcessHandle: THandle; ProcessInformationClass: Byte; ProcessInformation: Pointer;
ProcessInformationLength: ULONG; ReturnLength: PULONG): DWORD; stdcall; external "ntdll.dll";
function ParentProcName: string;
const
ProcessBasicInformation = 0;
var
Info: PROCESS_BASIC_INFORMATION;
dwProcessHandle: dword;
ProcessName: string;
Hndl: THandle;
begin
Result:= "noname";
dwProcessHandle:= GetCurrentProcess;
if NtQueryInformationProcess(dwProcessHandle, ProcessBasicInformation, @Info, SizeOf(Info), nil) = NO_ERROR
then begin
Hndl:= OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, Info.uInheritedFromUniqueProcessId);
if Hndl <> 0 then
try
SetLength(ProcessName, MAX_PATH);
if GetModuleBaseNameA(Hndl, 0, PChar(ProcessName), MAX_PATH) > 0
then Result:= PChar(ProcessName);
finally
CloseHandle(Hndl);
end;
end;
end;
function ParentProcName: string;
var
i: integer;
ProcessID, ParentProcessID: DWORD;
hSnapshot: THandle;
ProcessEntry: TProcessEntry32;
ProcessList: TStringList;
begin
result:= "noname";
ProcessList:= TStringList.Create;
try
ProcessID:= GetCurrentProcessID;
ParentProcessID:= 0;
hSnapshot:= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if hSnapshot <> INVALID_HANDLE_VALUE then try
ProcessEntry.dwSize:= SizeOf(ProcessEntry);
if Process32First(hSnapshot, ProcessEntry) then begin
repeat
ProcessList.AddObject(ProcessEntry.szExeFile, TObject(ProcessEntry.th32ProcessID));
if ProcessEntry.th32ProcessID = ProcessID then
ParentProcessID:= ProcessEntry.th32ParentProcessID;
until not Process32Next(hSnapshot, ProcessEntry)
end
finally
CloseHandle(hSnapshot)
end;
if ParentProcessID <> 0 then begin
i:= ProcessList.IndexOfObject(TObject(ParentProcessID));
if i <> -1 then
result:= ProcessList.Strings[i]
end;
finally
ProcessList.Free
end;
end;
function IsStartAsService: boolean;
begin
Result:= AnsiSameText(ParentProcName(), "services.exe");
end;
}
> Чет не пойму, а нафига это вообще делать? Где это может понадобиться?
у меня сервер в трехзвенке сервисом сделан, в установщике ставится, многие даже не в курсе что он есть. но отлаживать, даже не сам сервис, а протокол/запросы сквозь него проходящие не удобно, поэтому есть режим "как программа" в нем показывается форма и в ней все взаимодействие при работе. ... делалось само собой для себя, но сейчас используется и для клиентов которые реализуют наш протокол... проще сказать "возьмите нашу программу, запустите сервер как программу делайте что вам нужно и смотрите что посылается... ориентируйтесь на это, у вас должно быть примерно также" чем вникать в каждую проблему и разбираться, что же они там сделали не так.
← →
han_malign © (2015-08-26 12:12) [28]
> Отвечаю: читай MSDN - получишь ошибку при старте.
- только он минут пять определяет - что контекст SCM отсутвует(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)...
по поводу WinSta0DWORD _isInteractive() {
/*
based on code from Rohan Phillips
http://win32.mvps.org/security/is_svc.txt
*/
HANDLE hProcessToken = NULL;
BYTE speculativeBuf[1000];
DWORD groupSize = sizeof(speculativeBuf);
PTOKEN_GROUPS pgpi = NULL;
void* _gpAllocated = NULL;
SID_IDENTIFIER_AUTHORITY _siaNt = SECURITY_NT_AUTHORITY;
DWORD res = 0;
if( !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hProcessToken) ) {
_svcDbg(GetLastError(), _T("OpenProcessToken"));
return 0;//probably access violation
}
if( !GetTokenInformation(hProcessToken, TokenGroups, speculativeBuf, sizeof(speculativeBuf), &groupSize) ) {
if( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
_gpAllocated = malloc(groupSize);
if( _gpAllocated && GetTokenInformation(hProcessToken, TokenGroups, _gpAllocated, groupSize, &groupSize) )
pgpi = (PTOKEN_GROUPS)_gpAllocated;
else {
_svcDbg(GetLastError(), _T("GetTokenInformation"));
}
} else {
_svcDbg(GetLastError(), _T("GetTokenInformation"));
}
} else
pgpi = (PTOKEN_GROUPS)speculativeBuf;
if( pgpi ) {
DTRACE(app, "\tTokenGroups\n");
for(DWORD i = 0; i < pgpi->GroupCount; i++) {
PSID pSid = pgpi->Groups[i].Sid;
if( IsValidSid(pSid) ) {
PSID_IDENTIFIER_AUTHORITY siA = GetSidIdentifierAuthority(pSid);
PUCHAR sacnt = GetSidSubAuthorityCount(pSid);
# ifdef _DEBUG
_TCHAR buf[1000];
if( siA ) {
int len = wsprintf(buf, _T("\t%u.%u.%u.%u.%u.%u x "),
siA->Value[0], siA->Value[1], siA->Value[2], siA->Value[3], siA->Value[4], siA->Value[5]
);
if( sacnt ) {
len += wsprintf(buf+len, _T("%u "), *sacnt);
for(int j = 0; j < (int)(*sacnt); j++) {
PDWORD rid = GetSidSubAuthority(pSid, j);
if(rid)
len += wsprintf(buf+len, _T("%u-"), *rid);
else {
_tcscpy(buf + len, _T("NULL-"));
len += 5;
}
}
buf[len-1] = _T("\n");
} else
_tcscpy(buf + len, _T("NULL\n"));
DTRACE(app, _CAST_LPCSTR_(buf));
} else {
DTRACE(app, "\tNULL\n");
}
# endif
if( siA && sacnt /*just in case*/ ) {
if(
memcmp(siA, &_siaNt, sizeof(_siaNt)) == 0 &&
*sacnt == 1
) {
PDWORD rid = GetSidSubAuthority(pSid, 0);
if(rid/*just in case*/) {
switch(*rid)
{
case SECURITY_INTERACTIVE_RID:
res |= LGTF_INTERACTIVE;
break;
case SECURITY_SERVICE_RID:
res |= LGTF_SERVICE;
break;
}
}
}
}
} else {
DTRACE(sid, "INVALID\n");
}
}
}
if( _gpAllocated )
free(_gpAllocated);
if( hProcessToken )
CloseHandle(hProcessToken);
return res;
}
(_svcDbg, DTRACE - логи, код приводить не буду)
← →
Masterucs © (2015-08-26 14:51) [29]как я уже писал, профи сказали, что WinSta0 уже не катит, для новых ОС это не соблюдается
← →
Rouse_ © (2015-08-26 16:13) [30]Я тебе говорил про SessionID, то что уплывает, а про WinSta0 сказал проверить нужно ибо помню что что-то там менялось но всех нюансов на вскидку не скажу.
Страницы: 1 вся ветка
Форум: "Прочее";
Текущий архив: 2016.05.08;
Скачать: [xml.tar.bz2];
Память: 0.55 MB
Время: 0.002 c