Detect if external process' window is flashing/blinking

5

How can I detect when a process that I'm not controlling is flashing due to some notification in it. I've seen only solutions that focus on an application that you have control over. In my case, there might be multiple instances of said process active at once and just one of them might be blinking.

This is my attempt:

using (Process process = Process.GetProcesses().FirstOrDefault(p => p.ProcessName.ToLower() == "..."))
using (ProcessModule module = process.MainModule)
{

    var a = GetModuleHandleEx(0x00000004, module.ModuleName, out var hModule);
    var hHook = SetWindowsHookEx(HookType.WH_SHELL, (code, param, lParam) =>
    {
        //test
        return IntPtr.Zero;
    }, hModule, 0);
}

Where the DLL imports are as follows:

public enum HookType : int
{
    WH_JOURNALRECORD = 0,
    WH_JOURNALPLAYBACK = 1,
    WH_KEYBOARD = 2,
    WH_GETMESSAGE = 3,
    WH_CALLWNDPROC = 4,
    WH_CBT = 5,
    WH_SYSMSGFILTER = 6,
    WH_MOUSE = 7,
    WH_HARDWARE = 8,
    WH_DEBUG = 9,
    WH_SHELL = 10,
    WH_FOREGROUNDIDLE = 11,
    WH_CALLWNDPROCRET = 12,
    WH_KEYBOARD_LL = 13,
    WH_MOUSE_LL = 14
}

delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, uint dwThreadId);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool GetModuleHandleEx(UInt32 dwFlags, string lpModuleName, out IntPtr phModule);

The problem here is that GetModuleHandleEx doesn't successfully return the proper handle, probably because I don't have the process is external and I don't have it loaded in mine (this is not possible).

I'm using win 10 64bit and the process that I'm targeting is 64 bit as well.

c#
windows
winforms
hook
kernel32
asked on Stack Overflow Jan 1, 2020 by Deadzone • edited Jan 1, 2020 by Deadzone

2 Answers

2

It is not possible to implement a global hook in the .net Framework, except for the WH_KEYBOARD_LL and the WH_MOUSE_LL low-level hooks. Global hooks for another types should be declared as C-style, that is not supported by the .NET Framework.

As a workaround it is possible to implement global hook in a native (unmanaged) DLL and transfer a callback (delegate) to a function that will be called each time, when the required message is received. Therefore, even if you fix the problem of calling the GetModuleHandleEx() you will receive only WH_SHELL messages related to your current application.

answered on Stack Overflow Jan 13, 2020 by Jackdaw • edited Jan 13, 2020 by Jackdaw
0

I was going to leave this as a comment, but I wanted to provide some code to elaborate what I meant.

You said that there may be multiple instances of the process. In your code, you performed a FirstOrDefault, grabbing the first instance that you find that matches your process name. Is there any chance that the process name repeats? For example, I fired up the F# Interactive and ran the following:

open System.Diagnostics;;
Process.GetProcesses() |> Array.map(fun(p) -> p.ProcessName);;

[|"chrome"; "fsiAnyCpu"; "svchost"; "InteractiveHost64"; "conhost"; "conhost"; "ServiceHub.RoslynCodeAnalysisService32"; "conhost"; "conhost"; ...|]

"conhost" repeated several times, meaning that the ProcessName can be repeated. I suggest trying the following, removing the FirstOrDefault with a Where, making sure you grab every Process that matches your criteria.

    // First, lets grab all of the processes that match the desired name
    var flashingProcesses = Process.GetProcesses().Where(p => p.ProcessName.ToLower() == "...")
        // Next, lets Select what we want from these processes
        .Select(process =>
        {
            using (process)
            {

                using (ProcessModule module = process.MainModule)
                {

                    var moduleHandle = GetModuleHandleEx(0x00000004, module.ModuleName, out var hModule);
                    var hHook = SetWindowsHookEx(HookType.WH_SHELL, (code, param, lParam) =>
                    {
                        //test
                        return IntPtr.Zero;
                    }, hModule, 0);
                    return hHook;
                }
            }
        })
        // Finally, lets filter out the non-flashing proceses
        .Where(ptr => ptr != IntPtr.Zero);
    var count = flashingProcesses.Count();
    Console.WriteLine($"There are ${count} flashing processes");

This may not be the exact answer, but I hope it helps.

answered on Stack Overflow Jan 10, 2020 by GLJ • edited Jan 10, 2020 by GLJ

User contributions licensed under CC BY-SA 3.0