0x777
0x777

Reputation: 945

How to get the value of a property of a class in a non executing assembly through reflection

I'm having trouble while getting the value of the text property in a non-executing assembly; I read an assembly from disk via reflection, then i get all classes in the assembly to search for the Text property in a windows form class which is initialized by win forms designer. So far i have the following code:

static void Main(string[] args)
{
    Assembly asm = Assembly.LoadFrom(Path.Combine(path, "Assembly.exe"));
    PropertyInfo[] props;
    foreach (Type t in asm.GetTypes())
    {
        var value = t.GetProperty("Text").GetValue(/*Not sure what to put here*/)
    }
}

And this is how the designer generated the form

    Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None
    Me.BackColor = System.Drawing.Color.FromArgb(CType(CType(0, Byte), Integer), CType(CType(128, Byte), Integer), CType(CType(128, Byte), Integer))
    Me.ClientSize = New System.Drawing.Size(234, 181)
    Me.Cursor = System.Windows.Forms.Cursors.Default
    Me.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
    Me.ForeColor = System.Drawing.SystemColors.WindowText
    Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog
    Me.Location = New System.Drawing.Point(581, 222)
    Me.MaximizeBox = False
    Me.MinimizeBox = False
    Me.Name = "winform"
    Me.RightToLeft = System.Windows.Forms.RightToLeft.No
    Me.StartPosition = System.Windows.Forms.FormStartPosition.Manual
    Me.Text = "Title"
    Me.fraDías.ResumeLayout(False)
    Me.ResumeLayout(False)

Keep in mind that the assembly is on disk and non-executing and that I want to retrieve the value of the Text property of every winform (I guess it should be somewhere hardcoded in the assembly since it was generated by the winforms designer)

Please tell me if this is possible, thanks!

Upvotes: 0

Views: 1391

Answers (3)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112632

You cannot read a property of a class that has not been instantiated! The parameter you are missing is an instance of your type.

You must create an instance of the type with object o = Activator.CreateInstance(type); before accessing its members (unless they are static).


Your problem is related to how add-ins (plug-ins) can be loaded at runtime. Here is how I made an Add-In Loader. Below, I will explain how you can adapt it to your problem. Add-Ins have to implement the IAddIn interface in my example. You are totally free in the definition of IAddIn. You could define it like this:

public interface IAddIn
{
    bool OnLoad();

    string Version { get; set; }
    string Text { get; set; }
}

This allows you to access members without reflection.

public class AddInLoader
{
    // Loads all non abstract types implementing IAddIn in all DLLs found in a folder.
    public IList<IAddIn> Load(string folder)
    {
        var addIns = new List<IAddIn>();
        string[] files = Directory.GetFiles(folder, "*.dll");
        foreach (string file in files) {
            addIns.AddRange(LoadFromAssembly(file));
        }
        return addIns;
    }

    // Loads all non abstract types implementing IAddIn found in a file.
    private static IEnumerable<IAddIn> LoadFromAssembly(string fileName)
    {
        Assembly asm = Assembly.LoadFrom(fileName);
        string addInInterfaceName = typeof(IAddIn).FullName;
        foreach (Type type in asm.GetExportedTypes()) {
            Type interfaceType = type.GetInterface(addInInterfaceName);
            if (interfaceType != null &&
               (type.Attributes & TypeAttributes.Abstract) != TypeAttributes.Abstract){
                IAddIn addIn = (IAddIn)Activator.CreateInstance(type);
                addIn.Version = asm.GetName().Version.ToString();
                yield return addIn;
            }
        }
    }
}

Now you can load and access the add-ins like this:

var loader = new AddInLoader();
IList<IAddIn> addIns = loader.Load(folderPath);
foreach (IAddIn addIn in addIns) {
    if (addIn.OnLoad()) {
        Console.WriteLine("Version = {0}, Text = {1}", addIn.Version, addIn.Text);
    }
}

Reading the titles of Forms at runtime:

You can easily adapt this example. Instead of searching for types implementing an interface, search for types deriving from System.Windows.Forms.Form.

private static IEnumerable<Form> LoadFormsFromAssembly(string fileName)
{
    Assembly asm = Assembly.LoadFrom(fileName);
    foreach (Type type in asm.GetExportedTypes()) {
        if (typeof(Form).IsAssignableFrom(type) &&
           (type.Attributes & TypeAttributes.Abstract) != TypeAttributes.Abstract) {
            Form form = (Form)Activator.CreateInstance(type);
            yield return form;
        }
    }
}

Now you can get the texts of the forms like this:

var forms = LoadFormsFromAssembly(path);
foreach (Form frm in forms) {
    Console.WriteLine(frm.Text);
}

Note: You must instantiate the forms, however you do not need to open (show) them. The code works only if the forms have a default constructor, i.e. a constructor without parameters.

Upvotes: 1

Alejandro
Alejandro

Reputation: 7819

Your requirements are contradictory, when you load an aseembly via reflection, and instantiate an object or try to get a property value, what happens is that some code begins to run, there is no way around that.

Remember that properties are just "syntax sugar" for a pair of methods, the getter and setter. Their current value is nothing but the value returned by the getter method, and when you change its value, you're in fact calling its setter method. So, to retrieve property values, you must make some code to run, even if it's a trivial get method.

I think maybe your confusion comes from the fact that you're using a designer to create the form. Particularly with the WinForms designer (WPF for instance is substantially different), all it does is to autogenerate some code for you. Setting properties, placing and moving controls around, what's happening under the hood is that it writes code that replicate your actions at runtime, specifically, it codes the InitializeComponent method. The real property value is set when the constructor is called (that in turn calls InitializeComponent), and then you may read/change using many properties.

What you would need to read those designer attributes is that those were hardcoded in some form of metadata, so that it's simply read as data and not as the result of code execution. That's not the case with WinForms, as it "saves" the form as code.

Upvotes: 2

Huseyin
Huseyin

Reputation: 81

You need an instance object for that type to get the value of a property. It looks like you just want to check if a type has a "Text" property or not. You can to it by checking
bool hasTextProperty = t.GetProperty("Text") !=null;

Upvotes: 1

Related Questions