Advanced Concepts
Adding Windows native code to an application
Use .NET libraries, NuGet packages, WinRT components and Win32 DLLs in NativeScript Windows apps.
Experimental
The Windows platform is experimental. See Developing for Windows.
All of the Windows Runtime (Windows.*) and WinUI 3 (Microsoft.*) APIs are available to your app without any setup. On top of that, you can add:
The Windows host project
When you build for Windows, the CLI generates a WinUI 3 host project in platforms/windows/<ProjectName>/ and builds it with dotnet build. You don't edit the generated project directly. Instead, add MSBuild files to App_Resources/Windows, which are imported by the host project:
App_Resources/
├─ Windows/
│ ├─ app.csproj # imported by the host project
│ ├─ before-plugins.props # imported before plugin files
│ ├─ after-plugins.props # imported after plugin files
│ ├─ Package.appxmanifest
│ └─ Assets/
└─ ... moreapp.csprojis the place for most customizations, such as package references and build properties. It is similar toapp.gradleon Android.before-plugins.propsandafter-plugins.propslet you set properties before plugins are applied or override values set by plugins. They are similar tobefore-plugins.gradleon Android.
All three files are optional MSBuild fragments with a <Project> root element:
<!-- App_Resources/Windows/app.csproj -->
<Project>
<PropertyGroup>
<ApplicationManifest Condition="'$(ApplicationManifest)' == '' and Exists('$(MSBuildThisFileDirectory)app.manifest')">$(MSBuildThisFileDirectory)app.manifest</ApplicationManifest>
</PropertyGroup>
</Project>Note
The whole App_Resources/Windows folder is copied into the host project, so $(MSBuildThisFileDirectory) points at the copied folder. To reference files elsewhere in your project, use $(MSBuildProjectDirectory)\..\..\..\, which resolves to your project root.
Using .NET libraries
The Windows runtime hosts .NET in-process, so .NET APIs can be called directly from JavaScript. The base class library is available through the System global:
const stopwatch = System.Diagnostics.Stopwatch.StartNew()
// ... do some work
stopwatch.Stop()
console.log(`Took ${stopwatch.ElapsedMilliseconds}ms`)
console.log(System.Environment.MachineName)Adding a NuGet package
Add a PackageReference to App_Resources/Windows/app.csproj:
<Project>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>Then register the root namespace of the library with the assembly that contains it, and use it from JavaScript:
NSWinRT.dotnet.registerNamespace('Newtonsoft', 'Newtonsoft.Json')
const json = Newtonsoft.Json.JsonConvert.SerializeObject({ hello: 'world' })registerNamespace(root, assemblyName) defines a global for the namespace root (Newtonsoft above), which resolves types from the given assembly. Assemblies are loaded from the app's output folder, including its libs and plugins subfolders.
Adding your own C# code
To add your own C# code, create a .NET class library targeting net10.0 (or net10.0-windows10.0.xxxxx.0 if it uses WinRT APIs) in your project, for example in native/windows/MyLibrary, and reference it from App_Resources/Windows/app.csproj:
<Project>
<ItemGroup>
<ProjectReference Include="$(MSBuildProjectDirectory)\..\..\..\native\windows\MyLibrary\MyLibrary.csproj" />
</ItemGroup>
</Project>// native/windows/MyLibrary/Greeter.cs
namespace MyCompany.Native;
public static class Greeter
{
public static string Hello(string name) => $"Hello {name} from C#!";
}NSWinRT.dotnet.registerNamespace('MyCompany', 'MyLibrary')
console.log(MyCompany.Native.Greeter.Hello('NativeScript'))
// prints: Hello NativeScript from C#!.NET tasks and delegates
- Convert a returned
Taskto a promise withNSWinRT.toPromise(task). - Create a .NET delegate (for example a
System.Action) withNSWinRT.dotnet.asDelegate('System.Action', fn). For WinRT delegates useNSWinRT.asDelegateinstead, see Windows Marshalling › Events. - .NET objects are released when they are garbage collected. Call
obj.release()to release one immediately.
Adding C++/WinRT components
Any WinRT component, for example one written in C++/WinRT, can be used from JavaScript once its metadata (.winmd) and implementation (.dll) are deployed with the app, and its classes are registered in the app manifest. @nativescript/core itself uses this approach for its NativeScript.Widgets component.
Note
Building C++/WinRT components requires Visual Studio with the Desktop development with C++ workload and the C++/WinRT extension. Build the component for every architecture you ship (x64, arm64).
1. Deploy the component
Place the built files in App_Resources/Windows, one folder per architecture:
App_Resources/
└─ Windows/
├─ app.csproj
└─ libs/
├─ x64/
│ ├─ MyCompany.Native.dll
│ └─ MyCompany.Native.winmd
└─ arm64/
├─ MyCompany.Native.dll
└─ MyCompany.Native.winmdCopy the files matching the target architecture next to the app executable with a target in app.csproj:
<Project>
<Target Name="CopyMyCompanyNative" AfterTargets="Build">
<ItemGroup>
<_MyNativeFiles Include="$(MSBuildThisFileDirectory)libs\$(Platform)\*.dll;$(MSBuildThisFileDirectory)libs\$(Platform)\*.winmd" />
</ItemGroup>
<Copy SourceFiles="@(_MyNativeFiles)" DestinationFolder="$(OutDir)" SkipUnchangedFiles="true" />
</Target>
</Project>On startup, the runtime loads every .winmd file found next to the executable and in the app root.
2. Register the activatable classes
Add an inProcessServer extension for your classes to App_Resources/Windows/Package.appxmanifest:
<Package ...>
<!-- ... -->
<Extensions>
<Extension Category="windows.activatableClass.inProcessServer">
<InProcessServer>
<Path>MyCompany.Native.dll</Path>
<ActivatableClass ActivatableClassId="MyCompany.Native.Greeter" ThreadingModel="both" />
</InProcessServer>
</Extension>
</Extensions>
</Package>3. Use it from JavaScript
The component's namespaces are available as globals, like Windows and Microsoft:
const greeter = new MyCompany.Native.Greeter()
console.log(greeter.Hello('NativeScript'))Note
When using TypeScript, you can generate typings from the component's .winmd, or declare the root namespace as any:
declare const MyCompany: anyCalling Win32 DLLs
Functions exported from Win32 DLLs can be called without any native code using NSWinRT.win32:
const kernel32 = NSWinRT.win32.define(
'kernel32.dll',
{ GetTickCount64: [] },
'u64',
)
console.log(kernel32.GetTickCount64())See Windows Marshalling › Win32 functions for the supported types.
Plugins
Plugins provide Windows implementations with .windows.ts files. Native Windows files are placed in the plugin's platforms/windows folder:
my-plugin/
├─ index.windows.ts
├─ plugin.props # optional, imported by the host project
├─ plugin.targets # optional, imported by the host project
└─ platforms/
└─ windows/
├─ x64/
└─ arm64/The CLI copies the contents of platforms/windows into the host project (under plugins/<plugin-name>/) and imports the plugin's plugin.props and plugin.targets files. Use them to add package references, copy native files to the output folder or register activatable classes, the same way an app does with app.csproj. When a plugin has no plugin.props/plugin.targets, the CLI generates default ones that copy the plugin's files into plugins\<plugin-name> in the app output folder.
See @nativescript/core's plugin.targets for a complete example that deploys a C++/WinRT component and registers its classes.
- Previous
- Adding ObjectiveC/Swift Code
