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

    Question

  • 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
    Tuesday, June 19, 2012 6:15 AM

All replies

  •  

    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 4:52 AM
  • How are these defined?

    m_mapPictures1

    m_mapPictures2


    David Lamb

    Wednesday, June 20, 2012 5:30 AM
    Moderator
  • 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 8:31 PM
  • 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

    Wednesday, June 20, 2012 11:13 PM
    Moderator
  • 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

    Thursday, June 21, 2012 10:54 AM
  • 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);
    }



    Friday, June 22, 2012 12:50 PM