2

I'm developing an application which needs to get the windows names that have been active while the application is running, currently i'm performing a Call to GetForegroundwindow() every half a second, but that's hardly accurate and i don't wanna put it to 100ms interval, i think it's just bad and not accurate, is there any system event that can done that for me?

Thanks.

SomeNickName
  • 511
  • 6
  • 32

1 Answers1

2

-- UPDATE --

You could use SetWindowsHookEx to monitor the current Desktop, but this doesn't work in managed code. Instead, you must use SetWinEventHook.

You had asked how to use the SetWinEventHook p/Invoke method. Here is your answer:

First make sure this line is at the top of your code file:

Imports System.Runtime.InteropServices

Next, declare all that you need to invoke the call:

Public Const WINEVENT_OUTOFCONTEXT = &H0  
'' Events are ASYNC

Public Const WINEVENT_SKIPOWNTHREAD = &H1  
'' Don't call back for events on installer's thread

Public Const WINEVENT_SKIPOWNPROCESS = &H2  
'' Don't call back for events on installer's process

Public Const WINEVENT_INCONTEXT = &H4  
'' Events are SYNC, this causes your dll to be injected into every process

    Public Declare Function SetWinEventHook Lib "user32.dll" _
       (eventMin As UInteger, _
        eventMax As UInteger, _
        hmodWinEventProc As IntPtr, _
        lpfnWinEventProc As IntPtr, _
        idProcess As UInteger, _
        idThread As UInteger, _
        dwflags As UInteger) As IntPtr

    Public Declare Function UnhookWinEvent Lib "user32.dll" _
        (hWinEventHook As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean

    Public Delegate Sub WinEventProc( _
          hWinEventHook As IntPtr, _
          [event] As UInteger, _
          hwnd As IntPtr, _
          idObject As Integer, _
          idChild As Integer, _
          dwEventThread As UInteger, _
          dwmsEventTime As UInteger)

Next, you declare a function and a new variable as a function with an address to that function:

    Public Sub EventCallBack( _
      hWinEventHook As IntPtr, _
      [event] As UInteger, _
      hwnd As IntPtr, _
      idObject As Integer, _
      idChild As Integer, _
      dwEventThread As UInteger, _
      dwmsEventTime As UInteger)

' Some code goes here
    End Sub

    Private eventProc As New WinEventProc(AddressOf EventCallBack)
    Private hEventHook As IntPtr

Finally, you tie it all together, and you pass the following line of code to create your hook (0 and 255 are arbitrary numbers, replace them with the min and max message codes you want to watch):

hEventHook = SetWinEventHook(0, _
                             255, _
                             IntPtr.Zero, _
                             Marshal.GetFunctionPointerForDelegate(eventProc), _
                             0, _
                             0, _
                             WINEVENT_OUTOFCONTEXT)

And when your program has finished add the following line to an Application termination event, or the form's Dispose or Finalize methods:

        UnhookWinEvent(hEventHook)

And this runs as expected on my test application.

Nathan M
  • 873
  • 5
  • 18
  • Don't forget to call CallNextHookEx() when you are done so that other programs that are hooked into the desktop continue to function properly. – Nathan M Dec 16 '13 at 19:38
  • Well, i have never worked with SetWindowsHookEx or Hooks in general before, and that link only has c++ xyntax, can you provide a proper declaration for what i am trying to do in vb.net code? It's seems somehow confusing for me, thanks. – SomeNickName Dec 16 '13 at 20:10
  • Sure, give me just a second. – Nathan M Dec 16 '13 at 20:21
  • Apparently I've done a little digging and global windows hooks are not supported in the .NET framework. It is possible to create a small C++ DLL with an exported function that redirects the callback to a function in your VB .NET application. I don't know if you've ever worked with Win32 C++ before, but if not, I can help you there, too. – Nathan M Dec 16 '13 at 21:04
  • Thanks for your time, i searched a little more too and found this http://stackoverflow.com/questions/4407631/is-there-windows-system-event-on-active-window-changed , apparently he could done it in C#, it's .Net framework but i'm not that familiar with it's sintax to convert it to vb.net, don't want to put more work in your hands but if i read correctly he could do it, it's possible to convert though? If not, i never worked with C++ :(. Thanks anyway. – SomeNickName Dec 16 '13 at 22:10
  • That is a different function but it appears to work in managed code... Public Declare Function SetWinEventHook Lib "user32.dll" _ (eventMin As UInteger, _ eventMax As UInteger, _ hmodWinEventProc As IntPtr, _ lpfnWinEventProc As IntPtr, _ idProcess As UInteger, _ idThread As UInteger, _ dwflags As UInteger) As IntPtr – Nathan M Dec 16 '13 at 22:28
  • Okay ... I've edited the response to give you an answer on how to use the Window hook function. – Nathan M Dec 16 '13 at 23:01
  • Thanks for your help !! It works, i just have one more question if i'm not being an hassle, could you explain in what the eventMin and eventMax impacts? Because i've been testing and if i set event max higher than 7~ this happens http://gifti.me/i/TfgrhVwyd4.gif It's just triggers the callback event if i somehow click the same window or move it around... is that supposed to happen? If i put it to 7~ it works, if i put it more low it just doesn't... I read the documentation about that two parameters but i can't seem to understand... So sorry my lack of knowledge in this "Hook" situation. – SomeNickName Dec 17 '13 at 02:30
  • 1
    Those would be Event constants... http://msdn.microsoft.com/en-us/library/windows/desktop/dd318066(v=vs.85).aspx – Nathan M Dec 17 '13 at 08:42
  • The constant you probably want is EVENT_OBJECT_FOCUS = 32773 – Nathan M Dec 17 '13 at 09:05
  • I've been testing multiple constants to see what suits my problem, thank you very much though there's no black-and-white solution to get every windows title, some programs don't use the "regular" way like Firefox... though what you suggested "EVENT_OBJECT_FOCUS" makes the event raise, but multiple times, i've arranged it already to don't keep duplicates. Thank you for your help ! – SomeNickName Dec 17 '13 at 16:49
  • Oh there's way... you would get the instance that window belonged to, get the module name and the executable filename of the program, then you'd call out to the window's shell to get the "Friendly Name" registered with the OS for that program (or embedded in the EXE info). This requires much p/Invoke. But it can be done. – Nathan M Dec 17 '13 at 17:20
  • No, i'm fine with what, if it requires p/invoke it's too much trouble for this situation. I have what i need from the code you posted, thank you for the information, really helpful. I've adapted my code pretty well to what i had before :D – SomeNickName Dec 17 '13 at 18:46