.NET Framework - How to use InvokeRequired when in the middle of a DLL

Asked By Roy Chastain on 02-Aug-07 02:59 PM
I have a DLL that will be called by a Forms main program.

In the middle of that DLL, I need to do a ShowDialog() on a class contained within the DLL.  It is currently working, but sometime
after the dialog processing completes, I receive a an exception that is 	"Undo operation encountered a context ...".  All
comments about this exception indicate that it is a UI cross thread issue.  I have no problem believing that is my problem because
there several threads running around in the dll and the ShowDialog is being done on a Async Socket completion thread.

The question is,
How do I call InvokeRequired and/or how does the system know what thread to invoke to, when the dialog I am about to show has just
be created and I do not have the any other control from the originating form to use in the call to InvokeRequired?

Thanks
-------------------------------------------
Roy Chastain
KMSYS Worldwide, Inc.
http://www.kmsys.com




v-lli replied on 03-Aug-07 04:20 AM
Hi Roy,

Firstly, could you tell me in which thread you create the instance of the
class? You should create the Class instance in the UI thread, which has a
message pump, so as to make the application stable.

Secondly, when we just create a Form instance by calling its constructor
and don't show it, the form's handle is not created, which we can tell from
the form's IsHandleCreated property.

In the case where the form's handle has not yet been created, you should
not simply call properties, methods, or events on the form. This might
cause the form's handle to be created on the background thread, isolating
the form on a thread without a message pump and making the application
unstable.

A way to force the form to create its handle is to access its Handle
property. You access the form's Handle property right after you create the
form. The following is a sample.

Form1 frm = new Form1();
IntPtr frmHandle = frm.Handle;


I suggest that you write a method in the form and use the InvokeRequired
property within the method. The following is a sample.

public partial class Form1 : Form
{
delegate void ShowFormDelegate();
public void ShowForm()
{
if (this.InvokeRequired)
{
this.Invoke(new ShowFormDelegate(ShowForm));
}
else
{
this.ShowDialog();
}
}
}


Controls in Windows Forms are bound to a specific thread through their
handle.

If you have any question, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
Roy Chastain replied on 03-Aug-07 06:19 AM
Thanks for the response.

You asked in which thread I create an instance of the class.  If you mean the dialog box class, it is being created on whatever
thread happens to get there.  Most likely an IO Completion Port thread that is the result of an async socket completion.

If I understand what you are saying.  I can create the form class object on any thread.  Simply do an Invoke as shown in your code
to display it.  I would guess that the first time through I should just do the invoke so that I get onto the thread that handles
the main form and not do the check since InvokeRequired returns a false if the handle has not been created.

Bottom line, I am assuming that I need the handle creation for this form to be done on the same thread that created the original
form.


-------------------------------------------
Roy Chastain
KMSYS Worldwide, Inc.
http://www.kmsys.com
v-lli replied on 06-Aug-07 06:32 AM
Hi Roy,

Thank you for your prompt reply.

I think you're almost right except the following comment:

that I get onto the thread that handles the main form and not do the check
since InvokeRequired returns a false if the handle has not been created. "

If you create a form in a thread, you can show the form by calling the
ShowDialog method of the form directly right after the code of creating the
form, and needn't call the Invoke method.

If you create a form in a thread and don't show it in this thread, and
would like to show the form in another thread, I suggest that you access
the Handle property of the form right after the code of creating the form
in the first thread to create the form's handle in the first thread. Then
you call the Invoke method to show the form in another thread.

Hope this helps.

Sincerely,
Linda Liu
Microsoft Online Community Support
Roy Chastain replied on 06-Aug-07 07:21 AM
Well, if you statement
is correct and I have no reason to believe that it is not, then I have no idea what the exception I am receiving is trying to tell
me.

Could you at least point me in a direction.  (Like what does this exception actually mean or which new group to post follow up
in?)

Exception info is below

An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll

Additional information: The Undo operation encountered a context that is different from what was applied in the corresponding Set
operation. The possible cause is that a context was Set on the thread and not reverted(undone).

mscorlib.dll!System.Threading.SynchronizationContextSwitcher.Undo() + 0xb8 bytes
mscorlib.dll!System.Threading.ExecutionContextSwitcher.Undo() + 0x9c bytes
mscorlib.dll!System.Threading.ExecutionContext.runFinallyCode(object userData, bool exceptionThrown) + 0x17 bytes
mscorlib.dll!System.Runtime.CompilerServices.RuntimeHelpers.ExecuteBackoutCodeHelper(object backoutCode, object userData, bool
exceptionThrown) + 0x29 bytes
[Frames below may be incorrect and/or missing, no symbols loaded for mscorwks.dll]
mscorlib.ni.dll!793428f0()
mscorlib.ni.dll!791b9387()
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext,
System.Threading.ContextCallback callback, object state) + 0xa7 bytes
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext,
System.Threading.ContextCallback callback, object state) + 0x92 bytes
System.dll!System.Net.ContextAwareResult.Complete(System.IntPtr userToken) + 0xa7 bytes
System.dll!System.Net.LazyAsyncResult.ProtectedInvokeCallback(object result, System.IntPtr userToken) + 0x8b bytes
System.dll!System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(uint errorCode, uint numBytes,
System.Threading.NativeOverlapped* nativeOverlapped) + 0x10c bytes
mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint errorCode, uint numBytes,
System.Threading.NativeOverlapped* pOVERLAP) + 0x68 bytes

Of course this exception happens sometime after the form is OK'd.  (And I do not know where.)  The data on the form is accessed by
the createing thread just after the user responds to the form.  The created form object is on the stack and the method that
allocated the form has been exited.




-------------------------------------------
Roy Chastain
KMSYS Worldwide, Inc.
http://www.kmsys.com
v-lli replied on 08-Aug-07 04:50 AM
Hi Roy,

Thank you for your reply and more detailed information.

I searched in our inner database, and found a similar issue which produces
the same exception and call stack as your application does.

The following is the comment for the issue I found in our inner database:

WindowsFormsSynchronizationContext. The WindowsFormsSynchronizationContext
is a subclass of System.Threading.SynchronizationContext which adds some
installation helper functions. The AutoInstall method tells WinForms if it
should install the synchronization context on the thread when instantiating
a Control or message loop. "

The solution is to set WindowsFormsSynchronicationContext.AutoInstall =
false before Application.Run."

Please try the solution in your applicaiton to see if it works and let me
know the result.

Sincerely,
Linda Liu
Microsoft Online Community Support
Roy Chastain replied on 08-Aug-07 08:23 AM
Thank you for the response.  Putting in WindowsFormsSynchronicationContext.AutoInstall = false changes the situation.

The original error is gone.
If I run the application in debug mode under Visual Studio, strange things happen.  2 out of 3 times, the application hung and
locked VS to the point that a popup message appeared in the ICON tray telling me that VS was busy internally and if this condition
happened often to report it to Microsoft.  (Almost a quote of the message).  When this happens the system is not busy.  Every
program but the application and VS are responding and Task Manager indicates the system is idle.  VS will not respond at all, but
I can kill the application via Task Manager and then VS starts working again.

When I ran the application outside of VS, it received an error, but I would say it was an expected error that I need to look at.

1) - Are there any issues with this new setting and debugging environment?

2) - When this DLL is complete it will be run in a Windows application (message loop etc), but it will not be a Forms Mode
application.  By that I mean that the original .exe is a Win32 application not a .NET application.  All of the DLL (with the
exception of the new dialog box and associated code) is already working in that environment.  Will there be issues with the
context stuff because of the origin from then unmanaged code?

Thanks


-------------------------------------------
Roy Chastain
KMSYS Worldwide, Inc.
http://www.kmsys.com
v-lli replied on 10-Aug-07 07:18 AM
Hi Roy,

Thank you for your feedback.


I have spent some time trying to find out the answer to this question, but
unfortunately I haven't found an anwer until now. I have consulted this in
our inner discussion group and as soon as I get an answer, I will get it
back to you.

(message loop etc), but it will not be a Forms Mode application.

Well, when the DLL is used by an unmanaged Win32 application, I don't think
you can use the WindowsFormsSynchronicationContext class in the unmanaged
Win32 application, because this is a .NET class.

If you run the DLL with a native Win32 application, is there any exception
occuring?

Sincerely,
Linda Liu
Microsoft Online Community Support
Roy Chastain replied on 13-Aug-07 06:46 AM
Linda,
As far as the debugging issue is concerned.  I changed something in the code on Friday (not really sure what) and the lockup in VS
went away.  The program is now as debuggable as any other.

As to running under unmanaged code.
I have not been able to test it with the unmanaged driver application and will not be able to for at least a few more days.  I was
just trying to get in front of any potential problem.

I will let you know once I get to test it with the unmanaged main program.

Thanks




-------------------------------------------
Roy Chastain
KMSYS Worldwide, Inc.
http://www.kmsys.com
v-lli replied on 15-Aug-07 04:31 AM
Hi Roy,

Thank you for your reply.

Firstly, let me summarize this post.

1. You use a DLL within a WinForm application and call the ShowDialog
method of a form contained in the DLL. Sometimes, after the dialog is
closed, you get an exception that is "Undo operation encountered a
context..".

I do some search and find an issue with the same exception and call stack
in our inner database. The solution to this problem is to set
WindowsFormsSnychronicationContext.AutoInstall=false before Application.Run.

2. When you try the above solution and run the application in debug mode
under Visual Studio, you find that the application sometimes hangs and the
VS IDE is locked.

You change something in the code and this problem goes away.

3. When the DLL is completed, it will be run in an unmanaged Win32
application. You wonder whether there's any problem when the DLL is used by
a native Win32 application.

Since I don't have much information about the unmanaged application, I'm
not sure wether there will be any problem in that case. What the type of
the unmanaged application, Windows application, Console application or a
Windows Service? How would you use the managed DLL in the unmanaged
application?

Of course, if you have a chance to test it with an unmanaged application
later and encounter any problem, please don't hesitate to contact us(you'd
better post a new post in the newsgroup for the new question).

By now, I consider this post is ready to close in the two days. If you feel
you need assistance on this particular issue later, please reopen it freely.

Thank you for using our MSDN Managed Newsgroup Support Service!

Sincerely,
Linda Liu
Microsoft Online Community Support