Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2016.05.08;
Скачать: CL | DM;

Вниз

Как понять, что приложение запустили в виде сервиса?   Найти похожие ветки 

 
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)...

по поводу WinSta0

DWORD _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;
Скачать: CL | DM;

Наверх




Память: 0.57 MB
Время: 0.008 c
15-1440059481
K-1000
2015-08-20 11:31
2016.05.08
Unable to locate file "Drawer.pas".


15-1439995071
DayGaykin
2015-08-19 17:37
2016.05.08
Посоветуйте утилиту для сжатия тома NTFS


15-1440451804
Юрий
2015-08-25 00:30
2016.05.08
С днем рождения ! 25 августа 2015 вторник


15-1440158492
Masterucs
2015-08-21 15:01
2016.05.08
Project -> Options -> Compiling -> Symbol reference info


2-1411153202
ser
2014-09-19 23:00
2016.05.08
запись в ini файл