Imports System.Reflection
Imports System.Text
Module AssemblyScanner
'Questa classe è presa da quella che ho scritto per la guida
'http://totem.altervista.org/guida/versione2/index.php
'Funzione privata che restituisce lo specificatore di accesso di
'un membro.
'Questa funzione utilizza un meccanismo di late-binding per
'risparmiare spazio, seguendo il seguente ragionamento: non si puo'
'specificare come parametro un oggetto di tipo MemberInfo, poiche'
'questo non espone le proprita' che servono alla funzione; scrivere un
'overload di funzioni sprecherebbe troppo spazio; la conclusione
'logica piu' semplice e' quella di usare un parametro di tipo Object, che
'poiche' la funzione e' privata, sara' sempre dotato di tali proprieta'
'in quanto saremo noi stessi a passare parametri adeguati
Private Function GetScope(ByVal MI As Object) As String
Dim Scope As String
If MI.IsAssembly Then
Scope = "Friend"
ElseIf MI.IsFamily Then
Scope = "Protected"
ElseIf MI.IsFamilyOrAssembly Then
Scope = "Protected Friend"
ElseIf MI.IsPrivate Then
Scope = "Private"
ElseIf MI.IsPublic Then
Scope = "Public"
End If
Return Scope
End Function
'Determina se il membro ha un nome speciale. Ad esempio, si e' visto
'che la reflection considera Get e Set di una proprieta' come metodi
'a se' stanti. Percio' se c'e' una proprieta' A in una classe,
'la reflection enumerera' anche i metodi get_A e set_A, che corrispondono
'ai blocchi Get e Set. Per evitare questo, e altri problemi simili,
'si deve controllare che IsSpecialName sia false.
Private Function IsSpecialName(ByVal Member As Object) As Boolean
Return Member.IsSpecialName
End Function
'Funzione privata che restituisce il nome di un tipo. Utilizza una
'algoritmo ricorsivo, poiche' potrebbero anche esserci generic nidificati,
'come ad esempio:
'Dim A As New List(Of Dictionary(Of String, Of Nullable(Of Integer)))
'Non si sa mai...
Public Function GetTypeName(ByVal T As Type) As String
Dim CompleteName As New StringBuilder
If T.IsGenericType And T.Name.IndexOf("`") >= 0 Then
Dim Position As Int16 = 0
CompleteName.Append(T.Name.Remove(T.Name.IndexOf("`")))
CompleteName.Append("(Of ")
For Each GenArg As Type In T.GetGenericArguments
If Position > 0 Then
CompleteName.Append(", ")
End If
'Qui la funzione richiama se' stessa
CompleteName.Append(GetTypeName(GenArg))
Next
CompleteName.Append(")")
Else
CompleteName.Append(T.Name)
End If
Return CompleteName.ToString
End Function
Public Function GetTypeRelatedTypes(ByVal T As Type) As List(Of Type)
Dim Result As New List(Of Type)
If T.IsGenericType And T.Name.IndexOf("`") >= 0 Then
For Each GenArg As Type In T.GetGenericArguments
Result.Add(GenArg)
Next
Else
Result.Add(T)
End If
Return Result
End Function
'Analizza un tipo e ne restituisce il nome nella forma:
'[Scope] [Modifier] [Category] [Name]
'Ad esmpio:
'Public MustInherit Class Ciao
Public Function GetTypeInfo(ByVal T As Type) As String
Dim CompleteName As New StringBuilder
'Non si puo' utilizzare la funzione GetScope poiche' le proprieta'
'di Type inerenti all'ambito di visibilita' hanno nome diverso
If T.IsNested Then
'Scope di tipi nidificati
If T.IsNestedAssembly Then
CompleteName.Append("Friend ")
ElseIf T.IsNestedFamily Then
CompleteName.Append("Protected ")
ElseIf T.IsNestedFamORAssem Then
CompleteName.Append("Protected Friend ")
ElseIf T.IsNestedPrivate Then
CompleteName.Append("Private ")
ElseIf T.IsNestedPublic Then
CompleteName.Append("Public ")
End If
Else
'Scope di tipi non nidificati
If T.IsPublic Then
CompleteName.Append("Public ")
ElseIf T.IsNotPublic Then
'Dato che per i tipi non nidificati non si puo' sapere
'con precisione lo scope, se non sono pubblici si mette
'un generico NotPublic. Questa non e' una keyword del Vb.Net
CompleteName.Append("NotPublic ")
End If
End If
'Modificatori vari di ereditarieta' (valgono solo per le classi)
If T.IsClass Then
If T.IsAbstract Then
CompleteName.Append("MustInherit ")
ElseIf T.IsSealed Then
CompleteName.Append("NotInheritable ")
End If
End If
'Categoria del tipo
If T.IsClass Then
CompleteName.Append("Class ")
ElseIf T.IsEnum Then
CompleteName.Append("Enum ")
ElseIf T.IsInterface Then
CompleteName.Append("Interface ")
ElseIf T.IsValueType Then
'Dato che Type rappresenta un tipo, solo le struttura sono
'comprese nei tipi Value, poiche' non e' possibile definire
'tipi base come Char e String
CompleteName.Append("Structure ")
End If
CompleteName.Append(T.Name)
'Se e' una dichiarazione di un tipo generic, bisogna ricordarsi
'di aggiunge la signature degli argomenti generic.
'Attenzione! Type.IsGenericType e Type.IsGenericTypeDefinition
'sono proprieta' diverse! La prima specifica un tipo generic collegato
'mentre la seconda una definizione di tipo generic. Ad esempio:
'"Class Ciao(Of T1, T2)" oppure "Sub Ciao(Of K1, K2)"
'Sono definizioni di tipi generic, mentre
'"Dim A As List(Of Integer)" e "..., ByVal Elem1 As K1"
'Sono tipi generic collegati
If T.IsGenericTypeDefinition Then
Dim Position As Int16 = 0
CompleteName.Append("(Of ")
For Each GenArg As Type In T.GetGenericArguments
If Position > 0 Then
CompleteName.Append(", ")
End If
CompleteName.Append(GenArg.Name)
Position += 1
Next
CompleteName.Append(")")
End If
Return CompleteName.ToString
End Function
'Analizza un metodo e ne restituisce il nome nella forma:
'[Scope] (Shared) [Modifier] [Category] [Name][Signature]
'Ad esempio:
'Protected Overridable Sub Ciao(Of T)(ByVal A As List(Of T))
Public Function GetMethodInfo(ByVal MI As MethodInfo) As String
Dim CompleteName As New StringBuilder
'MethodInfo espone tutte le proprieta' necessarie, quindi e'
'lecito passarlo come parametro a GetScope
CompleteName.Append(" " & GetScope(MI) & " ")
'Se e' shared oppure no
If MI.IsStatic Then
CompleteName.Append("Shared ")
End If
'Modificatori
If MI.IsAbstract Then
CompleteName.Append("MustOverride ")
ElseIf MI.IsFinal Then
'Niente
ElseIf MI.IsVirtual Then
CompleteName.Append("Overridable ")
End If
'Categoria
If MI.ReturnType IsNot GetType(Void) Then
CompleteName.Append("Function ")
Else
CompleteName.Append("Sub ")
End If
'Se e'un costruttore oppure no
If MI.IsConstructor Then
CompleteName.Append("New")
Else
CompleteName.Append(MI.Name)
End If
'Signature
Dim Position As Int16 = 0
'Potrebbe essere un metodo generic
If MI.IsGenericMethodDefinition Then
CompleteName.Append("(Of ")
For Each GenArg As Type In MI.GetGenericArguments
If Position > 0 Then
CompleteName.Append(", ")
End If
CompleteName.Append(GenArg.Name)
Position += 1
Next
CompleteName.Append(")")
Position = 0
End If
CompleteName.Append("(")
For Each ArgTy As ParameterInfo In MI.GetParameters
If Position > 0 Then
CompleteName.Append(", ")
End If
If ArgTy.ParameterType.IsByRef Then
CompleteName.Append("ByRef ")
Else
CompleteName.Append("ByVal ")
End If
If ArgTy.IsOptional Then
CompleteName.Append("Optional ")
End If
CompleteName.Append(ArgTy.Name)
CompleteName.AppendFormat(" As {0}", GetTypeName(ArgTy.ParameterType))
If ArgTy.IsOptional Then
CompleteName.AppendFormat(" = {0}", ArgTy.DefaultValue.ToString)
End If
Position += 1
Next
CompleteName.Append(")")
If MI.ReturnType IsNot GetType(Void) Then
CompleteName.AppendFormat(" As {0}", GetTypeName(MI.ReturnType))
End If
'Ora ci occupiamo del corpo
Dim MB As MethodBody = MI.GetMethodBody
Dim Desc As New StringBuilder
If MB Is Nothing Then
Return CompleteName.ToString
End If
'Massima memoria occupata sullo stack
Desc.AppendFormat(" Massima memoria stack : {0} bytes{1}", MB.MaxStackSize, vbCrLf)
Desc.AppendLine()
If MB.LocalVariables.Count > 0 Then
'Variabili locali (LocalVariableInfo è una variante di FieldInfo)
Desc.AppendLine("Variabili locali:")
For Each L As LocalVariableInfo In MB.LocalVariables
'Dato che non si può ottenere il nome, ci si deve
'accontentare di un indice
Desc.AppendFormat(" Var({0}) As {1}{2}", L.LocalIndex, L.LocalType.Name, vbCrLf)
Next
Desc.AppendLine()
End If
If MB.ExceptionHandlingClauses.Count > 0 Then
'Gestione delle eccezioni
Desc.AppendLine("Gestori di eccezioni:")
For Each Ex As ExceptionHandlingClause In MB.ExceptionHandlingClauses
'Tipo di clausola: distingue tra filtro (When),
'clausola (Catch) o un blocco Finally
Desc.AppendFormat(" Tipo : {0}{1}", Ex.Flags.ToString, vbCrLf)
'Se si tratta di un blocco Catch, ne specifica la natura
If Ex.Flags = ExceptionHandlingClauseOptions.Clause Then
Desc.AppendLine(" Catch Ex As " & Ex.CatchType.Name & vbCrLf)
End If
'Offset, ossia posizione in bytes nel Try, del gestore
Desc.AppendFormat(" Offset : {0}{1}", Ex.HandlerOffset, vbCrLf)
'Lunghezza, in bytes, del codice eseguibile del gestore
Desc.AppendFormat(" Lunghezza : {0}{1}", Ex.HandlerLength, vbCrLf)
Desc.AppendLine()
Next
End If
Return CompleteName.ToString & vbCrLf & Desc.ToString
End Function
Public Function GetMethodRelatedTypes(ByVal MI As MethodInfo) As List(Of Type)
Dim Result As New List(Of Type)
Dim MB As MethodBody = MI.GetMethodBody
'Parametri
For Each ArgTy As ParameterInfo In MI.GetParameters
Result.AddRange(GetTypeRelatedTypes(ArgTy.ParameterType))
Next
'Tipo restituito
If MI.ReturnType IsNot GetType(Void) Then
Result.AddRange(GetTypeRelatedTypes(MI.ReturnType))
End If
'Variabili Locali
If MB IsNot Nothing Then
For Each L As LocalVariableInfo In MB.LocalVariables
Result.AddRange(GetTypeRelatedTypes(L.LocalType))
Next
'Eccezioni
For Each Ex As ExceptionHandlingClause In MB.ExceptionHandlingClauses
If Ex.Flags = ExceptionHandlingClauseOptions.Clause Then
Result.AddRange(GetTypeRelatedTypes(Ex.CatchType))
End If
Next
End If
Return Result
End Function
'Analizza un campo e ne restituisce il nome nella forma:
'(Const)/[Scope] [Modifier] [Nome] As [Tipo] (= [Costante])
Public Function GetFieldInfo(ByVal FI As FieldInfo) As String
Dim CompleteName As New StringBuilder
CompleteName.Append(GetScope(FI) & " ")
'Costante
If FI.IsLiteral Then
CompleteName.Append("Const ")
ElseIf FI.IsInitOnly Then
CompleteName.Append("ReadOnly ")
End If
CompleteName.Append(FI.Name & " ")
CompleteName.AppendFormat("As {0}", GetTypeName(FI.FieldType))
If FI.IsLiteral Then
CompleteName.AppendFormat(" = {0}", FI.GetRawConstantValue)
End If
Return CompleteName.ToString
End Function
Public Function GetFieldRelatedTypes(ByVal FI As FieldInfo) As List(Of Type)
Return GetTypeRelatedTypes(FI.FieldType)
End Function
'Analizza una proprieta' e ne restituisce il nome:
'[Modifier] Property [Nome][Signature]
Public Function GetPropertyInfo(ByVal PI As PropertyInfo) As String
Dim CompleteName As New StringBuilder
If PI.CanRead Then
If PI.CanWrite Then
'Niente
Else
CompleteName.Append("ReadOnly ")
End If
Else
CompleteName.Append("WriteOnly ")
End If
CompleteName.Append("Property ")
CompleteName.Append(PI.Name)
'Signature
Dim Position As Int16 = 0
CompleteName.Append("(")
For Each Arg As ParameterInfo In PI.GetIndexParameters
If Position > 0 Then
CompleteName.Append(", ")
End If
CompleteName.Append("ByVal ")
If Arg.IsOptional Then
CompleteName.Append("Optional ")
End If
CompleteName.Append(Arg.Name)
CompleteName.AppendFormat(" As {0}", GetTypeName(Arg.ParameterType))
If Arg.IsOptional Then
CompleteName.AppendFormat(" = {0}", Arg.DefaultValue.ToString)
End If
Next
CompleteName.AppendFormat(") As {0}", GetTypeName(PI.PropertyType))
Return CompleteName.ToString
End Function
Public Function GetPropertyRelatedTypes(ByVal PI As PropertyInfo) As List(Of Type)
Dim Result As New List(Of Type)
If PI.CanRead Then
Result.AddRange(GetMethodRelatedTypes(PI.GetGetMethod))
End If
If PI.CanWrite Then
Result.AddRange(GetMethodRelatedTypes(PI.GetSetMethod))
End If
Result.AddRange(GetTypeRelatedTypes(PI.PropertyType))
For Each Arg As ParameterInfo In PI.GetIndexParameters
Result.AddRange(GetTypeRelatedTypes(Arg.ParameterType))
Next
Return Result
End Function
'Analizza un evento e ne restituisce il nome nella forma:
'Event [Nome] As [Tipo]
Public Function GetEventInfo(ByVal EI As EventInfo) As String
Dim CompleteName As New StringBuilder
CompleteName.AppendFormat("Event {0} As {1}", EI.Name, _
EI.EventHandlerType.Name)
Return CompleteName.ToString
End Function
Public Function GetEventRelatedTypes(ByVal EI As EventInfo) As List(Of Type)
Dim Result As New List(Of Type)
Result.AddRange(GetTypeRelatedTypes(EI.EventHandlerType))
Return Result
End Function
Public Function GetMemberAttributes(ByVal Member As MemberInfo) As String
Dim Result As New StringBuilder
Dim Attr() As Object = Member.GetCustomAttributes(False)
For I As Int16 = 0 To Attr.Length - 1
Result.AppendFormat("Attributo [{0}]: {1}{2}", I, GetTypeInfo(Attr(I).GetType), vbCrLf)
For Each PI As PropertyInfo In Attr(I).GetType.GetProperties
If PI.CanRead AndAlso PI.GetGetMethod IsNot Nothing AndAlso PI.GetGetMethod.GetParameters.Length = 0 Then
Result.AppendFormat(" {0} = {1}{2}", PI.Name, PI.GetValue(Attr(I), Nothing), vbCrLf)
End If
Next
Next
Return Result.ToString
End Function
'Funzione che suddivide il membro in base al tipo
Public Function GetMemberInfo(ByVal Member As MemberInfo) As String
Dim Temp As String = ""
If TypeOf Member Is MethodInfo Then
Temp = GetMethodInfo(Member)
ElseIf TypeOf Member Is FieldInfo Then
Temp = GetFieldInfo(Member)
ElseIf TypeOf Member Is PropertyInfo Then
Temp = GetPropertyInfo(Member)
ElseIf TypeOf Member Is EventInfo Then
Temp = GetEventInfo(Member)
ElseIf Member.MemberType = MemberTypes.NestedType Then
Temp = GetTypeInfo(Member)
End If
Return Temp
End Function
Public Function GetMemberRelatedTypes(ByVal Member As MemberInfo) As List(Of Type)
Dim Result As New List(Of Type)
If TypeOf Member Is MethodInfo Then
Result.AddRange(GetMethodRelatedTypes(Member))
ElseIf TypeOf Member Is FieldInfo Then
Result.AddRange(GetFieldRelatedTypes(Member))
ElseIf TypeOf Member Is PropertyInfo Then
Result.AddRange(GetPropertyRelatedTypes(Member))
ElseIf TypeOf Member Is EventInfo Then
Result.AddRange(GetEventRelatedTypes(Member))
ElseIf Member.MemberType = MemberTypes.NestedType Then
Result.AddRange(GetTypeRelatedTypes(Member))
End If
Return Result
End Function
End Module