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.
Related
I've created a VSTO addin for Microsoft Access by following the directions by Microsoft guru Andrew Whitechapel here, and it's working nicely. But the addin has a CustomTaskPane, and I'm having an issue with it when Access is closing.
If the CustomTaskPane is open when Access closes, the addin should save the properties of the CustomTaskPane controls. If code for this is placed in ThisAddIn_Shutdown(), I receive the following error:
System.ObjectDisposedException: Cannot access a disposed object.
at Microsoft.Office.Tools.CustomTaskPane.get_Control()
at MyAddin.ThisAddIn.ThisAddIn_Shutdown(Object sender, EventArgs e) in C:\...\ThisAddIn.vb:line nn
I'm not sure if this is the normal operation of CustomTaskPanes or Windows Forms controls, or if it's because VSTO isn't designed for Access. I'm wondering if it happens because Access doesn't have application-level events such as Access.Application."OnClose", as do the other VSTO-approved apps such as Excel & Word.
After some experimentation I found a workaround by using the HandleDestroyed event for the controls, which occurs before Dispose(), and thus the control properties are still available. This works:
Private Sub TextBox1_HandleDestroyed(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles TextBox1.HandleDestroyed
MsgBox(TextBox1.Text)
End Sub
Is there a better way..? Workarounds make me nervous.
In following the trail of events, I realized the answer to my own question. The focal point is the Dispose method in usercontrol.designer.vb. However, it is widely known designer-generated code shouldn't be directly modified, as it can and will be refreshed and over-written after any subsequent changes to the usercontrol in the designer.
Except...that rule doesn't completely apply to certain methods such as Dispose. See here. If the programmer subsequently moves such methods from usercontrol.designer.vb to usercontrol.vb, the designer will defer to them in usercontrol.vb and will not regenerate them in usercontrol.designer.vb.
And so, we've arrived at the answer: move the Dispose method to usercontrol.vb, remove the System.Diagnostics.DebuggerNonUserCode attribute, and then add the necessary code to save the control properties, as shown below:
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
SaveUserControlProperties() <--- additional code added here
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
I have found nice application for NFC card reading which in winforms works very nice.
Code found here:
NfcReader: A very simple NFC library for C# that supports insert and discard events
Git: https://github.com/h4kbas/NfcReader
But i have a problem. I need now to make this work in com for vb6.
I must "replicate" event hooking, like in the following code.
I exposed methods and events to vb6 successfully.
NFC = new NFCReader();
NFC.CardInserted += new NFCReader.CardEventHandlerDelgate(Card_Inserted);
NFC.CardEjected += new NFCReader.CardEventHandlerDelgate(Card_Ejected);
NFC.DeviceDisconnected += new NFCReader.CardEventHandlerDelgate(Device_disconected);
NFC.StartCardMonitoring();
With the information #kunif provided, you first have to make the .NET library COM Visible in order to use the NFC Reader in VB6. If you have the source code, you can do this fairly easily. Otherwise, if you just have a DLL, you can write a wrapper DLL and make that COM Visible. Sounds like you might've done this already when you say "i exposed events and methods to vb6 successfully".
The next step is to add a reference to the library in VB6: Project > References...
Then, you can create an instance of the NFCReader:
Public WithEvents objNFC As NFCReader
Private Sub Form_Load()
' Create NFCReader object
Set objNFC = New NFCReader
objNFC.StartCardMonitoring
End Sub
' Card Inserted event handler
Private Sub objNFC_CardInserted()
' Handle Card Inserted event
End Sub
' Card Ejected event handler
Private Sub objNFC_CardEjected()
' Handle Card Ejected event
End Sub
' Device Disconnected event handler
Private Sub objNFC_DeviceDisconnected()
' Handle Device Disconnected event
End Sub
Once you've added a reference to the DLL, you should see the events and their parameters show up in VB6. Make sure you declare the object as WithEvents and the object will appear in the left dropdown in the code window in Visual Studio. The right dropdown will display the available events.
The VB.Net program I am creating dynamically created Panels within a TableLayoutPanel and adds form elements to them. I need to keep track of what the last of these Panels to have focus was, and am hitting a bit of a brick wall.
I have tried creating an event class for when the Panel has focus:
Private Sub Self_GotFocus(ByVal sender As Object, ByVal e As EventArgs) Handles Me.GotFocus
GlobalController.Focus_Target = Me.Name
End Sub
The classes for each Panel Inherit from Windows.Forms.FlowLayoutPanel, which I why I have the call being Me.GotFocus. Additionally, the GlobalController class is just a class meant to hold global variables for the program.
Now the issue I am having, is that this event only seems to trigger when I actually am deleting the panel. When the panel is created, if I click on it, or any of it's form elements, the event never gets triggered (I debugged the program with a breakpoint).
I can't exactly figure out why this only triggers when I go to delete the panel, and not at any other time. Is there another event I should be using instead of GotFocus?
Use .Enter event in your panel since GotFocus is related only to focused control (not it's parent), mostly when UICues is set.
See MSDN GotFocus
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.
My project is built in VB.Net
Many times I find that Visual Studio has added subroutines to my code files even if a subroutine of the exact same name already exists. This can cause debugging nightmares as the new empty routine seems to override the correct routine. I think this can happen if I double-click on a control in the form Design view, but I try not to do this.
Is there any way to turn this off?
Example:
Hand entered
Private Sub TS_Main_View_Network_Click Handles TS_Main_View_Network.Click
System added:
Private Sub TS_Main_View_Network_Click( ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TS_Main_View_Network.Click
I guess that the system adds the second routine because the argument list (which is unneeded but may be required) list is missing from the first routine.
In the example you gave in the comments the methods are overloaded.
I assume you added the first one by hand because the signature is not correct for an event handler. .NET event handlers should have a signature that matches void EventHandler(object sender, EventArgs e).
Private Sub TS_Main_View_Network_Click Handles TS_Main_View_Network.Click
Private Sub TS_Main_View_Network_Click( ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TS_Main_View_Network.Click
When Visual Studio adds an event handler on double click (which you can't disable out-of-the-box) it always creates a method with the correct signature.
Actually, your code takes advantage of some VB features (late-binding, perhaps) to let your declare an event handler without arguments. You cannot do that in a purely static-typed language, like C#, because it is not valid IL: the method without arguments cannot be assigned to an EventHandler delegate (the type of the Click event). Behind the scenes the VB compiler creates some sort of adapter for the method with no arguments.
This happens when you write a handler for a control that does not exist, then add the control to the form in design mode, and then double click on that control.