Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este guia mostra como criar um complemento nativo em C# que usa o WinML (Windows Machine Learning) em seu aplicativo Electron. O WinML permite que você execute machine learning modelos (formato ONNX) localmente em dispositivos Windows para tarefas como classificação de imagem, detecção de objetos e muito mais.
Pré-requisitos
Antes de iniciar este guia, verifique se você:
- Concluído a configuração do ambiente de desenvolvimento
- Windows 11 ou Windows 10 (versão 1809 ou posterior)
Observação
O WinML é executado em qualquer dispositivo Windows 10 (1809+) ou Windows 11. Para obter melhor desempenho, dispositivos com GPUs ou NPUs são recomendados, mas a API também funciona na CPU.
Importante
O complemento WinML requer o experimental SDK do Aplicativo Windows. Se você selecionou "SDKs estáveis" no guia de instalação durante winapp init, precisará atualizar sua versão do SDK. Edite winapp.yaml e altere a versão Microsoft.WindowsAppSDK para 2.0.0-experimental3 e execute npx winapp restore para atualizar.
Etapa 1: Criar um complemento nativo em C#
Vamos criar um complemento nativo que usará APIs WinML. Usaremos um modelo C# que aproveita o node-api-dotnet para fazer a ponte entre JavaScript e C#.
npx winapp node create-addon --template cs --name winMlAddon
Isso cria uma winMlAddon/ pasta com:
-
addon.cs- Seu código C# que chamará APIs WinML -
winMlAddon.csproj– arquivo Project com referências ao SDK do Windows e SDK do Aplicativo Windows -
README.md- Documentação sobre como usar o complemento
O comando também adiciona um build-winMlAddon script à sua package.json criação do complemento e um clean-winMlAddon script para limpar artefatos 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 para ambos os SDKs, para que você possa iniciar imediatamente a chamada Windows APIs!
Vamos verificar se tudo está configurado corretamente criando o complemento:
# Build the C# addon
npm run build-winMlAddon
Observação
Você também pode criar um complemento C++ usando npx winapp node create-addon (sem o --template sinalizador). Os complementos do C++ usam node-addon-api e fornecem acesso direto às APIs Windows com desempenho máximo. Consulte o guia do Complemento de Notificação do C++ para obter um passo a passo ou a documentação completa do comando para obter mais opções.
Etapa 2: Baixar o modelo SqueezeNet e obter o código de exemplo
Usaremos o exemplo classificar imagem da Galeria de Desenvolvimento de IA como nossa referência. Este exemplo usa o modelo SqueezeNet 1.1 para classificação de imagem.
2.1. Baixar o modelo
- Instalar a Galeria de Desenvolvimento de IA
- Navegue até o exemplo Classificar Imagem
- Baixe o modelo SqueezeNet 1.1 (ele dá suporte a CPU, GPU e NPU)
- Clique em Abrir Pasta De Contenção para localizar o
.onnxarquivo
- Copie o arquivo
squeezenet1.1.onnxpara uma pastamodels/na raiz do project
Observação
O modelo também pode ser baixado diretamente do repositório ONNX Model Zoo GitHub
Etapa 3: Adicionar pacotes NuGet necessários
Antes de adicionar o código WinML, precisamos adicionar pacotes NuGet adicionais necessários para o processamento de imagens, o ONNX Runtime e o suporte ao GenAI.
3.1. Atualizar Directory.packages.props
Adicione as seguintes versões de pacote ao Directory.packages.props arquivo na raiz do projeto (deveria ter sido criado quando você criou o 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. Atualizar winMlAddon.csproj
Abra winMlAddon/winMlAddon.csproj e adicione as referências de 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 esses pacotes fazem:
- Microsoft.ML.OnnxRuntime.Extensions – fornece operadores e utilitários adicionais para o ONNX Runtime
- System.Drawing.Common – Habilita o carregamento e a manipulação de imagem para pré-processamento
- Microsoft. Extensions.AI – abstrações de IA para .NET
- Microsoft.ML.OnnxRuntimeGenAI.Managed – Associações gerenciadas para o ONNX Runtime GenAI
- Microsoft.ML.OnnxRuntimeGenAI.WinML – integração winml para ONNX Runtime GenAI
Etapa 4: Adicionar o código de exemplo
A Galeria de Desenvolvimento de IA mostra a implementação completa para classificação de imagem com SqueezeNet:
Adaptamos esse código para o Electron e você pode encontrar a implementação completa no exemplo de electron-winml. A winMlAddon/ pasta contém o código modificado da Galeria de Desenvolvimento de IA.
Copie a pasta inteira winMlAddon/ de exemplos/electron-winml/winMlAddon/ para a raiz do projeto, substituindo a criada na Etapa 1. O exemplo inclui vários arquivos além addon.cs (classes auxiliares em Utils/, um cliente de chat etc.) que são necessários para o complemento criar e executar.
Importante
Você deve copiar a pasta inteira, não apenas addon.cs. O complemento depende de arquivos auxiliares na Utils/ subpasta (Prediction.cs, , ImageNet.csBitmapFunctions.csetc.).
Principais detalhes da implementação
Vamos realçar as partes importantes da implementação e as principais diferenças do código da Galeria de Desenvolvimento de IA:
1. Requisito de Caminho Raiz do Projeto
Ao contrário do código da Galeria de Desenvolvimento de IA, nosso addon Electron requer que o código JavaScript passe o caminho raiz do projeto. Isso é necessário porque:
- O complemento precisa localizar o arquivo de modelo ONNX na
models/pasta - As DLLs (dependências nativas) precisam ser carregadas 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;
}
Isso seleciona automaticamente o melhor provedor de execução (CPU, GPU ou NPU) com base nos recursos do dispositivo.
2. Pré-carregamento de dependências nativas
O complemento inclui um PreloadNativeDependencies() método para carregar DLLs necessárias. Essa abordagem funciona para cenários de desenvolvimento e produção sem a 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
}
Isso é chamado durante a inicialização antes de carregar o modelo, garantindo que todas as bibliotecas nativas estejam disponíveis.
3. Configurando o Electron Forge para Empacotação
Para garantir que o complemento funcione corretamente em builds de produção, você precisa configurar o empacotador para:
- Desempacotar arquivos nativos – DLLs, modelos ONNX e arquivos .node devem estar acessíveis fora do arquivo ASAR
- Excluir arquivos desnecessários – Mantenha o tamanho do pacote pequeno excluindo artefatos de build e arquivos temporários
Para Electron Forge, atualize 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 isso faz:
asar.unpack- Extrai DLLs, executáveis, binários .node e modelos ONNX paraapp.asar.unpacked/- Isso os torna acessíveis no runtime por meio de caminhos do sistema de arquivos
- O código JavaScript ajusta caminhos automaticamente (consulte a
app.asarsubstituição de →app.asar.unpackedacima)
ignore– Exclui do pacote final:-
.winapp/- Pacotes e cabeçalhos do SDK (não necessários no runtime) -
.msixarquivos – Saídas empacotadas -
winMlAddon/arquivos de origem – mantém apenas adist/pasta com binários compilados
-
Observação
Se você estiver usando uma ferramenta de empacotamento diferente (construtor de elétrons etc.), precisará definir configurações semelhantes para desempacotar dependências nativas e excluir arquivos de desenvolvimento. Verifique a documentação do empacotador para opções de descompactação ASAR.
4. Classificação de imagem
O ClassifyImage método processa uma imagem e retorna 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 lida com:
- Carregamento e pré-processamento de imagem (redimensionamento, normalização)
- Executando a inferência do modelo
- Resultados pós-processamento para obter previsões principais com rótulos e pontuações de confiança
Observação
O código-fonte completo inclui pré-processamento de imagem, criação de tensor e análise de resultados. Verifique a implementação de exemplo para obter todos os detalhes.
Noções básicas sobre o código
O complemento fornece estas funções principais:
- CreateAsync – Inicializa o complemento e carrega o modelo SqueezeNet
- ClassifyImage – Usa 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.
Etapa 5: Criar o complemento em C#
Agora, crie o complemento:
npm run build-winMlAddon
Isso compila seu código C# usando Native AOT (compilação em tempo antecipado), que:
- Cria um
.nodebinário (formato de complemento nativo) - Corta o código não utilizado para um tamanho de pacote menor
- Não requer .NET runtime em computadores de destino
- Fornece desempenho nativo
O complemento compilado estará em winMlAddon/dist/winMlAddon.node.
Etapa 6: Testar o complemento
Agora, vamos testar se o complemento funciona chamando-o do processo principal. Abra src/main.js e siga estas etapas:
6.1. Carregar o complemento
Adicione as declarações 'require' na parte superior:
const winMlAddon = require('../winMlAddon/dist/winMlAddon.node');
6.2. Criar uma função de teste
Adicione esta função para testar a classificação de 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 funcione tanto em ambientes de desenvolvimento quanto em aplicativos empacotados. - Isso acessa os arquivos nativos desempacotados 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 imagem:
- Criar uma
test-images/pasta na raiz do projeto - Adicionar uma imagem de teste chamada
sample.jpg(o código espera esse nome de arquivo exato) - O modelo SqueezeNet reconhece 1000 classes ImageNet diferentes (animais, objetos, cenas etc.)
Ao executar o aplicativo, você verá os resultados da classificação no console!
Dica
Para obter uma implementação completa com manipuladores de IPC, caixas de diálogo de seleção de arquivo e uma interface do usuário, consulte o exemplo de electron-winml.
Etapa 7: Atualizar identidade de depuração
Para garantir que o SDK do Aplicativo Windows esteja carregado e disponível para uso, precisamos garantir que configuremos a identidade de depuração, o que garantirá que a estrutura seja carregada sempre que nosso aplicativo for executado. Da mesma forma, sempre que você modificar Package.appxmanifest ou alterar recursos referenciados no manifesto (como ícones de aplicativo), será necessário atualizar a identidade de depuração do seu aplicativo. Execute:
npx winapp node add-electron-debug-identity
Este comando:
- Lê o seu
Package.appxmanifestpara obter detalhes e funcionalidades do aplicativo - Registra
electron.exeem suanode_modulescom uma identidade temporária - Permite testar APIs que requerem identidade sem o empacotamento MSIX completo
Observação
Esse comando já faz parte do postinstall script que adicionamos no guia de instalação, portanto, ele é executado automaticamente após npm install. No entanto, você precisa executá-lo manualmente sempre que:
- Modificar
Package.appxmanifest(alterar recursos, identidade ou propriedades) - Atualizar ativos do aplicativo (ícones, logotipos etc.)
Agora, execute seu aplicativo:
npm start
Verifique a saída do console – você deve ver os resultados do teste do WinML!
⚠️ Problema Conhecido: Falhas de aplicativo ou janela em branco (clique para expandir)
Há um bug de Windows conhecido com aplicativos Electron de empacotamento esparso que faz com que o aplicativo falhe no início ou não renderize o conteúdo da Web. O problema foi corrigido em Windows mas ainda não foi propagado para todos os dispositivos.
Consulte a configuração do ambiente de desenvolvimento para solução alternativa.
Próximas etapas
Parabéns! Você criou com êxito um complemento nativo que pode executar modelos de machine learning com o WinML! 🎉
Agora você está pronto para:
- Empacotar seu aplicativo para distribuição – Criar um pacote MSIX que você pode distribuir
Ou explore outros guias:
- Criando um complemento Phi Silica – Saiba como usar a API de IA do Phi Silica
- Visão geral de introdução – Retornar ao guia principal
Personalizando para seu modelo
Para integrar totalmente seu modelo ONNX, você precisará:
- Entenda as entradas do modelo – Imagens, tensores, sequências etc.
- Criar associações de entrada adequadas – Converter seus dados no formato que o WinML espera
- Processar as saídas – Analisar e interpretar as previsões do modelo
- Manipular erros de forma eficaz – 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 do winapp – Referência completa da CLI
- Exemplo de aplicativo Electron – exemplo totalmente funcional
- Galeria de Desenvolvimento de IA – Galeria de exemplos de todas as APIs de IA
- SDK do Aplicativo Windows Samples - Coleção de amostras de SDK do Aplicativo Windows
- node-api-dotnet – biblioteca de interoperabilidade do JavaScript em C# ↔
Solução de problemas
Falha na compilação com NU1010: os itens PackageReference não definem um PackageVersion correspondente
Verifique se todos os pacotes referenciados em winMlAddon.csproj têm entradas correspondentes em Directory.packages.props. Consulte a Etapa 3 para obter a lista completa de pacotes necessários.
"não é um aplicativo Win32 válido" ao carregar o complemento
Isso significa que o complemento foi criado para uma arquitetura diferente da Node.js/Electron runtime. Verifique sua arquitetura de Node.js:
node -e "console.log(process.arch)"
Em seguida, recompile o complemento com o destino 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 você alterou recentemente sua instalação do Node.js, também reinstale node_modules para obter o binário Electron compatível.
rm -rf node_modules package-lock.json
npm install
Obter Ajuda
- Encontrou um bug?Registrar um problema
- Perguntas sobre WinML? Verificar a documentação do WinML
Feliz aprendizado de máquina! 🤖
Windows developer