diff --git a/doc/docs/dev-loop.md b/doc/docs/dev-loop.md
index c0d4fc907..db28a6c13 100644
--- a/doc/docs/dev-loop.md
+++ b/doc/docs/dev-loop.md
@@ -58,7 +58,7 @@ This will generate a `wsl.sln` file that you can build either with Visual Studio
Build parameters:
- `cmake . -A arm64`: Build a package for ARM64
-- `cmake . -DCMAKE_BUILD_TYPE=Release`: Build for release
+- `cmake . -DCMAKE_BUILD_TYPE=Release` and then `cmake --build . --config Release`: Build for release
- `cmake . -DBUILD_BUNDLE=TRUE`: Build a bundle msix package (requires building ARM64 first)
Note: To build and deploy faster during development, see options in `UserConfig.cmake`.
diff --git a/nuget/Microsoft.WSL.Containers/build/net/Microsoft.WSL.Containers.Interop.cs b/nuget/Microsoft.WSL.Containers/build/net/Microsoft.WSL.Containers.Interop.cs
new file mode 100644
index 000000000..bde0cfaa9
--- /dev/null
+++ b/nuget/Microsoft.WSL.Containers/build/net/Microsoft.WSL.Containers.Interop.cs
@@ -0,0 +1,822 @@
+// Copyright (c) Microsoft. All rights reserved.
+//
+// C# P/Invoke interop for the WSL Container SDK (wslcsdk.dll).
+//
+// This file is a direct mapping of wslcsdk.h. All structures, enums, delegates,
+// and entry points are exposed for use by managed callers.
+//
+// Notes:
+// - Opaque settings structs (WslcSessionSettings, WslcContainerSettings,
+// WslcProcessSettings) must only be manipulated through the SDK functions.
+// - Handle types (WslcSession, WslcContainer, WslcProcess) are IntPtr values
+// that must be released with the corresponding WslcRelease* function.
+// - Output strings allocated by the SDK (errorMessage, inspectData) are
+// CoTaskMem-allocated; free them with Marshal.FreeCoTaskMem after use.
+// - Delegate instances passed as callbacks must be kept alive (prevent GC)
+// for the entire duration they may be invoked by native code.
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.WSL.Containers.Interop
+{
+ [StructLayout(LayoutKind.Sequential, Size = 80, Pack = 8)]
+ public struct WslcSessionSettings
+ {
+ // Opaque internal data - do not access directly.
+ }
+
+ [StructLayout(LayoutKind.Sequential, Size = 96, Pack = 8)]
+ public struct WslcContainerSettings
+ {
+ // Opaque internal data - do not access directly.
+ }
+
+
+ [StructLayout(LayoutKind.Sequential, Size = 72, Pack = 8)]
+ public struct WslcProcessSettings
+ {
+ // Opaque internal data - do not access directly.
+ }
+
+ public enum WslcContainerNetworkingMode : int
+ {
+ None = 0,
+ Bridged = 1
+ }
+
+ public enum WslcVhdType : int
+ {
+ Dynamic = 0,
+ Fixed = 1
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WslcVhdRequirements
+ {
+ // Ignored by WslcSetSessionSettingsVhd
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string Name;
+
+ public ulong SizeInBytes;
+ public WslcVhdType Type;
+ }
+
+ [Flags]
+ public enum WslcSessionFeatureFlags : int
+ {
+ None = 0x00000000,
+ EnableGpu = 0x00000004
+ }
+
+ public enum WslcSessionTerminationReason : int
+ {
+ Unknown = 0,
+ Shutdown = 1,
+ Crashed = 2
+ }
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ public delegate void WslcSessionTerminationCallback(
+ WslcSessionTerminationReason reason,
+ IntPtr context);
+
+ public enum WslcPortProtocol : int
+ {
+ Tcp = 0,
+ Udp = 1
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WslcContainerPortMapping
+ {
+ public ushort WindowsPort;
+ public ushort ContainerPort;
+ public WslcPortProtocol Protocol;
+ ///
+ /// Optional pointer to a SockAddrStorage containing an IPv4 or IPv6 binding address.
+ /// Set to IntPtr.Zero to use the default (127.0.0.1).
+ ///
+ public IntPtr WindowsAddress;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WslcContainerVolume
+ {
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string WindowsPath;
+
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string ContainerPath;
+
+ [MarshalAs(UnmanagedType.Bool)]
+ public bool ReadOnly;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WslcContainerNamedVolume
+ {
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string Name;
+
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string ContainerPath;
+
+ [MarshalAs(UnmanagedType.Bool)]
+ public bool ReadOnly;
+ }
+
+ [Flags]
+ public enum WslcContainerFlags : int
+ {
+ None = 0x00000000,
+ AutoRemove = 0x00000001,
+ EnableGpu = 0x00000002,
+ Privileged = 0x00000004
+ }
+
+ [Flags]
+ public enum WslcContainerStartFlags : int
+ {
+ None = 0x00000000,
+ Attach = 0x00000001
+ }
+
+ public enum WslcContainerState : int
+ {
+ Invalid = 0,
+ Created = 1,
+ Running = 2,
+ Exited = 3,
+ Deleted = 4
+ }
+
+ public enum WslcSignal : int
+ {
+ None = 0,
+ SigHup = 1,
+ SigInt = 2,
+ SigQuit = 3,
+ SigKill = 9,
+ SigTerm = 15
+ }
+
+ [Flags]
+ public enum WslcDeleteContainerFlags : int
+ {
+ None = 0x00000000,
+ Force = 0x00000001
+ }
+
+ public enum WslcProcessIOHandle : int
+ {
+ StdIn = 0,
+ StdOut = 1,
+ StdErr = 2
+ }
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ public delegate void WslcStdIOCallback(
+ WslcProcessIOHandle ioHandle,
+ IntPtr data,
+ uint dataSize,
+ IntPtr context);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ public delegate void WslcProcessExitCallback(
+ int exitCode,
+ IntPtr context);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WslcProcessCallbacks
+ {
+ public WslcStdIOCallback OnStdOut;
+ public WslcStdIOCallback OnStdErr;
+ public WslcProcessExitCallback OnExit;
+ }
+
+ public enum WslcProcessState : int
+ {
+ Unknown = 0,
+ Running = 1,
+ Exited = 2,
+ Signalled = 3
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WslcImageProgressDetail
+ {
+ public ulong Current;
+ public ulong Total;
+ }
+
+ public enum WslcImageProgressStatus : int
+ {
+ Unknown = 0,
+ Pulling = 1,
+ Waiting = 2,
+ Downloading = 3,
+ Verifying = 4,
+ Extracting = 5,
+ Complete = 6
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WslcImageProgressMessage
+ {
+ public IntPtr Id; // PCSTR - use Marshal.PtrToStringAnsi
+
+ public WslcImageProgressStatus Status;
+
+ public WslcImageProgressDetail Detail;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WslcRegistryAuthenticationInformation
+ {
+ // TBD
+ }
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ public delegate int WslcContainerImageProgressCallback(
+ ref WslcImageProgressMessage progress,
+ IntPtr context);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WslcPullImageOptions
+ {
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string Uri;
+
+ public WslcContainerImageProgressCallback ProgressCallback;
+ public IntPtr ProgressCallbackContext;
+ public IntPtr AuthInfo; // pointer to WslcRegistryAuthenticationInformation, or IntPtr.Zero
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WslcImportImageOptions
+ {
+ public WslcContainerImageProgressCallback ProgressCallback;
+ public IntPtr ProgressCallbackContext;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WslcLoadImageOptions
+ {
+ public WslcContainerImageProgressCallback ProgressCallback;
+ public IntPtr ProgressCallbackContext;
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ public struct WslcImageInfo
+ {
+ public const int ImageNameLength = 256;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = ImageNameLength)]
+ public string Name;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
+ public byte[] Sha256;
+
+ public ulong SizeBytes;
+ public ulong CreatedTimestamp;
+ }
+
+ [Flags]
+ public enum WslcComponentFlags : int
+ {
+ None = 0,
+ VirtualMachinePlatform = 1,
+ WslPackage = 2
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WslcVersion
+ {
+ public uint Major;
+ public uint Minor;
+ public uint Revision;
+ }
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ public delegate void WslcInstallCallback(
+ WslcComponentFlags component,
+ uint progress,
+ uint total,
+ IntPtr context);
+
+ // ========================================================================
+ // Socket address helpers (for WslcContainerPortMapping)
+ // ========================================================================
+
+ ///
+ /// Mirrors the native SOCKADDR_STORAGE structure (128 bytes).
+ /// Use to create instances for IPv4/IPv6.
+ ///
+ [StructLayout(LayoutKind.Sequential, Size = 128)]
+ public struct SockAddrStorage
+ {
+ public short Family; // AF_INET (2) or AF_INET6 (23)
+ }
+
+ [StructLayout(LayoutKind.Sequential, Size = 16)]
+ public struct SockAddrIn
+ {
+ public short Family; // AF_INET = 2
+ public ushort Port; // network byte order
+ public uint Address; // IPv4 address in network byte order
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
+ public byte[] Zero;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Size = 28)]
+ public struct SockAddrIn6
+ {
+ public short Family; // AF_INET6 = 23
+ public ushort Port; // network byte order
+ public uint FlowInfo;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
+ public byte[] Address; // 128-bit IPv6 address
+
+ public uint ScopeId;
+ }
+
+ ///
+ /// Helpers to create native socket address structures for use with port mappings.
+ ///
+ public static class SockAddrHelpers
+ {
+ public const short AF_INET = 2;
+ public const short AF_INET6 = 23;
+
+ ///
+ /// Creates a SockAddrStorage from a .NET IPAddress and pins it, returning
+ /// a GCHandle the caller must free and a pointer suitable for
+ /// WslcContainerPortMapping.WindowsAddress.
+ ///
+ public static GCHandle CreatePinned(System.Net.IPAddress address, out IntPtr pointer)
+ {
+ if (address == null)
+ throw new ArgumentNullException(nameof(address));
+
+ byte[] raw;
+
+ if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
+ {
+ raw = new byte[128];
+ raw[0] = (byte)(AF_INET & 0xFF);
+ raw[1] = (byte)((AF_INET >> 8) & 0xFF);
+ byte[] addrBytes = address.GetAddressBytes(); // 4 bytes
+ Buffer.BlockCopy(addrBytes, 0, raw, 4, 4);
+ }
+ else if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
+ {
+ raw = new byte[128];
+ raw[0] = (byte)(AF_INET6 & 0xFF);
+ raw[1] = (byte)((AF_INET6 >> 8) & 0xFF);
+ byte[] addrBytes = address.GetAddressBytes(); // 16 bytes
+ Buffer.BlockCopy(addrBytes, 0, raw, 8, 16);
+ long scopeId = address.ScopeId;
+ raw[24] = (byte)(scopeId & 0xFF);
+ raw[25] = (byte)((scopeId >> 8) & 0xFF);
+ raw[26] = (byte)((scopeId >> 16) & 0xFF);
+ raw[27] = (byte)((scopeId >> 24) & 0xFF);
+ }
+ else
+ {
+ throw new ArgumentException("Only IPv4 and IPv6 addresses are supported.", nameof(address));
+ }
+
+ var handle = GCHandle.Alloc(raw, GCHandleType.Pinned);
+ pointer = handle.AddrOfPinnedObject();
+ return handle;
+ }
+ }
+
+ // ========================================================================
+ // Native methods (P/Invoke)
+ // ========================================================================
+
+ public static class WslcNativeMethods
+ {
+ private const string DllName = "wslcsdk.dll";
+ public const int ContainerIdBufferSize = 65;
+
+ // ----------------------------------------------------------------
+ // Session
+ // ----------------------------------------------------------------
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
+ public static extern int WslcInitSessionSettings(
+ [MarshalAs(UnmanagedType.LPWStr)] string name,
+ [MarshalAs(UnmanagedType.LPWStr)] string storagePath,
+ out WslcSessionSettings sessionSettings);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcCreateSession(
+ ref WslcSessionSettings sessionSettings,
+ out IntPtr session,
+ out IntPtr errorMessage);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetSessionSettingsCpuCount(
+ ref WslcSessionSettings sessionSettings,
+ uint cpuCount);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetSessionSettingsMemory(
+ ref WslcSessionSettings sessionSettings,
+ uint memoryMb);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetSessionSettingsTimeout(
+ ref WslcSessionSettings sessionSettings,
+ uint timeoutMS);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetSessionSettingsVhd(
+ ref WslcSessionSettings sessionSettings,
+ ref WslcVhdRequirements vhdRequirements);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall, EntryPoint = "WslcSetSessionSettingsVhd")]
+ public static extern int WslcSetSessionSettingsVhd(
+ ref WslcSessionSettings sessionSettings,
+ IntPtr vhdRequirements);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetSessionSettingsFeatureFlags(
+ ref WslcSessionSettings sessionSettings,
+ WslcSessionFeatureFlags flags);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetSessionSettingsTerminationCallback(
+ ref WslcSessionSettings sessionSettings,
+ WslcSessionTerminationCallback terminationCallback,
+ IntPtr terminationContext);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcTerminateSession(IntPtr session);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcReleaseSession(IntPtr session);
+
+ // ----------------------------------------------------------------
+ // Container
+ // ----------------------------------------------------------------
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
+ public static extern int WslcInitContainerSettings(
+ [MarshalAs(UnmanagedType.LPStr)] string imageName,
+ out WslcContainerSettings containerSettings);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcCreateContainer(
+ IntPtr session,
+ ref WslcContainerSettings containerSettings,
+ out IntPtr container,
+ out IntPtr errorMessage);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcStartContainer(
+ IntPtr container,
+ WslcContainerStartFlags flags,
+ out IntPtr errorMessage);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
+ public static extern int WslcSetContainerSettingsName(
+ ref WslcContainerSettings containerSettings,
+ [MarshalAs(UnmanagedType.LPStr)] string name);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetContainerSettingsInitProcess(
+ ref WslcContainerSettings containerSettings,
+ ref WslcProcessSettings initProcess);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetContainerSettingsNetworkingMode(
+ ref WslcContainerSettings containerSettings,
+ WslcContainerNetworkingMode networkingMode);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
+ public static extern int WslcSetContainerSettingsHostName(
+ ref WslcContainerSettings containerSettings,
+ [MarshalAs(UnmanagedType.LPStr)] string hostName);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
+ public static extern int WslcSetContainerSettingsDomainName(
+ ref WslcContainerSettings containerSettings,
+ [MarshalAs(UnmanagedType.LPStr)] string domainName);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetContainerSettingsFlags(
+ ref WslcContainerSettings containerSettings,
+ WslcContainerFlags flags);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetContainerSettingsPortMappings(
+ ref WslcContainerSettings containerSettings,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] WslcContainerPortMapping[] portMappings,
+ uint portMappingCount);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetContainerSettingsVolumes(
+ ref WslcContainerSettings containerSettings,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] WslcContainerVolume[] volumes,
+ uint volumeCount);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetContainerSettingsNamedVolumes(
+ ref WslcContainerSettings containerSettings,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] WslcContainerNamedVolume[] namedVolumes,
+ uint namedVolumeCount);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcCreateContainerProcess(
+ IntPtr container,
+ ref WslcProcessSettings newProcessSettings,
+ out IntPtr newProcess,
+ out IntPtr errorMessage);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcReleaseContainer(IntPtr container);
+
+ // ----------------------------------------------------------------
+ // Container management
+ // ----------------------------------------------------------------
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
+ public static extern int WslcGetContainerID(
+ IntPtr container,
+ [MarshalAs(UnmanagedType.LPArray, SizeConst = ContainerIdBufferSize)] byte[] containerId);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcGetContainerInitProcess(
+ IntPtr container,
+ out IntPtr initProcess);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcInspectContainer(
+ IntPtr container,
+ out IntPtr inspectData);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcGetContainerState(
+ IntPtr container,
+ out WslcContainerState state);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcStopContainer(
+ IntPtr container,
+ WslcSignal signal,
+ uint timeoutSeconds,
+ out IntPtr errorMessage);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcDeleteContainer(
+ IntPtr container,
+ WslcDeleteContainerFlags flags,
+ out IntPtr errorMessage);
+
+ // ----------------------------------------------------------------
+ // Process
+ // ----------------------------------------------------------------
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcInitProcessSettings(
+ out WslcProcessSettings processSettings);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
+ public static extern int WslcSetProcessSettingsCurrentDirectory(
+ ref WslcProcessSettings processSettings,
+ [MarshalAs(UnmanagedType.LPStr)] string currentDirectory);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetProcessSettingsCmdLine(
+ ref WslcProcessSettings processSettings,
+ [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string[] argv,
+ UIntPtr argc);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetProcessSettingsEnvVariables(
+ ref WslcProcessSettings processSettings,
+ [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string[] keyValue,
+ UIntPtr argc);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSetProcessSettingsCallbacks(
+ ref WslcProcessSettings processSettings,
+ ref WslcProcessCallbacks callbacks,
+ IntPtr context);
+
+ // ----------------------------------------------------------------
+ // Process management
+ // ----------------------------------------------------------------
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcGetProcessPid(
+ IntPtr process,
+ out uint pid);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcGetProcessExitEvent(
+ IntPtr process,
+ out IntPtr exitEvent);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcGetProcessState(
+ IntPtr process,
+ out WslcProcessState state);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcGetProcessExitCode(
+ IntPtr process,
+ out int exitCode);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcSignalProcess(
+ IntPtr process,
+ WslcSignal signal);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcGetProcessIOHandle(
+ IntPtr process,
+ WslcProcessIOHandle ioHandle,
+ out IntPtr handle);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcReleaseProcess(IntPtr process);
+
+ // ----------------------------------------------------------------
+ // Image management
+ // ----------------------------------------------------------------
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcPullSessionImage(
+ IntPtr session,
+ ref WslcPullImageOptions options,
+ out IntPtr errorMessage);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
+ public static extern int WslcImportSessionImage(
+ IntPtr session,
+ [MarshalAs(UnmanagedType.LPStr)] string imageName,
+ IntPtr imageContent,
+ ulong imageContentLength,
+ IntPtr options, // pointer to WslcImportImageOptions, or IntPtr.Zero
+ out IntPtr errorMessage);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcImportSessionImageFromFile(
+ IntPtr session,
+ [MarshalAs(UnmanagedType.LPStr)] string imageName,
+ [MarshalAs(UnmanagedType.LPWStr)] string path,
+ IntPtr options, // pointer to WslcImportImageOptions, or IntPtr.Zero
+ out IntPtr errorMessage);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcLoadSessionImage(
+ IntPtr session,
+ IntPtr imageContent,
+ ulong imageContentLength,
+ IntPtr options, // pointer to WslcLoadImageOptions, or IntPtr.Zero
+ out IntPtr errorMessage);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcLoadSessionImageFromFile(
+ IntPtr session,
+ [MarshalAs(UnmanagedType.LPWStr)] string path,
+ IntPtr options, // pointer to WslcLoadImageOptions, or IntPtr.Zero
+ out IntPtr errorMessage);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
+ public static extern int WslcDeleteSessionImage(
+ IntPtr session,
+ [MarshalAs(UnmanagedType.LPStr)] string nameOrId,
+ out IntPtr errorMessage);
+
+ ///
+ /// Lists container images. The returned array is CoTaskMem-allocated.
+ /// Use to convert to a managed array,
+ /// then free the native pointer with Marshal.FreeCoTaskMem.
+ ///
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcListSessionImages(
+ IntPtr session,
+ out IntPtr images,
+ out uint count);
+
+ // ----------------------------------------------------------------
+ // Storage
+ // ----------------------------------------------------------------
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcCreateSessionVhdVolume(
+ IntPtr session,
+ ref WslcVhdRequirements options,
+ out IntPtr errorMessage);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
+ public static extern int WslcDeleteSessionVhdVolume(
+ IntPtr session,
+ [MarshalAs(UnmanagedType.LPStr)] string name,
+ out IntPtr errorMessage);
+
+ // ----------------------------------------------------------------
+ // Install
+ // ----------------------------------------------------------------
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcCanRun(
+ [MarshalAs(UnmanagedType.Bool)] out bool canRun,
+ out WslcComponentFlags missingComponents);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcGetVersion(
+ out WslcVersion version);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.StdCall)]
+ public static extern int WslcInstallWithDependencies(
+ WslcInstallCallback progressCallback,
+ IntPtr context);
+
+ // ----------------------------------------------------------------
+ // Managed helpers
+ // ----------------------------------------------------------------
+
+ ///
+ /// Reads the CoTaskMem-allocated error message string and frees the native memory.
+ /// Returns null if the pointer is IntPtr.Zero.
+ ///
+ public static string ConsumeErrorMessage(IntPtr errorMessage)
+ {
+ if (errorMessage == IntPtr.Zero)
+ return null;
+
+ string result = Marshal.PtrToStringUni(errorMessage);
+ Marshal.FreeCoTaskMem(errorMessage);
+ return result;
+ }
+
+ ///
+ /// Reads the CoTaskMem-allocated ANSI string and frees the native memory.
+ /// Returns null if the pointer is IntPtr.Zero.
+ ///
+ public static string ConsumeAnsiString(IntPtr ansiString)
+ {
+ if (ansiString == IntPtr.Zero)
+ return null;
+
+ string result = Marshal.PtrToStringAnsi(ansiString);
+ Marshal.FreeCoTaskMem(ansiString);
+ return result;
+ }
+
+ ///
+ /// Reads a container ID from the byte buffer returned by WslcGetContainerID.
+ ///
+ public static string ContainerIdToString(byte[] containerId)
+ {
+ if (containerId == null)
+ throw new ArgumentNullException(nameof(containerId));
+
+ int length = Array.IndexOf(containerId, (byte)0);
+ if (length < 0)
+ length = containerId.Length;
+
+ return System.Text.Encoding.ASCII.GetString(containerId, 0, length);
+ }
+
+ ///
+ /// Marshals a native CoTaskMem-allocated WslcImageInfo array to a managed array
+ /// and frees the native memory.
+ ///
+ public static WslcImageInfo[] MarshalImageList(IntPtr images, uint count)
+ {
+ if (images == IntPtr.Zero || count == 0)
+ return Array.Empty();
+
+ var result = new WslcImageInfo[count];
+ int structSize = Marshal.SizeOf();
+
+ for (uint i = 0; i < count; i++)
+ {
+ IntPtr current = IntPtr.Add(images, (int)(i * structSize));
+ result[i] = Marshal.PtrToStructure(current);
+ }
+
+ Marshal.FreeCoTaskMem(images);
+ return result;
+ }
+
+ ///
+ /// Throws a COMException if the HRESULT indicates failure.
+ ///
+ public static void ThrowIfFailed(int hr)
+ {
+ if (hr < 0)
+ Marshal.ThrowExceptionForHR(hr);
+ }
+ }
+}
\ No newline at end of file
diff --git a/nuget/Microsoft.WSL.Containers/build/net/Microsoft.WSL.Containers.targets b/nuget/Microsoft.WSL.Containers/build/net/Microsoft.WSL.Containers.targets
index 245e57a2c..fa6491401 100644
--- a/nuget/Microsoft.WSL.Containers/build/net/Microsoft.WSL.Containers.targets
+++ b/nuget/Microsoft.WSL.Containers/build/net/Microsoft.WSL.Containers.targets
@@ -11,4 +11,7 @@
+
+
+
\ No newline at end of file