6935316 DCOM Microsoft Distributed Component Object Model

392
Brief Full Advanced Search Search Tips To access the contents, click the chapter and section titles. DCOM: Microsoft Distributed Component Object Model (Publisher: IDG Books Worldwide, Inc.) Author(s): Frank E. Redmond III ISBN: 0764580442 Publication Date: 09/01/97 Search this book: Preface About the Author Part 1—Introduction to COM Chapter 1—A COM Overview COM: The Programming Model COM Objects Interfaces COM Servers COM: The System Services COM’s APIs COM’s Implementation Locator Service COM’s Transparent LPC and RPC Mechanism Summary Chapter 2—Building In-Process Servers The UserInfo Server Allocating GUIDs Defining Each Object’s Interfaces Implementing Interface Functions Implementing a Class Factory Registering Class Information Exposing the Class Factory Server Unloading

Transcript of 6935316 DCOM Microsoft Distributed Component Object Model

Page 1: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Preface

About the Author

Part 1—Introduction to COM

Chapter 1—A COM OverviewCOM: The Programming Model

COM Objects

Interfaces

COM Servers

COM: The System Services

COM’s APIs

COM’s Implementation Locator Service

COM’s Transparent LPC and RPC Mechanism

Summary

Chapter 2—Building In-Process ServersThe UserInfo Server

Allocating GUIDs

Defining Each Object’s Interfaces

Implementing Interface Functions

Implementing a Class Factory

Registering Class Information

Exposing the Class Factory

Server Unloading

Page 2: 6935316 DCOM Microsoft Distributed Component Object Model

The UserInfoClient Application

Initializing the COM Library

Obtaining an Initial Interface

Manipulating a COM Object

Releasing the COM Object

Uninitializing the COM Library

Summary

Chapter 3—Building Out-of-Process ServersThe UserInfoHandler Server

Allocating CLSIDs

Defining an Object’s Interfaces

Implementing Interface Methods

Implementing a Class Factory

Registering Class Information

Exposing the Class Factory

Server Unloading

Marshaling

Summary

Chapter 4—Reusing COM ObjectsUnderstanding Containment

Understanding Aggregation

Summary

Chapter 5—Building Automation ObjectsAn Introduction to Automation

Understanding IDispatch

Understanding Dual Interfaces

Understanding Variants

Understanding BSTRs

Understanding SAFEARRAYs

Building an Automation Object

Isolating Automation Specifics

Exposing a Type Library

Implementing IDispatch

Registering an Automation Object

Summary

Page 3: 6935316 DCOM Microsoft Distributed Component Object Model

Chapter 6—Building Automation ControllersBuilding the AccountInfoAutoVTBL Application

Initializing the COM Library

Obtaining an Initial Interface

Manipulating the COM Object

Releasing the COM Object

Uninitializing the COM Library

Building the AccountInfoAutoDisp Client

Setting Property Values Using IDispatch

Retrieving Property Values Using IDispatch

Summary

Part II—Building Componentized Applications

Chapter 7—Building Object HierarchiesDefining an Object Hierarchy

The Account Object

The Product Object

The Invoice Object

The LineItem Object

The Accounts Object

The Products Object

The Invoices Object

The LineItems Object

Building an Object Hierarchy

Building the Entry Objects

Building the Collection Objects

Summary

Chapter 8—Building the Client/ServerOrder-Entry Application

Understanding the Order-Entry Application

Understanding the Client/Server ApplicationArchitecture

Developing the Client/Server Application

Adding New Accounts

Page 4: 6935316 DCOM Microsoft Distributed Component Object Model

Retrieving Existing Accounts

Updating Existing Accounts

Removing Existing Accounts

Adding and Updating Invoices

Limitations of the Client/Server ApplicationArchitecture

Summary

Chapter 9—Building the Web Order-EntryApplication

Understanding the Web Application Architecture

Developing the Web Application

Adding New Accounts

Identifying Existing Accounts

Updating Existing Accounts

Removing Existing Accounts

Limitations of the Web Application Architecture

Summary

Chapter 10—Using DCOMDCOM Security

Using DCOMCNFG

Providing System-Wide ConfigurationInformation

Providing Object-Specific ConfigurationInformation

Improving the Client/Server Order-Entry Application

Improving the Web Order-Entry Application

Summary

Appendix A

Appendix B

Appendix C

Quick References: ODL Language Features inMIDL

Index [an error occurred while processing this directive]

Page 5: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 6: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Table of Contents

PrefaceMicrosoft’s Distributed Component Object Model (DCOM) provides thesoftware infrastructure you need to build your next-generation distributedapplication. However, you will need to know more than just “how to buildCOM objects” to successfully develop that next-generation distributedapplication. While learning to build COM objects is important, for corporateapplications developers it is only the beginning. After building the necessaryCOM objects, the corporate applications developers must then assemble theminto a complete application, which is no trivial matter. A poorly writtencomponent-based application will perform as equally unsatisfactorily as apoorly written monolithic application. DCOM: Microsoft® DistributedComponent Object Model not only teaches you the fundamentals of COM andbuilding COM objects, but also goes the extra mile to show you how to buildcomponentized applications.

This book teaches you how to do the following:

•  Create both in-process and out-of-process COM servers

•  Create new COM objects from existing COM objects by usingcontainment and aggregation, COM’s implementation inheritancemechanisms

•  Create COM objects that support custom interfaces as well as COMobjects that support Automation through the use of dual interfaces

•  Develop multiple COM objects into a single cohesive object hierarchy

•  Develop client/server applications using an object hierarchy

•  Develop web-based applications using an object hierarchy

•  Use DCOM to improve both the client/server and web-basedapplication architectures.

Page 7: 6935316 DCOM Microsoft Distributed Component Object Model

Who This Book Is For

If you are responsible for the architecture, development, or deployment of acorporate enterprise application, then this book is for you! This book willprovide you with a firm understanding of COM through a combination ofclear, concise explanations and related samples. In addition to providing youwith a firm understanding of COM, DCOM: Microsoft® DistributedComponent Object Model will also teach you how to create both client/serverand web-based applications using COM. Finally, this book will show you howDCOM can be used to improve both the client/server and web-basedapplication architectures.

This book assumes that you are at least familiar with C++ and HTML.Familiarity with Microsoft Visual Basic and Visual Basic Scripting Edition(VBScript) should enhance your understanding of Chapters 8, 9, and 10,although intimate knowledge of these two products is not required.

What You Need Before You Begin

As a bare minimum, you should have one computer that is equipped with anoperating system that supports DCOM, a C++ compiler that supports thedevelopment of COM objects, Microsoft Internet Explorer 3.0.2, and VisualBasic. Even though Windows NT 4.0 Workstation and Server have built-insupport for DCOM, you should install the latest service pack from theMicrosoft web site () DCOM support for Windows 95 is freely downloadablefrom the Microsoft web site as well. Information regarding DCOM support forother non-Windows operating systems can be found on the Microsoft web site,the Active Group web site (http://www.activex.org), or on the web sites ofMicrosoft partners like Software AG (http://www.sagus.com).

Support for Internet Information Server (IIS) and Active Server Pages (ASP)are also provided as part of Windows NT 4.0 Workstation and Server.Windows 95 supports Peer Web Services, which can also be freelydownloaded from the Microsoft web site.

The sample DCOM source code provided throughout this book and on theaccompanying CD-ROM were developed using Microsoft Visual C++ 5.0 andtested on Windows NT 4.0 Workstation and Server as well as Windows 95.The web-based applications developed in Chapters 9 and 10 were tested usingMicrosoft Internet Explorer version 3.0.2.

What’s in This Book

For corporate application developers, building COM objects is just half thestory; the other half is actually using those COM objects to develop their finalapplication. DCOM: Microsoft® Distributed Component Object Model takesexactly this approach. DCOM: Microsoft® Distributed Component ObjectModel is divided into two parts. The first part begins with a conceptualoverview of what COM is and how COM works, by describing the variouspieces of COM and how they interact, and goes on to illustrate thefundamentals of COM development. This is the “how to build COM objects”

Page 8: 6935316 DCOM Microsoft Distributed Component Object Model

part. The second part describes how to encapsulate application functionalityinto a single tightly integrated group of COM objects called an objecthierarchy and ultimately describes how to use the object hierarchy to buildboth client/server and web-based enterprise applications.

Part I: COM Fundamentals

In Part I, Chapters 1–6, you learn about the various pieces of the COMinfrastructure, everything from the System Registry to the Service ControlManager (SCM). You learn how to build COM interfaces, COM objects, COMservers, and COM clients. You also learn how to reuse existing COM objectsthrough aggregation and containment, COM’s mechanisms for implementationinheritance. Finally, you learn how to implement COM objects that supportAutomation and how to create Automation controllers that use them.

Part II: Building Enterprise Applications Using DCOM

In Part II, Chapters 7–10, you will increase your knowledge and understandingof COM by creating a tightly integrated group of COM objects called an objecthierarchy. You then learn how to use this object hierarchy to develop an orderentry application using both the client/server and web-based applicationarchitectures. Finally, you learn how DCOM can be used to improve both ofthese application architectures.

How to Reach Me

If you have any questions, comments, or suggestions, you can reach methrough my CompuServe account at [email protected].

Table of Contents

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 9: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Table of Contents

About the AuthorFrank E. Redmond III is a Software Design Engineer in Microsoft’s DeveloperRelations Group (DRG), where he assists strategic Independent SoftwareVendors (ISVs) by providing them with technical expertise on a wide range ofMicrosoft COM-based technologies. Frank has authored several articles forvarious trade journals including Microsoft Interactive Developer and Dr.Dobb’s Journal, and frequently speaks at various corporations, trade shows,and conferences all over the world.

Acknowledgments

I would like to thank several people at IDG Books: John Osborn, who helpedme formulate my original ideas into this final book, and Matt Lusher, who hadthe thankless task of keeping me on schedule throughout the developmentprocess. I would also like to thank Luann Rouff, Anne Friedman, and SusanParini and the rest of the IDG Books staff who helped make this book possible.I would like to thank Mary Kirtland, Charlie Kindel, Markus Horstman, andeveryone else who helped review the various technical aspects of this book.Special thanks to Brian Staples for taking time out to not only review thisbook, but to also provide valuable insight during this book’s earlydevelopmental stages. I would also like to thank Ted Hase, Morris Beton, andthe rest of DRG for supporting me throughout this entire process.

To my family: Mr. and Mrs. Frank E. Redmond Jr., Alicia,Angela, and Darlene, and my loving wife, Jill, for all of theirenthusiasm, encouragement, and support. Without you this booksimply would not have been possible. Thanks.

Page 10: 6935316 DCOM Microsoft Distributed Component Object Model

Table of Contents

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 11: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Part 1Introduction to COM

Chapter 1A COM OverviewIN THIS CHAPTER

•  COM clients

•  COM objects

•  Interfaces

•  COM servers

•  COM’s APIs

•  COM’s implementation locator service

•  COM’s transparent LPC and RPC mechanism

ALTHOUGH MICROSOFT’S COMPONENT Object Model (COM) has beenreferred to as many things, it is essentially only two: a programming modeland a set of related system services. In this chapter, I describe COM, theprogramming model, and how it relates to COM, the system services. By theend of this chapter, you should have a clear understanding of COMfundamentals and the advantages of adopting COM as the foundation fordeveloping your next generation of software applications.

COM: The Programming Model

The COM programming model is a client/server, object-based programming

Page 12: 6935316 DCOM Microsoft Distributed Component Object Model

model designed to promote software interoperability. The primary goal ofCOM is to provide a means for client objects to make use of server objects,despite the fact that the two may have been developed by different companies,using different programming languages, at different times. In order to achievethis level of interoperability, COM defines a binary standard, which specifieshow an object is laid out in memory at run time. By defining how an object islaid out in memory, COM allows any language that is capable of reproducingthe required memory layout to create a COM object. We look at the memorylayout of a COM object later in this chapter.

A Word About Interoperability

There are many reasons why two or more applications may need tointeroperate, such as to exchange data or to programmatically control oneanother. However, COM doesn’t define the underlying purpose forapplications to communicate. COM exists to provide a single standardizedway for two or more applications to interoperate regardless of the purposefor their interaction. OLE and ActiveX are examples of two industryspecifications that define specific purposes for applications to interact. OLEis a specification that describes how COM objects can be used to create andmanipulate compound documents. ActiveX is a specification that describeshow COM objects can be used on the Internet. In addition, COM has beenused as the foundation for many industry-specific initiatives in such verticalmarket segments as retailing, banking, and insurance. For a complete list ofthe industry-specific initiatives that rely on COM as their foundation, checkMicrosoft’s Web site at www. microsoft.com.

While COM’s primary objective is to provide basic interoperability betweenobject clients and servers at a binary level, COM also has several otherobjectives:

•  Providing a solution to versioning and evolution problems

•  Providing a system view of objects

•  Providing a singular programming model

•  Providing support for distributed capabilities

Before we investigate how COM accomplishes each of these objectives, let’sfinish our discussion of COM’s basic interoperability.

In COM, programming model, COM clients connect to one or more COMobjects, which are themselves contained in COM servers. Here, a client is anypiece of software that makes use of the services provided by a COM object.Each COM object exposes its services through one or more interfaces, whichare essentially groupings of semantically related functions. The compiledimplementation of each COM object is contained within a binary module(EXE or DLL) called a COM server. A single COM server is capable ofcontaining the compiled implementations of several different COM objects.The COM programming model defines what a COM server must do to exposeCOM objects, what a COM object must do to expose its services, and what aCOM client must do to use a COM object’s services. Part 1 of this bookfocuses on these topics exclusively, and provides the background information

Page 13: 6935316 DCOM Microsoft Distributed Component Object Model

necessary to understand the material covered in Part 2. As a corporateapplications developer, I assume that ultimately you want to know how COMwill help you build better applications. Rather than tell you how, Part 2 of thisbook shows you how. I illustrate how COM can be used to create traditionalclient/server applications, as well as next-generation Web-based applications. Ialso show you how COM’s distributed aspect (DCOM) can be used to enhancethese two popular application architectures.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 14: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

COM Objects

A COM object, like any other object, is a run-time instantiation of a particulardefining class. However, unlike most other objects, which are identified by ahuman-readable name, COM objects are identified by a unique Class Identifier(CLSID). CLSIDs are part of a special group of identifiers called Globally UniqueIdentifiers, or GUIDs. GUIDs are 128-bit values that are statistically guaranteedto be unique across time and space. The following illustrates the internal structureof a GUID:

typedef struct GUID{ DWORD Data1; //32-bits WORD Data2; //16-bits WORD Data3; //16-bits BYTE Data4[8]; //64-bits}GUID;

The internal structure members of a GUID are typically not accessed directly,except for debugging purposes or when GUIDs are being transmitted betweenmachines with different byte orders.

To understand why it’s imperative that COM use CLSIDs to uniquely identifyobject classes, consider the following scenario. Imagine that you’ve justdeveloped a COM object and identified it using a traditional human-readablename, such as “MyObject.” You then ship your object in binary form to thousandsof anxious developers who quickly install your component. If one of thesedevelopers already has an object named “MyObject” installed on his or hersystem, there is no way to resolve the naming conflict because both objects are inbinary form. Therefore, to prevent this type of naming conflict, COM usesCLSIDs to uniquely identify each individual object class. Instead of having a

Page 15: 6935316 DCOM Microsoft Distributed Component Object Model

central authority that is responsible for issuing GUIDs, COM provides theCoCreateGuid API, which is used by various GUID generation tools, such asGUIDGEN.EXE and UUIDGEN.EXE, which both ship as part of MicrosoftVisual C++. Internally, CoCreateGuid calls the RPC function UuidCreateto generate a 128-bit, globally unique identifier, which can be used as a CLSID.While CLSIDs are great for uniquely identifying object classes, they are not verydeveloper-friendly. To make dealing with CLSIDs more developer-friendly andmore like traditional object-based development, you can assign them to traditionalhuman-readable names for use throughout your applications:

//{7AF31102-7A1B-11DO-BADC-0080C7B24880}const CLSID CLSID_MyObject = {0x7af31101,0x7a1b,0x11d0, {0xba,0xdc,0x00,0x80,0xc7,0xb2,0x48,0x8}};

Typically, the CLSID information is included as part of a header file, which isredistributed — along with the object itself — for consumption by otherdevelopers. However, the header file is not redistributed with any resultantapplications created using the object. The header file is only required by otherdevelopers who want to use the object as part of their development efforts. WhileCoCreateGuid guarantees the uniqueness of each CLSID, it is the developer’sresponsibility to make sure that each human-readable name is unique within thescope of its definition. And like traditional object-based development, any namingconflicts will be caught at compile time, when you will be forced to resolve them.

Interfaces

A COM object is defined in terms of the individual interfaces that it supports.Conceptually, an interface is simply a group of semantically related functions.Figure 1-1 shows an example object, the UserInfoHandler COM object, withthree interfaces: ICopyInfo, IReverseInfo, and ISwapInfo (the “I”stands for interface, of course). Each interface contains four functions:ICopyInfo contains CopyName, CopyAge, CopySex, and CopyAll;IReverseInfo contains ReverseName, ReverseAge, ReverseSex, andReverseAll; and ISwapInfo contains SwapName, SwapAge, SwapSex,and SwapAll.

Figure 1-1  The ICopyInfo, IReverseInfo, and ISwapInfo interfaces of theUserInfoHandler COM object

Each interface is identified by a unique identifier called an interface identifier(IID), similar to the way in which each COM object is identified by a uniqueCLSID. Like CLSIDs, IIDs are also GUIDs, which means that they are createdlike any other GUID using the COM API CoCreateGuid or some GUIDgeneration tool such as GUIDGEN.EXE or UUIDGEN.EXE. Again, the IIDinformation is typically included as part of the same header file that includes the

Page 16: 6935316 DCOM Microsoft Distributed Component Object Model

object’s CLSID, which is redistributed — along with the object itself — forconsumption by other developers. When developing COM objects and interfaces,you will need to assign each IID a human-readable name, just as you would if youwere working with CLSIDs:

//{23237f09-e569-11d0-94ab-00a024a85a21}const IID IID_MyInterface = {0x23237f09,0xe569,0x11d0, {0x94,0xab,0x00,0xa0,0x24,0xa8,0x5a,0x21}};

Interfaces are essential to COM programming because they are the only way tointeract with a COM object. Instead of obtaining a pointer to an entire COMobject, a COM client must obtain a pointer to a particular interface, which is thenused to access the functions defined as part of that particular interface. The onlyway to access the functions of a particular interface is through a pointer to thatinterface. So if you have a pointer to the ICopyInfo interface, you will only beable to access the CopyName, CopyAge, CopySex, and CopyAll memberfunctions. In order to access SwapName, SwapAge, SwapSex, or SwapAll,you must first obtain a pointer to the ISwapInfo interface. The fact thatinterfaces are the only way to interact with COM objects should help explain whyeach interface must be uniquely identifiable. The process of moving from oneinterface to another is known as interface navigation.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 17: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

INTERFACE NAVIGATION

To support interface navigation, every interface must implement a special function namedQueryInterface. QueryInterface takes two parameters, one to specify the desiredinterface’s IID, and the other to receive the actual interface pointer. If the COM objectimplements the interface identified by the IID, the QueryInterface call will succeed andreturn a pointer to the interface in the second parameter; otherwise, the QueryInterfacecall will fail, and a NULL value will be returned in the second parameter. The followingsnippet shows how a client would obtain a pointer to the ISwapInfo interface, assumingthat it already had an IReverseInfo interface pointer. We’ll uncover the process of howthe client obtained the original interface in the section on COM’s implementation locatorservice.

HRESULT hr;ISwapInfo *pISwapInfo; //Declare a pointer to an ISwapInfo //interface

//pIReverseInfo is a pointer to the IReverseInfo interfacehr = pIReverseInfo->QueryInterface(IID_ISwapInfo, &pISwapInfo);if (SUCCEEDED(hr)){ //pISwapInfo points to the ISwapInfo interface}else{ //Error - pISwapInfo contains a NULL value}

Because every interface must implement QueryInterface, you are guaranteed the abilityto navigate from one interface on an object to any other interface on that same object.However, if the object doesn’t implement a particular interface, you will never be able toobtain a pointer to it, and QueryInterface will always fail when asked to retrieve thatparticular interface.

Page 18: 6935316 DCOM Microsoft Distributed Component Object Model

Three things essentially define an interface:

•  The number of supported functions

•  The function prototypes of each supported function

•  The order in which the function prototypes are listed

Changing any of these things effectively changes the interface, and because interfaces are theonly way to manipulate a COM object, once an interface is exposed for client usage, it mustnever change. In other words, interfaces are immutable. The logic behind this is simple.Suppose, that as a client of the UserInfoHandler COM object, my program relies on theCopyName and CopyAge functions of the ICopyInfo interface. If the definition of theICopyInfo interface or any of its functions is altered or removed, my application will ceaseworking properly. Therefore, to preserve client compatibility, COM stipulates that aninterface must never change.

Interface navigation is not the only critical function that must be supported by every interface.Every COM interface must also support the AddRef and Release functions (see “LifetimeManagement” below). Together, QueryInterface, AddRef, and Release defineCOM’s most fundamental interface, IUnknown. Because each interface must support thesethree fundamental functions, every interface must inherit from IUnknown.

LIFETIME MANAGEMENT

We have already seen how QueryInterface is used for interface navigation; now we willlook at how AddRef and Release are used to manage the lifetime of a COM object.Typically, the client of an object is responsible for managing the lifetime of that object. Theclient creates the object whenever it needs to, uses the object, and destroys it once it is done.However, COM objects may have multiple clients that are each unaware of the others. Toprevent one client from destroying a COM object and leaving the others with invalid interfacepointer references, both the client and the COM object share the responsibility of lifetimemanagement. A COM object’s lifetime is managed through a process called referencecounting. Every COM object maintains an internal counter variable:

class CSomeObject : IUnknown{private: ULONG m_cRef; //Reference counting variable . .//other member variables .};

When a COM object is first created, its internal counter variable is set to zero. Whenever theCOM object issues an interface pointer — as a result of a QueryInterface call, forexample — it is the COM object’s responsibility to call AddRef on that interface:

HRESULT CSomeObject::QueryInterface(REFIID iid, LPVOID *ppv){ *ppv = NULL; if (IID_Iunknown == iid) *ppv = (LPVOID)(IUnknown *)this;

Page 19: 6935316 DCOM Microsoft Distributed Component Object Model

else if (…) . .//check for other supported interfaces . else return E_NOINTERFACE; //interface not supported //AddRef through the returned interface to accommodate //per interface ref counting ((IUnknown *)*ppv)->AddRef(); return NOERROR;}

AddRef serves to increment the value of the internal counter variable by one:

ULONG CSomeObject::AddRef(void){ return ++m_cRef;}

Whenever a client is finished using an interface, it is the client’s responsibility to callRelease on that interface:

pIX->Release(); //decrement reference count

The Release method serves to decrement the object’s internal counter variable by one.When the internal counter variable reaches zero, it is the responsibility of the COM object todestroy itself:

ULONG CSomeObject::Release(void){ m_cRef-; if (0 == m-cRef) { delete this; . . //other object destruction code . return 0; } return m_cRef;}

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 20: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

OBJECT VERSIONING AND EVOLUTION

While COM strictly prohibits modifying an object’s interfaces, COM doesprovide a simple yet effective strategy for introducing new features in a COMobject without modifying existing interfaces and without breaking existingclient applications. The solution is to simply add new interfaces. For example,suppose that you’ve created a ReferenceMaterials COM object, andthat version 1 has one interface, IDictionary, which has only one function,CheckWord. Clients can call CheckWord with a single argument containingthe spelling of a particular word that they would like to validate. CheckWordreturns TRUE or FALSE depending on whether the word is spelled correctly ornot. However, in version 2, the ReferenceMaterials COM object addsan additional interface, IDictionary2, which has two functions,CheckWord, and GetDefinition. Since the original IDictionaryinterface hasn’t changed, older clients are completely unaware that theReferenceMaterials COM object has changed at all. However, newerclients can obtain a pointer to the new IDictionary2 interface and use the newGetDefinition function (see Figure 1-2). By simply adding newinterfaces, COM objects are able to add new features and functionality whilesimultaneously maintaining backward compatibility with existing clientapplications.

Figure 1-2  By adding a new IDictionary2 interface, version 2 of theReferenceMaterials COM object is able to provide increasedfunctionality to version 2 clients and still provide support for legacy version 1

Page 21: 6935316 DCOM Microsoft Distributed Component Object Model

clients.

While an interface defines the function prototypes for each of its supportedfunctions, the implementation of each function is left totally to the developer’sdiscretion. Each function must be implemented, but the implementationspecifics are fully encapsulated within the COM object. This, plus the fact thatinterfaces are immutable, allows COM objects to change implementationspecifics without breaking existing clients. Consider the following example.Suppose that version 1 of the ReferenceMaterials COM object from theprevious example maintains its list of known words in memory. Based oncustomer feedback, you later decide that the memory requirements are toodemanding, and you therefore decide to maintain the list of known words ondisk in version 2. Even though you’ve changed the implementation of theCheckWord function, you haven’t changed the interface, and existing clientapplications are none the wiser! By separating definition from implementation,COM allows object developers to expose proprietary implementations in anopen and standard way, which is important for protecting intellectual propertyand sensitive corporate information.

COM Servers

The class for each COM object is implemented in a binary code module (DLLor EXE) called a COM server. COM servers implemented as DLLs are loadeddirectly into the client process’s address space, and are commonly referred toas in-process servers. The nature of a Win32 DLL is such that a copy of it ismapped directly into each client application’s own private address space. Thismeans that each client application owns any resources allocated by thein-process server. Since in-process servers don’t own their resources, theycannot maintain global resources that are accessible by multiple clients (seeFigure 1-3).

Figure 1-3  Because DLLs do not maintain their own address space, theirclients each receive a separate and independent copy of all global resources.

While it may at times seem a bit disadvantageous for in-process servers not toown their resources, in-process servers do have a major advantage … speed.Because the in-process server is already mapped onto the client’s addressspace, there is no need for the operating system to perform a context switch inorder to access the code contained in the DLL. As a result, there is very littleoverhead associated with invoking the interface functions of a COM objectimplemented in an in-process server.

COM servers can also be created as stand-alone EXEs, in which case theymaintain an address space apart from that of the client. COM servers created asEXEs are commonly referred to as out-of-process servers. Since EXEsmaintain their own address space, out-of-process servers are also capable ofowning their resources, which may be shared among their clients (see Figure

Page 22: 6935316 DCOM Microsoft Distributed Component Object Model

1-4).

Figure 1-4  Because EXEs maintain their own address space, out-of-processservers are also capable of owning their resources, which may be sharedamong their clients.

An out-of-process server running on the same machine as its client(s) isreferred to as a local server and is said to serve the client(s) local objects.However, any COM server, in-process or out-of-process, that is running on amachine other than its client(s) is referred to as a remote server and is said toserve the client(s) remote objects. In the case where a remote server is anin-process server, COM automatically creates a separate surrogate process andloads the in-process server into its address space (see Figure 1-5).

Figure 1-5  When an in-process server is used remotely, COM automaticallycreates a surrogate process and loads the in-process server into its addressspace.

However, the benefit of resource ownership is not without its drawbacks —one of which, as you may have guessed, is reduced speed. Whenever a clientaccesses code or resources located within the out-of-process server, theoperating system is forced to perform a context switch, and you must pay aperformance penalty. On the other hand, accessing code or resources locatedwithin an in-process server is extremely fast. However, in-process servers areincapable of owning their own resources. Clearly, there are benefits anddrawbacks to both in-process and out-of-process servers. Ultimately, the typeof COM server you create will depend on the overall architecture of yourapplication.

COM: The System Services

In order for an operating system to support the COM programming model, itmust include a set of COM system services, commonly referred to as the COMLibrary. The COM Library is composed of three essential items:

•  A set of APIs necessary for accessing COM’s services

•  The implementation locator service used to locate and start COMservers

•  Transparent Local Procedure Calls (LPCs) and Remote ProcedureCalls (RPCs) when objects are running in local or remote servers

Page 23: 6935316 DCOM Microsoft Distributed Component Object Model

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 24: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

COM’s APIs

The COM Application Programming Interfaces (APIs) are used to access theservices offered by the COM Library. The COM APIs are similar to most otherWin32 APIs in the sense that they are ordinary function calls, not methods ofan interface. For easy identification, COM API functions typically begin withthe prefix “Co,” such as CoCreateInstance. When you look at thedefinitions of the COM APIs, you may notice that many of them return astrange HRESULT data type. An HRESULT is not a handle to a result, as itsname might imply. An HRESULT is used to return status informationregarding the success or failure of an operation. By dividing the 32-bitHRESULT value into an internal structure containing four fields, it is possibleto return information regarding not only the success or failure of an operation,but also detailed information regarding the source of the failure and the reasonfor the failure. The internal structure of an HRESULT is described in Table1-1.

Table 1-1 The Internal Structure of an HRESULT

Field Name Bit Positions Description

S 31 Severity field.0: Success. The function completedsuccessfully.1: Error. The function failed.

R 29–30 Reserved for future use.Facility 16–28 A number indicating the source of the failure.

This value must be universally unique, andtherefore is issued by Microsoft. (See Table2-2 for a description of the currently definedfacility codes.)

Page 25: 6935316 DCOM Microsoft Distributed Component Object Model

Error Code 0–15A number describing the reason the erroroccurred.

Table 1-2 Currently Defined Facility Codes

Facility Name Value Description

FACILITY_NULL 0 Used for broadly applicable error codesFACILITY_RPC 1 Used to report errors that result from an

underlying Remote Procedure Call (RPC)FACILITY_DISPATCH 2 Used to report IDispatch-interface-related

status codesFACILITY_STORAGE 3 Used to report status codes that relate to

persistent storageFACILITY_ITF 4 Used to report an error from an interface

member functionFACILITY_WIN32 7 Used to map an error code from a Win32

API function onto an HRESULTFACILITY_WINDOWS 8 Used to report error codes from

Microsoft-defined interfaces

FACILITY_SSPI 9Used to report error codes that relate tosecurity

FACILITY_CONTROL 10Used to report OLE-Control-related errorcodes

FACILITY_CERT 11 Used to report error codes that relate topublic key certificates and Authenticode

FACILITY_INTERNET 12 Used to report error codes that relate toInternet APIs

FACILITY_MSMQ 14 Used to report error codes that relate toMicrosoft Message Que

FACILITY_SETUPAPI 15 Used to report error codes that relate tothe Win32 setup APIs

Constants defining HRESULT return values typically use the following namingconvention:

<Facility>_<Sev>_<Reason>

where <Facility> is the facility name, <Sev> is either S or E, indicatingsuccess or error, and <Reason> is a short description of the reason the erroroccurred. In cases where the <Facility> value is FACILITY_NULL, thenaming convention is shortened to

<Sev>_<Reason>

as in E_UNEXPECTED or E_NOMEMORY. Table 1-3 shows some of the morecommonly used constants defining HRESULT return values.

Page 26: 6935316 DCOM Microsoft Distributed Component Object Model

Table 1-3 Commonly Used HRESULT Return Value Constants

Constant Meaning

S_OK Function completed and the result is TRUES_FALSE Function completed and the result is FALSENOERROR Function completed with no return valueE_UNEXPECTED An unexpected error has occurredE_INVALIDARG One of the user-supplied arguments is invalidE_OUTOFMEMORY Sufficient memory could not be allocatedE_NOINTERFACE The requested interface is not supported

Because an HRESULT returns not only success or failure, but other detailedinformation as well, COM defines several macros that allow you to probe theinternal structure of an HRESULT value. Table 1-4 describes several of themore commonly used macros.

Table 1-4 HRESULT Macros

Syntax Description

SUCCEEDED(HRESULT status) If the severity field of the HRESULT is0, returns TRUE; otherwise, returnsFALSE

FAILED(HRESULT status) If the severity field of the HRESULT is1, returns TRUE; otherwise, returnsFALSE

HRESULT_CODE(HRESULT hr)Returns the error code field of theHRESULT

HRESULT_FACILITY(HRESULThr)

Returns the facility field of theHRESULT

HRESULT_SEVERITY(HRESULThr)

Returns the severity field of theHRESULT

HRESULT MAKE_HRESULT(SEVERITY sev, FACILITY fac,CODE code)

Creates a new HRESULT given aseverity, a facility, and a status code

User-defined error codes should have a code value between 0x0200 and0xFFFF, as values 0x0000 and 0x01FF are used by the COM-definedFACILITY_ITF codes.

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 27: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 28: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

COM’s Implementation Locator Service

In order to provide a system view of COM objects, COM maintains a system-widedatabase called the system registry, which is essentially a lookup table mappingCLSIDs to COM server filenames. The system registry is composed of a hierarchyof keys. Each key may have an associated value and may also define othersubordinate keys, or subkeys (see Figure 1-6).

Figure 1-6  In order to provide a system view of COM objects, COM maintains asystem-wide database called the system registry, which is essentially a lookup tablemapping CLSIDs to COM server filenames.

All interactions with the system registry are done through the Win32 APIs listed inTable 1-5.

Table 1-5 Win32 APIs for Manipulating the System Registry

RegCloseKey RegConnectRegistry

RegCreateKey RegCreateKeyExRegDeleteKey RegDeleteValueRegEnumKey RegEnumKeyExRegEnumValue RegFlushKeyRegGetKeySecurity RegLoadKeyRegNotifyChangeKeyValue RegOpenKey

Page 29: 6935316 DCOM Microsoft Distributed Component Object Model

RegOpenKeyEx RegQueryInfoKeyRegQueryMultipleValues RegQueryValueRegQueryValueEx RegReplaceKeyRegRestoreKey RegSaveKeyRegSetKeySecurity RegSetValueRegSetValueEx RegUnLoadKey

Top-level keys in the hierarchy are called root keys. COM-specific information ismaintained under the root key HKEY_CLASSES_ROOT. UnderHKEY_CLASSES_ROOT is another key called “CLSID,” under which each COMserver is responsible for creating its own key composed of a string representation ofthe CLSID enclosed in curly braces, along with an optional string description as theassociated value:

HKEY_CLASSES_ROOT CLSID {12345678-ABCD-1234-5678-9ABCDEF00000} = Description

Under each COM object’s CLSID key, you will find one or more additionalsubkeys that define the types of servers present for serving COM objects with thatCLSID. In-process servers must add the “InprocServer32” key and set its valueequal to the string representation of the DLL server’s pathname. Local servers mustadd the “LocalServer32” key and set its value equal to the string representation ofthe EXE server’s pathname. In order to provide a wide range of flexibility, a COMobject may be available from both in-process and out-of-process servers, in whichcase both the “InprocServer32” and “LocalServer32” keys would be defined underthe object’s “CLSID key.”

HKEY_CLASSES_ROOT CLSID {12345678-ABCD-1234-5678-9ABCDEF00000} = Description InprocServer32 = C:\SomeServer.dll LocalServer32 = C:\SomeServer.exe

In the case where a COM object is available for use in different execution contexts,it is the client’s responsibility to specify the desired context(s). When multiplecontexts are specified, the COM library will attempt to load in-process servers first,followed by local servers and, finally, remote servers.

By maintaining a system view, any client on the system is capable of instantiating aregistered COM object. The COM API provides the CoCreateInstancefunction, which client applications can use to create an instance of a particularclass. When a client calls CoCreateInstance to instantiate a COM object, as in

hr = CoCreateInstance(CLSID_SomeObject, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_ISomeInterface, &pSomeInterface);

it is invoking COM’s implementation locator service. COM’s implementation

Page 30: 6935316 DCOM Microsoft Distributed Component Object Model

locator service is implemented in the form of a Service Control Manager (SCM),pronounced like scum. The SCM is ultimately responsible for the following:

•  Locating the appropriate server for a COM object identified by aclient-supplied CLSID

•  Launching the COM server

The SCM uses the system registry to locate and launch the appropriate COMserver.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 31: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

COM’s Transparent LPC and RPC Mechanism

As a developer, you are free to implement a COM object in either anin-process server or out-of-process server. For that matter, Distributed COM(DCOM) allows you to choose between local or remote servers. However,because of process and machine boundaries, there are fundamental differencesbetween accessing code in a DLL, accessing code in a separate EXE, andaccessing code on an entirely different machine. Rather than burden the clientapplication developer with the headache of three different programmingmodels depending on where a particular COM object is located, COMprovides a single programming model for accessing COM objects regardless ofwhere they are located. COM’s single programming model provides locationtransparency to the client: the client has no idea where a particular COMobject is actually running. This is not to say that the client has no control overwhere a particular COM object runs, for as I mentioned earlier, by usingCoCreateInstance, a client can specify the context in which a particularCOM object should run.

The secret to COM’s singular programming model lies in the interface. As youalready know, conceptually, an interface is a group of semantically relatedfunctions. Architecturally, an interface is a pointer to a virtual function table,known as a VTBL. This VTBL contains pointers to functions that provide theactual implementation defined by the interface. As Figure 1-7 shows, whenyou obtain an interface pointer, you are actually receiving a pointer to a pointerthat is pointing to a VTBL of function pointers! (Try saying that very fast threetimes!)

Page 32: 6935316 DCOM Microsoft Distributed Component Object Model

Figure 1-7  An interface is actually a pointer to a VTBL of function pointers.

While very subtle in its design, this level of indirection is all that is requiredfor COM to transparently provide you with a single location-independentprogramming model. When a server is in-process, the pointers in the VTBLpoint directly to methods that are also located in the same process space. Sincethe method implementations are in the same process space as the client, verylittle overhead is associated with invoking the method through multiple-pointerindirection. However, pointers are only able to access information within asingle process space. So when a server is out-of-process, client interfacepointers are not allowed to access information in the server’s process space. Tosolve this problem, COM relies on a special piece of in-process software calleda proxy. When you receive an interface pointer to an out-of-process object, youare actually receiving a pointer to a proxy. The proxy exists to take the place ofthe object and to forward any client requests to another special piece ofsoftware called a stub. Since the proxy is in-process, the client’s interfacepointer can access it. To the client, the proxy is the object. The proxy is alsoresponsible for packaging any parameters that are needed to invoke aparticular method, a process known as marshaling.

Like the proxy, the stub is also in-process; however, the stub is located in theserver’s process space. The stub receives requests from the proxy andunmarshals any parameters before actually invoking the method of theinterface. To the object, the stub is the client. The object (the COM server)passes any return data to the stub, which forwards it to the proxy, which passesit to the client. All of this takes place behind the scenes, and the client andserver are none the wiser.

When the client is accessing a local server, and the proxy and stub are locatedon the same machine, they communicate via Local Procedure Calls (LPCs).An LPC is a form of interprocess communication specifically designed for oneprocess to invoke the methods of a different process. LPCs work fine as longas the proxy and stub are located on the same computer. When the proxy andstub are located on different computers, they communicate via RemoteProcedure Calls (RPCs). Like LPCs, RPCs are also a form of interprocesscommunication; however, RPCs are designed to allow a process on onemachine to invoke the methods of a process located on a different machine.This distributed aspect of COM is the basis of DCOM. COM’s use of LPCsand RPCs is diagrammed in Figure 1-8.

Figure 1-8  Clients of local out-of-process objects actually communicate withan in-process proxy, which communicates with a stub loaded into the addressspace of the object. The proxy then communicates with the stub via LocalProcedure Calls (LPCs). Clients of remote objects, either in-process orout-of-process, communicate with an in-process proxy, which communicates

Page 33: 6935316 DCOM Microsoft Distributed Component Object Model

with a remote stub via Remote Procedure Calls (RPCs).

Summary

In this chapter, we explored:

•  COM’s binary standard, which describes how objects are laid out inmemory and how this allows COM to maintain language independence.

•  The significance of interfaces in general, and the significance of theIUnknown interface specifically, for interface navigation and objectlifetime management.

•  How adding additional interfaces allows a COM object to introducenew functionality while simultaneously supporting existing clientapplications.

•  The advantages and disadvantages of implementing a COM object inan in-process server as opposed to an out-of-process server.

•  How the implementation locator service of the COM Library makesuse of the system registry to provide a system-wide view of registeredCOM objects.

•  How the VTBL design of COM interfaces combines with proxies,stubs, Local Procedure Calls (LPCs), and Remote Procedure Calls(RPCs) to provide a singular programming model and locationtransparency.

In the next chapter, you learn the responsibilities required of both the COMclient and the COM server by building an in-process COM server as well as aclient to manipulate it.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 34: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Chapter 2Building In-Process ServersIN THIS CHAPTER

•  Allocate GUIDs for use as CLSIDs, IIDs, and LIBIDs

•  Define interfaces

•  Implement interface functions

•  Implement an object class factory

•  Register an in-process server with the system registry

•  Load and unload an in-process server

•  Initialize and uninitialize the COM Library

•  Obtain initial and subsequent interface pointers

•  Manipulate a COM object

NOW THAT YOU’VE had a fifty-thousand-foot view of COM, it’s time to divein for a closer look. The first target area is in-process COM servers. To betterillustrate the process and requirements of building an in-process COM server,we build the UserInfo server.

The UserInfo Server

While COM servers are capable of supporting multiple COM objects, ourexample UserInfo in-process server only supports one, the UserInfoCOM object. The UserInfo COM object is used to maintain informationabout an individual person. Each piece of information that is maintained isexposed as a property of the UserInfo COM object (see Table 2-1).

Page 35: 6935316 DCOM Microsoft Distributed Component Object Model

Table 2-1 UserInfo Object Properties

Property Name Data Type

Age shortName LPSTRSex unsigned char

While the UserInfo server is admittedly simple, its purpose is todemonstrate the fundamental elements that every COM server is responsiblefor implementing. The UserInfo server also demonstrates what a typicalin-process server must do to fulfill these responsibilities. Every COM server isresponsible for the following:

•  Allocating GUIDs for each supported object, interface, and typelibrary

•  Defining the interfaces supported by each object

•  Implementing the functions defined by each interface

•  Implementing a class factory capable of creating each supportedobject

•  Registering class information for each supported object

•  Exposing a class factory for each supported object

•  Unloading (destroying) itself when appropriate

Allocating GUIDs

Every COM object must have a unique CLSID, and every interface must havea unique IID. As we discovered in the last chapter, CLSIDs and IIDs are bothGUIDs. You also know that COM supplies the CoCreateGuid API functionto facilitate the creation of GUIDs. However, Microsoft Visual C++ includestwo applications, GUIDGEN.EXE and UUIDGEN.EXE, that both rely onCoCreateGuid internally. These two applications help to further expeditethe creation of GUIDs. GUIDGEN is a Windows-based application thatgenerates GUIDs in a couple of different formats, and allows you to copy themto the Windows clipboard so that you can paste them directly into your code.The UUIDGEN application is a command-line application that allows you tocreate a series of consecutive GUIDs with a single call; it also allows you tosave them to a text file. Creating multiple consecutive GUIDs can be reallyhelpful if you ever need to locate information regarding a specific COM serverin the registry. While you could use GUIDGEN several times to generateenough GUIDs for the UserInfo server, I used UUIDGEN to generate threeconsecutive GUIDs and have them written out to an ASCII text file namedguids.txt:

C:>uuidgen -n3 -oguids.txt

acceeb00-86c7-11d0-94ab-0080c74c7e95acceeb01-86c7-11d0-94ab-0080c74c7e95acceeb02-86c7-11d0-94ab-0080c74c7e95

Page 36: 6935316 DCOM Microsoft Distributed Component Object Model

The newly allocated GUIDs will be used for the CLSID in the definition of theUserInfo COM object and also as an IID for UserInfo’s IUserInfointerface.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 37: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Defining Each Object’s Interfaces

Objects and their interfaces are defined using the Interface Definition Language (IDL). While acomplete IDL reference is beyond the scope of this book, you can find it on Microsoft’s Web siteat www.microsoft.com. IDL, with its C-style syntax, is a simple and easy-to-use language fordefining a number of COM elements such as objects, interfaces, and type libraries. A type libraryis essentially a language-neutral description of COM elements. Type libraries are typically usedduring automation to perform parameter type checking. Automation is the process of manipulatingan application’s COM objects from outside the application using special automation interfaces.(You can find more on automation and type libraries in Chapter 5.) While IDL can be used todefine several different types of elements, the syntax to describe objects and interfaces isessentially the same: [attributes] elementname typename {memberdescriptions};

The attributes section is used to define the element’s characteristics. Elementname is a keywordthat indicates the type of element being defined (coclass, interface, library, etc.). The typenameassigns a name to the element. The memberdescriptions section contains definitions for one ormore additional elements contained within the element being defined. Listing 2-1 shows the IDLdefinition of the IUserInfo interface.

Listing 2-1. The IDL definition of the IUserInfo interface

import "unknwn.idl" ;

//IID_IUserInfo//These are the attributes of the IUserInfo interface[ object, uuid(acceeb02-86c7-11d0-94ab-0080c74c7e95), helpstring("IUserInfo Interface.")]//Declaration of the IUserInfo interfaceinterface IUserInfo : IUnknown{ //List of function definitions for each method supported //by the interface

Page 38: 6935316 DCOM Microsoft Distributed Component Object Model

// //[attributes] returntype [calling convention] //funcname(params); // [propget, helpstring("Sets or returns the age of the user.")] HRESULT Age([out, retval] short *nRetAge); [propput, helpstring("Sets or returns the age of the user.")] HRESULT Age([in] short nAge); [propget, helpstring("Sets or returns the name of the user.")] HRESULT Name([out, retval] LPSTR *lpszRetName); [propput, helpstring("Sets or returns the name of the user.")] HRESULT Name([in] LPSTR lpszName); [propget, helpstring("Sets or returns the sex of the user.")] HRESULT Sex([out, retval] unsigned char *byRetSex); [propput, helpstring("Sets or returns the sex of the user.")] HRESULT Sex([in] unsigned char bySex);}

Notice the three attributes, object, uuid, and helpstring, used in the definition of theIUserInfo interface. The object attribute is used to indicate that the interface is a customCOM interface. The uuid attribute is used to assign a GUID to the interface. The helpstringattribute is used to provide helpful information about the IUserInfo interface. Inside theinterface definition are definitions of each of the functions supported by the interface. SinceIUserInfo inherits from IUnknown, we don’t need to define the IUnknown functions in thememberdescriptions of IUserInfo, but we do have to include IUnknown’s definition. Toinclude the definition of an existing element, IDL provides the import statement. By importingunknwn.idl in the first line of IDL code in Listing 2-1, we have included the definition of theIUnknown interface.

As you look at the IUserInfo function definitions, notice that there are two functions for eachproperty: a propget function, used to retrieve the property value; and a propput function,used to alter the property value. Had any of the UserInfo properties been read-only, thepropput function would have been missing. Likewise, had any of the UserInfo propertiesbeen write-only, the propget function would have been missing. Because access to some COMobjects may require marshaling, you must provide as much information as possible about theparameters required by each interface function. To help provide this information, IDL defines thein and out attributes, which are used to describe the purpose of each individual functionparameter. The in attribute is used to signal parameters responsible for bringing information intoa particular function, while the out attribute is used to signal parameters responsible for returninginformation to the caller. These attributes can be combined to indicate a parameter that isresponsible for transferring information into and out of a function.

Each interface function is required to return an HRESULT so that the client can receive statusinformation regarding the success or failure of an operation. Therefore, to facilitate traditionalfunction-specific return values, a function must declare a pointer to memory that will receive thereturn value, and have the function return information via the pointer. This pointer must includeboth the out and retval attributes, and should always be the last parameter in the list.

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 39: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 40: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

While these attributes identify the purpose of a parameter, each parameter still needs adata type. Like all languages, IDL has a set of intrinsic data types it supports, which canbe seen in Table 2-2. In addition, IDL provides the typedef, enum, union, andstruct keywords for the definition of user-defined data types.

Table 2-2 Intrinsic Data Types Supported by IDL

Data Type Description

boolean Data item with either a TRUE or FALSE valuechar 8-bit, signed data itemdouble 64-bit IEEE floating-point numberint System-dependent signed integerfloat 32-bit IEEE floating-point numberlong 32-bit signed integershort 16-bit signed integerwchar_t Unicode character accepted only for 32-bit type librariesBSTR Length-prefixed stringCURRENCY 8-byte, fixed-point numberDATE 64-bit, floating-point fractional number of days since

December 30, 1899DECIMAL 98-bit, unsigned binary integer scaled by a power of 10.

Provides size and scale for a number (as in coordinates).SCODE Built-in error type that corresponds to VT_ERROR. An

SCODE (used on 16-bit systems only) does not containthe additional error information provided by anHRESULT.

VARIANT One of the variant data types described in Chapter 5IDispatch* Pointer to an IDispatch interface

Page 41: 6935316 DCOM Microsoft Distributed Component Object Model

IUnknown* Pointer to an IUnknown interfaceSAFEARRAY(TypeName) TypeName is any of the above types. An array of these

typesTypeName* TypeName is any of the above types. A pointer to a typevoid Allowed only as a function return type or in a parameter

list to indicate no argumentsHRESULT Return type used for reporting error information in

interfaces as described in Chapter 1LPWSTR Unicode string accepted only for 32-bit type librariesLPSTR Zero-terminated string

Once the IUserInfo interface is defined, we can use it in the definition of theUserInfo object. The UserInfo object is defined as an element of the UserInfolibrary element. The library element represents the type library as a whole, and isidentified by a library identifier (LIBID). LIBIDs, like CLSIDs and IIDs, are also GUIDs.Following is the IDL definition of the UserInfo library and UserInfo object:

//LIBID_UserInfo//These are the attributes of the type library[ uuid(acceeb00-86c7-11d0-94ab-0080c74c7e95), helpstring("UserInfo Type Library."), version(1.0)]//Definition of the UserInfo type librarylibrary UserInfo{ //CLSID_UserInfo //Attributes of the UserInfo object [ uuid(acceeb01-86c7-11d0-94ab-0080c74c7e95), helpstring("UserInfo Object.") ] //Definition of the UserInfo object coclass UserInfo { //List all of the interfaces supported by the object [default] interface IUserInfo; }}

Because a COM object may support many different interfaces, IDL supplies thedefault attribute to signal macro languages that IUserInfo is the interface to usefor programmatic control. Had the UserInfo COM object supported additionalinterfaces, they would have all been listed as part of the coclass UserInfo definition.The coclass defines the various interfaces supported by a particular COM object, whichultimately defines the object itself. Once we have defined the type library and all of theobjects and interfaces, we can compile the file using the Microsoft Interface Definition

Page 42: 6935316 DCOM Microsoft Distributed Component Object Model

Language (MIDL) compiler. The MIDL compiler takes UserInfo.idl and generates fivefiles: UserInfo.tlb, UserInfo_i.c, UserInfo_i.h, UserInfo_p.c, and dlldata.c.UserInfo.tlb is the actual compiled type library, which is essentially alanguage-independent header file. We’ll get to UserInfo_i.c and UserInfo_i.h in just aminute. The UserInfo_p.c and dlldata.c files contain code that can be used to generate aproxy/stub pair for marshaling and unmarshaling UserInfo function calls. Sincein-process servers are located in the process space of their client and thus don’t requiremarshaling, we will ignore these two files. Now, back to UserInfo_i.c and UserInfo_i.h.UserInfo_i.c contains the definitions of the human-readable names that are used to referto the IUserInfo interface, the type library, and the UserInfo object class.

const IID IID_IUserInfo = {0xacceeb02,0x86c7,0x11d0, {0x94,0xab,0x00,0x80,0xc7,0x4c,0x7e,0x95}};const CLSID CLSID_UserInfo = {0xacceeb01,0x86c7,0x11d0, {0x94,0xab,0x00,0x80,0xc7,0x4c,0x7e,0x95}};const IID LIBID_UserInfo = {0xacceeb00,0x86c7,0x11d0, {0x94,0xab,0x00,0x80,0xc7,0x4c,0x7e,0x95}};

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 43: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The UserInfo_i.h file contains the C and C++ definitions for the IUserInfo interface; wewill, however, focus our attention on the C++ definition. The IUserInfo interface isdeclared as a C++ abstract class, which means that at least one of its member functions is apure virtual function. Typically, whenever a C++ object is used to define an interface, all ofthe interface functions will be represented as pure virtual functions. By declaring eachinterface function as a pure virtual function, we force each object supporting the interface toprovide an implementation for each interface function. This is important, as we don’t wantclients attempting to invoke interface methods that aren’t actually implemented! Using C++abstract base classes to define COM interfaces also allows COM objects to separate functionaldefinition from implementation. For example, one developer implementing IUserInfo maychoose to store the values of each property in a database, while another developerimplementing IUserInfo may choose to store the values of each property in a table inmemory. A developer creating an IUserInfo client is shielded from these implementationspecifics and only knows that calling a particular function with the appropriate parameterswill result in a predictable outcome. By shielding clients from implementation specifics, asingle application can be developed to work with any number of COM objects in aplug-and-play fashion.

Implementing Interface Functions

While all of the interfaces have been defined, they are defined as C++ abstract base classes,which means that the C++ classes that define them cannot provide an implementation forthem. Implementation is provided in the C++ classes that subsequently inherit from theabstract base class. In our case, we will create the CUserInfo C++ class, which will inheritfrom the IUserInfo abstract base class. We will then provide the implementation ofIUserInfo via the CUserInfo C++ class. Had the UserInfo COM object supportedmultiple interfaces, we would’ve just defined CUserInfo such that it multiply inheritedfrom the abstract base class of each supported interface, supplying the implementations ofeach interface as part of the same CUserInfo C++ class. You will see an example ofmultiple-interface inheritance in the next chapter. In the class declaration for CUserInfothat follows, notice that the first three functions are QueryInterface, AddRef, andRelease, member functions of IUnknown from which IUserInfo itself is derived.Remember that every interface must inherit from IUnknown, which means that

Page 44: 6935316 DCOM Microsoft Distributed Component Object Model

QueryInterface, AddRef, and Release must be the first three functions of everyinterface that you create (see Listing 2-2).

Listing 2-2. The C++ class declaration for the CUserInfo object

class CUserInfo : IUserInfo{private: ULONG m_cRef; short m_nAge; LPSTR m_lpszName; BYTE m_bySex;public: //IUnknown STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppv); STDMETHODIMP_(ULONG)AddRef(void); STDMETHODIMP_(ULONG)Release(void); //IUserInfo STDMETHODIMP get_Age(short *nRetAge); STDMETHODIMP put_Age(short nAge); STDMETHODIMP get_Name(LPSTR *lpszRetName); STDMETHODIMP put_Name(LPSTR lpszname); STDMETHODIMP get_Sex(BYTE *byRetSex); STDMETHODIMP put_Sex(BYTE bySex); //Constructor CUserInfo(); //Destructor ~CUserInfo();};//CUserInfo

Now all we have to do is provide an implementation for each CUserInfo member function.The first COM-related function that we will investigate is QueryInterface.QueryInterface, as you recall, is used for interface navigation. If a client callsQueryInterface with the IID of a supported interface, the object should respond byreturning a pointer to that particular interface. In the case of the UserInfo COM object, if aclient calls QueryInterface with the IID for either IUnknown or IUserInfo,UserInfo should return a pointer to that interface. In theCUserInfo::QueryInterface function listed below, notice how the CUserInfoobject casts itself into either a IUserInfo pointer or an IUnknown pointer, depending onthe requested IID. If the requested interface is supported, QueryInterface calls AddRefto increase the reference count in accordance with COM’s reference counting rules. If all goeswell, QueryInterface reports NOERROR to the client. If a client requests an unsupportedinterface, which in this case would be any interface other than IUnknown or IUserInfo,QueryInterface returns E_NOINTERFACE to notify the client that the requestedinterface is not supported. NOERROR and E_NOINTERFACE may look familiar, as they wereintroduced in the last chapter in Table 1-3: Commonly Used HRESULT Return ValueConstants.

STDMETHODIMP CUserInfo::QueryInterface(REFIID iid, LPVOID *ppv){ *ppv = NULL; if (IID_IUnknown == iid)

Page 45: 6935316 DCOM Microsoft Distributed Component Object Model

*ppv = (LPVOID)(IUnknown *)this; else if (IID_IUserInfo == iid) *ppv = (LPVOID)(IUserInfo *)this; else return E_NOINTERFACE; //Interface not supported //Perform reference count through the returned interface ((IUnknown *)*ppv)->AddRef(); return NOERROR;}//QueryInterface

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 46: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The next COM-specific function that we investigate is the AddRef function.Whenever the number of outstanding references to an object is increased,AddRef is called to increase the object’s internal reference counter:

STDMETHODIMP_(ULONG)CUserInfo::AddRef(void){ return ++m_cRef;}//AddRef

The Release function is the opposite of the AddRef function. Whenever thenumber of outstanding references to an object is decreased, Release is calledto decrease the object’s internal reference counter. When there are nooutstanding references, the object deletes itself. Once the object has deleteditself, the global object counter g_cObjects is decremented to reflect the factthat there is now one less object in existence. After all of this, theServerCanUnloadNow function is consulted to determine if it is all right forthe entire COM server to unload itself from memory, a feat that would beaccomplished by the UnloadModule function. But, because DLLs aren’tresponsible for unloading themselves, the UnloadModule function simplyreturns. However, if the UserInfo COM object were being implemented in anEXE server, UnloadModule would actually unload the EXE server frommemory. (You learn more about EXE servers in the next chapter.) Suffice it tosay that I have provided the UnloadModule function as a way to shield theobject from the differences between unloading a DLL server and unloading anEXE server, should you decide on your own to implement the UserInfo COMobject in an EXE server. (See the sidebar “Encapsulating Server PackagingSpecifics.”)

STDMETHODIMP_(ULONG)CUserInfo::Release(void){ m_cRef-;

Page 47: 6935316 DCOM Microsoft Distributed Component Object Model

if (0 == m_cRef) { delete this; //Decrement the global object count g_cObjects-; //See if it's alright to unload the server if (::ServerCanUnloadNow()) ::UnloadServer(); return 0; } return m_cRef;}//Release

The rest of the implementation of the CUserInfo object is vanilla C++ and notvery COM-specific. However, the full implementation of the CUserInfoobject is provided in Listing 2-8 if you are so inclined. Now let’s turn ourattention to creating the class factory that is ultimately responsible for creatingindividual UserInfo COM objects.

Implementing a Class Factory

So far, we have seen how to define and implement the UserInfo COM, butwe don’t have a way to actually instantiate an instance of the object. Sure, wecould just instantiate a single instance of the object when the DLL server is firstloaded, but what if the client needs to create more than just a single instance ofthe UserInfo object? In order to provide the client with a mechanism forcontrolling the object instantiation process, COM employs the concept of a classfactory, and has defined the IClassFactory interface. A class factory is anobject that implements the IClassFactory interface and is ultimatelyresponsible for creating other COM objects. The IClassFactory interfacehas only two methods, LockServer and CreateInstance. We will look atthe LockServer function in the section “Server Unloading.” TheCreateInstance function is called whenever a client wants to instantiate aninstance of a particular COM object; through a call to CoCreateInstance,for example. The CUserInfoFactory is derived from IClassFactory,and it is the class factory responsible for creating UserInfo objects. TheCreateInstance method of the CUserInfoFactory object can be seenin Listing 2-3. Notice how once a CUserInfo object is created, CUserInfoClassFactory calls the newly created CUserInfo object’sQueryInterface function. This allows clients using CreateInstance tocreate an instance of a COM object and receive a pointer to a specific interfaceall in one call. Since an object has an initial reference count of zero uponinstantiation, the call to QueryInterface also serves to increment theobject’s reference count to one. If all goes well, CreateInstance alsoincrements a global object counter, which is used to keep track of the totalnumber of objects being served. Since in-process servers cannot maintain theirown global memory, the global object counter is really the total number ofobjects being used by a particular client.

Listing 2-3. CUserInfoFactory::CreateInstance

Page 48: 6935316 DCOM Microsoft Distributed Component Object Model

STDMETHODIMP CUserInfoFactory::CreateInstance (IUnknown* pUnknownOuter, REFIID iid, LPVOID *ppv){ HRESULT hr; CUserInfo *pCUserInfo = NULL;

*ppv = NULL; //This object doesn't support aggregation if (NULL != pUnknownOuter) return CLASS_E_NOAGGREGATION; //Create the CUserInfo object pCUserInfo = new CUserInfo(); if (NULL == pCUserInfo) return E_OUTOFMEMORY; //Retrieve the requested interface hr = pCUserInfo->QueryInterface(iid, ppv); if (FAILED(hr)) { delete pCUserInfo; pCUserInfo = NULL; return hr; } //Increment the global object counter g_cObjects++;

return NOERROR;}//CreateInstance

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 49: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Registering Class Information

In order for COM’s implementation locator services to locate, load, and launch yourserver, you must add each COM object’s class information to the system registry, whichis typically done once, as part of the installation process. Therefore, every server mustprovide a mechanism through which it can be notified to either register or unregister classinformation. COM requires that in-process servers provide this capability through theDllRegisterServer and DllUnregisterServer functions, which are definedin <olectl.h>. Applications like REGSVR32.EXE that are used to register and unregisterin-process COM servers simply call the Win32 API function LoadLibrary to load aparticular server, and then call the Win32 API function GetProcAddress to obtain apointer to either the DllRegisterServer or DllUnregisterServer function.

When a UserInfo client invokes the DllRegisterServer function, the servermust add the UserInfo CLSID as a subkey under theHKEY_CLASSES_ROOT\CLSID key and optionally provide a textual description of theUserInfo COM object. The server must also add the “InprocServer32” subkey andprovide the path of the UserInfo.dll server. The following is a representation of theinformation that the UserInfo server must add to the system registry:

HKEY_CLASSES_ROOT CLSID {acceeb01-86c7-11d0-94ab-0080c74c7e95} = Description InprocServer32 = C:\UserInfo\UserInfo.dll

I designed the SetRegKeyValue function to help expedite the process of updating thesystem registry (see Listing 2-4). SetRegKeyValue uses the Win32 API functionsRegCreateKeyEx, RegSetValueEx, and RegCloseKey to actually add or updateinformation in the registry.

Listing 2-4. The SetRegKeyValue function

Page 50: 6935316 DCOM Microsoft Distributed Component Object Model

BOOL SetRegKeyValue(LPTSTR lpszkey, LPTSTR lpszSubKey, LPTSTR lpszValue){ BOOL bOk = FALSE; long lErrorCode; HKEY hKey; _TCHAR szKeY[MAX_STRING_LENGTH + 1];

_tcscpy(szKey, lpszkey); if (NULL != lpszSubKey) { _tcscat(szKey, _TEXT("\\")); _tcscat(szKey, lpszSubKey); } lErrorCode = RegCreateKeyEx(HKEY_CLASSES_ROOT, szKey, O, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL); if (ERROR_SUCCESS == lErrorCode) { lErrorCode = RegSetValueEx(hKey, NULL, O, REG_SZ, (BYTE *)lpszValue, sizeof(lpszValue) / sizeof(_TCHAR)); if (ERROR_SUCCESS == lErrorCode) bOk = TRUE; RegCloseKey(hKey); }

return bOk;}//SetRegKeyValue

The following code snippet shows how the DllRegisterServer function usesSetRegKeyValue to update appropriate information in the registry:

bOK = SetRegKeyValue(szCLSIDKey, NULL, _TEXT("DCOM Enterprise Apps - UserInfo Object.")); if (bOK) bOK = SetRegKeyValue(szCLSIDKey, _TEXT("InProcServer32"), szModulePath);

When a client invokes the DllUnregisterServer function, the server must removeits information from the system registry. The following code snippet shows howDllUnregisterServer uses the Win32 API function RegDeleteKey to removeinformation from the registry:

//Delete sub-keys first lErrorCode = RegDeleteKey(HKEY_CLASSES_ROOT, szInprocServer32Key); //Delete the entry under CLSID. if (ERROR_SUCCESS == lErrorCode) lErrorCode = RegDeleteKey(HKEY_CLASSES_ROOT,

Page 51: 6935316 DCOM Microsoft Distributed Component Object Model

szCLSIDKey);

Exposing the Class Factory

When a client calls the COM API CoCreateInstance, the COM library makes a callto the COM API function CoGetClassObject to retrieve a pointer to theIClassFactory interface responsible for creating objects of the desired CLSID,which is supplied as the first parameter to CoCreateInstance. To obtain theappropriate IClassFactory pointer from an in-process server, COM checks theregistry to retrieve the pathname of the appropriate server. After obtaining the server’spathname, COM calls the COM API function CoLoadLibrary to load the server intomemory. Once the server is loaded into memory, COM calls the Win32 API functionGetProcAddress to request the address of the DllGetClassObject function.COM requires that every in-process server implement and expose aDllGetClassObject function. Based on a CLSID that is passed to it,DllGetClassObject is responsible for creating the appropriate class factory. Afterobtaining the appropriate IClassFactory interface pointer, the COM system servicescall IClassFactory:: CreateInstance to instantiate the desired COM object.This entire process is illustrated in Figure 2-1.

Figure 2-1  The COM object instantiation process

The DllGetClassObject function begins by validating that the object identified bythe CLSID parameter can be created by the server; if it can’t, DllGetClassObjectreturns CLASS_E_CLASSNOTAVAILABLE. However, if the server is capable ofcreating the object, DllGetClassObject creates an instance of the class factoryresponsible for creating the desired object. Once the class factory is instantiated,DllGetClassObject calls QueryInterface, passing it the client-specified IID,which in most cases is IClassFactory. The call to QueryInterface also servesto increment the class factory object’s reference count from zero to one (see Listing 2-5).

Listing 2-5. The DllGetClassObject function

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv){ CUserInfoFactory *pCUserInfoFactory = NULL; HRESULT hr = NOERROR;

if (CLSID_UserInfo == rclsid) { //Create the UserInfo classFactory pCUserInfoFactory = new CUserInfoFactory(); //Check for out of memory error if (NULL == pCUserInfoFactory)

Page 52: 6935316 DCOM Microsoft Distributed Component Object Model

return E_OUTOFMEMORY; //Get the requested interface hr = pCUserInfoFactory->QueryInterface(riid, ppv); if (FAILED(hr)) { delete pCUserInfoFactory; pCUserInfoFactory = NULL; return hr; } } else //Object not supported hr = CLASS_E_CLASSNOTAVAILABLE;

return hr;}//DllGetClassObject

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 53: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Server Unloading

Typically, when the last reference on the last object being served by a server is released, theserver is unloaded. However, if the server is responsible for creating objects that typicallyhave a short life span, it may be desirable to keep the server loaded in memory even when itisn’t serving any objects, thus eliminating the overhead associated with loading andunloading the server. To accommodate this functionality, the IClassFactory interfacesupports the LockServer function. LockServer takes a single boolean parameter thatdetermines whether or not the server should be “locked” in memory. If the value is true, theglobal reference counter is incremented. If the value is false, the global reference counter isdecremented. Only when there are no locks and no instantiated objects can the server unload:

STDMETHODIMP CUserInfoFactory::LockServer(BOOL block){ if (bLock) g-cLocks++; else { g_cLocks-; //See if it's alright to unload the server if (::ServerCanUnloadNow()) ::UnloadServer(): } return NOERROR;}//LockServer

Because an in-process server cannot unload itself, the operating system must ask the server ifit can be unloaded by calling the DllCanUnloadNow function. This means that everyin-process server is responsible for implementing and exposing the DllCanUnloadNowfunction. This is a relatively simple function that returns S-OK if there are no existinginstances of objects and no outstanding locks; otherwise, the function returns S_FALSE.

Page 54: 6935316 DCOM Microsoft Distributed Component Object Model

Encapsulating Server Packaging Specifics

There will often be times when a particular COM object needs to be exposed by both anin-process server and an out-of-process server. I have tried to encapsulate theimplementation differences between in-process servers and out-of-process servers as muchas possible in order to shield the individual COM objects from these server packagingspecifics. As you will see in Chapter 3, the biggest differences between in-process andout-of-process servers are in the areas of registering class information and serverunloading. Both of these differences occur as a result of the in-process server’s ability todirectly export functions for client consumption. Since out-of-process servers maintainan address space apart from the client, they do not have the ability to simply exportfunctions for use by other processes. However, all COM servers are responsible forproviding mechanisms for registering class information and server unloading.

As you know, DLL servers are responsible for exporting the two functionsDLLRegisterServer and DLLUnregisterServer for the explicit purpose ofregistering and unregistering class information. (Source code for the UserInfo object’simplementation of DLLRegisterServer and DLLUnregisterServer can be seenin Listing 2.6.) EXE servers, on the other hand, register their class information in responseto the /REGSERVER or -REGSERVER command-line arguments and unregister their classinformation in response to the /UNREGSERVER or -UNREGSERVER command-linearguments. In the spirit of the DLLRegisterServer and DLLUnregisterServerfunctions used by DLL servers to manage registration information, I created theRegisterServer and UnregisterServer functions, which are used by the variousEXE servers created throughout this book to manage their registration information. As partof their WinMain entry point, each EXE server searches for the defined registrationcommand-line arguments, and calls either RegisterServer or UnregisterServeras appropriate (see Listing 3-4). (Implementations of the RegisterServer andUnregisterServer functions can be seen in Listing 3-5.)

When it comes to server unloading, DLL servers again have the luxury of simply exportingthe DllCanUnloadNow function, which is called automatically by the COM systemservices to determine whether or not a particular COM server can be unloaded frommemory. EXE servers, on the other hand, are responsible for unloading themselves. In bothcases, a server is only unloaded when there are no outstanding objects and no class factorylocks. (Class factory locks are explained in Chapter 3.) In both cases, I have created theServerCanUnloadNow function, which is used to determine when it is appropriate tounload a server from memory:

BOOL ServerCanUnloadNow(void){ //The server can unload if there are no outstanding //objects or class factory locks if(O == g_cObjects && O == g_cLocks) return TRUE; else return FALSE;}//ServerCanUnloadNow

ServerCanUnloadNow is called internally by the server itself, whenever the lastoutstanding interface reference count of a supported COM object is released, and alsowhenever an object class factory lock is released. In addition, DLL servers call

Page 55: 6935316 DCOM Microsoft Distributed Component Object Model

ServerCanUnloadNow as part of their implementation of DllCanUnloadNow,which again is called automatically by COM to determine whether or not a server can beunloaded from memory:

STDAPI DllCanUnloadNow(void){ if (ServerCanUnloadNow()) return S_OK; else return S_FALSE;}//DllCanUnloadNow

Whenever the last outstanding interface reference count of a supported COM object isreleased, or an object class factory lock is released, the implementation callsServerCanUnloadNow to determine whether there are any outstanding objects or classfactory locks. If there aren’t, the implementation calls UnloadServer to unload theserver from memory:

STDMETHODIMP_(ULONG)CUserInfo::Release(void){ m_cRef-; if (0 == m-cRef) { delete this; //Decrement the global object count g_cObjects-; //See if it's alright to unload the server if (::ServerCanUnloadNow()) ::UnloadServer(); return 0; } return m_cRef;}//Release

Because COM automatically unloads DLL servers in response to DllCanUnloadNow,the DLL version of UnloadServer simply returns:

void UnloadServer(void){ //Since DLLs aren't responsible for unloading themselves, //simply return return;}//UnloadServer

However, EXE servers are responsible for unloading themselves. Therefore, the EXEversion of UnloadServer unloads the server by posting the WM_QUIT message to theserver’s message queue:

void UnloadServer(void){ //Unload the server by posting the WM_QUIT to the message

Page 56: 6935316 DCOM Microsoft Distributed Component Object Model

queue PostQuitMessage(O);}//UnloadServer

The last thing that I have done to help encapsulate the server packaging differences is tophysically encapsulate the DLL-specific code in a file called DLLMain.cpp. Similarly, forEXE servers, the EXE-specific code is physically encapsulated in a file calledEXEMain.cpp. By encapsulating the packaging-specific code, the actual objectimplementation files can be included as part of a totally separate project, perhaps toimplement the object in a different execution context.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 57: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

All that is needed now to build the UserInfo server is a module definition file (.def).The module definition file is used to export the DllRegisterServer,DllUnregisterServer, DllGetClassObject, and DllCanUnloadNowfunctions from the DLL. The module definition file for the UserInfo server looks likethe following:

LIBRARY UserInfoDESCRIPTION "UserInfo In-Process Server."EXPORTS DllRegisterServer @1 PRIVATE DllUnregisterServer @2 PRIVATE DllGetClassObject @3 PRIVATE DllCanUnloadNow @4 PRIVATE

The UserInfo server is composed of several files: UserInfo.idl, UserInfo.h,UserInfo.cpp, DllMain.cpp, and UserInfo.def.

•  UserInfo.idl contains the IDL definitions of the UserInfo type library; theIUserInfo interface; and the UserInfo COM object. UserInfo.idl is compiledusing the MIDL compiler to generate the UserInfo_i.c, UserInfo_i.h, UserInfo.tlb,UserInfo_p.c, and dlldata.c files, of which only UserInfo_i.c and UserInfo_i.h areused. UserInfo_i.c contains the declarations of the CLSID, IID, and LIBID, and theUserInfo_i.h file contains C/C++ definitions for the IUserInfo interface.UserInfo_p.c and dlldata.c are automatically generated by the MIDL compiler incase you need to create a proxy/stub pair. Proxy/stub pairs are covered in more detailin Chapter 3. However, because in-process servers are loaded directly into theirclients’ address space and thus don’t require proxies or stubs, we don’t need theUserInfo-p.c and dlldata.c files.

•  UserInfo.h contains the definition of the CUserInfo C++ object that inheritsfrom the IUserInfo interface. The UserInfo.h file also contains the definition ofthe CUserInfoFactory class factory that is responsible for creating theUserInfo COM object.

Page 58: 6935316 DCOM Microsoft Distributed Component Object Model

•  UserInfo.cpp contains the implementation for both the CUserInfo andCUserInfoFactory C++ objects.

•  DllMain.cpp contains functions specific to implementing and registeringin-process COM servers.

•  UserInfo.def is a module definition file used to export functions from the DLL.

DllMain.cpp can be seen in Listing 2-6; UserInfo.h in Listing 2-7; and UserInfo.cpp inListing 2-8. As you look at the source code, notice how the DLL-specific code has beenconfined to just the DllMain.cpp source file, making it easier to implement UserInfo asan EXE if you so choose. (You can find more on creating EXE servers in the next chapter.)

Listing 2-6. DllMain.cpp

////DLLMain.cpp//#define MAX_STRING_LENGTH 255#define GUID_SIZE 128

#include <objbase.h>#include <olectl.h> //for DLLRegisterServer and //DLLUnregisterServer#include <tchar.h>#include "UserInfo.h"

////Forward declarations//BOOL SetRegKeyValue(LPTSTR lpszkey, LPTSTR lpszSubKey, LPTSTR lpszValue);BOOL ServerCanUnloadNow(void);void UnloadServer(void);////Global variables//HMODULE g_hModule = NULL;ULONG g_cObjects = 0;ULONG g_cLocks = 0;

////DllRegisterServer//STDAPI DllRegisterServer(void){ BOOL bOK; _TCHAR szModulePath[MAX_PATH + 1]; _TCHAR szCLSID[GUID_SIZE + 1]; _TCHAR szCLSIDKey[MAX_STRING_LENGTH + 1]; wchar_t wszGUID[GUID_SIZE + 1];

//Obtain the path to server's executable file for

Page 59: 6935316 DCOM Microsoft Distributed Component Object Model

//later use GetModuleFileName(g_hModule, szModulePath, sizeof(szModulePath) / sizeof(_TCHAR)); //Convert the CLSID to the format //{00000000-0000-0000-0000-000000000000} StringFromGUID2(CLSID_UserInfo, wszGUID, sizeof(wszGUID) / sizeof(wchar_t));#ifdef _UNICODE //UNICODE _tcscpy(szCLSID, wszGUID);#else //SBCS and MBCS //Convert from the wide character set to the //multibyte character set WideCharToMultiByte(CP_ACP, 0, wszGUID, -1, szCLSID, sizeof(szCLSID) / sizeof(_TCHAR), NULL, NULL);#endif //HKEY_CLASSES_ROOT\CLSID\{00000000-0000-0000-0000- //000000000000} _tcscpy(szCLSIDKey, _TEXT("CLSID\\")); _tcscat(szCLSIDKey, szCLSID); bOK = SetRegKeyValue(szCLSIDKey, NULL, _TEXT("DCOM Enterprise Apps - UserInfo Object.")); if (bOK) bOK = SetRegKeyValue(szCLSIDKey, _TEXT("InProcServer32"), szModulePath); if (bOK) return NOERROR; else return SELFREG_E_CLASS;}//DllRegisterServer

////DllUnregisterServer//STDAPI DllUnregisterServer(void){ long lErrorCode; _TCHAR szCLSID[GUID_SIZE + 1]; _TCHAR szCLSIDKey[MAX_STRING_LENGTH + 1]; _TCHAR szInprocServer32Key[MAX_STRING_LENGTH + 1]; wchar_t wszGUID[GUID_SIZE + 1];

//Convert the CLSID to the format //{00000000-0000-0000-0000-000000000000} StringFromGUID2(CLSID_UserInfo, wszGUID, sizeof(wszGUID) / sizeof(wchar_t));#ifdef _UNICODE //UNICODE _tcscpy(szCLSID, wszGUID);

Page 60: 6935316 DCOM Microsoft Distributed Component Object Model

#else //SBCS and MBCS //Convert from the wide character set to the //multibyte character set WideCharToMultiByte(CP_ACP, 0, wszGUID, -1, szCLSID, sizeof(szCLSID) / sizeof(_TCHAR), NULL, NULL);#endif //HKEY_CLASSES_ROOT\CLSID\{00000000-0000-0000-0000- //000000000000} _tcscpy(szCLSIDKey, _TEXT("CLSID\\")); _tcscat(szCLSIDKey, szCLSID); _tcscpy(szInprocServer32Key, szCLSIDKey); _tcscat(szInprocServer32Key, _TEXT("\\InProcServer32")); //Delete sub-keys first lErrorCode = RegDeleteKey(HKEY_CLASSES_ROOT, szInprocServer32Key); //Delete the entry under CLSID. if (ERROR-SUCCESS == lErrorCode) lErrorCode = RegDeleteKey(HKEY_CLASSES_ROOT, szCLSIDKey); if (ERROR_SUCCESS == lErrorCode) return NOERROR; else return SELFREG_E_CLASS;}//DllUnregisterServer

////DllGetClassObject//STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv){ CUserInfoFactory *pCUserInfoFactory = NULL; HRESULT hr = NOERROR;

if (CLSID_UserInfo == rclsid) { //Create the UserInfo classFactory pCUserInfoFactory = new CUserInfoFactory(); //Check for out of memory error if (NULL == pCUserInfoFactory) return E_OUTOFMEMORY; //Get the requested interface hr = pCUserInfoFactory->QueryInterface(riid, ppv); if (FAILED(hr)) { delete pCUserInfoFactory; pCUserInfoFactory = NULL; return hr; }

Page 61: 6935316 DCOM Microsoft Distributed Component Object Model

} else //Object not supported hr = CLASS_E_CLASSNOTAVAILABLE;

return hr;}//DllGetClassObject

////DllCanUnloadNow//STDAPI DllCanUnloadNow(void){ if (ServerCanUnloadNow()) return S_OK; else return S_FALSE;}//DllCanUnloadNow

////SetRegKeyValue//BOOL SetRegKeyValue(LPTSTR lpszkey, LPTSTR lpszSubKey, LPTSTR lpszValue){ BOOL bOk = FALSE; long lErrorCode; HKEY hKey; _TCHAR szKey[MAX_STRING_LENGTH + 1];

_tcscpy(szKey, lpszkey); if (NULL != lpszSubKey) { _tcscat(szKey, _TEXT("\\")); _tcscat(szKey, lpszSubKey); } lErrorCode = RegCreateKeyEx(HKEY_CLASSES_ROOT, szkey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL); if (ERROR_SUCCESS == lErrorCode) { lErrorCode = RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)lpszValue, sizeof(lpszValue) / sizeof(_TCHAR)); if (ERROR_SUCCESS == lErrorCode) bOk = TRUE; RegCloseKey(hKey); }

return bOk;

Page 62: 6935316 DCOM Microsoft Distributed Component Object Model

}//SetRegKeyValue

////ServerCanUnloadNow//BOOL ServerCanUnloadNow(void){ //The server can unload if there are no outstanding //objects or class factory locks if(0 == g_cObjects && 0 == g-cLocks) return TRUE; else return FALSE;}//ServerCanUnloadNow

////UnloadServer//void UnloadServer(void){ //Since DLLs aren't responsible for unloading themselves, //simply return return;}//UnloadServer

////DllMain//BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved){ //Save the dll module handle for later use if (DLL_PROCESS_ATTACH == dwReason) g_hModule = hModule;

return TRUE;}//DllMain

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 63: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Listing 2-7. UserInfo.h

////UserInfo.h//#if !defined USERINFO_H#define USERINFO_H

#include "UserInfo_i.h"

////CUserInfo object//class CUserInfo : IUserInfo{private: ULONG m-cRef; short m_nAge; LPSTR m-lpszName; BYTE m-bySex;public: //IUnknown STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppv); STDMETHODIMP_(ULONG)AddRef(void); STDMETHODIMP_(ULONG)Release(void); //IUserInfo STDMETHODIMP get_Age(short *nRetAge); STDMETHODIMP put_Age(short nAge); STDMETHODIMP get_Name(LPSTR *lpszRetName); STDMETHODIMP put_Name(LPSTR lpszName); STDMETHODIMP get_Sex(BYTE *byRetSex); STDMETHODIMP put_Sex(BYTE bySex); //Constructor

Page 64: 6935316 DCOM Microsoft Distributed Component Object Model

CUserInfo(); //Destructor ~CUserInfo();};//CUserInfo

////CUserInfoFactory//class CUserInfoFactory : public IClassFactory{private: ULONG m-cRef;public: //IUnknown STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppv); STDMETHODIMP_(ULONG)AddRef(void); STDMETHODIMP_(ULONG)Release(void); //IClassFactory STDMETHODIMP CreateInstance(IUnknown* pUnknownOuter, REFIID iid, LPVOID *ppv); STDMETHODIMP LockServer(BOOL block); //Constructor CUserInfoFactory*() { m_cRef = 0; }};//CUserInfoFactory

#endif

Listing 2-8. UserInfo.cpp

////UserInfo.cpp//#include "UserInfo.h"

////Forward declarations//extern BOOL ServerCanUnloadNow(void);extern void UnloadServer(void);

////Global variables//extern ULONG g_cObjects;extern ULONG g_cLocks;

////CUserInfo//

Page 65: 6935316 DCOM Microsoft Distributed Component Object Model

////get_Age//STDMETHODIMP CUserInfo::get_Age(short *nRetAge){ *nRetAge = m_nAge; return NOERROR;}//get_Age

////put-Age//STDMETHODIMP CUserInfo::put_Age(short nAge){ m_nAge = nAge; return NOERROR;}//put_Age

////get_Name//STDMETHODIMP CUserInfo::get_Name(LPSTR *lpszRetName){ *lpszRetName = m_lpszName; return NOERROR;}//get_Name

////put_Name//STDMETHODIMP CUserInfo::put_Name(LPSTR lpszName){ long lStringLen;

//Deallocate any previously allocated storage if (m_lpszName) delete[] m_lpszName; m_lpszName = NULL; //Allocate enough storage for the string lStringLen = strlen(lpszName); if (IStringLen > 0) { m_lpszName = new char[lStringLen + 1]; //Copy the string strcpy(m_lpszName, lpszName); } return NOERROR;}//put_Name

////get_Sex//

Page 66: 6935316 DCOM Microsoft Distributed Component Object Model

STDMETHODIMP CUserInfo::get_Sex(BYTE *byRetSex){ *byRetSex = m_bySex; return NOERROR;}//get_Sex

////put_Sex//STDMETHODIMP CUserInfo::put_Sex(BYTE bySex){ m_bySex = bySex; return NOERROR;}//put_Sex

////QueryInterface//STDMETHODIMP CUserInfo::QueryInterface(REFIID iid, LPVOID *ppv){ *ppv = NULL; if (IID_IUnknown == iid) *ppv = (LPVOID)(IUnknown *)this; else if (IID_IUserInfo == iid) *ppv = (LPVOID)(IUserInfo *)this; else return E_NOINTERFACE; //Interface not supported //Perform reference count through the returned interface ((IUnknown *)*ppv)->AddRef(); return NOERROR;}//QueryInterface

////AddRef//STDMETHODIMP_(ULONG)CUserInfo::AddRef(void){ return ++m_cRef;}//AddRef

////Release//STDMETHODIMP_(ULONG)CUserInfo::Release(void){ m_cRef-; if (0 == m-cRef) { delete this; //Decrement the global object count g_cObjects-; //See if it's alright to unload the server

Page 67: 6935316 DCOM Microsoft Distributed Component Object Model

if (::ServerCanUnloadNow()) ::UnloadServer(); return 0; } return m_cRef;}//Release

////Constructor//CUserInfo::CUserInfo(){ m_cRef = 0; m_nAge = 0; m_lpszName = NULL; m_bySex = 'M';}//CUserInfo

////Destructor//CUserInfo::~CUserInfo(){ if (m_lpszName) delete[] m_lpszName;}//~CUserInfo

////CUserInfoFactory Class Factory//

////CreateInstance//STDMETHODIMP CUserInfoFactory::CreateInstance (Iunknown* pUnknownOuter, REFIID iid, LPVOID *ppv){ HRESULT hr; CUserInfo *pCUserInfo = NULL;

*ppv = NULL; //This object doesn't support aggregation if (NULL != pUnknownOuter) return CLASS_E_NOAGGREGATION; //Create the CUserInfo object pCUserInfo = new CUserInfo(); if (NULL == pCUserInfo) return E_OUTOFMEMORY; //Retrieve the requested interface hr = pCUserInfo->QueryInterface(iid, ppv); if (FAILED(hr)) {

Page 68: 6935316 DCOM Microsoft Distributed Component Object Model

delete pCUserInfo; pCUserInfo = NULL; return hr; } //Increment the global object counter g_cObjects++;

return NOERROR;}//CreateInstance

////LockServer//STDMETHODIMP CUserInfoFactory::LockServer(BOOL bLock){ if (bLock) g_cLocks++; else { g_cLocks--; //See if it's alright to unload the server if (::ServerCanUnloadNow()) ::UnloadServer(); } return NOERROR;}//LockServer

////QueryInterface//STDMETHODIMP CUserInfoFactory::QueryInterface (REFIID iid, LPVOID *ppv){ *ppv = NULL; if (IID_IUnknown == iid) *ppv = (LPVOID)(IUnknown *)this; else if (IID-IClassFactory == iid) *ppv = (LPVOID)(IClassFactory *)this; else return E_NOINTERFACE; //Interface not supported //Perform reference count through the returned interface ((IUnknown *)*ppv)->AddRef(); return NOERROR;}//QueryInterface

////AddRef//STDMETHODIMP_(ULONG) CUserInfoFactory::AddRef(void){ return ++m-cRef;}//AddRef

Page 69: 6935316 DCOM Microsoft Distributed Component Object Model

////Release//STDMETHODIMP_(ULONG) CUserInfoFactory::Release(void){ m_cRef-; if (0 == m-cRef) { delete this; return 0; } return m_cRef;}//Release

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 70: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Before you can use the UserInfo server, you must register it. To register the UserInfoserver, use REGSVR32.EXE, which should be in either your Windows\System directory oryour Windows\System32 directory, depending on whether you’re running Win95 or WindowsNT. REGSVR32.EXE works in the manner described in the section “Registering ClassInformation.” The following shows how to use REGSVR32.EXE to register the UserInfoserver:

C:>Regsvr32 UserInfo.dll

REGSVR32.EXE can also be used to unregister the UserInfo server:

C:>Regsvr32 /u UserInfo.dll

Now all we need is a client capable of using the UserInfo server!

The UserInfoClient Application

To test our COM server, we will build the UserInfoClient application. TheUserInfoClient application will create and use the UserInfo COM object that we’ve justdeveloped. Once the UserInfo object is created, we will use QueryInterface to change tothe IUserInfo interface, which we will then use to set each of the UserInfo object’sproperties. After setting each property, we will retrieve, format, and display their values via theWin32 API function MessageBox, which simply creates and displays a small windowcontaining a message. Before we build the UserInfoClient application, note theresponsibilities of a COM client:

•  Initializing the COM Library

•  Obtaining initial and subsequent interfaces

•  Manipulating the COM object

•  Releasing the COM object when it is no longer needed

•  Uninitializing the COM Library

Initializing the COM Library

Page 71: 6935316 DCOM Microsoft Distributed Component Object Model

The first item of business for COM clients is initializing the COM Library by calling the COMAPI function CoInitialize. The CoInitialize function is provided by COM toinitialize the COM Library, and it must be called before any other COM Library calls, except theCoGetMalloc function and memory allocation calls. Usage of CoInitialize is prettystraightforward.

//Initialize the COM Libraryhr = CoInitialize(NULL);if ( SUCCEEDED(hr) ){ ...}

Obtaining an Initial Interface

Once the COM Library is initialized, we have to obtain an initial interface pointer to theUserInfo object. You can obtain an initial interface pointer in several different ways, the mostpopular of which is to call CoCreateInstance. The UserInfoClient application callsCoCreateInstance with the CLSID of the UserInfo COM object and requests a pointerto its IUnknown interface. However, in order to resolve both the CLSID_UserInfo constantand the definition of the IUserInfo interface, the UserInfoClient application mustinclude the UserInfo_i.c and UserInfo_i.h files. As you might recall from earlier in this chapter,we used the MIDL compiler to compile the UserInfo.idl file, which generated these two files.Note the use of the CLSCTX_INPROC_SERVER class execution context constant to specify thatwe are only interested in an in-process server for the CLSID_UserInfo object:

hr = CoCreateInstance(CLSID_UserInfo, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID *)&pIUnknown);if ( SUCCEEDED(hr) ){...}

While we are only interested in the CLSCTX_INPROC_SERVER class context, COM definesthe additional class execution contexts in Table 2-3:

Table 2-3 Defined Class Execution Contexts (CLSCTX)

Execution Context Meaning

CLSCTX_INPROC_SERVER In-Process serverCLSCTX_INPROC_HANDLER In-Process proxyCLSCTX_LOCAL_SERVER Local serverCLSCTX_REMOTE_SERVER Remote serverCLSM_SERVER CLSCTX_INPROC_SERVER,

CLSCTX_LOCAL_SERVER, orCLSCTX_REMOTE_SERVER

CLSCTX_ALL CLSCTX_INPROC_HANDLER or CLSCTX_SERVER

Manipulating a COM Object

If the call to CoCreateInstance is successful, it will return a pointer to the IUnknown

Page 72: 6935316 DCOM Microsoft Distributed Component Object Model

interface in the pIUnknown parameter. After obtaining an initial interface pointer, we can beginto manipulate the object. As all of the action seems to take place in the IUserInfo interface,let’s go there! A simple QueryInterface call requesting the IID_IUserInfo pointer is allit takes to obtain a pointer to the IUserInfo interface. Once we have obtained an IUserInfointerface pointer, we can set any of the available properties. The UserInfoClientapplication sets the Age, Name, and Sex properties. UserInfoClient then retrieves eachproperty value back, formats their values, and displays them.

Releasing the COM Object

When the UserInfoClient application has finished manipulating the UserInfo object, itcalls the Release function on all of its outstanding interface pointers, which includes oneIUnknown pointer and one IUserInfo pointer. The UserInfo object then destroys itself:

//Release the IUserInfo interfacepIUserInfo->Release();//Release the IUnknown interfacepIUnknown->Release();

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 73: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Uninitializing the COM Library

After releasing the UserInfo COM object, the UserInfoClient prepares to terminateitself by uninitializing the COM Library with a call to CoUninitialize. Every successfulcall to CoInitialize must be matched with a call to CoUninitialize. OnceCoUninitialize has been called, no other COM APIs should be called, with the exceptionof the CoGetMalloc function and the memory allocation calls.

//Shut down the COM LibraryCoUninitialize();

The source code for the UserInfoClient can be found in Listing 2-9.

Listing 2-9. UserInfoClient.cpp

////UserInfoClient.cpp//#include <windows.h>#include <objbase.h>#include <tchar.h>#include "UserInfo_i.h"

////Forward declarations//void DisplayMessage(LPTSTR lpMessage);void DisplayUserInfo(IUserInfo *pIUserInfo, LPTSTR lpszRetrievedMsg);

////Global variables//const_TCHAR g_lpszApplicationTitle[] = _TEXT("UserInfoClient");

Page 74: 6935316 DCOM Microsoft Distributed Component Object Model

////DisplayMessage//void DisplayMessage(LPTSTR lpszmessage){ MessageBox(NULL, lpszMessage, g_lpszApplicationTitle, MB_OK | MB_ICONEXCLAMATION);}//DisplayMessage

////DisplayUserInfo//void DisplayUserInfo(IUserInfo *pIUserInfo, LPTSTR lpszRetrievedMsg){ char szAge[25]; LPSTR lpszName = NULL; short nAge; unsigned char bySex; char szDisplayText[255]; _TCHAR szMsgText[255]; long lStringLen;

//Retrieve each property pIUserInfo->get_Name(&lpszName); pIUserInfo->get_Age(&nAge); pIUserInfo->get_Sex(&bySex);

DisplayMessage(lpszRetrievedMsg);

//Format the Name strcpy(szDisplayText, "Name: "); if (lpszName) strcat(szDisplayText, lpszName); //Add a carriage return lStringLen = strlen(szDisplayText); szDisplayText[lStringLen] = '\r'; //Null terminate the string szDisplayText[lStringLen + 1] = '\0';

//Format the Age ltoa(nAge, szAge, 10); strcat(szDisplayText, "Age: "); strcat(szDisplayText, szAge); //Add a carriage return lStringLen = strlen(szDisplayText); szDisplayText[]StringLen] = '\r'; //Null terminate the string szDisplayText[]StringLen + 1] = '\0';

//Format the Sex strcat(szDisplayText, "Sex: ");

Page 75: 6935316 DCOM Microsoft Distributed Component Object Model

lStringLen = strlen(szDisplayText); szDisplayText[lStringLen] = bySex; //Null terminate the string szDisplayText[]StringLen + 1] = '\0';

#ifdef _UNICODE //UNICODE //Convert from the multibyte character set to the //wide character set mbstowcs(szMsgText, szDisplayText, sizeof(szMsgText) / sizeof(_TCHAR));#else //SBCS and MBCS _tcscpy(szMsgText, szDisplayText);#endif DisplayMessage(szMsgText);}//DisplayUserInfo

////WinMain//int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ HRESULT hr; IUnknown *pIUnknown = NULL; IUserInfo *pIUserInfo = NULL;

//Initialize the COM Library hr = CoInitialize(NULL); if (SUCCEEDED(hr)) { DisplayMessage(_TEXT("The COM Library has been initialized."));

//Ask the COM Library to instantiate the UserInfo object //and return us an initial pointer to IUnknown hr = CoCreateInstance(CLSID_UserInfo, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID *)&pIUnknown);

if (SUCCEEDED(hr)) { DisplayMessage(_TEXT("The UserInfo object has been created.")); //Begin using the object

//QueryInterface for the IUserInfo interface hr = pIUnknown->QueryInterface(IID-IUserInfo, (LPVOID *)&pIUserInfo); if (SUCCEEDED(hr)) {

Page 76: 6935316 DCOM Microsoft Distributed Component Object Model

DisplayMessage(_TEXT("Changed to the IUserInfo interface."));

//Set each property pIUserInfo->put_Name("Frank E. Redmond III"); pIUserInfo->put_Age(24); pIUserInfo->put_Sex('M');

DisplayMessage(_TEXT("Each UserInfo property has been set.")); DisplayUserInfo(pIUserInfo, _TEXT("Each UserInfo property has been retreived."));

//Release the IUserInfo interface pIUserInfo->Release(); DisplayMessage(_TEXT("Released the IUserInfo interface.")); } else DisplayMessage(_TEXT("Couldn't change to the IUserInfo interface."));

//Release the IUnknown interface pIUnknown->Release(); DisplayMessage(_TEXT("Released the IUnknown interface.")); } else DisplayMessage(_TEXT("The UserInfo object couldn't be created."));

//Shut down the COM Library CoUninitialize(); DisplayMessage(_TEXT("Shut down the COM Library.")); } else DisplayMessage(_TEXT("The COM Library initialization failed."));

DisplayMessage(_TEXT("Terminating the Application.")); //Terminate the application return FALSE;}//WinMain

That’s all there is to it! While the UserInfoClient is a simple application, it exemplifiesthe fundamental steps required by all COM clients.

Summary

We covered a lot of ground in this chapter, primarily the responsibilities of the COM server andthe COM client.

COM servers are responsible for:

Page 77: 6935316 DCOM Microsoft Distributed Component Object Model

•  Allocating GUIDs for each supported object, interface, and type library

•  Defining the interfaces supported by each object

•  Implementing the functions defined by each interface

•  Implementing a class factory capable of creating each supported object

•  Exposing a class factory for each supported object

•  Registering the appropriate class information

•  Unloading themselves when appropriate

COM clients are responsible for:

•  Initializing the COM Library

•  Obtaining initial and subsequent interfaces

•  Manipulating the COM object

•  Releasing the COM object when it is no longer needed

•  Uninitializing the COM Library

In the next chapter, we build an out-of-process server and examine how its architecture differsfrom the UserInfo in-process server that we created in this chapter.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 78: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Chapter 3Building Out-of-Process ServersIN THIS CHAPTER

•  The similarities and differences between in-process and out-of-processCOM servers.

•  How out-of-process servers expose their class factories.

•  How out-of-process servers expose their registration facilities.

•  How to build and register a proxy/stub pair. In the previous chapter,you learned the responsibilities of COM servers and clients by buildingthe UserInfo in-process server and the UserInfoClientapplication. In this chapter you will further your understanding of COMclients and servers by building the UserInfoHandler out-of-processserver.

AS YOU NOW know, an out-of-process server is a COM server implementedas an EXE. By being packaged within an EXE, a COM object actually exists inits own process space apart from its client. Maintaining a separate processspace can have significant advantages:

•  Process isolation. This is the ability to isolate the effects of oneprocess from another related process. Consider a case in which a COMclient relies on a COM object that’s implemented as part of anin-process server. If the COM object crashes for some unknown reason,the entire client application will also crash, because the in-process serverand the client share the same address space. However, if the COMobject in question were created as part of an out-of-process server, thenthe client application would be afforded process isolation, and thuswould be shielded from the effects of the crashing COM object.

Page 79: 6935316 DCOM Microsoft Distributed Component Object Model

•  Better security. On secure systems such as Windows NT, theresources of each process can be guarded against unauthorized usage byrogue processes. The same level of security is not possible with anin-process server, because the client and the server share the sameprocess space.

•  The ability to maintain shared global resources. Since EXEsmaintain ownership of their own resources, an out-of-process server iseasily capable of allocating global resources and sharing them among itsvarious clients. The same is not true for DLLs, because an independentcopy of the DLL, and thus an independent copy of the DLL’s resources,is loaded into each client’s address space.

However, maintaining an address space apart from the client also means thateach method invocation requires marshaling, which is definitely slower thanin-process execution. Separate address spaces also means that out-of-processservers must use different techniques to provide functionality that wouldotherwise be exported by in-process servers. Learning the functional andarchitectural differences between in-process servers and out-of-process serversis the focus of this chapter.

The UserInfoHandler Server

The UserInfoHandler server is an out-of-process server that supportsboth the UserInfo object from the last chapter and theUserInfoHandler object. The UserInfo object is implemented usingthe same code introduced in Chapter 2, except for the DLL-specific sectionscontained in DllMain.cpp. The UserInfo COM object maintains informationabout an individual user and exposes this information through the propertiesgiven in Table 3-1.

Table 3-1 UserInfo Object Properties

Property Name Data Type

Age shortName LPSTRSex unsigned char

The UserInfoHandler object is used to perform operations on UserInfoobjects. UserInfoHandler has three interfaces: ICopyInfo,IReverseInfo, and ISwapInfo. The ICopyInfo interface is used tocopy information from one UserInfo object to another. TheIReverseInfo interface is used to reverse a UserInfo object’sinformation. The ISwapInfo interface is used to exchange informationbetween two UserInfo objects. Each interface has four functions, one foreach UserInfo property and one that affects all of the properties. TheUserInfoHandler is outlined in Table 3-2:

Table 3-2 UserInfoHandler Object Outline

Page 80: 6935316 DCOM Microsoft Distributed Component Object Model

Interface Name Function

ICopyInfo CopyName(IUserInfo *pDest, IUserInfo *pSrc)CopyAge(IUserInfo *pDest, IUserInfo *pSrc)CopySex(IUserInfo *pDest, IUserInfo *pSrc)CopyAll(IUserInfo *pDest, IUserInfo *pSrc)

IReverseInfo ReverseName(IUserInfo *pIUserInfo)ReverseAge(IUserInfo *pIUserInfo)ReverseSex(IUserInfo *pIUserInfo)ReverseAll(IUserInfo *pIUserInfo)

ISwapInfoSwapName(IUserInfo *pIUserInfo, IUserInfo*pIUserInfo)SwapAge(IUserInfo *pIUserInfo, IUserInfo*pIUserInfo)SwapSex(IUserInfo *pIUserInfo, IUserInfo*pIUserInfo)SwapAge(IUserInfo *pIUserInfo, IUserInfo*pIUserInfo)

Like in-process servers, out-of-process servers are also responsible for:

•  Allocating GUIDs for each supported object and interface

•  Defining the interfaces supported by each object

•  Implementing the functions defined by each interface

•  Implementing a class factory capable of creating each supportedobject

•  Registering class information for each supported object

•  Exposing a class factory for each supported object

•  Unloading (destroying) itself when appropriate

Allocating CLSIDs

Using either GUIDGEN or UUIDGEN, you need to generate enough GUIDsfor the UserInfoHandler CLSID, the type library, and each of the threesupported interfaces. If you are using GUIDGEN, this will require severalsteps. However, UUIDGEN can be used to accomplish this in one single step:

C:>uuidgen -n5 -oguids.txt

b04faa80-8bef-11d0-94ab-00a024a85a21b04faa81-8bef-11d0-94ab-00a024a85a21b04faa82-8bef-11d0-94ab-00a024a85a21b04faa83-8bef-11d0-94ab-00a024a85a21b04faa84-8bef-11d0-94ab-00a024a85a21

Previous Table of Contents Next

Page 81: 6935316 DCOM Microsoft Distributed Component Object Model

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 82: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Defining an Object’s Interfaces

Listing 3-1 shows the IDL definitions for the UserInfoHandler type library, the IUserInfo,ICopyInfo, IReverseInfo, and ISwapInfo interfaces; and the UserInfo andUserInfoHandler COM objects. Notice that the ICopyInfo, IReverseInfo, andISwapInfo interfaces all inherit from IUnknown, like IUserInfo.

Listing 3-1. UserInfoHandler.idl

////UserInfoHandler.idl//

import "unknwn.idl";

//IID_IUserInfo//These are the attributes of the IUserInfo interface[ object, uuid(acceeb02-86c7-11d0-94ab-0080c74c7e95), helpstring("IUserInfo Interface.")]//Declaration of the IUserInfo interfaceinterface IUserInfo : IUnknown{ //List of function definitions for each method supported //by the interface // //[attributes] returntype [calling convention] //funcname(params); // [propget, helpstring("Sets or returns the age of the user.")] HRESULT Age([out, retval] short *nRetAge); [propput, helpstring("Sets or returns the age of the user.")]

Page 83: 6935316 DCOM Microsoft Distributed Component Object Model

HRESULT Age([in] short nAge); [propget, helpstring("Sets or returns the name of the user.")] HRESULT Name([out, retval] LPSTR *lpszRetName); [propput, helpstring("Sets or returns the name of the user.")] HRESULT Name([in] LPSTR lpszName); [propget, helpstring("Sets or returns the sex of the user.")] HRESULT Sex([out, retval] unsigned char *byRetSex); [propput, helpstring("Sets or returns the sex of the user.")] HRESULT Sex([in] unsigned char bySex);}

//ICopyInfo//These are the attributes of the ICopyInfo interface[ object, uuid(b04faa82-8bef-11d0-94ab-00a024a85a21), helpstring("ICopyInfo Interface.")]//Definition of the ICopyInfo interfaceinterface ICopyInfo : IUnknown{ //List of function definitions for each method supported //by the interface // //[attributes] returntype [calling convention] funcname(params); // [helpstring("Copies the Age property from one UserInfo object to another.")] HRESULT CopyAge([in] IUserInfo *lpDest, [in] IUserInfo *lpSrc); [helpstring("Copies the Name property from one UserInfo object to another.")] HRESULT CopyName([in] IUserInfo *lpDest, [in] IUserInfo *lpSrc); [helpstring("Copies the Sex property from one UserInfo object to another.")] HRESULT CopySex([in] IUserInfo *lpDest, [in] IUserInfo *lpSrc); [helpstring("Copies all of the properties from one UserInfo object to another.")] HRESULT CopyAll([in] IUserInfo *lpDest, [in] IUserInfo *lpSrc);}

//IReverseInfo//These are the attributes of the IReverseInfo interface[ object, uuid(b04faa83-8bef-11d0-94ab-00a024a85a21), helpstring("IReverseInfo Interface.")]

Page 84: 6935316 DCOM Microsoft Distributed Component Object Model

//Definition of the IReverseInfo interfaceinterface IReverseInfo : IUnknown{ //List of function definitions for each method supported //by the interface // //[attributes] returntype [calling convention] // funcname(params); // [helpstring("Reverses the Age property of a UserInfo object.")] HRESULT ReverseAge([in] IUserInfo *lpIUserInfo); [helpstring("Reverses the Name property of a UserInfo object.")] HRESULT ReverseName([in] IUserInfo *lpIUserInfo); [helpstring("Reverses the Sex property of a UserInfo object.")] HRESULT ReverseSex([in] IUserInfo *lpIUserInfo); [helpstring("Reverses all of the properties of a UserInfo object.")] HRESULT ReverseAll([in] IUserInfo *lpIUserInfo);}

//ISwapInfo//These are the attributes of the ISwapInfo interface[ object, uuid(b04faa84-8bef-11d0-94ab-00a024a85a21), helpstring("ISwapInfo Interface.")]//Definition of the ISwapInfo interfaceinterface ISwapInfo : IUnknown{ //List of function definitions for each method supported //by the interface // //[attributes] returntype [calling convention] // funcname(params); // [helpstring("Swaps the Age property of two UserInfo objects.")] HRESULT SwapAge([in] IUserInfo *lpIUserInfo1, [in] IUserInfo *lpIUserInfo2); [helpstring("Swaps the Name property of two UserInfo objects.")] HRESULT SwapName([in] IUserInfo *lpIUserInfo1, [in] IUserInfo *lpIUserInfo2); [helpstring("Swaps the Sex property of two UserInfo objects.")] HRESULT SwapSex([in] IUserInfo *lpIUserInfo1, [in] IUserInfo *lpIUserInfo2); [helpstring("Swaps all of the properties of two UserInfo objects.")] HRESULT SwapAll([in] IUserInfo *lpIUserInfo1, [in] IUserInfo *lpIUserInfo2);}

Page 85: 6935316 DCOM Microsoft Distributed Component Object Model

//LIBID_UserInfoHandler//These are the attributes of the type library[ uuid(b04faa80-8bef-11d0-94ab-00a024a85a21), helpstring("UserInfoHandler Type Library."), version(1.0)]//Definition of the UserInfoHandler type librarylibrary UserInfoHandler{ importlib("stdole32.tlb");

//CLSID_UserInfo //Attributes of the UserInfo object [ uuid(acceeb01-86c7-11d0-94ab-0080c74c7e95), helpstring("UserInfo Object.") ] //Definition of the UserInfo object coclass UserInfo { //List all of the interfaces supported by the object [default] interface IUserInfo; }

//CLSID_UserInfoHandler //Attributes of the UserInfoHandler object [ uuid(b04faa81-8bef-11d0-94ab-00a024a85a21), helpstring("UserInfoHandler Object.") ] //Definition of the UserInfoHandler object coclass UserInfoHandler { //List all of the interfaces supported by the object [default] interface ICopyInfo; interface IReverseInfo; interface ISwapInfo; }}

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 86: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The UserInfoHandler.idl file is compiled using the MIDL compiler to generate fiveadditional files: UserInfoHandler.tlb, UserInfoHandler_i.c, UserInfoHandler_i.h,UserInfoHandler_p.c, and dlldata.c. You should be familiar with the UserInfoHandler_i.cand UserInfoHandler_i.h files, as they are similar to the UserInfo_i.c and UserInfo_i.h filesused in Chapter 2 to create the UserInfo in-process server. The UserInfoHandler_i.c filecontains definitions of the human-readable constants that are used to refer to the type library:the UserInfo and UserInfoHandler object classes: and the IUserInfo,ICopyInfo, IReverseInfo, and ISwapInfo interfaces:

const IID IID_IUserInfo = {0xacceeb02,0x86c7,0x11d0, {0x94,0xab,0x00,0x80,0xc7,0x4c,0x7e,0x95}};const IID IID_ICopyInfo = {0xb04faa82,0x8bef,0x11d0, {0x94,0xab,0x00,0xa0,0x24,0xa8,0x5a,0x21}};const IID IID_IReverseInfo = {0xb04faa83,0x8bef,0x11d0, {0x94,0xab,0x00,0xa0,0x24,0xa8,0x5a,0x21}};const IID IID_ISwapInfo = {0xb04faa84,0x8bef,0x11d0, {0x94,0xab,0x00,0xa0,0x24,0xa8,0x5a,0x21}};const IID LIBID_UserInfoHandler = {0xb04faa80,0x8bef,0x11d0, {0x94,0xab,0x00,0xa0,0x24,0xa8,0x5a,0x21}};const CLSID CLSID_UserInfo = {0xacceeb01,0x86c7,0x11d0, {0x94,0xab,0x00,0x80,0xc7,0x4c,0x7e,0x95}};const CLSID CLSID_UserInfoHandler = {0xb04faa81,0x8bef,0x11d0, {0x94,0xab,0x00,0xa0,0x24,0xa8,0x5a,0x21}};

The UserInfoHandler_i.h file contains the C and C++ definitions for the IUserInfo,ICopyInfo, IReverseInfo, and ISwapInfo interfaces. We examine theUserInfoHandler_p.c and dlldata.c files later on in this chapter.

Implementing Interface Methods

Implementing the IUserInfo interface is a snap, as we already have the code from the lastchapter. However, we must still provide implementations for the ICopyInfo,IReverseInfo, and ISwapInfo interfaces.

Page 87: 6935316 DCOM Microsoft Distributed Component Object Model

To implement each of these interfaces, we must:

•  Define a C++ class that multiply inherits from the ICopyInfo, IReverseInfo,and ISwapInfo abstract base classes.

•  Implement each function defined by the derived C++ class.

Listing 3-2 contains the declaration of the CUserInfoHandler C++ class, whichrepresents the UserInfoHandler COM object. Notice that even though each interfacedefines the required IUnknown functions QueryInterface, AddRef, and Release asits first three functions, there is only one implementation of IUnknown for the entireCUserInfoHandler object.

Listing 3-2. Definition of the CUserInfoHandler C++ object

class CUserInfoHandler : public ICopyInfo, public IReverseInfo, public ISwapInfo{private: ULONG m_cRef;public: //IUnknown STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppv); STDMETHODIMP_(ULONG)AddRef(void); STDMETHODIMP_(ULONG)Release(void); //ICopyInfo STDMETHODIMP CopyAge(IUserInfo *lpDest, IUserInfo *lpSrc); STDMETHODIMP CopyName(IUserInfo *lpDest, IUserInfo *lpSrc); STDMETHODIMP CopySex(IUserInfo *lpDest, IUserInfo *lpSrc); STDMETHODIMP CopyAll(IUserInfo *lpDest, IUserInfo *lpSrc); //IReverseInfo STDMETHODIMP ReverseAge(IUserInfo *lpIUserInfo); STDMETHODIMP ReverseName(IUserInfo *lpIUserInfo); STDMETHODIMP ReverseSex(IUserInfo *lpIUserInfo); STDMETHODIMP ReverseAll(IUserInfo *lpIUserInfo); //ISwapInfo STDMETHODIMP SwapAge(IUserInfo *lpIUserInfo1, IUserInfo *lpIUserInfo2); STDMETHODIMP SwapName(IUserInfo *lpIUserInfo1, IUserInfo *lpIUserInfo2); STDMETHODIMP SwapSex(IUserInfo *lpIUserInfo1, IUserInfo *lpIUserInfo2); STDMETHODIMP SwapAll(IUserInfo *lpIUserInfo1, IUserInfo *lpIUserInfo2); //Constructor CUserInfoHandler( ) { m_cRef = 0; }};//CUserInfoHandler

Next we have to provide the implementation for each CUserInfoHandler memberfunction. Because the UserInfoHandler object supports four interfaces, itsQueryInterface implementation must be capable of returning each one. The following

Page 88: 6935316 DCOM Microsoft Distributed Component Object Model

listing is of CUserInfoHandler::QueryInterface. Notice that because theCUserInfoHandler C++ class doesn’t directly inherit from IUnknown, it must cast itselfinto an ICopyInfo interface pointer before casting to an IUnknown interface pointer. AC++ class can be cast to any interface that it inherits from, regardless of whether theinheritance is direct, like the ICopyInfo, IReverseInfo, and ISwapInfo interfaces;or indirect, like the IUnknown interface:

STDMETHODIMP CUserInfoHandler::QueryInterface (REFIID iid, LPVOID *ppv){ *ppv = NULL; if (IID_IUnknown == iid) *ppv = (LPVOID)(IUnknown *)(ICopyInfo *)this; else if (IID_ICopyInfo == iid) *ppv = (LPVOID)(ICopyInfo *)this; else if (IID_IReverseInfo == iid) *ppv = (LPVOID)(IReverseInfo *)this; else if (IID_ISwapInfo == iid) *ppv = (LPVOID)(ISwapInfo *)this; else return E_NOINTERFACE; //Interface not supported //Perform reference count through the returned interface ((IUnknown*)*ppv)->AddRef( ); return NOERROR;}//QueryInterface

Implementations of the remaining interface functions are pretty straightforward and use theIUserInfo interface to manipulate the information maintained by the UserInfo object.The following code snippet shows how the CopyAge function uses the IUserInfointerface to copy the Age property from one UserInfo object to another:

STDMETHODIMP CUserInfoHandler::CopyAge(IUserInfo *lpDest, IUserInfo *lpSrc){ HRESULT hr; short nTmpAge;

//Retrieve information from the source hr = lpSrc->get_Age(&nTmpAge); if (SUCCEEDED(hr)) { //Apply it to the destination hr = lpDest->put_Age(nTmpAge); } return hr;}//CopyAge

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 89: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 90: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Implementing a Class Factory

The UserInfoHandler server has two class factories: one for creating UserInfoobjects and one for creating UserInfoHandler objects. The UserInfo class factory islifted directly from the code in Chapter 2, and the implementation of theUserInfoHandler class factory is very similar. Implementation of theUserInfoHandler class factory’s CreateInstance method can be seen in Listing3-3.

Listing 3-3. Implementation of the UserInfoHandler class factory’sCreateInstance method

STDMETHODIMP CUserInfoHandlerFactory::CreateInstance (IUnknown* pUnknownOuter, REFIID iid, LPVOID *ppv){ HRESULT hr; CUserInfoHandler *pCUserInfoHandler = NULL;

*ppv = NULL; //This object doesn't support aggregation if (NULL != pUnknownOuter) return CLASS_E_NOAGGREGATION; //Create the CUserInfo object pCUserInfoHandler = new CUserInfoHandler( ); if (NULL == pCUserInfoHandler) return E_OUTOFMEMORY; //Retrieve the requested interface hr = pCUserInfoHandler->QueryInterface(iid, ppv); if (FAILED(hr)) { delete pCUserInfoHandler;

Page 91: 6935316 DCOM Microsoft Distributed Component Object Model

pCUserInfoHandler = NULL; return hr; } //Increment the global object counter g_cObjects++;

return NOERROR;}//CreateInstance

Registering Class Information

Until this point, developing the UserInfoHandler out-of-process server has beenidentical to developing the UserInfo in-process server presented in the last chapter. Butfrom this point on, you will notice that things will be slightly different. Becauseout-of-process servers are in a process space apart from their clients, they cannot exposefunctions and have clients access them with the Win32 API function GetProcAddress.Therefore, out-of-process servers cannot expose DllRegister or DllUnregister toupdate registry information. To signal an out-of-process server to update information in theregistry, simply run the server and specify either “/RegServer” or “-RegServer” on thecommand line.

C:>UserInfoHandler /RegServer

Likewise, to remove registry entries, run the server and specify either “/UnRegServer” or“-UnRegServer” on the command line:

C:>UserInfoHandler /UnRegServer

This means that the out-of-process servers must parse the command line for these twoarguments. Because an out-of-process server is an executable, it is possible for a user toexecute it directly from the command line without using a client. To determine the contextin which an out-of-process server is started, COM defines the “/Embedding” or“-Embedding” command-line arguments. Whenever your server is started by a COM client,COM will pass the “/Embedding” command-line argument to your server. Listing 3-4 showshow the UserInfoHandler object searches the command line to gather and process anycommand-line arguments as part of the WinMain entry-point function, which is called bythe operating system to actually start the application. Out-of-process servers also writeslightly different information to the system registry. Instead of adding the “InprocServer32”subkey to the CLSID registry entry, out-of-process servers add the “LocalServer32” subkey:

HKEY_CLASSES_ROOT CLSID {b04faa81-8bef-11d0-94ab-00a024a85a21} = Description LocalServer32 = C:\UserInfoHandler\UserInfoHandler.dll

Exposing the Class Factory

The restriction on exporting functions from an out-of-process server also means thatUserInfoHandler server must devise a different mechanism for exposing classfactories. The COM Library provides the CoRegisterClassObject andCoRevokeClassObject API functions for exposing object class factories. When anout-of-process server is first loaded, it must create each class factory that it supports.

Page 92: 6935316 DCOM Microsoft Distributed Component Object Model

Information regarding each class factory must then be registered in a system-wide table witha call to CoRegisterClassObject as quickly as possible (see sidebar).

The Importance of Quick Registration

To understand why it is important to register class factories as soon as possible, imaginethat you’re a client of the UserInfoHandler server, and you callCoCreateInstance to instantiate a UserInfoHandler object. COM responds bycalling CoGetClassObject to actually load the UserInfoHandler server. OnceCoGetClassObject has loaded the server, COM then begins searching through asystem-wide table of registered class factories, based on the CLSID parameter specified inthe call to CoCreateInstance. If COM finds the entry, life is good! However, if COMdoesn’t find the object’s class factory entry in the system-wide table of registered classfactories, the call to CoGetClassObject will be suspended. But COM won’t waitforever, and if the UserInfoHandler server doesn’t register its class factory within acertain amount of time, the call to CoGetClassObject will time-out with theCO_E_APPDIDNTREG error code. Thus, for an out-of-process server, it is important toregister the object’s class factories as soon after application start-up as possible in order toprevent calls to CoGetClassObject and ultimately CoCreateInstance fromtiming out.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 93: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

If the call to CoRegisterClassObject is successful, the server’s supported class factorieswill be exposed for client consumption. Every successful call toCoRegisterClassObject must be paired with a call to CoRevokeClassObject aspart of the server’s shutdown procedure in order to remove registration information exposed byCoRegisterClassObject. Listing 3-4 shows UserInfoHandler’s WinMainentry-point function, which is called by the operating system to actually start the application.Notice that UserInfoHandler registers two class factories: one for the UserInfo objectand one for the UserInfoHandler object. Also notice the calls to CoInitialize andCoUninitialize. Out-of-process servers are required to call the CoInitialize function— which is provided to initialize the COM Library — before any other COM Library calls,except for the CoGetMalloc function and other memory allocation calls. Every successfulcall to CoInitialize must be matched with a call to CoUninitialize, so at applicationshutdown, every out-of-process server is responsible for calling CoUninitialize. OnceCoUninitialize has been called, no other COM APIs should be called, with the exceptionof the CoGetMalloc function and other memory allocation calls.

Listing 3-4. The UserInfoHandler WinMain function

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ HRESULT hr; MSG msg; _TCHAR szTokens[ ] = _TEXT("-/"); LPTSTR szNextToken; LPTSTR szCmdLine; CUserInfoFactory *pCUserInfoFactory = NULL; CUserInfoHandlerFactory *pCUserInfoHandlerFactory = NULL;

g_hModule = GetModuleHandle(NULL); // Read the command line.#ifdef _UNICODE //UNICODE szCmdLine = GetCommandLine( );

Page 94: 6935316 DCOM Microsoft Distributed Component Object Model

#else //SBCS and MBCS szCmdLine = lpCmdLine;#endif //Find the first token szNextToken = _tcstok(szCmdLine, szTokens); while (NULL != szNextToken) { if (0 == _tcsicmp(szNextToken, _TEXT("UnregServer"))) { ::UnregisterServer(CLSID_UserInfoHandler); ::UnregisterServer(CLSID-UserInfo); return FALSE; } else if (0 == _tcsicmp(szNextToken,

_TEXT("RegServer"))) { ::RegisterServer(CLSID_UserInfoHandler, _TEXT("DCOM Enterprise Apps - UserInfoHandler Object.")); ::RegisterServer(CLSID_UserInfo, _TEXT("DCOM Enterprise Apps - UserInfo Object.")); return FALSE; } else if (0 == _tcsicmp(szNextToken, _TEXT("Embedding"))) { break; } //Find the next token szNextToken = _tcstok(NULL, szTokens); } // Initialize the COM Library. hr = CoInitialize(NULL); if (FAILED(hr)) return FALSE; //Create the UserInfo class factory pCUserInfoFactory = new CUserInfoFactory( ); //Check for out of memory error if (NULL != pCUserInfoFactory) { //Register the UserInfo class factory hr = CoRegisterClassObject(CLSID_UserInfo, (IUnknown *)pCUserInfoFactory, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &g_dwRegisterUserInfo); if (FAILED(hr)) { delete pCUserInfoFactory; pCUserInfoFactory = NULL; } else

Page 95: 6935316 DCOM Microsoft Distributed Component Object Model

{ //Create the UserInfoHandler class factory pCUserInfoHandlerFactory = new CUserInfoHandlerFactory( ); //Check for out of memory error if (NULL != pCUserInfoHandlerFactory) { //Register the UserInfoHandler class factory hr = CoRegisterClassObject (CLSID_UserInfoHandler, (IUnknown *) pCUserInfoHandlerFactory, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &g_dwRegisterUserInfoHandler); if (FAILED(hr)) { delete pCUserInfoHandlerFactory; pCUserInfoHandlerFactory = NULL; } else { while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); //Unregister the UserInfoHandler //class factory CoRevokeClassObject (g_dwRegisterUserInfoHandler); } } //Unregister the UserInfo class factory CoRevokeClassObject(g_dwRegisterUserInfo); } } //Uninitialize the COM Library. CoUninitialize( ); return (msg.wParam);}//WinMain

Server Unloading

Unlike in-process servers, out-of-process servers are totally capable of unloading themselves.When there are no locks, and no instantiated objects, out-of-process servers can unloadthemselves by posting a WM_QUIT message to their primary thread’s message queue using theWin32 API function PostQuitMessage.

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 96: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 97: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Marshaling

Since the UserInfoHandler server maintains a process space apart from its clients,we’ll need to create a proxy/stub pair in order for the client and server to communicateacross process boundaries. Whenever a client invokes an interface function of anout-of-process server, the proxy — which runs in the client’s address space — packages therequired parameters and communicates them to the corresponding stub running in theserver’s address space. The stub unpackages the parameters and executes the function call,then packages any return values and transmits them back to the proxy. The proxy thenunpackages the return values and forwards them to the actual client. When a proxy and stubare located on the same physical machine, they communicate using Local Procedure Calls(LPCs). However, when they are located on separate machines, proxies and stubscommunicate using Remote Procedure Calls (RPCs). The entire process, including thedetermination of when to use LPCs as opposed to RPCs, is totally transparent to thedeveloper. The entire marshaling process is shown in Figure 3-1.

Figure 3-1  The COM marshaling process

Because the proxy and stub are contained in the same module, we only have to build oneproxy/stub pair. To build a proxy/stub pair for the UserInfoHandler server, we mustfirst create a separate DLL project. Add into this project the following files:UserInfoHandler_i.c, UserInfoHandler_i.h, UserInfoHandler_p.c, and dlldata.c. As youmay recall, all of these files were generated by the MIDL compiler when it processed ourUserInfoHandler.idl file. You are already familiar with UserInfoHandler_i.c andUserInfoHandler_i.h, so we’ll concentrate on UserInfoHandler_p.c and dlldata.c.UserInfoHandler_p.c actually contains all the code necessary to build a proxy and a stub

Page 98: 6935316 DCOM Microsoft Distributed Component Object Model

for the interfaces defined in UserInfoHandler.idl. All it’s lacking is the code responsiblefor creating the DLL itself. The code responsible for creating the DLL is contained indlldata.c. Before you are ready to build your proxy project, you must do two things:

•  You must include REGISTER_PROXY_DLL in your preprocessor definitions.

•  You must create a .def file for the proxy project.

By including REGISTER_PROXY_DLL in your preprocessor definitions, you instruct thecompiler to automatically include default implementations for DllGetClassObject,DllCanUnloadNow, GetProxyDllInfo, DllRegisterServer, andDllUnregisterServer. This additional code is necessary in order for the proxy/stubpair to be able to register itself.

As you can probably imagine, proxy/stub pairs require slightly different registry settingsthan COM servers. Under the registry key HKEY_CLASSES_ROOT\Interface, theproxy/stub will add the IID of each supported interface as a subkey. Under each of thesesubkeys, the proxy/stub will add the ProxyStubClsid32 subkey, setting its value equal to theCLSID for the proxy/stub. This same CLSID is then registered under theHKEY_CLASSES_ROOT\CLSID key, and it has the InprocServer32 subkey, with a valueequal to the path of the proxy/stub DLL:

HKEY_CLASSES_ROOT CLSID {acceeb02-86c7-11d0-94ab-0080c74c7e95} = PSFactoryBuffer InprocServer32 = C:\UserInfoHandlerProxy.dll . . .HKEY_CLASSES_ROOT Interface {acceeb02-86c7-11d0-94ab-0080c74c7e95} = IUserInfo ProxyStubClsid32 = {acceeb02-86c7-11d0-94ab-0080c74c7e95}

Because these entry points must be exposed, you must create a .def file to export them.Following is the contents of the UserInfoHandlerProxy.def file:

LIBRARY UserInfoHandlerProxyDESCRIPTION "UserInfoHandler Proxy."EXPORTS DllRegisterServer @1 PRIVATE DllUnregisterServer @2 PRIVATE GetProxyDllInfo @3 PRIVATE DllGetClassObject @4 PRIVATE DllCanUnloadNow @5 PRIVATE

Now you have everything you need to build your proxy/stub pair, and you only had to writeeight lines of code in a .def file!

The UserInfoHandlerProxy.dll is registered just like any other DLL, usingREGSVR32.EXE:

C:>Regsvr32 UserInfoHandlerProxy.dll

Page 99: 6935316 DCOM Microsoft Distributed Component Object Model

It is also unregistered like any other DLL:

C:>Regsvr32 UserInfoHandlerProxy.dll /u

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 100: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Whenever a client invokes a function on the IUserInfo, ICopyInfo, IReverseInfo, orISwapInfo interfaces, your new proxy/stub pair will be invoked automatically toappropriately marshal and unmarshal any required data. Listings 3-5, 3-6, and 3-7 contain thesource code for EXEMain.cpp, UserInfoHandler.h, and UserInfoHandler.cpp. As you lookat the source code, notice how the EXE-specific code has been confined to just theEXEMain.cpp source file, making it easier to implement the UserInfoHandler COMobject as a DLL if you so choose.

Listing 3-5. EXEMain.cpp

////ExeMain.cpp//#define MAX_STRING_LENGTH 255#define GUID_SIZE 128

#include <objbase.h>#include <tchar.h>#include "UserInfo.h"#include "UserInfoHandler.h"

////Forward declarations//BOOL RegisterServer(CLSID clsid, LPSTR lpszDescription);BOOL UnregisterServer(CLSID clsid);BOOL SetRegKeyValue(LPTSTR lpszkey, LPTSTR lpszSubKey, LPTSTR lpszValue);BOOL ServerCanUnloadNow(void);void UnloadServer(void);

////Global variables//

Page 101: 6935316 DCOM Microsoft Distributed Component Object Model

HMODULE g_hModule = NULL;ULONG g_cObjects = 0;ULONG g_cLocks = 0;DWORD g_dwRegisterUserInfo;DWORD g_dwRegisterUserInfoHandler;

////Register the server//BOOL RegisterServer(CLSID clsid, LPSTR lpszDescription){ BOOL bOK; _TCHAR szModulePath[MAX_PATH + 1]; _TCHAR szCLSID[GUID_SIZE + 1]; _TCHAR szCLSIDKey[MAX_STRING_LENGTH + 1]; wchar_t wszGUID[GUID_SIZE + 1];

//Obtain the path to server's executable file for //later use GetModuleFileName(g_hModule, szModulePath, sizeof(szModulePath) / sizeof(_TCHAR)); //Convert the CLSID to the format //{00000000-0000-0000-0000-000000000000} StringFromGUID2(clsid, wszGUID, sizeof(wszGUID) / sizeof(wchar_t));#ifdef _UNICODE //UNICODE _tcscpy(szCLSID, wszGUID);#else //SBCS and MBCS //Convert from the wide character set to the //multibyte character set WideCharToMultiByte(CP_ACP, 0, wszGUID, -1, szCLSID, sizeof(szCLSID) / sizeof(_TCHAR), NULL, NULL);#endif //HKEY_CLASSES_ROOT\CLSID\ //{00000000-0000-0000-0000-000000000000} _tcscpy(szCLSIDKey, _TEXT("CLSID\\")); _tcscat(szCLSIDKey, szCLSID); bOK = SetRegKeyValue(szCLSIDKey, NULL, lpszDescription); if (bOK) bOK = SetRegKeyValue(szCLSIDKey, _TEXT("LocalServer32"), szModulePath);

return bOK;}//RegisterServer

////Unregister the server//BOOL UnregisterServer(CLSID clsid){ long lErrorCode;

Page 102: 6935316 DCOM Microsoft Distributed Component Object Model

_TCHAR szCLSID[GUID_SIZE + 1]; _TCHAR szCLSIDKey[MAX_STRING_LENGTH + 1]; _TCHAR szLocalServer32Key[MAX_STRING_LENGTH + 1]; wchar_t wszGUID[GUID_SIZE + 1];

//Convert the CLSID to the format //{00000000-0000-0000-0000-000000000000} StringFromGUID2(clsid, wszGUID, GUID_SIZE);#ifdef _UNICODE //UNICODE _tcscpy(szCLSID, wszGUID);#else //SBCS and MBCS //Convert from the wide character set to the //multibyte character set WideCharToMultiByte(CP_ACP, 0, wszGUID, -1, szCLSID, sizeof(szCLSID) / sizeof(_TCHAR), NULL, NULL);#endif //HKEY_CLASSES_ROOT\CLSID\ //{00000000-0000-0000-0000-000000000000} _tcscpy(szCLSIDKey, _TEXT("CLSID\\")); _tcscat(szCLSIDKey, szCLSID); _tcscpy(szLocalServer32Key, szCLSIDKey); _tcscat(szLocalServer32Key, _TEXT("\\LocalServer32")); //Delete sub-keys first lErrorCode = RegDeleteKey(HKEY_CLASSES_ROOT, szLocalServer32Key); //Delete the entry under CLSID. if (ERROR_SUCCESS == lErrorCode) lErrorCode = RegDeleteKey(HKEY_CLASSES_ROOT, szCLSIDKey); if (ERROR_SUCCESS == lErrorCode) return TRUE; else return FALSE;}//UnregisterServer

////SetRegKeyValue//BOOL SetRegKeyValue(LPTSTR lpszkey, LPTSTR lpszSubKey, LPTSTR lpszValue){ BOOL bOk = FALSE; long lErrorCode; HKEY hkey; _TCHAR szKey[MAX_STRING_LENGTH + 1];

_tcscpy(szKey, lpszKey); if (NULL != lpszSubKey) { _tcscat(szKey, _TEXT("\\")); _tcscat(szKey, lpszSubKey);

Page 103: 6935316 DCOM Microsoft Distributed Component Object Model

} lErrorCode = RegCreateKeyEx(HKEY_CLASSES_ROOT, szKy, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL); if (ERROR_SUCCESS == lErrorCode) { lErrorCode = RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)lpszValue, sizeof(lpszValue) / sizeof(_TCHAR)); if (ERROR_SUCCESS == lErrorCode) bOk = TRUE; RegCloseKey(hKey); } return bOk;}//SetRegKeyValue

////ServerCanUnloadNow//BOOL ServerCanUnloadNow(void){ //The server can unload if there are no outstanding //objects or class factory locks if(0 == g_cObjects && 0 == g_cLocks) return TRUE; else return FALSE;}//ServerCanUnloadNow

////UnloadServer//void UnloadServer(void){ //Unload the server by posting the WM_QUIT to the //message queue PostQuitMessage(0);}//UnloadServer

////WinMain//int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ HRESULT hr; MSG msg; _TCHAR szTokens[ ] = _TEXT("-/"); LPTSTR szNextToken; LPTSTR szCmdLine; CUserInfoFactory *pCUserInfoFactory = NULL; CUserInfoHandlerFactory *pCUserInfoHandlerFactory = NULL;

Page 104: 6935316 DCOM Microsoft Distributed Component Object Model

g_hModule = GetModuleHandle(NULL); // Read the command line.#ifdef _UNICODE //UNICODE szCmdLine = GetCommandLine();#else //SBCS and MBCS szCmdLine = lpCmdLine;#endif //Find the first token szNextToken = _tcstok(szCmdLine, szTokens); while (NULL != szNextToken) { if (0 == _tcsicmp(szNextToken, _TEXT("UnregServer"))) { ::UnregisterServer(CLSID_UserInfoHandler); ::UnregisterServer(CLSID_UserInfo); return FALSE; } else if (0 == _tcsicmp(szNextToken, _TEXT("RegServer"))) { ::RegisterServer(CLSID_UserInfoHandler, _TEXT("DCOM Enterprise Apps - UserInfoHandler Object.")); ::RegisterServer(CLSID_UserInfo, _TEXT("DCOM Enterprise Apps - UserInfo Object.")); return FALSE; } else if (0 == _tcsicmp(szNextToken, _TEXT("Embedding"))) { break; } //Find the next token szNextToken = _tcstok(NULL, szTokens) ; } // Initialize the COM Library. hr = CoInitialize(NULL); if (FAILED(hr)) return FALSE; //Create the UserInfo class factory pCUserInfoFactory = new CUserInfoFactory(); //Check for out of memory error if (NULL != pCUserInfoFactory) { //Register the UserInfo class factory hr = CoRegisterClassObject(CLSID_UserInfo, (IUnknown *)pCUserInfoFactory, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &g_dwRegisterUserInfo);

Page 105: 6935316 DCOM Microsoft Distributed Component Object Model

if (FAILED(hr)) { delete pCUserInfoFactory; pCUserInfoFactory = NULL; } else { //Create the UserInfoHandler class factory pCUserInfoHandlerFactory = new CUserInfoHandlerFactory(); //Check for out of memory error if (NULL != pCUserInfoHandlerFactory) { //Register the UserInfoHandler class //factory hr = CoRegisterClassObject (CLSID_UserInfoHandler, (IUnknown *) pCUserInfoHandlerFactory, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &g_dwRegisterUserInfoHandler); if (FAILED(hr)) { delete pCUserInfoHandlerFactory; pCUserInfoHandlerFactory = NULL; } else { while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); //Unregister the UserInfoHandler //class factory CoRevokeClassObject (g_dwRegisterUserInfoHandler); } } //Unregister the UserInfo class factory CoRevokeClassObject(g_dwRegisterUserInfo); } } //Uninitialize the COM Library. CoUninitialize( ); return (msg.wParam);}//WinMain

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 106: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 107: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Listing 3-6. UserInfoHandler.h

////UserInfoHandler.h//#if !defined USERINFOHANDLER_H#define USERINFOHANDLER_H

#include "UserInfoHandler_i.h"

////CUserInfoHandler//class CUserInfoHandler : public ICopyInfo, public IReverseInfo, public ISwapInfo{private: ULONG m_cRef;public: //IUnknown STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppv); STDMETHODIMP_(ULONG)AddRef(void); STDMETHODIMP_(ULONG)Release(void); //ICopyInfo STDMETHODIMP CopyAge(IUserInfo *lpDest, IUserInfo *lpSrc); STDMETHODIMP CopyName(IUserInfo *lpDest, IUserInfo *lpSrc); STDMETHODIMP CopySex(IUserInfo *lpDest, IUserInfo *lpSrc); STDMETHODIMP CopyAll(IUserInfo *lpDest, IUserInfo *lpSrc); //IReverseInfo STDMETHODIMP ReverseAge(IUserInfo *lpIUserInfo); STDMETHODIMP ReverseName(IUserInfo *lpIUserInfo); STDMETHODIMP ReverseSex(IUserInfo *lpIUserInfo); STDMETHODIMP ReverseAll(IUserInfo *lpIUserInfo);

Page 108: 6935316 DCOM Microsoft Distributed Component Object Model

//ISwapInfo STDMETHODIMP SwapAge(IUserInfo *lpIUserInfo1, IUserInfo *lpIUserInfo2); STDMETHODIMP SwapName(IUserInfo *lpIUserInfo1, IUserInfo *lpIUserInfo2); STDMETHODIMP SwapSex(IUserInfo *lpIUserInfo1, IUserInfo *lpIUserInfo2); STDMETHODIMP SwapAll(IUserInfo *lpIUserInfo1, IUserInfo *lpIUserInfo2); //Constructor CUserInfoHandler() { m_cRef = 0; }};//CUserInfoHandler

////CUserInfoHandlerFactory//class CUserInfoHandlerFactory : public IClassFactory{private: ULONG m_cRef;public: //IUnknown STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppv); STDMETHODIMP_(ULONG)AddRef(void); STDMETHODIMP_(ULONG)Release(void); //IClassFactory STDMETHODIMP CreateInstance(IUnknown* pUnknownOuter, REFIID iid, LPVOID *ppv); STDMETHODIMP LockServer(BOOL bLock); //Constructor CUserInfoHandlerFactory() { m_cRef = 0; }};//CUserInfoHandlerFactory

#endif

Listing 3-7. UserInfoHandler.cpp

////UserInfoHandler.cpp//#include "UserInfoHandler.h"

////Forward declarations//extern BOOL ServerCanUnloadNow(void);extern void UnloadServer(void);

Page 109: 6935316 DCOM Microsoft Distributed Component Object Model

////Global variables//extern ULONG g_cObjects;extern ULONG g_cLocks;

////CUserInfoHandler//

////CopyAge//STDMETHODIMP CUserInfoHandler::CopyAge(IUserInfo *lpDest, IUserInfo *lpSrc){ HRESULT hr; short nTmpAge;

//Retrieve information from the source hr = lpSrc->get_Age(&nTmpAge); if (SUCCEEDED(hr)) { //Apply it to the destination hr = lpDest->put_Age(nTmpAge); } return hr;}//CopyAge

////CopyName//STDMETHODIMP CUserInfoHandler::CopyName(IUserInfo *lpDest, IUserInfo *lpSrc){ HRESULT hr; LPSTR lpszTmpName = NULL;

//Retrieve information from the source hr = lpSrc->get_Name(&lpszTmpName); if (SUCCEEDED(hr)) { //Apply it to the destination hr = lpDest->put_Name(lpszTmpName); } return hr;}//CopyName

////CopySex//STDMETHODIMP CUserInfoHandler::CopySex(IUserInfo *lpDest,

Page 110: 6935316 DCOM Microsoft Distributed Component Object Model

IUserInfo *lpSrc){ HRESULT hr; BYTE byTmpSex;

//Retrieve information from the source hr = lpSrc->get_Sex(&byTmpSex); if (SUCCEEDED(hr)) { //Apply it to the destination hr = lpDest->put_Sex(byTmpSex); } return hr;}//CopySex

////CopyAll//STDMETHODIMP CUserInfoHandler::CopyAll(IUserInfo *lpDest, IUserInfo *lpSrc){ HRESULT hr;

//Copy each property //Copy Age hr = CopyAge(lpDest, lpSrc); if (SUCCEEDED(hr)) { //Copy Name hr = CopyName(lpDest, lpSrc); if (SUCCEEDED(hr)) { //Copy Sex hr = CopySex(lpDest, lpSrc); } } return hr;}//CopyAll

////ReverseAge//STDMETHODIMP CUserInfoHandler::ReverseAge(IUserInfo *lpIUserInfo){ HRESULT hr; short nTmpAge; short nReversedAge; char szAgeString[10]; char szReversedAgeString[10];

//Retrieve the age hr = lpIUserInfo->get_Age(&nTmpAge); if (SUCCEEDED(hr))

Page 111: 6935316 DCOM Microsoft Distributed Component Object Model

{ //Convert the number to a string ltoa(nTmpAge, szAgeString, 10); //Reverse the string strcpy(szReversedAgeString, _strrev(szAgeString)); //Convert the string to a number nReversedAge = atoi(szReversedAgeString); //Set the age property to the reversed value hr = lpIUserInfo->put_Age(nReversedAge); } return hr;}//ReverseAge

////ReverseName//STDMETHODIMP CUserInfoHandler::ReverseName(IUserInfo *lpIUserInfo){ HRESULT hr; LPSTR lpszTmpName = NULL; LPSTR lpszReversedName = NULL;

//Retrieve the name hr = lpIUserInfo->get_Name(&lpszTmpName); if (SUCCEEDED(hr)) { //Reverse the string lpszReversedName = _strrev(lpszTmpName); //Set the name property to the reversed value hr = lpIUserInfo->put_Name(lpszReversedName); } return hr;}//ReverseName

////ReverseSex//STDMETHODIMP CUserInfoHandler::ReverseSex(IUserInfo *lpIUserInfo){ HRESULT hr; BYTE byTmpSex;

//Retrieve the sex hr = lpIUserInfo->get_Sex(&byTmpSex); if (SUCCEEDED(hr)) { //Reverse the sex if (('M' == byTmpSex)||('m' == byTmpSex)) hr = lpIUserInfo->put_Sex('F'); else hr = lpIUserInfo->put_Sex('M');

Page 112: 6935316 DCOM Microsoft Distributed Component Object Model

} return hr;}//ReverseSex

////ReverseAll//STDMETHODIMP CUserInfoHandler::ReverseAll(IUserInfo *lpIUserInfo){ HRESULT hr;

//Reverse each property //Reverse Age hr = ReverseAge(lpIUserInfo); if (SUCCEEDED(hr)) { //Reverse Name hr = ReverseName(lpIUserInfo); if (SUCCEEDED(hr)) { //Reverse Sex hr = ReverseSex(lpIUserInfo); } } return hr;}//ReverseAll

////SwapAge//STDMETHODIMP CUserInfoHandler::SwapAge(IUserInfo *lpIUserInfo1, IUserInfo *lpIUserInfo2){ HRESULT hr; short nTmpAge1; short nTmpAge2;

//Retrieve the age from the first UserInfo hr = lpIUserInfo1->get_Age(&nTmpAge1); if (SUCCEEDED(hr)) { //Retrieve the age from the second UserInfo hr = lpIUserInfo2->get_Age(&nTmpAge2); if (SUCCEEDED(hr)) { //Set the age of the first UserInfo hr = lpIUserInfo1->put_Age(nTmpAge2); if (SUCCEEDED(hr)) { //Set the age of the second UserInfo hr = lpIUserInfo2->put_Age(nTmpAge1); }

Page 113: 6935316 DCOM Microsoft Distributed Component Object Model

} } return hr;}//SwapAge

////SwapName//STDMETHODIMP CUserInfoHandler::SwapName(IUserInfo *lpIUserInfo1, IUserInfo *lpIUserInfo2){ HRESULT hr; LPSTR lpszTmpName1 = NULL; LPSTR lpszTmpName2 = NULL; LPSTR lpszCopiedName1 = NULL; LPSTR lpszCopiedName2 = NULL; long lStringLen;

//Retrieve the name from the first UserInfo hr = lpIUserInfo1->get_Name(&lpszTmpName1); if (SUCCEEDED(hr)) { lStringLen = strlen(lpszTmpName1); if (lStringLen > 0) { lpszCopiedName1 = new char[lStringLen + 1]; //Copy the string value strcpy(lpszCopiedName1, lpszTmpName1); } //Retrieve the name from the second UserInfo hr = lpIUserInfo2->get_Name(&lpszTmpName2); if (SUCCEEDED(hr)) { lStringLen = strlen(lpszTmpName2); if (lStringLen > 0) { lpszCopiedName2 = new char[lStringLen + 1]; //Copy the string value strcpy(lpszCopiedName2, lpszTmpName2); } //Set the name of the first UserInfo hr = lpIUserInfo1->put_Name(lpszCopiedName2); if (SUCCEEDED(hr)) { //Set the name of the second UserInfo hr = lpIUserInfo2->put_Name(lpszCopiedName1); } } } //Clean up if (lpszCopiedName1) delete[ ] lpszCopiedName1; if (lpszCopiedName2)

Page 114: 6935316 DCOM Microsoft Distributed Component Object Model

delete[ ] lpszCopiedName2; return hr;}//SwapName

////SwapSex//STDMETHODIMP CUserInfoHandler::SwapSex(IUserInfo *lpIUserInfo1, IUserInfo *lpIUserInfo2){ HRESULT hr; BYTE byTmpSex1; BYTE byTmpSex2;

//Retrieve the sex from the first UserInfo hr = lpIUserInfo1->get_Sex(&byTmpSex1); if (SUCCEEDED(hr)) { //Retrieve the sex from the second UserInfo hr = lpIUserInfo2->get_Sex(&byTmpSex2); if (SUCCEEDED(hr)) { //Set the sex of the first UserInfo hr = lpIUserInfo1->put_Sex(byTmpSex2); if (SUCCEEDED(hr)) { //Set the sex of the second UserInfo hr = lpIUserInfo2->put_Sex(byTmpSex1); } } } return hr;}//SwapSex

////SwapAll//STDMETHODIMP CUserInfoHandler::SwapAll(IUserInfo *lpIUserInfo1, IUserInfo *lpIUserInfo2){ HRESULT hr; //Swap each property //Swap Age hr = SwapAge(lpIUserInfo1, lpIUserInfo2); if (SUCCEEDED(hr)) { //Swap Name hr = SwapName(lpIUserInfo1, lpIUserInfo2); if (SUCCEEDED(hr)) { //Swap Sex hr = SwapSex(lpIUserInfo1, lpIUserInfo2); }

Page 115: 6935316 DCOM Microsoft Distributed Component Object Model

} return hr;}//SwapAll

////QueryInterface//STDMETHODIMP CUserInfoHandler::QueryInterface(REFIID iid, LPVOID *ppv){ *ppv = NULL; if (IID_IUnknown == iid) *ppv = (LPVOID)(IUnknown *)(ICopyInfo *)this; else if (IID_ICopyInfo == iid) *ppv = (LPVOID)(ICopyInfo *)this; else if (IID_IReverseInfo == iid) *ppv = (LPVOID)(IReverseInfo *)this; else if (IID_ISwapInfo == iid) *ppv = (LPVOID)(ISwapInfo *)this; else return E_NOINTERFACE; //Interface not supported //Perform reference count through the returned interface ((IUnknown *)*ppv)->AddRef(); return NOERROR;}//QueryInterface

////AddRef//STDMETHODIMP_(ULONG)CUserInfoHandler::AddRef(void){ return ++m_cRef;}//AddRef

////Release//STDMETHODIMP_(ULONG)CUserInfoHandler::Release(void){ m_cRef-; if (0 == m_cRef) { delete this; //Decrement the global object count g_cObjects-; //See if it's alright to unload the server if (::ServerCanUnloadNow( )) ::UnloadServer( ); return 0; } return m_cRef;}//Release

Page 116: 6935316 DCOM Microsoft Distributed Component Object Model

////CUserInfoHandlerFactory Class Factory//

////CreateInstance//STDMETHODIMP CUserInfoHandlerFactory::CreateInstance (IUnknown* pUnknownOuter, REFIID iid, LPVOID *ppv){ HRESULT hr; CUserInfoHandler *pCUserInfoHandler = NULL;

*ppv = NULL; //This object doesn't support aggregation if (NULL != pUnknownOuter) return CLASS_E_NOAGGREGATION; //Create the CUserInfo object pCUserInfoHandler = new CUserInfoHandler( ); if (NULL == pCUserInfoHandler) return E_OUTOFMEMORY; //Retrieve the requested interface hr = pCUserInfoHandler->QueryInterface(iid, ppv); if (FAILED(hr)) { delete pCUserInfoHandler; pCUserInfoHandler = NULL; return hr; } //Increment the global object counter g_cObjects++;

return NOERROR;}//CreateInstance

////LockServer//STDMETHODIMP CUserInfoHandlerFactory::LockServer(BOOL bLock){ if (bLock) g_cLocks++; else { g_cLocks-; //See if it's alright to unload the server if (::ServerCanUnloadNow( )) ::UnloadServer( ); } return NOERROR;}//LockServer

//

Page 117: 6935316 DCOM Microsoft Distributed Component Object Model

//QueryInterface//STDMETHODIMP CUserInfoHandlerFactory::QueryInterface (REFIID iid, LPVOID *ppv){ *ppv = NULL; if (IID_IUnknown == iid) *ppv = (LPVOID)(IUnknown *)this; else if (IID_IClassFactory == iid) *ppv = (LPVOID)(IClassFactory *)this; else return E_NOINTERFACE; //Interface not supported //Perform reference count through the returned interface ((IUnknown *)*ppv)->AddRef( ); return NOERROR;}//QueryInterface

////AddRef//STDMETHODIMP_(ULONG) CUserInfoHandlerFactory::AddRef(void){ return ++m_cRef;}//AddRef

////Release//STDMETHODIMP_(ULONG) CUserInfoHandlerFactory::Release(void){ m_cRef-; if (0 == m_cRef) { delete this; return 0; } return m_cRef;}//Release

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 118: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The UserInfoHandlerClient application is, as its name suggests, asample UserInfoHandler client. The UserInfoHandlerClient putsthe UserInfoHandler server through its paces by using several functionsfrom each of the supported interfaces. I have included the source code for theUserInfoHandlerClient application on the CD-ROM for your viewingpleasure, so enjoy!

Summary

In this chapter, we learned:

•  That because out-of-process servers don’t share the same addressspace as their clients, they must rely on proxy/stub pairs to marshal andunmarshal data.

•  How to use the MIDL-generated files to create proxy/stub pairs.

•  The different techniques that out-of-process servers must use toexpose COM’s required registration and class factory information, asthey cannot export functions because of process boundary restraints.

•  That components are implemented in the exact same manner,regardless of whether they are part of an in-process or out-of-processserver.

In the next chapter you learn about containment and aggregation, COM’sobject-reuse techniques.

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 119: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 120: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Chapter 4Reusing COM ObjectsIN THIS CHAPTER

•  How to reuse a COM object through containment

•  How to reuse a COM object through aggregation

OFTEN, THE COM objects at your disposal may provide only a limited subset of thefunctionality that you desire; when that happens, you must develop new components thatfully implement the desired level of functionality. However, COM provides two techniquesfor reusing and extending the functionality of existing COM objects. These twomechanisms, containment and aggregation, are the focus of this chapter.

Understanding Containment

Containment is the simplest form of COM object reuse. In containment, the new COMobject, known as the outer object, simply acts as a client of the existing COM object,which is known as the inner object. Like all COM objects, the outer object exposes its ownset of interfaces. However, instead of being solely responsible for providing theimplementation for each of its interface functions, the outer object relies on thefunctionality supplied by the inner object for assistance (see Figure 4-1).

Figure 4-1  In containment, the outer object acts as a client of the inner object.

To further illustrate the containment technique, we will build the MemberInfo COM

Page 121: 6935316 DCOM Microsoft Distributed Component Object Model

object. Using the containment technique, the MemberInfo COM object expands on theUserInfo object that we developed in Chapter 2. While the UserInfo object providessuch basic information as the user’s name and age, the MemberInfo object providesadditional information such as the user’s mailing address and telephone number.

The MemberInfo object begins with the usual routine of allocating GUIDs and defininginterfaces. The MemberInfo object has only one interface, IMemberInfo, whichdefines the properties of the MemberInfo object (see Table 4-1).

Table 4-1 Memberinfo Object Properties

Property Name Data Type Implemented In

Name LPSTR IUserInfoSex unsigned char IUserInfoAddress LPSTR IMemberInfoCity LPSTR IMemberInfoState LPSTR IMemberInfoZip LPSTR IMemberInfoPhone LPSTR IMemberInfo

The MemberInfo object and the IMemberInfo interface are then defined in theMemberInfo.idl file using IDL (see Listing 4-1). The MemberInfo.idl file is thencompiled using MIDL to generate a type library and the support files necessary to create anIMemberInfo interface proxy/stub pair.

Listing 4-1. MemberInfo.idl

////MemberInfo.idl//

import "unknwn.idl";

//IID_IMemberInfo//These are the attributes of the IMemberInfo interface[ object, uuid(930e5c02-a792-11d0-94ab-00a024a85a21), helpstring("IMemberInfo Interface.")]//Declaration of the IMemberInfo interfaceinterface IMemberInfo : IUnknown{ //List of function definitions for each method supported //by the interface // //[attributes] returntype [calling convention] // funcname(params); //

Page 122: 6935316 DCOM Microsoft Distributed Component Object Model

[propget, helpstring("Sets or returns the address of the member.")] HRESULT Address([out, retval] LPSTR *lpszRetAddress); [propput, helpstring("Sets or returns the address of the member.")] HRESULT Address([in] LPSTR lpszAddress); [propget, helpstring("Sets or returns the city of the member.")] HRESULT City([out, retval] LPSTR *lpszRetCity); [propput, helpstring("Sets or returns the city of the member.")] HRESULT City([in] LPSTR lpszCity); [propget, helpstring("Sets or returns the name of the member.")] HRESULT Name([out, retval] LPSTR *lpszRetName); [propput, helpstring("Sets or returns the name of the member.")] HRESULT Name([in] LPSTR lpszName); [propget, helpstring("Sets or returns the phone number of the member.")] HRESULT Phone([out, retval] LPSTR *lpszRetPhone); [propput, helpstring("Sets or returns the phone number of the member.")] HRESULT Phone([in] LPSTR lpszPhone); [propget, helpstring("Sets or returns the sex of the member.")] HRESULT Sex([out, retval] unsigned char *byRetSex); [propput, helpstring("Sets or returns the sex of the member.")] HRESULT Sex([in] unsigned char bySex); [propget, helpstring("Sets or returns the state of the member.")] HRESULT State([out, retval] LPSTR *lpszRetState); [propput, helpstring("Sets or returns the state of the member.")] HRESULT State([in] LPSTR lpszState); [propget, helpstring("Sets or returns the zip code of the member.")] HRESULT Zip([out, retval] LPSTR *lpszRetZip); [propput, helpstring("Sets or returns the zip code of the member.")]

HRESULT Zip([in] LPSTR lpszZip);}

//LIBID_MemberInfo//These are the attributes of the type library[ uuid(930e5c00-a792-11d0-94ab-00a024a85a21), helpstring("MemberInfo Type Library."), version(1.0)

Page 123: 6935316 DCOM Microsoft Distributed Component Object Model

]//Definition of the MemberInfo type librarylibrary MemberInfo{ //CLSID_MemberInfo //Attributes of the MemberInfo object [ uuid(930e5c01-a792-11d0-94ab-00a024a85a21), helpstring("MemberInfo Object.") ] //Definition of the MemberInfo object coclass MemberInfo { //List all of the interfaces supported by the object [default] interface IMemberInfo; }}

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 124: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The next step is to define the CMemberInfo C++ class and provideimplementations for the IMemberInfo interface. The containment process beginswith the creation of the inner object, which in this case is the UserInfo object. TheCOM API CoCreateInstance is used to create the inner UserInfo object aspart of the CMemberInfo::Initialize function. Notice that theCMemberInfo member variable m_pIUserInfo is used to maintain thereference to the IUserInfo interface returned by the call toCoCreateInstance:

HRESULT CMemberInfo::Initialize(void){ return CoCreateInstance(CLSID_UserInfo, NULL, CLSCTX_INPROC_SERVER, IID_IUserInfo, (LPVOID *)&m_pIUserInfo);}//Initialize

CMemberInfo::Initialize is called withinCMemberInfoFactory::CreateInstance, the class factory functionresponsible for creating MemberInfo objects:

STDMETHODIMP CMemberInfoFactory::CreateInstance (IUnknown* pUnknownOuter, REFIID iid, LPVOID *ppv){ HRESULT hr; CMemberInfo *pCMemberInfo = NULL;

*ppv = NULL; //This object doesn’t support aggregation if (NULL != pUnknownOuter) return CLASS_E_NOAGGREGATION;

Page 125: 6935316 DCOM Microsoft Distributed Component Object Model

//Create the CMemberInfo object pCMemberInfo = new CMemberInfo(); if (NULL == pCMemberInfo) return E_OUTOFMEMORY; //Initialize the new object hr = pCMemberInfo->Initialize(); if (FAILED(hr)) goto cleanUP; //Retrieve the requested interface hr = pCMemberInfo->QueryInterface(iid, ppv); if (FAILED(hr)) goto cleanUP; //Increment the global object counter g_cObjects++;

return NOERROR;cleanUP: //Some type of error occurred, cleanup before //returning if (pCMemberInfo) { delete pCMemberInfo; pCMemberInfo = NULL; } return hr;}//CreateInstance

The MemberInfo object then uses the IUserInfo interface reference stored inthe m_pIUserInfo member variable to manipulate the inner UserInfo objectduring the MemberInfo object’s implementation of both the Name and Sexproperties:

STDMETHODIMP CMemberInfo::get_Name(LPSTR *lpszRetName){ return m_pIUserInfo->get_Name(lpszRetName);}//get_Name

STDMETHODIMP CMemberInfo::put_Name(LPSTR lpszName){ return m_pIUserInfo->put_Name(lpszName);}//put_Name...STDMETHODIMP CMemberInfo::get_Sex(BYTE *byRetSex){ return m_pIUserInfo->get_Sex(byRetSex);}//get_Sex

STDMETHODIMP CMemberInfo::put_Sex(BYTE bySex)

Page 126: 6935316 DCOM Microsoft Distributed Component Object Model

{ return m_pIUserInfo->put_Sex(bySex);}//put_Sex

That’s all there is to containment! When the MemberInfo object is finished usingthe UserInfo object, it destroys it by calling Release as part of its destructor:

CMemberInfo::~CMemberInfo(){ if (m_pIUserInfo) m_pIUserInfo->Release(); if (m_lpszAddress) delete[] m_lpszAddress; if (m_lpszCity) delete[] m_lpszCity; if (m_lpszPhone) delete[] m_lpszPhone; if (m_lpszState) delete[] m_lpszState; if (m_lpszZip) delete[] m_lpszZip;}//~CMemberInfo

Since the MemberInfo object is an in-process object, the MemberInfo clientperforms the required calls to CoInitialize and CoUninitialize, relievingthe MemberInfo object of that responsibility. Definitions of the CMemberInfoand CMemberInfoFactory C++ classes can be seen in MemberInfo.h, which isprovided in Listing 4-2. Their implementations can be seen in MemberInfo.cpp,which is also provided, in Listing 4-3.

Listing 4-2. MemberInfo.h

////MemberInfo.h//#if !defined MEMBERINFO_H#define MEMBERINFO_H

#include "MemberInfo_i.h"#include "UserInfo_i.h"

////CMemberInfo//class CMemberInfo : IMemberInfo{private: ULONG m_cRef; IUserInfo *m_pIUserInfo; LPSTR m_lpszAddress; LPSTR m_lpszCity;

Page 127: 6935316 DCOM Microsoft Distributed Component Object Model

LPSTR m_lpszPhone; LPSTR m_lpszState; LPSTR m_lpszZip;public: //IUnknown STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppv); STDMETHODIMP_(ULONG)AddRef(void); STDMETHODIMP_(ULONG)Release(void); //IMemberInfo STDMETHODIMP get_Address(LPSTR *lpszRetAddress); STDMETHODIMP put_Address(LPSTR lpszAddress); STDMETHODIMP get_City(LPSTR *lpszRetCity); STDMETHODIMP put_City(LPSTR lpszCity); STDMETHODIMP get_Name(LPSTR *lpszRetName); STDMETHODIMP put_Name(LPSTR lpszName); STDMETHODIMP get_Phone(LPSTR *lpszRetPhone); STDMETHODIMP put_Phone(LPSTR lpszPhone); STDMETHODIMP get_Sex(BYTE *byRetSex); STDMETHODIMP put_Sex(BYTE bySex); STDMETHODIMP get_State(LPSTR *lpszRetState); STDMETHODIMP put_State(LPSTR lpszState); STDMETHODIMP get_Zip(LPSTR *lpszRetZip); STDMETHODIMP put_Zip(LPSTR lpszZip);

HRESULT Initialize(void); //Constructor CMemberInfo(); //Destructor ~CMemberInfo();};//CMemberInfo

////CMemberInfoFactory//class CMemberInfoFactory : public IClassFactory{private: ULONG m_cRef;public: //IUnknown STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppv); STDMETHODIMP_(ULONG)AddRef(void); STDMETHODIMP_(ULONG)Release(void); //IClassFactory STDMETHODIMP CreateInstance(IUnknown* pUnknownOuter, REFIID iid, LPVOID *ppv); STDMETHODIMP LockServer(BOOL bLock); //Constructor CMemberInfoFactory() {

Page 128: 6935316 DCOM Microsoft Distributed Component Object Model

m_cRef = 0; }};//CMemberInfoFactory

#endif

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 129: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Listing 4-3. MemberInfo.pp

////MemberInfo.cpp//#include "MemberInfo.h"

////Forward declarations//extern BOOL ServerCanUnloadNow(void);extern void UnloadServer(void);

////Global variables//extern ULONG g_cObjects;extern ULONG g_cLocks;

////CMemberInfo//

////get_Address//STDMETHODIMP CMemberInfo::get_Address(LPSTR *lpszRetAddress){ *lpszRetAddress = m_lpszAddress; return NOERROR;}//get_Address

Page 130: 6935316 DCOM Microsoft Distributed Component Object Model

////put_Address//STDMETHODIMP CMemberInfo::put_Address(LPSTR lpszAddress){ long lStringLen;

//Deallocate any previously allocated storage if (m_lpszAddress) delete[] m_lpszAddress; m_lpszAddress = NULL; //Allocate enough storage for the string lStringLen = strlen(lpszAddress); if (lStringLen > 0) { m_lpszAddress = new char[lStringLen + 1]; //Copy the string strcpy(m_lpszAddress, lpszAddress); } return NOERROR;}//put_Address

////get_City//STDMETHODIMP CMemberInfo::get_City(LPSTR *lpszRetCity){ *lpszRetCity = m_lpszCity; return NOERROR;}//get_City

////put_City//STDMETHODIMP CMemberInfo::put_City(LPSTR lpszCity){ long lStringLen;

//Deallocate any previously allocated storage if (m_lpszCity) delete[] m_lpszCity; m_lpszCity = NULL; //Allocate enough storage for the string lStringLen = strlen(lpszCity); if (lStringLen > 0) { m_lpszCity = new char[lStringLen + 1]; //Copy the string strcpy(m_lpszCity, lpszCity);

Page 131: 6935316 DCOM Microsoft Distributed Component Object Model

} return NOERROR;}//put_City

////get_Name//STDMETHODIMP CMemberInfo::get_Name(LPSTR *lpszRetName){ return m_pIUserInfo->get_Name(lpszRetName);}//get_Name

////put_Name//STDMETHODIMP CMemberInfo::put_Name(LPSTR lpszName){ return m_pIUserInfo->put_Name(lpszName);}//put_Name

////get_Phone//STDMETHODIMP CMemberInfo::get_Phone(LPSTR *lpszRetPhone){ *lpszRetPhone = m_lpszPhone; return NOERROR;}//get_Phone

////put_Phone//STDMETHODIMP CMemberInfo::put_Phone(LPSTR lpszPhone){ long lStringLen;

//Deallocate any previously allocated storage if (m_lpszPhone) delete[] m_lpszPhone; m_lpszPhone = NULL; //Allocate enough storage for the string lStringLen = strlen(lpszPhone); if (lStringLen > 0) { m_lpszPhone = new char[lStringLen + 1]; //Copy the string strcpy(m_lpszPhone, lpszPhone); } return NOERROR;}//put_Phone

Page 132: 6935316 DCOM Microsoft Distributed Component Object Model

////get_Sex//STDMETHODIMP CMemberInfo::get_Sex(BYTE *byRetSex){ return m_pIUserInfo->get_Sex(byRetSex);}//get_Sex

////put_Sex//STDMETHODIMP CMemberInfo::put_Sex(BYTE bySex){ return m_pIUserInfo->put_Sex(bySex);}//put_Sex

////get_State//STDMETHODIMP CMemberInfo::get_State(LPSTR *lpszRetState){ *lpszRetState = m_lpszState; return NOERROR;}//get_State

////put_State//STDMETHODIMP CMemberInfo::put_State(LPSTR lpszState){ long lStringLen;

//Deallocate any previously allocated storage if (m_lpszState) delete[] m_lpszState; m_lpszState = NULL; //Allocate enough storage for the string lStringLen = strlen(lpszState); if (lStringLen > 0) { m_lpszState = new char[lStringLen + 1]; //Copy the string strcpy(m_lpszState, lpszState); } return NOERROR;}//put_State

////get_Zip

Page 133: 6935316 DCOM Microsoft Distributed Component Object Model

//STDMETHODIMP CMemberInfo::get_Zip(LPSTR *lpszRetZip){ *lpszRetZip = m_lpszZip; return NOERROR;}//get_Zip

////put_Zip//STDMETHODIMP CMemberInfo::put_Zip(LPSTR lpszZip){ long lStringLen;

//Deallocate any previously allocated storage if (m_lpszZip) delete[] m_lpszZip; m_lpszZip = NULL; //Allocate enough storage for the string lStringLen = strlen(lpszZip); if (lStringLen > 0) { m_lpszZip = new char[lStringLen + 1]; //Copy the string strcpy(m_lpszZip, lpszZip); } return NOERROR;}//put_Zip

////QueryInterface//STDMETHODIMP CMemberInfo::QueryInterface(REFIID iid, LPVOID *ppv){ *ppv = NULL; if (IID_IUnknown == iid) *ppv = (LPVOID)(IUnknown *)this; else if (IID_IMemberInfo == iid) *ppv = (LPVOID)(IMemberInfo *)this; else return E_NOINTERFACE; //Interface not supported //Perform reference count through the returned interface ((IUnknown *)*ppv)->AddRef(); return NOERROR;}//QueryInterface

////AddRef//

Page 134: 6935316 DCOM Microsoft Distributed Component Object Model

STDMETHODIMP_(ULONG)CMemberInfo::AddRef(void){ return ++m_cRef;}//AddRef

////Release//STDMETHODIMP_(ULONG)CMemberInfo::Release(void){ m_cRef—; if (0 == m_cRef) { delete this; //Decrement the global object count g_cObjects—; //See if it's alright to unload the server if (::ServerCanUnloadNow()) ::UnloadServer(); return 0; } return m_cRef;}//Release

////Initialize//HRESULT CMemberInfo::Initialize(void)

{ return CoCreateInstance(CLSID_UserInfo, NULL, CLSCTX_INPROC_SERVER, IID_IUserInfo, (LPVOID *)&m_pIUserInfo);}//Initialize

////Constructor//CMemberInfo::CMemberInfo(){ m_cRef = 0; m_pIUserInfo = NULL; m_lpszAddress = NULL; m_lpszCity = NULL; m_lpszPhone = NULL; m_lpszState = NULL; m_lpszZip = NULL;}//CMemberInfo

//

Page 135: 6935316 DCOM Microsoft Distributed Component Object Model

//Destructor//CMemberInfo::~CMemberInfo(){ if (m_pIUserInfo) m_pIUserInfo->Release(); if (m_lpszAddress) delete[] m_lpszAddress; if (m_lpszCity) delete[] m_lpszCity; if (m_lpszPhone) delete[] m_lpszPhone; if (m_lpszState) delete[] m_lpszState; if (m_lpszZip) delete[] m_lpszZip;}//~CMemberInfo

////CMemberInfoFactory Class Factory//

////CreateInstance//STDMETHODIMP CMemberInfoFactory::CreateInstance (IUnknown* pUnknownOuter, REFIID iid, LPVOID *ppv){ HRESULT hr; CMemberInfo *pCMemberInfo = NULL; *ppv = NULL; //This object doesn't support aggregation if (NULL != pUnknownOuter) return CLASS_E_NOAGGREGATION; //Create the CMemberInfo object pCMemberInfo = new CMemberInfo(); if (NULL == pCMemberInfo) return E_OUTOFMEMORY; //Initialize the new object hr = pCMemberInfo->Initialize(); if (FAILED(hr)) goto cleanUP; //Retrieve the requested interface hr = pCMemberInfo->QueryInterface(iid, ppv); if (FAILED(hr)) goto cleanUP; //Increment the global object counter g_cObjects++;

Page 136: 6935316 DCOM Microsoft Distributed Component Object Model

return NOERROR;cleanUP: //Some type of error occurred, cleanup before returning if (pCMemberInfo) { delete pCMemberInfo; pCMemberInfo = NULL; } return hr;}//CreateInstance

////LockServer//STDMETHODIMP CMemberInfoFactory::LockServer(BOOL bLock){ if (bLock) g_cLocks++; else { g_cLocks—; //See if it's alright to unload the server if (::ServerCanUnloadNow()) ::UnloadServer(); } return NOERROR;}//LockServer

////QueryInterface//STDMETHODIMP CMemberInfoFactory::QueryInterface (REFIID iid, LPVOID *ppv){ *ppv = NULL; if (IID_IUnknown == iid) *ppv = (LPVOID)(IUnknown *)this; else if (IID_IClassFactory == iid) *ppv = (LPVOID)(IClassFactory *)this; else return E_NOINTERFACE; //Interface not supported //Perform reference count through the returned interface ((IUnknown *)*ppv)->AddRef(); return NOERROR;}//QueryInterface

////AddRef//STDMETHODIMP_(ULONG) CMemberInfoFactory::AddRef(void)

Page 137: 6935316 DCOM Microsoft Distributed Component Object Model

{ return ++m_cRef;}//AddRef

////Release//STDMETHODIMP_(ULONG) CMemberInfoFactory::Release(void){ m_cRef—; if (0 == m_cRef) { delete this; return 0; } return m_cRef;}//Release

While containment is best suited for those situations in which the interfaces of the innerobject provide some, but not all, of the functionality you desire, there are instances whenan object’s interface provides the exact level of functionality that you desire. So ratherthan duplicate and delegate each interface function from the outer object to the innerobject, COM provides an alternative technique for object reuse known as aggregation.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 138: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Understanding Aggregation

In aggregation, interfaces of the inner object are exposed directly, as if theywere implemented on the outer object itself (see Figure 4-2).

Figure 4-2  In aggregation, the outer object exposes the interfaces of the innerobject as its own.

Unlike containment, in which the inner object has no idea that it is being usedas part of another object, in aggregation, the inner object is not only aware thatit is being used as part of an aggregate, it must be developed specifically tosupport aggregation. To understand why a COM object must be specificallydeveloped to support aggregation, imagine that you have a pointer to interfacex, which is implemented by the inner object, and you call QueryInterfacerequesting interface y, which is implemented by the outer object. Since theinner object has no knowledge of interface y, it has no recourse but to fail theQueryInterface call with an E_NOINTERFACE error code. However,from the perspective of the outer object’s client, this clearly violates COM’srules of interface navigation, which state that a client must be able to get to anyinterface defined by an object from any other interface defined on that sameobject. As a client of the outer object, you should be able toQueryInterface on interface x and receive interface y, as well asQueryInterface on interface y and receive interface x. Solving thisproblem requires cooperation from both the inner and outer objects. When theouter object creates the inner object using CoCreateInstance, the twoobjects exchange IUnknown interfaces. The outer object supplies a pointer toits IUnknown interface to the inner object as the second parameter to

Page 139: 6935316 DCOM Microsoft Distributed Component Object Model

CoCreateInstance, while the inner object returns a pointer to itsIUnknown interface to the outer object as the final parameter toCoCreateInstance. If a client has a reference to an interface of the outerobject and performs a QueryInterface call for an interface supported bythe inner object, the outer object simply delegates the QueryInterface callto the inner object using its reference to the inner object’s IUnknowninterface. Likewise, if a client has a reference to an interface of the innerobject, and performs a QueryInterface call for an interface supported bythe outer object, the inner object simply delegates the QueryInterface callto the outer object using its reference to the outer object’s IUnknowninterface (see Figure 4-3).

Figure 4-3  By exchanging IUnknown interface pointers, the outer object candelegate to the inner object for further processing requests for interfacessupported by the inner object. Likewise, the inner object can delegate to theouter object for further processing requests for interfaces supported by theouter object.

To illustrate COM’s aggregation technique, we will build the AccountInfoCOM object. The AccountInfo object defines two interfaces —IAccountInfo and IMemberInfo — for maintaining informationregarding individual credit card accounts. The properties of theAccountInfo object are listed in Table 4-2.

Table 4-2 AccountInfo Object Interfaces and Properties

Property Data Type Implemented In

AccountNumber long IAccountInfoAccountBalance float IAccountInfoAccountLimit long IAccountInfoName LPSTR IMemberInfoSex unsigned char IMemberInfoAddress LPSTR IMemberInfoCity LPSTR IMemberInfoState LPSTR IMemberInfoZip LPSTR IMemberInfoPhone LPSTR IMemberInfo

As you may have noticed, the AccountInfo object’s IMemberInfointerface is exactly the same as the IMemberInfo interface defined earlierby the MemberInfo object. That’s because we are going to use theMemberInfo object as the inner object. The AccountInfo object will

Page 140: 6935316 DCOM Microsoft Distributed Component Object Model

implement the IAccountInfo interface itself, but will aggregate theMemberInfo object in order to expose the IMemberInfo interface. I thinkit’s worth mentioning that containment and aggregation aren’t mutuallyexclusive. Interfaces created through containment can be aggregated later and,likewise, aggregate interfaces can be contained later. For example, thischapter’s AccountInfo object exposes the IMemberInfo interfacedirectly through aggregation, and it just so happens that the IMemberInfointerface was created through containment of Chapter 2’s IUserInfointerface.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 141: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Creation of the AccountInfo object begins with the same routine as always — allocatingGUIDs and defining the object using IDL. Even though the AccountInfo object itself doesn’tdirectly implement IMemberInfo, it must still define it as a supported interface in the objectdefinition. But rather than redefine the IMemberInfo interface, we can just import itsdefinition from the MemberInfo.idl file using the import directive:

import "MemberInfo.idl";

Once the IMemberInfo interface definition has been imported, and the IAccountInfointerface defined, the AccountInfo object can be defined (see Listing 4-4).

Listing 4-4. AccountInfo.idl

////AccountInfo.idl//import "unknwn.idl";//Import declaration for IMemberInfoimport "MemberInfo/MemberInfo.idl";

//IID_IAcountInfo//These are the attributes of the IAccountInfo interface[ object, uuid(4d463de2-a80b-11d0-94ab-00a024a85a21), helpstring("IAccountInfo Interface.")]//Declaration of the IAccountInfo interfaceinterface IAccountInfo : IUnknown{ //List of function definitions for each method //supported by the interface // //[attributes] returntype [calling convention]

Page 142: 6935316 DCOM Microsoft Distributed Component Object Model

// funcname(params); // [propget, helpstring("Sets or returns the account number.")] HRESULT Number([out, retval] long *lRetNumber); [propput, helpstring("Sets or returns the account number.")] HRESULT Number([in] long lNumber); [propget, helpstring("Sets or returns the account balance.")] HRESULT Balance([out, retval] float *flRetBalance); [propput, helpstring("Sets or returns the account balance.")] HRESULT Balance([in] float flBalance); [propget, helpstring("Sets or returns the account limit.")] HRESULT Limit([out, retval] long *lRetLimit); [propput, helpstring("Sets or returns the account limit.")] HRESULT Limit([in] long lLimit);}

//LIBID_AccountInfo//These are the attributes of the type library[ uuid(4d463de0-a80b-11d0-94ab-00a024a85a21), helpstring("AccountInfo Type Library."), version(1.0)]//Definition of the AccountInfo type librarylibrary AccountInfo{ //CLSID_AccountInfo //Attributes of the AccountInfo object [ uuid(4d463de1-a80b-11d0-94ab-00a024a85a21), helpstring("AccountInfo Object.") ] //Definition of the AccountInfo object coclass AccountInfo { //List all of the interfaces supported by the object [default] interface IAccountInfo; interface IMemberInfo; }}

Development of the AccountInfo object continues in typical fashion, by compiling the IDLfile using the MIDL compiler and defining the CAccountInfo C++ class. Notice that theCAccountInfo class doesn’t inherit from IMemberInfo; nor does it define any of theIMemberInfo interface functions. This is because the AccountInfo object will aggregatethe MemberInfo object in order to provide the implementation for the IMemberInfointerface. AccountInfo aggregates MemberInfo by exchanging IUnknown interfacepointers with MemberInfo in a call to CoCreateInstance, which is called as part ofCAccountInfo::Initialize. AccountInfo offers its IUnknown pointer toMemberInfo in the second parameter to CoCreateInstance, and at the same time requestsMemberInfo’s IUnknown pointer, which is to be returned in m_pMemberInfoIUnknown— the fifth parameter to CoCreateInstance — if the aggregation process is successful:

Page 143: 6935316 DCOM Microsoft Distributed Component Object Model

HRESULT CAccountInfo::Initialize(void){ return CoCreateInstance(CLSID_MemberInfo, (IUnknown *)this, CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID *)&m_pMemberInfoIUnknown);}//Initialize

CAccountInfo::Initialize is called from within CAccountInfo::CreateInstance, the class factory function responsible for creating AccountInfo objects. Noticethat even though the AccountInfo object aggregates the MemberInfo object, theAccountInfo object itself cannot be aggregated:

STDMETHODIMP CAccountInfoFactory::CreateInstance (IUnknown* pUnknownOuter, REFIID iid, LPVOID *ppv){ HRESULT hr; CAccountInfo *pCAccountInfo = NULL;

*ppv = NULL; //This object doesn't support aggregation if (NULL != pUnknownOuter) return CLASS_E_NOAGGREGATION; //Create the CAccountInfo object pCAccountInfo = new CAccountInfo(); if (NULL == pCAccountInfo) return E_OUTOFMEMORY; //Initialize the new object hr = pCAccountInfo->Initialize(); if (FAILED(hr)) goto cleanUP; //Retrieve the requested interface hr = pCAccountInfo->QueryInterface(iid, ppv); if (FAILED(hr)) goto cleanUP; //Increment the global object counter g_cObjects++;

return NOERROR;cleanUP: //Some type of error occurred, cleanup before //returning if (pCAccountInfo) { delete pCAccountInfo; pCAccountInfo = NULL; } return hr;}//CreateInstance

Ultimately, the call to CoCreateInstance results in the invocation of CreateInstanceon the MemberInfo object’s class factory. The MemberInfo object knows that it is beingcreated as part of an aggregate whenever it detects a nonNULL value in the second parameter toCreateInstance. In an aggregate situation, the second parameter to CreateInstance

Page 144: 6935316 DCOM Microsoft Distributed Component Object Model

contains the IUnknown interface pointer of the outer object, which in this case is theAccountInfo object. The MemberInfo object stores the outer object’s IUnknown interfacepointer, also called the controlling unknown, in the m_pControllingIUnknown membervariable, and uses it to delegate calls to the outer object’s IUnknown functions as necessary:

STDMETHODIMP_(ULONG)CMemberInfo::AddRef(void){ //Delegate to the controlling IUnknown return m_pControllingIUnknown->AddRef();}//AddRef

STDMETHODIMP_(ULONG)CMemberInfo::Release(void){ //Delegate to the controlling IUnknown return m_pControllingIUnknown->Release();}//Release

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 145: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The inner MemberInfo object delegates its IUnknown functions to the controlling unknown,which in this case happens to be the outer AccountInfo object, in order to properly maintainthe reference count of the outer object. For example, if a client calls AddRef on theIMemberInfo interface, the reference count of the outer AccountInfo object should beincremented; likewise, if a client calls Release on the IMemberInfo interface, the referencecount of the outer object should be decremented. This explains why AddRef and Release aredelegated to the controlling unknown, but what about QueryInterface? TheQueryInterface function of the inner object is also delegated to the controlling unknown:

STDMETHODIMP CMemberInfo::QueryInterface(REFIID iid, LPVOID *ppv){ //Delegate to the controlling IUnknown return m_pControllingIUnknown->QueryInterface(iid, ppv);}//QueryInterface

This makes sense when you consider that the inner object has no knowledge of what interfaces aresupported by the outer object. The outer AccountInfo object’s QueryInterface is thenslightly modified to take into account the interfaces of the inner MemberInfo object:

STDMETHODIMP CAccountInfo::QueryInterface(REFIID iid, LPVOID *ppv){ *ppv = NULL; if (IID_IUnknown == iid) *ppv = (LPVOID)(IUnknown *)this; else if (IID_IAccountInfo == iid) *ppv = (LPVOID)(IAccountInfo *)this; else if (IID_IMemberInfo == iid) return m_pMemberInfoIUnknown->QueryInterface(iid, ppv); else return E_NOINTERFACE; //Interface not supported //Perform reference count through the returned interface ((IUnknown *)*ppv)->AddRef(); return NOERROR;}//QueryInterface

Page 146: 6935316 DCOM Microsoft Distributed Component Object Model

I know what you’re thinking, and you’re right…sort of. You’re thinking that ifm_pMemberInfoIUnknown is a pointer to the inner object’s IUnknown, and the innerobject’s IUnknown simply delegates to the outer object’s IUnknown, then we’ve just created acircular reference! You are right, except that m_pMemberInfoIUnknown doesn’t point to theinner object’s IUnknown. The MemberInfo object actually implements two IUnknowns: thedelegating unknown, which simply delegates to the controlling unknown, and the nondelegatingunknown, which actually implements IUnknown as we know it. To avoid a circular reference,CAccountInfo’s m_pMemberInfoIUnknown points to the nondelegating unknown, whichis used by the outer object to control the lifetime of the inner object and access its interfaces. TheMemberInfo object’s nondelegating unknown interface is created by first defining its VTBLlayout, which must be identical to the VTBL layout of IUnknown:

class INonDelegatingUnknown{ virtual HRESULT STDMETHODCALLTYPE NonDelegatingQueryInterface (REFIID iid, LPVOID *ppv) = 0; virtual ULONG STDMETHODCALLTYPE NonDelegatingAddRef(void) = 0; virtual ULONG STDMETHODCALLTYPE NonDelegatingRelease(void) = 0;};//INonDelegatingUnknown

The CMemberInfo C++ class used to implement the MemberInfo COM object is defined suchthat it inherits not only from IMemberInfo, but also from INonDelegatingUnknown:

class CMemberInfo : IMemberInfo, INonDelegatingUnknown{private: ULONG m_cRef; //Controlling unknown IUnknown *m_pControllingIUnknown; IUserInfo *m_pIUserInfo; LPSTR m_lpszAddress; LPSTR m_lpszCity; LPSTR m_lpszPhone; LPSTR m_lpszState; LPSTR m_lpszZip;public: //IUnknown STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppv); STDMETHODIMP_(ULONG)AddRef(void); STDMETHODIMP_(ULONG)Release(void); //IMemberInfo STDMETHODIMP get_Address(LPSTR *lpszRetAddress); STDMETHODIMP put_Address(LPSTR lpszAddress); STDMETHODIMP get_City(LPSTR *lpszRetCity); STDMETHODIMP put_City(LPSTR lpszCity); STDMETHODIMP get_Name(LPSTR *lpszRetName); STDMETHODIMP put_Name(LPSTR lpszName); STDMETHODIMP get_Phone(LPSTR *lpszRetPhone); STDMETHODIMP put_Phone(LPSTR lpszPhone); STDMETHODIMP get_Sex(BYTE *byRetSex); STDMETHODIMP put_Sex(BYTE bySex); STDMETHODIMP get_State(LPSTR *lpszRetState);

Page 147: 6935316 DCOM Microsoft Distributed Component Object Model

STDMETHODIMP put_State(LPSTR lpszState); STDMETHODIMP get_Zip(LPSTR *lpszRetZip); STDMETHODIMP put_Zip(LPSTR lpszZip); //NonDelegatingUnknown STDMETHODIMP NonDelegatingQueryInterface(REFIID iid, LPVOID*ppv); STDMETHODIMP_(ULONG)NonDelegatingAddRef(void); STDMETHODIMP_(ULONG)NonDelegatingRelease(void); HRESULT Initialize(void); //Constructor CMemberInfo(IUnknown* pUnknownOuter); //Destructor ~CMemberInfo();};//CMemberInfo

The interface functions of the MemberInfo object’s nondelegating unknown,INonDelegatingUnknown, are then implemented the way you would normally implement thefunctions of IUnknown:

STDMETHODIMP CMemberInfo::NonDelegatingQueryInterface (REFIID iid, LPVOID *ppv){ *ppv = NULL; if (IID_IUnknown == iid) *ppv = (LPVOID)(IUnknown *)(INonDelegatingUnknown *)this; else if (IID_IMemberInfo == iid) *ppv = (LPVOID)(IMemberInfo *)this; else return E_NOINTERFACE; //Interface not supported //Perform reference count through the returned interface ((IUnknown *)*ppv)->AddRef(); return NOERROR;}//NonDelegatingQueryInterface

STDMETHODIMP_(ULONG)CMemberInfo::NonDelegatingAddRef(void){ return ++m_cRef;}//NonDelegatingAddRef

STDMETHODIMP_(ULONG)CMemberInfo::NonDelegatingRelease(void){ m_cRef—; if (0 == m_cRef) { delete this; //Decrement the global object count g_cObjects—; //See if it's alright to unload the server if (::ServerCanUnloadNow()) ::UnloadServer(); return 0; } return m_cRef;}//NonDelegatingRelease

Page 148: 6935316 DCOM Microsoft Distributed Component Object Model

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 149: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The MemberInfo object’s class factory is then modified to use the nondelegating unknown toobtain the initial IUnknown interface pointer in response to the aggregating object’s call toCoCreateInstance:

STDMETHODIMP CMemberInfoFactory::CreateInstance(IUnknown* pUnknownOuter, REFIID iid, LPVOID *ppv){ HRESULT hr; CMemberInfo *pCMemberInfo = NULL;

*ppv = NULL; //This object supports aggregation //Create the CMemberInfo object pCMemberInfo = new CMemberInfo(pUnknownOuter); if (NULL == pCMemberInfo) return E_OUTOFMEMORY; //Initialize the new object hr = pCMemberInfo->Initialize(); if (FAILED(hr)) goto cleanUP; //Retrieve the requested interface hr = pCMemberInfo->NonDelegatingQueryInterface(iid, ppv); if (FAILED(hr)) goto cleanUP; //Increment the global object counter g_cObjects++;

return NOERROR;cleanUP: //Some type of error occurred, cleanup before returning if (pCMemberInfo) { delete pCMemberInfo; pCMemberInfo = NULL; }

Page 150: 6935316 DCOM Microsoft Distributed Component Object Model

return hr;}//CreateInstance

When the last outstanding AccountInfo interface pointer is released and the object destroysitself, it releases the last outstanding call on the aggregated object, forcing it to destroy itself aswell:

CAccountInfo::~CAccountInfo(){ if (m_pMemberInfoIUnknown) m_pMemberInfoIUnknown->Release();}//~CAccountInfo

In order for the MemberInfo object to continue to work in a nonaggregated fashion, all we have todo is use the INonDelegatingUnknown as the controlling unknown. This way, any calls to thedelegating unknown will simply be forwarded to the object’s own INonDelegatingUnknowninterface, which contains the traditional IUnknown implementation. The decision of whether ornot the MemberInfo object is actually being created as part of an aggregate is made in theCMemberInfo object’s constructor:

CMemberInfo::CMemberInfo(IUnknown* pUnknownOuter){ m_cRef = 0; m_pIUserInfo = NULL; m_lpszAddress = NULL; m_lpszCity = NULL; m_lpszPhone = NULL; m_lpszState = NULL; m_lpszZip = NULL;

//Even though we are creating a copy of an interface //don't call AddRef, because it will create a circular //reference count! if (pUnknownOuter) //Part of an aggregate //save the controlling IUnknown m_pControllingIUnknown = pUnknownOuter; else //Not part of an aggregate //the controlling IUnknown is the NonDelegating Unknown m_pControllingIUnknown = (IUnknown *)(INonDelegatingUnknown *)this;}//CMemberInfo

That’s all there is to it! Aggregation allows an outer COM object to expose the interfaces of aninner COM object as if the outer object implemented them directly. As a potential outer object, youcan determine if an object supports aggregation by simply calling CoCreateInstance andattempting to aggregate it. If the call succeeds, the object supports aggregation; otherwise, itdoesn’t:

hr = CoCreateInstance(CLSID_MemberInfo, (IUnknown *)this, CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID *)&38;m_pMemberInfoIUnknown);if (SUCCEEDED(hr)){

Page 151: 6935316 DCOM Microsoft Distributed Component Object Model

//Object supports aggregation}else{ //Object doesn't support aggregation}

Despite its inherent benefits, aggregation has two major restrictions:

•  Both the outer and inner objects must be in the same process.

•  The inner object must specifically support aggregation by supporting both a delegating andnondelegating IUnknown.

For those COM objects that don’t support aggregation, your only opportunity for reuse is throughthe containment process. For this reason, I strongly suggest that you take the extra step and supportaggregation when creating your own COM objects. Source code for the AccountInfoClientapplication can be found on the accompanying CD-ROM. The AccountInfoClient applicationputs the AccountInfo object through its paces by using various interface functions from each ofthe object’s supported interfaces.

Summary

In this chapter, you learned that:

•  Containment is the easiest way to reuse an existing COM object, with the outer objectsimply acting as a client of the inner object.

•  Containment is best suited for reusing interfaces that implement some, but not all, of thedesired level of functionality.

•  Aggregation requires cooperation from both the outer object and the inner object, andtherefore requires slightly more work.

•  Aggregation is best suited for reusing interfaces exactly as is.

•  In order to aggregate a component, both the outer and inner component must be located inthe same process space.

•  Both methods of component reuse are transparent to the client of the outer object, in thesense that the client has no idea that the outer object contains or aggregates other innerobjects.

In the next chapter you learn about the benefits of Automation and how to implement COM objectsthat support dual interfaces.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 152: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Chapter 5Building Automation ObjectsIN THIS CHAPTER

•  The benefits and limitations of VTBL interfaces

•  The benefits and limitations of Automation

•  How to build a COM object that supports both VTBL binding andAutomation

UNTIL THIS POINT, all the interfaces we have defined have been custominterfaces, which means that we defined them ourselves; they were not definedby COM, OLE, or ActiveX. Clients use the VTBL definitions of COMinterfaces to perform type checking at compile time in a process known asVTBL binding. VTBL binding is extremely fast because all type checking isdone at compile time. However, scripting languages like VBScript (VBS) andJava Script (JScript) are interpreted, not compiled, and their ever-increasingpopularity cannot be ignored. To accommodate the run-time type checking thatis required by these and other scripting and macro languages, Microsoftdefined Automation, the subject of this chapter.

An Introduction to Automation

Automation is a technology, built on top of COM, that is designed to provide astandard way of exposing COM objects to macro languages, programmingtools, and other COM clients. It was originally created as a way forapplications developed in Visual Basic to control other applications, such asMicrosoft Excel. COM objects that are programmatically controllable viaAutomation are called Automation objects. COM clients that are capable ofcontrolling such Automation objects are called Automation controllers.

Page 153: 6935316 DCOM Microsoft Distributed Component Object Model

Automation controllers communicate with Automation objects via theAutomation-defined IDispatch interface.

Understanding IDispatch

Like all COM interfaces, IDispatch inherits from IUnknown, butIDispatch also defines the member functions in Table 5-1.

Table 5-1 IDispatch Member Functions

Function Name Purpose

Invoke Provides access to properties and methods exposed by anAutomation object

GetIDsOfNames Maps a single member name and an optional set ofargument names to a corresponding set of DISPIDs (seebelow), which may then be used in subsequent calls toInvoke

GetTypeInfo Retrieves type information about an Automation objectGetTypeInfoCount Determines whether type information is available for a

particular interface

The IDispatch interface separately maintains a special dispatch interface,called a dispinterface. Like a VTBL, the dispinterface maintains the addressesof each supported interface function. However, unlike a VTBL, thedispinterface identifies each function using a special dispatch identifier, orDISPID. A DISPID is not a GUID, but simply a long integer that uniquelyidentifies a function within a particular dispinterface. Clients callIDispatch::GetIDsOfNames with the name of the property or methodthat they are interested in accessing. The Automation object’s implementationof IDispatch::GetIDsOfNames uses the name to perform a lookupagainst the type library to obtain the appropriate DISPID, which it returns tothe client. The client may then call IDispatch::GetTypeInfoCount todetermine if the Automation object supplies a type library. If the Automationobject supplies a type library for the dispinterface in question, the client canobtain a pointer to it by calling IDispatch::GetTypeInfo. The clientcan use this type information to perform any necessary type checking. At thispoint, the client can call IDispatch::Invoke with the DISPID andappropriate function parameters to actually access the desired property ormethod. This entire process is illustrated in Figure 5-1.

Figure 5-1  The Automation process

While Automation provides a mechanism for the run-time type checkingrequired by popular script and macro languages, it doesn’t come for free.Instead of directly accessing an interface function through a VTBL (VTBLbinding), Automation controllers are required to use IDispatch::Invoke

Page 154: 6935316 DCOM Microsoft Distributed Component Object Model

with DISPIDs that are obtained at run time, either via calls toIDispatch::GetIDsOfNames, in a process known as late binding, or atcompile time via access to the Automation object’s type library, in a processknown as early binding.

Late binding is an expensive operation because the DISPID for each propertyor method used must be retrieved at run time usingIDispatch::GetIDsOfNames before the property or method can beaccessed by IDispatch::Invoke. However, some Automation controllersare capable of obtaining and caching the various property and methodDISPIDs of an Automation object from the object’s type library at compiletime by using early binding. By obtaining and caching the DISPIDs from thetype library, the Automation controller can avoid callingIDispatch::GetIDsOfNames every time it needs to access a particularproperty or method. By avoiding the additional call toIDispatch::GetIDsOfNames, the Automation controller is able toincrease its overall performance. However, to support early binding, theAutomation controller must have access to the Automation object’s typelibrary at compile time, unlike late binding, in which the Automationcontroller must have access to the object’s type library at run time.

In either case, supporting IDispatch requires additional programming effortfor the programmer when creating the Automation controller, due in large partto the process of packaging each required function parameter into a variant.While Automation definitely has its drawbacks, so does direct VTBL binding,the most significant of which is its lack of support for run-time type checking.In order to provide support for the run-time type checking required by popularscripting and macro languages, without penalizing C/C++ developers who aremost interested in the speed and ease of development offered by VTBLbinding, you should implement dual interfaces.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 155: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Understanding Dual Interfaces

A dual interface is an interface whose methods can be called either directly through the VTBL orindirectly through IDispatch. While implementing dual interfaces does require additionalwork, it’s not as complicated as it may seem. There are several requirements for implementing adual interface:

•  The interface being defined must inherit from IDispatch.

•  The interface must be defined using the dual IDL keyword.

•  The interface must be defined using Automation-compatible datatypes.

Figure 5-2 shows the memory layout for a VTBL interface that inherits from IUnknown, whileFigure 5-3 shows the memory layout for a dual interface that inherits from IDispatch. Noticethat even though the dual interface inherits from IDispatch, the first three member functionsare those of IUnknownQueryinterface, AddRef, and Release. This is because, like allCOM interfaces, IDispatch itself inherits from IUnknown. Automation controllers use theIDispatch side of the dual interface, while C/C++ programmers can use the VTBL side of thedual interface.

Figure 5-2  The memory layout for a VTBL interface that inherits from IUnknown.

Figure 5-3  The memory layout for a dual interface that inherits from IDispatch.

When defining a dual interface, you must use the dual IDL keyword to signal that the interface isin fact a dual interface:

[ object,

Page 156: 6935316 DCOM Microsoft Distributed Component Object Model

uuid(01234567-89ab-cdef-0123-456789abcdef), helpstring("ISomeDualInterface interface."), dual]//declaration of the ISomeDualInterface interfaceinterface ISomeDualInterface : IDispatch{...}

In Chapter 2, we saw the wide range of IDL-supported datatypes useful for defining COMinterfaces (see Table 2-2). However, in order for an interface to be compatible with Automation,its member functions can only be defined using the Automation-compatible datatypes listed inTable 5-2.

Table 5-2 Intrinsic IDL Datatypes Supported by Automation

Datatype Description

VARIANT_BOOL Data item having either a TRUE or FALSE valuechar 8-bit signed data itemdouble 64-bit IEEE floating-point numberint System-dependent signed integerfloat 32-bit IEEE floating-point numberlong 32-bit signed integershort 16-bit signed integerBSTR Length-prefixed stringCURRENCY 8-byte, fixed-point numberDATE 64-bit floating-point fractional number of days since December

30, 1899SCODE Built-in error type that corresponds to VT_ERROR. An

SCODE (used on 16-bit systems only) does not contain theadditional error information provided by an HRESULT.

IDispatch* Pointer to an IDispatch interfaceIUnknown* Pointer to an IUnknown interfaceSAFEARRAY(TypeName) An array of TypeName types. TypeName can be any of the

above datatypes.TypeName* A pointer to TypeName. TypeName can be any of the above

datatypes.VARIANT A structure consisting of a union of all the above datatypes.void Allowed only as a function return type or in a parameter list to

indicate no arguments.HRESULT Return type used for reporting error information in interfaces

as described in Chapter 1.

As you can see, the list of datatypes supported by Automation is a subset of the list of typessupported by IDL. To understand why this is so, you must first understand the variant datatype.

Understanding Variants

As Automation was originally defined as part of Visual Basic, it stands to reason that variants

Page 157: 6935316 DCOM Microsoft Distributed Component Object Model

would also be somehow related to VB. Variants are the default datatypes of VB, and they serve asa way to store different types of data in a common manner. As Listing 5-1 illustrates, a variant isessentially a structure that consists of a union of each supported datatype and an indicator variableused to identify the datatype currently being stored by the variant.

Listing 5-1. Internal structure of a variant

typedef struct FARSTRUCT tagVARIANT VARIANT;

typedef struct tagVARIANT { VARTYPE vt; unsigned short wReserved1; unsigned short wReserved2; unsigned short wReserved3; union { unsigned char bVal; // VT_UI1 short iVal; // VT_I2 long lVal; // VT_I4 float fltVal; // VT_R4 double dblVal; // VT_R8 VARIANT_BOOL bool; // VT_BOOL SCODE scode; // VT_ERROR CY cyVal; // VT_CY DATE date; // VT_DATE BSTR bstrVal; // VT_BSTR Iunknown FAR* punkVal; // VT_UNKNOWN IDispatch FAR* pdispVal; // VT_DISPATCH SAFEARRAY FAR* parray; // VT_ARRAY | * unsigned char FAR* pbVal; // VT_BYREF | VT_UI1 short FAR* piVal; // VT_BYREF | VT_I2 long FAR* plVal; // VT_BYREF | VT_I4 float FAR* pfltVal; // VT_BYREF | VT_R4 double FAR* pdblVal; // VT_BYREF | VT_R8 VARIANT_BOOL FAR* pbool; // VT_BYREF | VT_BOOL SCODE FAR* pscode; // VT_BYREF | VT_ERROR CY FAR* pcyVal; // VT_BYREF | VT_CY DATE FAR* pdate; // VT_BYREF | VT_DATE BSTR FAR* pbstrVal; // VT_BYREF | VT_BSTR Iunknown FAR* FAR* ppunkVal; // VT_BYREF | VT_UNKNOWN IDispatch FAR* FAR* ppdispVal; // VT_BYREF | VT_DISPATCH SAFEARRAY FAR* FAR* parray; // VT_ARRAY | * VARIANT FAR* pvarVal; // VT_BYREF | VT_VARIANT void FAR* byref; // Generic ByRef };};

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 158: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 159: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

When retrieving information from a variant, you first need to determine what type of datais being stored in it. This is done by referring to the variant’s vt member. Table 5-3 listsand describes the meanings of the various vt values. The vt value signals which memberof the variant structure actually contains data. For example, if a variant has a vt value of 2,then the variant is storing a 2-byte integer value in its iVal member.

The same holds true for storing data in a variant. The first thing that you’ll want to do isidentify the type of data that you’re storing in the variant by assigning the appropriatevalue to the variant’s vt member. For example, to store a long integer value in a variant,set the variant’s vt member to 3, then store the actual long data in the variant’s Valmember. The following code snippet demonstrates how a variant can be used to storevarious types of data:

VARIANT ageVariant; VARIANT nameVariant

//Initialize each variant VariantInit(&ageVariant); VariantInit(&nameVariant);

//Use the variant for storage of a long ageVariant.vt = VT_I4; ageVariant.lVal = 333; //Use the variant for storage of a BSTR nameVariant.vt = VT_BSTR nameVariant.bstrVal = SysAllocString(OLESTR("Ken Lewis"));

A variant’s vt value is used to determine which member of the union contains valid data.Table 5-3 lists and describes the various variant vt values:

Table 5-3 Interpreting Variant VT Values

Page 160: 6935316 DCOM Microsoft Distributed Component Object Model

VT Value Defined Constant Description

0 VT_EMPTY VARIANT contains no data1 VT_NULL VARIANT contains NULL2 VT_I2 A 2-byte integer is in iVal3 VT_I4 A 4-byte integer is in lVal4 VT_R4 An IEEE 4-byte real is in fltVal5 VT_R8 An IEEE 8-byte real is in dblVal6 VT_CY An 8-byte two’s complement currency value is

in cyVal7 VT_DATE A double-precision date is in date8 VT_BSTR A BSTR is in bstrVal9 VT_DISPATCH An IDispatch pointer is in pdispVal10 VT_ERROR An SCODE is in scode11 VT_BOOL A Boolean (True = 0xFFFF/False = 0) value is

in bool12 VT_VARIANT Must be combined with VT_BYREF. A pointer

to a VARIANTARG is in pvarVal.13 VT_UNKNOWN An Iunknown pointer is in punkVal0×800 VT_RESERVED Reserved0×400 VT_BYREF Can be combined with other VT values to

indicate values being passed by reference0×200 VT_ARRAY Can be combined with other VT values, except

VT_EMPTY and VT_NULL, to indicate an arrayof that datatype. The array descriptor is inpByrefVal.

Because variants can be used to store various datatypes in a single common format,Automation uses variants as a way to pass different types of arguments to dispinterfacefunctions. However, as variants only support a limited number of datatypes, andAutomation relies on variants, Automation is limited to supporting only those datatypessupported by variants. Table 5-4 lists Win32 API functions that are useful for working withvariants.

Table 5-4 Win32 API Functions Useful for Working with Variants

Function Purpose

VariantChangeType Converts a variant from one type to anotherVariantChangeTypeEx Converts a variant from one type to another according

to a locale identifier (LCID), an identifier used todetermine the text and data formatting conventions of aparticular geographical region

VariantClear Releases resources associated with a particular variantand sets the variant to VT_EMPTY

VariantCopy Copies a variantVariantCopyInd Copies a variant that may contain a pointer

Page 161: 6935316 DCOM Microsoft Distributed Component Object Model

VariantInit Initializes a variantDosDateTimeToVariantTime Converts MS-DOS date and time representations to a

variant timeVariantTimeToDosDateTime Converts a variant time to MS-DOS date and time

representationsVariantTimeToSystemTime Converts a variant time to system date and time

representationsSystemTimeToVariantTime Converts system date and time representations to a

variant time

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 162: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Understanding BSTRs

As you looked through the datatypes supported by variants and Automation,you may have noticed two that were unfamiliar: BSTR and SAFEARRAYdatatypes. A BSTR, or binary string, is a pointer used to refer toNULL-terminated string data. However, unlike traditional char*s, the lengthof the BSTR is stored as an integer value that precedes any actual data (seeFigure 5-4).

Figure 5-4  Unlike a traditional char*, the length of a BSTR is stored as aninteger that precedes any actual data.

Even though the length of a BSTR precedes any actual data, note that theBSTR pointer itself points to the memory location of the first byte of data.Because BSTRs include their length, they are capable of containing binarydata that may include multiple NULL characters. On 16-bit systems, BSTRscontain ANSI characters, while on 32-bit systems, BSTRs contain Unicodecharacters. (See the sidebar “Understanding Character Sets” for moreinformation on different character sets.) Table 5-5 lists Win32 API functionsthat are useful for working with BSTRs.

Table 5-5 Win32 API Functions Useful for Working with BSTRs

Function Purpose

SysAllocString Creates and initializes a BSTRSysAllocStringByteLen Creates a NULL-terminated BSTR of a specified

length (32-bit only)

Page 163: 6935316 DCOM Microsoft Distributed Component Object Model

SysAllocStringLen Creates a BSTR of a specified lengthSysFreeString Releases BSTR resources previously allocated with

SysAllocStringSysReAllocString Changes the size and value of an existing BSTRSysReAllocStringLen Changes the size of an existing BSTRSysStringByteLen Returns the length of a BSTR in bytes (32-bit only)SysStringLen Returns the length of a BSTR

Understanding Character Sets

A character set is basically an encoding scheme in which each character inthe set is associated with a unique code that serves as its identifier. Charactersets are typically categorized according to the number of bytes they use torepresent each ID. A single-byte character set (SBCS) can use up to one byte(8 bits) of storage to represent each character, while a double-byte characterset (DBCS) can use up to two bytes (16-bits) of storage to represent eachcharacter. Because some of the characters of a DBCS only require one byteof storage while others require two, DBCSs are often referred to as multibytecharacter sets (MBCSs). The more bytes a particular character set uses foreach of its IDs, the more unique IDs are possible, and thus the more possiblecharacters the set can represent. For example, most of us are familiar withthe American National Standards Institute (ANSI) SBCS, which is afixed-width character set that uses eight bits for each character ID, whichmeans that it is only capable of representing 256 different character IDs:0–255. Furthermore, there is more than one ANSI character set — WesternEuropean, Eastern European, Baltic, Arabic, Hebrew, Greek, Turkish, etc.Windows tracks the current ANSI character set in a code page; whicheverANSI code page is currently loaded determines the character set used todecode the character IDs. This not only makes it tough for developers, butthe numerous ANSI character sets also make it very difficult to createmultilingual documents. While room for 256 different characters is morethan adequate to represent the 26 characters of the English language, somelanguages — like the Chinese language, with more than 10,000 characters —have far greater requirements.

Rather than force developers to manage all of the various single- andmultibyte character sets, a group of vendors united to form the UnicodeConsortium, and they created the Unicode standard. Unicode is a fixed-widthcharacter set that uses 16 bits for each character ID. The 16 bits of storagemeans that Unicode has room for up to 65,536 unique character IDs.Currently, Windows NT is the only version of Windows that uses Unicode asits base character-encoding mechanism. However, because the Win32 API issupported on Windows 3.x and Windows 95 as well as Windows NT, theWin32 APIs provide two different entry points for each system function thatexpects a string parameter: an ANSI version and a Unicode version, called awide-character version. Which entry point is used depends on whether or notthe compile-time UNICODE symbol is defined. If UNICODE is defined,then the Unicode version of each Win32 API is used; otherwise, the ANSIversion is used.

Page 164: 6935316 DCOM Microsoft Distributed Component Object Model

This all works thanks to macro-magic in the Windows.h file. Windows.hprovides a macro for each Win32 API function, which expands to theappropriate version, whether or not UNICODE is defined. Windows.h alsodefines several useful generic text-mapping macros such as TEXT and T, aswell as several generic datatypes, such as TCHAR and LPTSTR. The TEXTand T macros should be used to ensure that string and character literals areappropriately defined as either ANSI or Unicode, depending on whether ornot UNICODE is defined. TCHAR and LPTSTR are generic datatypes thatshould be used to replace references to the char and LPSTR datatypes. IfUNICODE is defined, TCHAR equates to a wchar_t or wide character,which is essentially a Unicode character; otherwise, it equates to a char.Likewise, LPTSTR equates to either a pointer to an ANSI string or a pointerto a Unicode string, depending on whether or not UNICODE is defined.These and other text-mapping macros and generic datatypes help developerswrite a single source code base to the Win32 APIs, and still easily targetUnicode systems like Windows NT by simply defining the UNICODEsymbol at compile time.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 165: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Understanding SAFEARRAYs

The other datatype that you may not have recognized is the SAFEARRAY. ASAFEARRAY is exactly what its name implies, an array that incorporates itsown safety mechanisms to prevent writing beyond the bounds of the array. ASAFEARRAY accomplishes this by maintaining information regarding thefollowing:

•  The number of dimensions, stored in the cDims member variable

•  The upper and lower bounds of each dimension of the array, stored inthe rgsabound array member variable

•  The size of each element in the array, stored in the cbElementsmember variable

Following is the definition of the SAFEARRAY datatype:

typedef struct tagSAFEARRAY { USHORT cDims; USHORT fFeatures; ULONG cbElements; ULONG cLocks; PVOID pvData; SAFEARRAYBOUND rgsabound[ 1 ]; } SAFEARRAY;

typedef struct tagSAFEARRAYBOUND { ULONG cElements; LONG lLbound; } SAFEARRAYBOUND;

Page 166: 6935316 DCOM Microsoft Distributed Component Object Model

A SAFEARRAY can be used to store any of the variant-supported datatypesexcept VT_ARRAY, VT_EMPTY, VT_NULL, and those types that have theirVT_BYREF flag set. Table 5-6 lists Win32 API functions that are useful forworking with SAFEARRAYs.

Table 5-6 Win32 API Functions Useful for Working with SAFEARRAYs

Function Purpose

SafeArrayAccessData Increments the lock count of an array and returnsa pointer to array data

SafeArrayAllocData Allocates memory for a safe array based on adescriptor created withSafeArrayAllocDescriptor

SafeArrayAllocDescriptor Allocates memory for a safe array descriptorSafeArrayCopy Copies an existing safe arraySafeArrayCopyData Copies a source array to a target array after

releasing source resourcesSafeArrayCreate Creates a new array descriptorSafeArrayCreateVector Creates a one-dimensional array whose lower

bound is always zeroSafeArrayDestroy Destroys an array descriptorSafeArrayDestroyData Frees memory used by the data elements in a

safe arraySafeArrayDestroyDescriptor Frees memory used by a safe array descriptorSafeArrayGetDim Returns the number of dimensions in an arraySafeArrayGetElement Retrieves an element of an arraySafeArrayGetElemsize Returns the size of an elementSafeArrayGetLBound Retrieves the lower bound for a given dimensionSafeArrayGetUBound Retrieves the upper bound for a given dimensionSafeArrayLock Increments the lock count of an arraySafeArrayPtrOflndex Returns a pointer to an array elementSafeArrayPutElement Assigns an element to an arraySafeArrayRedim Resizes a safe arraySafeArrayUaccessData Frees a pointer to array data and decrements the

lock count of the arraySafeArrayUnlock Decrements the lock count of an array

Building an Automation Object

We will now expand on our introductory knowledge of Automation by takingthe AccountInfo COM object that we created in Chapter 4 and recreating itas the AccountInfoAuto Automation object. However, to reduce anypotential confusion, AccountInfoAuto fully implements each of theinterfaces that it exposes, and doesn’t rely on any other COM objects.

Page 167: 6935316 DCOM Microsoft Distributed Component Object Model

Isolating Automation Specifics

The AccountInfoAuto object will provide the same basic informationregarding individual credit card accounts as the AccountInfo object, but inan Automation-compatible manner. The AccountInfoAuto object exposesthe exact same IAccountInfo and IMemberInfo custom interfaces as theAccountInfo object. However, the AccountInfoAuto object alsoexposes the IAccountInfoDispatch dual interface. Table 5-7 lists theproperties defined by each AccountInfoAuto object-supported interface.As you look at the interface definitions, you will notice that theIAccountInfoDispatch dual interface is an Automation-compatiblecombination of the IAccountInfo and IMemberInfo custom interfaces.However, because IAccountInfoDispatch is a dual interface, it isrestricted to the Automation-compatible datatypes listed in Table 5-2 andreplaces the LPSTR datatype of the properties originally defined by theIMemberInfo interface with the Automation-compatible, BSTR datatype.

Table 5-7 AccountInfoAuto Object Properties

Property Name Datatype Implemented In

Number long IAccountInfoDispatch

Balance float IAccountInfoDispatch

Limit long IAccountInfoDispatch

Name BSTR IAccountInfoDispatch

Sex unsigned char IAccountInfoDispatch

Address BSTR IAccountInfoDispatch

City BSTR IAccountInfoDispatch

State BSTR IAccountInfoDispatch

Zip BSTR IAccountInfoDispatch

Phone BSTR IAccountInfoDispatch

Number long IAccountInfo

Balance float IAccountInfo

Limit long IAccountInfo

Name LPSTR IMemberInfo

Sex unsigned char IMemberInfo

Address LPSTR IMemberInfo

City LPSTR IMemberInfo

State LPSTR IMemberInfo

Zip LPSTR IMemberInfo

Phone LPSTR IMemberInfo

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 168: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 169: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The IDL definition of the IAccountInfoDispatch interface can be seen in Listing 5-2. Asyou look at the definition, notice the use of the dual IDL keyword in the Attributes sectionof the interface definition, signaling that the interface is in fact a dual interface that must beAutomation-compatible.

Listing 5-2. IDL definition of the IAccountInfoDispatch interface

//IAccountInfoDispatch//These are the attributes of the IAccountInfoDispatch interface[ object, uuid(dd3b2be4-a91e-11d0-94ab-00a024a85a21), helpstring("IAccountInfoDispatch Interface."), dual]//Declaration of the IAccountInfoDispatch interfaceinterface IAccountInfoDispatch : IDispatch{ //List of function definitions for each method supported by //the interface // //[attributes] returntype [calling convention] // funcname(params); // [id(0), propget, helpstring("Sets or returns the account number.")] HRESULT Number([out, retval] long *lRetNumber); [id(0), propput, helpstring("Sets or returns the account number.")] HRESULT Number([in] long lNumber); [propget, helpstring("Sets or returns the account balance.")] HRESULT Balance([out, retval] float *flRetBalance); [propput, helpstring("Sets or returns the account balance.")] HRESULT Balance([in] float flBalance);

Page 170: 6935316 DCOM Microsoft Distributed Component Object Model

[propget, helpstring("Sets or returns the account limit.")] HRESULT Limit([out, retval] long *lRetLimit); [propput, helpstring("Sets or returns the account limit.")] HRESULT Limit([in] long lLimit);

[propget, helpstring("Sets or returns the address of the member.")] HRESULT Address([out, retval] BSTR *bstrRetAddress); [propput, helpstring("Sets or returns the address of the member.")] HRESULT Address([in] BSTR bstrAddress); [propget, helpstring("Sets or returns the city of the member.")] HRESULT City([out, retval] BSTR *bstrRetCity); [propput, helpstring("Sets or returns the city of the member.")] HRESULT City([in] BSTR bstrCity); [propget, helpstring("Sets or returns the name of the member.")] HRESULT Name([out, retval] BSTR *bstrRetName); [propput, helpstring("Sets or returns the name of the member.")] HRESULT Name([in] BSTR bstrName); [propget, helpstring("Sets or returns the phone number of the member.")] HRESULT Phone([out, retval] BSTR *bstrRetPhone): [propput, helpstring("Sets or returns the phone number of the member.")] HRESULT Phone([in] BSTR bstrPhone); [propget, helpstring("Sets or returns the sex of the member.")] HRESULT Sex([out, retval] unsigned char *byRetSex); [propput, helpstring("Sets or returns the sex of the member.")] HRESULT Sex([in] unsigned char bySex); [propget, helpstring("Sets or returns the state of the member.")] HRESULT State([out, retval] BSTR *bstrRetState); [propput, helpstring("Sets or returns the state of the member.")] HRESULT State([in] BSTR bstrState); [propget, helpstring("Sets or returns the zip code of the member.")] HRESULT Zip([out, retval] BSTR *bstrRetZip); [propput, helpstring("Sets or returns the zip code of the member.")] HRESULT Zip([in] BSTR bstrZip);}

Because the AccountInfoAuto object supports several interfaces, we use the IDL keyworddefault in the definition of the AccountInfoAuto coclass, to indicateIAccountInfoDispatch as the default interface to be used by Automation controllers:

Page 171: 6935316 DCOM Microsoft Distributed Component Object Model

//CLSID_AccountInfoAuto //attributes of the AccountInfoAuto object [ uuid(dd3b2be1-a91e-11d0-94ab-00a024a85a21), helpstring("AccountInfoAuto object") ] //definition of the AccountInfoAuto object coclass AccountInfoAuto { //list all of the interfaces supported by the object [default] interface IAccountInfoDispatch; interface IAccountInfoAuto; interface IMemberInfoAuto; };}

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 172: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The IAccountInfoDispatch dual interface allows Automation controllers to manipulate theAccountInfoAuto object. The custom IAccountInfo and IMemberInfo interfaces allowC++ programmers to manipulate the AccountInfoAuto object without the burden of beingAutomation controllers. While it is very beneficial to support both C++ andAutomation-compatible clients, it can be a very time-consuming task to implement specialinterfaces for each. To reduce the amount of additional code necessary to support both C++ andAutomation-compatible clients, the IAccountInfoDispatch dual interface will delegate itsimplementation to either the IAccountInfo or IMemberInfo custom interfaces, after somebasic preprocessing (see Listing 5-3). As you look at the listing, notice the use of theSysAllocString and SysStringLen Win32 API functions to manipulate BSTR data.

Listing 5-3. The properties of the IAccountInfoDispatch dual interface are implemented bydelegating to either the IAccountInfo or IMemberInfo custom interfaces, after some basicpreprocessing.

////get_Address//STDMETHODIMP CAccountInfoAuto::get_Address(BSTR *bstrRetAddress){ HRESULT hr; LPSTR szAddress = NULL; LPOLESTR olestrAddress = NULL; long lStringLen;

//Use the custom interface hr = get_Address(&szAddress); if (SUCCEEDED (hr)) { //Allocate enough storage for the string lStringLen = strlen(szAddress); if (lStringLen > 0) { olestrAddress = new OLECHAR[lStringLen + 1]; //Convert from the multibyte character set to the wide

Page 173: 6935316 DCOM Microsoft Distributed Component Object Model

//character set mbstowcs(olestrAddress, szAddress, lStringLen + 1); } } *bstrRetAddress = SysAllocString(olestrAddress); return hr;}//get_Address

////put_Address//STDMETHODIMP CAccountInfoAuto::put_Address(BSTR bstrAddress){ HRESULT hr; LPSTR szAddress = NULL; long lStringLen;

//Allocate enough storage for the string if (bstrAddress) lStringLen = SysStringLen(bstrAddress); if (lStringLen > 0) { szAddress = new char[lStringLen + 1]; //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrAddress, -1, szAddress, lStringLen + 1, NULL, NULL); } //Use the custom interface hr = put_Address(szAddress); return hr;}//put_Address...////get_Address//STDMETHODIMP CAccountInfoAuto::get_Address(LPSTR *lpszRetAddress){ *lpszRetAddress = m_lpszAddress; return NOERROR;}//get_Address

////put_Address//STDMETHODIMP CAccountInfoAuto::put_Address(LPSTR lpszAddress){ long lStringLen;

//Deallocate any previously allocated storage if (m_lpszAddress) delete[] m_lpszAddress;

Page 174: 6935316 DCOM Microsoft Distributed Component Object Model

m_lpszAddress = NULL; //Allocate enough storage for the string lStringLen = strlen(lpszAddress); if (lStringLen > 0) { m_lpszAddress = new char[lStringLen + 1]; //Copy the string strcpy(m_lpszAddress, lpszAddress); } return NOERROR;}//put_Address

Exposing a Type Library

In order for an Automation controller to perform run-time type checking for an Automation object,the object must provide a type library. The type library is just one of the files created by the MIDLcompiler whenever you compile your IDL file. The library IDL keyword signals to the MIDLcompiler that you want to have a type library generated:

//LIBID_AccountInfoAuto//These are the attributes of the type library[ uuid(dd3b2be0-a91e-11d0-94ab-00a024a85a21), helpstring("AccountInfoAuto Type Library."), version(1.0)]//Definition of the AccountInfoAuto type librarylibrary AccountInfoAuto{ importlib("stdole32.tlb");

//CLSID AccountInfoAuto //Attributes of the AccountInfoAuto object [ uuid(dd3b2be1-a91e-11d0-94ab-00a024a85a21), helpstring("AccountInfoAuto object") ] //Definition of the AccountInfoAuto object coclass AccountInfoAuto { //List all of the interfaces supported by the object [default] interface IAccountInfoDispatch; interface IAccountInfo; interface IMemberInfo; };}

You may have noticed that the library definition also contains the definition of theAccountInfoAuto object. Unlike interface definitions, object definitions cannot exist outsideof a library definition. Once the MIDL compiler has compiled the IDL file and generated atype library, you need to bind it to your compiled executable (DLL or EXE). To bind the typelibrary to your executable, you need to include the type library as a resource to your application’sproject. Because you can bind multiple type libraries to a single executable (possibly to supportmultiple languages), you need to number each type library as a way of uniquely identifying each

Page 175: 6935316 DCOM Microsoft Distributed Component Object Model

one. The following line of code is from the AccountInfoAuto.rc resource file and is used to bindthe AccountInfoAuto type library with the AccountInfoAuto.dll:

1 TYPELIB MOVEABLE PURE "AccountInfoAuto.tlb"

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 176: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Implementing IDispatch

Our introduction to Automation already described the basics of the IDispatch interface.Now you will see how to actually implement this most important interface. We will beginwith IDispatch::GetTypeInfoCount. Automation controllers interested in obtaininga pointer to a type library will call IDispatch::GetTypeInfoCount to determine if theAutomation object exposes type information for the IDispatch interface. The Automationcontroller uses the type information to perform any required run-time syntax checking. If theobject does in fact expose type information, IDispatch::GetTypeInfoCount willrespond with a value of 1; otherwise, it responds with 0:

STDMETHODIMP CAccountInfoAuto::GetTypeInfoCount(UINT *pctinfo){ //1 if the object does provide type information //0 if the object does not provide type information

*pctinfo = 1; return NOERROR;}//GetTypeInfoCount

Once the Automation controller knows that the object exposes type information, all it has todo is call IDispatch::GetTypeInfo to obtain a pointer to the type information for theIDispatch interface. Before the object can hand out such a pointer, it must first load thetype information for the IDispatch pointer into memory. This is done as part of theinitialization process for the CAccountInfoAuto object:

HRESULT CAccountInfoAuto::Initialize(){ HRESULT hr;

//load the type information for the IAccountInfoDispatch //dispatch interface hr = ::LoadTypeInfo(&m_pTypeInfo,

Page 177: 6935316 DCOM Microsoft Distributed Component Object Model

LIBID_AccountInfoAuto, IID_IAccountInfoDispatch, 0);

return hr;}//Initialize

The LoadTypeInfo function (see Listing 5-4) takes a pointer that will point to the actualtype information of interest — a GUID identifying the type library itself and a GUIDidentifying the object or interface whose type information you are interested in retrieving.LoadTypeInfo does three things:

•  It loads the type library into memory if it is not already loaded.

•  It registers the type library if it is not already registered.

•  It retrieves the type information for an object or interface identified by GUID.

Upon entry, LoadTypeInfo attempts to load an already registered type library identified bythe incoming rguid parameter. If the call is successful, the type library was in fact alreadyloaded and registered. If the call is unsuccessful, the type library is registered in a call toLoadTypeLib. LoadTypeLib will only register the type library if the filename passed toit is not a fully qualified filename. To ensure that the type library is registered, a call toRegisterTypeLib is made. Once the type library is loaded and a pointer to it is obtained,a call to GetTypeInfoOfGuid is made to obtain the type information about the object orinterface identified by the incoming CLSID parameter. Finally, the pointer to the type libraryis returned in the incoming pptinfo parameter.

Listing 5-4. The LoadTypeInfo function

HRESULT LoadTypeInfo(ITypeInfo **pptinfo, REFGUID rguid, REFCLSID clsid, LCID lcid){ HRESULT hr; _TCHAR szModuleName[MAX_STRING_LENGTH]; wchar_t wszModuleName[MAX_STRING_LENGTH]; LPTYPELIB ptlib = NULL; LPTYPEINFO ptinfo = NULL;

*pptinfo = NULL; // Load the type library. hr = LoadRegTypeLib(rguid, 1, 0, lcid, &ptlib); if (FAILED(hr)) { //Library wasn't registered, try to load it from the //server itself GetModuleFileName(g_hModule, szModuleName, sizeof(szModuleName) / sizeof(_TCHAR));#ifdef _UNICODE //UNICODE _tcscpy(wszModuleName, szModuleName);#else //SBCS and MBCS //Convert from the multibyte character set to the wide //character set mbstowcs(wszModuleName, szModuleName, sizeof(szModuleName) / sizeof(_TCHAR));

Page 178: 6935316 DCOM Microsoft Distributed Component Object Model

#endif //If LoadTypeLib is successful, it will register //the type library automatically but only if //the entire path of the library is NOT specified hr = LoadTypeLib(wszModuleName, &ptlib) ; if(FAILED(hr)) return hr;

//This will ensure that the type library is registered //for next time. hr = RegisterTypeLib(ptlib, wszModuleName, NULL) ; if(FAILED(hr)) return hr; } // Get type information for interface of the object. hr = ptlib->GetTypeInfoOfGuid(clsid, &ptinfo); if (FAILED(hr)) { ptlib->Release(); return hr; } ptlib->Release(); *pptinfo = ptinfo;

return NOERROR;}//LoadTypeInfo

Now that the object has a pointer to the type information for the IDispatch interface, it iscapable of responding to Automation controller requests for IDispatch’s type information;these requests arrive via calls to IDispatch::GetTypeInfo.

STDMETHODIMP CAccountInfoAuto::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **ppinfo){

*ppinfo = NULL;

//if not trying to get type information about IDispatch if(itinfo != 0) return DISP_E_BADINDEX;

//ref count m_pTypeInfo->AddRef(); //return the type information *ppinfo = m_pTypeInfo;

return NOERROR;}//GetTypeInfo

Incoming calls to IDispatch::GetIDsOfNames and IDispatch::Invoke can bedelegated to member functions of the m_pTypeInfo IDispatch type informationinterface pointer:

Page 179: 6935316 DCOM Microsoft Distributed Component Object Model

////GetIDsOfNames//STDMETHODIMP CAccountInfoAuto::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid){ if (IID_NULL != riid) return DISP_E_UNKNOWNINTERFACE;

return DispGetIDsOfNames(m_pTypeInfo, rgszNames, cNames, rgdispid);}//GetIDsOfNames

////Invoke//STDMETHODIMP CAccountInfoAuto::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pdispparams, VARIANT *pvarResult, EXCEPINFO *pexcepinfo, UINT *puArgErr){ if (IID_NULL != riid) return DISP_E_UNKNOWNINTERFACE;

return DispInvoke(this, m_pTypeInfo, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);}//Invoke

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 180: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Now that IDispatch has been implemented, we need to expose it via QueryInterface.Because IAccountInfoDispatch inherits from IDispatch, we can simply castIAccountInfoDispatch into an IDispatch pointer:

STDMETHODIMP CAccountInfoAuto::QueryInterface(REFIID iid, LPVOID *ppv){ *ppv = NULL; if (IID_IUnknown == iid) *ppv = (LPVOID)(IUnknown *)(IAccountInfoDispatch *)this; else if (IID_IDispatch == iid) *ppv = (LPVOID)(IDispatch *)(IAccountInfoDispatch *)this; else if (IID_IAccountInfo == iid) *ppv = (LPVOID)(IAccountInfo *)this; else if (IID_IMemberInfo == iid) *ppv = (LPVOID)(IMemberInfo *)this; else if (IID_IAccountInfoDispatch == iid) *ppv = (LPVOID)(IAccountInfoDispatch *)this; else return E_NOINTERFACE; //Interface not supported //Perform reference count through the returned interface ((IUnknown *)*ppv)->AddRef(); return NOERROR;}//QueryInterface

The rest of the implementation of the AccountInfoAuto object is pretty straight-forward:Complete the implementation of the various interface functions, provide a class factory for theAccountInfoAuto object, and fill out DLLMain.cpp. Therefore, we will turn our attentionto the additional registry entries that are required of Automation objects.

Registering an Automation Object

Aside from simply registering the CLSID, Automation servers are required to add the followingregistry entries:

Page 181: 6935316 DCOM Microsoft Distributed Component Object Model

•  A programmatic ID (ProgID)

•  Type library information

•  Automation interface proxy/stub information

Automation controllers use ProgIDs as a way to refer to Automation objects withhuman-readable names. ProgIDs are of two different types: version-dependent andversion-independent. Unlike CLSIDs, which are generated by a COM API call, you as adeveloper are ultimately responsible for creating your own ProgIDs, which typically use thefollowing syntax:

AppName.ObjectName.VersionNumber

where:

AppName is the name of the binary executable

ObjectName is the name of the COM object

VersionNumber is the version of the COM object

The following lines of VB code demonstrate how an Automation controller would use aversion-dependent ProgID to create a version 6 Word document and a version-independentProgID to create a Word document in the latest version of Microsoft Word:

Set objWord6Doc = CreateObject("Word.Document.6")Set objWordDoc = CreateObject("Word.Document")

ProgIDs can be up to 39 characters long, and their entries must be placed under the object’sCLSID key:

HKEY_CLASSES_ROOT CLSID {12345678-ABCD-1234-5678-9ABCDEFOOOOO} = Description ProgID = AppName.ObjectName.VersionNumber VersionIndependentProgID = AppName.ObjectName InprocServer32 = C:\SomeServer.dll LocalServer32 = C:\SomeServer.exe

ProgID entries must also be placed directly under the HKEY_CLASSES_ROOT key:

HKEY_CLASSES_ROOT AppName.ObjectName = Description CLSID = {12345678-ABCD-1234-5678-9ABCDEFOOOOO} AppName.ObjectName.VersionNumber = Description CLSID = {12345678-ABCD-1234-5678-9ABCDEFOOOOO}

Automation servers are also required to register their type libraries. This can be done eitherexplicitly or through calls to LoadTypeLib or RegisterTypeLib, as we have done inLoadTypeInfo (see Listing 5-4). Regardless of which technique is used, the Automationserver is responsible for adding the following registry entries under the HKEY_CLASSES_ROOTkey:

HKEY_CLASSES_ROOT TypeLib {12345678-ABCD-1234-5678-9ABCDEFOOOOO} = Description major.minor = Description

Page 182: 6935316 DCOM Microsoft Distributed Component Object Model

lcid platform = C:\SomeServer.dll HELPDIR = C:\ FLAGS = 0

Here are descriptions of the key identifiers in the above code snippet:

major.minor The version number of the type library.lcid A one- to four-hex-digit string representation of the locale ID (LCID) cannot

include leading zeros or 0x. A value of 0 represents theLANG_SYSTEM_DEFAULT(0).

Platform The target operating system platform: Win 16, Win32, or Mac.

Lastly, the Automation server is responsible for registering a proxy/stub pair for each supportedinterface. Luckily, the Automation library supplies a default Automation proxy/stub pair that canbe used by any Automation-compatible interface. The CLSID for the default Automationproxy/stub pair is {00020424-0000-0000-C000-000000000046}.

HKEY_CLASSES_ROOT Interface {12345678-ABCD-1234-5678-9ABCDEFOOOOO} = Description TypeLib = {12345678-ABCD-1234-5678-9ABCDEFOOOOO} ProxyStubClSID = {00020424-0000-0000-C000-000000000046}

That’s it! While building an Automation object requires a little more work than building a vanillaCOM object, it is not necessarily harder. Also, by implementing dual interfaces such that theydelegate to custom interfaces, you can create COM objects that appeal to a larger developeraudience. In the next chapter, we will build two client applications that make use of theAccountInfoAuto Automation object. One uses the VTBL side of the dual interface; theother is an Automation controller that uses the IDispatch side of the dual interface. The fullsource code for the AccountInfoAuto Automation object can be found on the companionCD-ROM.

Summary

In this chapter you learned that:

•  Despite its inherent speed, VTBL binding does not allow for run-time type checking.

•  While Automation objects support run-time type checking, they require more work toimplement than VTBL binding, and they are somewhat slower because of their additionaloverhead.

•  Supporting dual interfaces allows clients to bind using either the VTBL side or theIDispatch side of an interface.

•  In order to support IDispatch, interfaces are restricted to using only the datatypesallowed by variants.

In Chapter 6, you learn how to build client applications that use the VTBL and IDispatch sides ofthe dual interface for this Automation object.

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 183: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 184: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Chapter 6Building Automation ControllersIN THIS CHAPTER

•  How to access the properties and methods of an Automation object from the VTBL sideof a dual interface

•  How to access the properties and methods of an Automation object from theIDispatch side of a dual interface

•  How to use variants to pass arguments to Automation-compatible interface functions

•  About DISPIDs, which can be obtained at both compile time and run time

IN THE LAST chapter we built the AccountInfoAuto Automation object. TheAccountInfoAuto object supports the IAccountInfoDispatch dual interface, whichmeans that IAccountInfoDispatch can be accessed both at compile time via VTBLbinding as well as at run time via IDispatch. In addition, AccountInfoAuto also supportsthe IAccountInfo and IMemberInfo custom interfaces, which may only be bound to atcompile time via VTBL binding. You already learned how to access custom interfaces via VTBLbinding by building the UserInfoClient application in Chapter 2. The UserInfoClientwas able to manipulate the UserInfo COM object by using the IUserInfo custom interface.Therefore, in this chapter, we will focus on how to access the VTBL side of theIAccountInfoDispatch dual interface, by building the AccountInfoAutoVTBLapplication. You will also learn how to access the IDispatch side of theIAccountInfoDispatch dual interface, by building the AccountInfoAutoDispapplication. Let’s start by taking a look at the AccountInfoAutoVTBL application first.

Building the AccountInfoAutoVTBL Application

The AccountInfoAutoVTBL application is a simple application that will create and use theAccountInfoAuto object that we developed in the last chapter. Once theAccountInfoAuto object is created, we will use the VTBL-side of theIAccountInfoDispatch dual interface to set each of the object’s properties. After each

Page 185: 6935316 DCOM Microsoft Distributed Component Object Model

property has been set, we will again use the VTBL side of the IAccountInfoDispatch dualinterface to retrieve the various properties of the AccountInfoAuto object. Once all of theproperties have been retrieved, we will format them and display their values in a small window,via the MessageBox Win32 API function. The various properties defined for theAccountInfoAuto object can be seen below in Table 6-1.

Table 6-1 AccountInfoAuto Object Properties

Property Name Datatype

Number longBalance floatLimit longName BSTRSex unsigned charAddress BSTRCity BSTRState BSTRZip BSTRPhone BSTR

We will build the AccountInfoAutoVTBL application by following the responsibilities of aCOM client. In Chapter 2, you learned that a COM client is responsible for the following:

•  Initializing the COM Library

•  Obtaining initial and subsequent interfaces

•  Manipulating the COM object

•  Releasing the COM object when it is no longer needed

•  Uninitializing the COM Library

Initializing the COM Library

When the AccountInfoAutoVTBL application is first loaded, Windows invokes theWinMain application entry-point function. Once inside WinMain, theAccountInfoAutoVTBL application’s first responsibility is to initialize the COM librarywith a call to the CoInitialize COM API function:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ HRESULT hr; BSTR bstrAddress = NULL; BSTR bstrCity = NULL; BSTR bstrName = NULL; BSTR bstrPhone = NULL; BSTR bstrState = NULL; BSTR bstrZip = NULL; IUnknown *pIUnknown = NULL; IAccountInfoDispatch *pIAccountInfoDispatch = NULL;

//Initialize the COM Library

Page 186: 6935316 DCOM Microsoft Distributed Component Object Model

hr = CoInitialize(NULL); if (SUCCEEDED(hr)) { DisplayMessage(_TEXT("The COM Library has been initialized.")); . . . } else DisplayMessage(_TEXT("The COM Library initialization failed."));

Obtaining an Initial Interface

After the COM library has been successfully initialized, the AccountInfoAutoVTBLapplication calls CoCreateInstance to create a new AccountInfoAuto object andrequest an initial pointer to its IUnknown interface. Notice the use of theCLSCTX_INPROC_SERVER class execution context constant to specify that we are onlyinterested in an in-process server for the AccountInfoAuto object, which is identified by theCLSID_AccountInfoAuto constant:

DisplayMessage(_TEXT("The COM Library has been initialized."));

//Ask the COM Library to instantiate the //AccountInfoAuto object and return us an initial

//pointer to Iunknown hr = CoCreateInstance(CLSID_AccountInfoAuto, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID *)&pIUnknown);

if (SUCCEEDED(hr)) { DisplayMessage(_TEXT("The AccountInfoAuto object has been created.")); . . . } else DisplayMessage(_TEXT("The AccountInfoAuto object couldn't be created."));

While we are only interested in the CLSCTX_INPROC_SERVER execution context, COMdefines several other execution contexts, which can be found in Table 2-3.

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 187: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 188: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Manipulating the COM Object

Once the AccountInfoAuto object has been successfully instantiated, and we haveobtained an initial IUnknown interface, we can use QueryInterface to navigate to theIAccountInfoDispatch interface. Ultimately, we will use theIAccountInfoDispatch interface to set each property defined by theIAccountInfoDispatch interface. (A complete list of the properties defined by theIAccountInfoDispatch interface can be found in Table 6-1.)

DisplayMessage(_TEXT("The AccountInfoAuto object has been created.")); //Begin using the object

//QueryInterface for the IAccountInfoAuto interface hr = pIUnknown->QueryInterface (IID_IAccountInfoDispatch, (LPVOID *)&pIAccountInfoDispatch); if (SUCCEEDED(hr)) { DisplayMessage(_TEXT("Changed to the IAccountInfoDispatch interface.")); . . . } else DisplayMessage(_TEXT("Couldn't change to the IAccountInfoDispatch interface."));

The process of setting the various properties of the IAccountInfoDispatch dualinterface from the VTBL side is the same as setting the properties of a custom interface, theonly difference being that dual interfaces are restricted to Automation-compatible datatypesonly. (See Table 5-2 for a list of Automation-compatible datatypes.) The Win32 API provides

Page 189: 6935316 DCOM Microsoft Distributed Component Object Model

functions for manipulating some of the more specialized Automation datatypes, such as theBSTR and the SAFEARRARY. Table 5-5 lists Win32 API functions that are useful forworking with BSTRs, while Table 5-6 lists Win32 API functions that are useful for workingwith SAFEARRAYs.

Before we can set each property of the IAccountInfoDispatch interface, we need toinitialize the various BSTR variables that will be used to transfer the actual value of eachBSTR property. The BSTR transfer variable for each BSTR property is initialized using theSysAllocString Win32 API function. SysAllocString takes a pointer to anOLESTR as its only parameter, and returns a BSTR. OLESTRs are created using the OLESTRmacro, and are essentially wide-character strings. (See the sidebar “Understanding CharacterSets” in Chapter 5 for more information on different character sets.)

DisplayMessage(_TEXT("Changed to the IAccountInfoDispatch interface.")); bstrAddress = SysAllocString(OLESTR("1 One Way")); bstrCity = SysAllocString(OLESTR("Essex")); bstrName = SysAllocString(OLESTR("John E. Doe")); bstrPhone = SysAllocString(OLESTR ("(555) 555-0122")); bstrState = SysAllocString(OLESTR("IL")); bstrZip = SysAllocString(OLESTR("60606")); //Set each property . . .

Next, AccountInfoAutoVTBL sets each property defined by theIAccountInfoDispatch interface, and frees the resources allocated to the various BSTRvariables by calling SysFreeString:

//Set each property pIAccountInfoDispatch->put_Number(333); pIAccountInfoDispatch->put_Balance(250.25); pIAccountInfoDispatch->put_Limit(1000); pIAccountInfoDispatch->put_Address(bstrAddress); pIAccountInfoDispatch->put_City(bstrCity); pIAccountInfoDispatch->put_Name(bstrName); pIAccountInfoDispatch->put_Phone(bstrPhone); pIAccountInfoDispatch->put_Sex('M'); pIAccountInfoDispatch->put_State(bstrState); pIAccountInfoDispatch->put_Zip(bstrZip);

SysFreeString(bstrAddress); SysFreeString(bstrCity); SysFreeString(bstrName); SysFreeString(bstrPhone); SysFreeString(bstrState); SysFreeString(bstrZip);

DisplayAccountInfoDispatch(pIAccountInfoDispatch, _TEXT("Each property has been retreived."));

After each property has been set and SysFreeString is called to free the resources

Page 190: 6935316 DCOM Microsoft Distributed Component Object Model

allocated to the various BSTR variables, DisplayAccountInfoDispatch is called toretrieve, format, and display the values of the various properties defined by theIAccountInfoDispatch interface. DisplayAccountInfoDispatch takes twoparameters: pIAccountInfoDispatch — an IAccountInfoDispatch pointer; andlpszRetreivedMsg — a string pointer to a message to be displayed viaDisplayMessage. DisplayAccountInfoDispatch begins by retrieving the variousproperties of the incoming pIAccountInfoDispatch parameter, and displaying theincoming message pointed to by the lpszRetrievedMsg parameter. Next,DisplayAccountInfoDispatch builds a single string consisting of the name of eachproperty, followed by the value of that particular property. A return character (\ r) separateseach property, name/property value pair, and all string values are converted to the appropriatecharacter format, as part of the property, formatting process. Once all of the properties havebeen retrieved and formatted, the entire resulting string is displayed usingDisplayMessage. The source code for the DisplayAccountInfoDispatch functioncan be seen in Listing 6-1.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 191: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Listing 6-1. The DisplayAccountInfoDispatch function

void DisplayAccountInfoDispatch(IAccountInfoDispatch *pIAccountInfoDispatch, LPTSTR lpszRetrievedMsg){ long lAccountNumber; float flAccountBalance; long lAccountLimit; BSTR bstrAddress = NULL; BSTR bstrCity = NULL; BSTR bstrName = NULL; BSTR bstrPhone = NULL; unsigned char bySex; BSTR bstrState = NULL; BSTR bstrZip = NULL; _TCHAR szAccountNumber[255]; _TCHAR szAccountBalance[255]; _TCHAR szAccountLimit[255]; _TCHAR szDisplayText[255]; _TCHAR szConvertedText[255]; _TCHAR charSex; long lStringLen;

//Retrieve each property pIAccountInfoDispatch->get_Number(&lAccountNumber); pIAccountInfoDispatch->get_Balance(&flAccountBalance); pIAccountInfoDispatch->get_Limit(&lAccountLimit); pIAccountInfoDispatch->get_Address(&bstrAddress); pIAccountInfoDispatch->get_City(&bstrCity); pIAccountInfoDispatch->get_Name(&bstrName); pIAccountInfoDispatch->get_Phone(&bstrPhone); pIAccountInfoDispatch->get_Sex(&bySex); pIAccountInfoDispatch->get_State(&bstrState); pIAccountInfoDispatch->get_Zip(&bstrZip);

Page 192: 6935316 DCOM Microsoft Distributed Component Object Model

DisplayMessage(lpszRetrievedMsg);

//Format the Account number //Convert the long to a string _ltot(lAccountNumber, szAccountNumber, 10); _tcscpy(szDisplayText, _TEXT("Account Number: ")); _tcscat(szDisplayText, szAccountNumber); //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Account balance //Convert the float to a string _stprintf(szAccountBalance, _TEXT("%-8.2f"), flAccountBalance); _tcscat(szDisplayText, _TEXT("Account Balance: ")); _tcscat(szDisplayText, szAccountBalance); //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Account limit //Convert the long to a string _ltot(lAccountLimit, szAccountLimit, 10); _tcscat(szDisplayText, _TEXT("Account Limit: ")); _tcscat(szDisplayText, szAccountLimit); lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Name _tcscat(szDisplayText, _TEXT("Name: ")); if (bstrName) {#ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrName);#else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrName, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL);#endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string

Page 193: 6935316 DCOM Microsoft Distributed Component Object Model

szDisplayText[lStringLen + 1] = _T('\0');

//Format the Sex _tcscat(szDisplayText, _TEXT("Sex: "));#ifdef _UNICODE //UNICODE mbtowc(&charSex, (LPCH)&bySex, 1);#else //SBCS and MBCS charSex = bySex;#endif lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = charSex; //Add a carriage return szDisplayText[lStringLen + 1] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 2] = _T('\0');

//Format the Address _tcscat(szDisplayText, _TEXT("Street Address: ")); if (bstrAddress) {#ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrAddress);#else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrAddress, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL);#endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the City _tcscat(szDisplayText, _TEXT("City: ")); if (bstrCity) {#ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrCity);#else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrCity, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL);

Page 194: 6935316 DCOM Microsoft Distributed Component Object Model

#endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the State _tcscat(szDisplayText, _TEXT("State: ")); if (bstrState) {#ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrState);#else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrState, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL);#endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Zip _tcscat(szDisplayText, _TEXT("Zip: ")); if (bstrZip) {#ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrZip);

#else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrZip, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL);#endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

Page 195: 6935316 DCOM Microsoft Distributed Component Object Model

//Format the Phone Number _tcscat(szDisplayText, _TEXT("Phone: ")); if (bstrPhone) {#ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrPhone);#else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrPhone, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL);#endif

_tcscat(szDisplayText, szConvertedText); } lStringLen = _tcsclen(szDisplayText); //Null terminate the string szDisplayText[lStringLen] = _T('\0');

DisplayMessage(szDisplayText);}//DisplayAccountInfoDispatch

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 196: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Releasing the COM Object

After DisplayAccountInfoDispatch is called to retrieve, format, and display the variousIAccountInfoDispatch properties, WinMain continues by calling the Release functionon all of its outstanding interface pointers, which includes one IUnknown pointer and oneIAccountInfoDispatch pointer. At that point, the AccountInfoAuto object destroysitself:

//Release the IAccountInfoAuto interface pIAccountInfoDispatch->Release(); DisplayMessage(_TEXT("Released the IAccountInfoDispatch interface.")); . . . //Release the IUnknown interface pIUnknown->Release(); DisplayMessage(_TEXT("Released the Iunknown interface."));

Uninitializing the COM Library

Once all the outstanding interface references have been Released, all that’s left to do isuninitialize the COM library by calling CoUninitialize:

//Shut down the COM Library CoUninitialize(); DisplayMessage(_TEXT("Shut down the COM Library."));

The source code for the AccountInfoAutoVTBL application can be seen in Listing 6-2.

Listing 6-2. AccountInfoAutoVTBL.cpp

//

Page 197: 6935316 DCOM Microsoft Distributed Component Object Model

//AccountInfoAutoVTBL.cpp//#include <windows.h>#include <objbase.h>#include <tchar.h>#include <stdio.h> //for sprintf#include "AccountInfoAuto_i.h"

////Forward declarations//void DisplayMessage(LPTSTR lpMessage);void DisplayAccountInfoDispatch(IAccountInfoDispatch *pIAccountInfoDispatch, LPTSTR lpszRetrievedMsg);////Global variables//const _TCHAR g_lpszApplicationTitle[] = _TEXT("AccountInfoAutoVTBL");

////DisplayMessage//void DisplayMessage(LPTSTR lpszMessage)

{ MessageBox(NULL, lpszMessage, g_lpszApplicationTitle, MB-OK | MB_ICONEXCLAMATION);}//DisplayMessage

////DisplayAccountInfoDispatch//void DisplayAccountInfoDispatch(IAccountInfoDispatch *pIAccountInfoDispatch, LPTSTR lpszRetrievedMsg){

long lAccountNumber; float flAccountBalance; long lAccountLimit; BSTR bstrAddress = NULL; BSTR bstrCity = NULL; BSTR bstrName = NULL; BSTR bstrPhone = NULL; unsigned char bySex; BSTR bstrState = NULL; BSTR bstrZip = NULL; _TCHAR szAccountNumber[255]; _TCHAR szAccountBalance[255]; _TCHAR szAccountLimit[255]; _TCHAR szDisplayText[255]; _TCHAR szConvertedText[255]; _TCHAR charSex; long lStringLen;

Page 198: 6935316 DCOM Microsoft Distributed Component Object Model

//Retrieve each property pIAccountInfoDispatch->get_Number(&lAccountNumber); pIAccountInfoDispatch->get_Balance(&flAccountBalance); pIAccountInfoDispatch->get_Limit(&lAccountLimit); pIAccountInfoDispatch->get_Address(&bstrAddress); pIAccountInfoDispatch->get_City(&bstrCity); pIAccountInfoDispatch->get_Name(&bstrName); pIAccountInfoDispatch->get_Phone(&bstrPhone); pIAccountInfoDispatch->get_Sex(&bySex); pIAccountInfoDispatch->get_State(&bstrState); pIAccountInfoDispatch->get_Zip(&bstrZip);

DisplayMessage(lpszRetrievedMsg);

//Format the Account number //Convert the long to a string _ltot(lAccountNumber, szAccountNumber, 10); _tcscpy(szDisplayText, _TEXT("Account Number: ")); _tcscat(szDisplayText, szAccountNumber); //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Account balance //Convert the float to a string _stprintf(szAccountBalance, _TEXT("%-8.2f"), flAccountBalance); _tcscat(szDisplayText, _TEXT("Account Balance: ")); _tcscat(szDisplayText, szAccountBalance); //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Account limit //Convert the long to a string _ltot(lAccountLimit, szAccountLimit, 10); _tcscat(szDisplayText, _TEXT("Account Limit: ")); _tcscat(szDisplayText, szAccountLimit); lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Name _tcscat(szDisplayText, _TEXT("Name: ")); if (bstrName) {#ifdef _UNICODE

Page 199: 6935316 DCOM Microsoft Distributed Component Object Model

//UNICODE _tcscpy(szConvertedText, bstrName);#else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrName, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL);#endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Sex _tcscat(szDisplayText, _TEXT("Sex: "));#ifdef _UNICODE //UNICODE mbtowc(&charSex, (LPCH)&bySex, 1);#else //SBCS and MBCS charSex = bySex;#endif lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = charSex; //Add a carriage return szDisplayText[lStringLen + 1] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 2] = _T('\0');

//Format the Address _tcscat(szDisplayText,_TEXT("Street Address: ")); if (bstrAddress) {#ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrAddress);#else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrAddress, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL);#endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText);

Page 200: 6935316 DCOM Microsoft Distributed Component Object Model

szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the City _tcscat(szDisplayText, _TEXT("City: ")); if (bstrCity) {#ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrCity);#else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrCity, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL);#endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string

szDisplayText[lStringLen + 1] = _T('\0');

//Format the State _tcscat(szDisplayText, _TEXT("State: ")); if (bstrState) {#ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrState);#else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrState, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL);#endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Zip _tcscat(szDisplayText, _TEXT("Zip: "));

Page 201: 6935316 DCOM Microsoft Distributed Component Object Model

if (bstrZip) {#ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrZip);#else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrZip, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL);#endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Phone Number _tcscat(szDisplayText, _TEXT("Phone: ")); if (bstrPhone) {#ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrPhone);#else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0. bstrPhone, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL);#endif _tcscat(szDisplayText, szConvertedText); } lStringLen = _tcsclen(szDisplayText); //Null terminate the string szDisplayText[lStringLen] = _T('\0');

DisplayMessage(szDisplayText);}//DisplayAccountInfoDispatch

////WinMain//int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR IpCmdLine, int nCmdShow){ HRESULT hr; BSTR bstrAddress = NULL;

Page 202: 6935316 DCOM Microsoft Distributed Component Object Model

BSTR bstrCity = NULL; BSTR bstrName = NULL; BSTR bstrPhone = NULL; BSTR bstrState = NULL; BSTR bstrZip = NULL; IUnknown *pIUnknown = NULL; IAccountInfoDispatch *pIAccountInfoDispatch NULL;

//Initialize the COM Library hr = CoInitialize(NULL); if (SUCCEEDED(hr)) { DisplayMessage(_TEXT("The COM Library has been initialized."));

//Ask the COM Library to instantiate the AccountInfoAuto //object and return us an initial pointer to Iunknown hr = CoCreateInstance(CLSID_AccountInfoAuto, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID *)&pIUnknown);

if (SUCCEEDED(hr)) { DisplayMessage(_TEXT("The AccountInfoAuto object has been created.")); //Begin using the object

//QueryInterface for the IAccountInfoAuto interface hr = pIUnknown->QueryInterface (IID_IAccountInfoDispatch, (LPVOID *)&pIAccountInfoDispatch); if (SUCCEEDED(hr)) { DisplayMessage(_TEXT("Changed to the IAccountInfoDispatch interface.")); bstrAddress = SysAllocString(OLESTR("1 One Way")); bstrCity = SysAllocString(OLESTR("Essex")); bstrName = SysAllocString(OLESTR("John E. Doe")); bstrPhone = SysAllocString(OLESTR ("(555) 555-0122")); bstrState = SysAllocString(OLESTR("IL")); bstrZip = SysAllocString(OLESTR("60606"));

//Set each property pIAccountInfoDispatch->put_Number(333); pIAccountInfoDispatch->put_Balance(250.25); pIAccountInfoDispatch->put_Limit(1000); pIAccountInfoDispatch->put_Address(bstrAddress); pIAccountInfoDispatch->put_City(bstrCity); pIAccountInfoDispatch->put_Name(bstrName); pIAccountInfoDispatch->put_Phone(bstrPhone); pIAccountInfoDispatch->put_Sex('M'); pIAccountInfoDispatch->put_State(bstrState);

Page 203: 6935316 DCOM Microsoft Distributed Component Object Model

pIAccountInfoDispatch->put_Zip(bstrZip);

SysFreeString(bstrAddress); SysFreeString(bstrCity); SysFreeString(bstrName); SysFreeString(bstrPhone); SysFreeString(bstrState); SysFreeString(bstrZip);

DisplayAccountInfoDispatch(pIAccountInfoDispatch, _TEXT("Each property has been retreived."));

//Release the IAccountInfoAuto interface pIAccountInfoDispatch->Release(); DisplayMessage(_TEXT("Released the IAccountInfoDispatch interface.")); } else DisplayMessage(_TEXT("Couldn't change to the IAccountInfoDispatch interface."));

//Release the IUnknown interface pIUnknown->Release(); DisplayMessage(_TEXT("Released the Iunknown interface."));}

else DisplayMessage(_TEXT("The AccountInfoAuto object couldn't be created."));

//Shut down the COM Library CoUninitialize(); DisplayMessage(_TEXT("Shut down the COM Library.")); } else DisplayMessage(_TEXT("The COM Library initialization failed."));

DisplayMessage(_TEXT("Terminating the Application.")); //Terminate the application return FALSE;}//WinMain

Working with the VTBL side of a dual interface is very similar to working with custominterfaces. However, because dual interfaces are restricted to Automation-compatible datatypesonly, you will have to get acquainted with the various Win32 APIs useful for working with someof the more specialized Automation datatypes, such as the BSTR and the SAFEARRAY.

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 204: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 205: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Building the AccountInfoAutoDisp Client

In the first part of this chapter, you learned how to access the VTBL-side of a dual interface bybuilding the AccountInfoAutoVTBL application. The AccountInfoAutoVTBL applicationwas a simple application that created the AccountInfoAuto object developed in Chapter 5, andused the VTBL side of the object's AccountInfoDispatch dual interface to set and retrieve thevarious properties defined by the object before formatting and displaying their respective values.(Table 6-1 lists the various properties defined by the AccountInfoAuto object.) In this sectionyou learn how to access the IDispatch side of a dual interface by building theAccountInfoAutoDisp application. Like the AccountInfoAutoVTBL application, theAccountInfoAutoDisp application is a simple application that creates theAccountInfoAuto object developed in Chapter 5, and uses the object’s IDispatch interface toset and retrieve the various properties defined by the object before formatting and displaying theirrespective values. To better enable you to compare and contrast the differences between VTBL andIDispatch access of a dual interface, we use the source code from the AccountInfoAutoVTBLapplication as our boilerplate, and modify it as necessary to create the AccountInfoAutoDispapplication.

Setting Property Values Using IDispatch

Like the AccountInfoAutoVTBL application, the AccountInfoAutoDisp application beginsby initializing the COM library, creating the AccountInfoAuto object, and obtaining an initialinterface pointer to the AccountInfoAuto object’s IUnknown interface. However, instead ofusing IUnknown::QueryInterface to obtain an IAccountInfoDispatch interfacepointer, the AccountInfoAutoDisp application uses IUknown::QueryInterface to obtainan IDispatch interface pointer. After retrieving the IDispatch interface pointer, theAccountInfoAutoDisp application uses it to set each of the properties defined by theAccountInfoAuto object. However, before we can set any of the AccountInfoAuto object’sproperties using IDispatch, we must have the dispatch identifier (DISPID) for each individualproperty. Each DISPID can be obtained by calling IDispatch::GetIDsOfNames with the nameof each property. In order to automate the process of obtaining the DISPID for each property, I havedefined a global array of wide-character strings containing the names of each individual property:

////Property names//

Page 206: 6935316 DCOM Microsoft Distributed Component Object Model

LPOLESTR rgszNames[] = {OLESTR("Number"), OLESTR("Balance"), OLESTR("Limit"), OLESTR("Address"), OLESTR("City"), OLESTR("Name"), OLESTR("Phone"), OLESTR("Sex"), OLESTR("State"), OLESTR("Zip")};

The name of each individual property in the array is then accessed using a for loop, and used byIDispatch::GetIDsOfNames to retrieve the DISPID for each property, which is then stored ina global array of DISPIDs. As you look at the following code snippet, notice the use of theLOCALE_SYSTEM_DEFAULT to signify that the default system locale should be used to interpretthe various property names:

//Get the DISPID for the each propertyfor (index = 0; index < 10; index++) pIDispatch->GetIDsOfNames(IID_NULL, &rgszNames[index], 1, LOCALE-SYSTEM-DEFAULT, &dispids[index]);

Once the DISPID for each property has been retrieved, we can use them to read and write eachproperty. Reading and writing an individual property is a two-step process:

•  Fill in a DISPPARAMS structure with information regarding any parameters expected by theproperty or method.

•  Make the actual call to the property or method using IDispatch::Invoke.

The DISPPARAMS structure is used to send arguments into properties and methods.

typedef struct FARSTRUCT tagDISPPARAMS{VARIANTARG FAR* rgvarg; // Array of arguments. DISPID FAR* rgdispidNamedArgs; // Dispatch IDs of named arguments. unsigned int cArgs; // Number of arguments. unsigned int cNamedArgs; // Number of named arguments.} DISPPARAMS;

Note that rgvarg identifies an array of VARIANT argument values and cArgs identifies thenumber of arguments supplied in the rgvarg array. Arguments may be passed by position and/or byname. To pass arguments by position, package each argument into a VARIANT and store theVARIANTs in the rgvarg array in the reverse order of their positions, such that the last positionalargument is in rgvarg[0] and the first positional argument is in rgvarg [cArgs - 1]. (See Figure6-1.)

Figure 6-1  Arguments are supplied to the rgvarg array in the reverse order of their positions.

To pass arguments by name, include the DISPID of each named argument in thergdispidNamedArgs array element. DISPIDs for property or method parameters can be retrievedby supplying the names of each parameter after the property or method name in a call toIDispatch::GetIDsOfNames. However, don’t forget to changeIDispatch::GetIDs0fNames’s third parameter to reflect the number of DISPIDs that you areexpecting to receive in the return DISPIDs array. Update cNamedArgs to reflect the number ofnamed arguments being passed in rgdispidNamedArgs. While the ordering of named argumentsshould be irrelevant, named arguments are typically supplied in reverse order, just like positionalarguments. Automation defines the DISPID_PROPERTYPUT DISPID, which is required whensetting properties.

Page 207: 6935316 DCOM Microsoft Distributed Component Object Model

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 208: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

As all of the AccountInfoAuto properties require only one parameter — the new valueto assign to the property — the DISPPARAMS structure is initialized once and then usedrepeatedly in subsequent calls to IDispatch::Invoke, changing only the VARIANTarguments supplied in the rgvarg element of the DISPPARAMS structure:

DISPPARAMS dispparams;DISPID dispidsNamedArgs[1];VARIANTARG varg[1];

VariantInit(&varg[0]);dispidsNamedArgs[0] = DISPID_PROPERTYPUT;...//Initialize the dispatch parametersdispparams.rgvarg = &varg[0];dispparams.rgdispidNamedArgs = &dispidsNamedArgs[0];dispparams.cArgs = 1;dispparams.cNamedArgs = 1;...

Notice that although each argument is being sent by position, cNamedArgs is one. This isto reflect the one DISPID-PROPERTYPUT DISPID that is being supplied as the firstelement in the rgdispidNamedArgs array, signaling that we are attempting to set aproperty. The only thing left to do before calling IDispatch::Invoke to actuallyupdate each property’s value is to provide the new value for each property. The value ofeach property must be supplied as a VARIANT in the rgvarg array. As you look at thefollowing code snippet, notice how the VARIANT’s vt value is used to reflect the type ofdata being supplied to the VARIANT (see Table 5-3 for a list of VARIANT vt values):

Page 209: 6935316 DCOM Microsoft Distributed Component Object Model

//Number dispparams.rgvarg[0].vt = VT_I4; dispparams.rgvarg[0].lVal = 333; pIDispatch->Invoke(dispids[0], IID_NULL, LOCALE-SYSTEM-DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);

For those properties that expect BSTRs, we must use the SysAllocString function toinitialize the various BSTR variables that will contain the actual data. These BSTR variablesare then assigned to the rgvarg VARIANT array like all the other property values. Theresources allocated to these BSTR variables are later freed using SysFreeString:

bstrAddress = SysAllocString(OLESTR("1 One Way"));... //Address dispparams.rgvarg[0].vt = VT_BSTR; dispparams.rgvarg[0].bstrVal = bstrAddress; pIDispatch->Invoke(dispids[3], IID_NULL, LOCALE-SYSTEM-DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); . . . SysFreeString(bstrAddress);

Once a property’s value has been supplied, IDispatch::Invoke is called to actuallyupdate its value. The first parameter expected by IDispatch::Invoke is the DISPID ofthe property or method being accessed. The second parameter is reserved, and must beIID_NULL, while the third parameter specifies the ID of the locale to be used to interpretany arguments supplied in the call. To avoid naming conflicts between properties andmethods with the same name on the same Automation object, the fourth parameter toIDispatch::Invoke provides a way for the Automation controller to specify thecontext of a call, and may be one of the values in Table 6-2:

Table 6-2 Defined IDispatch::Invoke Context Constants

Context Constant Description

DISPATCH_METHOD Accesses a method. This value may be combined withDISPATCH_PROPERTYGET to indicate a property withthe same name.

DISPATCH_PROPERTYGET Returns the value of a propertyDISPATCH_PROPERTYPUT Assigns a new value to a propertyDISPATCH_PROPERTYPUTREF Assigns a valid object reference, rather than a value, to a

property.

The fifth parameter expected by IDispatch::Invoke is the address of theDISPPARAMS structure containing the parameter information. The sixth parameter is used

Page 210: 6935316 DCOM Microsoft Distributed Component Object Model

to store any expected return results. The seventh parameter is used to gather any exceptioninformation that may be returned and, finally, the eighth parameter is used to returninformation about the first invalid argument. Because all arguments are supplied asVARIANTs, the Automation client is responsible for converting data from oneVARIANT-supported datatype to another where appropriate.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 211: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Retrieving Property Values Using IDispatch

IDispatch::Invoke is also used to read property values. The AccountInfoAutoDispapplication’s implementation of the DisplayAccountInfoDispatch function has beenmodified to use IDispatch::Invoke to read each property value before formatting anddisplaying them. DISPATCH_PROPERTYGET is used to indicate that we are interested in receivingthe value of a property. Notice how dispparamsNoArgs is configured, with its pointer elementsset to NULL and its numeric elements set to zero, reflecting that fact that no arguments are requiredto read a property. The return value of each property is actually returned as a VARIANT invRetVal, the sixth parameter of IDispatch::Invoke:

.

.

.DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};VARIANT vRetVal;

//Retrieve each property//NumberpIDispatch->Invoke(dispids[0], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vRetVal, NULL, NULL);lAccountNumber = vRetVal.lVal;...

The source code for AccountInfoAutoDisp can be seen in Listing 6-3.

Listing 6-3. AccountInfoAutoDisp.cpp

////AccountInfoAutoDisp.cpp//#include <windows.h>#include <objbase.h>

Page 212: 6935316 DCOM Microsoft Distributed Component Object Model

#include <oleauto.h>#include <tchar.h>#include <stdio.h> //for sprintf#include "AccountInfoAuto_i.h"

////Forward declarations//void DisplayMessage(LPTSTR lpMessage);void DisplayAccountInfoDispatch(IDispatch *pIDispatch, LPTSTR lpszRetrievedMsg);////Global variables//const_TCHAR g_lpszApplicationTitle[] = _TEXT("AccountInfoAutoDisp");

////Property names//LPOLESTR rgszNames[] = {OLESTR("Number"), OLESTR("Balance"), OLESTR("Limit"), OLESTR("Address"), OLESTR("City"), OLESTR("Name"), OLESTR("Phone"), OLESTR("Sex"), OLESTR("State"), OLESTR("Zip")};////Dispatch IDs for the properties//DISPID dispids[10];

////DisplayMessagevoid DisplayMessage(LPTSTR lpszMessage){ MessageBox(NULL, lpszMessage, g_lpszApplicationTitle, MB_OK | MB_ICONEXCLAMATION);}//DisplayMessage

////DisplayAccountInfoDispatch//void DisplayAccountInfoDispatch(Idispatch *pIDispatch, LPTSTR lpszRetrievedMsg){ long lAccountNumber; float flAccountBalance; long lAccountLimit; BSTR bstrAddress = NULL; BSTR bstrCity = NULL; BSTR bstrName = NULL; BSTR bstrPhone = NULL; unsigned char bySex; BSTR bstrState = NULL; BSTR bstrZip = NULL; _TCHAR szAccountNumber[255];

Page 213: 6935316 DCOM Microsoft Distributed Component Object Model

_TCHAR szAccountBalance[255]; _TCHAR szAccountLimit[255]; _TCHAR szDisplayText[255]; _TCHAR szConvertedText[255]; _TCHAR charSex; long lStringLen; DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0}; VARIANT vRetVal;

//Retrieve each property //Number pIDispatch->Invoke(dispids[0], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vRetVal, NULL, NULL); lAccountNumber = vRetVal.lVal; //Balance pIDispatch->Invoke(dispids[1], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vRetVal, NULL, NULL); flAccountBalance = vRetVal.fltVal; //Limit pIDispatch->Invoke(dispids[2], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vRetVal, NULL, NULL); lAccountLimit = vRetVal.lVal; //Address pIDispatch->Invoke(dispids[3], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vRetVal, NULL, NULL); bstrAddress = vRetVal.bstrVal; //City pIDispatch->Invoke(dispids[4], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vRetVal, NULL, NULL); bstrCity = vRetVal.bstrVal; //Name pIDispatch->Invoke(dispids[5], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vRetVal, NULL, NULL); bstrName = vRetVal.bstrVal; //Phone pIDispatch->Invoke(dispids[6], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vRetVal, NULL, NULL); bstrPhone = vRetVal.bstrVal; //Sex pIDispatch->Invoke(dispids[7], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vRetVal, NULL, NULL); bySex = (unsigned char)vRetVal.iVal; //State pIDispatch->Invoke(dispids[8], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vRetVal, NULL, NULL); bstrState = vRetVal.bstrVal;

Page 214: 6935316 DCOM Microsoft Distributed Component Object Model

//Zip pIDispatch->Invoke(dispids[9], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vRetVal, NULL, NULL); bstrZip = vRetVal.bstrVal;

DisplayMessage(lpszRetrievedMsg);

//Format the Account number //Convert the long to a string _ltot(lAccountNumber, szAccountNumber, 10); _tcscpy(szDisplayText, _TEXT("Account Number: ")); _tcscat(szDisplayText, szAccountNumber); //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0'); //Format the Account balance //Convert the float to a string _stprintf(szAccountBalance, _TEXT("%-8.2f"), flAccountBalance); _tcscat(szDisplayText, _TEXT("Account Balance: ")); _tcscat(szDisplayText, szAccountBalance); //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Account limit //Convert the long to a string _ltot(lAccountLimit, szAccountLimit, 10); _tcscat(szDisplayText, _TEXT("Account Limit: ")); _tcscat(szDisplayText, szAccountLimit); lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Name _tcscat(szDisplayText, _TEXT("Name: ")); if (bstrName) { #ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrName); #else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrName, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL);

Page 215: 6935316 DCOM Microsoft Distributed Component Object Model

#endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Sex _tcscat(szDisplayText, _TEXT("Sex: ")); #ifdef _UNICODE //UNICODE mbtowc(&charSex, (LPCH)&bySex, 1); #else //SBCS and MBCS charSex = bySex; #endif lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = charSex; //Add a carriage return szDisplayText[lStringLen + 1] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 2] = _T('\0');

//Format the Address _tcscat(szDisplayText, _TEXT("Street Address: ")); if (bstrAddress) { #ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrAddress); #else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrAddress, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL); #endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the City _tcscat(szDisplayText, _TEXT("City: ")); if (bstrCity) { #ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrCity);

Page 216: 6935316 DCOM Microsoft Distributed Component Object Model

#else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrCity, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL); #endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the State _tcscat(szDisplayText, _TEXT("State: ")); if (bstrState) { #ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrState); #else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrState, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL); #endif _tcscat(szDisplayText, szConvertedText); } //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Zip _tcscat(szDisplayText, _TEXT("Zip: ")); if (bstrZip) { #ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrZip); #else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrZip, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL); #endif _tcscat(szDisplayText, szConvertedText);

Page 217: 6935316 DCOM Microsoft Distributed Component Object Model

} //Add a carriage return lStringLen = _tcsclen(szDisplayText); szDisplayText[lStringLen] = _T('\r'); //Null terminate the string szDisplayText[lStringLen + 1] = _T('\0');

//Format the Phone Number _tcscat(szDisplayText, _TEXT("Phone: ")); if (bstrPhone) { #ifdef _UNICODE //UNICODE _tcscpy(szConvertedText, bstrPhone); #else //SBCS and MBCS //Convert from the wide character set to the multibyte //character set WideCharToMultiByte(CP_ACP, 0, bstrPhone, -1, szConvertedText, sizeof(szConvertedText) / sizeof(_TCHAR), NULL, NULL); #endif _tcscat(szDisplayText, szConvertedText); } lStringLen = _tcsclen(szDisplayText); //Null terminate the string szDisplayText[lStringLen] = _T('\0');

DisplayMessage(szDisplayText); }//DisplayAccountInfoDispatch

// //WinMain // int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HRESULT hr; BSTR bstrAddress = NULL; BSTR bstrCity = NULL; BSTR bstrName = NULL; BSTR bstrPhone = NULL; BSTR bstrState = NULL; BSTR bstrZip = NULL; IUnknown *pIUnknown = NULL; IDispatch *pIDispatch = NULL; int index; DISPPARAMS dispparams; DISPID dispidsNamedArgs[1]; VARIANTARG varg[1];

VariantInit(&varg[0]); dispidsNamedArgs[0] = DISPID_PROPERTYPUT;

Page 218: 6935316 DCOM Microsoft Distributed Component Object Model

//Initialize the COM Library hr = CoInitialize(NULL); if (SUCCEEDED(hr)) { DisplayMessage(_TEXT("The COM Library has been initialized."));

//Ask the COM Library to instantiate the AccountInfoAuto //object and return us an initial pointer to IUnknown hr = CoCreateInstance(CLSID_AccountInfoAuto, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID *)&pIUnknown);

if (SUCCEEDED(hr)) { DisplayMessage(_TEXT("The AccountInfoAuto object has been created.")); //Begin using the object

//QueryInterface for the IDispatch interface hr = pIUnknown->QueryInterface(IID_IDispatch, (LPVOID *)&pIDispatch); if (SUCCEEDED(hr)) { DisplayMessage(_TEXT("Changed to the IDispatch interface."));

//Get the DISPID for the each property for (index = 0; index < 10; index++) pIDispatch->GetIDsOfNames(IID_NULL, &rgszNames[index], 1, LOCALE_SYSTEM_DEFAULT, &dispids[index]);

//Initialize the dispatch parameters dispparams.rgvarg = &varg[0]; dispparams.rgdispidNamedArgs = &dispidsNamedArgs[0]; dispparams.cArgs = 1; dispparams.cNamedArgs = 1;

bstrAddress = SysAllocString(OLESTR("1 One Way")); bstrCity = SysAllocString(OLESTR("Essex")); bstrName = SysAllocString(OLESTR("John E. Doe")); bstrPhone = SysAllocString(OLESTR ("(555) 555-0122")); bstrState = SysAllocString(OLESTR("IL")); bstrZip = SysAllocString(OLESTR("60606"));

//Set each property //Number dispparams.rgvarg[0].vt = VT_14; dispparams.rgvarg[0].lVal = 333; pIDispatch->Invoke(dispids[0], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);

Page 219: 6935316 DCOM Microsoft Distributed Component Object Model

//Balance dispparams.rgvarg[0].vt = VT_R4; dispparams.rgvarg[0].fltVal = 250.25; pIDispatch->Invoke(dispids[1], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); //Limit dispparams.rgvarg[0].vt = VT_14; dispparams.rgvarg[0].lVal = 1000; pIDispatch->Invoke(dispids[2], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); //Address dispparams.rgvarg[0].vt = VT_BSTR; dispparams.rgvarg[0].bstrVal = bstrAddress; pIDispatch->Invoke(dispids[3], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); //City dispparams.rgvarg[0].vt = VT_BSTR; dispparams.rgvarg[0].bstrVal = bstrCity; pIDispatch->Invoke(dispids[4], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); //Name dispparams.rgvarg[0].vt = VT_BSTR; dispparams.rgvarg[0].bstrVal = bstrName; pIDispatch->Invoke(dispids[5], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); //Phone dispparams.rgvarg[0].vt = VT_BSTR; dispparams.rgvarg[0].bstrVal = bstrPhone; pIDispatch->Invoke(dispids[6], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); //Sex dispparams.rgvarg[0].vt = VT_12; dispparams.rgvarg[0].iVal = (short)'M'; pIDispatch->Invoke(dispids[7], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); //State dispparams.rgvarg[0].vt = VT_BSTR; dispparams.rgvarg[0].bstrVal = bstrState; pIDispatch->Invoke(dispids[8], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); //Zip dispparams.rgvarg[0].vt = VT_BSTR; dispparams.rgvarg[0].bstrVal = bstrZip; pIDispatch->Invoke(dispids[9], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT &dispparams, NULL, NULL, NULL);

Page 220: 6935316 DCOM Microsoft Distributed Component Object Model

SysFreeString(bstrAddress); SysFreeString(bstrCity); SysFreeString(bstrName); SysFreeString(bstrPhone); SysFreeString(bstrState); SysFreeString(bstrZip);

DisplayAccountInfoDispatch(pIDispatch, _TEXT("Each property has been retreived."));

//Release the IDispatch interface pIDispatch->Release(); DisplayMessage( _TEXT("Released the IDispatch interface.")); } else DisplayMessage(_TEXT("Couldn't change to the IDispatch interface."));

//Release the IUnknown interface pIUnknown->Release(); DisplayMessage(_TEXT("Released the IUnknown interface.")); } else DisplayMessage(_TEXT("The AccountInfoAuto object couldn't be created."));

//Shut down the COM Library CoUninitialize(); DisplayMessage(_TEXT("Shut down the COM Library.")); } else DisplayMessage(_TEXT("The COM Library initialization failed."));

DisplayMessage(_TEXT("Terminating the Application.")); //Terminate the application return FALSE;}//WinMain

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 221: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

While working with the IDispatch side of a dual interface is not terriblycomplex, it does require more effort on the part of the client. This extra workstems from the process of obtaining the DISPIDs of the various properties andmethods supported by an Automation object and using them to actuallyexecute a particular function. Clients can obtain an Automation object’ssupported DISPIDs at compile time (early binding), or at run time (latebinding); both processes are discussed in Chapter 5. Late binding gives anAutomation controller the ability to query the services of an Automation objectat run time. After obtaining the DISPID for a particular property or method, anAutomation controller can execute the function by simply calling the object’sIDispatch::Invoke function with the function’s DISPID.

Summary

In this chapter you learned:

•  That using the VTBL side of a dual interface is a lot like using acustom COM interface, except that dual interfaces are restricted toAutomation-compatible datatypes.

•  That while the IDispatch side of a dual interface allows forrun-time binding, it requires more work (from the Automationcontroller’s perspective).

•  That obtaining an Automation object’s DISPIDs from its type libraryat compile time can significantly increase the performance of executinga function through IDispatch::Invoke.

•  That while Automation objects are restricted to using only thedatatypes supported by VARIANTs, the Automation library providesintrinsic translation from one VARIANT-supported datatype to another.

Previous Table of Contents Next

Page 222: 6935316 DCOM Microsoft Distributed Component Object Model

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 223: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Part IIBuilding Componentized Applications

Chapter 7Building Object HierarchiesIN THIS CHAPTER

•  How to define and build an object hierarchy, a collection of highlyinteroperable COM objects designed to solve a specific task

•  The benefits of building an object hierarchy

•  How to build COM objects that encapsulate data access to an ODBCdata source

IN THE FIRST part of this book, you learned the fundamentals of COM andeven built several different COM servers. In this part, you will learn how todevelop component-based applications by using COM objects to create twothree-tiered versions of a simple order-entry system. One version of the systemfollows the traditional client/server architecture; the other follows aWeb-based architecture.

While the two applications follow different architectural designs, both arerepresentative of what might be used by a typical mail-order company, wherecustomers order products over the telephone and pay for their purchases usinga special credit card account. To be effective, the order-entry application mustallow the mail-order company to maintain information for each product beingsold, each customer’s account, and each purchase invoice describing whichcustomer bought which product(s). All of the information regarding customeraccounts, product inventory, and customer purchase invoices will be stored in

Page 224: 6935316 DCOM Microsoft Distributed Component Object Model

a central data source. Both versions of the application will use the OpenDataBase Connectivity (ODBC) API to provide a universal way to accessdifferent data sources. Although the two applications have differentarchitectures, they’re both required to perform many of the exact same tasks inorder to be successful. For example, both versions of the order-entryapplication must have the ability to add new customer accounts to theunderlying data source. These areas of common functionality are prime targetsfor componentization.

In this chapter, we build the OrderEntry object hierarchy, a collection ofhighly interoperable COM objects designed to provide basic order-entrycapabilities. By using the OrderEntry object hierarchy as the basis for bothversions of the order-entry application, you gain firsthand experience with theinteroperability and component reuse aspects of COM. By building thehierarchy itself in C++; the client/server version of the order-entry applicationin Microsoft Visual Basic; and the Web-based version of the order-entryapplication using HTML, Microsoft Active Server Pages (ASP), and VisualBasic Scripting Edition (VBScript); you will gain firsthand experience withthe language-independent aspects of COM. Finally, in the last chapter, yougain firsthand experience with the location-independent aspects of COM, byusing DCOM to access various objects of the hierarchy remotely.

Defining an Object Hierarchy

The various objects of the OrderEntry object hierarchy fall into one of twobroad categories: entry objects and collection objects. Entry objects are used toconvey relatively static information about an individual entry in the datasource and are typically named according to the information they convey. Forexample, the Account entry object would be used to convey informationabout a particular customer account in the underlying data source. Collectionobjects are used to manage and represent multiple data source entries of aparticular type and are typically given the plural version of the entry objectnames they represent. For example, the Accounts collection object would beused to maintain multiple customer account entries. As Figure 7-1 illustrates,the OrderEntry object hierarchy defines four different types of entry objects:the Account object, the Product object, the Invoice object, and theLineItem object; and four different types of collection objects: theAccounts object, the Products object, the Invoices object, and theLineItems object. Information for each type of entry object is stored in adifferent table in the underlying OrderEntry.mdb Microsoft Access database.Information for the Account object is stored in the Accounts table; theProduct object’s information is in the Products table; the Invoiceobject’s information is in the Invoices table; and the LineItem object’sinformation is in the LineItems table.

Figure 7-1  The OrderEntry object hierarchy

The following sections briefly describe the various entry objects of the

Page 225: 6935316 DCOM Microsoft Distributed Component Object Model

hierarchy. After each object description is a layout of the underlying databasetable used to store the object’s information.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 226: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The Account Object

The Account object is used to provide information regarding a particularcustomer account, similar to that provided by the AccountInfoAuto objectdeveloped in Chapter 5. Even though the Account object andAccountInfoAuto object share many resemblances, we will develop theAccount object from the ground up as opposed to reusing Chapter 5’sAccountInfoAuto object. The Account object maintains the informationgiven in Table 7-1 for each customer’s account.

Table 7-1 Properties of the Account Object

Property Datatype

Number longBalance floatLimit longName BSTRSex BSTRAddress BSTRCity BSTRState BSTRZip BSTRPhone BSTRInService VARIANT_BOOL

The InService property is used to determine if a particular account hasbeen removed from service. Individual accounts are removed from service, asopposed to being deleted from the system. By deactivating an account, the

Page 227: 6935316 DCOM Microsoft Distributed Component Object Model

company is not only able to maintain records of every customer it has everhad, it is also able to maintain data integrity between the Accounts andInvoices database tables. The InService property has aVARIANT_BOOL datatype, which, like a C/C++ Boolean, is used to reflectvalues that are either true or false. However, unlike C/C++ Booleans that usezero to represent FALSE and nonzero to represent TRUE, VARIANT_BOOLsuse negative one (-1) to represent TRUE and zero (0) to represent FALSE. Theproperties of the Account object are designed to reflect the columns of theunderlying Accounts database table as closely as possible (see Table 7-2).

Table 7-2 Columns of the Accounts Database Table

Column Name Datatype

Number longBalance singleLimit longName text(40)Sex byteAddress text(80)City text(20)State text(2)Zip text(5)Phone text(13)InService Boolean

The Product Object

The Product object is used to provide the information shown in Table 7-3for each individual product in the system.

Table 7-3 Properties of the Product Object

Property Datatype

Number longDescription BSTRPrice floatStock longInService VARIANT_BOOL

Again, notice the InService property, which is used to determine if anindividual product has been removed from service. Products no longeravailable for purchase are removed from service, not deleted, which helps tomaintain data integrity between the Products and Invoices databasetables. Table 7-4 lists the columns of the Products database table. Again,notice how the columns of the Products database table reflect the propertiesof the Product object.

Page 228: 6935316 DCOM Microsoft Distributed Component Object Model

Table 7-4 Columns of the Products Database Table

Column Name Datatype

Number longDescription text(40)Price singleStock longInService Boolean

The Invoice Object

The Invoice object is used to provide the information shown in Table 7-5for each individual purchase invoice.

Table 7-5 Properties of the Invoice Object

Property Datatype

Number longEntryDate DateCustomerAccount longLineItems ILineItems*

The CustomerAccount property can be used in conjunction with theAccounts object to determine for a particular invoice the account of thecustomer making the purchase. The internal LineItems object, which isexposed through the LineItems property, provides a mechanism foriterating through the list of products that were purchased as part of an invoice,and also determining how many units of each product were purchased. Table7-6 lists the columns of the Invoices database table.

Table 7-6 Columns of the Invoices Database Table

Column Name Datatype

Number longEntryDate Date/TimeAccountNumber long

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 229: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 230: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The LineItem Object

The LineItem object is used to provide the information shown in Table 7-7for each individual item purchased as part of an invoice.

Table 7-7 Properties of the LineItem Object

Property Datatype

Number longInvoiceNumber longProductNumber longUnitsPurchased long

Table 7-8 lists the columns of the LineItems database table.

Table 7-8 Columns of the LineItems Database Table

Column Name Datatype

Number longInvoiceNumber longProductNumber longUnitsPurchased long

While each entry object is responsible for conveying information regarding anindividual entry in the data source, none of the entry objects actuallymanipulate the underlying data source. Instead, the various collection objectsare responsible for all data source manipulation. Each collection objectprovides the same basic functionality, allowing you to add new entries to the

Page 231: 6935316 DCOM Microsoft Distributed Component Object Model

underlying data source; update existing entries; remove specific entries; andlocate and retrieve specific entries. Whenever a collection object is used toreturn information regarding a specific entry, it first creates a blank entryobject and initializes it with information retrieved from the underlying datasource. The following sections briefly describe the various collection objectsof the hierarchy.

The Accounts Object

The functionality provided by the Accounts object is listed in Table 7-9.

Table 7-9 Properties and Methods of the Accounts Object

Property Datatype Description

BOF short Returns a Boolean value used todetermine whether the currentrecord pointer has gone beyondthe beginning of the record set.

EOF short Returns a Boolean value used todetermine whether the currentrecord pointer has gone beyondthe end of the record set.

Item(longAccountNumber)

IAccount* Returns a pointer to the Accountobject identified byAccountNumber, the account’sunique number. The Itemproperty also allows the user toaccess the Account identified bythe current record pointer, bycalling Item with -1.

Count long Returns the number of accounts inthe data source.

Method DescriptionAdd(IAccount*) Used to add the account defined by the incoming

Account object to the data source.Update(IAccount*) Used to modify an existing account entry in the data

source.Remove(longAccountNumber)

Used to remove the account identified byAccountNumber, the account’s unique number, fromservice, such that it can no longer be used.

Move(long INumRecs) Enables the user to reposition the current recordpointer INumRecs to the next record in the record set.

MoveFirst Enables the user to reposition the current recordpointer to the first record in the record set.

MoveLast Enables the user to reposition the current recordpointer to the last record in the record set.

MovePrev Enables the user to reposition the current recordpointer to the previous record in the record set.

Page 232: 6935316 DCOM Microsoft Distributed Component Object Model

MoveNext Allows the user to reposition the current recordpointer to the next record in the record set.

The Item property of the Accounts, Products, Invoices, andLineItems objects can be used in two different ways. In the first scenario,the Item property can be used to return information regarding a specificcustomer account identified by the customer’s account number. The followingcode shows how a client written in Visual Basic might use the Accountsobject’s Item property in the previously described manner:

'Return information about account #333Set objAccount = g_objAccounts.Item(333)

In the second scenario, the Item property is used in conjunction with the fivenavigation methods, Move, MoveFirst, MoveLast, MovePrev, orMoveNext, to retrieve information for the account identified by the currentrecord pointer. The following code illustrates this second scenario from aVisual Basic client’s perspective:

'Move to the first recordg_objAccounts.MoveFirstIf Not g_objAccounts.EOF Then 'Return the information about the first account Set objAccount = g_objAccounts.ItemEnd If

Regardless of which technique is used, whenever the Item property mustreturn an individual object, the Accounts object first creates a blankAccount object, then retrieves the account information from the underlyingdata source and uses it to initialize the newly created Account object.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 233: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The Products Object

The Products object is used to interact with the underlying Productsdatabase table in much the same way that an Accounts object is used tointeract with the underlying Accounts database table. The functionalityprovided by the Products object is listed in Table 7-10.

Table 7-10 Properties and Methods of the Products Object

Property Datatype Description

BOF short Returns a Boolean value used todetermine whether the currentrecord pointer has gone beyondthe beginning of the record set.

EOF short Returns a Boolean value used todetermine whether the currentrecord pointer has gone beyondthe end of the record set.

Item(longAccountNumber)

IProduct* Returns a pointer to the Productobject identified byProductNumber, the product’sunique number. The Itemproperty also allows the user toaccess the Product identified bythe current record pointer, bycalling Item with -1.

Count long Returns the number of products inthe data source.

Method Description

Page 234: 6935316 DCOM Microsoft Distributed Component Object Model

Add(IProduct*) Used to add the account defined by the incomingProduct object to the data source.

Update(IProduct*)Used to modify an existing product entry in the datasource.

Remove(longProductNumber)

Used to remove the product identified byProductNumber, the product’s unique number, fromservice, such that it can no longer be purchased.

Move(long INumRecs) Enables the user to reposition the current recordpointer INumRecs to the next record in the record set.

MoveFirst Enables the user to reposition the current recordpointer to the first record in the record set.

MoveLast Enables the user to reposition the current recordpointer to the last record in the record set.

MovePrev Enables the user to reposition the current recordpointer to the previous record in the record set.

MoveNext Enables the user to reposition the current recordpointer to the next record in the record set.

The Invoices Object

The Invoices object is used to interact with the underlying Invoicesdatabase table, in much the same way that the Accounts and Productsobjects are used to interact with their respective underlying database tables.The functionality provided by the Invoices object is listed in Table 7-11.

Table 7-11 Properties and Methods of the Invoices Object

Property Datatype Description

BOF short Returns a Boolean value used todetermine whether the currentrecord pointer has gone beyondthe beginning of the record set.

EOF short Returns a Boolean value used todetermine whether the currentrecord pointer has gone beyondthe end of the record set.

Item(long InvoiceNumber) IInvoice* Returns a pointer to the Invoiceobject identified byInvoiceNumber, the invoice’sunique number. The Itemproperty also allows the user toaccess the Invoice identified bythe current record pointer, bycalling Item with -1.

Count long Returns the number of invoices inthe data source.

Method Description

Page 235: 6935316 DCOM Microsoft Distributed Component Object Model

Add(IInvoice*) Used to add the invoice defined by the incomingInvoice object to the data source.

Update(IInvoice*)Used to modify an existing invoice entry in thedata source.

Remove(longInvoiceNumber)

Used to remove the invoice identified byInvoiceNumber, the invoice’s unique number,from the data source.

Move(long INumRecs) Enables the user to reposition the current recordpointer INumRecs to the next record in the recordset.

MoveFirst Enables the user to reposition the current recordpointer to the first record in the record set.

MoveLast Enables the user to reposition the current recordpointer to the last record in the record set.

MovePrev Enables the user to reposition the current recordpointer to the previous record in the record set.

MoveNext Enables the user to reposition the current recordpointer to the next record in the record set.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 236: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The LineItems Object

The LineItems object is used to interact with the underlying LineItemsdatabase table, in much the same way that the Accounts and Productsobjects are used to interact with their respective underlying database tables.The functionality provided by the LineItems object is listed in Table 7-12.

Table 7-12 Properties and Methods of the LineItems Object

Property Datatype Description

BOF short Returns a Boolean value used todetermine whether the currentrecord pointer has gone beyondthe beginning of the record set.

EOF short Returns a Boolean value used todetermine whether the currentrecord pointer has gone beyondthe end of the record set.

Item(longLineItemNumber)

ILineItem* Returns a pointer to theLineItem object identified byLineItemNumber, the line item’sunique number. The Itemproperty also allows the user toaccess the LineItem identifiedby the current record pointer, bycalling Item with -1.

Count long Returns the number of line itemsin the invoice.

Method Description

Page 237: 6935316 DCOM Microsoft Distributed Component Object Model

Add(ILineItemm*) Used to add the line item defined by the incomingLineItem object to the data source.

Update(ILineItem*)Used to modify an existing line item entry in thedata source.

Remove(longLineItemNumber)

Used to remove the line item identified byLineItemNumber, the line item’s unique number,from the data source.

Move(long INumRecs) Enables the user to reposition the current recordpointer INumRecs to the next record in the recordset.

MoveFirst Enables the user to reposition the current recordpointer to the first record in the record set.

MoveLast Enables the user to reposition the current recordpointer to the last record in the record set.

MovePrev Enables the user to reposition the current recordpointer to the previous record in the record set.

MoveNext Enables the user to reposition the current recordpointer to the next record in the record set.

Building an Object Hierarchy

Now that you have a clearer understanding of the roles of each object in theorder entry object hierarchy, we’ll move on to the next step, which is toactually build the various entry and collection objects of the hierarchy! We’llstart by building the Account, Product, Invoice, and LineItem“noun” objects because their static nature makes them a cinch to implement.Then we’ll build the Accounts, Products, Invoices, and LineItems“verb” objects, which are a lot more involved because of their interaction withtheir underlying database tables, which are accessed via ODBC. Each of thevarious objects of the hierarchy will be implemented as part of a singlein-process COM server (OrderEntry.DLL).

Building the Entry Objects

The basic design of the “entry” objects is simple: Each entry object maintainsa member variable for each property, which is then manipulated using variousGet/Put member functions. The Get functions are used to retrieve the valueof the member variable, while the Put functions are used to change the valueof the member variable, as shown in the code below for the Account object.See Tables 7-1, 7-3, 7-5, and 7-7 for lists of the properties supported by thevarious entry objects.

class CAccount : IAccount{private:..//Other member variables.

Page 238: 6935316 DCOM Microsoft Distributed Component Object Model

long m_lNumber;..//Other member variables.public:.. //Other member functions.STDMETHODIMP get_Number(long *lRetNumber);STDMETHODIMP put_Number(long lNumber);.. //Other member functions.};//CAccount

STDMETHODIMP CAccount::get_Number(long *lRetNumber){ *lRetNumber = m_lNumber; return NOERROR;}//get_Number

STDMETHODIMP CAccount::put_Number(long lNumber){ m_lnumber = lNumber; return NOERROR;}//put_Number

Building the Collection Objects

The Accounts, Products, Invoices, and LineItems objects are alittle more complex because of their interaction with the underlying databasetables, which are accessed via ODBC. As each of these objects is developed inidentical fashion, we will focus our investigation on the Accounts object;the same techniques are used to implement each of the other collection objects.However, because each of the collection objects encapsulates interactivity withan underlying database table, I think it makes sense to provide a quick anddirty overview of ODBC programming. For a more in-depth look at ODBCprogramming, you should pick up a copy of the Microsoft ODBC 3.0Programmer’s Reference and SDK Guide published by Microsoft Press.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 239: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

AN ODBC PROGRAMMING OVERVIEW

ODBC provides applications developers with a mechanism for offering database supportto their applications in a general, DataBase Management System (DBMS)-independentmanner. Instead of accessing a particular DBMS using its native protocol, applicationsdevelopers write their database manipulation routines using the ODBC API, which allowsthem to use any number of ODBC-compliant DBMSS. Architecturally, ODBC iscomprised of four components (see Figure 7-2):

•  The Application is responsible for calling ODBC API functions to submit SQLstatements, retrieve the results of those statements, and react accordingly.

•  The Driver Manager is responsible for loading the appropriate database driveron behalf of the application.

•  The Driver is a vendor-specific DLL used to process ODBC function calls,submit SQL requests to the data source, and return data to the application.

•  The Data Source is a combination of a particular DBMS product running on aparticular operating system that is accessible using a particular network.

Figure 7-2  An architectural overview of ODBC.

Of the four ODBC architectural segments depicted in Figure 7-2, building the applicationis probably the easiest. The steps for building an application, or in this case an objecthierarchy, that interacts with an ODBC data source are pretty well choreographed:

1.  Allocate a new ODBC environment handle by calling SQLAllocEnv. The

Page 240: 6935316 DCOM Microsoft Distributed Component Object Model

environment maintains global information regarding each valid connection handleand the current active connection handle. There is only one environment perapplication.

2.  Allocate a new connection handle by calling SQLAllocConnect. Once aconnection handle has been created, you can establish an actual connection withthe data source by calling SQLConnect. An application can have multipleconnections to any number of data sources.

3.  Establish a connection with the desired data source using SQLConnect and aconnection handle created using SQLAllocConnect.

4.  Allocate a new statement handle by calling SQLAllocStmt and initialize itwith an SQL statement.

5.  Use the statement handle created with SQLAllocStmt to execute your SQLstatement(s) and receive and process their results. SQL statements can be executedby calling either SQLExecDirect or SQLExecute. Use SQLExecDirect forSQL statements that you will only execute one time; otherwise, use SQLPrepareand SQLExecute.

6.  Free each statement’s resources by calling SQLFreeStmt. Each successfulcall to SQLAllocStmt should have a matching SQLFreeStmt call.

7.  Disconnect from the data source by calling SQLDisconnect. Each successfulcall to SQLConnect should have a matching SQLDisconnect call.

8.  Free each connection’s resources by calling SQLFreeConnect. Eachsuccessful call to SQLAllocConnect should have a matchingSQLFreeConnect call.

9.  Free the environment resources by calling SQLFreeEnv. Each successful callto SQLAlocEnv should have a matching SQLFreeEnv call.

While this is admittedly a highly oversimplified blueprint for building an ODBCapplication, it should serve the purpose of providing you with enough backgroundinformation for you to feel your way through the code presented throughout the rest ofthis chapter.

Before making any ODBC API calls, each application must allocate a new ODBCenvironment handle, which must be released as part of the application cleanup process.We will use the DLL entry point, DllMain, to determine when a client process attachesor detaches, so that we can allocate or free the application’s global ODBC environmenthandle using SQLAllocEnv and SQLFreeEnv respectively:

BOOL APIENTRY DllMain(HANDLE hmodule, DWORD dwReason, LPVOID lpReserved){ if (DLL_PROCESS_ATTACH == dwReason) { //Save the dll module handle for later use g_hModule = hModule; //Initialize the ODBC environment for the attaching //process if (SQL_ERROR == SQLAllocEnv(&g_hEnvironment)) return FALSE; .

Page 241: 6935316 DCOM Microsoft Distributed Component Object Model

.//Other initialization code . } if (DLL_PROCESS_DETACH == dwReason) { //Clean up the ODBC environment for this process if (g_hEnvironment) SQLFreeEnv(g_hEnvironment); . .//Other clean up code . }

return TRUE;}//Dll Main

Now that you know how to initialize and clean up the ODBC environment for each clientof the OrderEntry object hierarchy, let’s resume our discussion of how to build theAccounts collection object.

DATA ACCESS AND MANIPULATION PROPERTIES ANDMETHODS

We will begin our investigation of the Accounts object with its initialization. Theinitialization of the Accounts object occurs immediately after the object is first createdas part of the CreateInstance member function of the Accounts class factory:

STDMETHODIMP CAccountsFactory::CreateInstance(IUnknown* pUnknownOuter, REFIID iid, LPVOID *ppv){ HRESULT hr; CAccounts *pCAccounts = NULL;

*ppv = NULL; //This object doesn't support aggregation if (NULL != pUnknownOuter) return CLASS_E_NOAGGREGATION; //Create the CAccounts object pCAccounts = new CAccounts(); if (NULL == pCAccounts) return E_OUTOFMEMORY; //Initialize the new object hr = pCAccounts->Initialize(); if (FAILED(hr)) goto cleanUP;....

Page 242: 6935316 DCOM Microsoft Distributed Component Object Model

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 243: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The initialization process for the Accounts object is responsible for performing four tasks:

•  Loading the type library information for the IAccounts interface using theLoadTypeInfo function that we created in Chapter 5.

•  Allocating a connection handle using SQLAllocConnect and the environment handlethat was previously allocated as part of DllMain, the DLL entry point, and establishing aconnection to the OrderEntry data source using SQLConnect. Each connection handlemaintains information regarding a specific connection to the underlying data source. TheOrderEntry data source used by the OrderEntry object hierarchy should be configured as aSystem DSN, using the ODBC Data Source Administrator, which can be found in theWindows Control Panel. By configuring a data source as a System DSN, it will beavailable to all users of the machine, including Windows NT services. If you configureyour data source such that it requires an authorized user account and password, thisadditional information can be supplied along with the data source name in a later call toSQLConnect.

•  Allocating statement handles using SQLAllocStmt for each of the five preparedstatements that will be used by the Accounts object. Each statement handle maintainsinformation regarding a specific SQL statement and associates it with a specificconnection.

•  Navigating the current record pointer to the first record of the data source.

Once all of the necessary connection and statement handles have been allocated, the initializationprocess continues by creating five prepared SQL statements, one each for:

•  Inserting a new account

•  Updating an existing account

•  Removing an account from service

•  Retrieving information for a particular account

•  Retrieving the total number of accounts in the data source

Now that we know what the initialization process is responsible for, let’s look at how toaccomplish these objectives. We’ll begin by looking at the definitions of the various SQLstatements that will eventually be used to create prepared statements. As you look at the SQLstatement definitions, notice how the question mark (?) is used as a placeholder for values thatwill be supplied later when the statement is actually executed.

Page 244: 6935316 DCOM Microsoft Distributed Component Object Model

SQLTCHAR szSQLInsert[] = _TEXT("INSERT INTO Accounts (Balance, Limit, Name, Sex, Address, City, State, Zip, Phone, InService) VALUES (?, ?, ?, ?, ?, ?, ?, ?,?,?)");

Defining the SQL statement only represents half of the work that must be done to create aprepared statement. The next step is to allocate an ODBC statement handle and initialize it usingthe predefined SQL statement:

//Create prepared statements to reflect the //SQL operations that will be used extensively //Add //Create a statement handle for each prepared statement SQLAllocStmt(m_hdbc, &m_hstmtInsert); if (!SQL_SUCCEEDED(SQLPrepare(m_hstmtInsert, szSQLInsert, SQL_NTS))) return E_UNEXPECTED;

Notice how the current record pointer is positioned using the MoveFirst member functiononce all of the prepared statements are created. We will look into the MoveFirst method alittle later, when we discuss the Accounts object’s navigation properties and methods, but let’sfirst examine some of the Accounts object’s data manipulation properties and methods.

The first data manipulation method that we will investigate is the Add method. The followingcode shows how a client written in Visual Basic might use the Accounts object to add a newcustomer account to the data source. As you look at the source code, notice that the Numberproperty is not set when adding a new account. This is because the value for the Numberproperty is automatically generated by the data source, to prevent the possibility of two accountshaving the same account number:

'New accounts start off with zero balanceobjNewAccount.Balance = 0'New accounts start off with a $1200.00 limitobjNewAccount.Limit = 1200objNewAccount.Name = "Ken Lewis"objNewAccount.Sex = FobjNewAccount.Address = "9000 Essex"objNewAccount.City = "Chicago"objNewAccount.State = "IL"objNewAccount.Zip = "60617"objNewAccount.Phone = "(123) 456-7890"'New accounts start off in serviceobjNewAccount.InService = TRUE'Add the new account to the data sourceg_objAccounts.Add objNewAccount

The Add method performs two basic steps to actually add the new account to the data source.The first step is to retrieve the information from the incoming Account object:

//retrieve the data from the incoming object //Balance pIAccount->get_Balance(&flBalance); //Limit

Page 245: 6935316 DCOM Microsoft Distributed Component Object Model

pIAccount->get_Limit(&lLimit);

The second step is to execute the prepared insert statement using the incoming accountinformation. However, before the prepared insert statement can be executed, we must supplyvalues for each ? placeholder used in the insert statement’s definition:

SQLTCHAR szSQLInsert[] = _TEXT("INSERT INTO Accounts (Balance, Limit, Name, Sex, Address, City, State, Zip, Phone, Inservice) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");

To supply values for each of the prepared statement parameters, we will use the ODBCSQLBindParameter function. The SQLBindParameter function uses C variables astransfer buffers to shuttle data into and out of SQL prepared statements. The definition of theSQLBindParameter function looks like the following:

RETCODE SQLBindParameter(hstmt, ipar, fParamType, fCType,fSqlType, cbColDef, ibScale, rgbValue, cbValueMax, pcbValue)

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 246: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The SQLBindParameter function accepts the arguments given in Table 7-13.

Table 7-13 Arguments Accepted by SQLBindParameter

Type Argument Use Description

HSTMT hstmt Input Statement handleUWORD ipar Input Parameter number, ordered sequentially

left to right, starting at 1.SWORD fParamType Input The type of the parameter, either

SQL_PARAM_INPUT,SQL_PARAM_INPUT_OUTPUT, orSQL_PARAM_OUTPUT.

SWORD fCType Input The C datatype of the parameter (seeTable 7-5).

SWORD fSqlType Input The SQL datatype of the parameter (seeTable 7-6).

UDWORD cbColDef Input For character and binary data, specifiesthe actual length of the rgbValuebuffer. For all other datatypes, this valueis ignored.

SWORD ibScale Input The maximum number of digits to theright of the decimal point. Ignored by alldatatypes except SQL_DECIMAL andSQL_NUMERIC.

PTR rgbValue Input/Output A pointer to a buffer containing the actualdata for the parameter.

SDWORD cbValueMax Input For character and binary data, specifiesthe maximum length of the rgbValuebuffer. For all other datatypes, this valueis ignored.

Page 247: 6935316 DCOM Microsoft Distributed Component Object Model

SDWORD FAR* pcbValue Input/Output A pointer to a buffer to receive the lengthof the character or binary data returned inrgbValue. For all other datatypes, thisvalue is ignored.

Table 7-14 C Datatypes Supported by SQLBindParameter

SQL_C_BINARY SQL_C_BIT SQL_C_CHARSQL_C_DATE SQL_C_TIME SQL_C_TIMESTAMPSQL_C_DEFAULT SQL_C_DOUBLE SQL_C_FLOATSQL_C_LONG SQL_C_SLONG SQL_C_ULONGSQL_C_SHORT SQL_C_SSHORT SQL_C_USHORTSQL_C_TINYINT SQL_C_STINYINT SQL_C_UTINYINT

Table 7-15 SQL Datatypes supported by SQLBindParameter

SQL_TINYINT SQL_SMALLINT SQL_BIGINTSQL_DATE SQL_TIME SQL_TIMESTAMPSQL_DECIMAL SQL_DOUBLE SQL_FLOATSQL_INTEGER SQL_NUMERIC SQL_REALSQL_BINARY SQL_VARBINARY SQL_LONGVARBINARYSQL_CHAR SQL_VARCHAR SQL_LONGVARCHARSQL_BIT

Once a C variable has been bound for each parameter, the prepared insert statement can beexecuted using SQLExecute:

//execute the prepared statement retcode = SQLExecute(m_hstmtInsert);

Finally, after the statement has been executed, the bound parameters are released by callingSQLFreeStmt with the SQL_RESET_PARAMS option:

//release the bound parameter buffers SQLFreeStmt(m_hstmtInsert, SQL_RESET_PARAMS);

The source code for the Accounts object’s Add method can be seen in its entirety in Listing 7-1.

Listing 7-1. The Accounts object’s Add method

STDMETHODIMP CAccounts::Add(IAccount *pIAccount){ RETCODE retcode; BSTR bstrName = NULL; BSTR bstrSex = NULL; BSTR bstrAddress = NULL; BSTR bstrCity = NULL; BSTR bstrState = NULL; BSTR bstrZip = NULL; BSTR bstrPhone = NULL;

Page 248: 6935316 DCOM Microsoft Distributed Component Object Model

float flBalance; long lLimit; char szName[NAME_LEN]; char szSex[SEX_LEN]; char szAddress[ADDRESS_LEN]; char szCity[CITY_LEN]; char szState[STATE_LEN]; char szZip[ZIP_LEN]; char szPhone[PHONE_LEN]; VARIANT_BOOL vbInService;

SDWORD cbBalance = 0; SDWORD cbLimit = 0; SDWORD cbName = SQL_NTS; SDWORD cbSex = SQL_NTS; SDWORD cbAddress = SQL_NTS; SDWORD cbCity = SQL_NTS; SDWORD cbState = SQL_NTS; SDWORD cbZip = SQL_NTS; SDWORD cbPhone = SQL_NTS; SDWORD cbInService = 0;

//retrieve the data from the incoming object //Balance pIAccount->get_Balance(&flBalance); //Limit pIAccount->get_Limit(&lLimit); //Name pIAccount->get_Name(&bstrName); wcstombs(szName, bstrName, NAME_LEN); if (bstrName) SysFreeString(bstrName); //Sex pIAccount->get_Sex(&bstrSex); wcstombs(szSex, bstrSex, SEX_LEN); if (bstrSex) SysFreeString(bstrSex); //Address pIAccount->get_Address(&bstrAddress); wcstombs(szAddress, bstrAddress, ADDRESS_LEN); if (bstrAddress) SysFreeString(bstrAddress); //City pIAccount->get_City(&bstrCity); wcstombs(szCity, bstrCity, CITY_LEN); if (bstrCity) SysFreeString(bstrCity); //State pIAccount->get_State(&bstrState); wcstombs(szState, bstrState, STATE_LEN); if (bstrState) SysFreeString(bstrState); //Zip pIAccount->get_Zip(&bstrZip);

Page 249: 6935316 DCOM Microsoft Distributed Component Object Model

wcstombs(szZip, bstrZip, ZIP_LEN); if (bstrZip) SysFreeString(bstrZip); //Phone pIAccount->get_Phone(&bstrPhone); wcstombs(szPhone, bstrPhone, PHONE_LEN); if (bstrPhone) SysFreeString(bstrPhone); //InService pIAccount->get_InService(&vbInService);

//Setup parameter transfer buffers //Balance SQLBindParameter(m_hstmtInsert, 1, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_REAL, 0, 0, &flbalance, 0, &cbBalance); //Limit SQLBindParameter(m_hstmtInsert, 2, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &lLimit, 0, &cbLimit); //Name SQLBindParameter(m_hstmtInsert, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, NAME_LEN, 0, szName, 0, &cbName); //Sex SQLBindParameter(m_hstmtInsert, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, SEX_LEN, 0, szSex, 0, &cbSex); //Address SQLBindParameter(m_hstmtInsert, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, ADDRESS_LEN, 0, szAddress, 0, &cbAddress); //City SQLBindParameter(m_hstmtInsert, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, CITY_LEN, 0, szCity, 0, &cbCity); //State SQLBindParameter(m_hstmtInsert, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, STATE_LEN, 0, szState, 0, &cbState); //Zip SQLBindParameter(m_hstmtInsert, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, ZIP_LEN, 0, szZip, 0, &cbZip); //Phone SQLBindParameter(m_hstmtInsert, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, PHONE_LEN, 0, szPhone, 0, &cbPhone); //InService SQLBindParameter(m_hstmtInsert, 10, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_SMALLINT, 1, 0, &vbInService, 0, &cbInService); //execute the prepared statement retcode = SQLExecute(m_hstmtInsert); //release the bound parameter buffers SQLFreeStmt(m_hstmtInsert, SQL_RESET_PARAMS); if (!SQL_SUCCEEDED(retcode)) return E_UNEXPECTED;

return NOERROR;}//Add

Page 250: 6935316 DCOM Microsoft Distributed Component Object Model

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 251: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Prepared SQL statements and bound parameters are also used to implement the Update andRemove methods as well as the Count property. The Update method exists to enable users tomake changes to existing account, product, and invoice entries:

'Update the user's account information to reflect a recent change of addressobjExistingAccount.Address = "One Redmond Way"objExistingAccount.City = "Redmond"objExistingAccount.State = "WA"objExistingAccount.Zip = "98052"objExistingAccount.Phone = "(123) 456-7890"'Propagate the account changes to the data sourceg_objAccounts.Update objExistingAccount

As you might imagine, implementation of the Update method is practically identical to that of theAdd method in Listing 7-1.

The Remove method of the Accounts and Products objects doesn’t actually remove an entryfrom the data source, but rather marks an entry as deactivated. The logic behind deactivating accountand product entries as opposed to deleting them is simple. Part of the process of displaying an invoiceis displaying the name of the purchasing customer, as well as a description of each purchasedproduct. However, if the customer’s account has been deleted, we have no way of knowing to whomthe invoice belongs. Likewise, if a product entry has been deleted, we have no way of knowing whatproducts were purchased on the invoice. In other words, if we were to delete customer accounts andproduct entries, we would have no way of knowing which customer purchased which products on aninvoice! The deactivation solution is just one of many possible solutions to this problem, and waschosen because it is the simplest. On the other hand, invoices themselves are actually deleted fromthe system. The Remove method of the Invoices object actually deletes invoice entries from theunderlying Invoices database table. However, deleting an invoice has no direct effect on either theAccounts or Products database table.

The Count property is used to determine the number of entries in the underlying data source:

Dim lNumRecs As Long

lNumRecs = g_objAccounts.Count

Page 252: 6935316 DCOM Microsoft Distributed Component Object Model

Implementation of the Count property is pretty straightforward and can be seen along withimplementation of the Remove method in Listing 7-2.

Listing 7-2. The Accounts object’s Remove method and Count property

////Remove//STDMETHODIMP CAccounts::Remove(long lNumber){ RETCODE retcode; SDWORD cbNumber = 0;

//Setup parameter transfer buffers SQLBindParameter(m_hstmtRemove, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &lNumber, 0, &cbNumber); //execute the prepared statement retcode = SQLExecute(m_hstmtRemove); //release the bound parameter buffers SQLFreeStmt(m_hstmtRemove, SQL_RESET_PARAMS); if (!SQL_SUCCEEDED(retcode)) return E_UNEXPECTED;

return NOERROR;}//Remove

////get_Count//STDMETHODIMP CAccounts::get_Count(long *lRetCount){ RETCODE retcode; SDWORD cbCount = 0;

//execute the prepared statement retcode = SQLExecute(m_hstmtCount); if (!SQL_SUCCEEDED(retcode)) return E_UNEXPECTED; //retrieve the count retcode = SQLFetch(m_hstmtCount); if (SQL_SUCCEEDED(retcode)) SQLGetData(m_hstmtCount, 1, SQL_C_SLONG, lRetCount, 0, &cbCount); //close the recordset SQLFreeStmt(m_hstmtCount, SQL_CLOSE); if (!SQL_SUCCEEDED(retcode)) return E_UNEXPECTED;

return NOERROR;}//get_Count

The only other data manipulation property that requires in-depth discussion is the Accountsobject’s Item property. As you may recall, the Accounts object’s Item property can be used intwo different ways. In the first scenario, calling Item with the number of a specific account willcause Item to return an Account object complete with that account’s information. The followingcode shows how a client written in Visual Basic might use the Item property of the Accounts

Page 253: 6935316 DCOM Microsoft Distributed Component Object Model

object in the previously described manner:

'Return information about account #333Set objAccount = g_objAccounts.Item(333)

To retrieve information regarding a specific customer account, we use the SQLBindParameterand SQLExecute functions to transfer the user-supplied account number to the prepared querystatement and execute it. However, unlike the prepared insert, update, and remove statements,the prepared query statement returns a record set of data. To retrieve the record set data generatedby the prepared query statement, we will use the SQLBindcol and SQLFetch functions. TheSQLBindcol function allows you to use variables as transfer buffers to retrieve data from specificcolumns of a record set, similar to the way that SQLBindParameter allows you to use variables toshuttle data into and out of SQL prepared statements.

The definition of the SQLBindCol function looks like the following:

RETCODE SQLBindCol (hstmt, icol, fCType, rgbValue, cbValueMax, pcbValue)

The SQLBindcol function accepts the arguments shown in Table 7-16.

Table 7-16 Arguments Accepted by SQLBindCol

Type Argument Use Description

HSTMT hstmt Input Statement handleUWORD icol Input Column number of result data,

ordered sequentially left to right,starting at 1.

SWORD fCType Input The C datatype of the parameter(see Table 7-14).

PTR rgbValue Input/Output A pointer to a buffer containing theactual data for the parameter.

SDWORD cbValueMax Input For character and binary data,specifies the maximum length of thergbValue buffer. For all otherdatatypes, this value is ignored.

SDWORD FAR* pcbValue Input/Output A pointer to a buffer to receive thelength of the character or binarydata returned in rgbValue. For allother datatypes, this value isignored.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 254: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Once each parameter has been bound, we can retrieve the column values by calling SQLFetch:

//Setup transfer buffers //Number SQLBindCol(m_hstmtQuery, 1, SQL_C_SLONG, &lNumber, 0, &cbNumber); //Balance SQLBindCol(m_hstmtQuery, 2, SQL_C_FLOAT, &flBalance, 0, &cbBalance);..//More code. //Retrieve the data SQLFetch(m_hstmtQuery);

Finally, after the column values have been fetched, the bound parameters are released by callingSQLFreeStmt with the SQL_UNBIND option, and the record set is closed by callingSQLFreeStmt with the SQL_CLOSE option:

//unbind any columns SQLFreeStmt(m_hstmtQuery, SQL_UNBIND); //close the recordset SQLFreeStmt(m_hstmtQuery, SQL_CLOSE);

Once all of the account information has been retrieved, a blank Account object is created andinitialized using the account data:

//create a blank Account object hr = g_pIAccountFactory->CreateInstance(NULL, IID_IAccount, (LPVOID *)pIAccount); if (SUCCEEDED(hr)) { //set the properties of the return object //Number (*pIAccount)->put_Number(lNumber); .

Page 255: 6935316 DCOM Microsoft Distributed Component Object Model

.//More code .}

The second scenario for using the Item method is to retrieve the information for the accountidentified by the current record pointer, which is manipulated using the Move, MoveFirst,MoveLast, MovePrev, and MoveNext navigation methods. The following code snippet showshow a VB client might use the Item method in this second manner:

'Move to the first recordg_objAccounts.MoveFirstIf Not g_objAccounts.EOF Then 'Return the information about the first account Set objAccount = g_objAccounts.ItemEnd If

The key to the special behavior of the Item property is in its definition. As you look at the followingdefinition for the Item property, notice the optional and defaultvalue IDL keywords:

[propget, defaultcollelem, id(0), helpstring("Returns an Account from the database.")] HRESULT Item([in, optional, defaultvalue(-1)] VARIANT Key, [out, retval] IAccount **pIAccount);

The optional keyword is used to signal an optional parameter, and is only allowed on VARIANT andVARIANT * type parameters. To determine whether or not an optional parameter has been supplied,we can use the default value IDL keyword to assign a value to an optional parameter, to be usedwhenever the user doesn’t provide an argument for the parameter. In our implementation of the Itemproperty, we simply test the Key parameter for -1 to determine whether or not the user has supplied avalue for the Key parameter, since the Item property uses -1 as its default value. If Key is equal to-1, the developer is requesting information about the account identified by the current record pointer;otherwise, the developer is requesting information about the account identified by the Key. In thefirst scenario, the Item property works in conjunction with the five navigation methods:MoveFirst, MoveLast, MovePrev, MoveNext, and Move to reposition the current recordpointer and retrieve customer account information. Internally, the navigation methods useSQLExtendedFetch to reposition the current record pointer as well as to transfer data toCAccounts member variables previously bound to record set columns using SQLBIndcol. Afterthe successful completion of a navigation method, you can retrieve information about the currentcustomer account by simply querying the Item property with no parameters. The Item propertyresponds by copying the data from the CAccounts member variables to local variables, which thenuses the data to initialize a newly created Account object, which is then returned to the developer.Source code for the Accounts object’s Item method can be seen in Listing 7-3.

Listing 7-3. The Accounts object’s Item property

STDMETHODIMP CAccounts::get_Item(VARIANT Key, IAccount **pIAccount){ HRESULT hr; RETCODE retcode; BSTR bstrName = NULL; BSTR bstrSex = NULL; BSTR bstrAddress = NULL; BSTR bstrCity = NULL; BSTR bstrState = NULL; BSTR bstrZip = NULL;

Page 256: 6935316 DCOM Microsoft Distributed Component Object Model

BSTR bstrPhone = NULL;

long lNumber; float flBalance; long lLimit; wchar_t wszName[NAME_LEN]; char szName[NAME_LEN]; wchar_t wszSex[SEX_LEN]; char szSex[SEX_LEN]; wchar_t wszAddress[ADDRESS_LEN]; char szAddress[ADDRESS_LEN]; wchar_t wszCity[CITY_LEN]; char szCity[CITY_LEN]; wchar_t wszState[STATE_LEN]; char szState[STATE_LEN]; wchar_t wszZip[ZIP_LEN]; char szZip[ZIP_LEN]; wchar_t wszPhone[PHONE_LEN]; char szPhone[PHONE_LEN]; VARIANT_BOOL vbInService;

SDWORD cbNumber = 0; SDWORD cbBalance = 0; SDWORD cbLimit = 0; SDWORD cbName = SQL_NTS; SDWORD cbSex = SQL_NTS; SDWORD cbAddress = SQL_NTS; SDWORD cbCity = SQL_NTS; SDWORD cbState = SQL_NTS; SDWORD cbZip = SQL_NTS; SDWORD cbPhone = SQL_NTS; SDWORD cbInService = 0;

//Initialize the return value *pIAccount = NULL; //Coerce the incoming VARIANT into a long VariantChangeType(&Key, &Key, VARIANT_NOVALUEPROP, VT_14); //Retrieve the converted long value lNumber = V_I4(&Key); //Determine if the default is being used //which would signal us to retrieve the data pointed to by //the current record pointer if (-1 == lNumber) { //Only return an object if not BOF or EOF if (m_bBOF || m_bEOF) return NOERROR;

//The user is trying to retrieve the current Account

//Copy the data from the appropriate member functions used //to store the values for the current record //Number lNumber = m_lNavNumber; //Balance

Page 257: 6935316 DCOM Microsoft Distributed Component Object Model

flBalance = m_flNavBalance; //Limit lLimit = m_lNavLimit; //Name memcpy(szName, m_szNavName, NAME_LEN); //Sex memcpy(szSex, m_szNavSex, SEX_LEN); //Address memcpy(szAddress, m_szNavAddress, ADDRESS_LEN); //City memcpy(szCity, m_szNavCity, CITY_LEN); //State memcpy(szState, m_szNavState, STATE_LEN); //Zip memcpy(szZip, m_szNavZip, ZIP_LEN); //Phone memcpy(szPhone, m_szNavPhone, PHONE_LEN); //InService vbInService = m_vbNavInService; } else { //The user is trying to retrieve a specific account

//Setup parameter transfer buffers

//Number SQLBindParameter(m_hstmtQuery, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &lNumber, 0, &cbNumber); //Execute the prepared statement retcode = SQLExecute(m_hstmtQuery); //Release the bound parameter buffers SQLFreeStmt(m_hstmtQuery, SQL_RESET_PARAMS); if (!SQL_SUCCEEDED(retcode)) return E_UNEXPECTED;

//Setup transfer buffers //Number SQLBindCol(m_hstmtQuery, 1, SQL_C_SLONG, &lNumber, 0, &cbNumber); //Balance SQLBindCol(m_hstmtQuery, 2, SQL_C_FLOAT, &flBalance, 0, &cbBalance); //Limit SQLBindCol(m_hstmtQuery, 3, SQL_C_SLONG, &lLimit, 0, &cbLimit); //Name SQLBindCol(m_hstmtQuery, 4, SQL_C_CHAR, szName, NAME_LEN, &cbName); //Sex SQLBindCol(m_hstmtQuery, 5, SQL_C_CHAR, szSex, SEX_LEN, &cbSex); //Address SQLBindCol(m_hstmtQuery, 6, SQL_C_CHAR, szAddress,

Page 258: 6935316 DCOM Microsoft Distributed Component Object Model

ADDRESS_LEN, &cbAddress); //City SQLBindCol(m_hstmtQuery, 7, SQL_C_CHAR, szCity, CITY_LEN, &cbCity); //State SQLBindCol(m_hstmtQuery, 8, SQL_C_CHAR, szState, STATE_LEN, &cbState); //Zip SQLBindCol(m_hstmtQuery, 9, SQL_C_CHAR, szZip, ZIP_LEN, &cbZip); //Phone SQLBindCol(m_hstmtQuery, 10, SQL_C_CHAR, szPhone, PHONE_LEN, &cbPhone); //InService SQLBindCol(m_hstmtQuery, 11, SQL_C_SSHORT, &vbInService, 0, &cbInService);

//Get the next row retcode = SQLFetch(m_hstmtQuery); //Unbind any columns SQLFreeStmt(m_hstmtQuery, SQL_UNBIND); //Close the recordset SQLFreeStmt(m_hstmtQuery, SQL_CLOSE); if (SQL_NO_DATA == retcode) return NOERROR; if (!SQL_SUCCEEDED(retcode)) return E_UNEXPECTED; } //Create a blank Account object hr = g_pIAccountFactory->CreateInstance(NULL, IID_IAccount, (LPVOID *)pIAccount); if (SUCCEEDED(hr)) { //Set the properties of the return object //Number (*pIAccount)->put_Number(lNumber); //Balance (*pIAccount)->put_Balance(flBalance); //Limit (*pIAccount)->put_Limit(lLimit); //Name //Convert the string to a unicode string mbstowcs(wszName, szName, NAME_LEN); //Convert the unicode string to a BSTR bstrName = SysAllocString(wszName); (*pIAccount)->put_Name(bstrName); //Free the BSTR SysFreeString(bstrName); //Sex //Convert the string to a unicode string mbstowcs(wszSex, szSex, SEX_LEN); //Convert the unicode string to a BSTR bstrSex = SysAllocString(wszSex); (*pIAccount)->put_Sex(bstrSex); //Free the BSTR

Page 259: 6935316 DCOM Microsoft Distributed Component Object Model

SysFreeString(bstrSex); //Address //Convert the string to a unicode string mbstowc (szAddress, szAddress, ADDRESS_LEN); //Convert the unicode string to a BSTR bstrAddress = SysAllocString(wszAddress); (*pIAccount)->put_Address(bstrAddress); //Free the BSTR SysFreeString(bstrAddress); //City //Convert the string to a unicode string mbstowcs(wszCity, szCity, CITY_LEN); //Convert the unicode string to a BSTR bstrCity = SysAllocString(wszCity); (*pIAccount)->put_City(bstrCity); //Free the BSTR SysFreeString(bstrCity); //State //Convert the string to a unicode string mbstowcs(wszState, szState, STATE_LEN); //Convert the unicode string to a BSTR bstrState = SysAllocString(wszState): (*pIAccount)->put_State(bstrState); //Free the BSTR SysFreeString(bstrState); //Zip //Convert the string to a unicode string mbstowcs(wszZip, szZip, ZIP_LEN); //Convert the unicode string to a BSTR bstrzip = SysAllocString(wszZip); (*pIAccount)->put_Zip(bstrZip); //Free the BSTR SysFreeString(bstrZip); //Phone //Convert the string to a unicode string mbstowcs(wszPhone, szPhone, PHONE_LEN); //Convert the unicode string to a BSTR bstrPhone = SysAllocString(wszPhone); (*pIAccount)->put_Phone(bstrPhone); //Free the BSTR SysFreeString(bstrPhone); //InService (*pIAccount)->put_InService(vbInService); } return hr;}//get_Item

Finaly, the only remaining properties and methods left to implement are those regarding navigationthrough the collection object’s underlying data source.

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 260: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 261: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

NAVIGATION PROPERTIES AND METHODS

As I described earlier, the Item property works in conjunction with the five navigationmethods: MoveFirst, MoveLast, MovePrev, MoveNext and Move to reposition thecurrent record pointer and retrieve customer account information. When an Accounts objectis first initialized, the MoveFirst method is called to position the current record pointer tothe first customer account in the record set. Our exploration of the MoveFirst methodbegins in CAccounts::Initialize with the definition of the navigationalprepared statement, which is used by all of the five navigation methods. Thenavigational statement generates a record set containing every record from the accountsdatabase table sorted by their descriptions:

SQLTCHAR szSQLNavigationalAccess[] = _TEXT("SELECT * FROM Accounts ORDER BY Number");

Before the navigational prepared statement is actually created, we free any resourcesthat may have been previously allocated by an earlier call to MoveFirst, because adeveloper is free to call the MoveFirst method at any point in time. Once previouslyallocated resources have been freed, a statement handle is allocated using SQLAllocStmtand configured to create a keyset-driven record set using SQLSetStmtOption, after whichthe navigational prepared statement is finally created and executed:

//Free all resources associated with navigational statement if (m_hstmtNavigationalAccess) { SQLFreeStmt(m_hstmtNavigationalAccess, SQL_DROP); m_hstmtNavigationalAccess = NULL; } //Create the prepared statement that will be used //for navigational access SQLAllocStmt(m_hdbc, &m_hstmtNavigationalAccess); //Configure the options for the statement

Page 262: 6935316 DCOM Microsoft Distributed Component Object Model

SQLSetStmtOption(m_hstmtNavigationalAccess, SQL_CURSOR_TYPE, SQL_CURSOR_KEYSET_DRIVEN); if (!SQL_SUCCEEDED(SQLPrepare(m_hstmtNavigationalAccess, szSQLNavigationalAccess, SQL_NTS))) return E_UNEXPECTED; //Execute the prepared statement retcode = SQLExecute(m_hstmtNavigationalAccess); if (!SQL_SUCCEEDED(retcode)) return E_UNEXPECTED;

Next, using SQLBindCol and several CAccounts member variables, transfer buffers areestablished for each column returning data from the navigational record set:

//Setup transfer buffers //Number SQLBindCol(m_hstmtNavigationalAccess, 1, SQL_C_SLONG, &m_lNavNumber, 0, &m_cbNumber); //Balance SQLBindCol(m_hstmtNavigationalAccess, 2, SQL_C_FLOAT, &m_flNavBalance, 0, &m_cbBalance); //Limit SQLBindCol(m_hstmtNavigationalAccess, 3, SQL_C_SLONG, &m_lNavLimit, 0, &m_cbLimit); //Name SQLBindCol(m_hstmtNavigationalAccess, 4, SQL_C_CHAR, m_szNavName, NAME_LEN, &m_cbName); //Sex SQLBindCol(m_hstmtNavigationalAccess, 5, SQL_C_CHAR, m_szNavSex, SEX_LEN, &m_cbSex); //Address SQLBindCol(m_hstmtNavigationalAccess, 6, SQL_C_CHAR, m_szNavAddress, ADDRESS_LEN, &m_cbAddress); //City SQLBindCol(m_hstmtNavigationalAccess, 7, SQL_C_CHAR, m_szNavCity, CITY_LEN, &m_cbCity); //State SQLBindCol(m_hstmtNavigationalAccess, 8, SQL_C_CHAR, m_szNavState, STATE_LEN, &m_cbState): //Zip SQLBindCol(m_hstmtNavigationalAccess, 9, SQL_C_CHAR, m_szNavZip, ZIP_LEN, &m_cbZip); //Phone SQLBindCol(m_hstmtNavigationalAccess, 10, SQL_C_CHAR, m_szNavPhone, PHONE_LEN, &m_cbPhone); //InService SQLBindCol(m_hstmtNavigationalAccess, 11, SQL_C_SSHORT,

Page 263: 6935316 DCOM Microsoft Distributed Component Object Model

&m_vbNavInService, 0, &m_cbInService);

Finally, SQLExtendedFetch is used to position the current record pointer to the firstrecord of the navigational record set, and to retrieve the account information containedtherein. The last responsibility of the MoveFirst method is to determine whether or not thecurrent record pointer is before the beginning of the record set (BOF) or after the end of therecord set (EOF), and to adjust the member variables responsible for reporting theseconditions accordingly:

//Get the first record retcode = SQLExtendedFetch(m_hstmtNavigationalAccess, SQL_FETCH_NEXT, 1, &cRowsFetched, rgfRowStatus); if (SQL_SUCCEEDED(retcode)) { //Operation completed successfully, //there must be at least one record m_bBOF = FALSE; m_bEOF = FALSE; } else { //Operation failed, no current record m_bBOF = TRUE; m_bEOF = TRUE; }

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 264: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

MoveFirst is the most involved navigation method, because it is responsible for not onlyinitializing the transfer buffers for each column, but also for repositioning the current recordpointer and retrieving the appropriate data. However, the other four navigation methods useSQLExtendedFetch to reposition the current record pointer and update the contents of thebound member variables, in the same way as the MoveFirst method (see Listing 7-4).

Listing 7-4. The BOF and EOF properties and the five navigation methods of the Accountsobject: MoveFirst, MoveLast, MovePrev, MoveNext, and Move

////get_BOF//STDMETHODIMP CAccounts::get_BOF(short *sRetBOF){ *sRetBOF = (m_bBOF) ? VARIANT_TRUE : VARIANT_FALSE; return NOERROR;}//get_BOF

////get_EOF//STDMETHODIMP CAccounts::get_EOF(short *sRetEOF){ *sRetEOF = (m_bEOF) ? VARIANT_TRUE : VARIANT_FALSE; return NOERROR;}//get_EOF

////MoveFirst//STDMETHODIMP CAccounts::MoveFirst(void){ HRESULT hr = NOERROR; RETCODE retcode;

Page 265: 6935316 DCOM Microsoft Distributed Component Object Model

UDWORD cRowsFetched; UWORD rgfRowStatus[1]; SQLTCHAR szSQLNavigationalAccess[] = _TEXT("SELECT * FROM Accounts ORDER BY Number");

//Free all resources associated with navigational statement if (m_hstmtNavigationalAccess) { SQLFreeStmt(m_hstmtNavigationalAccess, SQL_DROP); m_hstmtNavigationalAccess = NULL; } //Create the prepared statement that will be used //for navigational access SQLAllocStmt(m_hdbc, &m_hstmtNavigationalAccess); //Configure the options for the statement SQLSetStmtOption(m_hstmtNavigationalAccess, SQL_CURSOR_TYPE, SQL_CURSOR_KEYSET_DRIVEN); if (!SQL_SUCCEEDED(SQLPrepare(m_hstmtNavigationalAccess, szSQLNavigationalAccess, SQL_NTS))) return E_UNEXPECTED; //Execute the prepared statement retcode = SQLExecute(m_hstmtNavigationalAccess); if (!SQL_SUCCEEDED(retcode)) return E_UNEXPECTED;

//Initialize return byte counters m_cbNumber = 0; m_cbBalance = 0; m_cbLimit = 0; m_cbName = SQL_NTS; m_cbSex = SQL_NTS; m_cbAddress = SQL_NTS; m_cbCity = SQL_NTS; m_cbState = SQL_NTS; m_cbZip = SQL_NTS; m_cbPhone = SQL_NTS; m_cbInService = 0;

//Setup transfer buffers //Number SQLBindCol(m_hstmtNavigationalAccess, 1, SQL_C_SLONG, &m_lNavNumber, 0, &m_cbNumber); //Balance SQLBindCol(m_hstmtNavigationalAccess, 2, SQL_C_FLOAT, &m_flNavBalance, 0, &m_cbBalance); //Limit SQLBindCol(m_hstmtNavigationalAccess, 3, SQL_C_SLONG, &m_lNavLimit, 0, &m_cbLimit); //Name SQLBindCol(m_hstmtNavigationalAccess, 4, SQL_C_CHAR, m_szNavName, NAME_LEN, &m_cbName); //Sex SQLBindCol(m_hstmtNavigationalAccess, 5, SQL_C_CHAR,

Page 266: 6935316 DCOM Microsoft Distributed Component Object Model

m_szNavSex, SEX_LEN, &m_cbSex); //Address SQLBindCol(m_hstmtNavigationalAccess, 6, SQL_C_CHAR, m_szNavAddress, ADDRESS_LEN, &m_cbAddress); //City SQLBindCol(m_hstmtNavigationalAccess, 7, SQL_C_CHAR, m_szNavCity, CITY_LEN, &m_cbCity); //State SQLBindCol(m_hstmtNavigationalAccess, 8, SQL_C_CHAR, m_szNavState, STATE_LEN, &m_cbState): //Zip SQLBindCol(m_hstmtNavigationalAccess, 9, SQL_C_CHAR, m_szNavZip, ZIP_LEN, &m_cbZip); //Phone SQLBindCol(m_hstmtNavigationalAccess, 10, SQL_C_CHAR, m_szNavPhone, PHONE_LEN, &m_cbPhone); //InService SQLBindCol(m_hstmtNavigationalAccess, 11, SQL_C_SSHORT, &m_vbNavInService, 0, &m_cbInService);

//Get the first record retcode = SQLExtendedFetch(m_hstmtNavigationalAccess, SQL_FETCH_NEXT, 1, &cRowsFetched, rgfRowStatus); if (SQL_SUCCEEDED(retcode)) { //Operation completed successfully, //there must be at least one record m_bBOF = FALSE; m_bEOF = FALSE; } else { //Operation failed, no current record m_bBOF = TRUE; m_bEOF = TRUE; } return hr;}//MoveFirst

////MoveLast//STDMETHODIMP CAccounts::MoveLast(void){ HRESULT hr = NOERROR; RETCODE retcode; UDWORD cRowsFetched; UWORD rgfRowStatus[1];

//Get the last record retcode = SQLExtendedFetch(m_hstmtNavigationalAccess, SQL_FETCH_LAST, 1, &cRowsFetched, rgfRowStatus); if (SQL_SUCCEEDED(retcode))

Page 267: 6935316 DCOM Microsoft Distributed Component Object Model

{ //Operation completed successfully, //there must be at least one record m_bBOF = FALSE; m_bEOF = FALSE; } else { //Operation failed, no current record m_bBOF = TRUE; m_bEOF = TRUE; } return hr;}//MoveLast

////MovePrev//STDMETHODIMP CAccounts::MovePrev(void){ HRESULT hr = NOERROR; RETCODE retcode; UDWORD cRowsFetched; UWORD rgfRowStatus[1];

//Get the last record retcode = SQLExtendedFetch(m_hstmtNavigationalAccess, SQL_FETCH_PRIOR, 1, &cRowsFetched, rgfRowStatus); if (SQL_SUCCEEDED (retcode)) { //Operation completed successfully, //there must be at least one record m_bBOF = FALSE; m_bEOF = FALSE; } else { //Operation failed, no current record m_bBOF = TRUE; } return hr;}//MovePrev

////MoveNext//STDMETHODIMP CAccounts::MoveNext(void){ HRESULT hr = NOERROR; RETCODE retcode; UDWORD cRowsFetched; UWORD rgfRowStatus[1];

Page 268: 6935316 DCOM Microsoft Distributed Component Object Model

//Get the last record retcode = SQLExtendedFetch(m_hstmtNavigationalAccess, SQL_FETCH_NEXT, 1, &cRowsFetched, rgfRowStatus); if (SQL_SUCCEEDED (retcode)) { //Operation completed successfully, //there must be at least one record m_bBOF = FALSE; m_bEOF = FALSE; } else { //Operation failed, no current record m_bEOF = TRUE; } return hr;}//MoveNext

////Move//STDMETHODIMP CAccounts::Move(long lRecs){ HRESULT hr = NOERROR; RETCODE retcode; UDWORD cRowsFetched; UWORD rgfRowStatus[1];

//Get the last record retcode = SQLExtendedFetch(m_hstmtNavigationalAccess, SQL_FETCH_RELATIVE, lRecs, &cRowsFetched, rgfRowStatus); if (SQL_SUCCEEDED (retcode)) { //Operation completed successfully, //there must be at least one record m_bBOF = FALSE; m_bEOF = FALSE; } else { //Operation failed, no current record if (lRecs < 0) m_bBOF = TRUE; else m_bEOF = TRUE; hr = E_UNEXPECTED; } return hr;}//Move

While we’ve only implemented the Accounts object thus far, each of the other collectionobjects is implemented using these very same techniques. The OrderEntry object hierarchy

Page 269: 6935316 DCOM Microsoft Distributed Component Object Model

encapsulates not only the data access to the underlying ODBC data source, but alsofundamental business rules — for example, the concept of removing an account from service asopposed to physically deleting it from the data source. By encapsulating business rules, we canassure ourselves that every application using the OrderEntry object model will follow theappropriate business rules. Also, if we should decide to change our business rule logic, we canmake the change in one place (the object hierarchy) and have it affect existing applicationswithout them having to be recompiled.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 270: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Summary

In this chapter, you learned:

•  That one of the best ways to ensure interoperability and promoteobject reuse between various COM objects is to develop an objecthierarchy.

•  That object hierarchies should encapsulate the functionality necessaryfor one or more applications to perform their fundamental tasks.

•  That by encapsulating functionality common to multiple applicationsinto a single object hierarchy, corporations can spread a good portion oftheir development and maintenance costs across the various applicationsthat are built on top of the hierarchy.

•  How to build COM objects that encapsulate data access to an ODBCdata source.

Now that we have built the OrderEntry object hierarchy, the rest of this book isdedicated to showing you how to build applications with different architecturalstyles. So that you’ll be better able to compare and contrast these differentarchitectures, we will continue to use the order entry theme. In the nextchapter, we will use the OrderEntry object hierarchy to build an order-entryapplication that follows a typical client/server design.

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 271: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 272: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Chapter 8Building the Client/Server Order-EntryApplicationIN THIS CHAPTER

•  The order-entry application’s design requirements

•  The detailed architecture of the client/server order-entry application

•  How to use the OrderEntry object hierarchy developed in Chapter 7 todevelop a client/server version of an order entry application

•  The benefits and limitations of the client/server applicationarchitecture

IN THE LAST chapter you learned about object hierarchies. You learned thatwhile COM objects are inherently interoperable, one of the best ways topromote object reuse is to develop a group of interoperable COM objectsdesigned to solve a specific purpose, as part of a single object hierarchy. Youwere then able to design and build the OrderEntry object hierarchy, which ismade up of entry objects and collection objects. Entry objects are used toconvey relatively static information about an individual entry in the datasource, while collection objects are used to manage and represent multiple datasource entries of a particular type. In the case of the OrderEntry objecthierarchy, the Account, Product, Invoice, and LineItem objects are all entryobjects, while the Account, Product, Invoice, and LineItem objects are allcollection objects (see Figure 8-1). The collection objects use ODBC tointeract with an arbitrary backend data source, which in this case happens to bethe OrderEntry.mdb Access database. In this chapter, you use the OrderEntryobject hierarchy as the foundation for a simple order-entry application. As youdevelop the order-entry application, you will find that the object hierarchy

Page 273: 6935316 DCOM Microsoft Distributed Component Object Model

encapsulates most of the basic functionality required for the order-entryapplication to perform its duties. By encapsulating its basic functionality into asingle object hierarchy, we are free to implement the actual order-entryapplication using any number of application architectures. In this chapter, wecreate a client/server version of the order-entry application, and in Chapter 9we create a Web version of the order-entry application. However, bothversions of the application use the exact same object hierarchy.

Figure 8-1  The OrderEntry object hierarchy developed in Chapter 7.

Understanding the Order-Entry Application

The order-entry application resembles an application that might be used by amail-order company to manage customer accounts that are in the form ofspecial credit cards, product inventory, and customer purchase invoices.Customers use special mail-order credit cards to purchase products from thecompany’s catalog. Telephone operators who work for the mail-ordercompany complete purchase invoices whenever customers call in to place anorder. The functionality offered by the order-entry application can be dividedinto three basic sections: customer account management, product inventorymanagement, and customer invoice management. Customer accountmanagement functions include adding new customer account entries,modifying existing customer account entries, deactivating customer accounts,and displaying a list of all the available customer account entries in the system.Likewise, the product inventory management functions include adding newproduct entries, modifying existing product entries, removing products fromservice so that they cannot be purchased, and displaying a list of all theavailable product entries in the system. Customer invoice managementfunctions include adding new invoice entries, modifying existing invoiceentries, deleting invoice entries, and displaying a list of all the invoice entriesin the system. Table 8-1 is a quick reference guide detailing the functionalityoffered by the order-entry application.

Table 8-1 The order-entry application quick reference guide

Menu Option Function Description

Accounts New Account Used to add a new customeraccount entry to the system.

Modify Existing Account Used to change some aspect of anexisting customer account entry.

Deactivate Account Used to remove a customeraccount from service.

List Accounts Used to present a list of all thecustomer account entries in thesystem.

Products New Product Used to add a new product entryto the system.

Page 274: 6935316 DCOM Microsoft Distributed Component Object Model

Modify Existing Product Used to change some aspect of anexisting product entry.

Deactivate Product Used to remove a product fromservice.

List Product Used to present a list of all theproduct entries in the system.

Invoices New Invoice Used to add a new invoice entryto the system.

Modify Existing Invoice Used to change some aspect of anexisting invoice entry.

Delete Invoice Used to remove an invoice entryfrom the system.

List Invoices Used to present a list of all theinvoice entries in the system.

Understanding the Client/Server ApplicationArchitecture

The order-entry application that we build in this chapter follows a client/serverarchitecture in which the client-side of the order-entry application isresponsible for gathering information from the user, formatting it, andforwarding it to the server for processing. The client is also responsible forreceiving processed information from the server, formatting it, and presentingit to the user. The server is responsible for receiving information from theclient, processing it, and returning the results back to the client.

Each client application maintains one global reference to an individualAccounts object, Products object, and Invoices object, which are usedwhenever the application needs to interact with the underlying data source foradding, updating, removing, or retrieving information (see Figure 8-2).

Figure 8-2  An architectural overview of the client/server order-entry system.

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 275: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 276: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Developing the Client/Server Application

Development of the client/server version of the order-entry system begins by creating theglobal references to the Accounts, Products, and Invoices objects that are used tointeract with the underlying data source. They are created as part of the MDIForm_Loadevent:

Private Sub MDIForm_Load() Set g_AccountsCol = CreateObject("OrderEntry.Accounts") Set g_ProductsCol = CreateObject("OrderEntry.Products") Set g_InvoicesCol = CreateObject("OrderEntry.Invoices")End Sub

These global object references provide the functionality we need to manage customeraccounts, product inventory, and customer purchase invoices, which are all very similar. Eachallows you to add new entries, as well as update, remove, and list existing entries. Sincemanaging accounts is similar to managing products and invoices, we will focus our discussionon the implementation of the account management functions with the understanding that thesame techniques used to implement the account management functions are used to implementthe product and invoice management functions. However, during our investigation, I willpoint out any significant differences between the various implementations of the account,product, and invoice management functions.

Adding New Accounts

Before a customer can purchase a product using the order-entry application, he or she musthave an active account. Telephone operators use a window similar to the one in Figure 8-3 toadd new customer accounts. Notice how the purchasing privileges of an account can besuspended by simply unchecking the “In Service” check box.

Page 277: 6935316 DCOM Microsoft Distributed Component Object Model

Figure 8-3  The New Account window is used to add new customer accounts.

Adding customer account entries to the data source is a very straightforward process. TheNew Account window is displayed with blank entry fields, allowing the user to enterinformation regarding the new customer account into each field. Once the new accountinformation has been entered, the user presses the OK button to submit the new information tothe data source. Behind the scenes, when the user presses the OK button, a blank Accountobject is created and initialized using the data supplied in the various fields of the NewAccount window. Once the information has been transferred from the various fields to thenewly created Account object, the object is added to the data source using the globalAccounts object reference obtained when the application was first started. The source codefor this process can be seen in Listing 8-1, the OK button’s Click event. As you look at thelisting, notice that information for an account number is not supplied. This is because the datasource automatically generates an account number for each new entry to prevent thepossibility of an overlap in user-supplied account numbers.

Listing 8-1. The Click event of the AccountInfoForm’s OK button

Private Sub cmdOK_Click() If f_ADDING Then Set f_localAccount = CreateObject("OrderEntry.Account")End If f_localAccount.Balance = CLng(txtBalance.Text) f_localAccount.Limit = CLng(txtLimit.Text) ' f_localAccount.Name = Trim$(txtName.Text) f_localAccount.Sex = Trim$(txtSex.Text) f_localAccount.Address = Trim$(txtAddress.Text) f_localAccount.City = Trim$(txtCity.Text) f_localAccount.State = Trim$(txtState.Text) f_localAccount.Zip = Trim$(txtZip.Text) f_localAccount.Phone = Trim$(txtPhone.Text) f_localAccount.InService = (chkInService.Value = 1) If f_ADDING Then 'add to the data source g_AccountsCol.Add f_localAccount Else 'update the current product g_AccountsCol.Update f_localAccount End If Unload MeEnd Sub

Retrieving Existing Accounts

The order-entry application has two management functions — Modify Account andDeactivate Account — that require the user to retrieve a specific customer’s account

Page 278: 6935316 DCOM Microsoft Distributed Component Object Model

information as its first step. The order-entry application provides two different ways toretrieve a specific customer’s account information, the easiest of which is to simply locate theaccount using the account number (see Figure 8-4).

Figure 8-4  The easiest way to retrieve a customer’s account information is to simply searchfor the account using the account number.

The LocateAccount function of the SearchForm exists to locate and retrieve a specificcustomer account using only the customer’s account number. The LocateAccountfunction displays the SearchForm and waits for the user to press either the OK button orthe Cancel button. The LocateAccount function returns TRUE if the user presses the OKbutton; otherwise, LocateAccount returns FALSE. Clicking on the OK button causes theSearchForm to disappear, and the account number that was entered into the txtNumberentry field is retrieved and used as a parameter in the retrieval of the global Accountsobject’s Item property. The Item property is responsible for actually locating and retrievingthe specified customer’s account information, which is ultimately returned in theLocateAccount function’s retAccount parameter. However, if the user presses theCancel button, or if the desired customer’s account cannot be located, LocateAccountreturns a reference to Nothing in the retAccount parameter:

Function LocateAccount(ByVal sCaption$, retAccount As Object) As Integer Dim lNumber As Long

Set retAccount = Nothing Load Me Me.Caption = sCaption$ Me.Show 1 LocateAccount = f_iRetVal If f_iRetVal Then If Trim$(txtNumber.Text) <> "" Then lnumber = CLng(txtNumber.Text) 'retrieve the account information Set retAccount = g_AccountsCol.item(lNumber) End If End If Unload MeEnd Function

Locating an account using the account number only works if the customer happens to have hisor her account number handy. If the customer doesn’t know his or her account number, theaccount can also be located using the customer’s name (see Figure 8-5).

Page 279: 6935316 DCOM Microsoft Distributed Component Object Model

Figure 8-5  Customer accounts can also be located using the customer’s name.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 280: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

The FillListboxWithAccounts subroutine of the ListForm is responsible for displayingthe name of every customer in a listbox. FillListboxWithAccounts uses the MoveFirstnavigation method of the global Accounts object to reposition the current record pointer to thefirst customer account in the data source. After clearing the current contents of the listbox,FillListboxWithAccounts enters a Do…While loop and uses the Item property of theglobal Accounts object to retrieve each account entry from the data source until there are nomore, an event signaled by the global Accounts object’s EOF property. As each customer’sname is added to the contents of the listbox, the customer’s account number is added to theItemData array of the listbox. By storing each account number in the ItemData array, detailedinformation regarding the currently selected listbox entry can be obtained immediately by simplyretrieving the account number of the selected listbox entry and supplying it to the Item propertyof the global Accounts object with code similar to the following:

lNumber = lstListing.ItemData(lstListing.ListIndex)Set localAccount = g_AccountsCol.item(lNumber)

After each entry is added to the listbox, the current record pointer of the global Accounts objectis repositioned to the next customer account in the data source using the object’s MoveNextmethod. Following is the source code for the FillListboxWithAccounts subroutine:

Sub FillListboxWithAccounts() Dim localObject As Object

g_AccountsCol.MoveFirst lstListing.Clear Do While Not g_AccountsCol.EOF Set localObject = g_AccountsCol.item lstListing.AddItem localObject.Name lstListing.ItemData(lstListing.NewIndex) = localObject.Number Set localObject = Nothing g_AccountsCol.MoveNext LoopEnd Sub

Page 281: 6935316 DCOM Microsoft Distributed Component Object Model

Updating Existing Accounts

Telephone operators use the Modify Account window to update information regarding existingcustomer accounts, in much the same way that they use the New Account window to add newcustomer accounts. Note that instead of displaying the New Account window with blank entryfields, the entry fields of the window are filled with information about the customer account beingmodified (see Figure 8-6).

Figure 8-6  The Modify Account window is used to update information regarding existingcustomer accounts.

The updating process begins by retrieving the desired customer’s account information (see“Retrieving Existing Entries”). Once the account information has been retrieved in the form of anAccount object, it is sent to the AccountInfoForm’s ModifyAccount subroutine, whichformats and displays the account information in the appropriate fields of the Modify Accountwindow.

Sub ModifyAccount(localAccount As Object) f_ADDING = False 'release any existing references If Not (f_localAccount Is Nothing) Then Set f_localAccount = Nothing Set f_localAccount = localAccount 'release the reference on the incoming object Set localAccount = Nothing Load Me Me.Caption = "Modify Account - " & CStr(f_localAccount.Number) 'display the information txtNumber.Text = CStr(f_localAccount.Number) txtBalance.Text = CStr(f_localAccount.Balance) txtLimit.Text = CStr(f_localAccount.Limit) ' txtName.Text = f_localAccount.Name txtSex.Text = f_localAccount.Sex txtAddress.Text = f_localAccount.Address txtCity.Text = f_localAccount.City txtState.Text = f_localAccount.State txtZip.Text = f_localAccount.Zip txtPhone.Text = f_localAccount.Phone chkInService.Value = Abs(f_localAccount.InService) Me.ShowEnd Sub

Once the information is displayed, it can be modified and propagated to the underlying data sourceby simply clicking on the OK button. The source code for the Click event of theAccountInfoForm’s OK button has logic to determine whether a new account is being createdor an existing account is being updated. If an existing account is being updated, the data suppliedin the various fields of the Modify Account window is transferred to the Account object beingupdated. Once the information has been transferred, it is propagated to the data source using the

Page 282: 6935316 DCOM Microsoft Distributed Component Object Model

global Accounts object reference that was obtained when the application was first started. Thesource code for the Click event of the AccountInfoForm’s OK button can be seen in Listing8-1.

Removing Existing Accounts

Removing a customer’s account from service is probably the easiest management function toimplement. The first step is to locate the desired customer’s account using one of the two methodsdescribed in “Retrieving Existing Entries.” Once the desired account has been located, removing itfrom service is simply a matter of calling the Remove method of the global Accounts objectwith the desired account number:

Dim localAccount As Object

If SearchForm.LocateAccount("Deactivate Account . . .", localAccount) Then If localAccount Is Nothing Then MsgBox "No such account could be located." Else 'deactivate the account g_AccountsCol.Remove localAccount.Number End If End If

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 283: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Adding and Updating Invoices

Ultimately, the purpose of the order-entry application is to allow telephone operators to completeonline purchase invoices as customers purchase products using their respective accounts.Telephone operators use the New Invoice window to add new invoices to the data source, andthe Modify Invoice window (see Figure 8-7) to update existing invoices.

Figure 8-7  The Modify Invoice window.

Adding and updating invoices is slightly different than adding and updating accounts orproducts, primarily because each invoice maintains a list of line items, each consisting of aproduct and the quantity being purchased (see Figure 8-8).

Figure 8-8  Each individual line item of an invoice consists of a product and the quantity beingpurchased.

Adding invoices to the data source begins with the familiar pattern of displaying a window withblank entry fields and allowing the user to enter information. However, processing and storingthe data that’s provided in the various entry fields is slightly different for Invoice objects thanfor Account or Product objects, due to the Invoice object’s internal LineItems object.The Invoice object’s internal LineItems object is responsible for maintaining the list ofproducts to be purchased, along with their respective quantities. Adding an invoice actuallyrequires two steps. In the first step, information about the invoice itself, such as its date ofcreation and the purchasing customer’s account number, is saved to the data source. Once theinvoice’s information has been saved using the global Invoices object, the second step is toadd the individual line items of the invoice. However, to prevent adding redundant line itementries to the data source, the order-entry application simply deletes all of the invoice’s existingline items from the data source before adding the current line items. While the order entry

Page 284: 6935316 DCOM Microsoft Distributed Component Object Model

application takes a simplistic approach to preventing redundant line item entries in the datasource, other more sophisticated techniques can be used to minimize the total number of entriesthat must be deleted and then re-added. The source code responsible for adding and updatingindividual invoices can be seen in Listing 8-2, the Click event for the InvoiceInfoForm’s OKbutton.

Listing 8-2. The Click event of the InvoiceInfoForm’s OK button

Private Sub cmdOK_Click() Dim invoiceLineItems As Object Dim lineItem As Object Dim item As Long

If f_ADDING Then Set f_localInvoice = CreateObject("OrderEntry.Invoice") f_localInvoice.EntryDate = CDate(txtDate.Text) f_localInvoice.CustomerAccount = CLng(txtCustomerID.Text) 'add to the data source g_InvoicesCol.Add f_localInvoice 'retrieve the last invoice which is 'the one we just added Set f_localInvoice = Nothing g_InvoicesCol.MoveLast Set f_localInvoice = g_InvoicesCol.item Else f_localInvoice.CustomerAccount = CLng(txtCustomerID.Text) 'update the current invoice g_InvoicesCol.Update f_localInvoice End If 'set the invoice properties Set invoiceLineItems = f_localInvoice.LineItems 'delete any existing line item entries 'for this invoice invoiceLineItems.MoveFirst Do While Not invoiceLineItems.EOF Set lineItem = invoiceLineItems.item invoiceLineItems.Remove lineItem.Number Set lineItem = Nothing invoiceLineItems.MoveNext Loop Set lineItem = CreateObject("OrderEntry.LineItem") For item = 1 To lvLineItems.ListItems.Count 'set each line item lineItem.InvoiceNumber = f_localInvoice.Number lineItem.ProductNumber = CLng (lvLineItems.ListItems.item(item).Text) lineItem.UnitsPurchased = CLng (lvLineItems.ListItems.item(item).SubItems(3)) invoiceLineItems.Add lineItem Next item Set lineItem = Nothing Set invoiceLineItems = Nothing Unload Me

Page 285: 6935316 DCOM Microsoft Distributed Component Object Model

End Sub

Limitations of the Client/Server Application Architecture

The client/server design of the order-entry application allows us to use client-side systemservices such as the ODBC API as well as server-side resources such as DBMSs to createpowerful yet flexible applications. However, the benefits of client/server application architecturedo not come for free. One of the primary drawbacks of client/server design is that each clientmachine’s system services must be properly configured in order for the application to workproperly. In the case of the order-entry application, each client desktop must have the appropriateODBC drivers installed and configured in order to connect to the server-side OrderEntry DBMS.This may not seem like a terribly big problem, but imagine if you had to install the order-entryapplication on hundreds or thousands of desktops. Other marks against client/server applicationsare in the areas of deployment and support. Imagine that it’s your responsibility to deploy andsupport the order-entry application. As you make modifications and improvements, how wouldyou upgrade all of your users to the latest version of the application?

All of these issues affect the application’s Total Cost of Ownership (TCO), and are just some ofthe more prevalent TCO problems with regard to the client/server application architecture.Recently, a new application architecture has come along with the promise of solving all of theseproblems, and more. That application architecture is the Web application architecture, and it isthe focus of the next chapter.

Summary

In this chapter, you learned that:

•  Building an application on top of an object hierarchy helps to reduce the complexity ofthe application development process.

•  Client/server applications allow you to make effective use of both client-side andserver-side system resources.

•  Client/server applications have several limitations in the areas of deployment,configuration, and support, which ultimately increases their Total Cost of Ownership(TCO).

In the next chapter, we build a Web version of the order-entry application that addresses some ofthe architectural limitations of the client/server design.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 286: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Chapter 9Building the Web Order-EntryApplicationIN THIS CHAPTER

•  The architecture of the Web application

•  How Active Server Pages (ASP) enhances the Web applicationarchitecture

•  How to reuse the OrderEntry object hierarchy developed in Chapter 7to develop a Web version of the order-entry application developed inChapter 8

•  The benefits and limitations of the Web application architecture

IN CHAPTER 8 you created a client/server order-entry application that allowsoperators to manage product inventory, individual customer accounts, andcustomer purchase invoices. The application is built on top of an objecthierarchy, which encapsulates the functionality necessary for the order-entryapplication to perform its required tasks of managing accounts, products, andinvoices. The object hierarchy itself is built on top of ODBC, which allows thehierarchy, and, ultimately the order-entry application, to beDBMS-independent. As you saw however, the client/server version of theorder entry application suffers from several significant limitations:

•  ODBC must be properly installed and configured on each clientmachine.

•  The order-entry application must be deployed to each client machine.

•  Each client’s copy of the order-entry application must be updated asnewer versions are made available.

Page 287: 6935316 DCOM Microsoft Distributed Component Object Model

Recently, a new Web application, or weblication, architecture has presenteditself as a more effective alternative to the traditional client/server architecture.In this chapter, we explore the Web application architecture by building a Webversion of the order-entry application created in Chapter 8.

Understanding the Web Application Architecture

The Web application architecture has increased in popularity for manyreasons, which include its ability to address many of the shortcomings of theclient/server architecture. Ironically, the Web application architecture isactually a variation of the client/server architecture and is often referred to asthin client/server. One immediate distinction between Web applications andother client/server architectures is that the infrastructure of a Web applicationis, at least at this point, very well-defined. The anatomy of a Web applicationconsists of a Web server, the HyperText Transfer Protocol (HTTP), theHyperText Markup Language (HTML), and a Web browser (see Figure 9-1).

Figure 9-1  The infrastructure of a Web application consists of a Web server,the HyperText Transfer Protocol (HTTP), the HyperText Markup Language(HTML), and a Web browser.

Clients use the Web browser to request HTML documents from the Webserver. Each HTML document can contain text, images and other forms ofmultimedia, executable or interpretable code, and links to other HTMLdocuments. The Web server delivers the HTML page using HTTP as itstransfer protocol. Once the document arrives at the client machine, the browserinterprets, formats, and displays the document’s contents to the user accordingto the HTML tags contained therein. At a simplistic level, Web publisherscreate static HTML documents and place them on the Web server, where theycan be served to client Web browsers. HTML documents of this type areconsidered static in the sense that they are created ahead of time, as opposed tobeing created dynamically, on-the-fly, in response to some outside influence.

While static HTML pages are extremely effective as pure communicationsmechanisms, they don’t lend themselves well to application development,primarily because applications require the ability to generate outputdynamically, in response to user or system demands. Microsoft’s ActiveServer Pages (ASP) is a server-side technology that works in conjunction withMicrosoft’s Internet Information Server (IIS) to generate HTML documentsdynamically in response to user request. Developers mix HTML andapplication script logic together in ASP files to control the dynamic HTML

Page 288: 6935316 DCOM Microsoft Distributed Component Object Model

document creation process. ASP scripts can be written in a wide variety ofscripting languages, including JavaScript (JScript) and a subset of Visual Basicknown as Visual Basic Scripting Edition (VBScript). If a client requests astatic HTML page, IIS fetches it and sends it to the client, just as you wouldexpect. However, if a client requests an ASP page, the ASP engine isautomatically invoked and proceeds to fetch the ASP page and process it fromtop to bottom, executing any script logic it finds and skipping over any HTML.The ASP engine identifies anything contained within <% %> as script logic.Once the entire ASP document has been processed, the resulting HTMLdocument is sent to the client just like any other HTML document, and theclient has no clue as to what has taken place (see Figure 9-2).

Figure 9-2  Active Server Pages (ASP) is used to dynamically generate HTMLdocuments in response to user requests.

In addition to providing scripting support, ASP also allows developers to useCOM components that support the IDispatch interface, which, as youlearned in Chapter 5, is the interface responsible for facilitating Automation.ASP’s combined support for various scripting languages and COMcomponents makes it an ideal platform for building Web applications. Webapplications created using ASP are referred to as ASP applications. An ASPapplication is essentially a collection of ASP files located under a commondirectory. An ASP application is allowed to have one global.asa file, which iswhere variables, functions, and subroutines with global scope are declared.The global.asa file must be located in the application’s root directory.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 289: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

While HTTP is the protocol of the Web, it is based on what amounts to stateless connections,which means that neither the client nor the server is responsible for maintaining informationabout the state of their current connection. In other words, each connection between a clientand the server is treated as a brand-new connection. Programming in such a statelessenvironment is extremely tedious and time-consuming. Thankfully, ASP maintains stateinformation for us, and presents us with what appears to be a stated environment. The firsttime that a client browser accesses any individual page of an ASP application, ASP creates anew server-side context called a Session for that particular client. Developers can use theSession context to save information on a client-by-client basis. Information that has beenstored in a Session may only be accessed from that particular session context. Informationthat needs to be accessible from within any Session must be stored in the Application context.The Application context exists as long as there is at least one client using the ASP application.A client’s Session is terminated when that client is no longer accessing one of theapplication’s ASP files, or after a predetermined period of client nonactivity. The defaulttime-out period for a Session is initially set to 90 seconds, but can be easily reconfigured bythe ASP administrator. Whenever a new Session is created, ASP fires theSession_OnStart event. Likewise, whenever a Session is terminated, ASP fires theSession_OnEnd event. Before the very first client Session is created, ASP fires theApplication_OnStart event, and when the very last client Session is terminated, ASPfires the Application_OnEnd event. Any code that you write in response to these eventsmust be contained in the global.asa file.

Web applications present an interesting alternative to traditional client/server applications, thelimitations of which are discussed at the end of Chapter 8. Since the pages that ultimatelymake up the clients are stored on the server, the client is forced to go to the server for eachnew page. By maintaining a single version of the application on the Web server, clients areguaranteed to be running the latest version of the application. So as you can see, Webapplications provide an automatic solution to the application deployment problem. Lastly,more sophisticated Web applications are capable of automatically downloading and installingadditional resources to the client machine as necessary. (We’ll see an example of this in thenext chapter.) Each of these advances ultimately helps to reduce the Total Cost of Ownership(TCO) for Web applications.

Page 290: 6935316 DCOM Microsoft Distributed Component Object Model

Developing the Web Application

One of the primary design points for the Web version of the order-entry application is that itshould resemble the client/server version as closely as possible with regard to look and feel.The more the Web version looks and feels like the client/server version, the smaller thelearning curve will be in terms of staff retraining. However, at the same time, you want theWeb version of the application to behave like other Web applications and pages so that youcan leverage any Web experience the user may already have.

Adding New Accounts

The process of adding new customer accounts using the Web version of the application isalmost identical to that of the client/server version, with telephone operators entering newaccount information into the various fields of the account information page and submittingthem to the system using the OK button (compare Figures 9-3a and 9-3b).

Figure 9-3a  The Web version of the order-entry application.

Figure 9-3b  The client/server version of the order-entry application.

However, as none of the Web clients have an ODBC connection to the data source, they areunable to add the new account information to the data source directly. Web clients interactwith the data source by requesting ASP files that encapsulate the interaction with the datasource. For example, the user interface (UI) used by Web clients to add new customeraccounts is actually an HTML form comprised of multiple text-entry fields and a single OKbutton. The static NewAccount.htm page generates the UI that is used by Web clients toreceive new customer account information (see Figure 9-3a). Whereas an HTML page canhave multiple forms, each form can only be associated with a single action, which is theUniform Resource Locator (URL) of the script that will be used to process the form’s data.The action associated with the form used by the NewAccount.htm page is the URL of theSaveAccountLogic.asp file, which happens to be an ASP file. When the user clicks on theOK button, the browser requests the SaveAccountLogic.asp file from the Web server, whichresponds by invoking the ASP engine, which then loads the SaveAccountLogic.asp file andprocesses it. This two-phase data-access technique is used whenever the Web client needs tointeract with the data source. As its name suggests, SaveAccountLogic.asp contains the codelogic ultimately responsible for saving customer account information to the data source. TheSaveAccountLogic.asp page begins with a piece of code that you will see quite oftenthroughout the Web order-entry application. The following code snippet attempts to obtain areference to a Session-level Accounts object, which has been stored in order to minimizethe unnecessary overhead of creating and destroying the Accounts object and its ODBC

Page 291: 6935316 DCOM Microsoft Distributed Component Object Model

connection. If for some reason the Session level reference cannot be obtained, the Accountsobject is recreated and then stored in the current Session.

<% Dim globalReferenceObtained

globalReferenceObtained = FALSE On Error Resume Next 'Try to obtain the Accounts object that may have been saved 'as part of this session globalReferenceObtained = Not (Session("g_AccountsCol") Is Nothing) 'If there is no Accounts object stored in this session 'then create one If Not globalReferenceObtained Then Set Session("g_AccountsCol") = CreateObject("OrderEntry.Accounts") End If%>

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 292: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Next, the page determines whether it has been called to add a new customer account or toupdate an existing customer’s account information. To determine its purpose, the pageretrieves the incoming AccountNumber parameter and attempts to retrieve the accountidentified by it. If the attempt is unsuccessful, we assume that the caller is performing an add;otherwise, we assume that the caller is performing an update. If the caller is performing anadd, we create a blank Account object, call SetProperties to initialize its propertiesusing the data from the form variables, and use the Session-level Accounts object referenceto add the new account to the data source. If the caller is performing an update, we callSetProperties to modify the existing account, and use the Session-level Accountsobject reference to propagate the changes to the underlying data source. The source code forthis process can be seen in Listing 9-1.

Listing 9-1. Code from the SaveAccountLogic.asp ASP page that is used to add new accountinformation to the data source or update existing account information in the data source

<% Sub SetProperties(account) account.Balance = Request("txtBalance") account.Limit = Request("txtLimit") ' account.Name = Request("txtName") account.Sex = Request("txtSex") account.Address = Request("txtAddress") account.Address = "Home" account.City = Request("txtCity") account.State = Request("txtState") account.Zip = Request("txtZip") account.Phone = Request("txtPhone") account.InService = ("ON" = UCASE(Request("chkInService"))) End Sub

Dim localAccount

Page 293: 6935316 DCOM Microsoft Distributed Component Object Model

Dim lNumber

'The account that we should display is in the AccountNumber 'Response parameter lNumber = Request("AccountNumber") 'Retrieve the account information Set localAccount = Session("g_AccountsCol").item(lNumber) If localAccount Is Nothing Then 'The account was not located

'add the new account 'create a blank object Set localAccount = CreateObject("OrderEntry.Account") Call SetProperties(localAccount) 'add to the data source Session("g_AccountsCol").Add localAccount%> <p align="center"><font size="5"> <strong>Account successfully added!</strong> </font></p><% Else 'The account was located

'update the existing account Call SetProperties(localAccount) 'add to the data source Session("g_AccountsCol").Update localAccount%> <p align="center"><font size="5"> <strong>Account successfully updated!</strong> </font></p><% End If 'Release the object's reference Set localAccount = Nothing%>

Identifying Existing Accounts

Before an existing customer account can be updated or removed, it must first be identified. Asyou saw, the client/server version of the order-entry application has functions that areresponsible for not only identifying accounts, but also for locating and retrieving them. Oncethe account is located and retrieved, the client/server version of the order-entry applicationcan pass the retrieved account information to another function for either updating orremoving. However, the nature of Web application architectures, specifically their inability tomaintain references to COM objects located on different machines over the HTTP protocol,prevents the Web version of the order-entry application from functioning in a similar manner.When it comes to identifying existing customer accounts, the Web version of the order-entryapplication is capable of displaying UIs similar to those used by the client/server version foridentifying, locating, and retrieving customer account information (see Figures 9-4a and 9-4band 9-5a and 9-5b.)

Page 294: 6935316 DCOM Microsoft Distributed Component Object Model

Figure 9-4a  The Web version of the order-entry application provides a UI for identifying anaccount using the account number; compare it to Figure 9-4b.

Figure 9-4b  The client/server version of the same functionality.

Figure 9-5a  The Web version of the order-entry application provides a UI for selecting anaccount from a list using the account name; compare it to Figure 9-5b.

Figure 9-5b  The client/server version of the same functionality.

The ListAccounts.asp file is used to generate an HTML form-based UI comprised of alistbox containing the names of each customer account in the data source, and a Details button(see Figure 9-5a). After obtaining a reference to a Session-level Accounts object, theListAccounts.asp file moves to the first record of the data source and iterates through eachrecord, adding each account name and account number to the listbox, which is generatedusing the HTML <SELECT> tag:

<select name="lstListing" size="15"><% Dim localObject

Session("g_AccountsCol").MoveFirst Do While Not Session("g_AccountsCol").EOF Set localObject = Session("g_AccountsCol").item%> <option value = <% = localObject.Number%><% = localObject.Name%></option><% Set localObject = Nothing Session("g_AccountsCol").MoveNext

Page 295: 6935316 DCOM Microsoft Distributed Component Object Model

Loop%> </select>

To select an account from the list, the user highlights the desired account in the listbox andclicks on the Details button. When the user clicks on the Details button, the account numberof the selected account is sent to the ModifyAccount.asp file, which then locates, retrieves,and displays the account identified by the incoming AccountNumber parameter forupdating.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 296: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Updating Existing Accounts

Before an account can be updated, it must be located and retrieved first. As you saw, whereas theclient/server version of the order-entry application provides two different mechanisms for locatingand retrieving existing customer accounts, the Web version of the order-entry application providestwo different mechanisms for identifying customer accounts. The Web order-entry applicationdoesn’t provide a single mechanism for actually locating and retrieving existing customeraccounts. This is a subtle but important difference between the implementations of the twoversions. For example, the SearchForm.LocateAccount function of the client/server order-entryapplication actually returns an Account object in one of its parameters. The returned Accountobject can then be updated or removed from service. While the Web order-entry applicationprovides an HTML form-based UI similar to that produced by calling theSearchForm.LocateAccount function of the client/server order-entry application, the formonly returns an account number. The ASP file that is the action of the form is ultimatelyresponsible for locating and retrieving the customer account information based on an accountnumber parameter. Once an account is identified using one of the two account identificationtechniques described in “Identifying Existing Accounts,” the Web order-entry application usestwo additional ASP files to update an existing account: ModifyAccount.asp, which is used tolocate, retrieve, and display the desired customer’s account information (see Figure 9-6) andSaveAccountLogic.asp, which actually saves customer account information to the data source.(We examined the SaveAccountLogic.asp file in “Adding New Accounts.”)

Figure 9-6  The ModifyAccount.asp file is responsible for locating, retrieving, and displaying anexisting customer’s account information.

ModifyAccount.asp begins with the process that was used in the SaveAccountLogic.asp file toobtain a reference to a Session-level Accounts object, after which ModifyAccount.asp uses anincoming AccountNumber parameter along with the Session-level Accounts object to locate

Page 297: 6935316 DCOM Microsoft Distributed Component Object Model

and retrieve the desired customer account information. If the account cannot be located, theModifyAccount.asp will generate an HTML page similar to that in Figure 9-7.

Figure 9-7  If ModifyAccount.asp cannot locate the specified customer account, it will notify theuser with an HTML page.

If the account is located, its information is retrieved and used to fill the individual entry fields ofthe HTML form-based UI (see Listing 9-2).

Listing 9-2. Code from the ModifyAccount.asp file that is used to locate and retrieve customeraccount information before displaying it in an HTML form-based UI for the user to edit

<% Dim localAccount Dim lNumber

'The account that we should display is in the AccountNumber 'Response parameter lNumber = Request("AccountNumber") 'Retrieve the account information Set localAccount = Session("g_AccountsCol").item(lNumber) If localAccount Is Nothing Then 'The account was not located%> <p align="center"><font size="5"> <strong>No such account could be located.</strong> </font></p><% Else 'The account was located%> <p><font size="5"> <strong>Modify Existing Account </strong> </font></p>

<form action="SaveAccountLogic.asp"> <input type="hidden" name="AccountNumber" value="<% = lNumber%>"> <table border="0" width="100%"> <tr> <td valign="top"><p align="left">Account #: <strong><% = localAccount.Number%></strong></p> </td> <td valign="top"><p align="right"><input type="submit" name="cmdOK" value="OK"></p> </td> </tr>

Page 298: 6935316 DCOM Microsoft Distributed Component Object Model

</table> <div align="left"><table border="0" width="100%"> <tr> <td valign="top"><% If localAccount.InService = True Then%> <input type="checkbox" name="chkInService" Checked>In Service<% Else%> <input type="checkbox" name="chkInService">In Service<% End If%> </td> </tr> </table> </div><p align="left"><strong>Credit Information</strong></p> <div align="left"><table border="0"> <tr> <td align="right">Balance:</td> <td><input type="text" size="10" name="txtBalance" value="<% = localAccount.Balance%>"></td> </tr> <tr> <td align="right">Limit:</td> <td><input type="text" size="10" name="txtLimit" value="<% = localAccount.Limit%>"></td> </tr> </table> </div><p align="left"><strong>Billing Information</strong></p> <div align="left"><table border="0"> <tr> <td align="right">Name: </td> <td><input type="text" size="40" name="txtName" value="<% = localAccount.Name%>"></td> </tr> <tr> <td align="right">Sex:</td> <td><input type="text" size="1" name="txtSex" value="<% = localAccount.Sex%>"></td> </tr> <tr> <td align="right">Address :</td> <td><p align="left"><textarea name="txtAddress" rows="2" cols="40"><% = localAccount.Address%></textarea></p> </td> </tr> <tr> <td align="right">City:</td> <td><input type="text" size="20" name="txtCity"

Page 299: 6935316 DCOM Microsoft Distributed Component Object Model

value="<% = localAccount.City%>"></td> </tr> <tr> <td align="right">State:</td> <td><input type="text" size="2" name="txtState" value="<% = localAccount.State%>"></td> </tr> <tr> <td align="right">Zip:</td> <td><input type="text" size="5" name="txtZip" value="<% = localAccount.Zip%>"></td> </tr> <tr> <td align="right">Phone:</td> <td><input type="text" size="13" name="txtPhone" value="<% = localAccount.Phone%>"></td> </tr> </table> </div></form><% End If 'Release the object's reference Set localAccount = Nothing%>

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 300: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Removing Existing Accounts

Before an account can be removed from service, it must first be identified, using either of the twosupported account identification methods previously described. However, once an existingcustomer account has been identified, the DeactivateAccountLogic.asp file is used to actuallyremove the account from service. The DeactivateAccountLogic.asp file begins with the samelogic as the SaveAccountLogic.asp and ModifyAccount.asp files, which exist to obtain areference to a Session-level Accounts object. The Accounts object is then used to locate andretrieve the existing customer account identified by the incoming AccountNumber parameter. Ifthe account cannot be located, the DeactivateAccountLogic.asp will generate an HTML pagesimilar to that in Figure 9-6. If the account is located, its information is retrieved and used toremove the account from service (see Listing 9-3).

Listing 9-3. Code from the DeactivateAccountLogic.asp file that is used to locate, retrieve, andremove an existing customer account from service

<% Dim localAccount Dim lNumber

'The account that we should deactivate is in the AccountNumber 'Response parameter lNumber = Request("AccountNumber") 'Retrieve the account information Set localAccount = Session("g_AccountsCol").item(lNumber) If localAccount Is Nothing Then 'The account was not located%> <p align="center"><font size="5"> <strong>No such account could be located.</strong> </font></p><% Else 'The account was located 'deactivate the account

Page 301: 6935316 DCOM Microsoft Distributed Component Object Model

Session("g_AccountsCol").Remove localAccount.Number%> <p align="center"><font size="5"> <strong>Account successfully deactivated!</strong> </font></p><% End If 'Release the object's reference Set localAccount = Nothing%>

Limitations of the Web Application Architecture

While the Web version of the order-entry application definitely solves some of the major problemsplaguing the client/server version of the application, specifically the installation, configuration,and support issues, it also presents an entirely new set of problems. The most fundamentalproblem with the basic Web application architecture is that the Web application is only able toaccess server-side resources. This also means that developers must use an entirely new, unfamiliarprogramming model to develop Web applications. For example, the client/server version of theorder-entry application simply maintains global references to the various objects that provideaccess to the underlying data source, using them whenever it needs to interact with the datasource. However, since the Web version of the application doesn’t have access to client-sidesystem resources, specifically ODBC, this technique doesn’t work. Instead, the Web version of theorder-entry application must take a two-step approach, in which data is gathered using one HTMLpage, and processed on the server as part of the creation of a second, ASP-generated, HTML page.

Summary

In this chapter, you learned that:

•  The infrastructure required to run a Web application is very well-defined and consists of aWeb server, HTTP, HTML, and a Web browser.

•  By encapsulating an application’s basic functionality inside an object hierarchy, you havethe freedom and flexibility to develop an application using any of today’s popularapplication architectures.

•  Web applications can be used to solve many of the basic deployment, configuration, andsupport problems associated with client/server applications.

•  Web applications introduce their own set of problems, including the need to learn aslightly different programming model that often only allows access to server-side systemresources.

In the final chapter, we create a slightly different version of the order-entry application based onthe Web application architecture developed in this chapter. The new version of the order-entryapplication will use DCOM to overcome the limitations of the Web version created in this chapter.

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 302: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 303: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Chapter 10Using DCOMIN THIS CHAPTER

•  How DCOM extends the COM programming model beyond the boundariesof a single machine

•  The two forms of security employed by DCOM: activation security and callsecurity

•  The various levels of authentication and impersonation supported by DCOM

•  How to use DCOMCNFG to provide both default system-wide andapplication-specific DCOM configuration information

•  The various registry settings used by DCOM

•  The additional APIs added to COM on behalf of DCOM

•  How DCOM can be used to improve both the traditional client/serverapplication architecture and the Web application architecture

DCOM CAN BE defined simply as the extension of the COM programming modelbeyond the boundaries of one physical machine. DCOM allows COM clients tomanipulate COM objects located on physically separate machines through whatamounts to a remote procedure call. In fact, DCOM is based on MS-RPC,Microsoft’s implementation of the Open Software Foundation’s (OSF) DistributedComputing Environment (DCE) Remote Procedure Call (RPC) system. By buildingon top of RPC, DCOM is shielded from the underlying network protocol, and is thuscapable of running a variety of connected and connectionless protocols, such as TCP,UDP, SPX, IPX, Netbios, and NetBEUI, just to name a few. DCOM supports allcombinations of connectivity between in-process and out-of-process clients andremote servers. It is able to support remote in-process servers by loading them into aspecial surrogate process designed specifically for this purpose. The ability to

Page 304: 6935316 DCOM Microsoft Distributed Component Object Model

instantiate or connect to a remote COM object on a different machine raises somevery interesting issues with regard to security, as you’ll see in the next section.

DCOM Security

In the pre-DCOM days, security was not a major concern for most COM developers.All objects were created locally and inherited the user’s security context and accessprivileges. However, with the advent of DCOM, users are able to create objects onremote machines and gain access to the remote machine’s resources. To preventapplications from using remote resources in an irresponsible or malicious manner,DCOM employs two forms of security: activation security and call security.Activation security effectively regulates who is authorized to launch a particularCOM server, and it is totally independent of call security, which regulates who isauthorized to access the various interface functions of a particular COM object. Sincethese two methods of security work independently of each other, it is possible for aclient to have activation authorization and be able to start a particular object’s server,but not have call authorization to actually access the various interface functionsprovided by the object itself.

Before a user can be granted or denied authorization to perform a specific task, he orshe must be authenticated. Authentication is the process of verifying that a user iswho they claim to be. Table 10-1 illustrates the various levels of authenticationsupported by DCOM. It should be noted that, at the time this book is going to print,DCOM on Windows 95 only supports the RPC_C_AUTHN_LEVEL_NONE andRPC_C_AUTHN_LEVEL_CONNECT levels of authentication.

Table 10-1 Authentication Levels Supported by DCOM

Name Level Description

RPC_C_AUTHN_LEVEL_NONE 1No authentication isperformed.

RPC_C_AUTHN_LEVEL_CONNECT 2 Used by connection-orientedprotocols to performauthentication whenever aclient establishes a connectionwith the server. Connectionlessprotocols useRPC_C_AUTHN_LEVEL_PKTinstead.

RPC_C_AUTHN_LEVEL_CALL 3 Used by connection-orientedprotocols to performauthentication whenever theserver receives an RPC call.Connectionless protocols useRPC_C_AUTHN_LEVEL_PKTinstead.

RPC_C_AUTHN_LEVEL_PKT 4 Authentication of all data isperformed on a per-packetbasis.

Page 305: 6935316 DCOM Microsoft Distributed Component Object Model

RPC_C_AUTHN_LEVEL_PKT_INTEGRITY 5 Same as above, with addedvalidation that no data hasbeen modified.

RPC_C_AUTHN_LEVEL_PKT_PRIVACY 6All of the above, plusencryption.

Once a user has been validated, DCOM allows you to decide the impersonation levelor security context of each call to the object, which ultimately determines theresources accessible by the object. For example, if the call is executed under thecaller’s security context, then the object will impersonate the caller and only haveaccess to those system resources accessible by the caller. Table 10-2 illustrates thevarious levels of security impersonation supported by DCOM.

Table 10-2 Impersonation Levels Supported by DCOM

Name Level Description

RPC_C_IMP_LEVEL_ANONYMOUS 1 The client is anonymous to theserver, preventing the serverfrom obtaining identificationinformation or impersonating theclient. (Currently unsupported)

RPC_C_IMPLEVEL_IDENTIFY 2 The server can impersonate theclient to ascertain accessprivileges, but not to actuallyaccess resources.

RPC_C_lMP_LEVEL_IMPERSONATE 3 The server can impersonate theclient to access resources.

RPC_C_IMP_LEVEL_DELEGATE 4 The server can impersonate theclient to not only accessresources, but also to make callsto other servers. (Currentlyunsupported)

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 306: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Ultimately, DCOM gains access to the security services of the underlyingoperating system via the Security Support Provider Interface (SSPI), whichallows DCOM to use a variety of different security systems, such as WindowsNT’s default Windows NT LAN Manager Security Support Provider(NTLMSSP) or DCE RPC’s Kerberos security system.

Microsoft has defined and added several new APIs and interfaces specificallyfor the programmatic control and configuration of DCOM. (See Appendix Afor a complete list.) However, this does nothing to accommodate existingCOM applications. To accommodate existing COM applications, Microsoftdeveloped DCOMCNFG.

Using DCOMCNFG

DCOMCNFG ships as part of DCOM and provides a point-and-click way toconfigure it. DCOMCNFG is ultimately a way for all existing COM clientsand servers to be accessed via DCOM without changing a single line of code!Besides providing a way to configure DCOM for individual COM objects,DCOMCNFG also enables you to provide a system-wide default DCOMconfiguration (see Figure 10-1).

Figure 10-1  DCOMCNFG not only enables you to configure DCOM forindividual COM objects, but also provides a system-wide default DCOMconfiguration.

Page 307: 6935316 DCOM Microsoft Distributed Component Object Model

Providing System-Wide Configuration Information

As Figure 10-1 illustrates, DCOMCNFG has two tabs for providingsystem-wide configuration information: Default Properties and DefaultSecurity. As you can see in Figure 10-2, the Default Properties tab allows youto:

•  Enable/disable DCOM for the entire computer.Manipulates the EnableDCOM value under theHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ole registrykey to control whether or not client applications can launch or connectto any installed servers or classes. A Y means DCOM is enabled; an Nmeans it’s not.

•  Specify the default authentication level.Manipulates the LegacyAuthenticationlevel value under theHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ole registrykey to control which level of authentication to use in the event that anobject doesn’t supply an authentication level of its own. See Table 10-1for legal values.

•  Specify the default impersonation level.Manipulates the Legacy Impersonationlevel value under theHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ole registrykey to control which level of impersonation to use in the event that anobject doesn’t supply an impersonation level of its own. See Table 10-2for legal values.

•  Request additional security for client calls to an object’s AddRefand Release methods. Manipulates theLegacySecureReferences value under theHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ole registrykey to control whether or not security should be applied to client callson an object’s AddRef and Release methods. A Y means addsecurity; an N means don’t.

Figure 10-2  System-wide DCOM configuration information is accessed fromthe Default Properties tab of DCOMCNFG.

The other DCOMCNFG tab useful for providing system-wide configurationinformation is the Default Security tab (see Figure 10-3). The Default Securitytab allows you to:

•  Control which users are allowed access to registered COMobjects. Stores access control information under theDefaultAccessPermission value of theHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ole registry

Page 308: 6935316 DCOM Microsoft Distributed Component Object Model

key for those users authorized to access any of the system’s registeredCOM objects.

•  Control which users are allowed to launch a COM server. Storesaccess control information under the DefaultLaunchPermissionvalue of theHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ole registrykey for those users authorized to launch any of the system’s registeredCOM servers. (NT only)

•  Control which users are allowed to modify OLE classconfiguration information in the system registry. Manipulates thesystem registry’s Access Control List (ACL). (NT only)

Figure 10-3  System-wide DCOM security information is accessed from theDefault Security tab of DCOMCNFG.

Because these values affect the entire machine, your applications typically willnever need to modify these values, which should only be modified by thesystem’s administrators.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 309: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Providing Object-Specific Configuration Information

DCOM’s system-wide default security is only applied if an object fails to provide securityinformation of its own. However, you can also use DCOMCNFG to configure object-specificsecurity as well as other valuable object-specific information. On DCOMCNFG’sApplications tab, you will notice the names of the various COM servers that have registeredthemselves as being available for remote invocation (see Figure 10-1). COM servers exposethemselves for remote invocation by adding the following two entries to the system registry:

HKEY_CLASSES_ROOT APPID {APPID_GUID} RemoteServerName = servername

For the preceding code:

•  APPID_GUID indicates the COM server responsible for creating a particular COMobject.

•  servername is either the DNS or UNC name of the machine where the COM servershould be launched.

Following is the other registry setting required of COM servers that wish to exposethemselves for remote invocation:

HKEY_CLASSES_ROOT CLSID {CLSID_GUID} AppId = {APPID_GUID}

In this case:

•  CLSID_GUID is the COM object’s CLSID.

•  APPID_GUID is the APPID of the remote process responsible for creating the object.

In-process servers that wish to expose themselves for remote invocation must also add the

Page 310: 6935316 DCOM Microsoft Distributed Component Object Model

DLLSurrogate value under their appropriate HKEY_CLASSES_ROOT\APPID\{APPID_GUID} key:

HKEY_CLASSES_ROOT APPID {APPID_GUID} DLLSurrogate = surrogatePath RemoteServerName = servername

In this example, surrogatePath is the name of a custom surrogate, or you can use NULL torequest the default (DllHost.exe) surrogate.

To configure the security and other application-specific information for a specific COMserver, highlight the server in the Applications tab of DCOMCNFG and click on theProperties button. DCOMCNFG will respond by displaying the current settings for theapplication (see Figure 10-4).

Figure 10-4  COM server-specific DCOM settings can be configured by highlighting theserver in the Applications tab of DCOMCNFG and clicking on the Properties button.

As Figure 10-5a, Figure 10-5b, and Figure 10-5c illustrate, DCOMCNFG hasapplication-specific tabs that allow you to configure the execution location, launch, access,and configuration security, as well as the impersonation identity, for a particular COM server.

Page 311: 6935316 DCOM Microsoft Distributed Component Object Model

Figures 10-5a - 10-5c  DCOMCNFG allows you to configure a COM server’s executionlocation, launch, access, and configuration security, as well as impersonation identity.

While DCOMCNFG provides a way for existing applications to use DCOM without writing asingle line of code, these applications may achieve less than optimal performance, as theysimply were not designed to perform in a distributed manner. Due to latencies introduced bythe network, distributed applications must take special precautions to minimize the totalnumber of trips back and forth across the network required to execute a remote procedure andreturn a result, otherwise known as network round-trips (see Figure 10-6).

Figure 10-6  Each remote procedure call executed via DCOM requires one networkround-trip - one leg to the server that actually executes the call and one leg back to the clientwith the results of the call.

To understand the negative impact of too many network round-trips, consider the followingcode snippet, typical of a legacy VB application that retrieves each property from an Accountobject in order to display them to the user:

txtNumber.Text = CStr(localAccount.Number)txtBalance.Text = CStr(localAccount.Balance)txtLimit.Text = CStr(localAccount.Limit)'txtName.Text = localAccount.NametxtSex.Text = localAccount.SextxtAddress.Text = localAccount.AddresstxtCity.Text = localAccount.CitytxtState.Text = localAccount.StatetxtZip.Text = localAccount.ZiptxtPhone.Text = localAccount.PhonechkInService.Value = Abs(localAccount.InService)

If localAccount is a remote object being accessed via DCOM, performance will be lessthan optimal to say the least, as the above code requires eleven trips back and forth across thenetwork, one for each property. Ideally, the Account object referenced in localAccountwould support a single method that could be used to retrieve all of the object’s properties atthe same time (see Figure 10-7):

localAccountObj.GetProperties lNumber, sBalance, lLimit, sName, sSex, sAddress, sCity, sState, sZip, sPhone, bInService

Page 312: 6935316 DCOM Microsoft Distributed Component Object Model

Figure 10-7  You can reduce the number of network round-trips associated with retrievingseveral individual properties by implementing a single method that returns multiple propertyvalues.

Likewise, the localAccount Account object would support a single method, perhapswith optional named parameters, that would allow you to set multiple properties at the sametime:

localAccountObj.SetProperties Balance:=sBalance, Limit:=lLimit, Name:=sName, Sex:=sSex, Address:=sAddress, City:=sCity, State:=sState, Zip:=sZip, Phone:=sPhone, InService:=bInService

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 313: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Another area where performance can be easily improved is in the retrieval of many differentinterface pointers. For example, imagine that you need to retrieve several different interfacepointers from a particular remote object. Each time you call QueryInterface, you arecharged one network round-trip. Among the special modifications that DCOM has added to theunderlying COM system services is the ability to query for multiple interfaces with a singlecall, using the IMultiQI interface. IMultiQI is a special interface that has been added tothe standard COM proxy, which means that you never have to actually implement it! All youhave to do to obtain multiple interfaces is call QueryInterface to obtain an IMultiQIinterface pointer, then call IMultiQI::QueryMultipleInterfaces with theappropriate arguments. How ’bout that! The function prototype forQueryMultipleInterfaces is

HRESULT QueryMultiplelnterfaces(ULONG cMQls, MULTI_QI *pMQls);

where:

•  cMQIs is the number of MULTI_QI structures in the array pointed to by pMQIs.

•  pMQIs is a pointer to an array of MULTI_QI structures.

A MULTI_QI structure is defined as

typedef struct _MULTI_QI { const IID* pIID; IUnknown * pItf; HRESULT hr; }MULTI_QI ;

where:

•  pIID is the IId of the desired interface.

•  pItf is the pointer that will receive the actual interface pointer. Must be NULL onentry.

•  hr is the result of the remote QueryInterface call used to actually retrieve theinterface pointer. Must be zero on entry.

Page 314: 6935316 DCOM Microsoft Distributed Component Object Model

Upon the successful completion of QueryMultipleInterfaces, each MULTI_QIstructure in the array will contain an interface pointer value (pItf) and a return value (hr) thatcan be used to determine whether or not pItf contains a valid interface pointer.

Besides IMultiQI, DCOM provides several additional APIs that enable you to use DCOMprogrammatically, as opposed to using DCOMCNFG or mucking with the system registry. Youcan find a complete list of these new APIs in the DCOM section of Appendix A.

Now that you have more insight into what DCOM is and how it can and should be used, let’ssee how we can use DCOM to improve the architectures of both the client/server and Webversions of the order-entry application.

Improving the Client/Server Order-Entry Application

In Chapter 7, we built eight interoperable COM objects as part of the cohesive OrderEntryobject hierarchy. Then, in Chapter 8, we used that object hierarchy to develop a client/serverorder-entry application. The client/server architecture afforded us the ability to use client-sidesystem resources, such as the ODBC API, as well as server-side resources, such as theODBC-compliant DBMS itself. However, we also discovered in Chapter 8 that the client/serverversion of the order-entry application has several significant limitations:

•  It is time-consuming to properly install and configure client-side resources, such asODBC, on each client machine.

•  It is time-consuming to deploy the client side of the application to each client machine.

•  It is difficult to update and synchronize a single version of the client/server applicationacross every client machine.

While using DCOM won’t make it any easier to deploy the client side of the application to eachclient machine, and it won’t make it any easier to synchronize a single version of theclient/server application across every client machine, it can eliminate the need to install andconfigure ODBC on each client machine. If you recall, the OrderEntry object hierarchy iscomprised of two different types of objects: entry objects, which are used to maintain fairlystatic information; and collection objects, which are used to interact with the underlying ODBCdata source. It is the collection objects that ultimately require each client machine to haveODBC properly installed and configured. However, by moving the collection objects to theserver, and accessing them remotely from the client using DCOM, we eliminate the need toinstall and configure ODBC on each client machine. We then deploy the entry objects to eachclient machine as part of the order-entry weblication itself. Finally, we add theGetProperties and SetProperties methods described earlier to each entry object tohelp minimize any unnecessary network round-trips that may occur whenever a collectionobject needs to access multiple properties from a remote entry object. Following is the IDLdefinition for the Account object’s new GetProperties and SetProperties methods:

[helpstring("Sets multiple properties simultaneously.")]HRESULT SetProperties([in] long lNumber, [in] float fbalance, [in] long lLimit, [in] BSTR bstrName, [in] BSTR bstrSex, [in] BSTR bstrAddress, [in] BSTR bstrCity, [in] BSTR bstrState, [in] BSTR bstrZip, [in] BSTR bstrPhone, [in] VARIANT_BOOL vbInService);[helpstring("Returns multiple properties simultaneously.")]HRESULT GetProperties([out] long *lRetNumber, [out] float *fRetBalance, [out] long *lRetLimit, [out] BSTR *bstrRetName, [out] BSTR *bstrRetSex, [out] BSTR *bstrRetAddress, [out] BSTR *bstrRetCity,

Page 315: 6935316 DCOM Microsoft Distributed Component Object Model

[out] BSTR *bstrRetState, [out] BSTR *bstrRetZip, [out] BSTR *bstrRetPhone, [out] VARIANT BOOL *vbRetInService);

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 316: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Listing 10-1 shows the implementation of the Account object’s new GetProperties andSetProperties methods, which are used to minimize the amount of network round-tripsnecessary to get or set multiple properties of a remote Account object. As you look at theimplementation, notice how both methods delegate to the other Get/Set member functions.

Listing 10-1. Implementation of the Account object’s GetProperties and SetProperties methods.

////SetProperties//STDMETHODIMP CAccount::SetProperties(long lNumber, float fBalance, long llimit, BSTR bstrName, BSTR bstrSex, BSTR bstr Address, BSTR bstrCity, BSTR bstrState, BSTR bstrZip, BSTR bstrPhone, VARIANT_BOOL vbInService){ HRESULT hr = NOERROR;

hr = put_Number(lNumber); if (FAILED(hr)) return hr: hr = put_Balance(fBalance): if (FAILED(hr)) return hr: hr = put_Limit(lLimit): if (FAILED(hr)) return hr; hr = put_Name(bstrName); if (FAILED(hr)) return hr; hr = put_Sex(bstrSex); if (FAILED(hr)) return hr; hr = put_Address(bstrAddress); if (FAILED(hr)) return hr;

Page 317: 6935316 DCOM Microsoft Distributed Component Object Model

hr = put_City(bstrCity); if (FAILED(hr)) return hr; hr = put_State(bstrState); if (FAILED(hr)) return hr; hr = put_Zip(bstrZip); if (FAILED(hr)) return hr; hr = put_Phone(bstrPhone); if (FAILED(hr)) return hr; hr = put_InService(vbInService);

return hr;}//SetProperties

////GetProperties//STDMETHODIMP CAccount::GetProperties(long *lRetNumber, float *fRetBalance, long *lRetLimit, BSTR *bstrRetName, BSTR *bstrRetSex, BSTR *bstrRetAddress, BSTR *bstrRetCity, BSTR *bstrRetState, BSTR *bstrRetZip, BSTR *bstrRetPhone, VARIANT_BOOL *vbRetInService){ HRESULT hr = NOERROR;

hr = get_Number(lRetNumber); if (FAILED(hr)) return hr; hr = get_Balance(fRetBalance); if (FAILED(hr)) return hr; hr get_Limit(lRetLimit); if (FAILED(hr)) return hr; hr get_Name(bstrRetName); if (FAILED(hr)) return hr; hr = get_Sex(bstrRetSex); if (FAILED(hr)) return hr; hr = get_Address(bstrRetAddress): if (FAILED(hr)) return hr; hr = get_City(bstrRetCity); if (FAILED(hr)) return hr; hr = get_State(bstrRetState); if (FAILED(hr)) return hr; hr = get_Zip(bstrRetZip); if (FAILED(hr))

Page 318: 6935316 DCOM Microsoft Distributed Component Object Model

return hr; hr = get_Phone(bstrRetPhone); if (FAILED(hr)) return hr; hr = get_InService(vbRetInService);

return hr;}//GetProperties

When the order-entry application is deployed, we add the appropriate DCOM settings to thesystem registry such that the execution location of each collection object points to the appropriateremote server machine. The remote server machine is responsible for having the new serverportion of the OrderEntry object hierarchy, as well as ODBC, properly installed and configured.Currently, whenever the client/server order-entry application retrieves an entry object using theItem property of a collection object, the application receives an interface pointer to an entry object.However, now that the entry objects reside on a different machine than the collection objects, atbest the collection objects can only return a remote reference to the client, which would force theclient to make unnecessary network round-trips whenever it needed to access the object. The othersolution is for the client to create a local blank object, then pass it as a remote reference to theItem property. The Item property could then use the SetProperties method of the remoteobject to define all of the object’s properties in a single network round-trip, and the client wouldmaintain a local reference to the object. Because the Item property no longer returns an interfacepointer, there is no need for it to be a property, so we’ll convert it to a method. Following is thenew IDL definition for the Item method:

[helpstring("Returns an Account from the database.")] HRESULT Item([in] IAccount *pIAccount, [in, optional, defaultvalue(-1)] VARIANT Key);

As a result, instead of writing the following code to retrieve information for a specific account:

Dim localAccount As Object

Set localAccount = g_AccountsCol.Item(lNumber)

.

.//Manipulate retrieved object

.

the client will use code like this:

Dim localAccount As Object

Set localAccount = CreateObject("OrderEntryClient.Account")g_AccountsCol.Item localAccount, lNumber..//Manipulate retrieved object.

Previous Table of Contents Next

[an error occurred while processing this directive]

Page 319: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 320: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Listing 10-2 shows the new implementation of the Accounts object’s Item method. Notice howthe remote object’s SetProperties method is used to minimize the total number of networkround-trips.

Listing 10-2. The new implementation of the Accounts object’s Item method.

////Item//STDMETHODIMP CAccounts::Item(IAccount *pIAccount, VARIANT Key){ HRESULT hr = NOERROR; RETCODE retcode;

long lNumber; float flBalance; long lLimit; wchar_t wszName[NAME_LEN]; char szName[NAME_LEN]; wchar_t wszSex[SEX_LEN]; char szSex[SEX_LEN]; wchar_t wszAddress[ADDRESS_LEN]; char szAddress[ADDRESS_LEN]; wchar_t wszCity[CITY_LEN]; char szCity[CITY_LEN]; wchar_t wszState[STATE_LEN]; char szState[STATE_LEN]; wchar_t wszZip[ZIP_LEN]; char szZip[ZIP_LEN]; wchar_t wszPhone[PHONE_LEN]; char szPhone[PHONE_LEN]; VARIANT-BOOL vbInService;

SDWORD cbNumber = 0; SDWORD cbBalance = 0; SDWORD cbLimit = 0;

Page 321: 6935316 DCOM Microsoft Distributed Component Object Model

SDWORD cbName SQL_NTS; SDWORD cbSex = SQL_NTS; SDWORD cbAddress = SQL_NTS;

SDWORD cbCity = SQL_NTS; SDWORD cbState = SQL_NTS; SDWORD cbZip = SQL_NTS; SDWORD cbPhone = SQL_NTS; SDWORD cbInService = O;

//coerce the incoming VARIANT into a long VariantChangeType(&Key, &Key, VARIANT-NOVALUEPROP, VT_I4); //retrieve the converted long value lNumber = V_I4(&Key); //determine if the default is being used //which would signal us to retrieve the data pointed to by //the current record pointer if (-1 == lNumber) { //only return an object if not BOF or EOF if (m_bBOF || m_bEOF) return NOERROR;

//the user is trying to retrieve the current Account //copy the data from the appropriate member functions //used to store the values for the current record //Number lNumber = m_lNavNumber; //Balance flBalance = m_flNavBalance; //Limit lLimit = m_lNavLimit //Name memcpy(szName, m_szNavName, NAME_LEN); //Sex memcpy(szSex, m_szNavSex, SEX_LEN); //Address memcpy(szAddress, m_szNavAddress, ADDRESS_LEN); //City memcpy(szCity, m_szNavCity, CITY_LEN); //State memcpy(szState, m_szNavState, STATE_LEN); //Zip memcpy(szZip, m_szNavZip, ZIP_LEN); //Phone memcpy(szPhone, m_szNavPhone, PHONE_LEN); //InService vblnservice = m_vbNavInService; } else { //the user is trying to retrieve a specific account

//Setup parameter transfer buffers //Number SQLBindParameter(m_hstmtQuery, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, O, O, &lNumber, O,

Page 322: 6935316 DCOM Microsoft Distributed Component Object Model

&cbNumber); //execute the prepared statement retcode = SQLExecute(m_hstmtQuery); //release the bound parameter buffers SQLFreeStmt(m_hstmtQuery, SQL_RESET_PARAMS); if (!SQL_SUCCEEDED(retcode)) return E_UNEXPECTED;

//Set up transfer buffers //Number SQLBindCol(m_hstmtQuery, 1, SQL_C_SLONG, &lNumber, 0, &cbNumber); //Balance SQLBindCol(m_hstmtQuery, 2, SQL_C_FLOAT, &flBalance, 0, &cbBalance); //Limit SQLBindCol(m_hstmtQuery, 3, SQL_C_SLONG, &lLimit, 0, &cbLimit); //Name SQLBindCol(m_hstmtQuery, 4, SQL_C_CHAR, szName, NAME_LEN, &cbName); //Sex SQLBindCol(m_hstmtQuery, 5, SQL_C_CHAR, szSex, SEX_LEN, &cbSex); //Address SQLBindCol(m_hstmtQuery, 6, SQL_C_CHAR, szAddress, ADDRESS_LEN, &cbAddress); //City SQLBindCol(m_hstmtQuery, 7, SQL_C_CHAR, szCity, CITY_LEN, &cbCity); //State SQLBindCol(m_hstmtQuery, 8, SQL_C_CHAR, szState, STATE_LEN, &cbState); //Zip SQLBindCol(m_hstmtQuery, 9, SQL_C_CHAR, szZip, ZIP_LEN, &cbZip) //Phone SQLBindCol(m_hstmtQuery, 10, SQL_C_CHAR, szPhone, PHONE_LEN, &cbPhone); //InService SQLBindCol(m_hstmtQuery, 11, SQL_C_SSHORT, &vbInService, 0, &cbInService);

//get the next row retcode = SQLFetch(m_hstmtQuery); //unbind any columns SQLFreeStmt(m_hstmtQuery, SQL_UNBIND); //close the recordset SQLFreeStmt(m_hstmtQuery, SQL-CLOSE); if (SQL_NO_DATA == retcode) return NOERROR; if (!SQL_SUCCEEDED(retcode)) return E_UNEXPECTED; } //Name

Page 323: 6935316 DCOM Microsoft Distributed Component Object Model

//convert the string to a unicode string mbstowcs(wszName, szName, NAME_LEN); //Sex //convert the string to a unicode string mbstowcs(wszSex, szSex, SEX_LEN); //Address //convert the string to a unicode string mbstowcs(wszAaddress, szAddress, ADDRESS_LEN); //City //convert the string to a unicode string mbstowcs(wszCity, szCity, CITY_LEN); //State //convert the string to a unicode string mbstowcs(wszState, szState, STATE_LEN); //Zip //convert the string to a unicode string mbstowcs(wszZip, szZip, ZIP_LEN); //Phone //convert the string to a unicode string mbstowcs(wszPhone, szPhone, PHONE_LEN); //set the properties of the return object hr = pIAccount->SetProperties(lNumber, flBalance, lLimit, wszName, wszSex, wszAddress, wszCity, wszState, wszZip wszPhone, vbInservice);

return hr;}//Item

Improving the Web Order-Entry Application

In Chapter 9 we developed a Web version of the order-entry application, which was made up ofboth statically generated HTML pages and dynamically generated HTML pages. The dynamicallygenerated HTML pages are created using Microsoft’s Active Server Pages (ASP). Each ASP ismade up of a combination of HTML and application script logic. While ASP supports a variety ofscripting languages, the order-entry weblication was developed using Visual Basic Scripting Edition(VBScript). While the Web version of the order-entry application manages to overcome thelimitations of the client/server version, it suffers from several significant limitations of its own:

•  It is limited to server-side resources only.

•  Developers must use an entirely new, unfamiliar programming model to developweblications.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 324: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

Ideally, we would like to develop the Web version of the order-entry application using theCOM programming model, in which the application’s user interface is created from variousvisual objects that execute code in response to various events supported by the individualvisual objects. Microsoft Internet Explorer (IE) 3.0 provides us with exactly this level ofsupport, and even allows us to manipulate the various objects with client-side scripts.Client-side scripts that are written to run within the browser can be included as part of theHTML file itself, enclosed within <%...%> tags; similar to the way in which server-sidescripts are included as part of an ASP file. When the browser loads an HTML file, it processesthe file line-by-line from the top to the bottom, locating and executing the various lines ofscript enclosed within the <%...%> tags. In addition, IE 3.0 supports many differentclient-side scripting languages, including VBScript, JScript, Perl, and Rexx. IE 3.0 is includedon the companion CD-ROM if you don’t already have IE 3.0 or greater installed on yoursystem.

We will use VBScript because of its close resemblance to VB, which we used to create theclient/server version of the order-entry application. By combining IE 3.0’s ability to executeclient-side scripts and access client-side system resources using the COM programmingmodel with DCOM’s ability to remotely access server-side resources in a secure manner, weget the best of both worlds. We get the benefits of the weblication model - ease of installation,configuration, and support - plus the benefits of the client/server model: access to bothclient-side and server-side system resources! Let’s look at how we can apply this new hybridapplication architecture to the Web version of our order-entry application.

Currently, the Web version of the order-entry application uses ASP extensively as a way toaccess server-side system resources. However, this means that the client must issue requestsfor new HTML pages in order to communicate with ASP. For example, the user interfacegenerated by NewAccount.htm relies on SaveAccountLogic.asp to provide the processinglogic necessary to actually add a new customer account to the underlying data source (seeFigure 10-8).

Page 325: 6935316 DCOM Microsoft Distributed Component Object Model

Figure 10-8  Currently, the user interface presented by NewAccount.htm relies onSaveAccountLogic.asp to actually add a new customer account to the underlying data source.

But now we can use our new hybrid application architecture to embed client-side VBScriptdirectly within NewAccount.htm. The new hybrid architecture allows the user interfacepresented by NewAccount.htm to manipulate the Account and Accounts OrderEntryobjects directly, in the exact same manner as the client/server version of the order-entryapplication, to add a new customer account to the data source all by itself (see Figure 10-9).

Figure 10-9  The combination of client-side scripting and DCOM allows the user interfacepresented by NewAccount.htm to manipulate the Account and Accounts objects directly toadd a new customer account to the underlying data source.

The VBScript code used by NewAccount.htm to add a new customer account to the datasource can be seen in Listing 10-3, and is remarkably similar to the VBScript code used bythe AccountInfoForm form in the client/server version, which can be seen in Listing10-4.

Listing 10-3. VBScript code from the NewAccount.htm page responsible for actually addingnew customer accounts.

<script language="VBScript"><!-Sub cmdOK_onClick() Dim localAccountsCol Dim localAccount

Set localAccountsCol = CreateObject("DCOMOrderEntryServer.Accounts") Set localAccount = CreateObject ("DCOMOrderEntryClient.Account")

localAccount.Balance = CLng(AddForm.txtBalance.Value)

Page 326: 6935316 DCOM Microsoft Distributed Component Object Model

localAccount.Limit = CLng(AddForm.txtLimit.Value) ' localAccount.Name = Trim(AddForm.txtName.Value) localAccount.Sex = Trim(AddForm.txtSex.Value) localAccount.Address = Trim(AddForm.txtAddress.Value) localAccount.City = Trim(AddForm.txtCity.Value) localAccount.State = Trim(AddForm.txtState.Value) localAccount.Zip = Trim(AddForm.txtZip.Value) localAccount.Phone = Trim(AddForm.txtPhone.Value) localAccount.InService = (AddForm.chkInService.Checked = 1) 'add to the data source localAccountsCol.Add localAccount

Set localAccount = Nothing Set localAccountsCol = Nothing Window.location.href = "SuccessMessage.htm"End Sub-></script>

Listing 10-4. VBScript code from the AccountInfoForm of the client/server version of theorder-entry application responsible for actually adding new customer accounts.

Private Sub cmdOK_Click() If f_ADDING Then Set f_localAccount = CreateObject ("DCOMOrderEntryClient.Account") End If f_localAccount.Balance = CLng(txtBalance.Text) f_localAccount.Limit = CLng(txtLimit.Text) ' f_localAccount.Name = Trim$(txtName.Text) f_localAccount.Sex = Trim$(txtSex.Text) f_localAccount.Address = Trim$(txtAddress.Text) f_localAccount.City = Trim$(txtCity.Text) f_localAccount.State = Trim$(txtState.Text) f_localAccount.Zip = Trim$(txtZip.Text) f_localAccount.Phone = Trim$(txtPhone.Text) f_localAccount.InService = (chkInService.Value = 1) If f_ADDING Then 'add to the data source g_AccountsCol.Add f_localAccount Else 'update the current product g_AccountsCol.Update f_localaccount End If Unload MeEnd Sub

Previous Table of Contents Next

Page 327: 6935316 DCOM Microsoft Distributed Component Object Model

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 328: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Previous Table of Contents Next

While the hybrid architecture allows us to access server-side resources using DCOM, there areplaces where ASP is still the best solution. For example, consider how the ModifyAccount.aspfile is used to retrieve information about a specific customer account and display it on anHTML page. ASP provides a great facility for the entire page, complete with customer accountinformation, to be generated on the server and shipped to the client for display. However, oncethe account information has been edited via the ModifyAccount.asp user interface, theAccount and Accounts OrderEntry objects are manipulated using client-side VBScript tosave the edited contents to the data source, using the code in Listing 10-5.

Listing 10-5. VBScript code from the ModifyAccount.asp page responsible for actually savingmodified customer account information to the data source.

<script language="VBScript"><-!Sub cmdOK_onClick() Dim localAccountsCol Dim localAccount

Set localAccountsCol = CreateObject ("DCOMOrderEntryServer.Accounts") Set localAccount = CreateObject ("DCOMOrderEntryClient.Account")

localAccount.Number = CLng(EditForm.AccountNumber.value) localAccount.Balance = CLng(EditForm.txtBalance.Value) localAccount.Limit = CLng(EditForm.txtLimit.Value) ' localAccount.Name = Trim(EditForm.txtName.Value) localAccount.Sex = Trim(EditForm.txtSex.Value) localAccount.Address = Trim(FditForm.txtAddress.Value) localAccount.City = Trim(EditForm.txtCity.Value) localAccount.State = Trim(EditForm.txtState.Value) localAccount.Zip = Trim(EditForm.txtZip.Value) localAccount.Phone = Trim(EditForm.txtPhone.Value)

Page 329: 6935316 DCOM Microsoft Distributed Component Object Model

localAccount.InService = (EditForm.chkInService.Checked = 1) 'update the data source localAccountsCol.Update localAccount

Set localAccount = Nothing Set localAccountsCol = Nothing 'notify the user that the operation was successful Window.location.href = "../SuccessMessage.htm"End Sub-></script>

IE 3.0’s support for VBScript, combined with its intrinsic support for COM objects, enables usto create a Web DCOM version of the order-entry application using the same basicarchitectural design that we used earlier to create the client/server DCOM version of theapplication. In addition, because the DCOM Web version of the application is stillHTML-based, we can continue to use the Web as a way to distribute the application to eachclient’s machine.

The ideal application architecture seems to be a combination of DCOM and a hybridclient/server/weblication solution. What we want to do is use HTML to create the application’suser interface; program the HTML-generated UI in such a way that we can create the variousobjects of the OrderEntry object hierarchy; and manipulate the objects using client-side code.This allows us to:

•  Leverage the weblication architecture’s distribution model of HTML over HTTP toensure on-demand delivery of the latest version of the order-entry application to eachclient desktop

•  Eliminate the need to install and configure ODBC on each client machine

•  Continue to develop applications using the familiar client/server COM programmingmodel

Summary

In this chapter, you learned:

•  That DCOM extends the COM programming model beyond the physical machineboundaries by using MS-RPC, Microsoft’s implementation of the OSF DCE RPCsystem.

•  That DCOM employs two independently operating forms of security: activationsecurity and call security.

•  That activation security regulates who is authorized to launch a particular COM server.

•  That call security regulates who is authorized to access the various interface functionsdefined by a particular COM object.

•  That DCOM offers several levels of user authentication and impersonation to suit awide variety of needs.

•  How to use DCOMCNFG to provide both default system-wide andapplication-specific DCOM configuration information.

•  That DCOM relies on the HKEY_CLASSES_ROOT\APPID\ registry key and theAPPID named value to locate and launch remote COM servers.

•  That DCOM can be used programmatically via the additional APIs that have beenadded to COM on DCOM’s behalf.

Page 330: 6935316 DCOM Microsoft Distributed Component Object Model

•  How DCOM can be used to improve the architectures of both the traditionalclient/server application and the Web application.

Previous Table of Contents Next

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 331: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Table of Contents

Appendix AFrequently Used Interfaces and APIs

THE FOLLOWING IS a list of frequently used interfaces and APIs. For furtherinformation, consult Microsoft Developer Network (MSDN) or the on-linedocumentation provided by your development environment.

InterfacesIAdviseSink IBindCtxICatInformation ICatRegisterIClassActivator IClassFactoryIClassFactory2 IConnectionPointIConnectionPointContainer IDataAdviseHolderIDataObject IEnumXXXXIEnumConnectionPoints IEnumConnectionsIEnumFORMATETC IEnumMonikerIEnumSTATDATA IEnumSTATPROPSETSTGIEnumSTATSTG IEnumStringIEnumUnknown IErrorLogIExternalConnection ILockBytesIMalloc IMallocSpyIMarshal IMessageFilterIMoniker IOleItemContainerIOXIDResolver IParseDisplayNameIPersist IPersistFile

Page 332: 6935316 DCOM Microsoft Distributed Component Object Model

IPersistMemory IPersistMonikerIPersistPropertyBag IPersistStorageIPersistStream IPersistStreamInitIPropertyBag IPropertySetStorageIPropertyStorage IProvideClassInfoIProvideClassInfo2 IRootStorageIROTData IRunnableObjectIRunningObjectTable IStdMarshalInfoIStorage IStreamIUnknown

DCOMIClientSecurity IMultiQIIServerSecurity

AutomationICreateErrorInfo ICreateTypeInfoICreateTypeInfo2 ICreateTypeLibICreateTypeLib2 IErrorInfoIDispatch IProvideClassInfoIProvideClassInfo2 ISupportErrorInfoITypeComp ITypeInfoITypeInfo2 ITypeLibITypeLib2

APIs

GeneralBindMoniker CLSIDFromProgIDCLSIDFromString CoAddRefServerProcessCoCreateFreeThreadedMarshaler CoCreateGuidCoCreateInstance CoDisconnectObjectCoDosDateTimeToFileTime CoFileTimeNowCoFileTimeToDosDateTime CoFreeAllLibrariesCoFreeLibrary CoFreeUnusedLibrariesCoGetClassObject CoGetCurrentProcessCoGetInstanceFromFile CoGetInstanceFormIStorageCoGetInterfaceAndReleaseStream CoGetMallocCoGetMarshalSizeMax CoGetPSClsidCoGetStandardMarshal CoGetTreatAsClassCoInitialize CoInitializeExCoIsHandlerConnected CoLoadLibrary

Page 333: 6935316 DCOM Microsoft Distributed Component Object Model

CoLockObjectExternal CoMarshalIntefaceCoMarshalInterThreadInterfaceInStreamCoRegisterClassObject CoRegisterMallocSpyCoRegisterMessageFilter CoRegisterPSClsidCoReleaseMarshalData CoReleaseServerProcessCoResumeClassObjects CoRevokeClassObjectCoRevokeMallocSpy CoSuspendClassObjectsCoTaskMemAlloc CoTaskMemFreeCoTaskMemRealloc CoTreatAsClassCoUninitialize CoUnmarshalInterfaceCreateAntiMoniker CreateBindCtxCreateClassMoniker CreateDataAdviseHolderCreateGenericComposite CreateILockBytesOnHGlobalCreateItemMoniker CreatePointerMonikerCreateStreamOnHGlobal DllCanUnloadNowDllGetClassObject DllRegisterServerDllUnregisterServer FreePropVariantArrayGetClassFile GetConvertStgGetHGlobalFromILockBytes GetHGlobalFromStreamGetRunningObjectTable IIDFromStringIsEqualGUID IsEqualCLSIDIsEqualIID MkParseDisplayNameMonikerCommonPrefixWith MonikerRelativePathToProgIDFromCLSID PropStgNameToFmtIdPropVariantClear PropVariantCopyReadClassStg ReadClassStmReadFmtUserTypeStg ReleaseStgMediumSetConvertStg StgCreateDocfileStgCreateDocfileOnILockBytes StgCreatePropSetStgStgCreatePropStg StgIsStorageFileStgIsStorageILockBytes StgOpenPropStgStgOpenStorage StgOpenStorageOnILockBytesStgSetTimes StringFromCLSIDStringFromGUID2 StringFromIIDWriteClassStg WriteClassStmWriteFmtUserTypeStg

DCOMCoCopyProxy CoCreateInstanceExCoGetCallContext CoImpersonateClientCoInitializeSecurity CoQueryAuthenticationServicesCoQueryClientBlanket CoQueryProxyBlanket

Page 334: 6935316 DCOM Microsoft Distributed Component Object Model

CoRevertToSelf CoSetProxyBlanket

Automation

GENERAL

CreateErrorInfo CreateStdDispatchDispGetIDsOfNames DispGetParamDispInvoke GetActiveObjectGetAltMonthNames GetErrorInfoRegisterActiveObject RevokeActiveObjectSetErrorInfo

TYPE LIBRARY

CreateDispTypeInfo CreateTypeLibLHashValOfName LHashValOfNameSysLoadRegTypeLib LoadTypeLibLoadTypeLibEx RegisterTypeLibUnRegisterTypeLib

BSTR

BstrFromVector SysAllocStringSysAllocStringByteLen SysAllocStringLenSysFreeString SysReAllocStringSysReAllocStringLen SysStringByteLenSysStringLen VectorFromBstr

SAFEARRAY

SafeArrayAccessData SafeArrayAllocDataSafeArrayAllocDescriptor SafeArrayCopySafeArrayCopyData SafeArrayCreateSafeArrayCreateVector SafeArrayDestroySafeArrayDestroyData SafeArrayDestroyDescriptorSafeArrayGetDim SafeArrayGetElementSafeArrayGetElemsize SafeArrayGetLBoundSafeArrayGetUBound SafeArrayLockSafeArrayPtrOfIndex SafeArrayPutElementSafeArrayRedim SafeArrayUnaccessDataSafeArrayUnlock

VARIANT

DosDateTimeToVariantTime SystemTimeToVariantTimeVarDateFromUdate VariantChangeTypeVariantChangeTypeEx VariantClearVariantCopy VariantCopyInd

Page 335: 6935316 DCOM Microsoft Distributed Component Object Model

VariantInit VariantTimeToDosDateTimeVariantTimeToSystemTime VarNumFromParseNumVarParseNumFromStr VarUdateFromDate

National LanguageCompareString LCMapStringGetLocaleInfo GetStringTypeGetSystemDefaultLangID GetSystemDefaultLCIDGetUserDefaultLangID GetUserDefaultLCID

RegistryRegCloseKey RegConnectRegistryRegCreateKey RegCreateKeyExRegDeleteKey RegDeleteValueRegEnumKey RegEnumKeyExRegEnumValue RegFlushKeyRegGetKeySecurity RegLoadKeyRegNotifyChangeKeyValue RegOpenKeyRegOpenKeyEx RegQueryInfoKeyRegQueryMultipleValues RegQueryValueRegQueryValueEx RegReplaceKeyRegRestoreKey RegSaveKeyRegSetKeySecurity RegSetValueRegSetValueEx RegUnLoadKey

Table of Contents

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 336: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Table of Contents

Appendix BResources for COM Developers

BESIDES THE COM Specification that is included on the companionCD-ROM and the Microsoft Win32 Software Development Kit (SDK)documentation that is typically included as part of your compiler’sdocumentation, the following is a list of essential resources containingadditional COM programming information.

BooksKraig Brockschmidt, Inside OLE (Microsoft Press); ISBN1-55615-618-9

OLE 2 Programmer’s Reference, Volume 1 (Microsoft Press); ISBN1-55615-628-6

OLE 2 Programmer’s Reference, Volume 2 (Microsoft Press); ISBN1-55615-629-4

OLE Automation Programmer’s Reference (Microsoft Press); ISBN1-55615-851-3

The Microsoft WebsiteWeb Site URLThe Microsoft OLE Web Site http://www.microsoft.com/oledev/

The Microsoft Developer Network http://www.microsoft.com/msdn/

The Microsoft Knowledge Base http://www.microsoft.com/kb/

Microsoft TechNet World Wide WebEdition

http://www.microsoft.com/technet/

Page 337: 6935316 DCOM Microsoft Distributed Component Object Model

Site Builder Network http://www.microsoft.com/sitebuilder/

Developer Network News http://www.microsoft.com/devnews/

Table of Contents

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 338: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Table of Contents

Appendix CWhat’s on the CD-ROM

THE COMPANION CD-ROM contains:

•  Sample source code from the examples presented throughout the book

•  The COM Specification

•  DCOM for Windows 95

Sample Source Code

The source code files on the CD-ROM are arranged according to chapter andcan be copied directly to your hard drive. The C++ samples in this book werebuilt using Microsoft Visual C++5.0 and can be built using either an MSVC++5.0 project file or a standard makefile, both of which are included on theCD-ROM. Each project has four folders: Debug, Release, DebugU, andReleaseU, supporting both ANSI and Unicode debug and release builds. TheVisual Basic samples in this book were built using Microsoft Visual Basic 4.0.As you investigate the various source code files, keep in mind that eachchapter builds on information covered in previous chapters, and as a result,you may need to refer to source code files from previous chapters.

The COM Specification

The COM Specification provides in-depth information about the ComponentObject Model and is a very valuable resource. On the CD-ROM, you will finda folder named com_spec. This folder contains two additional folders: doc andrtf, which contain versions of the October 24, 1995 draft of the ComponentObject Model Specification. Printed out, the COM Specification is roughly270 pages. However, to facilitate on-line reading and printing, the document

Page 339: 6935316 DCOM Microsoft Distributed Component Object Model

exists in multiple formats. The doe folder contains each chapter of thespecification in a separate Word 6.0 document. The filenames for eachdocument reflect the contents of that chapter (e.g. CH08 Security.doc). TheThe COM Specification.DOC file is a Word 6.0 Master Document that holdsall of the individual chapters together as well as providing the title page, tableof contents, and appendix. The rtf folder contains a single Rich Text Format(RTF) file, COM_Spec.RTF.

DCOM for Windows 95

While DCOM is included as part of Windows NT 4.0, it is not included as partof Windows 95. However, if you look in the DCOM95 folder on thecompanion CD-ROM, you will find two self-extracting executables:DCOM95.EXE and DCM95CFG.EXE. DCOM95.EXE will install version1.0 of DCOM for Windows 95, and DCM95CFG.EXE will install theWindows 95 version of DCOMCNFG.EXE. However, if you are currentlyrunning Internet Explorer 4.0 (IE 4.0) or Windows 98, you should not installeither of these two executables, as both of these products ship with morecurrent builds of DCOM. While there are several differences between DCOMon Windows 95 and DCOM on Windows NT, one of the biggest is that bydefault, DCOM for Windows 95 doesn’t allow COM servers to act as DCOMservers. Thank goodness this functionality is controlled by theEnableRemoteConnect named-value under theHKEY_LOCAL_MACHINE\Software\Microsoft\ OLE registry key. Thisvalue is set to “N” by default, which means that the machine is allowed toconnect to remote objects, but cannot act as a server. Setting this value to “Y”allows remote clients to connect to running objects. Notice I said runningobject; unlike Windows NT, DCOM for Windows 95 only allows clients toconnect to running COM servers. DCOM for Windows 95 will not launch aCOM server. Additional information regarding DCOM for Windows 95including the latest builds can be obtained from the Microsoft Web site athttp://www.microsoft.com/oledev/olemkt/oledcom/dcom95.htm.

Table of Contents

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 340: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Table of Contents

Appendixes

Quick ReferenceODL Language Features in MIDL

THE MICROSOFT INTERFACE Definition Language (MIDL) compiler and the MkTypLib utility are bothcapable of compiling scripts that are written in the Object Description Language (ODL). Since Microsofthas expanded the Interface Definition Language (IDL) to contain the complete ODL syntax, you shoulduse the MIDL compiler in preference to MkTypLib, as MkTypLib is being phased out and will no longerbe supported.

For more information about the MIDL compiler, refer to the MIDL Programmer’s Guide and Reference inthe Win32 Software Development Kit (SDK).

Contents of a Type Library

Type libraries are compound document files (.tlb files) that include information about types and objectsexposed by an ActiveX application. A type library can contain any of the following:

•  Information about data types, such as aliases, enumerations, structures, or unions.

•  Descriptions of one or more objects, such as a module, interface, IDispatch interface(dispinterface), or COM object class (coclass). Each of these descriptions is commonly referred toas a typeinfo.

•  References to type descriptions from other type libraries.

By including the type library with a product, the information about the objects in the library can be madeavailable to the users of the applications and programming tools. Type libraries can be shipped in any ofthe following forms:

•  A resource in a dynamic link library (DLL). This resource should have the type TypeLib and aninteger identifier. It must be declared in the resource (.rc) file as follows:

1 typelib mylib1.tlb2 typelib mylib2.tlb

There can be multiple type library resources in a DLL. Application developers should use the resourcecompiler to add the .tlb file to their own DLL. A DLL with one or more type library resources typicallyhas the file extension .olb (object library).

Page 341: 6935316 DCOM Microsoft Distributed Component Object Model

•  A resource in an .exe file. The file can contain multiple type libraries.

•  A stand-alone binary file. The .tlb (type library) file output by the MkTypLib utility is a binaryfile.

Object browsers, compilers, and similar tools access type libraries through the interfaces ITypeLib,ITypeLib2, ITypeInfo, ITypeInfo2 and ITypeComp. Type library tools (such as MkTypLib)can be created using the interfaces ICreateTypeLib, ICreateTypeLib2, ICreateTypeInfo,and ICreateTypeInfo2.

Using MIDL and MkTypLib

Files parsed by MkTypLib are .odl files. Files parsed by MIDL are referred to as .idl files, although theycan contain the same syntax elements as .odl files. The MIDL compiler and the MkTypLib utility bothcompile scripts written in ODL. However, MkTypLib is obsolete, and you should use the MIDL compilerinstead. The following sections describe the differences and special considerations for using MkTypLiband MIDL to create type libraries.

Adding ODL to an IDL Definition

The .odl files provide object definitions that are added to the type descriptions in a type library. TheMkTypLib utility parses files written in ODL syntax, generates the type libraries, and optionally createsC++ header files that contain the same definitions.

The top-level element of ODL syntax is the library statement (or library block). Every other ODLstatement (with the exception of the attributes that can be applied to the library statement) must bedefined in the library block.

The MIDL compiler generates a type library when it sees a library statement in the same way thatMkTypLib does. The statements found in the library block follow essentially the same syntax as earlierversions of ODL.

ODL attributes can be applied to an element both inside and outside of the library block. Outside theblock, they typically do nothing, unless the element is referenced from within the block by using it as abase type, inheriting from it, or referencing it on a line such as this:

library a{ interface [xyz]]; struct bar; ...};

If an element defined outside of the block is referenced in the block, its definition is put into the generatedtype library.

Anything outside of the library block is an .idl file, and the MIDL compiler processes it as usual.Typically, this means generating proxy stubs for it.

Differences Between MIDL and MkTypLib

There are a few key areas in which the MIDL compiler differs from the MkTypLib utility. Most of thesedifferences arise because MIDL is more C-syntax oriented than MkTypLib. All of the differencesdescribed here, with the exception of floating point constants and the enum scope, can be resolved byusing the /mktyplib203 MIDL compiler option (see “The /mktyplib203 Option” later in this appendix).This switch forces MIDL to behave like MkTypLib.exe, version 2.03, the last release of MkTypLib.exe.

In general, you will want to use the MIDL syntax in your .idl files. However, the /mktyplib203 optionis useful if you need to compile an existing .odl file, or otherwise maintain compatibility with MkTypLib.

TYPEDEF SYNTAX FOR COMPLEX DATA TYPES

Page 342: 6935316 DCOM Microsoft Distributed Component Object Model

In MkTypLib, both of the following definitions generate a TKIND_RECORD for “bar” in the type library.The tag “foo” is optional and, if used, will not show up in the type library.

typedef struct foo { ... } bar;typedef struct { ... } bar;

In MIDL, the first definition will generate a TKIND_RECORD for “foo” and a TKIND_ALIAS for “bar”(defining “bar” as an alias for “foo”). For the second definition, MIDL will generate a TKIND_RECORDfor a mangled name internal to MIDL that is not meaningful to the user and a TKIND_ALIAS for “bar”.This has potential implications for type library browsers that simply show the name of a record in its userinterface. If you expect a TKIND_RECORD to have a real name, there is a potential for unrecognizablenames to appear in the user interface. This behavior also applies to union and enum definitions, with theMIDL compiler generating TKIND_UNIONs and TKIND_ENUMs, respectively.

MIDL also allows C-style struct, union, and enum definitions. For example, the following definitionis legal in MIDL:

struct foo { ... };typedef struct foo bar;

MKTYPLIB AND BOOLEAN DATA TYPES

In MkTypLib, the Boolean base type and the MkTypLib datatype BOOL equate to VT_BOOL, whichmaps to VARIANT_BOOL, and which is defined as a short. In MIDL, the Boolean base type is equivalentto VT_UI1, which is defined as an unsigned char, and the BOOL datatype is defined as a long. This leadsto difficulties if you mix IDL syntax and ODL syntax in the same file while still trying to maintaincompatibility with MkTypLib. Because the datatypes are different sizes, the marshaling code will notmatch what is described in the type information. If you want a VT_BOOL in your type library, you shoulduse the VARIANT_BOOL datatype.

GUID DEFINITIONS IN HEADER FILES

When using the MkTypLib utility, GUIDs are defined in the header file with a macro that can beconditionally compiled to generate either a GUID predefinition or an instantiated GUID. MIDL normallyputs GUID predefinitions in its generated header files and only puts GUID instantiations in the filegenerated by the /iid switch.

SCOPE OF SYMBOLS IN AN ENUM DECLARATION

In MkTypLib the scope of symbols in an enum is local. In MIDL, the scope of symbols in an enum isglobal, as it is in C. For example, the following code will compile in MkTypLib, but will generate aduplicate name error in MIDL:

typedef struct { ... } a;enum {a=1, b=2, c=3};

SUPPORT FOR ODL BASE TYPES

There are a number of base types supported by MkTypLib that are not directly supported by MIDL. TheMIDL function gets its definitions for these base types by automatically importing oleauto.idl, andoleidl.idl whenever it encounters a library statement. This means that oleauto.idl, oaidl.idl, and oleidl.idl(along with the imported unknwn.idl and wtypes.idl files) must be somewhere in the user's INCLUDEpath. The OLE and Automation DLLs must also be in the system if the user compiles an .idl file thatcontains a library statement.

THE /MKTYPLIB203 OPTION

The MIDL compiler behaves differently from the MkTypLib utility. The /mktyplib203 optionremoves most of these differences and makes MIDL act like MkTypLib, version 2.03.

For example, BOOL (a MkTypLib base type) is defined differently in MIDL than it is in MkTypLib.MkTypLib treats BOOL as a VARIANT_BOOL. However, BOOL is defined in the file Wtypes.idl as a

Page 343: 6935316 DCOM Microsoft Distributed Component Object Model

long data type. If a VARIANT_BOOL is to be placed in the type library, it has to explicitly useVARIANT_BOOL in the .idl/.odl file. If BOOL is used when VARIANT_BOOL is meant to be used,then the /mktyplib203 option should also be used.

MIDL normally puts globally unique identifier (GUID) predefinitions in its generated header files, andonly puts GUID instantiations in the file generated by the /iid option. With the /mktyplib203option, MIDL defines GUIDs in the header files in the way that MkTypLib does. They are defined with amacro that can be compiled conditionally to generate either a predefined or an instantiated GUID. Withthe /mktyplib203 option enabled, it is invalid to put any statements outside of the library block. Apure ODL syntax must be used; it cannot be mixed and matched in this mode.

MkTypLib is used to require struct, union, and enum to be defined as part of type definitions. Forexample:

typedef struct foo { int i; } bar;

In this statement, MkTypLib generates a TKIND_RECORD named “bar.” Because the “foo” was notrecorded anywhere in the type library, it can be omitted. MIDL allows normal C definitions of struct,union, and enum:

struct foo {int i;}; typedef struct foo bar;

- Or -

typedef struct foo {int i;} bar;

This statement generates a TKIND_RECORD named “foo” and (if the type definition is public) aTKIND_ALIAS named “bar.” The “foo” can still be omitted, in which case MIDL generates a name for it.

When the /mktyplib203 option is enabled, the original MkTypLib type definition syntax is requiredfor structures, unions, and enumerators. The behavior is the same as under MkTypLib (that is, “foo” is notincluded in the type library).

MkTypLib: Type Library Creation Tool

MkTypLib processes scripts written in ODL, producing a type library and an optional C or C++ headerfile.

MkTypLib uses the ICreateTypeLib and ICreateTypeInfo interfaces to create type libraries.Type libraries can then be accessed by tools, such as type browsers and compilers that use the ITypeLiband ITypeInfo interfaces.

Invoking MkTypLib

To invoke MkTypLib, enter the following command line:

MkTypLib [options] ODLfile

MkTypLib creates a type library (.tlb) file based on the object description script in the file specified byODLfile. It can optionally produce a header (.h) file, which is a stripped version of the input file. This fileis included in C or C++ programs that want to access the types defined in the input file. In the header file,MkTypLib inserts DEFINE_GUID macros for each element defined in the type library (such asinterface, dispinterface, and so on).

There can be a series of options, each prefixed with a hyphen (-) or a slash (/), as follows:

Option Description/? or /help Displays command line Help. In this case, ODLfile does not need to be

specified

Page 344: 6935316 DCOM Microsoft Distributed Component Object Model

/align:alignment Sets the default alignment for types in the library. An alignment value of1 indicates natural alignment; n indicates alignment on byte n.

/cpp_cmd cpppath Specifies cpppath as the command to run the C preprocessor. By default,MkTypLib invokes CL.

/cpp_opt “options” Specifies options for the C preprocessor. The default is /C/E/D_MkTypLib_.

/D define[=value] Defines the name define for the C preprocessor. The value is its optionalvalue. No space is allowed between the equal sign (=) and the value.

/h filename Specifies filename as the name for a stripped version of the input file.This file can be used as a C or C++ header file.

/I includedir Specifies includedir as the directory where include files are located forthe C preprocessor.

/nocpp Suppresses invocation of the C preprocessor./nologo Disables the display of the copyright banner./o outputfile Redirects output (for example, error messages) to the specified outputfile./tlb filename Specifies filename as the name of the output .tlb file. If not specified, it

will be the same name as the ODLfile, with the extension .tlb./win16 /win32/mac /mips/alpha /ppc/ppc32 Specifies the output type library to be produced. The default is the current

operating system./w0 Disables warnings.

Although MkTypLib offers minimal error reporting, error messages include accurate line number andcolumn number information that can be used with text editors to locate the source of errors.

ODL File Syntax

The general syntax for an .odl file is as follows:

[attributes] library libname{definitions};

The attributes associate characteristics with the library, such as its Help file and universally uniqueidentifier (UUID). Attributes must be enclosed in square brackets.

The definitions consist of the descriptions of the imported libraries, data types, modules, interfaces,dispinterfaces, and coclasses that are part of the type library. Braces ({}) must surround the definitions.Each module, interface, dispinterface, and coclass in the definitions section follows the same generalsyntax:

[attributes] elementname typename{memberdescriptions};

The attributes set characteristics for the element. The elementname is a keyword that indicates the kind ofitem (module, interface, dispinterface, or coclass), and the typename defines the name of the item. Thememberdescriptions define the members (constants, functions, properties, and methods) of each element.

Aliases, enumerations, unions, and structures have the following syntax:

typedef [typeattributes] typekind typename

Page 345: 6935316 DCOM Microsoft Distributed Component Object Model

{ memberdescriptions};

For these types, the attributes follow the typedef keyword, and the typekind indicates the data type(enum, union, or struct). For details, see “Attribute Descriptions” later in this appendix.

Source File Contents

The following sections describe the proper format for comments, constants, identifiers, and other syntacticitems in an .odl file.

ARRAY DEFINITIONS

MkTypLib accepts both fixed-size arrays and arrays declared as SAFEARRAY. Use a C-style syntax for afixed size array:

type arrname[size];

To describe a SAFEARRAY, use the following syntax:

SAFEARRAY (elementtype) *arrayname

A function returning a SAFEARRAY has the following syntax:

SAFEARRAY (elementtype) myfunction(parameterlist);

COMMENTS

To include comments in an .odl file, use a C-style syntax in either block form (/*...*/) or single-line form(//). MkTypLib ignores the comments and does not preserve them in the header (.h) file.

CONSTANTS

A constant can be either numeric or a string, depending on the attribute.

NUMERIC  Numeric input is usually an integer (in either decimal or in hexadecimal, using the standard0x format), but can also be a single character constant (for example, \0).

STRING  A string is delimited by double quotation marks (") and cannot span multiple lines. Thebackslash character (\) acts as an escape character. The backslash character followed by any character(even another backslash) prevents the second character from being interpreted with any special meaning.The backslash is not included in the text.

For example, to include a double quotation mark (") in the text without causing it to be interpreted as theclosing delimiter, it should be preceded with a backslash (\"). Similarly, a double backslash (\\) should beused to put a backslash into the text. Some examples of valid strings are:

"commandName""This string contains a \"quote\".""Here's a pathname: c:\\bin\\binp"

A string can be up to 255 characters long.

FILE NAMES

A file name is a string that represents either a full or partial path. Automation expects to find files indirectories that are referenced by the type library registration entries, so partial path names are typicallyused.

FORWARD DECLARATIONS

Forward declarations permit forward references to types. Forward references have the following form:

Page 346: 6935316 DCOM Microsoft Distributed Component Object Model

typedef struct mydata;interface aninterface;dispinterface fordispatch;coclass pococlass;

GLOBALLY UNIQUE IDENTIFIER (GUID)

A universally unique identifier (UUID) is a globally unique identifier (GUID). This number is created byrunning the Guidgen.exe command line program. Guidgen.exe never produces the same number twice, nomatter how many times it is run or how many different machines it runs on. Every entity that needs to beuniquely identified (such as an interface) has a GUID.

IDENTIFIERS

Identifiers can be up to 255 characters long, and must conform to C-style syntax. MkTypLib is casesensitive, but it generates type libraries that are case insensitive. It is therefore possible to define auser-defined type whose name differs from that of a built-in type only by case. User-defined type names(and member names) that differ only in case refer to the same type or member. Except for propertyaccessor functions, it is invalid for two members of a type to have the same name, regardless of case.

STRING DEFINITIONS

Strings can be declared using the LPSTR data type, which indicates a zero-terminated string, and with theBSTR data type, which indicates a length-prefixed string. In 32-bit type libraries, Unicode strings can bedefined with the LPWSTR data type.

Attributes List

The following is a list of the Object Description Language (ODL) attributes, statements, and directivesthat are now part of the Microsoft Interface Definition Language (MIDL).

appobject aggregatablebindable controlcustom defaultdefaultbind defaultcollelemdefaultvalue defaultvtbldisplaybind dllnamedual entryhelpcontext helpfilehelpstring helpstringcontexthelpstringdll hiddenid immediatebindin lcidlicensed nonbrowsablenoncreateable nonextensibleodl oleautomationoptional outpropget propputpropputref publicreadonly replaceablerequestedit restrictedretval sourcestring uidefaultusesgetlasterror uuidvararg version

Page 347: 6935316 DCOM Microsoft Distributed Component Object Model

Statements Listcoclass dispinterfaceenum interfacelibrary modulestruct typedefunion

Directives List

importlib

Attribute Descriptions

The following sections describe the ODL attributes and the types of objects that they apply to, along withthe equivalent flags set in the object’s type information.

appobjectDescriptionIdentifies the Application object.

Allowed onCoclass.

CommentsIndicates that the members of the class can be accessed without qualification when accessing thistype library.

FlagsTYPEFLAG_FAPPOBJECT

aggregatableDescriptionIndicates that the class supports aggregation.

Allowed onCoclass.

CommentsIndicates that the members of the class can be aggregated.

FlagsTYPEFLAG-FAGGREGATABLE

Example

[uuid(1e196b20-1f3c-1069-996b-00dd010fe676), aggregatable]coclass Form{ [default] interface IForm; [default, source] interface IFormEvents;}

bindableDescriptionIndicates that the property supports data binding.

Allowed onProperty.

Comments

Page 348: 6935316 DCOM Microsoft Distributed Component Object Model

Refers to the property as a whole, so it must be specified wherever the property is defined. Theattribute should be specified on both the property get description and the property set description.

FlagsFUNCFLAG_FBINDABLE

VARFLAG_FBINDABLE

controlDescriptionIndicates that the item represents a control from which a container site will derive additional typelibraries or coclasses.

Allowed onType library, coclass.

CommentsThis attribute allows type libraries that describe controls to be marked so that they are not displayedin type browsers intended for nonvisual objects.

FlagsTYPEFLAG_FCONTROL

LIBFLAG_FCONTROL

custom(guid, value)DescriptionIndicates a custom attribute (one not defined by Automation). This feature enables the independentdefinition and use of attributes.

Parameters<guid> The standard GUID form.

<value> A value that can be put into a variant. See also the const directive.

Allowed onLibrary, typeinfo, typlib, variable, function, parameter.

Not allowed onA member of a coclass (IMPLTYPE).

RepresentationCan be retrieved using:

ITypeLib2::GetCustDataITypeInfo2::GetCustDataITypeInfo2::GetAllCustDataITypeInfo2::GetFuncCustDataITypeInfo2::GetAllFuncCustDataITypeInfo2::GetVarCustDataITypeInfo2::GetAllVarCustDataITypeInfo2::GetParamCustDataITypeInfo2::GetAllParamCustDataITypeInfo2::GetImplTypeCustDataITypeInfo2::GetAllImplTypeCustData

ExampleThe following example shows how to add a string-valued attribute that gives the ProgID for a class:

[custom(GUID_PROGID, "DAO.Dynaset")]coclass Dynaset{ [default] interface Dynaset; [default, source] interface IDynasetEvents;}

Page 349: 6935316 DCOM Microsoft Distributed Component Object Model

defaultDescriptionIndicates that the interface or dispinterface represents the default programmability interface.Intended for use by macro languages.

Allowed onCoclass member.

CommentsA coclass can have two default members at most. One represents the source interface ordispinterface, and the other represents the sink interface or dispinterface. If the default attributeis not specified for any member of the coclass or cotype, the first source and sink members that donot have the restricted attribute will be treated as the defaults.

FlagsIMPLTYPEFLAG_FDEFAULT

defaultbindDescriptionIndicates the single, bindable property that best represents the object.

Allowed onProperty.

CommentsProperties that have the defaultbind attribute must also have the bindable attribute. Thedefaultbind attribute cannot be specified on more than one property in a dispinterface.This attribute is used by containers that have a user model that involves binding to an object ratherthan binding to a property of an object. An object can support data binding and not have thisattribute.

FlagsFUNCFLAG_FDEFAULTBIND

VARFLAG_FDEFAULTBIND

defaultcollelemDescriptionAllows for optimization of code.

Allowed onProperty, members in dispinterface and interface.

CommentsIn Visual Basic for Applications (VBA5.0), foo!bar is normally syntactic shorthand forfoo.defaultprop (“bar”). Because such a call is significantly slower than accessing a datamember of foo directly, an optimization has been added in which the compiler looks for a membernamed “bar” on the type of foo. If such a member is found and flagged as an accessor function foran element of the default collection, a call is generated to that member function. To allow vendorsto produce object servers that will be optimized in this way, the member flag should bedocumented.Because this optimization searches the type of item that precedes the ‘!’, it will optimize calls of theform MyForm!bar only if MyForm has a member named “bar,” and it will optimizeMyForm.Controls!bar only if the return type of Controls has a member named “bar.” Eventhough MyForm!bar and MyForm.Controls!bar both would normally generate the samecalls to the object server, optimizing these two forms requires that the object server add the barmethod in both places.Use of [defaultcollelem] must be consistent for a property. For example, if it is present on aGet, it must also be present on a Put.

FlagsFUNCFLAG_FDEFAULTCOLLELEM

Page 350: 6935316 DCOM Microsoft Distributed Component Object Model

VARFLAG_FDEFAULTCOLLELEM

ExampleA form has a button on it named Button1. User code can access the button using property syntaxor ! syntax, as shown below.

Sub Test()Dim f As Form1Dim b1 As ButtonDim b2 As Button

Set f = Form1

Set b1 = f.Button1 ' Property syntaxSet b = f!Button1 ' ! syntaxEnd Sub

To use the property syntax and the ! syntax properly, see the form in the type information below.

[odl, dual, uuid(1e196b20-1f3c-1096-996b-00dd010ef676),helpstring("This is IForm"), restricted]interface IForm1: Iform{ [propget, defaultcollelem] HRESULT Button1([out, retval] Button *Value);}

defaultvalue(vallue)DescriptionEnables specification of a default value for a typed optional parameter.

Allowed onParameter.

CommentsThe expression value resolves to a constant that can be described in a variant. The ODL alreadyallows some expression forms, as when a constant is declared in a module. The same expressionsare supported without modification.The following example shows some legal parameter descriptions:

interface IFoo{ void Ex1([defaultvalue(44)] LONG i); void Ex2([defaultvalue(44)] SHORT i); void Ex3([defaultvalue("Hello")] BSTR i);}

The following rules apply:

1.  It is invalid to specify a default value for a parameter whose type is a safe array. It isinvalid to specify a default value for any type that cannot go in a variant, including structuresand arrays.

2.  Parameters can be mixed. Optional parameters and default value parameters must followmandatory parameters.

3.  The default value can be any constant that is represented by a VARIANT datatype.

Flags

Page 351: 6935316 DCOM Microsoft Distributed Component Object Model

None.

Example

interface QueryDef{

// Type is now known to be a LONG type (good for browser in VBA// and for a C/C++ programmer) and also has a default value of// dbOpenTable (constant).

HRESULT OpenRecordset([in, defaultvalue(dbOpenTable)] LONG Type,[out,retval] Recordset **pprst);}

defaultvtblDescriptionEnables an object to have two different source interfaces.

CommentsThe default interface is an interface or dispinterface that is the default source interface. If theinterface is a:

•  Dual interface, sinks receive events through IDispatch.

•  VTBL interface, event sinks receive events through VTBL.

•  Dispinterface, sinks receive events through IDispatch.

•  Defaultvtable, a default VTBL interface, which cannot be a dispinterface — it must be adual, VTBL, or interface. If the interface is a dual interface, then sinks receive events throughthe VTBL.

An object can have both a default source and a default VTBL source interface with the sameinterface identifier (IID or GUID). In this case, a sink should advise using IID_IDISPATCH toreceive dispatch events and use the specific interface identifier to receive VTBL events.

Allowed onA member of a coclass.

CommentsFor normal (non-source) interfaces, an object can support a single interface that satisfies consumerswho want to use IDispatch access as well as VTBL access (a dual interface). Because of the waysource interfaces work, it is not possible to use dual interface for source interfaces. The object withthe source interface is in control of whether calls are made through IDispatch or through theVTBL. The sink does not provide any information about how it wants to receive the events. Theonly action that object-sourcing events can take would be to use the least common denominator, theIDispatch interface. This effectively reduces a dual interface to a dispatch interface with regardto sourcing events. 

Interface Flag it translates intodefault IMPLTYPEFLAG_FDEFAULTdefault, source IMPLTYPEFLAG_FDEFAULT

IMPLTYPEFLAG_FSOURCEdefaultvtable,source

IMPLTYPEFLAG_FDEFAULT

IMPLTYPEFLAG_FDEFAULTVTABLEIMPLTYPEFLAG_FSOURCE

FlagsIMPLTYPEFLAG_FDEFAULTVTABLE. (If this flag is set, then IMPLTYPEFLAG_ FSOURCE

Page 352: 6935316 DCOM Microsoft Distributed Component Object Model

is also set.)

Example

[odl, dual, uuid(1e196b20-1f3c-1069-996b-00dd010ef676), restricted]interface IForm: IDispatch{ [propget] HRESULT Backcolor([out, retval] long *Value); [propput] HRESULT Backcolor([in] long Value); [propget] HRESULT Name([out, retval] BSTR *Value); [propput] HRESULT Name([in] BSTR Value);}

[odl, dual, uuid(1e196b20-1f3c-1069-996b-00dd010ef767), restricted]interface IFormEvents: IDispatch{

HRESULT Click(); HRESULT Resize();}

[uuid(1e196b20-1f3c-1069-996b-00dd010fe676)]coclass Form{

[default] interface IForm; [default, source] interface IFormEvents; [defaultvtable, source] interface IFormEvents;}

displaybindDescriptionIndicates that a property should be displayed as bindable to the user.

Allowed onProperty.

CommentsProperties that have the displaybind attribute must also have the bindable attribute. Anobject can support data binding and not have this attribute.

FlagsFUNCFLAG_FDISPLAYBIND

VARFLAG_FDISPLAYBIND

dllname(str)DescriptionDefines the name of the DLL that contains the entry points for a module.

Allowed onModule (required).

CommentsThe str argument gives the file name of the DLL.

dual

Page 353: 6935316 DCOM Microsoft Distributed Component Object Model

DescriptionIdentifies an interface that exposes properties and methods through IDispatch and directlythrough the VTBL.

Allowed onInterface.

CommentsThe interface must be compatible with Automation and derive from IDispatch. Not allowed ondispinterfaces.The dual attribute creates an interface that is both a Dispatch interface and a COM interface. Thefirst seven entries of the VTBL for a dual interface are the seven members of IDispatch, and theremaining entries are COM entries for direct access to members of the dual interface. All of theparameters and return types specified for members of a dual interface must be compatible withAutomation types.All members of a dual interface must pass an HRESULT as the function’s return value. Membersthat need to return other values should specify the last parameter as [retval, out] indicating anoutput parameter that returns the value of the function. In addition, members that need to supportmultiple locales should pass an lcid parameter.A dual interface provides for both the speed of direct VTBL binding and the flexibility ofIDispatch binding. For this reason, dual interfaces are recommended whenever possible.Specifying dual on an interface implies that the interface is compatible with Automation, andtherefore causes both the TYPEFLAG_FDUAL and TYPEFLAG_FOLEAUTOMATION flags tobe set.

FlagsTYPEFLAG_FDUAL

TYPEFLAG_FOLEAUTOMATION

entry(entryid)DescriptionIdentifies the entry point in the DLL.

Allowed onFunctions in a module (required).

CommentsIf entryid is a string, this is a named entry point. If entryid is a number, the entry point is defined byan ordinal. This attribute provides a way to obtain the address of a function in a module.

helpcontext(numctxt)DescriptionSets the context in the Help file.

Allowed onLibrary, interface, dispinterface, struct, enum, union, module, typedef, method,struct member, enum value, property, coclass, const.

CommentsRetrieved by the GetDocumentation functions in the ITypeLib and ITypeInfo interfaces.The numctxt is a 32-bit Help context identifier in the Help file.

helpfile(filename)DescriptionSets the name of the Help file.

Allowed onLibrary.

CommentsRetrieved through the GetDocumentation functions in the ITypeLib and ITypeInfointerfaces.

Page 354: 6935316 DCOM Microsoft Distributed Component Object Model

All types in a library share the same Help file.

helpstring(string)DescriptionSets the Help string.

Allowed onLibrary, interface, dispinterface, struct, enum, union, module, typedef, method,struct member, enum value, property, coclass, const.

CommentsRetrieved through the GetDocumentation functions in the ITypeLib and ITypeInfointerfaces.

helpstringcontext(contextid)DescriptionSets the string context in the Help file.

Allowed onType library, type information (Typeinfo), function, and variable level.

CommentsRetrieved by the GetDocumentation2 functions in the ITypeLib2 and ITypeInfo2interfaces. The contextid is a 32-bit Help context identifier in the Help file.

helpstringdll(dllname)DescriptionSets the name of the DLL to use to perform the document string lookup (localization).

Allowed onType library.

CommentsRetrieved through the GetDocumentation2 functions in the ITypeLib2 and ITypeInfo2interfaces.

hiddenDescriptionIndicates that the item exists, but should not be displayed in a user-oriented browser.

Allowed onProperty, method, coclass, dispinterface, interface, library.

CommentsThis attribute allows members to be removed from an interface by shielding them from further use,while maintaining compatibility with existing code.When specified for a library, the attribute prevents the entire library from being displayed. It isintended for use by controls. Hosts need to create a new type library that wraps the control withextended properties.

FlagsVARFLAG_FHIDDEN

FUNCFLAG_FHIDDEN

TYPEFLAG_FHIDDEN

id(num)DescriptionIdentifies the DISPID of the member.

Allowed onMethod or property in an interface or dispinterface.

Comments

Page 355: 6935316 DCOM Microsoft Distributed Component Object Model

The num is a 32-bit integral value in the following format: 

Bits Value0-15 Offset. Any value is permissible.16-21 The nesting level of this type information in the inheritance

hierarchy. For example:interface mydisp : IDispatchThe nesting level of IUnknown is 0, IDispatch is 1, andMyDisp is 2.

22-25 Reserved. Must be zero.26-28 Dispatch identifier (DISPID) value.

29True if this is the member identifier for a FuncDesc; otherwiseFalse.

30-31 Must be 01.

Negative identifiers are reserved for use by Automation.

immediatebindDescriptionAllows individual bindable properties on a form to specify this behavior. When this bit is set, allchanges will be notified.

CommentsAllows controls to differentiate two different types of bindable properties. One type ofbindable property needs to notify every change to the database (for example, with a check boxcontrol where every change needs to be sent through to the underlying database, even though thecontrol has not lost the focus). However, controls such as a list box need to have the change of aproperty communicated to the database when the control loses focus, because the user may havechanged the selection with the arrow keys before finding the desired setting. If the changenotification was sent to the database every time the user pressed an arrow key, it would give anunacceptable performance.The bindable and requestedit attribute bits need to be set for this new bit to have an effect.

FlagsVARFLAG_FIMMEDIATEBIND

FUNCFLAG_FIMMEDIATEBIND

inDescriptionSpecifies an input parameter.

Allowed onParameter.

CommentsThe parameter can be a pointer (such as char*) but the value it refers to is not returned.

lcidDescriptionIndicates that the parameter is a locale ID (LCID).

Allowed onParameter in a member of an interface.

CommentsOnly one parameter can have this attribute. The parameter must have the in attribute and not theout attribute, and its type must be long. The lcid attribute is not allowed on dispinterfaces.The lcid attribute allows members in the VTBL to receive an LCID at the time of invocation. By

Page 356: 6935316 DCOM Microsoft Distributed Component Object Model

convention, the lcid parameter is the last parameter not to have the retval attribute. If themember specifies propertyput or propertyputref, the lcid parameter must precede theparameter that represents the right side of the property assignment.ITypeInfo::Invoke passes the LCID of the type information into the lcid parameter.Parameters with this attribute are not displayed in user-oriented browsers.

lcid(numid)DescriptionThis attribute identifies the locale for a type library.

Allowed onLibrary.

CommentsThe numid is a 32-bit LCID, as used in Win32 National Language Support. The LCID is typicallyentered in hexadecimal format.

licensedDescriptionIndicates that the class is licensed.

Allowed onCoclass.

FlagsTYPEFLAG_FLICENSED

nonbrowsableDescriptionIndicates that the property appears in an object browser (which does not show property values), butdoes not appear in a properties browser (which does show property values).

Allowed onProperty.

FlagsVARFLAG_FNONBROWSABLE

FUNCFLAG_FNONBROWSABLE

noncreatableDescriptionIndicates that the class does not support creation at the top level (for example, throughITypeInfo::CreateInstance or CoCreateInstance). An object of such a class isusually obtained through a method call on another object.

Allowed onCoclass.

Example

[uuid(1e196b20-1fc3-1069-996b-00dd010ef671),helpstring("This is Dynaset"),noncreatable]coclass Dynaset{ [default] interface IDynaset; [default, source] interface IDynasetEvents;}

Flags

Page 357: 6935316 DCOM Microsoft Distributed Component Object Model

TYPEFLAG_FCANCREATE

nonextensibleDescriptionIndicates that the IDispatch implementation includes only the properties and methods listed inthe interface description.

Allowed onDispinterface, interface.

CommentsThe interface must have the dual attribute.By default, Automation assumes that interfaces can add members at run time, meaning that itassumes the interfaces are extensible.

FlagsTYPEFLAG_FNONEXTENSIBLE

odlDescriptionIdentifies an interface as an Object Description Language (ODL) interface.

Allowed onInterface (required).

CommentsThis attribute must appear on all interfaces.

oleautomationDescriptionThe oleautomation attribute indicates that an interface is compatible with Automation.

Allowed onInterface.

CommentsNot allowed on dispinterfaces.The parameters and return types specified for its members must be compatible with Automation. Aparameter is compatible with Automation if its type is compatible with an Automation type, apointer to an Automation type, or a SAFEARRAY of an Automation type. A return type iscompatible with Automation if its type is an HRESULT or is void. Methods in Automation mustreturn either HRESULT or void. A member is compatible with Automation if its return type andall of its parameters are compatible with Automation. An interface is compatible with Automation ifit derives from IDispatch or IUnknown, if it has the oleautomation attribute, or if all of itsVTBL entries are compatible with Automation. For 32-bit systems, the calling convention for allmethods in the interface must be STDCALL. For 16-bit systems, all methods must have the CDECLcalling convention. Every dispinterface is compatible with Automation.

FlagsTYPEFLAG_FOLEAUTOMATION

optionalDescriptionSpecifies an optional parameter.

Allowed onParameter.

CommentsValid only if the parameter is of type VARIANT or VARIANT*. All subsequent parameters of thefunction must also be optional.

Page 358: 6935316 DCOM Microsoft Distributed Component Object Model

outDescriptionSpecifies an output parameter.

Allowed onParameter.

CommentsThe parameter must be a pointer to memory that will receive a result.

propgetDescriptionSpecifies a property-accessor function.

Allowed onFunction, method in interface, dispinterface.

CommentsThe property must have the same name as the function. At most, one of propget, propput, andpropputref can be specified for a function.

FlagsINVOKE_PROPERTYGET

propputDescriptionSpecifies a property-setting function.

Allowed onFunction, method in interface, dispinterface.

CommentsThe property must have the same name as the function. Only one propget, propput, andpropputref can be specified.

FlagsINVOKE_PROPERTYPUT

propputrefDescriptionSpecifies a property-setting function that uses a reference instead of a value.

Allowed onFunction, method in interface, dispinterface.

CommentsThe property must have the same name as the function. Only one propget, propput, andpropputref can be specified.

FlagsINVOKE_PROPERTYPUTREF

publicDescriptionIncludes an alias declared with the typedef keyword in the type library.

Allowed onAlias declared with typedef.

CommentsBy default, an alias that is declared with typedef, and has no other attributes, is treated as a#define and is not included in the type library. Using the public attribute ensures that the aliasbecomes part of the type library.

Page 359: 6935316 DCOM Microsoft Distributed Component Object Model

readonlyDescriptionProhibits assignment to a variable.

Allowed onVariable.

FlagsVARFLAG_FREADONLY

replaceableDescriptionTags an interface as having default behaviors.

Allowed onMethods and properties of dispinterfaces and interfaces.

CommentsThe object supports IConnectionPointWithDefault.

FlagsTYPEFLAG_FREPLACEABLE

FUNCFLAG_FREPLACEABLE

VARFLAG_FREPLACEABLE

requesteditDescriptionIndicates that the property supports the OnRequestEdit notification.

Allowed onProperty.

CommentsThe property supports the OnRequestEdit notification, raised by a property before it is edited.An object can support data binding and not have this attribute.

FlagsFUNCFLAG_FREQUESTEDIT

VARFLAG_FREQUESTEDIT

restrictedDescriptionPrevents the item from being used by a macro programmer.

Allowed onType library, type information, coclass member, or member of a module or interface.

CommentsThis attribute is allowed on a member of a coclass, independent of whether the member is adispinterface or interface, and independent of whether the member is a sink or source. A member ofa coclass cannot have both the restricted and default attributes.

FlagsIMPLTYPEFLAG_FRESTRICTED

FUNCFLAG_FRESTRICTED

TYPEFLAG_FRESTRICTED

VARFLAG_FRESTRICTED

Example

[odl, dual, uuid(1e196b20-1f3c-1069-996b-00dd010ef676),

Page 360: 6935316 DCOM Microsoft Distributed Component Object Model

helpstring("This is IForm"), restricted]interface IForm: IDispatch{ [propget] HRESULT Backcolor([out, retval] long *Value);

[propput] HRESULT Backcolor([in] long Value);}

[odl, dual, uuid(1e196b20-1f3c-1069-996b-00dd010ef767), helpstring("This is IFormEVents"), restricted]interface IFormEvents: IDispatch{ HRESULT Click();}

[uuid(1e196b20-1f3c-1069-996b-00dd010fe676), helpstring("This is Form")]coclass Form{ [default] interface IForm; [default, source] interface IFormEvents;}

retvalDescriptionDesignates the parameter that receives the return value of the member.

Allowed onParameters of interface members that describe methods or get properties.

CommentsThis attribute can be used only on the last parameter of the member. The parameter must have theout attribute and must be a pointer type.Parameters with this attribute are not displayed in user-oriented browsers.

FlagsIDLFLAG_FRETVAL

sourceDescriptionIndicates that a member is a source of events.

Allowed onMember of a coclass, property, or method.

CommentsFor a member of a coclass, this attribute indicates that the member is called rather thanimplemented.On a property or method, this attribute indicates that the member returns an object or VARIANTthat is a source of events. The object implements the interfaceIConnectionPointContainer.

FlagsIMPLTYPEFLAG_FSOURCE

VARFLAG_SOURCE

Page 361: 6935316 DCOM Microsoft Distributed Component Object Model

FUNCFLAG_SOURCE

stringDescriptionSpecifies a string.

Allowed onStructure, member, parameter, property.

CommentsIncluded only for compatibility with the Interface Definition Language (IDL). Use LPSTR for azero-terminated string.

uidefaultDescriptionIndicates that the type information member is the default member for display in the user interface.

Allowed onA member of an interface or dispinterface.

CommentsThis attribute is used to mark an event as the default (the first one created) or a property as thedefault (the one to select first in the properties browser). For example, Visual Basic uses thisattribute in the following ways:

•  When an object is double-clicked at design time, Visual Basic jumps to the event in thedefault source interface that is marked as [uidefault]. If there is no such member, thenVisual Basic displays the first one listed in the default source interface.

•  When an object is selected at design time, by default, the Properties window in VisualBasic displays the property in the default interface that is marked as [uidefault]. If thereis no such member, then Visual Basic displays the first one listed in the default interface.

FlagsFUNCFLAG_FUIDEFAULT

VARFLAG_FUIDEFAULT

Example

[odl, dual, uuid(1e196b20-1f3c-1069-996b-00dd010ef676), restricted]interface IForm: IDispatch{ [propget] HRESULT Backcolor([out, retval] long *Value); [propput] HRESULT Backcolor([in] long Value);

[propget, uidefault] HRESULT Name([out, retval] BSTR *Value); [propput, uidefault] HRESULT Name([in] BSTR Value);}

[odl, dual, uuid(1e196b20-1f3c-1069-996b-00dd010ef767), restricted]interface IFormEvents: Idispatch{[uidefault] HRESULT Click(); HRESULT Resize();}

Page 362: 6935316 DCOM Microsoft Distributed Component Object Model

[uuid(1e196b20-1f3c-1069-996b-00dd010fe676)]coclass Form{ [default] interface IForm; [default, source] interface IFormEvents;}

usesgetlasterrorDescriptionTells the caller that, if there is an error when calling that function, the caller can then callGetLastError to retrieve the error code.

Allowed onMember of a module.

CommentsThe usesgetlasterror attribute can be set on a module entry point, if that entry point uses theWin32 function SetLastError to return error codes. The attribute tells the caller that, if there isan error when calling that function, the caller can then call GetLastError to retrieve the errorcode.

Example

[dllname("MyOwn.dll")]module MyModule{[entry("One"), usesgetlasterror, bindable, requestedit, propputref, defaultbind]void Func1 ([in]IUnknown * iParam1, [out] long * Param2) ;

[entry("TwentyOne"), usesgetlasterror, hidden, vararg]SAFEARRAY (int) Func2 ([in, out] SAFEARRAY (variant) *varP) ;};

uuid(uuidval)DescriptionSpecifies the universally unique ID (UUID) of the item.

Allowed onRequired for library, dispinterface, interface, and coclass. Optional for struct, enum, union,module, and typedef.

CommentsThe uuidval is a 16-byte value using hexadecimal digits in the following format:12345678-1234-1234-1234-123456789ABC. This value is returned in the TypeAttr structureretrieved by ITypeInfo::GetTypeAttr.

varargDescriptionIndicates a variable number of arguments.

Allowed onFunction.

CommentsIndicates that the last parameter is a safe array of VARIANT type, which contains all of theremaining parameters.

version(versionval)Description

Page 363: 6935316 DCOM Microsoft Distributed Component Object Model

Specifies a version number.

Allowed onLibrary, struct, module, dispinterface, interface, coclass, enum, union.

CommentsThe argument versionval is a real number in the format n.m, where n is a major version number andm is a minor version number.

Statement Descriptions

The following sections describe the statements and directives that make up the Object DescriptionLanguage (ODL).

coclass

This statement describes the globally unique ID (GUID) and the supported interfaces for a ComponentObject Model (COM).

Syntax

[attributes]coclass classname{[attributes2] [interface | dispinterface] interfacename;};

Syntax ElementsattributesThe uuid attribute is required on a coclass. This is the same uuid that is registered as a CLSID inthe system registration database. The helpstring, helpcontext, licensed, version,control, hidden, and appobject attributes are accepted, but not required, before a coclassdefinition. For more information about the attributes accepted before a coclass definition, see“Attribute Descriptions” earlier in this appendix. The appobject attribute makes the functionsand properties of the coclass globally available in the type library.

classname

Name by which the common object is known in the type library.

attributes2

Optional attributes for the interface or dispinterface. The source, default, and restrictedattributes are accepted on an interface or dispinterface in a coclass.

interfacename

Either an interface declared with the interface keyword, or a dispinterface declared with thedispinterface keyword.

CommentsCOM defines a class as an implementation that allows QueryInterface between a set ofinterfaces.

Example

[uuid(BFB73347-822A-1068-8849-00DD011087E8), version(1.0), helpstring("A class"), helpcontext(2481), appobject]coclass myapp{[source] interface IMydocfuncs;dispinterface DMydocfuncs;};

Page 364: 6935316 DCOM Microsoft Distributed Component Object Model

[uuid 00000000-0000-0000-0000-123456789019]coclass foo{[restricted] interface bar;interface bar;}

dispinterface

This statement defines a set of properties and methods on which IDispatch::Invoke can be called.A dispinterface can be defined by explicitly listing the set of supported methods and properties (Syntax 1)or by listing a single interface (Syntax 2).

Syntax 1

[attributes] dispinterface intfname{properties:proplistmethods:methlist};

Syntax 2

[attributes] dispinterface intfname{interface interfacename};

Syntax ElementsattributesThe helpstring, helpcontext, hidden, uuid, and version attributes are acceptedbefore dispinterface. For more information about the attributes accepted before a dispinterfacedefinition, see “Attribute Descriptions ” earlier in this appendix. Attributes (including the brackets)can be omitted, except for the uuid attribute, which is required.

intfnameThe name by which the dispinterface is known in the type library. This name must be unique withinthe type library.

interfacename(Syntax 2) The name of the interface to declare as an IDispatch interface.

proplist(Syntax 1) An optional list of properties supported by the object, declared in the form of variables.This is the short form for declaring the property functions in the methods list. See the commentssection for details.

methlist(Syntax 1) A list comprising a function prototype for each method and property in the dispinterface.Any number of function definitions can appear in methlist. A function in methlist has the followingform:

[attributes] returntype methname(params);

The following attributes are accepted on a method in a dispinterface: helpstring,helpcontext, string (for compatibility with the Interface Definition Language), bindable,defaultbind, displaybind, propget, propput, propputref, and vararg. Ifvararg is specified, the last parameter must be a safe array of VARIANT type.The parameter list is a comma-delimited list, each element of which has the following form:

Page 365: 6935316 DCOM Microsoft Distributed Component Object Model

[attributes] type paramname

The type can be any declared or built-in type, or a pointer to any type. Attributes on parameters arein, out, optional, and string.If optional is specified, it must only be specified on the right-most parameters, and the types ofthose parameters must be VARIANT.

CommentsMethod functions are specified exactly as described in the “module ” statement except that theentry attribute is not allowed.Properties can be declared either in the properties or methods lists. Declaring properties in theproperties list does not indicate the type of access the property supports (get, put, or putref).Specify the readonly attribute for properties that do not support put or putref. If the propertyfunctions are declared in the methods list, functions for one property will all have the same ID.Using Syntax 1, the properties: and methods: tags are required. The id attribute is also required oneach member. For example

properties: [id(0)] int Value; // Default property.methods: [id(1)] void Show();

Unlike interface members, dispinterface members cannot use the retval attribute to return a valuein addition to an HRESULT error code. The lcid attribute is also invalid for dispinterfacesbecause IDispatch::Invoke passes a locale ID (LCID). However, it is possible to declare aninterface again that uses these attributes.Using Syntax 2, interfaces that support IDispatch and are declared earlier in an ODL script canbe redeclared as IDispatch interfaces as follows:

dispinterface helloPro{interface hello;};

This example declares all of the members of the Hello sample and all of the members that it inheritsto support IDispatch. In this case, if Hello was declared earlier with lcid and retvalmembers that returned HRESULTs, MkTypLib would remove each lcid parameter andHRESULT return type, and instead mark the return type as that of the retval parameter.The properties and methods of a dispinterface are not part of the VTBL of the dispinterface.Consequently, CreateStdDispatch and DispInvoke cannot be used to implementIDispatch::Invoke. The dispinterface is used when an application needs to expose existingnon-VTBL functions through Automation. These applications can implementIDispatch::Invoke by examining the dispidmember parameter and directly calling thecorresponding function.

Example

[uuid(BFB73347-822A-1068-8849-00DD011087E8), version(1.0), helpstring("Useful help string."), helpcontext(2480)]dispinterface MyDispatchObject{properties: [id(1)] int x; // An integer property named x. [id(2)] BSTR y; // A string property named y.methods:

Page 366: 6935316 DCOM Microsoft Distributed Component Object Model

[id(3)] void show; // No arguments, no result. [id(11)] int computeit(int inarg, double *outarg);};

[uuid 00000000-0000-0000-0000-123456789012]dispinterface MyObject{properties:methods: [id(1), propget, bindable, defaultbind, displaybind] long x();

[id(1), propput, bindable, defaultbind, displaybind] void x(long rhs);}

enum

This statement defines a C-style enumerated type.

Syntax

typedef [attributes] enum [tag]{enumlist} enumname;

Syntax ElementsattributesThe helpstring, helpcontext, hidden, and uuid attributes are accepted before an enumstatement. The helpstring and helpcontext attributes are accepted on an enumerationelement. For more information about the attributes accepted before an enumeration definition, see“Attribute Descriptions” earlier in this appendix. Attributes (including the brackets) can be omitted.If uuid is omitted, the enumeration is not uniquely specified in the system.

tag

An optional tag, as with a C enum.

enumlist

List of enumerated elements.

enumname

Name by which the enumeration is known in the library.

CommentsThe enum keyword must be preceded by typedef. The enumeration description must precedeother references to the enumeration in the library. If value is not specified for enumerators, thenumbering progresses, as with enumerations in C. The type of the enum element is int, the systemdefault integer, which depends on the target type library specification.

Examples

typedef[uuid(DEADFOOD-CODE-BIFF-F001-A100FF001ED), helpstring("Farm Animals are friendly"), helpcontext(234)]enum{[helpstring("Moo")] cows = 1,pigs = 2

Page 367: 6935316 DCOM Microsoft Distributed Component Object Model

} ANIMALS;

interface

This statement defines an interface, which is a set of function definitions. An interface can inherit fromany base interface.

Syntax

[attributes] interface interfacename [:baseinterface]{functionlist};

Syntax ElementsattributesThe attributes dual, helpstring, helpcontext, hidden, odl, oleautomation, uuid,and version are accepted before interface. If the interface is a member of a coclass, theattributes source, default, and restricted are also accepted. For more information aboutthe attributes that can be accepted before an interface definition, refer to the section “AttributeDescriptions” earlier in this appendix. The attributes odl and uuid are required on allinterface declarations.

interfacename

The name by which the interface is known in the type library.

Baseinterface

The name of the interface that is the base class for this interface.

FunctionlistList of function prototypes for each function in the interface. Any number of function definitionscan appear in the function list. A function in the function list has the following form:

[attributes] returntype [calling convention] funcname(params);

The following attributes are accepted on a function in an interface: helpstring,helpcontext, string, propget, propput, propputref, bindable, defaultbind,displaybind, and vararg. If vararg is specified, the last parameter must be a safe array ofVARIANT type. The optional calling convention can be __pascal/_pascal/pascal,__cdecl/_cdecl/cdecl, or __stdcall/_stdcall/stdcall. The calling conventionspecification can include up to two leading underscores.The parameter list is a comma-delimited list, as follows:

[attributes] type paramname

The type can be any previously declared type, built-in type, a pointer to any type, or a pointer to abuilt-in type. Attributes on parameters are in, out, optional, and string.If optional is used, it must be specified only on the right-most parameters, and the types of thoseparameters must be VARIANT.

CommentsBecause the functions described by the interface statement are in the VTBL, DispInvokeand CreateStdDispatch can be used to provide an implementation ofIDispatch::Invoke. For this reason, interface is more commonly used thandispinterface to describe the properties and methods of an object.Functions in interfaces are the same as described in the “module” statement except that the entryattribute is not allowed.Members of interfaces that need to raise exceptions should return an HRESULT and specify aretval parameter for the actual return value. The retval parameter is always the last parameterin the list.

Page 368: 6935316 DCOM Microsoft Distributed Component Object Model

ExamplesThe following example defines an interface named Hello with two member functions, Helloprocand Shutdown:

[uuid(BFB73347-822A-1068-8849-00DD011087E8), version(1.0)]interface hello : IUnknown{void HelloProc([in, string] unsigned char * pszString);void Shutdown(void);};

The next example defines a dual interface named IMyInt, which has a pair of accessor functionsfor the MyMessage property, and a method that returns a string.

[dual]interface IMyInt : Idispatch{ // A property that is a string. [propget]HRESULT MyMessage([in, lcid] LCID lcid, [out, retval] BSTR *pbstrRetVal); [propput]HRESULT MyMessage([in] BSTR rhs, [in, lcid] DWORD lcid);

// A method returning a string. HRESULT SayMessage([in] long NumTimes, [in, lcid] DWORD lcid, [out, retval] BSTR *pbstrRetVal);}

The members of this interface return error information and function return values through theHRESULT values and retval parameters, respectively. Tools that access the members can returnthe HRESULT to their users, or can simply expose the retval parameter as the return value, andhandle the HRESULT transparently. A dual interface must derive from IDispatch.

library

This statement describes a type library. This description contains all of the information in a MkTypLibinput file (ODL).

Syntax

[attributes] library libname{definitions};

Syntax ElementsattributesThe helpstring, helpcontext, lcid, restricted, hidden, control, and uuidattributes are accepted before a library statement. For more information about the attributesaccepted before a library definition, see “Attribute Descriptions” earlier in this appendix. Theuuid attribute is required.

libname

The name by which the type library is known.

definitions

Page 369: 6935316 DCOM Microsoft Distributed Component Object Model

Descriptions of any imported libraries, data types, modules, interfaces, dispinterfaces, and coclassesrelevant to the object being exposed.

CommentsThe library statement must precede any other type definitions.

Example

[ uuid(F37C8060-4AD5-101B-B826-00DD01103DE1), // LIBID_Hello. helpstring("Hello 2.0 Type Library"), lcid(0x0409), version(2.0)]library Hello{ importlib("stdole.tlb");[ uuid(F37C8062-4AD5-101B-B826-00DD01103DE1), // IID_Ihello. helpstring("Application object for the Hello application."), oleautomation, dual] interface IHello : Idispatch { [propget, helpstring("Returns the application of the object.")] HRESULT Application([in, lcid] long localeID,[out, retval] IHello** retval); }}

module

This statement defines a group of functions, typically a set of DLL entry points.

Syntax

[attributes] module modulename{elementlist};

Syntax ElementsattributesThe attributes uuid, version, helpstring, helpcontext, hidden, and dllname areaccepted before a module statement. For more information about the attributes that can beaccepted before a module definition, see “Attribute Descriptions” earlier in this appendix. Thedllname attribute is required. If uuid is omitted, the module is not uniquely specified in thesystem.

modulename

The name of the module.

elementlistList of constant definitions and function prototypes for each function in the DLL. Any number offunction definitions can appear in the function list. A function in the function list has the followingform:

[attributes] returntype [calling convention] funcname(params);[attributes] const constname = constval;

Page 370: 6935316 DCOM Microsoft Distributed Component Object Model

Only the attributes helpstring and helpcontext are accepted for a const. The followingattributes are accepted on a function in a module: helpstring, helpcontext, string,entry, propget, propput, propputref, vararg. If vararg is specified, the lastparameter must be a safe array of VARIANT type.The optional calling convention can be one of __pascal/_pascal/pascal,__cdecl/_cdecl/cdecl, or stdcall/stdcall. The calling convention can include up totwo leading underscores.The parameter list is a comma-delimited list.

[attributes] type paramname

The type can be any previously declared type or built-in type, a pointer to any type, or a pointer to abuilt-in type. Attributes on parameters are in, out, and optional.If optional is specified, it must only be specified on the right-most parameters, and the types ofthose parameters must be VARIANT.

CommentsThe header file (.h) output for modules is a series of function prototypes. The module keywordand surrounding brackets are stripped from the header file output, but a comment (\\ modulemodulename) is inserted before the prototypes. The keyword extern is inserted before thedeclarations.

Example

[uuid(DOOBEDOO-CEDE-B1FF-F001-A100FF001ED),helpstring("This is not GDI.EXE"), helpcontext(190),dllname("MATH.DLL")]module somemodule{[helpstring("Color for the frame")]unsigned long const COLOR_FRAME = 0xH80000006;[helpstring("Not a rectangle but a square"), entry(1)]pascal double square([in] double x);};

struct

This statement defines a C-style structure.

Syntax

typedef [attributes]struct [tag]{memberlist} structname;

Syntax ElementsattributesThe attributes helpstring, helpcontext, uuid, hidden, and version are acceptedbefore a struct statement. The attributes helpstring, helpcontext, and string areaccepted on a structure member. For more information about the attributes accepted before astructure definition, see “Attribute Descriptions” earlier in this appendix. Attributes (including thebrackets) can be omitted. If uuid is omitted, the structure is not specified uniquely in the system.

Tag

An optional tag, as with a C struct.

Page 371: 6935316 DCOM Microsoft Distributed Component Object Model

memberlist

List of structure members defined with C syntax.

structname

Name by which the structure is known in the type library.

CommentsThe struct keyword must be preceded with a typedef. The structure description must precedeother references to the structure in the library. Members of a struct can be of any built-in type, orany type defined lexically as a typedef before the struct. For a description of how strings andarrays can be entered, see the sections “String Definitions” and “Array Definitions” earlier in thisappendix.

Example

typedef[uuid(BFB7334B-822A-1068-8849-00DD011087E8), helpstring("A task"), helpcontext(1019)]struct{DATE startdate;DATE enddate;BSTR ownername;SAFEARRAY (int) subtasks;int A_C_array[10];} TASKS;

typedef

This statement creates an alias for a type.

Syntax

typedef [attributes] basetype aliasname;

Syntax ElementsattributesAny attribute specifications must follow the typedef keyword. If no attributes and no other type(for example, enum, struct, or union) are specified, the alias is treated as a #define and doesnot appear in the type library. If no other attribute is desired, public can be used to explicitly includethe alias in the type library. The helpstring, helpcontext, and uuid attributes are acceptedbefore a typedef. For more information, see “Attribute Descriptions” earlier in this appendix. Ifuuid is omitted, the typedef is not uniquely specified in the system.

basetype

The type for which the alias is defined.

aliasname

Name by which the type will be known in the type library.

CommentsThe typedef keyword must also be used whenever a struct or enum is defined. The namerecorded for the enum or struct is the typedef name, and not the tag for the enumeration. Noattributes are required to make sure the alias appears in the type library.Enumerations, structures, and unions must be defined with the typedef keyword. The attributesfor a type defined with typedef are enclosed in brackets following the typedef keyword. If asimple alias typedef has no attributes, it is treated like a #define, and the aliasname does notappear in the library. Any attribute (public can be used if no others are desired) specified betweenthe typedef keyword and the rest of a simple alias definition causes the alias to appear explicitlyin the type library. The attributes typically include such items as a Help string and Help context.

Page 372: 6935316 DCOM Microsoft Distributed Component Object Model

Examples

typedef [public] long DWORD;

This example creates a type description for an alias type with the name DWORD.

typedef enum{ TYPE_FUNCTION = 0, TYPE_PROPERTY = 1, TYPE_CONSTANT = 2, TYPE_PARAMETER = 3} OBJTYPE;

The second example creates a type description for an enumeration named OBJTYPE, which hasfour enumerator values.

union

This statement defines a C-style union.

Syntax

typedef [attributes] union [tag]{memberlist} unionname;

Syntax ElementsattributesThe attributes helpstring, helpcontext, uuid, hidden, and version are acceptedbefore a union. The helpstring, helpcontext, and string attributes are accepted on aunion member. For more information about the attributes accepted before a union definition, see“Attribute Descriptions” earlier in this appendix. Attributes (including the square brackets) can beomitted. If uuid is omitted, the union is not uniquely specified in the system.

tag

An optional tag, as with a C union.

memberlist

List of union members defined with C syntax.

unionname

Name by which the union is known in the type library.

CommentsThe union keyword must be preceded with a typedef. The union description must precedeother references to the structure in the library. Members of a union can be of any built-in type, orany type defined lexically as a typedef before the union. For a description of how strings andarrays can be entered, see the sections “String Definitions” and “Array Definitions” earlier in thisappendix.

Example

[uuid(BFB7334C-822A-1068-8849-00DD011087E8), helpstring("A task"), helpcontext(1019)]typedef union{ COLOR polycolor;

Page 373: 6935316 DCOM Microsoft Distributed Component Object Model

int cVertices; boolean filled; SAFEARRAY (int) subtasks;} UNIONSHOP;

Directives Description

importlib

This directive makes types that have already been compiled into another type library available to thelibrary currently being created. All importlib directives must precede the other type descriptions in thelibrary.

Syntax

importlib(filename);

Syntax ElementfilenameThe location of the type library file when MkTypLib is executed.

CommentsThe importlib directive makes any type defined in the imported library accessible from withinthe library being compiled. Ambiguity is resolved as the current library is searched for the type. Ifthe type cannot be found, MkTypLib searches the imported library that is lexically first, and thenthe next, and so on. To import a type name in code, the name should be entered aslibname.typename, where libname is the library name as it appeared in the library statementwhen the library was compiled.The imported type library should be distributed with the library being compiled.

ExampleThe following example imports the libraries Stdole.tlb and Mydisp.tlb:

library BrowseHelper{importlib("stdole.tlb");importlib("mydisp.tlb");// Additional text omitted.}

Table of Contents

[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.

Page 374: 6935316 DCOM Microsoft Distributed Component Object Model

               

 

Brief Full Advanced

      Search Search Tips

To access the contents, click the chapter and section titles.

DCOM: Microsoft Distributed Component Object Model(Publisher: IDG Books Worldwide, Inc.)Author(s): Frank E. Redmond IIIISBN: 0764580442Publication Date: 09/01/97

Search this book:

 

Table of Contents

Index

AAccount List listbox, 238, 257, 258

Account object

implementing

GetProperties and SetProperties method, 280-283

Item method, 283-286

object properties of, 191-192

AccountInfo COM object

interfaces and properties of, 114

object properties of, 114

AccountInfo.idl file, 115-116

AccountInfoAuto object

building with AccountInfoAutoVTBL application, 153-154

object properties of, 139, 154

properties of, 139, 154

releasing, 162-163

AccountInfoAutoDisp.cpp file, 175-184

AccountInfoAutoVTBL application, 153-170

initializing the COM library, 155

manipulating the COM object, 156-162

obtaining an initial interface, 155-156

releasing the COM object, 162-163

Page 375: 6935316 DCOM Microsoft Distributed Component Object Model

uninitializing the COM library, 163-170

AccountInfoForm file, 290-291

AccountInfoAutoDisp client

retrieving property values using IDispatch function, 175-185

setting property values using IDispatch interface pointer, 171-174

accounts

adding, 234-237

deactivating with Remove method, 213

removing, 241

retrieving, 237-239

updating, 239-241

Accounts object

functionality of, 195-197

initialization of, 205-208

Item property source code for, 218-222

source code

for Add method, 210-213

for BOF and EOF properties and navigation methods for,225-229

for Remove method and Count property, 214

activation security, 268

Active Server Pages (ASP), 249-250

ActiveX, 4

Add method for Accounts object, 210-213

AddRef function, 8-10

aggregation, 111-123

containment vs., 111

delegating

inner objects to outer objects, 113

unknown member variable, 118

exposing interfaces of inner object, 112

interface pointers

releasing with nondelegating unknown, 118-122

storing in controlling unknown, 117

interfaces and properties of AccountInfo object, 114

restrictions of, 122-123

APIs (Application Programming Interfaces)

with BSTRS, 134

HRESULT data type, 14-15, 16-17

manipulating system registry with WIN32, 18-19

Page 376: 6935316 DCOM Microsoft Distributed Component Object Model

with SAFEARRAYs, 137-138

with variants, 133

applications

AccountInfoAutoVTBL, 153-170

client/server, 231-245

improving with DCOMCNFG, 279-286

VBScript for improving Web order-entry, 286-289

Web order-entry, 247-265

Applications tab (Distributed COM Configuration Properties dialogbox), 270, 274

architecture

for client/server applications, 233-234

limitations of, 244

order-entry systems, 235

combining DCOM with existing, 292

of Web applications

limitations of, 264

understanding, 248-250

arguments

for SQLBindCol function, 215-216

for SQLBindParameter function, 208-209

ASP (Active Server Pages), 249-250, 286-287

authentication levels supported by DCOM, 268-269

Automation

about, 125

intrinsic IDL datatypes supported by, 129-130

process of, 126-128

Automation controllers, 153-185

building the AccountInfoAutoDisp client, 170-185

retrieving property values using IDispatch function,175-185

setting property values using IDispatch interface pointer,171-174

building the AccountInfoAutoVTBL application, 153-170

initializing the COM library, 155

manipulating the COM object, 156-162

obtaining an initial interface, 155-156

releasing the COM object, 162-163

uninitializing the COM library, 163-170

Automation objects, 125-151

character sets, 135-136

Page 377: 6935316 DCOM Microsoft Distributed Component Object Model

dual interfaces, 128-130

exposing a type library, 143-144

implementing IDispatch interface, 145-149

isolating Automation specifics, 138-143

registering, 149-151

understanding

automation, 125

BSTRs, 133-134

IDispatch interface, 126-128

SAFEARRAYs, 136-138

variants, 130-133

Bbinding. See VTBL binding

BSTRs (binary strings), 133-134

Ccall security, 268

character sets, 135-136

class factories

exposing

for in-process servers, 37-39

for out-of-process servers, 74-77

implementing

for in-process servers, 34-35

for out-of-process servers, 72-73

registering, 74

click event for InvoiceInfoForm's OK button, 243-244

client/server applications, 231-245

about the order-entry application, 232-233

developing, 234-244

adding accounts, 234-237

adding and updating invoices, 241-244

removing accounts, 241

retrieving accounts, 237-239

updating accounts, 239-241

improving with DCOMCNFG, 279-286

limitations of, 244

object hierarchies and, 231-232

understanding architecture for, 233-234

See also Web order-entry applications

Page 378: 6935316 DCOM Microsoft Distributed Component Object Model

CLSIDs (Class Identifiers)

allocating for out-of-process servers, 66

COM objects and, 5-6

mapping to COM server filenames, 18

collection objects, 202-229

about ODBC programming, 203-205

data access and manipulation properties and methods, 205-222

navigation properties and methods, 222-229

for OrderEntry object hierarchy, 190

columns

of Accounts database table, 192

of Invoice database table, 194

of LineItem database table, 195

of Products database table, 193

COM (Component Object Model), 3-23

as programming model, 3-14

COM objects, 5-6

COM servers, 11-14

DCOM as extension of, 267

interfaces, 6-7

managing COM objects, 8-10

navigating interfaces, 7-8

object versioning and evolution, 10-11

objectives, 3-5

system services

APIs, 14-17

system registry, 17-20

VTBL design and transparent LPC and RPC mechanism,20-23

COM client

Automation controllers and, 125

initializing the COM library, 55

manipulating COM objects, 56

obtaining an initial interface, 55-56

releasing COM objects, 56

testing in-process servers with, 54-60

uninitializing the COM library, 57-60

COM library

composition of, 14

initializing for AccountInfoAutoVTBL application, 155

Page 379: 6935316 DCOM Microsoft Distributed Component Object Model

uninitializing for the AccountInfoAutoVTBL application,163-170

COM objects, 5-6, 97-123

Automation objects and, 125

CLSIDs and GUIDs, 5

defining interfaces for, 27-31, 66-70

encapsulating server packaging code, 40-42

instantiation process of, 37-39

interfaces and, 6-7

managing, 8-10

manipulating with AccountInfo Dispatch interface, 156-162

object hierarchies, 189-230

object properties for UserInfo, 64

object versioning and evolution, 10-11

registering

Automation objects, 149-151

class information for, 35-37

releasing in AccountInfoAutoVTBL application, 162-163

See also reusing COM objects

COM servers, 11-14

configuring security for, 274

defined, 4

out-of-process servers as, 63-64

See also in-process servers

Component Object Model. See COM

containment, 97-111

aggregation vs., 111

creating inner objects, 100-101

implementing object properties, 101-102

releasing objects, 102

controlling unknown member variable, 117

Count property

deactivating accounts with, 213

source code for, 214

creating inner objects, 100-101

Ddata manipulation methods, Add method, 207-208

datatypes

Automation support for intrinsic IDL, 129-130

SAFEARRAY, 136-138

Page 380: 6935316 DCOM Microsoft Distributed Component Object Model

SQLBindParameter function support for C, 209-210

SQLBindParameter function support for SQL, 210

supported by IDL, 29-30

DBCS (double-byte character sets), 135

DCOM (Distributed Component Object Model), 267-292

as extension of COM programming model, 267

security, 268-270

using DCOMCNFG, 270-279

improving the client/server order-entry application,279-286

improving the Web order-entry application, 286-292

providing object-specific configuration information,273-279

providing system-wide configuration information, 270-272

DCOM impersonation levels, 269

DCOMCNFG, 270-279

application-specific tabs for, 274, 275, 286

improving applications

for client/server order-entry, 279-286

for Web order-entry, 286-292

providing configuration information

object-specific, 273-279

system-wide, 270-272

user interface for, 270

DCOM authentication levels, 268-269

DeactivateAccountLogic.asp file, 263-264

Default Properties tab (Distributed COM Configuration Propertiesdialog box), 272

Default Security tab (Distributed COM Configuration Properties dialogbox), 272

defining

dual interfaces, 139-142

object hierarchies, 190-201

delegating

inner objects to outer objects, 113

unknown member variable, 118

developing

client/server applications, 234-244

Web order-entry applications, 251-264

DISPARAMS structure, 172

DISPID (dispatch identifier), 171

Page 381: 6935316 DCOM Microsoft Distributed Component Object Model

DisplayAccountInfoDispatch function, 158-152

Distributed COM Configuration Properties

dialog box, 270

Distributed Component Object Model. See DCOM

DllMain.cpp file, 43, 44-48

DLLs, 11, 12

double-byte character sets (DBCS), 135

dual interfaces, 128-130, 139-142

custom interfaces and, 170

handling with AccountInfoAutoDisp client, 170-185

setting IAccountInfoDispatch, 157

See also interfaces

Eearly binding, 127

encapsulating server packaging code, 40-42

entry objects

building, 201-202

for OrderEntry object hierarchy, 190

EXEMain.cpp file, 80-85

EXEs, 12, 13

exposing

class factories

for in-process servers, 37-39

for out-of-process servers, 74-77

interfaces of inner object, 112

in aggregation, 112

a type library, 143-144

Ffacility codes, 15-16

functionality

of Accounts object, 195-197

of Invoices object, 198-200

of LineItems object, 200-201

of Products object, 197-198

Gglobal.asa file, 250

GUIDs (Globally Unique Identifiers)

allocating for in-process servers, 26-27

Page 382: 6935316 DCOM Microsoft Distributed Component Object Model

COM objects and, 5

HHKEY_CLASSES_ROOT\APPID\ registry key, 273

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ole registry key,272

HRESULT data type

internal structure of, 14-15

macros, 17

return value constants for, 16-17

HTML (HyperText Markup Language)

generating user messages with HTML pages, 260

in Web applications, 248-249

HTTP (HyperText Transfer Protocol), 248

IIAccountInfoDispatch interface, 142-143

IDispatch::Invoke function

defined context constants for, 174

reading property values with, 175-185

updating property values before calling, 73

IDispatch interface

about, 126-128

ASP applications, 249-250

implementing, 145-149

member functions, 126

IDL (Interface Definition Language)

about, 27-31

of IAccountInfoDispatch interface, 139-142

intrinsic data types

supported by, 29-30

supported by Automation, 129-130

syntax for, 27

IID (interface identifier), 6-7

impersonation levels, 269

implementation locator service, 20

implementing

a class factory for out-of-process servers, 72-73

IDispatch interface, 145-149

interface methods for out-of-process servers, 70-72

object properties, 101-102

Page 383: 6935316 DCOM Microsoft Distributed Component Object Model

inner objects, 97

creating, 100-101

delegating to outer objects, 113

effects of aggregation on, 111-114

exposing interfaces of, 112

in-process servers, 25-61

about Interface Definition Language (IDL), 27-31

allocating GUIDs for, 26-27

building UserInfo, 25-26

exposing the class factory, 37-39

implementing

a class factory, 34-35

interface functions, 32-34

registering class information, 35-37

server unloading, 39-54

testing with COM client, 54-60

initializing the COM library, 55

manipulating COM objects, 56

obtaining an initial interface, 55-56

releasing COM objects, 56

uninitializing the COM library, 57-60

See also COM server

Interface Definition Language. See IDL

interface identifier (IID), 6-7

interface navigation, 7-8

interface pointers

releasing with nondelegating unknown, 118-122

setting property values with IDispatch, 171-174

storing in controlling unknown, 117

interfaces

of AccountInfo COM object, 114

of AccountInfo object, 114

for AccountInfoAutoVTBL application, 155-156

COM objects and, 6-7

custom and dual, 170

defined, 4

dual, 128-130, 139-142

IDispatch, 126-128

implementing functions of, 32-34

for objects on out-of-process servers, 66-70

Page 384: 6935316 DCOM Microsoft Distributed Component Object Model

QueryInterface function and navigation, 7-8

user interfaces for Web clients, 251, 256, 257

as VTBL, 20-21

See also dual interfaces

internal structure

of HRESULT data type, 14-15

of variants, 130-132

interoperability, 4

InvoiceInfoForm click event for OK button, 243-244

invoices, 241-244

Invoices object

functionality of, 198-200

object properties of, 193-194

isolating Automation specifics, 138-143

Item method

retrieving information with, 217-218

source code for, 218-222

Llate binding, 127

limitations

of aggregation, 122-123

of client/server application architecture, 244

of Web application architecture, 264

Line Item Information dialog box, 242

LineItems object

functionality of, 200-201

object properties of, 194-195

ListAccounts.asp file, 255

LoadTypeInfo function

functions of, 145-146

listing for, 146-147

local objects, 12

local servers, 12

LPC (local procedure calls), 21-22

Mmacros for HRESULT, 17

manipulating

Account and Account objects with NewAccount.htm file, 289

collection objects, 205-222

Page 385: 6935316 DCOM Microsoft Distributed Component Object Model

COM objects with in-process servers, 56

system registry with WIN32 APIs, 18-19

marshaling process

for out-of-process servers, 77-96

proxies and, 21

MemberInfo COM object, 98

Memberinfo.cpp file, 104-111

Memberinfo.h file, 102-104

Memberinfo.idl file, 98-102

menus for order-entry application, 233

methods

data access and manipulation properties and, 205-222

implementing interfaces for out-of-process servers, 70-72

navigation properties and, 222-229

See also methods listed individually

Microsoft Remote Procedure Call (MS-RPC) system, 267

Modify Account dialog box

retrieving account information with, 237, 257

updating accounts with, 239-241

Modify Invoice dialog box, 241-242

ModifyAccount.asp file

source code for, 261

user interface for, 259

user messages with, 260

ModifyAccount.asp page, 291

Move navigation method, 223-229

MoveFirst navigation method, 222, 224-229

MoveLast navigation method, 223-229

MoveNext navigation method, 223-229

MovePrev navigation method, 223-229

MS-RPC (Microsoft Remote Procedure Call) system, 267

Nnavigation

interface, 7-8

properties and methods for Accounts object, 222-229

network round trips, 274, 276-277

New Account window, 236, 253

NewAccount.htm file

manipulating Account and Account objects with, 289

SaveAccountLogic.asp file and, 288

Page 386: 6935316 DCOM Microsoft Distributed Component Object Model

NewAccount.htm page, 289-290

nondelegating unknown member variable, 118-122

Oobject hierarchies, 189-230

building collection objects, 202-229

about ODBC programming, 203-205

data access and manipulation properties and methods,205-222

navigation properties and methods, 222-229

building entry objects, 201-202

client/server applications and, 231-232

defining, 190-201

functionality

of Accounts object, 195-197

of Invoices object, 198-200

of LineItems object, 200-201

of Products object, 197-198

properties

of Account object, 191-192

of Invoice object, 193-194

of LineItem object, 194-195

of Product object, 192-193

object outline for UserInfoHandler COM object, 65

object properties

of Account object, 191-192

of AccountInfo COM object, 114

of AccountInfoAuto, 139, 154

for collection objects, 205-222

implementing, 101-102

of Invoice object, 193-194

of LineItem object, 194-195

for MemberInfo COM object, 98

of Product object, 192-193

for UserInfo COM object, 64

object versioning and evolution, 10-11

object-specific configuration information, 273-279

obtaining DISPID, 171

ODBC (Open Database Connectivity) API calls, 203-205

OLE, 4

order-entry applications, 234-244

Page 387: 6935316 DCOM Microsoft Distributed Component Object Model

client/server

about, 232-233

adding accounts, 234-237

adding and updating invoices, 241-244

menus for, 233

removing accounts, 241

retrieving accounts, 237-239

updating accounts, 239-241

developing Web, 251-264

adding accounts, 251-255

identifying existing accounts, 255-258

removing accounts, 263-264

updating accounts, 258-263

improving with DCOMCNFG

for client-server, 279-286

for Web applications, 286-292

See also Web order-entry applications

OrderEntry object hierarchy, 190

outer objects, 97

delegating to inner object, 113

exposing interfaces of inner object, 112

out-of-process servers, 63-96

allocating CLSIDs, 66

defined, 63-64

defining object's interfaces, 66-70

exposing the class factory, 74-77

implementing

a class factory, 72-73

interface methods, 70-72

marshaling process for, 77-96

registering class information, 73-74

unloading, 77

UserInfoHandler server, 64-65

overview

of Automation, 125

of BSTRs, 133-134

of character sets, 135-136

of client/server application architecture, 233-234

of IDispatch interface, 126-128

of ODBC programming, 203-205

Page 388: 6935316 DCOM Microsoft Distributed Component Object Model

of SAFEARRAYs, 136-138

variants, 130-133

PProducts object

functionality of, 197-198

object properties of, 192-193

property values

retrieving using IDispatch function, 175-185

setting with IDispatch interface pointer, 171-174

See also object properties

proxy

creating for out-of-process servers, 77-80

as object substitute, 21

QQueryInterface function, 7-8

Rreference counting, 8

registering

Automation objects, 149-151

class factories, 74

class information, 35-37

class information for out-of-process servers, 73-74

See also system registry

Release function, 8-10

releasing

AccountInfoAuto object, 162-163

interface pointers with nondelegating unknown, 118-122

objects in containment, 102

remote objects, 12

remote servers, 12

Remove method

deactivating accounts with, 213

source code for, 214

return value constants, for HRESULTS, 16-17

reusing COM objects, 97-123

aggregation, 111-123

delegating inner objects to outer objects, 113

delegating unknown member variable, 118

Page 389: 6935316 DCOM Microsoft Distributed Component Object Model

exposing interfaces of inner object, 112

interfaces and properties of AccountInfo object, 114

releasing interface pointers with

nondelegating unknown, 118-122

restrictions of, 122-123

storing interface pointers in controlling

unknown, 117

containment, 97-111

creating inner objects, 100-101

implementing object properties, 101-102

releasing objects, 102

rgvarg array, arguments supplied to, 172

RPCs (remote procedure calls)

network round-trips and, 276, 277

VTBL design and, 20-22

SSAFEARRAY datatype, 136-138

SaveAccountLogic.asp ASP page

NewAccount.htm and, 288

source code for, 254-255

security

configuring object-specific, 273-279

DCOM authentication levels, 268-269

single byte character sets (SBCS), 135

SQLBindCol function, 215-216

SQLBindParameter function

arguments accepted by, 208-209

C datatypes supported by, 209-210

SQL datatypes supported by, 210

stateless connections, 250

stub

creating for out-of-process servers, 77-80

using a, 21

syntax

for binary strings, 134

for IDL (Interface Definition Language), 27

system registry, 17-20

adding class information to, 35-37

as hierarchy of keys, 17-18

HKEY_CLASSES_ROOT\APPID\ registry key, 273

Page 390: 6935316 DCOM Microsoft Distributed Component Object Model

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Oleregistry key, 272

manipulating with WIN32 APIs, 18-19

registering Automation objects, 149-151

registering class information for out-of-process servers, 73-74

system-wide configuration information, 270-272

TTCO (Total Cost of Ownership), 244

type libraries

defined, 27

exposing, 143-144

Uuninitializing

the COM library for the AccountInfoAutoVTBL application,163-170

UserInfoClient.cpp file, 57-60

unloading

in-process servers, 39-54

out-of-process servers, 77

Update method, for Account object, 213

user interface

for DCOMCNFG, 270

for modifying Web accounts, 259

for Web clients, 251, 256, 257

UserInfo COM object, object properties for, 64

UserInfo.cpp file, 43, 49-54

UserInfo.def file, 43

UserInfo.h file, 43, 48-49

UserInfo in-process server, 25-26

UserInfoClient application, 54-60

UserInfoClient.cpp file, 57-60

UserInfoHandler.cpp file, 87-95

UserInfoHandler.h file, 85-86

UserInfoHandler.idl file, 66-70

UserInfoHandler object, 65

UserInfoHandler server, 64-96

Vvariants

Page 391: 6935316 DCOM Microsoft Distributed Component Object Model

internal structure of, 130-132

interpreting vt values, 132

understanding, 130-133

WIN32 API functions for, 133

VBScript

improving Web order-entry application and, 286-289

source code for AccountInfoForm, 290-291

source code for ModifyAccount.asp page, 291

source code for NewAccount.htm page, 289-290

Visual Basic, 130-133

vt variant, 131-132

VTBL binding

Automation and, 127-128

defined, 125

VTBL (virtual function table) interfaces, 20-21, 128-130

WWeb browsers

user interface for, 256, 257

in Web applications, 248

Web order-entry applications, 247-265

developing, 251-264

adding accounts, 251-255

identifying existing accounts, 255-258

removing accounts, 263-264

updating accounts, 258-263

improving with DCOMCNFG, 286-292

Web applications

limitation of architecture, 264

understanding architecture of, 248-250

See also order-entry applications

weblications. See Web order-entry applications

WIN32 API functions

for SAFEARRAYs, 137-138

for variants, 133

for working with BSTRS, 134

Table of Contents

[an error occurred while processing this directive]

Page 392: 6935316 DCOM Microsoft Distributed Component Object Model

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb isprohibited. Read EarthWeb's privacy statement.