Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 351
  • Last Modified:

Is my OCX already in use?

This is probably a bit strange but it is important...

I have an OCX that is going to have some very special code in it. This OCX is not to be used by more than one program at a time on any given machine (which I know is not the point of an OCX). I only want one copy running in memory and only one application to use it at any given moment.

Why? There are some hook codes in there along with special file connection routines. If more than one program access these commands problems could be caused. Such as a system crash when it comes to the hooks.

It is being written mainly to make it really easy to get various user interface forms and these routines into new programs without the need to recode or include the original code.



HERE IS THE QUESTION

In the initialization routine, is there a way to check if this routine is already in use by another program and send an error message or better yet trigger an event telling the calling program to shut down?

Help me detect another program already using this OCX. I can figure out how to best cause a program shut down later.
0
schworak
Asked:
schworak
  • 7
  • 3
  • 3
  • +3
1 Solution
 
mcmonniesaCommented:
Here's a suggestion, why not allow the OCX to make and test a boolean entry (InUse) into the registry where the OCX is registered.  So when the OCX first intializes test the boolean in the registry.  If it' s true, off to the error handler.  If It's false, set it to true then press on.  Don't forget to reset it back to false when your finished.
0
 
samopalCommented:
To mcmonniesa: Not a good idea. What happens if your app crash ? Yes, you will never start it again before you clean InUse in registry.
To schworak: Does your OCX has visual interface? If not, change it to DLL. If yes, may be add additional DLL, and make your OCX to register in this DLL every time it's started. This DLL will return an error, if your OCX try to register at the second time...
0
 
schworakAuthor Commented:
I do use a graphic interface. But please, explane about the DLL concept. I would split the program into an OCX and DLL combo if your idea works.



I just had a suggestion... How about setting an environment variable? Can we do this from VB and read it back?

That way if there is a crash the variable will be cleared from memory automatically on reboot. The program may not run again before a reboot but I don't care about that.
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
schworakAuthor Commented:
This would also allow me to do other things like pass variables between several programs if needed. (I do need that in another project later)
0
 
bhamiltoCommented:
If I remember correctly, a BAS module in any activeX control (OCX or DLL) is shared among all instances of the control.  In fact hook routines for multi-use controls (which must use "addressof") have to jump through hoops because of this.

So won't a simp;e usage count in a BAS module work just fine?

0
 
j3877Commented:
This is an ugly solution:


Put a winsock control on the form
(It only increases the distribution file size by about 30KB...)

set the remotehost to "localhost" / "127.0.0.1", then:

bind the control to some port (eg. 3487) - you'll get an error if the program's already in use! Just catch this error, & terminate.... It would be fine if your program crashes, because it doesn't rely on setting regvalues. If you're using it on multiple machines, get them to send udp messages to one another when the OCX inits, and therefore you'll know (based on whether/not there are responses) whether or not another computer is using it, and you'll know wot computer's using it...
0
 
schworakAuthor Commented:
No, I don't really want to do something hokey like hooking to a winsock port.

There has got to be a better way.


The idea of using a variable within the BAS module doesn't seem to be working either.
0
 
bhamiltoCommented:
Here is the MS article about a shared BAS module with multiple instances of a control.
http://support.microsoft.com/support/kb/articles/Q179/3/98.ASP
The 3rd paragraph of "MORE INFORMATION" explains the basic premise.

I don't see why you can't take advantage of this for your control
0
 
gcs001Commented:
The problem with the variable won't work because each new VB program will create it's own instance of your OCX, thereby creating  a new variable global to itself, not to the other programs already running.

You would probably have to write code within your OCX to search the OS's memory to determine if another instance of the OCX is already loaded.  Their should be some System API call you could use to do this.
Unfortunately I don't know of any off-hand.

Check up on MSDN if anything comes up.

Regards,
Grant.
0
 
gcs001Commented:
The problem with the variable won't work because each new VB program will create it's own instance of your OCX, thereby creating  a new variable global to itself, not to the other programs already running.

You would probably have to write code within your OCX to search the OS's memory to determine if another instance of the OCX is already loaded.  There should be some System API call you could use to do this.
Unfortunately I don't know of any off-hand.

Check up on MSDN if anything comes up.

Regards,
Grant.
0
 
bhamiltoCommented:
gcs001

This is true with Class and Form modules, but not BAS modules in the control.  Check-out the MS article I referenced.
0
 
samopalCommented:
Schworak, you idea with environment variables is great! If you don't worry that you variable will stay in system after your program crash, then

Set WSHShell = CreateObject("WScript.Shell")
set WSE = WshShell.Environment
if WSE("MyCheckOCX") = "" Then
  WSE("MyCheckOCX") = "Started"
Else
  MSGBOX "Already started!"
'do smth
end if

Don't forget to clear it before exit program :-))
0
 
schworakAuthor Commented:
The variable idea didn't work. It may be that the code is not being replicated, but the variables are and each get their own section of memory. I ran some tests and every time the control reported that it was the only copy in memory. I know it wasn't because I could see the others all at one time.
0
 
schworakAuthor Commented:
Here is a little test program I used when trying to figure out a way to communicate between instances. I only needed to know if another instance was running, so anything could be loaded into the environment variable. But for this test, I only wanted one isntance to be enabled but to all to be loaded and visable.


Just copy this to a text file titled MyTest.ctl and load it into a control project and play with it.
(you can name the control file anything that ends with .ctl)


------------------------------------------------

VERSION 5.00
Begin VB.UserControl TestControl
   ClientHeight    =   2115
   ClientLeft      =   0
   ClientTop       =   0
   ClientWidth     =   2805
   ScaleHeight     =   2115
   ScaleWidth      =   2805
   Begin VB.Timer Timer1
      Interval        =   100
      Left            =   2100
      Top             =   240
   End
   Begin VB.Shape Circ
      BackStyle       =   1  'Opaque
      FillStyle       =   0  'Solid
      Height          =   375
      Left            =   840
      Shape           =   3  'Circle
      Top             =   960
      Width           =   435
   End
End
Attribute VB_Name = "TestControl"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
Option Explicit
'
'
' This is a simple experiment in only allowing
' one control to be enabled at a time through
' the use of environment variables being passed
' between all instances of the control.
'
' The idea of only letting one instance work
' is great when dealing with subclassing controls
' or hook controls because a hook can damage another
' instance of itself.
'
' Hoods and subclassing is too hard to control when
' things go wrong. this test uses a graphic display
' to demonstarate the point.
'
' Put several of this one control on one form or on
' several forms or even on several programs. Try to
' enable more than one at any given moment. You will
' see that the first control gets to be enabled when
' the rest are locked out. You can disable the enabled
' control then you will be able to enable any other. But
' only one control will work at any given moment.
'
Dim WshShell As Object
Dim WSE As Object

Dim xi As Integer
Dim yi As Integer

Private Sub Timer1_Timer()
    Dim x As Long
    Dim y As Long
   
    x = Circ.Left + xi * Screen.TwipsPerPixelX * 3
    y = Circ.Top + yi * Screen.TwipsPerPixelY * 3
    If x < 1 Then xi = 1
    If x > UserControl.Width - Circ.Width Then xi = -1
    If y < 1 Then yi = 1
    If y > UserControl.Height - Circ.Height Then yi = -1
    Circ.Move x, y
End Sub

Public Property Get Enabled() As Boolean
    Enabled = Timer1.Enabled
End Property

Public Property Let Enabled(ByVal Mode As Boolean)
    If WSE("MyCheckOCX") = "" Or WSE("MyCheckOCX") = UserControl.hWnd Or Mode = False Then
        Timer1.Enabled = Mode
        PropertyChanged "Enabled"
    Else
        Err.Raise 1, "Testing OCX", "Only one control may be enabled at a time."
    End If
    If Mode Then
        WSE("MyCheckOCX") = UserControl.hWnd
    Else
        WSE("MyCheckOCX") = ""
    End If
End Property

Private Sub UserControl_InitProperties()
    Startup
End Sub


Private Sub Startup()
    Randomize Timer
    xi = Int(Rnd * 2) * 2 - 1
    yi = Int(Rnd * 2) * 2 - 1
   
   
    Circ.Move Int((UserControl.Width - Circ.Width) / 2), Int((UserControl.Height - Circ.Height) / 2)
   
    Set WshShell = CreateObject("WScript.Shell")
    Set WSE = WshShell.Environment
    Debug.Print "Opening " & UserControl.hWnd,
    If WSE("MyCheckOCX") = "" Or WSE("MyCheckOCX") = UserControl.hWnd Then
        WSE("MyCheckOCX") = UserControl.hWnd
        Timer1.Enabled = True
        Debug.Print "Activate"
    Else
        Timer1.Enabled = False
        Debug.Print "Deactivate"
    End If
    PropertyChanged "Enabled"
End Sub

Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
    Startup
End Sub

Private Sub UserControl_Terminate()
    If WSE("MyCheckOCX") = UserControl.hWnd Then
        WSE("MyCheckOCX") = ""
    End If
    Debug.Print "Closing " & UserControl.hWnd
WSE("MyCheckOCX") = ""
End Sub
0
 
samopalCommented:
I don't know why, but this works on my computer fine! It started only one copy of usercontrol; all other copies reported about their deactivations (NT4)

But... Why do we use environment variables??????? Why not to use just file?
1) Look for file (for example C:\FlagOcx.flg)
2) Not Found? - goto 5)
3) try to kill this file
4) Got error 55 (file is open) - exit
5) Open "C:\FlagOcx.flg" for input as 1

If your programm will crash, this file will be close by OS
0
 
schworakAuthor Commented:
Yeah, that would work too. I didn't want to write to the user's disk drive but I supose your idea will work.

It works because you can't kill a file that is opened by another process. So the first process kills and then opens the file.

The next would try to kill and fail and then exit.

The fact that we are not storing a flag to test means after a reboot we can kill and restart. Not a bad idea.
0
 
schworakAuthor Commented:
The nice thing about the environment variables is that I can not only use it to tell if an instance of a control (or program) is running, I can pass information from one instance to another.
0

Featured Post

Important Lessons on Recovering from Petya

In their most recent webinar, Skyport Systems explores ways to isolate and protect critical databases to keep the core of your company safe from harm.

  • 7
  • 3
  • 3
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now