Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   API de Windows (https://www.clubdelphi.com/foros/forumdisplay.php?f=7)
-   -   Dar privilegios de Administrador a la Aplicación (https://www.clubdelphi.com/foros/showthread.php?t=30396)

PoZi 05-04-2006 11:44:46

Dar privilegios de Administrador a la Aplicación
 
Hola a todos.
Estoy realizando una aplicación de control de acceso para un Pc con XP SP2 donde el usuario que tiene que acceder tiene un perfil limitado (con pocos permisos). Para que la aplicación pudiera escribir en disco le tuve que dar privilegios de administrador con la función CreateProcessWithLogonW de la siguiente forma:

Código Delphi [-]
var 
  StartupInfo: TStartupInfo; 
  ProcessInfo: TProcessInformation; 
begin 
  FillChar (StartupInfo, SizeOf(StartupInfo), #0); 
  StartupInfo.cb := SizeOf(StartupInfo); 
  Win32Check( CreateProcessWithLogonW( StringToOleStr('UsuarioConPermisos'),StringToOleStr ('Dominio'), 
    StringToOleStr('Password'),0, StringToOleStr('Exe'), nil, 0, nil, StringToOleStr('DirectorioTrabajo'), 
    StartupInfo, ProcessInfo ) ); 
end;

En este enlace se define la función CreateProcessWithLogonW:
http://msdn.microsoft.com/library/de...withlogonw.asp

De esta forma la aplicación puede escribir en disco con toda libertad mientras que el usuario no.
Pues bien a partir de ese momento la función: ExitWindowsEx(EWX_FORCE,0); que utilizaba para cerrar la sesión cuando el tiempo de acceso al sistema se ha sobrepasado dejó de funcionar :confused: . Lo único que hace es cerrar la aplicación pero el Pc no hace un logoff.
He probado todas la formas que conozco para cerrar la sesión y no lo consigo.
Gracias de antemano.


Bicho 05-04-2006 12:08:49

Hola PoZi, no se mucho del tema, pero podría ser que necesitaras cerrar el proceso, que tú aplicación se cierra correctamente, pero windows no se cierra porque está esperando a que se termine el procese, es posible eso?

Revisa ésta página a la que he llegado desde tú enlace alomejor te sirve.

http://msdn.microsoft.com/library/de..._a_process.asp

Saludos

PoZi 05-04-2006 20:52:15

Probado
 
Buenas.
He intentado hacer lo que me has indicado Bicho (por cierto gracias por responder tan rápido) y sigue haciendo lo mismo.
Lo que he hecho es crear una aplicación con el siguiente código:
Código Delphi [-]
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  ExitWindowsEx(EWX_FORCE,0);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  HProceso: THandle;
begin
  HProceso:=GetCurrentProcess();
  try
    TerminateProcess(HProceso,0);
  finally
    CloseHandle(Handle);
  end;
end;

y arrancarlo dándole permisos de administrador utilizando CreateProcessWithLogonW. Sin embargo la aplicación se cierra pero windows se queda tan pancho.:(
Saludos

Lepe 06-04-2006 11:13:08

Código Delphi [-]
function TFrmteclas.ShutDownWindows(Flag: Word): Boolean;
var
  TokenPriv: TTokenPrivileges;
  H:         DWord;
  HToken:    THandle;
begin
  if Win32Platform = VER_PLATFORM_WIN32_NT then
  begin
    OpenProcessToken(GetCurrentProcess,
      TOKEN_ADJUST_PRIVILEGES, HToken);
    LookUpPrivilegeValue(NIL, 'SeShutdownPrivilege',
      TokenPriv.Privileges[0].Luid);
    TokenPriv.PrivilegeCount := 1;
    TokenPriv.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
    H := 0;
    AdjustTokenPrivileges(HToken, FALSE,
      TokenPriv, 0, PTokenPrivileges(NIL)^, H);
    CloseHandle(HToken);
  end;
  Result := ExitWindowsEx(Flag, 0);
end;

Opciones:
Código Delphi [-]
  ShutDownWindows(EWX_POWEROFF); // apagar windows
  ShutDownWindows(EWX_REBOOT); // reiniciar windows

  ShutDownWindows(EWX_POWEROFF or EWX_FORCE); // forzar apagado
  ShutDownWindows(EWX_REBOOT or EWX_FORCE); // forzar reiniciado

  shutdownwindows(EWX_LOGOFF); // cerrar session

Con la opción "Forzar" ningun programa puede denegar el apagado del sistema. Si hay cambios que no se han guardado en Word, directamente se pierden :D

Probado en Windows XP Sp2 ;)

Saludos

PoZi 06-04-2006 14:22:12

Sigue sin funcionar
 
Hola
Muchas Gracias por ayudarme Lepe.
Lo que me dices ya lo había probado yo, pero por si acaso lo volví a probar, y nada, sigo sin poder hacer un logoff del Pc; lo único que
me funciona es ShutDownWindows(EWX_REBOOT); y ShutDownWindows(EWX_POWEROFF); . No sé que estaré haciendo mal:
Creo un form con 2 botones:
Código Delphi [-]
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  ShutDownWindows(EWX_LOGOFF or EWX_FORCE);
end;
 
procedure TForm1.BitBtn2Click(Sender: TObject);
begin
  ShutDownWindows(EWX_FORCE);
end;
 
function TForm1.ShutDownWindows(Flag: Word): Boolean;
var
  TokenPriv: TTokenPrivileges;
  H: DWord;
  HToken: THandle;
begin
  if Win32Platform = VER_PLATFORM_WIN32_NT then
    begin
      OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES, HToken);
      LookUpPrivilegeValue(NIL, 'SeShutdownPrivilege', TokenPriv.Privileges[0].Luid);
      TokenPriv.PrivilegeCount := 1;
      TokenPriv.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
      H := 0;
      AdjustTokenPrivileges(HToken, FALSE, TokenPriv, 0, PTokenPrivileges(NIL)^, H);
      CloseHandle(HToken);
    end;
  Result := ExitWindowsEx(Flag, 0);
end;

y el ejecutable Project1.exe lo meto en la carpeta C:\Prueba. Creo una aplicación que arranca Project1.exe con privilegios de administrador:
Código Delphi [-]
function CreateProcessWithLogonW(
  lpUsername,lpDomain,lpPassword: PWideChar;
  dwLogonFlags: Dword;
  lpApplicationName: PWideChar;
  lpCommandLine: PWideChar;
  dwCreationFlags: Dword;
  lpEnvironment: Pointer;
  lpCurrentDirectory: PWideChar;
  const lpStartupInfo: tSTARTUPINFO;
  var lpProcessInformation: TProcessInformation): BOOL ; stdcall; external 'advapi32.dll';
begin
  { TODO -oUser -cConsole Main : Insert code here }
  FillChar (StartupInfo, SizeOf(StartupInfo), #0);
  StartupInfo.cb := SizeOf(StartupInfo);
  CreateProcessWithLogonW(StringToOleStr('UsuarioConPermisos'), nil, StringToOleStr('Contraseña'), 0, 
    StringToOleStr('C:\Prueba\Project1.exe'), nil, 0, nil, StringToOleStr('C:\Prueba'), 
    StartupInfo, ProcessInfo);
end.

Lo ejecuto, se inicia el form y al pulsar a alguno de los botones lo único que ocurre es que el form se cierra:eek: ; el guindols ni se inmuta:confused: ...
Muchas gracias y perdonen las molestias.

Lepe 06-04-2006 16:28:15

Acabo de probar el código de BitBtn1 y efectivamente me cierra la sessión abortando los programas en ejecución.

El BitBtn2 no debe funcionar jejeje.

Cita:

Empezado por Ayuda de ExitWindowsEx
EWX_LOGOFF Shuts down all processes running in the security context of the process that called the ExitWindowsEx function. Then it logs the user off.

EWX_POWEROFF Shuts down the system and turns off the power. The system must support the power-off feature.Windows NT: The calling process must have the SE_SHUTDOWN_NAME privilege. For more information, see the following Remarks section.

Según interpreto, que puedo estar equivocado. El contexto de tu aplicación no es la misma que el resto de procesos, ya que has usado el "lanzador" para que tenga permisos de administrador.

como ves el POWEROFF no dice nada del contexto de seguridad.... dice que debe tener el privilegio SE_SHUTDOWN_NAME, que precisamente es el que se le da en la rutina que te dí.

Ejecuta el programa directamente sin el lanzador y cuentanos si cierra la sesion.

PoZi 06-04-2006 20:18:57

Funciona bien sin lanzador
 
Hola.
Sí sí, sin utilizar el lanzador me funciona perfectamente ExitWindowsEx() para cualquier caso (o Flag), incluso poniendo ExitWindowsEx(EWX_FORCE); , pero necesito utilizar el lanzador que da permisos de administrador para que el programa pueda escribir en la base de datos que tengo implementada y el usuario que accede al Pc no.
Saludos

Lepe 07-04-2006 00:06:07

Buscate la funcion RunAndWait32 que anda por el foro.

El lanzador abre el programa de Bases de datos y se queda esperando con esa función hasta que termine el programa de BBDD.

El programa de bases de datos pone el Timer en marcha y cuando el tiempo expire, informa al usuario y se cierra el programa.

Ahora que se ha cerrado, el lanzador continua su ejecución y cierra el sistema con el Logoff.

Como el programa lanzador se está ejecutando en el contexto de los demás procesos, si cerrará el sistema bien.

EDITO:
Código Delphi [-]
var 
StartupInfo: TStartupInfo; 
ProcessInfo: TProcessInformation; 
begin 
FillChar (StartupInfo, SizeOf(StartupInfo), #0); 
StartupInfo.cb := SizeOf(StartupInfo); 
Win32Check( CreateProcessWithLogonW( StringToOleStr('UsuarioConPermisos'),StringToOleStr ('Dominio'), 
StringToOleStr('Password'),0, StringToOleStr('Exe'), nil, 0, nil, StringToOleStr('DirectorioTrabajo'), 
StartupInfo, ProcessInfo ) ); 
  WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
ShutDownwindows(logoff+ewx_force);
end;

Saludos

PoZi 07-04-2006 11:41:48

Buena idea
 
Hola Lepe.
Muy buena idea. Probaré lo que me dices y ya os contaré pero estoy seguro que funcionará.
Muchas Gracias de verdad. Os felicito por esta gran comunidad.

Saludos

PoZi 24-04-2006 19:07:59

Hola¡¡¡
Perdonenme por haber respondido tan tarde, pero he estado probando cosas y no he podido antes.
La idea que me dio Lepe funciona bien salvo cuando el lanzador se encuentra esperando la finalización del programa prinicipal (en WaitForSingleObject) y el usuario pulsa logoff. En este caso windows no puede cerrar el lanzador puesto que se encuentra ocupado y aparece la pantalla de End Now o Cerrar ahora. Si le pulso al botón,el programa efectivamente se cierra pero el código del evento OncloseQuery, donde almaceno la hora y fecha de salida del usuario, no se ejecuta.
Por cierto les informo, por si a alguien le puede interesar, que al usar la función CreateProcessWithLogonW aparte de dejar de funcionar la función ExitWindowsEx también lo hace el evento OnCloseQuery del programa lanzado. Por ese motivo yo he tenido que guardar los datos de salida del usuario desde el lanzador, pero como ven no consigo hacerlo :confused: .

Lepe 25-04-2006 11:20:38

Mira este hilo que he "reabierto" hace unos minutos, creo que los tiros van por ahí.

El mensaje de roman, como siempre, no tiene desperdicio ;).

Saludos

PoZi 25-04-2006 20:12:23

Hola¡¡¡

He probado lo que me dices Lepe y lamentablemente no me funciona: He hecho todo lo que dice Roman, he probado a guardar los datos en la función WMEndSession, en la WMQueryEndSession, con Msg.Result:=1 sin él.... y nada.
Parece ser que al lanzar el programa con Createprocesswithlogon se crea un proceso aislado del resto como si estuviera en una sesión distinta y para windows es como si no existiera (lo tiene marginado); Por eso ni funciona el ExitwindowsEx ni el evento OnCloseQuery ya que no le manda los mensajes EndSession ni QueryEndSession.

También he probado lo siguiente: Al lanzador le he quitado WaitForSingleObject y ShutDownWindows y le he puesto, como explica en el foro Roman, lo siguiente:

Código Delphi [-]
procedure TForm1.FormCreate(Sender: TObject);
begin
  SetProcessShutDownParameters($100, 0);
end;
 
procedure TForm1.WMQueryEndSession(var Msg: TWMQueryEndSession);
begin
  WinSesionFin := true;
  inherited;
end;

procedure TForm1.WMEndSession(var Msg: TWMEndSession);
begin
  WinSesionFin := Msg.EndSession;
  inherited;
end;


procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if WinSesionFin then
    begin
      GuardarDatosSalida; (*Guarda la hora y fecha en una tabla*)
      CanClose := true;
    end
  else
    begin
      ExitWindowsEx(EWX_FORCE,0);
      Application.Terminate;
   end;
end;

De esta forma al hacer un log off se guarda la hora y fecha de salida, puesto que no está ocupado con WaitForSingleObject, y cuando quiera cerrar la sesión de windows desde el programa principal solo tengo que cerrar el lanzador para que se ejecute el código del evento OnCloseQuery y esto lo he hecho con la función KillTask (ver código). Pero al cerrarlo no se ejecuta el evento OnCloseQuery, sólo cierra el proceso y ya está.
Saludos¡¡¡

Lepe 25-04-2006 20:38:08

Pero a ver....

El usuario pulsa Logoff... pero ¿donde?

Se supone que el usuario pulsa un botón que dice Logoff en tu programa de BBDD, pero en realidad lo que hace tu programa de BBDD es cerrarse él (Application.Terminate, o bien cerrar el Form Principal), y puesto que el lanzador está esperando ese momento, el lanzador es el que continúa su ejecución y hace el Logoff del sistema.

¿no quedamos en eso? :confused:

saludos

PoZi 26-04-2006 11:21:40

Buenas¡¡
Mi programa no tiene ningún botón de log off. Quizá no me haya explicado bien. Esto es lo que deseo hacer:
Al iniciarse la sesión el programa bloquea el Pc (Ctrl+Alt+Supr, Alt+F4, etc.) y muestra un form en el que hay que introducir el usuario y la contraseña. Una vez introducidos, comprueba en una base de datos si son correctos y si el usuario accede en su franja horaria permitida. Si todo está bien, el form se minimiza, se desbloquea el Pc a excepción del Ctr+Alt+Supr (para evitar que cierre mi aplicación) y se guarda el nombre del usuario y la fecha y hora de acceso para tener un control de los usuarios que han accedido al Pc.
El usuario trabaja en el Pc de forma normal. Cuando termina su trabajo cierra la sesión de windows presionando a Inicio/Log off. Si antes de terminar su trabajo llega la hora en que termina su franja horaria de acceso, el programa debe cerrar la sesión. En ambos casos se debe guardar la hora y fecha de salida.

Los problemas surgieron a raiz de utilizar el lanzador que da privilegios de administrador al programa. El ExitWindowsEx que utilizaba para cerrar la sesión de windows cuando terminaba el tiempo de acceso al sistema del usuario dejó de funcionar y el evento OnCloseQuery, donde tenía puesto el código que guardaba la hora y fecha de salida del usuario y que se ejecutaba al cerrar el programa, ya fuera por windows (al presionar al botón log off de windows) o por el propio programa (al ejecutarse la instrucción Close;), también dejó de funcionar.

Por estos motivos tuve que cerrar la sesión de windows y guardar los datos de salida del usuario desde el lanzador (como muy acertadamente me indicaste tú Lepe). Y eso hice.

Todo funcionaba a la mil maravillas salvo cuando el lanzador se encontraba esperando en WaitForSingleObject y el usuario decidía cerrar la sesión de windows porque había terminado su trabajo. Supongo que lo que ocurría es que windows le mandaba el mensaje QueryEndSession pero al estar ocupado esperando la finalización del programa principal no respondía con el famoso Msg.Result:=1. Windows entonces mostraba el mensaje End Now. Si se le aceptaba, la sesión se cerraba pero el lanzador no guardaba la hora y fecha de salida. Por eso hice la prueba que mencioné antes:
Cita:

También he probado lo siguiente: Al lanzador...
Espero haberme explicado con la suficiente claridad.
Gracias y Saludos

seoane 26-04-2006 12:55:09

Parece que lo que pasa es que tus dos aplicaciones tienen problemas de comunicacion :) Bien, pues podemos usar un fallo de seguridad de windows, o fallo de diseño como dicen ellos, que permite a una aplicacion mandar pulsaciones de teclado a otra si comparten el mismo escritorio aunque pertenezcan a usuarios distintos.

En la aplicacion principal (lanzador o como la llames) la que recibe el mensage de finalizar sesion, utilizamos el siguiente codigo para simular un HotKey:

Código Delphi [-]
  // Esto simula la combinacion de teclas CTRL+SHIFT+F12
  keybd_event(VK_LCONTROL,0,0,0);
  keybd_event(VK_MENU,0,0,0);
  keybd_event(VK_F12,0,0,0);
  keybd_event(VK_F12,0,KEYEVENTF_KEYUP,0);
  keybd_event(VK_LCONTROL,0,KEYEVENTF_KEYUP,0);
  keybd_event(VK_MENU,0,KEYEVENTF_KEYUP,0);

En la segunda aplicacion, la que corre como otro usuario, previamente hemos regsitrado el Hotkey Ctr+Shift+F12. Cuando recibimos el mensage del HotKey, cerramos la aplicacion, o realizamos cualquier otra tarea que necesitemos. Un poco de codigo, para ilustrar todo esto:

Código Delphi [-]
// Para resgitar el HotKey (Colocalo por ejemplo en el OnCreate)
 RegisterHotKey(Handle,0,MOD_ALT or MOD_CONTROL, VK_F12);

// Para quitar el Hotkey (en el Onclose por ejemplo)
UnregisterHotKey(Handle,0);


// Por ultimo atrapamos el mensaje del Hotkey
type
  TForm1 = class(TForm)
  private
    { Private declarations }
    procedure WMHOTKEY(var Msg: TMessage);message WM_HOTKEY;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.WMHOTKEY(var Msg: TMessage);
begin
  // Y cualquier otra cosa que tengas que hacer antes de cerrar
  Close;
end;

Bueno, aqui te dejo mi aportacion, se que no esta en la linea que estabais siguiendo, pero solo es una sugerencia. Espero que te sirva

PoZi 26-04-2006 14:11:20

A probar
 
Gracias seoane.
Probaré lo que me dices y ya os contaré.
Es algo parecido a lo que yo quería hacer con la diferencia de que yo le quería enviar el mensaje WM_CLOSE para que se ejecutase el evento OnCloseQuery, pero como el lanzador no tiene ventana no he podido enviárselo o no sé.
Hasta luego¡¡¡

PoZi 26-04-2006 20:17:52

Por fin
 
Muy Buenas a todos¡¡¡
Por fin ha funcionado. Ha costado sudor y lágrimas, pero bueno todavía no quiero echar las campanas al vuelo porque no lo he probado profundamente.
Me ha funcionado de dos formas:
1ª.- Mandando el mensaje WM_CLOSE al lanzador para que al recibirlo cierre windows:
Código Delphi [-]
{ Esto va en la aplicación principal }
procedure TForm1.CerrarProceso;
var
  IdVentana: HWnd;
begin
  IdVentana := FindWindow(nil, 'Lanzador');
  SendMessage(IdVentana, WM_CLOSE, 0, 0);
end;
 
{ Y esto en el lanzador }
procedure TForm1.WMQueryEndSession(var Msg: TWMQueryEndSession);
begin
  WinSesionFin := true;
  inherited;
end;
procedure TForm1.WMEndSession(var Msg: TWMEndSession);
begin
  WinSesionFin := Msg.EndSession;
  inherited;
end;
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if WinSesionFin then
    begin
      EnvioMensaje; { Envia HotKey a Aplicación principal para que guarde los datos de salida }
      WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
      CanClose := true;
    end
  else
    begin
      ExitWindowsEx(EWX_FORCE,0);
      Application.Terminate;
    end;
end;
Initialization
  Application.Title:='Lanzador';
  Application.ShowMainForm:=False;

No me funcionaba porque tenía puesto Application.Title:=''; y además yo pensaba que como no tenía ventana activa, pues usaba Application.ShowMainForm:=False; ), no le podía enviar el mensaje.

2ª.- Igual que antes pero enviando un HotKey al lanzador (como indica arriba seoane), en lugar de WM_CLOSE. Cuando el lanzador lo detecta Cierra Windows y se cierra él.

Muchas Gracias a todos, sobretodo a Lepe ;) que le he estado dando la tabarra durante bastante tiempo.
Saludos.


La franja horaria es GMT +2. Ahora son las 14:02:55.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi