简体   繁体   中英

ofstream.is_open() always false in DirectX12 application using VS 2015

I've been trying for hours now, and I can't for the life of me get my DirectX12 application to write a simple file...

A bit about my setup:

  • Windows 10 updated.
  • DirectX12 default "spinning cube" application. DirectX 12 App (Universal Windows)
  • Visual Studio 2015

I am doing this:

ofstream outFile;
// I have even tried with \\ slashes...
outFile.open("c://Users//pookie//Documents//WHERETHEFISMYFILE.txt");
if (outFile.is_open())
{
    outFile << "Writing this to a file.\n";
    outFile.close();
}

What I have tried (almost everything under the sun and the kitchen sink):

  • I've also tried using fstream , wfstream as well as doing !outfile.fail()
  • I've checked every directory in my project, and even ventured out into the Microsoft DirectX SDK.
  • I've tried relative paths: outFile.open("WHERETHEFISMYFILE.txt");
  • I've tried setting an absolute path.
  • I've tried adding permissions to the folder by allowing "everyone" and giving full access - just for sanity.
  • I've also tried getting the current working directory, which is C:\\Users\\pookie\\Documents\\Visual Studio 2015\\Projects\\demoDX12\\x64\\Debug\\demoDX12\\AppX and setting it to c:\\
  • I have created the file manually, in every folder of my project...
  • I've tried in both Debug and Release configs, as well as x86 and x64 and all possible combinations thereof
  • I've tried \\\\ as well as // in my file path
  • I've tried replacing spaces in path with %20
  • I have also tried running Visual Studio in admin mode.

The problem occurs here: if (outFile.is_open()) . For some reason, it always returns false.

What am I doing wrong here?

UPDATE: To rest my mind, I tried an empty console application with the following code:

#include <fstream>
#include <iostream>

using namespace std;

int main()
{
    try
    {
        wfstream  outFile;
        outFile.open("C:\\Users\\pookie\\Documents\\WHERETHEFISMYFILE.txt");
        if (outFile.is_open())
        {
            outFile << "Writing this to a file.\n";
            outFile.close();
        }
        else
        {
            cout << "sdsadsdsd";
        }
    }
    catch (const std::exception& ex)
    {
        cout << ex.what();
    }
    return 0;
}

The result is the same: is_open() == false . I'm at a loss here guys.

Update 2:

As requested, I am updating this question to show the exact project I am working with. I am working with the default DirectX12 application - the spinning cube. I followed this tutorial

Within my project, there is a method called void DX::DeviceResources::Present() and it is within this method that I am trying to write to file (although I have tried this in numerous other places within this project, too.

Here it is:

// Present the contents of the swap chain to the screen.
void DX::DeviceResources::Present()
{
    // The first argument instructs DXGI to block until VSync, putting the application
    // to sleep until the next VSync. This ensures we don't waste any cycles rendering
    // frames that will never be displayed to the screen.
    HRESULT hr = m_swapChain->Present(1, 0);

    try
    {
        wfstream  outFile;
        std::string 
         //This has been done numerous ways, but ultimately, I believe that 
         //ios_base::out is required if the file does not yet exist.
        name("c:\\Users\\pookie\\Documents\\WHERETHEFISMYFILE.txt");
        outFile.open(name.c_str(), ios_base::out);
        if (outFile.is_open())
        {
            outFile << "Writing this to a file.\n";
            outFile.close();
        }
        else
        {
            cout << "sdsadsdsd";
        }
    }
    catch (const std::exception& ex)
    {
        cout << ex.what();
    }


    // If the device was removed either by a disconnection or a driver upgrade, we 
    // must recreate all device resources.
    if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
    {
        m_deviceRemoved = true;
    }
    else
    {
        DX::ThrowIfFailed(hr);

        MoveToNextFrame();
    }
}

Update 3

So, a blank project with file output works fine, if I use

name("c:\\Users\\pookie\\Documents\\WHERETHEFISMYFILE.txt");
outFile.open(name.c_str(), ios_base::out);

Note the ios_base::out

This is fine. However, this does still not work in the default DirectX12 application.

This is definitely an DirectX related issue. See this . I have tried doing as the solution in that post suggested, but I can still not get it to work.

I can also confirm that a brand new DirectX12 project has the same issue. Try it.

SOLUTION

Thanks to ebyrob, I have got this working. It turns out that these new Windows Apps can only write to certain folders... More specifically, this:

auto platformPath = ApplicationData::Current->RoamingFolder->Path;

Unfortunately, the path is not a standard string... so it must be converted first:

auto platformPath = ApplicationData::Current->RoamingFolder->Path;
std::wstring platformPathW(platformPath->Begin());
std::string convertedPlatformPath(platformPathW.begin(), platformPathW.end());

Then just add the file name:

std::string path = convertedPlatformPath + "\\WHERETHFISMYFILE.txt";

and finally:

try
{
    wofstream  outFile;
    char buff[256]; 
    outFile.open(path.c_str(), ios_base::out);
    if (outFile.is_open())
    {
        outFile << "Writing this to a file.\n";
        outFile.close();
    }
    else
    {
        cout << "Cannot open file " << name << ": " << strerror_s(buff,0) << endl;
    }
}
catch (const std::exception& ex)
{
    cout << ex.what();
}

Thank you ebyrob!!

See this answer:

std::fstream doesn't create file

You need to specify the correct options to wfstream::open() in order to create a new file. ::in without ::trunc will require an existing file.

Example:

outFile.open("C:\\Users\\pookie\\Documents\\WHERETHEFISMYFILE.txt", ios_base::out);

In this case, it seems there is another issue. The application is part of Windows 10 app store, so it can only access files in a few locations.

Please see this thread: Can't create file using fstream in windows store app 8.1

For how to open a file in local or roaming user profile directories which the app actually does get access to.

The root problem here is that the poster is writing a UWP, not a Win32 desktop app, which means the application does not have read or write access to arbitrary parts of the file system, specifically C:\\\\Users\\\\pookie\\\\Documents\\\\WHERETHEFISMYFILE.txt .

The use of DirectX 12 or not has no bearing on the problem.

The only parts of the file system that a UWP can directly access are:

  • read-only access to the installed packaged directory. Windows::ApplicationModel::Package::Current->InstalledLocation

  • read-write access to an isolated temporary file directory. Windows::Storage::ApplicationData::Current->TemporaryFolder

  • read-write access to an isolated per-user/per-application file directory either local or roaming. Windows::Storage::ApplicationData::Current->LocalFolder or RoamingFolder ;

To get access to a user documents folder in a UWP requires using Windows Runtime file pickers in Windows.Storage.Pickers or via StorageFile functionality with an appropriate declared capability. For such protected files, you also can't use C++ iostream operations directly as they might not even be a file on the local file system. You could use Windows Runtime APIs to copy the file to one of the locations above, and then use traditional C/C++ functions on the copy there.

Here is an example of using C/C++ functions to read a user file located in a protected area like user documents in UWP:

#include <ppltasks.h>
using namespace concurrency;

using Windows::Storage;
using Windows::Storage::Pickers;

create_task(openPicker->PickSingleFileAsync()).then([](StorageFile^ file)
{
    if (file)
    {
        auto tempFolder = Windows::Storage::ApplicationData::Current->TemporaryFolder;
        create_task(file->CopyAsync(tempFolder, file->Name, NameCollisionOption::GenerateUniqueName)).then([](StorageFile^ tempFile)
        {
            if (tempFile)
            {
                ifstream inFile(tempFile->Path->Data())
                ...
            }
        });
    });
}

Here is an example of using C/C++ functions to write a user file located in a protected area like user documents in UWP:

#include <ppltasks.h>
using namespace concurrency;

using Windows::Storage;
using Windows::Storage::Pickers;

auto folder = Windows::Storage::ApplicationData::Current->TemporaryFolder;

WCHAR fname[ _MAX_PATH ];
wcscpy_s( fname, folder->Path->Data() );
wcscat_s( fname, L"\\MYFILE.txt" );

ofstream outFile;
outFile.open(fname, ios_base::out);

...

outFile.close();

create_task(savePicker->PickSaveFileAsync()).then([](StorageFile^ file)
{
    if ( file )
    {
        auto folder = Windows::Storage::ApplicationData::Current->TemporaryFolder;
        auto task = create_task( folder->GetFileAsync("MYFILE.txt") );
        task.then([file](StorageFile^ tempFile) ->IAsyncAction^
        {
            return tempFile->MoveAndReplaceAsync( file );
        });
    }
});

See File access and permissions (Windows Runtime apps) and Dual-use Coding Techniques for Games

Note that this model should be familiar to anyone who followed the Windows Vista era User Account Control guidelines where your install directory should be read-only (ie under C:\\Program Files ), and you were supposed to use SHGetFolderPath with CSIDL_LOCAL_APPDATA / CSIDL_APPDATA for application data, and use GetTempPath for temporary directories you have read/write access to. With UWP, the rest of the file system is explicitly protected which for UAC it was not.

I don't know what exactly is going wrong, but I hope this code will help you to diagnose the problem:

#include <iostream>
#include <fstream>
#include <string>
#include <string.h>

using namespace std;

int main()
{
    ofstream outFile;
    std::string name("c://Users//pookie//Documents//WHERETHEFISMYFILE.txt");
    outFile.open(name.c_str());
    if(outFile.is_open()) {
        outFile << "Writing this to a file.\n";
        outFile.close();
    } else {
        cout << "Cannot open file " << name << ": " << strerror(errno) << endl;
    }
}

This code has been tested on MSVC++ here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM