Solved

Visual Basic Multithreading Example

Posted on 1998-10-02
12
6,628 Views
1 Endorsement
Last Modified: 2013-11-25
I Want a working multithreading example, written in Visual basic, using the free threading model
1
Comment
Question by:224
  • 4
  • 3
  • 2
  • +3
12 Comments
 
LVL 3

Expert Comment

by:altena
Comment Utility
I want to get paid real money for stuff like that.
0
 
LVL 2

Expert Comment

by:schild
Comment Utility
As a mater of fact you are not clear enough.
Did you ever work with mulithreaded application?
If you didn't a simple example will not help you. If you did, you didn't do it VB, because there is one way to do it - with OCX.
Another way to do it is use a dll which doesn't writen in VB.
Any way good luck
0
 
LVL 14

Expert Comment

by:waty
Comment Utility
0
 
LVL 14

Expert Comment

by:waty
Comment Utility
0
 

Author Comment

by:224
Comment Utility
I asked for a working simple exmalpe written VB5 or VB6!, I have already read those articles and tried the ActiveThread ver 1.1.

Anyway, Thank your for your efforts.
0
 
LVL 13

Expert Comment

by:Mirkwood
Comment Utility
Ingenious Ways to Implement Multiple Threads in Visual Basic 5.0, Part I

John Robbins

John Robbins is a software engineer at Numega Technologies, Inc. who specializes in debuggers. He can be reached at john@jprobbins.com.

Visual Basic® 5.0 brought with it a slew of new features and capabilities. One feature really stands out and moves Visual Basic into the big leagues of software development: the AddressOf operator. With Visual Basic 5.0 and AddressOf, you can pass pointers to functions—callbacks in the Win32® vernacular—to APIs that were off-limits to previous versions. Developers using Visual Basic 5.0 can apply the en­tire Win32 API to their advantage. I don’t know about you, but when I saw that Visual Basic could handle callbacks, I immediately wondered if real multithreading was possible. After all, a callback is the same thing that gets passed
to the lpStartAddress parameter of CreateThread—and
a callback is a callback no matter what language you are developing in. I figured the possibility was worth exploring.

As it turns out, the news is good. You can multithread
all you want! Building a multithreaded application with Visual Basic 5.0 is pretty straightforward, but there are
a few issues that can trip you up. Visual Basic Books Online does a pretty good job of describing the new support
for windows subclassing and enumeration callbacks, but that is where the callback discussion stops. In this article, I will start out with simple samples that are easy to understand and build up to a multithreaded application that you might not expect to see written completely in Visual Basic: a Win32 debugger! When I mentioned earlier that Visual Basic lets you take full advantage of the Win32 API, I wasn’t kidding. You’ll also find that all of the code works on both Intel and DEC Alpha CPUs, including the de­bugger code.

What is AddressOf?

As the name implies, AddressOf is used to return the address of a Visual Basic procedure. The intent of this unary operator is to pass the address of a Visual Basic pro­cedure to an API call out of a DLL such as Enum­Windows. AddressOf has a couple of restrictions. Visual Basic won’t let you take the address returned by Address­Of and call through it as you could with a pointer to a function in C. In addition, AddressOf can only be used in the arguments list for a function and cannot be used to just assign a variable. However, AddressOf is very powerful and relatively simple to use.

I wrote a simple program, EnumWnds, that demonstrates AddressOf. EnumWnds enumerates all the top-level windows using the API function EnumWindows with a callback function. When the Enumerate Windows button is pressed, the EnumWindows API function is called with the callback function WndEnumProc, and the listbox control is passed for the application-defined data. If the window has a title, EnumWynds inserts the title into the main form’s listbox. Figure 1 lists the form code and code module that holds the callback function.

Dim bRet As Long

bRet = EnumWindows(AddressOf WndEnumProc, lstOutput)

Figure 1 EnumWnds

FrmMain.frm

VERSION 5.00

Begin VB.Form frmMain

BorderStyle = 1 'Fixed Single

Caption = "AddressOf Example - Enumerate Windows"

ClientHeight = 4695

ClientLeft = 45

ClientTop = 330

ClientWidth = 5160

LinkTopic = "Form1"

MaxButton = 0 'False

ScaleHeight = 4695

ScaleWidth = 5160

StartUpPosition = 2 'CenterScreen

Begin VB.CommandButton btnEnd

Caption = "E&nd"

Height = 495

Left = 2640

TabIndex = 2

Top = 3960

Width = 2295

End

Begin VB.CommandButton btnEnumThem

Caption = "&Enumerate Windows"

Height = 495

Left = 120

TabIndex = 1

Top = 3960

Width = 2415

End

Begin VB.ListBox lstOutput

BeginProperty Font

Name = "Courier New"

Size = 9.75

Charset = 0

Weight = 400

Underline = 0 'False

Italic = 0 'False

Strikethrough = 0 'False

EndProperty

Height = 3420

Left = 120

TabIndex = 0

Top = 120

Width = 4815

End

End

Attribute VB_Name = "frmMain"

Attribute VB_GlobalNameSpace = False

Attribute VB_Creatable = False

Attribute VB_PredeclaredId = True

Attribute VB_Exposed = False

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' John Robbins, Microsoft Systems Journal - August 1997

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Option Explicit

Private Sub btnEnd_Click()

End

End Sub

Private Sub btnEnumThem_Click()

lstOutput.Clear



Dim bRet As Long

bRet = EnumWindows(AddressOf WndEnumProc, lstOutput)



End Sub

EnumWnds.bas

Attribute VB_Name = "mod_EnumWnds"

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' John Robbins, Microsoft Systems Journal - August 1997

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Option Explicit

' Declare the API functions.

Declare Function EnumWindows Lib "User32" _

(ByVal lpEnumFunc As Any, ByVal lParam As Any) As Long

Declare Function GetWindowText Lib "User32" _

Alias "GetWindowTextA" _

(ByVal hwnd As Long, _

ByVal lpString As String, _

ByVal cch As Long) As Long

Function WndEnumProc(ByVal hwnd As Long, _

ByVal lParam As ListBox) As Long

Dim szTitle As String

Dim bRet As Long

szTitle = String(512, 0)

bRet = GetWindowText(hwnd, szTitle, 512)

' Only show those that have titles.

If (0 <> bRet) Then

lParam.AddItem szTitle

End If

WndEnumProc = 1

End Function

Keep in mind that the procedure after the AddressOf can be either a function or a subroutine. It just depends on what the individual API requires. With EnumWindows, the window enumeration callback routine must continue to return 1 (TRUE in C/C++ terms) to continue to be called. If the callback returns zero (FALSE), then Windows will cease the enumeration.

The window enumeration callback is relatively simple in this example as well. It calls the Windows® API Get­WindowText to retrieve the text from the passed-in window handle and displays the title if there is one. With the callback functions, it’s a good idea to explicitly declare the ByVal and ByRef declarations so that you keep everything straight. In the EnumWnds example, if you leave off the ByVal declarations on WndEnumProc your program will crash, and will crash the IDE bugger as well because parameters will be treated as pointers instead of values.

Besides providing callback functions for enumeration APIs, AddressOf can be used to subclass window procedures. Subclassing a window means inserting your own window procedure in place of the original one so you can perform additional processing on the message stream. For example, subclassing is used to intercept the messages passed to an edit control so that the edit control can receive all the keyboard input when the edit control has focus. Microsoft Word subclasses the edit control where the user can type in the return address in the envelope dialog. By trapping the Enter key, the subclassing forces it to break the lines when the user is typing in a return address, just as the user would expect. If the return address edit control in the envelope dialog was not subclassed, the user would have to remember some strange keyboard combination for breaking the lines—as well as almost always dismissing the dialog with the Enter key.

Multithreading 101

Now that you have an understanding of AddressOf, I will begin discussing some of the issues associated with multi­threading. First, let’s take a look at what multithreading cannot do: it cannot make your application instantly faster, it cannot solve bad programming practices, and it cannot cure world hunger. In fact, writing multithreaded applications is extremely difficult and nearly impossible to get right without careful planning and more testing than you ever dreamed of.

At the same time, when you need multithreading, you really need it. Many times, having a background thread handle an operation can make the difference between a decent application and a great one. And since 85 percent of all Visual Basic-based apps are written to access a database, opportunities to use multithreading are numerous.

For example, you can use multithreading to save data for printing into a temporary file and then print the file as a background thread. If you are writing a data analysis-intensive front end with huge server-driven SQL queries, you could use multithreading to keep the queries cranking in the background while allowing the user interface to remain responsive. If you are writing a field sales application that runs on a salesperson’s laptop computer and uses RAS to connect to a central database, you could create a thread to run in the background that reserves a place in the queue for a new order. That way, when the salesperson is typing the final sales agreement, she can report to the customer when the order is expected to ship based on the most up-to-date information. Finally, being able to multithread makes it possible to create things like debuggers in Visual Basic. Since writing debuggers is what I do for a living, it gives me a quick prototyping tool that I have never had before.

There are a few rules to keep in mind when multithreading. First, only multithread if you have chunks of work in your application that can be done at the same time as other work. Second, do not multithread if you will end up disabling the entire user interface because it’s waiting on the background thread. Remember, the point of multithreading is to let the user continue using your application for other things while the background thread is running. Third, you should double, maybe even triple, the expected development and testing time because there will be many subtle timing and synchronization problems. Fourth, run your multithreaded applications on as many machines as possible, including very fast machines and multiprocessor machines. When writing VBDebug, my Win32 debugger example, a nasty synchronization problem showed up only when I ran it on a 400Mhz DEC Alpha, even though I ran it successfully on a 200Mhz Pentium Pro. I also had nasty synchronization problems on a multiprocessor system. Finally, always assume that all the threads are running at exactly the same time—if there is an infinitesimal chance that they will deadlock, they will.

I can give you some basic guidelines about using multi­threading, but I recommend learning more to really understand how multi­threading works. Perhaps the best place
to start is Advanced Windows, Third Edition by
Jeffrey Richter (Microsoft Press, 1997). It has the most lucid explanation of multi­threading for Win32 I have found. Also, it discusses multi­threading and synchronization at the SDK API level, which is how it has to be used in Visual Basic. Another great resource is the Microsoft Developer Network CD.

Debugging and Native Code Generation

Before I can show you a simple Visual Basic-based program that does multithreading, there are three things that you must know about developing these types of applications. First, you cannot use the Visual Basic IDE to run or debug your application. If you try to run the programs I wrote for this article inside the IDE, it will crash. The Visual Basic Books Online mentions several times that the IDE cannot debug multithreaded applications, even single-threaded Visual Basic executables that use multithreaded ActiveX™ controls written in C++. To debug your app, you will need to set your projects to generate unoptimized native code and turn on symbolic debug information.

The fact that the Visual Basic IDE cannot handle multithreaded programs is not a design flaw. Remember, writing a multithreaded Visual Basic-based application that isn’t a simple, non-UI ActiveX control is way beyond what Microsoft designed Visual Basic to do. As long as you can debug these applications once they are compiled to native EXEs, which you can, it’s not really a big deal. Although this should take most of the sting out of not being able to use the Visual Basic debugger, the Visual Basic runtime is, from everything I can tell, pretty much thread-safe. If it wasn’t, then you couldn’t do multithreading at all without crashing every time.

The second issue that makes Visual Basic native EXE debugging difficult is that there seems to be a bug in the symbol table generation; sometimes the source line numbers do not align with the code being executed. If you can deal with your code being off a line or two in the debugger, this should not be too big a problem.

The last issue you should be aware of is that the sym-
bols generated for Visual Basic native executables are not always very descriptive. For the most part, there are no symbols for global variables, though most, but not all, of the local variables have proper symbols. To see the values of globals and those locals without symbols, I generally sprinkle calls to Output­DebugString liberally around the application. Keep in mind that the new Visual Basic 5.0 Debug object does not get compiled into your native applications, so you have to use conditional compilation for asserts and prints. Also, there are many temporary variables generated in Visual Basic code and they all get the same name: unnamed_var1. You can pretty much figure out which one applies to what section of the code by watching them change as you step through in the debugger.

Let’s Start Simple

Now that you know what to watch out for when developing and debugging native code in Visual Basic, I can show you some multi­threaded code. Figure 2 lists the code for MT.EXE, which is about as simple a Visual Basic-based program as you will find. It is simply a Sub Main that creates two threads that each show a message box (see Figure 3). Despite its simplicity, this sample demonstrates many of the issues associated with multithreading in Visual Basic.



Figure 2 MT.bas

Attribute VB_Name = "MultiThreaded_Main"

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' John Robbins, Microsoft Systems Journal - August 1997

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE **

' NEVER RUN THIS PROGRAM IN THE VB IDE!

' ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE **

Option Explicit

' Note: This declaration of CreateThread does not allow you to pass

' thread attributes. This is the CreateThread that should be used most

' often because the parameter for the thread function is passed by

' reference.

Public Declare Function CreateThread Lib "kernel32" _

(ByVal lpThreadAttributes As Long, _

ByVal dwStackSize As Long, _

ByVal lpStartAddress As Any, _

ByRef lpParameter As Any, _

ByVal dwCreationFlags As Long, _

ByRef lpThreadId As Long) As Long

' If you only are passing a single variable type like an integer or

' string as the thread parameter, use this version of CreateThread.

' Note that I set the lpParameter type to long.

Public Declare Function CreateThread_ByValParam Lib "kernel32" _

Alias "CreateThread" _

(ByVal lpThreadAttributes As Long, _

ByVal dwStackSize As Long, _

ByVal lpStartAddress As Any, _

ByVal lpParameter As Long, _

ByVal dwCreationFlags As Long, _

ByRef lpThreadId As Long) As Long

' So you can determine which thread the code is executing in.

Public Declare Function GetCurrentThreadId Lib "kernel32" () As Long

' This is the type that is passed to DaThreadFunc so you can do the by

' reference demonstration.

Type PARAM_TYPE

lValue As Long

End Type

' The function used to show a pass by reference.

Function DaThreadFunc(ByRef lpParam As PARAM_TYPE) As Long

Dim szStr As String

szStr = "From DaThreadFunc - Parameter = " & _

CStr(lpParam.lValue) & vbNewLine & _

"Thread ID: " & CStr(GetCurrentThreadId)

MsgBox szStr, , "Function Cool!"

' Just to make the return value different, I return -2.

DaThreadFunc = -2

End Function

' The subroutine to show pass by value.

' By the way, I noticed that subs always return 0.

Sub DaThreadSub(ByVal lpVoid As Long)

Dim szStr As String

szStr = "From DaThreadSub - Parameter = " & _

CStr(lpVoid) & vbNewLine & _

"Thread ID: " & CStr(GetCurrentThreadId)

MsgBox szStr, , "Sub Cool!"

End Sub

Sub Main()

Dim lRet As Long

Dim lThreadID As Long

Dim stParam As PARAM_TYPE

Dim szStr As String

lThreadID = 0

' Do the call to CreateThread with a by reference parameter.

stParam.lValue = -1

lRet = CreateThread(0, _

0, _

AddressOf DaThreadFunc, _

stParam, _

0, _

lThreadID)

' Do the call to CreateThread with a by value parameter.

lRet = CreateThread_ByValParam(0, _

0, _

AddressOf DaThreadSub, _

-2, _

0, _

lThreadID)

szStr = "From Da Main Thread" & vbNewLine & _

"Thread ID: " & CStr(GetCurrentThreadId)

MsgBox szStr

End Sub





<Picture>

Figure 3 Message Boxes

The best way to see how MT.EXE works is to compile and run it. When you do, you will see the three message boxes pop up that indi­cate which thread the message box came from: two message boxes come from threads created in the code, and the other is the application main thread. To prove that each message box is from a different thread, each message box shows the thread identification for the thread in which it is called. If you really want to make sure that MT.EXE is creating threads, use the SDK program PVIEW.EXE to see what it reports.

If you run MT.EXE a couple of times and try different shutdown scenarios, you will notice that shutting down the main thread message box first and leaving the other threads running causes all of them to end at once. While this may seem odd, it isn’t when you know that the main thread eventually calls Exit­Process when it is shutting down. ExitProcess automatically kills any outstanding threads that the process still has running. Keep this in mind when developing your multithreaded programs—make sure to end your threads before the main thread exits or you could cause large resource leaks or, worse yet, database locks when TerminateThread is called on your threads.

In C/C++ development, there is no difference between functions or subroutines since creative casting can turn one thing into something else. However, with the extra-strict declarations of Visual Basic, I originally thought that only true Visual Basic functions could be used because there might be a difference internally between a function and a subroutine. As I discovered in MT.BAS, the AddressOf operator can pass both functions and subroutines to CreateThread or any other API.

Even though you can pass Visual Basic subroutines
and functions to CreateThread, keep in mind that one of
the main ways to tell that another thread finished properly is to check the thread exit code with GetExitCodeThread.
If you pass a Visual Basic subroutine to CreateThread,
the thread exit code will always be zero. If you pass a Visual Basic function to CreateThread, the return value of
the function will be the exit code of the thread. So, if you need to know the exit code, your thread routine needs to be a function.

If you look at MT.BAS, you’ll see that I set up two ways of calling CreateThread. When creating the thread for the DaThreadFunc function, I use the straight CreateThread declaration to show how to pass the thread parameter by reference because I am passing a user-defined type to DaThreadFunc. For the most part, you will almost always be passing some sort of user-defined type by reference to the thread function to have it work on unique data. To make passing the thread parameter easy, lpThreadParameter is declared of type Any. As for DaThreadSub, it only needs a value, not a reference, so I used the CreateThread_
By­ValParam declaration of CreateThread. While Visual Basic is extremely type-safe when doing Basic code, it fortunately allows you to coerce the different values to external DLL functions.

But Wait! There’s More

Now that you have seen MT.EXE in action, you are probably salivating at the thought of making your Visual Basic application multithreaded. Before you end up with a 37-thread monster, there are still a few more things that you want to keep in mind when creating multithreaded Visual Basic-based applications. None of these minor issues are showstoppers, but they affect how you will utilize threads and classes in your application’s design.

The first issue is a limitation of the AddressOf operator. AddressOf will only accept functions and routines that are part of a standard module, or code module. Simply put, your thread function must reside in a BAS file. This is not a big limitation, but it means you cannot have a helper function in a CLS module, for example, be the thread function. While you might want to do this so the thread function can access items in the class, it simply will not work and results in a compilation error.

As an alternative to having the thread function come out of a class module, you may have thought about using a class method as the threaded function. AddressOf will not allow this either. Since Visual Basic classes are COM objects, they conform to the COM binary standard. This means that the COM object is a pointer to a vtable so the address of a COM method is not valid. The same restrictions apply in C++: COM methods, or C++ methods for that matter, cannot be used as parameters to CreateThread.

It is perfectly legal to pass user-defined types or a specific type as the thread parameter. You have to be careful passing items as the thread parameter because some items cannot be used. For example, if you try to pass a Label control to a normal Visual Basic routine, you will get a warn­ing at compile time. Since CreateThread uses As Any as the type for lpParameter, if you try passing a Label control, your app compiles but will crash when you run it.

What if you need to pass a Label control to a thread? Just wrap the Label control in a class—Visual Basic classes work great as the thread parameter. Just keep in mind that you need to define the lpParameter variable to CreateThread as “ByRef lpParameter as Any” so you can pass any type of class into your thread function. If you design your application with care, you can even have an abstract class as the type your thread function takes. This lets you pass in polymorphic classes to do different levels of work. Before you can pass the class into CreateThread, however, you must create the class by using the New keyword. As you will see later, I use this heavily to do some extensible multi­threading. Once the thread function starts, it can then call methods off of the class variable to do whatever you need to do in the thread.

As well as Visual Basic classes work as the thread para­meter in multithreaded applications, they do not work at all if the class is declared WithEvents, one of the cool, new Visual Basic 5.0 features. While you cannot use events in your classes, you can use a workaround to get the same effect. I will show you how later.

In normal Visual Basic 5.0-based applications, you can implement components such as business rules with classes so that the user of the class can provide the user interface instead of having the component drag it around. Events make this possible by allowing the item that instantiates the class to declare it with the WithEvents keyword and by handling the various events inside the module that declares the class. If this is done in a form, then the form can provide the event handlers for the class and display the class’s data. The Visual Basic Books Online has an excellent example of this where the class has a PercentDone event that it calls when the class is doing a lengthy operation.

Unfortunately, because the Visual Basic class that is declared through WithEvents does not seem to be a true COM object, you cannot pass a WithEvents-declared class as a thread parameter. I originally questioned the COM-ness of a WithEvents-declared class because there is no way to do events in straight C++ OLE development—and it did not work when I tested it. EVENTPROBLEM.EXE, included with the source code for this article, shows what happens when a WithEvents-declared class is called from a thread. (See page 5 for details on downloading the source code.)

In EVENTPROBLEM.EXE, the WithEvents-declared class is TheClass. When the CallMeFromTheThread method is called, it displays a message box indicating that it was called and then calls RaiseEvent on the DoTheEvent event. In the main form, there are two different instances of this class declared: g_ClassWithEvents has events and g_Class­NoWith does not. When the Declared WITHOUT EVENTS! button is pressed on the form, a thread is created with the function TheThread, and the thread just calls CallMeFrom­TheThread on the class. Since the g_ClassNoWith variable is not declared as WithEvents, the message box in the class shows up but no event can be called. When the Declared With Events button is pressed, the g_ClassWithEvents variable is passed to another thread. When that thread tries to access the thread parameter, there is a runtime error that reports “Object variable or With block variable not set,” and no functions can be called on the class that raises events.

To try getting the WithEvents-declared class in EVENT­PROBLEM.EXE to work, I created a global variable of the same type and then did a Set operation to take care of
the assignment of the thread parameter. I discovered, however, that Visual Basic won’t let you declare an object variable WithEvents in a standard module, even as a local variable. Just accessing the thread parameter that has a WithEvents-declared class variable in it causes an exception to be thrown. WithEvents and multithreading do not mix, so you lose one of the best benefits of the new Visual Basic capabilities.

Fortunately, although not as elegant as class events, there is still a way to isolate the output for the class that is passed as the thread parameter. The idea is similar to that of events: when the class needs to show some output, it needs to tell something else to handle the output. Obviously, this something else should be capable of outputting information in many different ways.

The idea is to have an abstract class as a public member of the class. This class is what you would normally handle with events. For the most part, this is where you would handle the entire user interface, and the class, instead of raising an event, would call through the abstract base class for all of its output. Classes using this abstract output class can be passed safely to CreateThread as the thread parameter. This is an acceptable solution, but you must carefully define the abstract output class so that it will meet the future needs of the main thread parameter class, just like you would have to do when designing and using events. Figure 4 shows the code for EVENTSOLN.EXE, which illustrates the implementation of an output class and its use to show the output.

Figure 4 Event Solution Sample

frmMain.frm

VERSION 5.00

Begin VB.Form fmrMain

Caption = "Form1"

ClientHeight = 1740

ClientLeft = 60

ClientTop = 345

ClientWidth = 4680

LinkTopic = "Form1"

ScaleHeight = 1740

ScaleWidth = 4680

StartUpPosition = 3 'Windows Default

Begin VB.CommandButton btnNoWith

Caption = "Using the output class to fake events!"

Height = 855

Left = 600

TabIndex = 0

Top = 360

Width = 3375

End

End

Attribute VB_Name = "fmrMain"

Attribute VB_GlobalNameSpace = False

Attribute VB_Creatable = False

Attribute VB_PredeclaredId = True

Attribute VB_Exposed = False

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' John Robbins

' Microsoft Systems Journal - August, 1997

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Option Explicit

Private g_ClassNoWith As TheClass

Private g_Output As OutputClass

Private Sub Form_Load()

Set g_ClassNoWith = New TheClass

Set g_Output = New OutputClass

' Set the output class property of the "NoWith" class.

Set g_ClassNoWith.clsOutput = g_Output

End Sub

Private Sub btnNoWith_Click()

Dim hThread As Long

Dim lThreadID As Long

' Create the thread.

hThread = CreateThread(0, _

0, _

AddressOf TheThread, _

g_ClassNoWith, _

0, _

lThreadID)

End Sub

OutputClass.cls

VERSION 1.0 CLASS

BEGIN

MultiUse = -1 'True

END

Attribute VB_Name = "OutputClass"

Attribute VB_GlobalNameSpace = False

Attribute VB_Creatable = True

Attribute VB_PredeclaredId = False

Attribute VB_Exposed = False

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' John Robbins, Microsoft Systems Journal - August, 1997

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Option Explicit

Public Sub DoMe(msg As String)

Dim iRet As Integer

iRet = MsgBox(msg, Title:="OutputClass")



End Sub

TheClass.cls

VERSION 1.0 CLASS

BEGIN

MultiUse = -1 'True

END

Attribute VB_Name = "TheClass"

Attribute VB_GlobalNameSpace = False

Attribute VB_Creatable = True

Attribute VB_PredeclaredId = False

Attribute VB_Exposed = False

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' John Robbins, Microsoft Systems Journal - August, 1997

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Option Explicit

Public clsOutput As OutputClass

' The public method that will be called from the thread.

Public Sub CallMeFromTheThread()

Dim iRet As Integer

iRet = MsgBox("TheClass.Call
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 

Author Comment

by:224
Comment Utility
I would like to thank you for your valuable help, but I can accept your answer only if you make your example work!. I have tried the MT.BAS above and executed project1.exe from outside VB6, but I received the following error:

PROJECT1 caused an invalid page fault in
module MSVBVM60.DLL at 0137:6605f131.
Registers:
EAX=00000000 CS=0137 EIP=6605f131 EFLGS=00010287
EBX=81655480 SS=013f ESP=0189fedc EBP=0189ff94
ECX=00000006 DS=013f ESI=00000000 FS=599f
EDX=81655490 ES=013f EDI=00401be4 GS=0000
Bytes at CS:EIP:
89 b0 9c 00 00 00 5e c3 55 8b ec 83 ec 10 56 57
Stack dump:
00000008 00401c23 00401be4 00000008 81655480 00000000 ffc50c2d 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0
 

Author Comment

by:224
Comment Utility
I would like to thank you for your valuable help, but I can accept your answer only if you make your example work!. I have tried the MT.BAS above and executed project1.exe from outside VB6, but I received the following error:

PROJECT1 caused an invalid page fault in
module MSVBVM60.DLL at 0137:6605f131.
Registers:
EAX=00000000 CS=0137 EIP=6605f131 EFLGS=00010287
EBX=81655480 SS=013f ESP=0189fedc EBP=0189ff94
ECX=00000006 DS=013f ESI=00000000 FS=599f
EDX=81655490 ES=013f EDI=00401be4 GS=0000
Bytes at CS:EIP:
89 b0 9c 00 00 00 5e c3 55 8b ec 83 ec 10 56 57
Stack dump:
00000008 00401c23 00401be4 00000008 81655480 00000000 ffc50c2d 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0
 
LVL 13

Expert Comment

by:Mirkwood
Comment Utility
I've never tried it in VB 6. What does it do? Try using messageboxes to see where it crashes.
0
 
LVL 13

Accepted Solution

by:
Mirkwood earned 350 total points
Comment Utility
Here is an example of http://www.hilonet.com/vbthread/
The code
1.Assuming that we have an application that among other things can copy a file on the click of a button.

2.The copy button could have code like this:

Private Sub cmdCopyFile_Click()
Dim lpThreadID As Long
Dim hThread As Long

'spawn a new thread of execution starting with the AsyncFileCopy function
hThread = CreateThread(ByVal 0&, ByVal 0&, AddressOf AsyncFileCopy, ByVal 0&, ByVal 0&, lpThreadID)
CloseHandle hThread 'clean up after ourselves
End Sub


3.Code this for you module:

Option Explicit

Public gsOriginalFile As String
Public gsDuplicateFile As String
Declare Function CreateThread Lib "kernel32" (lpThreadAttributes As Any, ByVal dwStackSize As Long, ByVal lpStartAddress As Long, lpParameter As Any, ByVal dwCreationFlags As Long, lpThreadID As Long) As Long
Declare Function CloseHandle Lib "kernel32" (ByVal hHandle As Long) As Long

Public Function AsyncFileCopy() As Long
'Assuming that gsOriginalFile & gsDuplicateFile
'are valid path/file names set elsewhere.

FileCopy gsOriginalFile, gsDuplicateFile
End Function

See also
http://www.microsoft.com/msj/0897/multithreading.htm
0
 

Author Comment

by:224
Comment Utility
Adjusted points to 350
0
 
LVL 10

Expert Comment

by:caraf_g
Comment Utility
Hello everyone....

Did anyone here ever download the "threading without rocket science" example? If you did, do you still have it?

Great. I lost it. Can you please e-mail it to me: pinocarafa@carobit.com

Cheers

Pino
0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

Suggested Solutions

The debugging module of the VB 6 IDE can be accessed by way of the Debug menu item. That menu item can normally be found in the IDE's main menu line as shown in this picture.   There is also a companion Debug Toolbar that looks like the followin…
When designing a form there are several BorderStyles to choose from, all of which can be classified as either 'Fixed' or 'Sizable' and I'd guess that 'Fixed Single' or one of the other fixed types is the most popular choice. I assume it's the most p…
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…
Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…

728 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

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now