throbber

`
`
`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

This document is available on Docket Alarm but you must sign up to view it.


Or .

Accessing this document will incur an additional charge of $.

After purchase, you can access this document again without charge.

Accept $ Charge
throbber

Still Working On It

This document is taking longer than usual to download. This can happen if we need to contact the court directly to obtain the document and their servers are running slowly.

Give it another minute or two to complete, and then try the refresh button.

throbber

A few More Minutes ... Still Working

It can take up to 5 minutes for us to download a document if the court servers are running slowly.

Thank you for your continued patience.

This document could not be displayed.

We could not find this document within its docket. Please go back to the docket page and check the link. If that does not work, go back to the docket and refresh it to pull the newest information.

Your account does not support viewing this document.

You need a Paid Account to view this document. Click here to change your account type.

Your account does not support viewing this document.

Set your membership status to view this document.

With a Docket Alarm membership, you'll get a whole lot more, including:

  • Up-to-date information for this case.
  • Email alerts whenever there is an update.
  • Full text search for other cases.
  • Get email alerts whenever a new case matches your search.

Become a Member

One Moment Please

The filing “” is large (MB) and is being downloaded.

Please refresh this page in a few minutes to see if the filing has been downloaded. The filing will also be emailed to you when the download completes.

Your document is on its way!

If you do not receive the document in five minutes, contact support at support@docketalarm.com.

Sealed Document

We are unable to display this document, it may be under a court ordered seal.

If you have proper credentials to access the file, you may proceed directly to the court's system using your government issued username and password.


Access Government Site

We are redirecting you
to a mobile optimized page.





Document Unreadable or Corrupt

Refresh this Document
Go to the Docket

We are unable to display this document.

Refresh this Document
Go to the Docket