Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FolderPicker works fine on most user systems but on some systems it gives exception 0x80004005 #4625

Open
JosHuybrighs opened this issue Aug 9, 2024 · 6 comments
Labels
area-File access bug Something isn't working

Comments

@JosHuybrighs
Copy link

JosHuybrighs commented Aug 9, 2024

Describe the bug

I have a strange exception that some users have reported to occur when using FolderPicker as follows:

var folderPicker = new Windows.Storage.Pickers.FolderPicker();
WinRT.Interop.InitializeWithWindow.Initialize(folderPicker, MainWindow.sWindowHandle);
folderPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.DocumentsLibrary;
folderPicker.FileTypeFilter.Add("*");

var chosenFolder = await folderPicker.PickSingleFolderAsync();

The picker popup isn't displayed and using a try/catch block and some logging in the app I can see that exception 0x80004005 is raised with an empty Exception.Message.

The window handle which is required in WinUI3/WinAppSDK is assigned by getting it from a static property in MainWindow.
Assignment of the static is done in the constructor of MainWindow, like so:

public sealed partial class MainWindow : Window
{
    public static Page sCurrentPage { get; private set; }
    public static IntPtr sWindowHandle { get; private set; }
    public AppWindow MainAppWindow { get; private set; }

    PageNavigation _pageNavigation;

    public MainWindow()
    {
        this.InitializeComponent();
        Title = "SyncFolder";

        sWindowHandle = WinRT.Interop.WindowNative.GetWindowHandle(this);
        WindowId myWndId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(sWindowHandle);
        MainAppWindow = AppWindow.GetFromWindowId(myWndId);

        ..
    }
    ..
}

The app is available on the Store and most users don't seem to have a problem.

Some thoughts:

  • Is the way I use a once-set static property to get the window handle correct or should I retrieve it by calling a static method in MainWindow/App and get it there again?
  • Could it be that for some users access to the file system is disabled? My manifest file only has capability "runFullTrust" set.

Steps to reproduce the bug

Can't reproduce this.
The error only occurs on some systems.

Expected behavior

No response

Screenshots

No response

NuGet package version

WinUI 3 - Windows App SDK 1.5.5: 1.5.240607001

Windows version

No response

Additional context

No response

@JosHuybrighs JosHuybrighs added the bug Something isn't working label Aug 9, 2024
Copy link

github-actions bot commented Aug 9, 2024

Hi I'm an AI powered bot that finds similar issues based off the issue title.

Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one. Thank you!

Closed similar issues:

Note: You can give me feedback by thumbs upping or thumbs downing this comment.

@codendone codendone transferred this issue from microsoft/microsoft-ui-xaml Aug 9, 2024
@DarranRowe
Copy link

DarranRowe commented Aug 9, 2024

Is the way I use a once-set static property to get the window handle correct or should I retrieve it by calling a static method in MainWindow/App and get it there again?

The window handle, aka the HWND, is the identity of the window. While the window that is created by Microsoft.UI.Xaml.Window stays alive, the HWND will never change. While this is wrapped up to look like the UWP model, this is still the desktop environment.
So caching the handle at the start will never be a problem, as long as the window stays alive.

Could it be that for some users access to the file system is disabled? My manifest file only has capability "runFullTrust" set.

Assuming the "runFullTrust" capability is there due to the package being full trust, either by setting the entry point to windows.fullTrustApplication or by setting uap10:TrustLevel to mediumIL, then capabilities don't really affect the application. The capabilities are there to tell the appcontainer what it is allowed to access, not the package. So when the application is running outside of an appcontainer, which is the case when you set the package to windows.fullTrustApplication/mediumIL, then you have almost the same environment as if the application is running unpackaged. The only difference shouldn't have an effect on the pickers, since registry writes to HKCU and filesystem writes to AppData should be redirected to the package profile directory.

I assume the above because of how infrequently you stated the problem occurs. But just to mention it. If your application is running as partial trust, either by setting the entry point to windows.partialTrustApplication or by setting uap10:TrustLevel to appContainer, then it will be running into the long running problem of pickers not being allowed to set a HWND as the parent.

The only thing I can think of is when the Documents library has been redirected. While the Documents library is at %USERPROFILE%\Documents by default, it is possible to change where it is stored.

Screenshot 2024-08-09 221857

It is also possible to add directories to the library too. So I'm wondering if it has been redirected by OneDrive or something else on the systems which have failed.

@JosHuybrighs
Copy link
Author

@DarranRowe
As allways, thanks for the detailed answer and clarifications.

In the mean time I got additional feedback from a user stating that the issue occurs when the app is opened "as admin". I then tested this also and indeed also get the exception when running as admin.
I did some lookup and some sources say that this is because the way FolderPicker works. It is part of the Windows Shell which can't run in an elevated admin context. Not sure if this is correct but if so how would I then have to deal with this if, for some reason the user decides to open the app with "Open as admin"?

BTW: The reason the user opens the app "as admin" is because the app when opened in the normal user context failed to respond on any click command and crashes. Windows Event Viewer lists an error indicating that the Microsoft.UI.Input.dll can't be accessed (with exception 0xC0000602). It turns out that the user's 3D Connection mouse driver is causing this. With the mouse driver disabled the app runs without problems.
The Microsoft.UI.Input.dll is a key file in any WinUI/WinAppSDK desktop app and it bothers me that an external driver can prevent the app from working.
Maybe needs an extra "discussion" topic in WINUI / WinAppSDK.

@whiskhub
Copy link

It's a known issue that the File/FolderPickers don't work as admin... #2504

Workaround is to revert to the good old Win32/COM API: https://learn.microsoft.com/en-us/uwp/api/windows.storage.pickers.filesavepicker?view=winrt-22621#in-a-desktop-app-that-requires-elevation

@DarranRowe
Copy link

@JosHuybrighs
I believe that, if your application has the option for running as Admin then you should be using the Common Item Dialogs exclusively. It takes a little bit of work to make them async but it is doable. The folder picker is part of the file open picker implementation.

But I also believe that more of a focus should be placed on the underlying issue here. 0xC0000602 is the NTSTATUS STATUS_FAIL_FAST_EXCEPTION. Fail fasts are used in places where it would indicate that things are so broken that it is just better to kill the application. Knowing what causes this allows for proper investigation of the issue. In the end, getting the driver fixed is the best option all around.

@JosHuybrighs
Copy link
Author

@DarranRowe
Correct. I modified the picker to use a Win32 dialog.
Instead of adding all the dllimports and definitions myself (as I usually do) I decided to try the Win32 API Source Generator (github: Microsoft.Windows.CsWin32) and found out that this works wonderfully well. For anyone interested, the folder picker looks as follows:

using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.Com;
using Windows.Win32.UI.Shell;

namespace WinUIExtensions
{
    public class Win32FolderPicker
    {
        public static string PickSingleFolder(string defaultPath, nint hWnd)
        {
            try
            {
                int hr = PInvoke.CoCreateInstance<IFileOpenDialog>(typeof(FileOpenDialog).GUID,
                                                                   null,
                                                                   CLSCTX.CLSCTX_INPROC_SERVER,
                                                                   out var dialog);
                if (hr < 0)
                {
                    Marshal.ThrowExceptionForHR(hr);
                }

                // Set options to pick a folder
                dialog.SetOptions(FILEOPENDIALOGOPTIONS.FOS_PICKFOLDERS | FILEOPENDIALOGOPTIONS.FOS_FORCEFILESYSTEM);

                // Set the default folder.
                hr = PInvoke.SHCreateItemFromParsingName(defaultPath,
                                                         null,
                                                         typeof(IShellItem).GUID,
                                                         out var directoryShellItem);
                if (hr < 0)
                {
                    Marshal.ThrowExceptionForHR(hr);
                }

                dialog.SetFolder((IShellItem)directoryShellItem);
                dialog.SetDefaultFolder((IShellItem)directoryShellItem);

                dialog.Show(new HWND(hWnd));

                dialog.GetResult(out var ppsi);

                unsafe
                {
                    PWSTR filename;
                    ppsi.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, &filename);
                    var pathName = filename.ToString();
                    return pathName;
                }
            }
            catch (Exception e)
            {
                if ((e.HResult & 0xFFFFFF) == 0x000704C7)
                {
                    // User selected 'Cancel'
                    return null;
                }
                // Rethrow to the caller
                throw;
            }
        }

        public static async Task<string> PickSingleFolderAsync(string defaultPath, nint hWnd)
        {
            string pathName = null;
            await Task.Run(() =>
            {
                pathName = PickSingleFolder(defaultPath, hWnd);
            });
            return pathName;
        }

    }
}

and the following in NativeMethods.txt

CoCreateInstance
FileOpenDialog
IFileOpenDialog
SHCreateItemFromParsingName

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-File access bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants