How to get connection string which is connected right now - vb.net

How can i get the connection string which is right now connected to the database?
I can get all the connection strings but how can i get which one is connected?
Thanks in advance.

For connections that are not using the default connection e.g. .\SQLEXPRESS the following will give you the connections stored in app.config and a method to track the connection. In the form example I am using ms-access but this will work with sql-server also.
Edit: here is a fully function code example on Microsoft's code sample web sute I just created.
Supporting classes
Public Class ConfigItem
Public Property Data As System.Configuration.ConnectionStringSettings
Public Property Index As Integer
End Class
Public Class ConnectionInfo
Public Property Name As String
Public Property ConnectionString As String
Public Property Index As Integer
End Class
Worker class to get connection names, index and connection string
Imports System.Configuration
''' <summary>
''' Must add a reference to System.Configuration
''' to this project.
''' </summary>
''' <remarks></remarks>
Public Class ProjectConnections
''' <summary>
''' Storage for connections
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property Items As New List(Of ConnectionInfo)
''' <summary>
''' Used to remember the current connection
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property CurrentConnectionIndex As Integer
Private RemoveThis As String = ""
Public Sub New()
' look at parent assembly as this class is in a class project used by a
' forms project
RemoveThis = Reflection.Assembly.GetEntryAssembly.GetName.Name & ".My.MySettings."
' get connection data into the Items propery of this class
GetInformation()
End Sub
''' <summary>
''' Traverse through connection strings in app.config, exclude local sql-server connection
''' </summary>
''' <remarks>
''' tested with ms-access, sql-server attached and server based
''' </remarks>
Private Sub GetInformation()
ConfigurationManager.ConnectionStrings.Cast(Of ConnectionStringSettings)().Select(
Function(item, index) New ConfigItem With {.Data = item, .Index = index}).ToList _
.ForEach(
Sub(ConfigItem)
If ConfigItem.Data.Name.Contains(".") Then
Items.Add(
New ConnectionInfo With
{
.Name = ConfigItem.Data.Name.Replace(RemoveThis, ""),
.ConnectionString = ConfigItem.Data.ConnectionString,
.Index = ConfigItem.Index
})
End If
End Sub)
End Sub
End Class
Form utilizing the classes above. In this case there are two connections stored in app.config. The worker class is instantiated at form level so we can use it to track the current connection string. Optionally we could localize the class and use a private integer variable to remember the current connection. In form load, I am choosing which connection to use rather than the default, stored the index for that connection in the worker class instance, display connection string names in a ComboBox and exposing all information in a DataGridView. Pressing a button, we change the connection at runtime while a second button shows the underlying connection string.
Note the import statement, I placed the worker class into a class project so the form project must have a reference to it then followed by the import statement.
Imports ConfigurationLibrary
Public Class Form1
Private connections As ProjectConnections = New ProjectConnections()
Private Sub CustomersBindingNavigatorSaveItem_Click(sender As Object, e As EventArgs) _
Handles CustomersBindingNavigatorSaveItem.Click
Me.Validate()
Me.CustomersBindingSource.EndEdit()
Me.TableAdapterManager.UpdateAll(Me.Database1DataSet)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' here I am loading a connection other than the default
CustomersTableAdapter.Connection.ConnectionString = connections.Items(1).ConnectionString
' for keeping track later as in cmdGetConnection.Click
connections.CurrentConnectionIndex = 1
Me.CustomersTableAdapter.Fill(Me.Database1DataSet.Customers)
cboConnections.DataSource = connections.Items
cboConnections.DisplayMember = "Name"
cboConnections.SelectedIndex = 1
dgvInformation.AutoGenerateColumns = False
dgvInformation.DataSource = connections.Items
CustomersDataGridView.ExpandColumns()
dgvInformation.ExpandColumns()
End Sub
Private Sub cmdSetConnection_Click(sender As Object, e As EventArgs) Handles cmdSetConnection.Click
Dim OrdinalIndex As Integer = CType(cboConnections.SelectedItem, ConnectionInfo).Index - 1
CustomersTableAdapter.Connection.Close()
CustomersTableAdapter.Connection.ConnectionString = connections.Items(OrdinalIndex).ConnectionString
CustomersTableAdapter.Connection.Open()
CustomersTableAdapter.Fill(Me.Database1DataSet.Customers)
cboConnections.SelectedIndex = OrdinalIndex
End Sub
Private Sub cmdGetConnection_Click(sender As Object, e As EventArgs) Handles cmdGetConnection.Click
Dim sb As New System.Text.StringBuilder
sb.AppendLine(cboConnections.Text)
sb.AppendLine(connections.Items(connections.CurrentConnectionIndex).ConnectionString)
MessageBox.Show(sb.ToString)
End Sub
End Class
Screenshot

Related

Cannot close/unload plugin without closing the entire application

I need some help with IPlugin implementation for my app.
PluginContracts code:
Public Interface IPlugin
ReadOnly Property Name() As String
Sub DoSomething()
Sub DoExit()
End Interface
Main App:
Imports PluginContracts
Module Program
Sub Main()
LoadTray()
End Sub
Sub LoadTray()
Dim dll As Assembly = Assembly.LoadFrom(GetDLL.TrayDll)
For Each t As Type In dll.GetTypes
If Not t.GetInterface("IPlugin") = Nothing Then
Try
Dim PluginClass As IPlugin = Type(Activator.CreateInstance(t), IPlugin)
PluginClass.DoSomething()
Catch ex As Exception
MessageBox.Show(ex.ToString())
End Try
End If
Next
End Sub
End Module
Plugin:
Imports PluginContracts
Imports System.Windows.Forms
Public Class Plugin
Implements IPlugin
Public Sub DoSomething() Implements IPlugin.DoSomething
Application.Run(New MyContext)
End Sub
Public Sub New()
End Sub
Public Sub DoExit() Implements IPlugin.DoExit
Application.Exit()
End Sub
Public ReadOnly Property Name As String Implements IPlugin.Name
Get
Name = "First Plugin"
End Get
End Property
End Class
(The plugin app is a Dll with a tray icon in Class “MyContext”)
I have everything working, the plugin loads (with the Tray Icon), but I can't close it and load something else.
I have a FileSystemWatcher that will close the plugin, update the Dll and then reopen it, but it closes the Main App and I can’t do anything else…
Thanks for the help
Sorry for taking so long to answer. This turned out to be a lot more complicated than I thought, at least if you want to impose security restrictions on your plugin.
The unsafe way
If you're okay with your plugin executing in so-called full trust, meaning that it can do anything it wants, then you have to do three things:
Tweak your code to launch the plugin in a separate AppDomain.
Run the DoSomething() method in its own thread (this is necessary since Application.Run() tries to create a new UI thread).
Change IPlugin from an Interface to a MustInherit class. This is because code that is marshalled between different AppDomains MUST run in an object that inherits from MarshalByRefObject.
New code:
EDIT (2018-05-29)
I figured the best way to wait for a plugin to exit in a windowless application would be to utilize a ManualResetEvent.
I have made a quick implementation for this in the code below. You must now call PluginClass.Exit() instead of DoExit() when closing your plugin.
I will add this to the safer solution after some more testing.
Dim PluginDomain As AppDomain = Nothing
Dim PluginClass As PluginBase = Nothing
Sub Main()
LoadTray()
If PluginClass IsNot Nothing Then
PluginClass.WaitForExit()
End If
End Sub
Sub LoadTray()
Dim dll As Assembly = Assembly.LoadFrom(GetDLL.TrayDll)
For Each t As Type In dll.GetTypes
If GetType(PluginBase).IsAssignableFrom(t) = True Then
Try
Dim PluginDomainSetup As New AppDomainSetup() With {
.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
.PrivateBinPath = "Plugins"
}
PluginDomain = AppDomain.CreateDomain("MyPluginDomain", Nothing, PluginDomainSetup)
PluginClass = CType(PluginDomain.CreateInstanceFromAndUnwrap(GetDLL.TrayDll, t.FullName), PluginBase)
Dim PluginThread As New Thread(AddressOf PluginClass.DoSomething)
PluginThread.IsBackground = True
PluginThread.Start()
Catch ex As Exception
MessageBox.Show(ex.ToString())
End Try
Exit For 'Don't forget to exit the loop.
End If
Next
End Sub
PluginBase.vb:
Public MustInherit Class PluginBase
Inherits MarshalByRefObject
Private IsExitingEvent As New ManualResetEvent(False)
Public MustOverride ReadOnly Property Name As String
Public MustOverride Sub DoSomething()
Protected MustOverride Sub OnExit()
Public Sub [Exit]()
Me.OnExit()
Me.IsExitingEvent.Set()
End Sub
Public Sub WaitForExit()
Me.IsExitingEvent.WaitOne(-1)
End Sub
End Class
Your plugin:
Imports PluginContracts
Imports System.Windows.Forms
Public Class Plugin
Inherits PluginBase
Protected Overrides Sub DoSomething()
Application.Run(New MyContext)
End Sub
Protected Overrides Sub OnExit()
Application.Exit()
End Sub
Public Overrides ReadOnly Property Name As String
Get
Return "First Plugin" 'Use "Return" instead of "Name = ..."
End Get
End Property
End Class
However, as letting the plugin run in full trust is extremely unsafe I designed a solution which lets you control what the plugin can do. This required me to rewrite most of the code though, as it needed a different structure to work.
The safe(r) way
In my test case PluginContracts is a separate DLL (project) with only four classes:
PluginBase - The base class for all plugins.
PluginInfo - A wrapper containing info about a plugin (used by PluginManager).
PluginManager - A manager for loading, unloading and keeping track of plugins.
PluginUnloader - A class that runs with full trust in the plugin's restricted AppDomain. Only used to be able to call Application.Exit().
First and foremost, for everything to work the PluginContracts DLL needs to be signed with a Strong Name. Here's how that can be done:
Right-click the PluginContracts project in the Solution Explorer and press Properties.
Select the Signing tab.
Check the check box that says Sign the assembly, leave Delay sign only unchecked.
Open the drop down and press <New...>.
Give the key a file name and password (be sure to remember this!).
Done!
Now that that's fixed you need to make the PluginContracts DLL available to be called by partially trusted code. This is so that our plugin can use it since it will be running as untrusted code.
Select the project in the Solution Explorer again.
Press the button in the Solution Explorer that says Show all files.
Expand the My Project node.
Double-click AssemblyInfo.vb to edit it and add this line at the end of the file:
<Assembly: AllowPartiallyTrustedCallers(PartialTrustVisibilityLevel:=Security.PartialTrustVisibilityLevel.NotVisibleByDefault)>
There is a backside to doing this: All code inside the PluginContracts DLL will now run with rather low permissions. To make it run with the standard permissions again you have to decorate each class with the SecurityCritical attribute, except for the PluginBase class (in the code below I've already fixed all this, so you don't need to change anything). Due to this I recommend that you only have below four classes in the PluginContracts project:
PluginBase.vb:
''' <summary>
''' A base class for application plugins.
''' </summary>
''' <remarks></remarks>
Public MustInherit Class PluginBase
Inherits MarshalByRefObject
Public MustOverride ReadOnly Property Name As String
Public MustOverride Sub DoSomething()
Public MustOverride Sub OnExit()
End Class
PluginInfo.vb:
Imports System.Security
Imports System.Runtime.Remoting
''' <summary>
''' A class holding information about a plugin.
''' </summary>
''' <remarks></remarks>
<SecurityCritical()>
Public Class PluginInfo
Private _file As String
Private _plugin As PluginBase
Private _appDomain As AppDomain
Private Unloader As PluginUnloader
Friend Unloaded As Boolean = False
''' <summary>
''' Gets the AppDomain that this plugin runs in.
''' </summary>
''' <remarks></remarks>
Friend ReadOnly Property AppDomain As AppDomain
Get
Return _appDomain
End Get
End Property
''' <summary>
''' Gets the full path to the plugin assembly.
''' </summary>
''' <remarks></remarks>
Public ReadOnly Property File As String
Get
Return _file
End Get
End Property
''' <summary>
''' Gets the underlying plugin.
''' </summary>
''' <remarks></remarks>
Public ReadOnly Property Plugin As PluginBase
Get
Return _plugin
End Get
End Property
''' <summary>
''' DO NOT USE! See PluginManager.UnloadPlugin() instead.
''' </summary>
''' <remarks></remarks>
<SecurityCritical()>
Friend Sub Unload()
Me.Unloader.Unload()
Me.Unloaded = True
End Sub
''' <summary>
''' Initializes a new instance of the PluginInfo class.
''' </summary>
''' <param name="File">The full path to the plugin assembly.</param>
''' <param name="Plugin">The underlying plugin.</param>
''' <param name="AppDomain">The AppDomain that the plugin runs in.</param>
''' <remarks></remarks>
<SecurityCritical()>
Friend Sub New(ByVal File As String, ByVal Plugin As PluginBase, ByVal AppDomain As AppDomain)
_file = File
_plugin = Plugin
_appDomain = AppDomain
'Create an instance of PluginUnloader inside the plugin's AppDomain.
Dim Handle As ObjectHandle = Activator.CreateInstanceFrom(Me.AppDomain, GetType(PluginUnloader).Module.FullyQualifiedName, GetType(PluginUnloader).FullName)
Me.Unloader = CType(Handle.Unwrap(), PluginUnloader)
End Sub
End Class
PluginManager.vb:
'-------------------------------------------------------------------------------
'Copyright (c) 2018, Vincent Bengtsson
'All rights reserved.
'
'Redistribution and use in source and binary forms, with or without
'modification, are permitted provided that the following conditions are met:
'1. Redistributions of source code must retain the above copyright notice, this
' list of conditions and the following disclaimer.
'2. Redistributions in binary form must reproduce the above copyright notice,
' this list of conditions and the following disclaimer in the documentation
' and/or other materials provided with the distribution.
'
'THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
'ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
'WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
'DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
'ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
'(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
'LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
'ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
'(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
'SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'-------------------------------------------------------------------------------
Imports System.Collections.ObjectModel
Imports System.Reflection
Imports System.IO
Imports System.Security
Imports System.Security.Permissions
Imports System.Security.Policy
''' <summary>
''' A class for managing application plugins.
''' </summary>
''' <remarks></remarks>
<SecurityCritical()>
Public NotInheritable Class PluginManager
Implements IDisposable
Private PluginLookup As New Dictionary(Of String, PluginInfo)
Private PluginList As New List(Of String)
Private CurrentAppDomain As AppDomain = Nothing
Private _loadedPlugins As New ReadOnlyCollection(Of String)(Me.PluginList)
''' <summary>
''' Gets a list of all loaded plugins' names.
''' </summary>
''' <remarks></remarks>
Public ReadOnly Property LoadedPlugins As ReadOnlyCollection(Of String)
Get
Return _loadedPlugins
End Get
End Property
''' <summary>
''' Returns the plugin with the specified name (or null, if the plugin isn't loaded).
''' </summary>
''' <param name="Name">The name of the plugin to get.</param>
''' <remarks></remarks>
<SecurityCritical()>
Public Function GetPluginByName(ByVal Name As String) As PluginInfo
Dim Plugin As PluginInfo = Nothing
Me.PluginLookup.TryGetValue(Name, Plugin)
Return Plugin
End Function
''' <summary>
''' Checks whether a plugin by the specified name is loaded.
''' </summary>
''' <param name="Name">The name of the plugin to look for.</param>
''' <remarks></remarks>
<SecurityCritical()>
Public Function IsPluginLoaded(ByVal Name As String) As Boolean
Return Me.PluginLookup.ContainsKey(Name)
End Function
''' <summary>
''' Loads a plugin with the specified permissions (or no permissions, if omitted).
''' </summary>
''' <param name="File">The path to the plugin assembly to load.</param>
''' <param name="Permissions">Optional. A list of permissions to give the plugin (default permissions that are always applied: SecurityPermissionFlag.Execution).</param>
''' <remarks></remarks>
<SecurityCritical()>
Public Function LoadPlugin(ByVal File As String, ByVal ParamArray Permissions As IPermission()) As PluginInfo
Dim FullPath As String = Path.GetFullPath(File)
If System.IO.File.Exists(FullPath) = False Then Throw New FileNotFoundException()
'Check if the plugin file has already been loaded. This is to avoid odd errors caused by Assembly.LoadFrom().
If Me.PluginLookup.Values.Any(Function(info As PluginInfo) info.File.Equals(FullPath, StringComparison.OrdinalIgnoreCase)) = True Then
Throw New ApplicationException("Plugin """ & FullPath & """ is already loaded!")
End If
'Load assembly and look for a type derived from PluginBase.
Dim PluginAssembly As Assembly = Assembly.LoadFrom(FullPath)
Dim PluginType As Type = PluginManager.GetPluginType(PluginAssembly)
If PluginType Is Nothing Then Throw New TypeLoadException("""" & FullPath & """ is not a valid plugin!")
'Set up the application domain.
'Setting PartialTrustVisibleAssemblies allows our plugin to make partially trusted calls to the PluginBase DLL.
Dim PluginDomainSetup As New AppDomainSetup() With {
.ApplicationBase = Me.CurrentAppDomain.BaseDirectory,
.PartialTrustVisibleAssemblies = New String() {GetType(PluginUnloader).Assembly.GetName().Name & ", PublicKey=" & BitConverter.ToString(GetType(PluginUnloader).Assembly.GetName().GetPublicKey()).ToLower().Replace("-", "")}
}
'Set up the default (necessary) permissions for the plugin:
' SecurityPermissionFlag.Execution - Allows our plugin to execute managed code.
' FileIOPermissionAccess.Read - Allows our plugin to read its own assembly.
' FileIOPermissionAccess.PathDiscovery - Allows our plugin to get information about its parent directory.
Dim PluginPermissions As New PermissionSet(PermissionState.None) 'No permissions to begin with.
PluginPermissions.AddPermission(New SecurityPermission(SecurityPermissionFlag.Execution))
PluginPermissions.AddPermission(New FileIOPermission(FileIOPermissionAccess.Read Or FileIOPermissionAccess.PathDiscovery, FullPath))
'Load all additional permissions (if any).
For Each Permission As IPermission In Permissions
PluginPermissions.AddPermission(Permission)
Next
'Get the strong name for the assembly containing PluginUnloader and create the AppDomain.
'The strong name is used so that PluginUnloader may bypass the above added restrictions.
Dim TrustedAssembly As StrongName = GetType(PluginUnloader).Assembly.Evidence.GetHostEvidence(Of StrongName)()
Dim PluginDomain As AppDomain = AppDomain.CreateDomain(File, Nothing, PluginDomainSetup, PluginPermissions, TrustedAssembly)
'Create an instance of the plugin.
Dim Plugin As PluginBase = CType(PluginDomain.CreateInstanceFromAndUnwrap(FullPath, PluginType.FullName), PluginBase)
Dim PluginInfo As New PluginInfo(FullPath, Plugin, PluginDomain)
'Is a plugin by this name already loaded?
If Me.IsPluginLoaded(Plugin.Name) = True Then
Dim Name As String = Plugin.Name
Me.UnloadPlugin(PluginInfo)
Throw New ApplicationException("A plugin by the name """ & Name & """ is already loaded!")
End If
'Add the plugin to our lookup table and name list.
Me.PluginLookup.Add(Plugin.Name, PluginInfo)
Me.PluginList.Add(Plugin.Name)
'Return the loaded plugin to the caller.
Return PluginInfo
End Function
''' <summary>
''' Unloads a plugin.
''' </summary>
''' <param name="Name">The name of the plugin to unload.</param>
''' <remarks></remarks>
<SecurityCritical()>
Public Sub UnloadPlugin(ByVal Name As String)
Dim Plugin As PluginInfo = Me.GetPluginByName(Name)
If Plugin Is Nothing Then Throw New ArgumentException("No plugin by the name """ & Name & """ is loaded.", "Name")
Me.UnloadPlugin(Plugin)
End Sub
''' <summary>
''' Unloads a plugin.
''' </summary>
''' <param name="PluginInfo">The plugin to unload.</param>
''' <remarks></remarks>
<SecurityCritical()>
Public Sub UnloadPlugin(ByVal PluginInfo As PluginInfo)
If PluginInfo Is Nothing Then Throw New ArgumentNullException("PluginInfo")
If PluginInfo.Unloaded = True Then Return
Dim PluginName As String = PluginInfo.Plugin.Name
Dim Permission As New SecurityPermission(SecurityPermissionFlag.ControlAppDomain)
Permission.Assert()
PluginInfo.Plugin.OnExit()
PluginInfo.Unload()
AppDomain.Unload(PluginInfo.AppDomain)
CodeAccessPermission.RevertAssert()
Me.PluginLookup.Remove(PluginName)
End Sub
''' <summary>
''' Attempts to get a class derived from PluginBase in the specified assembly.
''' </summary>
''' <param name="PluginAssembly">The assembly to check.</param>
''' <remarks></remarks>
<SecurityCritical()>
Private Shared Function GetPluginType(ByVal PluginAssembly As Assembly) As Type
For Each t As Type In PluginAssembly.GetTypes()
If GetType(PluginBase).IsAssignableFrom(t) = True Then Return t
Next
Return Nothing
End Function
''' <summary>
''' Initializes a new instance of the PluginManager class.
''' </summary>
''' <remarks></remarks>
<SecurityCritical()>
Public Sub New()
Me.CurrentAppDomain = AppDomain.CurrentDomain
End Sub
#Region "IDisposable Support"
Private disposedValue As Boolean ' To detect redundant calls
' IDisposable
<SecurityCritical()>
Protected Sub Dispose(disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: dispose managed state (managed objects).
'Unload all plugins.
For Each PluginPair As KeyValuePair(Of String, PluginInfo) In Me.PluginLookup
Try : Me.UnloadPlugin(PluginPair.Value) : Catch : End Try
Next
End If
' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
' TODO: set large fields to null.
End If
Me.disposedValue = True
End Sub
' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
'Protected Overrides Sub Finalize()
' ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
' Dispose(False)
' MyBase.Finalize()
'End Sub
' This code added by Visual Basic to correctly implement the disposable pattern.
<SecurityCritical()>
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
PluginUnloader.vb:
Imports System.Windows.Forms
Imports System.Security
Imports System.Security.Permissions
''' <summary>
''' A class for unloading plugins from within their AppDomains.
''' </summary>
''' <remarks></remarks>
<SecurityCritical()>
Public NotInheritable Class PluginUnloader
Inherits MarshalByRefObject
''' <summary>
''' Calls Application.Exit(). This must be called inside the plugin's AppDomain.
''' </summary>
''' <remarks></remarks>
<SecurityCritical()>
Public Sub Unload()
'Request permission to execute managed code (required to call Application.Exit()).
Dim Permission As New SecurityPermission(SecurityPermissionFlag.UnmanagedCode)
Permission.Assert()
'Exits the plugin's UI threads (if any exist).
Application.Exit()
'Revert UnmanagedCode privilege.
CodeAccessPermission.RevertAssert()
End Sub
''' <summary>
''' Initializes a new instance of the PluginUnloader class.
''' </summary>
''' <remarks></remarks>
<SecurityCritical()>
Public Sub New()
End Sub
End Class
Example usage
Main code (currently used in a form):
Dim PluginManager As New PluginManager
Dim PluginClass As PluginInfo
Private Sub RunPluginButton_Click(sender As System.Object, e As System.EventArgs) Handles RunPluginButton.Click
'Load our plugin and give it UI permissions.
'The "UIPermissionWindow.AllWindows" flag allows our plugin to create a user interface (display windows, notify icons, etc.).
PluginClass = PluginManager.LoadPlugin("Plugins\TestPlugin.dll", New UIPermission(UIPermissionWindow.AllWindows))
'IMPORTANT: Each plugin must run in its own thread!
Dim tr As New Thread(AddressOf PluginClass.Plugin.DoSomething)
tr.IsBackground = True
tr.Start()
End Sub
Private Sub ExitPluginButton_Click(sender As System.Object, e As System.EventArgs) Handles ExitPluginButton.Click
If PluginClass Is Nothing Then
MessageBox.Show("Plugin not loaded!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
End If
PluginManager.UnloadPlugin(PluginClass)
PluginClass = Nothing
End Sub
Example plugin:
Public Class TestPlugin
Inherits PluginBase
Public Overrides ReadOnly Property Name As String
Get
Return "Test Plugin"
End Get
End Property
Public Overrides Sub DoSomething()
Application.Run(New MyContext)
End Sub
Public Overrides Sub OnExit()
'Do some cleanup here, if necessary.
'No need to call Application.Exit() - it is called automatically by PluginUnloader.
End Sub
End Class
How permissions work
When you call the PluginManager.LoadPlugin() method you pass it a path to the plugin which to load, however you can also pass it a set of permissions that you want to apply to the plugin (if you want to, this is optional).
By default all plugins are loaded only with these permissions:
It can execute its own managed code.
It has read access to the plugin file itself and its directory.
This means that a plugin:
Can not execute any unmanaged (also known as native) code. This is for example DllImport or Declare Function declarations.
Can not read/write/create/delete any files.
Can not have a User Interface (open any windows, use notify icons, etc.).
Does not have internet access.
Can not run any code other than its own and the framework's (within the boundaries of its restrictions).
...and so on, and so forth...
This can be changed by specifying which permissions the plugin should be granted, when you're loading it. For instance if you want your plugin to be able to read and write files in a certain directory you could do:
PluginManager.LoadPlugin("Plugins\TestPlugin.dll",
New FileIOPermission(FileIOPermissionAccess.AllAccess, "C:\some\folder"))
Or if you want it to be able to access any folder or file:
PluginManager.LoadPlugin("Plugins\TestPlugin.dll",
New FileIOPermission(PermissionState.Unrestricted))
Multiple permissions can be added by just keep adding arguments:
PluginManager.LoadPlugin("Plugins\TestPlugin.dll",
New FileIOPermission(PermissionState.Unrestricted),
New UIPermission(UIPermissionWindow.AllWindows),
New WebPermission(PermissionState.Unrestricted))
For a list of all available permission types see:
https://msdn.microsoft.com/en-us/library/h846e9b3(v=vs.110).aspx

Methods to use the serial port in multiple forms

I'm designing a windows userform to interact with a microcontroller through the serial port.
The GUI includes multiple userforms which will use the same serialport. I researched how to do that and I found different ideas. Some I don't know if it works the others I'm not sure how to implement it in code. Let's say I have
Form1: Start.vb
Form2: Shield1.vb
1) Can I declare the serial port in the start userform as:
Public Shared SerialPort1 As New System.IO.Ports.SerialPort
And use it in the other forms ?
2) First alternative: Use a Module to declare a new Serialport
Module Module1
Public WithEvents mySerialPort1 As New IO.Ports.SerialPort
Private Sub mySerialPort1_DataReceived(sender As Object, _
e As System.IO.Ports.SerialDataReceivedEventArgs) _
Handles mySerialPort1.DataReceived
End Sub
End Module
Is this method right? If yes How do I use it in the code of my forms ? How to include the DataReceived Event in the code of the forms ?
3) Second alternative: Constructor of the Serialport in the start form and then pass the data to the other forms as mentionned in this post: Alternate Solution 1
private void OnSetup(object sender, EventArgs e)
{
this.port = new SerialPort(...);
// TODO: initialize port
Form2 f2 = new Form2(this.port);
f2.Show();
Form3 f3 = new Form3(this.port);
f3.Show();
Form4 f4 = new Form4(this.port);
f4.Show();
}
Are then the Events also included ? How do I use them?
4) Third alternative: Use a static class like done in this solution:
Alternate Solution 2
Is the code written in C# here right ? I'm writing my programm in VB.net but I could take this as a reference.
Which is the recommended solution for a beginner ? Could you please write it in a small code if you have another suggestion or a correction ?
I apologize in advance for any ambiguity or falsly used terms.
Thanks you!
I would follow the "Singleton" design pattern, which ensures that only one instance of a class is created. Here is a well-accepted template for such a class:
Public NotInheritable Class MySerial
Private Shared ReadOnly _instance As New Lazy(Of MySerial)(Function() New
MySerial(), System.Threading.LazyThreadSafetyMode.ExecutionAndPublication)
Private Sub New()
End Sub
Public Shared ReadOnly Property Instance() As MySerial
Get
Return _instance.Value
End Get
End Property
End Class
In the New() method you should set up your serial port as you need to. Then no matter where you need to use the port, you make your references to the Instance:
Dim singletonSerial As MySerial = MySerial.Instance
This is the canonical pattern to ensure that you have only one copy of an object without resorting to static classes. It's a design pattern that dates back more than 20 years and still works great when you need exactly one copy of an object.
I found this very helpful example of a sigleton C# class for serial port:
C# Singleton Example
As a beginner I used a Code converter to have it in VB.Net. Can you guys let me know if the resulting code is correct and I can use it ? Thanks a lot!!
Imports System
Imports System.IO
Imports System.IO.Ports
Imports System.Threading
Namespace HeiswayiNrird.Singleton
Public NotInheritable Class SerialPortManager
Private Shared lazy As Lazy(Of SerialPortManager) = New Lazy(Of SerialPortManager)(() => { }, New SerialPortManager)
Public Shared ReadOnly Property Instance As SerialPortManager
Get
Return lazy.Value
End Get
End Property
Private _serialPort As SerialPort
Private _readThread As Thread
Private _keepReading As Boolean
Private Sub New()
MyBase.New
Me._serialPort = New SerialPort
Me._readThread = Nothing
Me._keepReading = false
End Sub
''' <summary>
''' Update the serial port status to the event subscriber
''' </summary>
Public Event OnStatusChanged As EventHandler(Of String)
''' <summary>
''' Update received data from the serial port to the event subscriber
''' </summary>
Public Event OnDataReceived As EventHandler(Of String)
''' <summary>
''' Update TRUE/FALSE for the serial port connection to the event subscriber
''' </summary>
Public Event OnSerialPortOpened As EventHandler(Of Boolean)
''' <summary>
''' Return TRUE if the serial port is currently connected
''' </summary>
Public ReadOnly Property IsOpen As Boolean
Get
Return Me._serialPort.IsOpen
End Get
End Property
''' <summary>
''' Open the serial port connection using basic serial port settings
''' </summary>
''' <param name="portname">COM1 / COM3 / COM4 / etc.</param>
''' <param name="baudrate">0 / 100 / 300 / 600 / 1200 / 2400 / 4800 / 9600 / 14400 / 19200 / 38400 / 56000 / 57600 / 115200 / 128000 / 256000</param>
''' <param name="parity">None / Odd / Even / Mark / Space</param>
''' <param name="databits">5 / 6 / 7 / 8</param>
''' <param name="stopbits">None / One / Two / OnePointFive</param>
''' <param name="handshake">None / XOnXOff / RequestToSend / RequestToSendXOnXOff</param>
Public Sub Open(Optional ByVal portname As String = "COM1", Optional ByVal baudrate As Integer = 9600, Optional ByVal parity As Parity = Parity.None, Optional ByVal databits As Integer = 8, Optional ByVal stopbits As StopBits = StopBits.One, Optional ByVal handshake As Handshake = Handshake.None)
Me.Close
Try
Me._serialPort.PortName = portname
Me._serialPort.BaudRate = baudrate
Me._serialPort.Parity = parity
Me._serialPort.DataBits = databits
Me._serialPort.StopBits = stopbits
Me._serialPort.Handshake = handshake
Me._serialPort.ReadTimeout = 50
Me._serialPort.WriteTimeout = 50
Me._serialPort.Open
Me.StartReading
Catch As IOException
If (Not (OnStatusChanged) Is Nothing) Then
OnStatusChanged(Me, String.Format("{0} does not exist.", portname))
End If
Catch As UnauthorizedAccessException
If (Not (OnStatusChanged) Is Nothing) Then
OnStatusChanged(Me, String.Format("{0} already in use.", portname))
End If
Catch ex As Exception
If (Not (OnStatusChanged) Is Nothing) Then
OnStatusChanged(Me, ("Error: " + ex.Message))
End If
End Try
If Me._serialPort.IsOpen Then
Dim sb As String = StopBits.None.ToString.Substring(0, 1)
Select Case (Me._serialPort.StopBits)
Case StopBits.One
sb = "1"
Case StopBits.OnePointFive
sb = "1.5"
Case StopBits.Two
sb = "2"
End Select
Dim p As String = Me._serialPort.Parity.ToString.Substring(0, 1)
Dim hs As String = "No Handshake"
'TODO: Warning!!!, inline IF is not supported ?
(Me._serialPort.Handshake = Handshake.None)
Me._serialPort.Handshake.ToString
If (Not (OnStatusChanged) Is Nothing) Then
OnStatusChanged(Me, String.Format("Connected to {0}: {1} bps, {2}{3}{4}, {5}.", Me._serialPort.PortName, Me._serialPort.BaudRate, Me._serialPort.DataBits, p, sb, hs))
End If
If (Not (OnSerialPortOpened) Is Nothing) Then
OnSerialPortOpened(Me, true)
End If
Else
If (Not (OnStatusChanged) Is Nothing) Then
OnStatusChanged(Me, String.Format("{0} already in use.", portname))
End If
If (Not (OnSerialPortOpened) Is Nothing) Then
OnSerialPortOpened(Me, false)
End If
End If
End Sub
''' <summary>
''' Close the serial port connection
''' </summary>
Public Sub Close()
Me.StopReading
Me._serialPort.Close
If (Not (OnStatusChanged) Is Nothing) Then
OnStatusChanged(Me, "Connection closed.")
End If
If (Not (OnSerialPortOpened) Is Nothing) Then
OnSerialPortOpened(Me, false)
End If
End Sub
''' <summary>
''' Send/write string to the serial port
''' </summary>
''' <param name="message"></param>
Public Sub SendString(ByVal message As String)
If Me._serialPort.IsOpen Then
Try
Me._serialPort.Write(message)
If (Not (OnStatusChanged) Is Nothing) Then
OnStatusChanged(Me, String.Format("Message sent: {0}", message))
End If
Catch ex As Exception
If (Not (OnStatusChanged) Is Nothing) Then
OnStatusChanged(Me, String.Format("Failed to send string: {0}", ex.Message))
End If
End Try
End If
End Sub
Private Sub StartReading()
If Not Me._keepReading Then
Me._keepReading = true
Me._readThread = New Thread(ReadPort)
Me._readThread.Start
End If
End Sub
Private Sub StopReading()
If Me._keepReading Then
Me._keepReading = false
Me._readThread.Join
Me._readThread = Nothing
End If
End Sub
Private Sub ReadPort()
While Me._keepReading
If Me._serialPort.IsOpen Then
'byte[] readBuffer = new byte[_serialPort.ReadBufferSize + 1];
Try
'int count = _serialPort.Read(readBuffer, 0, _serialPort.ReadBufferSize);
'string data = Encoding.ASCII.GetString(readBuffer, 0, count);
Dim data As String = Me._serialPort.ReadLine
If (Not (OnDataReceived) Is Nothing) Then
OnDataReceived(Me, data)
End If
Catch As TimeoutException
End Try
Else
Dim waitTime As TimeSpan = New TimeSpan(0, 0, 0, 0, 50)
Thread.Sleep(waitTime)
End If
End While
End Sub
End Class
End Namespace

Download text file and set components?

How to make vb download a text file and then read all the lines to see what components are available and then set them on form? Text file would contain something like this:
[1] = number of component, pyc_file as the component name to display to user. and last is a link from where the file is downloaded.
The pyc_file should be displayed next on a checkbox.
[1];pyc_file;www.mediafire.com/abcd1234/_init_.pyc;
I don't know how to explain it very well. I hope you understand!
You can create your own type to parse the parts like this:
Public Class Form1
Dim LineToParse As String =
"[1];pyc_file;www.mediafire.com/abcd1234/_init_.pyc;"
Private Sub Test() Handles MyBase.Shown
Dim Comp As Component = Me.GetComponent(LineToParse)
Dim sb As New System.Text.StringBuilder
sb.AppendFormat("Line: {0}", LineToParse)
sb.AppendLine(Environment.NewLine)
sb.AppendFormat("Index: {0}", CStr(Comp.Index))
sb.AppendLine()
sb.AppendFormat("Name: {0}", Comp.Name)
sb.AppendLine()
sb.AppendFormat("Url: {0}", Comp.URL)
MessageBox.Show(sb.ToString, "Component information",
MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
''' <summary>
''' Parses a comma-delimited component text-line and returns a Component Object.
''' </summary>
''' <param name="ComponentLine">Indicates the component line to parse.</param>
''' <returns>Component.</returns>
Friend Function GetComponent(ByVal ComponentLine As String) As Component
Dim ComponentParts As IEnumerable(Of String) =
ComponentLine.Split(";"c)
Dim Index As Integer =
Integer.Parse(ComponentParts(0).
Replace("[", String.Empty).Replace("]", String.Empty))
Dim Name As String =
ComponentParts(1)
Dim URL As Uri =
New Uri(If(Not ComponentParts(2).StartsWith("http://", StringComparison.OrdinalIgnoreCase),
String.Format("http://{0}", ComponentParts(2)),
ComponentParts(2)))
Return New Component With
{
.Index = Index,
.Name = Name,
.URL = URL
}
End Function
''' <summary>
''' A component.
''' </summary>
Friend NotInheritable Class Component
''' <summary>
''' Indicates the number of component.
''' </summary>
Public Property Index As Integer
''' <summary>
''' Indicates the component name to display to user.
''' </summary>
Public Property Name As String
''' <summary>
''' Indicates a link from where the file is downloaded.
''' </summary>
Public Property URL As Uri
End Class
End Class
I have solved it now :-)
Wasn't actually that hard.

How to have two instances of a class in VB.NET

I have created a class and want to create two separate instances of this class. I've been able to achieve this in web applications, but I'm attempting to do this in a windows form and for some reason when I create the second instance of the class it has the variable data from the first instance.
I've tried to create the two instances as global variables as I want them to be accessed by separate sub routines. The two instances are declared as such:
Public Class Form1
Dim oTree1_Animation As New clsAnimation()
Dim oTree2_Animation As New clsAnimation()
I then attempt to populate the instance in a sub routine which fires on a MouseDown event:
Private Sub PictureBox1_MouseDown(sender As System.Object, e As System.EventArgs) Handles PictureBox1.MouseDown
Dim oFrame As New clsFrame(2, {0, -32}, {0, 0})
Dim timTimer As New Timer()
oTree1_Animation.Initialise(2, 100, oFrame, PictureBox1, timTimer)
oTree1_Animation.AnimationStart()
End Sub
The second is then populated in a similar fashion:
Private Sub PictureBox2_MouseDown(sender As System.Object, e As System.EventArgs) Handles PictureBox2.MouseDown
Dim oFrame As New clsFrame(2, {0, -32}, {0, 0})
Dim timTimer As New Timer()
oTree2_Animation.Initialise(2, 100, oFrame, PictureBox2, timTimer)
oTree2_Animation.AnimationStart()
End Sub
The class looks like this:
Public Class clsAnimation
Public Event Tick As EventHandler
Public Shared FrameCount As Integer
Public Shared FrameInterval As Integer
Public Shared CurrentFrame As Integer = 0
Public Shared FrameSet As clsFrame
Public Shared Image As PictureBox
Public Shared WithEvents Timer As Timer
''' <summary>
''' Creates an empty instance of the Animation class
''' </summary>
''' <remarks></remarks>
Public Sub New()
End Sub
''' <summary>
''' Creates a new instance of the Animation class and preloads it with variables
''' </summary>
''' <param name="iFrameCount">Number of frames in this animation as Integer</param>
''' <param name="iFrameInterval">Frame transition speed (milliseconds) as Integer</param>
''' <param name="clsFrameSet">Frame information as clsFrame</param>
''' <param name="imgImage">The picturebox that we're animating as PictureBox</param>
''' <remarks></remarks>
Public Sub Initialise(ByVal iFrameCount As Integer, ByVal iFrameInterval As Integer, ByVal clsFrameSet As clsFrame, ByRef imgImage As PictureBox, ByRef timTimer As Timer)
FrameCount = iFrameCount
FrameInterval = iFrameInterval
FrameSet = clsFrameSet
Image = imgImage
Timer = timTimer
Timer.Interval = FrameInterval
End Sub
However, both oTree1_Animation and oTree2_Animation share the same variables. I don't know if I'm missing something small or if I'm just trying to use classes in an impossible way, any help on this would be much appreciated.
You have to remove Shared from all your class variables. Shared means that the values are shared between all instances of this class.
This is normally referred to as "static" (but for some reason VB.NET uses a different terminology).
See MSDN for Microsoft explanation (thanks benjineer).

ConnectionString In Class Library

I'm merging two of my projects that can share a lot of the same classes into one solution with two web applications and a shared class library.
I've literally just dumped all the classes into the class library project and as expected I've got a ton of errors to fix. My main problem at the moment is the connection string. Currently I have this (which is obviously not going to work):
''' <summary>
''' Initialise the data access layer by loading the database connection string from the Web.Config file
''' </summary>
''' <remarks></remarks>
Shared Sub New()
_connectionString = WebConfigurationManager.ConnectionStrings("ClientFamilyManagementConnectionString").ConnectionString
End
What do I do with my connection strings now the classes are not in a web application?
I really feel like I'm missing something here so below I'm include an example of a class from the BLL and DAL. I can't pass in the connection string to the constructor of the DAL - it says it can't have any parameters.
BLL:
Imports Microsoft.VisualBasic
Imports System.Collections.Generic
Namespace CompanyName
<Serializable()> Public Class SubMarketSector
Private _id As Integer
Private _name As String
Public Property ID() As Integer
Get
Return _id
End Get
Set(ByVal value As Integer)
_id = value
End Set
End Property
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Public Sub New()
End Sub
Public Shared Function GetAllSubMarketSectors() As List(Of CompanyName.SubMarketSector)
Dim newSubMarketSectorDAO As New SubMarketSectorDAO
Return newSubMarketSectorDAO.GetAllSubMarketSectors
End Function
Public Shared Function GetSubMarketSectorByID(ByVal subMarketSectorID As Integer) As CompanyName.SubMarketSector
Dim newSubMarketSectorDAO As New SubMarketSectorDAO
Return newSubMarketSectorDAO.GetSubMarketSectorByID(subMarketSectorID)
End Function
End Class
End Namespace
DAL:
Namespace CompanyName
Public Class SubMarketSectorDAO
Private Const MainSelectByStatement As String = "SELECT ID, Name FROM Sub_Market_Sectors"
Private Const MainOrderByStatement As String = " ORDER BY Name"
Private Shared ReadOnly _connectionString As String = String.Empty
''' <summary>
''' Initialise the data access layer by loading the database connection string from the Web.Config file
''' </summary>
''' <remarks></remarks>
Shared Sub New()
_connectionString = WebConfigurationManager.ConnectionStrings("PursuitsConnectionString").ConnectionString
End Sub
''' <summary>
''' Returns a List of all Sub Market Sectors
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Function GetAllSubMarketSectors() As List(Of CompanyName.SubMarketSector)
' Create the Connection
Dim currentConnection As SqlConnection = New SqlConnection(_connectionString)
' Create the Command Object, set the CommandText, add any required Parameters and set the Connection
Dim currentCommand As New SqlCommand
currentCommand.CommandText = MainSelectByStatement & MainOrderByStatement
currentCommand.Connection = currentConnection
Dim listOfSubMarketSectors As New List(Of CompanyName.SubMarketSector)
Using currentConnection
' Open the Connection
currentConnection.Open()
' Create the DataReader and Execute the Command
Dim currentDataReader As SqlDataReader = currentCommand.ExecuteReader()
' Populate the list with data
Do While currentDataReader.Read
Dim newSubMarketSector As CompanyName.SubMarketSector = PopulateSubMarketSector(currentDataReader)
listOfSubMarketSectors.Add(newSubMarketSector)
Loop
End Using
Return listOfSubMarketSectors
End Function
''' <summary>
''' Return a single Sub Market Sector
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Function GetSubMarketSectorByID(ByVal subMarketSectorID As Integer) As CompanyName.SubMarketSector
' Create the Connection
Dim currentConnection As SqlConnection = New SqlConnection(_connectionString)
' Create the Command Object, set the CommandText, add any required Parameters and set the Connection
Dim currentCommand As New SqlCommand
currentCommand.CommandText = MainSelectByStatement & " WHERE ID = #subMarketSectorID" & MainOrderByStatement
currentCommand.Parameters.AddWithValue("#subMarketSectorID", subMarketSectorID)
currentCommand.Connection = currentConnection
Dim newSubMarketSector As New CompanyName.SubMarketSector
Using currentConnection
' Open the Connection
currentConnection.Open()
' Create the DataReader and Execute the Command
Dim currentDataReader As SqlDataReader = currentCommand.ExecuteReader()
' Populate the Market Sector
Do While currentDataReader.Read
newSubMarketSector = PopulateSubMarketSector(currentDataReader)
Loop
End Using
Return newSubMarketSector
End Function
Private Function PopulateSubMarketSector(ByVal currentDataReader As SqlDataReader) As CompanyName.SubMarketSector
Dim newSubMarketSector As New CompanyName.SubMarketSector
If Not (currentDataReader.IsDBNull(currentDataReader.GetOrdinal("ID"))) Then
newSubMarketSector.ID = currentDataReader("ID")
End If
If Not (currentDataReader.IsDBNull(currentDataReader.GetOrdinal("Name"))) Then
newSubMarketSector.Name = currentDataReader("Name")
End If
Return newSubMarketSector
End Function
End Class
End Namespace
How about storing the ConnectionStrings in each WebApplication and passing the specific connection string to the class library on instanciation. I don't know how you designed the class library (static methods, singleton, or whatever) but maybe you could pass them (each ConnectionString) to the constructor to set an instance variable which you later use (the same way like before).