0

I have a winforms app, and I need to access the Handle property of a main form, inside a Backgroundworker thread.

I have made a thread safe method that calls the main form with InvokeRequired. My question is - why do I still get "InvalidOperationException cross-thread operation not valid" error, even when calling this thread safe method like this:

ProcessStartInfo psi = new ProcessStartInfo(file);
psi.ErrorDialogParentHandle = Utils.GetMainAppFormThreadSafe().Handle;

And below is the code of the thread safe method (my main app form is called Updater):

    /// <summary>
    /// delegate used to retrieve the main app form
    /// </summary>
    /// <returns></returns>
    private delegate Updater delegateGetMainForm();

    /// <summary>
    /// gets the mainform thread safe, to avoid cross-thread exception
    /// </summary>
    /// <returns></returns>
    public static Updater GetMainAppFormThreadSafe()
    {
        Updater updaterObj = null;
        if (GetMainAppForm().InvokeRequired)
        {
            delegateGetMainForm deleg = new delegateGetMainForm(GetMainAppForm);
            updaterObj = GetMainAppForm().Invoke(deleg) as Updater;
        }
        else
        {
            updaterObj = GetMainAppForm();
        }
        return updaterObj;
    }

    /// <summary>
    /// retrieves the main form of the application
    /// </summary>
    /// <returns></returns>
    public static Updater GetMainAppForm()
    {
        Updater mainForm = System.Windows.Forms.Application.OpenForms[Utils.AppName] as Updater;
        return mainForm;
    }

Am I doing smth wrong? Thank you in advance.

LATER EDIT: I'll post the reason why I need the handle in the first place, perhaps there is another solution/approach. In My Backgroundworker thread I need to install multiple programs in a loop, and I start a process for each installer. However I need to ask for elevation so that this operation can work for standard users as well, not only admins. In short, I am trying to follow the tutorial here

Amc_rtty
  • 3,662
  • 11
  • 48
  • 73

1 Answers1

1

You aren't getting the handle in a thread-safe way. Instead you get the Form instance in a thread-safe way and then access the Handle property in an unsafe way.

You should add a method GetMainAppFormHandle() that directly returns the handle and call that one in a thread-safe way:

public static IntPtr GetMainAppFormHandle()
{
    return System.Windows.Forms.Application.OpenForms[Utils.AppName].Handle;
}

Update:

In addition you need GetMainAppFormHandleThreadSafe() instead of GetMainAppFormThreadSafe():

public static IntPtr GetMainAppFormHandleThreadSafe()
{
    Form form = GetMainAppForm();
    if (form.InvokeRequired)
    {
        return (IntPtr)form.Invoke(new Func<IntPtr>(GetMainAppFormHandle));
    }
    else
    {
        return GetMainAppFormHandle();
    }
}
Codo
  • 75,595
  • 17
  • 168
  • 206
  • I just tried this, but realised that IntPtr is not a Control object and therefore has no properties like InvokeRequired or Invoke so that I can call this static method in a thread safe way. – Amc_rtty Aug 14 '11 at 08:56
  • I just realized that your code was more broken than I first thought: you call `GetMainAppForm()` in an unsafe way just to determine whether the very same method needs to be called in a thread-safe way or not. That makes no sense. Anyhow, I've updated my answer and included more code. – Codo Aug 14 '11 at 09:16
  • Thank you for your comment - you mean the part that makes no sense is in my code at: GetMainAppForm().InvokeRequired, or at updaterObj = GetMainAppForm().Invoke(deleg) as Updater ? – Amc_rtty Aug 14 '11 at 11:34
  • Both. Your original assumption was that `GetMainAppForm()` must be called in a thread-safe way. Yet you call it twice in an unsafe way before calling `Invoke`, which at the core is the thread-safe way. However, the thread-safe access is needed somewhere else, namely when using the `Handle` property. – Codo Aug 14 '11 at 11:54