Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este guia mostra-lhe como criar um addon nativo de C# que utilize Windows Machine Learning (WinML) na sua aplicação Electron. O WinML permite-lhe executar modelos de machine learning (formato ONNX) localmente em dispositivos Windows para tarefas como classificação de imagens, deteção de objetos e mais.
Pré-requisitos
Antes de começar este guia, certifique-se de que:
- Concluí a configuração do ambiente de desenvolvimento
- Windows 11 ou Windows 10 (versão 1809 ou posterior)
Observação
O WinML funciona em qualquer dispositivo Windows 10 (1809+) ou Windows 11. Para melhor desempenho, recomendam-se dispositivos com GPUs ou NPUs, mas a API também funciona no CPU.
Importante
O addon WinML requer o experimental SDK de Aplicações Windows. Se selecionaste "SDKs estáveis" no winapp init guia de configuração, terás de atualizar a versão do SDK. Edita winapp.yaml e altera a versão Microsoft.WindowsAppSDK para 2.0.0-experimental3, depois executa npx winapp restore para atualizar.
Passo 1: Criar um addon nativo de C#
Vamos criar um addon nativo que utilize APIs do WinML. Vamos usar um template em C# que aproveita node-api-dotnet para fazer a ponte entre JavaScript e C#.
npx winapp node create-addon --template cs --name winMlAddon
Isto cria uma winMlAddon/ pasta com:
-
addon.cs- O seu código C# que irá chamar as APIs do WinML -
winMlAddon.csproj- ficheiro Project com referências ao Windows SDK e SDK de Aplicações Windows -
README.md- Documentação sobre como usar o addon
O comando também adiciona um build-winMlAddon script ao teu package.json para construir o addon e um clean-winMlAddon script para limpar artefactos de build:
{
"scripts": {
"build-winMlAddon": "dotnet publish ./winMlAddon/winMlAddon.csproj -c Release",
"clean-winMlAddon": "dotnet clean ./winMlAddon/winMlAddon.csproj"
}
}
O modelo inclui automaticamente referências a ambos os SDKs, para que possa começar imediatamente a chamar APIs do Windows!
Vamos verificar se tudo está configurado corretamente ao construir o adddon:
# Build the C# addon
npm run build-winMlAddon
Observação
Também podes criar um addon em C++ usando npx winapp node create-addon (sem o --template flag). Os addons C++ utilizam node-addon-api e fornecem acesso direto a APIs Windows com desempenho máximo. Consulte o guia do Adicionar de Notificações em C++ para uma explicação detalhada ou a documentação completa dos comandos para mais opções.
Passo 2: Descarregue o modelo SqueezeNet e obtenha código de exemplo
Vamos usar a amostra Classify Image da AI Dev Gallery como referência. Esta amostra utiliza o modelo SqueezeNet 1.1 para classificação de imagens.
2.1. Descarregue o modelo
- Instalar a Galeria de Desenvolvimento de IA
- Vá para o exemplo Classify Image
- Descarregue o modelo SqueezeNet 1.1 (suporta CPU, GPU e NPU)
- Clique em Abrir Pasta Contendo para localizar o
.onnxficheiro
- Copie o ficheiro
squeezenet1.1.onnxpara uma pastamodels/na raiz do seu projeto
Observação
O modelo pode também ser descarregado diretamente do ONNX Model Zoo GitHub repositório
Passo 3: Adicionar os Pacotes NuGet Obrigatórios
Antes de adicionar o código WinML, precisamos de adicionar pacotes NuGet adicionais necessários para processamento de imagem, ONNX Runtime e suporte a GenAI.
3.1. Atualize o arquivo Directory.packages.props
Adicione as seguintes versões de pacotes ao Directory.packages.props ficheiro na raiz do seu projeto (deveria ter sido criada quando criou o adddon):
<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. Atualizar winMlAddon.csproj
Abra winMlAddon/winMlAddon.csproj e adicione as referências do pacote ao <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>
O que fazem estes pacotes:
- Microsoft.ML.OnnxRuntime.Extensions - Fornece operadores e utilidades adicionais para o Runtime ONNX
- System.Drawing.Common - Permite carregamento e manipulação de imagens para pré-processamento
- Microsoft. Extensions.AI - Abstrações de IA para .NET
- Microsoft.ML.OnnxRuntimeGenAI.Managed - Ligações geridas para ONNX Runtime GenAI
- Microsoft.ML.OnnxRuntimeGenAI.WinML - Integração WinML para ONNX Runtime GenAI
Passo 4: Adicionar o Código de Exemplo
A AI Dev Gallery mostra a implementação completa para classificação de imagens com o SqueezeNet:
Adaptámos este código para o Electron e pode encontrar a implementação completa na amostra electron-winml. A winMlAddon/ pasta contém o código modificado da AI Dev Gallery.
Copie a pasta inteira winMlAddon/ de samples/electron-winml/winMlAddon/ para a raiz do seu projeto, substituindo a criada no Passo 1. O exemplo inclui vários ficheiros além addon.cs disso (classes auxiliares em Utils/, um cliente de chat, etc.) que são necessários para que o addon seja construído e executado.
Importante
Deve copiar a pasta inteira, não apenas addon.cs. O addon depende dos ficheiros auxiliares na Utils/ subpasta (Prediction.cs, ImageNet.cs, BitmapFunctions.cs, etc.).
Detalhes Chave da Implementação
Vamos destacar as partes importantes da implementação e as principais diferenças em relação ao código da AI Dev Gallery:
1. Requisito do Project Root Path
Ao contrário do código da AI Dev Gallery, o nosso addon Electron requer que o código JavaScript passe pelo caminho raiz do projeto. Isto é necessário porque:
- O addon precisa de localizar o ficheiro do modelo ONNX na
models/pasta - Dependências nativas (DLLs) precisam de ser carregadas a partir de diretórios 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;
}
Isto seleciona automaticamente o melhor fornecedor de execução (CPU, GPU ou NPU) com base nas capacidades do dispositivo.
2. Pré-carregamento de dependências nativas
O addon inclui um PreloadNativeDependencies() método para carregar as DLLs necessárias. Esta abordagem funciona tanto para cenários de desenvolvimento como de produção sem necessidade de copiar DLLs para a raiz do projeto:
private void PreloadNativeDependencies()
{
// Loads required DLLs from the winMlAddon build output
// This ensures dependencies are available regardless of the execution context
}
Isto é chamado durante a inicialização antes de carregar o modelo, garantindo que todas as bibliotecas nativas estão disponíveis.
3. Configuração do Electron Forge para Empacotamento
Para garantir que o addon funciona corretamente em builds de produção, precisa de configurar o seu empacotador para:
- Desembalar ficheiros nativos - DLLs, modelos ONNX e ficheiros .node devem estar acessíveis fora do arquivo ASAR
- Excluir ficheiros desnecessários - Mantenha o tamanho do pacote pequeno excluindo artefactos de compilação e ficheiros temporários
Para Electron Forge, atualize o seu 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
};
O que isto faz:
asar.unpack- Extrai DLLs, executáveis, binários .node e modelos ONNX paraapp.asar.unpacked/- Isto torna-as acessíveis em tempo de execução através de caminhos do sistema de ficheiros
- O código JavaScript ajusta automaticamente os caminhos (ver a
app.asarsubstituição →app.asar.unpackedacima)
ignore- Exclui do pacote final:-
.winapp/- Pacotes e cabeçalhos SDK (não necessários em tempo de execução) -
.msixficheiros - Resultados empacotados -
winMlAddon/ficheiros de origem - Mantém apenas adist/pasta com binários compilados
-
Observação
Se estiveres a usar uma ferramenta de embalagem diferente (electron-builder, etc.), terás de configurar definições semelhantes para desempacotar dependências nativas e excluir ficheiros de desenvolvimento. Verifique a documentação do seu empacotador para as opções de descompactação ASAR.
4. Classificação da Imagem
O ClassifyImage método processa uma imagem e devolve previsões:
[JSExport]
public async Task<Prediction[]> ClassifyImage(string imagePath)
{
// Loads the image, preprocesses it, and runs inference
// Returns top predictions with labels and confidence scores
}
A implementação completa aborda:
- Carregamento e pré-processamento de imagem (redimensionamento, normalização)
- Execução da inferência do modelo
- Pós-processamento de resultados para obter as melhores previsões com etiquetas e níveis de confiança
Observação
O código-fonte completo inclui pré-processamento de imagens, criação de tensores e análise de resultados. Verifique a implementação de exemplo para todos os detalhes.
Compreender o Código
O addon fornece as seguintes funções principais:
- CreateAsync - Inicializa o addon e carrega o modelo SqueezeNet
- ClassifyImage - Toma um caminho de imagem e retorna previsões de classificação
O WinML seleciona automaticamente o melhor dispositivo de execução (CPU, GPU ou NPU) com base na disponibilidade.
Passo 5: Constrói o addon C#
Agora constrói o adddon:
npm run build-winMlAddon
Isto compila o código C# usando Native AOT (compilação antecipada), que:
- Cria um
.nodebinário (formato nativo de addon) - Remove o código não utilizado para um tamanho de pacote menor
- Não requer tempo de execução do .NET nas máquinas alvo
- Proporciona desempenho nativo
O addon compilado estará em winMlAddon/dist/winMlAddon.node.
Passo 6: Testa o Addon
Agora vamos testar o funcionamento do addon chamando-o do processo principal. Abra src/main.js e siga estes passos:
6.1. Carregar o Addon
Adicione as instruções de requerimento no topo:
const winMlAddon = require('../winMlAddon/dist/winMlAddon.node');
6.2. Criar uma Função de Teste
Adicione esta função para testar a classificação da imagem:
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);
}
};
Pontos principais:
- O ajuste de caminho (
app.asar→app.asar.unpacked) garante que o código funciona tanto em aplicações de desenvolvimento como em aplicações empacotadas - Isto acede aos ficheiros nativos descompactados configurados em
forge.config.js
6.3. Chamar a Função de Teste
Adicione esta linha no final da createWindow() função:
testWinML();
6.4. Preparar Imagens de Teste
Para testar a classificação de imagens:
- Crie uma
test-images/pasta na raiz do seu projeto - Adicione uma imagem de teste chamada
sample.jpg(o código espera exatamente este nome de ficheiro) - O modelo SqueezeNet reconhece 1000 classes diferentes de ImageNet (animais, objetos, cenas, etc.)
Quando executares a aplicação, vais ver os resultados da classificação na consola!
Sugestão
Para uma implementação completa com IPC handlers, diálogos para seleção de ficheiros e uma interface de utilizador, veja electron-winml sample.
Passo 7: Atualizar a Identidade de Depuração
Para garantir que o SDK de Aplicações Windows está carregado e disponível para uso, precisamos de garantir que configuramos a identidade de depuração que garantirá que o framework seja carregado sempre que a nossa aplicação for executada. Da mesma forma, sempre que modifica Package.appxmanifest ou altera os ativos referenciados no manifesto (como ícones da app), é necessário atualizar a identidade de depuração da sua aplicação. Corrida:
npx winapp node add-electron-debug-identity
Este comando:
- Lê o seu
Package.appxmanifestpara obter detalhes e funcionalidades da aplicação. - Regista
electron.exeno seunode_modulescom uma identidade temporária - Permite testar APIs que exigem identidade sem empacotamento MSIX completo
Observação
Este comando já faz parte do postinstall script que adicionámos no guia de configuração, por isso corre automaticamente após npm install. No entanto, é necessário executá-lo manualmente sempre que:
- Modificar
Package.appxmanifest(alterar capacidades, identidade ou propriedades) - Atualize os recursos da aplicação (ícones, logótipos, etc.)
Agora executa a tua aplicação:
npm start
Verifica a saída da consola – deves ver os resultados do teste do WinML!
⚠️ Problema Conhecido: A Aplicação Crasha ou Janela Em Branco (clique para expandir)
Existe um bug conhecido no Windows com aplicações Electron em empacotamento esparso que faz com que a aplicação crashe ao iniciar ou não renderize conteúdo web. O problema foi resolvido no Windows, mas ainda não se propagou a todos os dispositivos.
Consulte configuração do ambiente de desenvolvimento para solução alternativa.
Próximas Etapas
Parabéns! Criou com sucesso um addon nativo que pode correr modelos de aprendizagem automática com WinML! 🎉
Agora está pronto para:
- Embale a sua aplicação para distribuição - Crie um pacote MSIX que possa distribuir
Ou explore outros guias:
- Criar um Addon Phi Silica - Aprenda a usar a API de IA da Phi Silica
- Visão Geral do Início - Voltar ao guia principal
Personalização para o Seu Modelo
Para integrar totalmente o seu modelo ONNX, terá de:
- Compreende as entradas do seu modelo - imagens, tensores, sequências, etc.
- Crie bindings de entrada adequados - Converta os seus dados para o formato que o WinML espera
- Processar os resultados - Analisar e interpretar as previsões do modelo
- Lidar com erros de forma elegante - O carregamento e a inferência do modelo podem falhar
Recursos adicionais
- Documentação do WinML - Documentação oficial do WinML
- Documentação da CLI winapp - Referência completa da CLI
- Exemplo de Aplicação Electron - Exemplo completo de funcionamento
- Galeria de Desenvolvimento de IA - Galeria de exemplos de todas as APIs de IA
- SDK de Aplicações Windows Amostras - Recolha de SDK de Aplicações Windows amostras
- node-api-dotnet - Biblioteca interop JavaScript em C# ↔
Troubleshooting
Falhas na compilação com o NU1010: Os itens PackageReference não definem uma PackageVersion correspondente
Certifique-se de que todos os pacotes referenciados em winMlAddon.csproj têm entradas correspondentes em Directory.packages.props. Consulte o Passo 3 para a lista completa de pacotes obrigatórios.
"não é uma aplicação Win32 válida" ao carregar o addon
Isto significa que o addon foi criado para uma arquitetura diferente da do teu tempo de execução Node.js/Electron. Verifique a sua arquitetura Node.js:
node -e "console.log(process.arch)"
Depois reconstrói o addon com o alvo correspondente:
# 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 mudaste recentemente a tua instalação Node.js, reinstala node_modules também para obter o binário Electron correspondente:
rm -rf node_modules package-lock.json
npm install
Obter Ajuda
- Encontraste um bug?Apresenta uma reclamação
- Perguntas sobre WinML? Consulta a documentação do WinML
Boa aprendizagem automática! 🤖
Windows developer