Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > CreateProcessWithLogonW и запуск из под сервиса


Автор: DaDe 3.10.2010, 15:07
Добрый день.

Долго пытаюсь уже решить данную проблему, но безуспешно.
Может кто из читающих, что-нибудь подскажет по теме.

Проблема в том, что есть сервис запущенный с минимальным привилегиями и нужно, чтобы этот сервис запускал консольное приложение от имени администратора.
Проблема состоит в том, что в ОС Windows 7 не консольные приложения нормально запускаются, а вот консольные не запускаются.
Под XP и консольные запускаются нормально.

Один из вариантов запуска:
Код

var
   pwUsername,pwDomain,pwPassword,pwCmdLine:PWidechar;
var
   StartupInfo:TStartupInfo;
   ProcessInfo:TProcessInformation;
begin
FillChar(StartupInfo,Sizeof(StartupInfo),#0);
StartupInfo.cb := Sizeof(StartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := SW_SHOWDEFAULT;
GetMem(pwCmdLine,80 * sizeof(WideChar));
GetMem(pwUsername,80 * sizeof(WideChar));
GetMem(pwDomain,80 * sizeof(WideChar));
GetMem(pwPassword,80 * sizeof(WideChar));
StringToWideChar(CMD,pwCmdLine,80);
StringToWideChar(Lo,pwUsername,80);
StringToWideChar(DM,pwDomain,80);
StringToWideChar(PAS,pwPassword,80);
StartupInfo.lpDesktop:= Pchar(ST+'\'+DS);
StartupInfo.lpTitle:=pwUsername;
Log('CreateProcessWithLogon ...');
try
if not CreateProcessWithLogon(pwUsername,pwDomain,pwPassword,
      1,
      nil,
      pwCmdLine,                      { pointer to command line string }
      CREATE_NEW_CONSOLE,
      nil,                           { pointer to new environment block }
      nil,                           { pointer to current directory name }
      StartupInfo,                   { pointer to STARTUPINFO }
      ProcessInfo)
    then Log('error CreateProcessWithLogon: '+SysErrorMessage(GetLastError))
    else begin
                Log('CreateProcessWithLogon is Ok');
                if ToWait then WaitforSingleObject(ProcessInfo.hProcess,INFINITE);
                if ToWait then Log('Object terminated');
         end;
except end;
if ProcessInfo.hThread <>null then CloseHandle(ProcessInfo.hThread );
if ProcessInfo.hProcess<>null then CloseHandle(ProcessInfo.hProcess);
FreeMem(pwPassword);
FreeMem(pwDomain);
FreeMem(pwUsername);
FreeMem(pwCmdLine);


Может, кто сможет подсказать.
Буду очень признателен.

Автор: grofast 4.10.2010, 14:09
Добрый день.

Я тоже мучаюсь с этой проблемой.

А подскажи как ты определяешь.

Рабочую станцию и рабочий стол?

StartupInfo.lpDesktop:= Pchar(ST+'\'+DS);

Спасибо.

Автор: DaDe 4.10.2010, 15:31
winsta0\Default

Автор: grofast 4.10.2010, 15:40
Блин у меня на windows7 x64 +delphi 2011 не работает(

Если как обычное приложение все отлично если как сервис ни одно окно не показывается.
Хотя процесс запускается отлично.(

Автор: DaDe 4.10.2010, 21:28
Цитата(grofast @ 4.10.2010,  15:40)
Блин у меня на windows7 x64 +delphi 2011 не работает(

Если как обычное приложение все отлично если как сервис ни одно окно не показывается.
Хотя процесс запускается отлично.(

Результат процедуры Log покажи. Или вопрос, у тебя CreateProcessWithLogon выполняется или нет?

Автор: grofast 5.10.2010, 16:26
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs,
  ExtCtrls;

type
  TMenedgerZadanii = class(TService)
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure ServiceStart(Sender: TService; var Started: Boolean);
  private
    { Private declarations }
  public
    function GetServiceController: TServiceController; override;
    { Public declarations }
  end;


procedure MyCreateProcess(ConstCommandLine: string);
 function CreateProcessWithLogonW(const lpUsername: PWideChar;
  const lpDomain: PWideChar; const lpPassword: PWideChar;
  dwLogonFlags: DWORD; const lpApplicationName: PWideChar;
  lpCommandLine: PWideChar; dwCreationFlags: DWORD;
  lpEnvironment: Pointer; const lpCurrentDirectory: PWideChar;
  lpStartupInfo: PStartupInfo;
  lpProcessInfo: PProcessInformation): Boolean; stdcall;


  type
  TCreateProcessWithLogonW =
    function(const lpUsername: PWideChar;
    const lpDomain: PWideChar; const lpPassword: PWideChar;
    dwLogonFlags: DWORD; const lpApplicationName: PWideChar;
    lpCommandLine: PWideChar; dwCreationFlags: DWORD;
    lpEnvironment: Pointer; const lpCurrentDirectory: PWideChar;
    lpStartupInfo: PStartupInfo;
    lpProcessInfo: PProcessInformation): Boolean; stdcall;



  const
  DllName = 'advapi32.dll';


    LOGON_WITH_PROFILE = $00000001;
  LOGON_NETCREDENTIALS_ONLY = $00000002;
  LOGON_ZERO_PASSWORD_BUFFER = $80000000;




var
  MenedgerZadanii: TMenedgerZadanii;

   DllHandle: THandle;
  _CreateProcessWithLogonW: TCreateProcessWithLogonW;


implementation


{$R *.DFM}


procedure OpenTxt(Text:String);
 var
   F: Textfile;
 begin


   AssignFile(f,'D:\Служба.txt'); {Assigns the Filename}

   if Not FileExists('D:\Служба.txt') Then
   Begin
     ReWrite(f);
     CloseFile(f);
   End;

   AssignFile(f,'D:\Служба.txt'); {Assigns the Filename}
   //Reset(f);
   Append(f); {Opens the file for editing}
   Writeln(f,Text);
   Closefile(f); {Closes file F}
 end;




function InitLib: Boolean;
begin
  if DllHandle = 0 then
    if Win32Platform = VER_PLATFORM_WIN32_NT then
    begin
      DllHandle := LoadLibrary(DllName);
      if DllHandle <> 0 then
      begin
        _CreateProcessWithLogonW := GetProcAddress(DllHandle,
          'CreateProcessWithLogonW');
      end;
    end;
  Result := (DllHandle <> 0);
end;

function NotImplementedBool: Boolean;
begin
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  Result := false;
end;



function CreateProcessWithLogonW(const lpUsername: PWideChar;
  const lpDomain: PWideChar; const lpPassword: PWideChar;
  dwLogonFlags: DWORD; const lpApplicationName: PWideChar;
  lpCommandLine: PWideChar; dwCreationFlags: DWORD;
  lpEnvironment: Pointer; const lpCurrentDirectory: PWideChar;
  lpStartupInfo: PStartupInfo;
  lpProcessInfo: PProcessInformation): Boolean; stdcall;
begin
  if InitLib and Assigned(_CreateProcessWithLogonW) then
    Result := _CreateProcessWithLogonW(lpUsername, lpDomain, lpPassword,
      dwLogonFlags, lpApplicationName, lpCommandLine, dwCreationFlags,
      lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInfo)
  else
    Result := NotImplementedBool;
end;




 procedure MyCreateProcess(ConstCommandLine: string);
const
  UserName: WideString = 'programist';
  Password: WideString = '35673576';
  //ConstCommandLine : String = 'MONIC_S.EXE ';
  Title: WideString ='';
  Domain: WideString = '';
var
  MyStartupInfo: STARTUPINFO;
  ProcessInfo: PROCESS_INFORMATION;
  CommandLine: array[0..512] of WideChar;
begin
  FillChar(MyStartupInfo, SizeOf(MyStartupInfo), 0);
  MyStartupInfo.cb := SizeOf(MyStartupInfo);


  //MyStartupInfo.lpDesktop :=Nil;

  //MyStartupInfo.lpDesktop :=PChar('winsta0\Default');

  StringToWideChar(ConstCommandLine, CommandLine,
    Sizeof(CommandLine) div SizeOf(WideChar));
  MyStartupInfo.lpTitle := PWideChar(Title);


  OpenTxt('Дошли до креат процес');


  if not CreateProcessWithLogonW(PWideChar(UserName), PWideChar(Domain),
    PWideChar(Password), LOGON_WITH_PROFILE, nil,
    PWideChar(WideString(CommandLine)),CREATE_NEW_CONSOLE, nil, nil, @MyStartupInfo, @ProcessInfo) then
    OpenTxt(intToStr(GetLastError)+' '+SysErrorMessage(GetLastError))
  else
  begin
    CloseHandle(ProcessInfo.hProcess);
    CloseHandle(ProcessInfo.hThread);
  end;


  OpenTxt('Окончание процедуры '+IntToStr(GetLastError))

end;



procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  MenedgerZadanii.Controller(CtrlCode);
end;

function TMenedgerZadanii.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;









procedure TMenedgerZadanii.ServiceStart(Sender: TService; var Started: Boolean);
begin
//MyCreateProcess('C:\windows\system32\cmd.exe');
 MyCreateProcess('C:\windows\explorer.exe');
end;

procedure TMenedgerZadanii.Timer1Timer(Sender: TObject);
begin

//MyCreateProcess('D:\w32dasm10\W32dsmv10\kWdsm.exe');

//MyCreateProcess('C:\windows\system32\cmd.exe');


end;

end.




Если строчку с указанием рабочего стола закоментировать то процесс запускается 
но на десктопе текущего пользователя не появляется.
Если раскоментировать то вообще не запускается


CreateProcessWithLogonW всегда возврашает true типа все удачно
gestlasterror возврашает 0.

Служба под пользователем programist (он в группе администраторы)



Автор: grofast 5.10.2010, 16:45
Если из под Ситемы запускаю то выводит в лог ошибку 5
Отказано в доступе.

Автор: DaDe 5.10.2010, 19:14
Цитата(grofast @  5.10.2010,  16:26 Найти цитируемый пост)
CreateProcessWithLogonW всегда возврашает true типа все удачно
gestlasterror возврашает 0.

Судя по коду. Когда у тебя отображается результат gestlasterror, то CreateProcessWithLogonW не выполняется.

Цитата(grofast @  5.10.2010,  16:45 Найти цитируемый пост)
Если из под Ситемы запускаю то выводит в лог ошибку 5
Отказано в доступе. 

Из под System не получится, есть ограничения.

Автор: bems 5.10.2010, 19:57
grofast, кнопка КОД справа вверху над формой ответа

Автор: kami 6.10.2010, 01:30
Цитата(DaDe @  3.10.2010,  15:07 Найти цитируемый пост)
запуск из под сервиса, под ОС Windows 7

Нарыл у себя в закромах. 
Писалось несколько лет назад, проверено сейчас под Vista - cmd.exe из сервиса запускается на активном десктопе "только так". В висте система разграничения учетных записей, afair, не отличается от семерки - так же в разных терминальных сессиях, соответственно - разные WinSta и Desktop. Смотреть метод Timer1Timer.
Функции когда-то писались под запуск вообще любого файла, у которого есть ассоциации в реестре.

Может быть, что-то (или большинство) лишнее - просьба удалить самим smile

Автор: grofast 6.10.2010, 10:16
Цитата(DaDe @ 5.10.2010,  19:14)
Цитата(grofast @  5.10.2010,  16:26 Найти цитируемый пост)
CreateProcessWithLogonW всегда возврашает true типа все удачно
gestlasterror возврашает 0.

Судя по коду. Когда у тебя отображается результат gestlasterror, то CreateProcessWithLogonW не выполняется.

Цитата(grofast @  5.10.2010,  16:45 Найти цитируемый пост)
Если из под Ситемы запускаю то выводит в лог ошибку 5
Отказано в доступе. 

Из под System не получится, есть ограничения.

Нет почему

у меня в коде прописано и если удачно процесс создается и если не удачно

Выводить лог



Код

//****************Если не удачно

  if not CreateProcessWithLogonW(PWideChar(UserName), PWideChar(Domain),
    PWideChar(Password), LOGON_WITH_PROFILE, nil,
    PWideChar(WideString(CommandLine)),CREATE_NEW_CONSOLE, nil, nil, @MyStartupInfo, @ProcessInfo) then
    OpenTxt(intToStr(GetLastError)+' '+SysErrorMessage(GetLastError))





  else     
  begin
    CloseHandle(ProcessInfo.hProcess);
    CloseHandle(ProcessInfo.hThread);
  end;


  OpenTxt('Окончание процедуры '+IntToStr(GetLastError))  //*********************В любом случае вывести последнюю ошибку




Я кстати досмотрелся вчера ,в логах пишет что приложение запустилось но не может инициализироваться.
Видимо из-за виртуализации.

Если под тем пользователем под которым стартует служба то приложение запускается
но на десктопе текущего пользователя не отображается  я так думаю оно в нулевой сессии.
Висит себе в процесах.


Если из под другого пользователя приложение пытается запуститься
то в логах пишет


explorer.exe - Ошибка приложения 
   Ошибка при запуске приложения (0xc0000142). Для выхода из приложения нажмите кнопку "ОК".  



То есть CreateProcessWithLogonW отрабатывает на отлично

приложение запускается но почемуто не может инициализироватся.

Я так думаю из-за виртуализации.

Пока эксперементирую.)

Добавлено @ 10:18
Цитата(bems @ 5.10.2010,  19:57)
grofast, кнопка КОД справа вверху над формой ответа

Спасибо.
 smile 

Автор: grofast 6.10.2010, 11:28
Цитата(kami @ 6.10.2010,  01:30)
Цитата(DaDe @  3.10.2010,  15:07 Найти цитируемый пост)
запуск из под сервиса, под ОС Windows 7

Нарыл у себя в закромах. 
Писалось несколько лет назад, проверено сейчас под Vista - cmd.exe из сервиса запускается на активном десктопе "только так". В висте система разграничения учетных записей, afair, не отличается от семерки - так же в разных терминальных сессиях, соответственно - разные WinSta и Desktop. Смотреть метод Timer1Timer.
Функции когда-то писались под запуск вообще любого файла, у которого есть ассоциации в реестре.

Может быть, что-то (или большинство) лишнее - просьба удалить самим smile

Спасибо огромное.

Я много функций переписал одной строкой и много нового узнал.

И все заработало.


Спасибо спасибо спасибо)))).

Автор: grofast 6.10.2010, 14:24
kami


А еще просьба подскажи плиз нужно для решения второй части задачи.


Если ни один пользователь не залогинен можно ли из под службы (system) запустить мою программу с правами пользователя?

Или может залогинится сначала под ним нужно.?


Спасибо.

Автор: bems 6.10.2010, 16:43
Модератор: grofast, я второй раз прошу тебя пользоваться кнопкой "код". В следующий раз пойдешь на пару дней в ридонли

Автор: kami 6.10.2010, 22:03
Цитата(grofast @  6.10.2010,  14:24 Найти цитируемый пост)
Если ни один пользователь не залогинен можно ли из под службы (system) запустить мою программу с правами пользователя?

Чтобы запустить программу с правами пользователя, нужно получить права пользователя. Чтобы получить права пользователя, нужно или получить его токен (для не-вошедшего в систему пользователя - вызвать LogonUser) или сразу вызвать CreateProcessWithLogonW, которая автоматом его "залогинет".
P.S. Могу ошибаться - давно не освежал в памяти данные этого направления.
P.P.S. 
Цитата(grofast @  6.10.2010,  11:28 Найти цитируемый пост)
Спасибо огромное.

Вспомнил, что у этих моих функций есть ограничение, вытекающее из ограничения WTSGetActiveConsoleSessionID - если активно используется FastUserSwitching, то она будет возвращать SessionID для первого из залогиненных, и не важно, находится его сессия в WTS_SESSIONSTATE_LOCK (вызывана "смена пользователя") или активна.
Решение, afair, в регистрации для своего сервиса нотификаций о смене сессии (SERVICE_CONTROL_SESSIONCHANGE), правда для этого нужно задействовать HandlerEx, а не Handler, как в Delphi ( по крайней мере по D7 включительно) и работе уже с этими данными.

Автор: DaDe 7.10.2010, 21:10
Цитата(kami @  6.10.2010,  01:30 Найти цитируемый пост)
проверено сейчас под Vista - cmd.exe из сервиса запускается на активном десктопе "только так"


Получается от имени учетной записи, запустившей сервис.

Уже отчаиваюсь найти решение и возникают бредовые идеи типа создания задания в шедулере от имени другого пользователя, но это как-то не элегантно.
Неужели никто не сталкивался с подобной задачей.

Автор: kami 8.10.2010, 22:03
Цитата(DaDe @  7.10.2010,  21:10 Найти цитируемый пост)
Получается от имени учетной записи, запустившей сервис.

Это как? LocalSystem|NetworkService|LocalService?
Тогда процесс был бы запущен на их десктопе, в 0 терминальной сессии и его окно не было бы видно...

Автор: bems 8.10.2010, 22:05
kami, одно не всегда означает другое

Автор: DaDe 9.10.2010, 17:41
Цитата(kami @  8.10.2010,  22:03 Найти цитируемый пост)
Это как? LocalSystem|NetworkService|LocalService?
Тогда процесс был бы запущен на их десктопе, в 0 терминальной сессии и его окно не было бы видно... 

В вашем примере, если запустить сервис например, от имени пользователя User. Залогиневшись под пользователем User2 и запустив сервис, то мы не увидим окна cmd и оно будет запущенно от имени User.

Автор: seldir 12.11.2016, 18:59
Цитата(kami @ 6.10.2010,  01:30)
Нарыл у себя в закромах. 
Писалось несколько лет назад, проверено сейчас под Vista - cmd.exe из сервиса запускается на активном десктопе "только так".

Я новичок в системном программировании.
Попытался изучить Ваш пример, все просто замечательно работает под win 8.1., но только при запуске сервиса или при перезагрузки системы,
если сервис установить и выключить компьютер, а потом включить, то окно с cmd.exe не запускается, хотя сама служба находится в системе в состоянии "выполняется".
Пыхтел над кодом целый день, но, очевидно, базовых знаний не хватает, подскажите, пожалуйста, куда смотреть?

Автор: kami 19.11.2016, 13:44
Цитата(seldir @  12.11.2016,  18:59 Найти цитируемый пост)
 хотя сама служба находится в системе в состоянии "выполняется".

Служба пусть находится в любом состоянии. Вам нужно понять, почему не запускается нужный процесс. Для этого нужно анализировать коды возврата WinAPI функций. Т.к. работа идет в сервисе - пишите всё в лог.
Подозрение на то, что сервис пытается стартануть приложение до того, как появится активная сессия пользователя.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)