Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questa guida illustra come creare un componente aggiuntivo nativo C# che usa Windows Machine Learning (WinML) nell'app Electron. WinML consente di eseguire machine learning modelli (formato ONNX) in locale nei dispositivi Windows per attività come la classificazione delle immagini, il rilevamento degli oggetti e altro ancora.
Prerequisiti
Prima di iniziare questa guida, assicurarsi di avere:
- Completata la configurazione dell'ambiente di sviluppo
- Windows 11 o Windows 10 (versione 1809 o successiva)
Annotazioni
WinML viene eseguito in qualsiasi dispositivo Windows 10 (1809+) o Windows 11. Per ottenere prestazioni ottimali, i dispositivi con GPU o NPU sono consigliati, ma l'API funziona anche sulla CPU.
Importante
Il componente aggiuntivo WinML richiede il experimental SDK per app di Windows. Se hai selezionato "SDK stabili" nella guida alla configurazione durante winapp init, dovrai aggiornare la versione dell'SDK. Modificare winapp.yaml e modificare la versione di Microsoft.WindowsAppSDK in 2.0.0-experimental3, quindi eseguire npx winapp restore per eseguire l'aggiornamento.
Passaggio 1: Creare un componente aggiuntivo nativo C#
Verrà ora creato un componente aggiuntivo nativo che userà le API WinML. Si userà un modello C# che sfrutta node-api-dotnet per collegare JavaScript e C#.
npx winapp node create-addon --template cs --name winMlAddon
Verrà creata una winMlAddon/ cartella con:
-
addon.cs- Il codice C# che chiamerà le API WinML -
winMlAddon.csproj- File di Project con riferimenti a Windows SDK e SDK per app di Windows -
README.md- Documentazione su come usare il componente aggiuntivo
Il comando aggiunge anche uno script al tuo build-winMlAddon per la creazione del componente aggiuntivo e uno script al tuo clean-winMlAddon per la pulizia degli artefatti di compilazione:
{
"scripts": {
"build-winMlAddon": "dotnet publish ./winMlAddon/winMlAddon.csproj -c Release",
"clean-winMlAddon": "dotnet clean ./winMlAddon/winMlAddon.csproj"
}
}
Il modello include automaticamente riferimenti a entrambi gli SDK, quindi è possibile iniziare immediatamente a chiamare Windows API.
Verificare che tutto sia configurato correttamente creando il componente aggiuntivo:
# Build the C# addon
npm run build-winMlAddon
Annotazioni
È anche possibile creare un componente aggiuntivo C++ usando npx winapp node create-addon (senza il --template flag). I componenti aggiuntivi C++ usano node-addon-api e forniscono l'accesso diretto alle API Windows con prestazioni massime. Per una procedura dettagliata o la documentazione completa dei comandi per altre opzioni, vedere la guida per il componente aggiuntivo per le notifiche C++.
Passaggio 2: Scaricare il modello SqueezeNet e ottenere il codice di esempio
Come riferimento si userà l'esempio Classifica immagine da AI Dev Gallery . Questo esempio usa il modello SqueezeNet 1.1 per la classificazione delle immagini.
2.1. Scaricare il modello
- Installare AI Dev Gallery
- Passare all'esempio di classificazione dell'immagine
- Scaricare il modello SqueezeNet 1.1 (supporta CPU, GPU e NPU)
- Fare clic su Apri cartella contenitore per individuare il
.onnxfile
- Copiare il file
squeezenet1.1.onnxin la cartellamodels/nella cartella principale del progetto
Annotazioni
Il modello può anche essere scaricato direttamente dal repository GitHub ONNX Model Zoo
Passaggio 3: Aggiungere pacchetti NuGet necessari
Prima di aggiungere il codice WinML, è necessario aggiungere altri pacchetti NuGet necessari per l'elaborazione delle immagini, il runtime ONNX e il supporto genAI.
3.1. Aggiornare Directory.packages.props
Aggiungere le versioni del pacchetto seguenti al Directory.packages.props file nella radice del progetto (dovrebbe essere stato creato al momento della creazione del componente aggiuntivo):
<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. Aggiornare winMlAddon.csproj
Aprire winMlAddon/winMlAddon.csproj e aggiungere i riferimenti al pacchetto 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>
Operazioni eseguite da questi pacchetti:
- Microsoft.ML.OnnxRuntime.Extensions : fornisce operatori e utilità aggiuntivi per il runtime ONNX
- System.Drawing.Common - Abilita il caricamento e la manipolazione delle immagini per la pre-elaborazione
- Microsoft. Extensions.AI - Astrazioni di intelligenza artificiale per .NET
- Microsoft.ML.OnnxRuntimeGenAI.Managed - Associazioni gestite per ONNX Runtime GenAI
- Microsoft.ML.OnnxRuntimeGenAI.WinML - Integrazione WinML per ONNX Runtime GenAI
Passaggio 4: Aggiungere il codice di esempio
La Galleria di sviluppo AI mostra l'implementazione completa per la classificazione delle immagini con SqueezeNet.
Questo codice è stato adattato per Electron ed è possibile trovare l'implementazione completa nell'esempio electron-winml. La winMlAddon/ cartella contiene il codice modificato da AI Dev Gallery.
Copiare l'intera winMlAddon/ cartella da samples/electron-winml/winMlAddon/ alla radice del progetto, sostituendo quella creata nel passaggio 1. L'esempio include più file oltre a addon.cs (classi helper in Utils/, un client di chat e così via) necessari per la creazione e l'esecuzione dell'addon.
Importante
È necessario copiare l'intera cartella, non solo addon.cs. Il componente aggiuntivo dipende dai file helper nella Utils/ sottocartella (Prediction.cs, ImageNet.cs, BitmapFunctions.cse così via).
Dettagli dell'implementazione chiave
Verranno ora evidenziate le parti importanti dell'implementazione e le differenze principali rispetto al codice di AI Dev Gallery:
Requisito percorso radice progetto
A differenza del codice di AI Dev Gallery, il componente aggiuntivo Electron richiede che il codice JavaScript passi il percorso radice del progetto. Questa operazione è necessaria perché:
- Il componente aggiuntivo deve individuare il file del modello ONNX nella
models/cartella - Le dipendenze native (le DLL) devono essere caricate da cartelle specifiche
[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;
}
In questo modo viene selezionato automaticamente il provider di esecuzione migliore (CPU, GPU o NPU) in base alle funzionalità del dispositivo.
2. Precaricamento delle dipendenze native
Il componente aggiuntivo include un PreloadNativeDependencies() metodo per caricare le DLL necessarie. Questo approccio funziona sia per gli scenari di sviluppo che per gli scenari di produzione senza dover copiare DLL nella radice del progetto:
private void PreloadNativeDependencies()
{
// Loads required DLLs from the winMlAddon build output
// This ensures dependencies are available regardless of the execution context
}
Questa operazione viene chiamata durante l'inizializzazione prima del caricamento del modello, assicurando che tutte le librerie native siano disponibili.
3. Configurazione di Electron Forge per l'impacchettamento
Per garantire che il componente aggiuntivo funzioni correttamente nelle build di produzione, è necessario configurare il packager per:
- Decomprimere i file nativi : le DLL, i modelli ONNX e i file con estensione node devono essere accessibili all'esterno dell'archivio ASAR
- Escludere file non necessari : mantenere le dimensioni del pacchetto ridotte escludendo gli artefatti di compilazione e i file temporanei
Per Electron Forge aggiornare :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
};
Scopo di questa funzione:
asar.unpack- Estrae DLL, eseguibili, binari .node e modelli ONNX inapp.asar.unpacked/- In questo modo è possibile accedervi in fase di esecuzione tramite percorsi del file system
- Il codice JavaScript regola automaticamente i percorsi (vedere la
app.asarsostituzione →app.asar.unpackedprecedente)
ignore- Esclude dal pacchetto finale:-
.winapp/- Pacchetti e intestazioni SDK (non necessari in fase di esecuzione) -
.msixfiles - Output confezionati in pacchetto -
winMlAddon/file di origine : mantiene solo ladist/cartella con file binari compilati
-
Annotazioni
Se si usa un diverso strumento per la creazione di pacchetti (generatore di elettroni e così via), è necessario configurare impostazioni simili per decomprimere le dipendenze native ed escludere i file di sviluppo. Controllare la documentazione del pacchetto per le opzioni di decompressione ASAR.
4. Classificazione delle immagini
Il ClassifyImage metodo elabora un'immagine e restituisce stime:
[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 completa implementazione gestisce:
- Caricamento e pre-elaborazione delle immagini (ridimensionamento, normalizzazione)
- Esecuzione dell'inferenza del modello
- Risultati post-elaborazione per ottenere stime principali con etichette e punteggi di attendibilità
Annotazioni
Il codice sorgente completo include la pre-elaborazione delle immagini, la creazione di tensor e l'analisi dei risultati. Controllare l'implementazione di esempio per tutti i dettagli.
Informazioni sul codice
Il componente aggiuntivo fornisce queste funzioni principali:
- CreateAsync : inizializza il componente aggiuntivo e carica il modello SqueezeNet
- ClassifyImage : accetta un percorso di immagine e restituisce stime di classificazione
WinML seleziona automaticamente il dispositivo di esecuzione migliore (CPU, GPU o NPU) in base alla disponibilità.
Passaggio 5: Compilare il componente aggiuntivo C#
Compilare ora il componente aggiuntivo:
npm run build-winMlAddon
Questo compila il codice C# usando native AOT (compilazione ahead-of-time), che:
- Crea un
.nodefile binario (formato di componente aggiuntivo nativo) - Riduce il codice inutilizzato per diminuire la dimensione del bundle
- Non richiede runtime .NET sui computer di destinazione
- Offre prestazioni native
Il componente aggiuntivo compilato sarà in winMlAddon/dist/winMlAddon.node.
Passaggio 6: Testare il componente aggiuntivo
Adesso testiamo il funzionamento del componente aggiuntivo chiamandolo dal processo principale. Aprire src/main.js e seguire questa procedura:
6.1. Caricare il componente aggiuntivo
Aggiungi le istruzioni require nella parte superiore:
const winMlAddon = require('../winMlAddon/dist/winMlAddon.node');
6.2. Creare una funzione di test
Aggiungere questa funzione per testare la classificazione delle immagini:
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);
}
};
Punti principali:
- La regolazione del percorso (
app.asar→app.asar.unpacked) garantisce che il codice funzioni sia nelle app di sviluppo che nelle app in pacchetto - In questo modo si accede ai file nativi decompressi configurati in
forge.config.js
6.3. Chiamare la funzione test
Aggiungere questa riga alla fine della createWindow() funzione:
testWinML();
6.4. Preparare le immagini di test
Per testare la classificazione delle immagini:
- Creare una
test-images/cartella nella radice del progetto - Aggiungere un'immagine di test denominata
sample.jpg(il codice prevede questo nome file esatto) - Il modello SqueezeNet riconosce 1000 classi ImageNet diverse (animali, oggetti, scene e così via)
Quando si esegue l'app, nella console verranno visualizzati i risultati della classificazione.
Tip
Per un'implementazione completa con i gestori IPC, le finestre di dialogo di selezione dei file e un'interfaccia utente, vedi l'esempio electron-winml.
Passaggio 7: Aggiornare l'identità di debug
Per assicurarsi che il SDK per app di Windows venga caricato e disponibile per l'utilizzo, è necessario assicurarsi di configurare l'identità di debug che assicurerà che il framework venga caricato ogni volta che viene eseguita l'app. Analogamente, ogni volta che modifichi Package.appxmanifest o cambi gli asset a cui si fa riferimento nel manifesto (ad esempio le icone dell'app), devi aggiornare l'identità di debug dell'app. Corri!
npx winapp node add-electron-debug-identity
Questo comando:
- Legge il tuo
Package.appxmanifestper ottenere i dettagli e le funzionalità dell'app - Registra
electron.exenelnode_modulescon un'identità temporanea. - Consente di testare le API necessarie per l'identità senza pacchetti MSIX completi
Annotazioni
Questo comando fa già parte dello postinstall script aggiunto nella guida all'installazione, quindi viene eseguito automaticamente dopo npm install. Tuttavia, è necessario eseguirlo manualmente ogni volta che:
- Modifica
Package.appxmanifest(modifica funzionalità, identità o proprietà) - Aggiornare gli asset dell'app (icone, logo e così via)
Ora esegui l'applicazione:
npm start
Controllare l'output della console. Verranno visualizzati i risultati del test WinML.
⚠️ Problema noto: arresto anomalo dell'app o finestra vuota (fare clic per espandere)
Esiste un bug noto di Windows con la creazione sparsa di pacchetti di applicazioni Electron che causa l'arresto anomalo dell'app all'avvio o al mancato renderizzazione del contenuto web. Il problema è stato risolto in Windows ma non è ancora stato propagato a tutti i dispositivi.
Per una soluzione alternativa, vedere Configurazione dell'ambiente di sviluppo .
Operazioni successive
Complimenti. È stato creato un componente aggiuntivo nativo in grado di eseguire modelli di Machine Learning con WinML. 🎉
A questo momento è possibile:
- Creare un pacchetto dell'app per la distribuzione : creare un pacchetto MSIX che è possibile distribuire
In alternativa, esplorare altre guide:
- Creazione di un addon di Phi Silica - Informazioni su come usare l'API Di intelligenza artificiale Phi Silica
- Panoramica introduttiva - Tornare alla guida principale
Personalizzazione per il tuo modello
Per integrare completamente il modello ONNX, è necessario:
- Comprendere gli input del modello : immagini, tensori, sequenze e così via.
- Creare associazioni di input appropriate - Convertire i dati nel formato richiesto da WinML
- Elaborare gli output : analizzare e interpretare le stime del modello
- Gestire gli errori elegantemente - il caricamento e l'inferenza del modello possono fallire
Risorse aggiuntive
- Documentazione di WinML - Documentazione ufficiale di WinML
- Documentazione dell'interfaccia della riga di comando di winapp - Informazioni di riferimento complete dell'interfaccia della riga di comando
- Esempio di app Electron - Esempio di lavoro completo
- AI Dev Gallery - Raccolta di esempi di tutte le API di intelligenza artificiale
- SDK per app di Windows Samples - Raccolta di esempi di SDK per app di Windows
- node-api-dotnet - Libreria di interoperabilità JavaScript C# ↔
Risoluzione dei problemi
Compilazione non riuscita con NU1010: gli elementi PackageReference non definiscono un PackageVersion corrispondente
Verificare che tutti i pacchetti a cui viene fatto riferimento in winMlAddon.csproj abbiano voci corrispondenti in Directory.packages.props. Per l'elenco completo dei pacchetti necessari, vedere Passaggio 3.
"non un'applicazione Win32 valida" durante il caricamento del componente aggiuntivo
Ciò significa che il componente aggiuntivo è stato creato per un'architettura diversa rispetto al runtime di Node.js/Electron. Controllare l'architettura Node.js:
node -e "console.log(process.arch)"
Ricompilare quindi il componente aggiuntivo con la destinazione corrispondente:
# 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
Se di recente è stata modificata l'installazione Node.js, reinstallare node_modules anche per ottenere il file binario Electron corrispondente:
rm -rf node_modules package-lock.json
npm install
Ottenere informazioni
- È stato trovato un bug?Inviare un problema
- Domande su WinML? Controllare la documentazione di WinML
Buon apprendimento automatico! 🤖