Link to home
Start Free TrialLog in
Avatar of TechMonster
TechMonster

asked on

Last activated application, vb.net

Does anyone here know how to get the below code to work with GetActiveWindow versus getting foreground window?


Imports System.Runtime.InteropServices

Public Class Form1
    Inherits System.Windows.Forms.Form




    ' Shell Events Constants
    Public Enum ShellEvents
        HSHELL_WINDOWCREATED = 1
        HSHELL_WINDOWDESTROYED = 2
        HSHELL_ACTIVATESHELLWINDOW = 3
        HSHELL_WINDOWACTIVATED = 4
        HSHELL_GETMINRECT = 5
        HSHELL_REDRAW = 6
        HSHELL_TASKMAN = 7
        HSHELL_LANGUAGE = 8
        HSHELL_ACCESSIBILITYSTATE = 11
    End Enum

    ' API Declares
    Public Declare Function RegisterWindowMessage Lib "user32.dll" Alias "RegisterWindowMessageA" (ByVal lpString As String) As Integer
    Public Declare Function DeregisterShellHookWindow Lib "user32" (ByVal hWnd As IntPtr) As Integer
    Public Declare Function RegisterShellHookWindow Lib "user32" (ByVal hWnd As IntPtr) As Integer
    Public Declare Function GetForegroundWindow Lib "user32" () As IntPtr
    Public Declare Function SetForegroundWindow Lib "user32" Alias "SetForegroundWindow" (ByVal hWnd As IntPtr) As Integer

    Private uMsgNotify As Integer
    Private lastWindow As IntPtr





    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.TopMost = True

        uMsgNotify = RegisterWindowMessage("SHELLHOOK")
        RegisterShellHookWindow(Me.Handle)

    End Sub

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If m.Msg = uMsgNotify Then
            Select Case m.WParam.ToInt32
                Case ShellEvents.HSHELL_WINDOWACTIVATED
                    Debug.WriteLine("window activated")
                    Dim curWindow As IntPtr = GetForegroundWindow()
                    If (Not curWindow.Equals(IntPtr.Zero)) AndAlso (Not curWindow.Equals(Me.Handle)) Then
                        lastWindow = curWindow
                    End If
            End Select
        End If
        MyBase.WndProc(m)
    End Sub


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        If Not lastWindow.Equals(IntPtr.Zero) Then
            SetForegroundWindow(lastWindow)
            System.Threading.Thread.Sleep(100)
            SendKeys.Send("Hello World...")

        End If

    End Sub

    Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
        DeregisterShellHookWindow(Me.Handle)
    End Sub


End Class
************************************************************
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

You sure you want GetActiveWindow()?
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/getactivewindow.asp

    "The GetActiveWindow function retrieves the window handle to the active window attached to the calling thread's message queue. "

"attached to the calling thread's message queue"...so that will only get the active window of the app that contains Form1.  It won't give you any windows from other processes.
Avatar of TechMonster
TechMonster

ASKER

Idle Mind,
 
Not really sure. I have used the getforegroundwindow and it works for the most part but has recently failed when used with a "web-based" application running on citrix.

The user uses the mouse to designate where the text from my program is going to paste.  They click on the destination application and then go back to my program and press "Go."  

I was hoping there would an alternate way to capture the last activated application since it is failing with vb.net.

Interesting enough I know a different c++ application works fine and is using hooking to remember the last application.  I just don't know the code they are using.  

Can you help?
 
So basically you need a way to let the user pick a "target" window?

Do you only need the "main" window or do you need to get the hWnd of a specific control in the target window?
"you need a way to let the user pick a "target" window?" <-- Exactly.

Just the main window should do it.  The user picks a text field within the window which places the cursor in it.  When they go back to my program and press go it will (should) remember that same window, set it as the active window, then paste some information from my program into the window where the cursor was.  

For the most part the getforeground window DID work.  Works great with Word and Notepad and some other applications but, if they had an application running with "on top" (the most forefront application) on then it will fail thinking that the forefront application was the last activated window.  
Try this...

Add three Labels, a TextBox, and a small PictureBox to a form.  Make the PictureBox so you can see it by setting the BorderStyle, BackColor or Image properties.

Open up NotePad or Wordpad and then drag the PictureBox onto that window and drop it.

You could use SendKeys.Send() instead of SendMessage().

You could make your app hide or minimize when the user "drags" your PictureBox (making it reappear on MouseUp).



Public Class Form1

    Public Declare Function WindowFromPoint Lib "user32" (ByVal xPoint As Integer, ByVal yPoint As Integer) As IntPtr

    Public Declare Function GetAncestor Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal gaFlags As Integer) As IntPtr
    Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As IntPtr, ByVal lpString As String, ByVal cch As Integer) As Integer
    Public Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hWnd As IntPtr) As Integer
    Public Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hWnd As IntPtr, ByVal lpClassName As String, ByVal nMaxCount As Integer) As Integer

    Public Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As IntPtr, ByVal lpdwProcessId As Integer) As Integer
    Public Declare Function AttachThreadInput Lib "user32" (ByVal idAttach As Integer, ByVal idAttachTo As Integer, ByVal fAttach As Integer) As Integer
    Public Declare Function Putfocus Lib "user32" Alias "SetFocus" (ByVal hWnd As IntPtr) As Integer
    Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As String) As Integer

    Public Const GA_ROOT As Integer = 2
    Public Const EM_REPLACESEL As Integer = &HC2

    Private overWindow As IntPtr
    Private mainWindow As IntPtr
    Private windowText As String
    Private className As String
    Private controlText As String

    Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
        If e.Button = Windows.Forms.MouseButtons.Left Then            
            ' for informational purposes...
            overWindow = WindowFromPoint(Windows.Forms.Cursor.Position.X, Windows.Forms.Cursor.Position.Y)

            mainWindow = GetAncestor(overWindow, GA_ROOT)
            windowText = New String(Microsoft.VisualBasic.Chr(0), GetWindowTextLength(mainWindow) + 1)
            GetWindowText(mainWindow, windowText, Len(windowText))
            Label1.Text = "Main Target Window Caption: " & windowText

            className = Space(256)
            Dim retVal As Long
            retVal = GetClassName(overWindow, className, 256)
            className = className.Substring(0, retVal)
            Label2.Text = "Target Window ClassName: " & className

            controlText = New String(Microsoft.VisualBasic.Chr(0), GetWindowTextLength(overWindow) + 1)
            GetWindowText(overWindow, controlText, Len(controlText))
            Label3.Text = "Target Window Caption: " & controlText
        End If
    End Sub

    Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
        If e.Button = Windows.Forms.MouseButtons.Left Then
            ' attempt to paste text into the window we are over
            If Not Me.Handle.Equals(mainWindow) Then
                Dim ThreadID1 As Integer
                Dim ThreadID2 As Integer
                ThreadID1 = GetWindowThreadProcessId(Me.Handle, 0)
                ThreadID2 = GetWindowThreadProcessId(overWindow, 0)

                Call AttachThreadInput(ThreadID1, ThreadID2, True)

                Putfocus(overWindow)
                SendMessage(overWindow, EM_REPLACESEL, &O0, TextBox1.Text)
                Call AttachThreadInput(ThreadID1, ThreadID2, False)
            Else
                Putfocus(overWindow)
                SendMessage(overWindow, EM_REPLACESEL, &O0, TextBox1.Text)
            End If
        End If
    End Sub

End Class
I'm taking a look at your solution right now.  Thanks for doing this.

The only concern of mine is the drop method.  I plan on implementing a version of the solution that
Putsfocus over the window when they press on a "Enter" button on my program.  The trick here is that I am not sure how to capture the "overwindow." when the click on the destination application.  You see once they click on the destination application they stil have to go back to my program to select "enter."

 

That's fine...

The handle to the window is captured in the "overWindow" variable where you can use it later.

I am running code in the MouseUp event but you could do it from anywhere you want.

If you want you could change the cursor as the user drags or drag the outline of the PB for a better visual indicator.

So the user would drag and drop onto the target window to select it.  Then they can go back and press the button whenever they need to.
The code does work well and solves my intial problem of not being able to paste into a webbased application  loaded on Citrix.  The program I have will not use drag and drop method however.  

The user clicks on the destination application text field
Then the user clicks on a "enter" button  on my program which triggers some text within my program to be pasted into the destination application.

It is somewhat different then your solution but your solution I think is going to put me on the right direction.  I should have something here shortly.  
 
 
You could make a small floating window that is always on top for the user drag from and designate the target field to make it easier to work with.

Let me know what else I can do...
Thanks Idle_Mind.  I appreciate your help GREATLY!  

The only thing I need to do is get the destination application via mouseclick
and then capture that destination application via a "enter Button" on from my applicaiton.

Your solution has gotten me further than I been in in weeks!


You won't be able to get the target window via mouse click using the method I showed...only drag and drop.

To get the mouse click route to work, we either need to go back to the SetCapture route (which I'm still playing with...didn't anticipate problems converting it to VB.Net!), use a low level mouse hook, or poll the mouse with a timer (not very pretty).

Another option would be to use a low level keyboard hook so you can trap keypresses.  Then the user would click on the target window to give it focus and press a key combination (F2 or something) to designate that field.
I would lean more towards the SetCapture route..or low level mouse hook(never heard of it before).  They seem to be more intuitive.  

ASKER CERTIFIED SOLUTION
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Idle Mind - Any updates on this?