Detecting if a window is full screen - vb.net

My English is not so good, but I try to make a logic story.
I like home domotica, so I made a program in visual basic (visual studio) that control de lightning of my room (with Arduino). A function of the program is turning on/off the lights when I run an application in full screen (for movies, gaming). I used a code for detect full screen from internet, but the part for comparing the forgroundscreen with the desktop isn't working, I get a falsely fullscreen. :
Private Function detectfullscreen()
'Detect if the current app is running in full screen
Dim runningFullScreen As Boolean = False
Dim appBounds As RECT
Dim screenBounds As Rectangle
Dim hWnd As IntPtr
'get the dimensions of the active window
hWnd = GetForegroundWindow()
If hWnd <> Nothing AndAlso Not hWnd.Equals(IntPtr.Zero) Then
'Check we haven't picked up the desktop or the shell
If Not (hWnd.Equals(desktopHandle) OrElse hWnd.Equals(shellHandle)) Then
'If hWnd <> GetDesktopWindow() Then
GetWindowRect(hWnd, appBounds)
'determine if window is fullscreen
screenBounds = Screen.FromHandle(hWnd).Bounds
If (appBounds.Bottom - appBounds.Top) = screenBounds.Height AndAlso (appBounds.Right - appBounds.Left) = screenBounds.Width Then
runningFullScreen = True
End If
End If
End If
Return runningFullScreen
End Function
The rest of de code (for the register) is:
<StructLayout(LayoutKind.Sequential)> Public Structure RECT
Public Left As Integer
Public Top As Integer
Public Right As Integer
Public Bottom As Integer
End Structure
Private desktopHandle As IntPtr = GetDesktopWindow()
Private shellHandle As IntPtr = GetShellWindow()
<DllImport("user32.dll")> Private Shared Function GetForegroundWindow() As IntPtr
End Function
<DllImport("user32.dll")> Private Shared Function GetDesktopWindow() As IntPtr
End Function
The code works perfect and i use a timer to run this sub, but if I go to desktop, the program also thinks I use a full screen application.
My operatingsystem is Windows 10, is it possible that I need an other way to detect if the current forgroundscreen is the desktop? I try to use de ID of the desktop, but when I restart the computer, this ID will change.
I hoop my story was clear. Thank you for reading this!

Related

Launch an application and "force" focus if an error occurs?

I have a scaling program used in our shipping department. The user scans a carton, it handles some database processing, and inserts a record identifying that carton into a "queue" table. The scale program also starts up a separate .exe which handles label processing (making calls to FedEx or UPS, or building custom ZPLs depending on what is needed, then sends to the printers attached to the machine). These PCs are Windows 7, if that makes a difference.
Occasionally a bad piece of data will cause an error in the label program. We recently discovered that one of our customers do not require a valid phone number when one of their customers place an order, and if the carton ends up shipping FedEx Home Delivery or SmartPost where a phone number is required, the API returns an error.
The print label program's error messages are popping up behind the scale program. Is there a way to force the error messages from the print label program to the top of any other open windows?
EDIT: I am calling the label program by first checking to see if there is a process running with the .exe's name. If not, it calls a launcher utility which looks on our app server to see if an updated exe is available, and copies to the c:\tmp directory if it is. Then runs the local c:\tmp copy of the program for that machine.
One way to accomplish this is to set your error form as a TopMost window. If you would always like the form to be above all other windows (including windows from other processes), add this to your form:
Private Const WS_EX_TOPMOST As Integer = &H00000008
Protected Overrides ReadOnly Property CreateParams() As CreateParams
Get
Dim cp As CreateParams = MyBase.CreateParams
cp.ExStyle = cp.ExStyle Or WS_EX_TOPMOST
Return cp
End Get
End Property
This code overrides CreateParams for the window, adding in the TOPMOST flag. You don't have to do anything after this - the window will always be topmost.
You can also use the p/Invoke SetWindowPos to make the window topmost on-demand:
<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
Public Sub SetWindowAsTopMost()
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE)
End Sub

SendMessage between WinForms Applications - form needs focus

I'm using the Windows Messages API to communicate between two Windows Forms Apps. I took out a form that was a part of the application and made it into it's own app so that when it is loading the user can still work on the other forms now in a separate app from the one form.
I need to be able to communicate between the two apps so that the now separate app form can tell the main app what to open.
I use this code on the main form of the main app and it works great... Except when the main form doesn't have focus.
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Try
Select Case m.Msg
Case &H400
BS.BuildString(m.LParam)
Case Else
MyBase.WndProc(m)
End Select
Catch ex As Exception
End Try
End Sub
I've never used the Windows Message API before, I just grabbed this from looking up how to communicate between forms, so I'm new at this. What I do know from doing some more research is that I need a Message only Window to handle the messages. I don't understand how to do this.
I've looked at a few articles and solutions like this one and I think the problem I'm having is that I don't know how to implement it.
Edit 1
Here is how the second app finds and sends to the main app.
'used to send a message using SendMessage API to the
'main app to open the ID
Private WithEvents BS As New BuildString
'get this running process
Dim proc As Process = Process.GetCurrentProcess()
'get all other (possible) running instances
Dim processes As Process() = Process.GetProcessesByName("ProcessName")
If processes.Length > 0 Then
'iterate through all running target applications
For Each p As Process In processes
'now send the ID to the running instance of the main app
BS.PostString(p.MainWindowHandle, &H400, 0, "ID:" & ID)
Next
Else
MessageBox.Show("Main application not running")
End If
Here's the class for the BuildString that is in both apps.
Imports System.Text
Public Class BuildString
Private Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
Public Event StringOK(ByVal Result As String)
Private hwnd As Integer = 0
Private wMsg As Integer = 0
Private wParam As Integer = 0
Private lParam As String = ""
Private tempA(-1) As Byte
Private enc As Encoding = Encoding.UTF8
Public Property Encode() As Encoding
Get
Return enc
End Get
Set(ByVal value As Encoding)
enc = value
End Set
End Property
Public Sub BuildString(ByVal b As IntPtr)
If b <> 0 Then
'build temp array
Dim tempB(tempA.Length) As Byte
tempA.CopyTo(tempB, 0)
tempB(tempA.Length) = b
ReDim tempA(tempB.Length - 1)
tempB.CopyTo(tempA, 0)
Else
'decode byte array to string
Dim s As String
If enc Is Encoding.UTF8 Then
s = Encoding.UTF8.GetString(tempA)
ElseIf enc Is Encoding.Unicode Then
s = Encoding.Unicode.GetString(tempA)
ElseIf enc Is Encoding.ASCII Then
s = Encoding.ASCII.GetString(tempA)
Else
s = Encoding.Default.GetString(tempA)
End If
'send out result string via event
RaiseEvent StringOK(s)
ReDim tempA(-1)
End If
End Sub
Public Sub PostString(ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As String)
Me.hwnd = hwnd
Me.wMsg = wMsg
Me.wParam = wParam
Me.lParam = lParam
'create a new thread to post window message
Dim t As Threading.Thread
t = New Threading.Thread(AddressOf SendString)
t.Start()
End Sub
Private Sub SendString()
'create byte array
Dim ba() As Byte
'encode string to byte array
If enc Is Encoding.UTF8 Then
ba = Encoding.UTF8.GetBytes(lParam)
ElseIf enc Is Encoding.Unicode Then
ba = Encoding.Unicode.GetBytes(lParam)
ElseIf enc Is Encoding.ASCII Then
ba = Encoding.ASCII.GetBytes(lParam)
Else
ba = Encoding.Default.GetBytes(lParam)
End If
Dim i As Integer
For i = 0 To ba.Length - 1
'start post message
PostMessage(hwnd, wMsg, wParam, ba(i))
Next
'post a terminator message to destination window
PostMessage(hwnd, wMsg, wParam, 0)
End Sub
End Class
Here is the code on the main app that is run once the message gets decoded.
Private Sub SB_StringOK(ByVal Result As String) Handles BS.StringOK
Dim sArray() As String = Result.Split(";")
'rest of the code to open the ID
End Sub
Thanks to #JustinRyan I figured it out using FindWindow.
I added this to my second application that send the message.
Private Declare Function FindWindow1 Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Integer
Instead of finding the application process I find the window I want and send it directly to it in the send message.
Dim parenthwnd As Integer = FindWindow1(Nothing, "Form Name")
BS.PostString(parenthwnd, &H400, 0, "ID:" & ID)
It works just as I needed.
I'm not sure of how else to show the correct answer since the answer was in the comments.
Please let me know of the proper etiquette in this situation to give the user his due credit.
Instead of using this API, you could try using a new thread and having your second form created on this second thread, so both forms run on separate threads and can still be part of the same project
Dim Form2Thread As New System.Threading.Thread(Address of MethodName)
Sub Form1Load() Handles Form1.Shown
Form2Thread.Start()
End Sub
Sub MethodName()
'All your form creation code here
'Form2.Show()
End Sub
This is simple enough, but the downside is you can not directly edit a control or property of Form2 from a method running on your original thread. All changes to Form2 have to be made through your second thread.
(There is an exception to this but things start to get more complicated, search for how to do cross-thread operations)
The other solution is to use a Background Worker component. Plenty of tutorials around on using those.
[To add a bit more information:]
According to Process.MainWindowHandle,
The main window is the window opened by the process that currently has
the focus (the TopLevel form). You must use the Refresh method to
refresh the Process object to get the current main window handle if it
has changed.
TopLevel is defined here (as linked from MainWindowHandle) as,
A top-level form is a window that has no parent form, or whose parent
form is the desktop window. Top-level windows are typically used as
the main form in an application.
This would explain why messages are being sent elsewhere when the form is inactive. So, while using the Process properties may work for getting the window handle of single form applications, this makes it unreliable otherwise.
Coincidentally, FindWindow also states,
Retrieves a handle to the top-level window whose class name and window
name match the specified strings. This function does not search child
windows.
However, FindWindow (and FindWindowEx) is agnostic to focus (activation) and will therefore return a specific window's handle without concern of whether or not it is on top.

VS application stops in Win7, when Debug and Release binaries are used

So, I've made an iterative Towers of Hanoi algorithm in Visual Basic, that runs in a while loop (recursion is slow in VB). The catch is it compiles okey, it even runs okey when launched through Visual Studio, but when launched though the Debug and Release generated execs the animation stops with the following message:
After a while, I just see all the pieces moved to the destination pole and the message disappears. So its not a crash per say, as the application is still running in the background, its just this message that pops out, ruining the animation. I just want my program to run just as it runs when launched directly from Visual Studio.
After a bit of thinking ...
I'm starting to believe this happens because Win7 treats the fact the application runs in a while loop as unresponsive (7 pieces in Towers of Hanoi ca take a while to rearrange), therefore it tries to close it.
How can I just make my application ignore Window's advertisements ?
I suggest that you do the calculation in the application idle event just like you do when creating a windows game. This way you ensure that the message queue is not blocked.
Public Class Form1
Public Sub New()
Me.InitializeComponent()
AddHandler Application.Idle, AddressOf Me.OnApplicationIdle
End Sub
Private Sub OnApplicationIdle(sender As Object, e As EventArgs)
Static rnd As New Random()
Dim message As MSG = Nothing
Do While (Not PeekMessage(message, IntPtr.Zero, 0, 0, 0))
'...
Me.BackColor = Color.FromArgb(rnd.Next(0, 256), rnd.Next(0, 256), rnd.Next(0, 256))
'...
Loop
End Sub
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Friend Shared Function PeekMessage(<[In](), Out()> ByRef msg As MSG, ByVal hwnd As IntPtr, ByVal msgMin As Integer, ByVal msgMax As Integer, ByVal remove As Integer) As Boolean
End Function
<StructLayout(LayoutKind.Sequential)> _
Friend Structure MSG
Public hwnd As IntPtr
Public message As Integer
Public wParam As IntPtr
Public lParam As IntPtr
Public time As Integer
Public pt_x As Integer
Public pt_y As Integer
End Structure
End Class

How to make a window not associated with the application minimize or maximize its window state in vb?

If you have ever noticed in the Task Manager, when you right-click on the running task, you have many options which include 'Minimize' and 'Maximize'. Is there anyway to do achieve this in vb?
Here is an example of the code you are looking for. It will loop through all the active processes and minimize all the windows.
In your app you will probably want to use something like Process.GetProcessesByName to find the specific window you want to manipulate.
Imports System.Runtime.InteropServices
Module ManipulateWindows
Const SW_HIDE As Integer = 0
Const SW_RESTORE As Integer = 1
Const SW_MINIMIZE As Integer = 2
Const SW_MAXIMIZE As Integer = 3
<DllImport("User32")> _
Private Function ShowWindow(ByVal hwnd As Integer, ByVal nCmdShow As Integer) As Integer
End Function
Public Sub Main()
'iterate through all the open processes.
For Each p As Process In Process.GetProcesses
'Get the Window Handle
Dim hWnd as integer = CType(p.MainWindowHandle, Integer)
'Write out the title of the main window for the process.
System.Console.WriteLine(p.MainWindowTitle)
'Minimize the Window
ShowWindow(hWnd, SW_MINIMIZE)
Next p
End Sub
End Module

record amount of time computer has been in use?

i would like to have program a timer that will count the seconds during which there is mouse movement or any keyboard movement.
the point of this application is to record the amount of time an employee has been using the computer (does not matter what purpose or application it has been in use for)
i would like to do this in vb.net for winforms
I do exactly this using P/Invoke to talk to the GetLastInputInfo API.
Edit: Here's a complete VB.Net program to display the number of milliseconds since the last input event, system-wide. It sleeps for a second before getting the information, so it reports a time of around a thousand milliseconds, assuming you use the mouse or keyboard to run it. :-)
Imports System.Runtime.InteropServices
Module Module1
<StructLayout(LayoutKind.Sequential)> _
Public Structure LASTINPUTINFO
Public Shared ReadOnly SizeOf As Integer = Marshal.SizeOf(GetType(LASTINPUTINFO))
<MarshalAs(UnmanagedType.U4)> _
Public cbSize As Integer
<MarshalAs(UnmanagedType.U4)> _
Public dwTime As Integer
End Structure
<DllImport("user32.dll")> _
Public Function GetLastInputInfo(ByRef plii As LASTINPUTINFO) As Boolean
End Function
Sub Main()
Dim lii As New LASTINPUTINFO()
lii.cbSize = LASTINPUTINFO.SizeOf
lii.dwTime = 0
System.Threading.Thread.Sleep(1000)
GetLastInputInfo(lii)
MsgBox((Environment.TickCount - lii.dwTime).ToString)
End Sub
End Module