Tag Archives: Otimização

Executando um processo com segurança no Windows

Imagine a seguinte situação: sua empresa solicita um importante processo, que, por apresentar alto custo de execução, deve ser executado a noite ou iniciado no final da tarde e o tempo de execução seja de aproximadamente 8 horas (por exemplo).

Você inicia esse processo, mas, no meio da execução, ele é interrompido. Seu Windows entrou em hibernação após um determinado tempo. Infelizmente todo o seu trabalho acabou atrasando um dia, pois terá que ser executado no dia seguinte.

Esse é um exemplo banal mas que acontece muito no cotidiano dos profissionais de TI. Para não dependermos de configurações externas, podemos utilizar a própria API do Windows, utilizando a função SetThreadExecutionState e tornando o nosso software mais inteligente e evitando assim surpresas desagradáveis!  :lol:

Vejam a classe desenvolvida abaixo. Através dela, conseguimos proteger qualquer rotina que seja critica enquanto estiver em execução.

unit CriticalProcess;

interface

type
  TCriticalProcess = class
  protected
    procedure Execute(const Value: Boolean); virtual;
  public
    constructor Create;
    destructor Destroy; override;

    procedure Stop;
    procedure Start;
  end;

var
  CriticalProcess: TCriticalProcess;

implementation

uses
  Windows;

type
  EXECUTION_STATE = DWORD;

const
  ES_CONTINUOUS      = $80000000;
  ES_SYSTEM_REQUIRED = $00000001;

procedure SetThreadExecutionState(ESFlags: EXECUTION_STATE); stdcall;
  external kernel32 name 'SetThreadExecutionState';

constructor TCriticalProcess.Create;
begin
  inherited;

end;

destructor TCriticalProcess.Destroy;
begin

  inherited;
end;

procedure TCriticalProcess.Stop;
begin
  Execute(False);
end;

procedure TCriticalProcess.Start;
begin
  Execute(True);
end;

procedure TCriticalProcess.Execute(const Value: Boolean);
begin
  if Value then
    SetThreadExecutionState(ES_SYSTEM_REQUIRED or ES_CONTINUOUS)
  else
    SetThreadExecutionState(ES_CONTINUOUS);
end;

initialization
  CriticalProcess := TCriticalProcess.Create;

finalization
  CriticalProcess.Free;

end.

Exemplo de utilização da classe:

uses
  CriticalProcess;

procedure TfrmExemplo.btnExecutarClick(Sender: TObject);
begin
  CriticalProcess.Start;
  try
    // Coloque aqui todo o processamento crítico.

    // Todo o bloco de código inserido aqui estará protegido
    // e será executado sem interferências do sistema operacional.

  finally
    CriticalProcess.Stop;
  end;
end;

Toda a documentação sobre o método SetThreadExecutionState é encontrado no site da Microsoft.

Apenas uma aplicação por sessão

Uma rotina muito simples e útil para quem deseja apenas uma cópia de seu aplicativo sendo executado por vez. Isso dificulta que usuários desavisados consumam todos os recursos do Windows indevidamente e fiquem pentelhando o suporte técnico da sua empresa.  :wink:

unit ApplicationSession;

interface

uses
  Windows, Forms;

type
  TApplicationSession = class
  private
    FHandle: THandle;
  public
    constructor Create(const Handle: THandle);
    destructor Destroy; override;
  end;

var
  ApplicationSession: TApplicationSession;

implementation

constructor TApplicationSession.Create(const Handle: THandle);
begin
  FHandle := Handle;

  if GlobalFindAtom(PChar(Application.Title)) = 0 then
  begin
    FHandle := GlobalAddAtom(PChar(Application.Title));
  end
  else
  begin
    Application.MessageBox('This process is already running.',
      PChar(Application.Title), MB_OK + MB_ICONSTOP);
    Application.Terminate;
  end;
end;

destructor TApplicationSession.Destroy;
begin
  GlobalDeleteAtom(FHandle);

  inherited;
end;

initialization
  ApplicationSession := TApplicationSession.Create(Application.Handle);

finalization
  ApplicationSession.Free;

end.

Para utilizá-la basta adicionar a unit no seu arquivo de projeto, como o exemplo abaixo:

program SessionExample;

uses
  ApplicationSession in 'ApplicationSession.pas',
  Forms,
  FSessionExample in 'FSessionExample.pas' {frmSessionExample};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.Title := 'Session Example';
  Application.CreateForm(TfrmSessionExample, frmSessionExample);
  Application.Run;
end.

Se o usuário tentar executar o mesmo aplicativo com uma cópia já em execução a mensagem “This process is already running.” é apresentada.

Após isso o aplicativo será finalizado.

Lembrando que você pode personalizar como quiser essa rotina, como por exemplo a mensagem, que está em inglês, apenas edite o construtor da classe.