Vytvoření doplňku WinML

V této příručce se dozvíte, jak vytvořit nativní doplněk jazyka C#, který ve vaší aplikaci Electron používá Windows Machine Learning (WinML). WinML umožňuje spouštět machine learning modely (formát ONNX) místně na zařízeních s Windows pro úlohy, jako je klasifikace obrázků, detekce objektů a další.

Předpoklady

Než začnete s touto příručkou, ujistěte se, že máte:

Poznámka:

WinML běží na jakémkoli zařízení Windows 10 (1809+) nebo Windows 11. Pro zajištění nejlepšího výkonu se doporučuje zařízení s GRAFICKÝmi procesory nebo NPU, ale rozhraní API funguje i na procesoru.

Důležité

Doplněk WinML vyžaduje experimental Windows App SDK. Pokud jste v winapp init průvodci nastavením vybrali stabilní sady SDK, budete muset aktualizovat verzi sady SDK. Upravte winapp.yaml a změňte verzi Microsoft.WindowsAppSDK na 2.0.0-experimental3 a potom spusťte npx winapp restore, aby se aktualizovala.

Krok 1: Vytvoření nativního doplňku jazyka C#

Pojďme vytvořit nativní doplněk, který bude používat rozhraní API WinML. Použijeme šablonu jazyka C#, která využívá node-api-dotnet k propojení JavaScriptu a C#.

npx winapp node create-addon --template cs --name winMlAddon

Tím se winMlAddon/ vytvoří složka s:

  • addon.cs – Kód v jazyce C#, který bude volat rozhraní API WinML
  • winMlAddon.csproj – Projektový soubor s odkazy na sadu Windows SDK a Windows App SDK
  • README.md - Dokumentace k používání doplňku

Příkaz také přidá skript build-winMlAddon do package.json pro sestavení addonů a skript clean-winMlAddon pro vyčištění artefaktů sestavení:

{
  "scripts": {
    "build-winMlAddon": "dotnet publish ./winMlAddon/winMlAddon.csproj -c Release",
    "clean-winMlAddon": "dotnet clean ./winMlAddon/winMlAddon.csproj"
  }
}

Šablona automaticky zahrnuje odkazy na obě sady SDK, takže můžete ihned volat Windows API.

Pojďme ověřit, jestli je všechno správně nastavené, a to sestavením doplňku:

# Build the C# addon
npm run build-winMlAddon

Poznámka:

Můžete také vytvořit doplněk jazyka C++ pomocí npx winapp node create-addon příznaku (bez příznaku --template ). Doplňky jazyka C++ používají node-addon-api a poskytují přímý přístup k rozhraním API Windows s maximálním výkonem. Viz průvodce doplňkem oznámení pro C++ pro návod nebo úplnou dokumentaci příkazů pro další možnosti.

Krok 2: Stažení modelu SqueezeNet a získání ukázkového kódu

Jako referenci použijeme ukázku Klasifikovat obrázek z galerie vývojářů AI . Tato ukázka používá model SqueezeNet 1.1 pro klasifikaci obrázků.

2.1. Stažení modelu

  1. Nainstalujte AI galerii pro vývojáře
  2. Přejít na ukázku Klasifikace obrazu
  3. Stáhněte si model SqueezeNet 1.1 (podporuje procesor, GPU a NPU).
  4. Kliknutím na Otevřít složku obsahující soubor vyhledejte .onnx .

Stažení SqueezeNet z galerie pro vývojáře AI

  1. Zkopírujte soubor squeezenet1.1.onnx do složky models/ v kořenovém adresáři project.

Poznámka:

Model lze stáhnout také přímo z úložiště ONNX Model Zoo GitHub

Krok 3: Přidání požadovaných balíčků NuGet

Před přidáním kódu WinML musíme přidat další balíčky NuGet potřebné pro zpracování obrázků, modul runtime ONNX a podporu GenAI.

3.1. Aktualizace Directory.packages.props

Do souboru v kořenovém adresáři projektu přidejte následující verze Directory.packages.props balíčků (měly by být vytvořeny při vytváření doplňku):

<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. Aktualizace winMlAddon.csproj

Otevřete winMlAddon/winMlAddon.csproj a přidejte odkazy na balíček do :<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>

Co tyto balíčky dělají:

  • Microsoft.ML.OnnxRuntime.Extensions – poskytuje další operátory a nástroje pro modul runtime ONNX
  • System.Drawing.Common – Umožňuje načítání a manipulaci s obrázky pro předběžné zpracování.
  • Microsoft. Extensions.AI – abstrakce AI pro .NET
  • Microsoft.ML.OnnxRuntimeGenAI.Managed – spravované vazby pro ONNX Runtime GenAI
  • Microsoft.ML.OnnxRuntimeGenAI.WinML – integrace WinML pro ONNX Runtime GenAI

Krok 4: Přidání ukázkového kódu

Galerie AI Dev zobrazuje úplnou implementaci klasifikace obrázků pomocí SqueezeNetu:

Vzorový kód SqueezeNet

Tento kód jsme přizpůsobili pro Elektron a kompletní implementaci najdete ve vzorku elektron-winml. Složka winMlAddon/ obsahuje upravený kód z galerie vývojářů AI.

Zkopírujte celou winMlAddon/ složku z samples/elektron-winml/winMlAddon/ do kořenového adresáře projektu a nahraďte ji vytvořenou v kroku 1. Ukázka obsahuje více souborů nad rámec addon.cs (pomocné třídy v Utils/chatovacím klientovi atd.), které jsou potřeba k sestavení a spuštění doplňku.

Důležité

Musíte zkopírovat celou složku, nejen addon.cs. Doplněk závisí na pomocných Utils/ souborech v podsložce (Prediction.cs, ImageNet.cs, BitmapFunctions.csatd.).

Klíčové podrobnosti implementace

Pojďme zdůraznit důležité části implementace a klíčové rozdíly od kódu AI Dev Gallery:

Požadavek na kořenovou cestu projektu

Na rozdíl od kódu AI Dev Gallery vyžaduje náš doplněk Electron kód JavaScriptu, aby předal kořenovou cestu projektu. To je nezbytné, protože:

  • Doplněk musí vyhledat soubor modelu ONNX ve models/ složce.
  • Nativní knihovny (DLL) je potřeba načíst z konkrétních adresářů.
[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;
}

Tím se automaticky vybere nejlepší poskytovatel spouštění (CPU, GPU nebo NPU) na základě možností zařízení.

2. Předběžné načtení nativních závislostí

Doplněk obsahuje metodu PreloadNativeDependencies() pro načtení požadovaných knihoven DLL. Tento přístup funguje pro vývojové i produkční scénáře bez nutnosti kopírovat knihovny DLL do kořenového adresáře projektu:

private void PreloadNativeDependencies()
{
    // Loads required DLLs from the winMlAddon build output
    // This ensures dependencies are available regardless of the execution context
}

Tato operace se volá během inicializace před načtením modelu a zajišťuje dostupnost všech nativních knihoven.

3. Konfigurace Electron Forge pro vytváření balíčků

Abyste měli jistotu, že doplněk v produkčních buildech funguje správně, je potřeba nakonfigurovat, aby váš packager fungoval správně:

  1. Rozbalení nativních souborů – knihovny DLL, modely ONNX a soubory .node musí být přístupné mimo archiv ASAR.
  2. Vyloučení nepotřebných souborů – Zachovejte malou velikost balíčku vyloučením artefaktů sestavení a dočasných souborů

Pro Electron Forge aktualizujte 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
};

Co to dělá:

  1. asar.unpack – Extrahuje knihovny DLL, spustitelné soubory, binární soubory .node a modely ONNX do app.asar.unpacked/

    • Díky tomu jsou přístupné za běhu prostřednictvím cest souborového systému.
    • Kód JavaScriptu upravuje cesty automaticky (viz nahrazení app.asarapp.asar.unpacked).
  2. ignore - Vyloučí z konečného balíčku:

    • .winapp/ – Balíčky a hlavičky sady SDK (není potřeba za běhu)
    • .msix soubory – zabalené výstupy
    • winMlAddon/ zdrojové soubory – uchovává pouze dist/ složku s kompilovanými binárními soubory.

Poznámka:

Pokud používáte jiný nástroj pro balení (elektron-builder atd.), budete muset nakonfigurovat podobná nastavení pro rozbalení nativních závislostí a vyloučení vývojových souborů. Informace o možnostech rozbalení ASAR najdete v dokumentaci vašeho packageru.

4. Klasifikace obrázků

Metoda ClassifyImage zpracuje obrázek a vrátí předpovědi:

[JSExport]
public async Task<Prediction[]> ClassifyImage(string imagePath)
{
    // Loads the image, preprocesses it, and runs inference
    // Returns top predictions with labels and confidence scores
}

Kompletní implementace zpracovává:

  • Načítání a předběžné zpracování obrázků (změna velikosti, normalizace)
  • Spuštění inference modelu
  • Výsledky následného zpracování pro získání nejlepších předpovědí s popisky a skóre spolehlivosti

Poznámka:

Úplný zdrojový kód zahrnuje předběžné zpracování obrázku, vytvoření tensoru a analýzu výsledků. Zkontrolujte ukázkovou implementaci pro všechny podrobnosti.

Principy kódu

Doplněk poskytuje tyto hlavní funkce:

  1. CreateAsync – Inicializuje doplněk a načte model SqueezeNet.
  2. ClassifyImage – přijímá cestu k obrázku a vrací výsledky klasifikace.

WinML automaticky vybere nejlepší spouštěcí zařízení (CPU, GPU nebo NPU) na základě dostupnosti.

Krok 5: Sestavení doplňku jazyka C#

Teď sestavte doplněk:

npm run build-winMlAddon

Tento kód C# se zkompiluje pomocí Native AOT (Ahead-of-Time kompilace), která:

  • Vytvoří binární soubor ve formátu nativního doplňku .node.
  • Ořezává nepoužívaný kód pro menší velikost balíčku.
  • Vyžaduje no .NET runtime na cílových počítačích.
  • Poskytuje nativní výkon.

Zkompilovaný doplněk bude v winMlAddon/dist/winMlAddon.node.

Krok 6: Otestování doplňku

Teď pojďme doplněk otestovat tak, že ho zavoláme z hlavního procesu. Otevřete src/main.js a postupujte takto:

6.1. Načtení doplňku

Nahoře přidejte příkazy require:

const winMlAddon = require('../winMlAddon/dist/winMlAddon.node');

6.2. Vytvoření testovací funkce

Přidejte tuto funkci pro testování klasifikace obrázků:

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);
  }
};

klíčové body:

  • Úprava cesty (app.asarapp.asar.unpacked) zajišťuje, že kód funguje ve vývojových i zabalených aplikacích.
  • Tím se přistupují k rozbaleným nativním souborům nakonfigurovaným v forge.config.js

6.3. Volání testovací funkce

Přidejte tento řádek na konec createWindow() funkce:

testWinML();

6.4. Příprava testovacích imagí

Testování klasifikace obrázků:

  1. Vytvoření test-images/ složky v kořenovém adresáři projektu
  2. Přidejte testovací obrázek s názvem sample.jpg (kód očekává tento přesný název souboru).
  3. Model SqueezeNet rozpozná 1000 různých tříd ImageNet (zvířata, objekty, scény atd.).

Po spuštění aplikace se v konzole zobrazí výsledky klasifikace.

Tip

Kompletní implementaci s obslužnými rutinami IPC, dialogovými okny pro výběr souborů a uživatelským rozhraním najdete v ukázce elektron-winml.

Krok 7: Aktualizace ladicí identity

Abychom zajistili načtení a dostupnost Windows App SDK pro použití, musíme nastavit identitu ladění, která zajistí načítání frameworku při každém spuštění naší aplikace. Stejně tak je potřeba aktualizovat identitu ladění aplikace pokaždé, když upravíte Package.appxmanifest nebo změníte prostředky, na které odkazuje manifest (například ikony aplikací). Běh:

npx winapp node add-electron-debug-identity

Tento příkaz:

  1. Přečte vaše Package.appxmanifest a zjistí podrobnosti a schopnosti aplikace.
  2. Zaregistruje electron.exe ve vaší node_modules s dočasnou identitou.
  3. Umožňuje otestovat rozhraní API vyžadující identitu bez kompletního balíčku MSIX.

Poznámka:

Tento příkaz je již součástí postinstall skriptu, který jsme přidali v průvodci nastavením, takže se spustí automaticky po npm install. Musíte ho ale spustit ručně pokaždé, když:

  • Úprava Package.appxmanifest (změna schopností, identit nebo vlastností)
  • Aktualizace zdrojů aplikace (ikony, loga atd.)

Teď spusťte aplikaci:

npm start

Zkontrolujte výstup konzoly – měli byste vidět výsledky testu WinML.

⚠️ Známý problém: Chybové ukončení aplikace nebo prázdné okno (kliknutím rozbalte)

Existuje známá chyba Windows s řídkým balením aplikací Electron, která způsobuje, že aplikace se při startu zhroutí nebo nevykreslí webový obsah. Tento problém je opravený v Windows, ale zatím se nerozšířel na všechna zařízení.

Alternativní řešení najdete v části Nastavení vývojového prostředí .

Další kroky

Gratulujeme! Úspěšně jste vytvořili nativní doplněk, který dokáže spouštět modely strojového učení pomocí WinML! 🎉

Teď jste připraveni:

Nebo prozkoumejte další příručky:

Přizpůsobení vašeho modelu

Pokud chcete plně integrovat model ONNX, budete muset:

  1. Seznamte se se vstupy modelu – obrázky, tensory, sekvence atd.
  2. Vytvoření správných vstupních vazeb – Převod dat do formátu, který WinML očekává
  3. Zpracování výstupů – Parsování a interpretace předpovědí modelu
  4. Elegantní zpracování chyb – Načítání a inference modelu může selhat

Další zdroje

Troubleshooting

Sestavení selže s NU1010: Položky PackageReference nedefinují příslušnou PackageVersion

Ujistěte se, že všechny balíčky uvedené v winMlAddon.csproj mají odpovídající položky v Directory.packages.props. Úplný seznam požadovaných balíčků najdete v kroku 3.

"není platná aplikace Win32" při načítání doplňku

To znamená, že doplněk byl vytvořen pro jinou architekturu než váš modul runtime Node.js/Electron. Zkontrolujte architekturu Node.js:

node -e "console.log(process.arch)"

Potom znovu sestavte doplněk s odpovídajícím cílem:

# 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

Pokud jste nedávno změnili instalaci Node.js, přeinstalujte také node_modules, abyste získali odpovídající binární soubor pro Electron.

rm -rf node_modules package-lock.json
npm install

Získání nápovědy

Šťastné strojové učení! 🤖