[C#] Hybride Service / Stand-alone applicatie maken

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • RiDo78
  • Registratie: Juli 2002
  • Niet online
Ik ben in C# een 3-tier programmatje aan het schrijven. De database wordt SQL-Server, de server en client bouw ik zelf.

Nu is het natuurlijk de bedoeling om de server als service te laten draaien. Maar aangezien er veel mis kan gaan wil ik ook de mogelijkheid bieden om de software handmatig te starten en uitvoer in een console te tonen.

Nu heb ik het al voor elkaar gekregen om het programma netjes als service te starten en te stoppen en ook als stand-alone applicatie functioneert het prima. Maar je wilt natuurlijk niet 2 versies leveren en onderhouden. En juist dat combineren lukt niet. Zodra er ook maar een hint naar SCM in staat, is het programma niet meer stand-alone te gebruiken.

Heeft iemand een idee hoe ik tijdens runtime een detectie kan doen of de software door SCM gestart is?

Alvast bedankt!

Acties:
  • 0 Henk 'm!

  • kluyze
  • Registratie: Augustus 2004
  • Niet online
Het lijkt me eerlijk gezegd erg onwenselijk om te debuggen vanop een andere environment als je productie omgeving. Kan je niet beter de mogelijkheid voorzien om je debug logging aan of uit te zetten?

Ik denk dan bv aan rechten die de applicatie als service mist of juist wel heeft en dat soort zaken.

Acties:
  • 0 Henk 'm!

  • jmzeeman
  • Registratie: April 2007
  • Laatst online: 03-07 08:08
Volgens mij is er niet echt een mooie standaard methode die je hier op toe kan passen. Maar op basis van de eigenschappen van een service is het wel te doen. De methode die ik hier onder versimpeld uit een projectje van mij heb getrokken maakt gebruik van het feit dat (de meeste) services niet onder het reguliere window station gestart worden waardoor ze geen interactie kunnen hebben met de desktop. In plaats van station=="WinSta0" zou je ook staton.StartsWith("Service") kunnen gebruiken.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    static class Program
    {
        [DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern IntPtr GetProcessWindowStation();
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool GetUserObjectInformation(IntPtr hObj, int nIndex, [Out] byte[] pvInfo, uint nLength, out uint lpnLengthNeeded);
        private const int UOI_NAME = 2;

        static void Main()
        {
            byte[] buffer= new byte[1000];
            uint bytesUsed=0;            
            GetUserObjectInformation(GetProcessWindowStation(), UOI_NAME, buffer, 1000, out bytesUsed);
            string station = Encoding.ASCII.GetString(bytes,0,(int)bytesused-1);//-1 to exclude /0
            if (station=="WinSta0")
            {
                MyActualServiceCode.Start();
            }
            else
            {
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[] { new MyService() };
                ServiceBase.Run(ServicesToRun);
            }
        }
    }

Deze methode is niet 100% waterdicht maar voor de meeste reguliere gevallen werkt dit prima.

Ik zie trouwens net dat er ook de Environment.UserInteractive variabele is die aangeeft of je in interactive mode draait, ik neem aan dat dat net zo goed werkt als de code die ik heb gepost (ben misschien iets te enthousiast met het toepassen van de kennis uit Windows Internals :) ). Een andere optie is om te kijken naar je parent proces (pinvoke CreateToolhelp32Snapshot, Process32First, Process32Next) als dat de SCM is ben je een service.
Laatste (maar zeker niet de slechtste) optie is: gebruik gewoon een commandline argument. Op die manier heb je er zelf volledige controle over.

[ Voor 10% gewijzigd door jmzeeman op 03-07-2011 02:34 ]


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 01:20
Alle server-logica in een DLL steken; en dan 2 applicaties die die server logica gebruiken:
- één als Windows service
- één als Console App.

Alle logica is slechts één keer geimplementeerd; enkel de specifieke 'hosting-logica' heb je dan nog eens apart (die toch verschillend zal zijn).

Of denk ik nu te simpel ?

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • armageddon_2k1
  • Registratie: September 2001
  • Laatst online: 25-04 18:21
Ik weet niet helemaal of ik het goed begrijp, maar ik denk dat ik voor iets vergelijkbaars het volgende heb gebruikt:

http://topshelf-project.com/

Hiermee kan je heel makkelijk services definieren, maar je kan hem ook gewoon als console-app starten. Gebruik dan iets als Log4Net en je kan dingen loggen naar de console of naar een file. En je hebt ook nog eens 1 applicatie voor zowel service als console-app.

Engineering is like Tetris. Succes disappears and errors accumulate.


Acties:
  • 0 Henk 'm!

  • RiDo78
  • Registratie: Juli 2002
  • Niet online
Iedereen hartelijk dank voor de reacties, met name jmzeeman voor het stukje code, dat is precies wat ik zocht.
kluyze schreef op zondag 03 juli 2011 @ 01:13:
Het lijkt me eerlijk gezegd erg onwenselijk om te debuggen vanop een andere environment als je productie omgeving. Kan je niet beter de mogelijkheid voorzien om je debug logging aan of uit te zetten?
Het is niet zozeer het echte debuggen, meer zaken die tijdens runtime-initialisatie mis kunnen gaan. Ik denk bijvoorbeeld aan een onbereikbare database, volgelopen disken enz. Dingen waar de service afhankelijk van is en die het bij het starten al initieert. Als er na de initialisatie iets mis gaat, dan kunnen de clients daarover geinformeerd worden.

Temeer omdat de service ook veel afhankelijkheden met Windows heeft. Zo wordt er een trusted connection naar de database gemaakt, maar zodra er een client inlogt dan wordt de client-thread geimpersoneerd en wordt er een verbinding in de context van de gebruiker gemaakt. Om dat te kunnen, en zeker te weten dat de bende niet in de soep loopt, is het van belang dat de server een verbinding heeft met een (ro-) domaincontroller. Zonder de DC kan ik niet garanderen dat gebruikers kunnen aanloggen. Om de service te vinden gaan de clients ook afhankelijk worden van de AD en (in geval van stand-alone machines) network broadcasts. Het is dus altijd handig om eventueel op de console ook te kunnen zien of er machines verbinden. Al denk ik dat ik voor die functionaliteit een aparte monitoring/control socket op ga zetten.

Nogmaals dank voor jullie reacties, ik kan weer vrolijk verder.

Acties:
  • 0 Henk 'm!

  • RiDo78
  • Registratie: Juli 2002
  • Niet online
whoami schreef op zondag 03 juli 2011 @ 09:30:
Alle server-logica in een DLL steken; en dan 2 applicaties die die server logica gebruiken:
- één als Windows service
- één als Console App.

Alle logica is slechts één keer geimplementeerd; enkel de specifieke 'hosting-logica' heb je dan nog eens apart (die toch verschillend zal zijn).

Of denk ik nu te simpel ?
Dat is zeker niet te simpel gedacht, maar de ' hosting-logica' zal dan uit een paar regeltjes bestaan. Dat is ook een tikkeltje overkill.

Acties:
  • 0 Henk 'm!

  • RiDo78
  • Registratie: Juli 2002
  • Niet online
armageddon_2k1 schreef op zondag 03 juli 2011 @ 11:27:
Ik weet niet helemaal of ik het goed begrijp, maar ik denk dat ik voor iets vergelijkbaars het volgende heb gebruikt:

http://topshelf-project.com/

Hiermee kan je heel makkelijk services definieren, maar je kan hem ook gewoon als console-app starten. Gebruik dan iets als Log4Net en je kan dingen loggen naar de console of naar een file. En je hebt ook nog eens 1 applicatie voor zowel service als console-app.
Ja daar ben ik ook al tegenaan gelopen. Maar het lijkt mij overkill om een compleet project over te nemen, enkel voor zoiets kleins als dit. Het is echt maar een paar regels code...

Topshelf kan erg handig zijn, maar is een beetje overkill voor waar ik het voor nodig heb.

Acties:
  • 0 Henk 'm!

  • MTWZZ
  • Registratie: Mei 2000
  • Laatst online: 13-08-2021

MTWZZ

One life, live it!

RiDo78 schreef op zondag 03 juli 2011 @ 15:39:
[...]

Dat is zeker niet te simpel gedacht, maar de ' hosting-logica' zal dan uit een paar regeltjes bestaan. Dat is ook een tikkeltje overkill.
Die paar regels kun je best neer gooien zonder dat dat overkill is.
Ander argument is ook nog dat als je applicatie logica apart staat het beter (geautomatiseerd) te testen is.

Nu met Land Rover Series 3 en Defender 90


Acties:
  • 0 Henk 'm!

  • GrimaceODespair
  • Registratie: December 2002
  • Laatst online: 23:15

GrimaceODespair

eens een tettenman, altijd ...

Ik heb zelf nooit anders gedaan dan voor elke service minstens 3 projecten:
[list]
• Project.Core
• Project.Console
• Project.Service

Wij onderbreken deze thread voor reclame:
http://kalders.be


Acties:
  • 0 Henk 'm!

  • sig69
  • Registratie: Mei 2002
  • Laatst online: 23:45
jmzeeman schreef op zondag 03 juli 2011 @ 02:18:
Volgens mij is er niet echt een mooie standaard methode die je hier op toe kan passen. Maar op basis van de eigenschappen van een service is het wel te doen. De methode die ik hier onder versimpeld uit een projectje van mij heb getrokken maakt gebruik van het feit dat (de meeste) services niet onder het reguliere window station gestart worden waardoor ze geen interactie kunnen hebben met de desktop. In plaats van station=="WinSta0" zou je ook staton.StartsWith("Service") kunnen gebruiken.

[code=C#]
Wij doen vaak iets soortgelijks, maar dan gewoon simpelweg door een -console flag mee te geven in development. En log4net idd.

Roomba E5 te koop


Acties:
  • 0 Henk 'm!

  • Matis
  • Registratie: Januari 2007
  • Laatst online: 22:21

Matis

Rubber Rocket

Misschien ben ik wat te laat met reageren, maar ik liep recentelijk tegen exact hetzelfde probleem aan. Ik ontwikkel namelijk eenzelfde 3-tier applicatie.
Client in Java <= xmlrpc => Server in C# <= => MSSQL-server en WebProxy in ASP.net

Ik heb voor het server-gedeelte Vici WinService gebruikt.
Gewoon dll als reference toevoegen,
C#:
1
2
3
4
5
6
7
8
9
10
using Vici.WinService;
class MyServiceTask : CyclicServiceTask
{
    public MyServiceTask() : base(TimeSpan.FromSeconds(30), false) {}
    
    protected override void RunTask()
    {
        // Do your stuff here
    }
}


C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
static class Program
{
    static void Main(params string[] parameters)
    {
        MyService service = new MyService();

        if (parameters.Length > 0)
        {
            string option = parameters[0].ToLower();

            switch (option)
            {
                case "/console": service.RunConsole(); return;
                case "/install": service.Install(); return;
                case "/uninstall": service.UnInstall(); return;
            }
        }

        service.Run();
    }
}

class MyService : Service
{
    public MyServiceService() : base(new ServiceInfo("MyService"))
    {
        ServiceTasks.Add(new MyServiceTask());
    }
}


Middels de debug-parameter /console, draait de applicatie gewoon in de console en kun je hem debuggen. Wanneer je (als admin) de /install parameter gebruikt, zal de applicatie zich als service installeren en vanuit die directory draaien als service.

Werkt voor mij iig prima :)

If money talks then I'm a mime
If time is money then I'm out of time

Pagina: 1