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

StorageFile.GetFileFromPathAsync #4844

Closed
hu13 opened this issue Nov 2, 2024 · 13 comments
Closed

StorageFile.GetFileFromPathAsync #4844

hu13 opened this issue Nov 2, 2024 · 13 comments

Comments

@hu13
Copy link

hu13 commented Nov 2, 2024

I am have been trying to debug an issue i have with StorageFile.GetFileFromPathAsync.

In my use case, i am accessing this API via a winrt-go (https://github.com/hu13/winrt-go).

I have a test that creates the test file in a os.TempDir's location which belongs in: ~/AppData/Local/Temp/myDir/myTestFile.txt, then i call the getfilepath api and it keeps returning UnauthorizedAccessException error aka 0x80070005.
Some stacktrace:
Exception 0xc0000005 0x1 0x50 0x7ffe99921c58
PC=0x7ffe99921c58

runtime.cgocall(0x795080, 0xc00007a330)
C:/hostedtoolcache/windows/go/1.23.2/x64/src/runtime/cgocall.go:167 +0x3e fp=0xc000043c60 sp=0xc000043bf8 pc=0x78439e
syscall.SyscallN(0xc00003caa0?, {0xc00020e318?, 0xc00013f160?, 0xb4f765?})
C:/hostedtoolcache/windows/go/1.23.2/x64/src/runtime/syscall_windows.go:519 +0x46 fp=0xc000043c80 sp=0xc000043c60 pc=0x78f506
github.com/saltosystems/winrt-go/windows/storage.StorageFileGetFileFromPathAsync({0xc00003caa0, 0x53})

This only happens in github action VM that runs windows server 2022. The same test passes on my physical windows PC.
From my understanding, the StorageApi should have access to TemporaryFolder by default.

Any ideas? Thanks

@whiskhub
Copy link

whiskhub commented Nov 2, 2024

The ApplicationData.TemporaryFolder property only works in (MSIX) packaged apps. It returns a special temporary directory for your MSIX package, like %LOCALAPPDATA%\Packages\PackageName_CompanyName\TempState.

But you seem to have an issue with StorageFile.GetFileFromPathAsync. I know on Windows Server, there is often just one Administrator user that is always elevated. And these WinRT APIs often have difficulties with elevated users. But the StorageFile APIs should normally work.

Can you confirm the directory created by os.TempDir is really ~/AppData/Local/Temp and not C:\Windows\Temp? What happens if you try to open the file with the Go filesystem functions?

@mominshaikhdevs
Copy link

I dont think Windows.Storage.* Family APIs work properly outside AppContainer.

ref : #8 (comment)

@DarranRowe
Copy link

@mominshaikhdevs
In the end, it depends on what your definition of "properly" is. For file access, they were initially designed to be used with UWP applications running inside an AppContainer, these runtime classes should go through a broker. I believe the issue is that they always go through a broker. Because of this, they are slow.

Folder access, such as ApplicationData, only require a package and they work perfectly fine outside of an AppContainer. This doesn't include UserDataPaths since this provide paths to the user profile and will work without a package.

There is more here, but I haven't really dug into them properly. But they should all be usable if you just double check the documentation and avoid problematic things like app specific URIs.

@hu13
Copy link
Author

hu13 commented Nov 4, 2024

@whiskhub
os.TempDir() on github action windows-latest returns C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\
yes, go os.Open(...) has no trouble accessing that path.

hmm, are you saying Windows.Storage API is not designed to be used outside the context of MSIX package app? @mominshaikhdevs

@hu13
Copy link
Author

hu13 commented Nov 4, 2024

@DarranRowe can you elaborate more on how i can use the Storage api outside the AppContainer?

basically, my project set up is like this. I am using cldapi.dll to create a sync root for a cloud provider service that i built.
and i am integrating winRT in order to interact with file explorer UI.
so, this is not really a traditional MSIX packaged app.

@DarranRowe
Copy link

@hu13

Not really, since I have already stated the important points.

I guess the only thing to mention is that getting a StorageFolder to the profile's temporary directory isn't obvious. An unpackaged application would normally use UserDataPaths. But like SHGetKnownFolderPath that it takes the naming pattern from, UserDataPaths doesn't expose a temporary directory.

There are only two ways that I know how to make a StorageFolder to the temporary directory. The first is to query Windows (C++ GetTempPath) and then use that in GetFolderFromPathAsync.

#include <Windows.h>
#include <cstdio>

#undef GetCurrentTime

#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Storage.h>

int wmain()
{
	winrt::init_apartment(winrt::apartment_type::multi_threaded);

	wchar_t temp_dir[MAX_PATH + 1]{};
	GetTempPathW(MAX_PATH + 1, temp_dir);

	auto a_storage_folder = winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(temp_dir);
	auto storage_foler = a_storage_folder.get(); //StorageFolder containing the temporary directory path.

	return 0;
}

The second would be to use UserDataPaths.LocalAppData and then use that. The user's temporary directory is normally Temp in the user's local appdata directory.

@mominshaikhdevs
Copy link

mominshaikhdevs commented Nov 4, 2024

@hu13 AppContainer and MSIX are totally two independent things. AppContainer is built on top of LowIL Process, where MediumIL is Full Trust Process, HighIL is Elevated/Admin Process. these are kernel-mode "processes integrity levels" deeply rooted into the kernel. Broker Processes for LowIL Processes are very poorly implemented in Windows (because targeted
Windows 8 era lightweight apps). MSIX is just a glorified "zip package format" that is managed by Windows itself and hence, all these (buggy) "(MSIX) Package Deployment Manager" Family APIs exist.

Windows.Storage.* Family APIs mostly just happens to be, tied to both AppContainer and MSIX. And we all are in a big, slow, poorly implemented, useless, buggy mess.

@RDMacLachlan RDMacLachlan added area-Win32 Support for Win32 packaged and non-packaged apps area-File access and removed needs-triage area-Win32 Support for Win32 packaged and non-packaged apps labels Nov 4, 2024
@RDMacLachlan
Copy link
Member

Please provide a complete repro, that is a simple targeted one.

@hu13
Copy link
Author

hu13 commented Nov 5, 2024

@RDMacLachlan
Repo: https://github.com/hu13/winrt-go/blob/hangkun/poc/windows/storage/storagefile_test.go#L75

Action: https://github.com/hu13/winrt-go/actions/runs/11692496497/job/32561988370 this is the smallest reproducible test with the following set up:

  1. create a temp directory inside ~/AppData/Local/Temp.
  2. create a file inside the temp dir.
  3. use winRT's storage.GetFileFromPathAsync(fpath) to get the storage file.

if you checkout the branch and run this test locally on a windows machine, it will work. However, it does not work on github action windows VM.

@hu13
Copy link
Author

hu13 commented Nov 6, 2024

i got my set up to work now.
it seems like that there is a difference between golang's os.TempDir() vs. os.UserCacheDir() on windows github action.
the former returns: C:\\Users\\RUNNER~1\\AppData\\Local\Temp\\
and the latter returns: C:\\Users\\runneradmin\\AppData\\Local\

storage.GetFileFromPathAsync(fpath) works on any path created under the latter.

@DarranRowe
Copy link

DarranRowe commented Nov 8, 2024

@hu13
Your latest post contradicts your earlier post.
In the post from the 4th November (this is what Github reports in my timezone), you state that os.TempDir() returns C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\. Notice that there are two backslashes between Local and Temp.
However, in the post from the 6th November(this is what Github reports in my timezone), you state that os.TempDir() returns C:\\Users\\RUNNER~1\\AppData\\Local\Temp\\. Notice the single backslash between Local and Temp.

I ask this because Go is using double backslashes for a reason, and a malformed path can cause problems.

Besides this, making the assumption that the single backslash is a mistake, have you tried using the long filename version of the path for temp. For example, instead of using os.TempDir(), have you tried hardcoding the path C:\\Users\\runneradmin\\AppData\\Local\\Temp\\? Testing it by hardcoding C:\\Users\\RUNNER~1\\AppData\\Local\\ to see if the short path version of os.UserCacheDir() would work too.

I will emphasise that these hardcoded paths would just be for testing purposes to understand why the calls are failing. This isn't a suggestion to use hardcoded paths as a solution to the problem.

@DrusTheAxe
Copy link
Member

In the end, it depends on what your definition of "properly" is. For file access, they were initially designed to be used with UWP applications running inside an AppContainer, these runtime classes should go through a broker. I believe the issue is that they always go through a broker

Not always. They're designed to work in an AppContainer and in MediumIL. These APIs often run inproc when called in a MediumIL process.

That said, Win32 GetTempPath(), C++ std::filesystem::temp_directory_path(), C# System.IO.Path.GetTempPath(), Python tempfile.gettempdir(), etc all work just fine. Is there a reason this isn't sufficient?

Copy link
Contributor

This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 7 days. It will be closed if no further activity occurs within 7 days of this comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants