Win32 Desktop functions not available in WinUI 3 Runtime Component.
Describe the bug
Attempting to bring up a FilePicker in from a Windows Runtime Component which was started using the WinUI 3 template. When using #include <shobjidl_core.h> I get a large number of compile issues. It seems that the Component is not a Desktop component so large parts of the header are #ifdef'ed out.
Steps to reproduce the bug
Create a Windows Runtime Component with a Page and a button in which the handler brings up a FileOpenPicker, and reference it in a Blank Page App. The component has the following code:
#include <shobjidl_core.h> #include <winrt/Windows.Storage>
Windows::Foundation::IAsyncAction PopupPickerAsync() { auto picker = Windows::Storage::Pickers::FileOpenPicker();
picker.as<IInitializeWithWindow>()->Initialize(hwnd); // This was hard to find for C++ hwnd is in a static singleton in my app
.....
Windows::Storage::StorageFile infile = co_await picker.PickSingleFileAsync();j
}
Expected behavior
No response
Screenshots
No response
NuGet package version
1.0.0-preview3
Packaging type
Packaged (MSIX)
Windows version
Windows 11 (22000)
IDE
Visual Studio 2022
Additional context
No response
@evelynwu-msft is this a similar "Desktop APIs not available from C++" bug in 1.0 Preview 3? In this case it seems it's from a WinUI 3 Runtime Component (a library) rather than the app itself, but is it the same bug?
@andrewleader Similar, but it's a different bug. I suspect that the change we made ages ago to allow C++ Windows Runtime Components to be useable by Desktop WinUI apps (by setting <DesktopCompatible>true</DesktopCompatible>) doesn't actually lift the UWP-only API restriction.
The description for the Windows Runtime Component (WinUI 3) C++ project template says that it can be used in both UWP and desktop apps which means it shouldn't access Desktop only APIs.
Ok, then there is no way to bring up file/folder pickers from a windows runtime component since they now need access to the HWND and a Win32 only api to associate it with the picker.
The description for the Windows Runtime Component (WinUI 3) C++ project template says that it can be used in both UWP and desktop apps which means it shouldn't access Desktop only APIs.
This description is indicating that Windows Runtime components can target one or more device families, to include desktop PCs. It's perfectly valid to consume Desktop APIs and only target desktop PCs.
That is what I am trying to do and it does not work.
@timmarriott Every time you reply via email, a bunch of your signature comes along and trashes the issue. (GitHub isn't great in this area.) Can you clean up the thread here?
Sorry about that. Should be cleaned up now. Thanks for letting me know.
We just started writing a new Application and decided to use C++/WinRT. Initially this was limited to UWP which is not really what we wanted. When I saw the Windows App SDK I jumped on board and began to port my UWP app to that. I made heavy use of Windows Runtime Components which are great. We are also targeting Desktop PC as the platform.
One of my Runtime Components uses a FilePicker and a MessageBox. Both of these APIs now require the HWND and use a Win32 API to gain access to it. The issue is that I can not find a way to get the Win32 API able to compile and work because the Runtime Components do not appear to be desktop compatible. I replaced the MessageBox with a ContentDialog which works. I really don't want to write my own FilePicker.
The current work around is to host those pages from the top level Desktop App. This works but is not optimal and defeats my defined Architecture delivering reusable content via Runtime Components.
We just started writing a new Application and decided to use C++/WinRT. Initially this was limited to UWP which is not really what we wanted. When I saw the Windows App SDK I jumped on board and began to port my UWP app to that. I made heavy use of Windows Runtime Components which are great. We are also targeting Desktop PC as the platform.
One of my Runtime Components uses a FilePicker and a MessageBox. Both of these APIs now require the HWND and use a Win32 API to gain access to it. The issue is that I can not find a way to get the Win32 API able to compile and work because the Runtime Components do not appear to be desktop compatible. I replaced the MessageBox with a ContentDialog which works. I really don't want to write my own FilePicker.
The current work around is to host those pages from the top level Desktop App. This works but is not optimal and defeats my defined Architecture delivering reusable content via Runtime Components.
For file picker, you can use APIs in the Windows.Storage.Pickers* namespace instead of the old Win32 file picker APIs.
That is what I am using. I will try to explain the issue again.
In Windows App SDK the interface for FilePicker requires the HWND to be set for it to work. I grab the top level HWND when the App starts up and keep track of it on a View Model Singleton that is global.
Unfortunately to set this on the FileOpenPicker it seems to be required to us something like the following line:
picker.as<IInitializeWithWindow>()->Initialize(hwnd);
This code requires a Win32 header file #include <shobjidl_core.h>
This file will not compile in a Windows Runtime Component because most of it is not available since the runtime component does not make desktop interfaces available.
Workaround: Instead of including the header that contains the interface, copy just the interface definition and paste it into the source file that you're using it in.
Hi @timmarriott, here's a working contrived sample based on the WinUI 3 template for your reference https://github.com/riverar/was-repros/tree/master/was_issue_1780. Ideally, you wouldn't try to smuggle HWNDs into a Windows Runtime component, cache the IWindowNative, etc.
Some things to note:
- Looking at msbuild binlogs
<DesktopCompatible>doesn't seem to be wired into anything. It gets set byMicrosoft.Cpp.Default.props, reset by the component project, then never touched again? (@angelazhangmsft @andrewleader) - I had to manually define
WINAPI_PARTITION_DESKTOP(https://github.com/riverar/was-repros/blob/master/was_issue_1780/component/pch.h#L2) so something is broken (@angelazhangmsft @andrewleader) - The repro was incorrect in that you didn't
#include <winrt/Windows.Storage.Pickers>explicitly; it's also incorrectly formatted on GitHub (@timmarriott) - The Visual Studio projects all say (Universal Windows), which is bad UX
- FileTypeFilter doesn't support wildcards and will throw, despite what I found in an unrelated issue #1188 (@andrewleader).
I frankly dislike working with this API.
Thanks. I will take a look.
In the meantime, I am up and running with the work around idea provided. I basically pulled what I needed to get IInitializeWithWindow to work. The code I pulled was directly from the Win32 header with only a few small updates. The following code for your information: //#include <shobjidl_core.h> // <<< Can't include this header in a Windows Runtime Component. #ifndef IInitializeWithWindow_FWD_DEFINED #define IInitializeWithWindow_FWD_DEFINED typedef interface IInitializeWithWindow IInitializeWithWindow;
#endif /* IInitializeWithWindow_FWD_DEFINED */
#include <Unknwnbase.h>
using IUnknown = ::IUnknown; // <<< Had to add :: to get the correct implementation.
#ifndef IInitializeWithWindow_INTERFACE_DEFINED #define IInitializeWithWindow_INTERFACE_DEFINED
/* interface IInitializeWithWindow / / [unique][object][uuid] */
EXTERN_C const IID IID_IInitializeWithWindow;
MIDL_INTERFACE("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1") IInitializeWithWindow : public ::IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Initialize( /* [in] */ __RPC__in HWND hwnd) = 0;
}; #endif /* IInitializeWithWindow_INTERFACE_DEFINED */
I do agree with the "Universal Windows" tag being a UX issue. It is a bit on the confusing side.
I also am not a fan of the FilePicker interface. One enhancement I would really like to see is the ability to start in any folder rather than the present enum based selection for starting point. Also, the folder picker requires a file extension filter to be set. No need for that.
Thanks for following up.
Don't use that workaround, that's a terrible idea. I believe the sample I provided has everything you need.
Thanks, I agree that it is a terrible idea, but I assumed short term so I went with it.
I am working with your sample and have it building but there is a collision in the Macro GetCurrentTime. It appears that it is defined differently in Win32 than in WinRT or Windows App SDK. I ran out of time today to continue working on it but will continue tomorrow. The issue is reported as the following: 1>C:\Users\tim\Source\Repos\PointCloudWRC\PointCloudWRC\Generated Files\winrt\impl\Windows.UI.Xaml.Media.Animation.0.h(2306,39): warning C4002: too many arguments for function-like macro invocation 'GetCurrentTime' (compiling source file Generated Files\XamlTypeInfo.g.cpp)
Obviously ignore the PointCloud stuff but appears to be in Generated code from the App side. Also, it is a warning and the code still compiles. I have not yet tried to run it. I am attempting to move headers around to eliminate the issue.
This issue is caused because the Runtime Component projects create a UWP project behind the scenes, so you are limited by default to UWP APIs (iirc in earlier previews even the main WinUI app project would be a UWP project maskerading as desktop). As @riverar noticed, DesktopCompatible doesn't change anything build-wise, it just tells the IDE to allow adding a reference to a UWP project from a desktop project (normally it gives an error when you try this).
My guess why this is done is to reuse VS/MSBuild infrastructure relating to WinRT that is only enabled in UWP projects (and, before WinUI 3 in UWP got canned, allows the runtime component to run on both desktop and UWP)
You can edit the vcxproj's global PropertyGroup and add these two directives right now to force the UWP project to use the desktop API set, and link the desktop CRT (removing the need to carry around the VCRT forwarders, which are honestly a terrible hack)
<_NoWinAPIFamilyApp>true</_NoWinAPIFamilyApp>
<_VC_Target_Library_Platform>Desktop</_VC_Target_Library_Platform>
I've used this in an actual UWP (not WinUI 3) XAML runtime component project that is hosted in a Win32 app via XAML Islands to be able to call into desktop-only APIs.
Alternatively, it seems that merely setting <AppContainerApplication>false</AppContainerApplication> works as well.
maybe close this issue since there are documentations on WinRT APIs which apps can be used on desktop apps.
This issue has been addressed inside PR# 3745. Therefore, the issue has been closed as completed. If you encounter any related concerns or regressions, please feel free to open a new issue. Thanks!
@ssparach Can you update your GitHub profile to indicate who you are? Are you a FTE/vendor?