Reputation: 13
I've recently finished creating a multi-threaded server as a console application in C#. Originally it runs on a company computer which is exposed to the internet by port forwarding. However, I believe it may require pushing out to a real server setup as clients increase. I've never had to do this before, so I was wondering:
What must I do to deploy a C# console application like this onto a server? Does it need to be converted to a service? Could I get a VPS and run it on that?
I'd really appreciate any answers or suggestions, thank you
Upvotes: 1
Views: 108
Reputation: 21128
In c# it is very simple to write a windows service. I like to combine console application and services. I combine this, because for debugging reason a console applicaiton is ways better and for production the service is better. As a service base i always use:
Program.cs:
#define __USE_AS_CONSOLE___
using MyService.Service;
using System;
using System.Collections.Generic;
using System.Configuration.Install;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Diagnostics;
namespace MyService
{
public class Program
{
#region Private Member
private static ASServiceBase myServiceBase;
private static string serviceName;
#endregion
#region Console
const bool ShowConsole = true;
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool FreeConsole();
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool AddDllDirectory(string lpPathName);
#endregion
static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += ResolveError;
string installCommand = "";
serviceName = GetServiceName();
foreach(string arg in args)
{
if (arg.ToLower().StartsWith("/install"))
{
installCommand = "/install";
}
else if (arg.ToLower().StartsWith("/uninstall"))
{
installCommand = "/uninstall";
}
}
if (System.Environment.UserInteractive)
{
string parameter = "";
foreach (string arg in args)
{
parameter += arg;
if (!arg.EndsWith(" "))
{
parameter += "";
}
}
switch (installCommand)
{
case "/install":
if (!IsAdministrator())
{
System.Console.WriteLine("Die Anwendung muss als Administrator installiert werden.");
System.Console.ReadLine();
return;
}
ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
return;
break;
case "/uninstall":
if (!IsAdministrator())
{
System.Console.WriteLine("Die Anwendung muss als Administrator installiert werden.");
System.Console.ReadLine();
return;
}
ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
return;
break;
}
AllocConsole();
myServiceBase = new ASServiceBase();
myServiceBase.Start();
System.Console.ReadLine();
}
else
{
// ===============================================
// Start Console
AllocConsole();
System.Console.WriteLine("Version 1.0");
myServiceBase = new ASServiceBase();
//Start service
System.ServiceProcess.ServiceBase.Run(myServiceBase);
}
}
public static bool IsAdministrator()
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
#region [Resolve Error]
/// <summary>
/// Resolve Error
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static Assembly ResolveError(object sender, ResolveEventArgs args)
{
try
{
Assembly cMyAssembly = null;
string strTempAssmbPath = string.Empty;
Assembly objExecutingAssemblies = Assembly.GetExecutingAssembly();
AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();
AssemblyName myAssemblyName = Array.Find<AssemblyName>(arrReferencedAssmbNames, a => a.Name == args.Name);
if (myAssemblyName != null)
{
cMyAssembly = Assembly.LoadFrom(myAssemblyName.CodeBase);
}
else
{
string rootFolder = GetAssemblyPath(args, "");
if (!string.IsNullOrEmpty(rootFolder))
{
if (File.Exists(rootFolder))
{
// Loads the assembly from the specified path.
cMyAssembly = Assembly.LoadFrom(rootFolder);
}
}
string assemblyFolder = GetAssemblyPath(args, "Assemblies\\");
if (!string.IsNullOrEmpty(assemblyFolder))
{
if (File.Exists(assemblyFolder))
{
// Loads the assembly from the specified path.
cMyAssembly = Assembly.LoadFrom(assemblyFolder);
}
}
}
// Returns the loaded assembly.
return cMyAssembly;
}
catch (Exception exc)
{
FileLog.WriteLog("Fehler in Init.ResolveError:\r\n" + exc.ToString());
return null;
}
}
private static string GetAssemblyPath(ResolveEventArgs args, string AdditionalDirectory)
{
string returnValue = null;
string cRMSAssemblyFolder = GlobalSettings.StudioPath + "\\" + AdditionalDirectory;
Assembly cMyAssembly = null;
string strTempAssmbPath = string.Empty;
Assembly objExecutingAssemblies = Assembly.GetExecutingAssembly();
AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();
AssemblyName myAssemblyName = Array.Find<AssemblyName>(arrReferencedAssmbNames, a => a.Name == args.Name);
if (myAssemblyName == null)
{
if (args.Name.Contains(","))
{
strTempAssmbPath = Path.Combine(cRMSAssemblyFolder, args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll");
}
else
{
strTempAssmbPath = Path.Combine(cRMSAssemblyFolder, args.Name + ".dll");
}
returnValue = strTempAssmbPath;
}
return returnValue;
}
#endregion
}
}
Service installer:
using System;
using System.Configuration.Install;
using System.ComponentModel;
using System.ServiceProcess;
using System.IO;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Configuration;
using System.Diagnostics;
/// <summary>
/// Installerklasse für den Service
/// </summary>
[RunInstaller(true)]
public class QServiceInstaller : Installer
{
#region private Member
private ServiceInstaller myThisService;
private IContainer components;
private ServiceProcessInstaller myThisServiceProcess;
#endregion
public QServiceInstaller()
{
myThisService = new ServiceInstaller();
myThisServiceProcess = new ServiceProcessInstaller();
string Path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
myThisServiceProcess.Account = ServiceAccount.LocalSystem;
myThisService.ServiceName = "Your application name";
myThisService.StartType = ServiceStartMode.Automatic;
Installers.Add(myThisService);
Installers.Add(myThisServiceProcess);
}
private void InitializeComponent()
{
}
}
Your service-base:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
namespace MyService.Service
{
public class ASServiceBase : ServiceBase
{
#region Private Member
private Thread myServiceThread;
private bool myDoStop;
#endregion
#region Constructor
/// <summary>
/// Constructor
/// </summary>
public ASServiceBase()
{
myDoStop = false;
}
#endregion
#region Public Methods
#region OnStart
protected override void OnStart(string[] args)
{
Start();
}
/// <summary>
/// Start
/// </summary>
public void Start()
{
myServiceThread = new Thread(new ThreadStart(Do));
myServiceThread.Start();
MainThread = myServiceThread;
}
#endregion
#region Do Anything
/// <summary>
/// Execute
/// </summary>
public void Do()
{
while (!myDoStop)
{
// Do some stuff
Thread.Sleep(10);
}
LoggingManager.Singleton.Deactivate();
// =====================================================================================
// Stop anything
// =====================================================================================
}
#endregion
#region OnStop
protected override void OnStop()
{
Stop();
}
/// <summary>
/// Stop
/// </summary>
public void Stop()
{
myDoStop = true;
}
#endregion
#endregion
#region Private Methods
#endregion
#region Public Member
/// <summary>
/// Main Thread
/// </summary>
public static Thread MainThread
{
get;
set;
}
#endregion
}
}
Hope my code help. Ask if you have any questions
Upvotes: 1
Reputation: 11916
It's better to make a service for that kind of program. The console application output use a lot of resources and must be started manually. Whereas a service can be executed on each computer start without an user even be logged on.
In order to deploy it, the best way is to create a setup process, then install it on the server. Then you can create an uptade process for your server so you don't need to install it for each update you make.
Upvotes: 0