I recently encountered a problem with asynchronous operations in MSMQ. In .NET 2.0, 3.0 and 3.5, if there is a pending asynchronous receive, and the queue is deleted, the callback is invoked and upon calling EndReceive, the exception is thrown.
In .NET 4.0, the callback is never invoked, but the exception can be caught by the AppDomain.UnhandledException event handler. When running in the debugger, the application will simply terminate with no notification from Visual Studio that an exception occurred.
This code is executing on Windows 7 Professional, 64-bit. However the behavior is the same whether the application is targeting x86 or x64. (Edit: verified this behavior on XP SP3 32-bit as well - this appears to be a framework bug, not OS-related)
I am assuming this new behavior is related to .NET 4.0 being a completely new runtime. I'm not sure what to do at this point, but essentially I am looking to get the pre-.NET 4.0 behavior back, while still targeting the .NET 4.0 runtime. Any help or advice would be greatly appreciated. Here is sample code to reproduce the problem:
class Program
{
static void Main( string[] args )
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler( CurrentDomain_UnhandledException );
string path = #".\private$\mytestqueue";
// Create queue only if it doesn't already exist.
var queue = MessageQueue.Exists( path ) ? new MessageQueue( path ) : MessageQueue.Create( path );
queue.BeginReceive( TimeSpan.FromSeconds( 15 ), queue, new AsyncCallback( ReceiveComplete ) );
Thread.Sleep( 5000 );
MessageQueue.Delete( path );
}
static void CurrentDomain_UnhandledException( object sender, UnhandledExceptionEventArgs e )
{
var mqEx = (MessageQueueException) e.ExceptionObject;
// .NET 4.0:
// "The queue does not exist or you do not have sufficient
// permissions to perform the operation."
Console.WriteLine( mqEx.Message );
// "QueueNotFound"
Console.WriteLine( mqEx.MessageQueueErrorCode );
}
static void ReceiveComplete( IAsyncResult ar )
{
// This callback is never invoked under .NET 4.0.
Console.WriteLine( "Finishing Receive." );
var queue = (MessageQueue) ar.AsyncState;
try
{
queue.EndReceive( ar );
}
catch ( MessageQueueException mqEx )
{
// .NET 2.0 through 3.5:
// "Queue handle can no longer be used to receive messages
// because the queue was deleted. The handle should be closed."
Console.WriteLine( mqEx.Message );
// "QueueDeleted"
Console.WriteLine( mqEx.MessageQueueErrorCode );
}
}
}
Addendum:
After spending way too much time trying to use source stepping (System.Messaging source is available for 4.0 but not for 2.0/3.5, it appears), and hunting through the two different System.Messaging assemblies with Reflector, I finally found the problem.
In the 2.0 assembly, some try/catch blocks are used in the MessageQueue.AsynchronousRequest.RaiseCompletionEvent method to catch exceptions and store an error code so that the exception can be raised when .EndReceive() is called. However, in the 4.0 assembly, these try/catches are gone, so when an exception occurs the process must terminate since they are un-caught on a background thread.
Unfortunately this doesn't help me fix the problem. I am considering switching to a synchronous Receive, but I liked the idea of taking advantage of I/O completion ports for this.
Well, I am going to answer this and accept it, since I think it's the best answer for the near future. It could be months (or more) before there is a proper solution.
As mentioned above, I filed a bug report on Microsoft Connect, so it is pretty much up to them to revert the behavior to how it worked in CLR 2.0.
Microsoft Connect: http://connect.microsoft.com/VisualStudio/feedback/details/626177/messagequeue-beginreceive-asynchronous-exception-behavior
As far as how this affects my application, I am not willing to switch to a synchronous Receive method, as that would consume all of the available worker threads on the thread pool. My application frequently creates and removes a lot of queues, and this issue arose when a command to remove a queue was issued, but an outstanding read operation was pending. Instead, I will just mark that a queue needs to be removed, and once a safe period of time has elapsed (two times the BeginReceive timeout, for instance), I will actually remove the queue.
Or switch to a different queuing system than MSMQ, though I've been happy with it so far.
Related
We're currently using a Windows Service to generate PDF files. I was recently optimizing the code and noticed the abusive use of memory. This was caused by the lack of a using statement around var reportViewer = new ReportViewer()
However, after adding this the code threw a runtime nullreference exception after closing the using block, the code originating in the internal Dispose method.
Why does this error occur and how can I dispose the object properly?
The answer, as can be found in Visual Basic here is because for some reason the ReportViewer expects a HttpContext. If none is present, this error will occur.
As a workaround the following lines can be added to resolve the issue:
if (System.Web.HttpContext.Current == null)
{
System.Web.HttpContext.Current = new System.Web.HttpContext(
new System.Web.HttpRequest(System.IO.Path.GetRandomFileName(), "https://www.stackoverflow.com", string.Empty),
new System.Web.HttpResponse(System.IO.TextWriter.Null)
);
}
This will create a fake HttpContext, allowing somehow to dispose the ReportViewer instance properly.
In the EFContextProvider (EF6) SaveChangesCore method, the exception handling looks like this:
} catch (Exception e) {
while (e.InnerException != null) {
e = e.InnerException;
}
throw e;
}
This throws only the most internal exception and hides the relevant information revealed by the external exceptions.
When the SaveChanges process goes through multiple layers the next direct layer exception is lost, and only the last exception in the chain is thrown. It doesn't allow to handle well the exceptions for the caller.
Updated Post
As of Breeze 1.4.6, any .NET Exceptions thrown on the server are now available in their original form in the httpResponse.data property of any async breeze result. Breeze will still drill down to extract a "good" error message, but will no longer obscure the initial exception.
Original Post Below -------------------
It's an interesting point. The reason we did this was because most client side apps aren't written to navigate thru the exception chain and we wanted to expose the most 'relevant' error to the client. Most of the apps we looked at just exposed the client "error.message" property directly and with EF errors this was almost always useless.
However, your point is well taken. I think what we need to do is create a new Exception that has a top level message that is the innermost exception message but still expose the entire exception chain for those that want to drill. I've added an internal feature request for this and will try to get it into a near term release ( probably not the next one because we are already in testing for that one).
And thanks for the input.
I have a strange little issue with a WCF RIA service I'm using in a SL4 application. Here is the code for a button click handler I've got:
private void btnTest_Click(object sender, RoutedEventArgs e)
{
LanguageContext context = new LanguageContext();
LoadOperation<Language> op = context.Load(context.GetLanguagesQuery());
op.Completed += (obj, args) =>
{
if (!op.HasError)
{
System.Threading.Thread.Sleep(500);
MessageBox.Show(context.Languages.FirstOrDefault().DisplayName);
}
};
}
Note that there's a Sleep call in the handler. Without that sleep call, I get an exception (A transport-level error has occurred when sending the request to the server. (provider: Shared Memory Provider, error: 0 - No process is on the other end of the pipe.)). If this code is in the "Completed" handler, I figured it was actually, well, completed by the time it got there. Why does it die without the Sleep()? BTW, the Sleep() isn't an option for production, it was just a problem-sovling tool :)
So, If I add "pooling=false" to my connection string, everything works. However, I don't really like that answer. Connection pooling is a good thing. Is there a way to leave it on and have things still work?
I can also reproduce this issue.
For example, I have a unit test that if run twice in succession will fail the second time.
This unit test performs a few things:
1. Drops and recreates the database using a custom Entity Framework 4.1 DbContext Initiailzer
2. Launches a silverlight application
3. Click a button in the silverlight application
At this point the silverlight application makes a call to a wcf ria service to query the database that was just created.
However, every time the unit test is run a second time, I get the same error.
But the error goes away immediately if I click the button again for example.
Setting "Pooling=False" in my connection string did not resolve the issue in my case.
However, I was able to resolve the issue by restarting the web server which hosts the silverlight application and ria service after the database is created.
In my case, I just decided to use Cassini Dev Web Server v4 and run the tests on that web server instead of iis.
Windows 7 Ultimate x64
Visual Studio 2010 SP1
Entity Framework 4.1
WCF RIA Services SP1 for Silverlight 4
Silverlight 4
MSTest
Edit:
Entity Framework 4.1 Update 1 contains a bug fix to remove the need to specify ‘Persist Security Info=True’ in the connection string when using SQL authentication.
http://www.microsoft.com/download/en/details.aspx?id=26825
I am not (yet) sure if the bug is related and might resolve this issue as well.
I have a (rather large) application that I have written in C++ and until recently it has been running fine outside of visual studio from the release build. However, now, whenever I run it it says "Unhandled exception at 0x77cf205b in myprog.exe: 0xC0000005: Access violation writing location 0x45000200.", and leads me to "crtexe.c" at line 582 ("mainret = main(argc, argv, envp);") if I attempt to debug it. Note that this problem never shows if I run my debug executable outside of visual studio, or if I run my debug or release build within visual studio. It only happens when running the release build outside of visual studio.
I have been through and put plenty of printfs and a couple of while(1)s in it to see when it actually crashed, and found that the access violation occurs at exactly the point that the value is returned from the function (I'm returning a pointer to an object). I don't fully understand why I would get an access violation at the point it returns, and it doesn't seem to matter what I'm returning as it still occurs when I return 0.
The point it started crashing was when I added a function which does a lot of reading from a file using ifstream. I am opening the stream every time I attempt to read a new file and close it when I finish reading it.
If I keep attempting to run it, it will run once in about 20 tries. It seems a lot more reliable if I run it off my pen drive (it seems to crash the first 3 or 4 times then run fine after that - maybe it's due to its slower read speed).
Thanks for your help, and if I've missed anything let me know.
EDIT: New info
Well I removed the entirity of the function and replaced it with:
IndexedMesh * loadObj(char * objName)
{
ifstream fp_in;
fp_in.open("lol.bmp", ios::in);
fp_in.clear();
fp_in.close();
IndexedMesh * mesh = new IndexedMesh();
printf("finished");
return mesh;
}
I also tried it with "return 0" and "return new IndexedMesh()". It's all fine until you put the ifstream stuff in. I do have 2 other ifstreams open in different functions (accessing completely different files). Could this be the problem?
It actually errors on the return mesh line, (I got the debugger working with the separate release file). It completely nulls the mesh object when it attempts to return it.
The point it started crashing was when I added a function which does a lot of reading from a file using ifstream. I am opening the stream every time I attempt to read a new file and close it when I finish reading it.
Given your description of the code only failing in release mode outside the debugger I'd examine this function for any unset variables. Compiling debug sets variables (or at least it used to) as did running release code in the debugger.
You are probably running over something stored deep in the stack.
I'll bet that if you were to put this at near the top of your code:
int my_main(int argc, char * argv[], char * envp[]);
int main(int argc, char * argv[], char * envp) {
char ** a;
char ** e;
a = malloc(argc+1); // note: you should test the results for NULL
e = malloc(1+count(envp) ) ;// I'm not writing code to count it, but it's easy
int i = 0;
while (argv[i++]) {
a[i] = strdup(argv[i]);
}
a[i] = argv[i]; // argv[i] is NULL and already in a register
// do the same thing for envp
return my_main(argc, a, e);
}
#define main my_main
then whatever it is that is smashing your stack would instead end up smashing this duplicated environment. It's not garenteed, and it is no fix for your problem, but not that difficult.
Thanks very much for your help, I haven't exactly solved the problem but I have managed to evade it. Basically, if I even mentioned an ifsteam (in that function and that function only), the program crashed.
I actually went as far as altering the function to simply declare an ifstream then return 0. I "fixed" it by declaring the ifstreams as pointers and new-ing them. If I deleted the pointer it crashed out again so I had to set it to 0 (leeeeak).
If anyone could enlighten me as to why this occurs, that would be great. While I'm just happy it works now, I'd rather know why..
I have an MFC application compiled with /clr and I'm trying to implement a final handler for otherwise un-caught managed exceptions. For native exceptions, overriding CWinApp::ProcessWndProcException works.
The two events suggested in Jeff's CodeProject article,Application.ThreadException and AppDomain.CurrentDomain.UnhandledException, are not raised.
Can anyone suggest a way to provide a final managed exception handler for a mixed executable?
Update:
It appears that these exception handlers are only triggered downstream of Application.Run or similar (there's a worker thread flavor, can't remember the name.) If you want to truly globally catch a managed exception you do need to install an SEH filter. You're not going to get a System.Exception and if you want a callstack you're going to have to roll your own walker.
In an MSDN forum question on this topic it was suggested to override a sufficiently low-level point of the main MFC thread in a try ... catch (Exception^). For instance, CWinApp::Run. This may be a good solution but I haven't looked at any perf or stability implications. You'll get a chance to log with a call stack before you bail and you can avoid the default windows unahndled exception behavior.
Taking a look around the internets, you'll find that you need to install a filter to get the unmanaged exceptions passing the filters on their way to your AppDomain. From CLR and Unhandled Exception Filters:
The CLR relies on the SEH unhandled exception filter mechanism to catch unhandled exceptions.
Using those two exception handlers should work.
Why "should?"
The events are not raised using the below:
extern "C" void wWinMainCRTStartup();
// managed entry point
[System::STAThread]
int managedEntry( void )
{
FinalExceptionHandler^ handler = gcnew FinalExceptionHandler();
Application::ThreadException += gcnew System::Threading::ThreadExceptionEventHandler(
handler,
&FinalExceptionHandler::OnThreadException);
AppDomain::CurrentDomain->UnhandledException += gcnew UnhandledExceptionEventHandler(
handler,
&FinalExceptionHandler::OnAppDomainException);
wWinMainCRTStartup();
return 0;
}
// final thread exception handler implementation
void FinalExceptionHandler::OnThreadException( Object^ /* sender */, System::Threading::ThreadExceptionEventArgs^ t )
{
LogWrapper::log->Error( "Unhandled managed thread exception.", t->Exception );
}
// final appdomain exception handler implementation
void FinalExceptionHandler::OnAppDomainException(System::Object ^, UnhandledExceptionEventArgs ^args)
{
LogWrapper::log->Error( "Unhandled managed appdomain exception.", (Exception^)(args->ExceptionObject) );
}
BOOL CMyApp::InitInstance()
{
throw gcnew Exception("test unhandled");
return TRUE;
}
Using those two exception handlers should work. Are you sure you've added them in a place where they're going to be called and properly set (ie, in your application's managed entry point -- you did put one in, right?)