Click here to Skip to main content
15,887,436 members
Please Sign up or sign in to vote.
4.78/5 (2 votes)
See more:
I have a strange problem with FileStream.ReadAsync(). I call a function that performs an asynchronous read from a stream from my main application OnStartup(). When I read asynchronously, the application doesn't ever display its main window. If I simply change the read to a synchronous version, the application starts up.

This is my OnStartup() override:

protected override async void OnStartup( StartupEventArgs e )
{
	base.OnStartup( e );
	Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
	LogsEmailer logsEmailer = new LogsEmailer();
	string testString = await logsEmailer.TestReadFileAsync( Path.Combine( logsFolder, "TestFile.txt" ) );
	StartupUri = new Uri( "./View/MainWindow.xaml", UriKind.Relative );
}


The application never starts with this function:

public async Task<string> TestReadFileAsync( string filePath )
{
	using( FileStream sourceStream = File.OpenRead( filePath ) )
	{
		byte[] copyBuffer = new byte[ 4096 ];
		bool doneCopying = false;
		while( !doneCopying )
		{
			int numSourceBytes = await sourceStream.ReadAsync( copyBuffer, 0, copyBuffer.Length );
			if( numSourceBytes == 0 )
				doneCopying = true;
		}
	}
	return "test string";
}


But it does start with this:

public async Task<string> TestReadFileAsync( string filePath )
{
	using( FileStream sourceStream = File.OpenRead( filePath ) )
	{
		byte[] copyBuffer = new byte[ 4096 ];
		bool doneCopying = false;
		while( !doneCopying )
		{
			int numSourceBytes = sourceStream.Read( copyBuffer, 0, copyBuffer.Length );
			if( numSourceBytes == 0 )
				doneCopying = true;
		}
	}
	return "test string";
}


Obviously, I do get a warning with the second piece of code that says it will complete synchronously. And when I say that the second version works, I mean only that it allows the application to start; I can't use it because the operation I am trying to perform takes too long to be done synchronously in OnStartup().

I guess I am missing something silly here. Can anyone tell me what?

What I have tried:

I I mentioned above, I have tried removing the async from the stream-read and the application starts okay.

I have just discovered that if I change my OnStartup() override to be as follows then the application starts up okay.

protected override async void OnStartup( StartupEventArgs e )
{
	base.OnStartup( e );
	Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
	StartupUri = new Uri( "./View/MainWindow.xaml", UriKind.Relative );
	LogsEmailer logsEmailer = new LogsEmailer();
	string testString = await logEmailer.TestReadFileAsync( Path.Combine( logsFolder, "TestFile.txt" ) );
}
Posted
Updated 16-Jan-18 9:53am
v2
Comments
phil.o 15-Jan-18 8:58am    
So, with what you just dicovered, are you able to finally see something coming in your testString variable (when you debug your application and put a breakpoint somewhere in your override)?
Good question, by the way, I upvoted it.
Patrick Skelton 15-Jan-18 9:55am    
Yes, I get a string value returned with both versions of the function, regardless of whether I put the call to the function before or after the setting of the start-up URI.
BillWoodruff 15-Jan-18 19:29pm    
Is this a WinForm app ?

This is one of the big problems with async void methods - the method returns before it has completed, and the caller has no way of knowing that the method is still doing work.

The Application class raises the Startup event. When the handler returns - at the first await call in your example - it then calls the private DoStartup method. This method reads the StartupUri, which you code hasn't set yet, so it has nothing to do.

As you discovered, if you move the line which assigns the StartupUri before the first await call, then it will work as expected.

Ideally, you should move the async code to a task-retuning method. You can then store that task in a field, and later code can await it to ensure that it has completed.

Avoid async void methods | You’ve Been Haacked[^]
 
Share this answer
 
I am not sure about your code but I think you are doing this to early in the start-up phase of the application.

If you have a window to show with a status, why don't let the window load first and do the async call with a progress display? I have implemented a few demos that show how this could work:

See, for example, MainWindow.cs -> Loaded Event in FilterTreeView.zip demo:

Advanced WPF TreeViews in C#/VB.Net Part 3 of n[^]

Downloads: Advanced WPF TreeViews in C#/VB.Net Part 3 of n[^]
 
Share this answer
 

Try loading your window as a resource in the continuation part of the async method.


C#
protected override async void OnStartup( StartupEventArgs e )
{
	base.OnStartup( e );
	Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
	LogsEmailer logsEmailer = new LogsEmailer();
        //StartupUri = new Uri( "./View/MainWindow.xaml", UriKind.Relative );
	string testString = await logsEmailer.TestReadFileAsync( Path.Combine( logsFolder, "TestFile.txt" ) );
	Uri startUpUri = new Uri("MainWindow.xaml", UriKind.Relative);
        MainWindow=LoadComponent(startUpUri)as Window;
        MainWindow.Show();
}
 
Share this answer
 
Comments
Patrick Skelton 20-Jan-18 11:28am    
I haven't tried this (because I am temporarily sans development environment - different story), but won't this be more-or-less the same as doing my file work synchronously? By this I mean that the main window will not load until the file work (which could take a while) completes?

Got to admit that I need to refresh my memory about task continuation, so I may be completely misunderstanding here.

You are correct. I thought that your main window depended upon the text file being loaded.

 
Share this answer
 
v2
Comments
Patrick Skelton 21-Jan-18 4:44am    
Sorry - I should have been more clear.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900