[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

In VB6, how can I call a procedure from Timer1 and not have Timer1 wait for the procedure to finish.

Posted on 2006-03-20
43
Medium Priority
?
1,718 Views
Last Modified: 2011-10-03
Application:  Multi-Station Conveyor that performs a different task per station.

This is my problem and how I bandaided the problem in order to run production.  There is a better way to do what I want to do, but I don't know how.  I don't have much experience with threading or parallel processing in VB.

PseudoCode simplified:  The Problem reside where I check photoeye 3

Main Conveyor Timer1()    ' 200msec

          If Photoeye1 = true  output solenoid A

          If Photoeye2 = true  output solenoid B

          If Photoeye3 = true   Enable Timer2 which Calls Elect_Test_Part()  

' PROBLEM:  The Elect_Test_Part procedure takes about 12seconds to complete and Timer1 will not continuing scanning until the Elect_Test_Part procedure is finished.  By using Timer2, instead of putting a direct call to Elect_Test_Part in Timer1,  I thought would free up Timer1 to continue running.  This was not the case.  So, my bandaid was that I periodically check status of Photoeye1,2,4 in the Elect_Test_Part procedure.  I kind of created my own interrupt, which isn't very efficient.  I realize I can Shell an ".exe", because I've done this before as a bandaid and it was pretty efficient.  This is not as easy in this application though, because I lose AND/OR masking ability between the 2 ".exe's" with the I/O handler I'm using.

          If Photoeye4 = true output solenoid C

          If Photoeye5 = true output solenoid D
     
End

     
0
Comment
Question by:dve01
  • 24
  • 16
  • 2
  • +1
43 Comments
 
LVL 13

Expert Comment

by:jmundsack
ID: 16240357
VBAccelerator has an excellent example of implementing multi-threading in VB6:

http://www.vbaccelerator.com/home/VB/Code/Libraries/Threading/Multi-threading_using_classes_in_ActiveX_EXEs/article.asp

HTH-Jon
0
 

Author Comment

by:dve01
ID: 16240661
I checked out the example.  It's very unclear to me on how to translate that example to my code.  
0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16240963
What you would need to do is:

1. Create a new VB ActiveX exe project (name it, say, ElectTestProj)
2. Set the thread pool to 1 thread
3. Add the file mStart.bas from the VBAccelerator demo to it
4. Create a class module (name it, say, cElectTest)
5. Copy the code from cAsync.cls from the VBAccelerator demo into it
6. Copy the code from Elect_Test_Part into the Runnable_Start sub
7. Compile it

In the main program, go into Project | References and add the ElectTestProj component.  If you don't care when the async procedure finishes (which is what it sounds like), you can ignore the stuff with the Complete and Cancelled events, and declaring the object WithEvents at the module level.  To just launch the process in its own thread your code would be roughly:

    Dim objElectTest As ElectTestProj.cElectTest
    ...
    If PhotoEye3 = True Then
        Set objElectTest = New ElectTestProj.cElectTest
        objElectTest.Start
    End If
    ...

Each time an ElectTestProj.cElectTest object is created it will run in its own thread.

HTH-Jon
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 

Author Comment

by:dve01
ID: 16241156
Much clearer.  It's the end of my day, so give me a day to try it out.
0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16241172
Note, there is a .tlb file you need to download separately from the demo project on VBAccelerator.  That was the only gotcha I noticed.  Good luck.
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
ID: 16245544
The easiest way would be to set up an event using CreateEvent API and one of the I/O wait API's such as WaitForMultipleObjects
0
 

Author Comment

by:dve01
ID: 16258200
jmundsack,

I created an ActiveX exe and called it TestProject.  TestProject consisted of a small amount of code to append Date and Time to a file 100,000 times.  I used the objTest.Start in my Mainproject to run the TestProject ActiveX. While the ActiveX was doing it's thing, my MainProject was adding Date and Time to a list on my main form.  Everything was asynchronous.   It worked great.

Here is the problem though.  My ElectTestProject is very large and shares many global variables with my Mainproject.  I also show a lot of test data to the main form in my Mainproject, which I lose.   I could spend a lot of time making my Main project completely seperate from the ElecTestProject, which might be worthwhile, but I don't think so.

The problem with having seperate exe's with my application is that I use a I/O handler that my Main project and ElectTestProject both use.   Because my Main project uses I/O on the same computer relay card that the ElectTestProject uses, the I/O handler software must reside in an exe that contains all my code.  I've tried putting the I/O handler in 2 different exe's and shelling an exe from my Main project and it doesn't work out.  The only way to get around this is to have a dedicated I/O card to Main project completely seperate from I/O card to ElectTestProject.  This would reguire an upgraded computer since I'm out of slots, not to mention  a lot rewiring.  Not the best option.

The ActiveX exe seems much more effecient than shelling a seperate exe,  But in regards to not sharing variables it seems the same.

Let me know if I'm off base.  My unfamilarity with ActiveX exe's could easily make me spew ignorance.
0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16258420
Well, this is getting a little more sophisticated, but one possibility is to create a component that exposes a "singleton" object (an object for which there is one instance running on the machine accessible to multiple processes).  If you could encapsulate the global variables and I/O functions in this singleton object, and then your main project and the async project could both access it.  There is a good article explaining how to implement a singleton in VB6 at the following address:

http://users.skynet.be/wvdd2/General_techniques/Singletons/singletons.html

Of course this would represent a lot of rewriting.  Another possible solution (which I am unfamiliar with) is described in this thread I found on Google:

http://groups.google.com/group/microsoft.public.vb.winapi/browse_thread/thread/e26d4ddb79095460/d81ef9a66fc4d0cb?lnk=st&q=multithreading+api+vb6+group%3A*vb*&rnum=1&hl=en#d81ef9a66fc4d0cb

Sorry but any way you slice it, it seems you're staring down a lot of revision.
0
 

Author Comment

by:dve01
ID: 16259220
Since I already have all my global variables and I/O functions in a seperate module, it might not be too bad to revise.  I'll give it a try on a smaller scale, just to see if I understand it.  I don't know if I could somehow reference the main form from my main project in the singleton in order display data from the async project.
0
 

Author Comment

by:dve01
ID: 16259276
egl1044,
You mention an easy way using a Create Event API  and an I/O wait API.  If you would like to expound, I'm listening.  I'm all for easy.  
0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16259721
As far as exposing your main form to the singleton, I have this recommendation.

Since your main project will need a reference to the singleton project, create an interface class in the singleton project that defines the properties and methods that you'll want the singleton to access on the main form.  For example:

(class IParent)

Public Property Get Something() As String
End Property

Public Property Let Something(ByVal NewValue As String)
End Property

Public Sub DoSomething()
End Sub

(etc.)

Then in your main form put this in the declarations section:

Implements IParent
Private mSomething As String

Then you can drop down the object selector in the code view of the main form and select IParent, and define the implementation of this interface, e.g.:

Private Sub IParent_DoSomething()
    'implementation of the DoSomething method
End Sub

Private Property Let IParent_Something(ByVal RHS As String)
    mSomething = RHS
End Property

Private Property Get IParent_Something() As String
    IParent_Something = mSomething
End Property

In the singleton object, include a parameter of type IParent on the procedures that you want to access the main form, e.g.:

(in the singleton class)

Public Sub SomeAction(ByVal Parent AS IParent)
    'now you can access these properties and methods defined on the main form
    Parent.Something = "blah"
    Parent.DoSomething
End Sub

And in the main project when you want to execute the SomeAction method on the singleton:

    'lets assume you have a global variable named gMainForm to reference your main form
    'and a global variable named gSingleton to reference your singleton object
    gSingleton.SomeAction gMainForm

Good luck.

Jon
0
 

Author Comment

by:dve01
ID: 16260335
ok,
I've been trying to create a singleton object and need some clarification.  This is what I'm working with, just don't know how to piece everything together.

1. I have a MainProject
2. I have a  GlobVar.bas in Mainproject  (assume similar to myModule.bas in article)
3. Globvar.bas contains over 200 global variables.  I'm just try work with 1 global variable called 'public VoltVal as double'.
4. I have a TestProject which is an ActiveX exe.

I think I need MainProject and TestProject to be able to access global variables in GMU class.

Where does GMU class go?  In Mainproject? or TestProject?  or a 3rd Project?

In the article under 'My way', he refers to myClass in his code.  What would myClass be in my code.

Once I get this singleton object understood, I'll pursue the Mainform sharing.

thanks

0
 
LVL 13

Accepted Solution

by:
jmundsack earned 2000 total points
ID: 16260620
Ok, the VoltVal variable should be a public property of the PNC class, not the GMU class.

Let me help:

1. Create a new ActiveX dll project called SingletonProj
2. Change the name of Class1 to SingletonClass
3. Change the instancing to Public Not Creatable
4. Paste in this code:

Option Explicit

Public VoltVal As Double

5. Add a standard module to the project
6. Paste in this code:

Option Explicit

Dim mSingleton As SingletonClass

Public Function Singleton() As SingletonClass
    If mSingleton Is Nothing Then Set mSingleton = New SingletonClass
    Set Singleton = mSingleton
End Function

7. Add another class module to the project
8. Change the name of the class to GMUClass
9. Change the instancing to Global Multi Use
10. Paste in this code:

Option Explicit

Public Property Get Singleton() As SingletonClass
    Set Singleton = Module1.Singleton
End Property

Now you reference SingletonProj in your main project and in your ActiveX EXE project, and access the VoltVal like this:

    SingletonProj.Singleton.VoltVal = 5.434

HTH-Jon
0
 

Author Comment

by:dve01
ID: 16261645
Nice explanation.  I got everything setup, but not working like I thought.  For instance:

In my Main Project I set  SingletonProj.Singleton.VoltVal=5.434

When I activate my ActiveX EXE project I have a procedure that prints SingletonProj.Singleton.VoltVal to a file.  The value printed to the file is 0. I thought it would be 5.434.

If I set SingletonProj.Singleton.VoltVal = 1.2345 in my ActiveX EXE project. The value printed to the file is 1.2345.

It appears that I can set the VoltVal in each project, but if the variable changes in one of the projects it doesn't reflect in the other project.
0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16261670
Is the Threading Model "Single Threaded" on your SingletonProj?
0
 

Author Comment

by:dve01
ID: 16261809
The threading model was "Apartment Threaded".  I changed to "Single Threaded" on SingletonProj and get same results.
0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16262223
Fascinating.  I've used this technique before with a few variations, and apparently those variations are what made it work.  Because using the directions I gave you it certainly didn't.  But, I may have found a solution.  By simply changing the SingletonProj type from ActiveX DLL to ActiveX EXE, suddenly it works.  Why, I have no idea.
0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16262257
Actually, now it makes sense.  A DLL runs in-process with the project that references it.  An EXE runs in its own process.
0
 

Author Comment

by:dve01
ID: 16262375
Excellent!  It's a good place to start tommorrow.  Now that the global variable issue is resolved, I'll try the same techniques with the I/O handler and use your suggestions to access the MainForm from my ActiveX Exe.  My day is coming to a close, so I communicate tommorrow.

Thanks so much
0
 

Author Comment

by:dve01
ID: 16279868
I couldn' access the Mainform from the ActiveX exe, but I think a better way is to have the ActiveX project launch it's own form and not worry about the Mainform.  I've already done this, but I would like to know how do you know when the ActiveX exe is done running and how come the AcitveX exe shows it's still running in task manager.  Even when I unload the form in the ActiveX it still shows it's running in the task manager.
0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16279937
Does it stay there even if you close the main program?
0
 

Author Comment

by:dve01
ID: 16280047
Yes,  the Singular ActiveX exe is also still running.  So I have to delete the tasks in task manager if I want to recompile changes in the ActiveX projects .
0
 

Author Comment

by:dve01
ID: 16280195
I have to take off for a few hours.  I'll check back around 2 EST.
0
 

Author Comment

by:dve01
ID: 16283653
Ok,  
I think I found a solution for both problems.  I should have dug into this deeper, before submitting a question. I got a little lazy, I guess.

1. To know that "ElectTest" ActiveX program is done, I just created a ElectTestDone variable in the Singleton ActiveX.  I set the ElectTestDone variable to false in my MainProject just before I activate "ElectTest".  In "ElectTest", I set the variable to true when the test is complete.

2. As far as the ActiveX programs always running in the background, it appears that it doesn't matter when I call the ActiveX program  "ElectTest" multiple times.  So I don't have duplicate ActiveX programs running in the task manager, which is what I thought would happen.  All I did was use API functions "FindWindow(ElectTest)", "PostMessage(WM_close), and "WaitforSingleObject" to close the ActiveX programs when I unload the MainProject.  When "ElectTest" closes the "Singleton" activeX closes also.

Last Issue, before I start rewriting my project.

I'm gonna tackle using the I/O handler in the Singleton class.  Before I accept the "Singleton" answer, I would like to keep this question open in case I run into problems with the I/O handler stuff in the Singleton class.  

You've been a great help

thanks




0
 

Author Comment

by:dve01
ID: 16303753
I've hit another roadblack in the Singleton class.

The I/O handler uses Type Definitions and some constants.  The Singleton class module won't compile a Public Type Def or Constant.   When I make them private it compiles, but can't see variables as objects in MainProject or ElectTest project.  Am I screwed? or is there a way around this?

0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16303811
For constants you can use a public Enum.  For public types define a public class that has public properties matching the type members.  The only thing you lose is the ability to define fixed-length string properties, but you can enforce the length in the property let statement.
0
 

Author Comment

by:dve01
ID: 16303893
Thanks, I'll try it and get right back with you.
0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16303930
Note, the public enum will only work for numeric constants--if you have constants of other data types let me know--there are other things you can do for that.
0
 

Author Comment

by:dve01
ID: 16304568
ok,
My constants are numeric.  I think I need further explananation though.  I tried some things to no avail.  Here is what I'm working with:


SingletonClass

    Public VoltVal as double    ' shows up ok in other projects

    Type D_IO   ' stands for digital i/o        ' compiling error
        Board as integer
        Port as integer
        Bit as integer
        On as boolean
    end Type

    Public Const Estop = 1                         'compiling error
    Public Const Alarm= 2
    Public Const Running=3

   Not sure what you mean by defining a public class that has public properties matching the type numbers.

  Also not clear on how to use the Enum.  I've tried some things using help, but didn't work out.
0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16304712
In your Singleton class, define the enum:

(declarations section of the class)
Public Enum IOStateEnum
    Estop = 1
    Alarm = 2
    Running = 3
End Enum

This should compile and then you can access these in the main project as though they were constants.

For defining a class to replace the type, so something like:

(in the singleton class, add a new multi-use class named, say, clsIO)

Private mBoard As Integer
Private mPort As Integer
Private mBit As Integer
Private mOn As Boolean

Public Property Get Board() As Integer
    Board = mBoard
End Property
Public Property Let Board(ByVal NewValue As Integer)
    mBoard = NewValue
End Property

(etc...)

Then wherever you would declare a variable of type D_IO, declare a varaible of type clsIO instead:

Dim objIO As clsIO
Set objIO = New clsIO
With objIO
    .Board = 1
    'etc....
End With
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
ID: 16304896
How To Use Events to Generate Asynchronous Callbacks
http://support.microsoft.com/kb/q176951/
0
 

Author Comment

by:dve01
ID: 16305275
I got the clsIO to work.  thanks

Enum still can't access in main project.  It's about the end of my day.  I'll check back tommorrow.
0
 

Author Comment

by:dve01
ID: 16313823
I can access the Enum in both projects now.  I was using "Singleton.", instead of "SingletonProj."    I'm not sure why some objects show up when you just type just the Class, but others you have to type the project.

The last thing I'm trying to figure out is whether I can use a component (I/O Device) in the Singleton Project and access it in both the MainProject and ElectTest Project to turn Outputs On/OFF through either project.  Any advice?
0
 

Author Comment

by:dve01
ID: 16314004
I know I've asked many follow up questions to the original question.  I can accept the Singleton object answer and post new questions related to the Singleton object with new point totals.  I'm a new account member, so not sure of proper way to handle followup questions to original question.
0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16314055
About the question, "The last thing I'm trying to figure out is whether I can use a component (I/O Device) in the Singleton Project and access it in both the MainProject and ElectTest Project to turn Outputs On/OFF through either project.  Any advice?"  That's sort of the whole point of the Singleton, isn't it?  To have two distinct processes referencing a single object in memory?  Unless I'm missing something....?  (And don't worry about the follow-ups, I just hope the advice I've given you actually ends up working.)
0
 

Author Comment

by:dve01
ID: 16314126
Since the component is dropped on a Form in the Singletonproj, I wasn't sure if the component object on the Form could be accessed just the same as global variables in a Class from the Singletonproj.  Didn't know if there would be differences between a Form and a Class in this regard.  I'm new to VB classes, that's why I second guess.
0
 

Author Comment

by:dve01
ID: 16314696
For instance, I created a SingletonFrm and dropped 2 Output_BD components on SingletonFrm.   In the SingletonProj I can do everything with the component, such as,   SingletonFrm.Output_BD(0).DeviceName
       SingletonFrm.Output_BD(0).DeviceNum
       SingletonFrm.Output_BD(0).OpenDevice
       SingletonFrm.Output_BD(0).CloseDevice

How do I do this from MainProject and ElectTestProject? I thought possible  SingletonProj.SingletonFrm.Output_BD(0).OpenDevice

This isn't correct.  Maybe have to transfer to another class in SingletonProj and reference this class from MainProject?

0
 

Author Comment

by:dve01
ID: 16332243
I tried another thing to no avail.  In trying to access the component from other projects,  I tried to mimic the clsIO for the DAQ component.

I defined a class to replace the DAQDevice and DAQDO (component)

(in the singleton class, I add a new multi-use class named, say, DAQIO)

Private IO_CARD(0 to 1) As DAQDevice
Private Ouput_BD(0 to 1) As DAQDO

Then tried to use the Public Property functions.

I couldn't get anything to compile.  I'm obviously doing something wrong.  I don't think I can treat the component like the Type Def in clsIO.

0
 

Author Comment

by:dve01
ID: 16365090
jmund?   Haven't heard any feedback.  Do you want me to close question and re-enter new questions to board?  Are you on vacation?  Or tired of this thread?  You've been helpful to this point, I just need some feedback to make decision on my next move.  

If you're there,  I'm still struggling to access a component in Singleton ActiveX and define ClsIO variables in main project.   These are the problems I've run into.

1.  In Mainproject I'm trying to initialize I/O variables.  These I/O variables are in Singleton class and defined as a clsIO.  For instance:
        Singleton Class
                   Conveyor_Motor as ClsIO

        Mainproject
                   SingletonProj.Singleton.Conveyor_Motor.board=0
                   SingletonProj.Singleton.Conveyor_Motor.bit=32

*When I run main project I get an "Object variable or with Block Not Set"

2.  I have a DAQclass in singleton project.  This class contains a procedure to Turn On an Output.  OUTPUT_BD(0 to 1) is DAQ output board on form.

            DAQclass  (GLobal multiuse)
                  Public Sub TurnOn_Output(Outputsig as ClsIO)
                          singletonfrm.OUTPUT_BD(outputsig.board).port=outputsig.port
                          singletonfrm.OUTPUT_BD(outputsig.board).bit=outputsig.bit
                          singletonfrm.OUTPUT_BD(outputsig.board).output(true)
                 end sub

When I run main project to TurnOn_Output I get an 'Object Required' error. This probably has something to do with "Object variable or with Block Not Set", when I try to initialize IO variables, Since I'm passing in a ClsIO to TurnOn_Output.

                     singletonproj.daqclass.TurnOn_Output(Conveyor_motor)
0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16373862
In the Class_Initialize() procedure for the Singleton class, you need to put

Private Sub Class_Initialize()
    Set Conveyor_Motor = New clsIO
End Sub

I think that will address the "Object variable not set" error.

When you call a sub, you should avoid parentheses, or use the Call statement:

Instead of:
    singletonproj.daqclass.TurnOn_Output(Conveyor_motor)

Use:
    singletonproj.daqclass.TurnOn_Output Conveyor_motor

Or:
    Call singletonproj.daqclass.TurnOn_Output(Conveyor_motor)

When you use the following:
    singletonproj.daqclass.TurnOn_Output(Conveyor_motor)

VB actually interprets this as:
    singletonproj.daqclass.TurnOn_Output (Conveyor_motor)
    (note the space in between "Output" and the open paren)

Then VB evaluates the expression in parentheses, and passes the result of that evaluation as the parameter to TurnOn_Output.  So, that might have something to do with the "Object required" error too.

I noticed you did a similar thing here:
    singletonfrm.OUTPUT_BD(outputsig.board).output(true)

Again this should be:
    singletonfrm.OUTPUT_BD(outputsig.board).output true

Or:
    Call singletonfrm.OUTPUT_BD(outputsig.board).output(true)

So OUTPUT_BD is an array 0 To 1 of a class?  Note that each array element that is a class must be initialized (probably in form_load) with a Set statement, much like the Set statement I described in the Class_Initialize procedure for the Conveyor_Motor member.  E.g.:

Private Sub Form_Load()
    Set OUTPUT_BD(0) = New <whateverclassitis>
    Set OUTPUT_BD(1) = New <whateverclassitis>
End Sub

Since points have already been awarded for this specific question, I wonder if the admins here at E.E. would permit my offering dve01 my email address for the remaining follow-up questions that are not related to this specific solution?
0
 
LVL 13

Expert Comment

by:jmundsack
ID: 16380508
dve01 you can get my email address from http://www.experts-exchange.com/M_378880.html if you want to follow up on any of the stuff we've talked about.

Jon
0
 

Author Comment

by:dve01
ID: 16381199
Thanks for feedback even though points awarded.   I started a new question on 4-4-06 called "Object Variable or With Block not Set" - 500pts.   There is another expert helping.  Feel free to jump in.   I'll mess around with what you sent me.  
0

Featured Post

[Webinar] Cloud and Mobile-First Strategy

Maybe you’ve fully adopted the cloud since the beginning. Or maybe you started with on-prem resources but are pursuing a “cloud and mobile first” strategy. Getting to that end state has its challenges. Discover how to build out a 100% cloud and mobile IT strategy in this webinar.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction While answering a recent question about filtering a custom class collection, I realized that this could be accomplished with very little code by using the ScriptControl (SC) library.  This article will introduce you to the SC library a…
When trying to find the cause of a problem in VBA or VB6 it's often valuable to know what procedures were executed prior to the error. You can use the Call Stack for that but it is often inadequate because it may show procedures you aren't intereste…
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…
Suggested Courses
Course of the Month18 days, 15 hours left to enroll

834 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question