I have this procedure (converted from C#):
Private Sub _biometrics_IdentifyFailed(ByVal sender As Object, ByVal e As AuthenticationFailedEventArgs)
' See comment above...
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, New Action(Function()
StatusTextBox.Text = "Failed"
UsernameTextBox.Text = [String].Empty
_session.Close()
_session = _biometrics.OpenSession()
End Function))
End Sub
I am having an error in the 'Dispatcher.BegingInvoke' saying 'Reference to a non-shared member requires an object reference'.
I can't seem to figure out what this means or how to solve it.
Can someone understand it and help me solve it?
This is a windows forms application, VS 2010, .NET framework 4.0.
Thanks.
There is no Dispatcher property available in the current scope. Since the Dispatcher is also a type, the compiler defaults to attempting to call a static BeginInvokemethod defined on the Dispatcher type. There is none, there is only an instance method, and that's what the exception is saying.
What you are really doing is you are copypasting WPF code snippets into your Windows Forms application. The Dispatcher is used in WPF applications. This is known as "god tier" application development. You aren't programming within this heightened sphere. Because this can be read by children, I'll refrain from describing what Windows Forms development using VB.NET is.
You're probably trying to update the UI from a background thread. In this case, you will be using Control.BeginInvoke to update the control from a background thread. You're probably in the codebehind for a control, so just invoke the method this way:
Private Sub _biometrics_IdentifyFailed(ByVal sender As Object, ByVal e As AuthenticationFailedEventArgs)
' See comment above...
BeginInvoke(New InvokeDelegate(AddressOf InvokeMethod))
_session.Close()
_session = _biometrics.OpenSession()
end Sub
Public Sub InvokeMethod()
StatusTextBox.Text = "Failed"
UsernameTextBox.Text = [String].Empty
End Sub
Note that _biometrics_IdentifyFailed is executing on the background thread, so only background work should be happening there. InvokeMethod will execute on the UI thread, so only UI updates should happen there. I don't VB, so I might have some syntax errors in here. Good luck.
Related
I made a projet a month ago in C# where I used application settings to save data. This time, I'm making a project in VB .NET where I need to store data. I have a textbox where the user input a certain key and if he gets it right, it sets a setting to true. Here's the code.
Public Class Form4
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If TextBox1.Text = ("CF4A438C1F68D" Or "7552B2C629D11" Or "9C9C94A73141A" Or "7EE3998DAB3D3" Or "FC4DABC9CA7EA" Or "FDEE4B1F2C113" Or "D773E97B47531" Or "AABBBF181D39A" Or "36F37B39D19A5") Then
Properties.Settings.Default.ActivatedVersion = True
Properties.Settings.Default.Save()
End If
End Sub
End Class
Unfortunately, when I write Properties, it doesn't work. How can I do this?
In VB, you use My.Settings rather than Properties.Settings.Default.
Also, you don't necessarily need to call Save explicitly as it will happen automatically, by default, when the application exits. It won't happen if the application crashes but, if you have done the right thing and handled the UnhandledException event, the application will not crash, even if an unhandled exception is thrown.
I'm updating an existing application that scans barcodes and is written in VB.net running on windows compact framework 3.5. The scanner is a POCKETPC running windows mobile handheld 6.5. I have added code that uses Asynchronous TCP sockets in a class module. The sockets code is reading and sending data to and from a buffer pool. I now need to “inform” the GUI form that data has been received from the TCP socket and is ready for processing. Because the two processes are running on different threads I realise I cannot access the GUI controls directly. I therefore create a windows message (WM_CUSTOMMSG = &H400) and then use “SENDMESSAGE”. There is an existing WndProc sub (Protected Overrides Sub WndProc(ByRef msg As Microsoft.WindowsCE.Forms.Message)) that handles the WM_DECODEDATA for the scanner message. I added in code to now also process the WM_CUSTOMMSG message I am creating. The WM_CUSTOMMSG is arriving at the WndProc and I am able to display a MessageBox and write a log file, but any changes made to the GUI controls just disappear. I tried to start a forms timer but this also has no effect. Code for the WM_DECODEDATA message updates the GUI controls perfectly. What am I missing / done wrong?
Public Class frmHome
Public SockReceiveMsg As Microsoft.WindowsCE.Forms.Message
Public Sub New()
Private yy As Integer = 0
Private xx As Integer = 0
InitializeComponent()
Me.MsgWin = New MsgWindow(Me)
' Add any initialization after the InitializeComponent() call.
SockReceiveMsg = Microsoft.WindowsCE.Forms.Message.Create(MsgWin.Hwnd, MsgWindow.WM_CUSTOMMSG, New IntPtr(xx), New IntPtr(yy))
end class
Private Sub ReceiveCallback(ByVal ar As IAsyncResult)
'This is the async call back sub
MessageWindow.SendMessage(frmHome.SockReceiveMsg)
end sub
Protected Overrides Sub WndProc(ByRef msg As Microsoft.WindowsCE.Forms.Message)
Dim rc As Integer
Dim ar() As String
If msg.Msg = WM_CUSTOMMSG Then
Try
MsgBox("restart timer") 'this displays
Reader.ReaderEngineAPI.Beeper(8, "") 'a quick ok beep. this works
frmHome.timer1.Enabled = False
frmHome.timer1.Interval = 100
frmHome.timer1.Enabled = True
Catch ex As Exception
MsgBox("wndproc Error1: " & ex.Message)
End Try
End If
'pass all messages onto the base processing. Here the windows ones get processed and our ones get cleared and the storage released
MyBase.WndProc(msg)
End Sub
I don't know if/what you're doing wrong, but you can probably do things alot easier. All Controls (including Forms) have an Invoke() method that you can call to let the framework deal with the windows messages.
This article describes it in a bit more detail, including the InvokeRequired property, which you can probably ignore if you know the data is sent from another thread.
If you do choose to handle these messages manually (since you already have the WndProc routine), be sure to catch all exceptions in the method that updates the GUI, and perhaps inspect the InvokeRequired to see if the control agrees that you can update it from that thread.
I would go with a delegate and an eventhandler on the thread code and use InvokeRequired with a custom delegate to update the GUI thread.
Handling custom message is not recommended. Or why does MS hide WndProc in WindowsCE.Forms? OTOH you may need to Refresh the controls that have been changed to let them know that they need to update. Do you use PostMessage or SendMessage. If later, that would block the thread code until the message is processed, which may block the GUI to update itself, if the WndProc itself uses SendMessage inside the code to handle the custom message.
Private Sub UpdateTextBox(ByVal sender As Object, ByVal e As EventArgs)
'---delegate to update the textbox control
txtMessagesArchive.Text += str
End Sub
That would be called from a background thread in the same class via:
Me.Invoke(New EventHandler(AddressOf UpdateTextBox))
Different ways to update GUI from background thread, but in C#
Thank you to all who provided answers. I had previously tried a delegate with invoke but this caused the system to crash. I spent over a week trying to find out what was wrong with the delegate code – I mean it is so simple, but was not successful. I then tried the WndProc route. Josef’s example in his answer showed me that the delegate must be in the same class as the backgound thread. I had placed the delegate in the form class. Once I put the delegate in the correct class it works perfectly. I have 50 years of experience in IT and programming and you can still learn.
I am not going to try to get to the bottom of the WndProc problem. This is now academic as the delegate route is a better solution and is now working. Thank you
I'm building a "plug-in" of sorts for an already-deployed VB6 executable. I'm using .NET along with COM-Interop. The VB6 creates a blank form and then loads my .NET UserControl into it (however by now the .dll has been compiled into a .ocx ActiveX UserControl that can be seen by VB6).
I've got it working well, but I would like to be able to close the VB6 parent form from inside of my .NET code. I am able to add VB6 code into my VB6-ifyed UserControl, but I cannot seem to find an event that fires when the UserControl is destroyed.
What I've tried so far:
Calling ParentForm.Close from the Disposing event of my .NET control. Receive error Object Reference not set to Instance of an Object.
Trying to close from the VB6 (I am able to get a handle on the parent form from there). Using ControlRemoved, Terminated, and a couple other hackish workarounds that truly make no sense in retrospect don't get triggered.
Calling Application.Exit (truly getting desperate at this point) closes the whole Application (who woulda thunk...)
I looked in the VB6 Interop code that I put in my .NET control and the following does look promising:
#Region "VB6 Events"
'This section shows some examples of exposing a UserControl's events to VB6. Typically, you just
'1) Declare the event as you want it to be shown in VB6
'2) Raise the event in the appropriate UserControl event.
Public Shadows Event Click() 'Event must be marked as Shadows since .NET UserControls have the same name.
Public Event DblClick()
Private Sub InteropUserControl_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Click
RaiseEvent Click()
End Sub
Private Sub InteropUserControl_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.DoubleClick
RaiseEvent DblClick()
End Sub
#End Region
Is it just a matter of adding an event in this section? I'm not terribly familiar with Interop, or VB6 for that matter.
Alright, I figured it out and will post what I did for future generations :P
I was right with the event handlers in the VB6 code, and MarkJ was correct as well.
I created an event in the .NET code,
Public Event KillApp()
and then when I wanted to close everything, raised it:
RaiseEvent KillApp()
In the VB6 UserControl code, I declared the event again,
Public Event KillApp()
and then added a handler for it:
Private Sub MenuCtl_KillApp()
Unload Me.ParentForm
End Sub
where MenuCtl is my instance of the .NET control, and Me.ParentForm is the VB6 container form that houses the control. It now correctly closes the form!
In retrospect it makes a lot of sense, but I was unaware that you could pass events back and forth between managed/unmanaged that easily.
My “form1” is just a simple page with buttons that launch different forms which do all the work, the "form1" code for the first four buttons is below.
What I want is for each form to run in a separate thread.
Public Class Main
Private Sub btnDownLoadStockPrices_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDownLoadStockPrices.Click
LoadStocksFromDownloadSite.Visible = True
End Sub
Private Sub btnLoadOptionsIntoDatabase_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoadOptionsIntoDatabase.Click
LoadOptionsIntoDatabase.Visible = True
End Sub
Private Sub btnVerifyDatabases_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnVerifyDatabases.Click
VerifyDatabase.Visible = True
End Sub
Private Sub btnAnalyzeStock_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAnalyzeStock.Click
AnalyzeSingleStock.visible = True
End Sub
End Class
I’ve found plenty of code examples to have different buttons on a single form run a subroutine in a separate thread, but for some reason I can’t seem to apply it to an entire form.
I think it’s something simple, like I need to tie each new thread as a handle to each forms “load” routine, but I just can’t get it to work. I don’t care about “synchronizing” threads at all, as each form is really a completely different functional program.
Any help would be much appriciated!
This isn't very common; generally it's best to limit all UI stuff to a single thread. But if you're convinced that you need each form to run on a separate thread, you must take into account the Windows API event handling model. The [over]-simplified version is that each form must have its own message loop to remove event messages from the queue and process them, so if you want to open a form on a new thread, you need to create that message pump.
The easiest way to do that is using the Application.Run method, and let the .NET Framework handle creating that message pump for you. For example:
Dim frm As Form1 = New Form1()
Application.Run(frm)
From looking at the code shown in your question, I can't discern any possible reason why those forms would need to run on separate threads. You can call the Show method of multiple forms so that they will be displayed on the screen at the same time. They won't block each other as long as you don't use the ShowDialog method, which displays each as a modal dialog. This is the way so many applications display multiple toolbox windows and other kinds of forms on the screen at the same time.
If you need to do some type of processor-intensive calculation, you still don't need to run each on a separate thread. Spin up a background thread (the BackgroundWorker class makes this very simple) and update the appropriate form's UI using the Invoke method.
You can certainly do this on Win32 but I don't know how well this maps over to .net.
The essential issue is that window handles have thread affinity. So you really need all interaction with them to happen in that thread. Essentially this means that you create all the window handles associated with that form in its thread. You also need to run a message loop in the thread.
The reason that people usually run all the UI out of the main thread and handle long-running actions in separate threads is that it is easier that way. You should ask yourself again why you want to do it this non-standard way.
I suspect you are not quite seeing the full picture. The need for threads in a desktop app principally arises when you have long running actions. Usually you want to keep your UI responsive and providing feedback for the long running action. Doing so leads to threads.
However, in your proposed solution you now have a multitude of extra threads and complexity, and you are still faced with the original problem! A long running action on one of your forms will hang it unless you perform that action in a separate thread, and once again we reach the standard solution to the problem.
I am new to multi-threading in VB.NET and have come across a problem whereby I am wanting to append text to a text box on a form from a service thread running in the background.
The application I am developing is a client/server listener, I have been able to get the client and server PC's to talk with each other (confirmed through MsgBox), however I am now struggling to get the service thread on the server to append the text to the textbox, nothing vissible occurs.
I have a form named testDebug which calls a class (RemoteSupport), this class does all the handshake tasks and updates the textbox with the connection data.
Can anyone identify where I am going wrong and point me in the right direction?
The following is the code I have:
The form has a textbox named txtOutput, the following is from the remoteSupport class
Dim outMessage As String = (encoder.GetString(message, 0, bytesRead))
MsgBox(outMessage, MsgBoxStyle.Information, "MEssage Received")
If outMessage IsNot Nothing Then
If testDebug.InvokeRequired Then
' have the UI thread call this method for us
testDebug.Invoke(New UpdateUIDelegate(AddressOf HandleClientComm), New Object() {outMessage}) '
Else
testDebug.txtOutput.AppendText(outMessage)
End If
'RaiseEvent MessageReceived(outMessage) // a previous attempt to use custom events
End If
I am not sure if the invoke method is the ideal solution or if custom events are, I have spent some time on trying to get custom events to work, but these didnt work either.
// In the RemoteSupport class
Public Delegate Sub MessageReceivedHandler(ByVal message As String)
Public Shared Event MessageReceived As MessageReceivedHandler
// Located throughout the RemoteSupport class where debug information is required.
RaiseEvent MessageReceived(outMessage)
// Located in the code-behind of the form
Private Sub Message_Received(ByVal message As String)
testDebugOutput(message) // this is a function I have created
// to append the text to the text box
End Sub
The code supplied has been cut down so if there is anything else that you want to see or any questions please let me know.
Thanks for your assistance.
EDIT: I have uploaded the two VB files (form and class) to my site, I would appreciate it if someone could have a look at it to help me with identifying the problem with the UI not updating.
I have tried a few other things but nothing seems to be updating the UI once the worker thread has started.
Form: mulholland.it/testDebug.vb.txt
Class: mulholland.it/remoteSupport.vb.txt
Thanks for your assistance.
Matt
I have a form named testDebug...
If testDebug.InvokeRequired Then
This is a classic trap in VB.NET programming. Set a breakpoint on the If statement. Notice how it returns False, even though you know that the code is running on another thread?
InvokeRequired is an instance property of a Form. But testDebug is a class name, not a reference to an instance of a form of type testDebug. That this is possible in VB.NET has gotten a lot of VB.NET programmers in deep trouble. It is an anachronism carried over from VB6. It completely falls apart and blows up in your face when you do this in a thread. You'll get a new instance of the form, instead of the one that the user is looking at. One that isn't visible because its Show() was never called. And otherwise dead as a doornail since the thread isn't running a message loop.
I answered this question several times already, with the recommended fix. I'll just refer you to them rather than rehashing it here:
Form is not updating, after custom class event is fired
Accessing controls between forms
The Delegate method is likely the way you want to go, but I don't see the declaration of the UpdateUIDelegate anywhere
I believe your code should look something like this (assuming you have a reference to the testdebug form local to your remotesupport class
Dim outMessage As String = (encoder.GetString(message, 0, bytesRead))
MsgBox(outMessage, MsgBoxStyle.Information, "MEssage Received")
If outMessage IsNot Nothing Then
If testDebug.InvokeRequired Then
' have the UI thread call this method for us
testDebug.Invoke(New MessageReceivedHandler(AddressOf Testdebug.Message_Received), New Object() {outMessage})
Else
testDebug.txtOutput.AppendText(outMessage)
End If
end if