Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Dieser Leitfaden zeigt, wie ONNX-Modellinferenz aus Electron mithilfe von JS-Bindungen für Windows App SDK ML-APIs ausgeführt wird (Ermittlung des Ausführungsanbieters über ExecutionProviderCatalog und Herunterladen des Modells über ModelCatalog), kombiniert mit onnxruntime-node für die Inferenz – kein C#-Add-On erforderlich. Inferenz wird in einem Electron-Dienstprozess ausgeführt, sodass der Hauptprozess nicht blockiert wird.
Voraussetzungen
Bevor Sie diesen Leitfaden starten, stellen Sie sicher, dass Sie folgendes haben:
- Die Einrichtung der Entwicklungsumgebung wurde abgeschlossen.
ONNX-Runtime für Node installieren:
npm install onnxruntime-node@1.24.3
Important
Die Windows-App Runtime lädt ihre eigenen onnxruntime.dll in untergeordnete Prozesse vor. Die onnxruntime-node-Version muss mit der ORT-ABI übereinstimmen, die mit Ihrer Windows App SDK-Version gebündelt ist. Verwenden Sie onnxruntime-node@1.24.xfür Windows App SDK 2.x .
Schritt 1: Bestätigen von WinML-Bindungen
Das Windows App SDK ist transitiv von Microsoft.WindowsAppSDK.ML abhängig, sodass die WinML-APIs bereits in Ihren generierten Bindings enthalten sind. Überprüfen:
Erfordert
@microsoft/dynwinrt-codegen≥0.1.0-preview.8– siehe Erste Schritte mit Electron für Fallbacks bei älteren Projekten.
node -e "console.log(Object.keys(require('#winapp/bindings')).filter(k => k.startsWith('ExecutionProvider')))"
[ 'ExecutionProvider', 'ExecutionProviderCatalog', 'ExecutionProviderReadyState' ] sollte angezeigt werden.
Schritt 2: Herunterladen des Modells über den Modellkatalog (Hauptprozess)
Verwenden Sie ModelCatalog aus JS-Bindungen, um das Modell lokal herunterzuladen und zwischenzuspeichern. Der Katalog liest ein JSON-Manifest (remote oder lokal gehostet), das verfügbare Modelle und deren Download-URLs beschreibt. Nach dem ersten Download verwenden nachfolgende Ausführung die zwischengespeicherte Kopie:
Erstellen Sie src/winml-model.js:
const { ModelCatalog, ModelCatalogSource, Uri } = require('#winapp/bindings');
const fs = require('node:fs');
const path = require('node:path');
// Remote catalog JSON hosted in the WindowsAppSDK-Samples repo
const MODEL_CATALOG_URL =
'https://raw.githubusercontent.com/microsoft/WindowsAppSDK-Samples/main/Samples/WindowsML/Resources/SqueezeNetModelCatalog.json';
async function downloadModel(modelId, onProgress) {
const uri = Uri.createUri(MODEL_CATALOG_URL);
const source = await ModelCatalogSource.createFromUriAsync(uri);
const catalog = ModelCatalog.createInstance([source]);
const model = await catalog.findModelAsync(modelId);
const op = model.getInstanceAsync();
if (onProgress) {
op.progress((value) => {
try { onProgress(value); } catch {}
});
}
const result = await op;
const instance = result.getInstance();
if (!instance) return undefined;
const paths = instance.modelPaths;
// modelPaths returns directories containing model files
for (let i = 0; i < paths.size; i++) {
const dir = paths.getAt(i);
if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
const onnx = fs.readdirSync(dir).find((f) => f.endsWith('.onnx'));
if (onnx) {
instance.close();
return path.join(dir, onnx);
}
} else if (dir.endsWith('.onnx')) {
instance.close();
return dir;
}
}
instance.close();
return undefined;
}
module.exports = { downloadModel };
Schritt 3: Ermitteln und Sicherstellen von Ausführungsanbietern (Hauptprozess)
Dient ExecutionProviderCatalog zum Auflisten der verfügbaren Anbieter (CPU, DirectML, QNN/NPU) und ensureReadyAsync zum Herunterladen der Laufzeit bei Bedarf:
Erstellen Sie src/winml-ep.js:
const { ExecutionProviderCatalog, ExecutionProviderReadyState, ExecutionProviderReadyResultState } = require('#winapp/bindings');
function listProviders() {
const catalog = ExecutionProviderCatalog.getDefault();
return catalog.findAllProviders().map((p) => ({
name: p.name,
readyState: p.readyState,
libraryPath: p.libraryPath,
}));
}
async function ensureProviderReady(providerName, onProgress) {
const catalog = ExecutionProviderCatalog.getDefault();
const providers = catalog.findAllProviders();
const provider = providers.find((p) => p.name === providerName);
if (!provider) {
throw new Error(`Execution provider not found: ${providerName}`);
}
if (provider.readyState === ExecutionProviderReadyState.Ready) {
return { name: provider.name, readyState: 'Ready', libraryPath: provider.libraryPath };
}
const op = provider.ensureReadyAsync();
if (onProgress) {
op.progress((value) => {
try { onProgress(value); } catch {}
});
}
const result = await op;
let readyState;
if (result.status === ExecutionProviderReadyResultState.Success) readyState = 'Ready';
else if (result.status === ExecutionProviderReadyResultState.Failure) readyState = 'Failed';
else readyState = 'InProgress';
return {
name: provider.name,
readyState,
diagnosticText: result.diagnosticText,
libraryPath: provider.libraryPath,
};
}
module.exports = { listProviders, ensureProviderReady };
Schritt 4: Ausführen von Rückschlüssen in einem Hilfsprogrammprozess
ONNX-Runtime-Sitzungserstellung und -ableitung blockieren – führen Sie sie in einem Electron-Hilfsprozess aus, um den Hauptprozess reaktionsfähig zu halten.
4.1. Erstellen Sie den Worker
Erstellen src/winml-worker.js (diese Datei wird in einem Hilfsprogrammprozess ausgeführt):
const { roInitialize } = require('@microsoft/dynwinrt');
// When dynwinrt and onnxruntime-node share the same process, ORT's native
// init can leave the COM apartment uninitialized. Explicitly init MTA first.
roInitialize(1);
const ort = require('onnxruntime-node');
async function runModel(modelPath, inputData, inputShape, ep) {
const providers = ep === 'dml'
? [{ name: 'dml', deviceId: 0 }, 'cpu']
: ['cpu'];
const session = await ort.InferenceSession.create(modelPath, {
executionProviders: providers,
graphOptimizationLevel: 'all',
});
const inputName = session.inputNames[0];
const input = new ort.Tensor('float32', inputData, inputShape);
const outputs = await session.run({ [inputName]: input });
return Array.from(outputs[session.outputNames[0]].data);
}
process.parentPort.on('message', async (e) => {
const { id, method, args } = e.data;
try {
if (method === 'classify') {
const [modelPath, inputData, inputShape, ep] = args;
const result = await runModel(modelPath, new Float32Array(inputData), inputShape, ep);
process.parentPort.postMessage({ id, ok: true, result });
}
} catch (err) {
process.parentPort.postMessage({ id, ok: false, error: err.message });
}
});
4.2. Worker aus main starten und aufrufen
Fügen Sie Folgendes zu Ihrem src/index.js:
const { utilityProcess } = require('electron');
const path = require('node:path');
const { listProviders, ensureProviderReady } = require('./winml-ep.js');
const { downloadModel } = require('./winml-model.js');
let worker = null;
let workerReady = null;
const pending = new Map();
let nextId = 1;
function startWinmlWorker() {
worker = utilityProcess.fork(path.join(__dirname, 'winml-worker.js'), [], {
stdio: 'pipe',
serviceName: 'winml-worker',
});
worker.on('message', (msg) => {
const entry = pending.get(msg.id);
if (!entry) return;
pending.delete(msg.id);
if (msg.ok) entry.resolve(msg.result);
else entry.reject(new Error(msg.error));
});
workerReady = new Promise((resolve) => worker.once('spawn', resolve));
}
async function classify(modelPath, inputData, inputShape, ep) {
if (!worker) startWinmlWorker();
await workerReady;
return new Promise((resolve, reject) => {
const id = nextId++;
pending.set(id, { resolve, reject });
worker.postMessage({ id, method: 'classify', args: [modelPath, Array.from(inputData), inputShape, ep] });
});
}
4.3. Verwenden Sie es
Stellen Sie sicher, dass Ihre createWindow Funktion lautet async, und fügen Sie Folgendes hinzu:
const createWindow = async () => {
// ... existing window creation code ...
// List and ensure all execution providers are ready
const providers = listProviders();
console.log('Available providers:', providers);
for (const ep of providers) {
console.log(`Ensuring ${ep.name} is ready...`);
const result = await ensureProviderReady(ep.name, (progress) => {
const pct = progress <= 1 ? Math.round(progress * 100) : Math.round(progress);
process.stdout.write(`\r ${ep.name}: ${pct}%`);
});
process.stdout.write('\n');
console.log(` ${ep.name}: ${result.readyState}`);
}
// Download model via Model Catalog (cached after first run)
console.log('Downloading model...');
const modelPath = await downloadModel('squeezenet', (progress) => {
if (progress >= 0 && progress <= 100) {
process.stdout.write(`\rDownloading model: ${Math.round(progress)}%`);
}
});
process.stdout.write('\n');
console.log('Model path:', modelPath);
// Run inference in utility process (replace with real preprocessed data)
const inputData = new Float32Array(1 * 3 * 224 * 224);
const output = await classify(modelPath, inputData, [1, 3, 224, 224], 'dml');
console.log('Model output (top 5 values):', output.slice(0, 5));
};
Schritt 5: Ausführen
npx winapp node add-electron-debug-identity
npm start
In der Konsole sollten die verfügbaren Ausführungsanbieter und die Modellausgabe angezeigt werden.
Tip
Eine vollständige Bildklassifizierungspipeline mit Bilddecodierung über JS-Bindungen (StorageFile, BitmapDecoder, BitmapTransform), finden Sie im WinML-Beispiel der Elektronengalerie.
Nächste Schritte
Herzlichen Glückwunsch! Sie führen WinML-Ausführungsanbieter und ONNX-Runtime aus JavaScript aus – kein C#-Addon erforderlich. 🎉
Jetzt sind Sie bereit für:
- Paketieren Sie Ihre App für die Verteilung — erstellen Sie ein MSIX, das Sie bereitstellen können.
Oder erkunden Sie andere Guides:
- Anzeigen einer Benachrichtigung aus JavaScript – Windows App SDK Benachrichtigungen über JS-Bindungen.
- Rufen Sie Windows APIs aus JavaScript auf – wählen Sie eine Datei mit JS-Bindungen aus.
- Rufen Sie Phi-Silikade aus JavaScript auf – fassen Sie Text mit Windows App SDK KI zusammen.
- Erstellen eines WinML-Add-Ons – systemeigenes C#-Addon-Gegenstück.
- Übersicht über erste Schritte – Kehren Sie zum Hauptleitfaden zurück.
Windows developer