views:

560

answers:

2

This is what I have so far:

    Dim bProcess = Process.GetProcessesByName("By").FirstOrDefault
    If bProcess IsNot Nothing Then
        SwitchToThisWindow(bProcess.MainWindowHandle, True)
    Else
        Process.Start("C:\Program Files\B\B.exe")
    End If

It has two problems.

  1. Some people have told me that SwitchToThisWindow is depricated.
  2. If application B is minimized, this function silently fails from the user's perspective.

So what's the right way to do this?

+6  A: 

Get the window handle (hwnd), and then use this user32.dll function:

VB.net declaration:

Declare Function SetActiveWindow Lib "user32.dll" (ByVal hwnd As Integer) As Integer 

C# declaration:

[DllImport("user32.dll")] public static extern int SetActiveWindow(int hwnd) 

My VB.net is VERY rusty, here is the C# code though:

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
    static extern bool ShowWindow(IntPtr hWnd, ShowWindowEnum flags);
    private enum ShowWindowEnum
    {
        Hide = 0,
        ShowNormal = 1, ShowMinimized = 2, ShowMaximized = 3,
        Maximize = 3, ShowNormalNoActivate = 4, Show = 5,
        Minimize = 6, ShowMinNoActivate = 7, ShowNoActivate = 8,
        Restore = 9, ShowDefault = 10, ForceMinimized = 11
    };

    [System.Runtime.InteropServices.DllImport("user32.dll")] 
    public static extern int SetActiveWindow(int hwnd);
    public void BringWindowToFront()
    {
        //get the process
        System.Diagnostics.Process bProcess = System.Diagnostics.Process.GetProcessesByName("By").FirstOrDefault<System.Diagnostics.Process>;
        //check if the process is nothing or not.
        if(bProcess != null)
        {
            //get the (int) hWnd of the process
            int hwnd = (int)bProcess.MainWindowHandle;
            //check if its nothing
            if (hwnd != 0)
            {
                //if the handle is other than 0, then set the active window
                SetActiveWindow(hwnd);
            }
            else
            {
                //we can assume that it is fully hidden or minimized, so lets show it!
                ShowWindow(bProcess.Handle, ShowWindowEnum.Restore);
                SetActiveWindow((int)bProcess.MainWindowHandle);
            }
        }
        else
        {
            //tthe process is nothing, so start it
            System.Diagnostics.Process.Start(@"C:\Program Files\B\B.exe");
        }
    }

Using that code, it would be as simple as calling BringWindowToFront(); Here is my VB.net guesswork, im really sorry if there are any issues with it!

<System.Runtime.InteropServices.DllImport("user32.dll")> _ 
Private Shared Function ShowWindow(ByVal hWnd As IntPtr, ByVal flags As ShowWindowEnum) As <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
End Function
Private Enum ShowWindowEnum
    Hide = 0
    ShowNormal = 1
    ShowMinimized = 2
    ShowMaximized = 3
    Maximize = 3
    ShowNormalNoActivate = 4
    Show = 5
    Minimize = 6
    ShowMinNoActivate = 7
    ShowNoActivate = 8
    Restore = 9
    ShowDefault = 10
    ForceMinimized = 11
End Enum

<System.Runtime.InteropServices.DllImport("user32.dll")> _ 
Public Shared Function SetActiveWindow(ByVal hwnd As Integer) As Integer
End Function
Public Sub BringWindowToFront()
    'get the process
    Dim bProcess As System.Diagnostics.Process = System.Diagnostics.Process.GetProcessesByName("By").FirstOrDefault
    'check if the process is nothing or not.
    If bProcess IsNot Nothing Then
        'get the (int) hWnd of the process
        Dim hwnd As Integer = CInt(bProcess.MainWindowHandle)
        'check if its nothing
        If hwnd <> 0 Then
            'if the handle is other than 0, then set the active window
            SetActiveWindow(hwnd)
        Else
            'we can assume that it is fully hidden or minimized, so lets show it!
            ShowWindow(bProcess.Handle, ShowWindowEnum.Restore)
            SetActiveWindow(CInt(bProcess.MainWindowHandle))
        End If
    Else
        'tthe process is nothing, so start it
        System.Diagnostics.Process.Start("C:\Program Files\B\B.exe")
    End If
End Sub
Tommy
Why are declaring it as Integer instead of IntPtr?
Jonathan Allen
It doesn't seem to work. Can you show me how to use it in conjunction with the code in the original question?
Jonathan Allen
@Jonathan: I will attempt to code a solution and edit my answer when i am done
Tommy
I'm getting a zero for SetActiveWindow's return value, which indicates an error. But I'm also getting 0 for GetLastWin32Error, which indicates success. Any idea where to look next?
Jonathan Allen
look at my updated code, perhaps i have fixxed the issue in my last update
Tommy
For ShowWindow, I need to pass bProcess.MainWindowHandle. What's really frustrating is that your code works on every application except the one I need it for.
Jonathan Allen
I'd need to know more about your application to be able to refine it further! sorry
Tommy
Int32 as handle will not work correctly on 64-bit. It just *seems* to work, since you're on a 32-bit machine or OS. IntPtr is defined to be of the same size as the bit-ness of the operating system, so running on a 64-bit OS will give you 64-bit IntPtr's.
Lasse V. Karlsen
+1  A: 

There is another way, which uses the not well-known UI Automation API:

AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
if (element == null)
{
    element.SetFocus();
}

Most of the time, this will work if it's possible to switch to that window. There are a lot of limitations in Windows (security, UAC, specific configuration, etc...) that can prevent you to change the end-user focus.

Simon Mourier