Can anyone explain why the following code works on some Win7 PC's but on some I get a MissingMethodException and the Timer.Elapsed event is never called.
Private Sub _timer2_Elapsed(ByVal sender As Object, ByVal e As System.EventArgs) Handles _timer2.Elapsed
_timer2.Enabled = False
Dispatcher.Invoke(Sub()
HandleSingleKeyPress(-1)
End Sub)
End Sub
After some investigation I have found that the following code works much better:-
Public Delegate Sub InvokedSubDelegate()
Private Sub _timer2_Elapsed(ByVal sender As Object, ByVal e As System.EventArgs) Handles _timer2.Elapsed
_timer2.Enabled = False
Dispatcher.Invoke(New InvokedSubDelegate(Sub()
HandleSingleKeyPress(-1)
End Sub))
End Sub
Not sure why the first approach works only sometimes but hope the solution can help someone else with similar problems.
Jerry
It doesn't sound like you are close to identifying the true problem. There certainly is more than one in that snippet.
A MissingMethodException is a DLL Hell problem. In other words, you are running your code with an old version of the assembly, one that doesn't yet have the method you are trying to call. You avoid DLL Hell by paying lots of attention when you deploy the assemblies. And by religiously incrementing the [AssemblyVersion]. In the VB.NET IDE that's done with Project + Properties, Application tab, Assembly Information button. This does explain why the 2nd snippet doesn't seem to have this problem, you are just less likely to be running with that old version of the assembly.
This does end up rather poorly when you use the System.Timers.Timer class. It is a nasty class. In a highly uncharacteristic lapse of judgement, Microsoft decided to swallow all exceptions that are raised in the Elapsed event handler. Which explains why the timer appears to be stop working, it just won't do what you asked it to do when the exception aborts the code. Favor the System.Threading.Timer class, it doesn't swallow exceptions. Or always using try/catch inside the Elapsed handler, albeit that it is pretty hard to figure out what to do when you catch. Environment.Exit() is wise.
But most of all, you are just using the entirely wrong timer. There's no point in using an asynchronous one when you make it synchronous again by using Dispatcher.Begin/Invoke(). Simply use a DispatcherTimer instead. Gets you the exact same outcome, minus the nastiness and overhead. And the need to ask this question.
Related
I've previously used the Task Parallel Library to keep the UI thread responsive, but I'm trying to switch to async/await to simplify the code. The project I'm working on is in WinForms. There is a presentation layer that communicates with view interfaces, and an assembly that contains winforms forms that implement these interfaces, with dependency injection binding everything together. In the specific example below, the form has a menu/button/etc that sends a request to the presentation layer for new data. I've ignored exception handling to keep the example simple.
In the form code we have an event handler like this:
private async sub DataRequest(sender as object, e as EventArgs) handles SomeButton.click
Dim etask = New Threading.Tasks.Task(Sub() RaiseEvent DataRefreshRequest(Me, EventArgs.Empty))
etask.Start()
Await etask
end sub
Not sure if there's a neater way of awaiting an event being raised, the above seems a bit clumsy
The presentation layer responds to this event with code like this:
Private Async Sub HandleDataRequest() Handles _view.DataRefreshRequest
Dim gdtask = New Tasks.Task(Of IEnumerable(Of Summary))(Function() GetDataTask())
gdtask.Start()
_view.Data = Await gdtask
End Sub
We are stuck with .Net 4 on this project so we are using Microsoft.BCL.Async. GetDataTask is effectively a Linq query on entities exposed by the business layer, but without .Net 4.5 we can't execute it using .ToListAsync
The bit I'm a little unsure of is the way I'm raising the event in the winforms assembly. Is that the right way to do it? It seems to work okay, but I'm concerned about any unforeseen problems of doing it this way. Better to do it right from the start then find there's a problem once this pattern is in common use.
Using Task.Run is better than the Task constructor with Task.Start.
Other than that, I'd just recommend some comments. As you noted, the ideal solution would be to use ToListAsync and make it truly asynchronous, but that's not possible given your platform.
Task.Run is normally used for running CPU-bound code on a background thread, and this code is not CPU-bound. So I'd recommend a comment for your future self so that when you do upgrade to .NET 4.5, you can know those Task.Run calls are no longer necessary.
I am very new to SQL Dependencies and found this is a great way to handle data updating within my application. Previously I was constantly querying my Database every minute or so to check for changes, whereas now I receive update notifications on change events as they occur.
When my application first launches, it starts the SQL Dependency to the database with: SqlDependency.Start(ConnectionString) and in turn Stops it once I exit.
I then setup about 3 or 4 dependency queries to receive updates for different data within my application - all works great and exactly how I need.
However, my issue is this:
I have a second form of which I call multiple instances of. This form also has about 3 different dependencies attached to update different data again. However, when I close the instance of the form, I am finding that the dependency continues to run and thus throws an error in my application due to the form now being closed.
My question is: Is there a way I can stop the individual SQL Dependencies once I close the form? The dependencies on the Main form should continue to run, but every time I open and close an instance of the second form, I need to stop and start those queries from running.
Is this possible?
I can provide sample code snippets if required.
Any help appreciated. Thankyou
There is a Stop method in the SqlDependency class that you can invoke when you are done listening. Is there some issue with this?
ADDED
According to the docs, "Stops a listener for a connection specified in a previous Start call."
So, thhough I have not ever done exactly this, it should work if you are using a different connections in the main and child forms.
Okay, I ended up resolving this issue myself after a little bit of thought and researching.
Previously, I was declaring the SQLDependency within the same Sub Routine as the Handler like so:
Private Sub RefreshRegisterData()
...
Dim Register_dep As SqlDependency
Register_dep = New SqlDependency(cmd)
AddHandler Register_dep.OnChange, AddressOf Registers_OnChange
...
End Sub
However, to resolve my issue I basically moved the scope of the SQLDependency outside of the Sub Routine and declared it globally within the Class:
Public Class frmStorePage
Private Register_dep As SqlDependency
...
Private Sub RefreshRegisterData()
...
Register_dep = New SqlDependency(cmd)
AddHandler Register_dep.OnChange, AddressOf Registers_OnChange
...
End Sub
End Class
Then, when exiting my form, I simply implemented a RemoveHandler call to stop the message queue and destroy the existing handler:
Private Sub frmStorePage_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
RemoveHandler Register_dep.OnChange, AddressOf Registers_OnChange
...
End Sub
Now, not sure if this is the preferred method of solving this issue, however, I am yet to witness any implications by this code and it solves my problem.
We are trying to setup a SysTray application which can be activated from elsewhere. To be more specific the activation will come from a third party app which we cannot modify but allows us to activate our own app via its path (plus a parameter/argument).
When it gets activated we want to put up a BalloonText, there are to be no forms involved.
Therefore we have two problems to solve:
Make our SysTray application single instance (since it's no good generating multiple instances).
Allow this other app to activate our application with arguments
Lots of help out there to help learners create simple SysTray applications (and indeed we've done it ourselves as part of a solution to an unconnected project).
However we've never tried to make it single instance before.
Lots of help out there to help learners create single instance Winforms applications (again we've done this as part of other projects) but always simple applications with conventional forms (not SysTray). We use the VisualBasic WindowsFormsApplicationBase method.
Can't seem to combine these two approaches into a single solution.
Update:
Hans answer below nails it (and especially his comment):
This is already taken care of with a NotifyIcon, drop it on the form.
And the "Make single instance application" checkbox. And the
StartupNextInstance event. You'll need to stop assuming there's
anything special about this
As far as your first question about checking for other instances this seems to work. I used the CodeProject example as a baseline. In your Sub Main routine you can check for other instances by using the GetProcessesByName Method of the Process class. Something like this:
Public Sub Main()
'Turn visual styles back on
Application.EnableVisualStyles()
'Run the application using AppContext
Dim p() As Process
p = Process.GetProcessesByName("TrayApp") 'Your application name here
If UBound(p) >= 0 Then
End
End If
Application.Run(New AppContext)
End Sub
For the second question if your SysTray application is already running you might want to give this article on .Net Interprocess Communication a try. Otherwise parse the CommandLine arguments in yourSub Main as it is created.
From above article:
The XDMessaging library provides an easy-to-use, zero-configuration solution to same-box cross-AppDomain communications. It provides a simple API for sending and receiving targeted string messages across application boundaries. The library allows the use of user-defined pseudo 'channels' through which messages may be sent and received. Any application can send a message to any channel, but it must register as a listener with the channel in order to receive. In this way, developers can quickly and programmatically devise how best their applications can communicate with each other and work in harmony.
Everything becomes trivial when you actually do use a form. It is simple to put your app together with the designer, simple to get your app to terminate, simple to avoid a ghost icon in the tray, simple to create a context menu, simple to add popups if you ever need them.
The only un-simple thing is getting the form to not display. Paste this code in the form's class:
Protected Overrides Sub SetVisibleCore(ByVal value As Boolean)
If Not Me.IsHandleCreated Then
Me.CreateHandle()
value = False
End If
MyBase.SetVisibleCore(value)
End Sub
The "Exit" command in the context menu is now simply:
Private Sub ExitToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExitToolStripMenuItem.Click
Me.Close()
End Sub
I have an application that interacts with third party COM objects. A method of the COM object that I call will sometimes stop responding. I created a Backgroundworker thread to call the function and I am trying to monitor it from my main thread to kill it if it hangs. I have additional code (not supplied) that tracks the processing time using system.timer from the main thread and I raise an event if it exceeds my threshold (this part is fine). This is when I want to kill the thread and stop code execution. The problem is.. if I use the cancelasync method it will just pend since the code execution is stuck on the function call.
The particular function call in the code snippet that hangs is "objCOM.SendDataToServer()". It typically takes 1-3 seconds to return, but if it gets no response it will just hang indefinitely and won't return at all (no errors just hangs).. there is no timeout... and since I don't have access to the source function I cannot supply one. I tried the .dispose() method of the thread, but apparently that doesn't kill it and neither does cancelasync. I just need help figuring out how to KILL this thread so I can reset the server connection and call the function again. Any help is MUCH appreciated!
Public Class COMobject
Private objCOM as new acs.manager
Public Sub CallComFunction()
Dim bw_com as New Backgroundworker
AddHandler bw_com.DoWork, AddressOf bw_com_dowork
AddHandler bw_com.RunWorkerCompleted, AddressOf bw_com_runworkercompleted
AddHandler bw_com.ProgressChanged, AddressOf bw_com_progresschanged
bw_com.WorkerReportsProgress = True
bw_com.RunWorkerAsync()
End Sub
Private Sub bw_com_dowork(byval sender as object,byval e as doworkeventargs)
'this is the long running function that will hang
call objCOM.SendDataToServer()
End sub
End Class
I am a little hesitant to suggest this - but you may want to consider using the Win32 API - Terminate thread.
It is a very dangerous API to use and may leave your application in a potentially dangerous/inconsistent state, but for the given scenario - this is the only way that I can think off.
If it is permissible to perform the work in a separate process - Hans Passant's suggestion is the better one. It is much much safer to terminate a process rather than a thread.
I have started messing around with the MVVP pattern, and I am having some problems with UI responsiveness versus data processing.
I have a program that tracks packages. Shipment and package entities are persisted in SQL database, and are displayed in a WPF view. Upon initial retrieval of the records, there is a noticeable pause before displaying the new shipments view, and I have not even implemented the code that counts shipments that are overdue/active yet (which will necessitate a tracking check via web service, and a lot of time).
I have built this with the Ocean framework, and all appears to be doing well, except when I first started my foray into multi-threading. It broke, and it appeared to break something in Ocean... Here is what I did:
Private QueryThread As New System.Threading.Thread(AddressOf GetShipments)
Public Sub New()
' Insert code required on object creation below this point.
Me.New(ViewManagerService.CreateInstance, ViewModelUIService.CreateInstance)
'Perform initial query of shipments
'QueryThread.Start()
GetShipments()
Console.WriteLine(Me.Shipments.Count)
End Sub
Public Sub New(ByVal objIViewManagerService As IViewManagerService, ByVal objIViewModelUIService As IViewModelUIService)
MyBase.New(objIViewModelUIService)
End Sub
Public Sub GetShipments()
Dim InitialResults = From shipment In db.Shipment.Include("Packages") _
Select shipment
Me.Shipments = New ShipmentsCollection(InitialResults, db)
End Sub
So I declared a new Thread, assigned it the GetShipments method and instanced it in the default constructor. Ocean freaks out at this, so there must be a better way of doing it.
I have not had the chance to figure out the usage of the SQL ORM thing in Ocean so I am using Entity Framework (perhaps one of these days i will look at NHibernate or something too).
I have looked at a number of articles and they all have examples of simple uses. Some have mentioned the Dispatcher, but none really go very far into how it is used.
The way I do it is, using a backgroundworker. Then I use MVVM Light to send a message to the main ui to update progress.