Delphi - Have external's app minimization to trigger another app minimize procedure - delphi

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).

Related

TDialogService.ShowMessage not blocking on Win10

In my Delphi 10.4 FMX program, I am asking the user for a new file name using the code below
procedure TForm6.btnBlockingClick(Sender: TObject);
begin
//In Win10, this blocks form access when ShowMessage is called
NameCallBack(mrOk, ['name']);
end;
procedure TForm6.btnNonBlockingClick(Sender: TObject);
begin
//In Win10, this does not block form access when ShowMessage is called in the NameCallBack routine.
TDialogService.InputQuery('Enter name', ['Name'], [''], NameCallBack);
end;
procedure TForm6.NameCallBack(const AResult: TModalResult; const AValues: array of string);
begin
if aResult = mrOK then
TDialogService.ShowMessage('Ok pressed')
else
TDialogService.ShowMessage('Cancel pressed');
end;
Any idea why ShowMessage is not blocking when NameCallBack is used as the Callback event for InputQuery? In Win10, what is the best way to show a message to a user in this type of callback routine that keeps the user from accessing the underlying form until the dialog is closed in some way.
FYI: the same thing happens if you use MessageDialog, to allow user interaction, instead of ShowMessage in the callback routine.
Note: this logic works in OSX and IOS, with both dialogs blocking. On Android, neither dialog is blocking but is not a problem, as touching anywhere but the dialogs closes the dialog and requires a second touch to interact with the underlying form again. On Win10, I can doing anything I want with the underlying form while the ShowMessage dialog is visible when used in a callback event.
Thanks for any help with this.
This may not be the best way to work around the bug I found, but here is what I did in case someone else has this problem.
I added a timer to my form, set the interval to 200 and disabled it.
For any TDialogServices.MessageDialog and TDialogServices.InputQuery callback routine where the callback routine also called MessageDialog or ShowMessage, I moved the callback logic into new routines. I then changed the callback routines to set a form variable to indicate which callback routine was called, saved off the relevant info from the callback routine as needed, then enabled the timer.
In the timer event, I first disable the timer then call the new routines based on the form variable.
This now allows both the original dialog and the dialog needed in the callback routine to be blocking on Win10. In addition, Android, OSX and IOS appear to still work correctly as explained in my question.

SetWindowsHookEx creates a local hook. How to make it global?

In a Delphi XE application I am trying to set up a global hook to monitor focus changes. The hook is created in a dll:
focusHook := SetWindowsHookEx( WH_CBT, #FocusHookProc, HInstance, 0 );
// dwThreadId (the last argument) set to 0 should create a global hook
In the same dll I have the hook procedure that posts a message to the host app window:
function FocusHookProc( code : integer; wParam: WPARAM; lParam: LPARAM ) : LResult; stdcall;
begin
if ( code < 0 ) then
begin
result := CallNextHookEx( focusHook, code, wParam, lParam );
exit;
end;
result := 0;
if ( code = HCBT_SETFOCUS ) then
begin
if ( hostHWND <> INVALID_HANDLE_VALUE ) then
PostMessage( hostHWND, cFOCUSMSGID, wParam, lParam );
end;
end;
This works, but the host only receives notifications on focus changes within the application itself. There is a memo and a few TButtons on the main form, and switching focus between them produces the expected message. However, any focus changes outside the application itself are never reported.
I suppose it has something to do with multiple instances of the DLL getting injected into other processes. There is a similar question with an accepted reply here, but it is for C, and I can't quite see how I can do the same in a Delphi dll (e.g. the pragma statements to set up shared memory).
(This is mostly a proof of concept, but I'd still like to get it to work. I need to know what window was active just before my app got activated by way of clicking, alt+tab, activation hotkey etc. The problem is that if the mouse or alt+tab is used, GetForegroundWindow always returns my own app's window handle, no matter how early I put it, such as by hooking the application's main message queue. So the hook seems like the only viable solution, though I don't really like the idea.)
Since the DLL is injected into another process, you're not going to get any breakpoints hit for anything other than the process you're debugging. Also, each instance of the DLL in the other process also get their own global/static data. If hostHWND is a global, it won't be the same value in the other process as it is in this one. In fact it won't even get initialized. You need to use a shared memory block to share values among the processes. Shared mutexes and other synchronization objects may need to be used to ensure any shared memory writes are protected. Finally, if you're using Windows Vista+, only processes with the same access level and below will get the DLL injected. IOW, if you're running the process as the logged-in user, only processed running as the logged-in user will get that DLL injected.
Try using WinEvents instead of the CBT hook: SetWinEventHook looking for EVENT_OBJECT_FOCUS as both min and max event, with the WINEVENT_OUTOFPROC flag, and 0 for idThread and idProcess. This will give you a hook that can listen to focus events from any process in the same desktop, without requiring a separate DLL, and it will work across both 32-bit and 64-bit applications.
There's a couple of caveats: one is that the events are not instantaneous; there's a slight lag as they are essentially posted to your process (which is how the out-of-proc option that avoids requiring a DLL works), but they may well be fast enough for your use. (And you'd have this same issue if you use PostMessage in your DLL hook anyhow!)
Also, you will get more events than actual HWND focus changes: various controls send these focus change events to signal internal focus change - focus moving between items in a list box, for example. You can filter these out by filtering in the callback for only those with idObject=OBJID_WINDOW and idChild=0.
Alternatively, if you listen for EVENT_SYSTEM_FOREGROUND events instead of EVENT_OBJECT_FOCUS (see MSDN for the full list of events), then it seems you should only get top-level window foreground events, which sounds like what you are actually after here.

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.

Detect if an application is not in use

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.

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