`
`
`MIcRosor-‘r® PROGRAMMING SERIES
`
`Designed for
`’fl!“.4
`.u‘’1!an:n
`
`Microsoft'
`Windows NT”
`Windows’98
`
`
`
`
`
`CD—ROM
`Included
`
`
`
`“Excellent
`book...a must
`
`for any serious
`VB developer.”
`Dr. Dobb's Journal
`
`
`
`
`Programming
`Wit
`I
`_'
`.i --
`a
`.i—jc
`
`I.
`
`._ 9 u;-
`
`l
`
`Master
`
`object-oriented
`
`programming
`
`techniques for
`
`rapid 32-bit
`
`development
`
`Francesco Balena
`
`Foreword by James Fawcette
`Publisher of Visual Basic
`
`Programmer’s Journal
`
`Microsoft Corp. Exhibit 1064
`
`Microsoft Corp. Exhibit 1064
`
`
`
`MicrosoffiPress
`
`P rogra m m i n g
`
`r iosua]
`Base 6.0
`
`Francesco Balena
`
`Microsoft Corp. Exhibit 1064
`
`
`
`PUBLISHED BY
`Microsoft Press
`
`A Division of Microsoft Corporation
`One Microsoft Way
`Redmond, Washington 98052—6399
`
`Copyright © 1999 by Francesco Balena
`
`All rights reserved. No part of the contents of this book may be reproduced or transmitted in
`any form or by any means without the written permission of the publisher.
`
`Library of Congress Cataloging—in—Publication Data
`Balena, Francesco, 1960—
`Programming Microsoft Visual Basic 6.0 / Francesco Balena.
`p.
`cm.
`Includes index.
`ISBN 0-7556—0558-0
`1. Microsoft Visual BASIC. 2. BASIC (Computer program language)
`I. Title.
`
`QA76.75.135B545
`005.26‘8——dc21
`
`1999
`
`99-20581
`CIP
`
`Printed and bound in the United States of America.
`
`56789 QWTQWT
`
`4 3210
`
`Distributed in Canada by Penguin Books Canada Limited.
`
`A CIP catalogue record for this book is available from the British Library.
`
`Microsoft Press books are available through booksellers and distributors worldwide. For
`further information about international editions, contact your local Microsoft Corporation
`office or contact Microsoft Press International directly at fax (425) 936-7329. Visit our Web
`site at mspress.microsoft.com.
`
`Active Desktop, ActiveX, the BackOffice logo, FrontPage, IntelliSense, Microsoft, Microsoft
`Press, MS-DOS, Visual Basic, and Windows are either registered trademarks or trademarks
`of Microsoft Corporation in the United States and/or other countries. Other product and
`company names mentioned herein may be the trademarks of their respective owners.
`
`The example companies, organizations, products, people, and events depicted herein are
`fictitious. No association with any real company, organization, product, person, or event is
`intended or should be inferred.
`
`Acquisitions Editor: Eric Stroo
`Project Editor: Kathleen Atkins
`Manuscript Editors: Kathleen Atkins, Sally Stickney
`Technical Editor: Marzena Makuta
`
`Microsoft Corp. Exhibit 1064
`
`Microsoft Corp. Exhibit 1064
`
`
`
`Chapter 16 ActiveX Components
`
`cdeIose.Caption = rs.LoadResString(rsC10$e)
`cdeefresh.Caption = rs.LoadResString<rsRefresh>
`'
`(Other string assignments omitted...)
`Set
`imgFIag.Picture = rs.LoadResPicture(rpF1ag)
`End Sub
`
`Because the MyApplicationOOO.Resource class declares enumerated constants
`for all the strings and other resources in the satellite DLL, you can use IntelliSense to
`speed up the development phase and produce a more readable and self-documenting
`code at the same time.
`
`MULTITHREADED ACTIVEX COMPONENTS
`
`Both Visual Basic 5 and 6 can create multithreaded ActiveX components. Components
`built with the first release of Visual Basic 5, however, could only support multithread—
`
`ing if they had no user interface, which is a serious limitation in some cases. This
`restriction was lifted in Service Pack 2.
`
`Threading Models
`
`In a nutshell, multithreading is the ability to execute different code portions of an
`application at the same time. Many popular Windows applications are multithreaded.
`For example, Microsoft Word uses at least two threads, and the Visual Basic environ-
`ment uses five threads. Multiple threads are a good choice when you need to exe-
`cute complex tasks in the background (for example, paginating a document) or when
`you want to keep the user interface responsive even when your application is do-
`ing something else. Multiple threads are especially necessary when you’re building
`scalable remote components that have to serve hundreds of clients at the same time.
`There are two main types of threading models: free threading and apartment
`threading. In the free-threading model, each thread can access the entire process’s
`data area and all threads share the application’s global variables. Free threading is
`powerful and efficient, but it’s a nightmare even for most experienced programmers
`because you must arbitrate among all the shared resources, including variables. For
`example, even an innocent statement such as
`
`If x > 1 Then x = x —
`
`l
`
`'
`
`X shouid aiways be greater than 1.
`
`can create problems. Imagine this scenario: Thread A reads the value of the x vari—
`able and finds that it is 2, but before it executes the Then portion of the statement,
`the CPU switches to Thread B. Thread B happens to be executing the same statement
`
`(an unlikely but not impossible circumstance), finds that x is 2, and therefore decre-
`ments it to 1. When Thread A regains the control of the CPU, it decrements the vari-
`
`able to O, which is an invalid value that will probably cause other logic errors later
`
`in the program’s life.
`
`875
`
`Microsoft Corp. Exhibit 1064
`
`Microsoft Corp. Exhibit 1064
`
`
`
`Pan
`
`‘i‘s
`
`’
`
`‘l-l—r"m‘>w " ".4 . .1"
`
`1,, u...
`
`~
`
`1‘
`
`The apartment—threading model solves these problems by encapsulating each
`thread in an apartment. Code executed in a given apartment can’t access variables
`belonging to other apartments. Each apartment has its own set of variables, so if two
`threads are accessing the x variable at the same time, they’re actually referencing two
`different memory locations. This mechanism neatly solves the synchronization prob-
`lem described earlier, and for this reason the apartment-threading model is inherently
`safer than the free-threading model. In Visual Basic, you can build ActiveX compo-
`nents that support the apartment model only.
`
`Multithreaded ActiveX EXE Components
`
`Visual Basic 5 and 6 let you create out—of—process servers that create an additional
`thread when a client instantiates a new object. All you need to do to transform a
`regular ActiveX EXE component into a multithreaded component is select an option
`in the General tab of the Project Properties dialog box. (See Figure 16—18.) There are
`three possible settings. The default setting is the Thread Pool option with 1 thread;
`this corresponds to a single-threaded component.
`
`Pmiecfl - Proiecl Properties
`
`General lMake I Compile] Component ] Debugging I
`
`Project Iype: fitartup Object:
`ActiveX EXE
`v]
`jrmone)
`3]
`Project flame:
`MyServer
`
`Project Help
`Conteyt TD:
`J 1°
`
`
`
`flelp File Name:
`1
`Eroject Description:
`A multithreaded component
`
`I'— Unattended Execution
`l7 gpgrade ActiveX Controls
`
`Threat“; " w ‘7,
`IP Threadperobject
`5- Threadpgol
`4
`
`v
`. “weeds
`
`r" W934”: LIE-75¢: Vb
`l— m—‘e “lid inflmmm
`
`,
`'
`
`Figure 16-18. Create a multithreaded component with afew mouse die/e5 in [be
`Project Properties dialog box.
`
`If you select the Thread Per Object option, you build a multithreaded compo-
`nent that creates a new thread for every object requested by its clients. Because all
`objects are executed in their own threads, no client can ever block another client,
`so these components are highly responsive. The disadvantage of this approach is that
`too many threads can bring even a powerful system to its knees because Windows
`has to spend a lot of time just switching from one thread to the other.
`
`876
`
`Microsoft Corp. Exhibit 1064
`
`Microsoft Corp. Exhibit 1064
`
`
`
`Chapter 16 Activex Components
`
`Thread pools
`If you select the Thread Pool option and enter a value greater than 1 in the Threads
`field, you build a multithreaded component that’s allowed to create only a limited
`number of threads. This is a scalable solution in the sense that you can increase the
`size of the thread pool when you deploy your application on a more powerful sys
`tem. (You need to recompile the application, though.) This setting prevents the sys-
`tem from wasting too much time on thread management because the pool can’t grow
`larger than the limit you set. To assign threads to objects, the pool uses a round robin
`algorithm, which always tries to assign the first available thread to each new request
`for an object.
`Let’s say that you created a multithreaded component with a pool size of 10
`threads. When the first client makes a request for an object, COM loads the compo-
`nent, which returns the created object in its first thread. When a second client makes
`a request, the component creates an object in a second thread, and so on, until the
`tenth client gets the last available thread in the pool. When the eleventh request
`comes, the component has to return an object in one of the threads that have been
`created previously. The thread used for this new object can’t be determined in ad—
`vance because it depends on several factors. For this reason, the round robin algo-
`rithm is said to be a nondeterministic algorithm.
`Here are a few interesting points that concern object pooling. First, when there
`are more objects than threads, each thread can serve more objects, possibly owned by
`different clients. In this situation, a given thread can’t execute an object’s method if it’s
`already serving another object. In other words, an object pool doesn’t completely pre-
`vent objects from blocking one another (as components with one thread per object do),
`even if this problem happens less frequently than with single-threaded components.
`Second, once an object has been created in a thread, it must execute in that
`thread; this is a requirement of apartment threading. Therefore, a client might be
`blocked by another client even if the component has some unallocated threads.
`Imagine this scenario: You have a pool with 10 threads, and you instantiate 20 ob-
`jects. In an ideal situation, the pool is perfectly balanced and each thread serves exactly
`two objects. But suppose that all the objects served by threads 1 through 9 are released
`while the two objects served by thread 10 aren’t. In this case, the pool has become
`highly unbalanced and the two objects will block each other, even if the pool has
`nine available threads.
`
`Finally, even if the apartment model ensures that all apartments have a differ-
`ent set of variables, objects in the same thread share the same apartment and there-
`fore share the same global values. This might appear to be a cheap way to exchange
`data among objects, but in practice you can’t use this technique because you can’t
`predict which objects will share the same thread.
`
`877
`
`Microsoft Corp. Exhibit 1064
`
`Microsoft Corp. Exhibit 1064
`
`
`
`Part IV anti” 7
`
`i
`
`n It] .I.
`
`The multithreading advantage
`
`Many programmers mistakenly believe that multithreading is always a good thing.
`The truth, however, is that most computers have only one CPU, which has to exe-
`cute all the threads in all the processes in the system. Multithreading is always a good
`thing if you’re executing your component on a Windows NT machine with multiple
`CPUs; in this situation, the operating system automatically takes advantage of the
`additional processors to balance the workload. In the most common case, however,
`you’re working with a single—processor machine and you might find that multithread-
`ing can even make your performance worse. This is a somewhat counter-intuitive
`
`concept, so I’ll explain it with an example.
`Let’s say that you have two threads that execute two different tasks, each one
`taking 10 seconds to complete. In a single-threaded environment, one of the two tasks
`completes in 10 seconds, and the other waits for the first one to complete and there-
`fore takes 20 seconds in total. The result is that the average time is 15 seconds per
`task. In a multithreaded environment, the two tasks would execute in parallel and
`will complete more or less at the same time. Unless you have two CPUs, in this case
`the average time is 20 seconds, which is worse than in the single-threaded case.
`In summary, multithreading isn’t always the best solution. Sometimes, however,
`it clearly offers advantages over single-threading:
`
`I When you’re executing tasks of different duration, multithreading is of-
`ten preferable. For example, if you have a task that takes 10 seconds and
`another task that takes only 1 second, in a single-thread environment the
`shorter task might take 1 second or 11 seconds to complete, which results
`in an average time of 6 seconds, while in a multithreaded environment it
`doesn’t take more than 2 seconds on average. By comparison, the longer
`task takes 10 or 11 seconds to complete in the single-threaded scenario
`(10.5 seconds on average), whereas it always requires 11 seconds in the
`multithreaded scenario. So the multithreaded scenario is slightly disadvan-
`tageous for longer tasks, but the user will hardly notice the difference.
`
`I When you have some tasks, such as user-interface tasks, that have to be
`responsive, it’s better to execute them in a multithreaded environment.
`
`I When you have background tasks with low priority, multithreading is also
`a good choice. A typical example is formatting and spooling a document.
`
`When you’re deciding between single- and multithreading, don’t forget that
`Visual Basic applications implicitly use multithreading for some tasks—for example,
`when printing data. Moreover, some database engines (most notably, the Microsoft
`Jet engine) internally use multithreading.
`
`878
`
`Microsoft Corp. Exhibit 1064
`
`Microsoft Corp. Exhibit 1064
`
`
`
`Chapter 16 ActiveX Components
`
`User-interface issues
`
`Visual Basic 6 lets you create multithreaded components that expose a user interface.
`(You need Service Pack 2 to have this feature work under Visual Basic 5.) You can
`
`achieve this because all the forms and the ActiveX controls that you create are thread
`
`safe, which means that multiple instances of them can independently execute in
`different threads. The same is true for ActiveX documents and designers, such as
`
`the DataEnvironment designer, as well as the majority of the ActiveX controls that
`are in the package—for example, the MaskEdBox control and all the Windows comn
`mon controls.
`
`But a few ActiveX controls are inherently single-threaded and can’t be safely used
`
`inside multithreaded components-for example, the Microsoft Chart (MSCHRTZOOCX)
`and Microsoft Data Bound Grid (DBGRID32.0CX) controls. If you attempt to add these
`
`controls to an ActiveX DLL project whose threading model is Apartment Threaded
`or to an ActiveX EXE project whose threading model is Thread Per Object or Thread
`Pool with a number of threads greater than 1, you get an error and the control isn’t
`added to the Toolbox. You also get an error if you have a project that already includes
`one or more single-threaded controls and you change the project type to a value that
`isn’t compatible with such controls. When you buy a third-party control, check with
`its vendor to learn whether it supports multithreading.
`
`"7"” “=31 You can force Visual Basic to accept a single-threaded ActiveX
`control in a multithreaded project by manually editing the VBP file. There are
`many reasons not to do that, however. Single-threaded controls running in a
`multithreaded application perform poorly and, above all, can cause many prob—
`lems and unexpected behavior. For example, the Tab key and Alt+key combi-
`nations don’t work as they should, and a click on the control might not activate
`the form. Moreover, there might be some properties (moSt notably, the Picture
`property) whose values can’t be marshaled between different threads, and any
`attempt to do so raises a run-time error.
`
`Here are other minor issues concerning forms inside multithreaded components:
`
`I When you use a hidden form variable that Visual Basic creates for each
`form in the application, you’re implicitly using a variable that’s global to
`the thread but not shared among all the threads. Thus, each thread cre-
`ates a different instance of the form. To avoid confusion, you might want
`
`to use explicit form variables, as suggested in Chapter 9.
`
`I MDl forms aren’t allowed in multithreaded EXEs 0r DLLs because the Visual
`
`Basic MDI form engine isn’t thread safe. For this reason, the Add MDI Form
`command in the Project menu is grayed inside these types of projects.
`
`879
`
`Microsoft Corp. Exhibit 1064
`
`Microsoft Corp. Exhibit 1064
`
`
`
`Parth
`
`:37
`
`.
`
`i
`
`.'
`
`A-.
`
`-'-...
`
`I
`
`I
`
`A form can be modal only with respect to other forms in the same thread,
`but it’s modeless with respect to forms displayed by other threads. Con-
`sequently, a modal form blocks only the code in its own thread, not the
`code in other threads.
`
`In a multithreaded component, the Sub Main procedure is executed
`whenever a new thread is created. For this reason, if you need to display
`a form when the component is first created, you can’t simply invoke a
`form’s Show method from this procedure, and you need to distinguish the
`first component’s thread from all the others. See the “Determining the Main
`Thread” section later in this chapter.
`
`I
`
`DDE between forms works only if the two forms are in the same thread.
`(DDE isn’t covered in this book.)
`
`Unattended execution
`
`If your component doesn’t include a form, UserControl, or UserDocument module,
`you can tick the Unattended Execution check box in the General tab of the Project
`Properties dialog box. This indicates that your component is meant to execute with-
`out any user interaction, a reasonable option when you’re creating a component to
`run remotely on another machine.
`The Unattended Execution option suppresses any message boxes or other kinds
`of user interface (including error messages) and redirects them to a log file or the
`Windows NT Application Event Log. You can also send your own messages to this
`log file. Using this option is important with remote components because any mes-
`sage box would stop the execution of the component until the user closes it, but when
`a component is running remotely no interactive user can actually close the dialog box.
`The StartLoggmg method of the App object lets you select where your messages
`will be sent. Its syntax is as follows:
`
`App.StartLogging LogFile, LogMode
`
`where Long'le is the name of the file that will be used for logging, and LogMode is
`one of the values listed in Table 16—2. The vaogOverwrite and vaogThreadID set-
`tings can be combined with the other values in the table. When you’re sending a
`message to the Windows NT Application Event Log, “VBRunTime” is used as the
`application source and the App. Title property appears in the description. When you’re
`running under Windows 95 or 98, messages are sent by default to a file named
`Vbeventslog.
`
`fi“ii':’l~‘*‘ Watch out for two bugs. First, if you specify an invalid filename,
`no errors are raised and logged messages silently go to default output. Also,
`the vaogOverwrite option makes the StartLogg/ng method behave as if the
`vaogAuto option were specified. So you should always manually delete the log
`file and not rely on the vaogOverwrite option.
`
`880
`
`Microsoft Corp. Exhibit 1064
`
`Microsoft Corp. Exhibit 1064
`
`
`
`Chapter 16 Activex Components
`
`# C
`
`
`
`onstant Description Value
`
`
`
`vaogAuto
`
`vaogOff
`
`vabgToFile
`
`vaogToNT
`
`0
`
`1
`
`2
`
`3
`
`vaogOverwrite
`
`16
`
`If running under Windows 95 or 98, messages
`are logged to the file specified by the LogFtle
`argument; if running under Windows NT, mes-
`sages are logged to the Windows NT Applica—
`tion Event Log.
`
`Messages aren’t logged anywhere and are simply
`discarded; message boxes have no effect.
`Forces logging to file, or turns off logging if
`no valid file name is passed in the LogFtle ar—
`gument. (In the latter case, the LogMode prop-
`erty is set to vaogOff.)
`
`Forces logging to the Windows NT Application
`Event Log; if not running under Windows NT
`or the Event Log is unavailable, it turns off
`logging and resets the LogMode property to
`VbLogOff.
`
`When logging to a file, it re—creates the log file
`each time the application starts; it has no effect
`when logging to the Application Event Log. It
`can be combined with other values in this table
`
`using the OR operator.
`
`32
`
`vaogThreadID
`
`The current thread ID is added to the beginning
`of the message in the form “[T:0nnn]”; if this
`value is omitted, the thread ID is shown only if
`the message comes from a multithreaded appli-
`cation. It can be combined with other values in
`this table using the OR operator.
`
`Table 16-2. All the values for the LogMode argument of the App object’s StartLogging
`method; these are also the possible return values of the LogMode read—only property.
`
`Once you have set up logging, you can log messages using the App object’s
`LogEvent method, which has the following syntax:
`
`App.LogEvent LogMessage. EventType
`
`LogMessage is the text of the message, and EventType is an optional argument
`that states the type of the event (one of the following values: 1-vaogEventTypeError,
`Z—VbLogEventTypeWarning, or 4-vaogEventTypeInformation). For example, the
`following piece of code
`
`App.StartLogging "C:\Test.Log", vaogAuto
`App.LogEvent "Application Started", vaogEventTypeInformation
`App.LogEvent "Memory is running low", vaogEventTypewarning
`App.LogEvent "Unable to find data file", vaogEventTypeError
`MsgBox "Press any key to continue", vbCriticaI
`
`Microsoft Corp. Exhibit 1064
`
`Microsoft Corp. Exhibit 1064
`
`
`
`Part IV A::ii'm H t'rrwqumminu
`
`sends its output to the C:\TEST.LOG file if run under Windows 95 or 98 or to the
`
`
`
`Application Event Log if run under Windows NT. (See Figure 1649.)
`a testing Nniepad
`BEE
`
`Ede Eat $.enh nab
`InFnrmatlon application c:\prooa.lug: Thread ID: 187 ,Logged: application Started
`_;_I
`,Loggeo:
`warning
`application c:\proua.log: thread ID: 187
`Memory is running low
`
`Unable to Find data File
`,Logged:
`Error
`application c:\prnua.log: Thread ID: 187
`,Lngged:
`Information application o:\proua.1og: Thread ID: 187
`Msanx:
`, Press any key to continue
`
`
`- vanl Vowe- Apvlcdllun Loy un \\P2
`[M In Slum nub
`Source
`lUsev
`lEvcnl
`lCotegory
`pm
`fringe
`
`VBRunlime
`i12188
`4DS:[]9PM
`None
`ElFIlMIHiE
`‘ f‘i
`“IEUWFH
`VBRumima
`1112/1/93
`4 05 09 PM
`VBRumima
`4'05 09 PM
`012/1l98
`
`
`
`_Cornputcv
`
`Figure 16-19. Logged messages coming from a Visual Basic application as they
`appear in a log textfile (top window) or in the Windows NTApplication Event Log
`(bottom window).
`
`You can test the Unattended Execution attribute from code using the read-only
`UnattendedApp property of the App object. Likewise, you can retrieve the current log file
`and log mode using the App object’s LogPat/o and LogMoa’e properties, respectively.
`When you’ve compiled the code using the Unattended Execution attribute, all the
`MsgBox commands send their output to the log file or the Windows NT Application
`Event Log, as if a LogEvent method with the vaogEventTypeInformation argument
`were issued.
`
`One last note: If you run the program under the Visual Basic IDE, the Unattended
`
`Execution setting has no effect; all message boxes appear on screen as usual, and
`the AppStariLogging and ApplogEvent methods are ignored. To activate logging, you
`must compile your application to a stand-alone program.
`
`Multithreaded ActiveX DLL Components
`
`You can also create multithreaded ActiveX DLLs using Visual Basic 6. Unlike ActiveX
`EXE servers, however, Visual Basic’s DLLs can’t create new threads and can only use
`the threads of their clients. For this reason, multithreaded DLLs are most useful with
`
`multithreaded client applications. Because an ActiveX DLL doesn’t actually create any
`thread, the options you have in the Project Properties dialog box are simpler than
`those offered by an ActiveX EXE project. In practice, you only have to decide if you
`want to create a Single Threaded or Apartment Threaded server. (See Figure 16-20.)
`Both single— and multithreaded components are thread safe, which means that
`when an object in a thread is called by another thread, the calling thread is blocked
`until the called method returns. This prevents most reentrancy problems and greatly
`simplifies the job of the programmer.
`While it’s perfectly safe to use a single-threaded DLL with a multithreaded cli-
`ent, only one thread in the main application can directly call the methods of an object
`
`Microsoft Corp. Exhibit 1064
`
`Microsoft Corp. Exhibit 1064
`
`
`
`Chapter 16 ActiveX Components
`
`created by the DLL. This particular thread is the first thread created in the client
`application, or more precisely, the first thread that internally called the OleInitidlize
`function. All the objects exposed by a single-threaded DLL are created in this thread;
`when they are used from another thread in the client application, arguments and
`return values undergo the so-called cross-thread marshdlmg, which is almost as slow
`as cross-process marshaling.
`
`Proiecfl - Pmiect Pmpelllcs
`
`General [Make ] Cmpile] Compli 0W9]
`Project lype:
`gtartup Object:
`
`lActiveX DLL
`Prolect game:
`
`i Projectl
`Help Frle Name:
`
`" {Sub Main
`
`V
`
`Project Help
`Context 10:
`
`We] F—o
`Erojecl: De5cription:
`
`‘__¥___._.______.__._____
`Threading Model A
`I" Unattended Execution
`l7 Ljpgrada ActiveX Controls
`l—
`,1.f:LlCLxlS'tK/E}’
`F“
`1.~:'
`
`lApartment Threaded
`_.2mm mn.
`L"«i
`
`'l
`
`. F
`
`igure 16-20. Selecting the Threading Model option in the Project Properties dialog box.
`
`When you don’t know how your DLL will be used, selecting an Apartment
`Threaded option is usually the best choice. In fact, a multithreaded DLL can be used
`by single-threaded clients Without any problem and Without any noticeable overhead.
`In one case a single-threaded DLL can be conveniently used with a multithreaded
`client, namely, when you want to offer a simple way for all the threads in the client
`to communicate and share data with each other. An example of this technique is
`
`described in the “Testing a Multithreaded Application” section later in this chapter.
`
`Multithreaded Visual Basic Applications
`
`Many programmers aren’t aware that Visual Basic can create multithreaded regular
`applications, not just components. To be honest, creating such multithreaded appli—
`cations isn’t as straightforward as using other Visual Basic advanced features, and you
`have to account for a number of important issues.
`
`The trick to creating a multithreaded application is simple: The application must
`be a multithreaded ActiveX EXE server that exposes one or more objects that run
`in different threads. To build such an application, the conditions shown on the fol-
`
`lowing page must be fulfilled.
`
`883
`
`Microsoft Corp. Exhibit 1064
`
`Microsoft Corp. Exhibit 1064
`
`
`
`Part IV
`
`.'\.‘-.-|a.v , ( E
`
`.
`
`:hlh‘..;._
`
`I
`
`I
`
`I
`
`The application must be an ActiveX EXE server compiled with the Thread
`Per Object setting.
`
`The code for the task that is intended to run in a different thread is em-
`bedded in a MultiUse class.
`
`You create the new object using the CreateObject function instead of the
`New operator.
`
`When you create an object exposed by the current application using the New
`operator, Visual Basic uses internal instancing, which bypasses COM and creates the
`object using a more efficient mechanism that doesn’t undergo any restriction. (In fact,
`you can even create objects from Private or PublicNotCreatable classes.) Conversely,
`when you use Createobject, Visual Basic always creates the object through COM. For
`this reason, the object should be creatable (MultiUse).
`
`Determining the main thread
`As I stated previously, the Sub Main procedure in a multithreaded Visual Basic appli-
`cation is executed each time a new thread is created. This isn’t usually a problem for
`multithreaded EXE or DLL components, but it’s an issue when you’re creating an
`ActiveX EXE project that must work as a multithreaded application. In this case, it’s
`crucial that you distinguish the first execution from all the subsequent ones: The first
`time the Main. procedure executes, the program must create its main window, whereas
`in all other cases the procedure shouldn’t display any user interface. More precisely,
`when the procedure is being executed as a result of a request for a new object, it
`should exit as soon as possible to avoid having the request fail with a timeout
`error. For the same reason, you should never execute lengthy operations inside the
`CZasst‘tz‘alz’ze event procedure.
`Understanding whether the Main procedure has never been executed before
`isn’t as trivial a task as it might appear at first. You can’t simply use a global variable
`as a flag because that variable can’t be seen from a thread in another apartment.
`Creating a temporary file in the Main procedure isn’t a viable solution either because
`the application might terminate with a fatal error and never delete the file.
`There are at least two ways to solve this problem. The first one is based on the
`Fz‘nsz'ndow API function and is described in the Visual Basic documentation. In the
`following pages, I’ll show you an alternative method, which I believe is less com-
`plex and slightly more efficient because it doesn’t require that you create a window.
`This method is based on atom objects, which are sort of global variables managed
`by the Windows operating system. The Windows API provides functions that let you
`add a new atom, delete an existing atom, or query for an atom’s value.
`In the Main procedure of a multithreading application, you test whether a given
`atom exists. If it doesn’t exist, this is the first thread of the application, and you need
`
`884
`
`Microsoft Corp. Exhibit 1064
`
`Microsoft Corp. Exhibit 1064
`
`
`
`Chapter 16 ActiveX Components
`
`to create the atom. To have the mechanism work, you must also destroy the atom
`when you exit the application. This task is ideal for a class that creates the atom in
`its Class_Initialize procedure and destroys it in its Class_Termmate procedure. Here’s
`the complete source code of the CThread class in the demonstration application on
`the companion CD:
`
`Private Declare Function FindAtom Lib "kernelBZ" Alias "FindAtomA" a
`(ByVal atomName As String) As Integer
`Private Declare Function AddAtom Lib "kernel32" Alias "AddAtomA" _
`(ByVal atomName As String) As Integer
`Private Declare Function DeleteAtom Lib "kernelSZ" _
`(ByVal
`atomName As Integer) As Integer
`Private atomID As Integer
`
`Private Sub Class_Initialize()
`Dim atomName As String
`' Build an atom name unique for this instance of the application.
`atomName = App.EXEName & App.h1nstance
`' Create the atom if it doesn't exist already.
`If FindAtom(atomName) = 0 Then atomID = AddAtom<atomName>
`End Sub
`Private Sub Class_Terminate()
`' Delete the atom when this thread terminates.
`If atomID Then DeleteAtom atomID
`End Sub
`
`Function IsFirstThread() As Boolean
`' This is the first thread if it was the one which created the atom.
`IsFirstThread = (atomID <> 0)
`End Function
`
`The name of the atom is built using the application’s name and the instance
`handle (the App.h1nstance property). The latter value is different for each distinct
`instance of the same application, which ensures that this method works correctly even
`when the user launches multiple instances of the same executable. The CThread class
`module exposes only one property, Ist'rstWJread. The following code shows how
`you can use this class in a multithreaded application to understand whether it’s exe—
`cuting the first thread:
`
`' This is global because it has to live for the entire application's life.
`Public Thread As New CThread
`
`Sub Main()
`If Thread.IsFirstThread Then
`' First thread.
`refuse to be instantiated as a component.
`
`If App.StartMode = vbSModeAutomation Then
`
`(continued)
`
`885
`,
`Microsoft Corp. Exhibit 1064
`
`Microsoft Corp. Exhibit 1064
`
`
`
`ParilV
`
`:
`
`;_u~
`
`:': .r:d.1u h;.;.h
`
`Err.Raise 9999.
`End If
`' Show the user interface.
`frmMainForm.Show
`
`, "Unable to be instantiated as a component"
`
`Else
`
`' This is a component instantiated by this same application.
`End If
`End Sub
`
`Implementing multithreading
`Creating a new thread using the CreateObject function doesn’t suffice to actually
`implement a multithreaded Visual Basic application. In fact, the synchronization
`mechanism offered by Visual Basic, which usually prevents a series of nasty prob-
`lems, in this case gets in the way. When the program invokes a method of an object
`in another thread, the calling thread is blocked until the method returns. So you might
`have multiple threads, but only one of them is executing at a given time, which ob-
`viously isn’t what you want.
`The easy way to work around this issue is using a Timer control to “awaken”
`an object in a separate thread after it has returned the control back to the calling
`thread. You don’t need a visible form to achieve this; an invisible form with a Timer
`control on it can do the job. You can take advantage of the new CallByName func—
`tion to create a form module that you can easily reuse in all your applications that need
`this sort of callback mechanism. This is the complete source code of the CCallBa