views:

224

answers:

3

I'm trying to get a handle to the foreground window in C#/Net 2.0/WinForms by calling the native GetForegroundWindow WinAPI function, in my app's form's constructor.

When I run the program directly from Windows Explorer or Total Commander, it correctly identifies the Windows Explorer or Total Commander window.

However, if I create a shortcut to my program on desktop and set a shortcut key for the shortcut (let's say Ctrl+Alt+X), when I'm running my program using the shortcut, the foreground window is identified as the "Shell_TrayWnd window" (Handle 0x00010064), and not the actual window. (Let's say I'm running Firefox on top, when I press Ctrl+Alt+X my program starts and says that the foreground window is not Firefox, as it should, it says it's the taskbar - Shell_TrayWnd.)

    public MainForm()
    {
        this.InitializeComponent();

        IntPtr handle = WinAPI.GetForegroundWindow();
        this.Text = handle.ToString();
        StringBuilder title = new StringBuilder(255);
        if (WinAPI.GetWindowText(handle, title, 255) > 0)
        {
            this.Text += title.ToString();
        }
    }

How can I get the real foreground window? Should I (also) use other functions like GetWindow?

Thank you

+1  A: 

Note that the task bar may be the real foreground window at the time you call GetForegroundWindow, simply because it's Explorer who's handling the shortcut press, and the task bar belongs to Explorer (Shell_TrayWnd is the window class of the task bar).

If you want to do something with the global active window, you may be better off by starting your application and letting it wait in the background. Then you can handle key presses while your application is running, so Explorer won't interfere.

Somehow, this reminds me of an article by Raymond Chen.

OregonGhost
A: 

I think you're trying to do the same thing as I am — open a shell from Explorer at the current path.

I ran into exactly the same problem. Here's a program that's working for me. It uses EnumWindows to search through all visible windows until it finds one whose title is a real path.

using System;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;

public class ShellHere
{
    // Thanks to pinvoke.net for the WinAPI stuff

    [DllImport("user32.dll")]
    private static extern int EnumWindows(CallBackPtr callPtr, int lPar); 

    [DllImport("user32.dll")]
    static extern int GetWindowText(int hWnd, StringBuilder text, int count);

    [DllImport("user32.dll", EntryPoint="GetWindowLong")]
    private static extern IntPtr GetWindowLongPtr32(IntPtr hWnd, GWL nIndex);

    [DllImport("user32.dll", EntryPoint="GetWindowLongPtr")]
    private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, GWL nIndex);

    public delegate bool CallBackPtr(int hwnd, int lParam);
    private static CallBackPtr _callBackPtr;

    // This static method is required because Win32 does not support
    // GetWindowLongPtr directly
    public static IntPtr GetWindowLongPtr(IntPtr hWnd, GWL nIndex)
    {
        if (IntPtr.Size == 8)
            return GetWindowLongPtr64(hWnd, nIndex);
        else
            return GetWindowLongPtr32(hWnd, nIndex);
    }

    public static bool FindPathInTitle( int hwnd, int lparams )
    {
        const int nChars = 256;
        StringBuilder buffer = new StringBuilder( nChars );

        IntPtr result = GetWindowLongPtr( new IntPtr(hwnd), GWL.GWL_STYLE );

        // ignore invisible windows
        if ( (result.ToInt64() & WS_VISIBLE) != 0 )
        {
            if ( GetWindowText( hwnd, buffer, nChars ) > 0 )
            {
                string title = buffer.ToString();

                // ignore the taskbar
                if ( title.ToLower() != "start" && Directory.Exists( title ) )
                {
                    _folder = title;
                    return false;
                }
            }
        }

        return true;
    }

    private static string _folder;

    public static void Main()
    {
        _callBackPtr = new CallBackPtr( FindPathInTitle );
        EnumWindows( _callBackPtr, 0 );

        Process shell = new Process();
        shell.StartInfo.FileName = "cmd.exe";
        if ( !string.IsNullOrEmpty( _folder ) )
            shell.StartInfo.WorkingDirectory = _folder;
        shell.Start();
    }

    public enum GWL
    {
        GWL_WNDPROC =    (-4),
        GWL_HINSTANCE =  (-6),
        GWL_HWNDPARENT = (-8),
        GWL_STYLE =      (-16),
        GWL_EXSTYLE =    (-20),
        GWL_USERDATA =   (-21),
        GWL_ID =     (-12)
    }

    // Window Styles 
    const UInt32 WS_VISIBLE = 0x10000000;
}

It's working for me so far (Win7-64). Note that you don't have to be directly on the Explorer window for it to work — it'll use the next one in the tab order.

harpo
A: 

I'm not sure what you need the foreground window for, so this might or might not help:

You probably can detect that you were started through a shortcut by:

In that case you might try to get the window that's previous in Z order, or on top on the Desktop window.

peterchen