Search code examples
c#flashwindowex

Flashing a window


I am using flashwindowex winapi in my c# application to flash the IE window in icon tray. The code is running fine when I am adding a breakpoint in the code and running in debug. But the same code is not working when the breakpoint is removed.

It's a console application and I am using few windows api to find the handle of created IE process by its name. The handle of process is further passed to FlashWindowEX WINAPI for flashing the process.

public static System.IntPtr hnd = IntPtr.Zero;
public delegate bool CallBackPtr(IntPtr hwnd, int lParam);
public static CallBackPtr callBackPtr;
public const UInt32 FLASH_T = 3;
public const UInt32 FLASH_S = 12;

static void Main(string[] args)
{
    try
    {
        OrigCode();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    Console.ReadLine();
}

public static void OrigCode()
{
    string strPzszPath = string.Empty;
    string strCommandLine = string.Empty;
    string strpath1 = "http://localhost/Till/Default.aspx";

    strPzszPath = string.Concat(strPzszPath, strpath1);
    strPzszPath = string.Concat(strPzszPath, "?TSCashierID=JILL&TSBalPeriod=2&TSBalDate=2015-06-02");
    strCommandLine = strPzszPath;
    Process procRequested = new Process();

    ////Create the process in minimised mode by default
    procRequested.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
    procRequested.StartInfo.UseShellExecute = false;

    ////Get the file name in which process is required to be started
    procRequested.StartInfo.FileName = @"C:\Program Files\Internet Explorer\IExplore.exe";

    procRequested.StartInfo.Arguments = strCommandLine;

    ////Start the process, process should be created in minimised mode
    procRequested.Start();

    callBackPtr = new CallBackPtr(SetFocusEnum);
    int intResult = EnumWindows(callBackPtr, 0);

    FLASHWINFO fi = new FLASHWINFO();
    fi.cbSize = Convert.ToUInt32(Marshal.SizeOf(fi));
    fi.hwnd = hnd;
    fi.dwFlags = FLASH_T | FLASH_S;
    fi.ucount = UInt32.MaxValue;
    fi.dwTimeout = 0;
    FlashWindowEx(ref fi);
}

private static bool SetFocusEnum(IntPtr hWnd, int intLParam)
{
    int intSize = GetWindowTextLength(hWnd);
    try
    {

        if (intLParam == 0) //Process created so check by name
        {
            if (intSize++ > 0)
            {

                //Capture the running window name
                StringBuilder sbWindowName = new StringBuilder(intSize);
                GetWindowText(hWnd, sbWindowName, intSize);
                //Capture the running process with the window name
                if (sbWindowName.ToString().Contains("My Web Application Title"))
                {
                //Capture the handle which will be used to set the focus
                hnd = hWnd;
                }
            }
        }
    }
    catch (Exception ex)
    {
    }
    finally
    {
    }
    return true;
}

Below are the winapi functions I used:

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
internal static extern int EnumWindows(CallBackPtr callPtr, int intProc);

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
internal static extern int GetWindowTextLength(IntPtr intptrHwnd);

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
internal static extern int GetWindowText(IntPtr intptrHwnd, StringBuilder strText, int intMaxCount);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool FlashWindowEx(ref FLASHWINFO pwfi);

[StructLayout(LayoutKind.Sequential)]
private struct FLASHWINFO
{
    public UInt32 cbSize;
    public IntPtr hwnd;
    public UInt32 dwFlags;
    public UInt32 ucount;
    public UInt32 dwTimeout;
}

Solution

  • Like I stated in my comment, the issue here is that it takes too long to load the page for the window's text to be changed by the time you execute the EnumWindows() function. The code you use is executed almost immediatelly, whilst opening the process and loading the page takes a few hundred milliseconds to a few seconds. Adding a delay is the simplest workaround that will solve your problem.

    Here's what I did:

    procRequested.Start();
    procRequested.WaitForInputIdle();
    
    System.Threading.Thread.Sleep(750); //Wait for approximately 750 ms.
    

    It is normally adviced against using Thread.Sleep(), but since you're in a Console Application it doesn't make much difference other than blocking user input the little time that it runs.

    You may also have noticed that I added a call to Process.WaitForInputIdle(), this is to let your application wait until the process has finished its startup procedure (for example loading resources, or whatever Windows does with a process before the application actually starts) so that we know when we can expect the window to actually show.

    If you feel or experience that the 750 ms delay is not enough sometimes, try raising it to 1250 or something like that.


    EDIT:

    Since (as you say) a focused window won't flash, you can store a window handle to the previously focused window, and use that to focus the window again right before you let the FlashWindowEx code execute.

    For this you will need two WinAPI calls:

    [DllImport("user32.dll")]
    private static extern IntPtr GetForegroundWindow();
    
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool SetForegroundWindow(IntPtr hWnd);
    

    Then you just use those two functions here:

    IntPtr focusedhWnd = GetForegroundWindow(); //Get the window handle to the currently focused window.
    
    procRequested.Start();
    procRequested.WaitForInputIdle();
    
    System.Threading.Thread.Sleep(750); //Wait for approximately 750 ms.
    
    callBackPtr = new CallBackPtr(SetFocusEnum);
    int intResult = EnumWindows(callBackPtr, 0);
    
    SetForegroundWindow(focusedhWnd); //Focus the previously focused window again.
    
    FLASHWINFO fi = new FLASHWINFO();
    fi.cbSize = Convert.ToUInt32(Marshal.SizeOf(fi));
    fi.hwnd = hnd;
    fi.dwFlags = FLASH_T | FLASH_S;
    fi.ucount = UInt32.MaxValue;
    fi.dwTimeout = 0;
    FlashWindowEx(ref fi);