Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

App.PrevInstance

7 réponses
Avatar
Damien DEFEUX
Bonjour,

J'utilise App.PrevInstance depuis des années pour n'avoir mon application
ouverte qu'une seule fois. Mais j'ai eu plusieurs fois le cas où
App.PrevInstance m'indique que l'application est déjà lancée alors que ce
n'est pas le cas (pas de processus dans le gestionnaire de taches).

J'ai deux questions par rapport à ça :

- Quelqu'un sait-il comment fonctionne le App.PrevInstance et sur quoi il
se base ?

- Quelles sont les alternatives pour remplacer App.PrevInstance ?

Merci d'avance.

Dams

7 réponses

Avatar
Jean-Marc
On Apr 13, 2:49 pm, "Damien DEFEUX" wrote:
Bonjour,

J'utilise App.PrevInstance depuis des années pour n'avoir mon applicati on
ouverte qu'une seule fois. Mais j'ai eu plusieurs fois le cas où
App.PrevInstance m'indique que l'application est déjà lancée alors que ce
n'est pas le cas (pas de processus dans le gestionnaire de taches).



Hello,
si tu as ce genre de comportements, c'est que ton application est
toujours chargée en mémoire. Tu ne la vois pas dans la partie
"Applications" du gestionnaire de taches, mais elle peut etre encore à
l'état de zombie dans la partie "Processus". Il y a plein de causes à
ce genre de problèmes, le plus classique étant un programme VB ayant
instancié un objet ActiveX (Word, Excel, etc) et ayant quitté
anormalement ou sans faire le cleanup nécessaire.
Il y a des API qui font ce que fait App.PrevInstance, mais ce sera la
même chose car APP.Previnstance utilise ces API.

Cordialement,


--
Jean-marc Noury (jean_marc_n2)
FAQ VB: http://faq.vb.free.fr/
mailto: remove '_no_spam_' ;
Avatar
Jacques93
Bonjour Jean-Marc,
Jean-Marc a écrit :
On Apr 13, 2:49 pm, "Damien DEFEUX" wrote:
Bonjour,

J'utilise App.PrevInstance depuis des années pour n'avoir mon application
ouverte qu'une seule fois. Mais j'ai eu plusieurs fois le cas où
App.PrevInstance m'indique que l'application est déjà lancée alors que ce
n'est pas le cas (pas de processus dans le gestionnaire de taches).



Hello,
si tu as ce genre de comportements, c'est que ton application est
toujours chargée en mémoire. Tu ne la vois pas dans la partie
"Applications" du gestionnaire de taches, mais elle peut etre encore à
l'état de zombie dans la partie "Processus". Il y a plein de causes à
ce genre de problèmes, le plus classique étant un programme VB ayant
instancié un objet ActiveX (Word, Excel, etc) et ayant quitté
anormalement ou sans faire le cleanup nécessaire.
Il y a des API qui font ce que fait App.PrevInstance, mais ce sera la
même chose car APP.Previnstance utilise ces API.




Tout à fait d'accord, mais dans le cas de process zombie, on peux killer
le zombie. Dans le fil de driss hanib concernant App.Previnstance, je
n'avais pas indiqué le code, le voici (sans les déclarations) :

Public Sub Main()
If App.PrevInstance Then
Dim ThreadId As Long, PID As Long
Dim hProcess As Long, lResult As Long

hWnd = FindWindow("ThunderRT6MDIForm", "Caption de la fenêtre")
On Error Resume Next
Err.Clear
AppActivate "Caption de la fenêtre"
If Err.Number <> 0 Or (Err.Number = 0 And hWnd = 0) Then
' Err = 5 si Process actif et fenetre invisible
' (en général suite à plantage)
If hWnd <> 0 Then
' Recupere le ProcessId via le handle de la fenetre invisible
ThreadId = GetWindowThreadProcessId(hWnd, PID)
Else
' ou en balayant la table des process si le process ne répond pas
PID = GetProcessPID("NomExe.exe")
End If
' Ouverture et suppresion du process
hProcess = OpenProcess(PROC_SYNCHRONIZE Or PROCESS_TERMINATE, 0, PID)
If hProcess Then
lResult = TerminateProcess(hProcess, 0)
CloseHandle hProcess
End If
Else
' Sinon restaure / reactive la fenetre
If IsIconic(hWnd) Then
X = ShowWindow(hWnd, SW_RESTORE)
Else
X = ShowWindow(hWnd, SW_SHOW)
End If
End
End If
End If
End Sub


Public Function GetProcessPID(ModName As String) As Long
Const MAX_PROCESS As Long = 4096
Dim lResult As Long
Dim cb As Long, cbNeeded As Long
Dim Process(MAX_PROCESS) As Long
Dim nbProcess As Long, i As Long
Dim PID As Long

cb = Len(Process(0)) * MAX_PROCESS
lResult = EnumProcesses(Process(0), cb, cbNeeded)
nbProcess = cbNeeded / Len(Process(0))
For i = 0 To nbProcess - 1
PID = GetProcessInfo(ModName, Process(i))
If PID <> 0 Then
GetProcessPID = PID
Exit Function
End If
Next i
End Function

Private Function GetProcessInfo(ModName As String, PID As Long) As Long
Dim hProcess As Long
Dim lResult As Long
Dim cb As Long, cbNeeded As Long
Dim Modules(0 To 199) As Long
Dim ModuleName As String
Dim nbModule As Long
Dim pmc As PROCESS_MEMORY_COUNTERS

GetProcessInfo = 0
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ,
0, PID)
If hProcess Then
cb = 200
lResult = EnumProcessModules(hProcess, Modules(0), 200, cbNeeded)
If lResult <> 0 Then
ModuleName = Space(MAX_PATH)
cb = 500
lResult = GetModuleFileNameExA(hProcess, Modules(0), ModuleName, cb)
ModuleName = Left(ModuleName, lResult)
ModuleName = Mid(ModuleName, InStrRev(ModuleName, "") + 1)
pmc.cb = Len(pmc)
lResult = GetProcessMemoryInfo(hProcess, pmc, pmc.cb)
If UCase(ModName) = UCase(ModuleName) Then
GetProcessInfo = PID
End If
End If
CloseHandle hProcess
End If
End Function


--

Cordialement,

Jacques.
Avatar
Damien DEFEUX
J'ai regardé dans les processus, il n'y a rien.

J'ai fait un taskkill de mon application et malgres ça j'ai toujours
App.PrevInstance qui est vrai.

J'ai regardé avec un utilitaire qui affiche les process et je vois pas mon
appli.



"Jean-Marc" a écrit dans le message de news:

On Apr 13, 2:49 pm, "Damien DEFEUX" wrote:
Bonjour,

J'utilise App.PrevInstance depuis des années pour n'avoir mon application
ouverte qu'une seule fois. Mais j'ai eu plusieurs fois le cas où
App.PrevInstance m'indique que l'application est déjà lancée alors que ce
n'est pas le cas (pas de processus dans le gestionnaire de taches).



Hello,
si tu as ce genre de comportements, c'est que ton application est
toujours chargée en mémoire. Tu ne la vois pas dans la partie
"Applications" du gestionnaire de taches, mais elle peut etre encore à
l'état de zombie dans la partie "Processus". Il y a plein de causes à
ce genre de problèmes, le plus classique étant un programme VB ayant
instancié un objet ActiveX (Word, Excel, etc) et ayant quitté
anormalement ou sans faire le cleanup nécessaire.
Il y a des API qui font ce que fait App.PrevInstance, mais ce sera la
même chose car APP.Previnstance utilise ces API.

Cordialement,


--
Jean-marc Noury (jean_marc_n2)
FAQ VB: http://faq.vb.free.fr/
mailto: remove '_no_spam_' ;
Avatar
Driss HANIB
Bonjour Jacques

tu fais ton petit cachotier ? ;o))
Ton exemple complet est plus "robuste"; je le prends "as it"

merci

Driss

"Jacques93" a écrit dans le message de news:

Bonjour Jean-Marc,
Jean-Marc a écrit :
On Apr 13, 2:49 pm, "Damien DEFEUX" wrote:
Bonjour,

J'utilise App.PrevInstance depuis des années pour n'avoir mon
application
ouverte qu'une seule fois. Mais j'ai eu plusieurs fois le cas où
App.PrevInstance m'indique que l'application est déjà lancée alors que
ce
n'est pas le cas (pas de processus dans le gestionnaire de taches).



Hello,
si tu as ce genre de comportements, c'est que ton application est
toujours chargée en mémoire. Tu ne la vois pas dans la partie
"Applications" du gestionnaire de taches, mais elle peut etre encore à
l'état de zombie dans la partie "Processus". Il y a plein de causes à
ce genre de problèmes, le plus classique étant un programme VB ayant
instancié un objet ActiveX (Word, Excel, etc) et ayant quitté
anormalement ou sans faire le cleanup nécessaire.
Il y a des API qui font ce que fait App.PrevInstance, mais ce sera la
même chose car APP.Previnstance utilise ces API.




Tout à fait d'accord, mais dans le cas de process zombie, on peux killer
le zombie. Dans le fil de driss hanib concernant App.Previnstance, je
n'avais pas indiqué le code, le voici (sans les déclarations) :

Public Sub Main()
If App.PrevInstance Then
Dim ThreadId As Long, PID As Long
Dim hProcess As Long, lResult As Long

hWnd = FindWindow("ThunderRT6MDIForm", "Caption de la fenêtre")
On Error Resume Next
Err.Clear
AppActivate "Caption de la fenêtre"
If Err.Number <> 0 Or (Err.Number = 0 And hWnd = 0) Then
' Err = 5 si Process actif et fenetre invisible
' (en général suite à plantage)
If hWnd <> 0 Then
' Recupere le ProcessId via le handle de la fenetre invisible
ThreadId = GetWindowThreadProcessId(hWnd, PID)
Else
' ou en balayant la table des process si le process ne répond pas
PID = GetProcessPID("NomExe.exe")
End If
' Ouverture et suppresion du process
hProcess = OpenProcess(PROC_SYNCHRONIZE Or PROCESS_TERMINATE, 0,
PID)
If hProcess Then
lResult = TerminateProcess(hProcess, 0)
CloseHandle hProcess
End If
Else
' Sinon restaure / reactive la fenetre
If IsIconic(hWnd) Then
X = ShowWindow(hWnd, SW_RESTORE)
Else
X = ShowWindow(hWnd, SW_SHOW)
End If
End
End If
End If
End Sub


Public Function GetProcessPID(ModName As String) As Long
Const MAX_PROCESS As Long = 4096
Dim lResult As Long
Dim cb As Long, cbNeeded As Long
Dim Process(MAX_PROCESS) As Long
Dim nbProcess As Long, i As Long
Dim PID As Long

cb = Len(Process(0)) * MAX_PROCESS
lResult = EnumProcesses(Process(0), cb, cbNeeded)
nbProcess = cbNeeded / Len(Process(0))
For i = 0 To nbProcess - 1
PID = GetProcessInfo(ModName, Process(i))
If PID <> 0 Then
GetProcessPID = PID
Exit Function
End If
Next i
End Function

Private Function GetProcessInfo(ModName As String, PID As Long) As Long
Dim hProcess As Long
Dim lResult As Long
Dim cb As Long, cbNeeded As Long
Dim Modules(0 To 199) As Long
Dim ModuleName As String
Dim nbModule As Long
Dim pmc As PROCESS_MEMORY_COUNTERS

GetProcessInfo = 0
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, 0,
PID)
If hProcess Then
cb = 200
lResult = EnumProcessModules(hProcess, Modules(0), 200, cbNeeded)
If lResult <> 0 Then
ModuleName = Space(MAX_PATH)
cb = 500
lResult = GetModuleFileNameExA(hProcess, Modules(0), ModuleName, cb)
ModuleName = Left(ModuleName, lResult)
ModuleName = Mid(ModuleName, InStrRev(ModuleName, "") + 1)
pmc.cb = Len(pmc)
lResult = GetProcessMemoryInfo(hProcess, pmc, pmc.cb)
If UCase(ModName) = UCase(ModuleName) Then
GetProcessInfo = PID
End If
End If
CloseHandle hProcess
End If
End Function


--

Cordialement,

Jacques.
Avatar
Driss HANIB
salut jacques

As tu les valeurs des constantes
PROC_SYNCHRONIZE
PROCESS_TERMINATE
PROCESS_QUERY_INFORMATION
PROCESS_VM_READ

merci

Driss
Avatar
Jacques93
Bonjour Driss HANIB,
Driss HANIB a écrit :
salut jacques

As tu les valeurs des constantes
PROC_SYNCHRONIZE
PROCESS_TERMINATE
PROCESS_QUERY_INFORMATION
PROCESS_VM_READ




Je mets le code complet :

Option Explicit

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function IsIconic Lib "user32" (ByVal hWnd As Long) As Long

' Constantes nCmdShow
' ------------------
Public Const SW_HIDE = 0 ' Hides the window and
activates another window.
Public Const SW_MINIMIZE = 6 ' Minimizes the
specified window and activates the top-level window
' in the system's list.
Public Const SW_RESTORE = 9 ' Activates and displays
a window. If the window is minimized or maximized,
' Windows restores it
to its original size and position (same as SW_SHOWNORMAL).
Public Const SW_SHOW = 5 ' Activates a window and
displays it in its current size and position.
Public Const SW_SHOWMAXIMIZED = 3 ' Activates a window and
displays it as a maximized window.
Public Const SW_SHOWMINIMIZED = 2 ' Activates a window and
displays it as an icon.
Public Const SW_SHOWNA = 8 ' Displays a window in
its current state. The active window remains active.
Public Const SW_SHOWNOACTIVATE = 4 ' Displays a window in
its most recent size and position.
' The active window
remains active
Public Const SW_SHOWNORMAL = 1 ' Activates and displays
a window. If the window is minimized or maximized,
' Windows restores it
to its original size and position (same as SW_RESTORE).

Private Declare Function ShowWindow Lib "user32" _
(ByVal hWnd As Long, ByVal nCmdShow As Long) As Long

Private Const MAX_PATH = 260

Private Const PROCESS_TERMINATE = &H1
Private Const PROCESS_CREATE_THREAD = &H2
Private Const PROCESS_SET_SESSIONID = &H4
Private Const PROCESS_VM_OPERATION = &H8
Private Const PROCESS_VM_READ = &H10
Private Const PROCESS_VM_WRITE = &H20
Private Const PROCESS_DUP_HANDLE = &H40
Private Const PROCESS_CREATE_PROCESS = &H80
Private Const PROCESS_SET_QUOTA = &H100
Private Const PROCESS_SET_INFORMATION = &H200
Private Const PROCESS_QUERY_INFORMATION = &H400

Private Const DELETE = &H10000
Private Const READ_CONTROL = &H20000
Private Const WRITE_DAC = &H40000
Private Const WRITE_OWNER = &H80000
Private Const PROC_SYNCHRONIZE = &H100000
Private Const STANDARD_RIGHTS_REQUIRED = &HF0000
Private Const STANDARD_RIGHTS_READ = READ_CONTROL
Private Const STANDARD_RIGHTS_WRITE = READ_CONTROL
Private Const STANDARD_RIGHTS_EXECUTE = READ_CONTROL
Private Const STANDARD_RIGHTS_ALL = &H1F0000
Private Const SPECIFIC_RIGHTS_ALL = &HFFFF&
Private Const PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or
PROC_SYNCHRONIZE Or &HFFF)

Type PROCESS_MEMORY_COUNTERS
cb As Long
PageFaultCount As Long
PeakWorkingSetSize As Long
WorkingSetSize As Long
QuotaPeakPagedPoolUsage As Long
QuotaPagedPoolUsage As Long
QuotaPeakNonPagedPoolUsage As Long
QuotaNonPagedPoolUsage As Long
PagefileUsage As Long
PeakPagefileUsage As Long
End Type
Private Const ProcessBasicInformation = 0

Private Type PROCESS_BASIC_INFORMATION
ExitStatus As Long
PebBaseAddress As Long
AffinityMask As Long
BasePriority As Long
UniqueProcessID As Long
InheritedFromUniqueProcessID As Long
End Type

Private Type PROCESS_ENVIRONMENT_BLOCK
AllocationSize As Long
Unknown1 As Long
ProcessInstance As Long
ListDlls As Long
ProcessParameters As Long
Unknown2 As Long
Heap As Long
End Type

Private Declare Function GetWindowThreadProcessId Lib "user32" _
(ByVal hWnd As Long, lpdwProcessId As Long) As Long

Private Declare Function OpenProcess Lib "Kernel32.dll" _
(ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, _
ByVal dwProcessId As Long) As Long

Private Declare Function TerminateProcess Lib "Kernel32.dll" _
(ByVal hProcess As Long, ByVal ExitCode As Long) As Long

Private Declare Function CloseHandle Lib "Kernel32.dll" _
(ByVal Handle As Long) As Long

Private Declare Function EnumProcesses Lib "PSAPI.dll" _
(ByRef lpidProcess As Long, ByVal cb As Long, _
ByRef cbNeeded As Long) As Long

Private Declare Function EnumProcessModules Lib "PSAPI.dll" _
(ByVal hProcess As Long, ByRef lphModule As Long, _
ByVal cb As Long, ByRef cbNeeded As Long) As Long

Private Declare Function GetModuleFileNameExA Lib "PSAPI.dll" _
(ByVal hProcess As Long, ByVal hModule As Long, _
ByVal ModuleName As String, ByVal nSize As Long) As Long

Private Declare Function GetProcessMemoryInfo Lib "PSAPI.dll" _
(ByVal hProcess As Long, ppsmemCounters As
PROCESS_MEMORY_COUNTERS, _
ByVal cb As Long) As Long

Private Const SM_REMOTESESSION = &H1000
Private Declare Function GetSystemMetrics Lib "user32" _
(ByVal nIndex As Long) As Long



Public Sub Main()
Dim hWnd As Long, bRemote As Boolean

bRemote = GetSystemMetrics(SM_REMOTESESSION) ' Terminal Server
If bRemote = False Then
If App.PrevInstance Then
Dim ThreadId As Long, PID As Long
Dim hProcess As Long, lResult As Long

hWnd = FindWindow("ThunderRT6MDIForm", "Caption de la fenêtre")
On Error Resume Next
Err.Clear
AppActivate "Caption de la fenêtre"
If Err.Number <> 0 Or (Err.Number = 0 And hWnd = 0) Then
' Err = 5 si Process actif et fenetre invisible
' (en général suite à plantage)
If hWnd <> 0 Then
' Recupere le ProcessId via le handle de la fenetre invisible
ThreadId = GetWindowThreadProcessId(hWnd, PID)
Else
' ou en balayant la table des process si le process ne répond pas
PID = GetProcessPID("NomExe.exe")
End If
' Ouverture et suppresion du process
hProcess = OpenProcess(PROC_SYNCHRONIZE Or PROCESS_TERMINATE,
0, PID)
If hProcess Then
lResult = TerminateProcess(hProcess, 0)
CloseHandle hProcess
End If
Else
' Sinon restaure / reactive la fenetre
If IsIconic(hWnd) Then
lResult = ShowWindow(hWnd, SW_RESTORE)
Else
lResult = ShowWindow(hWnd, SW_SHOW)
End If
End
End If
End If
End If
End Sub


Public Function GetProcessPID(ModName As String) As Long
Const MAX_PROCESS As Long = 4096
Dim lResult As Long
Dim cb As Long, cbNeeded As Long
Dim Process(MAX_PROCESS) As Long
Dim nbProcess As Long, i As Long
Dim PID As Long

cb = Len(Process(0)) * MAX_PROCESS
lResult = EnumProcesses(Process(0), cb, cbNeeded)
nbProcess = cbNeeded / Len(Process(0))
For i = 0 To nbProcess - 1
PID = GetProcessInfo(ModName, Process(i))
If PID <> 0 Then
GetProcessPID = PID
Exit Function
End If
Next i
End Function

Private Function GetProcessInfo(ModName As String, PID As Long) As Long
Dim hProcess As Long
Dim lResult As Long
Dim cb As Long, cbNeeded As Long
Dim Modules(0 To 199) As Long
Dim ModuleName As String
Dim nbModule As Long
Dim pmc As PROCESS_MEMORY_COUNTERS

GetProcessInfo = 0
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ,
0, PID)
If hProcess Then
cb = 200
lResult = EnumProcessModules(hProcess, Modules(0), 200, cbNeeded)
If lResult <> 0 Then
ModuleName = Space(MAX_PATH)
cb = 500
lResult = GetModuleFileNameExA(hProcess, Modules(0), ModuleName, cb)
ModuleName = Left(ModuleName, lResult)
ModuleName = Mid(ModuleName, InStrRev(ModuleName, "") + 1)
pmc.cb = Len(pmc)
lResult = GetProcessMemoryInfo(hProcess, pmc, pmc.cb)
If UCase(ModName) = UCase(ModuleName) Then
GetProcessInfo = PID
End If
End If
CloseHandle hProcess
End If
End Function


--

Cordialement,

Jacques.
Avatar
Jacques93
Bonjour Driss Hanib,
Driss HANIB a écrit :
Bonjour Jacques

tu fais ton petit cachotier ? ;o))



Non, non ;-) , mais tu verras qu'avec les déclarations de types, de
constantes, et de fonctions, le code s'alourdit quelque peu. Il est
souvent plus simple de procéder par étapes, et de tester au fur et à mesure.

De plus, dans l'utilisation que j'en ai fait, les déclarations ne sont
pas toutes dans le même module, les fenêtres d'un côté, les processus
d'un autre, etc...

Pour ne pas être accusé de rétention d'information ;-) , j'ai laissé
dans le code le test sur SM_REMOTESESSION qui permet de savoir si le
programme tourne dans une session 'Terminal Server', auquel cas
App.Previnstance n'est pas pertinent, plusieurs process 'clients'
identiques pouvant tourner sur le même serveur.

Code posté sur ton message de demande de valeurs de constantes

--

Cordialement,

Jacques.