arm64 / UWP / WinRT for HoloLens 2

Visual Studio 2019 / CMAKE_VERSION = 3.16.19112601-MSVC_2

Trying to build an arm64 / UWP / WinRT package, ultimately for the HoloLens 2.

I am successfully building an x64 / UWP / WinRT package for the desktop. I can install the App and it executes successfully.

The arm64 variant appears to compile and link successfully. I deploy the package to the HoloLens 2. When I try to run it, I get the error:

The process started, but the activation request failed with error ‘The target process aborted before activation completed.’

I suspect maybe missing or incompatible DLLs. Comparing my x64 build (which works) and Microsoft’s example BasicXrApp_uwp (which works as x64 on the desktop as well as arm64 on the HoloLens 2), I see this difference in the DLLs being loaded:

My app:
Loaded ‘C:\Windows\System32\msvcp140d.dll’.
Loaded ‘C:\Windows\System32\vcruntime140_1d.dll’.
Loaded ‘C:\Windows\System32\vcruntime140d.dll’.

Microsoft’s example BasicXrApp_uwp
Loaded ‘C:\Program Files\WindowsApps\Microsoft.VCLibs.140.00.Debug_14.0.27810.0_x64__8wekyb3d8bbwe\msvcp140d_app.dll’.
Loaded ‘C:\Program Files\WindowsApps\Microsoft.VCLibs.140.00.Debug_14.0.27810.0_x64__8wekyb3d8bbwe\vcruntime140_1d_app.dll’.
Loaded ‘C:\Program Files\WindowsApps\Microsoft.VCLibs.140.00.Debug_14.0.27810.0_x64__8wekyb3d8bbwe\vcruntime140d_app.dll’.

I’ve seen that “It’s important that you resolve any linker errors that you can by linking WindowsApp.lib instead of an alternative static-link library, …”. I’ve done everything I can find to get my app to link to the WindowsApps\Microsoft.VCLibs* DLLs (which I suppose the WindowsApp.lib is supposed to arrange?), instead of the Windows\System32 DLLs, but no joy. I’ll reiterate that my desktop x64 UWP app runs fine with the Windows\System32 DLLs, but my HoloLens arm64 UWP app fails with the error quoted above, so I’ve latched onto this DLL issue as the cause.

$ENV{LIB} includes the path to WindowsApp.lib

I’m a bit surprised that ${CMAKE_LIBRARY_PATH} and ${CMAKE_SYSTEM_LIBRARY_PATH} start out empty. I tried set(CMAKE_LIBRARY_PATH “$ENV{LIB}”)

This is what I have for the arm64 UWP build at the top level, guided by the BasicXrApp_uwp configuration, and more – I guess that my EXE and all of my DLL builds inherit all of these:

set(PLATFORM_WINDOWS TRUE)
set(PLATFORM_UWP TRUE)
set(UNICODE TRUE)
add_compile_definitions(PLATFORM_WINDOWS PLATFORM_UWP)
add_compile_definitions(WINAPI_FAMILY=WINAPI_FAMILY_PC_APP)
add_compile_definitions(_UNICODE UNICODE)
add_compile_definitions(UWP)
add_compile_definitions(WRL_NO_DEFAULT_LIB)
add_compile_definitions(_DEBUG)
set(COMPILER_MSVC_64 TRUE)
set(CMAKE_SYSTEM_PROCESSOR “ARM64”)
add_compile_definitions(JM_SSE4_2)
set(CMAKE_VS_WINRT_BY_DEFAULT TRUE)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/ZW:nostdlib>)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/FU:platform.winmd>)
add_link_options(/DEFAULTLIB:WindowsApp.lib)
add_link_options(/SUBSYSTEM:WINDOWS)
add_link_options(/APPCONTAINER)
add_link_options(/WINMD:NO)
add_compile_options(/EHsc)
add_compile_options(/permissive-)
add_compile_options(/W4)
add_compile_options(/fp:precise)
add_compile_options(/FC)
add_compile_options(/Gd)
add_compile_options(/std:c++17)
add_compile_options(/Zc:wchar_t)
add_compile_options(/Zc:inline)
add_compile_options(/Zc:forScope)
add_compile_options(/GT)
add_compile_options(/sdl)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
add_compile_definitions(_ITERATOR_DEBUG_LEVEL=0)
add_compile_options(/wd26812)
add_compile_definitions(VMEM_REQUIRE_COMMIT)
add_link_options(/NXCOMPAT)
add_link_options(/DYNAMICBASE)
add_link_options(/MANIFESTUAC:NO)
add_link_options(/OPT:NOICF)
add_compile_options(/Zi)
add_compile_options(/MDd)
add_link_options(/DEBUG)
add_link_options(/OPT:NOREF)
add_link_options(/MACHINE:ARM64)

For the EXE, I also have
set (WIN32_EXECUTABLE TRUE)

It seems that the add_link_options(/DEFAULTLIB:WindowsApp.lib) might be redundant, because something in CMake is appending WindowsApp.lib to the link command anyway.

On the other hand, when linking the main program, something in CMake is still inserting /subsystem:console, though it’s followed by /SUBSYSTEM:WINDOWS, and according to the Microsoft docs the last setting should prevail.

I’m about out of ideas. Any insights appreciated.

Ted

P.S.:

  • In the above, WRL_NO_DEFAULT_LIB should be preceded and followed by double-underscores. I guess the text editor here misinterpreted those to mean boldface. I copied that from the settings in the Microsoft example.

  • The example BasicXrApp_uwp is in the form of standard Visual Studio SLN and VCXPROJ solution and project files, not a CMake project. There are features that may or may not be relevant that I don’t know how to translate into a CMake project – especially: a “References” container with an explicit reference to Microsoft.VCLibs That seems highly relevant. But, the VS interface for CMake projects is different, and I don’t see a way to add such references. I’ve looked at the SLN, VCXPROJ, etc. files with a text editor (Notepad++) and can’t see where that reference is actually recorded or how it affects the build process.

Partial solution:

The Microsoft build tools include a batch file – vcvarsall.bat – to set up the environment for building – especially, defining the values of ${LIB} and ${LIBPATH} appropriate for the destination system. On my system, it’s located at:

“C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat”

There are three arguments that may be passed in:

CALL vcvarsall.bat [arch] [platform_type] [winsdk_version]

[arch] specifies the architecture of the build and execute systems – e.g., x64_arm64 sets up the environment to use x64 tools to create an arm64 executable. The “inheritEnvironments” variable in the CMakeSettings.json file, labeled “Toolset” in the GUI, handles this fine.

[platform_type] is the root of our problem. To link to the correct libraries for deploying to the HoloLens 2, [platform_type] must be uwp or store (synonymous). I have not found any way to manage this through CMake or the Visual Studio interface to CMake. If this argument is left unspecified, the default uses the usual desktop libraries, which work fine in desktop apps (including UWP), but not in HoloLens 2 apps.

What we’re doing now is using CMake to build the cache, then using a command-line prompt to:

CD into output directory (${CMAKE_BINARY_DIR})
CALL vcvarsall.bat x64_arm64 uwp
ninja clean
ninja all

I’ve written my own BAT file to manage this, along with some final file copying and

MakeAppx.exe ...

to create the deployable App package file. I invoke my BAT as a COMMAND in add_custom_target(_) from CMakeLists.txt (The “target” is the final package file created by MakeAppx.exe)

It mostly works, but seems a bit of a kludge to have to “manually” reset the environment and re-link all of the EXE and DLL files. Also, it seems not to be working 100% with regard to a prerequisite ExternalProject.

Still open to suggestions.

I think that there ought to be a way to manage the “uwp” or “store” specification along with x64_arm64 in the CMakeSettings.json – that would make the most sense. Maybe such a capability exists, but isn’t documented. ?

(Just to confuse things, CMakeSettings.json reverses the order of the build and execute systems; there, it’s specified as msvc_arm64_x64)