3

I want to make a shell extension or app in .NET that logs all windows that are closed. The log should contain the process name that created a window.

I have no ideas on how to do this. Any pointers?

Elmo
  • 6,409
  • 16
  • 72
  • 140
  • 1
    well you are wanting what sounds like more than one thing.. checkout System.Diagnostics.Process and creating logs also check out JavaScript and Logging wonder why you would even want to log all that to begin with.. Look at querying the TaskManager hint ..WMI can do this as well... good luck – MethodMan Dec 27 '11 at 19:26

3 Answers3

6

This requires a hook, set by SetWindowsHookEx, WH_SHELL hook type. This hook type requires a DLL that can be injected into a process, such a DLL can't be written in C#. A 64-bit operating system and UAC will create plenty of obstacles as well. This project can help.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
3

Although you can certainly hook programs, that really seems overkill for what you want to do. This code should get you VERY close (you'll have to do something else for querying the windows explorer process for it's current directory, i haven't done that). Additionally, my needs are different from yours, as mine shows more than just a your definition of a Window (a tooltip popup is technically a 'window`). Explorer.exe, Chrome and Internet Explorer can all have multiple windows for a a single process, both those windows do close. Depending on your needs you might want to look at:

Enumerate windows like alt-tab does

EnumWindows filter to only windows that show up on alt-tab

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            ProcessListModel processes = Program.GetProccesses();

            foreach (ProcessModel process in processes.Items)
            {
                Console.WriteLine("Program: " 
                  + process.Filename 
                  + "[" + process.IsWindowsExplorer.ToString() + "]");
                Console.WriteLine("- Window Text: " + process.WindowText);
            }
            Console.ReadKey();
        }

        [DllImport("user32")]
        public static extern int EnumWindows(EnumWindowsDelegate CallBack,
                                             ProcessListModel Processes);
        [DllImport("user32.dll")]
        private static extern IntPtr GetWindowThreadProcessId(int hWnd, 
          out int lpdwProcessId);
        [DllImport("user32")]
        internal static extern int GetAncestor(int hwnd, int gaFlags);
        [DllImport("user32")]
        internal static extern int GetLastActivePopup(int hWnd);
        [DllImport("user32")]
        internal static extern bool IsWindowVisible(int hWnd);
        [DllImport("User32.Dll")]
        public static extern void GetWindowText(int h, 
          StringBuilder s, 
          int nMaxCount);

        internal delegate bool EnumWindowsDelegate(int Hwnd, 
          ProcessListModel Processes);

        internal static bool EnumWindowsCallBack(int Hwnd, 
          ProcessListModel Processes)
        {
            ProcessModel model = new ProcessModel();

            // Visible != Minimized (I think) 
            if (!IsWindowVisible(Hwnd))
                return true;

            // Can the Window be shown by using alt-tab 
            if (IsAltTabWindow(Hwnd))
            {
                model.WindowsHandle = Hwnd;
                try
                {
                    int pid = 0;
                    Program.GetWindowThreadProcessId(Hwnd, out pid);
                    if (pid > 0)
                    {
                        try
                        {
                            model.ProcessID = pid;

                            Process p = Process.GetProcessById(pid);
                            if (p != null)
                            {
                                string filename = p.MainModule.FileName;
                                filename = System.IO.Path.GetFileName(filename);
                                model.Filename = filename;

                                StringBuilder windowText = new StringBuilder(256);
                                Program.GetWindowText(Hwnd, windowText, 256);
                                model.WindowText = windowText.ToString();

                                if (filename.Contains("explorer.exe"))
                                {
                                    model.IsWindowsExplorer = true;
                                }

                                Processes.Items.Add(model);
                            }
                        }
                        // Do something or not,  
                        // catch probably if window process 
                        // is closed while querying info 
                        catch { }
                    }
                }
                // Do something or not,  
                // catch probably if window process 
                // is closed while querying info 
                catch { }
            }
            return true;
        }

        internal static bool IsAltTabWindow(int hwnd)
        {
            // Start at the root owner 
            int hwndWalk = GetAncestor(hwnd, 3);

            // See if we are the last active visible popup 
            int hwndTry;
            while ((hwndTry = GetLastActivePopup(hwndWalk)) != hwndTry)
            {
                if (IsWindowVisible(hwndTry))
                    break;
                hwndWalk = hwndTry;
            }

            return hwndWalk == hwnd;
        }

        public static ProcessListModel GetProccesses() 
        {
          ProcessListModel processes = new ProcessListModel();
          EnumWindowsDelegate enumCallback = 
            new EnumWindowsDelegate(EnumWindowsCallBack); 
          EnumWindows(enumCallback, processes); 
          return processes; 
        }

        public class ProcessListModel
        {
            public ProcessListModel()
            {
                this.Items = new List<ProcessModel>();
            }

            public List<ProcessModel> Items { get; private set; }
        }

        public class ProcessModel
        {
            public int WindowsHandle { get; set; }
            public int ProcessID { get; set; }
            public string Filename { get; set; }
            public bool IsWindowsExplorer { get; set; }
            public string WindowText { get; set; }
        }

    }
}

Output: (I have a lot of windows open, this is just a small list of them)

enter image description here

NOTE: This doesn't enumerate the child windows, so if I have Internet Options open in IE and I close it, that would be a child window, not the process main window. If you need child windows, then you'll have to implement the EnumChildWindows function in a similar fashion.

What you have asked for is ACTUALLY difficult, there is no straight forward way to do exactly what you want.

Community
  • 1
  • 1
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
1

This is an older article, but I have used it in the past with success when using hooks from .NET:

Windows Hooks in the .NET Framework

TWA
  • 12,756
  • 13
  • 56
  • 92