microsoft / microsoft/react-native-windows-samples

UIDispatcher how to return value/resolve/reject promise without crash? (Documentation unlcear)

Open
#603 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

documentation enhancement
Dominant language
C#
Stars
544
Forks
218
PR merge metrics
No merged PRs in 30d

Description

Problem Description

I have two small native c++ modules which use the Launcher::LaunchFileAsync and FileOpenPicker::pickSingleFileAsync.

After upgrading from 0.63 to 0.65 we have to use the UIDispatcher in order for it to work. So I had a look at the following Documentation:
Using UIDispatcher with C++/WinRT

Unfortunately, the example lacks the bit about how to return a value from there (in that example, that would probably be the file.Path()).

So I digged a bit further and had a look at the following 4 modules inside react-native-windows:
Clipboard Module
AccessibilityInfoModule
ImageViewManagerModule
AlertModule

I tried multiple different variations now and had a look at the crash dump but I always crash with an Access Violation on the promise.resolve line.

What am I doing wrong there? I don't see a lot of differences between my code and the above modules despite the co_await usage.
I'm also having the same problem with FileOpenPicker where I'd want to resolve the tempFile.Path() after picking+moving it to the apps temp folder (StorageFile tempFile{ co_await file.CopyAsync(winrt::Windows::Storage::ApplicationData::Current().TemporaryFolder(), file.Name(), NameCollisionOption::ReplaceExisting) };)

Steps To Reproduce
  1. Init a new project
  2. Add the FileOpener posted below to the project
  3. Add an openable file to the application folder (for example a pdf)
  4. Call NativeModules.FileOpener.openFileAsync(filePath, shouldShowOpenWithDialog)
Expected Results

Not crash, resolve the promise to JS.

Documentation to reflect an actual usage of FileOpenPicker/UIDispatcher with result returns/promise.resolve.

CLI version

6.1.0

Environment
info Fetching system and libraries information...
System:
    OS: Windows 10 10.0.19043
    CPU: (12) x64 AMD Ryzen 5 5600X 6-Core Processor
    Memory: 24.72 GB / 31.94 GB
  Binaries:
    Node: 14.16.0 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.11 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 6.14.11 - C:\Program Files\nodejs\npm.CMD
    Watchman: Not Found
  SDKs:
    Android SDK:
      API Levels: 28, 29, 30
      Build Tools: 29.0.0, 29.0.1, 29.0.2, 29.0.3, 30.0.0, 30.0.1, 30.0.2, 30.0.3
      System Images: android-28 | Google APIs Intel x86 Atom, android-29 | Google APIs Intel x86 Atom, android-30 | Google APIs Intel x86 Atom
      Android NDK: Not Found
    Windows SDK:
      AllowDevelopmentWithoutDevLicense: Enabled
      AllowAllTrustedApps: Enabled
      Versions: 10.0.16299.0, 10.0.17134.0, 10.0.17763.0, 10.0.18362.0, 10.0.19041.0
  IDEs:
    Android Studio: Not Found
    Visual Studio: 16.9.31313.79 (Visual Studio Community 2019)
  Languages:
    Java: 1.8.0_282 - /c/Program Files/OpenJDK/openjdk-8u282-b08/bin/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: 17.0.2 => 17.0.2
    react-native: 0.65.2 => 0.65.2
    react-native-windows: ^0.65.7 => 0.65.7
  npmGlobalPackages:
    *react-native*: Not Found
Target Platform Version

10.0.19041

Target Device(s)

Desktop

Visual Studio Version

Visual Studio 2019

Build Configuration

Release

Snack, code example, screenshot, or link to a repository

FileOpener.h

#pragma once

#include "pch.h"
#include "NativeModules.h"
#include <string>

namespace RN = winrt::Microsoft::ReactNative;

namespace inspection::FileOpener {
    REACT_MODULE(FileOpener, L"FileOpener");
    struct FileOpener final {
        RN::ReactContext m_reactContext;

        REACT_INIT(Initialize)
            void Initialize(RN::ReactContext const& reactContext) noexcept;

        REACT_METHOD(openFileAsync)
        winrt::fire_and_forget openFileAsync(std::string filepath, bool showOpenWithDialog, RN::ReactPromise<void> promise) noexcept;
    };
}

FileOpener.cpp

#include "pch.h"

#include "FileOpener.h"

#include <filesystem>
#include <windows.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.System.h>

using namespace winrt;
using namespace winrt::Windows::Storage;
using namespace winrt::Windows::Foundation;
using namespace Windows::System;

namespace inspection::FileOpener {
    void FileOpener::Initialize(RN::ReactContext const& reactContext) noexcept {
        m_reactContext = reactContext;
    }

    winrt::fire_and_forget FileOpener::openFileAsync(std::string filepath, bool showOpenWithDialog, RN::ReactPromise<void> promise) noexcept
    try {
        std::filesystem::path fPath{ filepath };
        fPath.make_preferred();
        auto jsDispatcher = m_reactContext.JSDispatcher();

        try {
            StorageFile file{ co_await StorageFile::GetFileFromPathAsync(winrt::to_hstring(fPath.c_str())) };
            try {
                if (file) {
                    m_reactContext.UIDispatcher().Post([showOpenWithDialog, file, promise, fPath, jsDispatcher]()->winrt::fire_and_forget {
                        LauncherOptions launchOptions;
                        launchOptions.DisplayApplicationPicker(showOpenWithDialog);
                        bool success{ co_await Launcher::LaunchFileAsync(file, launchOptions) };
                        if (success) {
                            jsDispatcher.Post([promise] { promise.Resolve(); });
                        } else {
                            jsDispatcher.Post([promise, fPath] { promise.Reject(RN::ReactError{ "Unable to open File", winrt::to_string(fPath.c_str()) }); });
                        }
                    });
                } else {
                    promise.Reject(RN::ReactError{ "Unable to open File", winrt::to_string(fPath.c_str()) });
                }
            } catch (const hresult_error& ex) {
                promise.Reject(RN::ReactError{ "Unable to LaunchFileAsync for File " + filepath, winrt::to_string(ex.message()).c_str() });
            }

        } catch(const hresult_error& ex) {
            promise.Reject(RN::ReactError{ "Unable to GetFileFromPathAsync for File " + filepath, winrt::to_string(ex.message()).c_str() });
        }

    } catch(const hresult_error& ex) {
        promise.Reject(RN::ReactError{ "Unable to make path or make_preferred for File " + filepath, winrt::to_string(ex.message()).c_str() });
    }
}

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked “Using UIDispatcher with C++/WinRT” documentation and compare its example with ClipboardModule.cpp, AccessibilityInfoModule.cpp, ImageViewManagerModule.cpp, and AlertModule.cpp. Clarify how a UIDispatcher callback returns or rejects a promise, including results from FileOpenPicker or LaunchFileAsync, and ensure the documented usage does not crash.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, react-native
Domain
desktop, documentation
Issue type
Documentation
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.