Monday, September 28, 2009

How to enumerate running programs in .NETCF

I've been helping someone out in the MSDN Smart Device Forums lately with enumerating running programs. I thought I should share it since I don't much results from internet search engines on the topic. So here we go...

To enumerate top-level windows we use the EnumWindows method. To enumerate the running programs the same way the Task manager in Windows Mobile does, we just filter out what we don't need. What we don't want are windows that:

1. Have a parent window - We check by calling GetParent()
2. Is not visible - We check by calling IsWindowVisible()
3. Tool windows - We check by getting the current extended style (through GetWindowLong() of the window and checking if WS_EX_TOOLWINDOW is set

Here's a simple smart device console application the loads and displays a list of running programs:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
 
namespace EnumWindowsNETCF
{
    class Program
    {
        static Dictionary<string, IntPtr> visibleWindows = new Dictionary<string, IntPtr>();
        static StringBuilder lpString;
        static bool visible;
        static bool hasOwner;
        static bool isToolWindow;
 
        delegate int WNDENUMPROC(IntPtr hwnd, uint lParam);
        const int GWL_EXSTYLE = -20;
        const uint WS_EX_TOOLWINDOW = 0x0080;
 
        [DllImport("coredll.dll")]
        static extern int EnumWindows(WNDENUMPROC lpEnumWindow, uint lParam);
 
        [DllImport("coredll.dll")]
        static extern bool IsWindowVisible(IntPtr hwnd);
 
        [DllImport("coredll.dll")]
        static extern IntPtr GetParent(IntPtr hwnd);
 
        [DllImport("coredll.dll")]
        static extern bool GetWindowText(IntPtr hwnd, StringBuilder lpString, int nMaxCount);
 
        [DllImport("coredll.dll")]
        static extern int GetWindowLong(IntPtr hwnd, int nIndex);
 
        static int Callback(IntPtr hwnd, uint lParam)
        {
            hasOwner = GetParent(hwnd) != IntPtr.Zero;
            visible = IsWindowVisible(hwnd);
            isToolWindow = (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) != 0;
            lpString.Remove(0, lpString.Length);
            GetWindowText(hwnd, lpString, 1024);
 
            string key = lpString.ToString();
            if (!hasOwner &&
                visible &&
                !isToolWindow &&
                !string.IsNullOrEmpty(key) &&
                !visibleWindows.ContainsKey(key))
            {
                visibleWindows.Add(key, hwnd);
            }
 
            return 1;
        }
 
        static void Main()
        {
            lpString = new StringBuilder(1024);
            EnumWindows(Callback, 0);
 
            foreach (var key in visibleWindows.Keys)
            {
                IntPtr hwnd = visibleWindows[key];
                visible = IsWindowVisible(hwnd);
                lpString.Remove(0, lpString.Length);
                GetWindowText(hwnd, lpString, 1024);
 
                Debug.WriteLine("Handle: " + hwnd + "; Is Visible: " + visible + "; Text: " + lpString);
            }
        }
    }
}

I hope you find this useful, otherwise it can always be an addition to your utility library.

6 comments:

Anonymous said...

Hi,

I tried it, it complied successfully, when I run this, nothing happened. I put breakpoint on int Callback(IntPtr hwnd, uint lParam) but nothing happened.

Please guide me

Christian Resma Helle said...

What platform are you running on? I just re-tested the code on a windows mobile 5 and 6 emulators and it seems to work fine.

Anonymous said...

Ok, I tried with console application again and it worked. But I could not get it properly:

I got output as below with windows mobile 6 emulator:

Handle: 2080835968; Is Visible: True; Text: CursorWindow
Handle: 2080846080; Is Visible: True; Text: Desktop
Handle: 2080871872; Is Visible: True; Text: Phone
Handle: 2080914320; Is Visible: True; Text: Contacts
Handle: 2080900864; Is Visible: True; Text: Messaging
Handle: 2080916016; Is Visible: True; Text: Calendar

Though, there was only desktop, no other window was open.

Can u please clear me?

Anonymous said...

I mean no windows was open then why it showed Visible as True?

Thank you

Christian Resma Helle said...

Applications by default will minimize when the window close button is clicked.

Unless these applications are focibly shutdown (e.g. through the running programs control panel applet) then these applications are still running in the background

Unknown said...

I got handle of Phone window following your blog, and hook the window like below:

Private Sub SetHook()
mOldWndProc = SetWindowLong(mhWnd, GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(mProc))
End Sub
'Restores the original window procedure for the control.
Private Sub Unhook()
SetWindowLong(mhWnd, GWL_WNDPROC, mOldWndProc)
End Sub
Protected Overridable Function WndProc(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
If msg = WM_HOTKEY Then
Dim fuModifiers As Int32 = LoWord(lParam)
Dim uVirtKey As Int32 = HiWord(lParam)
Dim isCorrectKey As Boolean = (uVirtKey = VK_TBACK)
If (lParam And &H1000) = 0 Then 'this is key down event
If isCorrectKey Then
Dim e As New KeyPressEventArgs(ChrW(uVirtKey Or fuModifiers))
RaiseEvent OnKeyPress(New Object, e)
End If
Else 'this is keyup event
End If
'return 1 so that OS won't process the key. We must return 1 for both KeyUp and KeyDown
If isCorrectKey Then Return 1
End If
'if we are here, the key was unhandled, call the parent handler
Return CallWindowProc(mOldWndProc, hwnd, msg, wParam, lParam)
End Function


After hook, when I click on Phone to open window, it raise error:

A native exception has occurred in Test.exe, Select Quit and restart program.
OR
The remote connection to the device has been lost. Please verify the device connection and restart debugging.
Please can u help me to fix it. Thank you