8
\$\begingroup\$

I have some code that is used to hook into third party code. It goes pretty much like below (mind you CodeWeDontHave can't be posted because we dont have the source). I have an issue where sometimes my code will deadlock on the line commented below. Also, we get a object reference error now and then.

Is this the most effective way to hook into a 3rd party program where we have no source code? Typically, in Winforms we aren't supposed to set controls in this way e.g. Control.Text = "blah"; from another Form, rather via properties & events. In fact, I'm surprised this works at all. Is it safe?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace BrianTests
{//this code requires a Form with a textbox called textBox1
    public partial class CodeWeDontHave : Form
    {
        [DllImport("user32.dll")]
        public extern static bool SetForegroundWindow(IntPtr hWnd);

        public CodeWeDontHave()
        {
            InitializeComponent();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            Text = "structured notes entry";

            //create another form that will attach to this one.
            Form formThatWeOwn = new Form();
            Button button = new Button() { Text = "upload text" };
            formThatWeOwn.Controls.Add(button);
            formThatWeOwn.Load += (a, b) => GetFormWithNoCode();
            button.Click += OtherFormButton_Click;
            formThatWeOwn.Show(this);
        }

        void OtherFormButton_Click(object sender, EventArgs e)
        {
            Button button = sender as Button;
            UploadRtfToNoCodeForm();
            //Close(); closes the parent...
            button.FindForm().Close();//hackjob            
        }

        private static Form GetFormWithNoCode()
        {
            Form noCodeForm = null;

            System.Threading.Thread.Sleep(50);

            noCodeForm = Application.OpenForms
                .Cast<Form>()
                .Where(form => form.Text.ToLower().StartsWith("structured notes entry"))
                .FirstOrDefault();

            return noCodeForm;
        }

        private static void UploadRtfToNoCodeForm()
        {
            Form noCodeForm = GetFormWithNoCode();

            var textBox = noCodeForm.Controls
                .Cast<Control>()
                .GetChildren(c => c.Controls.Cast<Control>())
                .Where(c => c is TextBox && String.Compare(c.Name, "textBox1", true) == 0)
                .LastOrDefault() as TextBox;

            string rtf = "here be some uploaded text";

            SetForegroundWindow(noCodeForm.Handle);//Does this handle need memory cleanup?

            noCodeForm.Focus();
            textBox.Focus();
            textBox.ReadOnly = false;//sometimes this line deadlocks...I can't reproduce in this test code
            textBox.Text = rtf;
            textBox.Update();
            textBox.Focus();
            textBox.ReadOnly = true;
        }
    }

    static class ExtensionStuff
    {
        public static IEnumerable<T> GetChildren<T>(this IEnumerable<T> startingParentSet, Func<T, IEnumerable<T>> childrenSelector, bool includeParent = true)
        {
            foreach (T item in startingParentSet)
            {
                foreach (T child in item.GetChildren(childrenSelector, includeParent))
                    yield return child;
            }
        }

        public static IEnumerable<T> GetChildren<T>(this T parent, Func<T, IEnumerable<T>> childrenSelector, bool includeParent = true)
        {
            if (parent == null) yield break;

            if (includeParent)
                yield return parent;

            IEnumerable<T> children = childrenSelector(parent);

            if (children == null) yield break;

            foreach (T child in children)
            {                
                foreach (T grandChild in child.GetChildren(childrenSelector, includeParent))
                    yield return grandChild;
            }

        }
    }
}
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$
  • TextBox's ReadOnly flag is for user input only, see MSDN.
  • Update only updates what is Invalidated. If you want to forcefully ensure a control is updated, use Invalidate.
  • I do not see a need to focus the textbox, unless the noCodeForm has some sort of validation or worker that requires it.

So we're removing this weird deadlock line of code because it isn't needed, and making the control update every time. Add back your focuses if you need.

    private static void UploadRtfToNoCodeForm()
    {
        Form noCodeForm = GetFormWithNoCode();

        var textBox = noCodeForm.Controls
            .Cast<Control>()
            .GetChildren(c => c.Controls.Cast<Control>())
            .Where(c => c is TextBox && String.Compare(c.Name, "textBox1", true) == 0)
            .LastOrDefault() as TextBox;

        string rtf = "here be some uploaded text";

        SetForegroundWindow(noCodeForm.Handle);

        // No focuses here, unless NoCodeForm requires it explicitly
        textBox.Text = rtf;
        textBox.Invalidate();
    }

Edit: noCodeForm does not need it's handle cleaned up by you. This handle is the hWnd aka Window Handle. It is the value you would see if using Spy++ to poke around and see things. You could use this hWnd to retrieve it directly or find its parent etc with PInvoke calls like FindWindowEx does. While you wouldn't actually do this here, just trying to explain what that Handle property is and a related use; tis term Handle is unrelated to pointers and memory.

Edit2: Alternatively, you can use SendMessage and PostMessage to update controls. This would be something for StackOverflow, or you can post working code and we'll clean it up here too.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.