Delphi - Gracefully Closing Created Process in Service. (using tprocess / createProcess) - delphi

I have a Windows Service written in Delphi which runs a number of programs.
On Stopping the service, I want to also close these programs. When the service was originally written, this worked fine, but I think I've updated the tProcess component and now - The subordinate programs are not being closed.
in tProcess - Here's the code which starts the new processes.
if CreateProcess( nil , PChar( FProcess.Command ) , nil , nil , False ,
NORMAL_PRIORITY_CLASS , nil , Directory ,
StartupInfo , ProcessInfo ) then
begin
if FProcess.Wait then
begin
WaitForSingleObject( ProcessInfo.hProcess , Infinite );
GetExitCodeProcess( ProcessInfo.hProcess , ExitCode );
if Assigned( FProcess.FOnFinished ) then
FProcess.FOnFinished( FProcess , ExitCode );
end;
CloseHandle( ProcessInfo.hProcess );
CloseHandle( ProcessInfo.hThread );
end;
Each of the executables called by this are Windows GUI Programs (With a close button at the top).
When I stop the service, I also want to stop (not kill) the programs I've started up via the createProcess procedure.
How would you do this?

I'd use TJvCreateProcess component of JVCL which wraps about any process related functionality of win32 in a graceful way. This answer comes from Dont-touch-winapi-unless-really-required department :-)

You want to enumerate open windows that match your launched ProcessId and tell those windows to close. Here's some sample code for that:
uses Windows;
interface
function MyTerminateAppEnum(hHwnd:HWND; dwData:LPARAM):Boolean; stdcall;
implementation
function MyTerminateAppEnum(hHwnd:HWND; dwData:LPARAM):Boolean;
var
vID:DWORD;
begin
GetWindowThreadProcessID(hHwnd, #vID);
if vID = dwData then
begin
PostMessage(hHwnd, WM_CLOSE, 0, 0); //tell window to close gracefully
Result := False; //can stop enumerating
end
else
begin
Result := TRUE; //keep enumerating until you find your id
end;
end;
Then you'll want to utilize this in your code when you want to shut down the launched applications:
Procedure TerminateMe(YourSavedProcessInfo:TProcessInformation);
var
vExitCode:UINT;
begin
GetExitCodeProcess(YourSavedProcessInfo.hProcess, vExitCode);
if (vExitCode = STILL_ACTIVE) then //launched app still running..
begin
//tell it to close
EnumWindows(#MyTerminateAppEnum, YourSavedProcessInfo.dwProcessId);
if WaitForSingleObject(YourSavedProcessInfo.hProcess, TIMEOUT_VAL) <> WAIT_OBJECT_0 then
begin
if not TerminateProcess(YourSavedProcessInfo.hProcess, 0) then //didn't close, try to terminate
begin
//uh-oh Didn't close, didn't terminate..
end;
end;
end;
CloseHandle(YourSavedProcessInfo.hProcess);
CloseHandle(YourSavedProcessInfo.hThread);
end;

The only generic way to stop a process is to use TerminateProcess. But that's as far from graceful as you can get. To gracefully close a process, you need to tell the process that you'd like it to stop, and then hope it obeys. There is no way to do that in general, though.
For a GUI program, the usual way to tell it that you want it to stop running is to close its main window. There's no formal idea of "main window," though. A program can have zero or more windows, and there's no way to know externally which one you're supposed to close in order to make the program stop working.
You could use EnumWindows to cycle through all the windows and select the ones that belong to your process. (They'd be the ones for which GetWindowThreadProcessId gives the same process ID that CreateProcess gave you.)
Closing a window might not be enough. The program might display a dialog box (prompting for confirmation, or asking to save changes, etc.). You would need to know in advance how to dismiss that dialog box.
Non-GUI programs can have similar problems. It might be enough to simulate a Ctrl+C keystroke. It might catch and handle that keystroke differently, though. It might have a menu system that expects you to type "Q" to quit the program.
In short, you cannot gracefully close a program unless you know in advance how that program expects to be closed.

Related

Detect key(s) held down in another process

I have a Delphi 2007 Win32 executable which sends keystrokes to other applications. My app is invoked from within these target applications by a hotkey like F11 or Shift+F11.
I want users to be able to hold down a key to abort the keystroke sending (say, if they realize they invoked my app in the wrong location). I had thought Shift, Ctrl, and Alt were good candidates because, alone, those key presses aren't likely to disrupt anything in the target application. (Escape, for instance, is a bad choice, as it might cause the target application to close one or more windows.)
I wrote the function farther below and call it periodically as follows, while sending keystrokes with the intent of detecting keys held down.
if wsAnyKeysDownInWindow( TgtWindow, [VK_Escape, VK_Menu{Alt}, VK_Control, VK_Shift] ) then
Abort;
Problem is, my app sends keystroke combinations like Shift+Tab and Ctrl+Home, which (I think) makes this approach fail--it always detects a down state for Shift and/or Ctrl. (I also tried a similar function which called SetKeyboardState just prior to beginning to send keystrokes, to set the key states' high-order (down) bit but that didn't help.)
Anyone think of a workable approach, short of hooking the keyboard?
function wsAnyKeysDownInWindow(Handle: HWnd; VKeys: array of byte): boolean;
{ Checks whether each of the VKeys set of virtual keys is down in Handle,
a window created by another process. }
var
OtherThreadID : integer;
State: TKeyboardState;
AKey: byte;
begin
Result := False;
if not IsWindow(Handle) then
exit;
OtherThreadID := GetWindowThreadProcessID( Handle, nil);
if AttachThreadInput( GetCurrentThreadID, OtherThreadID, True ) then try
GetKeyboardState(State);
for AKey in VKeys do
if (State[AKey] and 128) <> 0 then begin //If high-order bit is set, key is down
Result := True;
exit;
end;
finally
AttachThreadInput( GetCurrentThreadID, OtherThreadID, False );
end;
end;
Consider using Scroll Lock for this. It is rarely used (only of Excel comes to my mind), and you will have even visual indicator if keys are being sent or not.
BTW, Alt is not a good choice for another reason - it invokes the main menu in an application (if there is one, of course).

Check is current active window Desktop or no

I try to check if current active window is Desktop do something , i wrote below code in a timer but the handle value returned by GetDektopWindow & GetForegroundWindow is not same value :
if GetForegroundWindow = GetDesktopWindow then
// Do something
How do this ?
// not defined in D2007
function GetShellWindow: HWND; stdcall; external user32;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if GetForegroundWindow = GetShellWindow then
[..]
end;
With the only non-explorer shell I use (sharpe) it fails though.
update:
Sometimes the window hierarchy of the desktop is different (see Andreas' comments). The below shot is Spy++'s take when Windows 7's desktop picture rotation functionality is activated. Some 'WorkerW' window takes over the screen and it is the one that gets activated when clicked on the desktop. Since GetShellWindow returns the 'Progman's handle, the above test fails.
At this point it might seem reasonable to test if the foreground window has the shell's default view window as its immediate child, however I saw multiple references that indicate multiple 'WorkerW' windows might get nested. So I think the below would be a more fail-safe approach:
procedure TForm1.Timer1Timer(Sender: TObject);
function HasDefViewChild(Wnd: HWND): Boolean;
begin
Result := Wnd <> 0;
if Result then begin
Result := FindWindowEx(Wnd, 0, 'SHELLDLL_DefView', nil) <> 0;
if not Result then
Result := HasDefViewChild(FindWindowEx(Wnd, 0, 'WorkerW', nil));
end;
end;
begin
if HasDefViewChild(GetForegroundWindow) then
[...]
end;
This will work when the foreground window is 'Progman', because then the 'DefView' is 'Progman's child. OTOH when 'WorkerW' is the active window, the code will iterate if the first child is not 'DefView' and yet another 'WorkerW' instead.
A great tool for figuring out the structure of window parent/child relationships, window classes, etc., is WinDowse by Greatis Software.
http://www.greatis.com/delphicb/windowse/
I would start there. And I'd output the values of GetForegroundWindow and GetDesktopWindow (etc.,) onto labels in your test app. So you can see what those values are, as you poke around with WinDowse, in real time.
I search about this , GetWindowDesktop return the handle of desktop window but the desktop window is under another window called shell , so when you switch to dektop really you switch to shell window and must get shell handle , if you terminate process of shell window ( explorer.exe ) then you can see the real dektop window .

I have the Process ID and need to close the associate process programmatically with Delphi 5

Can anyone help me with a coding example to close the associated process when I have the Process ID. I will be using Delphi 5 to perform this operation programmatically on a Windows 2003 server.
If you have a process id and want to force that process to terminate, you can use this code:
function TerminateProcessByID(ProcessID: Cardinal): Boolean;
var
hProcess : THandle;
begin
Result := False;
hProcess := OpenProcess(PROCESS_TERMINATE,False,ProcessID);
if hProcess > 0 then
try
Result := Win32Check(Windows.TerminateProcess(hProcess,0));
finally
CloseHandle(hProcess);
end;
end;
Use EnumWindows() and GetWindowProcessThreadId() to locate all windows that belong to the process, and then send them WM_CLOSE and/or WM_QUIT messages.
Along with the WM_CLOSE and WM_QUIT, you can make it really elegant and simply launch a second instance of the app with STOP as the parameter. Like this:
In the project main body...
if ((ParamCount >= 1) and (UpperCase(paramstr(1)) = 'STOP')) then
// send the WM_CLOSE, etc..
When the app launches and sees that it has a parameter of 'STOP', then hunt down the first instance and kill it. Then quit the second instance without creating your main form, etc.. This way, you don't have to have to write/deploy a second program just to kill the first one.
If you want to close a program properly without killing the process:
procedure TmyFRM.btn_closeClick(Sender: TObject);
var
h: HWND;
begin
h := FindWindow('Notepad', nil);
if h <> 0 then
PostMessage(h, WM_QUIT, 0, 0);
end;
and consider it sometimes you can use WM_Close instead of WM_Quit and you can work around SendMessage instead of PostMessage too. when you are trying to close a program properly without killing its process, so you are following the program routines and programs may respond diffrent to closing messages; for example, some programs will be Minimized to Tray after closing them and etc...

Added the {APPTYPE CONSOLE} directive and now my application runs very slowly. Moving the mouse makes it run faster

I am trying to extend a 3rd party application so that it can be invoked via command line in addition to using the windows form GUI (mixed mode is desired). It's a fairly simple program which basically loads a file and then you click a button it starts sending UDP network packets.
I need to invoke the application from another and would like to pass in an argument and need to be able to return the ExitCode to the calling app. From what i've read, in order to do so you need to add the compiler directive {APPTYPE CONSOLE}.
I did this and my application worked as I wanted it to except sending the network packets slowed down to a crawl. I found that whenever I moved my mouse around on the form. That the network transfer rate increased significantly. I suspect there is some type of Windows Message queue problem and moving mouse is causing interrupts which in turn is causing the message queue to be processed?
I have googled around and tried calling Application.ProcessMessages and PeekMessages in a Timer with a 1ms interval and that didn't help at all. I found in this user manual for some other application it says that Indy 10 is supported in both APPTYPE CONSOLE and GUI types. Quite frankly this just confuses me as I would have assumed that all network library would work in both modes... but like I said I'm not familiar with Delphi.
I am positive that the issue is isolated to a single line in my application and that is whether or not {APPTYPE CONSOLE} is included or not.
Anyone have any ideas?
Version Info:
Delphi 7 Personal (Build 4.453)
Indy 9.0.4
If you add {APPTYPE CONSOLE} to your application even though you desire mixed mode execution, then you will have to live with a console even when the application is in GUI mode. You can of course close the console, but this will cause some flicker and feels a bit hackish to me.
You should be able to do what you want without a console program. A small test program proves that the exit code can be read from a GUI program:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Close;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ExitCode := 42;
Timer1.Interval := 1000;
Timer1.Enabled := TRUE;
end;
If this is executed with the following cmd file:
#echo off
start /WAIT project1.exe
echo %ERRORLEVEL%
the program shows its main form for 1 second, closes, and the script prints 42 to the console window.
Now for capturing the output - doing this from a GUI program is actually easier than doing it from a console program, if you allow for the use of a temporary file. You need to start the program with a command line parameter anyway, so why not give it the name of a temporary file, wait for the application to finish, read in the file and delete it afterwards?
If you want an application to return an "error" code there is no need to make it a console application. You only need to set the ExitCode, e.g.
ExitCode := 10;
in a batch file
#Echo off
project1
echo %errorlevel%
Will display the application, then display 10 when.
Note: It is also possible to create a console window dynamically from the windows API using AllocConsole or to attach using AttachConsole.
I created an object wrapper for this once, but no longer have the code available. From memory it didn't support redirection (because I didn't need it).
If I understand you correctly, then you want your app to have two modes:
If no argument is passed, run in GUI mode
Run in non-GUI mode otherwise
The easiest is if you can centralize your logic so it can be called from one method (CoreLogic in my example).
The below app then should work fine.
Two tricks:
Application.ShowMainForm := False; that will not make the MainForm show at all.
ExitCode := 327; which will set your return code (like mghie and Gerry already mentioned).
A few notes:
because the CoreLogic does not process any windows messages, anything in your application that depends on Windows messages being processed will stall.
if you need windows message processing, then just all Application.ProcessMessages() inside your CoreLogic
if you need your form to be visible, then you change the logic inside your MainForm to test for the commandline parameters, and exit when it's work as been done (by calling Application.Terminate()). The best place to put that logic in is the event method for the MainForm.OnShow event.
Hope this helps :-)
program VCLAppThatDoesNotShowMainForm;
uses
Forms,
MainFormUnit in 'MainFormUnit.pas' {MainForm},
Windows;
{$R *.res}
procedure CoreLogic;
begin
Sleep(1000);
ExitCode := 327;
end;
procedure TestParams;
begin
if ParamCount > 0 then
begin
MessageBox(0, CmdLine, PChar(Application.Title), MB_ICONINFORMATION or MB_OK);
CoreLogic();
Application.ShowMainForm := False;
end;
end;
begin
Application.Initialize();
Application.MainFormOnTaskbar := True;
TestParams();
Application.CreateForm(TMainForm, MainForm);
Application.Run();
end.
A timer with 1ms will only fire about every 40 ms (due to Windows limitations), so it won't help. I have seen effects like you describe with mixed console and GUI apps, another is that they don't minimize properly.
Instead of enabling the console in the project, you could probably use the CreateConsole API call (Not sure whether the name is correct) to create one after the programm was started. I have seen no adverse effects in the one (!) program I have done this.
But this is only necessary if you want to write to the console. If you only want to process command line parameters and return an exit code, you do not need a console. Just evaluate the ParamCount/ParamStr functions for the parameters and set ExitCode for the return value.
If some threads in your console application call Synchronize (and I guess the Indy stuff is actually doing that), you have to make some preparations:
Assign a method to the WakeMainThread variable. This method must have the signature of TNotifyEvent.
Inside this method call CheckSynchronize.
For additional information see the Delphi help for these two items.

How can I tell if another instance of my program is already running?

How do i tell if one instance of my program is running?
I thought I could do this with a data file but it would just be messy :(
I want to do this as I only want 1 instance to ever be open at one point.
As Jon first suggested, you can try creating a mutex. Call CreateMutex. If you get a non-null handle back, then call GetLastError. It will tell you whether you were the one who created the mutex or whether the mutex was already open before (Error_Already_Exists). Note that it is not necessary to acquire ownership of the mutex. The mutex is not being used for mutual exclusion. It's being used because it is a named kernel object. An event or semaphore could work, too.
The mutex technique gives you a Boolean answer: Yes, there is another instance, or no, there is not.
You frequently want to know more than just that. For instance, you might want to know the handle of the other instance's main window so you can tell it to come to the foreground in place of your other instance. That's where a memory-mapped file can come in handy; it can hold information about the first instance so later instances can refer to it.
Be careful when choosing the name of the mutex. Read the documentation carefully, and keep in mind that some characters (such as backslash) are not allowed in some OS versions, but are required for certain features in other OS versions.
Also remember the problem of other users. If your program could be run via remote desktop or fast user switching, then there could be other users already running your program, and you might not really want to restrict the current user from running your program. In that case, don't use a global name. If you do want to restrict access for all users, then make sure the mutex object's security attributes are such that everyone will be able to open a handle to it. Using a null pointer for the lpSecurityAttributes parameter is not sufficient for that; the "default security descriptor" that MSDN mentions gives full access to the current user and no access to others.
You're allowed to edit the DPR file of your program. That's usually a good place to do this kind of thing. If you wait until the OnCreate event of one of your forms, then your program already has a bit of momentum toward running normally, so it's clumsy to try to terminate the program at that point. Better to terminate before too much UI work has been done. For example:
var
mutex: THandle;
mutexName: string;
begin
mutexName := ConstructMutexName();
mutex := CreateMutex(nil, False, PChar(mutexName));
if mutex = 0 then
RaiseLastOSError; // Couldn't open handle at all.
if GetLastError = Error_Already_Exists then begin
// We are not the first instance.
SendDataToPreviousInstance(...);
exit;
end;
// We are the first instance.
// Do NOT close the mutex handle here. It must
// remain open for the duration of your program,
// or else later instances won't be able to
// detect this instance.
Application.Initialize;
Application.CreateForm(...);
Application.Run;
end.
There's a question of when to close the mutex handle. You don't have to close it. When your process finally terminates (even if it crashes), the OS will automatically close any outstanding handles, and when there are no more handles open, the mutex object will be destroyed (thus allowing another instance of your program to start and consider itself to be the first instance).
But you might want to close the handle anyway. Suppose you chose to implement the SendDataToPreviousInstance function I mentioned in the code. If you want to get fancy, then you could account for the case that the previous instance is already shutting down and is unable to accept new data. Then you won't really want to close the second instance. The first instance could close the mutex handle as soon as it knows it's shutting down, in effect becoming a "lame duck" instance. The second instance will try to create the mutex handle, succeed, and consider itself the real first instance. The previous instance will close uninterrupted. Use CloseHandle to close the mutex; call it from your main form's OnClose event handler, or wherever else you call Application.Terminate, for example.
You can create a Semaphore and stop execution (put the code into your *.dpr file) and bring you running application to the screen.
var
Semafor: THandle;
begin
{ Don't start twice ... if already running bring this instance to front }
Semafor := CreateSemaphore(nil, 0, 1, 'MY_APPLICATION_IS_RUNNING');
if ((Semafor <> 0) and { application is already running }
(GetLastError = ERROR_ALREADY_EXISTS)) then
begin
RestoreWindow('TMyApplication');
CloseHandle(Semafor);
Halt;
end;
Application.CreateForm(....);
Application.Initialize;
Application.Run;
CloseHandle(Semafor);
end;
EDIT (added the RestoreWindow method):
The aFormName is the name of your main form class in your application.
procedure RestoreWindow(aFormName: string);
var
Wnd,
App: HWND;
begin
Wnd := FindWindow(PChar(aFormName), nil);
if (Wnd <> 0) then
begin { Set Window to foreground }
App := GetWindowLong(Wnd, GWL_HWNDPARENT);
if IsIconic(App) then
ShowWindow(App, SW_RESTORE);
SetForegroundwindow(App);
end;
end;
The all-mighty JVCL has a component for this purpose. See "TJvAppInstances".
The normal solution is to create a named, system-wide mutex.
If you manage to create it, you're the one running application.
If you don't, you know there's a different one.
EDIT:
I haven't provided code as I don't know Delphi. I can provide C# code if that would be helpful though.
You create a system mutex.
I don't have Delphi code, but here's C++ code:
HANDLE Mutex;
const char MutexName[] = "MyUniqueProgramName";
Mutex = OpenMutex(MUTEX_ALL_ACCESS, false, MutexName);
if (Mutex)
throw Exception("Program is already running.");
else
Mutex = CreateMutex(NULL, true, MutexName);
I'd like to add one point to the excellent answer by Rob Kennedy (apart from the fact that it would be best to make a function out of his code instead of copying everything into the DPR file. You only need two parameters, the name of the mutex, and a boolean whether the mutext should be per-user or system-wide).
The answer does not give much consideration to the naming of the mutex. If you expect your program to be installed via Inno Setup (and maybe other setup tools too) you should choose the name carefully, as the mutex can be used to have the setup program check whether the application is currently running, and alert the user that they should close all instances of the application. If you choose to allow one instance of the program per user you may need to create a second system-wide mutex too, as the setup may need to have no running instances of the application at all in order to be able to replace files. The name that is to be used for synchronization with an InnoSetup installer must be hard-coded.
I would say that there are several different strategies that you can employ. But the easiest one (and not platform specific) is the one you yourself suggested, namely to, at the start of the program check to see if there is a lock file created in a set, specific location. If this lock file exists, then another instance is already running, if it doesn't exist, then there is not another instance running. When your program exits, you delete the lock file.
However, employing this strategy you have another problem, what happens if your program crashes? The lock file still remains, and this specific case need to be handled.
Another strategy is the system-wide mutex solution, where you register your presence within the operating system (or it's also plausible that this is done automagically). When a second instance then tries to start, it checks if there's already a process active with a specific ID. If it already exists, the second process chooses not to start, and optionally brings the first process' window in focus (if the process in question owns a window that is).
However, this strategy is platform specific, and the implementation will differ from platform to platform.
You can simply use FindWindow windows api function. In delphi class name of the window is the same as class name, you can redefine class name by overriding CreateParams function. To check if window exists add code before main window is created , before Application.Initialize;
Program test
var
handle :HWND;
begin
handle := FindWindow('TMySuperApp', nil);
if IsWindow(handle) then
begin
//app is running
exit;
end.
Application.Initialize;
Application.CreateForm(TMySuperApp, SuperApp);
Application.Run;
end;
Controlling the number of application instances:
http://delphi.about.com/od/windowsshellapi/l/aa100703a.htm
If You want to stop execution your app more then once in the same time (put the code into *.dpr file of the project).
will show a message after second app will be running and stop it instantly .
Forms,
Unit1 in 'Unit1.pas' {Form1},
// add this units ....
TlHelp32,SysUtils,Windows,Dialogs;
{$R *.res}
function ProcessCount(const ExeName: String): Integer;
var
ContinueLoop: BOOL;
FSnapshotHandle: THandle;
FProcessEntry32: TProcessEntry32;
begin
FSnapshotHandle:= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize:= SizeOf(FProcessEntry32);
ContinueLoop:= Process32First(FSnapshotHandle, FProcessEntry32);
Result:= 0;
while Integer(ContinueLoop) <> 0 do begin
if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
UpperCase(ExeName)) or (UpperCase(FProcessEntry32.szExeFile) =
UpperCase(ExeName))) then Inc(Result);
ContinueLoop:= Process32Next(FSnapshotHandle, FProcessEntry32);
end;
CloseHandle(FSnapshotHandle);
end;
begin
if ProcessCount(ExtractFileName(Application.ExeName)) > 1 then begin
MessageDlg('Application is already running!', mtError, [mbOK], 0);
Application.Terminate;
end else begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end;
end.
See this unit (using CreateMutex): UiApp
Additionally at this page, you can read the advantages and disadvantages for to this work with differents methods (mutex, FindWindows,...).
This unit have the solution to activate the previos instance of the application when this is detected.
Regards and excuse-me for my bad english.
Neftalí -Germán Estévez-
In the past, I've used a socket to prevent multiple instances from running at the same time. If the socket is in use, don't continue the program, if it's available let everything run as normal.

Resources