We have a large WinForms app, and there is a built-in bug reporting system that can be activated during testing via the F5 Key. I am capturing the F5 key with .Net's PreFilterMessage system. This works fine on the main forms, modal dialog boxes, etc.
Unfortunately, the program also displays windows messageboxes when it needs to. When there is a bug with that, e.g., wrong text in the messagebox or it shouldn't be there, the messagefilter isn't executed at all when the messagebox is up!
I realize I could fix it by either rewriting my own messagebox routine, or kicking off a separate thread that polls GetAsyncKeyState and calls the error reporter from there. However I was hoping for a method that was less of a hack. Here's code that manifests the problem:
Public Class Form1
Implements IMessageFilter
Private Sub Form1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Click
MsgBox("now, a messagebox is up!")
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Application.AddMessageFilter(Me)
End Sub
Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message) _
As Boolean Implements IMessageFilter.PreFilterMessage
Const VK_F5 As Int32 = &H74
Const WM_KEYDOWN As Integer = &H100
If m.Msg = WM_KEYDOWN And m.WParam.ToInt32 = VK_F5 Then
' In reality code here takes a screenshot, saves the program state, and shows a bug report interface '
IO.File.AppendAllText("c:\bugs.txt", InputBox("Describe the bug:"))
End If
End Function
End Class
Many thanks.
IMessageFilters are a .Net feature and are invoked by the .Net message loop.
Since MessageBox.Show runs the native message loop (inside the MessageBox API call), the IMessageFilters are not called in it.
You need to make a keyboard hook, like this.
Related
My problem is what event will file after i kill the Main Process?
I have no problem when killing the Sub Process. When i kill the Sub frmAdmin Process,
For eg.
This event will be fire,
Private Sub frmAdmin_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If e.CloseReason = CloseReason.TaskManagerClosing Then
'Put you desired Code inside this!
'updateLogoutStatus(Euserid)
'updateLogoutStatus(DecryptAdminUser)
MsgBox("Why are you terminating me from : TaskManager?")
End If
End Sub
But when i kill the Main Process, Which contains the sub process,
Nothing happens. No event fire.
I already searched to internet but they only kill the sub process, not Main process.
Any ideas will be a big help.
The reason why i'm doing this because if the user Kill the main process using Task Manager, I want to trigger a Function.
Here is something I use, similar to your requirement.
Execute the update Query on Form Dispose Event
Private Sub frmAdmin_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
Try
'Update query and set flags to true / false'
Catch ex As Exception
'log exceptions (if any) to a local log file'
Finally
GC.Collect() 'Just release whatever memory your application occupied.'
End Try
End Sub
Firstly I am using VS2013 Winforms .net 4.0.
After excluding all other possibilities (from my set of possibilities) the culprit appears to be Me.close() in one specific form. After the me.close() method executes the coded-ui-test application seems to freeze and does not give any feedback about the buttons i am pressing or text i am entering. When I ask it to generate the code it goes as long as 1 hour before I decide to give up and kill the process. When I try the same test without the me.close it works as expected. Does anyone have any idea how to fix this bug in the automated ui testing? If not can you suggest any alternatives please?
Edit: This does not happen when I simply press the forms 'X' button in the top right. This is very strange.
Edit2: I have tried this in a fresh project. It is indeed me.close that causes the coded ui test application to 'freeze' such that I cannot generate the automated code and I will end up stuck at the 'please wait' loading bar.
Edit3: It appears to be specific to calling me.close in an infragistics click handler of a ultrabutton. Wow, here is example.
Designer
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class closemepls
Inherits System.Windows.Forms.Form
'Form overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Me.UltraButton1 = New Infragistics.Win.Misc.UltraButton()
Me.SuspendLayout()
'
'UltraButton1
'
Me.UltraButton1.Location = New System.Drawing.Point(45, 47)
Me.UltraButton1.Name = "UltraButton1"
Me.UltraButton1.Size = New System.Drawing.Size(232, 157)
Me.UltraButton1.TabIndex = 0
Me.UltraButton1.Text = "UltraButton1"
'
'closemepls
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(284, 262)
Me.Controls.Add(Me.UltraButton1)
Me.Name = "closemepls"
Me.Text = "closemepls"
Me.ResumeLayout(False)
End Sub
Friend WithEvents UltraButton1 As Infragistics.Win.Misc.UltraButton
End Class
Code
Public Class closemepls
Private Sub UltraButton1_Click(sender As Object, e As EventArgs) Handles UltraButton1.Click
Me.Close()
End Sub
End Class
If I call closemepls.showdialog() and click the button the coded ui test application freezes! Infragistics FTW.
This is the result, it does not complete even after 1 hour.
I had problems like that in WPF before.
Here's how I found it: Comment everything out then find the code that causes the problem by adding blocks back one by one.
It's something that is binded (anything though, not data and more than likely a control) not being released.
I haven't tried mixing CodedUI and VB much, but I was hoping this question may help you:
Me.Close does not work
Alone, that doesn't mean much, right? But in conjunction with how CodedUI works it may provide a clue. Remember that when you're running a test you're technically initiating a UITesting.Playback, which is a process. You may want to add something to your TestCleanup method that makes sure all the processes are terminated, like so (Keep in mind that this is for a browser test):
/// <summary>
/// Closes the test browser and ends test playback
/// </summary>
[TestCleanup]//The decorator is what makes this a method a test cleanup
public void CleanTest()
{
if (Playback.IsInitialized) //This is the important part.
{
Playback.Cleanup();
}
if (browserWindow.Exists)
{
browserWindow.Close();
}
}
This is just a shot in the dark and I may even be misunderstanding what you need, really but I'm assuming that in both your real and example areas you're closing the entire application? This may be a question for Infragistics at the end of the day. Good luck!
This might be the same issue I've experienced when using MTM, on a few machines if they were running any form of capture for the resulting test. They where trying to save it to a illegal path(Found in Event Viewer). After performing a repair on VS thus MTM to was repaired and it worked for some machines. Others only seamed to be fixed when going to update 4.
But code wise I would suggest trying to click the close button on the form itself to see if you get a different behavior.
Dim closeButton = New WinButton(YourWindow);
closeButton.SearchProperties(UITestControl.PropertyNames.Name) = "Close";
Mouse.Click(closeButton);
I'm trying to make a simple windows service.
All this service does is change the volume for one device when another device's volume is altered. It currently works as an application, and is only two Subs and a dll to function.
Basically an event handler is created on the Form Load:
AddHandler device.AudioEndpointVolume.OnVolumeNotification, AddressOf volume
Which calls the sub:
Private Sub volume(data As CoreAudioApi.AudioVolumeNotificationData)
If Me.InvokeRequired Then
Me.Invoke(New AudioEndpointVolumeNotificationDelegate(AddressOf volume), data)
Else
loopback.AudioEndpointVolume.MasterVolumeLevelScalar = data.MasterVolume
loopback.AudioEndpointVolume.Mute = data.Muted
End If
End Sub
Now in a windows service I can't use invoke, and when I remove it the error The function evaluation requires all threads to run is thrown.
What I believe is happening is that the control data and loopback (which are the controls of the audio Device accessed via the CoreAudioApi.dll) is not resolving without the invoke, but I don't know how to make them resolve properly.
Determine changes of master audio volume using NAudio.dll from: https://github.com/SjB/NAudio
Imports NAudio.CoreAudioApi
Public Class Form1
Private enumer As MMDeviceEnumerator = New MMDeviceEnumerator()
Private dev As MMDevice = enumer.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Control.CheckForIllegalCrossThreadCalls = False
AddHandler dev.AudioEndpointVolume.OnVolumeNotification, AddressOf AudioEndpointVolume_OnVolumeNotification
End Sub
Private Sub AudioEndpointVolume_OnVolumeNotification(ByVal data As AudioVolumeNotificationData)
' This shows data.MasterVolume, you can do whatever you want here
Me.Text = CInt(data.MasterVolume.ToString() * 100) & " Mute=" & data.Muted
End Sub
End Class
If I remove the if around the invoke statement so it just has the line:
Me.Invoke(New AudioEndpointVolumeNotificationDelegate(AddressOf volume), data)
followed by the other two lines it throws the error:
"'Invoke' is not a member of 'VolumeSet.VolumeSet'"
So I don't think invoke can be used inside a windows service at all.
But thanks for the idea!
In .NET 4.0 I'm dealing with an app which has a long loading time (about 20 seconds), so I wanted to display a swish scrolling marquee on a form that comes on top of the application whilst it is loading.
Since the main UI thread is doing all the loading of data UI elements, I couldn't get that to execute on a separate thread, so I ended up trying to run the form on a new thread. I ended up sticking this code in the form itself, to have it show itself on a new thread, like this:
Public Class frmWait
Public Property Message As String
Get
Return Me.lblMessage.Text
End Get
Set(value As String)
If Not String.IsNullOrEmpty(value) Then
Me.lblMessage.Text = value
Else
Me.lblMessage.Text = DefaultMessage
End If
End Set
End Property
Private OwnerThread As Thread
Private OwnerForm As Form
Private Const DefaultMessage As String = "しばらくお待ちください..."
Public Sub New(ByVal ParentForm As Form)
InitializeComponent()
Me.Message = DefaultMessage
Me.OwnerForm = ParentForm
End Sub
Public Sub New(ByVal Message As String, ByVal ParentForm As Form)
Call InitializeComponent()
Me.Message = Message
Me.OwnerForm = ParentForm
End Sub
Public Sub ShowOnThread()
' Position the form in the center of the owner
With Me.OwnerForm
Dim ownerCenter As New Point(.Location.X + CInt(.Width / 2), .Location.Y + CInt(.Height / 2))
Me.Location = New Point(ownerCenter.X - CInt(Me.Width / 2), ownerCenter.Y - CInt(Me.Height / 2))
End With
Me.OwnerThread = New Thread(New ThreadStart(AddressOf Me.ShowDialog))
Call Me.OwnerThread.Start()
End Sub
Public Shadows Sub Close()
If Me.OwnerThread IsNot Nothing AndAlso Me.OwnerThread.IsAlive Then
Call Me.OwnerThread.Abort()
Else
Call MyBase.Close()
End If
End Sub
End Class
This is probably quite clumsy, but I am showing it in different places in the application, so this seemed the most code-efficient way of doing this...
It actually works quite well, but I am encountering problems from time to time with this and need some help on how to address these issues.
Sometimes when the form gets closed I get an error about the thread being aborted in an unsafe manner.
At the moment I position the form manually in the centre of the form I want it to cover form. Ideally I'd like to be able to call .ShowDialog(ParentForm) on it, but of course that raises an exception because of cross-thread access from one form to the other.
Any suggestions on how to resolve this would be most appreciated.
Because I know virtually nothing about threading I probably coded this like a monkey, so if there is a better method to get this done, I would very much like to know about it.
The code I list is in VB.NET, but answer code in C# is fine too (for any overzealous retaggers)
UPDATE:
I realise now that I should have given a lot more details in my question... The wait form is actually not the first form I am displaying the app. There first is a login screen. When the user is authenticated, the login form launches the main interface of the app, which is the form which actually takes a long time to load.
I am displaying the wait form in between the login form and the main interface. I also use this form to cover for any long running tasks launched on the main interface by the user.
VisualStudio can do this for you. You can certainly write your own, of course, but if you look at your project options under Application there is a setting for Splash Screen. You can assign any form (other than the startup form) to be your splash screen. This runs on its own thread and you can marshall calls to it during startup to advance a progress bar, etc.
MSDN : Splash Screen
Instead of trying to show a dialog on a separate thread you should be moving your loading code to a separate thread / background worker.
Then just start your threads and show the progressbar on form_load and hide the progressbar when the thread completes:
Dim _progressForm As frmLoading
Private Sub frmMain_Load(sender As System.Object, e As System.EventArgs) Handles Me.Load
'start loading on a separate thread
BackgroundWorker1.RunWorkerAsync()
'show a marquee animation while loading
_progressForm = New frmLoading
_progressForm.ShowDialog(Me)
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'simulate a long load
For i As Integer = 1 To 10
System.Threading.Thread.Sleep(1000)
Next
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
_progressForm.Close()
End Sub
Have a look at TaskFactory.New
Run your threaded code of a different thread to keep the UI responsive
http://msdn.microsoft.com/en-us/library/ee782519.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-3
You could use the ApplicationContext class to allow the splash screen to be run first and then swap in the Main form when it is ready. You use it in place of your main form in the call to Application.Run:
Application.Run(new MyApplicationCtx())
Just googled up a article for you as well:
http://www.codeproject.com/Articles/5756/Use-the-ApplicationContext-Class-to-Fully-Encapsul
I am using a WebBrowser control in VB.NET and calling the Print() method. I am printing out using a PDF printer and when Print() is called it is not immediately kicked off (it waits until it has completed running code for the entire Sub or block.
I need to make sure the file I am printing too is complete and continue process with this file, therefore, I would like to print on demand and get some status of when the operation is complete. I have tried usign printDocument and process without luck.
Anyone have any ideas?
Check out the PrintTemplateTeardown event of the underlying unmanaged WebBrowser object. Sometimes that event gets fired multiple times but hopefully this will point you in the right direction. You need to add a reference to Microsoft Internet Controls.
Private Sub Print()
AddHandler DirectCast(WebBrowser1.ActiveXInstance, SHDocVw.WebBrowser).PrintTemplateTeardown, AddressOf PrintDone
WebBrowser1.Print()
End Sub
Private Sub PrintDone(ByVal obj As Object)
Trace.WriteLine("printed")
RemoveHandler DirectCast(WebBrowser1.ActiveXInstance, SHDocVw.WebBrowser).PrintTemplateTeardown, AddressOf PrintDone
End Sub
Your best bet is to get a handle on your 'printjobscollection' for your default printer and ensure that the jobcount = 0
like this in vb.net:
Dim intprint As Integer = Nothing
retry2:
intprint = GetPrintJobsCollection(printerinuse)
If Not intprint = 0 Then
System.Threading.Thread.Sleep(1000)
GoTo retry2
End If
'do what you want to do after print completes here