Inno Setup Kill a running process

10,057

Solution 1

Version with use of Win32_Process, you call the procedure with i.e. 'notepad.exe':

const wbemFlagForwardOnly = $00000020;

procedure CloseApp(AppName: String);
var
  WbemLocator : Variant;
  WMIService   : Variant;
  WbemObjectSet: Variant;
  WbemObject   : Variant;
begin;
  WbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  WMIService := WbemLocator.ConnectServer('localhost', 'root\CIMV2');
  WbemObjectSet := WMIService.ExecQuery('SELECT * FROM Win32_Process Where Name="' + AppName + '"');
  if not VarIsNull(WbemObjectSet) and (WbemObjectSet.Count > 0) then
  begin
    WbemObject := WbemObjectSet.ItemIndex(0);
    if not VarIsNull(WbemObject) then
    begin
      WbemObject.Terminate();
      WbemObject := Unassigned;
    end;
  end;
end;

Solution 2

Your code makes several assumptions that are wrong, and you should address them all:

  • It's a bad idea to close any program without asking the user first. Consider the case of Internet Explorer, the user may have some web pages open with forms partially filled in - if you close them unconditionally you may actively piss off the user because they lost work.
    What you should do instead: Check whether any program is open that interferes with the normal progress of your installation, and if one is found alert the user that they need to close it. Ask them in a loop whether to cancel or to repeat the check, and do not continue unless all interfering programs are closed.

  • If you really need to send a WM_CLOSE message to a window, then the support function FindWindowByWindowName() won't work. Why would the window name of an Internet Explorer window be "iexplore.exe"? It will be the title of the open web page, so you won't know beforehand what it is.
    What you should do instead: Use the much more appropriate FindWindowByClassName() function. With a tool like Spy++ or UI Spy or similar you can find out what the class name of a program window is. For Internet Explorer on my system it is "IEFrame", but can you really rely on that? Again, better give the user control over things, and repeat the running process check until all interfering programs have been closed.

  • Closing a single window with the matching window or class name may not be enough. A single Windows process may have more than one window open, and there may be several instances of the same executable running, each with one or more windows open.
    What you should do instead: Execute your checking (and closing) code in a loop until everything is ready to proceed.

  • But even then you should be prepared for things to go wrong - Windows is a multitasking system, and new processes may be spawned or windows be opened at any time. If AutoPlay is enabled then inserting a CD or USB stick could open an Internet Explorer window right when you think that you have closed them all.

Solution 3

const 
  WM_QUIT = 18;

function InitializeUninstall(): Boolean;
var
  winHwnd: longint;
  retVal : boolean;
  strProg: string;
begin
  Result := true;
  strProg := 'Readme.txt - Notepad';
  winHwnd := FindWindowByWindowName(strProg);
  MsgBox('winHwnd: ' + inttostr(winHwnd),  mbInformation, MB_OK );
  if winHwnd<>0 then begin                    { no module found or ignored pressed}
    MsgBox('ravi:' #13#13 'Bye bye!', mbInformation, MB_OK); 
    abort();                                      { continue setup  }
  end;
end;

It works fine..

Using that code, I'm able to trace exit application which is running...

Solution 4

I had this problem, and wrote a short C++ program to kill certain processes that might be in a hung state and thus unable to respond to messages telling them to die. I only did this with applications that were part of my product, not system applications.

You could do the same thing in Pascal script if you want to suffer the pain of getting the calling parameters correct. Here is an outline of what I did:

Get the location of CSIDL_PROGRAM_FILES. Inno setup can do this with its {pf} or {pf32} constants. Or call the Windows api SHGetSpecialFolderLocation function.

Set a string equal to the path to the process you want to kill, for example String target = pf + "Mycompany/MyAppFolder/myHelperApp.exe"

Call the Windows api function CreateToolhelp32Snapshot, which returns a list of running processes.

Look through this returned list for your target using the Windows api's OpenProcess and GetModuleFleNameEx.

When you find the target call the Windows api TerminateProcess on the target process handle.

Share:
10,057
BrunoBozic
Author by

BrunoBozic

Updated on June 04, 2022

Comments

  • BrunoBozic
    BrunoBozic almost 2 years

    I have already implemented a way to find whether a process ("iexplore.exe") is running, I now need to find a way to close it (terminate the process) from Inno Setup.

    strProg := 'iexplore.exe';
    winHwnd := FindWindowByWindowName(strProg);
    MsgBox('winHwnd: ' + inttostr(winHwnd),  mbInformation, MB_OK );
    if winHwnd <> 0 then
      retVal:=postmessage(winHwnd,WM_CLOSE,0,0);
    

    The message box in the example above will always return 0 therefore no handle is ever gotten. (the WM_CLOSE constant in the example is properly initialized) I need another way of doing this, and hopefully one that does not involve writing a C++ DLL that does this (I am not proficient in C++, I might be able to write a DLL in C#, however I don't know whether Inno Setup will interop with that).

    This C# DLL would get the process list, iterate thru the names of processes, find a match (=="iexplorer") and then kill the processes with that name...however I am still hoping to find an easier solution so that I wouldnt have to interop it with Pascal script.

    Thanks in advance!