I currently have the following code that runs an external Virtual Application(created with Cameyo), when the Run Button is clicked. I also have a Timer that checks ever second to see if the Process(virtual exe program) is still open. In theory the GetProcessByName should find the program listed in the Task Manager right? However it doesn't! I even tried using GetProcessByName to Kill the process (one another button clicked), but the processs is not killed.
Could it be because I virtualized the program that I want GetProcessByName to recognize? Therefore the name of the Task in the Task Manager is not correct?.
Example
The program started: SmartDefrag.virtual.exe
It runs
The Task Manager shows it as SmartDefrag.exe
Use GetProcessByName("SmartDefrag.exe") to Disable Run Button if process SmartDefrag.exe running.
Does not Disable Run Button.
Could I use TITLE OF PROCESS? Or will the PID be the same everytime the process opens? Any other options?
Code:
Private Sub SMDFRunAppMainButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles SMDFRunAppMainButton.Click
' LoadingSMDFMainButton.Visibility = Windows.Visibility.Visible
Dim downloadlocation As String = (currentpath & "\1stAidApps\SMDF\SmartDefrag.virtual.exe")
My.Settings.FileLoad = downloadlocation
Try 'Errors on Cancel
dp1Timer = New DispatcherTimer
dp1Timer.Interval = TimeSpan.FromMilliseconds(1000)
AddHandler dp1Timer.Tick, AddressOf TickMe1
dp1Timer.Start()
fileload = My.Settings.FileLoad
Process.Start(fileload)
Catch ex As Exception
MessageBox.Show("Failed to launch. Please try again.", "Launch Failed")
End Try
End Sub
Private Sub TickMe1()
Dim p() As Process
p = Process.GetProcessesByName("SmartDefrag.exe")
If p.Count > 0 Then
LoadingSMDFMainButton.Visibility = Windows.Visibility.Hidden
SMDFRunAppMainButton.IsEnabled = False
Else
SMDFRunAppMainButton.IsEnabled = True
End If
End Sub
GetProcessByName doesn't take the full path, but rather the "name" of the process. This will likely need to be GetProcessByName("SmartDefrag").
From the documentation for GetProcessByName:
The process name is a friendly name for the process, such as Outlook, that does not include the .exe extension or the path.
Related
Specifically, I'm using WPF with MVVM. I have a MainWindow, which is a WPF Window where all of the action happens. It uses a corresponding View Model class for its properties, commands, etc.
I have set up main UI thread and non-UI thread exception handlers in Application.xaml.vb StartUp like this:
Private Sub Application_DispatcherUnhandledException(sender As Object, e As Windows.Threading.DispatcherUnhandledExceptionEventArgs) Handles Me.DispatcherUnhandledException
' catches main UI thread exceptions only
ShowDebugOutput(e.Exception)
e.Handled = True
End Sub
Private Sub Application_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup
' catches background exceptions
Dim currentDomain As AppDomain = AppDomain.CurrentDomain
AddHandler currentDomain.UnhandledException, AddressOf UnhandledExceptionHandler
AddHandler System.Threading.Tasks.TaskScheduler.UnobservedTaskException, AddressOf BackgroundTaskExceptionHandler
End Sub
Sub UnhandledExceptionHandler(sender As Object, args As UnhandledExceptionEventArgs)
Dim ex As Exception = DirectCast(args.ExceptionObject, Exception)
ShowDebugOutput(ex)
End Sub
Sub BackgroundTaskExceptionHandler(sender As Object, args As System.Threading.Tasks.UnobservedTaskExceptionEventArgs)
Dim ex As Exception = DirectCast(args.Exception, Exception)
ShowDebugOutput(ex)
End Sub
This part works
When I try to test this out, by deliberately throwing an exception, it works. It is actually in the View Model in the Sub that handles the Select All button click.
The button:
<Button Content="Select All" Height="23" Width="110" Command="{Binding SelectAllCommand}" />
The Command where I'm throwing the exception that is successfully caught:
Private Sub SelectAll()
Throw (New Exception("UI Thread exception"))
SetAllApplyFlags(True)
End Sub
This part doesn't work
There's another button in the same MainWindow similarly bound to a command. However, it uses a Task to perform its work in the background, and an exception thrown in there does NOT get caught by my catch-all handlers.
Private Sub GeneratePreview()
' run in background
System.Threading.Tasks.Task.Factory.StartNew(
Sub()
' ... stuff snipped out, issue is the same with or without the rest of the code here ...
Throw (New Exception("Throwing a background thread exception"))
End Sub)
End Sub
There are several similar questions, but I haven't been able to actually figure out my answer from them. The AppDomain UnhandledException seems to be the answer in most cases, but it isn't for mine. What exactly do I have to add to be able to catch an exception that might be thrown in a non-UI thread this way?
What I ended up doing
I could not get the TaskScheduler.UnobservedTaskException event to call my event handler when I was handling it in Application.xaml.vb. But I took hints from the other answer, and I'll mark it as the answer because it ultimately helped.
However, it is not at the application level, so if this was a larger application, I'd have to duplicate this in every instance where I used a Task. This wasn't really what I was looking for, but not willing to spend more time on it now.
I ended up putting a try-catch inside the Task. In the catch, I was able to use Dispatcher.Invoke to still display a user-friendly dialog with the exception info.
Private Sub GeneratePreview()
' run in background
System.Threading.Tasks.Task.Factory.StartNew(
Sub()
Try
' ... stuff snipped out, issue is the same with or without the rest of the code here ...
Throw (New Exception("Throwing a background thread exception"))
Catch ex As Exception
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, DirectCast(
Sub()
HRNetToADImport.Application.ShowDebugOutput(ex)
End Sub, Action))
End Try
End Sub)
End Sub
TaskScheduler.UnobservedTaskException Event is what you want to subscribe from App start.
https://msdn.microsoft.com/en-us/library/vstudio/system.threading.tasks.taskscheduler.unobservedtaskexception(v=vs.100).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
Occurs when a faulted Task's unobserved exception is about to trigger exception escalation policy, which, by default, would terminate the process.
This AppDomain-wide event provides a mechanism to prevent exception escalation policy (which, by default, terminates the process) from triggering.
NOTE: The event might not be fired right away (possible a few second delay). You could imagine there's some operations of call stack bubbling and context switching of normal exception operation before ended up reaching the UnobservedTaskException event.
One thing I want to point out is that, it's a must to wrap your whole application with generic exception handler to prevent application being terminate. But, please do remember that it's also a must to implement proper exception handling to all paths that might throw exception.
Sandra,
I read cscmh99 proposition,
took your code,and try to run, and it works !
I mean you can subscribe to TaskScheduler.UnobservedTaskException.
Then you will catch UnobservedException
But you won't catch observed exceptions
Observed exceptions are those from Tasks waited with .Wait() or .Result
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
' Here follows an Unobserved Exception
System.Threading.Tasks.Task.Factory.StartNew(
Sub()
Throw (New Exception("Throwing a background thread exception"))
End Sub)
' Here follows an ObservedException
' ObservedException because there is call to .Wait() (or .Result)
' You can't use UnobservedException for that one
Dim t As Task = System.Threading.Tasks.Task.Factory.StartNew(
Sub()
Throw (New Exception("Throwing a background thread exception"))
End Sub)
t.Wait()
End Sub
Here is code to working solution : http://1drv.ms/1XOvTbK
Regards
Sandra,
There is a way to catch the exception from inside your background Task.
I admit my solution is not global, but at least it catches and prevents crash !
Private Async Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim t As Task = Task.Factory.StartNew(
Sub()
' ... stuff snipped out, issue is the same with or without the rest of the code here ...
Throw (New Exception("Throwing a background thread exception"))
End Sub)
Try
Await t
Catch ex1 As Exception
Debug.WriteLine("Exception from inside task " & ex1.Message)
End Try
End Sub
Think it could help, may be you, may be others.
I have developed winform application using VB.NET. The application is deployed in the machine which is connected to wireless network. The machine is in the car(moving object).
The application has DataGridView loaded with the data get from MSSQL Server(in Remote machine). The data is refreshed for every 5 seconds.
I have used the NetworkAvailabilityChanged event to detect the network status. If the Network is available, then I retrieve the data from the table.
Code:
AddHandler NetworkChange.NetworkAvailabilityChanged, AddressOf NetworkStateChangeHandler
Public Sub NetworkStateChangeHandler(ByVal sender As Object,
ByVal e As NetworkAvailabilityEventArgs)
If e.IsAvailable = True Then
g_bNetworkAlive = True
Else
g_bNetworkAlive = False
End If
End Sub
private Sub GetData()
If g_bNetworkAlive = True
'code to get the data from table
End If
End Sub
Issue:
If the car movers out of the out of the network, the NetworkAvailabilityChanged event is not fired. so it throws the following error for every 5 seconds and application gets crashed.
A network-related or instance-specific error occurred while establishing
a connection to SQL Server. The server was not found or was not accessible.
Temporary fix: I have made Ping request to the SQL server machine for every 5 seconds to detect the network status. It affects the application's performance.
Note: If I manually switch off the Wifi, the NetworkAvailabilityChanged event is fired. The issue is only when the car moves out of the network.
Is there any some other feasible solution to detect the wireless network status?
Indeed there is.
Why send http request to some dummy website when you can check if you are connected to wifi or not locally.
Use ManagedWifi APIs, This will eliminate the slowdown you are facing.
//Create object at the beginning of your application
WlanClient wlanClient = new WlanClient();
//You this code to check wifi availability wherever you need
foreach (WlanInterface _interface in wlanClient.Interfaces)
{
If (_interface.CurrentConnection.wlanAssociationAttributes.dot11Ssid.SSID!=null)
{
// You are connected to wifi
}
}
EDIT: In case you you are not finding dll, direct link. Just reference the Dll and Voila! you are done.
Change your code as
Private Sub GetData()
If My.Computer.Network.IsAvailable AndAlso g_bNetworkAlive
' code to get the data from table
End If
End Sub
You also can check if you have an internet connection by using WebRequest like this:
Private Sub GetData()
If HasInternet AndAlso g_bNetworkAlive Then
'code to get the data from table
End If
End Sub
Public Shared Function HasInternet As Boolean
Return Not (HttpGet("http://www.google.com/") = "Error")
End Function
Public Shared Function HttpGet(url As String) As String
Dim request As WebRequest = WebRequest.Create(url)
request.Method = "GET"
Try
Dim response As WebResponse = request.GetResponse()
Dim dataStream As Stream = response.GetResponseStream()
Dim reader As New StreamReader(dataStream)
Dim responseFromServer As String = reader.ReadToEnd()
reader.Close()
dataStream.Close()
response.Close()
Return responseFromServer
Catch ex As Exception
Return "Error"
End Try
End Function
And you can check the sql connection by using the following function:
Private Function IsDatabaseConnected() As Boolean
Try
Using sqlConn As New SqlConnection("YourConnectionString")
sqlConn.Open()
Return (sqlConn.State = ConnectionState.Open)
End Using
Catch e1 As SqlException
Return False
Catch e2 As Exception
Return False
End Try
End Function
I would simply use try..catch, so if you get exception, you can know, based on its id, why the code failed, and decide what to do next, and finally you can execute some more code regardless of data being retrieved or not
Private Sub GetData()
Try
'code to get the data from table
Catch ex As Exception
' Show the exception's message.
MessageBox.Show(ex.Message)
' Show the stack trace, which is a list of methods
' that are currently executing.
MessageBox.Show("Stack Trace: " & vbCrLf & ex.StackTrace)
Finally
' This line executes whether or not the exception occurs.
MessageBox.Show("in Finally block")
End Try
End Sub
I didn't think this actually happened, but it appears that my WPF app (window) is not calling a Sub asynchronously, or something that looks like this... The use case is deceivingly simple:
wpfw = WPF Window
StatusLeft = textbox on wpfw
cmds = Command class instantiated in wpfw class to store all window commands (subs). the
wpfw instance is passed to it in the constructor.
UpdateStatusLeft = a Sub in cmds that looks like this:
Private Sub UpdateStatusLeft(UpdateText As String)
Try
Me.wpfw.Dispatcher.Invoke(CType(Sub() Me.wpfw.StatusLeft.Text = UpdateText, Action))
Catch ex As Exception
[...]
End Try
End Sub
The above works, but only updates the main window after the long running sub in cmds is finished. Calling that long running command is nothing special:
(XAML)
<MenuItem Header="Process Files" Click="mnuReference_ProcessFiles"/>
in wpfw, the handler for the click (mnuReference_ProcessFiles) is this:
Private Sub mnuReference_ProcessFiles(sender As Object, e As RoutedEventArgs)
Me.cmds.ParseFiles()
End Sub
Now cmds is instantiated as soon as the wpfw is, and the sub it is pointing to (mnuReference_ProcessFiles) looks like this:
Public Sub ParseFiles() Implements Icmds.ParseFiles
Try
Dim fu As FileUtils = New FileUtils()
Me.UpdateStatusLeft("Starting Batch 1...")
ipu.ParseFile(".\batch1")
Me.UpdateStatusLeft("Starting Batch 2...")
ipu.ParseFile(".\batch2")
[...]
Above, "Me.UpdateStatusLeft" is in the cmds class. In fact, I put a Sub UpdateStatusLeft in every class the mainwindow calls (including it's own class!), and I pass down the wpfc instance to each command/processing class.
If you try to update the textarea directly frm another thread/class other than the wpfw one, you get a thread error-which is why I use Dispatcher.Invoke(...
Clarity:
The commands are all firing off as expected, and do their job well. That never was an issue.
The issue is trying to find a way to update the top/originating UI thread's textarea as those tasks progress so the user doesn't fall asleep or think the program has crashed, etc.
The main application dispatcher uses the UI thread, so long-running processes will still lock up the UI thread
The typical solution is to run long-running processes in a background thread, then use the dispatcher to update your UI objects on the main thread once it's finished.
Or if you use something like the Task Parallel Library, it allows you to schedule your task for UI thread, and bypass the dispatcher completely. Here's an example:
Task.Factory
.StartNew(() =>
{
// This runs in a background thread
return GetLongRunningProcessResults();
})
.ContinueWith.StartNew((t) =>
{
// This runs in the UI thread because of the
// TaskScheduler.FromCurrentSynchronizationContext parameter
UpdateUI(t.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
Wrap the code in ParseFiles in a call to Tasks.Run.
If you want your application to stay responsive, you must execute intensive tasks in another thread than the main thread.
I'm using a serial port with a Serial Object and sometimes I get this error.
UnauthorizedAccessException: Access to the port is denied.
with a stack trace of:
at System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str)
at System.IO.Ports.InternalResources.WinIOError()
at System.IO.Ports.SerialStream.Dispose(Boolean disposing)
at System.IO.Ports.SerialStream.Finalize()
It doesn't occur at any line of code (within my code at least) so I'm not sure how to trap it. I figure what is happening is the serial (via USB) port is being physically unplugged/disconnected for a split second and is throwing everything into whack.
I can click Continue on the error which I'm debugging and everything is fine. Communication with the serial device is flawless otherwise. But when the program is actually published, deployed and running it gives me several error messages and is all ugly for the user.
How can I trap this error/what can I do to prevent it in the first place?
Thanks
I encounter the same exception and stack trace in my WinForms application when used with a USB-to-serial converter. I can consistently recreate it by
creating an instance of SerialPort
calling SerialPort.Open,
removing the USB-to-serial converter,
closing the app (which calls SerialPort.Dispose)
My suspicion is that the exception is being thrown in SerialPort's finalizer.
Others have experienced the same symptoms - see here.
To work around I followed the recommendation of Kuno and KyferEz (from link above) to implement my own ExtSerialPort. This inherits SerialPort, overrides the Dispose method and (using reflection) disposes the SerialPort's internalSerialStream.
Imports System.IO.Ports
Public Class ExtSerialPort
Inherits SerialPort
Public Sub New()
MyBase.new()
End Sub
Public Sub New(ByVal portName As String)
MyBase.New(portName)
End Sub
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Dim mytype As Type = GetType(SerialPort)
Dim field As Reflection.FieldInfo = mytype.GetField("internalSerialStream", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)
Dim stream As Object = field.GetValue(Me)
If stream IsNot Nothing Then
Try
stream.Dispose()
Catch ex As Exception
End Try
End If
MyBase.Dispose(disposing)
End Sub
End Class
My goal is to have this program send a logout command when the user is logging off or shutting down their pc.
The program is connected to a server application via a tcp socket using a Winsock object. Calling singleSock.SendData "quit" & vbCrLf is simply a way of logging out. I am going to start capturing data with Wireshark, but I'd like to know if I'm trying to do something fundamentally wrong.
Oddly enough, if I set Cancel to True, and allow a timer I have running to do the logout command, then call another unload, it works, however in testing this configuration (different code), this prevents the user from logging out the first time. They have to initiate a logout, it doesn't do anything, then they logout again and my program is gone at that point. Also oddly enough, in Vista the logout goes through after briefly displaying a screen saying my program was preventing the logout. Most of my deployment is on XP, which has the two logouts problem.
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
If UnloadMode = vbFormControlMenu Then
Me.WindowState = vbMinimized
Cancel = True
Else
If SHUTDOWN_FLAG = True Then
Cancel = False
Else
Cancel = True
SHUTDOWN_FLAG = True
End If
tmrSocket.Enabled = False
SHUTDOWN_FLAG = True
Sleep (1000)
singleSock.SendData "quit" & vbCrLf
Call pUnSubClass
'If singleSock.state <> sckConnected Then
' singleSock.Close
' tmrSocket.Enabled = False
' LogThis "tmrSocket turned off"
'End If
DoEvents
End If
End Sub
You're not waiting for the Winsock control to actually send the "quit" message. The SendData method is asynchronous: it can return before the data has actually been sent across the network. The data is buffered locally on your machine and sent at some later time by the network driver.
In your case, you are trying to send the "quit" message and then closing the socket almost immediately afterwards. Because SendData is asynchronous, the call might return before the "quit" message has actually been sent to the server, and therefore the code might close the socket before it has a chance to send the message.
It works when you cancel the unloading of the form first and let the timer send the "quit" message because you're giving the socket enough extra time to send the message to the server before the socket is closed. However, I wouldn't count on this always working; it's a coincidence that the extra steps gave the socket enough time to send the message, and it's not guaranteed to always work out that way.
You can fix the problem by waiting for the socket to raise a SendCompleted event after you send the "quit" message and before you close the socket. Below is a basic example. Note that the QueryUnload code is much simpler.
Private m_bSendCompleted As Boolean
Private m_bSocketError As Boolean
Private Sub singleSock_Error(ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)
'Set error flag so we know if a SendData call failed because of an error'
'A more robust event handler could also store the error information so that'
'it can be properly logged elsewhere'
m_bSocketError = True
End Sub
Private Sub singleSock_SendCompleted()
'Set send completed flag so we know when all our data has been sent to the server'
m_bSendCompleted = True
End Sub
'Helper routine. Use this to send data to the server'
'when you need to make sure that the client sends all the data.'
'It will wait until all the data is sent, or until an error'
'occurs (timeout, connection reset, etc.).'
Private Sub SendMessageAndWait(ByVal sMessage As String)
m_bSendCompleted = False
singleSock.SendData sMessage
singleSock.SendData sMessage
Do Until m_bSendCompleted or m_bSocketError
DoEvents
Loop
If m_bSocketError Then
Err.Raise vbObjectError+1024,,"Socket error. Message may not have been sent."
End If
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
'This is (almost) all the code needed to properly send the quit message'
'and ensure that it is sent before the socket is closed. The only thing'
'missing is some error-handling (because SendMessageAndWait could raise an error).'
If UnloadMode = vbFormControlMenu Then
Me.WindowState = vbMinimized
Cancel = True
Else
SendMessageAndWait "quit" & vbCrLf
singleSock.Close
End If
End Sub
You can make the code cleaner by putting the logic to send a message and wait for it to be sent in a separate class. This keeps the private variables and the event handlers in one place, instead of having them litter your main code. It also makes it easier to re-use the code when you have multiple sockets. I called the class SynchronousMessageSender for lack of a better name. This example also has more complete error handling:
SynchronousMessageSender.cls
Private WithEvents m_Socket As Winsock
Private m_bAttached As Boolean
Private m_bSendCompleted As Boolean
Private m_bSocketError As Boolean
Private Type SocketError
Number As Integer
Description As String
Source As String
HelpFile As String
HelpContext As Long
End Type
Private m_LastSocketError As SocketError
'Call this method first to attach the SynchronousMessageSender to a socket'
Public Sub AttachSocket(ByVal socket As Winsock)
If m_bAttached Then
Err.Raise 5,,"A socket is already associated with this SynchronousMessageSender instance."
End If
If socket Is Nothing Then
Err.Raise 5,,"Argument error. 'socket' cannot be Nothing."
End If
Set m_Socket = socket
End Sub
Private Sub socket_SendCompleted()
m_bSendCompleted = True
End Sub
Private Sub socket_Error(ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)
m_bSocketError = True
'Store error information for later use'
'Another option would be to create an Error event for this class'
'and re-raise it here.'
With m_lastSocketError
.Number = Number
.Description = Description
.Source = Source
.HelpFile = HelpFile
.HelpContext = HelpContext
End With
End Sub
'Sends the text in sMessage and does not return'
'until the data is sent or a socket error occurs.'
'If a socket error occurs, this routine will re-raise'
'the error back to the caller.'
Public Sub SendMessage(ByVal sMessage As String)
If Not m_bAttached Then
Err.Raise 5,,"No socket is associated with this SynchronousMessageSender. Call Attach method first."
End If
m_bSendCompleted = False
m_bSocketError = False
m_socket.SendData sMessage & vbCrLf
'Wait until the message is sent or an error occurs'
Do Until m_bSendCompleted Or m_bSocketError
DoEvents
Loop
If m_bSocketError Then
RaiseLastSocketError
End If
End Sub
Private Sub RaiseLastSocketError()
Err.Raise m_lastSocketError.Number, _
m_lastSocketError.Source, _
m_lastSocketError.Description, _
m_lastSocketError.HelpFile, _
m_lastSocketError.HelpContext
End Sub
Example Use
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
Dim sender As New SynchronousMessageSender
'Ignore errors since the application is closing...'
On Error Resume Next
If UnloadMode = vbFormControlMenu Then
Me.WindowState = vbMinimized
Cancel = True
Else
Set sender = New SynchronousMessageSender
sender.AttachSocket singleSock
sender.SendMessage "quit"
singleSock.Close
End If
End Sub
By using a separate class, now all the necessary code can be placed in the Form_QueryUnload, which keeps things tidier.
Wouldn't is be easier to just go without the QUIT command. In your server code just assume that the closing of a socket does the same thing as receiving a quit.
I addition, one thing you want to watch out for is abrupt shut downs of the client software. For example, a machine that losses power or network connection or a machine that goes into sleep or hibernate mode.
In those cases you should be periodically checking the connection for all of the clients from the server and closing any connections that do not respond to some kind of ping command.