1

How to verify if a process name (partial name e.g: notep* for notepad.exe) exist and create a loop to kill this process whenever run? In batch it's simple:

:a
taskkill -f -im notep*
goto a

Any help?

dummzeuch
  • 10,975
  • 4
  • 51
  • 158
V.Salles
  • 403
  • 2
  • 9
  • 16
  • With any answer you'll receive here, you should remember that there is a race condition. There's always a chance that a process is started or ended between checking and acting on the result. – Thijs van Dien Jun 06 '13 at 17:09

4 Answers4

9

You can use a asynchronous WMI event (like __InstanceCreationEvent) to detect when the a process is launched and the using the Win32_Process WMI Class and the Terminate method to kill the process.

You can wrote a WQL sentence like this to detect the launch of any process strating with the "notep" string.

Select * From __InstanceCreationEvent Within 1 Where TargetInstance ISA "Win32_Process" And TargetInstance.Name like "notep%"

Try this sample

{$APPTYPE CONSOLE}

uses
  Windows,
  {$IF CompilerVersion > 18.5}
  Forms,
  {$IFEND}
  OleServer,
  SysUtils,
  ActiveX,
  ComObj,
  Variants;

type
  TSWbemSinkOnObjectReady = procedure(ASender: TObject; const objWbemObject: OleVariant;
                                                        const objWbemAsyncContext: OleVariant) of object;
  TSWbemSinkOnCompleted = procedure(ASender: TObject; iHResult: TOleEnum;
                                                      const objWbemErrorObject: OleVariant;
                                                      const objWbemAsyncContext: OleVariant) of object;
  TSWbemSinkOnProgress = procedure(ASender: TObject; iUpperBound: Integer; iCurrent: Integer;
                                                     const strMessage: WideString;
                                                     const objWbemAsyncContext: OleVariant) of object;
  TSWbemSinkOnObjectPut = procedure(ASender: TObject; const objWbemObjectPath: OleVariant;
                                                      const objWbemAsyncContext: OleVariant) of object;

  ISWbemSink = interface(IDispatch)
    ['{75718C9F-F029-11D1-A1AC-00C04FB6C223}']
    procedure Cancel; safecall;
  end;

  TSWbemSink = class(TOleServer)
  private
    FOnObjectReady: TSWbemSinkOnObjectReady;
    FOnCompleted: TSWbemSinkOnCompleted;
    FOnProgress: TSWbemSinkOnProgress;
    FOnObjectPut: TSWbemSinkOnObjectPut;
    FIntf: ISWbemSink;
    function GetDefaultInterface: ISWbemSink;
  protected
    procedure InitServerData; override;
    procedure InvokeEvent(DispID: TDispID; var Params: TVariantArray); override;
  public
    procedure Connect; override;
    procedure ConnectTo(svrIntf: ISWbemSink);
    procedure Disconnect; override;
    procedure Cancel;
    property DefaultInterface: ISWbemSink read GetDefaultInterface;
  published
    property OnObjectReady: TSWbemSinkOnObjectReady read FOnObjectReady write FOnObjectReady;
    property OnCompleted: TSWbemSinkOnCompleted read FOnCompleted write FOnCompleted;
    property OnProgress: TSWbemSinkOnProgress read FOnProgress write FOnProgress;
    property OnObjectPut: TSWbemSinkOnObjectPut read FOnObjectPut write FOnObjectPut;
  end;


  TWmiAsyncEvent = class
  private
    FWQL      : string;
    FSink     : TSWbemSink;
    FLocator  : OleVariant;
    FServices : OleVariant;
    procedure EventReceived(ASender: TObject; const objWbemObject: OleVariant; const objWbemAsyncContext: OleVariant);
  public
    procedure  Start;
    constructor Create;
    Destructor Destroy;override;
  end;

{ TSWbemSink }

procedure TSWbemSink.Cancel;
begin
 DefaultInterface.Cancel;
end;

procedure TSWbemSink.Connect;
var
  punk: IUnknown;
begin
  if FIntf = nil then
  begin
    punk := GetServer;
    ConnectEvents(punk);
    Fintf:= punk as ISWbemSink;
  end;
end;

procedure TSWbemSink.ConnectTo(svrIntf: ISWbemSink);
begin
  Disconnect;
  FIntf := svrIntf;
  ConnectEvents(FIntf);
end;

procedure TSWbemSink.Disconnect;
begin
  if Fintf <> nil then
  begin
    DisconnectEvents(FIntf);
    FIntf := nil;
  end;
end;

function TSWbemSink.GetDefaultInterface: ISWbemSink;
begin
  if FIntf = nil then
    Connect;
  Assert(FIntf <> nil, 'DefaultInterface is NULL. Component is not connected to Server. You must call "Connect" or "ConnectTo" before this operation');
  Result := FIntf;
end;

procedure TSWbemSink.InitServerData;
const
  CServerData: TServerData = (
    ClassID:   '{75718C9A-F029-11D1-A1AC-00C04FB6C223}';
    IntfIID:   '{75718C9F-F029-11D1-A1AC-00C04FB6C223}';
    EventIID:  '{75718CA0-F029-11D1-A1AC-00C04FB6C223}';
    LicenseKey: nil;
    Version: 500);
begin
  ServerData := @CServerData;
end;


procedure TSWbemSink.InvokeEvent(DispID: TDispID; var Params: TVariantArray);
begin
  case DispID of
    -1: Exit;  // DISPID_UNKNOWN
    1: if Assigned(FOnObjectReady) then
         FOnObjectReady(Self,
                        Params[0] {const ISWbemObject},
                        Params[1] {const ISWbemNamedValueSet});
    2: if Assigned(FOnCompleted) then
         FOnCompleted(Self,
                      Params[0] {WbemErrorEnum},
                      Params[1] {const ISWbemObject},
                      Params[2] {const ISWbemNamedValueSet});
    3: if Assigned(FOnProgress) then
         FOnProgress(Self,
                     Params[0] {Integer},
                     Params[1] {Integer},
                     Params[2] {const WideString},
                     Params[3] {const ISWbemNamedValueSet});
    4: if Assigned(FOnObjectPut) then
         FOnObjectPut(Self,
                      Params[0] {const ISWbemObjectPath},
                      Params[1] {const ISWbemNamedValueSet});
  end; {case DispID}

end;


//Detect when a key was pressed in the console window
function KeyPressed:Boolean;
var
  lpNumberOfEvents     : DWORD;
  lpBuffer             : TInputRecord;
  lpNumberOfEventsRead : DWORD;
  nStdHandle           : THandle;
begin
  Result:=false;
  nStdHandle := GetStdHandle(STD_INPUT_HANDLE);
  lpNumberOfEvents:=0;
  GetNumberOfConsoleInputEvents(nStdHandle,lpNumberOfEvents);
  if lpNumberOfEvents<> 0 then
  begin
    PeekConsoleInput(nStdHandle,lpBuffer,1,lpNumberOfEventsRead);
    if lpNumberOfEventsRead <> 0 then
    begin
      if lpBuffer.EventType = KEY_EVENT then
      begin
        if lpBuffer.Event.KeyEvent.bKeyDown then
          Result:=true
        else
          FlushConsoleInputBuffer(nStdHandle);
      end
      else
      FlushConsoleInputBuffer(nStdHandle);
    end;
  end;
end;

{ TWmiAsyncEvent }

constructor TWmiAsyncEvent.Create;
const
  strServer    ='localhost';
  strNamespace ='root\CIMV2';
  strUser      ='';
  strPassword  ='';
begin
  inherited Create;
  CoInitializeEx(nil, COINIT_MULTITHREADED);
  FLocator  := CreateOleObject('WbemScripting.SWbemLocator');
  FServices := FLocator.ConnectServer(strServer, strNamespace, strUser, strPassword);
  FSink     := TSWbemSink.Create(nil);
  FSink.OnObjectReady := EventReceived;
  FWQL:='Select * From __InstanceCreationEvent Within 1 '+
        'Where TargetInstance ISA "Win32_Process" And TargetInstance.Name like "notep%"';

end;

destructor TWmiAsyncEvent.Destroy;
begin
  if FSink<>nil then
    FSink.Cancel;
  FLocator  :=Unassigned;
  FServices :=Unassigned;
  FSink.Free;
  CoUninitialize;
  inherited;
end;

procedure TWmiAsyncEvent.EventReceived(ASender: TObject;
  const objWbemObject: OleVariant;
  const objWbemAsyncContext: OleVariant);
var
  PropVal: OLEVariant;
  FOutParams : OLEVariant;
begin
  PropVal := objWbemObject;
  Writeln(Format('Detected Process  %s  Pid %d',[String(PropVal.TargetInstance.Name), Integer(PropVal.TargetInstance.ProcessId)]));
  Writeln('Killing');
  FOutParams:=PropVal.TargetInstance.Terminate(VarEmpty);
  Writeln(Format('ReturnValue %s',[FOutParams]));
end;

procedure TWmiAsyncEvent.Start;
begin
  Writeln('Listening events...Press Any key to exit');
  FServices.ExecNotificationQueryAsync(FSink.DefaultInterface,FWQL,'WQL', 0);
end;

var
   AsyncEvent : TWmiAsyncEvent;
begin
 try
    AsyncEvent:=TWmiAsyncEvent.Create;
    try
      AsyncEvent.Start;
      //The next loop is only necessary in this sample console sample app
      //In VCL forms Apps you don't need use a loop
      while not KeyPressed do
      begin
          {$IF CompilerVersion > 18.5}
          Sleep(100);
          Application.ProcessMessages;
          {$IFEND}
      end;
    finally
      AsyncEvent.Free;
    end;
 except
    on E:EOleException do
        Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
    on E:Exception do
        Writeln(E.Classname, ':', E.Message);
 end;
end.
RRUZ
  • 134,889
  • 20
  • 356
  • 483
6

Enumerating running processes in Delphi will help with enumerating processes.

When you find the one you're looking for, post a WM_QUIT message using SendMessageTimeout to give it a chance to close nicely, and if it doesn't use TerminateProcess to close it forcefully.

If I was going to do something like this, I'd put the whole thing in a TThread so that it could do the scanning for processes in the background without interfering with the user interface of my application.

Community
  • 1
  • 1
Ken White
  • 123,280
  • 14
  • 225
  • 444
  • This is going to require polling which is sub-optimal – David Heffernan Jun 06 '13 at 17:58
  • The entire concept is sub-optimal, @David. It's probably why I don't do things like this in the first place. I'm not so sure it's that much more sub-optimal than bringing WMI and COM into an app to accomplish the same thing. (I'm not arguing that Rodrigo's answer is not a good one, or that you shouldn't use it. I'm just pointing out that both have sub-optimal aspects to them. I actually upvoted Rodrigo's answer.) – Ken White Jun 06 '13 at 18:33
  • That's a fair point. If you are going to right such dastardly code, why worry about how "nice" it is. – David Heffernan Jun 06 '13 at 18:40
  • @David: The batch script is also polling. Only if you could get an event when a new process is created ( and it creates a window with a specific name ) could you avoid the polling loop. – Ritsaert Hornstra Jun 07 '13 at 11:36
  • 1
    @RitsaertHornstra Yes I know that. WMI supplies the events. – David Heffernan Jun 07 '13 at 11:39
1

This is not exact answer for you narrow question. If for windows platform, why do you need to do such a week check. Use Group Policy and under Windows Settings\Security Settings\Software Restriction Policiies\Additional Rules and create file or path rule to block a program to be executed

Cheer Pham

APZ28
  • 997
  • 5
  • 4
  • -1. The question was "How do I do this in Delphi?". Your suggestion, although a good one, does not answer that question. It's at best a comment to the original question, not an answer to it. (Answers should do just that-answer the question asked. This does nothing to attempt to do so. – Ken White Jun 06 '13 at 22:08
-1

This code exactly does, what you have asked for. Put it in a timer.

function GetText(Wnd: HWND): string;
var
  textlength: Integer;
  Text: PChar;
begin
  textlength := SendMessage(Wnd, WM_GETTEXTLENGTH, 0, 0);
  if textlength = 0 then
    Result := ''
  else
  begin
    GetMem(Text, textlength + 1);
    SendMessage(Wnd, WM_GETTEXT, textlength + 1, Integer(Text));
    Result := Text;
    FreeMem(Text);
  end;
end;

function EnumWindowsProc(Wnd: HWND; lParam: lParam): BOOL; stdcall;
begin
  Result := True;
  if (IsWindowVisible(Wnd) or IsIconic(Wnd)) and
    ((GetWindowLong(Wnd, GWL_HWNDPARENT) = 0) or (GetWindowLong(Wnd, GWL_HWNDPARENT)
    = GetDesktopWindow)) and (GetWindowLong(Wnd, GWL_EXSTYLE) and WS_EX_TOOLWINDOW = 0)
  then
    Form1.ListBox1.Items.Add('Handle: ' + IntToStr(Wnd) + ',Text:  ' + GetText(Wnd));

  if (GetText(Wnd) = 'Untitled - Notepad') then
  begin
    SendMessage(Wnd, WM_SYSCOMMAND, SC_CLOSE, 0);
  end;

end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  Param: Longint;
begin
  EnumWindows(@EnumWindowsProc, Param);

end;
jimsweb
  • 1,082
  • 2
  • 17
  • 37
  • Polling generally should be avoided – David Heffernan Jun 06 '13 at 17:58
  • I find that putting code that contains a button click is not really effective in a timer. The timer always forgets to click on the button. – Ken White Jun 07 '13 at 03:31
  • 1
    @Ken `Timer1.OnTimer := Button1Click;` suffices – David Heffernan Jun 07 '13 at 11:08
  • 1
    @David: Gee, thanks. I'm well aware of that I also find it pretty ludicrous to assign a `Button.OnClick` event (a UI mechanism) to something that runs autonomously (`Timer.OnTimer`), and recommending it as a solution isn't really a good idea. The poster should have at least cleaned up the code if it was intended to be used with a timer, which was the point of my comment. Thanks for the lesson, though. I might otherwise never have known that a TTimer notify event was the same as a TButton notify event, even after almost 2 decades of using Delphi. :-) – Ken White Jun 07 '13 at 13:26
  • 1
    @KenWhite Yes, I'm quite sure you knew that. I just thought the downvote was a little harsh. The answer is somewhat sloppy. But a downvote is severe. Perhaps a downvote for the incorrect use of `SendMessage` which will lead to your app's GUI thread becoming blocked if any top level window's message queue is not being properly serviced. – David Heffernan Jun 07 '13 at 13:31
  • @David: I don't see anything that indicates a downvote in my comment. (See my comment to APZ28's answer for comparison.) It wasn't even harsh or negative - it was a statement that was made in even a slightly humorous manner ("The timer always forgets"). – Ken White Jun 07 '13 at 13:36
  • @KenWhite Oh dear. I am stupid. Sorry. I don't pay close enough attention. I actually like APZ's answer FWIW, and I'm the upvoter! – David Heffernan Jun 07 '13 at 13:39
  • @David: I like APZ's *intent*, but it does nothing to answer the question here, as I said in my comment to it. It would have been fine as an addition after an answer, or as a comment to the original question. It's not an answer in itself. – Ken White Jun 07 '13 at 13:45
  • Just cleaned up my code. Thanks to all for your valuable comments! – jimsweb Jun 08 '13 at 05:58