I’ve written a full-featured Diary application. This works nicely (TextArea is sooo powerful…!)
However if I minimize it, and the User starts another one, there will be two running - I don’t want that.
When a new instance starts, I can easily check if there’s one already running (by shelling to TaskList), and get the PID.
But I want to go one step further, and NOT start another one, but just un-minimize the existing application, i.e. bring the existing application to the front.
How do I do that?
Note I have checked the database and there’s a number of older answers to this, but I still can’t find a straightforward way of doing it. MBS has some functions which (I think) can bring a known PID application to the front, but I want to do it within Xojo.
I assume you’re building for Windows? If so then see this thread. It discusses how to use a mutex to detect when another instance of your app is already running, and an IPCSocket to communicate between the first instance and subsequent instances of your app.
Re Mutex: Yes, I had seen that thread.
As mentioned in the question, I have no trouble detecting an existing instance of the program
(I use shell to run Tasklist and search for the application. This finds if it’s running, and also gives its PID if it is.)
I also don’t need to communicate between applications so don’t need IPCSocket.
I simply want to quit the second instance, after bringing the first instance (which is minimized with known PID) to the front.
Bringing the first instance to the front could be done by sending a message over the IPCSocket (“hey! wake up!”) and putting the actual window-frontmost-making code directly in first instance. It’s usually easier to manipulate a window from within the instance that actually owns it.
Manipulating another instance’s window will usually involve using win32 declares like FindWindowExW and BringWindowToTop.
That is a handle to the process, not the window. You simply use EnumWindows() and GetWindowThreadProcessId() to enumerate all top-level windows looking for the one(s) that belong to the Process ID you have. Then use SetForegroundWindow() to bring that window to the front making sure you comply with all the restrictions of SetForegroundWindow().
This seems to get asked about every six months. Here’s how I handle it. Remember, anything in <…> needs to be replaced with your app’s info.
' We need to know the exact name of the main window if we want to bring the first instance to the front.
' So we set it here and retrieve it for the window in its Open event
MainWindowTitle = <whatever you name your application's main or starting window>
// Look to see if the app is already executing
Dim s As shell = New Shell
s.Execute ("tasklist /fo CSV")
Dim theString As String = s.Result
Dim theStringArray () As String
theStringArray = theString.Split (<your App's Name>)
If theStringArray.LastIndex > 1 Then // There's more than one instance in the list of processes that are running
Soft Declare Function FindWindowA Lib "User32" (className As Integer, title As CString) As Integer
Soft Declare Function FindWindowW Lib "User32" (className As Integer, title As WString) As Integer
// Find the window via an exact match using FindWindow
Dim handle As Integer
If System.IsFunctionAvailable ("FindWindowW", "User32") Then
handle = FindWindowW (0, MainWindowTitle)
Else
handle = FindWindowA (0, MainWindowTitle)
End If
// If we found a handle, then we want to bring it to the front
If handle <> 0 Then
Var i As Integer
Declare Function ShowWindow Lib "User32" (hWnd As Integer, nCmdShow As Integer) As Integer
Declare Function BringWindowToTop Lib "User32" (hWnd As Integer) As Integer
Declare Sub SetForegroundWindow Lib "User32" (hwnd As Integer)
i = ShowWindow (handle, 1)
i = BringWindowToTop (handle)
SetForegroundWindow (handle)
// and lastly we kill ourself and let the original one run.
Quit
End If
End If
This doesn’t send any information between the instances of the app. It merely finds and brings the first one to the front and lets the second instance go away.
Yes, that’s true - I found many references to bringing a task to the foreground.
But other replies in other threads either didn’t do what I wanted, or were incomprehensible (at least to me).
Quit the second instance after bringing all windows of the first instance to the front:
Declare mMutex in the App’s Properties.
In the Open event of the application:
mMutex = New Mutex(App.ExecutableFile.Name)
If Not mMutex.TryEnter Then
dim w as new WindowsListMBS //to list all windows on a Windows system
dim i,c as integer
c=w.WindowCount-1
for i = 0 to c
if w.WindowImageFileName(i).IndexOf(App.ExecutableFile.Name)>-1 AND w.WindowText(i).Length>0 AND w.WindowClassName(i)="RBWindow" then
w.ActivateWindow(i)
end if
next
Quit
End If