Reference to non-shared member requires an object reference - vb.net

I am writing macro for Visual Studio in VB:
Imports EnvDTE
Imports EnvDTE80
Imports Microsoft.VisualBasic
Public Class C
Implements VisualCommanderExt.ICommand
Sub Run(DTE As EnvDTE80.DTE2, package As Microsoft.VisualStudio.Shell.Package) Implements VisualCommanderExt.ICommand.Run
FileNamesExample()
End Sub
Sub FileNamesExample()
Dim proj As Project
Dim projitems As ProjectItems
' Reference the current solution and its projects and project items.
proj = DTE.ActiveSolutionProjects(0)
projitems = proj.ProjectItems
' List the file name associated with the first project item.
MsgBox(projitems.Item(1).FileNames(1))
End Sub
End Class
And I got this after compilation:
Error (17,0): error BC30469: Reference to a non-shared member requires an object reference.
Have you any ideas what is wrong? I didn't develop in VB earlier and I just need instant help.

DTE is a type, as well as what you've given to the name of a parameter in your Run method. When used in this line:
proj = DTE.ActiveSolutionProjects(0)
It's using the type, rather than an instance of an object. You need to pass through the variable into your method:
Imports EnvDTE
Imports EnvDTE80
Imports Microsoft.VisualBasic
Public Class C
Implements VisualCommanderExt.ICommand
Sub Run(DTE As EnvDTE80.DTE2, package As Microsoft.VisualStudio.Shell.Package) Implements VisualCommanderExt.ICommand.Run
FileNamesExample(DTE)
End Sub
Sub FileNamesExample(DTE As EnvDTE80.DTE2)
Dim proj As Project
Dim projitems As ProjectItems
' Reference the current solution and its projects and project items.
proj = DTE.ActiveSolutionProjects(0)
projitems = proj.ProjectItems
' List the file name associated with the first project item.
MsgBox(projitems.Item(1).FileNames(1))
End Sub
End Class

Related

Get List of Functions and Subs in a class file

Is there a way to get a list of all of the Functions and Subs in Class file in Visual Studios? We're making extensive changes and I'd like to have a list in excel so I can use it to keep track of what I've already done.
I'd like to also get a list of every function/sub that references those subs/functions, but I can do that myself if necessary.
So, can this be done in Visual Studios?
Two options:
1. Programatically using Reflection and Type.GetMethods
See the MSDN page
Here is the example code on that page (I did not author this code, please see the link above)
Imports System
Imports System.Reflection
Imports System.Reflection.Emit
Imports Microsoft.VisualBasic
' Create a class having two public methods and one protected method.
Public Class MyTypeClass
Public Sub MyMethods()
End Sub 'MyMethods
Public Function MyMethods1() As Integer
Return 3
End Function 'MyMethods1
Protected Function MyMethods2() As [String]
Return "hello"
End Function 'MyMethods2
End Class 'MyTypeClass
Public Class TypeMain
Public Shared Sub Main()
Dim myType As Type = GetType(MyTypeClass)
' Get the public methods.
Dim myArrayMethodInfo As MethodInfo() = myType.GetMethods((BindingFlags.Public Or BindingFlags.Instance Or BindingFlags.DeclaredOnly))
Console.WriteLine((ControlChars.Cr + "The number of public methods is " & myArrayMethodInfo.Length.ToString() & "."))
' Display all the public methods.
DisplayMethodInfo(myArrayMethodInfo)
' Get the nonpublic methods.
Dim myArrayMethodInfo1 As MethodInfo() = myType.GetMethods((BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.DeclaredOnly))
Console.WriteLine((ControlChars.Cr + "The number of protected methods is " & myArrayMethodInfo1.Length.ToString() & "."))
' Display all the nonpublic methods.
DisplayMethodInfo(myArrayMethodInfo1)
End Sub 'Main
Public Shared Sub DisplayMethodInfo(ByVal myArrayMethodInfo() As MethodInfo)
' Display information for all methods.
Dim i As Integer
For i = 0 To myArrayMethodInfo.Length - 1
Dim myMethodInfo As MethodInfo = CType(myArrayMethodInfo(i), MethodInfo)
Console.WriteLine((ControlChars.Cr + "The name of the method is " & myMethodInfo.Name & "."))
Next i
End Sub 'DisplayMethodInfo
End Class 'TypeMain
2. With Visual Studio IDE and Code Metrics
Right click the Project
Calculate Code Metrics
In Code Metrics Results pane
Right click the Class
Open the Selection in Microsoft Excel
Might be an easier option.

Parse solution using DTE Visual Basic

I have to parse my solution to list all files that it is using.
I have created this:
Imports EnvDTE
Imports EnvDTE80
Imports Microsoft.VisualBasic
Imports System.Collections
Public Class C
Implements VisualCommanderExt.ICommand
Sub Run(DTE As EnvDTE80.DTE2, package As Microsoft.VisualStudio.Shell.Package) Implements VisualCommanderExt.ICommand.Run
listing(DTE)
End Sub
Sub listing(DTE As EnvDTE80.DTE2)
Dim prj As Project
Dim prjs As Projects
prjs = DTE.Solution.Projects
For Each prj In prjs
Dim item As String
Dim itemEnum as IEnumerator = prj.GetEnumerator()
itemEnum.Reset()
While itemEnum.MoveNext()
item = itemEnum.Current().FullName
My.Computer.FileSystem.WriteAllText("C:\tmp\list.txt", item, True)
End While
Next
End Sub
End Class
Unfortunately I encounter the Exception:
System.Runtime.InteropServices.COMExcption(0x80020003): Member not found....
My solution has 10 projects.
Navigating projects and files is recursive because they can be nested.
See my articles:
HOWTO: Navigate the files of a solution from a Visual Studio .NET macro or add-in.
HOWTO: Navigate the files of a solution using the IVsHierarchy interface from an add-in.
http://www.visualstudioextensibility.com/articles/add-ins/

Importing System.Linq solves "Class cannot be indexed" error

I have the following code which is failing to compile:
Dim ContentSent As HashSet(Of String) = New HashSet(Of String)(SubscriberContent.ContentIdSent.Split(","))
content = New Content
' The next line causes causing the error
content = contentCollection.Find(Function(x) x.CntId = Convert.ToInt32(ContentSent(0)))
The error it gets is:
Class cannot be indexed because it has no default property
That makes sense. The weird thing is, when I import System.Linq the compilation error disappears.
For a more simple example, this code gets the error:
Imports System
Imports System.Collections.Generic
Module MyModule
Public Sub Main()
Dim x As IEnumerable(Of Integer) = {0, 1, 2, 3}
Dim item As Integer = x(3) ' Gets error on this line
Console.WriteLine(item)
End Sub
End Module
But this works:
Imports System
Imports System.Collections.Generic
Imports System.Linq ' This is the only difference!
Module MyModule
Public Sub Main()
Dim x As IEnumerable(Of Integer) = {0, 1, 2, 3}
Dim item As Integer = x(3)
Console.WriteLine(item) ' Outputs "3"
End Sub
End Module
Why does importing the System.Linq namespace solve that particular error? It doesn't seem like it should.
Presumably the offending item is: ContentSent(0)
It appears that once you Import System.Linq, that VB automatically uses the extension method ElementAtOrDefault as a "pseudo default" indexer for ContentSent.
You can see this for yourself, if you backspace over the index and observe the Intellisense display.
#StevenDoggart's comment prompted me to revisit this issue. This behavior by VB is specified in the Visual Basic Language Specification.
This particular VB feature is known as the Default Query Indexer.
Default Query Indexer
Every queryable collection type whose element type is T and does not
already have a default property is considered to have a default
property of the following general form:
Public ReadOnly Default Property Item(index As Integer) As T
Get
Return Me.ElementAtOrDefault(index)
End Get
End Property
The default property can only be referred to using the default
property access syntax; the default property cannot be referred to by
name. For example:
Dim customers As IEnumerable(Of Customer) = ...
Dim customerThree = customers(2)
' Error, no such property
Dim customerFour = customers.Item(4)
If the collection type does not have an ElementAtOrDefault member, a
compile-time error will occur.

Error in VB Function in SSIS but not in VisualStudio

I have some VB.Net code that I'm trying to add to a SSIS package. The code runs fine in VisualStudio 2012 and when I compile it there are no errors and the executable runs as well. When I insert the code into an SSIS package Script Task and copy/paste my code in I'm getting an error in one of my functions:
'Any' is not a member of 'System.Collections.Generic.IEnumerable(Of String)'.
I copied and pasted the main body of the code exactly and inserted one Imports line:
#Region "Imports"
Imports System
Imports System.Data
Imports System.Math
Imports System.IO 'Added this one Imports
Imports Microsoft.SqlServer.Dts.Runtime
#End Region
<Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute()> _
<System.CLSCompliantAttribute(False)> _
Partial Public Class ScriptMain
Inherits Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
Public Sub Main()
Dim sDirectoryRoot As String = "\\Server\Drive$\SubFolder\SubFolder2\"
Dim dirList As New DirectoryInfo(sDirectoryRoot)
ListFiles(dirList)
Dts.TaskResult = ScriptResults.Success 'left this line from SSIS default
End Sub
Public Sub ListFiles(ByVal dirList As DirectoryInfo)
Dim bEmpty As Boolean = False
[...] bunch of code with no errors. Actual function call below where clientdir is a directory path
bEmpty = IsDirectoryEmpty(clientdir.ToString)
End Sub
Public Function IsDirectoryEmpty(ByVal path As String) As Boolean
Return Not (Directory.EnumerateFileSystemEntries(path).Any())
End Function
The Directory.EnumerateFileSystemEntries(path).Any is underlined with the 'Any' is not a member of 'System.Collections.Generic.IEnumerable(Of String)' error. I can't figure out why it runs fine in Visual Studio, but when I use the Edit Script button in the Script Task Editor (which starts another instance of Visual Studio) it errors out.
Any is an extension method and, as with all extension methods, requires that you import the appropriate namespace. It is, in fact, the System.Linq.Enumerable.Any method, hence you must import the System.Linq namespace. That's probably done at the project level by default in your non-SSIS project. Note that you will also have to have referenced the System.Core.dll assembly.

How to compile code Entered by the user?

I need to make a simple vb.net program that runs a piece of code entered by the user (also in vb.net). But I need my program to compile and run it.
Anyone have an idea on how to do this?
I actually wrote a blog post (link below) about this several years ago. The below example is from 2010, and there may be better methods to solve this problem today. More explanation can be found in the code comments.
Essentially:
Read the code from the file.
Create an instance of a VB.NET CodeProvider
Create compiler parameters and pass them to the compiler
Compile the code
Check for errors
Create an instance of the class containing the code
Create arguments to pass to our compiled code
Execute the code and see results
An example of this being utilized to execute code in a text file and displaying a result in a TextBox is below, but could easily be used to parse code from a Textbox. (more info at vbCity Blog):
Includes:
Imports System.IO
Imports System.Reflection
Imports System.CodeDom
Imports System.CodeDom.Compiler
Imports Microsoft.VisualBasic
Code:
' Read code from file
Dim input = My.Computer.FileSystem.ReadAllText("Code.txt")
' Create "code" literal to pass to the compiler.
'
' Notice the <% = input % > where the code read from the text file (Code.txt)
' is inserted into the code fragment.
Dim code = <code>
Imports System
Imports System.Windows.Forms
Public Class TempClass
Public Sub UpdateText(ByVal txtOutput As TextBox)
<%= input %>
End Sub
End Class
</code>
' Create the VB.NET Code Provider.
Dim vbProv = New VBCodeProvider()
' Create parameters to pass to the compiler.
Dim vbParams = New CompilerParameters()
' Add referenced assemblies.
vbParams.ReferencedAssemblies.Add("mscorlib.dll")
vbParams.ReferencedAssemblies.Add("System.dll")
vbParams.ReferencedAssemblies.Add("System.Windows.Forms.dll")
vbParams.GenerateExecutable = False
' Ensure we generate an assembly in memory and not as a physical file.
vbParams.GenerateInMemory = True
' Compile the code and get the compiler results (contains errors, etc.)
Dim compResults = vbProv.CompileAssemblyFromSource(vbParams, code.Value)
' Check for compile errors
If compResults.Errors.Count > 0 Then
' Show each error.
For Each er In compResults.Errors
MessageBox.Show(er.ToString())
Next
Else
' Create instance of the temporary compiled class.
Dim obj As Object = compResults.CompiledAssembly.CreateInstance("TempClass")
' An array of object that represent the arguments to be passed to our method (UpdateText).
Dim args() As Object = {Me.txtOutput}
' Execute the method by passing the method name and arguments.
Dim t As Type = obj.GetType().InvokeMember("UpdateText", BindingFlags.InvokeMethod, Nothing, obj, args)
End If