Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 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)...

по поводу 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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.55 MB
Время: 0.002 c
11-1263726606
GradeMax
2010-01-17 14:10
2016.05.08
Как задать высоту строк в ListView?


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


15-1440094960
Rouse_
2015-08-20 21:22
2016.05.08
Пятничная головоломка от Розыча


15-1440322751
megavoid
2015-08-23 12:39
2016.05.08
Нужна ли грамотность в работе программиста?


15-1440264646
Pavia
2015-08-22 20:30
2016.05.08
Линкер и редактор объектников





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский