>I see no way to detect the CurDir of a given process
This was the main obstacle. With some IPC techniques I was able to do it, in the following program.
The logic is a bit complicated, but I have tried to document the code as much as possible.
The following code goes in the form.
___
[tt]
Option Explicit
Private Sub Form_Load()
'Copy the path of the current directory to Path() array
CopyMemory Path(0), ByVal CStr(CurDir$ & String(MAX_PATH, 0)), MAX_PATH
'Make 32-bit user data of this window point to Path() array
SetWindowLong hwnd, GWL_USERDATA, VarPtr(Path(0))
'Show the current directory
AutoRedraw = True
Print "CurDir:" & vbLf & CurDir$
If CheckCurDirInPreviousInstances Then
MsgBox "Instance already running over this folder" & vbLf & CurDir$
Unload Me
End If
End Sub
'Returns true if a previous instance of this app
'(if present) has the same CurDir as this instance
Function CheckCurDirInPreviousInstances() As Boolean
Dim hSnapShot As Long, pe32 As PROCESSENTRY32
Dim myPid As Long, myPath As String
'Get current Pid
myPid = GetCurrentProcessId
'Get full path of the current process
myPath = GetPathFromPid(myPid)
'Enumerate processes
hSnapShot = CreateToolhelpSnapshot(TH32CS_SNAPPROCESS, ByVal 0)
pe32.dwSize = Len(pe32)
ProcessFirst hSnapShot, pe32
Do
'Don't check this very instance
If pe32.th32ProcessID <> myPid Then
'If the process (pe32) is a previous instance of this
'application then both processes will have the same path
If StrComp(myPath, GetPathFromPid(pe32.th32ProcessID), vbTextCompare) = 0 Then
Dim lpPath As Long, hProcess As Long, S As String * MAX_PATH
'Get a pointer to Path() array in that (previous) instance
lpPath = GetPathAddressFromPid(pe32.th32ProcessID)
'If a valid pointer
If lpPath Then
'Read the Path (CurDir) from that (previous) instance
hProcess = OpenProcess(PROCESS_VM_READ, 0, pe32.th32ProcessID)
ReadProcessMemory hProcess, ByVal lpPath, ByVal S, Len(S), ByVal 0&
CloseHandle hProcess
'If both instances have the same CurDir
If InStr(1, S, CurDir$ & vbNullChar, vbTextCompare) = 1 Then
CheckCurDirInPreviousInstances = True 'Return True
Exit Do
End If
End If
End If
End If
Loop While ProcessNext(hSnapShot, pe32)
CloseHandle hSnapShot
End Function
'Returns the pointer of Path() array in another
'instance of this application identified by Pid.
Function GetPathAddressFromPid(Pid As Long) As Long
Dim lParam As Long
lParam = Pid 'this Pid be used as lParam in EnumWindowsProc
EnumWindows AddressOf EnumWindowsProc, lParam
If lParam = Pid Then
'lParam was not modified by EnumWindowsProc
'which means no valid address was found.
GetPathAddressFromPid = 0 'return a NULL pointer
Else
'lParam contains a valid address to Path() array
'in other instance identified by Pid.
GetPathAddressFromPid = lParam 'return the address
End If
End Function[/tt]
___
This goes in a module.
___
[tt]
Option Explicit
Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Declare Function CreateToolhelpSnapshot Lib "kernel32" Alias "CreateToolhelp32Snapshot" (ByVal lFlags As Long, ByVal lProcessId As Long) As Long
Declare Function ProcessFirst Lib "kernel32" Alias "Process32First" (ByVal hSnapShot As Long, uProcess As PROCESSENTRY32) As Long
Declare Function ProcessNext Lib "kernel32" Alias "Process32Next" (ByVal hSnapShot As Long, uProcess As PROCESSENTRY32) As Long
Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Declare Function GetModuleFileNameEx Lib "psapi" Alias "GetModuleFileNameExA" (ByVal hProcess As Long, ByVal hModule As Long, ByVal lpFilename As String, ByVal nSize As Long) As Long
Declare Function GetCurrentProcessId Lib "kernel32" () As Long
Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, lParam As Long) As Long
Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Declare Sub CloseHandle Lib "kernel32" (ByVal hObject As Long)
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Public Const TH32CS_SNAPPROCESS = 2&
Public Const GWL_WNDPROC = -4&
Public Const GWL_USERDATA = -21&
Public Const PROCESS_QUERY_INFORMATION = &H400&
Public Const PROCESS_VM_READ = &H10&
Public Const MAX_PATH = 260&
Type PROCESSENTRY32
dwSize As Long
cntUsage As Long
th32ProcessID As Long
th32DefaultHeapID As Long
th32ModuleID As Long
cntThreads As Long
th32ParentProcessID As Long
pcPriClassBase As Long
dwFlags As Long
szExeFile As String * MAX_PATH
End Type
Public Path(MAX_PATH) As Byte
'This function checks if the window (hwnd) being enumerated belongs to the
'process (Pid=lParam). If so, its user data value is queried which holds
'the pointer to Path() array in remote process (Pid=lParam). This pointer
'is returned to the calling funtion by assigning to ByRef lParam parameter.
Function EnumWindowsProc(ByVal hwnd As Long, lParam As Long) As Long
Dim ThisPid As Long, UserData As Long
EnumWindowsProc = -1 'Keep looking
'Get the Pid of this window
GetWindowThreadProcessId hwnd, ThisPid
'lParam has the Pid of the process being investigated
'If the window belongs to this process
If ThisPid = lParam Then
'get its user data value
UserData = GetWindowLong(hwnd, GWL_USERDATA)
'if user data is non-zero...
If UserData Then
'This is a pointer to Path() array in the other process
'set lParam to this value. This change will be reflected in
'calling procedure GetPathAddressFromPid from where
'EnumWindows is called (lParam is passed ByRef)
lParam = UserData
'Stop enumeration
EnumWindowsProc = 0
End If
End If
End Function
'returns the full path of a process identified by Pid.
Function GetPathFromPid(ByVal Pid As Long) As String
Dim hProcess As Long, S As String * MAX_PATH
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, 0, Pid)
GetModuleFileNameEx hProcess, 0, S, MAX_PATH
CloseHandle hProcess
GetPathFromPid = Left$(S, InStr(S, vbNullChar) - 1)
End Function[/tt]
___
Don't run this program in IDE. Because the code
myPath = GetPathFromPid(myPid) will return the path of VB6.EXE instead of the application path.
Compile the program, make several shortcuts to it and specify different working directories in shortcut properties.
Try invoking shortcuts in different sequences. For me, the results were fine.
Luckily, various parts of the code used in this program were copy-pasted from previous threads in this forum. I just arranged the pieces together to solve the puzzle.
![[smile] [smile] [smile]](/data/assets/smilies/smile.gif)