IWICStream::InitializeFromFilename returns E_ACCESSDENIED

Nov 6, 2012 at 10:00 PM

I called ScreenGrab function of DirectXTK from my app and always get an E_ACCESSDENIED (General access denied error) for the statement

hr = stream->InitializeFromFilename( fileName, GENERIC_WRITE );

in line ScreenGrab.cpp:658. The saved file is in the Pictures Library and I've declared the "Picture Library" capabilities in Package.appxmanifest. Could some one point out where is wrong, thanks.

 

Coordinator
Nov 6, 2012 at 11:03 PM

This is because of the isolated access nature of the AppContainer. You don't ever get direct access rights to the user's Picture Library. You instead call the FileSavePicker and get back a StorageFile instance.

I recommend using ScreenGrab to save to the temporary folder you have full access to (Windows::Storage::ApplicationData::Current
->TemporaryFolder), and then use CopyAsync to put the file into the Picture Library. An example of this can be found in this thread.

Nov 6, 2012 at 11:32 PM

Thanks a lot for your quick response, I've already used the FileSavePicker for user to specify a filename to save. Here is the code

    if ( !EnsureUnsnapped() ) 
        return;

    // Prepare a file picker for customers to input image file name.
    Pickers::FileSavePicker^ savePicker = ref new Pickers::FileSavePicker();
    auto pngExtensions = ref new Platform::Collections::Vector<Platform::String^>();
    pngExtensions->Append(".png");
    savePicker->FileTypeChoices->Insert("PNG file", pngExtensions);
    auto jpgExtensions = ref new Platform::Collections::Vector<Platform::String^>();
    jpgExtensions->Append(".jpg");
    savePicker->FileTypeChoices->Insert("JPEG file", jpgExtensions);
    auto bmpExtensions = ref new Platform::Collections::Vector<Platform::String^>();
    bmpExtensions->Append(".bmp");
    savePicker->FileTypeChoices->Insert("BMP file", bmpExtensions);
    savePicker->DefaultFileExtension = ".png";
    savePicker->SuggestedFileName = "SaveScreen";
    savePicker->SuggestedStartLocation = Pickers::PickerLocationId::PicturesLibrary;

    // default format of saved image file
    std::shared_ptr<GUID> wicFormat = std::make_shared<GUID>(GUID_ContainerFormatPng);  

    // TODO: cannot understand the grammer here
    create_task(savePicker->PickSaveFileAsync()).then([=](StorageFile^ file)
    {
        if (file == nullptr)
        {
            // If user clicks "Cancel", reset the saving state, then cancel the current task.
            //m_screenSavingState = ScreenSavingState::NotSaved;
            cancel_current_task();
        }

        //m_imageFileName = file->Name;
        if (file->FileType == ".bmp")
        {
            *wicFormat = GUID_ContainerFormatBmp;
        }
        else if (file->FileType == ".jpg")
        {
            *wicFormat = GUID_ContainerFormatJpeg;
        }
        //return file->OpenAsync(FileAccessMode::ReadWrite);
        // Render the screen contents to an off-screen bitmap and then save that bitmap to a file.
        LPDXCONTEXT lpDxContext = (LPDXCONTEXT)mgral::getRenderingContext();
        ComPtr<ID3D11Texture2D> backBuffer;
        HRESULT hr = lpDxContext->swapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&backBuffer );
        if ( SUCCEEDED(hr) )
            hr = DirectX::SaveWICTextureToFile(lpDxContext->d3dContext.Get(), backBuffer.Get(), *wicFormat, file->Path->Data() );
    });

The LPDXCONTEXT struct contains the DX11 stuff like d3dDevice, d3dDeviceContext, swapChain and so on.
Nov 7, 2012 at 1:47 PM

Could you please elaborate how to create a file in the temporary folder? I use the code below but fail to get a reference to storage file using the CreateFileAsync method.

StorageFolder^ TempFolder = Windows::Storage::ApplicationData::Current->TemporaryFolder;

auto option= Windows::Storage::CreationCollisionOption::ReplaceExisting;
IAsyncOperation<StorageFile^>^ tempFile = TempFolder->CreateFileAsync(file->Name, option);

The CreateFileAsync method in C++ is different from that in C#, which returns a reference to storage file.

Coordinator
Nov 7, 2012 at 11:18 PM

You want to use ScreenGrab to 'create' the file in the temporary folder. You then write some code using WinRT to COPY it to the 'Pictures Folder'.