Skip to content

Commit

Permalink
Hide unmanaged-callers-only methods
Browse files Browse the repository at this point in the history
  • Loading branch information
wegylexy committed Jul 16, 2021
1 parent 16ba7e0 commit 64c5a10
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 88 deletions.
61 changes: 46 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,60 @@
# FlyByWireless.XPLM
P/Invoke for X-Plane plugin library manager

## Project
ILCompiler must be a top level dependency:
## Reference Packages
`Microsoft.DotNet.ILCompiler` must be a top level dependency for native AOT:
```xml
<PackageReference Include="FlyByWireless.XPLM" Version="1.0.0-*"/>
<PackageReference Include="Microsoft.DotNet.ILCompiler" Version="6.0.0-*"/>
<PackageReference Include="FlyByWireless.XPLM" Version="1.0.*-*"/>
<PackageReference Include="Microsoft.DotNet.ILCompiler" Version="6.0.*-*"/>
```
The plugin must export native entry points:
## Implement
The plugin must implement `FlyByWireless.XPLM.XPluginBase` as `XPlugin` in its assembly root namespace:
```cs
[UnmanagedCallersOnly(EntryPoint = "XPluginStart")]
public static int Start(in byte name, in byte signature, in byte description);
using FlyByWireless.XPLM;
using System;

[UnmanagedCallersOnly(EntryPoint = "XPluginStop")]
public static void Stop();
namespace XplTemplate
{
sealed class XPlugin : XPluginBase
{
public override string? Name => "Fly by Wireless";
public override string? Signature => "hk.timtim.flybywireless";
public override string? Description => "X-Plane plugin library template.";

[UnmanagedCallersOnly(EntryPoint = "XPluginEnable")]
public static int Enable();
public XPlugin() : base()
{
// e.g. check for API support
if (Utilities.Versions.XPLMVersion < 303)
{
throw new NotSupportedException("TCAS override not supported.");
}
}

[UnmanagedCallersOnly(EntryPoint = "XPluginDisable")]
public static void Disable();
public override void Dispose()
{
// TODO: uninitialize
}

[UnmanagedCallersOnly(EntryPoint = "XPluginReceiveMessage")]
public static void ReceiveMessage(int from, int message, nint param);
public override bool Enable()
{
// TODO: start loops
return true;
}

public override void Disable()
{
// TODO: stop loops
}

public override void ReceiveMessage(int from, int message, nint param)
{
// TODO: handle message from aother plugin
base.ReceiveMessage(from, message, param);
}
}
}
```
`Name` and `Signature` fall back to assembly name and root namespace respectively when not overridden to return non-`null` values.
## Publish
```bat
dotnet publish -r win-x64 -c Release
Expand Down
53 changes: 20 additions & 33 deletions XPL/Program.cs
Original file line number Diff line number Diff line change
@@ -1,56 +1,43 @@
using FlyByWireless.XPLM;
using System.Runtime.InteropServices;
using System.Text;
using System;

namespace FlyByWireless.XPL
namespace XplTemplate
{
static class Program
sealed class XPlugin : XPluginBase
{
const string
Name = "Fly by Wireless",
Signature = "hk.timtim.flybywireless",
Description = "X-Plane plugin library template.";
public override string? Name => "Fly by Wireless";
public override string? Signature => "hk.timtim.flybywireless";
public override string? Description => "X-Plane plugin library template.";

[UnmanagedCallersOnly(EntryPoint = "XPluginStart")]
public static int Start(in byte name, in byte signature, in byte description)
public XPlugin() : base()
{
static unsafe void S(in byte s, string value)
// e.g. check for API support
if (Utilities.Versions.XPLMVersion < 303)
{
fixed (byte* p = &s)
{
p[Encoding.UTF8.GetBytes(value, new(p, 255))] = 0;
}
throw new NotSupportedException("TCAS override not supported.");
}
S(name, Name);
S(signature, Signature);
S(description, Description);
Utilities.DebugString("Started.");
return 1;
}

[UnmanagedCallersOnly(EntryPoint = "XPluginStop")]
public static void Stop()
public override void Dispose()
{
Utilities.DebugString("Stopped.");
// TODO: uninitialize
}

[UnmanagedCallersOnly(EntryPoint = "XPluginEnable")]
public static int Enable()
public override bool Enable()
{
Utilities.DebugString("Enabled.");
return 1;
// TODO: start loops
return true;
}

[UnmanagedCallersOnly(EntryPoint = "XPluginDisable")]
public static void Disable()
public override void Disable()
{
Utilities.DebugString("Disabled.");
// TODO: stop loops
}

[UnmanagedCallersOnly(EntryPoint = "XPluginReceiveMessage")]
public static void ReceiveMessage(int from, int message, nint param)
public override void ReceiveMessage(int from, int message, nint param)
{
// TODO: process message from another plugin
// TODO: handle message from aother plugin
base.ReceiveMessage(from, message, param);
}
}
}
11 changes: 6 additions & 5 deletions XPL/XPL.csproj
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<RootNamespace>XplTemplate</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>FlyByWireless.XPL</AssemblyName>
<RootNamespace>FlyByWireless.XPL</RootNamespace>
<Nullable>enable</Nullable>
<AssemblyName>XplTemplate</AssemblyName>
<Authors>WONG Tin Chi Timothy</Authors>
<Company>Timmy Net Net Company</Company>
<Product>X-Plane plugin library template</Product>
<Copyright>(C) 2021 WONG Tin Chi Timothy. All rights reserved.</Copyright>
<RepositoryUrl>https://github.com/wegylexy/XPLM</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>1.0.1-alpha</Version>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FlyByWireless.XPLM" Version="1.0.0-*"/>
<PackageReference Include="Microsoft.DotNet.ILCompiler" Version="6.0.0-*"/>
<PackageReference Include="FlyByWireless.XPLM" Version="1.0.*-*" />
<PackageReference Include="Microsoft.DotNet.ILCompiler" Version="6.0.*-*" />
</ItemGroup>

</Project>
62 changes: 29 additions & 33 deletions XPLM/Program.cs.pp
Original file line number Diff line number Diff line change
@@ -1,56 +1,52 @@
using FlyByWireless.XPLM;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
using System.Text;

namespace $RootNamespace$
namespace FlyByWireless.XPLM
{
static class Program
{
const string
Name = "$AssemblyName$",
Signature = "$RootNamespace$",
Description = "X-Plane plugin using FlyByWireless.XPLM";
private static XPluginBase? _plugin;

[UnmanagedCallersOnly(EntryPoint = "XPluginStart")]
public static int Start(in byte name, in byte signature, in byte description)
private static int Start(ref byte name, ref byte signature, ref byte description)
{
static unsafe void S(in byte s, string value)
static void StrCpy(ref byte u, string value)
{
fixed (byte* p = &s)
{
p[Encoding.UTF8.GetBytes(value, new(p, 255))] = 0;
}
var s = MemoryMarshal.CreateSpan(ref u, 256);
s[Encoding.UTF8.GetBytes(value, s[..^1])] = 0;
}
try
{
_plugin = new $RootNamespace$.XPlugin();
StrCpy(ref name, _plugin.Name ?? "$AssemblyName$");
StrCpy(ref signature, _plugin.Signature ?? "$RootNamespace$");
StrCpy(ref description, _plugin.Description ?? "Built with FlyByWireless.XPLM");
return 1;
}
catch (Exception ex)
{
Utilities.DebugString(ex.ToString());
return 0;
}
S(name, Name);
S(signature, Signature);
S(description, Description);
Utilities.DebugString("Started.");
return 1;
}

[UnmanagedCallersOnly(EntryPoint = "XPluginStop")]
public static void Stop()
private static void Stop()
{
Utilities.DebugString("Stopped.");
using (_plugin)
{
_plugin = null;
}
}

[UnmanagedCallersOnly(EntryPoint = "XPluginEnable")]
public static int Enable()
{
Utilities.DebugString("Enabled.");
return 1;
}
private static int Enable() => _plugin!.Enable() ? 1 : 0;

[UnmanagedCallersOnly(EntryPoint = "XPluginDisable")]
public static void Disable()
{
Utilities.DebugString("Disabled.");
}
private static void Disable() => _plugin!.Disable();

[UnmanagedCallersOnly(EntryPoint = "XPluginReceiveMessage")]
public static void ReceiveMessage(int from, int message, nint param)
{
// TODO: process message from another plugin
}
private static void ReceiveMessage(int from, int message, nint param) =>
_plugin!.ReceiveMessage(from, message, param);
}
}
4 changes: 2 additions & 2 deletions XPLM/XPLM.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<AssemblyName>FlyByWireless.XPLM</AssemblyName>
<Version>1.0.0-alpha</Version>
<Version>1.0.1-alpha</Version>
<Authors>WONG Tin Chi Timothy</Authors>
<Company>Timmy Net Net Company</Company>
<Product>P/Invoke for X-Plane plugin library manager</Product>
Expand Down Expand Up @@ -36,7 +36,7 @@
<ItemGroup>
<Content Include="FlyByWireless.XPLM.props" PackagePath="build" />
<Content Include="FlyByWireless.XPLM.targets" PackagePath="build" />
<Content Include="Program.cs.pp" PackagePath="content" />
<Content Include="Program.cs.pp" PackagePath="contentFiles/cs/net6.0" BuildAction="Compile" />
<Content Include="SDK/**" PackagePath="build/SDK" />
</ItemGroup>

Expand Down
62 changes: 62 additions & 0 deletions XPLM/XPluginBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System;

namespace FlyByWireless.XPLM
{
/// <summary>
/// <see href="https://developer.x-plane.com/article/developing-plugins/#Basic_Plugin_Reference"/>
/// </summary>
public abstract class XPluginBase : IDisposable
{
/// <summary>
/// <para>Human-readable name of the plugin.</para>
/// See also <seealso href="https://developer.x-plane.com/article/developing-plugins/#XPluginStart">XPluginStart</seealso>
/// </summary>
public virtual string? Name => null;

/// <summary>
/// <para>Globally unique signature of the plugin.</para>
/// See also <seealso href="https://developer.x-plane.com/article/developing-plugins/#XPluginStart">XPluginStart</seealso>
/// </summary>
public virtual string? Signature => null;

/// <summary>
/// <para>Human-readable description of the plugin.</para>
/// See also <seealso href="https://developer.x-plane.com/article/developing-plugins/#XPluginStart">XPluginStart</seealso>
/// </summary>
public virtual string? Description => "Built with FlyByWireless.XPLM";

/// <summary>
/// <para>Do any initialization necessary for the plugin. This includes creating user interfaces, installing registered callbacks, allocating resources, etc.</para>
/// See also <seealso href="https://developer.x-plane.com/article/developing-plugins/#XPluginStart">XPluginStart</seealso>
/// </summary>
public XPluginBase() { }

/// <summary>
/// <para>Unregister any callbacks that can be unregistered, dispose of any objects or resources, and clean up all allocations done by the plugin.</para>
/// See also <seealso href="https://developer.x-plane.com/article/developing-plugins/#XPluginStop">XPluginStop</seealso>
/// </summary>
public abstract void Dispose();

/// <summary>
/// <para>This callback should be used to allocate any resources that the plugin maintains while enabled.</para>
/// See also <seealso href="https://developer.x-plane.com/article/developing-plugins/#XPluginEnable">XPluginEnable</seealso>
/// </summary>
/// <returns>Whether the plugin is enabled successfully.</returns>
public abstract bool Enable();

/// <summary>
/// <para>Deallocate any significant resources and prepare to not receive any callbacks for a potentially long duration.</para>
/// See also <seealso href="https://developer.x-plane.com/article/developing-plugins/#XPluginDisable">XPluginDisable</seealso>
/// </summary>
public abstract void Disable();

/// <summary>
/// <para>This function is called by the plugin manager when a message is sent to the plugin.</para>
/// See also <seealso href="https://developer.x-plane.com/article/developing-plugins/#XPluginReceiveMessage">XPluginReceiveMessage</seealso>
/// </summary>
/// <param name="from">The ID of the plugin that sent the message.</param>
/// <param name="message">An integer indicating the message sent.</param>
/// <param name="param">A pointer to data that is specific to the message.</param>
public virtual void ReceiveMessage(int from, int message, nint param) { }
}
}

0 comments on commit 64c5a10

Please sign in to comment.