0

I have a Windows Form app, that uses Forms.WebBrowser. Due a known issue of WebBrowser, there is an incremental memory leak during time. Following previous advices about this issue, i choosed to restart the app, using cmd. But i noticed that even if i launch the app minimized, my current ActiveWindow lose the focus: i.e. if i'm writing on a .txt file, when the app it's launched as minimized, i lost focus on the file .txt and i'm forced to stop writing.

Here the code i use.

Dim info As New ProcessStartInfo
With info
  .Arguments = "/c " + Chr(34) + Application.ExecutablePath + Chr(34)
  .WindowsStyle = ProcessWindowsStyle.Hidden
  .CreateNoWindow = True
  .FileName = "cmd.exe"
End With
Process.Start(info)
Application.Exit()

I would like to launch the application throught cmd, without loose my current active windows focus on another program i'm using. I suppose could be a cmd argument. Any help?

Update:

After some searches:

https://www.codeproject.com/Questions/170180/How-to-find-out-which-is-the-active-current-window

How to bring a window foreground using c#?

i've found a solution calling Windows API:

i made some test and using the Windows Api i can get the previous process with Active Window, and give back the focus to that process, after the re-launch of my app, leave it the focus.

But i reached to do it, waiting a delay (in ms).

I would like to restore the focus when the app it's launched, but here should be the issue: appear to me that even if i launch the app minimized, when the app it's settled on the TaskBar (NB not when the process is running), windows OS give the focus to the taskbar, and if i'm writing, i.e. in a .txt, i loose the focus while i'm writing.

i'm doing:

  • Launch my app
  • Do what i want (minimize, ...move on desktop, ...put the app in background using another app...)
  • before i re-launch the app, i store the current state of the app (i.e. minimized, ...location)
  • call Windows API for check the which is the process that is currently running as active Window, and store this process.
  • call cmd to re-launch my app, with stored parameters
  • wait (NB HERE IS MY PROBLEM: i can wait some seconds, but i would like to wait until re-launched app is present in TaskBar)
  • just after the re-launched app is present in taskBak, i use Windows API to restore focus in previous active Window process.
  • End my app (while my re-launched app is running)

here the code:

Private Declare Function GetForegroundWindow Lib "user32.dll" () As Integer
Private Declare Function GetLastActivePopup Lib "user32.dll" (hwnd As IntPtr) As Integer
Private Declare Function SetForegroundWindow Lib "user32.dll" (hwnd As IntPtr) As Integer
Private Declare Function SendMessageW Lib "user32.dll" (hwnd As IntPtr, msg As Integer, wParam As Int32, lParam As Int32) As Boolean
Private Shared WM_SYSCOMMAND As Integer = 274 'select an item from the Window menu
Private Shared SC_RESTORE As Int32 = 6172   'restore the Window

And:

Private Sub Restart()
  '' Get current Active Window
  Dim fw As IntPtr = GetForegroundWindow
  Dim lap As IntPtr = GetLastActivePopup(fw)
  Dim activeWindowsProcess As Process = Nothing
  Dim AllProcess() As Process = Process.GetProcesses
  For Each proc As Process In AllProcess
      If proc.MainWindowHandle = lap Then
          activeWindowsProcess = proc
      End If
  Next

  '' Set re-launched app
  Dim info As New ProcessStartInfo
  With info
    .Arguments = "/c " + Chr(34) + Application.ExecutablePath + Chr(34)

    If Not Me.Handle = GetForegroundWindow OrElse Me.WindowState = FormWindowState.Minimized Then
        .Arguments += " m"
    Else
        .Arguments += " " + Me.Location.X.ToString + " " + Me.Location.Y.ToString
    End If

    .WindowsStyle = ProcessWindowsStyle.Hidden
    .CreateNoWindow = True
    .FileName = "cmd.exe"
  End With
  Process.Start(info)

  '' loop waiting ...seconds
  Dim Delay As Date = Date.Now.AddMilliseconds(500)
  While Delay > Date.Now
    Application.DoEvents()
  End While

  '' loop waiting process its launched: NOT WORKING
  Dim AllMyProcess As Integer
  Dim myappname As String = System.Reflection.Assembly.GetExecutingAssembly().GetName.Name
  Dim myVSappname As String = myappname + ".vshost"
  Do
    For Each proc As Process In Process.GetProcesses
      If proc.ProcessName = myappname OrElse proc.ProcessName = myfullappname Then
        AllMyProcess += 1
      End If
    Next
  Loop While AllMyProcess < 2

  '' call back focus on Active Window
  If Not IsNothing(activeWindowsProcess) Then
    SetForegroundWindow(activeWindowsProcess.MainWindowHandle)
            SendMessageW(activeWindowsProcess.MainWindowHandle, WM_SYSCOMMAND, SC_RESTORE, 0)
  Else
    SetForegroundWindow(lap)
            SendMessageW(lap, WM_SYSCOMMAND, SC_RESTORE, 0)
  End If

  '' Close app
  Application.Exit()
End Sub

My problem now should be to call Windows API to TaskBar, checking until there will be 2 of my app launched.

Does someone know if this is a good code for my pourposes?

And if yes, how to Call Windows API to TaskBar for check current programs?

Thank you.

Marcello
  • 438
  • 5
  • 21
  • Before lunching new process get focused window using "GetForegroundWindow" Windows API and after lunching the process restore the focus to previously focused window using "SetForegroundWindow" Windows API. – Mojtaba Tajik Aug 22 '19 at 13:52
  • First thing, you could use `Application.Restart()`. Then you cannot *lose focus* on a file. What do you mean by that? What does *my current active windows focus on another program* mean? If you're interacting with a Window of another process, then just call FindWindow, or use `Process.GetProcessesByName()` or save the Process ID and use `Process.GetProcessById()` and use its `MainWindowHandle` to re-acquire the handle. You should try to give more context to your question. – Jimi Aug 22 '19 at 20:49
  • @MojtabaTajik Thanks for your help, i'm studying on it – Marcello Aug 22 '19 at 21:07
  • @Jimi thanks for helping, i tried in the beginning, Application.Restart, but i had an issue with this, dont remember well, seems something with process remaining running. When i will have time i will give a try again. But i think my issue its related to WindowsApi, because it's interaction with Active Windows, and TaskBar. – Marcello Aug 23 '19 at 16:09

2 Answers2

1

even if i launch the app minimized, my current ActiveWindow lose the focus

This is because the default is for the windows to activate on being shown. This behavior can be changed by overriding the Form.ShowWithoutActivation Property

From the documentation Remarks

Use this property if you want to show a top-level window, but don't want to interrupt a user's work by taking the input focus away from the current window.

Using this setting does work to prevent the new window from taking the focus, but it does not prevent it from popping up in front of the active window. Combining this along with starting the form in a minimized state gives a satisfactory experience.

One way of doing this would be to modify your code to include a command-line switch that would setup the form prior to showing it.

Since your posted code is VB, I will show a method that can be used if you are using the VB "Application Framework" (the default VB Winform project template).

From the Project Menu, select Project Properties->Application Tab and click on the "View Application Events" button.

Imports Microsoft.VisualBasic.ApplicationServices

Namespace My
  Partial Friend Class MyApplication
    Private Sub MyApplication_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup
      If e.CommandLine.Count > 0 AndAlso e.CommandLine.Item(0).Equals("/min", StringComparison.InvariantCultureIgnoreCase) Then
        Application.MainForm = New Form1 With {.NoActivate = True, .WindowState = FormWindowState.Minimized}
      End If
    End Sub
  End Class
End Namespace

Where Form1 would include the following:

Public Property NoActivate As Boolean = False

Protected Overrides ReadOnly Property ShowWithoutActivation As Boolean
  Get
    Return NoActivate
  End Get
End Property

Your ProcessInfo code should be modified to include the command-line switch ("/min").

Const quote As String = """"
With info
    .Arguments = "/c " & quote & Application.ExecutablePath & quote & " /min"
    .WindowStyle = ProcessWindowStyle.Hidden
    .CreateNoWindow = True
    .FileName = "cmd.exe"
End With
TnTinMn
  • 11,522
  • 3
  • 18
  • 39
  • thank you very much for your help, i tried, but... It doesn't give me what i was looking for. If you try, running the code on an app, and you open a file .txt and start to write on it, when the app in Windows background it's re-started, you lost the focus on the .txt. ...I was studying on Windows Api solutions, like the one proposed there: https://www.codeproject.com/Questions/170180/How-to-find-out-which-is-the-active-current-window and there: https://stackoverflow.com/questions/4867210/how-to-bring-a-window-foreground-using-c – Marcello Aug 23 '19 at 08:36
0

Well, i've found an answer that it's not properly correct, but it workaround, and for now my program run (more or less) as i like.

I implemented an instance of NotifyIcon Class. So when i minimize the app, i set the form not visible, and it's hidden from TaskBar (but showed in notification area on TaskBar). When i close the app, and i launch another from cmd, the new app, check initially if the previous was minimized (hidden and showed only in notification area). If it was, the new app it's launched as not visible, and in this mode, when the new app it's launched i don't loose the focus on the current Window program i'm using.

Marcello
  • 438
  • 5
  • 21