Mono.Cecil - How to get custom attributes - c#

I am trying to use Cecil to inspect the attributes associated with a given method. It seems to find it, but I cannot get its name using the following code:
AssemblyDefinition assembly = AssemblyFactory.GetAssembly(pathBin);
assembly.MainModule.Types[0].Methods[1].CustomAttributes[0].ToString()
I know this must be the attribute I've set my function to, because when I remove it from the dll, the second line of code will turn out to null. What I'd like to do is be able to get the attribute's name. Currently the second line of code will return just a "Mono.Cecil.CustomAttribute". I'd guess there should be a way of getting an attribute's name(class type) name, right?
Thanks!

I had trouble with this when writing MoMA as well. Here is the code it uses:
AssemblyDefinition assembly = AssemblyFactory.GetAssembly(pathBin);
assembly.MainModule.Types[0].Methods[1].CustomAttributes[0].Constructor.DeclaringType.ToString()

A CustomAttribute is an instance of a System.Attribute derived Type, so ToString() will do whatever the author decided.
If you want to know about attribute types you should ask for their type:
typeInfo.GetCustomAttributes(false)[0].GetType().ToString() ;
I haven't seen this property CustomAttributes you are using, so I rather used the method MemberInfo.GetCustomAttributes(bool) which I always use.

Related

The type name 'GetName' does not exist in the type 'MyCountry'

I create a new Class Library.
Add a new resx file called MyCountry.resx and add some resources to it.
Two entries of Name and Value. I set the Access Modifier to Public so i can reference this class and use it in other projects.
I create another new Class Library and reference the above CL and all seems to work well when i write the below code:
var someValue = ClassLibrary.MyCountry.GetName;
I then decide to change an Attribute of the same class declaration to use the same Resource entry. Example of working Attribute (shortened for clarity)
[Tree("SomethingHere", "SomethingThere")]
This works but when i change it to
[Tree(ClassLibrary.MyCountry.GetName, "SomethingThere")]
I receive the error
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type.
So change it to
[Tree(typeof(ClassLibrary.MyCountry.GetName), "SomethingThere")]
but then i receive
The type name 'GetName' does not exist in the type 'MyCountry'
When i know it does as it works on the code shown above.
Is it possible to have Attributes to use a value from a resx CL in the manner i have shown, am i missing something or is it not possible?
As the compiler states in warning. It needs a value that is known at compile time, but the values for .resx files are loaded at Runtime.
You can look this up in the MyCountry.Designer.cs that is created automatically along the MyCountry.resx file.
There are 2 Solutions for your Problem that come to my mind:
You create a class that contains all all the constants.
public class MyCountry
{
public const string GetName = "Lorem Ipsum";
}
You create/use an Source Generator that creats such a class from the .resx so that the compiler can access it at compile-time.

How can I determine the target type for a markup extension return value?

I am writing a custom markup extension.
In its ProvideValue method, I would like to modify/convert my return value based upon the intended target type of the property that the markup extension is being used to supply a value for.
(Basically, a TypeConverter knows about the target type of its surrounding binding and can adapt its behaviour accordingly; I would like to do the same in my markup extension.)
Now, the ProvideValue method only receives an IServiceProvider as an argument.
It seems I should be able to use it to get an object that provides me with the desired bit of context information, but so far, none of my attempts to do so has been entirely satisfying:
I have retrieved an IDestinationTypeProvider implementation. While it appears to do exactly what I need, based upon its name, unfortunately, it throws an exception:
var dtp = (IDestinationTypeProvider)serviceProvider.GetService(typeof(IDestinationTypeProvider));
var destType = dtp.GetDestinationType(); // NullReferenceException on this line
I have retrieved an IProvideValueTarget implementation. It supplies me with the target property, but only as a System.Object, so it seems I have to prepare my code myself for treating different (?) kinds of properties and retrieving the type myself.
What is the intended way for a markup extension to get its target type?
Use the IServiceProvider to get yourself an IProvideValueTarget, then look at TargetProperty, which should (but is not guaranteed to) be a DependencyProperty.
var provideValueTarget = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
var propertyType = (provideValueTarget.TargetProperty as DependencyProperty)?.PropertyType;
// Test propertyType for null
The target property might also be an EventInfo if the MarkupExtension is used with an event, or a MethodInfo if it's used with an attached event (or, as you pointed out in the comments, a PropertyInfo if it's used with a normal property).
Unfortunately I think this is the only way of doing this. DynamicResourceExtension has similar checks, see here and here - if there was a better API, I assume it would be using it.

Can't create object from string C#

I've scanned many topics on this site, searched the internet, and experimented with code and nothing has worked. Most people are using separate projects or assemblies which I am not, it's a custom class that exists in the same project and same namespace. If I build the object manually by hard coding it in it works fine but I don't want to do that.
It's a C# ASPX project and I am debugging on IIS from Visual Studio (so maybe that's the issue?).
Type type = Type.GetType("<namespace>."+classname);
Object obj = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod(function);
response = (<cast object>)(methodInfo.Invoke(obj, null));
I am aiming for variable code where I can write plugins that will be dynamically instantiated. Type.GetType always returns null.
In almost all cases type returns null or when I switch it up with other code I'll get other errors thrown about not finding file or assembly and other errors like this class just doesn't exist...
What do I have to do to be able to build an object dynamically off a string? Let's say I have a class called "Foobar" and I want to do this,
string classname = "Foobar";
Object foobar = new classname(); //easy in PHP, nightmare in C#
Any help would be great, thanks. And before you tell me just to reference another post, I have referenced many and still have no success so if it's not the code than maybe it's how I'm debugging in a browser on IIS?
Type.GetType(String) accepts assembly-qualified type name.
See Type.AssemblyQualifiedName Property
You need to get the type from the assembly is was defined in:
typeof(SomeTypeInAssembly).Assembly.GetType("Namespace.Type")
If it is in the same namespace and assembly as the current object as you say, you should just be able to do the following to get hold of the Type you require:
Type type = Type.GetType(this.GetType().Namespace + "." + classname);
The rest should work as you have it.
Thanks for the suggestions, final code I ended up with below from the suggestions and other post I found on this site.
string fullname = string.Format("{0}.{1}", this.GetType().Namespace, classname);
Type type = Type.GetType(fullname,true);
Baseclass class = (BaseClass)Activator.CreateInstance(type,<parameter1>,...);
MethodInfo methodInfo = type.GetMethod("<method>");
methodInfo.Invoke(class, null);
You can store the return (may need casting) if you want to deal with the return type. Hope this helps someone if they're having issues like me.
Thanks to Rhumborl post, I guess it was an issue in just how I was originally trying to call the Type.GetType function.
String is a native type , witch is automatically inherited from object , since every type (aka class) is an object, so there is no need to do such a thing

How to get attribute value for an assembly in Cecil

Is there a way to get str1 in code ?
[MyAttribute("str1")]
class X {}
The instance of Mono.Cecil.CustomAttribute.Fields is empty.
When using attributes in .NET you are either using the constructor parameters and setting some (named) fields. This is encoded differently in the metadata and ends up separately in Cecil.
the instance of Mono.Cecil.CustomAttribute.Fields is empty
What you're using is looking for fields when the constructor arguments were used for the custom attribute. So what you're looking for is:
type.CustomAttributes[0].ConstructorArguments[0].Value

How to add an assembly in other assembly?

I have a dll called Test.dll in which I have a class called ABC which has a method FindTYpe.
Now, I have a project called TestB and I have added the reference of Test.dll in TestB.
Now, if I am trying to find a type XYZ in TestB, from Test.ABC.FindTYpe(), it's throwing an exception, TypeNotLaoded Exception.
Please have a look at the problem and tell me how can I resolve it.
You'll need to post your code for FindType(). My guess is that you're doing something like;
System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
to find a list of types to search through, and the type in TestB.dll isn't in Test.dll, so the item isn't found.
You might want to try something like this instead;
/// <summary>
/// Returns all types in the current AppDomain
/// </summary>
public static IEnumerable<Type> LoadedType()
{
return AppDomain
.CurrentDomain
.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes());
}
which should give you the all types loaded into the current application domain -- which, unless you're doing anything odd with appdomains, will be the list of all types loaded into your program.
None of the code has been tested, but it should help you find the classes and methods you'll need to use.
Mos probably the type XYZ that you are trying to find is not loaded or not present in the paths your app looks for assemblies. The Test.dll and ABC should be present it you added the reference in your project to Test.dll.
What does the code look like in FindType?
Assuming you are creating the type from the type name (a string), then you must be sure to supply the "assembly qualified" type name, not just the "local" type name.
e.g. to retrieve the type you are about to create:
Type testB = Type.GetType("TestB.XYZ, TestB");
rather than
Type testB = Type.GetType("TestB");
Can you give some more specifics, like some code snippets?

Categories

Resources