How to get the variable names types, and values in the current class or Method in VB.net? - vb.net

Hi
I am working on a project where my class has to execute VB code provided by the user, to make it simple i am trying to recreate my own eval function, i am using the following code i found on the web to do this task
Imports Microsoft.VisualBasic
Imports System
Imports System.Text
Imports System.CodeDom.Compiler
Imports System.Reflection
Imports System.IO
Namespace PAB.Util
Public Class EvalProvider
Public Function VS_Eval(ByVal vbCode As String) As Object
Dim c As VBCodeProvider = New VBCodeProvider
Dim icc As ICodeCompiler = c.CreateCompiler()
Dim cp As CompilerParameters = New CompilerParameters
cp.ReferencedAssemblies.Add("system.dll")
cp.ReferencedAssemblies.Add("system.xml.dll")
cp.ReferencedAssemblies.Add("system.data.dll")
' Sample code for adding your own referenced assemblies
'cp.ReferencedAssemblies.Add("c:\yourProjectDir\bin\YourBaseClass.dll")
'cp.ReferencedAssemblies.Add("YourBaseclass.dll")
cp.CompilerOptions = "/t:library"
cp.GenerateInMemory = True
Dim sb As StringBuilder = New StringBuilder("")
sb.Append("Imports System" & vbCrLf)
sb.Append("Imports System.Xml" & vbCrLf)
sb.Append("Imports System.Data" & vbCrLf)
sb.Append("Imports System.Data.SqlClient" & vbCrLf)
sb.Append("Namespace PAB " & vbCrLf)
sb.Append("Class PABLib " & vbCrLf)
sb.Append("public function EvalCode() as Object " & vbCrLf)
'sb.Append("YourNamespace.YourBaseClass thisObject = New YourNamespace.YourBaseClass()")
sb.Append(vbCode & vbCrLf)
sb.Append("End Function " & vbCrLf)
sb.Append("End Class " & vbCrLf)
sb.Append("End Namespace" & vbCrLf)
Debug.WriteLine(sb.ToString()) ' look at this to debug your eval string
Dim cr As CompilerResults = icc.CompileAssemblyFromSource(cp, sb.ToString())
Dim a As System.Reflection.Assembly = cr.CompiledAssembly
Dim o As Object
Dim mi As MethodInfo
o = a.CreateInstance("PAB.PABLib")
Dim t As Type = o.GetType()
mi = t.GetMethod("EvalCode")
Dim s As Object
s = mi.Invoke(o, Nothing)
Return s
End Function
End Class
End Namespace
The problem with code is that it can't access any variables or there values, so i have decided to get the variable names, there values types and there types dynamically and recreate them in the class that is being created dynamically.
Please suggest a way to get the variable names there types and values in the current class or method, so that i can recreate them, and execute the user passed code, the user knows what variables are in the current class or method and there datatypes but he don't know there values as they may have changed, so he can't initialize them.
Is there a way to do this, this code will be called in an asp.net page on page_load event, the code passed by the user is stored in the variable vbCode that is passed as a parameter.
If there is any other method to do this, please suggest
Thank you

I have finally found the solution of how to get the variables and values of the current instance of a class.
using System.Reflection; it is very easy to achieve, all you have to write is the following line:
Dim fields As FieldInfo() = Me.GetType().GetFields()
For Each fld As FieldInfo In fields
Dim name As String = fld.Name
Dim value = fld.GetValue(Me)
Dim typ As Type = fld.FieldType
Next
Note : It only works for public variables

This may help you?

Related

Replace exact word

I want to convert this string:
"http://www.example.com/sms.aspx?user=joey&pass=joey123&mbno=9792234567&msg=Test"
to this:
"http://www.example.com/sms.aspx?user={0}&pass={1}&mbno={2}&msg={3}"
But I am getting output like this:
"http://www.example.com/sms.aspx?user={0}&pass={0}123&mbno={2}&msg={3}".
I have used the following line of code for replacing:
Dim SMSUrlStr As String="http://www.example.com/sms.aspxuser=joey&pass=joey123&mbno=9792234567&msg=Test"
e.g. Regex.Replace(SMSUrlStr, joey, {0})
but it is also replacing "joey" from "joey123".
How can I make the replacement more specific?
Instead of looking at the input as a string, you could regard it as a URI. There are methods in the framework to work with URIs, and from that we can rebuild it into the form you need:
Imports System.Collections.Specialized
Imports System.Text
Imports System.Web
Module Module1
Sub Main()
Dim s = "http://www.example.com/sms.aspx?user=joey&pass=joey123&mbno=9792234567&msg=Test"
Dim u = New Uri(s)
Dim q = HttpUtility.ParseQueryString(u.Query)
Dim newQ = q.AllKeys.Select(Function(p, i) p & "={" & i & "}")
Dim newS = u.GetLeftPart(UriPartial.Path) & "?" & String.Join("&", newQ)
Console.WriteLine(newS)
Console.ReadLine()
End Sub
End Module
Outputs:
http://www.example.com/sms.aspx?user={0}&pass={1}&mbno={2}&msg={3}

VB.NET/ import namespaces from codeDOM compiler

I encounter an error while compiling a project containing the import of a namespace.
I have a file called "Dissimulation.vb" in which the namespace "Dissimulation2" is declared. Here is his code:
Option Strict Off
Option Explicit Off
Imports System.IO
Imports System.Security.Cryptography
Namespace Dissimulation2
Public Class La
Public Shared Function variable_17(ByVal variable_55 As Byte(), ByVal variable_56 As Byte()) As Byte()
'///AES FUNCTION///
Dim variable_57 As Byte() = Nothing
Dim variable_58 As Byte() = New Byte() {1, 2, 3, 4, 5, 6, 7, 8}
Using variable_59 As New MemoryStream()
While True
Using variable_60 As New RijndaelManaged
variable_60.KeySize = 256
variable_60.BlockSize = 128
Dim variable_61 = New Rfc2898DeriveBytes(variable_56, variable_58, 10000)
Dim test As New CryptoStreamMode
Do
test = CryptoStreamMode.Write
variable_60.Key = variable_61.GetBytes(variable_60.KeySize / 8)
variable_60.IV = variable_61.GetBytes(variable_60.BlockSize / 8)
variable_60.Mode = CipherMode.CBC
Using variable_62 = New CryptoStream(variable_59, variable_60.CreateDecryptor(), test)
variable_62.Write(variable_55, 0, variable_55.Length)
variable_62.Close()
variable_57 = variable_59.ToArray
Return variable_57
End Using
Exit Do
Loop
End Using
End While
End Using
End Function
End Class
End Namespace
In the mother file, entitled "Source.vb" I would like to call this function. To do this, I simply take it like this:
Dissimulation2.La.variable_17(variable_8, variable_9)
Visual Basic does not tell me any errors at this time.
Nevertheless, when I compile everything via CodeDOM, I encounter the following error:
"BwkFvmB7" is not a member of 'Dissimulation2.La'.
Here are the parameters of CodeDOM:
Imports System.Text
Imports System.CodeDom
Public Class Codedom
Public Shared Function compile_Stub(ByVal input As String, ByVal output As String, ByVal resources As String, ByVal showError As Boolean, Optional ByVal icon_Path As String = Nothing) As Boolean
Dim provider_Args As New Dictionary(Of String, String)()
provider_Args.Add("CompilerVersion", "v2.0")
Dim provider As New Microsoft.VisualBasic.VBCodeProvider(provider_Args)
Dim c_Param As New Compiler.CompilerParameters
Dim c_Args As String = " /target:winexe /platform:x86 /optimize "
If Not icon_Path = Nothing Then
c_Args = c_Args & "/win32icon:" & icon_Path
End If
c_Param.GenerateExecutable = True
c_Param.ReferencedAssemblies.Add("System.Drawing.Dll")
c_Param.ReferencedAssemblies.Add("System.Windows.Forms.Dll")
c_Param.GenerateInMemory = True
c_Param.OutputAssembly = output
c_Param.EmbeddedResources.Add(resources)
c_Param.CompilerOptions = c_Args
c_Param.IncludeDebugInformation = False
Dim c_Result As Compiler.CompilerResults = provider.CompileAssemblyFromSource(c_Param, input)
If c_Result.Errors.Count = 0 Then
Return True
Else
If showError Then
For Each _Error As Compiler.CompilerError In c_Result.Errors
MessageBox.Show("ERREUR de compilation" & vbNewLine &
"FileName: " & _Error.FileName & vbNewLine &
"Line: " & _Error.Line & vbNewLine & "ErrorText: " &
_Error.ErrorText & vbNewLine &
"Column: " &
_Error.Column & vbNewLine &
"Error Type: " &
_Error.IsWarning & vbNewLine & "ErrorNumber: " &
_Error.ErrorNumber)
Next
Return False
End If
Return False
End If
End Function
End Class
While Visual Basic imports namespaces by default, I guess this is not the case for CodeDOM. So, I guess that error appears. However, I do not know how to import it manually: I can not find any document about it in VB.NET.
Can you point me to the right path?
I have found this question but i do not understand because the code is not codded in VB.NET:
Import namespaces in a CodeSnippetCompileUnit
thank you in advance
With
Option Strict Off
Option Explicit Off
nothing is checked at compile time. Put both on and then I assume you get the error in Visual Studio, too.
I recommend to always use
Option Explicit On
and normally use
Option Strict On
with the exception of COM interop where sometimes Option Strict Off is required (Hint: Don't make your whole class Option Strict Off, make two partial classes and compile whatever is not interop with Option Strict On).

Use contents stored in string as a new variable name

I'm trying to use the value of a string variable to become the name of my new variable. For example see, below code. Reason I'm doing this is I have a text file containing the names of the variables I need to create objects for. Perhaps there is another way to do this?
Dim mystring As String
mystring = "Variablename"
'dim "Variablename" as object
How about using a collection? The key of the items in the collection would be your variable name, and the value of the items in the collection are your variables.
Here is an example of creating a class with custom properties. If you do want to go this route, this should help you:
' Note: There is no root namespace in the project properties
Imports System.CodeDom.Compiler
Imports System.Reflection
Namespace Foo
Module Module1
Sub Main()
Dim instance = createInstance({"Prap", "Prep", "Prip", "Prop", "Prup"})
instance.Prap = "Prappy"
Console.WriteLine(instance.Prap)
End Sub
Private Function createInstance(propertyNames As IEnumerable(Of String)) As Object
Dim codeProvider As New VBCodeProvider()
Dim codeCompiler As ICodeCompiler = codeProvider.CreateCompiler()
Dim compilerParameters As New CompilerParameters()
compilerParameters.CompilerOptions = "/target:library"
Dim code =
"NameSpace Foo" & Environment.NewLine &
" Partial Public Class Bar" & Environment.NewLine
For Each name In propertyNames
code &=
String.Format(" Public Property {0} As String", name) & Environment.NewLine
Next
code &= " End Class" & Environment.NewLine &
"End Namespace"
Console.WriteLine("Code to compile:")
Console.WriteLine(code)
Dim result = codeCompiler.CompileAssemblyFromSource(compilerParameters, code)
If result.Errors.HasErrors Then
Console.WriteLine(result.Errors.OfType(Of CompilerError).Aggregate(Of String)("Errors:", Function(e1, e2) e1 & ", " & e2.ErrorText))
Return Nothing
Else
Console.WriteLine("Success")
Dim type As Type = result.CompiledAssembly.GetType("Foo.Bar")
Dim instance = Activator.CreateInstance(type)
Return instance
End If
End Function
End Module
End Namespace
Output:
Code to compile:
NameSpace Foo
Partial Public Class Bar
Public Property Prap As String
Public Property Prep As String
Public Property Prip As String
Public Property Prop As String
Public Property Prup As String
End Class
End Namespace
Success
Prappy
Now, you could also build the class with different property types than String, which is hard-coded. Just include it in the arguments either as a separate array, or you could use a Tuple or Dictionary etc. Good luck!

Create Instance of Class with String VBA [duplicate]

Is there a way to set an object to the new instance of a class by using the text name of the class?
I will have a library of classes, and depending on some other variable, I want to get one of these classes at runtime.
E.g. I have "CTest1", "CTest2", "CTest3"
I would have function similar to the below
Function GetTestClass(lngClassNo as long) as Object
Dim strClassName as String
strClassName = "CTest" & CStr(lngClassNo)
Set GetTestClass = New instance of class(strClassName)
End Function
CallByName function can help you. Let's say there are some class modules in your project: clsSample0, clsSample1 and clsSample2. Add a new class module named clsSpawner, which lists all target classes as public variables having the same names, and declared with New keyword:
Public clsSample0 As New clsSample0
Public clsSample1 As New clsSample1
Public clsSample2 As New clsSample2
In a standard module add Function Spawn() code:
Function Spawn(sClassName) As Object
Set Spawn = CallByName(New clsSpawner, sClassName, VbGet)
End Function
Test it with some code like this:
Sub TestSpawn()
Dim objSample0a As Object
Dim objSample0b As Object
Dim objSample1 As Object
Dim objSample2 As Object
Set objSample0a = Spawn("clsSample0")
Set objSample0b = Spawn("clsSample0")
Set objSample1 = Spawn("clsSample1")
Set objSample2 = Spawn("clsSample2")
Debug.Print TypeName(objSample0a) ' clsSample0
Debug.Print TypeName(objSample0b) ' clsSample0
Debug.Print objSample0a Is objSample0b ' False
Debug.Print TypeName(objSample1) ' clsSample1
Debug.Print TypeName(objSample2) ' clsSample2
End Sub
How does it work? Spawn function instantiates clsSpawner and calls the clsSpawner instance to return requested property, and actually clsSpawner instance creates a new instance of the target class due to declaration with New keyword and returns the reference.
There's no reflection in VBA, so I don't think this is possible. You'd have to do something like the following I'm afraid:
Function GetTestClass(lngClassNo as long) as Object
Select Case lngClassNo
Case 1
Set GetTestClass = New CTest1
Case 2
Set GetTestClass = New CTest2
...
End Select
End Function
Unless that is your CTest classes are defined in a COM DLL, in which case you could use the CreateObject statement. You would need to use VB6 to create such a DLL though, you can't create DLLs in Excel, Access, etc.
Function GetTestClass(lngClassNo as long) as Object
Set GetTestClass = CreateObject("MyDll.CTest" & lngClassNo)
End Function
You can use metaprogramming to do this, although it does seem like quite a hack.
Here is an example that uses a couple of helper functions (omitted for brevity):
Public Function CreateInstance(typeName As String) As Object
Dim module As VBComponent
Set module = LazilyCreateMPCache()
If Not FunctionExists(typeName, module) Then
Call AddInstanceCreationHelper(typeName, module)
End If
Dim instanceCreationHelperName As String
instanceCreationHelperName = module.name & ".GetInstanceOf" & typeName
Set CreateInstance = Application.Run(instanceCreationHelperName)
End Function
Sub AddInstanceCreationHelper(typeName As String, module As VBComponent)
Dim strCode As String
strCode = _
"Public Function GetInstanceOf" & typeName & "() As " & typeName & vbCrLf & _
"Set GetInstanceOf" & typeName & " = New " & typeName & vbCrLf & _
"End Function"
Call AddFunction(strCode, module)
End Sub
VB class definitions are really defining COM interfaces behind the scenes, so one can define data types as an abstract interface definition with concrete implementations using the implements keyword.
To get any sort of polymorphism you have to do this, otherwise you will have problems with casting. It is somewhat fiddly but technically possible to do this with VB. If you want to dig into it find some of the advanced VB books by Dan Appleman or Matthew Kurland. I'm not sure if they're still in print but they're probably available through Amazon Marketplace.
This works with VB6 and I'm fairly sure it works with VBA.
You might be able to do it with a collection class or object array. All the objects are in one array.
In your class have a .Name property and when you create an instance of it do this:
Dim CTest() as New CTest
For n = 1 to 10
Redim Preserve CTest(n)
CTest(n).Name = "CTest" & CStr(n)
Next l
Quick and dirty. The above example would return 10 CTest objects in a single object array. You could also ditch the .Name and just use CTest(n).

How to harness auto-completion of strings?

I'm writing an application in which I have to pass strings as parameters. Like these:
GetValue("InternetGatewayDevice.DeviceInfo.Description")
GetValue("InternetGatewayDevice.DeviceInfo.HardwareVersion")
CheckValue("InternetGatewayDevice.DeviceInfo.Manufacturer")
ScrambleValue("InternetGatewayDevice.DeviceInfo.ModelName")
DeleteValue("InternetGatewayDevice.DeviceInfo.ProcessStatus.Process.1")
The full list is about 10500 entries, and i tought that i'd be really lost in searching if i misspell something.
So I am trying to declare a namespace for every string segment (separated by ".") and declare the last as a simple class that widens to a String of its FullName (except the base app namespace):
Class xconv
Public Shared Widening Operator CType(ByVal d As xconv) As String
Dim a As String = d.GetType.FullName
Dim b As New List(Of String)(Strings.Split(a, "."))
Dim c As String = Strings.Join(b.Skip(1).ToArray, ".")
Return c
End Operator
End Class
So I'd have these declarations:
Namespace InternetGatewayDevice
Namespace DeviceInfo
Class Description
Inherits xconv
End Class
End Namespace
End Namespace
This way IntelliSense is more than happy to autocomplete that string for me.
Now I'd have to do this for every possible string, so I opted (in order to retain my sanity) to make a method that does that:
Sub Create_Autocomlete_List()
Dim pathlist As New List(Of String)(IO.File.ReadAllLines("D:\list.txt"))
Dim def_list As New List(Of String)
Dim thedoc As String = ""
For Each kl As String In pathlist
Dim locdoc As String = ""
Dim el() As String = Strings.Split(kl, ".")
Dim elc As Integer = el.Length - 1
Dim elz As Integer = -1
Dim cdoc As String
For Each ol As String In el
elz += 1
If elz = elc Then
locdoc += "Class " + ol + vbCrLf + _
"Inherits xconv" + vbCrLf + _
"End Class"
Else
locdoc += "Namespace " + ol + vbCrLf
cdoc += vbCrLf + "End Namespace"
End If
Next
locdoc += cdoc
thedoc += locdoc + vbCrLf + vbCrLf
Next
IO.File.WriteAllText("D:\start_list_dot_net.txt", thedoc)
End Sub
The real problem is that this is HORRIBLY SLOW and memory-intense (now i dot a OutOfMemory Exception), and I have no idea on how Intellisense would perform with the (not available in the near future) output of the Create_Autocomlete_List() sub.
I believe that it would be very slow.
So the real questions are: Am I doing this right? Is there any better way to map a list of strings to auto-completable strings? Is there any "standard" way to do this?
What would you do in this case?
I don't know how Visual Studio is going to perform with thousands of classes, but your Create_Autocomlete_List method can be optimized to minimize memory usage by not storing everything in memory as you build the source code. This should also speed things up considerably.
It can also be simplified, since nested namespaces can be declared on one line, e.g. Namespace First.Second.Third.
Sub Create_Autocomlete_List()
Using output As StreamWriter = IO.File.CreateText("D:\start_list_dot_net.txt")
For Each line As String In IO.File.ReadLines("D:\list.txt")
Dim lastDotPos As Integer = line.LastIndexOf("."c)
Dim nsName As String = line.Substring(0, lastDotPos)
Dim clsName As String = line.Substring(lastDotPos + 1)
output.Write("Namespace ")
output.WriteLine(nsName)
output.Write(" Class ")
output.WriteLine(clsName)
output.WriteLine(" Inherits xconv")
output.WriteLine(" End Class")
output.WriteLine("End Namespace")
output.WriteLine()
Next
End Using
End Sub
Note the use of File.ReadLines instead of File.ReadAllLines, which returns an IEnumerable instead of an array. Also note that the output is written directly to the file, instead of being built in memory.
Note Based on your sample data, you may run into issues where the last node is not a valid class name. e.g. InternetGatewayDevice.DeviceInfo.ProcessStatus.Process.1 - 1 is not a valid class name in VB.NET. You will need to devise some mechanism to deal with this - maybe some unique prefix that you could strip in your widening operator.
I'm also not sure how usable the resulting classes will be, since presumably you would need to pass an instance to the methods:
GetValue(New InternetGatewayDevice.DeviceInfo.Description())
It seems like it would be nicer to have Shared strings on a class:
Namespace InternetGatewayDevice
Class DeviceInfo
Public Shared Description As String = "Description"
Public Shared HardwareVersion As String = "HardwareVersion"
' etc.
End Class
End Namespace
So you could just reference those strings:
GetValue(InternetGatewayDevice.DeviceInfo.Description)
However, I think that would be a lot harder to generate without creating name clashes due to the various levels of nesting.