Skip to content

Instantly share code, notes, and snippets.

@rkttu
Last active March 25, 2024 08:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rkttu/865889fa4b1065ff4ca53af10dbd310d to your computer and use it in GitHub Desktop.
Save rkttu/865889fa4b1065ff4ca53af10dbd310d to your computer and use it in GitHub Desktop.
Windows NT Service Containerisation Examples

Windows NT Service Containerisation Examples

This sample GIST contains sample code that describes how to modify an NT service that implements a simple HTTP server for Windows to migrate the application to a container.

@echo off
pushd "%~dp0"
%windir%\Microsoft.NET\Framework64\v4.0.30319\csc.exe /nologo /target:exe /r:System.ServiceProcess.dll /out:ServiceMain.exe ServiceMain.cs
%windir%\Microsoft.NET\Framework64\v4.0.30319\csc.exe /nologo /target:exe /out:SimpleMain.exe SimpleMain.cs
:exit
popd
@echo on
@echo off
pushd "%~dp0"
if exist *.exe del /f /q *.exe
:exit
popd
@echo on
@echo off
pushd "%~dp0"
docker.exe build -t simpleweb:latest -f Dockerfile_Simple .
:exit
popd
@echo on
FROM mcr.microsoft.com/windows/servercore:ltsc2022 AS BUILD
WORKDIR /build
ADD . .
RUN C:/Windows/Microsoft.NET/Framework64/v4.0.30319/csc.exe /nologo /target:exe /out:SimpleMain.exe SimpleMain.cs
FROM mcr.microsoft.com/windows/servercore:ltsc2022
USER ContainerAdministrator
WORKDIR /server
COPY --from=BUILD /build/SimpleMain.exe .
EXPOSE 8000
USER ContainerUser
CMD ["SimpleMain.exe"]
@echo off
pushd "%~dp0"
docker.exe rm -f simpleweb > NUL
docker.exe run -dt -p 80:8000 --name simpleweb simpleweb:latest
:exit
popd
@echo on
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.ServiceProcess;
class HttpServerService : ServiceBase
{
private Thread serverThread;
public HttpServerService()
{
this.ServiceName = "HttpServerService";
}
protected override void OnStart(string[] args)
{
serverThread = new Thread(ServerThread);
serverThread.Start();
}
protected override void OnStop()
{
serverThread.Abort();
}
static void ServerThread()
{
TcpListener server = new TcpListener(IPAddress.Any, 8000);
server.Start();
while (true)
{
TcpClient client = server.AcceptTcpClient();
Thread thread = new Thread(() => HandleClientThread(client));
thread.Start();
}
}
static void HandleClientThread(TcpClient client)
{
NetworkStream stream = client.GetStream();
byte[] request = new byte[1024];
stream.Read(request, 0, request.Length);
Console.WriteLine(Encoding.UTF8.GetString(request));
string response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<html><body><h1>Hello, World!</h1></body></html>";
byte[] responseBytes = Encoding.UTF8.GetBytes(response);
stream.Write(responseBytes, 0, responseBytes.Length);
client.Close();
}
}
class Program
{
static void Main()
{
ServiceBase.Run(new HttpServerService());
}
}
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
class Program
{
private static TcpListener server;
private static bool isRunning = true;
static void Main()
{
SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);
Thread thread = new Thread(ServerThread);
thread.Start();
thread.Join();
}
private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{
if (ctrlType == CtrlTypes.CTRL_CLOSE_EVENT || ctrlType == CtrlTypes.CTRL_BREAK_EVENT || ctrlType == CtrlTypes.CTRL_C_EVENT ||
/* CTRL_SHUTDOWN_EVENT signal is sent when you run the docker stop command. */
ctrlType == CtrlTypes.CTRL_SHUTDOWN_EVENT)
{
isRunning = false;
server.Stop();
Console.WriteLine("Server is shutting down...");
}
return true;
}
static void ServerThread()
{
server = new TcpListener(IPAddress.Any, 8000);
server.Start();
while (isRunning)
{
if (!server.Pending())
{
Thread.Sleep(500); // choose a delay that makes sense for you
continue;
}
TcpClient client = server.AcceptTcpClient();
Thread thread = new Thread(() => HandleClientThread(client));
thread.Start();
}
}
static void HandleClientThread(TcpClient client)
{
NetworkStream stream = client.GetStream();
byte[] request = new byte[1024];
stream.Read(request, 0, request.Length);
Console.WriteLine(Encoding.UTF8.GetString(request));
string response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<html><body><h1>Hello, World!</h1></body></html>";
byte[] responseBytes = Encoding.UTF8.GetBytes(response);
stream.Write(responseBytes, 0, responseBytes.Length);
client.Close();
}
#region unmanaged
// Declare the SetConsoleCtrlHandler function
// as external and receiving a delegate.
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
// A delegate type to be used as the handler routine
// for SetConsoleCtrlHandler.
public delegate bool HandlerRoutine(CtrlTypes CtrlType);
// An enumerated type for the control messages
// sent to the handler routine.
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment