Bring VB.Net window on top of all windows - vb.net

I have a window which should stay on top of Power point slide shows. So it should be on top of all the windows. I did this easily using VB 6 using Lib "user32", but it seems to be difficut with VB.net.
Me.TopMost = True
This does not seem to work as it works only within the program.
Private Declare Function BringWindowToTop Lib "user32" Alias "BringWindowToTop" (ByVal hwnd As Long) As Long
Private Sub frmTmr_Activated(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Activated
BringWindowToTop(Me.Handle)
End Sub
This also gives a error!
Any help is greatly appreciated!
Thanks in advance,
Regards
Manjula

If you want a window in your application to always appear on top of a window of a different application, then the BringWindowToTop function is definitely not what you want. For starters, as you've noticed, you have to repeatedly call the function using a timer. That should be your first clue that it's the wrong API. Another problem is that it's only bringing your window to the top of the Z order for its process, not all of the other processes running on the system. As the documentation explains,
Calling this function is similar to calling the SetWindowPos function to change a window's position in the Z order. BringWindowToTop does not make a window a top-level window.
That last sentence should indicate that there is a better way. Windows has built-in support for top-level windows (i.e., those that should always appear on top of other windows): these are called top-most windows. This is exactly what you want. Top-most windows always appear above non-topmost windows.
Raymond Chen attempts to explain some of the confusion on his blog. Note that in this case, HWND_TOP is equivalent to BringWindowToTop. Instead, you want HWND_TOPMOST.
The simplest way of making a window top-most is to specify the WS_EX_TOPMOST flag when you create the window. The .NET Framework hides most of the window creation work behind the scenes, but you can customize the parameters when you need to by overriding the CreateParams property of your form class.
Here's some sample code to make a form always top-most:
Protected Overrides ReadOnly Property CreateParams() As CreateParams
Get
Const WS_EX_TOPMOST As Integer = &H00000008
Dim cp As CreateParams = MyBase.CreateParams
cp.ExStyle = cp.ExStyle Or WS_EX_TOPMOST
Return cp
End Get
End Property
This won't work if you want to toggle the top-most state of the window at run-time. To do that, you're going to have to P/Invoke the SetWindowPos function. P/Invoke is similar to what you used to do in VB6 with the Declare statement, but the semantics have changed slightly for the .NET world—that's why you can't use your old VB6 Declare statements in VB.NET.
Here's what that code might look like for VB.NET:
<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As Integer) As Boolean
End Function
Private Const SWP_NOSIZE As Integer = &H1
Private Const SWP_NOMOVE As Integer = &H2
Private Shared ReadOnly HWND_TOPMOST As New IntPtr(-1)
Private Shared ReadOnly HWND_NOTOPMOST As New IntPtr(-2)
Public Function MakeTopMost()
SetWindowPos(Me.Handle(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE)
End Function
Public Function MakeNormal()
SetWindowPos(Me.Handle(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE)
End Function

Related

".MoveSize" or ".Move" for a popup form positioning

I created an on click button event, which is supposed to move my popup form to the top left corner of the screen. The function that I used was:
Private Sub Command1_Click()
DoCmd.MoveSize(0 ,0)
End Sub
I noticed that if my popup form is positioned on my second monitor it will send the popup form to the top left corner of my primary monitor.
Is there a way to send the form to the top left corner of whichever monitor the form is being opened in?
I tried a different idea where I will use a function to use the ".Move" property of the form. I came up with this:
Private Sub Command1_Click()
Form.Move(0, 0)
End Sub
This didn't work either, as it seems that the "0, 0" coordinates are relative to wherever the Access Window is positioned on the screen and not the top left corner of the monitor.
Is this a limitation of Access VBA or is it doable using some other technique?
While this seems trivial, unfortunately, it isn't, and we're going to need to use a fair amount of WinAPI. That makes it really hard for beginners.
We need a couple of things:
We need to be able to determine on which monitor the form is
We need to be able to determine where that monitor is in the "virtual screen" (think about positioning monitors relative to eachother)
We need to be able to determine the size of the current window in pixels
We need to be able to position the form on the "virtual screen".
For that, we need a couple of declarations. These are best kept on separate modules, but if you're 100% sure they will only be used on this form, they can go on the form too.
First, the type and value declarations:
'https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect
Public Type RECT
left As Long
top As Long
right As Long
bottom As Long
End Type
'https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-monitorinfo
Public Type MONITORINFO
cbSize As Long
rcMonitor As RECT
rcWork As RECT
dwFlags As Long
End Type
'Either look this one up by Googling, or create a C++ program that references winuser.h and print it
Public Const MONITOR_DEFAULTTONEAREST = &H2
Then, the function declarations:
'https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmonitorinfow
Public Declare PtrSafe Function GetMonitorInfoW Lib "User32.dll" (ByVal hMonitor As LongPtr, ByRef lpmi As MONITORINFO) As Boolean
'https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfromwindow
Public Declare PtrSafe Function MonitorFromWindow Lib "User32.dll" (ByVal hWnd As LongPtr, ByVal dwFlags As Long) As LongPtr
'https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-movewindow
Public Declare PtrSafe Function MoveWindow Lib "User32.dll" (ByVal hWnd As LongPtr, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Boolean) As Boolean
'https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowrect
Public Declare PtrSafe Function GetWindowRect Lib "User32.dll" (ByVal hWnd As LongPtr, ByRef lpRect As RECT) As Boolean
And then, on the form, putting it all to work:
Private Sub Command0_Click()
Dim mi As MONITORINFO
Dim monitor As LongPtr
Dim myrect As RECT
'Get the current size and position of the window
GetWindowRect Me.hWnd, myrect
'Determine which monitor it is on
monitor = MonitorFromWindow(Me.hWnd, MONITOR_DEFAULTTONEAREST)
'Make sure WinAPI knows the size of the MONITORINFO struct we're working with
mi.cbSize = LenB(mi)
'Get the monitor info
GetMonitorInfoW monitor, mi
'Move the window to the top right, keep width and height equal to the current values
MoveWindow Me.hWnd, mi.rcMonitor.left, mi.rcMonitor.top, myrect.right - myrect.left, myrect.bottom - myrect.top, True
End Sub
Unfortunately, that's quite a lot more code and more complicated concepts than DoCmd.MoveSize(0 ,0), but I do not know of a simpler approach. VBA doesn't really have any support for multiple monitors, so you'll often have to go to WinAPI to account for them.

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

Switch focus to a vb.net application on key press

I have a vb.net application which will be minimized to task bar or notification area. Is there any way for me to maximize/focus on that application when user presses a key (even if the user is using any other application).
Tried windows hot key but doesn't focus the app when it is already open.Please help
You require global hotkeys. First, add these functions to your application.
Private Declare Function GetKeyPress Lib "user32" Alias "GetAsyncKeyState" (ByVal key As Integer) As Integer
<DllImport("user32.dll", EntryPoint:="FindWindow", SetLastError:=True)> _
Private Shared Function FindWindow(lpClassName As String, lpWindowName As String) As IntPtr
End Function
<DllImport("user32.dll")> _
Private Shared Function ShowWindow(hWnd As IntPtr, nCmdShow As Integer) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function SetForegroundWindow(hWnd As IntPtr) As Integer
End Function
Next, add a Timer and in its Timer.Tick Event, use this function like this:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
If GetKeyPress(Keys.LControlKey) And GetKeyPress(Keys.A) Then
Dim Handle As IntPtr = Process.GetProcessById(2916).MainWindowHandle
ShowWindow(Handle, 9)
SetForegroundWindow(Handle)
End If
End Sub
Set the Timer Interval to 150 to avoid repeated keypresses and make sure to enable the timer in the Form Load event.

Microsoft word opening in the background

I have an application written in VB.net running on Windows 7 that opens a word document and inserts some values into it. This works fine, but on my client's machines(development works fine) Word is opening up behind my application. I've tried maximizing the document in code, but it's still opening behind my application on the client machines. Does anyone have any idea how I can fix this?
Things I've already tried:
maximizing word in my application
giving focus to word in my application
I've made sure the patches are up to date.
I've performed a repair on Office
Have you tried minimizing the application, as opposed to maximizing the document?
Me.WindowState = FormWindowState.Minimized will minimize the form that calls it (this is assuming that you are using a forms application).
You might need to bring Word to the forefront. This is a bit different from bringing a form in your app to the top.
You'll need to have a reference to two APIs, FindWindow and SetWindowPos - the first one can find the windows handle for another application that is running, and the second sends a message to the operating system to give an application focus (it uses the windows handle from FindWindow)
Here's some sample code.
Public Class Form1
<Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True, CharSet:=Runtime.InteropServices.CharSet.Auto)> _
Private Shared Function FindWindow(ByVal lpClassName As String, _
ByVal lpWindowName As String) As IntPtr
End Function
<Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As Integer) As Boolean
End Function
Private Shared ReadOnly HWND_TOPMOST As New IntPtr(-1)
Private Shared ReadOnly HWND_NOTOPMOST As New IntPtr(-2)
Private Const SWP_NOSIZE As Integer = &H1
Private Const SWP_NOMOVE As Integer = &H2
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Shell("calc.exe")
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim myHandle As IntPtr = FindWindow(Nothing, "Calculator")
SetWindowPos(myHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE)
SetWindowPos(myHandle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE)
End Sub
End Class
Clicking the first button will instantiate a copy of Calculator, the second button will make it topmost, then set it back to normal... so it will still be the top form, but the user can activate other windows as well.

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.