Discussion:
Läuft meine Anwendung schon ? unter Terminalserver
(zu alt für eine Antwort)
Matthias Krach
2005-07-12 16:17:44 UTC
Permalink
Hallo NG,

mit If App.PrevInstance = True Then ...
kann ich prüfen, ob meine Anwendung bereits läuft. Das funktioniert auch
gut.
Nun habe ich aber das Problem, daß ich herausfinden möchte, ob meine
Anwendung bereits unter Terminalserver Serverweit bereits läuft, d.H.
hat ein anderer User in einer anderen Terminalsitzung meine Anwendung
bereits offen. Hier versagt die obige Lösung leider.

Wie komme ich an diese Information ?

Vielen herzlichen Dank schon mal im Voraus.
--
MfG

Matthias Krach
aus Plettenberg
Frank Heindörfer
2005-07-12 16:55:18 UTC
Permalink
Hallo Matthias,
Post by Matthias Krach
Wie komme ich an diese Information ?
Verwende doch in der Deiner Anwendung einen globalen Mutex.
Damit sollte es klappen.

Vielleicht hilft Dir ja einer dieser Artikel weiter:
(Hinweis: Auch WinXP ist ein Terminalserver - ein sogenannter Single-TS.
Das Umschalten zwischen den einzelnen Sitzung wird "Fast-User-Switching"
genannt.)
http://windowsxp.devx.com/articles/fus/default.asp
http://support.microsoft.com/default.aspx?scid=kb;de;310153#Task4

Tschüs Frank
Matthias Krach
2005-07-13 15:35:46 UTC
Permalink
Post by Frank Heindörfer
Hallo Matthias,
Post by Matthias Krach
Wie komme ich an diese Information ?
Verwende doch in der Deiner Anwendung einen globalen Mutex.
Damit sollte es klappen.
(Hinweis: Auch WinXP ist ein Terminalserver - ein sogenannter
Single-TS. Das Umschalten zwischen den einzelnen Sitzung wird
"Fast-User-Switching" genannt.)
http://windowsxp.devx.com/articles/fus/default.asp
http://support.microsoft.com/default.aspx?scid=kb;de;310153#Task4
Tschüs Frank
Hallo Frank,
vielen Dnak für Deine Hilfe.
Nach Studium der beiden Links habe ich nach Mutex und VB6 gegoogelt und
den
nachfolgenden Code gefunden und in meine Anwendung eingebaut, leider
ohne Erfolg.
Der Code lautet:(Hoffentlich ist er auch nach dem Senden noch lesbar -
Zeilenumbruch-)
'***********************************************************************
*******
'******This code is downloaded from this page:
'****** <http://www.Planet-Source-Code.com/vb/default.asp?lngCId=56211
&lngWId=1>
'****** Author: Isbat Sakib
'****** Email: ***@hotmail.com <mailto:***@hotmail.com>
'Just start your app from the Sub Main. And at the end of your app, call
MutexCleanUp Sub.
'Now what is a mutex? A mutex object is a synchronization object whose
state is set to
'signaled when it is not owned by any thread, and nonsignaled when it is
owned. Only one
'thread at a time can own a mutex object, whose name comes from the fact
that it is
'useful in coordinating mutually exclusive access to a shared resource.
Now, how this
'method of avoiding multiple instances works is explained in the
comments below. Enjoy.
'The API functions and constantfor mutex manipulation
Private Declare Function CreateMutex Lib "kernel32" Alias
"CreateMutexA" (ByVal
lpMutexAttributes As Long, ByVal bInitialOwner As Long, ByVal lpName As
String) As Long
Private Declare Function ReleaseMutex Lib "kernel32" (ByVal hMutex As
Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal
hHandle As Long, ByVal
dwMilliseconds As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As
Long) As Long
Private Const WAIT_OBJECT_0 As Long = &H0
'These APIs are only used for showing the previous instance of the app.
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA"
(ByVal
lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long,
ByVal
nCmdShow As Long) As Long
Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As
Long) As Long
Private Const SW_RESTORE = 9

'This string should be as unique as possible but GENERALLY not more than
254 characters
'actually not more than the value of MAX_PATH constant as documented in
MSDN.
Private Const UniqueString As String =
"StringForAvoidingMultipleInstance"
'This variable will have the handle of the mutex
Private GMutex As Long
Sub Main()
'Dim OldHWnd As Long

If CheckAndCreateMutex Then 'No previous instance, so load the main
form.
MDI_THS_SicherungsTool.Show '<- meine eigentliche Anwendung
Else
'A previous instance exists. Find that window as
'the caption is known.
'OldHWnd = FindWindow(vbNullString, "THS-Sicherungstool") <- von mir
auskommentiert

'If OldHWnd <> 0 Then 'If the window is found,then show it and set focus
'to it whether it is minimized or not.
^----------------- von mir auskommentiert
'Call ShowWindow(OldHWnd, SW_RESTORE) <- von mir auskommentiert
'Call SetForegroundWindow(OldHWnd) <- von mir auskommentiert
'End If
End If

End Sub
Public Function CheckAndCreateMutex() As Boolean

GMutex = CreateMutex(0&, 1&, UniqueString) 'First, lets create the mutex

MsgBox "Mutex ist: " & GMutex & vbNewLine & UniqueString, vbOKOnly,
"Test"<-von mir
zwecks Test eingefügt

If GMutex = 0 Then 'Error occurred for some reason.
MsgBox "The mechanism to ensure only one instance of this app has failed
for unknown
reasons.", vbCritical, "Error"
CheckAndCreateMutex = True
Else

'Now this requires some explanation. The mutex has been created, but
does not
'belong to this specific application thread. This could be done by
setting the
'second parameter of CreateMutex function to 1, but I don't know why it
doesn't
'work in VB, though the same thing works perfectly in C++. So, another
work-around
'is here. The next function will only check if the mutex is signaled or
not as the
'second parameter is given zero. The mutex will be non-signaled if a
thread owns
'it already. Now calling once this function makes the calling thread the
owner of
'the mutex if it doesn't have an owner already.

If WaitForSingleObject(GMutex, 0&) = WAIT_OBJECT_0 Then 'The mutex is
signaled and
'no other thread owns it.
'But from now on, this thread
'will own the mutex.
CheckAndCreateMutex = True
MsgBox "Mutex wurde zugeordnet", vbOKOnly, "Test" <-von mir zwecks Test
eingefügt

Else 'Several other things might have happened.
'The mutex is may be non-signal, or it already
'has an owner, or the time-out for the
'function return is finished.

MsgBox "THS-Sicherungstool läuft bereits." & vbNewLine & "Mehrfacher
Aufruf ist nicht
zulässig.", vbInformation, "THS-Sicherungstool"

Call CloseHandle(GMutex) 'The already owned mutex has been opened
'by this thread. Well, now close the handle
'of it.
CheckAndCreateMutex = False

End If

End If

End Function
Public Sub MutexCleanUp()
Call ReleaseMutex(GMutex) 'A thread should release the mutex when no
longer
'needed.
End Sub


Zum Testen haben ich einen Server2003, ein Winxp Prof und ein W2k.
wenn ich nun am Server das PGM zwei mal aufrufen will, bekomme ich die
Meldung, daß das PGM bereits läuft -ist ok. Wenn ich das PGM aber einmal
am Server (Console) und einmal vom XP-Client oder W2k-Client in einer
Session des RDP-Clients (MSTSC.EXE) aufrufe, wird das PGM anstandslos
ein zweites Mal aufgerufen. Warum?

Die zugeardnete Kennung (GMutex) ist jeweils gleich, also sollte er doch
finden, daß die Anwendung bereits läuft.

Der Terminalserverdienst läuft im Administratonsmodus, beie Anmeldungen
laufen unter Administrator. Könnte das das Problem sein?

Es wäre schön, wenn Du mir da weiter helfen könntest. Vielen Dank.
--
MfG

Matthias Krach
aus Plettenberg
Dieter Strassner
2005-07-13 16:21:03 UTC
Permalink
Hallo Matthias,

wir testen in unserer Anwendung bei einigen Programmen ebenfalls mit dem
Mutex-Eintrag ob ein erster/zweiter Start vorliegt. Das läuft 100%ig unter
MS-TS und ich behaupte: Auch 100%ig korrekt unter Citrix. Wir haben einige
Kunden die mit Citrix arbeiten, da hat noch keiner "gemeckert", bzw. es fiel
mir noch nicht auf, das dort Programme mehrfach aufrufbar wären...

Die Mutex-Abfragerei haben wir ein klein wenig für unsere Bedürfnisse
abgewandelt.

Wir fragen unmitelbar nach Porgrammstart erstmal ab:

If modMutex.FirstStart Then ... nur dann ist der Programmstart erfolgreich,
da erster Start in dieser Session

Im BAS-Modul "modMutex" liegt dann dieser Code:
______________

' ***********************************************
' ** **
' ** Wie oft läuft die Instanz einer EXE? **
' ** Diese Abfrage funktioniert auch unter **
' ** MS-Terminalserver! **
' ** **
' ***********************************************
' Quelle:
http://www.Planet-Source-Code.com/vb/scripts/ShowCode.asp?txtCodeId=15029&lngWId=1
'
' Legende:
' ~~~~~~~
' 26.02.2004 ds Eingebunden in SEMINAR



Option Explicit
'Public Const E_TOO_MANY_INSTANCES = 256

' Public auf Private umgestellt: 27.02.2004 ds
Private Declare Function CreateMutex Lib "kernel32" Alias "CreateMutexA"
(ByVal lpMutexAttributes As Long, ByVal bInitialOwner As Long, ByVal lpName
As String) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle
As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long)
As Long
'

Public Function FirstStart() As Boolean
' Erster und einziger Programmstart?
' (bei Start des OCX aus EXE-Hülle oder aus IE heraus)

On Error GoTo ErrorHandler
FirstStart = (DetermineInstanceNumber(1) = 1)
Exit Function

ErrorHandler:
Const strModName As String = "FirstStart."
Err.Raise Number:=Err.Number, _
Source:=strModName & Err.Source, _
Description:=Err.Description
End Function

Public Function SecondStart() As Boolean
' Zweiter Programmstart?
' (bei Start des OCX aus EXE-Hülle oder aus IE heraus)

On Error GoTo ErrorHandler
SecondStart = (DetermineInstanceNumber(2) = 2)
Exit Function

ErrorHandler:
Const strModName As String = "SecondStart."
Err.Raise Number:=Err.Number, _
Source:=strModName & Err.Source, _
Description:=Err.Description
End Function

Public Function DetermineInstanceNumber(ByRef MAX_INSTANCES As Integer) As
Integer
' Wieviele Instanzen dieses Programms laufen bereits?
Dim tiL As Integer
Dim hMutex As Long
Dim strMutexName As String

On Error GoTo ErrorHandler
For tiL = 1 To MAX_INSTANCES
strMutexName = App.ExeName + "-MUTEX-" + CStr(tiL) 'create our mutex
hMutex = CreateMutex(0, 0, strMutexName)
If hMutex <> 0 Then 'did it open?
'mutex is not signaled at this point... try and do that
If WaitForSingleObject(hMutex, 0) = 0 Then
DetermineInstanceNumber = tiL 'we got the mutex!
Exit Function
End If
'that instance number is already claimed... loop around for another
try
Call CloseHandle(hMutex)
End If
Next
DetermineInstanceNumber = MAX_INSTANCES + 1 ' Es laufen zuviele...
' MsgBox "Too many (" + CStr(MAX_INSTANCES) + ") instances already
running!"
' Err.Raise E_TOO_MANY_INSTANCES
Exit Function

ErrorHandler:
Const strModName As String = "DetermineInstanceNumber."
Err.Raise Number:=Err.Number, _
Source:=strModName & Err.Source, _
Description:=Err.Description
End Function


______________
--
Viele Grüße

Dieter


Rückfragen bitte nur in die Newsgroup!

EDV-Kommunikation Strassner e.K.
68623 Lampertheim
Internet: www.strassner.biz
Frank Heindörfer
2005-07-13 22:24:41 UTC
Permalink
Hallo Matthias,

anbei eine Quick&Dirty Lösung.

Viel Spaß beim Testen.

Tschüs Frank

'--- Testen ob ein Programm bereits gestartet wurde ---
'--- Autor Frank Heindörfer ---
Option Explicit

Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type

Private Const ERROR_ALREADY_EXISTS As Long = 183&
Private Const ERROR_ACCESS_DENIED As Long = 5&

Private Declare Function CreateMutex Lib "kernel32" Alias "CreateMutexA"
(lpMutexAttributes As Any, ByVal bInitialOwner As Long, ByVal lpName As
String) As Long
Private Declare Function ReleaseMutex Lib "kernel32" (ByVal hMutex As
Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As
Long) As Long

Public Function CheckMutex(MutexName As String) As Boolean
Dim sa As SECURITY_ATTRIBUTES, mlngHMutex As Long
sa.bInheritHandle = 1
sa.lpSecurityDescriptor = 0
sa.nLength = Len(sa)
mlngHMutex = CreateMutex(sa, 1, MutexName)
If Err.LastDllError = ERROR_ALREADY_EXISTS Or Err.LastDllError =
ERROR_ACCESS_DENIED Then
CheckMutex = True
Else
If mlngHMutex <> 0 Then
ReleaseMutex mlngHMutex
CloseHandle mlngHMutex
CheckMutex = False
End If
End If
End Function

Public Function CreateMutex1(MutexName As String) As Boolean
Dim sa As SECURITY_ATTRIBUTES, mlngHMutex As Long
sa.bInheritHandle = 1
sa.lpSecurityDescriptor = 0
sa.nLength = Len(sa)
mlngHMutex = CreateMutex(sa, 1, MutexName)
If (Err.LastDllError = ERROR_ALREADY_EXISTS) Then
CreateMutex1 = False
Else
CreateMutex1 = True
End If
End Function

Public Function ReleaseMutex1(Mutex As Long) As Boolean
ReleaseMutex1 = False
If Mutex <> 0 Then
ReleaseMutex Mutex
CloseHandle Mutex
ReleaseMutex1 = True
End If
End Function

Public Sub Main()
Dim MName As String, m1 As Long, m2 As Long
' Für den Mutex-Namen verwendet man im Allgemeinen eine eindeutige GUID,
' den man über die gesamte Projektdauer konstant halten sollte.
' Weitgehend könnte man ihn als Seriennummer des Programms betrachten.
' Hier ein Link dazu:
' http://www.aboutvb.de/khw/artikel/khwcreateguid.htm
MName = "BD259555-FD60-4C3D-94E4-3CC5A871DC0D"
' Um TS weit alle Sessions zu erfassen verwendet man den Prefix "Global\"
If CheckMutex(MName) Or CheckMutex("Global\" & MName) Then
MsgBox "Programm läuft bereits!"
Else
m1 = CreateMutex1(MName) ' lokaler Mutex
m2 = CreateMutex1("Global\" & MName) ' Globaler Mutex
MsgBox "Programm ist gestartet!" & vbCrLf & vbCrLf & _
"Drücken Sie auf ""OK"" um das Programm zu beenden."
' Beim Beenden des Programm sollten die erzeugten Mutexe wieder
freigegeben werden
ReleaseMutex1 m1
ReleaseMutex1 m2
End If
End Sub
Wolfgang Enzinger
2005-07-12 23:02:55 UTC
Permalink
Hallo Matthias,
Post by Matthias Krach
mit If App.PrevInstance = True Then ...
kann ich prüfen, ob meine Anwendung bereits läuft. Das funktioniert auch
gut.
Nun habe ich aber das Problem, daß ich herausfinden möchte, ob meine
Anwendung bereits unter Terminalserver Serverweit bereits läuft, d.H.
hat ein anderer User in einer anderen Terminalsitzung meine Anwendung
bereits offen. Hier versagt die obige Lösung leider.
sicher? Ich habe das eher so in Erinnerung, dass App.PrevInstance unter
TS dann True ist, wenn auf dem Server (egal in welcher Sitzung) bereits
eine Instanz läuft. Ich hatte im Gegenteil das Problem, dass ich mit
App.PrevInstance nicht feststellen konnt, ob in der betreffenden Sitzung
bereits eine Instanz vorhanden ist (habe ich dann mit FindWindow()
gelöst).

Von welcher TS-Variante sprichst du?
--
Viele Grüsse,
Wolfgang
http://www.enzinger.net
Frank Heindörfer
2005-07-13 05:34:42 UTC
Permalink
Hallo Wolfgang,
Post by Wolfgang Enzinger
sicher? Ich habe das eher so in Erinnerung, dass App.PrevInstance unter
TS dann True ist, wenn auf dem Server (egal in welcher Sitzung) bereits
eine Instanz läuft.
Ich habe das gerade mal überprüft und kann das nicht bestätigen. Obwohl
die Anwendung bereits in einer Sitzung gestartet war, liefert
"App.PrevInstance" in einer anderen Sitzung "False".
Post by Wolfgang Enzinger
... (habe ich dann mit FindWindow() gelöst).
Von diese Variante halte ich persönl. nichts, denn unter Umständen kann
dies je nach Fall bei speziellen Explorer-, Notepad- oder anderen
Anwendungsfenster zu Irrtümern führen. Ein Mutex schafft hier Klarheit.
Aber wie gesagt, das ist meine pers. Meinung.

Tschüs Frank
Matthias Krach
2005-07-13 15:40:44 UTC
Permalink
Post by Frank Heindörfer
Hallo Matthias,
Post by Matthias Krach
mit If App.PrevInstance = True Then ...
kann ich prüfen, ob meine Anwendung bereits läuft. Das funktioniert
auch gut.
Nun habe ich aber das Problem, daß ich herausfinden möchte, ob meine
Anwendung bereits unter Terminalserver Serverweit bereits läuft,
d.H. hat ein anderer User in einer anderen Terminalsitzung meine
Anwendung bereits offen. Hier versagt die obige Lösung leider.
sicher? Ich habe das eher so in Erinnerung, dass App.PrevInstance
unter TS dann True ist, wenn auf dem Server (egal in welcher
Sitzung) bereits eine Instanz läuft. Ich hatte im Gegenteil das
Problem, dass ich mit App.PrevInstance nicht feststellen konnt, ob
in der betreffenden Sitzung bereits eine Instanz vorhanden ist (habe
ich dann mit FindWindow() gelöst).
Von welcher TS-Variante sprichst du?
Sowohl unter W2k-Server als auch W2003-Server funktioniert
app.PrevInstance nur innerhalb der aktuellen Sitzung. Evtl. ist das
unter Citrx anders, kann ich aber nicht testen.
Zu Deinem Hinweis mit FindWindow() erlaube ich mir den Hinweis, daß MS
in den von Frank genanten Links das als Möglichkeit angibt, es aber
selbst nicht empfiehlt. Die Saubere Lösung ist wohl tatsächlich mit
einem Mutex zu arbeiten, was mir aber nichts gelingt. Siehe auch mein
Posting an Frank.

Trotzdem herzlichen Dank für Deine Hilfe.
--
MfG

Matthias Krach
aus Plettenberg
Wolfgang Enzinger
2005-07-13 21:47:47 UTC
Permalink
Hallo Matthias,
Post by Matthias Krach
Post by Wolfgang Enzinger
Von welcher TS-Variante sprichst du?
Sowohl unter W2k-Server als auch W2003-Server funktioniert
app.PrevInstance nur innerhalb der aktuellen Sitzung.
meine Erfahrungen (wenn ich jetzt nicht alles durcheinanderbringe)
beziehen sich auf Win2000 Server, egal welchen Client und VB5. Da war es
so, dass App.PrevInstance offenbar alle TS-Client-Sitzungen in einen
Topf geworfen hat. Deinem anderen Posting entnehme ich allerdings, dass
du Client-Sitzungen auch gegen das lokale Login abprüfen möchtest. Das
hab ich noch gar nicht getestet und kann daher nichts dazu beitragen.
Post by Matthias Krach
Evtl. ist das unter Citrx anders, kann ich aber nicht testen.
Ich auch nicht. ;-)
Post by Matthias Krach
Zu Deinem Hinweis mit FindWindow() erlaube ich mir den Hinweis, daß MS
in den von Frank genanten Links das als Möglichkeit angibt, es aber
selbst nicht empfiehlt.
Ich hab mir die Links jetzt nicht angeschaut, aber die Bedenken von
Frank teile ich durchaus. Ich verlasse mich auch nicht nicht nur auf
ClassName und Caption, sondern prüfe zur Sicherheit noch eine
Window-Property (SetProp(), GetProp()) und / oder, wenn das
Anwendungsfenster sowieso "ge-subclass-ed" wird, eine selbstdefinierte
Fensternachricht ab, um sicherzugehen, dass es sich wirklich um "mein"
Fenster handelt.
Post by Matthias Krach
Die Saubere Lösung ist wohl tatsächlich mit einem Mutex zu arbeiten
Damit habe ich mich noch kaum beschäftigt, somit keine eigenen
Erfahrungen. Ich kann aber mal eine Kollegin dazu befragen, die ein
Java-Programm mit Hilfe einer Windows-DLL gegen mehrfachen Programmstart
absichern und gleichzeitig "TS-proof" machen musste, die hat das IIRC
auch mit einem Mutex bewerkstelligt. Wir haben damals allerdings nur mit
mehreren TS-Sitzungen getestet, ein Programmstart unter lokalem Login am
Server war in dem Szenario nicht vorgesehen.
--
Viele Grüsse,
Wolfgang
http://www.enzinger.net
Fritz Meiners
2005-07-14 12:30:10 UTC
Permalink
Hallo Matthias, ich weiß nicht ob dein Problem schon gelöst ist, aber ich
hatte auch mal das Problem.
Ich weiß nicht ob du mit einer Datenbank zusammenarbeitest. Ich habe eine
Anwendung die x-mal durch TS User gestartet werden kann, diese benutzt ein
Login und alle eine gemeinsame Datenbank. Sobald die Anwendung gestartet
wurde , wurde ein Eintrag in der Datenbank vorgenommen. (auch Name des
Users)
Geht alles tadellos, und man kann sogar Lizenzprobleme damit lösen :)

Dies ganze Brimborium mit irgendwelchen hat nie richtig hingehauen und ist
auch zu unsicher.
Gruss
Post by Matthias Krach
Hallo NG,
mit If App.PrevInstance = True Then ...
kann ich prüfen, ob meine Anwendung bereits läuft. Das funktioniert auch
gut.
Nun habe ich aber das Problem, daß ich herausfinden möchte, ob meine
Anwendung bereits unter Terminalserver Serverweit bereits läuft, d.H.
hat ein anderer User in einer anderen Terminalsitzung meine Anwendung
bereits offen. Hier versagt die obige Lösung leider.
Wie komme ich an diese Information ?
Vielen herzlichen Dank schon mal im Voraus.
--
MfG
Matthias Krach
aus Plettenberg
Frank Heindörfer
2005-07-14 12:47:10 UTC
Permalink
Hallo Fritz,
Post by Fritz Meiners
Geht alles tadellos, und man kann sogar Lizenzprobleme damit lösen :)
Aber nur solange wie sich ein Benutzer brav an- und abmeldet. Was
passiert aber wenn der TS "abschmiert" oder die Administratoren
gewaltsam Sitzungen beenden müssen?

Frank
Fritz Meiners
2005-07-14 14:34:00 UTC
Permalink
Auch kein Problem weil laufend nach "toten" Clients geschaut wird. Und wenn
der TS abschmiert ???
Denn kann sich doch auch kein User anmelden (Anwendung starten)
Post by Frank Heindörfer
oder die Administratoren
Denn ist diese Anwendung ja auch nicht mehr da ...

Gruss
Post by Frank Heindörfer
Hallo Fritz,
Post by Fritz Meiners
Geht alles tadellos, und man kann sogar Lizenzprobleme damit lösen :)
Aber nur solange wie sich ein Benutzer brav an- und abmeldet. Was
passiert aber wenn der TS "abschmiert" oder die Administratoren
gewaltsam Sitzungen beenden müssen?
Frank
Dieter Strassner
2005-07-14 15:45:15 UTC
Permalink
Hallo Fritz,
Post by Fritz Meiners
Auch kein Problem weil laufend nach "toten" Clients geschaut wird. Und wenn
der TS abschmiert ???
Denn kann sich doch auch kein User anmelden (Anwendung starten)
Post by Frank Heindörfer
oder die Administratoren
Denn ist diese Anwendung ja auch nicht mehr da ...
ja, aber der Eintrag in der DB!
...und dann wird deine Anwendung nicht mehr starten, weil ja der Eintrag
drin hängt!

Wir haben das so gelöst:
Eintrag in DB je Programmstart wg. Lizenzen zählen. Bei regulärem
Programmstart wird der eigene Eintrag zzgl. alle "verweisten" Einträge
rausgeworfen. "Verweist" ist ein Eintrag wenn er älter als Heute ist.
Bei Programmabbruch hängt der Eintrag noch in der DB, Bei Start wird
versucht ein INSERT mit PC-Name und Programmname durchzuführen (eindeutiger
zusammengesetzter Key) Zusätzlich wird als Bedingung die max.Lizenzzahl in
die WHERE-Klausel eingebaut. Bei Fehlschlag wird versucht den Eintrag zu
aktualisieren (was dann auch klappen sollte, sofern die Lizenzen
ausreichen). Darf ein Programm jedoch nur einmal je Session/PC gestartet
werden, wird per Mutex-Abfrage dies VORHER abgefragt!

Diese Methoden sind "selbstheilend" und funktionieren unter MS-TS und -
soweit ich das weiß - auch unter Citrix.
--
Viele Grüße

Dieter


Rückfragen bitte nur in die Newsgroup!

EDV-Kommunikation Strassner e.K.
68623 Lampertheim
Internet: www.strassner.biz
Frank Heindörfer
2005-07-14 15:46:09 UTC
Permalink
Hallo Fritz,

lass uns ein wenig das Für und Wider gegeneinander abwägen. Und
sicherlich hat auch Deine Vorgehensweise ihre Berechtigung.

Datenbank:
Nachteile: Ich benötige zusätzliche Datenbanktreiber und eine Datenbank,
auch wenn ich damit nichts am Hut habe. Ich muss nach "Toten" Sitzungen
suchen (Wie machst Du das eigentlich auf einem TS, als Dienst und
schaust dann die Prozessliste durch?), was zusätzlich den Rechner
belastet. Ein nicht unerheblich zusätzlicher Code, der möglicherweise
auch noch gewartet werden muss.
Vorteil: Ich kann beim Start zusätzliche Informtionen ablegen,
Programm-Berechtigungen\Lizenzen abfragen etc..

Mutex:
Nachteil: Es können keine zusätzlichen Informationen abgelegt werden.
Vorteile: Es ist die von MS vorgeschlagene Vorgehensweise und
funktioniert lokal und auf TS. Der Code ist klein, stabil, setzt nichts
voraus und kann bequem auch in Setup-Programme (wie InnoSetup etc.)
integriert werden.

Wenn es nur darum geht, festzustellen ob meine Anwendung noch in einer
Sitzung auf einem TS läuft, würde ich als Administrator entweder zum
Taskmanager greifen oder wenn es bequemer sein soll, einen Mutex
verwenden (z.B. bei dem erwähnten Backup-Programm).
Wenn ich eine Datenbankanwendung mit integrierter Benutzerverwaltung zu
realisieren habe, könnte ich das auch damit erledigen. Wobei ich ehrlich
zugeben muss, dass ich mich an diesem Mutex so gewöhnt habe, das ich ihn
wahrscheinlich auch da anwenden würde. So kann ich auch mit jeden
x-beliebigen Setup-Programm danach abfragen, ob eines meiner Programme
noch läuft.

Tschüs Frank
Jens Duczmal
2005-07-15 08:01:53 UTC
Permalink
On Thu, 14 Jul 2005 14:47:10 +0200, Frank Heindörfer
Post by Frank Heindörfer
Hallo Fritz,
Post by Fritz Meiners
Geht alles tadellos, und man kann sogar Lizenzprobleme damit lösen :)
Aber nur solange wie sich ein Benutzer brav an- und abmeldet. Was
passiert aber wenn der TS "abschmiert" oder die Administratoren
gewaltsam Sitzungen beenden müssen?
Einfache, aber simple Lösung :

- Für jeden User der sich anmeldet, wird eine Textdatei erstellt.
- Diese kann leer sein oder meinetwegen auch den usernamen etc.
beinhalten.

- Die aktuelle Instanz hält diese bis zum Beenden offen
d.h. close #ff wird halt nicht ausgeführt.

- Eine zweite Instanz löscht nun alle Textdateien in diesem
speziellen Ordner. Die Dateien, die noch offen sind, werden
eben nicht mit gelöscht.

Liegen dort noch Files von abgeschmierten Clients, so werden
diese dann aber vernichtet.

- Anschließend die Anzahl der übriggebliebenen Dateien zählen
und Du hast die augenblickliche Userzahl.

Fertig.

Die Textdatei kann zudem noch als Shared geöffnet werden, damit
vielleicht in einer MsgBox der Names des blockierenden Users
ausgegeben werden kann.

Gruß,
Jens
Frank Heindörfer
2005-07-15 09:08:59 UTC
Permalink
Hallo Jens,
Post by Jens Duczmal
....
[snipped]
Post by Jens Duczmal
Fertig.
Für mich sieht der Mutex aber wesentlich einfacher aus, aber das ist
wahrscheinlich Geschmackssache. ;-)

Frank
Matthias Krach
2005-07-15 09:57:08 UTC
Permalink
An alle, die geantwortet haben,

vielen herzlichen Dank für die zahlreichen Anregungen und
Diskussionsbeiträge. Da ich gestern unterweg war, habe ich erst heute
(und natürlich am Wochenende) Zeit, mir Eure Beiträge in Ruhe
durchzulesen. Danach werde ich voraussichlich die Mutex-Lösung nochmal
versuchen umzusetzen. Das Ergebnis teile ich dann hier mit.

Nochmal herzlichen Dank an Euch alle.
--
MfG

Matthias Krach
aus Plettenberg
Matthias Krach
2005-07-20 19:23:05 UTC
Permalink
Hallo zusammen,

Nachdem ich nun Zeit hatte, die vielen Lösungsvorschläge für mein
Problem durchzuarbeiten, habe ich mir für die Lösung von Frank
Heindörfer entschieden mit ein paar kleinen Änderungen bzw. Anpassungen.
Für interessierte anbei das Modul, wie es nun bei mir einsatzfähig ist.

'--- Testen ob ein Programm bereits gestartet wurde ---
'--- Autor Frank Heindörfer ---
'--- Angepasst am 20.07.2005 von Matthias Krach zur Nutzung mit einem
globalen Mutex
' Modul erweitert um Public Const MyMutex As String = "Global\THS-
Sicherungstool-Mutex"
' und Public GlobalMutex As Long
' Funktion CreateMutex1 umbenannt in MakeMutex
' und erweitert um GlobalMutex=mlngHMutex
' Funktion ReleaseMutex1 umbenannt in KillMutex
' Sub Main auskommentiert, da Aufruf in MDI-Form erfolgt
' In MDI-Form Load erfolgt die Prüfung, Erstellung des Mutex
' In MDI-Form Queryunload erfolgt die Aufhebung des Mutex

Option Explicit

Public Const MyMutex As String = "Global\THS-Sicherungstool-Mutex" 'PGM-
Name als MutexName
Public GlobalMutex As Long

Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type

Private Const ERROR_ALREADY_EXISTS As Long = 183&
Private Const ERROR_ACCESS_DENIED As Long = 5&

Private Declare Function CreateMutex Lib "kernel32" Alias
"CreateMutexA" (lpMutexAttributes As Any, ByVal bInitialOwner As Long,
ByVal lpName As String) As Long
Private Declare Function ReleaseMutex Lib "kernel32" (ByVal hMutex As
Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As
Long) As Long

Public Function CheckMutex(Mutexname As String) As Boolean
Dim sa As SECURITY_ATTRIBUTES, mlngHMutex As Long
sa.bInheritHandle = 1
sa.lpSecurityDescriptor = 0
sa.nLength = Len(sa)
mlngHMutex = CreateMutex(sa, 1, Mutexname)
If Err.LastDllError = ERROR_ALREADY_EXISTS Or Err.LastDllError =
ERROR_ACCESS_DENIED Then
GlobalMutex = mlngHMutex
CheckMutex = True
Else
If mlngHMutex <> 0 Then
ReleaseMutex mlngHMutex
CloseHandle mlngHMutex
CheckMutex = False
End If
End If
End Function

Public Function MakeMutex(Mutexname As String) As Boolean
Dim sa As SECURITY_ATTRIBUTES, mlngHMutex As Long
sa.bInheritHandle = 1
sa.lpSecurityDescriptor = 0
sa.nLength = Len(sa)
mlngHMutex = CreateMutex(sa, 1, Mutexname)
If (Err.LastDllError = ERROR_ALREADY_EXISTS) Then
MakeMutex = False
Else
GlobalMutex = mlngHMutex
MakeMutex = True
End If
End Function

Public Function KillMutex(Mutex As Long) As Boolean
KillMutex = False
If Mutex <> 0 Then
ReleaseMutex Mutex
CloseHandle Mutex
KillMutex = True
End If
End Function

'Public Sub Main()
' Dim MName As String, m1 As Long, m2 As Long
' ' Für den Mutex-Namen verwendet man im Allgemeinen eine eindeutige
GUID,
' ' den man über die gesamte Projektdauer konstant halten sollte.
' ' Weitgehend könnte man ihn als Seriennummer des Programms
betrachten.
' ' Hier ein Link dazu:
' ' http://www.aboutvb.de/khw/artikel/khwcreateguid.htm
' MName = "BD259555-FD60-4C3D-94E4-3CC5A871DC0D"
'
''ReleaseMutex1 MName
''ReleaseMutex1 "Global\" & MName
'
' ' Um TS weit alle Sessions zu erfassen verwendet man den Prefix
"Global\"
' If CheckMutex("Global\" & MName) Then
' MsgBox "Programm läuft bereits!"
' Else
' m1 = CreateMutex1(MName) ' lokaler Mutex
' m2 = CreateMutex1("Global\" & MName) ' Globaler Mutex
' 'MsgBox "Programm ist gestartet!" & vbCrLf & vbCrLf & _
' ' "Drücken Sie auf ""OK"" um das Programm zu beenden."
' ' Beim Beenden des Programm sollten die erzeugten Mutexe wieder
freigegeben werden
' ReleaseMutex1 m1
' ReleaseMutex1 m2
' End If
'End Sub
--
MfG

Matthias Krach
aus Plettenberg
Lesen Sie weiter auf narkive:
Loading...