Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En esta guía se muestra cómo crear un complemento nativo de C# que usa Windows Machine Learning (WinML) en la aplicación Electron. WinML permite ejecutar modelos machine learning (formato ONNX) localmente en dispositivos Windows para tareas como la clasificación de imágenes, la detección de objetos y mucho más.
Prerrequisitos
Antes de iniciar esta guía, asegúrese de que tiene:
- Completado la configuración del entorno de desarrollo
- Windows 11 o Windows 10 (versión 1809 o posterior)
Nota:
WinML se ejecuta en cualquier dispositivo Windows 10 (1809 o posterior) o Windows 11. Para obtener el mejor rendimiento, se recomiendan dispositivos con GPU o NPUs, pero la API también funciona en la CPU.
Importante
El complemento WinML requiere el experimental SDK de Aplicaciones para Windows. Si seleccionó "SDKs estables" durante winapp init en la guía de configuración, deberá actualizar la versión del SDK. Edite winapp.yaml y cambie la versión Microsoft.WindowsAppSDK a 2.0.0-experimental3 y, a continuación, ejecute npx winapp restore para actualizar.
Paso 1: Crear un complemento nativo de C#
Vamos a crear un complemento nativo que usará las API de WinML. Usaremos una plantilla de C# que aproveche node-api-dotnet para puentear JavaScript y C#.
npx winapp node create-addon --template cs --name winMlAddon
Esto crea una winMlAddon/ carpeta con:
-
addon.cs: el código de C# que llamará a las API de WinML. -
winMlAddon.csproj- Archivo de proyecto con referencias a Windows SDK y SDK de Aplicaciones para Windows -
README.md- Documentación sobre cómo usar el complemento
El comando también agrega un build-winMlAddon script a package.json para compilar el complemento y un clean-winMlAddon script para limpiar los artefactos de compilación:
{
"scripts": {
"build-winMlAddon": "dotnet publish ./winMlAddon/winMlAddon.csproj -c Release",
"clean-winMlAddon": "dotnet clean ./winMlAddon/winMlAddon.csproj"
}
}
La plantilla incluye automáticamente referencias a ambos SDK, por lo que puede empezar a llamar inmediatamente a Windows API.
Vamos a comprobar que todo está configurado correctamente mediante la compilación del complemento:
# Build the C# addon
npm run build-winMlAddon
Nota:
También puede crear un complemento de C++ mediante npx winapp node create-addon (sin la --template marca). Los complementos de C++ usan node-addon-api y proporcionan acceso directo a las API de Windows con un rendimiento máximo. Consulte la guía del complemento de notificaciones de C++ para ver un tutorial o la documentación completa del comando para obtener más opciones.
Paso 2: Descargar el modelo de SqueezeNet y obtener código de ejemplo
Usaremos el ejemplo Clasificar imagen de la Galería de desarrollo de IA como referencia. En este ejemplo se usa el modelo SqueezeNet 1.1 para la clasificación de imágenes.
2.1. Descargar el modelo
- Instalar AI Dev Gallery
- Navegue al ejemplo Clasificar Imagen.
- Descargue el modelo de SqueezeNet 1.1 (admite CPU, GPU y NPU).
- Haga clic en Abrir carpeta contenedora para buscar el
.onnxarchivo.
- Copie el archivo
squeezenet1.1.onnxen una carpetamodels/en la raíz del proyecto.
Nota:
El modelo también se puede descargar directamente desde el repositorio de GitHub ONNX Model Zoo
Paso 3: Agregar paquetes NuGet necesarios
Antes de agregar el código WinML, es necesario agregar paquetes NuGet adicionales necesarios para el procesamiento de imágenes, el entorno de ejecución de ONNX y la compatibilidad con GenAI.
3.1. Actualizar Directory.packages.props
Agregue las siguientes versiones de paquete al Directory.packages.props archivo en la raíz del proyecto (debe haberse creado al crear el complemento):
<Project>
<PropertyGroup>
<!-- Enable central package versioning -->
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.JavaScript.NodeApi" Version="0.9.17" />
<PackageVersion Include="Microsoft.JavaScript.NodeApi.Generator" Version="0.9.17" />
<!-- Add these packages for WinML -->
+ <PackageVersion Include="Microsoft.ML.OnnxRuntime.Extensions" Version="0.14.0" />
+ <PackageVersion Include="System.Drawing.Common" Version="9.0.9" />
+ <PackageVersion Include="Microsoft.Extensions.AI" Version="9.9.1" />
+ <PackageVersion Include="Microsoft.ML.OnnxRuntimeGenAI.Managed" Version="0.10.1" />
+ <PackageVersion Include="Microsoft.ML.OnnxRuntimeGenAI.WinML" Version="0.10.1" />
<!-- These versions may be updated automatically during restore to match yaml -->
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="2.0.0-experimental3" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
</ItemGroup>
</Project>
3.2. Actualizar winMlAddon.csproj
Abra winMlAddon/winMlAddon.csproj y agregue las referencias del paquete a <ItemGroup>:
<ItemGroup>
<PackageReference Include="Microsoft.JavaScript.NodeApi" />
<PackageReference Include="Microsoft.JavaScript.NodeApi.Generator" />
<!-- Add these packages for WinML -->
+ <PackageReference Include="Microsoft.ML.OnnxRuntime.Extensions" />
+ <PackageReference Include="System.Drawing.Common" />
+ <PackageReference Include="Microsoft.Extensions.AI" />
+ <PackageReference Include="Microsoft.ML.OnnxRuntimeGenAI.Managed" />
+ <PackageReference Include="Microsoft.ML.OnnxRuntimeGenAI.WinML" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
<PackageReference Include="Microsoft.WindowsAppSDK" />
</ItemGroup>
Lo que hacen estos paquetes:
- Microsoft.ML.OnnxRuntime.Extensions: proporciona operadores y utilidades adicionales para el entorno de ejecución de ONNX
- System.Drawing.Common : habilita la carga y manipulación de imágenes para el preprocesamiento
- Microsoft. Extensions.AI: abstracciones de IA para .NET
- Microsoft.ML.OnnxRuntimeGenAI.Managed: enlaces administrados para ONNX Runtime GenAI
- Microsoft.ML.OnnxRuntimeGenAI.WinML: integración de WinML para ONNX Runtime GenAI
Paso 4: Agregar el código de ejemplo
La Galería de desarrollo de IA muestra la implementación completa de la clasificación de imágenes con SqueezeNet:
Hemos adaptado este código para Electron y puedes encontrar la implementación completa en el ejemplo electron-winml. La winMlAddon/ carpeta contiene el código modificado de la Galería de desarrollo de AI.
Copie la carpeta completa winMlAddon/ de samples/electron-winml/winMlAddon/ a la raíz de su proyecto, reemplazando la creada en el paso 1. El ejemplo incluye varios archivos más allá addon.cs de (clases auxiliares en Utils/, un cliente de chat, etc.) que son necesarios para que el complemento se compile y ejecute.
Importante
Debe copiar toda la carpeta, no solo addon.cs. El complemento depende de los archivos auxiliares de la Utils/ subcarpeta (Prediction.cs, ImageNet.cs, BitmapFunctions.cs, etc.).
Detalles clave de implementación
Vamos a resaltar las partes importantes de la implementación y las diferencias clave del código de la Galería de desarrollo de IA:
1. Requisito de ruta de acceso raíz del proyecto
A diferencia del código de la Galería de Desarrollo de IA, nuestro complemento de Electron requiere que el código JavaScript transmita la ruta de acceso raíz del proyecto. Esto es necesario porque:
- El complemento debe buscar el archivo de modelo ONNX en la
models/carpeta - Las dependencias nativas (DLL) deben cargarse desde directorios específicos
[JSExport]
public static async Task<Addon> CreateAsync(string projectRoot)
{
if (!Path.Exists(projectRoot))
{
throw new Exception("Project root is invalid.");
}
var addon = new Addon(projectRoot);
addon.PreloadNativeDependencies();
string modelPath = Path.Join(projectRoot, "models", @"squeezenet1.1-7.onnx");
await addon.InitModel(modelPath, ExecutionProviderDevicePolicy.DEFAULT, null, false, null);
return addon;
}
Esto selecciona automáticamente el mejor proveedor de ejecución (CPU, GPU o NPU) en función de las funcionalidades del dispositivo.
2. Cargar previamente dependencias nativas
El complemento incluye un PreloadNativeDependencies() método para cargar los archivos DLL necesarios. Este enfoque funciona para escenarios de desarrollo y producción sin necesidad de copiar archivos DLL en la raíz del proyecto:
private void PreloadNativeDependencies()
{
// Loads required DLLs from the winMlAddon build output
// This ensures dependencies are available regardless of the execution context
}
Esto se llama durante la inicialización antes de cargar el modelo, lo que garantiza que todas las bibliotecas nativas estén disponibles.
3. Configuración de la forja de electrones para el empaquetado
Para asegurarse de que el complemento funcione correctamente en las compilaciones de producción, debe configurar su empaquetador para:
- Desempaquetar archivos nativos : los archivos DLL, los modelos ONNX y .node deben ser accesibles fuera del archivo ASAR.
- Excluir archivos innecesarios : mantenga el tamaño del paquete pequeño excluyendo los artefactos de compilación y los archivos temporales.
Para Electron Forge, actualice forge.config.js:
// From samples/electron-winml/forge.config.js
module.exports = {
packagerConfig: {
asar: {
// Unpack native files so they can be accessed by the addon
unpack: "**/*.{dll,exe,node,onnx}"
},
ignore: [
// Exclude .winapp folder (SDK packages and headers)
/^\/.winapp\//,
// Exclude MSIX packages
"\\.msix$",
// Exclude winMlAddon source files, but keep the dist folder
/^\/winMlAddon\/(?!dist).+/
]
},
// ... rest of your config
};
Para qué sirve:
asar.unpack- Extrae archivos DLL, ejecutables, archivos binarios de .node y modelos ONNX paraapp.asar.unpacked/- Esto hace que sean accesibles en tiempo de ejecución a través de rutas de acceso del sistema de archivos
- El código JavaScript ajusta automáticamente las rutas de acceso (consulte el
app.asar→app.asar.unpackedreemplazo anterior).
ignore- Excluye del paquete final:-
.winapp/- Paquetes y encabezados del SDK (no necesarios en tiempo de ejecución) -
.msixarchivos - resultados empaquetados -
winMlAddon/archivos de origen: mantiene solo ladist/carpeta con archivos binarios compilados.
-
Nota:
Si usa una herramienta de empaquetado diferente (generador de electrones, etc.), deberá configurar opciones similares para desempaquetar dependencias nativas y excluir los archivos de desarrollo. Consulte la documentación del empaquetador para conocer las opciones de desempaquetado de ASAR.
4. Clasificación de imágenes
El ClassifyImage método procesa una imagen y devuelve predicciones:
[JSExport]
public async Task<Prediction[]> ClassifyImage(string imagePath)
{
// Loads the image, preprocesses it, and runs inference
// Returns top predictions with labels and confidence scores
}
La implementación completa maneja:
- Carga y preprocesamiento de imágenes (cambio de tamaño, normalización)
- Ejecución de la inferencia del modelo
- Resultados posteriores al procesamiento para obtener predicciones principales con etiquetas y puntuaciones de confianza
Nota:
El código fuente completo incluye preprocesamiento de imágenes, creación de tensor y análisis de resultados. Compruebe la implementación de ejemplo para obtener todos los detalles.
Descripción del código
El complemento proporciona estas funciones principales:
- CreateAsync : inicializa el complemento y carga el modelo de SqueezeNet
- ClassificationImage : toma una ruta de acceso de imagen y devuelve predicciones de clasificación
WinML selecciona automáticamente el mejor dispositivo de ejecución (CPU, GPU o NPU) en función de la disponibilidad.
Paso 5: Compilar el complemento de C#
Ahora compile el complemento:
npm run build-winMlAddon
Esto compila el código de C# mediante Native AOT (compilación adelantada), que:
- Crea un
.nodebinario (formato de complemento nativo) - Recorta el código sin usar para un tamaño de agrupación más pequeño
- Requiere no .NET runtime en máquinas de destino
- Proporciona rendimiento nativo
El complemento compilado estará en winMlAddon/dist/winMlAddon.node.
Paso 6: Probar el complemento
Ahora vamos a probar que el complemento funciona llamándolo desde el proceso principal. Abra src/main.js y siga estos pasos:
6.1. Cargar el complemento
Agregue las declaraciones require en la parte superior:
const winMlAddon = require('../winMlAddon/dist/winMlAddon.node');
6.2. Creación de una función de prueba
Agregue esta función para probar la clasificación de imágenes:
const testWinML = async () => {
console.log('Testing WinML addon...');
try {
let projectRoot = path.join(__dirname, '..');
// Adjust path for packaged apps
if (projectRoot.includes('app.asar')) {
projectRoot = projectRoot.replace('app.asar', 'app.asar.unpacked');
}
const addon = await winMlAddon.Addon.createAsync(projectRoot);
console.log('Model loaded successfully!');
// Classify a sample image
const imagePath = path.join(projectRoot, 'test-images', 'sample.jpg');
const predictions = await addon.classifyImage(imagePath);
console.log('Top predictions:');
predictions.slice(0, 5).forEach((pred, i) => {
console.log(`${i + 1}. ${pred.label}: ${(pred.confidence * 100).toFixed(2)}%`);
});
} catch (error) {
console.error('Error testing WinML:', error.message);
}
};
Puntos clave:
- El ajuste de ruta de acceso (
app.asar→app.asar.unpacked) garantiza que el código funciona tanto en aplicaciones de desarrollo como empaquetadas. - Esto tiene acceso a los archivos nativos desempaquetados configurados en
forge.config.js
6.3. Llama a la función Test
Agregue esta línea al final de la createWindow() función:
testWinML();
6.4. Preparación de imágenes de prueba
Para probar la clasificación de imágenes:
- Creación de una
test-images/carpeta en la raíz del proyecto - Agregar una imagen de prueba denominada
sample.jpg(el código espera este nombre de archivo exacto) - El modelo SqueezeNet reconoce 1000 clases ImageNet diferentes (animales, objetos, escenas, etc.)
Cuando ejecute la aplicación, verá los resultados de clasificación en la consola.
Tip
Para obtener una implementación completa con controladores IPC, cuadros de diálogo de selección de archivos y una interfaz de usuario, consulte el ejemplo electron-winml.
Paso 7: Actualizar la identidad de depuración
Para asegurarse de que el SDK de Aplicaciones para Windows está cargado y disponible para su uso, es necesario asegurarnos de configurar la identidad de depuración, lo que garantizará que el marco se cargue cada vez que se ejecute la aplicación. Del mismo modo, siempre que modifique Package.appxmanifest o cambie los recursos referenciados en el manifiesto (como los iconos de la aplicación), debe actualizar la identidad de depuración de la aplicación. ¡Corre!
npx winapp node add-electron-debug-identity
Este comando:
- Lee tu
Package.appxmanifestpara obtener detalles y capacidades de la aplicación. - Registra
electron.exeen sunode_modulescon una identidad temporal - Permite probar las API que requieren identidad sin el empaquetado completo de MSIX.
Nota:
Este comando ya está incluido en el script postinstall que hemos agregado en la guía de configuración, por lo que se ejecuta automáticamente después de npm install. Sin embargo, debe ejecutarlo manualmente cada vez que:
- Modificar
Package.appxmanifest(cambiar funcionalidades, identidades o propiedades) - Actualizar recursos de la aplicación (iconos, logotipos, etc.)
Ahora ejecute la aplicación:
npm start
Compruebe la salida de la consola: debería ver los resultados de la prueba de WinML.
⚠️ Problema conocido: Bloqueos de la aplicación o ventana en blanco (haga clic para expandir)
Hay un error conocido de Windows con aplicaciones Electron de empaquetado ligero o disperso que hace que la aplicación se bloquee al iniciar o no mostrar contenido web. El problema se ha corregido en Windows pero aún no se ha propagado a todos los dispositivos.
Consulte Configuración del entorno de desarrollo para obtener una solución alternativa.
Pasos siguientes
¡Enhorabuena! Ha creado correctamente un complemento nativo que puede ejecutar modelos de aprendizaje automático con WinML. 🎉
Ahora ya está listo para:
- Empaquetar la aplicación para la distribución : cree un paquete MSIX que pueda distribuir.
O explore otras guías:
- Creación de un addon Phi Silica - Aprenda a utilizar la API de Phi Silica AI
- Introducción: volver a la guía principal
Personalización para tu modelo
Para integrar completamente el modelo ONNX, deberá hacer lo siguiente:
- Comprender las entradas del modelo : imágenes, tensores, secuencias, etc.
- Crear enlaces de entrada adecuados: convierta los datos en el formato que WinML espera.
- Procesamiento de las salidas : análisis e interpretación de las predicciones del modelo
- Gestionar errores con elegancia - La carga del modelo y la inferencia pueden fallar.
Recursos adicionales
- Documentación de WinML : documentación oficial de WinML
- Documentación de la CLI de winapp : referencia completa de la CLI
- Ejemplo de aplicación Electron - Ejemplo funcional completo
- Galería de desarrollo de IA: galería de ejemplos de todas las API de IA
- SDK de Aplicaciones para Windows Samples - Colección de ejemplos de SDK de Aplicaciones para Windows
- node-api-dotnet : biblioteca de interoperabilidad de JavaScript de C# ↔
Solución de problemas
Error de compilación con NU1010: los elementos PackageReference no definen una packageVersion correspondiente.
Asegúrese de que todos los paquetes a los que se hace referencia en winMlAddon.csproj tienen entradas coincidentes en Directory.packages.props. Consulte el paso 3 para obtener la lista completa de paquetes necesarios.
"no una aplicación Win32 válida" al cargar el complemento
Esto significa que el complemento se creó para una arquitectura diferente de la del entorno de ejecución de Node.js/Electron. Compruebe la arquitectura de Node.js:
node -e "console.log(process.arch)"
A continuación, vuelva a generar el complemento con el objetivo correspondiente:
# For x64 Node.js:
dotnet publish ./winMlAddon/winMlAddon.csproj -c Release -r win-x64
# For ARM64 Node.js:
dotnet publish ./winMlAddon/winMlAddon.csproj -c Release -r win-arm64
Si ha cambiado recientemente la instalación de Node.js, vuelva a instalar también node_modules para obtener el binario de Electron coincidente.
rm -rf node_modules package-lock.json
npm install
Obtener ayuda
- ¿Encontró un error?Archivo de un problema
- ¿Preguntas de WinML? Consulte la documentación de WinML.
¡Feliz aprendizaje automático! 🤖
Windows developer