0

There are two exes/programs which perform tasks based on command line arguments, let's call it Profile. Users can run multiple instances of these exes with specific Profiles.

We have come to a situation that if one exe is running with a profile, the other one should not be running with the same profile or one exe can not be instantiated twice with same profile. And for this, we need to identify running processes with same profile.

One possibility would be to log but it may fail if the process/exe is terminated.

Edit

I need to inform the user by showing him/her the process' window/form title. Since my exes are form based so every process will have its own title based on the profile. WMI based solution, posted as my answer below, can only find processes with their file name, WMI does not have any functionality to list window/form title, so i decided to use Win32 API.

I used Code sample from this post because i have PID only but not the handle. The code sample is returning no title. I checked with following steps

  1. No title was returned when ProcessID from WMI was passed.
  2. Correct title of the parent exe was returned when ParentProcessID from WMI was passed.
  3. Correct title of the child exe was returned when form.hwnd was passed from another exe.

It looks the code sample is not working for a child exe (my exes are called from another exe i.e. my exes are made child and the caller is parent). The handle found by EnumCallBack is not equal to the form's hWnd and that is why it is not returning the title.

bjan
  • 2,000
  • 7
  • 32
  • 64

4 Answers4

1

Thanks to MC ND for identifying WMI to be used and Evripidis

Here is a sample code to get command line arguments of all processes

    Dim objWMIService, objProcess, colProcess
    Dim strComputer, strList

    strComputer = "."

    Set objWMIService = GetObject("winmgmts:" & _
          "{impersonationLevel=impersonate}!\\" & _
           strComputer & "\root\cimv2")

    Set colProcess = objWMIService.ExecQuery("Select * from Win32_Process")

    For Each objProcess In colProcess
        Debug.Print objProcess.Name & " | " & objProcess.CommandLine
    Next

The query to get processes which have been passed ABCD in their command line arguments would be

Set colProcess = objWMIService.ExecQuery("Select * from Win32_Process " & _
                                         " Where CommandLine Like ""%ABCD%"" ")

To read more

Edit:

To get window/form title from ProcessID, i used source code from below article by Evripidis

Get window handlle (hWnd) from process ID

Here is the central idea

  1. Store all handles pertaining to a PID
  2. Get the text of the handle which is Visible and Top Level Window On Taskbar
Community
  • 1
  • 1
bjan
  • 2,000
  • 7
  • 32
  • 64
0

Ask WMI

wmic process get name,commandline
MC ND
  • 69,615
  • 8
  • 84
  • 126
0

Normally a single instance program creates a mutex. If it can then no other instance is running. If it can't it switches to previous instance's window and then exits.

In vb6 the app object has a PrevInstance property, although this will only alert you that you need to check the parameters.

So create a mutex with same name as your profile.

CreateMutex
The CreateMutex function creates or opens a named or unnamed mutex object. 

HANDLE CreateMutex(
  LPSECURITY_ATTRIBUTES lpMutexAttributes,  // SD
  BOOL bInitialOwner,                       // initial owner
  LPCTSTR lpName                            // object name
);
phd443322
  • 493
  • 3
  • 4
0

Here's some code (without declerations and it's from a form so you need to edit it). It lists everything about every window on the system.

It outputs like this (into a rtfedit). Level is the number of indents to show parent child relationships.

Order   Level   WindowText  ClassName   HWnd    ParentHWnd  ProcessID   ParentProcessID ThreadID    ModuleNameHWin  EXENameProcess
1   0   «No Window Text 0»  msvb_lib_tooltips   133318  133318  1648    3876    1944    C:\Windows\system32\MSCOMCTL.OCX    VB6.EXE
2   0   «No Window Text 0»  msvb_lib_tooltips   133316  133318  1648    3876    1944    C:\Windows\system32\MSCOMCTL.OCX    VB6.EXE
3   0   «No Window Text 0»  msvb_lib_tooltips   198898  133318  1648    3876    1944    C:\Windows\system32\MSCOMCTL.OCX    VB6.EXE
4   0   «No Window Text 0»  msvb_lib_tooltips   133276  133318  1648    3876    1944    C:\Windows\system32\MSCOMCTL.OCX    VB6.EXE
5   0   «No Window Text 0»  tooltips_class32    8585400 133318  1648    3876    1944    C:\Program Files\Microsoft Visual Studio\VB98\vb6.exe   VB6.EXE
6   0   «No Window Text 0»  tooltips_class32    7341130 133318  1648    3876    1944    C:\Program Files\Microsoft Visual Studio\VB98\vb6.exe   VB6.EXE
7   0   MSCTFIME UI MSCTFIME UI 8127794 133318  3356    1500    3240    C:\Program Files\Microsoft Visual Studio\VB98\vb6.exe   explorer.exe
8   0   Default IME IME 721516  133318  3356    1500    3240    «Not Available Error=0» explorer.exe
9   0   «No Window Text 0»  tooltips_class32    7340808 133318  3356    1500    3240    «Not Available Error=0» explorer.exe
10  0   «No Window Text 0»  ThumbnailStackClass 4457892 133318  3356    1500    3240    «Not Available Error=0» explorer.exe

This is the code

Sub mnuInsertWindowList_Click()
'    WindowChain = 0
    Dim hwnd As Long
        hwnd = GetTopWindow(0)
        If hwnd <> 0 Then
            AddChildWindows hwnd, 0
        End If
End Sub


Private Function AddChildWindows(ByVal hwndParent As Long, ByVal Level As Long) As String
      Dim gwfnhwnd As Long, X As Long, WT As String, CN As String, Length As Long, hwnd As Long, TID As Long, PID As Long, MN As String, Ret As Long, Parenthwnd As Long
        Static Order As Long
        Static FirstTime As Long
        Parenthwnd = hwndParent
        If Level = 0 Then
                        hwnd = hwndParent
        Else
            hwnd = GetWindow(hwndParent, GW_CHILD)
        End If
        Do While hwnd <> 0
                 WT = Space(512)
                  Length = GetWindowText(hwnd, WT, 508)
                  WT = Left$(WT, Length)
                  If WT = "" Then WT = Chr(171) & "No Window Text " & Err.LastDllError & Chr(187)
                  CN = Space(512)
                  Length = GetClassName(hwnd, CN, 508)
                  CN = Left$(CN, Length)
                  If CN = "" Then CN = "Error=" & Err.LastDllError


                  TID = GetWindowThreadProcessId(hwnd, PID)

                  MN = Space(512)
                  Length = GetWindowModuleFileName(hwnd, MN, 508)
                  If Length = 0 Then
                    MN = Chr(171) & "Not Available Error=" & Err.LastDllError & Chr(187)
                  Else
                    MN = Left$(MN, Length)
                  End If


                 Dim f As Long, sname As String, PList As String, PPID As Long
                 Dim hSnap As Long, proc As PROCESSENTRY32, Temp As String
                 hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
                 If hSnap = hNull Then Exit Function
                 proc.dwSize = LenB(proc)
                 ' Iterate through the processes
                 f = Process32First(hSnap, proc)
                 Do
                        If PID = proc.th32ProcessID Then
                            sname = StrZToStr(proc.szExeFile)
                            PPID = proc.th32ParentProcessID
                        End If
                        f = Process32Next(hSnap, proc)
                 Loop While f = 1
                  Order = Order + 1
'                  CStr(Order) & " HWnd=" & FormatNumber$(hwnd, 0, vbFalse, vbFalse, vbFalse) & " Parent HWnd=" & FormatNumber$(Parenthwnd, 0, vbFalse, vbFalse, vbFalse) & " Level=" & CStr(Level) & WT & " (" & CN & ")" & " PID=" & FormatNumber$(PID, 0, vbFalse, vbFalse, vbFalse) & " TID=" & FormatNumber$(TID, 0, vbFalse, vbFalse, vbFalse) & " Module Name:" & MN & " ExeName:" & sname & vbCrLf
                If FirstTime = 0 Then
                    txtNote.SelText = vbCrLf & "Order" & vbTab & "Level" & vbTab & "WindowText" & vbTab & "ClassName" & vbTab & "HWnd" & vbTab & "ParentHWnd" & vbTab & "ProcessID" & vbTab & "ParentProcessID" & vbTab & "ThreadID" & vbTab & "ModuleNameHWin" & vbTab & "EXENameProcess"
                    FirstTime = 1
                End If
                txtNote.SelText = vbCrLf & CStr(Order) & vbTab & CStr(Level) & vbTab & WT & vbTab & CN & vbTab & CStr(hwnd) & vbTab & CStr(Parenthwnd) & vbTab & CStr(PID) & vbTab & CStr(PPID) & vbTab & CStr(TID) & vbTab & MN & vbTab & sname

                  AddChildWindows hwnd, Level + 1
                  hwnd = GetWindow(hwnd, GW_HWNDNEXT)
        Loop
      End Function

These are the declarations.

Public Declare Function GetTopWindow Lib "user32" (ByVal hwnd As Long) As Long
Public Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Public Declare Function GetWindowModuleFileName Lib "user32" Alias "GetWindowModuleFileNameA" (ByVal hwnd As Long, ByVal WinModule As String, StringLength As Long) As Long
Public Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Public Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Public Declare Function IsUserAnAdmin Lib "Shell32" () As Boolean

  Private Declare Function Process32Next Lib "kernel32" (ByVal hSnapshot As Long, lppe As PROCESSENTRY32) As Long
  Private Declare Function CloseHandle Lib "Kernel32.dll" (ByVal Handle As Long) As Long
  Private Declare Function Process32First Lib "kernel32" (ByVal hSnapshot As Long, lppe As PROCESSENTRY32) As Long
 Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" (ByVal dwFlags As Long, ByVal th32ProcessID As Long) As Long

 Private Declare Function GetVersionExA Lib "kernel32" _
     (lpVersionInformation As OSVERSIONINFO) As Integer



  Private Type PROCESSENTRY32
     dwSize As Long
     cntUsage As Long
     th32ProcessID As Long           ' This process
     th32DefaultHeapID As Long
     th32ModuleID As Long            ' Associated exe
     cntThreads As Long
     th32ParentProcessID As Long     ' This process's parent process
     pcPriClassBase As Long          ' Base priority of process threads
     dwFlags As Long
     szExeFile As String * 260 ' MAX_PATH
  End Type

  Private Type OSVERSIONINFO
     dwOSVersionInfoSize As Long
     dwMajorVersion As Long
     dwMinorVersion As Long
     dwBuildNumber As Long
     dwPlatformId As Long           '1 = Windows 95 2 = Windows NT
     szCSDVersion As String * 128
  End Type

   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
  'STANDARD_RIGHTS_REQUIRED Or SYNCHRONIZE Or &HFFF
   Private Const PROCESS_ALL_ACCESS = &H1F0FFF
   Private Const TH32CS_SNAPPROCESS = &H2&
   Private Const hNull = 0
   Private Const GW_CHILD = 5
   Private Const GW_HWNDNEXT = 2
phd443322
  • 493
  • 3
  • 4