VB comments in completion box - vb.net

I do not understand vb comments. I read that it is possible to give a hint in the code completion box when someone (or myself?) is using my code. But I do not find something in visual studio. Could you please give a short explanation how to get there.

Read this MSDN magazine article about code comments to understand how these work.
And here is the how to.
Simply put, you use three ' characters with the XML comments for them to be used as code comments ('''<summary>Summary of my code</summary>).

The feature you're asking about is called "XML Documentation Comments". This article discusses them from the VB.Net perspective. From the article:
XML comments for Visual Basic were first introduced in Visual Studio 2005. They can be used to create a documentation file for the project, and provide a rich development environment experience for yourself, your teammates, or other people using your code.
Example:
''' <summary>
''' This function does really cool stuff.
''' </summary>
''' <param name="foo">The foo to work with</param>
''' <returns>True on success, False on error</return>
Public Function CoolThing(ByVal foo As String) As Boolean
' ...
End Function
...but don't worry, you don't have to type those summary, param, returns things yourself, the editor helps you with them.

this is easy, you need only three times the commment marker, like: '''

Try this code to see how it works / looks
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
IllustrateComments() 'fix this statement to see how this works
Dim foo As Boolean = IllustrateFunctionComment() 'fix
End Sub
' Most of this is Auto Generated by typing three '
''' <summary>
''' This is the summary block
''' </summary>
''' <param name="firstParameter">This is a description of the first parameter</param>
''' <param name="secondParameter">This is a description of the optional second parameter</param>
''' <remarks>*** Some remarks ***</remarks>
''' <exception cref="Exception"> *** Exception here ***</exception>
Private Sub IllustrateComments(ByVal firstParameter As String, _
Optional ByVal secondParameter As Integer = 0)
'
Try
Catch ex As Exception
End Try
End Sub
''' <summary>
''' Summarry of the function. Note the return
''' </summary>
''' <param name="param1">param1 description</param>
''' <returns>returns a boolean - DUH!</returns>
''' <remarks>*** Some remarks - see object browser to see full benefit ***</remarks>
Private Function IllustrateFunctionComment(ByVal param1 As String) As Boolean
Return True
End Function
I found these two links helpful
http://msdn.microsoft.com/en-us/magazine/dd722812.aspx
http://msdn.microsoft.com/en-us/library/ms172653.aspx

Related

How do I reference a constructor in an XML comment in Visual Basic?

Let's say I have the following code:
Public Sub New(num As Integer)
End Sub
''' <summary>
''' <see cref=""/>
''' </summary>
Public Sub DoSomething()
End Sub
What do I put between the quotation marks in cref="" to reference New(Integer)?
New(Integer) will suffice:
''' <summary>
''' <see cref="New(Integer)">
''' </summary>
Public Sub DoSomething()
End Sub
If you hover over it, you will see that Visual Studio interprets it correctly:

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

GetFields Operator

I'm using a function to get the field data from a list. I need to know if the .GetFields operator will just return the fields, or if it will actually populate them with the data stored there? I think it's the latter after reading on msdn but I have no clue and I've never used these "test methods" before :(. Any help is appreciated! (or even if you can tell me how to do a test method would help!)
Here is code:
''' <summary>
''' This function will return all of the fields for a certain class as well as the data stored in them
''' </summary>
''' <param name="list"></param>
''' <returns></returns>
Public Shared Function GetFieldData(ByVal list As IList(Of Object)) As FieldInfo()
Dim fields() As FieldInfo = list.Single.GetType().GetFields()
Return fields
End Function
End Class
Here is code for creating newitems
''' <summary>
''' This function will create new Before and After objects
''' everything should be passed in as a IEnum
''' </summary>
''' <param name="Before"></param>
''' <param name="After"></param>
''' <returns></returns>
Function NewItem(Before As IEnumerable(Of Object), After As IEnumerable(Of Object))
If (Not ObjectsAreSameClass(Before, After)) Then 'If object classes are not the same, send an error message, else continue
'Pop error
Else
Dim BeforeFields() As FieldInfo = GetFieldData(Before)
Dim AfterFields() As FieldInfo = GetFieldData(After)
ObjectCounter += 1
'Now check and make sure the objects are not the same
If (BeforeFields.Equals(AfterFields)) Then
'Objects are the same so pop error?
End If
End If
Return Nothing
End Function
FieldInfo is information about the field, not including its value. To get the value you have to provide an instance of that object type. Here's an example that you can place on a form to see how it works:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim info() As FieldInfo = GetType(Form).GetFields(BindingFlags.NonPublic Or
BindingFlags.Instance)
For Each item As FieldInfo In info
Dim value As Object = item.GetValue(Me) ' Get the value from 'Me'
If Not IsNothing(value) Then
Debug.Print("{0} = {1}", item.Name, value.ToString())
Else
Debug.Print("{0} = Nothing", item.Name)
End If
Next
End Sub

vb.net: Using <see> Tags in XML comments

I'm trying to improve my code documentation and decided to give XML-comments a closer look.
If I'm not mistaken, the <see> Tag should allow to create clickable "links" or references to other parts of code.
pE:
Public Class Form1
''' <summary>
''' Does something.
''' </summary>
Private Sub aaa()
'do something
End Sub
''' <summary>
''' Calls <see cref="aaa"/>.
''' </summary>
Private Sub bbb()
aaa()
End Sub
End Class
This should create a clickable link to "aaa" in the documentation of "bbb".
The xml-code compiles just fine, intellisense even helps with choosing what to reference, but in the objectbrowser it doesn't work! The reference to "aaa" is just dead text.
Do you have any ideas on this? It would be a neat little feature if it worked.
(I'm working on VS2010Premium)
In Visual Studio 2015, the see tag will cause hyperlinks to referenced objects to appear within IntelliSense views. Object Viewer support for these hyperlinks is still missing.
The following example demonstrates the IntelliSense functionality (vb.net):
''' <summary>
''' Gets the collection of source XML files. See <see cref="imbuddy.Collections.ImeXmlFilePaths"/>
''' </summary>
''' <returns>See <see cref="imbuddy.Collections.ImeXmlFilePaths"/></returns>
Public ReadOnly Property SourceXmlFiles As Collections.ImeXmlFilePaths

VB.NET embedded DLL in another DLL as embedded resource?

I have seen this done in C#, such as here although, I cannot seem to figure out how to do this in VB.NET. For some background, I have created a custom ComboBox control as a .dll, and I need to implement it in another .dll(ArcMap Component).
Unfortunately, ArcMap does not allow for "third-party" DLL's to be loaded along with the component, because there is no option to reference any third-party assemblies for your add-in.
If someone could point me in the right direction, it would be more than appreciated.
We use this technique in VB.NET in Visual Studio 2008...
First, the project needs to know to include the "other" dll as an Embedded Resource. In the Solution Explorer, add the dll as a file to your project (not as a Reference). Then, open the Properties for the file and set the Build Action to "Embedded Resource." It is recommended that you create a local copy of the dll file in the structure of your project rather than linking to some other location. Once the project includes the dll file, you can then add the reference to that copy of the dll so that you can use its contents at design-time.
That ensures that the "other" dll is included in your compiled dll, but it doesn't make it automatically load when needed. That's where the following code comes in:
Public Module Core
Private _initialized As Boolean
Public Sub EnsureInitialized()
If Not _initialized Then
AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf AssemblyResolve
_initialized = True
End If
End Sub
Private Function AssemblyResolve(ByVal sender As Object, ByVal e As ResolveEventArgs) As Assembly
Dim resourceFullName As String = String.Format("[CONTAINER ASSEMBLY].{0}.dll", e.Name.Split(","c)(0))
Dim thisAssembly As Assembly = Assembly.GetExecutingAssembly()
Using resource As Stream = thisAssembly.GetManifestResourceStream(resourceFullName)
If resource IsNot Nothing Then Return Assembly.Load(ToBytes(resource))
Return Nothing
End Using
End Function
Private Function ToBytes(ByVal instance As Stream) As Byte()
Dim capacity As Integer = If(instance.CanSeek, Convert.ToInt32(instance.Length), 0)
Using result As New MemoryStream(capacity)
Dim readLength As Integer
Dim buffer(4096) As Byte
Do
readLength = instance.Read(buffer, 0, buffer.Length)
result.Write(buffer, 0, readLength)
Loop While readLength > 0
Return result.ToArray()
End Using
End Function
End Module
Place this Module somewhere in your project and be sure to call the EnsureInitialized method to attach the AssemblyResolve handler before calling any other code in your dll.
NOTE: You'll need to replace [CONTAINER ASSEMBLY] with the name of your dll.
Also note that the above code is a stripped-down version of what we actually use because ours includes log4net logging messages at strategic places. The logging messages aren't necessary for the true functionality, so I removed them for brevity and clarity.
The major caveat to this approach is that the AssemblyResolve handler has to be attached manually. Even if you can't set things up so that EnsureInitialized is called only once during the initialization of the consuming code, you can call EnsureInitialized within any of your own modules that require the "other" dll for execution. This makes the code a little more delicate because you have to remember to make that initialization call, but it does allow you to sleep at night knowing that the dll will be available when you need it.
In my experience, some "other" dlls don't play well when they are provided as embedded resources, so you might need to play around a bit to get things working.
Final Note: I've never used ArcMap Component, so Your Mileage May Vary!
I took a little bit of a different approach. I wanted something that would pretty much auto-initialize and dynamically load embedded assemblies as they were being used. I also wanted to avoid loading multiple instances of an assembly that already existed in the current AppDomain. The code below accomplishes all of those for me.
Imports System.Reflection
Imports System.Runtime.CompilerServices
''' <summary>
''' This class initializes a special AssemblyResolve handler for assemblies embedded in the current assembly's resources. <para/>
''' To auto initialize create a variable as a New EmbeddedAssemblyResolverClass in any class using an embedded assembly.
''' </summary>
Public Class EmbeddedAssemblyResolverClass
Implements IDisposable
''' <summary>
''' Initialization flag.
''' </summary>
''' <returns>[Boolean]</returns>
Public ReadOnly Property Initialized As Boolean
''' <summary>
''' Raised when successfully initialized.
''' </summary>
Public Event Initilized()
''' <summary>
''' Raised when successfully uninitialized.
''' </summary>
Public Event Uninitilized()
Sub New()
Try
If Not Initialized Then
AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf ResolveAppDomainAssemblies
Initialized = True
RaiseEvent Initilized()
End If
Catch ex As Exception
'Maybe some error logging in the future.
MsgBox(ex.Message)
End Try
End Sub
#Region "IDisposable Support"
Private disposedValue As Boolean ' To detect redundant calls
' IDisposable
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
RemoveHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf ResolveAppDomainAssemblies
_Initialized = False
RaiseEvent Uninitilized()
End If
End If
disposedValue = True
End Sub
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above.
Dispose(True)
End Sub
#End Region
End Class
Public Module EmbeddedAssemblyResolverModule
''' <summary>
''' Returns a dictionary of assemblies loaded in the current AppDomain by full name as key.
''' </summary>
''' <returns>[Dictionary(Of String, Assembly)]</returns>
Public ReadOnly Property AppDomainAssemblies As Dictionary(Of String, Assembly)
Get
Return AppDomain.CurrentDomain.GetAssemblies.ToDictionary(Function(a) a.FullName)
End Get
End Property
''' <summary>
''' Method that attempts to resolve assemblies already loaded to the current AppDomain.
''' </summary>
''' <param name="sender">[Object]</param>
''' <param name="args">[ResolveEventArgs]</param>
''' <returns>[Assembly]</returns>
Public Function ResolveAppDomainAssemblies(sender As Object, args As ResolveEventArgs) As Assembly
'Return the existing assembly if it has already been loaded into the current AppDomain.
If AppDomainAssemblies.ContainsKey(args.Name) Then Return AppDomainAssemblies.Item(args.Name)
'Build the potential embedded resource name.
Dim ResourceName As String = String.Format("{0}.{1}.dll", Assembly.GetExecutingAssembly().FullName.Split(",").First, args.Name.Split(",").First)
'Attempt to load the requested assembly from the current assembly's embedded resources.
Return Assembly.GetExecutingAssembly.LoadEmbeddedAssembly(ResourceName)
End Function
''' <summary>
''' Loads an assembly from the current assembly's embedded resources.
''' </summary>
''' <param name="CurrentAssembly">[Assembly] Current assembly which contains the embedded assembly.</param>
''' <param name="EmbeddedAssemblyName">[String] Full name of the embedded assembly.</param>
''' <returns>[Assembly]</returns>
<Extension>
Public Function LoadEmbeddedAssembly(CurrentAssembly As Assembly, EmbeddedAssemblyName As String) As Assembly
'Return the existing assembly if it has already been loaded into the current AppDomain.
If AppDomainAssemblies.ContainsKey(EmbeddedAssemblyName) Then Return AppDomainAssemblies.Item(EmbeddedAssemblyName)
'Attempt to load the requested assembly from the current assembly's embedded resources.
Using Stream = CurrentAssembly.GetManifestResourceStream(EmbeddedAssemblyName)
If Stream Is Nothing Then Return Nothing
Dim RawAssembly As [Byte]() = New [Byte](Stream.Length - 1) {}
Stream.Read(RawAssembly, 0, RawAssembly.Length)
Return Assembly.Load(RawAssembly)
End Using
End Function
End Module
The EmbeddedAssemblyResolverClass is used to create the actual AssemblyResolve event handler. I added a few bells and whistles by adding IDisposable support and events for Initialized and Uninitialized, but you can trim those off if not desired.
I created the rest of the code in the EmbeddedAssemblyResolverModule so that they would be global to my assembly and also because the LoadEmbeddedAssembly method is an Extension method, of which can only be created in Modules.
Now the only thing left to do is to create and instantiate the EmbeddedAssemblyResolverClass in any other class in your application that uses an assembly that is embedded into its resources.
'''' <summary>
'''' Used to auto initialize the EmbeddedAssemblyResolverClass.
'''' </summary>
Public WithEvents EAR As New EmbeddedAssemblyResolverClass
Once you call a method from an embedded resource it will first look to see if the assembly is already loaded in the current AppDomain, if it is then the assembly is returned. If the embedded assembly has not been loaded then it will be loaded dynamically from the embedded resources if it exists.
One thing that is nice about this code is that it works on assemblies that don't have an EntryPoint, like class libraries. Also I have had success in loading embedded assemblies with embedded assemblies which also used this code.