Search code examples
c#castingtextboxemit

Emit(OpCodes.Ldfld, textBox) fails with casting


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


Solution

  • 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);