Example from .Net Framework 4.8 Winform project. The form contains a single ListBox.
Public Class Form1
Protected Overrides Sub WndProc(ByRef m As Message)
Dim processId As Integer = 0
If m.Msg = Win32.WM_ACTIVATEAPP Then
If m.LParam <> 0 Then
Dim handle As IntPtr = Win32.OpenThread(Win32.THREAD_QUERY_LIMITED_INFORMATION, False, m.LParam.ToInt32())
If handle <> IntPtr.Zero Then
processId = Win32.GetProcessIdOfThread(handle)
If processId = 0 Then
ListBox1.Items.Add($"Error {Marshal.GetLastWin32Error()} getting processid")
End If
Win32.CloseHandle(handle)
Else
ListBox1.Items.Add($"Error {Marshal.GetLastWin32Error()} opening thread {m.LParam}")
End If
Else
ListBox1.Items.Add("Invalid thread id, lParam was 0")
End If
If m.WParam <> 0 Then
If processId <> 0 Then
ListBox1.Items.Add($"Window Activated, other process is {processId}")
Else
ListBox1.Items.Add("Window Activated, other process is unknown")
End If
Else
If processId <> 0 Then
ListBox1.Items.Add($"Window Deactivated, other process is {processId}")
Else
ListBox1.Items.Add("Window Deactivated, other process is unknown")
End If
End If
End If
MyBase.WndProc(m)
End Sub
End Class
Public Class Win32
Public Const WM_ACTIVATEAPP As Integer = &H1C
Public Const THREAD_QUERY_LIMITED_INFORMATION As Integer = &H800
<DllImport("kernel32.dll", ExactSpelling:=True, SetLastError:=True)>
Public Shared Function GetProcessIdOfThread(handle As IntPtr) As Integer
End Function
<DllImport("kernel32.dll", ExactSpelling:=True, SetLastError:=True)>
Public Shared Function OpenThread(desiredAccess As Integer, inherit As Boolean, threadId As Integer) As IntPtr
End Function
<DllImport("kernel32.dll", ExactSpelling:=True, SetLastError:=True)>
Public Shared Function CloseHandle(handle As IntPtr) As Boolean
End Function
End Class
Sample results when switching between the exampIe winforms application, notepad, calculator and Task Manager. Note the process ids in Task manager and that when switching to Calculator the thread id was from Application Frame Host.