C++ Anwendung ohne Fenster und Console

Magic1416

Lieutenant
Registriert
Dez. 2003
Beiträge
530
Hi,

ich bräuchte ein wenig Starthilfe bei der Erstellung einer Anwendung ohne Console oder Fenster, also komplett Silent. Diese Minianwendung wird so eine Art Heartbeat die pro angemeldeten User laufen soll. Ich habe diese Anwendung bereits in C# geschrieben wo diese allerdings zuviel Speicher braucht. Daher die Entscheidung, diese nochmal in unmanaged Code, also C++ zu entwickeln. Diesbezüglich habe ich kaum Erfahrung in C++. Ich kann mir nur meine Erfahrung aus dem C# Umfeld zu nutze machen.

Ich nutze zur Entwicklung Visual Studio 2008 Prof.

1. Mit welchem Projekttyp fange ich am besten an, um weder eine Console, noch ein WinForm zu bekommen. Ich will einfach, dass das Programm in die Main Function springt, ohne was anzuzeigen. Zielplattform ist Windows 2008 Server

2. Wie kann ich in C++ sicherstellen, dass der Prozess der Anwendung, nur ein einziges mal pro User gestartet werden kann.

Danke Gruß Magic
 
@kuddlmuddl: Der Code von deinem Link versteckt nur das Konsolenfenster kurz nachdem das Programm gestartet wurde, es wäre daher sinnvoller, wenn man erst gar kein Konsolenfenster erzeugt.

@TE: Du solltest in den Projekteinstellungen "Win32 Projekt" auswählen und dann einen Haken bei "Leeres Projekt" setzen.
Danach musst du statt der normalen main()-Funktion die WinMain()-Funktion nutzen und kannst dann dort dein Programm hineinschreiben.
Somit hast du weder ein Konsolenfenster, noch ein Windowsfenster und dein Programm läuft nur im Hintergrund.

Zu deiner zweiten Frage: Dazu musst du am Anfang deines Programmes schauen, ob ein anderer Prozess des selben Programmes schon läuft. Wie du die Namen aller laufenden Prozesse herausfinden kannst, findest du leicht über Google, da gibt es genügend Code-Schnipsel, die dir weiter helfen sollten.

Gruß
BlackMark
 
@toeffi
Es kann kein Windows Dienst werden, weil das Programm pro User in einer Multiuser Umgebung (Terminal Server) laufen muss. Daher darf es kein Dienst werden. Ebenso muss verhindert werden, dass pro User mehrere Instanzen gestartet werden können.

Hier mal mein Code:

Code:
#include "stdafx.h"


void do_work();

 


int WINAPI WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow )
{
    
	char* str = getenv("USERNAME");
	
	
	HANDLE hMutex = CreateMutex(NULL, TRUE,(LPCWSTR)&str);   
 
	if (!hMutex)
	{
			  return 1;
	}

	
	
	do_work();

	ReleaseMutex(hMutex);
        CloseHandle(hMutex);

    return 0;
}


void do_work()
{
	int* marker;
	marker = new int();
	

	while (*marker == 0)
	{
		Sleep(1000);
		//Hier wird irgendwann noch mehr passieren.
	}


	delete marker;


}

Um zu verhindern, dass das Programm zweimal gestartet wird, habe ich mir gedacht, ich erstelle einfach einen Mutex, der den Namen des Benutzers trägt. Somit soll sichergestellt werden, dass eine zweite Instanz, die gestartet werden könnte, sich sofort wieder beendet. Leider funktioniert das nicht so wie in C#. Sieht jemand den Fehler
 
Also irgend wie bezweifle ich jetzt aber, daß die Anwendung deshalb zu viel Speicher frißt, weil du sie in C# geschrieben hast. Um wieviel Speicher geht es denn dabei?
Ich kann mir schon vorstellen, daß wenn du 2 funktional äquivalente Programme in C++ und C# hast, die C++-Version unter Umständen etwas performanter sein könnte .... aber ich glaube kaum, daß die C++-Version plötzlich wie eine Eins laufen wird, wenn die äquivalente C#-Version für dein System zu langsam ist.

Hast du vorher auch sichergestellt, daß das Problem garantiert bei C# und nicht etwa in deinem Code liegt?
Ergänzung ()

Magic1416 schrieb:
Um zu verhindern, dass das Programm zweimal gestartet wird, habe ich mir gedacht, ich erstelle einfach einen Mutex, der den Namen des Benutzers trägt. Somit soll sichergestellt werden, dass eine zweite Instanz, die gestartet werden könnte, sich sofort wieder beendet. Leider funktioniert das nicht so wie in C#. Sieht jemand den Fehler


Lies dir mal die msdn-Doku zu CreateMutex() durch ... speziell die Sektion, die den Rückgabewert der Funktion erklärt:

http://msdn.microsoft.com/en-us/library/ms682411(VS.85).aspx

If the mutex is a named mutex and the object existed before this function call, the return value is a handle to the existing object, GetLastError returns ERROR_ALREADY_EXISTS, bInitialOwner is ignored, and the calling thread is not granted ownership. However, if the caller has limited access rights, the function will fail with ERROR_ACCESS_DENIED and the caller should use the OpenMutex function.
 
Zuletzt bearbeitet:
@TE

Könntest Du das Problem Deiner C# Implementierung etwas näher beschreiben? Ich denke nicht, dass das selbe Programm in C++ soviel weniger Speicher verbraucht, wenn es die selbe Funktionalität abbildet. Beseitige die Ursache gleich in C# anstatt diese in C++ erneut zu implementieren.
 
Bin ich Jesus?
Ich kann nur aus Erfahrung sagen das Mutex-Objekte extrem Schwere Kernel-Objekte sind, die ne Menge Overhead mitbringen. Zumindest weiß ich das aus C#, kann natürlich sein das der Overhead in C++ nicht so übertrieben ist, dann ist meine Aussage natürlich nicht zugebrauchen.
 
Na was denkt Ihr denn bitte? Dass die Vorteile, die C# gegenüber C und C++ mitbringt genau 0 Zeit kosten und exakt gleich schnell laufen?

Code:
	char* str = getenv("USERNAME");
	
	
	HANDLE hMutex = CreateMutex(NULL, TRUE,(LPCWSTR)&str);
Wieso castest Du einen char* nach LPCWSTR (Wide-String Pointer) ??? Damit Du Ruhe vor dem Compiler hast? Na vermutlich hat der Dich nicht ohne Grund gewarnt!
 
toeffi schrieb:
Bin ich Jesus?
Ich kann nur aus Erfahrung sagen das Mutex-Objekte extrem Schwere Kernel-Objekte sind, die ne Menge Overhead mitbringen. Zumindest weiß ich das aus C#, kann natürlich sein das der Overhead in C++ nicht so übertrieben ist, dann ist meine Aussage natürlich nicht zugebrauchen.

Ich wage zu behaupten, daß der Overhead, den ein Mutex dieser Art mit sich bringt, im Gesamtkontext des beschriebenen Problems betrachtet vollkommen uninteressant ist.
Ergänzung ()

XunnD schrieb:
Na was denkt Ihr denn bitte? Dass die Vorteile, die C# gegenüber C und C++ mitbringt genau 0 Zeit kosten und exakt gleich schnell laufen?

Nö, natürlich nicht. Aber ich denke durchaus, daß C++ für nicht zeitkritische PC-Anwendungen gegenüber C++ kaum nennenswerte Vorteile bringt. Ich glaube nicht, daß der Threadersteller das Steuerungsprogramm für eine bemannte Raumkapsel oder ähnliches schreiben möchte. Für eine normale Benutzeranwendung sollte C# vollkommen ausreichend sein.
 
@xunnd
Ich caste das nach LPCWSTR weil die Funktion diesen Typ sehen will. Ich bekomme ein Compiler Errror. Alle Beispiele die ich im Netz gefunden habe, sind ohne den Cast. Wegen meinen eingeschränkten C++ Kenntissen, kann ich nicht mal sagen wieso das bei mir so ist.

@all
Das C# Programm macht an der Stelle, wo die Schleife ist, eine Abfrage, wieviele Prozesse im Kontext des Users laufen. Ignoriert bei der Zählung werden die Prozesse, die in einem Array hinterlegt sind. Dieses Array wird beim Start des Programmes befüllt und aus der Registry ausgelesen.
Ich hole mir also mittels Process.GetProcesses() alle Prozesse in ein Array. Dann nutze ich Linq to Object um diese Prozesse nach dem angemeldeten User und den Prozessen zu filtern, die nicht im ignore Array sind.

Code:
            int prozesscount = 0;
            int sessionid = getSessionID();
            string[] ignoreProcesses = readIgnoreProcesses();
            
            do
            {
                System.Threading.Thread.Sleep(60000);

                
                Process[] p = Process.GetProcesses();

                var v = from prozess in p
                        where prozess.SessionId == sessionid && !ignoreProcesses.Contains( prozess.ProcessName )
                        select p;

                prozesscount = v.Count();
 
            
            }while (prozesscount > 0);


Solange Prozesse des Users in einer Session aktiv ist, solange ist auch das Programm aktiv. Hintergrund ist folgender:
Meldet sich ein User an einem Citrix Terminal Server über ein WebPortal an, so startet unter anderem dieses Tool hier. Würde der User sein gestartetes Programm beenden so würde Citrix sofort die Abmeldung vom Server einleiten. Das ist aber nicht gewünscht, weil viele User sofort wieder im Portal auf die nächste Anwendung klicken. Die Zeit für die Abmeldung und erneute Anmeldung vom Server nimmt zuviel Zeit in Anspruch (Look and Feel). Daher soll das Tool nach beenden des letzten Prozesses die Session noch aktiv halten bevor nach einer definierten Zeit die Abmeldung erfolgt. Das C# Programm konsumiert ca. 2 - 20 MB an Speicher wofür es keinen Grund gibt. So groß ist das Programm jetzt auch wieder net um ein Memory Leak zu finden. Bei einem TerminalServer wo 30 bis 40 User arbeiten sollen, und Speicher goldwert ist, braucht das Tool eindeutig zuviel.
Wegen der Funktion muss auch unbedingt verhindert werden, das Tool mehr als einmal pro User zu starten.
 
Magic1416 schrieb:
@xunnd
Ich caste das nach LPCWSTR weil die Funktion diesen Typ sehen will. Ich bekomme ein Compiler Errror. Alle Beispiele die ich im Netz gefunden habe, sind ohne den Cast. Wegen meinen eingeschränkten C++ Kenntissen, kann ich nicht mal sagen wieso das bei mir so ist.

LPCWSTR = Wide String (Unicode, 2 Bytes pro Zeichen)
char* = Ansi String (Multibyte, 1 Byte pro Zeichen)

Der Compiler hat sich genau deshalb beschwert - und zu Recht.

Die Funktion, die Du da benutzt, heißt CreateMutexW (W wie Wide) - Du brauchst aber CreateMutexA (A für Ansi). Die Windows-API benutzt für Unicode-Builds die W-Suffixe und für ANSI-Builds die A-Suffixe. Einstellen kannst Du das via den _UNICODE-Compilerswitch:

Dein C/C++-Projekt ist falsch konfiguriert. Projekt > Optionen > Allgemein > Zeichensatz: Multi-Byte statt Unicode.

Dann nochmal probieren - und nicht einfach wild herum casten!
 
XunnD schrieb:
Dein C/C++-Projekt ist falsch konfiguriert. Projekt > Optionen > Allgemein > Zeichensatz: Multi-Byte statt Unicode.

Was auch ginge, wäre eingach _tgetenv() zu verwenden. Das gibt je nach Projekteinstellung den String im passenden Format zurück.
 
antred schrieb:
Was auch ginge, wäre eingach _tgetenv() zu verwenden. Das gibt je nach Projekteinstellung den String im passenden Format zurück.
Das ist wahr - die Calls zu den UNICODE-Routinen sollen sogar schneller ausgeführt werden als die ANSI-Pendants, da dann keine Zeichensatzkonvertierung mehr vorgenommen werden muss.
 
Zurück
Oben