AppHangB1 trying to bring an Outlook.Explorer to the fore - vb.net

We have an application that may under some circumstances launch Outlook. It gets an Outlook.Explorer object (oWindow As Outlook.Explorer) and calls its .Activate() procedure.
Up to that point all is hunky dory, but then I want to bring the Explorer object to the foreground, and we call this procedure:
Public Shared Sub BringToFore(ByVal oWindow As Object)
Dim oFoo As IOleWindow
Dim hWnd As IntPtr
oFoo = TryCast(oWindow, IOleWindow)
If Not (oFoo Is Nothing) Then
Try
oFoo.GetWindow(hWnd)
Catch ex As Exception
End Try
End If
Try
If hWnd.ToInt32 <> IntPtr.Zero.ToInt32 Then
Try
If IsIconic(hWnd) Then
ShowWindow(hWnd, SW_RESTORE)
End If
SetForegroundWindow(hWnd)
Catch ex As System.Exception
End Try
Else
End If
Catch ex As Exception
End Try
End Sub
IOleWindow is defined as follows:
<ComImport(), Guid("00000114-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Private Interface IOleWindow
''' <summary>
''' Returns the window handle to one of the windows participating in in-place activation
''' (frame, document, parent, or in-place object window).
''' </summary>
''' <param name="phwnd">Pointer to where to return the window handle.</param>
Sub GetWindow(<System.Runtime.InteropServices.Out()> ByRef phwnd As IntPtr)
''' <summary>
''' Determines whether context-sensitive help mode should be entered during an
''' in-place activation session.
''' </summary>
''' <param name="fEnterMode"><c>true</c> if help mode should be entered;
''' <c>false</c> if it should be exited.</param>
Sub ContextSensitiveHelp(<[In](), MarshalAs(UnmanagedType.Bool)> ByVal fEnterMode As Boolean)
End Interface
and the usual declarations
Private Const SW_RESTORE As Integer = 9
Private Declare Auto Function IsIconic Lib "user32" (ByVal hWnd As IntPtr) As Boolean
Private Declare Auto Function SetForegroundWindow Lib "user32" (ByVal hwnd As IntPtr) As Long
Private Declare Auto Function ShowWindow Lib "user32" (ByVal hWnd As IntPtr, ByVal nCmdShow As Integer) As IntPtr
The BringToFore procedure works fine most of the time. And sometimes the calling application - a WPF application - just hangs. The Event Viewer records an AppHangB1 and the application crashes.
Is there anything I can do in the BringToFore procedure to prevent this happening? Any idea which of these are causing the problem? TryCast(oWindow, IOleWindow), oFoo.GetWindow(hWnd), IsIconic(hWnd), ShowWindow(hWnd, SW_RESTORE) or SetForegroundWindow(hWnd)? (I personally suspect it may be SetForegroundWindow, to be honest). If so, is there anything I can do in the code? Some condition to check? If this condition is true then don't try to do that ..... sort of thing? I'd rather not go as far as just abandoning the idea of setting the Explorer to the foreground; in that circumstance Outlook may appear "behind" my application and some users may just not realise that something has happened.... if you catch my drift.
Thanks
Pino

Windows would prevent you from using SetForegroundWindow if the window belongs to a process other than the active one. You'd need to call AttachThreadInput first. Here is the function (Delphi):
function ForceForegroundWindow(hWnd: THandle): BOOL;
var
hCurWnd: THandle;
begin
hCurWnd := GetForegroundWindow;
AttachThreadInput(
GetWindowThreadProcessId(hCurWnd, nil),
GetCurrentThreadId, True);
Result := SetForegroundWindow(hWnd);
AttachThreadInput(
GetWindowThreadProcessId(hCurWnd, nil),
GetCurrentThreadId, False);
end;

I think this issue would require more debugging. I found a similar issue posted here quite a few years back with a suggestion which might be useful to get to the root cause.

Related

Give focus to application control when application loses focus

I have an application which is designed to run full screen constantly. This works fine normally, however, when things run in the background, for example, an antivirus update this can bring that window above my app. Thats fine because I can use things like:
SetForegroundWindow
ShowWindow
SwitchToThisWindow
All of which allow me to bring my application back to the front. However, inside the application is a hidden text box which when the application loads is focussed. When I use one of the pInvoke calls above whilst the application is brought back to front, the focus is still on the existing application.
I am currently struggling with the best way of giving focus back to the control.
I could use Control.FromHandle but seems fairly complicated to get the controls I need and offer focus if a specific tab page is at the front. Is there a better way, any thoughts / ideas welcome.
I was running this on a Windows 10 LTSB unit and as previously mentioned SetForegroundWindow and Show functions were not working along with many other functions I found on pInvoke. I managed to select the correct process and bring it to the forefront if something else takes its place to the top. The issue was that it would never activate no matter what I tried.
In the end, I implemented the following code which checks every 5 seconds, if my app is not the foremost and not maximised then minimise / maximise the window and this reactivates and refocuses the app:
Public Declare Function FindWindow Lib "user32.dll" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Public Declare Function ShowWindowAsync Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal nCmdShow As Integer) As IntPtr
Private Declare Function GetWindowPlacement Lib "user32.dll" (ByVal hWnd As IntPtr, ByRef lpwndpl As WINDOWPLACEMENT) As Boolean
Private Declare Function GetForegroundWindow Lib "user32.dll" () As IntPtr
<Serializable>
Friend Structure WINDOWPLACEMENT
Public length As Integer
Public flags As Integer
Public showCmd As ShowWindowCommands
Public ptMinPosition As System.Drawing.Point
Public ptMaxPosition As System.Drawing.Point
Public rcNormalPosition As System.Drawing.Rectangle
End Structure
Friend Enum ShowWindowCommands
Hide = 0
Normal = 1
Minimized = 2
Maximized = 3
End Enum
Private Async Function CheckCurrentApp() As Task
Try
' Try and locate the core process
Dim coreHandle = FindWindow(Nothing, "Name of window")
If coreHandle = IntPtr.Zero Then
' Can't find the core. Exit here.
Exit Try
End If
' Get information about the Core window
Dim currentWindowInfo As WINDOWPLACEMENT
GetWindowPlacement(coreHandle, currentWindowInfo)
' If the core is not the foreground window or isn't maximised then send a minimise (6) and maximise (3) request.
' Activate functions in user32 don't work - I spent a day trying to make it so. I could get the foreground window as the core but the input would
' remain in a different application.
If coreHandle <> GetForegroundWindow() OrElse currentWindowInfo.showCmd <> ShowWindowCommands.Maximized Then
ShowWindowAsync(coreHandle, 6)
ShowWindowAsync(coreHandle, 3)
End If
Catch ex As Exception
' DO SOMETHING WITH THE EXCEPTION.
End Try
Await Task.Delay(TimeSpan.FromSeconds(5))
Await CheckCurrentApp()
End Function

How do I check if a certain process has focus?

I'm trying to check if javaw.exe has focus, then execute certain code if it does.
Previously I had code which would look for the process ID of javaw.exe, then compare it to the process which currently had focus, which worked for awhile, but then I noticed when I had more than one javaw.exe process running, it would only work on one of those processes, while I need it to work when any javaw.exe process has focus.
Is there any way to do this?
You can determine this quite easily using the GetForegroundWindow() and GetWindowThreadProcessId() WinAPI functions.
First call GetForegroundWindow to get the window handle of the currently focused window, then call GetWindowThreadProcessId in order to retrieve the process id of that window. Finally get it as a Process class instance by calling Process.GetProcessById()
Public NotInheritable Class ProcessHelper
Private Sub New() 'Make no instances of this class.
End Sub
<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function GetForegroundWindow() As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, ByRef lpdwProcessId As UInteger) As Integer
End Function
Public Shared Function GetActiveProcess() As Process
Dim FocusedWindow As IntPtr = GetForegroundWindow()
If FocusedWindow = IntPtr.Zero Then Return Nothing
Dim FocusedWindowProcessId As UInteger = 0
GetWindowThreadProcessId(FocusedWindow, FocusedWindowProcessId)
If FocusedWindowProcessId = 0 Then Return Nothing
Return Process.GetProcessById(CType(FocusedWindowProcessId, Integer))
End Function
End Class
Usage example:
Dim ActiveProcess As Process = ProcessHelper.GetActiveProcess()
If ActiveProcess IsNot Nothing AndAlso _
String.Equals(ActiveProcess.ProcessName, "javaw", StringComparison.OrdinalIgnoreCase) Then
MessageBox.Show("A 'javaw.exe' process has focus!")
End If
Hope this helps!

Get Application Name From Keyboard Input in VB.net

I am making application in vb.net which when ran detects keystrokes from keyboard in any editors. Now I can get keystrokes, thats easy part but I want to know the application name like if user is writing in notepad, then I want that name.
Please can any one suggest any idea.
Thanks
You need to use windows api:
Private Sub Main()
Dim shortName as String = GetShortEXEName(GetForegroundWindow() )
End Sub
' Define other methods and classes here
<DllImport("user32.dll", SetLastError := True)> _
Public Shared Function GetForegroundWindow() As IntPtr
End Function
Public Shared Function GetProcess(hwnd As IntPtr) As Process
Dim intID As Integer = 0
GetWindowThreadProcessId(hwnd, intID)
Return Process.GetProcessById(intID)
End Function
<DllImport("user32.dll", SetLastError := True)> _
Public Shared Function GetWindowThreadProcessId(hwnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer
End Function
Public Shared Function GetShortEXEName(hwnd As IntPtr) As String
' this seems to be better to use than calling GetProcessEXEName and then using substring functions to get the short executable name
' because that function seems to crash when dealing with Windows Explorer windows
Try
Return GetProcess(hwnd).ProcessName
Catch
Return ""
End Try
End Function

keyboard shortcut to show hidden form

I'm creating a program to manage a local network but Im having a little problem.
When the form starts, it starts hidden.
I used this code to hide it:
Protected Overrides Sub SetVisibleCore(ByVal value As Boolean)
If Not Me.IsHandleCreated Then
Me.CreateHandle()
value = False
End If
MyBase.SetVisibleCore(value)
End Sub
What I want exactly is to show the form when I click ALT+X for example.
Use a Global Hotkey. That way you don't have to worry about your Application having focus. A good example for VB.Net is here:
http://www.kirsbo.com/How_to_add_global_hotkeys_to_applications_in_VB.NET
To summarise. Define a HotKey class:
Public Class Hotkey
#Region "Declarations - WinAPI, Hotkey constant and Modifier Enum"
''' <summary>
''' Declaration of winAPI function wrappers. The winAPI functions are used to register / unregister a hotkey
''' </summary>
Public Declare Function RegisterHotKey Lib "user32" _
(ByVal hwnd As IntPtr, ByVal id As Integer, ByVal fsModifiers As Integer, ByVal vk As Integer) As Integer
Public Declare Function UnregisterHotKey Lib "user32" (ByVal hwnd As IntPtr, ByVal id As Integer) As Integer
Public Const WM_HOTKEY As Integer = &H312
Enum KeyModifier
None = 0
Alt = &H1
Control = &H2
Shift = &H4
Winkey = &H8
End Enum 'This enum is just to make it easier to call the registerHotKey function: The modifier integer codes are replaced by a friendly "Alt","Shift" etc.
#End Region
#Region "Hotkey registration, unregistration and handling"
Public Shared Sub registerHotkey(ByRef sourceForm As Form, ByVal triggerKey As String, ByVal modifier As KeyModifier)
RegisterHotKey(sourceForm.Handle, 1, modifier, Asc(triggerKey.ToUpper))
End Sub
Public Shared Sub unregisterHotkeys(ByRef sourceForm As Form)
UnregisterHotKey(sourceForm.Handle, 1) 'Remember to call unregisterHotkeys() when closing your application.
End Sub
Public Shared Sub handleHotKeyEvent(ByVal hotkeyID As IntPtr)
MsgBox("The hotkey was pressed")
End Sub
#End Region
End Class
Then add the following Sub to your main form:
'System wide hotkey event handling
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = Hotkey.WM_HOTKEY Then
Hotkey.handleHotKeyEvent(m.WParam)
End If
MyBase.WndProc(m)
End Sub
Then register a HotKey in the startup code for your form:
Hotkey.RegisterHotKey(Me, "X", Hotkey.KeyModifier.Alt)
The first parameter is the form, the second is the key to be handled and the third is the modifier key. Once this Hotkey is registered it will trigger code in HotKey.handleHotKeyEvent.
You can then use some sort of Callback to trigger a method in the calling form itself if you so desire.

Create a service to execute an exe after one screen opens in different exe ( VB.NET)

My question maybe not be very clear, but I just want to know how this process is called or references needed to create something similar, so I can investigate on my own ( but if you have code is welcome.. LOL...)
Basically I have 2 desktop programs ( A and B). For A, I do not have the source code, for B I do. What I need is to create some service/program that after a screen pops up from program A, automatically runs B. IN other words, capture the moment a specific screen is shown in A and execute B.
My real life scenario is that I have a very basic POS where I can't collect customer demographics ( zip code, etc), so I created a second application to capture that but my cashiers are always forgetting to run the program and I need to find a way to run it after a screen is shown ( let's say the "Change Due" in the POS, so they don't forget to run it.
Any pointings will be appreciated!
Thanks
So here's a very quick example of how you can do this with VB which should be very easy to convert to C# if needed. The code search visible windows by title. If your open window doesn't have a title you'll have to work a little more. You can probably find the title of your main app and just enumerate its child windows.
First, some unmanaged code to talk to Win32 directly:
Option Strict On
Option Explicit On
Imports System.Runtime.InteropServices
Imports System.Text
Public Class Unmanaged
<DllImport("user32.dll")>
Public Shared Function EnumWindows(ByVal lpEnumFunc As CallBack, ByVal lParam As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("user32.dll", SetLastError:=True)>
Public Shared Function GetWindowLong(ByVal hWnd As IntPtr, ByVal nIndex As Integer) As Integer
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Public Shared Function GetWindowText(ByVal hwnd As IntPtr, ByVal lpString As StringBuilder, ByVal cch As Integer) As Integer
End Function
<DllImport("user32.dll", SetLastError:=True)> _
Public Shared Function IsWindowVisible(ByVal hWnd As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Public Const SW_SHOW = 5
Public Const SW_RESTORE = 9
Public Const GW_OWNER = 4
Public Const GWL_HWNDPARENT = (-8)
Public Const GWL_EXSTYLE = (-20)
Public Const WS_EX_TOOLWINDOW = &H80
Public Const WS_EX_APPWINDOW = &H40000
Public Delegate Function CallBack(ByVal hwnd As IntPtr, ByVal lParam As Integer) As Boolean
End Class
Then a non-GUI module which should be set as the startup object:
Option Explicit On
Option Strict On
Module Module1
''//Code loosely base on http://msdntracker.blogspot.com/2008/03/list-currently-opened-windows-with.html
''//This is the title of the window that we are looking for
Public ReadOnly WatchForTitle As String = "About Mozilla Firefox"
''//This is the form that we will show when we find the above
Private MainForm As Form1
<STAThread()>
Public Sub Main()
''//Create the form but don not show it
MainForm = New Form1()
''//Create an infinite loop that checks to see if the target window is open and sleeps for a bit between checks
Do While True
Unmanaged.EnumWindows(AddressOf fEnumWindowsCallBack, IntPtr.Zero)
''//Sleep for a bit
System.Threading.Thread.Sleep(500)
Loop
End Sub
Private Function fEnumWindowsCallBack(ByVal hwnd As IntPtr, ByVal lParam As Integer) As Boolean
''//Ignore our own handle
If hwnd <> Form1.Handle Then
''//Make sure its visible
If Unmanaged.IsWindowVisible(hwnd) Then
Dim lExStyle = Unmanaged.GetWindowLong(hwnd, Unmanaged.GWL_EXSTYLE)
''//We probably want to ignore tool windows, but remove this if needed
If (((lExStyle And Unmanaged.WS_EX_TOOLWINDOW) = 0)) Then
''//Create a buffer to store the title of the window
Dim sWindowText As New System.Text.StringBuilder(256)
''//Get the title of the window
Dim lReturn = Unmanaged.GetWindowText(hwnd, sWindowText, sWindowText.Length - 1)
''//When you are looking for window title uncomment this line
'Trace.WriteLine(sWindowText)
''//Sanity check, make sure we found a window title
If lReturn <> 0 Then
''//See if it matches what we are looking for
If sWindowText.ToString() = WatchForTitle Then
''//If so, show our form
Form1.ShowDialog()
End If
End If
End If
End If
End If
fEnumWindowsCallBack = True
End Function
End Module
Lastly create a regular Windows Form called Form1 (or whatever you want, you'll just need to change it in the Module above).
If you test this it will pop open the form if you go to Firefox's Help->About menu.
I should point out, this is just a start. You'll want to perform better error checking and handle when the program closes or exists or whatever.
It's very unlikely that there is any 'good' way to interact with program A. There won't be a traditional 'event' that fires or anything like that.
Your best bet is going to be looking at some WIN32 API calls. For example, you can use GetPixel() to return the color at a specific location on the screen. So, if program A displays a particular image or screen that is measurably different from any other screen, you can write code that monitors the screen and checks for a match. When the match is found you can execute any code you want, including launching B.