-
Jan 31st, 2023, 10:45 AM
#1
Thread Starter
New Member
App.PrevInstance alternative?
Hi. Is there some alternative to check is more instances run, of same application. I need to make only two instances of application is allowed to run on same user. In VB.NET we can just use Count property of Process/Thread class, but how to achieve same in VB6 ?
Thanks.
-
Jan 31st, 2023, 11:55 AM
#2
Re: App.PrevInstance alternative?
Don’t think there is an easy built in way you will probably have to roll your own. Should be fairly easy but depends on how secure you want to get. Couple random ideas
You could enumerate running processes and count, but they could change exe name
You could register a windows message and send a broadcast on startup and see how many other windows respond. Would require a subclass
You could run a mini license server on a port in one of the processes or as a standalone exe and have each instance require authorization to start
Each instance could save its pid to the registry in start and remove on close, new clients could enumkeys and count after making sure pid was still valid.
-
Jan 31st, 2023, 12:57 PM
#3
Re: App.PrevInstance alternative?
-
Jan 31st, 2023, 01:05 PM
#4
Re: App.PrevInstance alternative?
Or use far memory, shared by all the running programs ... maybe just a little increment/decrement counter of the instances. A semaphore would be pretty easy though.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Jan 31st, 2023, 01:12 PM
#5
Re: App.PrevInstance alternative?
Depends on the actual goal.
Often I've wanted runs of a new instance to detect that they are seconrdary and "inform" the first instance to open another window on another document before terminating. DDE works well for that and the plumbing is already present in VB6 so no need for another dependency, subclassing, or threading stunts.
-
Jan 31st, 2023, 02:22 PM
#6
Re: App.PrevInstance alternative?
DDE works so well that the interwebs are full of horror stories how Excel and the rest of the office apps suddenly cannot be started for no apparent reason. Just DDE in action.
-
Jan 31st, 2023, 05:21 PM
#7
Re: App.PrevInstance alternative?
Odd. I just tested 5 old DDE applications on Windows 11. All last compiled over a decade ago. All of them work exactly as intended.
-
Feb 1st, 2023, 04:17 AM
#8
Re: App.PrevInstance alternative?
Can you post some sample code in CodeBank using DDE wich impl OP alternative i.e. if prev instance is detected running then instruct it to show and focus (or open filename from Command$) otherwise start normally.
I really would like to test it under Terminal Services and admin vs non-admin context. My current PutObject/GetObject approach has some troubles with security boundaries wondering if DDE can be a solution to these.
-
Feb 1st, 2023, 04:40 AM
#9
Re: App.PrevInstance alternative?
This is my solution for a database (sqlite3) which I want my program to run only one time, and inform other users when run again the program from which computer this program run.
Because we can execute a program from a shared folder, the first mutex created in the user pc, and we need another as file mutex, to indicate that this running somewhere else. The class mutex doing the two jobs, create and maintain a mutex and a file mutex. So If we want two instances we have to adjust it accordingly.
Code:
Private Declare Function GetComputerName Lib "kernel32" Alias "GetComputerNameW" (ByVal lpBuffer As Long, nSize As Long) As LongDim mut As New Mutex
Function strMachineName() As String
strMachineName = String(1000, Chr$(0))
GetComputerName StrPtr(strMachineName), 1000
strMachineName = Left$(strMachineName, InStr(1, strMachineName, Chr$(0)) - 1)
End Function
sub main()
If mut.Create("GK00015555543211367") And mut.CreateFileMutex("gk1.bin") Then
Call InitVisualStylesFixes
Splash.Show vbModal
preparebase
If Not Problem Then
ListPicker.Show
Else
CloseAllConnections
End If
Else
On Error Resume Next
Dim k As Long, a$
k = FreeFile
Open AppPath + "user" For Input As k
Line Input #k, a$
Close k
'
MsgBox "Base is open here:" + a$, vbCritical, "bla bla bla"
mut.Destroy
End
End If
End sub
Class Mutex
Code:
Private Const ERROR_ALREADY_EXISTS = 183&
Private Const MUTEX_ALL_ACCESS = &H1F0001
Public LastError As Long
Private Declare Function CreateMutex Lib "kernel32" Alias "CreateMutexW" (ByVal lpMutexAttributes As Long, ByVal bInitialOwner As Long, ByVal lpName As Long) As Long
Private Declare Function ReleaseMutex Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private mutname As String, myMutex As Variant
Private filenum As Long, fname$
Function CreateFileMutex(Name$) As Boolean
If filenum > 0 Then Exit Function
filenum = FreeFile
On Error GoTo 5000
Open AppPath + Name$ For Binary Lock Read Write As filenum
fname$ = AppPath + Name$
CreateFileMutex = True
Dim k As Long
k = FreeFile
Open AppPath + "user" For Output As k
Print #k, strMachineName
Close k
Exit Function
5000
filenum = 0
End Function
Function Create(Name$) As Boolean
If mutname <> "" Then Exit Function
myMutex = CVar(CreateMutex(0, 1, StrPtr(Name$)))
LastError = Err.LastDllError
If LastError = ERROR_ALREADY_EXISTS Then CloseHandle myMutex
If LastError = 0 Then mutname = Name$
Create = LastError = 0
End Function
Sub Destroy()
If myMutex = 0 Then Exit Sub
CloseHandle CLng(myMutex)
myMutex = 0
mutname = ""
End Sub
Private Sub Class_Terminate()
If filenum > 0 Then
Close filenum
On Error Resume Next
KillFile fname$
End If
If mutname = "" Then Exit Sub
CloseHandle CLng(myMutex)
End Sub
-
Feb 1st, 2023, 07:16 AM
#10
Thread Starter
New Member
Re: App.PrevInstance alternative?
Thanks you all, for answers and recommendations,even code share.
General goal is to limit opening application more than 2 times on same user, but i will parametrize that count, if users decide to give someone more instances.
I will first check Semaphore approach, i already read about Mutex stuff, but what i want to avoid is making file in app folder that contain some data like PC Name and maybe number of current instances, because if application suddenly crash, that will not change.
I will also look in georgekar source, but i see that there is local file that contains some info that i suppose about instances.
Is there some example how to propretly use CreateSemaphoreA Function, and how to get count of semaphore objects ?
Thanks
-
Feb 1st, 2023, 08:04 AM
#11
Re: App.PrevInstance alternative?
another way, kind of a hack is:
Code:
Private Sub Form_Load()
MsgBox AppInstances(Me.hWnd)
End Sub
in a module:
Code:
Private Declare Function FindWindow& Lib "user32" Alias "FindWindowA" (ByVal lpClassName$, ByVal lpWindowName$)
Private Declare Function GetWindow Lib "user32.dll" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
Private Declare Function GetParent Lib "user32" (ByVal hWnd 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 GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hWnd As Long) As Long
Private Declare Function GetModuleFileName Lib "kernel32" Alias "GetModuleFileNameA" (ByVal hModule As Long, ByVal lpFileName As String, ByVal nSize As Long) As Long
Private Declare Function GetWindowWord Lib "user32" (ByVal hWnd As Long, ByVal nIndex As Long) As Integer
Private Function GetModuleName$(ByVal hWnd&)
Dim ModuleName$, ln&
ModuleName = Space$(255)
ln = GetModuleFileName(GetWindowWord(hWnd, -6), ModuleName, Len(ModuleName))
GetModuleName = Left$(ModuleName, ln)
End Function
Private Function GetCaption$(ByVal hWnd&)
Dim tl&
tl = GetWindowTextLength(hWnd)
GetCaption = Space$(tl)
GetWindowText hWnd, GetCaption, tl + 1
End Function
Function AppInstances&(hWnd&)
Dim mName$, mCaption$
mName = GetModuleName(hWnd)
mCaption = GetCaption(hWnd)
hWnd = FindWindow(vbNullString, vbNullString)
Do While hWnd <> 0
If GetParent(hWnd) = 0 Then
If GetCaption(hWnd) = mCaption Then
If GetModuleName(hWnd) = mName Then AppInstances = AppInstances + 1
End If
End If
hWnd = GetWindow(hWnd, 2)
Loop
End Function
this will tell the amount of instances.
but it will only work if:
all instances have the same "title" and they are started from the same file-location.
-
Feb 1st, 2023, 08:24 AM
#12
Re: App.PrevInstance alternative?
This is an example without error checking:
Code:
Option Explicit
Private Declare Function CreateSemaphore Lib "kernel32" _
Alias "CreateSemaphoreW" ( _
ByRef lpSemaphoreAttributes As Any, _
ByVal lInitialCount As Long, _
ByVal lMaximumCount As Long, _
ByVal lpName As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" ( _
ByVal hObject As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" ( _
ByVal hHandle As Long, _
ByVal dwMilliseconds As Long) As Long
Private Declare Function ReleaseSemaphore Lib "kernel32" ( _
ByVal hSemaphore As Long, _
ByVal lReleaseCount As Long, _
ByRef lpPreviousCount As Long) As Long
Private m_hSem As Long
Private Sub Form_Load()
' // Select am arbitrary unique name for semaphore
' // 2 - instances max
m_hSem = CreateSemaphore(ByVal 0&, 0, 2, StrPtr("MY_SEMAPHORE_INST"))
If ReleaseSemaphore(m_hSem, 1, 0) = 0 Then
CloseHandle m_hSem
m_hSem = 0
Unload Me
End If
End Sub
Private Sub Form_Unload(Cancel As Integer)
If m_hSem Then
WaitForSingleObject m_hSem, -1
CloseHandle m_hSem
End If
End Sub
-
Feb 1st, 2023, 08:45 AM
#13
Re: App.PrevInstance alternative?
Here is a link to a CodeBank entry with the DDE approach suggested by dilletante above:
Using DDE to check for previous instance
No API used.
cheers,
</wqw>
-
Feb 1st, 2023, 09:46 AM
#14
Re: App.PrevInstance alternative?
There are two files, one of them is Locked. If your app crash, this unlocked by the OS. The other file just write the name of the PC for information only. You may skip this file.
Only one app can lock a file, until this app release the file handler. All file handlers released automatic from OS when an application exit (normal or by a crash).
Only if your program get in an endless loop hold the lock. You can use task manager to terminate the program and the lock released.
Using two files you have space for two instances.
The folder may not be in your application folder but in temporary folder.
Last edited by georgekar; Feb 1st, 2023 at 09:52 AM.
-
Feb 1st, 2023, 10:43 AM
#15
Re: App.PrevInstance alternative?
Originally Posted by wqweto
Here is a link to a CodeBank entry with the DDE approach...
You got to it before I did. Does your testing show it to be viable? You had concerns earlier.
-
Feb 1st, 2023, 11:22 AM
#16
Re: App.PrevInstance alternative?
This DDE stuff is pretty cool (never knew it was there, guess I always skipped over those "Link" properties in forms and controls), although I can't think of any practical use for it. All MSDN has to say about it in terms of examples is how to link to Excel and read data from cells.
The semaphore method is pretty slick as well (love it when I learn something new). That "ReleaseSemaphore" function is pretty counter-intuitive haha, MSDN says it increases the semaphore count instead of decreasing it as its name would suggest!
-
Feb 1st, 2023, 11:31 AM
#17
Re: App.PrevInstance alternative?
Originally Posted by dilettante
You got to it before I did. Does your testing show it to be viable? You had concerns earlier.
Will test it later. In production :-)) Because most weird things happen only there :-))
Originally Posted by VanGoghGaming
This DDE stuff is pretty cool (never knew it was there, guess I always skipped over those "Link" properties in forms and controls), although I can't think of any practical use for it.
Me neither, frankly!
I never knew that setting LinkMode property on a form *adds new events* like Form_LinkExecute for instance.
cheers,
</wqw>
-
Feb 1st, 2023, 11:41 AM
#18
Re: App.PrevInstance alternative?
Maybe start at About Dynamic Data Exchange which provides an overview and general discussion of what it was built for.
-
Feb 1st, 2023, 11:45 AM
#19
Re: App.PrevInstance alternative?
Originally Posted by wqweto
Seems it has no sync so you can run sevaral copies. Just run such batch file:
Code:
start MyApp.exe
start MyApp.exe -123124
start MyApp.exe -2345235
start MyApp.exe -546456
start MyApp.exe -54745
-
Feb 1st, 2023, 11:51 AM
#20
Re: App.PrevInstance alternative?
Originally Posted by VanGoghGaming
This DDE stuff is pretty cool (never knew it was there, guess I always skipped over those "Link" properties in forms and controls), although I can't think of any practical use for it. All MSDN has to say about it in terms of examples is how to link to Excel and read data from cells.
The semaphore method is pretty slick as well (love it when I learn something new). That "ReleaseSemaphore" function is pretty counter-intuitive haha, MSDN says it increases the semaphore count instead of decreasing it as its name would suggest!
Semaphore/mutex/event (and some others) has atomic access so you have no synchronization issues with those kernel objects. When you call ReleaseSemaphore/WaitForSingleObject the counter is changed atomically even if the function is called simultaneously from several threads.
-
Feb 1st, 2023, 11:53 AM
#21
Re: App.PrevInstance alternative?
Originally Posted by dilettante
Nice! But fast forward to 2017 and Microsoft disables DDE even in Excel because the technology is so legacy, it has been superseded by OLE which itself is a legacy stuff but still supported by Office (the only way) for communicating data to/from other applications.
cheers,
</wqw>
-
Feb 1st, 2023, 12:36 PM
#22
Re: App.PrevInstance alternative?
MS Office applications are notorious malware vectors. Almost as bad as the "Power" Shell scripting platform. As far as I can tell DDE blocks were implemented for them but I fail to see any issue for VB6 applications. Most people aren't blindly downloading and running them en masse unlike Excel workbooks and such.
-
Feb 1st, 2023, 06:07 PM
#23
Re: App.PrevInstance alternative?
Originally Posted by The trick
Semaphore/mutex/event (and some others) has atomic access so you have no synchronization issues with those kernel objects. When you call ReleaseSemaphore/WaitForSingleObject the counter is changed atomically even if the function is called simultaneously from several threads.
What exactly do you mean by "atomic access" and "changed atomically"?
-
Feb 1st, 2023, 06:45 PM
#24
Re: App.PrevInstance alternative?
Originally Posted by VanGoghGaming
What exactly do you mean by "atomic access" and "changed atomically"?
Won't let a Windows process timeslice interrupt until it's done.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Feb 2nd, 2023, 03:53 AM
#25
Re: App.PrevInstance alternative?
Originally Posted by VanGoghGaming
What exactly do you mean by "atomic access" and "changed atomically"?
As i've already wrote you can see sync issues here. When you have several threads which can change shared data you should ensure the atomic access. You could for example also use shared memory and an interlocked function and count instances.
-
Feb 2nd, 2023, 06:32 AM
#26
Re: App.PrevInstance alternative?
Originally Posted by The trick
As i've already wrote you can see sync issues here. When you have several threads which can change shared data you should ensure the atomic access. You could for example also use shared memory and an interlocked function and count instances.
Just fixed the CodeBank submission to work with the test .bat file but still without App.PrevInstance.
The fix is not ideal but good enough for my purposes.
cheers,
</wqw>
-
Feb 2nd, 2023, 06:32 AM
#27
Thread Starter
New Member
Re: App.PrevInstance alternative?
Your guys are amazing!. Thanks you so much. I learned something new, really i never used DDE, but now there is opportunity to give a try and to read some articles about it. For this situation i already go with Semaphore solution, finally i use The trick source, because i make it more complicated and The trick source is pretty straightforward.
I also like baka's code, i will save it for later use and also thanks wqweto for DDE example, and other you who replayed here for potential solutions.
For me this is Resolved
shon3i
-
Feb 2nd, 2023, 06:51 AM
#28
Re: App.PrevInstance alternative?
Originally Posted by wqweto
Just fixed the CodeBank submission to work with the test .bat file but still without App.PrevInstance.
The fix is not ideal but good enough for my purposes.
cheers,
</wqw>
Just i tried your new solution:
-
Feb 2nd, 2023, 07:28 AM
#29
Re: App.PrevInstance alternative?
Originally Posted by The trick
Just i tried your new solution:
This is by design. The simultaneous spawning of several processes is pretty contrived example to begin with which the sample code solves satisfactory for couple of processes (up to 5) not 20 or more you are pushing it way too far.
If you need a 100% solution for multi-spawn just guard pvNotifyPreviousInstance call in Form_Load with If App.PrevInstance Then ... -- works like a charm for 100 processes spawned.
Code:
Private Sub Form_Load()
On Error GoTo EH
LinkTopic = vbNullString
If App.PrevInstance Then
If pvNotifyPreviousInstance(STR_LINK_TOPIC, Command$) Then
Unload Me
Exit Sub
End If
End If
LinkTopic = STR_LINK_TOPIC
pvOpenDocument Command$
Exit Sub
EH:
MsgBox Err.Description, vbCritical, "Form_Load"
End Sub
Unfortunately this defeats the purpose of the submission -- *not* using App.PrevInstance for anything.
cheers,
</wqw>
-
Feb 2nd, 2023, 07:39 AM
#30
Re: App.PrevInstance alternative?
did u even try my "not that smart" solution just using FindWindow & GetModuleFileName?
I mean, its quite simple and should "count" the amount of exe running.
the solution works also:
- u make a copy of the .exe and place it somewhere else. (it seem to use the same modulefilename when u compiled the project)
- u can rename the .exe and it will work as well
yeah, its not anything fancy, but if it works why not?
I mean, the best way for this purpose is:
- count all the instances we have in real-time and give the amount as result.
if we instead use some "holder" like a file or something that "takes" time to initialize, or that can "crash" without removing the count, it can mess things up.
my example could be update to "allow" different titles, like: "my program #1" so we just require "my program" and skip anything else.
if u instead need some kind of network limitations. this will not work.
for that purpose, we need some kind of server or ways to GET/SET a property that all computers can access.
Last edited by baka; Feb 2nd, 2023 at 07:43 AM.
-
Feb 3rd, 2023, 04:50 AM
#31
Thread Starter
New Member
Re: App.PrevInstance alternative?
Originally Posted by baka
did u even try my "not that smart" solution just using FindWindow & GetModuleFileName?
I mean, its quite simple and should "count" the amount of exe running.
the solution works also:
- u make a copy of the .exe and place it somewhere else. (it seem to use the same modulefilename when u compiled the project)
- u can rename the .exe and it will work as well
yeah, its not anything fancy, but if it works why not?
I mean, the best way for this purpose is:
- count all the instances we have in real-time and give the amount as result.
if we instead use some "holder" like a file or something that "takes" time to initialize, or that can "crash" without removing the count, it can mess things up.
my example could be update to "allow" different titles, like: "my program #1" so we just require "my program" and skip anything else.
if u instead need some kind of network limitations. this will not work.
for that purpose, we need some kind of server or ways to GET/SET a property that all computers can access.
Yes i am, and that is what i looking for. Thanks for detailed explanation. I decide to try to both on production, yours and semaphore solution, so i will in future see if some unwanted behaviors are occurred.
-
Feb 3rd, 2023, 11:47 AM
#32
Re: App.PrevInstance alternative?
Originally Posted by VanGoghGaming
What exactly do you mean by "atomic access" and "changed atomically"?
Atomic operations are operations that execute as a single discrete operation on ONLY one thread.
For example take this code:-
That might appear as a single operation but by default it is not atomic. At a low level this would be several operations. It might look something like this in assembly.
Code:
mov ebx, [HEAP_VAR]
inc ebx
mov [HEAP_VAR], ebx
What we actually have are 3 operations. Normally this isn't really a problem but when we involve multiple threads this becomes a big problem. [HEAP_VAR] is a memory address for a variable on the heap, a module level variable perhaps. The first instruction moves the value at that address it into the EBX register. When that happens, it is possible that another thread could write a different value to that address. EBX would contain the old value however, which is then incremented and written back to the address. The problem is that the other thread was expecting the value it wrote to be incremented but because it wrote the value after it was read into the register it doesn't get incremented. What it will instead see is the result of incrementing the previous value.
The solution here to perform an atomic increment. An atomic increment guarantees that the increment operation takes place unmolested by another thread. I don't know how you guys do it in VB6 but in .Net we would use this:-
Code:
Interlocked.Increment(i)
The above when used from multiple threads guarantees that the value of the variable i would never be corrupted. It will never allow a write to take place while another thread is in the middle of incrementing.
Last edited by Niya; Feb 3rd, 2023 at 11:56 AM.
-
Feb 3rd, 2023, 12:13 PM
#33
Re: App.PrevInstance alternative?
Originally Posted by VanGoghGaming
That "ReleaseSemaphore" function is pretty counter-intuitive haha, MSDN says it increases the semaphore count instead of decreasing it as its name would suggest!
Think of it as the semaphore being "released back into the wild" where it can be used again. In that context, incrementing makes sense. You're adding one to the population of free semaphores available for use.
-
Feb 3rd, 2023, 12:27 PM
#34
Re: App.PrevInstance alternative?
Originally Posted by Niya
I don't know how you guys do it in VB6
We wouldn't 'cause there is no multi-threading in VB6, haha, unless you count ActiveX but I'm sure that one knows not to overwrite variables under the covers! That's why all this stuff about semaphores, mutexes and other tomfoolery is new to me. Thanks for the detailed explanation though, it makes perfect sense!
-
Feb 3rd, 2023, 12:50 PM
#35
Re: App.PrevInstance alternative?
Originally Posted by VanGoghGaming
We wouldn't 'cause there is no multi-threading in VB6, haha, unless you count ActiveX but I'm sure that one knows not to overwrite variables under the covers! That's why all this stuff about semaphores, mutexes and other tomfoolery is new to me.
As I understand it COM's apartment model schemes make it near impossible for ActiveX programmers to shoot themselves in the foot with race conditions at the expense of more convoluted developer experience. In the ActiveX world this means you could probably get away with never having to learn this stuff. However, in languages with simpler free threaded schemes like C++, you MUST understand synchronization otherwise you're in for a world of pain and I mean a lot of pain. Errors caused by race conditions are some of the most difficult bugs to find in programming.
Originally Posted by VanGoghGaming
Thanks for the detailed explanation though, it makes perfect sense!
Sure no problem.
-
Feb 3rd, 2023, 12:54 PM
#36
Re: App.PrevInstance alternative?
Originally Posted by Niya
I don't know how you guys do it in VB6 but in .Net we would use this:.
We use InterlockedIncrement/InterlockedCompareExchange/InterlockedExchange etc. functions.
Originally Posted by VanGoghGaming
We wouldn't 'cause there is no multi-threading in VB6, haha, unless you count ActiveX but I'm sure that one knows not to overwrite variables under the covers! That's why all this stuff about semaphores, mutexes and other tomfoolery is new to me. Thanks for the detailed explanation though, it makes perfect sense!
There is also multi-processing which is multithreading. If you have a shared memory between two processes you faced with multithreading issues here.
-
Feb 3rd, 2023, 01:03 PM
#37
Re: App.PrevInstance alternative?
Originally Posted by The trick
Ah I see, these are provided by the Win32 API itself. It's probably what .Net calls under the hood since these methods even have the same names in the Framework.
-
Feb 3rd, 2023, 01:16 PM
#38
Re: App.PrevInstance alternative?
@VanGoghGaming
Oh yes one other very important thing I forgot to mention in my explanation. With atomic operations it's important that ALL threads do it. For example, if you have multiple threads incrementing a variable, it's important that all threads use InterlockedIncrement. If even one thread tries to do it non-atomically like:-
It defeats the whole purpose. It is the same with any other type of synchronization.
All threads MUST participate in the synchronization of access to shared resources whether it's atomic increments or critical sections(Mutexes, semaphores etc).
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|