Unanswered Why hangs the app by the parallel loading of images?

  • Tuesday, June 19, 2012 6:15 AM
     
      Has Code

    Hi all,

    there are four sub-folders, each with 2 images. First, all images must be opened and loaded to a memory objects (BitmapImage class). Then the main-UI can be started, because they used these images in the binding. But this does not happen. The loading process hangs while waiting for the results from the BitmapImage::ImageOpened or BitmapImage::ImageFailed. Why?

    Here is a code snippet:

    void ImagingLoad::MainPage::Page_Loaded_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
    	task<void>([this]()
    	{
    		auto spRootSoundpool = KnownFolders::PicturesLibrary;
    		String^ csFolderPool = L"Test"; // Root folder
    
    		std::vector<String^> xSubFolders;
    		xSubFolders.push_back(L"Pic1"); // Folder with 2 pictures
    		xSubFolders.push_back(L"Pic2"); // Folder with 2 pictures
    		xSubFolders.push_back(L"Pic3"); // Folder with 2 pictures
    		xSubFolders.push_back(L"Pic4"); // Folder with 2 pictures
    
    		task<StorageFolder^> xOpenPool(spRootSoundpool->GetFolderAsync(csFolderPool));
    		auto spPoolFolder = xOpenPool.get();
    
    		if(spPoolFolder != nullptr)
    		{
    			HANDLE hWait[2] = {::CreateEventExW(NULL, NULL, 0, EVENT_ALL_ACCESS), ::CreateEventExW(NULL, NULL, 0, EVENT_ALL_ACCESS)};
    			HANDLE *pWait = hWait;
    
    			for(std::vector<String^>::iterator it = xSubFolders.begin(); it != xSubFolders.end(); ++it)
    			{
    				auto csName = *it;
    				Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([=]()
    				{
    					task<StorageFolder^>(spPoolFolder->GetFolderAsync(csName)).then([=](StorageFolder^ spFolder)
    					{
    						// Query only picture files.
    						Platform::Collections::Vector<String^>^ spList = ref new Platform::Collections::Vector<String^>;
    						spList->Append(L".jpg");
    						spList->Append(L".png");
    						auto spOptions = ref new QueryOptions(CommonFileQuery::DefaultQuery, spList);
    						auto spResult = spFolder->CreateFileQueryWithOptions(spOptions);
    
    						task<uint32>(spResult->GetItemCountAsync()).then([=](uint32 nCount)
    						{
    							if(nCount >= 2) // We must have at least 2 pictures.
    							{
    								task<IVectorView<StorageFile^>^>(spResult->GetFilesAsync()).then([=](IVectorView<StorageFile^>^ spListFiles)
    								{
    									auto spPic1 = spListFiles->GetAt(0);
    									auto spPic2 = spListFiles->GetAt(1);
    
    									// Open and load a first picture.
    									task<Streams::IRandomAccessStream^>(spPic1->OpenAsync(FileAccessMode::Read)).then([=](Streams::IRandomAccessStream^ spStream)
    									{
    										auto spBmp = ref new Windows::UI::Xaml::Media::Imaging::BitmapImage;
    										spBmp->ImageOpened += ref new RoutedEventHandler([pWait](Object^ sender, RoutedEventArgs^ e)
    										{
    											::SetEvent(pWait[0]);
    										});
    										spBmp->ImageFailed += ref new ExceptionRoutedEventHandler([pWait](Object^ sender, ExceptionRoutedEventArgs^ e)
    										{
    											::SetEvent(pWait[0]);
    										});
    										spBmp->SetSource(spStream);
    										m_mapPictures1->Insert(csName, spBmp);
    									});
    
    									// Open and load a second picture.
    									task<Streams::IRandomAccessStream^>(spPic2->OpenAsync(FileAccessMode::Read)).then([=](Streams::IRandomAccessStream^ spStream)
    									{
    										auto spBmp = ref new Windows::UI::Xaml::Media::Imaging::BitmapImage;
    										spBmp->ImageOpened += ref new RoutedEventHandler([pWait](Object^ sender, RoutedEventArgs^ e)
    										{
    											::SetEvent(pWait[1]);
    										});
    										spBmp->ImageFailed += ref new ExceptionRoutedEventHandler([pWait](Object^ sender, ExceptionRoutedEventArgs^ e)
    										{
    											::SetEvent(pWait[1]);
    										});
    										spBmp->SetSource(spStream);
    										m_mapPictures2->Insert(csName, spBmp);
    									});
    								});
    							}
    							else
    							{
    								::SetEvent(pWait[0]);
    								::SetEvent(pWait[1]);
    							}
    						});
    					});
    				}));
    
    				::WaitForMultipleObjectsEx(2, hWait, TRUE, INFINITE, FALSE);
    			}
    
    			::CloseHandle(hWait[0]);
    			::CloseHandle(hWait[1]);
    		}
    	}).then([this]()
    	{
    		Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([=]()
    		{
    			m_ring->IsActive = false; // Stop the waiting.
    		}));
    	});
    }
    Best regards, Anry

All Replies

  • Wednesday, June 20, 2012 4:52 AM
    Moderator
     
     
     

    Hello,

     

    Did you do this in UI thread?

     

    We suggest that we cannot wait any operator in UI thread, we should wait them in work thread.

    Please follow this document

    http://msdn.microsoft.com/en-us/library/hh750082(v=vs.110).aspx

     

    Best regards,

    Jesse


    Jesse Jiang [MSFT]
    MSDN Community Support | Feedback to us

  • Wednesday, June 20, 2012 5:30 AM
    Moderator
     
     

    How are these defined?

    m_mapPictures1

    m_mapPictures2


    David Lamb

  • Wednesday, June 20, 2012 8:31 PM
     
     

    Hello,

    1. All this is executed in a separate thread (see in the code):

    task<void>([this](){ <... THE ACTUAL WORK ...> });

    2. m_mapPicture1 and m_mapPicture2 are defined as Platform::Collections::Map<String^, BitmapImage^>.

    Best regards, Anry

  • Wednesday, June 20, 2012 11:13 PM
    Moderator
     
     

    My understanding is the BitmapImage will only read / decode the stream when added to the visual tree or ResourceDictionary. See if adding them to a

    Windows::UI::Xaml::ResourceDictionary^ m_MyResources;

    will trigger the ImageOpened event.


    David Lamb

  • Thursday, June 21, 2012 10:54 AM
     
      Has Code

    Hi David,

    this is very strange, because your recommendation does not work sometimes, although this has improved :-)

    My scenario:

    1. Some XAML-elements show an image. Each of this images has a binding to the data context:

    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid Height="150" Width="310">
                <Grid.Background>
                    <ImageBrush ImageSource="{Binding Icon}" Stretch="Uniform"/>
                </Grid.Background>
            </Grid>
        </DataTemplate>
    </GridView.ItemTemplate>

    2. The data context has a property of type Windows::UI::Xaml::Media::ImageSource^. The get() function provides an object of BitmapImage. The BitmapImage-instance is created only by first call of the get(). I want to be notified, if the ImageOpened/Failed event is triggered, to raise a PropertyChanged event for "Icon" and to force a show of this image again (in this case, the BitmapImage-instance is already there and ready to be showed).

    Best regards, Anry

  • Friday, June 22, 2012 12:50 PM
     
      Has Code

    Don know if it help to you, but i'm loading images this way :

    void TCover::OnLoadComplete(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
      if( m_parent != nullptr)
      {
        m_parent->RaisePropertyChanged( "AlbumArt"); // this is calling notify
      }
    }
    
    void TCover::OnLoadFailed(Platform::Object^ sender, Windows::UI::Xaml::ExceptionRoutedEventArgs^ e)
    {
      #ifdef _DEBUG
        OutputDebugString( ("Cannot open file - error: " + e->ErrorMessage)->Data());
      #endif
    }
    
    void TCover::loadCoverFromURI()
    {
      String^ uri = m_name;
      m_image = ref new BitmapImage(ref new Uri( uri));
      m_image->ImageOpened += ref new Windows::UI::Xaml::RoutedEventHandler(this, &TCover::OnLoadComplete);
      m_image->ImageFailed += ref new Windows::UI::Xaml::ExceptionRoutedEventHandler(this, &TCover::OnLoadFailed);
    }