Skip to content

Comments

Add WinML EP Catalog CMake sample with NuGet package integration#593

Draft
nieubank wants to merge 2 commits intorelease/experimentalfrom
user/nieubank/winmlepcatalog_vcpkg
Draft

Add WinML EP Catalog CMake sample with NuGet package integration#593
nieubank wants to merge 2 commits intorelease/experimentalfrom
user/nieubank/winmlepcatalog_vcpkg

Conversation

@nieubank
Copy link
Contributor

@nieubank nieubank commented Feb 7, 2026

Adds a CMake-based C++ sample demonstrating the WinML Execution Provider Catalog C API (WinMLEpCatalog*) for discovering and registering hardware-accelerated ML execution providers with ONNX Runtime.

What the sample shows:

  • Creating an EP catalog and enumerating registered execution providers
  • Inspecting provider metadata (name, version, certification status)
  • Preparing certified providers with WinMLEpEnsureReady and registering them with Ort::Env
  • Consuming the Microsoft.WindowsAppSDK.ML NuGet package from CMake with automatic .nupkg extraction at configure time
  • Deploying runtime DLLs via TARGET_RUNTIME_DLLS and an explicit WINML_DIRECTML_DLL copy for DirectML (which has no import library)

Build setup:

  • CMake 3.21+ with a single nuget preset; supports both Ninja and Visual Studio generators
  • build.ps1 helper script handles VS developer environment setup, prerequisite checks, and NuGet package resolution
  • The NuGet package is extracted directly by CMake's file(ARCHIVE_EXTRACT)

- Introduced a new sample demonstrating the WinMLEpCatalog Native C API for managing hardware-accelerated execution providers for machine learning inference on Windows.
- Added CMake configuration with presets for various architectures and build types.
- Created a PowerShell build script to automate the build process, including prerequisite checks and configuration.
- Implemented a batch wrapper for the PowerShell script for easier command-line usage.
- Included detailed README documentation covering sample usage, project structure, prerequisites, and build instructions.
- Established vcpkg configuration and overlay ports for dependency management, including the Microsoft Windows AI Machine Learning SDK.
- Implemented the main sample code to demonstrate provider registration and enumeration using the Windows ML API.
Adds a CMake-based C++ sample demonstrating the WinML Execution Provider Catalog C API (WinMLEpCatalog*) for discovering and registering hardware-accelerated ML execution providers with ONNX Runtime.

What the sample shows:

- Creating an EP catalog and enumerating registered execution providers
- Inspecting provider metadata (name, version, certification status)
- Preparing certified providers with WinMLEpEnsureReady and registering them with Ort::Env
- Consuming the Microsoft.WindowsAppSDK.ML NuGet package from CMake with automatic .nupkg extraction at configure time
- Deploying runtime DLLs via TARGET_RUNTIME_DLLS and an explicit WINML_DIRECTML_DLL copy for DirectML (which has no import library)

Build setup:

- CMake 3.21+ with a single nuget preset; supports both Ninja and Visual Studio generators
- build.ps1 helper script handles VS developer environment setup, prerequisite checks, and NuGet package resolution
- No vcpkg dependency -- the NuGet package is extracted directly by CMake's file(ARCHIVE_EXTRACT)
@nieubank nieubank changed the title Add Windows ML EP Catalog Sample with CMake and vcpkg integration Add WinML EP Catalog CMake sample with NuGet package integration Feb 18, 2026

function Write-ErrorMessage {
param([string]$Message)
Write-Host "[ERROR] $Message" -ForegroundColor Red
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Write-Host "[ERROR] $Message" -ForegroundColor Red
Write-Error $Message

Send this to the error stream? Maybe get rid of this function and call Write-Error directly.

# Local NuGet package configuration
set(WINML_NUGET_PACKAGE "${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.WindowsAppSDK.ML.2.0.246-experimental.nupkg"
CACHE FILEPATH "Path to Microsoft.WindowsAppSDK.ML nupkg")
set(WINML_LOCAL_PACKAGES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/local_packages"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting somewhere under the CMAKE_CURRENT_BINARY_DIR or CMAKE_BINARY_DIR so that the source tree isn't touched.

if(NOT DEFINED microsoft.windows.ai.machinelearning_DIR)
if(EXISTS "${WINML_NUGET_PACKAGE}")
get_filename_component(_winml_nupkg_filename "${WINML_NUGET_PACKAGE}" NAME)
string(REGEX REPLACE "\\.nupkg$" "" _winml_nupkg_name "${_winml_nupkg_filename}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cmake_path(GET WINML_NUGET_PACKAGE STEM _winml_nupkg_filename) looks like it will handle getting just the filename without the extension. get_filename_component was superseded by the cmake_path() in 3.20 too.

if(EXISTS "${WINML_NUGET_PACKAGE}")
get_filename_component(_winml_nupkg_filename "${WINML_NUGET_PACKAGE}" NAME)
string(REGEX REPLACE "\\.nupkg$" "" _winml_nupkg_name "${_winml_nupkg_filename}")
set(_winml_extract_dir "${WINML_LOCAL_PACKAGES_DIR}/${_winml_nupkg_name}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two could use cmake_Path(APPEND ...) to construct instead of string concatenation.

Comment on lines +63 to +68
if (FAILED(hr))
{
std::cout << " Failed to get library path\n\n";
return TRUE;
}
libraryPathUtf8.resize(used > 0 ? used - 1 : 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (FAILED(hr))
{
std::cout << " Failed to get library path\n\n";
return TRUE;
}
libraryPathUtf8.resize(used > 0 ? used - 1 : 0);
if (FAILED(hr) || used == 0)
{
std::cout << " Failed to get library path\n\n";
return TRUE;
}
libraryPathUtf8.resize(used - 1);

Minor: symmetry with the error check above and simplifies the resize call

}
}

WinMLEpCatalogRelease(catalog);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the catalog be released after the enumeration is done?


std::cout << std::format("\nRegistered {} certified provider(s).\n", ctx.registeredProviders.size());

// Unregister providers before env goes out of scope
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit more explanation about why this is needed would be helpful.
env is a C++ object, so I would expect it to automatically clean itself up.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants