0

I am using Reflection.Emit to build a simple dynamic method which gets Text property value of a TextBox object in a simple WPF program (MyTextBox.Text).

This dynamic method cannot be called correctly with Invoke and I found out something wrong at this line 'Emit(OpCodes.Ldfld, textBox)' thanks to VisualStudio.DebuggerVisualizers.

Here is the output of ILStream while debugging:

IL_0000: /* 02  |          */ ldarg.0    
IL_0001: /* 7b  | 04000002 */ ldfld      **!"Specified cast is not valid."!**
IL_0006: /* 28  | 06000003 */ call       System.String get_Text()/System.Windows.Controls.TextBox
IL_000b: /* 2a  |          */ ret  

And here is the code:

namespace MyWPFTest
{
    public partial class MainWindow1 : Window
    {
        public MainWindow1()
        {
            InitializeComponent();
        }

        private void MyTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            MyTextBox.Text = "Morning";
            DynamicMethod dm = new DynamicMethod("GetTextBoxText", typeof(void), new Type[] { }, typeof(MainWindow1), false);
            ILGenerator il = dm.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);

            FieldInfo textBox = typeof(MainWindow1).GetField("MyTextBox", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
            if (textBox == null)
            {
                throw new InvalidOperationException("no textbox");
            }
            il.Emit(OpCodes.Ldfld, textBox);
            var textProperty = typeof(TextBox).GetProperty("Text", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).GetGetMethod();
            if (textProperty == null)
            {
                throw new InvalidOperationException("no Text property");
            }
            il.Emit(OpCodes.Call, textProperty);
            il.Emit(OpCodes.Ret);
            TestShowVisualizer(dm);
            dm.Invoke(null, null);
        }       
    }
}

TestSHowVisulalizer() helps to display IL streams for debugging.

Does anyone have experience in making WPF Controls like TextBox work with Reflection.Emit?

I wrote this code 'var a = MyTextBox.Text' then used ilsdasm to get il. It looks like this: .locals init ([0] string a) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldfld class [PresentationFramework]System.Windows.Controls.TextBox MyWPFTest.MainWindow1::MyTextBox IL_0007: callvirt instance string [PresentationFramework]System.Windows.Controls.TextBox::get_Text() IL_000c: stloc.0 IL_000d: ret } // end of method MainWindow1::MyTextBox_TextChanged

1 Answers1

0

If you read the example on MSDN, you'd find that there's no this argument included in your argument list by default. Specifying the owner class of a DynamicMethod gives you access to private members, but not a this argument. Your DynamicMethod is like a static method in C# source code.

Right now you have an empty array of argument types, so there is no Ldarg_0.

Try specifying the argument type. Change

DynamicMethod dm = new DynamicMethod("GetTextBoxText",
                                     typeof(void),
                                     new Type[] { },
                                     typeof(MainWindow1),
                                     false);

to

DynamicMethod dm = new DynamicMethod("GetTextBoxText",
                                     typeof(void),
                                     new Type[] { typeof(MainWindow1) },
                                     typeof(MainWindow1),
                                     false);
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Thanks a lot, Ben. Very clear. I tested by pushing the value of 'MyTextBox' field of the 'MainWindow1' into eval stack then pop that value out right away and assign back to 'MyTextBox' then it works. Like this: il.Emit(OpCodes.Ldfld, textBox); il.Emit(OpCodes.Stfld, textBox); However, going further than that, to il.Emit(OpCodes.Call, textProperty), does not work. Do you know why? Another funny thing is it is not happy when il.Emit(OpCodes.Ldarg_0) stays alone. I had to add il.Emit(OpCodes.Dup) right below Ldarg to make thing OK. Just don't know why it requires Dup in evaluation stack. – Tam Nguyen Apr 13 '15 at 10:28
  • @TamNguyen: Try with `callvirt` instead of `call`. You can see this in the disassembled code you put in a comment (why is that not part of the question? use the edit link below the question to add the disassembly there) – Ben Voigt Apr 13 '15 at 14:07
  • I have tried all types of combinations including **callvirt** and some **classcast**. I don't think the problem lies in **callvirt** or **call**. Because the dynamic method returns void, to make evaluation stack empty before `Opcodes.Ret`, I have changed to assign value to Text property with GetSetMethod to test: `il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldfld, textBox); il.Emit(OpCodes.Ldstr, "dddd"); //il.Emit(OpCodes.Castclass, textProperty); il.Emit(OpCodes.Callvirt, textProperty); il.Emit(OpCodes.Ret);` Still it does not work. Do you any ideas on why? – Tam Nguyen Apr 13 '15 at 21:28
  • to make it clear and exact as ILASAM (the first is Field; the second set_Text). Still not work. `il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Dup);il.Emit(OpCodes.Ldfld, textBoxFieldInfo); il.Emit(OpCodes.Stfld, textBoxFieldInfo); //work to this point if commenting out the next 4 lines; not working if replacing this line by the next 4 lines /*il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldstr, "Gee"); il.Emit(OpCodes.Callvirt,setTextMethodInfo); */ il.Emit(OpCodes.Ret); Action delg = (Action)dm.CreateDelegate(typeof(Action), this); delg();` – Tam Nguyen Apr 14 '15 at 00:03
  • I replicated the scenario in Console App and it works in Console App. Just need to declare local first for loc_0 or remove the StLoc and LdLoc. However, it still does not work for WPF App. I think the problem relates to casting. TextBox.Text can be Obj String while the the input text is string or something like that. – Tam Nguyen Apr 14 '15 at 01:31
  • I don't do anything special now and all the sudden it works. Now itworks without Dup, as expected. Don't really know what have happened. Thanks, Ben again for the this argument. – Tam Nguyen Apr 14 '15 at 01:57