locked
Get std::ifstream for StorageFile^

    Question

  • I have a library that takes a file name as a  std::string or a std::basic_istream.  Is there some way to get a Platform::String^ that I can actually open a std::ifstream with, or is there a way to get a std::basic_istream from a Windows::Storage::StorageFile^?  The file that would be opened is potentially very large, and I only need enough of it opened (in a file buffer or something) at once to do a linear parse.
    Thursday, October 11, 2012 12:20 AM

Answers

  • Well, there is a clumsy way to get an ifstream from a StorageFile which involves copying the file into your local folder and then you can use ifstream to read that file. 

    void sampleCppFileRead()
    {
    	using namespace Windows::Storage;
    	using namespace Windows::Storage::Pickers;
    	using namespace Windows::Storage::Streams;
    	using namespace concurrency;
    
    
    	auto picker= ref new FileOpenPicker();
    	picker->CommitButtonText= "Read from a file with ifstream";
    	picker->ViewMode = PickerViewMode::List;
    	picker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary;
       	picker->FileTypeFilter->Append(".txt");	// Required 
    	picker->FileTypeFilter->Append("*");
    
    	Windows::Storage::StorageFolder ^ localFolder= Windows::Storage::ApplicationData::Current->LocalFolder;
    
    	task< StorageFile^ > getFileTask ( picker->PickSingleFileAsync() ); 
    
    	getFileTask.then( [=] ( StorageFile^ storageFile ) 
    	{
    		if ( nullptr == storageFile ) return;
    
    		Platform::String ^ destfilename = storageFile->Name + ".copy";
    
    		auto option= Windows::Storage::NameCollisionOption::ReplaceExisting;
    		create_task( storageFile->CopyAsync( localFolder, destfilename, option)).then([=](StorageFile^ sampleFileCopy) 
    		{ 
    			// Get a ascii version of the copied file path
    			std::string localPath= RtplusPlatform::String::to_string( sampleFileCopy->Path );
    
    			// Traditional C++ continues
    			std::ifstream	r;
    			r.open( localPath.data() );
    			if ( r.is_open() ) {
    				char line[999];
    				r.getline( line, 999 );
    				r.close();
    			}
    			
    		}); 
    	});
    
    }
    
    and 
    
    // Convert from wstring into a string
    std::string to_string( const std::wstring & wtxt )
    {
    	std::string str;
    	str.resize( wtxt.size() + 1 );
    
    	size_t numConverted;
    	errno_t		err= wcstombs_s( &numConverted, (char *)str.data(), str.size(), wtxt.data(), wtxt.size() );
    
    	str.pop_back();
    	return str;
    }
    
    
    
    std::string to_string( Platform::String^ pstring )
    {
    	std::wstring wStr( pstring->Data() );
    	return to_string( wStr );
    }
    

    I tried it once and it did read the first line.

    p.s. Sinofsky told the crowd of developers today that developers should have their own choice of platforms.  "You pick the language you want to use, and you create your own Metro-style applications with Windows 8...  We're bringing forward all the skills that you've established, and all the code that you've built."  In order to make that true for C++ and Store App programs there needs to be an interface between Standard C++ concepts like the iostreams and the WinRT API.

    • Proposed as answer by Jesse Jiang Tuesday, October 16, 2012 6:30 AM
    • Marked as answer by Jesse Jiang Tuesday, October 16, 2012 6:30 AM
    Saturday, October 13, 2012 6:05 PM

All replies

  • According to this post

    http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/36091428-a9c5-4089-8c76-9191a418954c,

    you may have to open the file with FileOpenPicker and access the file content using the stream returned by StorageFile.OpenAsync. The canonical ways of C/C++ to read/write files are not allowed in Metro apps.

    Thursday, October 11, 2012 1:05 AM
  • I vaguely remember seeing some way of getting a C++ stream from a WinRT stream.  I think it was in some presentation at Build/2011.
    Thursday, October 11, 2012 2:39 AM
  • I have been looking at it, and it looks like you should be able to wrap an IInputStream^ or IOutputStream^ in a std::basic_streambuf.  Is there any chance that they have already done this in the C++ language projection?  It should would be convenient...
    Thursday, October 11, 2012 6:22 PM
  • I wish such a wrap exists. But I could not find an article or reference on it and no MSFT employee come to this post offering their insights.
    Saturday, October 13, 2012 1:10 PM
  • Well, there is a clumsy way to get an ifstream from a StorageFile which involves copying the file into your local folder and then you can use ifstream to read that file. 

    void sampleCppFileRead()
    {
    	using namespace Windows::Storage;
    	using namespace Windows::Storage::Pickers;
    	using namespace Windows::Storage::Streams;
    	using namespace concurrency;
    
    
    	auto picker= ref new FileOpenPicker();
    	picker->CommitButtonText= "Read from a file with ifstream";
    	picker->ViewMode = PickerViewMode::List;
    	picker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary;
       	picker->FileTypeFilter->Append(".txt");	// Required 
    	picker->FileTypeFilter->Append("*");
    
    	Windows::Storage::StorageFolder ^ localFolder= Windows::Storage::ApplicationData::Current->LocalFolder;
    
    	task< StorageFile^ > getFileTask ( picker->PickSingleFileAsync() ); 
    
    	getFileTask.then( [=] ( StorageFile^ storageFile ) 
    	{
    		if ( nullptr == storageFile ) return;
    
    		Platform::String ^ destfilename = storageFile->Name + ".copy";
    
    		auto option= Windows::Storage::NameCollisionOption::ReplaceExisting;
    		create_task( storageFile->CopyAsync( localFolder, destfilename, option)).then([=](StorageFile^ sampleFileCopy) 
    		{ 
    			// Get a ascii version of the copied file path
    			std::string localPath= RtplusPlatform::String::to_string( sampleFileCopy->Path );
    
    			// Traditional C++ continues
    			std::ifstream	r;
    			r.open( localPath.data() );
    			if ( r.is_open() ) {
    				char line[999];
    				r.getline( line, 999 );
    				r.close();
    			}
    			
    		}); 
    	});
    
    }
    
    and 
    
    // Convert from wstring into a string
    std::string to_string( const std::wstring & wtxt )
    {
    	std::string str;
    	str.resize( wtxt.size() + 1 );
    
    	size_t numConverted;
    	errno_t		err= wcstombs_s( &numConverted, (char *)str.data(), str.size(), wtxt.data(), wtxt.size() );
    
    	str.pop_back();
    	return str;
    }
    
    
    
    std::string to_string( Platform::String^ pstring )
    {
    	std::wstring wStr( pstring->Data() );
    	return to_string( wStr );
    }
    

    I tried it once and it did read the first line.

    p.s. Sinofsky told the crowd of developers today that developers should have their own choice of platforms.  "You pick the language you want to use, and you create your own Metro-style applications with Windows 8...  We're bringing forward all the skills that you've established, and all the code that you've built."  In order to make that true for C++ and Store App programs there needs to be an interface between Standard C++ concepts like the iostreams and the WinRT API.

    • Proposed as answer by Jesse Jiang Tuesday, October 16, 2012 6:30 AM
    • Marked as answer by Jesse Jiang Tuesday, October 16, 2012 6:30 AM
    Saturday, October 13, 2012 6:05 PM
  • I am working on a class derived from std::basic_streambuf that wraps a StorageFile^, but it is taking more time that I would like.  The biggest issue comes from the fact that all of the IOStreams functions are synchronous, while the StorageFile functions are asynchronous.  I have figured out how to pretend that the StorageFiles are synchronous, but it could take a while to finish up.
    • Proposed as answer by Jesse Jiang Tuesday, October 16, 2012 6:30 AM
    Monday, October 15, 2012 7:03 PM