Hi Danuta (and Padgett if you haven't already modified this),
I played around with the Padgett's code you posted and came up with some
mods of my own. My version does just about list everything, although its
still a far cry from Windows Task Manager, with more time and effort it can
certainly be matured and refined. Maybe one of the gurus on this list will
take it from here and fix/add/refine/etc... (assuming something like this
isn't already out there :-) ).
Anyway I believe I have gotten it past what appeared to be troubling it
before, so give it a try.
hth
Jack
''''''''''''''
''' Stuff this into the form:
Option Explicit
Const PROCESS_TERMINATE = &H1
Const PROCESS_CREATE_THREAD = &H2
Const PROCESS_VM_OPERATION = &H8
Const PROCESS_VM_READ = &H10
Const PROCESS_VM_WRITE = &H20
Const PROCESS_DUP_HANDLE = &H40
Const PROCESS_CREATE_PROCESS = &H80
Const PROCESS_SET_QUOTA = &H100
Const PROCESS_SET_INFORMATION = &H200
Const PROCESS_QUERY_INFORMATION = &H400
'
'
'
Private Sub cmdExit_Click()
Unload Me
End Sub
Private Sub Form_Load()
Dim plist As New CProcessInfo
Dim j As Integer
Dim num As Long
Dim sListText As String
Dim itmX As ListItem
'initialize the list view
Call InitListView
num = plist.GetProcessesList
For j = 0 To num - 1
plist.ProcessID = plist.ListProcessID(j)
Select Case plist.ProcessID
Case 0:
Debug.Print plist.ProcessName, plist.ProcessID,
plist.ProcessPath, "System Idle Process"
' Set itmX = ListView1.ListItems.Add(, ,
CStr(plist.ProcessID))
' itmX.SubItems(1) = plist.ProcessPath
' itmX.SubItems(2) = "System Idle Process"
Case 2:
Debug.Print plist.ProcessName, plist.ProcessID,
plist.ProcessPath, "System"
' Set itmX = ListView1.ListItems.Add(, ,
CStr(plist.ProcessID))
' itmX.SubItems(1) = plist.ProcessPath
' itmX.SubItems(2) = "System"
Case Else:
Debug.Print plist.ProcessName, plist.Visible,
plist.WindowTitle
If plist.Enabled And plist.IsAWindow Then
If plist.WindowTitle <> "" Then
Set itmX = ListView1.ListItems.Add(, ,
CStr(plist.ProcessID))
itmX.SubItems(1) = plist.ProcessName
itmX.SubItems(2) = plist.WindowTitle
End If
End If
End Select
Next j
End Sub
'
'
'
Private Sub InitListView()
' Set View property to Report.
ListView1.View = lvwReport
ListView1.ColumnHeaders. _
Add , , "ProcessID", ListView1.Width / 5
ListView1.ColumnHeaders. _
Add , , "Process Name", ListView1.Width * 2 / 5, _
lvwColumnCenter
ListView1.ColumnHeaders. _
Add , , "Window Title", ListView1.Width * 2 / 5
End Sub
'''''''************************
' Stuff this into the class
Option Explicit
'CProcessInfo
Private Const PROCESS_QUERY_INFORMATION = 1024
Private Const PROCESS_VM_READ = 16
Private Const MAX_PATH = 260
Private Const STANDARD_RIGHTS_REQUIRED = &HF0000
Private Const SYNCHRONIZE = &H100000
Private Const PROCESS_ALL_ACCESS = &H1F0FFF
Private Const GW_HWNDFIRST = 0
Private Const GW_HWNDLAST = 1
Private Const GW_HWNDNEXT = 2
Private Const GW_HWNDPREV = 3
Private Const GW_OWNER = 4
Private Const GW_CHILD = 5
Private Const GW_MAX = 5
Private Declare Function EnumProcesses Lib "psapi.dll" (ByRef lpidProcess As
Long, ByVal cb As Long, ByRef cbNeeded As Long) As Long
Private Declare Function OpenProcess Lib "Kernel32.dll" (ByVal
dwDesiredAccessas As Long, ByVal bInheritHandle As Long, ByVal dwProcId As
Long) As Long
Private Declare Function CloseHandle Lib "Kernel32.dll" (ByVal Handle As
Long) As Long
Private Declare Function GetModuleBaseName Lib "psapi.dll" (ByVal hProcess
As Long, ByVal hModule As Long, ByVal BaseName As String, ByVal nSize 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 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 FindWindow Lib "user32" Alias "FindWindowA" (ByVal
lpClassName As Long, ByVal lpWindowName As Long) As Long
Private Declare Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal
hwnd As Long, ByVal wFlag As Long) As Long
Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd
As Long, lpdwProcessId As Long) As Long
Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal
wCmd As Long) As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA"
(ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long)
As Long
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA"
(ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function agGetStringFromLPSTR$ Lib "apigid32.dll" (ByVal
src$)
Private Declare Function IsWindowVisible Lib "user32" (ByVal hwnd As Long)
As Long
Private Declare Function IsWindow Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function IsWindowEnabled Lib "user32" (ByVal hwnd As Long)
As Long
Private m_lProcess() As Long
Private m_lNumProcesses As Long
Private m_lCurrentPID As Long
Private m_strAppName As String
Private m_strPath As String
Private m_strClassName As String
Private m_strWindowTitle As String
Private m_lhwnd As Long
Private m_bolVisible As Boolean
Private m_bolIsWindow As Boolean
Private m_bolEnabled As Boolean
'
'
'
Public Property Get GetProcessesList() As Long
Dim cb As Long
Dim cbNeeded As Long
cb = 1024
Do
cb = cb * 2
ReDim m_lProcess(cb / 4 - 1) As Long
EnumProcesses m_lProcess(0), cb, cbNeeded
If cbNeeded < cb Then Exit Do
Loop
If cbNeeded = 0 Then
m_lNumProcesses = 0
Else
m_lNumProcesses = cbNeeded / 4
ReDim Preserve m_lProcess(m_lNumProcesses - 1)
End If
GetProcessesList = m_lNumProcesses
GetProcessInfo
End Property
Private Sub GetProcessInfo()
On Error GoTo exitsub
Dim hProcess As Long
Dim lProp As Long
'Added this "reset" so values don't carry over to next processid
m_strPath = ""
m_strAppName = ""
m_strClassName = ""
m_strWindowTitle = ""
m_bolVisible = False
m_bolIsWindow = False
m_bolEnabled = False
lProp = PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ Or
PROCESS_ALL_ACCESS
hProcess = OpenProcess(lProp, 0, m_lCurrentPID)
If hProcess = 0 Then GoTo exitsub
Dim lRet As Long
Dim cbNeeded As Long
Dim Modules() As Long
'Retrieve the number of bytes that the array of module handles requires
ReDim Modules(1023)
lRet = EnumProcessModules(hProcess, Modules(0), 1024, cbNeeded)
If lRet = 0 Then GoTo exitsub
ReDim Modules(cbNeeded / 4 - 1)
'Get an array of the module handles for the specified process
lRet = EnumProcessModules(hProcess, Modules(0), cbNeeded, cbNeeded)
If lRet = 0 Then GoTo exitsub
Dim nSize As Long
m_strAppName = Space(MAX_PATH)
nSize = 500
lRet = GetModuleFileNameExA(hProcess, Modules(0), m_strAppName, nSize)
m_strAppName = Left$(m_strAppName, lRet)
'Make sure we close handle
CloseHandle (hProcess)
m_strPath = m_strAppName
lRet = InStrRev(m_strAppName, "\")
If lRet > 0 Then
m_strAppName = Right(m_strPath, Len(m_strPath) - lRet)
m_strPath = Left(m_strPath, lRet)
End If
m_lhwnd = ProcessToWnd(m_lCurrentPID)
m_strWindowTitle = GetTitle(m_lhwnd)
m_strClassName = GetMyClassName(m_lhwnd)
m_bolVisible = CBool(IsWindowVisible(m_lhwnd))
m_bolIsWindow = CBool(IsWindow(m_lhwnd))
m_bolEnabled = CBool(IsWindowEnabled(m_lhwnd))
exitsub:
CloseHandle hProcess
End Sub
'
'
'
Private Function ProcessToWnd(ByVal target_pid As Long) As Long
Dim test_hwnd As Long, test_pid As Long, test_thread_id As Long
'Find the first window
test_hwnd = FindWindow(ByVal 0&, GW_HWNDFIRST)
Do While test_hwnd <> 0
'Check if the window isn't a child
If GetParent(test_hwnd) = 0 Then
'Get the window's thread
test_pid = vbNull
test_thread_id = GetWindowThreadProcessId(test_hwnd, test_pid)
If test_pid = target_pid Then
ProcessToWnd = test_hwnd
Exit Do
End If
End If
'retrieve the next window
test_hwnd = GetNextWindow(test_hwnd, GW_HWNDNEXT)
Loop
End Function
'
'
'
Private Function GetTitle(ByVal hwnd As Long) As String
Dim lnglen As Long
Dim Title As String
Title = String$(255, vbNullChar)
lnglen = Len(Title)
If hwnd > 0 Then
If (GetWindowText(hwnd&, Title, lnglen) > 0) Then
GetTitle = Title
Else
GetTitle = ""
End If
Else
GetTitle = ""
End If
End Function
'
'
'
Private Function GetMyClassName(ByVal hwnd As Long) As String
Dim tbuf$
Dim lnglen As Long
' Initialize space
tbuf$ = String$(255, vbNullChar)
lnglen = Len(tbuf$)
If (GetClassName(hwnd&, tbuf$, lnglen) > 0) Then
GetMyClassName = tbuf$
Else
GetMyClassName = "No Class Name"
End If
End Function
'
'
'
Public Property Get NumProcesses() As Long
NumProcesses = m_lNumProcesses
End Property
'
'
Public Property Get ListProcessID(ByVal index As Long) As Long
ListProcessID = m_lProcess(index)
End Property
'
'
Public Property Let ProcessID(ByVal pid As Long)
m_lCurrentPID = pid
GetProcessInfo
End Property
'
'
Public Property Get ProcessID() As Long
ProcessID = m_lCurrentPID
End Property
'
'
Public Property Get ProcessName() As String
ProcessName = m_strAppName
End Property
'
'
Public Property Get ProcessPath() As String
ProcessPath = m_strPath
End Property
'
'
Public Property Get ProcessHwnd() As Long
ProcessHwnd = m_lhwnd
End Property
'
'
Public Property Get ClassName() As String
ClassName = m_strClassName
End Property
'
'
Public Property Get WindowTitle() As String
WindowTitle = m_strWindowTitle
End Property
'
'
Public Property Get Visible() As Boolean
Visible = m_bolVisible
End Property
'
'
Public Property Get IsAWindow() As Boolean
IsAWindow = m_bolIsWindow
End Property
'
'
Public Property Get Enabled() As Boolean
Enabled = m_bolEnabled
End Property
''**************************************
----- Original Message -----
From: "Danuta Iwaniec" <diwaniec@n...>
To: "professional vb" <pro_vb@p...>
Sent: Thursday, August 15, 2002 1:03 PM
Subject: [pro_vb] Re: Enumerating running applications
> Hi
> I have similar problem with finding window titles of running applications.
> I have found your message searching for info about it. I hope you can help
> me. I would really appreciate it.
>
> Danuta
>
> > Hello all,
> >
> > I have posted something similar to this question before. I have made
> > some headway (yeah!) but am finding a few anomalies that someone may be
> > able to shed some light on.
> >
> > My aim to produce a list of currently running applications, as seen in
> > the 'Windows task manager'.
> >
> > The class listed below was donated by Marco. I have extended it by
> > adding a few extra window related properties.
> >
> > Currently I am looping through all the running processes, getting the
> > window they belong to and "filtering" out the ones that I consider to be
> > NOT applications.
> >
> > The "filters" that are producing unexpected results are 'WindowTitle'
> > and 'Visible'.
> >
> > When I am running outlook, word, notepad and VB I get the following
> > debug output: (I have cut out all 'non application processes')
> >
> > ProcessName Visible WindowTitle
> >
> > OUTLOOK.EXE False
> > WINWORD.EXE True Enumerating running applications - Message -
> > Microsoft Word
> > winamp.exe False Winamp Playlist Editor
> > NOTEPAD.EXE True netsh.txt - Notepad
> > VB6.EXE False
> >
> > AS you can see, the visibility for both outlook and VB are false. I can
> > certainly see them.
> >
> > Outlook shows as having no WindowTitle, though it does, as does VB.
> > I am running two instances of word, one with this email title (as word
> > is my default editor for outlook). The second instance of word, with a
> > new document waiting, is not listed at all.
> >
> > Notepad is the only application that shows true.
> >
> > Does anyone have any clues as to why this behaviour is occurring?
> >
> > Cheers,
> >
> > Padgett
> >
> > 'Code to test:
> > Option Explicit
> >
> > Private Sub Form_Load()
> >
> > Const PROCESS_TERMINATE = &H1
> > Const PROCESS_CREATE_THREAD = &H2
> > Const PROCESS_VM_OPERATION = &H8
> > Const PROCESS_VM_READ = &H10
> > Const PROCESS_VM_WRITE = &H20
> > Const PROCESS_DUP_HANDLE = &H40
> > Const PROCESS_CREATE_PROCESS = &H80
> > Const PROCESS_SET_QUOTA = &H100
> > Const PROCESS_SET_INFORMATION = &H200
> > Const PROCESS_QUERY_INFORMATION = &H400
> >
> > Dim plist As New CProcessInfo
> > Dim j As Integer
> > Dim num As Long
> >
> > num = plist.GetProcessesList
> > For j = 0 To num - 1
> > plist.ProcessID = plist.ListProcessID(j)
> >
> > Select Case plist.ProcessID
> > Case 0
> > Debug.Print plist.ProcessName, plist.ProcessID,
> > plist.ProcessPath, "System Idle Process"
> > Case 2
> > Debug.Print plist.ProcessName, plist.ProcessID,
> > plist.ProcessPath, "System"
> > Case Else
> > Debug.Print plist.ProcessName, plist.Visible,
> > plist.WindowTitle
> > If plist.Enabled And plist.IsAWindow Then
> > lstProcesses.AddItem plist.ProcessName
> > End If
> > End Select
> > Next j
> > End Sub
> >
> > 'CProcessInfo
> > Option Explicit
> >
> > Private Const PROCESS_QUERY_INFORMATION = 1024
> > Private Const PROCESS_VM_READ = 16
> > Private Const MAX_PATH = 260
> > Private Const STANDARD_RIGHTS_REQUIRED = &HF0000
> > Private Const SYNCHRONIZE = &H100000
> > Private Const PROCESS_ALL_ACCESS = &H1F0FFF
> > Private Const GW_HWNDNEXT = 2
> >
> > Private Declare Function EnumProcesses Lib "psapi.dll" (ByRef
> > lpidProcess As Long, ByVal cb As Long, ByRef cbNeeded As Long) As Long
> > Private Declare Function OpenProcess Lib "Kernel32.dll" (ByVal
> > dwDesiredAccessas As Long, ByVal bInheritHandle As Long, ByVal dwProcId
> > As Long) As Long
> > Private Declare Function CloseHandle Lib "Kernel32.dll" (ByVal Handle As
> > Long) As Long
> > Private Declare Function GetModuleBaseName Lib "psapi.dll" (ByVal
> > hProcess As Long, ByVal hModule As Long, ByVal BaseName As String, ByVal
> > nSize 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 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 FindWindow Lib "user32" Alias "FindWindowA"
> > (ByVal lpClassName As Long, ByVal lpWindowName As Long) As Long
> > Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As
> > Long
> > Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal
> > hwnd As Long, lpdwProcessId As Long) As Long
> > Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long,
> > ByVal wCmd As Long) As Long
> > Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA"
> > (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As
> > Long) As Long
> > Private Declare Function GetWindowText Lib "user32" Alias
> > "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal
> > cch As Long) As Long
> > Private Declare Function agGetStringFromLPSTR$ Lib "apigid32.dll" (ByVal
> > src$)
> >
> >
> > Private Declare Function IsWindowVisible Lib "user32" (ByVal hwnd As
> > Long) As Long
> > Private Declare Function IsWindow Lib "user32" (ByVal hwnd As Long) As
> > Long
> > Private Declare Function IsWindowEnabled Lib "user32" (ByVal hwnd As
> > Long) As Long
> >
> > Private m_lProcess() As Long
> > Private m_lNumProcesses As Long
> > Private m_lCurrentPID As Long
> > Private m_strAppName As String
> > Private m_strPath As String
> > Private m_strClassName As String
> > Private m_strWindowTitle As String
> > Private m_lhwnd As Long
> > Private m_bolVisible As Boolean
> > Private m_bolIsWindow As Boolean
> > Private m_bolEnabled As Boolean
> >
> > Public Property Get GetProcessesList() As Long
> >
> > Dim cb As Long
> > Dim cbNeeded As Long
> >
> > cb = 1024
> > Do
> > cb = cb * 2
> > ReDim m_lProcess(cb / 4 - 1) As Long
> > EnumProcesses m_lProcess(0), cb, cbNeeded
> > If cbNeeded < cb Then Exit Do
> > Loop
> >
> > If cbNeeded = 0 Then
> > m_lNumProcesses = 0
> > Else
> > m_lNumProcesses = cbNeeded / 4
> > ReDim Preserve m_lProcess(m_lNumProcesses - 1)
> > End If
> >
> > GetProcessesList = m_lNumProcesses
> > GetProcessInfo
> >
> > End Property
> > Public Property Get NumProcesses() As Long
> > NumProcesses = m_lNumProcesses
> > End Property
> > Public Property Get ListProcessID(ByVal index As Long) As Long
> > ListProcessID = m_lProcess(index)
> > End Property
> > Public Property Let ProcessID(ByVal pid As Long)
> > m_lCurrentPID = pid
> > GetProcessInfo
> > End Property
> > Public Property Get ProcessID() As Long
> > ProcessID = m_lCurrentPID
> > End Property
> > Public Property Get ProcessName() As String
> > ProcessName = m_strAppName
> > End Property
> > Public Property Get ProcessPath() As String
> > ProcessPath = m_strPath
> > End Property
> > Public Property Get ProcessHwnd() As Long
> > ProcessHwnd = m_lhwnd
> > End Property
> > Public Property Get ClassName() As String
> > ClassName = m_strClassName
> > End Property
> > Public Property Get WindowTitle() As String
> > WindowTitle = m_strWindowTitle
> > End Property
> > Public Property Get Visible() As Boolean
> > Visible = m_bolVisible
> > End Property
> > Public Property Get IsAWindow() As Boolean
> > IsAWindow = m_bolIsWindow
> > End Property
> > Public Property Get Enabled() As Boolean
> > Enabled = m_bolEnabled
> > End Property
> > Private Sub GetProcessInfo()
> >
> > On Error GoTo exitsub
> >
> > Dim hProcess As Long
> > Dim lProp As Long
> >
> > m_strPath = ""
> > m_strAppName = ""
> >
> > lProp = PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ
> > hProcess = OpenProcess(lProp, 0, m_lCurrentPID)
> > If hProcess = 0 Then GoTo exitsub
> >
> > Dim lRet As Long
> > Dim cbNeeded As Long
> > Dim Modules() As Long
> >
> > 'Retrieve the number of bytes that the array of module handles
> > requires
> > ReDim Modules(1023)
> >
> > lRet = EnumProcessModules(hProcess, Modules(0), 1024, cbNeeded)
> > If lRet = 0 Then GoTo exitsub
> > ReDim Modules(cbNeeded / 4 - 1)
> > 'Get an array of the module handles for the specified process
> > lRet = EnumProcessModules(hProcess, Modules(0), cbNeeded, cbNeeded)
> > If lRet = 0 Then GoTo exitsub
> >
> > Dim nSize As Long
> >
> > m_strAppName = Space(MAX_PATH)
> > nSize = 500
> > lRet = GetModuleFileNameExA(hProcess, Modules(0), m_strAppName,
> > nSize)
> > m_strAppName = Left$(m_strAppName, lRet)
> >
> > m_strPath = m_strAppName
> > lRet = InStrRev(m_strAppName, "\")
> > If lRet > 0 Then
> > m_strAppName = Right(m_strPath, Len(m_strPath) - lRet)
> > m_strPath = Left(m_strPath, lRet)
> > End If
> >
> > m_lhwnd = ProcessToWnd(m_lCurrentPID)
> > m_strWindowTitle = GetTitle(m_lhwnd)
> > m_strClassName = GetMyClassName(m_lhwnd)
> > m_bolVisible = CBool(IsWindowVisible(m_lhwnd))
> > m_bolIsWindow = CBool(IsWindow(m_lhwnd))
> > m_bolEnabled = CBool(IsWindowEnabled(m_lhwnd))
> >
> > exitsub:
> > CloseHandle hProcess
> > End Sub
> >
> > Private Function ProcessToWnd(ByVal target_pid As Long) As Long
> > Dim test_hwnd As Long, test_pid As Long, test_thread_id As Long
> > 'Find the first window
> > test_hwnd = FindWindow(ByVal 0&, ByVal 0&)
> > Do While test_hwnd <> 0
> > 'Check if the window isn't a child
> > If GetParent(test_hwnd) = 0 Then
> > 'Get the window's thread
> > test_thread_id = GetWindowThreadProcessId(test_hwnd,
> > test_pid)
> > If test_pid = target_pid Then
> > ProcessToWnd = test_hwnd
> > Exit Do
> > End If
> > End If
> > 'retrieve the next window
> > test_hwnd = GetWindow(test_hwnd, GW_HWNDNEXT)
> > Loop
> > End Function
> > Private Function GetTitle(ByVal hwnd As Long) As String
> > Dim tbuf$
> > Dim dl&
> > tbuf$ = String$(256, 0) ' Initialize space again
> > dl& = GetWindowText(hwnd&, tbuf$, 255)
> > tbuf$ = agGetStringFromLPSTR$(tbuf$)
> > GetTitle$ = tbuf$
> > End Function
> >
> >
> > Private Function GetMyClassName(ByVal hwnd As Long) As String
> > Dim tbuf$
> > Dim dl&
> > tbuf$ = String$(256, 0) ' Initialize space again
> > dl& = GetClassName(hwnd&, tbuf$, 255)
> > tbuf$ = agGetStringFromLPSTR$(tbuf$)
> > GetMyClassName = tbuf$ 'desc$
> > End Function
> >
> >
> > ********************
> > Padgett Rowell
> > Aspect7
> > Office: +61 (08) 9202 1433
> > Mobile: 0402 122 419
> >
> >
>
> ---
> Visual C# - A Guide for VB6 Developers
> This book will make it easy to transfer your skills
> from Visual Basic 6 to C#, the language of choice
> of the .NET Framework.
> http://www.wrox.com/ACON11.asp?ISBN=1861007175&p2p0059
>
>