How can I detect if an application is not used for more than x minutes in DELPHI
If you write Windows app take a look at GetLastInputInfo function.
Here is some code that looks for mouse and keybord activity with the applicatin
procedure TUserActivity.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
Handled := False;
case Msg.message Of
WM_KEYDOWN,
WM_LBUTTONDOWN,
WM_MBUTTONDOWN,
WM_RBUTTONDOWN:
Activity := TRUE;
WM_MOUSEMOVE:
begin
if (LastXYPos <> Msg.lParam) then
Activity := True;
LastXYPos := Msg.lParam;
end;
end;
end;
Use the Application.OnIdle event:
Write an OnIdle event handler to perform special processing when an application is idle. An application is idle when it is not processing code. For example, an application is idle when it is waiting for input from the user.
OnIdle is called only once, as the application transitions into an idle state. It is not called continuously unless Done is set to false. Applications that set Done to false consume an inordinate amount of CPU time, which affects overall system performance.
Use either a timer or GetLastInputInfo as #aku suggests in this event to determine if you can start your maintenance without interrupting the user
Use the applications OnDeactivate and onActive events..
That way you can abort the longrunning job if the user activates your program again.
ex:
Application.OnDeactivate = yourDeactivProcedure;
procedure mainform.YourDecativateProcedure (sender : tObject);
begin
// do your job..
end;
To handle the activate event to abort you either have to do it a bad way with a sleep and after the sleep check if i global vairable has been set.
Or you can have a theared object that does the loongrunning job.
Which I would say is much better. You can set the loongrunningjobs priority to low and it wont affect your program as much,
Depends on how you're defining "used" -- if you were monitoring yourself, you could look at the last time you responded to user interaction by logging it when it happened (mouse move/key pressed/menu event fired/etc.). Monitoring another application is tricky as it'll be harder to define that it is "in use".
That really depends on the application and what it does. While users may not interact with it in the sense of new input, they certainly might be viewing the client area that is visible.
Also - you don't say if you want to detect this internal to the app or external to the app.
Simple methods
see if it has current focus.
check if the window is visible
lots of others too, but they rely on the app itself.
You must define what you mean by "used" as well. It could mean different things and that would make significant changes to how you determine whether it met your criteria or not.
Related
I'm looking for a solution, where my app would follow another external app with it's WindowState.
The basic idea is, that I'd have actions made on next changes:
When external app is normalized: Move my form to specific coordinates
When external app is maximized: Move my form to specific coordinates
when external app is minimized: Minimize my form to taskbar.
So said, I want my app to look and behave like it was an actual part of certain external app.
So far here's what I have:
I get the external window handle ok. Using the next function case I get the results as follows:
case WindowPlacement.showCmd of
SW_HIDE: Result := 0; // hidden
SW_SHOWNORMAL: Result := 1; // show normal
SW_SHOWMINIMIZED: Result := 2; // minimized
SW_SHOWMAXIMIZED: Result := 3; // maximized
end;
This works fine. However, at the moment, I'm using timer event to trigger every 100mS, which gets this function result and then takes actions as follows:
procedure TPre2.Timer1Timer(Sender: TObject);
var t: integer;
begin
t:= CheckWindowState(AvoHandle);
case t of
0: Application.Minimize;
1: Application.MainForm.WindowState:=wsNormal; // add position later
2: Application.Minimize;
3: Application.MainForm.WindowState:=wsNormal; // add position later
end;
which gets things randomly working or not. A few times it minimizes and restores just as it should, then the next moment after the external app minimization my app stays visible instead of hiding. Timer is still looping though.
Clicking on the app icon in the taskbar causes it to minimize instantly.
Is there a better approach to what I'm trying to achieve? Or, is there any plausible reason that could cause this not working always?
thx.
This kind of task can be done using a CBT Hook. To implement this type of hook follow these steps.
Use the SetWindowsHookEx function to install a global CBT hook. (In order to install a global hook you should call this method and the callback procedure from a dll, check the MSDN Documentation for details)
In the CBTProc callback function check for the HCBT_MINMAX code.
The LParam of the CBTProc callback function will contain a show-window value (SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE and so on)
From you application use the LoadLibrary method to load the dll with the global hook implementation (you can export a method from the dll to enable or disable the hook).
To communicate your App with the dll you can use a shared memory block (CreateFileMapping, MapViewOfFile) and a custom message (RegisterWindowMessage).
At program start, in the OnActivate event handler, I need to do something which blocks the program for a few seconds. During this time the form's client area is still not completely painted, which looks ugly for the user. (During this blocked time I don't need the program to respond to clicks or other user actions, so there is no need to put the blocking operation into a thread - I just need the form to be completely painted). So I use TForm.Update and Application-ProcessMessages to update the form before the blocking operation which works very well:
procedure TForm1.FormActivate(Sender: TObject);
begin
Form1.Update;
Application.ProcessMessages;
Sleep(7000);
end;
However, I wonder whether there is not another more elegant solution for this problem. This could be for example a OnShown event implemented in a descendant of TForm which will be fired AFTER the form has been completely painted. How could such an event be implemented?
Your real problem is that you are blocking the UI thread. Simply put, you must never do that. Move the long running task onto a different thread and thus allow the UI to remain responsive.
If you are looking for event which is fired when application finishes loading/repainting you should use TApplication.OnIdle event
http://docwiki.embarcadero.com/Libraries/XE3/en/Vcl.Forms.TApplication.OnIdle
This event is fired once application is read to recieve users input. NOTE this event will be fired every time application becomes idle so you need to implement some controll variable which will tel you when OnIdle even was fired for the first time.
But as David already pointed out it is not good to block your UI (main thread). Why? When you block your main thread the application can't normally process its messages. This could lead to OS recognizing your application as being "Hanged". And aou definitly wanna avoid this becouse it could cause the users to go and forcefully kill your application whihc would probably lead to data loss. Also if you ever wanna design your application for any other platforms than Windows your application might fail the certification proces becouse of that.
In the past a simple PostMessage did the trick.
Essentially you fire it during DoShow of the base form:
procedure TBaseForm.DoShow;
begin
inherited;
PostMessage(Handle, APP_AFTERSHOW, 0, 0);
end;
then catch the msg and create an AfterShow event for all forms inherited from this base form.
But that no longer works, well not if you are skinning and have a good number of VCL controls.
My next trick was to spawn a simple thread in DoShow and check for IsWindowVisible(Handle) and IsWindowEnabled(Handle). That really sped things up it cut 250ms from load time since db opening and other stuff was already in the AfterShow event.
Then finally I thought of madHooks, easy enough to hook the API ShowWindow for my application and fire APP_AFTERSHOW from that.
function ShowWindowCB(hWnd: HWND; nCmdShow: Integer): BOOL; stdcall;
begin
Result := ShowWindowNext(hWnd, nCmdShow);
PostMessage(hWnd, APP_AFTERSHOW, 0, 0);
end;
procedure TBaseForm.Loaded;
begin
inherited;
if not Assigned(Application.MainForm) then // Must be Mainform it gets assigned after creation completes
HookAPI(user32, 'ShowWindow', #ShowWindowCB, #ShowWindowNext);
end;
To get the whole thing to completely paint before AfterShow it still needed a ProcessPaintMessages call
procedure TBaseForm.APPAFTERSHOW(var AMessage: TMessage);
begin
ProcessPaintMessages;
AfterShow;
end;
procedure ProcessPaintMessages; // << not tested, pulled out of code
var
msg: TMsg;
begin
while PeekMessage(msg, 0, WM_PAINT, WM_PAINT, PM_REMOVE) do
DispatchMessage(msg);
end;
My final test was to add a Sleep to the AfterShow event and see the Form fully painted with empty db containers since the AfterShow events had not yet completed.
procedure TMainForm.AfterShow;
begin
inherited;
Sleep(8*1000);
......
I have an OnIdle handler in my D2006 app. With this code:
procedure TMainForm.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
begin
Inc (IdleCalls) ;
Sleep (10) ;
Done := False ;
end ;
the app runs smoothly, the idle handler is called 100 times per second, and the CPU usage is next to zero.
I then added a TActionList and connected up some controls to actions, coded an Execute and Update handler.
procedure TMainForm.ActionNewButtonExecute(Sender: TObject);
begin
DoNewProject ;
end ;
procedure TMainForm.ActionNewButtonUpdate(Sender: TObject);
begin
ActionNewButton.Enabled := AccessLevelIsSupervisor ;
end;
Problem. The OnUpdate event doesn't fire. On a hunch I set Done := true in the OnIdle handler and the OnIdle handler is then only called when I move the mouse. And the Update action still doesn't fire.
Why might the Update handler not be firing, and should I set Done to true or false? Or both?
Use the source, Luke. :)
Look at the Forms unit, specifically TApplication.Idle. It contains, in part, the following:
Done := True;
try
if Assigned(FOnIdle) then FOnIdle(Self, Done);
if Done then
if FActionUpdateDelay <= 0 then
DoActionIdle
// Excluded to avoid copyright violation
// See also the else portion, which contains (in part)
else
if IdleTimerHandle = 0 then
begin
IdleTimerHandle := SetTimer(0, 0, FActionUpdateDelay, IdleTimerDelegate);
if IdleTimerHandle = 0 then
DoActionIdle
end;
finally
// Omitted
end;
As you can see, DoActionIdle is only called when either Done = True and FActionUpdateDelay <= 0 or IdleTimerHandle = 0. DoActionIdle (also part of TApplication) is what calls UpdateAction. So if neither of the above conditions are met, TAction.OnUpdate is never called.
There's a separate method, TApplication.DoMouseIdle, that you may want to peruse as well.
As mentioned in the comments, Sleep in the idle handler will do no good, also the bacground processing will stall if there is no activity on the application.
You can however lower the CPU usage w/o much disturbing effects: After processing all OnIdle events, the application will call WaitMessage (which will sleep while the message queue is empty), if the Done parameter is True - you can just unconditionally set it in your handler.
As for background processing, use either a thread and call back to the main thread via Synchronize or, if you really-really have to, use a timer and don't ever forget to handle reentrancy (both solutions will by the way wake the application even while WaitMessage).
Get rid of that OnIdle event handler, you accepted it is there just in case.
If you later need to perform background tasks, learn how to use threads. To get a specific frequency, you're allowed to use sleep or any other technique within a thread.
My advice is in this way because, as you see, that way of do things is interfering with other parts of your application. If it is a bug in the TApplication, I don't know, maybe it is. If you want to investigate more, make a copy of your project, check everything and if you think this have to work another way, fill a QC entry about that.
I was looking the XE source code and it seems Ok, they set an event to update the actions if the Idle event is not done.. I don't see a bug there. I have no pre-2010 ready installations to check ancient versions.
I've got a legacy app here that has a few 'time-consuming' loops that get fired off as a result of various user interaction. The time-consuming code periodically updates something on the screen with progress information (typically a label) and then, seemingly to persuade the visual refresh to happen there-and-then, the code calls Application.ProcessMessages (argh!).
We all know now what kind of trouble this can introduce to a GUI app (being charitable, it was a more innocent time back then) and we're finding that sure as eggs, from time to time we get users achieving the impossible with the program because they're clicking on controls while the program is 'busy'.
What's the best way of periodically refreshing the visuals of the form without taking-on other events/messages etc?
My thoughts were to either;
- disable all of the controls before doing anything time-consuming, and leaving the '...ProcessMessages' calls in place to 'force' the refresh, or
- find another way to refresh a control periodically
I can do the former but it left me wondering - is there a better solution?
example code from legacy;
i:=0;
while FJobToBeDone do
begin
DoStepOfLoop;
inc(i);
if i mod 100 = 0 then
begin
UpdateLabelsEtc;
Application.ProcessMessages;
end;
end;
I can already hear you all fainting, at the back. :-)
If you call Update() on the controls after you have changed properties you will force them to redraw. Another way is to call Repaint() instead of Refresh(), which implies a call to Update().
You may need to call Update() on parent controls or frames as well, but this could allow you to eliminate the ProcessMessages() call completely.
The solution I use for long updates is by doing the calculations in a separate thread. That way, the main thread stays very responsive. Once the thread is done, it sends a Windows message to the main thread, indicating the main thread can process the results.
This has some other, severe drawbacks though. First of all, while the other thread is active, you'll have to disable a few controls because they might restart the thread again.
A second drawback is that your code needs to become thread-safe. This can be a real challenge sometimes. If you're working with legacy code, it's very likely that your code won't be thread-safe.
Finally, multi-threaded code is harder to debug and should be done by experienced developers.
But the big advantage of multi-threading is that your application stays responsive and the user can just continue to do some other things until the thread is done. Basically, you're translating a synchronous method into an asynchronous function. And the thread can fire several messages indicating certain controls to refresh their own data, which would be updated immediately, on the fly. (And at the moment when you want them to be updated.)
I've used this technique myself quite a few times, because I think it's much better. (But also more complex.)
Do not call Application.Processmessages, This is slow and might generate unlimited recursion.
For example, to update everything in Panel1 without flick, we can use this method:
procedure TForm1.ForceRepaint;
var
Cache: TBitmap;
DC: HDC;
begin
Cache := TBitmap.Create;
Cache.SetSize(Panel1.Width, Panel1.Height);
Cache.Canvas.Lock;
DC := GetDC(Panel1.Handle);
try
Panel1.PaintTo(Cache.Canvas.Handle, 0, 0);
BitBlt(DC, 0, 0, Panel1.Width, Panel1.Height, Cache.Canvas.Handle, 0, 0, SRCCOPY);
finally
ReleaseDC(Panel1.Handle, DC);
Cache.Canvas.Unlock;
Cache.Free;
end;
end;
For better performance, the cache bitmap should be created at first and free when the process has finished
The technique you're looking for is called threading. It is a diffucult technique in programming. The code should be handled with care, because it is harder to debug. Whether you go with threading or not, you should definitely disable the controls that users should not mess with (I mean the controls that can effect the ongoing process). You should consider using actions to enable/disable buttons etc...
Rather than disable the controls, we have a boolean var in the form FBusy, then simply check this when the user presses a button, we introduced it for the exact reasons you mention, users clicking buttons while they wait for long running code to run (it scary how familiar your code sample is).
So you end up with something like
procedure OnClick(Sender:TObejct);
begin
if (FBusy) then
begin
ShowMessage('Wait for it!!');
Exit;
end
else FBusy := True;
try
//long running code
finally
FBusy := False;
end;
end;
Its impotent to remember to rap the long running code up in a try-finally block in case of exits or exception, as you would end up with a form that will not work.
As suggested we do use threads if its for code that will not affect the data, say running a report or data analysis, but some things this is not an options, say if we are updating 20,000 product records, then we don't want anyone trying to sell or other wise altering the records mid flight, so we have to block the application until it is done.
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.