VS application stops in Win7, when Debug and Release binaries are used - vb.net

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

Related

Memory Leak When using InkEdit control

We have a Windows Forms application that uses the Microsoft InkEdit control. This has not been a problem for 15 years. Worked fine with windows 7, 8 and 10. However, a recent windows 10 update has introduced a memory leak when this control is called.
There was a similar question on Stack Overflow:
[https://stackoverflow.com/questions/63645140/memory-leaks-in-inkedit-control]
We have implemented those solutions into our code however we still have the memory leak.
Analysis seems to indicate the the windows DLL mshwLatin.dll is leaking memory.
I am showing a simple example to duplicate the problem. Again, occurs on somewhat latest Windows 10. Have a window call this window and then close it. My sample app starts at 4MB and grows to over 100MB after opening and closing 7-10 times.
Looking for a solution to the memory leak if there is one or how best to report this to Microsoft.
Imports Microsoft.Ink
Public Class Form2
Dim inkfield As New InkEdit
Dim inkfield2 As New InkEdit
Dim inkfield3 As New InkEdit
Dim inkfield4 As New InkEdit
Dim inkfield5 As New InkEdit
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.Controls.Add(inkfield)
Me.Controls.Add(inkfield2)
Me.Controls.Add(inkfield3)
Me.Controls.Add(inkfield4)
Me.Controls.Add(inkfield5)
inkfield.Location = New Point(10, 10)
inkfield2.Location = New Point(10, 40)
inkfield3.Location = New Point(10, 70)
inkfield4.Location = New Point(10, 100)
inkfield5.Location = New Point(10, 150)
inkfield.Height = 24
inkfield2.Height = 24
inkfield3.Height = 24
inkfield4.Height = 24
inkfield5.Height = 24
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Me.Close()
End Sub
End Class
I don't know if this will work or not for you, but you can try it, not in the same situation but it worked for me and I still use it. This reduce the size of memory used by the application. It's easy to create and test.
create a class with name clsMemory and insert this code:
Imports System.Runtime.InteropServices
Public Class clsMemory
<DllImport("KERNEL32.DLL", EntryPoint:="SetProcessWorkingSetSize", SetLastError:=True, CallingConvention:=CallingConvention.StdCall)> _
Friend Shared Function SetProcessWorkingSetSize(ByVal pProcess As IntPtr, ByVal dwMinimumWorkingSetSize As Integer, ByVal dwMaximumWorkingSetSize As Integer) As Boolean
End Function
<DllImport("KERNEL32.DLL", EntryPoint:="GetCurrentProcess", SetLastError:=True, CallingConvention:=CallingConvention.StdCall)>
Friend Shared Function GetCurrentProcess() As IntPtr
End Function
Public Sub New()
Dim pHandle As IntPtr = GetCurrentProcess()
SetProcessWorkingSetSize(pHandle, -1, -1)
End Sub
End Class
Create a button btnCleanMemory on your form and insert this code:
Dim MemClass As New clsMemory()
MemClass = Nothing
It's a long shot but just click on that button when there is a huge amount of memory used by the application to see if it will work for you.

vb.net Register Hotkey without a Form ? Can it be done ? (Using ApplicationContext maybe ?)

I am making a bare bone application and I would like to avoid adding a form class to my project.
Here is what I have done so far
First my new project is a "Empty Project (.NET framework"
Then I added a single empty class file
Then I added the system.windows.forms reference to my project
Lastly I add the following code
Imports System.Windows.Forms
Public Class AppCore
Inherits ApplicationContext
Shared Sub main()
Dim myAppCore As AppCore
myAppCore = New AppCore
System.Windows.Forms.Application.Run(myAppCore)
End Sub
End Class
From that point on, I have a barebone working app that does nothing but stays running forever.
Now I want to registers the keys combo "ALT+F6" and "ALT+F7"
I add the following code
Public Const MOD_CONTROL As Integer = &H11
Public Const MOD_SHIFT As Integer = &H10
Public Const MOD_ALT As Integer = &H1
Public Const WM_HOTKEY As Integer = &H312
Public Declare Function RegisterHotKey Lib "user32.dll" Alias "RegisterHotKey" (ByVal hwnd As IntPtr, ByVal id As Integer, ByVal fsModifiers As Integer, ByVal vk As Integer) As Integer
Public Declare Function UnregisterHotKey Lib "user32.dll" Alias "UnregisterHotKey" (ByVal hwnd As IntPtr, ByVal id As Integer) As Integer
Sub New()
RegisterHotKey(Me.Handle, 100, MOD_ALT, Keys.F6)
RegisterHotKey(Me.Handle, 200, MOD_ALT, Keys.F7)
End Sub
Protected Overrides Sub DefWndProc(ByRef m As System.Windows.Forms.Message)
MyBase.DefWndProc(m)
Dim x As Long
If m.Msg = WM_HOTKEY Then
Select Case CType(m.WParam, Integer)
Case 100
Beep()
Case 200
Beep()
End Select
End If
End Sub
This is where I run into trouble
First in Sub New(), the IDE is telling me that Handle is not part of appcore/applicationcontext
Second, I get the following error
At this point I am stuck !
How can I create hotkeys without adding a form ?
For DefWndProc, maybe I can get away with only removing the "Overrides" keywork and dropping "MyBase.DefWndProc(m)" ?
But I think the bigger problem is how to get a handle ?
Maybe I should look at the CreateWindow API ?
EDIT :
I have modified this project to include a new class of type NativeWindow
as follows
Imports System.Windows.Forms
Public Class clsHotkey
Inherits NativeWindow
Public Const WM_HOTKEY As Integer = &H312
Protected Overloads Sub DefWndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_HOTKEY Then
Select Case CType(m.WParam, Integer)
Case 100
Console.WriteLine("ID 100")
Case 200
Console.WriteLine("ID 200")
End Select
End If
End Sub
End Class
This provides the bare minimum to have a window handle for RegisterHotkey.
I have also modified the AppCore class to work with it.
It compiles and runs without error but the hotkeys do nothing
I believe this is because I have replaced the override keywork in the definition of the DefWndProc subroutine by overloads
If I try to use overrides, I get the following error
BC31086 'Protected Overrides Sub DefWndProc(ByRef m As Message)' cannot override 'Public Overloads Sub DefWndProc(ByRef m As Message)' because it is not declared 'Overridable'.
At this point I am stuck. I would rather not use a form class because of the added complexity (especially the addition of a designer file for it which will make manual compiling more complicated)
Is there a way to make it work with NativeWindow class ? Or some other alternative class that can obtain a window handle ?
Thanks !

Diagnosing AccessViolationException in optimized compiled code

I have a large vb.net x86 project that is running in VS2015 and .Net 4.5.2
When it is compiled and run in debug without optimization then it works ok. However if I compile and run it in Release mode with optimization turned on then I get a variety of exceptions at the same innocuous line of code. I have tried debugging it in release mode but the breakpoints are unreliable. Also the very act of debugging it seems to modify the exception. Also if I change the code (for example putting a MsgBox in to display information) then the problem can go away. For example I changed an Arraylist to a List(Of Control) and the problem no longer occurred where it did before but now moved elsewhere.
I have received all of the following at different times:
AccessViolationException,
NullReferenceException (somewhere deep within .Net classes)
and FatalExecutionEngineError
The exception detail in the AccessViolationException tells nothing except that "this is often an indication that other memory is corrupt". The stack trace is meaningless and there is no description of what it thought was at the invalid memory address.
I also cannot find any meaningful detail about what Optimization in the compiler actually does - one solution might be to turn Optimization off but I don't understand what the benefit / negative effect is of doing this.
Is the Optimization unreliable? How can one possibly try and ascertain what is happening?
The only unmanaged code we use is some calls to get Icons related to file extensions - which are then cloned into managed objects and the unmanaged memory destroyed. This is pretty standard and the same API has been used since 1.1 and through 4.5.2 for 10 years without this occurring before.
I cannot create a small project that reproduces the issue
Here's the code we use for extracting icons as it's the only potential cause I have right now. It was borrowed from elsewhere and I can't really tell whether it's as good as it should be:
Public Class IconExtractor
<Flags()> Private Enum SHGFI
SmallIcon = &H1
LargeIcon = &H0
Icon = &H100
DisplayName = &H200
Typename = &H400
SysIconIndex = &H4000
UseFileAttributes = &H10
End Enum
<StructLayout(LayoutKind.Sequential)>
Private Structure SHFILEINFO
Public hIcon As IntPtr
Public iIcon As Integer
Public dwAttributes As Integer
<MarshalAs(UnmanagedType.LPStr, SizeConst:=260)> Public szDisplayName As String
<MarshalAs(UnmanagedType.LPStr, SizeConst:=80)> Public szTypeName As String
Public Sub New(ByVal B As Boolean)
hIcon = IntPtr.Zero
iIcon = 0
dwAttributes = 0
szDisplayName = vbNullString
szTypeName = vbNullString
End Sub
End Structure
Private Declare Auto Function SHGetFileInfo Lib "shell32" (
ByVal pszPath As String, ByVal dwFileAttributes As Integer,
ByRef psfi As SHFILEINFO, ByVal cbFileInfo As Integer, ByVal uFlags As SHGFI) As Integer
<DllImport("user32.dll", SetLastError:=True)>
Private Shared Function DestroyIcon(ByVal hIcon As IntPtr) As Boolean
End Function
Public Shared Sub GetIconsForFile(ByVal rstrFileName As String, ByRef rzSmallIcon As Icon, ByRef rzLargeIcon As Icon)
Dim zFileInfo As New SHFILEINFO(True)
Dim cbSizeInfo As Integer = Marshal.SizeOf(zFileInfo)
Dim flags As SHGFI = SHGFI.Icon Or SHGFI.UseFileAttributes Or SHGFI.SmallIcon
SHGetFileInfo(rstrFileName, 256, zFileInfo, cbSizeInfo, flags)
' Use clone so we can destroy immediately
rzSmallIcon = DirectCast(Icon.FromHandle(zFileInfo.hIcon).Clone, Icon)
DestroyIcon(zFileInfo.hIcon)
zFileInfo = New SHFILEINFO(True)
cbSizeInfo = Marshal.SizeOf(zFileInfo)
flags = SHGFI.Icon Or SHGFI.UseFileAttributes Or SHGFI.LargeIcon
SHGetFileInfo(rstrFileName, 256, zFileInfo, cbSizeInfo, flags)
' Use clone so we can destroy immediately
rzLargeIcon = DirectCast(Icon.FromHandle(zFileInfo.hIcon).Clone, Icon)
DestroyIcon(zFileInfo.hIcon)
End Sub
End Class
I stumbled across the solution to this by chance.
I was reading this documentation of SHGETFILEINFO
https://msdn.microsoft.com/en-us/library/windows/desktop/bb762179(v=vs.85).aspx
and found that it said in the remarks: You should call this function from a background thread. Failure to do so could cause the UI to stop responding
It isn't clear why you should call it from a background thread nor is it clear what "stop responding" might actually manifest itself as.
However it seemed this was fairly likely what was causing the problem and so I refactored to execute the api call under a separate thread. This has certainly seemed to work. Many of the examples on the internet of SHGETFILEINFO do not seem to consider the separate thread requirement.
I reproduce the whole refactored code here:
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Threading
''' <summary>
''' Retrieves the small and large icons registered for a filename based on the file's extension
''' </summary>
Public Class FileIcons
Private mFileName As String
Private mSmallIconHandle As IntPtr
Private mSmallIcon As Icon
Private mLargeIconHandle As IntPtr
Private mLargeIcon As Icon
Public Sub New(ByVal rFileName As String)
mFileName = rFileName
Dim t As New Thread(AddressOf GetIconsForFile)
t.SetApartmentState(ApartmentState.STA)
t.Start()
t.Join()
' Use clone so we can destroy immediately
mSmallIcon = DirectCast(Icon.FromHandle(mSmallIconHandle).Clone, Icon)
DestroyIcon(mSmallIconHandle)
' Use clone so we can destroy immediately
mLargeIcon = DirectCast(Icon.FromHandle(mLargeIconHandle).Clone, Icon)
DestroyIcon(mLargeIconHandle)
End Sub
Public ReadOnly Property SmallIcon As Icon
Get
Return mSmallIcon
End Get
End Property
Public ReadOnly Property LargeIcon As Icon
Get
Return mLargeIcon
End Get
End Property
Private Sub GetIconsForFile()
' Go and extract the small and large icons
' Full filename must be < MAX_PATH - which is 260 chars in .Net (apparently) though a file path/length of 256 also causes an error.
' Otherwise SHGetFileInfo gets nothing, Icon.FromHandle then gives "System.ArgumentException: The Win32 handle you passed to Icon is invalid or of the wrong type."
' This needs to be stopped in the calling code, or the resulting error trapped
Dim zFileInfo As New SHFILEINFO(True)
Dim cbSizeInfo As Integer = Marshal.SizeOf(zFileInfo)
Dim flags As SHGFI = SHGFI.Icon Or SHGFI.UseFileAttributes Or SHGFI.SmallIcon
SHGetFileInfo(mFileName, 256, zFileInfo, cbSizeInfo, flags)
mSmallIconHandle = zFileInfo.hIcon
zFileInfo = New SHFILEINFO(True)
cbSizeInfo = Marshal.SizeOf(zFileInfo)
flags = SHGFI.Icon Or SHGFI.UseFileAttributes Or SHGFI.LargeIcon
SHGetFileInfo(mFileName, 256, zFileInfo, cbSizeInfo, flags)
mLargeIconHandle = zFileInfo.hIcon
End Sub
#Region "WinAPI"
<Flags()> Private Enum SHGFI
SmallIcon = &H1
LargeIcon = &H0
Icon = &H100
DisplayName = &H200
Typename = &H400
SysIconIndex = &H4000
UseFileAttributes = &H10
End Enum
<StructLayout(LayoutKind.Sequential)>
Private Structure SHFILEINFO
Public hIcon As IntPtr
Public iIcon As Integer
Public dwAttributes As Integer
<MarshalAs(UnmanagedType.LPStr, SizeConst:=260)> Public szDisplayName As String
<MarshalAs(UnmanagedType.LPStr, SizeConst:=80)> Public szTypeName As String
Public Sub New(ByVal B As Boolean)
hIcon = IntPtr.Zero
iIcon = 0
dwAttributes = 0
szDisplayName = vbNullString
szTypeName = vbNullString
End Sub
End Structure
Private Declare Auto Function SHGetFileInfo Lib "shell32" (
ByVal pszPath As String,
ByVal dwFileAttributes As Integer,
ByRef psfi As SHFILEINFO,
ByVal cbFileInfo As Integer,
ByVal uFlags As SHGFI) As Integer
<DllImport("user32.dll", SetLastError:=True)>
Private Shared Function DestroyIcon(ByVal hIcon As IntPtr) As Boolean
End Function
#End Region
End Class

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.