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.

Deadzone
  • 793
  • 1
  • 11
  • 33
  • Why does GetModuleHandleEx fails? What's the value of GetLastError? That might be a security problem. Not any process can hook any other process. – Simon Mourier Jan 04 '20 at 17:24
  • @SimonMourier The error is 126, `hModule` is set to 0 and the function returns false. I don't know why it fails, I assume it's because I don't have the dll loaded, but as stated in the question that's not a possibility. I'm not constraining the answers to this function, I'm looking for a solution to the problem described in the question in whatever way that is possible. – Deadzone Jan 05 '20 at 07:19
  • 126 is ERROR_MOD_NOT_FOUND "The specified module could not be found". Resolve that error first, hint: https://stackoverflow.com/questions/16700810/loadlibraryexw-fails-last-error-is-error-mod-not-found-but-no-missing-depend or https://devblogs.microsoft.com/oldnewthing/20131127-00/?p=2553 – Simon Mourier Jan 05 '20 at 09:01
  • @Deadzone could you please share any example of blinking, what do you mean here? Is it when app is minimized to taskbar and notify user about some progress? How can I reproduce this behavior? – Pavel Anikhouski Jan 05 '20 at 13:18
  • @PavelAnikhouski The icon in the taskbar is usually flashing orange/yellow (I also believe the whole window is flashing, but could be wrong on that), as far as I can tell this happens whenever there is some notification within the application. – Deadzone Jan 05 '20 at 18:02
  • @Deadzone I'm just wondering how to reproduce a situation. But how are going to detect blinking? Is your question about it or about getting a proper process handle? If your app or external process runs with elevated rights, you'll probably need to adjust a privileges – Pavel Anikhouski Jan 05 '20 at 18:15
  • 1
    @PavelAnikhouski take a look at this question https://stackoverflow.com/questions/25681443/how-to-detect-if-window-is-flashing That's essentialy my problem, however OP is looking for a way to detect if his own application is flashing, I'm looking for a way to detect if external application is flashing. – Deadzone Jan 05 '20 at 18:41
  • you should use CBT hooks ref : https://www.codeproject.com/Articles/4725/Manipulating-Windows-using-messages-and-simple-CBT – divyang4481 Jan 06 '20 at 20:56
  • @Deadzone - To be global the WH_SHELL hook should be packaged to a native (unmanaged) DLL. – Jackdaw Jan 13 '20 at 00:03

2 Answers2

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.

Jackdaw
  • 7,626
  • 5
  • 15
  • 33
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.

GLJ
  • 1,074
  • 1
  • 9
  • 17