78
Developing Documentum Desktop Client Components 9/3/2000 Documentum, Inc. Page 1 of 78 Developing Documentum Desktop Client Components Design Specification Document Version 1.16 September 3, 2000 Craig Randall

Developing Client Components

Embed Size (px)

Citation preview

Page 1: Developing Client Components

Developing Documentum Desktop Client Components 9/3/2000

Documentum, Inc. Page 1 of 78

Developing DocumentumDesktop Client ComponentsDesign SpecificationDocument Version 1.16September 3, 2000Craig Randall

Page 2: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 2 of 78

Table of Contents

Table of Contents...................................................................................... 2Table of Figures......................................................................................... 3

1 Background & Overview ........................................................................... 42 Acronyms .............................................................................................. 53 Development Platform Configuration .......................................................... 64 Interface Definition Language – A Powerful Learning Tool .............................. 85 Desktop Client Component Infrastructure.................................................... 8

5.1 Login Management ...........................................................................105.2 Event Dispatching.............................................................................135.3 Data Packaging ................................................................................205.4 Component Dispatching .....................................................................235.5 Report Management..........................................................................265.6 Icon Management .............................................................................275.7 Visual Basic Component Development Assistant .....................................285.8 Other Component Services.................................................................29

6 Creating a New Desktop Client Component.................................................306.1 Start with a Skeleton (Component Project Configuration).........................326.2 Add Muscles (Infrastructure Connections) .............................................34

6.2.1 Implementing IDcComponent ..........................................................346.2.2 Connecting to a Docbase.................................................................376.2.3 Functional Targets & Contexts – Working with Items & Item Containers ..396.2.4 Dispatching Another Component.......................................................426.2.5 Participating with Events.................................................................43

6.3 Add Skin (User Interface) ..................................................................536.4 Add Brains (Business Logic) ...............................................................546.5 Modal Versus Modeless Components (Lifetime Management)....................546.6 Setting Proper Version Compatibility....................................................55

7 Debugging Desktop Client Components .....................................................567.1 Testing Design-Time Behavior with Visual Basic .....................................567.2 Testing Run-Time Behavior with Visual C++..........................................58

8 Modifying an Existing Desktop Client Component .........................................619 Deploying Desktop Client Components.......................................................61

9.1 Creating Component Content..............................................................639.2 Signing Component Content ...............................................................659.3 Storing Component Content in a Docbase .............................................679.4 Deploying Component Content............................................................699.5 Testing Dynamic Component Delivery ..................................................719.6 Uninstalling Dynamic Component Content .............................................739.7 Configuring Dynamic Component Delivery .............................................74

Appendix A: Example VB Coding Standard .......................................................75Appendix B: Selected Bibliography .................................................................78

Page 3: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 3 of 78

Table of Figures

Figure 1. Frequently Used Acronyms.......................................................................... 6Figure 2. Required Software..................................................................................... 7Figure 3. Project References Dialog in Visual Basic ....................................................... 9Figure 4. Mapping Docoumentum Desktop Client Type Library References to Their

Implementations ............................................................................................ 10Figure 5. Looking at IDcLoginManager::Connect in VB’s Object Browser ........................ 11Figure 6. Looking at IDcLoginManager::Connect in OLEView’s ITypeLib Viewer................ 11Figure 7. Event Dispatching Interaction Diagram........................................................ 14Figure 8. Structure of a Notifier Event Code .............................................................. 14Figure 9. Looking at the Standard Implementation IDcEventDispatch::SendEvent in VB’s

Object Browser .............................................................................................. 16Figure 10. Notifier Registry Entries.......................................................................... 17Figure 11. Unanswered (False) Broadcast Query Event Dispatch Flow............................ 18Figure 12. Answered (True) Broadcast Query Event Dispatch Flow................................ 18Figure 13. Approved State Transition Broadcast Event Dispatch Flow ............................ 19Figure 14. Vetoed (Implicit or Explicit) State Transition Broadcast Event Dispatch Flow .... 19Figure 15. Looking at IDcObjectItem::ID in VB’s Object Browser .................................. 22Figure 16. Looking at IDcItems::Item in VB’s Object Browser...................................... 22Figure 17. Looking at Item IID String Constants in VB’s Object Browser ........................ 23Figure 18. Abstraction-based Component CLSID Example (DcProperties) ....................... 24Figure 19. Global Component CLSID Example (DcFind) ............................................... 25Figure 20. Looking at IDcComponent::Init in VB’s Object Browser ................................ 29Figure 21. Looking at IDcDataDictionaryUtils::GetAttributeDisplayName in VB’s Object

Browser ........................................................................................................ 30Figure 22. High-level Component Interaction Diagram................................................ 31Figure 23. VB Sample Code – Skeleton Implementation of IDcComponent...................... 35Figure 24. VB Sample Code – Simple Error Handler Using Report Manager ..................... 36Figure 25. IDcComponent::Init Parameters............................................................... 37Figure 26. VB Sample Code – Using Login Manager & DFC to Access a Docbase .............. 39Figure 27. VB Sample Code – Working with an Item Collection..................................... 40Figure 28. VB Sample Code – Working with an Item Collection..................................... 41Figure 29. VB Sample Code – Working with Item Containers ........................................ 42Figure 30. VB Sample Code – Calling Component Dispatcher ....................................... 43Figure 31. VB Sample Code – Skeleton Implementation of IDcEventSink........................ 45Figure 32. VB Sample Code – Handling Event Data within HandleEvent ......................... 50Figure 33. Mapping Event Codes to Event Data Interfaces........................................... 51Figure 34. VB Sample Code – Interfacing with the Notifier........................................... 52Figure 35. VB Sample Code – Queuing Events........................................................... 52Figure 36. Sample Component Resource Source/Definition (.RC) File ............................ 54Figure 37. Physical Desktop Client Component Flow Diagram....................................... 62Figure 38. Looking at the Cabinet Manager Type Library in VB’s Object Browser.............. 63Figure 39. VB Sample Code – Using the Documentum Cabinet Manager to Create

Component Content ........................................................................................ 64Figure 40. Selected dm_component Attributes of Interest to Dynamic Component Delivery67Figure 41. dm_component Attribute Values for Components without Content ................. 68Figure 42. dm_component Attribute Values for Components with Content ...................... 68Figure 43. ICD Properties Dialog from Windows Explorer............................................. 70Figure 44. User Interface in Windows Explorer for Internet Component Download ........... 73Figure 45. Identifying Standard Documentum Desktop Client Components Whose Source

Code Is Provided via DDS................................................................................. 74

Page 4: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 4 of 78

1 Background & Overview

Before reading further it may be useful to first understand a little of the history behind thisdocument and the development environment which it describes. Originally this documentwas written as a cookbook to kick-start the development of desktop client componentsusing Visual Basic at Documentum.

A desktop client component in Documentum 4i exists to satisfy a particular functionalrequest (e.g. “Give me the properties of the selected document,” “Check-in the selectedworkflow,” etc.). The requested function may be applied to a single object (i.e. item orfunctional target) or to a group of objects. Most desktop client components present somekind of user interface to enable human interaction. Access to desktop client componentfunctionality is based on several factors including but not limited to the object type of theselected functional target (item) and the current user’s permissions. For example, you getone component for an operation on a dm_document but another components for the sameoperation on your subtype of dm_document. (Or you may get nothing in both cases if youlack the necessary permissions on the selected functional target (item).)

Visual Basic was chosen as the language of choice for desktop client components for its easeof use and its broad acceptance in MIS development shops. Visual Basic is a higher-levellanguage than say, C++, and can allow developers to focus on their real-world businessproblems rather than lower-level details like how COM works. Documentum customersexpect a high degree of customization in Documentum client software (e.g. tailoring thestandard Documentum user interface to their specific requirements). Creating userinterfaces with Visual Basic is both straightforward and highly adaptable. So most ofstandard Documentum 4i Desktop Client components are written in Visual Basic to shortenthe time customers need to write custom solutions and to increase the level to which theycan customize Documentum standard solutions.

I did say most components. There are some cases where the underlying functionality of acomponent is not expected to change or encapsulates some core Documentum logic thatshould not be exposed externally. In these cases, Visual Basic is not the right tool for thejob. Instead Microsoft’s Active Template Library (ATL) is employed to produce lightweight,high-performance COM components that expose this core logic to Visual Basic componentsthrough standard COM interfaces. This is not meant to imply that Visual Basic componentsare slow; they’re actually quite efficient.

It’s been said that C++ (ATL’s implementation language) is the assembly language of COM.Lower-level programming is a balance between increased control and increased developerresponsibility. By writing core Documentum desktop client logic at a lower level means thatyou should experience a greater degree of flexibility and performance in the desktop clientcomponent development environment. You too are free to leverage this lower-level power inyour own components, through ATL for example. However, given that Visual Basic solutionsperform nearly as well, I think that you’ll prefer giving up those last few percentage pointsof performance in favor of shrinking your custom desktop client development substantially.Visual Basic allows you to focus more on your business problems rather than theinfrastructure of your solution.

Page 5: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 5 of 78

This document was written to provide developers with a formal design specification coveringthe overall architecture in which desktop client components operate. It documents thestandard COM interfaces through which services are provided to desktop client componentssuch as event dispatching and Docbase connectivity, as well as the COM interface which alldesktop client components are expected to implement (IDcComponent – section 6.2.1).

After reading this document you should be able to either customize a standard Documentum4i Desktop Client component or create a brand new desktop client component of your own.

This specification assumes that you are familiar with COM and Visual Basic (both thelanguage and the integrated development environment (IDE)). For more information onthese technologies, please consult the bibliography at the end of this document. Futurerevisions to this document may expand on the required knowledge base (e.g. to includeATL).

2 Acronyms

Here is an alphabetized list of the most frequently referenced acronyms, with theirexpanded meanings. From this point forward, the document gives preference to acronymsto minimize text.

Acronym MeaningATL Active Template Library (Microsoft)COM Component Object Model (Microsoft)– a binary

specification, a common library and a way of thinking aboutsoftware development as interacting components whereinterfaces (contracts) are everything.

DART DocApp Run-Time – “deliver the right functionality to theright person at the right time.” This Documentum system(infrastructure) entity interfaces with the desktop clientComponent Dispatcher. DART discovers the componentsthat the Component Dispatcher instantiates andmanipulates (i.e. calls the component’s IDcComponentmethods).

DCD Dynamic component delivery – mechanism that allows adeveloper to store component content in a Docbase thatenterprise end users may receive when requested, if therequest is qualified by the system

DDS Documentum Developer StudioDFC Documentum Foundation ClassesDISPID Dispatch ID or Procedure IDDLL Dynamic link libraryEXE Executable processGUID Globally unique identifier (pronounced “gwuid” like “squid”)

- 128-bit integers that can be reduced to readable, 32-digithexadecimal numbers. The uuid IDL keyword is equivalentto a GUID. IIDs (interface identifiers), CLSIDs (classidentifiers), LIBIDs (type library identifiers) are all specifickinds of GUIDs.

ICD Internet Component Download – a COM-based technologyfrom Microsoft

Page 6: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 6 of 78

IDE Integrated development environmentIDL Interface definition language – used to unambiguously

define COM entities like interfaces, coclasses, and typelibraries

IE Internet Explorer (Microsoft) – Web browserMIDL Microsoft IDL compiler (sounds like “middle”) – run your

IDL file through MIDL to produce type libraries andmarshaling code

OS Operating systemPID Process identifier - Until the process terminates, the

process identifier uniquely identifies the process throughoutthe system. Note that NT reuses process and thread IDs.

ProgID Programmatic identifier – human readable version of aGUID

UI User interface – in the context of the desktop clientcomponent focus of this document, this is what an end usersees and manipulates. A similar acronym GUI stands for“graphical user interface.”

VB Visual Basic (Microsoft) – a popular developmentenvironment featuring a high-level programming languageof the same name. VB4, VB5 and VB6 refer to specificversions of the Visual Basic development environment. VBAstands for “Visual Basic for Applications” and is a subset ofthe Visual Basic language. VBScript—one scripting languageof many—is a subset of VBA.

VC++ Visual C++ (Microsoft)

Figure 1. Frequently Used Acronyms

3 Development Platform Configuration

Before you can begin creating or modifying desktop client components, you must configureyour development platform to support such development. You will need to install at leastthe required software in the following list:

SoftwareRecommendedVersion Function

Windows NT 4.0 with Service Pack5 or above appliedand no DesktopUpdate (via InternetExplorer 4.x)

(Preferred) Suggested operatingsystem for shell integrationdebugging; other supportedplatforms are Windows 9x and NT4.0 with the Desktop Update, andWindows 2000

DocumentumDesktop Client

Release 4.1 or later (Required) Provides, among otherthings, the desktop clientenvironment including the standardDocumentum desktop clientcomponents, various componentservices, DFC, DART, special purposewidgets, etc.

Page 7: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 7 of 78

DocumentumDeveloper Studio

Release 4.1 or later (Required) Registers desktop clientcomponents with the Documentumsystem (e.g. DART).

Visual Basic 6.0 with Visual Studio6.0 Service Pack 4(VS6 SP4) or laterapplied

(Required) Primary componentdevelopment and design-timedebugging environment

Visual C++ 6.0 with VS6 SP4 orlater applied

(Optional/Preferred) Run-timecomponent debugging environment(e.g. using EXPLORER.EXE—Windows Explorer—as the parentprocess rather than VB)

OLEView.exe 2.10.057 (9/14/98) (Optional) This is a very useful utilityto look at the Windows registry fromthe perspective of a COM developer(e.g. translate ProgIDs to CLSIDs).Get your copy athttp://www.microsoft.com/com/resource/oleview.asp.

Regedit.exe (32-bitversion)

Whatever comesstandard with thelatest version of NT4.0

(Optional) This tool provides a morebasic interface to the registry thanOLEView; however, it is an editor nota viewer.

Figure 2. Required Software

To repeat, this document focuses on the creation of desktop client components throughVisual Basic. Consequently, the VB IDE needs to be properly configured.

1. Launch VB and choose a Standard EXE project. You must select a project type in orderto set up the tool. Once your (registry-based) set up is complete, you won’t be savingthis project.

2. Go to Tools | Options…3. Select the Editor tab. Set all options in Code Settings active. For example, “Require

Variable Declaration” will cause

Option Explicit

to be automatically placed on the first line of all newly created forms, class modules, andregular modules. For consistency’s sake across component projects, set Tab Width to 4.

4. Set up your editing environment to your preferences (i.e. Editor Format tab).5. Select the General tab. Set Error Trapping to “Break on Unhandled Errors.” Set your

compilation mode to Full Compile by clearing the Compile on Demand checkbox. Allprojects in the environment will be compiled each time they are run, allowing you todeal with all syntax errors before you begin your real test and debug process.

6. While still on the General tab, you may wish to modify Grid Units for tighter alignmentcontrol. The default value for both Width and Height is 120 (points). Decreasing GridUnits increases alignment control; however, custom values only apply to that particulardevelopment platform. This may create cross-platform alignment issues downstream, sothere is a tradeoff involved. I recommend keeping the same value at all times for Widthand Height.

7. Exit VB.

Page 8: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 8 of 78

4 Interface Definition Language – A PowerfulLearning Tool

As I mentioned earlier in section 2, Interface definition language (IDL) is used tounambiguously define COM entities like interfaces, coclasses, and type libraries.

Documentation like this specification always intends to reflect reality. Unfortunately it isprone to error, as much as I’d like to not have to say that. On the other hand, it has alsobeen said in software development that the code never lies. Looking at the actual interfacedefinition language (IDL) for a particular COM interface gives you the absolute truth aboutregistered interfaces, which can also be seen as active contracts in the system.

You can view IDL for all Documentum Desktop Client interfaces listed in this documentthrough the freely available OLEView tool from Microsoft referenced above in section 3.OLEView features a type library viewer which essentially reverse engineers IDL from a typelibrary. You access the viewer by launching OLEView, expanding the Type Libraries folder inits tree pane, selecting the type library entry of interest and double-clicking the entry.

IDL should be important to you as a component developer for the following reasons:- Documentum’s desktop client environment is based on COM- Interfaces are everything to COM (i.e. COM doesn’t care about the underlying

implementation of an interface)- Interfaces can be thought of as contracts between a client and an implementation- Ambiguous contracts are bad, both in COM and in real life- IDL (interface definition language) allows you to unambiguously specify COM interfaces

and other COM entities in a language independent manner

The beauty of using VB to develop components is that you don’t have to actually write anyIDL yourself—VB does this all for you. However, when in doubt about any aspect of aninterface, consult the original IDL for the definitive answer!

5 Desktop Client Component Infrastructure

As with most kinds of components, Documentum desktop client components don’t operatein a vacuum. They require the services of other system components and provide their ownset of services to their clients. After you’ve installed the desktop client on your developmentmachine, you can get a high-level view of the environment by launching VB, opening anykind of project (e.g. “Standard EXE”) and displaying its Project References dialog (viaProject | References…) as shown in Figure 3 on the next page.

Page 9: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 9 of 78

Figure 3. Project References Dialog in Visual Basic

As you can see there are a host of services available to desktop client components. Theseinclude login management, event dispatching, data packaging and component dispatching.Figure 4 shows a map of each selected reference to its implementation and what thatimplementation exposes and/or implements in the way of COM objects and COM interfaces.(“XXX” implies a series of related interfaces.)

Reference Module Objects Interfaces"Documentum CabinetManager 1.0 Type Library"

DCCABMGR.DLL DcCabCreatorDcCabInstallerDcCabResolver

IDcCabCreatorIDcCabInstallerIDcCabInstaller2IDcCabResolver

"Documentum ComponentDispatcher 1.0 TypeLibrary"

DCCMPDSP.DLL DcComponentDispatcher IDcComponentDispatcherIDcComponentDispatcher2

"DcDocumentManager 1.0Type Library"

DCDOCUMENTMANAGER.TLB DxDocMgrWrapper IDxDocMgrWrapper

"Documentum EventServer 1.0 Type Library"

DCEVTSRV.EXE DcNotifier IdcEventDispatchIdcEventDispatch2IDcEventQueueIDcEventSinkIDcXXXEventData

"Documentum Find TargetServer 1.0 Type Library"

DCFINDTARGET.DLL DcFindTargetObj IDcFindTargetIDcRunQuery

"Documentum FoundationClasses Type Library(<mm/dd/yy>)"

DFC.TLB Numerous – see DFCdocumentation for moredetails

Numerous – see DFCdocumentation for moredetails

"Documentum IconManager 1.0 Type Library"

DCICONMGR.DLL DcIcon IDcIcon

"Documentum Item Server1.0 Type Library"

DCITEMSERVER.DLL DcItemsDcXXXItem

IDcItemsIDcXXXItem

"Documentum LoginManager 1.0 Type Library"

DCLOGINMGR.DLL DcLoginManager IDcLoginManager

"Documentum ProgressSentinel 1.0 Type Library"

DCPROGRESSSENTINEL.EXE Progress _Progress

"Documentum ProgressMonitor Component 1.0

DCPROGRESSMONITOR.DLL DcProgressMonitor IDcProgressMonitorIDfOperationMonitor

Page 10: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 10 of 78

Reference Module Objects InterfacesType Library""Documentum ReportManager 1.0 Type Library"

DCREPORTS.DLL DcReport IDcReport

"Documentum DesktopClient Utilities Manager1.0 Type Library"

DCUTILMGR.DLL DcDataDictionaryUtilsDcDataProtectionUtilsDcDFCSessionUtilsDcFmtPrefCacheDcODMAIntfDcRegistryKeyDcShortcutUtil

IDcDataDictionaryUtilsIDcDataDictionaryUtils2IDcDataProtectionUtilsIDcDataProtectionUtils2IDcDFCSessionUtilsIDcFmtPrefCacheIDcODMAIntfIDcRegistryKeyIDcShortcutUtil

"Documentum VBComponent DevelopmentAssistant 1.0 TypeLibrary"

DCENABLEVB.DLL None Definitions only (i.e. noimplementations):IDcComponentIDcEventDispatchIDcEventQueueIDcEventSinkIDcXXXEventDataIDcItemsIDcXXXItemIDcXXXItemContainerIDcReport

Figure 4. Mapping Docoumentum Desktop Client Type Library References to TheirImplementations

Once you establish a reference to a piece of the desktop client component infrastructure bychecking it as in Figure 3, you can quickly get a more detailed look into its services throughVisual Basic’s Object Browser (F2). Subsequent figures will present you will examples of thiskind of information.

Now that you know about the power of IDL and have a glimpse of the desktop clientinfrastructure, let’s take a closer look at some of the major component services. After thiswe’ll talk about actually developing a component that can access these services.

5.1 Login Management

The Documentum Login Manager (DCLOGINMGR.DLL) provides a standard way for desktopclient system components as well as custom components to connect to a Docbase. It isresponsible for interfacing with the singleton (i.e. only one running instance at any point intime per desktop), out-of-process Documentum Authentication Manager (DCATHMGR.EXE),which maintains a single user profile which consists of username, password and domain.The Authentication Manager will also have the name of the user's home Docbase. The homeDocbase is for the user specified in the user profile (the first user to login to any Docbase).Components should not interface directly with the Authentication Manager. Insteadcomponents should go through the Login Manager.

The Login Manager’s default COM interface is IDcLoginManager, which is implemented bythe DcLoginManager COM class. Figure 5 shows the various properties and methods ofIDcLoginManager, highlighting the basic connection method, Connect.

Page 11: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 11 of 78

Figure 5. Looking at IDcLoginManager::Connect in VB’s Object Browser

Notice in Figure 5 that Connect returns a String. This string is an identifier to a DFC sharedsession. Shared sessions in DFC require locking and unlocking to ensure thread safetywithin a particular process. We’ll cover this in more detail in section 6.2.2.

As I mentioned in section 4, OLEView can be used to learn just about anything you want toknow for a particular desktop client component or interface. Figure 6 shows the result ofinvoking OLEView’s type library viewer on the Login Manager’s type library. As you can see,there’s a significant amount of information that can be gleaned.

Figure 6. Looking at IDcLoginManager::Connect in OLEView’s ITypeLib Viewer

Page 12: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 12 of 78

Login Manager Scenarios

The Login Manager is instantiated whenever a process is started (Windows Explorer shellintegration is entered, Word or VDM starts up). The Login Manager is an in-process COMserver or DLL. When the Login Manager is instantiated, it will start DFC. The Login Managerwill allow connections to multiple Docbases by a single user. Whenever the client needs asession, it calls "Connect" optionally passing in the Docbase name, user OS name, passwordand domain (caller can pass in just Docbase, or just Docbase and userOSname, or ...).

Scenario #1 - Caller passes in Docbase, user and (optionally) domain.

Step #1:

The Login Manager will check to see if a session already exists for therequested Docbase/user/domain. If so, the session ID is returned. If not, goto Step #2.

Step #2:

The Login Manager will compare the user/domain with the user profile fromthe Authentication Manager. If it matches, Login Manager gets an encryptedpassword from Authentication Manager, logs in and returns the session ID. Ifit does not match or the user profile is empty, go to Step #3.

Step #3:

If a session exists for the Docbase (already logged in as a different user), theuser will be told that if they login to this Docbase, they will be disconnectedfrom all Docbases. If they choose to login, then all sessions will bedisconnected, a notification will be sent out and then the user will be loggedin and the session ID is returned.

If no session exists for the Docbase, go to Step #4.

Step #4:

OK, if you get here there is no session for the Docbase, and the user specifiedlogin info differs from the user profile (or the profile is empty). The Logindialog will come up prompting the user to login.

Scenario #2 - the caller only passes in Docbase name

Step #1:

The Login Manager will check to see if a session already exists to thatDocbase (user name doesn’t matter). If so, return the session ID. If not, Step#2.

Step #2:

The Login Manager will get the user profile from the Authentication Manager,log in silently and return the session ID. If the user profile is empty, go toStep #3.

Step #3:

Prompt the user to login to the specified Docbase. Return the session ID.

Scenario #3 - the caller passes in "(Home Docbase)" as the Docbasename

Step #1:

Page 13: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 13 of 78

The Login Manager asks the Authentication Manager for name of the “(HomeDocbase)”. If a home Docbase name is returned, the scenario is the same as#1 or #2 (depending on if a user name was provided). If the home Docbasename is empty, Step #2.

Step #2:

The Login Manager puts up the login dialog asking the user to log into theirhome Docbase.

Scenario #4 - the caller doesn't pass in any info. I think we would onlyhave this scenario when a component (e.g. Find) is invoked, but is not passed asession ID.

Step #1:

Get the user profile from the Authentication Manager and last logged inDocbase name. Put up the login dialog (pre-filling dialog with user profileinfo).

In all of the scenarios when the Login Manager returns a DFC session ID, it will register theDocbase/user with the Authentication Manager, another piece of desktop clientinfrastructure with whom components should not directly interface. When the LoginManager logs into the first Docbase, it will try to figure out what is the user’s home Docbaseif it can get the home Docbase name from dm_user. When the Login Manager has a sessionto the home Docbase, it will register it with the Authentication Manager. If the LoginManager can not get the user’s home Docbase from dm_user, it will continue trying to getthe home Docbase from each subsequent Docbase that it logs in to.

A Docbase may also have silent logins disabled. In this case, when attempting to log in tosuch a Docbase, the user will always be prompted with a Connect dialog, regardless of whatDocbases have been previously logged into.

5.2 Event Dispatching

The Documentum Event Server (DCEVTSRV.EXE) contains an object commonly referred toas the Notifier, which is responsible for registering and unregistering client interest in eventsand for dispatching events to specific or registered client event sinks. It acts as a singletonCOM local (or out-of-process) server. The Notifier is not a connectable object in COM terms.That is, the events it dispatches are not ActiveX events, which are also known asConnection Points.

Page 14: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 14 of 78

ES

ES

ES

Client

Notifier

Client

Figure 7. Event Dispatching Interaction Diagram

There are four processes shown in Figure 7; each is bounded by a dotted line. The Notifierbox actually represents the DcNotifier COM object as well as all of its worker threads. ANotifier client may be any of the following things: a caller of its IDcEventDispatch interface,a registered event sink (represented in the figure by an “ES”) exposing a IDcEventSinkinterface to be called by a Notifier worker thread, or both a caller and an event sink. Asingle process may contain multiple callers and or multiple event sinks.

When developing an object that implements the IDcEventSink interface in C++, it isimportant to use the free-threaded threading model. Use of other threading models mayresult in deadlocks in some situations. When using the free-threaded model, you will needto synchronize access to data by multiple thread.

Event codes are defined to include their category as shown in Figure 8. This definitionprovides for 16 event categories, each with up to 2**16 (65536) individual codes. One ofthe 16 categories is reserved for customer use (i.e. DC_EVT_CATEGORY_USER, where eventcodes in the following range may be defined by customers: DC_EVT_USER_Min <=custom_event_code <= DC_EVT_USER_MAX). The other 15 event categories are reservedby Documentum.

Category Code

31 16 15 0

long eventCode

Figure 8. Structure of a Notifier Event Code

When a client registers an event sink with the Notifier, it associates that sink with one ormore event categories. To put it another way, the Notifier dispatches events based solely ontheir category. Other than extracting category information from a 32-bit value, the Notifier

Page 15: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 15 of 78

doesn’t concern itself with event codes. A sink may be registered and unregistered multipletimes. For example, your sink may initially be interested in only a few event categories.Once it receives a particular event code, however, the sink’s interest grows and it thereforeregisters for the additional categories of interest.

When an event sink is first registered with the Notifier, RegisterEventSink is called and apointer to its IDcEventSink implementation is passed. In return, the Notifier returns acookie. This cookie should be stored as a data member by the client object. It must bepassed to the Notifier in all subsequent calls to RegisterInterest and UnregisterInterest.Event sink cookies may also be specified in both SendEvent and HandleEvent.

Event categories are maskable, which means that you may “or” together a set of categoriesin RegisterEventSink, RegisterInterest and UnregisterInterest calls (e.g.DC_EVT_CATEGORY_OBJECT Or DC_EVT_CATEGORY_SESSION). If you are interested in allDocumentum event categories or all categories, no exclusions, you may use theDC_EVT_CATEGORY_ALL_DCTM or the DC_EVT_CATEGORY_ALL enumeration, respectively.Often a client will specify DC_EVT_CATEGORY_ALL in its final UnregisterInterest call to becertain that the event sink is completely unregistered. There is no error in unregisteringfrom a category that you are not currently registered.

There are four types of Notifier-based events: notifications, cancellations, queries and statetransitions.• Notifications are sent to inform the system that a planned or unplanned event has

happened.• Cancellations are sent to inform the system that a planned event has not happened.• Queries are sent to ask the system if something is true.• State transitions are sent to gather system approval for an event to occur. System

elements may either veto the state transition or approve it. Once a system elementapproves a state transition, it plans for the actual event to occur (e.g. puts itself into apending state). Any event code that is associated with a state transition event type mustalso be associated with both notification and cancellation event types. Figures 13 and 14show the connection among these event types depending on the outcome of an eventdispatch involving a state transition.

Page 16: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 16 of 78

Figure 9. Looking at the Standard Implementation IDcEventDispatch::SendEvent inVB’s Object Browser

For each call to IDcEventDispatch::SendEvent (Figure 9), the Notifier will create a separateworker thread for each event sink to receive the event. If the destination input parameter isset to DC_EVT_COOKIE_BROADCAST, all event sinks with registered interest in theunderlying event category will have their IDcEventSink::HandleEvent methods called (i.e. abroadcast has been specified). If destination is a valid event sink cookie, the associatedevent sink interface must have previously registered interest in the event categoryassociated with the event of the caller, in order to have its HandleEvent called.

For increased responsiveness to its callers, the Notifier and its worker threads are all free-threaded, which means that they all operate in the single MTA (multi-threaded apartment)of the DCEVTSRV.EXE process. Consequently, response time between caller and Notifierdepends upon the type of event to be sent and possibly registered event sink behavior.

An event sink should only react to all notifications and cancellations it receives. For thisreason, the Notifier will return back to its caller following the creation of worker threads,regardless of the interaction between a worker thread and its subject event sink interface.As a result, callers dispatching notifications or cancellations should not assume that thebroadcast has fully completed (or even begun) before control returns to it from the Notifier.

Queries and state transitions are handled differently. With these types of events, eventsinks are involved in the dispatch process. In order to better manage such dispatches, theNotifier keeps track of how long it takes each event sink to respond. By default the Notifierwill declare timeout on a query event after one second and after 15 seconds on a state

Page 17: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 17 of 78

transition. These timeout values, expressed in milliseconds, may be overwritten in theregistry as shown in Figure 10. By default the QueryTimeout and StateTransitionTimeoutvalues are not created in the registry, which causes the Notifier to use its internal defaults.

Figure 10. Notifier Registry Entries

Notifier clients can expect the standard response message format returned by Notifier fromSendEvent to be as follows: “[who] what” (e.g. “[Microsoft Word97] did not respond in timeto the specified event.”). The Notifier establishes “who” from the sink name providedpreviously in RegisterEventSink. The sinkName parameter should have the same value asthe Name property of the event sink being registered. If the response is internal or no sinkname has been provided, “who” will become “Documentum Notifier.” “What” is establishedeither through the response string of the driving event sink or by the Notifier itself. Forexample, if a state transition times out, the Notifier will define “what” to be “did not respondin time to the specified event.”.

Please observe the following rules to help ensure response message consistency:

1. All sinks should not capitalize the responseMsg strings they return in HandleEvent.2. All sinks should treat the responseMsg strings they return in HandleEvent as the latter

part of a sentence. That is, given "who" the complete message will form a grammaticallycorrect sentence (e.g. end with a period).

3. All registered sinks should provide a meaningful/useful IDcEventSink::Name property for"who" via IDcEventDispatch::RegisterEventSink (sinkName). Processes with multiplesinks should apply extra care in their naming of each sink (e.g. not ...1, ....2, etc).

4. Notifier will follow rules (1) and (2) for all messages it defines and returns to its clients(e.g. timeout, COM errors, etc.).

Notifier Scenarios

Perhaps it would be useful to diagram some event dispatch flows for queries and statetransitions. Figures 11 through 14 depict the flow of control among a client, the Notifier andregistered event sinks during a broadcast event. There are certainly many more variationsthan those represented here; however, you should be able to deduce how the flows operatebased on these four diagrams.

Please note that VB’s Boolean data type is the same as the VARIANT_BOOL IDL/C++ datatype. True translates to VARIANT_TRUE (0xFFFF); False translates to VARIANT_FALSE(zero).

Page 18: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 18 of 78

Client Notifier Sink1 Sink2 Sink3time SendEvent

HandleEvent

HandleEvent

HandleEvent

returns

Figure 11. Unanswered (False) Broadcast Query Event Dispatch Flow

Key points from Figure 11:

1. Event sinks answering “False” to a query should set their HandleEvent response outputparameter to VARIANT_FALSE (False) before returning from HandleEvent.

2. Notifier continues its broadcast until a sink answers “True.”a. Notifier manages timeouts only to keep the ball rolling.b. Upon timeout detection, the Notifier will no longer wait on the offending event sink.

That is, the Notifier is not at the mercy of ill-behaved sinks.3. Client should react based on SendEvent's rv output. rv should always equal

VARIANT_FALSE (False) in this case (broadcast). Client may optionally use responseMsg.

Client Notifier Sink1 Sink2 Sink3time SendEvent

HandleEvent

HandleEvent

returns

Figure 12. Answered (True) Broadcast Query Event Dispatch Flow

Key points from Figure 12:

1. In this example Sink2 answers “True” the query by setting its HandleEvent responseoutput parameter to VARIANT_TRUE (True) before returning from HandleEvent. Sink2may also optionally define responseMsg for Client to display additional information.

2. Notifier stops its broadcast upon finding an answer of “True” and immediately returnsinformation defined by Sink2 to Client—response (HandleEvent) becomes rv(SendEvent).

3. Client should react based on SendEvent's rv output. Client may optionally useresponseMsg.

4. Although not asked, Sink3 may have also answered "True" for this event.

Page 19: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 19 of 78

Client Notifier Sink1 Sink2 Sink3time

HandleEvent

HandleEvent

returns

SendEvent - notification (same event code)

HandleEvent

HandleEvent

HandleEvent

returns

SendEvent - state transitionHandleEvent

Figure 13. Approved State Transition Broadcast Event Dispatch Flow

Key points from Figure 13:

1. Event sinks granting permission to transition their state should enter their own “pendingmode” and set response to VARIANT_TRUE (True) before returning from HandleEvent.

2. Notifier continues its broadcast until someone vetoes the event. Timeouts are treated asimplicit vetoes.

3. Based on SendEvent's rv output of VARIANT_TRUE, the client should:a. Perform its operation, thenb. Call SendEvent specifying DC_EVT_TYPE_NOTIFICATION (i.e. same event code as

the original state transition but sent this time as a notification)4. Follow-up notification initiated by Client allows each sink to clear its pending mode (i.e.

the event has indeed occurred).

Client Notifier Sink1 Sink2 Sink3time SendEvent

HandleEvent

HandleEvent

returns

HandleEvent

HandleEvent

statetransitiondispatch

cancellationdispatch (sameSendEvent)

Figure 14. Vetoed (Implicit or Explicit) State Transition Broadcast Event DispatchFlow

Page 20: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 20 of 78

Key points from Figure 14:

1. In this example Sink2 vetoes the state transition. It could have done so explicitly bysetting response to VARIANT_FALSE (False) before returning from HandleEvent orimplicitly by not returning from HandleEvent before the Notifier times out on itsHandleEvent call.

2. Notifier stops its broadcast upon encountering a veto (explicit or implicit).3. Upon timeout detection, the Notifier will no longer wait on the offending event sink. That

is, the Notifier is not at the mercy of ill-behaved sinks.4. For each contacted event subscriber, Notifier invokes its HandleEvent method with a

DC_EVT_TYPE_CANCELATION (i.e. same event code as the original state transition butsent this time as a cancellation).

5. Follow-up cancellation initiated by Notifier allows each sink to clear its pending mode(i.e. the event has indeed not occurred).

6. After the second broadcast, Notifier returns information defined by Sink2 to Client—response (HandleEvent) becomes rv (SendEvent).

7. Based on SendEvent's rv output of VARIANT_FALSE (False), Client should not performits operation. It may optionally use responseMsg to tell its user why not.

8. Although not asked, Sink3 may have also vetoed this state transition (explicitly orimplicitly).

As shown previously in Figure 9, SendEvent expects an input Long value for pid. Pid shouldbe set by the caller to its process ID via the Win32 API GetCurrentProcessId. The Notifierpasses pid from SendEvent to the HandleEvent method of the appropriate event sink(s). Asignaled event sink can then compare the incoming pid with its own process ID to see if theevent came from the same process. Such information is useful, for example, if process-specific caching is involved. All event sinks as well as the Notifier expose read-only PIDproperities that allow other elements of the event dispatching system to learn more aboutcurrent system process distribution.

5.3 Data Packaging

An object that is aware of what its end user wants to act upon (e.g. I want to delete thesefive documents…”) typically creates functional targets and places them into a collection. Thiscollection is then passed to the functional component by way of Component Dispatcher. Thefunctional component extracts each functional target (item) from the collection and actsupon it, eventually returning control back to the collection’s creator (also known as an “itemcontainer”). Figure 22 in section 6 shows this flow graphically.

This section covers the COM interfaces used within the desktop client to support the passingof functional targets (items) from one place to another. There are currently three kinds ofinterfaces related to data packaging in the desktop client as follows: item (functional target)interfaces, item collection interface, and item container (functional context) interfaces.

Item Interfaces and the Item Collection Interface

Currently there are eight different item interfaces in the Desktop Client:• IDcAbstractionItem (target is a UI abstraction like “Documentum”)• IDcFileItem (target is a file),• IDcInboxItem (target is an Inbox item),• IDcObjectItem (target is a Docbase object),• IDcRenditionItem (target is a rendition),

Page 21: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 21 of 78

• IDcVirtualDocumentItem (target is a virtual document),• IDcNewDocumentItem (target is information required to create a new document),• IDcInvalidObjectItem (target is a Docbase Object)

Item interfaces may be derived from one another (e.g. IDcInboxItem derives fromIDcObjectItem) and more than one item interface may be implemented by a single object(e.g. DcFileObjectItem).

Here are some specific comments concerning IDcXXXItem interfaces:

1. The purpose of these interfaces is to uniquely identify an item in its container (i.e. afunctional target in a particular functional context). For example, “I want to getproperties on the selected folder from within Windows Explorer.” In this example theselected folder is the functional target, Windows Explorer is the functional context andthe properties is the function. The functional context (item container interface) mayprovide additional information to the component that is common for all items it receivedthrough IDcComponent::Init (e.g. document object ID for all renditions).

2. Given a session ID and an object ID, DFC can give you other attributes through fetchingan object. Either the item itself will provide the object ID (i.e. IDcObjectItem) or theitem container will provide it to the component. The component can obtain a session IDvia the Login Manager through IDcComponent::Init inputs.

There is one item collection interface, IDcItems.Here are some specific commentsconcerning theIDcItems interface:

1. An item collection maintains member type integrity through its Type property and Addmethod. Currently there are four basic rules to observe when specifying collectionmember (item) type or adding items (functional targets). The following rules aresubject to change:a) You cannot add an item to an item container of an unknown type. First set the Type

property of the item container, then Add items.b) You cannot specify a string for which there is no IID.c) You cannot add an item to an item container if the item's type doesn't match the

collection's type.d) You cannot change a non-empty collection's member type.

2. An IDcItems pointer is passed from a functional context to Component Dispatcher (viaits IDcComponentDispatcher interface) and from Component Dispatcher to the functionalcomponent (via its IDcComponent interface). That is, the collection is passed, notindividual items (functional targets). Even if only one item is involved, it must becontained in a collection.

3. IDcItems is defined as a standard collection interface so that VB programmers will beimmediately familiar with its access.

4. Item collections are one-based; that is, their index starts at one and grows from there.Trying to access an item at subscript/index zero will result in an error.

The Documentum Item Server (DCITEMSERVER.DLL) provides all clients with a standardimplementation of each standard item interface and the standard item collection interface.It also provides a set of string constants associated with the standard item interfaces sothat developers can avoid typing the wrong IID. Figures 15 through 17 show variousservices provided by the Documentum Item Server.

Page 22: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 22 of 78

Figure 15. Looking at IDcObjectItem::ID in VB’s Object Browser

DcAbstractionItem implements IDcAbstractionItem; DcFileItem implements IDcFileItem;and so on. DcFileObjectItem is an example of a COM object that implements more than oneitem interface. In this case DcFileObjectItem implements both IDcFileItem (its defaultinterface) and IDcObjectItem. The DcItemType enumeration is currently used withabstraction items (i.e. the IDcAbstractionItem::Type property).

Figure 16. Looking at IDcItems::Item in VB’s Object Browser

Again, please note that the Index parameter of the Item property to the DcItems objectshown above in Figure 16 starts with a value of one (not zero) and grows up to Count.

Page 23: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 23 of 78

Figure 17. Looking at Item IID String Constants in VB’s Object Browser

Hopefully you’ll agree that it’s much easier to enter “DC_OBJECT_ITEM_IID_STRING” than itis to remember a string of 32 hexadecimal characters!

Item Container Interfaces

Item container interfaces provide a way for components to find out about the context oftheir invocation. Note however that this kind of interface is an optional parameter in bothIDcComponentDispatcher::RunComponent and IDcComponent::Init, which means that anobject that is initially aware of what its end user wants to act upon may not expose itself tothe object performing the action itself.

Think of item container interfaces as a way to provide components with a set of propertiesthat all apply to all items. Design your item container interfaces to complement standarditem interfaces and any custom item interfaces you develop. Item interface propertiesshould be reserved for values that differ from one item to another. You can use theVB Assistant mentioned in section 5.7 to view the current set of standard item containerinterface definitions.

5.4 Component Dispatching

The Documentum Component Dispatcher (DCCMPDSP.DLL) is responsible for creatingfunctional components like yours to satisfy a particular functional request. The ComponentDispatcher interfaces with the DocApp RunTime (DART) and the Cabinet Manager(DCCABMGR.DLL) to deliver the right functionality to the right person at the right time.DART discovers the right component to satisfy a functional request; the Cabinet Managerdelivers and registers the component if necessary; and the Component Dispatcherinstantiates and manipulates the component (i.e. calls its IDcComponent methods).

The only direct client of any component in the Documentum Desktop Client system shouldbe the Component Dispatcher. That is, desktop client components should only be

Page 24: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 24 of 78

instantiated and accessed through IDcComponent by the Component Dispatcher. TheComponent Dispatcher together with DART enforces your company’s business rules,providing the right component for each request. If there are insufficient privileges toexecute the request, no component will be invoked. Hard-wiring a direct connectionbetween application and component or between one component and another componentmay violate policy.

Documentum Developer Studio (DDS) is the tool that you will use to make the Documentumsystem (i.e. DART) aware of your functional component according to the business logic youdefine (e.g. ACLs, document life cycle stages, etc.). Details about DDS as well as detailsconcerning how DART ends up with a component to discover from a particular Docbase areoutside the scope of this document.

In general, the Component Dispatcher will use DART to get the CLSIDs for components thatit needs to invoke. There are a few exceptions as follows in which cases the ComponentDispatcher will instead use the Windows Registry to acquire component CLSIDs:

1. Functional target (selected item) is an abstraction. An abstraction is something exposedby the desktop client UI that doesn’t resolve to a physical object within the system.

2. The user is not yet logged in to any Docbase and the requested functional class is for aglobal component. A global component is one that is not specific to an object type.Components that are meant to be run for particular object types are not global (e.g.DcCheckIn, DcCheckOut, etc).

3. The user is working offline.

Functional Target Is an Abstraction

Example abstractions include “Documentum,” “Docbases,” “Inbox,” “Local Files,” and “MyCabinet.” When an abstraction is specified as the target of a function, ComponentDispatcher will look for the associated functional class name under \HKEY_LOCAL_MACHINE\SOFTWARE\Documentum\Components. Figure 18 shows which registry key the ComponentDispatcher looks up for Properties component CLSIDs, matching the name of the selectedabstraction with a registry value.

Figure 18. Abstraction-based Component CLSID Example (DcProperties)

Global Component Is Requested without a Docbase Connection

An example global component is the Find component (i.e. the component associated withthe DcFind functional class). We need to be able to invoke the Find component even if theuser has not yet logged in. If no Docbase/user OS name/domain is passed intoIDcComponentDispatcher::RunComponent, the Component Dispatcher will look in theregistry for the CLSID of the component associated with the requested functional class.

Page 25: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 25 of 78

Figure 19 shows which registry key the Component Dispatcher looks up for the Findcomponent’s CLSID, which is the definition of the “(Default)” registry value.

Figure 19. Global Component CLSID Example (DcFind)

Here’s how to add a new global component to your local Windows Registry. You’ll still needto use DDS to establish your component in your enterprise.

1. Launch REGEDIT.EXE.2. In the tool’s left-hand side tree pane, drill down on HKEY_LOCAL_MACHINE\SOFTWARE\

\Documentum\Components.3. Select Components.4. Select Edit | New -> Key and replace the default selection with the name of your new

functional class. Standard Documentum functional class names take the formDc<function> (e.g. DcFind, etc.).

5. Select the key of the functional class in REGEDIT’s tree pane then double-click on the(Default) value in its content pane. In the “Value data” field of the “Edit String” dialogadd the CLSID of your component including delimiting curly braces (e.g. {46148CE5-234C-11D2-A08D-00104B72FD7F}), replacing any previous string value. You canuse a tool like OLEView to obtain your component’s locally registered CLSID.

User Is Working Offline

If the selected object is a local or working file, the Component Dispatcher will first try to finda component in the registry that is specific to the type of the object selected. For example,if “\HKEY_LOCAL_MACHINE\SOFTWARE\Documentum\Components\DcProperties\Offline:dm_document=<CLSID>” is defined in the registry, the component associated with CLSIDwill be invoked to provide offline properties on dm_document objects. You may havemultiple components for a functional class when they’re working offline each based on aparticular object type. If no type-specific registry entry is found, the Component Dispatcherwill look for a default entry (e.g. \HKEY_LOCAL_MACHINE\SOFTWARE\Documentum\Components\DcViewOptions\Offline: (Default)=<CLSID>). The default CLSID impliesoffline operations are supported by the associated component for all object types notspecifically listed in the registry. If no Offline registry key is found for a particular functionalclass, the function in question does not support offline operations.

Here’s how to add a new component that supports offline operations to your local WindowsRegistry. You’ll still need to use DDS to establish your component in your enterprise.

1. Launch REGEDIT.EXE.2. In the tool’s left-hand side tree pane, drill down on HKEY_LOCAL_MACHINE\SOFTWARE\

\Documentum\Components.3. Select Components.

Page 26: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 26 of 78

4. If you want to associate your component with a new functional class, go to the nextstep; otherwise proceed to (6).

5. Select Edit | New -> Key and replace the default selection with the name of your newfunctional class. Standard Documentum functional class names take the formDc<function> (e.g. DcProperties, etc.).

6. If necessary, select the key of the functional class in REGEDIT’s tree pane then selectEdit | New -> Key and replace the default selection with “Offline.”

7. If you want to associate your component with an entire functional class as the defaultoffline component, go to the next step; otherwise proceed to (9).

8. Select the Offline key in REGEDIT’s tree pane then double-click on the (Default) value inits content pane. In the “Value data” field of the “Edit String” dialog add the CLSID ofyour component* including delimiting curly braces (e.g. {57DA2981-9A05-11D2-80FA-00105A1F0288}), replacing any previous string value.

9. If you want to associate your component with one or more specific types under aparticular functional class (which you may or may not want to do in addition toestablishing your component as the default), do the following:a. Select the Offline key in REGEDIT’s tree pane.b. If you want to associate your component with a new type, go to the next step;

otherwise if you want to associate your component with an existing type, go to (d).c. Select Edit | New -> String Value Key and replace the default selection with the

name of your new type.d. Double-click on the desired string value. In the “Value data” field of the “Edit String”

dialog add the CLSID of your component* including delimiting curly braces (e.g.{57DA2981-9A05-11D2-80FA-00105A1F0288}), replacing any previous stringvalue.

e. Repeat this process for all types of interest under the selected functional class.

* VB hides COM implementation details from you the developer; however, you can useOLEView, for example, to gather such COM-related information as your component’s CLSID.

Application Name

The Component Dispatcher expects an application name as input to both RunComponent,RunComponentEx, and GetComponentCLSID methods in its IDcComponentDispatcherinterface. In most cases, the Component Dispatcher clients will specify the “default”application, which is the DocApp that is supposed to be installed on every server. If noapplication name is supplied, the Component Dispatcher will use “DcDesktopClient” as theapplication name. DcDesktopClient is the DocApp that specifies Documentum 4i DesktopClient components.

Note that the applicationName parameter is only used by the Component Dispatcher when aglobal component such as DcFind is being invoked or when an item from the “Applications”menu is selected. Application Name is not used if the component is type-specific (e.g.DcCheckIn).

How to create DocApp’s is beyond the scope of this document.

5.5 Report Management

The Documentum Report Manager (DCREPORTS.DLL) provides a way for your componentsto display standard Documentum error, warning, and information dialog boxes to end users.

Page 27: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 27 of 78

Use these in situations where your component is unable to complete an action requested bythe user.

Users should be informed of problems, and in some cases their input is required beforeproceeding. The Report Manager simplifies and standardizes this process by allowing yourcomponents to store all the information necessary to display a dialog box in a report object(DcReport).

Many desktop client services take an IDcReport interface pointer as input. For example,when your desktop client’s Init method is called, it receives a report object via the reporterinput parameter. Your component should use this report object to register and, if necessary,display errors. If your component involves dispatching other components to satisfy aparticular functional request, it can pass this same report object to Component Dispatchervia RunComponent. The Component Dispatcher, in turn, will pass the same report object toeach component is dispatches via the component’s Init method.

A report object maintains the stack of errors. There is no size limitation on this error stack.When a report object is asked to display itself, its error stack will be cleared as a result.When the user interaction is finished, it is your responsibility to destroy the report object ifyou created it.

Your component sometimes need only to notify the user of something, but at other times itmay require feedback from the user before proceeding. The Report Manager currentlyprovides five types of dialog boxes: Continue/Stop, Continue/Stop/Undo, Continue/Undo,OK/Cancel and OK only. By default, the first and last errors in the error stack are displayed,and the user can view the remaining errors by clicking the Show Details button. You mayalso configure the report object to display only the first error.

The OK only dialog box is the most basic since no information is needed from the user.Clicking the OK button dismisses the dialog box.

If an error occurs while a user is performing a delete operation on several items, yourcomponent should allow the user the option of stopping or continuing the operation. Forexample, a user might request that five documents be deleted, but not have sufficientpermissions to delete the second document. A user might not want to delete the last threedocuments when your component informs them that it cannot delete the second document.To determine if the user wants to continue with the operation, display a Continue/Stopdialog box in these situations.

An error might occur while performing an operation that is part of a transaction. The natureof the error might affect the user's decision to continue the transaction, stop the transactionwhere it is at, or undo all the operations in performed so far in the transaction. Todetermine which course of action the user desires, display a Continue/Stop/Undo dialog box

Note: The Report Manager does not perform the functionality behind the Stop, Continue,and Undo buttons. Your component must implement the functionality behind each of them.

5.6 Icon Management

The Documentum Icon Manager (DCICONMGR.DLL) provides a central location and deliverymechanism for both standard desktop client icons and custom icons specific to yourapplication.

Page 28: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 28 of 78

When you create a new object type or format as part of an application, you will probablywant to assign a new icon, for example, to represent it in the desktop client's integrationwith Windows Explorer and in dialog boxes featuring operations on the item. Componentsthat want to display a Documentum-provided icon must include the resource file containingall Documentum icons. When you create new icons, they must be added to theSystem\Images folder in the Docbase featuring the new object type or format and saved assysobjects. The Icon Manager is responsible for loading icons stored in this location with theDocumentum-provided icons and providing methods for retrieving these icons at runtime. Ifthe Icon Manager is unable to find a specified icon in the resource file, it traverses up theobject type hierarchy and uses the parent's icon.

If you create an icon in multiple sizes, include them all in the same icon file. Please use a256-color palette when creating custom icons Finally, adhere to the following guidelineswhen naming icons:

1. Start the icon name with the associated object type or format.2. If an icon is also associated with a format, append "_formatname" after the format.3. If the icon represents an object's open or closed state, append "_Opened" or "_Closed."4. The filename extension must be .ico.

5.7 Visual Basic Component DevelopmentAssistant

The Documentum Visual Basic Component Development Assistant (DCENABLEVB.DLL) isdesigned to simplify the life of a VB programmer developing desktop client components. TheVB Assistant doesn’t actually implement anything but instead exposes such things as COMinterface definitions and COM enumerations. In other words, DCENABLEVB.DLL is essentiallyjust a type library. Figure 20 shows that its type library exposes quite a lot of information.

Page 29: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 29 of 78

Figure 20. Looking at IDcComponent::Init in VB’s Object Browser

5.8 Other Component Services

The Documentum Desktop Client Utilities Manager (DCUTILMGR.DLL) provides a toolbox ofutilities designed to simplify support of various functions in your components. Currently thisserver provides an object that supports exposure of Data Dictionary functionality, an objectthat helps to protect unsaved changes from being lost, and an object that supports lockingof DFC shared sessions. Use DcDataDictionaryUtils to take internal names and receive backexternal or user-defined names. Use DcDataProtectionUtils to allow a user to save unsavedobjects. Use DcDFCSessionUtils to lock DFC shared sessions. There is also a utility class(DcSessionLock.cls) that encapsulates and simplifies calling IDcDFCSessionUtils interfacemethods. The Docbase object-based properties component (DCPROPERTIESCOMP.DLL) is anexample of a Documentum-provided desktop client component that uses bothDcDataDictionaryUtils and DcSessionLock.cls (and therefore DcDFCSessionUtils).

Figure 21 shows the type library of the Utility Manager.

Page 30: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 30 of 78

Figure 21. Looking at IDcDataDictionaryUtils::GetAttributeDisplayName in VB’sObject Browser

There are additional major services available to desktop client components that are not thefocus of this document. These services include Documentum Foundation Classes (DFC) andspecial purpose widgets (ActiveX Controls). Please consult the documentation that comeswith these system elements for more details.

6 Creating a New Desktop Client Component

Before talking about how to create a component, let’s look at a simplified interactiondiagram to see how a component is called into action. Desktop client components aredesigned to satisfy functional requests.

Page 31: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 31 of 78

ComponentDispatcher

Component

ApplicationIntegrationCheck-in

ItemContainer

IDcComponentDispatcher::RunComponent

IDcComponent::Init

IDcAppIntgCheckInItemContainer::CheckedInObjectID

(1)

(2)

(3)

DART

DFC

CabinetManager

Figure 22. High-level Component Interaction Diagram

Figure 22 shows how an item container interfaces with the Component Dispatcher to satisfya functional request, which in this case is a check-in request coming from Microsoft Word97.

1. The application integration item container instantiates the Component Dispatcher andcalls its IDcComponentDispatcher::RunComponent interface method. As part of its callthe item container provides a collection of one or more items to RunComponent.

2. Within RunComponent, the Component Dispatcher asks DART and the Cabinet Manager’sobjects to discover, deliver and register the right component(s) to satisfy this functionalrequest. (One function may involve more than one component.) Once DART and theCabinet Manager’s objects are finished with its part of the process (see section nine formore details), the Component Dispatcher instantiates the component associated withthe CLSID returned by DART and calls the component’s IDcComponent::Init interfacemethod, passing a filtered collection of items that apply to this specific component. Initcontains code to allocate resources necessary to complete a function (e.g. check-in adocument). Once control returns from Init, the Component Dispatcher calls thecomponent’s IDcComponent::Run interface method, telling the component to do its job.

3. While completing its function, the component may learn about the context of itsinvocation (i.e. functional context) by querying its item container through the itemcontainer’s COM interface, if one is provided to the component. The component may alsoaccess other services in the desktop client infrastructure like DFC. Following Run,IDcComponent::DeInit is called to free component resources.

Implementing the IDcComponent COM interface makes any COM object a desktop clientcomponent. Your component may optionally implement other interfaces to participate morefully in the desktop client infrastructure (e.g. IDcEventSink), but implementingIDcComponent is all that’s required. That is, desktop client components are simply COMobjects that implement at least the IDcComponent interface.

Page 32: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 32 of 78

Documentum Desktop Client software is not concerned with a component’s housing.Components may be housed in DLLs or EXEs. The following sub-sections assume your VisualBasic project to be of type “ActiveX DLL.” If you prefer, you can also use “ActiveX EXE”projects for your components.

The following text assumes that a standard Documentum Desktop Client component is beingdeveloped. Text that may vary between a standard Documentum Desktop Client componentimplementation and a custom component implementation is shown in bold purple.

Note that while there is a VB project with an active reference to a COM server (e.g. oneyou’re developing), the server is locked and cannot be deleted. That is, once you establish areference, VB loads the referenced server in memory to satisfy subsequent object request.What this means, for example, is that you may add functionality to a referenced serveroutside VB, but you won’t be able to overwrite the server itself until the active reference isreleased (e.g. the project is removed from the active VB session).

6.1 Start with a Skeleton (Component ProjectConfiguration)

Now that your VB environment is properly set up for Documentum Desktop Client softwaredevelopment from section 3, you can create a new component project.

1. Create a new folder to contain your VB component project. When desktop clientcomponents were developed, a common root “components” folder was specified underwhich a folder for each individual component was created (e.g. Documentum componentfolders took the form Dc<function>Comp—like components\DcPropertiesComp).

2. Launch VB and choose a new ActiveX DLL project. This will create a project (Project1)containing a single class module (Class1). Note that Class1 already has its Instancingproperty to “5 – Multiuse.” This is exactly as it should be. This instancing option allows asingle server to support any number of objects of a given class. The objects can becreated by applications using the New operator or the CreateObject function. In the caseof a DLL server, each object runs in the process space of the calling application.• An ActiveX DLL in VB supports self-registration. VB provides the required

DllRegisterServer and DllUnregisterServer entry points transparently.• An ActiveX DLL project produces a lighter-weight component than an ActiveX Control

project. You can still define properties in your component. If you have a requirementto graphically display such properties to end users, you may wish to create anActiveX Control project instead. ActiveX Control projects are beyond the currentscope of this document.

3. Establish the appropriate names for your project and main class module.a) Together, the project name and class name defines a VB ProgID. Unfortunately, VB

uses its LIBIDs for the first half of its ProgIDs.b) A VB component’s project name should be unique otherwise an end user’s Object

Browser may show multiple libraries of the same name for different components.c) The standard ProgID for all Documentum components written in VB is

DC<function – all uppercase>Lib.Dc<function – first letter of each wordcapitalized, remainder lowercase>Comp (e.g.DCCHECKOUTLib.DcCheckOutComp).

d) Click on Project1 in the Project Explorer window (i.e. typically the window on theupper right corner whose title bar caption begins “Project –”). In the Propertieswindow, click on Name and type, for example, “DCPROPERTIESLib” (without thequotes).

Page 33: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 33 of 78

e) Click on Class1 in the Project window, then click on Name in the Properties windowand enter your component’s name in the form Dc<function>Comp (e.g.DcPropertiesComp).

f) Double-click on your component class in the Project window to bring up its sourcecode. As you can see it’s pretty empty, except for “Option Explicit” at the top. Add astandard comment header (preamble). An example comment header is provided inAppendix A.

g) Choose File | Save Project As… to save your new class module (.CLS) and project(.VBP) files to the folder you established in (1) (e.g. C:\Documentum DesktopClient\components\DcPropertiesComp). Be sure to rename your project filename from DC<function – all uppercase>Lib.vbp to Dc<function>Comp.vbp(e.g. DcPropertiesComp.vbp). This rename will automatically carry-over toproperly naming your component housing (e.g. DcPropertiesComp.dll).

4. Define your project properties; go to Project | DC<function>Lib Properties…a) On the General tab, confirm that your Project Type is ActiveX DLL and your Project

Name is DC<function – all uppercase>Lib. Project Name is one of the mostimportant properties you will choose, and you should always set it for your project.This property becomes the name of your component’s type library. For example: Ifyou name your project DCPROPERTIESLib and it contains a class module namedDcPropertiesComp, the COM class behind the project’s class module can beinstantiated through its programmatic identifier or ProgID, which in this case wouldbe DCPROPERTIESLib.DcPropertiesComp. If the object is publicly creatable, youwill be able to pass this ProgID to the CreateObject function to create instances ofthose objects, as in the example:

Set newobj = CreateObject("DCPROPERTIESLib.DcPropertiesComp")

Enter something for Project Description. What you enter will be displayed in theReferences dialog when someone refers to your component. In IDL terms, what youenter is the helpstring value for the library section of your component housing’s IDLfile. Leave the rest of the General tab as-is.

b) On the Make tab, select Auto Increment, change Title to Dc<function>Comp (e.g.DcPropertiesComp) and enter Version Information as follows (e.g. propertiescomponent):

Type ValueComments <blank>Company Name Documentum, Inc.File Description DcPropertiesCompLegal Copyright Copyright © 2000, Documentum,

Inc.Legal Trademarks <blank>Product Name Documentum Docbase Object-

based Properties Component

What you enter for Version Information will be displayed to an end user in theWindows Explorer when your component housing is selected for propertyinformation. Version Information is VB’s UI for your component housing’sVS_VERSION_INFO resource.• You may revisit this tab later to add an icon for your DLL.• Activating auto increment is the easiest way to ensure that your component’s file

version grows with each change to its implementation. Auto Increment increasesthe value of Revision by one every time your component project (.VBP) file issaved. If your component participates in dynamic component delivery (DCD), it

Page 34: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 34 of 78

must specify an increasing file version in order to be properly installed on yourend users’ machines.

c) On the Compile tab, compile to native code creating symbolic debug information—toa .PDB file--with no optimization.• While you’re developing your component, you need to support both design-time

and run-time debugging—topics covered in more detail in section 7. This is whyyou include symbolic debug information and exclude optimizations.

• Once you’re through debugging your component, you need to go back to this tabto remove symbolic debug information and change the compilation to optimizefor speed.

• Before you release a DLL-based component (including ActiveX controls), be sureto reset the number for DLL Base Address to a value between &H60000000 and&H68000000. Choose any 64K boundary—this means that the rightmost fourdigits of the address will always be 0000. Microsoft suggests you choose arandom base address in that range as the base for all of your controls, thenallocate addresses from that point onward. The reason for this is as follows:When Windows loads a DLL, it tries to load it at the specified DLL base address. Ifthe memory space is available within the process space of the application that isloading the DLL, the DLL loads extremely quickly. If, however, another DLLcomponent is already using that space, Windows must relocate the DLL data andcode to a new location. This is a time-consuming operation and one that iswasteful because it may prevent Windows from sharing the DLL code with otherapplications.

• Currently Documentum desktop client software occupies base addresses from&H63000000 to &H68000000. This range is subject to change. For optimalloading performance on your component, please specify a base address thatdoesn’t conflict with this range (i.e. lies between &H60000000 and &H63000000).

d) On the Component tab, set Version Compatibility to Project Compatibility. In section6.6, you will change your version compatibility settings (to Binary Compatibility)once you are ready to release your component for use by other applications.

e) Click OK.5. Go to File | Make Dc<function>Comp.dll… (e.g. Make DcPropertiesComp.dll…).

Verify that your DLL will be built to the same folder you specified in (1) (e.g.C:\Documentum Desktop Client\components\DcPropertiesComp). You don’tneed to click on the Options… button since you set up your project properties in (4).Choose OK to build your rather empty component housing.

6. Save all of your work.

6.2 Add Muscles (Infrastructure Connections)

6.2.1 Implementing IDcComponent

In order for your project to become a desktop client component project, your class modulemust implement the IDcComponent interface. All desktop client components implement thiscommon interface which allows Component Dispatcher to treat them polymorphically. It isthrough this interface that the outside world will communicate with your component,passing it information in the form of items (functional targets) and optionally context (itemcontainer interface). You’ll be called to execute a particular function—this interface providesyou with the necessary information to do the job.

Use the following procedure to quickly generate a skeleton IDcComponent implementationin your class module:

Page 35: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 35 of 78

1. Establish a reference to the VB Assistant type library. That is, go to Project |References… and check off “Documentum VB Component Development Assistant 1.0Type Library.”

2. Open the code window on your class module.3. After the “Option Explicit” start typing “Implements … .” Microsoft IntelliSense (auto-

complete) technology should kick-in. Continue typing “dcc” to highlightDCCOMPONENTLib. Hit the Tab key then enter a period (i.e. you should have“Implements DCCOMPONENTLib.” up to this point). Finally type “idcco” to highlightIDcComponent. Hit the Tab key then the Enter key to wind up with “ImplementsDCCOMPONENTLib.IDcComponent.”

4. To avoid guesswork and error, use the IDE to provide a skeleton implementation ofIDcComponent. At the top of your class module’s source window there are twodropdown boxes. The one on the left shows an “Object” tooltip and the one on the rightshows a “Procedure” tooltip. Dropdown the Object box and select IDcComponent. TheIDE will provide a skeleton for IDcComponent::Init. Next dropdown the Procedure boxand select Run then DeInit. The IDE automatically alphabetizes routines.• Do not try to leverage VB's Object Browser (F2) (e.g. copying and pasting through

the list of methods). This approach won't catch the necessary ByVal qualifiers or theexplicit type library parameter qualifiers.

Figure 23 shows what your class module should look like at this point.

Option Explicit

Implements DCCOMPONENTLib.IDcComponent

Private Function IDcComponent_DeInit() As Long

End Function

Private Function IDcComponent_Init(ByVal docbaseName As String, _ ByVal userOSName As String, _ ByVal domain As String, _ ByVal contextID As String, _ ByVal items As DCCOMPONENTLib.IDcItems, _ ByVal hwndForDialog As Long, _ ByVal reporter As DCCOMPONENTLib.IDcReport, _ ByVal stringForIID As String, _ Optional ByVal itemContainer As Variant) As Long

End Function

Private Function IDcComponent_Run() As Long

End Function

Figure 23. VB Sample Code – Skeleton Implementation of IDcComponent

It’s worth repeating that all standard Documentum desktop client interfaces are COMinterfaces. Most, if not all, are dual interfaces which means that they derive from IDispatch,which in turn derives from IUnknown (the base interface all COM interfaces must derivefrom). In the case of IDcComponent from the VB developer's perspective, all you really careabout is IDcComponent itself—VB provides the implementation of IDispatch (and thereforeIUnknown) for you. However, if you're developing a desktop client component in C++, forexample, you'll have to implement not only IDcComponent but all of its base interfaces aswell!

Page 36: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 36 of 78

Note that VB classes may have their own initialization and termination methods. However,desktop client component class modules may not need to implement either Class_Initializeor Class_Terminate. Initialization in desktop client component class modules should occurprimarily in IDcComponent_Init (i.e. IDcComponent::Init); termination logic should existprimarily in IDcComponent_DeInit (i.e. IDcComponent::DeInit). Modeless componentlifetimes may need to be managed differently; in this case, for example, termination logicmay be better suited to Class_Terminate. Please consult section 6.5 for more details.

Add error handlers to all three IDcComponent method implementations. In general youshould use error handlers in all of your routines, unless they are completely straightforwardand will not raise errors. This is especially important in VB code, since failure HRESULTs areautomatically translated into VB exceptions. VB will handle any unhandled exceptions bydisplaying a fairly cryptic error dialog and then terminating your component!

Here is an example error-handling skeleton for IDcComponent::Run that uses theDocumentum Report Manager. This example assumes that you’ve established a reference tothe following type library: “Documentum Report Manager 1.0 Type Library”(DCREPORTSLib).

Private m_Reporter As DCREPORTSLib.DcReport 'Set within IDcComponent::InitPrivate m_Hwnd As Long 'Set within IDcComponent::Init

Private Function IDcComponent_Run() As Long 'i.e. all IDcComponent methods On Error GoTo HandleError 'return a DcCompReturnValue 'TODO: Add your logic here. IDcComponent_Run = DC_COMP_SUCCESS Exit FunctionHandleError: 'Use the Report Manager to present error information. Please note 'that you may need to add a new entry before calling Display. Dim btn As DcReportButtonType btn = m_Reporter.Display(m_Hwnd, DC_REPORT_OK_ONLY) 'In other cases you might need code to react based on the button 'pressed by the user to dismiss the Report Manager dialog. IDcComponent_Run = DC_COMP_FAILUREEnd Function

Figure 24. VB Sample Code – Simple Error Handler Using Report Manager

Please use the component return value constants available to you through the reference youestablished an implementation of IDcComponent (e.g. DCENABLEVB.DLL). You candetermine those available through VB’s Object Browser (F2). Scroll down on theProject/Library dropdown control until you find the type library of interest (e.g.DCCOMPONENTLib). Whenever constants like DC_COMP_SUCCESS are available to you(through the DcCompReturnValue enumeration), you should use them instead of hard-coded values.

At this point you still only have a skeleton implementation of IDcComponent. Before youadd your specific implementation code, please understand the role each method inIDcComponent should fulfill.

1. Init – In this method, your component should allocate any resources it needs to do itsjob. You may find that other forms or other IDcComponent methods require some of thedata that is passed into your component via its IDcComponent::Init method. If this isthe case, use private class module data members and/or properties on forms to save thenecessary input data before your component returns from Init. Init currently specifieseight parameters plus an optional ninth parameter. All of these parameters are inputs.

Page 37: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 37 of 78

As with each method of IDcComponent, Init returns a Long value from theDcCompReturnValue enumeration. Figure 25 provides a table that maps input parameterto its type and description.

2. Run – This is where your component executes its functionality, often presenting its enduser with a dialog of options or status.

3. DeInit – Resources that were allocated in Init need to be released in this method.Modeless component lifetimes may need to be managed differently. Please consultsection 6.5 for more details.

Parameter Type DescriptiondocbaseName String Name of the Docbase containing the functional

targets (items)userOSName String Name of the current user as expected by the

operating systemdomain String Domain namecontextID String r_object_id attribute of the qualified component

(i.e. dm_qual_comp Docbase object) associatedwith the invoked component; the component canretrieve context information concerning itsinvocation using this parameter (e.g. theclient_capability attribute states whether the enduser is a consumer, contributor or coordinator—3C; document lifecycle information is available aswell)

items IDcItemsinterface

Collection of one or more targets for the functionthis component is designed to execute

hwndForDialog Long HWND to use when presenting dialogs to the enduser (e.g. use with reporter’s Display function)

reporter IDcReportinterface

Object which handles the common display oferror information in report form (section 5.5)

stringForIID String String IID constant for an item containerinterface, if one is provided in itemContainer

itemContainer Variant [Optional] If provided, itemContainer is anIunknown interface from which the componentmust access the item container interfacespecified by stringForIID. VB components mayuse the IsMissing function on this parameter todetermine whether an interface has beenprovided

Figure 25. IDcComponent::Init Parameters

Components must free allocated resources if returning something other thanDC_COMP_SUCCESS from either IDcComponent::Init or IDcComponent::Run. If acomponent returns a failed HRESULT or a return code other than DC_COMP_SUCCESS,Component Dispatcher will not call Run or, more importantly, DeInit--where a componenttypically frees allocated resources.

6.2.2 Connecting to a Docbase

By using the Login Manager for all Docbase connections, your component can get a DFCsession (DfSession) object through IDfClient::findSession on the returned session ID string

Page 38: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 38 of 78

from IDcLoginManager::Connect. This DFC session will be a shared session that is shared byat least your component and the Login Manager it created.

When a shared session is created, a key (string) is specified. Any subsequent requests for ashared session, as the same Docbase/user and the same key, will return the same sharedsession. Currently, all sessions obtained from the Login Manager are shared sessions, wherethe key is "Login Manager."

Multiple clients within a process can use a DFC shared session. Since the session is shared,all clients are writing to the same DMCL cache, which eliminates some synchronizationproblems. However, problems may occur when multiple threads within a process are tryingto use the same shared session. DFC session (e.g. the CDfSession) objects have alock/unlock mechanism that should be used when writing data to the Docbase. For example,before the properties component sets new values on an object, it should lock the session.After the object changes have been saved, the session should be unlocked. Your errorhandler should also unlock the session if it is locked when it catches a thrown exception.

The easiest way to properly lock a DFC shared session is to add DcSessionLock.cls to yourcomponent project and use one of its DcSessionLock objects in your component code.

Here’s a simple VB example showing how to connect to a Docbase using the DocumentumLogin Manager and how to subsequently get and use a DFC shared session. If the user hitsCancel on the login dialog, the Login Manager will return successfully with an empty sessionID. Since canceling a login is a perfectly valid user action, there is no exception to catch(i.e. error to handle) in this case. This is why the code checks for session ID following itscall to Connect—you cannot assume that a successful return from Connect implies aconnection has been established.

This code assumes that you have references established to the following type libraries:“Documentum Foundation Classes Type Library (<mm/dd/yy>)" (DFCLib), “DocumentumLogin Manager 1.0 Type Library” (DCLOGINMGRLib) and “Documentum Report Manager 1.0Type Library” (DCREPORTSLib). It also assumes that your project includesDcSessionLock.cls as one of its class modules.

'DFC-related data membersPrivate m_DfClientX As DFCLib.DfClientX 'example assumes prior instantiationPrivate m_DfClient As DFCLib.IDfClientPrivate m_DfSession As DFCLib.IDfSession

'Data members set previously in IDcComponent::InitPrivate m_DocbaseName As StringPrivate m_UserOSName As StringPrivate m_Domain As String

Private Sub TestConnectDocbase()

Dim loginMgr As DCLOGINMGRLib.DcLoginManager Dim sessionLock As DcSessionLock 'from the DcSessionLock.cls utilities file Dim sessionID As String

On Error GoTo HandleError

'Create our instance of the Login Manager. Set loginMgr = New DCLOGINMGRLib.DcLoginManager

'Connect to the Docbase specified in IDcComponent::Init. sessionID = loginMgr.Connect(m_DocbaseName, _ m_UserOSName, _ "", _

Page 39: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 39 of 78

m_Domain, _ IS_DOCBASE_CONNECTED) If sessionID <> "" Then 'We have a connection to use. Set m_DfClient = m_DfClientX.getLocalClient Set m_DfSession = m_DfClient.findSession(sessionID)

'Lock the DFC shared session before using it. Set sessionLock = New DcSessionLock If Not sessionLock.GetLock(m_DfSession, True, " TestConnectDocbase") Then 'The user chose to cancel retrying for a lock on this session. 'Being asked to retry means that someone else currently holds 'the lock. GoTo ExitRoutine End If

'. . . (Do something that involves the DFC shared session.)

'Release your lock on the session ASAP so that someone else can acquire 'a lock if necessary. sessionLock.ReleaseLock

'Release our instance of the Login Manager resulting in its destruction '(COM reference count will be zero). Set loginMgr = Nothing 'Note that in this case we didn’t call loginMgr.Disconnect since other 'applications may still be interested in a connection to this Docbase. End If

GoTo ExitRoutineHandleError: Dim btn As DcReportButtonType btn = m_Reporter.Display(m_Hwnd, DC_REPORT_OK_ONLY)ExitRoutine: 'single exit point 'When sessionLock goes out of scope it effectively calls its ReleaseLock 'method if necessary.End Sub

Figure 26. VB Sample Code – Using Login Manager & DFC to Access a Docbase

How your specific component will use DFC to execute its operation is beyond the scope ofthis document. Please consult DFC documentation for more details on how to interface withand deploy DFC in your component. As you learn how to use DFC, you may want to turnDFC tracing on. A registry-based approach is recommended. Please consult DocumentumDesktop Client VB component source code for more details (i.e. examples).

6.2.3 Functional Targets & Contexts – Working withItems & Item Containers

Figure 27 provides sample VB code, which shows indexing through an item collection andtraversing an item collection without using index. (Item collection indices are one-based.)This code assumes that you have a reference established to the “Documentum Item Server1.0 Type Library” (DCITEMSERVERLib) type library.

Private Sub TestItemCollection()

On Error Resume Next 'i.e. ignore errors

Dim Items As New DCITEMSERVERLib.DcItems 'Requires an established 'reference to the Documentum Item Server (DCITEMSERVER.DLL)

Dim i As Long

Page 40: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 40 of 78

Items.Type = DC_OBJECT_ITEM_IID_STRING 'i.e. IID_IDcObjectItem For i = 1 To 3 Dim Item As New DCITEMSERVERLib.DcObjectItem 'AddRef (item reference count = 1) Item.ID = "Object Item " + CStr(i) Dim Added As Boolean Added = Items.Add(Item) 'collection AddRef (item ref count = 2) Print "Item was added to the collection = ", Added Debug.Print "Item was added to the collection = ", Added 'Clear the current reference in preparation for the next one. Set Item = Nothing 'Release (item reference count = 1) Next i

For Each Item In Items 'Use _NewEnum. Print "ID = ", Item.ID Debug.Print "ID = ", Item.ID Next Item

'Remove items from the (one-based) collection. For i = 1 To Items.Count Dim Removed As Boolean 'Since collections are reindexed automatically, remove 'the first member on each iteration. Removed = Items.Remove(1) Print "Item was removed from the collection = ", Removed Debug.Print "Item was removed from the collection = ", Removed Next i

Set Items = Nothing 'Release

End Sub

Figure 27. VB Sample Code – Working with an Item Collection

Accessing the item in the item collection your component receives via IDcComponent::Init isquite similar to the previous example. You will need to ask the item collection for its typeand then dimension a variable of the appropriate item type to retrieve functional targets.Please consult Documentum Desktop Client VB component source code for more details (i.e.examples).

Figure 28 shows that item collection data flows two ways. Original data contained in an itemcollection comes from a “trigger” and is passed on to one or more components throughComponent Dispatcher. However that data may be overwritten by these components so thatwhen the original item collection is returned to the trigger, it contains different data thanoriginally specified. A large selection of items by the trigger may result in more than onecomponent being dispatched. Since only one item collection is returned to the trigger sinceit only passes a single collection to Component Dispatcher, if more than one component isinvolved in the trigger's functional request, all involved components must return the sametype of item collection. Item collections cannot currently contain more than one type ofitem. The singular type may be different than that originally set by the trigger.

Page 41: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 41 of 78

Component

Trigger (source of functional request)

ComponentDispatcher

Component

Component

Items Items

Items

Items

Figure 28. VB Sample Code – Working with an Item Collection

Two-way data flow allows components to communicate with their triggers through itemcollections. It also requires both parties to be responsible with respect to clean up (i.e.freeing resources). Specifically all callers of IDcComponentDispatcher::RunComponent orIDcComponentDispatcher::RunComponentEx who create item collections must eventuallyerase their item collections prior to releasing the item collection interfaces. Callers ofRunComponent should not assume that an empty item collection passed intoRunComponent returns empty.

Figure 29 provides an example in VB of how to check if item container support is presentwithin IDcComponent::Init and, if so, how to test what kind of support has been provided.This code assumes that you have references established to the following type libraries:“Documentum Item Server 1.0 Type Library” (DCITEMSERVERLib), “Documentum VBComponent Development Assistant 1.0 Type Library” (DCCOMPONENTLib) and“Documentum Shell Integration 1.0 Type Library” (DCEXPLORERINTGLib).

Private m_IsLocationContainer As BooleanPrivate m_IsLocalDocument As BooleanPrivate m_LocationContainer As DCEXPLORERINTGLib.IDcLocationItemContainerPrivate m_RegularContainer As DCEXPLORERINTGLib.IDcItemContainer

'. . .m_IsLocationContainer = Falsem_IsLocalDocument = False'. . .If Not IsMissing(itemContainer) Then 'optional parameter to Init If StrComp(stringForIID, DC_IC_IID_STRING, vbTextCompare) = 0 Then 'We have a base item container interface (i.e. IDcItemContainer). On Error Resume Next Set m_LocationContainer = itemContainer If Err.Number <> 0 Then 'e.g. E_NOINTERFACE 'Access the more limited interface. Set m_RegularContainer = itemContainer 'AddRef If m_RegularContainer.Type = DCIC_ABSTRACTION Then If m_RegularContainer.Value = "Local Files" Then m_IsLocalDocument = True End If End If Else m_IsLocationContainer = True If m_LocationContainer.Type = DCIC_ABSTRACTION Then If m_LocationContainer.Value = "Local Files" Then m_IsLocalDocument = True End If End If

Page 42: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 42 of 78

End If ElseIf StrComp(stringForIID, DC_AI_IC_IID_STRING, vbTextCompare) = 0 Then 'We have a base application integration item container 'interface (i.e. IDcAppIntgItemContainer). '. . . Else 'We have an unrecognized item container interface! '. . . End IfEnd If'. . .

Figure 29. VB Sample Code – Working with Item Containers

6.2.4 Dispatching Another Component

If you haven’t already, please review sections 5.4 and 6 as well as Figure 22 to understandwhat’s involved in component dispatching. Here’s some sample VB code to implement theinformation in these previous sections. This code assumes that you have referencesestablished to the following type libraries: “Documentum Component Dispatcher 1.0 TypeLibrary” (DCCMPDSPLib), “Documentum Item Server 1.0 Type Library” (DCITEMSERVERLib)and “Documentum Report Manager 1.0 Type Library” (DCREPORTSLib).

'Data members set previously in IDcComponent::InitPrivate m_DocbaseName As StringPrivate m_UserOSName As StringPrivate m_Domain As StringPrivate m_Hwnd As LongPrivate m_Reporter As DCREPORTSLib.DcReport

. . . 'Tell Component Dispatcher to run the functional component for properties on the selected 'target, using the default application (DocApp). Note that no item container interface 'is provided by us to the other component. You could also have passed the original item 'container (if one was provided). DispatchAnotherComponent "DcProperties", _ "DcDesktopClient", _ m_DocbaseName, _ m_UserOSName, _ m_Domain, _ items, _ m_Hwnd, _ m_Reporter, _ "", _ "" . . .

Private Sub DispatchAnotherComponent(functionalClass As String, _ docAppName As String, _ docbaseName As String, _ userOSName As String, _ domain As String, _ itemCollection As DCITEMSERVERLib.DcItems, _ windowForUI As Long, _ reportMgr As DCREPORTSLib.DcReport, _ itemContainerType As String, _ Optional itemContainerInterface As Variant)

On Error GoTo HandleError

'Instantiate the Documentum Component Dispatcher. Dim componentDispatcher As DCCMPDSPLib.DcComponentDispatcher Set componentDispatcher = New DCCMPDSPLib.DcComponentDispatcher

Page 43: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 43 of 78

Dim rv As Long 'i.e. DcCompReturnValue rv = componentDispatcher.RunComponent("DcProperties", _ "DcDesktopClient", _ docbaseName, _ userOSName, _ domain, _ itemCollection, _ windowForUI, _ reportMgr, _ itemContainerType, _ itemContainerInterface) GoTo ExitRoutineHandleError: Dim btn As DcReportButtonType btn = reportMgr.Display(windowForUI, DC_REPORT_OK_ONLY)ExitRoutine: 'single exit pointEnd Sub

Figure 30. VB Sample Code – Calling Component Dispatcher

6.2.5 Participating with Events

Other elements of the Documentum Desktop Client may rely on your component to dispatchevents related to its function. Your component may also be interested in receiving eventsfrom other part of the system. This section will explain how to code an event sink in VB,how to register your component event sink with the Notifier covered in section 5.2 and howto tell the Notifier to send an event. First, we’ll create a simple component event sink.

Following the same kind of procedure that was presented in section 6.2.1, generate askeleton IDcEventSink implementation in a new class module for your component project asfollows:

1. Choose Project | Add Class Module | New tab and the “Class Module” icon to create anew, empty class module in your component project.

2. Give it a meaningful name (e.g. Dc<function>EventSink).3. If you haven’t already done so in your project, establish a reference to the event server

type library. That is, go to Project | References… and check off “Documentum EventServer Type Library - 1.0.”

4. Open the code window on your class module.5. After the “Option Explicit” start typing “Implements … .” Microsoft IntelliSense (auto-

complete) technology should kick-in. Continue typing “dcev” to highlightDCEVENTSERVERLib. Hit the Tab key then enter a period (i.e. you should have“Implements DCEVENTSERVERLib.” up to this point). Finally type “idcevents” to highlightIDcEventSink. Hit the Tab key then the Enter key to wind up with “ImplementsDCEVENTSERVERLib.IDcEventSink.”

6. To avoid guesswork and error, use the IDE to provide a skeleton implementation ofIDcEventSink. At the top of your class module’s source window there are two dropdownboxes. The one on the left shows an “Object” tooltip and the one on the right shows a“Procedure” tooltip. Dropdown the Object box and select IDcEventSink. The IDE willprovide a skeleton for IDcEventSink::PID (a read-only property). Next dropdown theProcedure box and select HandleEvent then “Name [PropertyGet]” and finally “Name[PropertyLet]”. The IDE automatically alphabetizes routines.• Do not try to leverage VB's Object Browser (F2) (e.g. copying and pasting through

the list of methods). This approach won't catch the necessary ByVal qualifiers or theexplicit type library parameter qualifiers.

7. Add private data members for your PID and Name properties.

Page 44: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 44 of 78

8. Initialize your read-only PID property by declaring the Win32 API GetCurrentProcessIdand calling it in your Class_Initialize routine.

9. Implement your three property procedures using your private data members from (7).

Figure 31 shows what your class module should look like at this point with a little additionalformatting done here for easier reading. The code in Figures 31, 32, 34 and 35, assumesthat you have a reference established to the event server type library (i.e. “DocumentumEvent Server 1.0 Type Library” (DCEVENTSERVERLib)).

Option Explicit

'---------- Class API dependencies (private) ----------

Private Declare Function GetCurrentProcessId& Lib "kernel32" ()

'---------- Class data members (private) ----------

Private m_PID As LongPrivate m_Name As String

'---------- Class COM interface inheritance ----------

Implements DCEVENTSERVERLib.IDcEventSink

'---------- Class methods (private) ----------

Private Sub Class_Initialize() 'You only need to get your process ID once for the life of your object. 'If you object is housed in a DLL, m_PID will be set to the process ID 'of the process that loaded your housing. m_PID = GetCurrentProcessId() 'Warning: NT reuses process and thread IDs! Until the process terminates, 'the process identifier uniquely identifies the process throughout the system.

'TODO: Add logic here to allocate any object-based resources your event 'sink might need during its lifetime.End Sub

Private Sub Class_Terminate() 'TODO: Add logic here to free any object-based resources your event 'sink allocated during its lifetime.End Sub

'---------- IDcEventSink implementation ----------

Private Function IDcEventSink_HandleEvent(ByVal eventCode As Long, _ ByVal eventType As Long, _ ByVal followUp As Long, _ ByVal clientName As String, _ ByVal PID As Long, _ ByVal eventData As Object, _ responseMsg As String) As Boolean

'TODO: Activate your own custom error handler here.

'Always have an active error handler in place around all COM-related code to 'prevent VB from handling unhandled exception in its default manner of putting 'up an error dialog and then terminating the parent process! On Error GoTo HandleError

'TODO: Add your own custom event handling logic here.

'For example, you may wish to compare the incoming process ID (PID) with your own '(m_PID) to see if the same process is involved).

GoTo ExitRoutineHandleError:

Page 45: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 45 of 78

'TODO: Add your own custom error handling logic here.ExitRoutine: 'single exit pointEnd Function

'Your event sink's name should be set to the same value as that passed into'the Notifier's RegisterEventSink method.

Private Property Let IDcEventSink_Name(ByVal RHS As String) m_Name = RHSEnd Property

Private Property Get IDcEventSink_Name() As String IDcEventSink_Name = m_NameEnd Property

Private Property Get IDcEventSink_PID() As Long IDcEventSink_PID = m_PIDEnd Property

Figure 31. VB Sample Code – Skeleton Implementation of IDcEventSink

The next code example in this section shows how to access event data that your event sinkmay receive through its HandleEvent method. This sample is designed to show you how toextract input data for each event code currently defined in the standard desktop client. Notethat your ability to handle these events is dependent upon how your event sink is registeredwith the Notifier. To take full advantage of the following code, you should callRegisterEventSink with categoryMask set to DC_EVT_CATEGORY_ALL.

Although nothing prevents a sink from registering for interest in all events while only reallyhandling a small subset, please consider overall event dispatching efficiency whendeveloping your event sinks and registering them. For example, only register for eventcategories that are of true interest to your sink. While the following sample code gives youan introduction to handling all standard events, it doesn't necessarily represent an efficientevent sink from the perspective of the system.

Finally, concerning the next code sample, please note that a single active error handler isused (in bold). The code explicitly shows the importance of not assuming that eitherexpected data is provided or unexpected data is not provided. However, the code might notbe as clear about handling event data whose format is unexpected (e.g. expecting aDCEVENTSERVERLib.DcContainedObjEventData object by receiving only aDCEVENTSERVERLib.DcObjectEventData object). When the following code (or any VB codefor that matter) tries to access event data that isn’t implemented, the event dataimplementation will cause VB to raise an exception. Given our active error handler, controlwill transfer to the HandleError label. If we didn’t have an active error handler, theexception would be passed by our event sink implementation to its client (i.e. the Notifier).

Private Function IDcEventSink_HandleEvent(ByVal eventCode As Long, _ ByVal eventType As Long, _ ByVal followUp As Long, _ ByVal clientName As String, _ ByVal PID As Long, _ ByVal eventData As Object, _ responseMsg As String) As Boolean

On Error GoTo HandleError

IDcEventSink_HandleEvent = DefaultHandleEventReturnValue(eventType)

Dim containedObjEventData As DCEVENTSERVERLib.DcContainedObjEventData Dim objectEventData As DCEVENTSERVERLib.DcObjectEventData

Page 46: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 46 of 78

Dim ccoObjEventData As DCEVENTSERVERLib.DcCancelCheckOutObjEventData Dim ciObjEventData As DCEVENTSERVERLib.DcCheckInObjEventData Dim docbaseObjEventData As DCEVENTSERVERLib.DcDocbaseObjEventData Dim deleteObjEventData As DCEVENTSERVERLib.DcDeleteObjEventData Dim functionObjEventData As DCEVENTSERVERLib.DcFunctionObjEventData Dim formatObjEventData As DCEVENTSERVERLib.DcFormatObjEventData Dim sessionEventData As DCEVENTSERVERLib.DcSessionEventData

Select Case eventCode Case DC_EVT_ENV_CLIPBOARD_REVISION Debug.Print " DC_EVT_ENV_CLIPBOARD_REVISION" If eventData Is Nothing Then Debug.Print " No data, as expected" Else Debug.Print " Unexpected data provided!" End If Case DC_EVT_ENV_VIEW_OPTIONS_REVISION Debug.Print " DC_EVT_ENV_VIEW_OPTIONS_REVISION" If eventData Is Nothing Then Debug.Print " No data, as expected" Else Debug.Print " Unexpected data provided!" End If Case DC_EVT_ENV_WORK_OFFLINE Debug.Print " DC_EVT_ENV_WORK_OFFLINE" If eventData Is Nothing Then Debug.Print " No data, as expected" Else Debug.Print " Unexpected data provided!" End If Case DC_EVT_ENV_WORK_ONLINE Debug.Print " DC_EVT_ENV_WORK_ONLINE" If eventData Is Nothing Then Debug.Print " No data, as expected" Else Debug.Print " Unexpected data provided!" End If Case DC_EVT_LINK_CREATION Debug.Print " DC_EVT_LINK_CREATION" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set containedObjEventData = eventData Debug.Print " Object ID = " + containedObjEventData.ID + "; Docbasename = " + containedObjEventData.docbaseName + "; container ID = " +containedObjEventData.ContainerID Set containedObjEventData = Nothing Else Debug.Print " Expected data missing!" End If Case DC_EVT_LINK_DELETION Debug.Print " DC_EVT_LINK_DELETION" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set containedObjEventData = eventData Debug.Print " Object ID = " + containedObjEventData.ID + "; Docbasename = " + containedObjEventData.docbaseName + "; container ID = " +containedObjEventData.ContainerID Set containedObjEventData = Nothing Else Debug.Print " Expected data missing!" End If Case DC_EVT_LOCAL_COPY_CREATION Debug.Print " DC_EVT_LOCAL_COPY_CREATION" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set objectEventData = eventData Debug.Print " Object ID = " + objectEventData.ID Set objectEventData = Nothing

Page 47: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 47 of 78

Else Debug.Print " Expected data missing!" End If Case DC_EVT_LOCAL_COPY_DELETION Debug.Print " DC_EVT_LOCAL_COPY_DELETION" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set objectEventData = eventData Debug.Print " Object ID = " + objectEventData.ID Set objectEventData = Nothing Else Debug.Print " Expected data missing!" End If Case DC_EVT_OBJ_CANCEL_CHECKOUT Debug.Print " DC_EVT_OBJ_CANCEL_CHECKOUT" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set ccoObjEventData = eventData Debug.Print " Object ID = " + ccoObjEventData.ID + "; Docbase name = "+ ccoObjEventData.docbaseName + "; is new? " + ccoObjEventData.IsNew Set ccoObjEventData = Nothing Else Debug.Print " Expected data missing!" End If Case DC_EVT_OBJ_CHECKIN Debug.Print " DC_EVT_OBJ_CHECKIN" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set ciObjEventData = eventData Debug.Print " Object ID = " + ciObjEventData.ID + "; Docbase name = "+ ciObjEventData.docbaseName + "; new object ID = " + ciObjEventData.NewID Set ciObjEventData = Nothing Else Debug.Print " Expected data missing!" End If Case DC_EVT_OBJ_CHECKOUT Debug.Print " DC_EVT_OBJ_CHECKOUT" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set docbaseObjEventData = eventData Debug.Print " Object ID = " + docbaseObjEventData.ID + "; Docbase name= " + docbaseObjEventData.docbaseName Set docbaseObjEventData = Nothing Else Debug.Print " Expected data missing!" End If Case DC_EVT_OBJ_CREATION Debug.Print " DC_EVT_OBJ_CREATION" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set containedObjEventData = eventData Debug.Print " Object ID = " + containedObjEventData.ID + "; Docbasename = " + containedObjEventData.docbaseName + "; container ID = " +containedObjEventData.ContainerID Set containedObjEventData = Nothing Else Debug.Print " Expected data missing!" End If Case DC_EVT_OBJ_DELETION Debug.Print " DC_EVT_OBJ_DELETION" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set deleteObjEventData = eventData

Page 48: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 48 of 78

Debug.Print " Object ID = " + deleteObjEventData.ID + "; Docbase name= " + deleteObjEventData.docbaseName + "; chronicle ID = " + deleteObjEventData.ChronicleID + ";type = " + CStr(deleteObjEventData.Type) Set deleteObjEventData = Nothing Else Debug.Print " Expected data missing!" End If Case DC_EVT_OBJ_LOCAL_REVISION Debug.Print " DC_EVT_OBJ_LOCAL_REVISION" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set docbaseObjEventData = eventData Debug.Print " Object ID = " + docbaseObjEventData.ID + "; Docbase name= " + docbaseObjEventData.docbaseName Set docbaseObjEventData = Nothing Else Debug.Print " Expected data missing!" End If Case DC_EVT_OBJ_OPEN_DIALOG Debug.Print " DC_EVT_OBJ_OPEN_DIALOG" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set functionObjEventData = eventData Debug.Print " Object ID = " + functionObjEventData.ID + "; Docbasename = " + functionObjEventData.docbaseName + "; function = " + functionObjEventData.Function Set functionObjEventData = Nothing Else Debug.Print " Expected data missing!" End If Case DC_EVT_OBJ_RENDITION_REVISION Debug.Print " DC_EVT_OBJ_RENDITION_REVISION" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set formatObjEventData = eventData Debug.Print " Object ID = " + formatObjEventData.ID + "; Docbase name= " + formatObjEventData.docbaseName + "; format = " + formatObjEventData.Format + "; new format= " + formatObjEventData.NewFormat Set formatObjEventData = Nothing Else Debug.Print " Expected data missing!" End If Case DC_EVT_OBJ_SERVER_REVISION Debug.Print " DC_EVT_OBJ_SERVER_REVISION" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set docbaseObjEventData = eventData Debug.Print " Object ID = " + docbaseObjEventData.ID + "; Docbase name= " + docbaseObjEventData.docbaseName + "; = " + docbaseObjEventData.ContainerID Set docbaseObjEventData = Nothing Else Debug.Print " Expected data missing!" End If Case DC_EVT_SESSION_CREATION Debug.Print " DC_EVT_SESSION_CREATION" 'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set sessionEventData = eventData Debug.Print " Docbase name = " + sessionEventData.docbaseName + ";user OS name = " + sessionEventData.userOSName + "; domain = " + sessionEventData.domain + ";secure password = " + sessionEventData.SecurePassword Set sessionEventData = Nothing Else Debug.Print " Expected data missing!" End If Case DC_EVT_SESSION_DELETION Debug.Print " DC_EVT_SESSION_DELETION"

Page 49: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 49 of 78

'Don't assume that just because you expect data to be present that it is. 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set sessionEventData = eventData Debug.Print " Docbase name = " + sessionEventData.docbaseName + ";user OS name = " + sessionEventData.userOSName + "; domain = " + sessionEventData.domain + ";secure password = " + sessionEventData.SecurePassword Set sessionEventData = Nothing Else Debug.Print " Expected data missing!" End If Case Else 'Be aware that the desktop client event dispatching system is 'designed to grow beyond the standard set of events initially 'provided. You should always design your event sinks to handle 'the unexpected. Debug.Print " Custom Event Code" If eventData Is Nothing Then Debug.Print " No data provided" Else Debug.Print " Additional data provided" 'It is assumed that one can tell what the associated data is based 'on event type and event code. So in this case, there would likely 'be a tight coupling between SendEvent caller and HandleEvent 'implementation in terms of event data knowledge. End If End Select

GoTo ExitRoutineHandleError: 'TODO: Add your own custom error handling logic here.ExitRoutine: 'single exit pointEnd Function

' . . .

'---------- Helper routines (private) ----------

Private Function DefaultHandleEventReturnValue(ByVal eventType As Long) As Boolean Select Case eventType Case DC_EVT_TYPE_STATE_TRANSITION Debug.Print " DC_EVT_TYPE_STATE_TRANSITION" 'The Documentum Notifier is looking for the first sink to veto this state 'transition, either explicitly by returning False from HandleEvent or 'implicitly by not responding to the state transition in the allotted time '(i.e. 15 seconds by default). 'Allow state transition (False implies veto). DefaultHandleEventReturnValue = True Case DC_EVT_TYPE_QUERY Debug.Print " DC_EVT_TYPE_QUERY" 'The Documentum Notifier is looking for the first sink to answer True 'to this query. Answering False will cause the Notifier to continue 'dispatching the query event. Event sinks can also time-out when handling 'query events (i.e. one second by default); however, time-outs only serve 'to signal the Notifier to move on to the next interested party (registered 'event sink). That is, a query event time-out is the same as returning False. 'Keep asking the system for an answer I don't have (True implies that I have 'an answer). DefaultHandleEventReturnValue = False Case DC_EVT_TYPE_NOTIFICATION Debug.Print " DC_EVT_TYPE_NOTIFICATION" 'Event sinks should always return True to notification events. Notifications 'should only be reacted to. That is, the specified operation has already taken 'place--your handler has no say in the matter! DefaultHandleEventReturnValue = True Case DC_EVT_TYPE_CANCELATION Debug.Print " DC_EVT_TYPE_CANCELATION" 'Event sinks should always return True to cancellation events. Cancellations 'should only be reacted to. That is, the specified operation has failed to take 'place--your handler has no say in the matter! DefaultHandleEventReturnValue = True

Page 50: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 50 of 78

Case Else 'Be aware that the desktop client event dispatching system is 'designed to grow beyond the standard set of events initially 'provided. You should always design your event sinks to handle 'the unexpected. Debug.Print " Custom Event Type" DefaultHandleEventReturnValue = True '??? End SelectEnd Function

Figure 32. VB Sample Code – Handling Event Data within HandleEvent

As Figure 32 demonstrates, always check for the existence of event data in your eventsink’s HandleEvent method, even when data is expected (i.e. “If Not (eventData Is Nothing)Then”). Just because your code is well-behaved doesn't mean that your coworker’s code willbe. Seriously, you should be able to assume the presence of a particular event datainterface based on code and type, but someone may accidentally develop code that setseventData to Nothing in VB. If you don't check for Nothing first, your code will throw anexception when it tries to use eventData (i.e. Set thisEventData = eventData).

When you’re done with the specific event data be sure to release it (i.e. Set thisEventData =Nothing). Do not release the input eventData object. eventData will be released by theoriginal SendEvent caller.

Figure 33 provides a summary mapping of standard desktop client event codes to expectedevent data interfaces. The Documentum Event Server provides a standard implementationfor each event data interface. In VB’s Object Browser, these objects in DCEVENTSERVERLibtake the form Dc…EventData.

Event Code (eventCode) Event Data Interface (eventData) NotesDC_EVT_ENV_CLIPBOARD_REVISION Nothing (VB) / NULL (C++)DC_EVT_ENV_VIEW_OPTIONS_REVISION Nothing (VB) / NULL (C++)DC_EVT_ENV_WORK_OFFLINE Nothing (VB) / NULL (C++)DC_EVT_ENV_WORK_ONLINE Nothing (VB) / NULL (C++)DC_EVT_LINK_CREATION IDcContainedObjEventDataDC_EVT_LINK_DELETION IDcContainedObjEventDataDC_EVT_LOCAL_COPY_CREATION IDcObjectEventDataDC_EVT_LOCAL_COPY_DELETION IDcObjectEventDataDC_EVT_OBJ_CANCEL_CHECKOUT IDcCancelCheckOutObjEventData 1DC_EVT_OBJ_CHECKIN IDcCheckInObjEventDataDC_EVT_OBJ_CHECKOUT IDcDocbaseObjEventDataDC_EVT_OBJ_CREATION IDcContainedObjEventDataDC_EVT_OBJ_DELETION IDcDeleteObjEventData 2DC_EVT_OBJ_LOCAL_REVISION IDcDocbaseObjEventDataDC_EVT_OBJ_OPEN_DIALOG IDcFunctionObjEventDataDC_EVT_OBJ_RENDITION_REVISION IDcFormatObjEventDataDC_EVT_OBJ_SERVER_REVISION IDcDocbaseObjEventDataDC_EVT_SESSION_CREATION IDcSessionEventDataDC_EVT_SESSION_DELETION IDcSessionEventData 3

Notes1. IsNew defaults to False; set to True if appropriate

Page 51: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 51 of 78

2. All clients should define DocbaseName and ID properties. VDM may also defineChronicleID and/or Type. Type defaults to DC_EVT_DELETE_SELECTED_VERSION.

3. SecurePassword is left empty (only used in creation event).

Figure 33. Mapping Event Codes to Event Data Interfaces

The next code example shows how to register your component event sink and also how todispatch an event via the Notifier.

Option Explicit

Private Declare Function GetCurrentProcessId& Lib "kernel32" ()

Private Sub TestRegisterSinkAndCallNotifier(DocbaseName As String, ContainerID As String) Dim notifier As DCEVENTSERVERLib.DcNotifier Dim myEventSink As DcTestEventSink Dim sinkItf As DCEVENTSERVERLib.IDcEventSink

On Error GoTo HandleError

'Create an instance of the Documentum Notifier. Set notifier = New DCEVENTSERVERLib.DcNotifier

'Create an instance of your event sink. Set myEventSink = New DcTestEventSink 'Default interface is not IDcEventSink Set sinkItf = myEventSink 'QueryInterface for IDcEventSink Set myEventSink = Nothing 'Release sinkItf.Name = "My Test Event Sink"

'Register interest with the Notifier by this sink in all event categories. 'You could also have registered interest incrementally (e.g. increase interest 'upon receipt of an event). Subsequent event category registrations should be 'made via RegisterInterest, not RegisterEventSink. Dim myCookie As Long myCookie = notifier.RegisterEventSink sinkItf, sinkItf.Name, DC_EVT_CATEGORY_ALL

Dim newObjectID As String '<Create a new object.>

'Define your event data. Dim myEventData As New DCEVENTSERVERLib.DcContainedObjEventData myEventData.ID = newObjectID myEventData.DocbaseName = DocbaseName myEventData.ContainerID = ContainerID

'Inform all interested parties of the new object via a notification event. 'You can also send events to a specific event sink by specifying that sink’s 'cookie (which you would had to have received earlier in your HandleEvent 'implementation). In our call, DC_EVT_COOKIE_NO_FOLLOWUP implies that we’re 'not interested in being contacted directly; otherwise we would have passed 'our event sink cookie (i.e. myCookie). Dim bSent As Boolean Dim responseMsg As String bSent = notifier.SendEvent(DC_EVT_OBJ_CREATION, _ DC_EVT_TYPE_NOTIFICATION, _ DC_EVT_COOKIE_BROADCAST, _ DC_EVT_COOKIE_NO_FOLLOWUP, _ "Test Notifier Client", _ GetCurrentProcessId(), _ myEventData, _ responseMsg) 'Output

Set myEventData = Nothing 'Release

'Completely unregister your event sink with the Notifier. Unregistration may 'also be incremental. Reset our local cookie data, too, since this is our final 'and complete unregistration call.

Page 52: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 52 of 78

notifier.UnregisterInterest myCookie, DC_EVT_CATEGORY_ALL myCookie = DC_EVT_COOKIE_MIN_VALID – 1 'i.e. an invalid cookie

'Explicit cleanup--objects are automatically cleaned up by the VB runtime 'when they go out of scope. Set sinkItf = Nothing 'Release Set notifier = Nothing 'Release

GoTo ExitRoutineHandleError: 'TODO: Add your error handling here.ExitRoutine: 'Single exit pointEnd Sub

Figure 34. VB Sample Code – Interfacing with the Notifier

The Documentum Event Server also supports the notion of queuing events. You call theNotifier’s GetEventQueue method to receive an event queue. Although event queues arecreated solely by the Notifier, their lifetimes are not dependent on the existence of theNotifier. That is, unless you have other reasons to keep the Notifier around, you can releasethe Notifier after calling GetEventQueue. When you call the event queue’s Send method, thequeue itself will instantiate the Notifier in order to call SendEvent.

Only events that don’t require or expect responses should be queued. You should not queuequery or state transition events. In fact, if you do attempt this, Add will return False and theevent won’t be queued. An application typically queues events while it has lock on a DFCshared session. Once it releases its lock it will tell the event queue to send itself. Since morethan one event may be queued, there isn’t a way for the event queue client to receive aresponse to any one event in the queue.

. . . 'Get an event queue object from the Notifier. Dim eventQueue As DCEVENTSERVERLib.IDcEventQueue Set eventQueue = notifier.GetEventQueue Set notifier = Nothing 'Release 'Lock a DFC shared session . . . 'Do some operation that will result in a notification Dim operationWasSuccessful As Boolean . . . 'Create the appropriate event data object. Dim eventData As DCEVENTSERVERLib.DcObjectEventData Set eventData = New DCEVENTSERVERLib.DcObjectEventData eventData.DocbaseName = docbaseName eventData.ObjectID = objectID Dim eventWasQueued As Boolean If operationWasSuccessful Then eventWasQueued = eventQueue.Add(..., eventData) 'Notification Else eventWasQueued = eventQueue.Add(..., eventData) 'Cancellation End If Set eventData = Nothing 'Release 'Continue to use the locked session. . . . 'Unlock the session. . . . eventQueue.Send Set eventQueue = Nothing 'Release . . .

Figure 35. VB Sample Code – Queuing Events

Page 53: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 53 of 78

6.3 Add Skin (User Interface)

If your component supports a user interface, add the necessary forms to your componenthousing project. I recommend that you use the following naming convention for all forms:• Form’s Name property – frmDc<descriptive name> (e.g. frmDcFolder)• Form’s physical file name – Dc<descriptive name>.frm (e.g. DcFolder.frm)

How to design a UI in VB by placing controls on a form, is beyond the scope of thisdocument. Please consult Microsoft documentation as well as the selected bibliographyprovided in Appendix B.

If your component will employ strings, bitmaps and/or cursors, etc., consider adding aresource (.RES) file to your component project. Using string tables in resource files is oneway to make your component internationalized (i.e. ready to be localized).

VB6 comes with a Resource Editor that makes resource manipulation fairly straightforward.Here’s how to add the Resource Editor to your VB6 IDE, in the event that you don’t alreadyfind it as a member of the standard toolbar:

1. Add the VB6 Add-in Toolbar to your IDE as follows:a. Select Add-ins | Add-in Manager...b. Hilight "VB 6 Add-in Toolbar" and check "Loaded/Unloaded" and "Load on Startup"

for its load behavior.c. Select OK to dismiss the dialog.d. You should see a "+/-" icon on a toolbar palette now.

2. Add the VB6 Resource Editor to the Add-in Toolbar as follows:a. Click on the new "+/-" icon in your toolbar.b. Check "VB 6 Resource Editor".c. Select OK to dismiss the dialog.d. You should see a "hand over a green Rubix cube" (for lack of a better description)

icon next to the "+/-" icon now.

To create a new binary resource (.RES) file using the editor:

1. Click on the new icon from (2d) above.2. Add the elements required by your component.3. Save your work to your component project folder. This will add a “Related Documents”

folder to your component project that contains your .RES file.

If your component will employ icons and wants to use the services of the Icon Manager(section 5.6), you will need to use a more manual process to produce a .RES file. That is,you won’t be able to use VB’s Resource Editor. Instead, please do as follows:

1. Create a Resource folder under your main component project folder.2. Open a new ASCII text file. Using standard resource source/definition (.RC) file syntax,

add all of your resources. Save the file to your new Resource folder with a .RC extension(e.g. C:\Documentum Desktop Client\components\DcPropertiesComp\Resource\DcPropertiesComp.rc). Figure 36 shows an example .RC file.

3. Add your icon, bitmap and/or cursor files to the Resource folder.4. Use the Resource Compiler (i.e. RC.EXE) to compile your .RC and resource files into a

binary resource (.RES) file. At Documentum, we use batch (.BAT) files to help automatethis process. A typical batch file contains the following lines:

Page 54: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 54 of 78

RC /V /X /I "../../iconmgr/Images" Resource\DcPropertiesComp.rcattrib -r DcPropertiesComp.rescopy Resource\DcPropertiesComp.res .\*del Resource\DcPropertiesComp.res

5. Add the resulting .RES file to your component project.

#include "DcIconRes.rc"

// Bitmaps

//#define IDB_BITMAP1 1

//IDB_BITMAP1 BITMAP LOADONCALL MOVEABLE DISCARDABLE "Bitmap1.bmp"

// Icons (In reality these are treated as bitmaps.)

// In Visual Basic, the icon resource whose ID is 1 is reserved for the application// icon. Icon resource ID zero is also reserved to VB. Therefore, you can't have a// resource in your .RES file with either ID number. VB generates an error message// if your code attempts to load either resource ID.

// Be sure to avoid index conflicts with Icon Manager controlled icons.//#define IDI_ICON 2

// String Table

//#define IDS_STRING1 1#define IDS_DOCUMENTUM_DESKTOP_CLIENT 2

STRINGTABLE LOADONCALL MOVEABLE DISCARDABLEBEGIN// IDS_STRING1, "String1 goes here" IDS_DOCUMENTUM_DESKTOP_CLIENT, "Documentum Desktop Client"END

Figure 36. Sample Component Resource Source/Definition (.RC) File

Please be especially careful when modifying component .RC files. Do not use Visual C++'sResource Editor to make changes as it will completely replace your existing resource filewith one that depends on a Resource.h header file—which makes sense to a C++ developerbut not a VB developer. Open your .RC file manually in text mode with VC++ or launchNotepad to make your edits.

6.4 Add Brains (Business Logic)

Your component is responsible for carrying out a specific function to satisfy your businessrules and processes. How it satisfies functional requests using the Documentum DesktopClient development environment is left up to you and is beyond the scope of this document.

6.5 Modal Versus Modeless Components (LifetimeManagement)

As we talked about in section 5.4, when the Documentum Component Dispatcherinstantiates a component it will then call the component’s IDcComponent methods in the

Page 55: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 55 of 78

following order: Init, Run and DeInit. Following its call to DeInit, Component Dispatchercalls Release on the IDcComponent interface pointer.

Modeless components are both similar to and different from modal components. They'resimilar in that their lifetime is COM-based in proper reference counting. They're different inhow they interact with Component Dispatcher due to the nature of their user interface (UI)(i.e. the characteristic which makes a component either modal or modeless).

Modal components will not return from their Run method to Component Dispatcher untiltheir user interface is dismissed. The DeInit method of a modal component usually performsthe job of freeing resources acquired during Init processing. When a modal componentreturns from its DeInit method to Component Dispatcher, it expects to be destroyed. WhenComponent Dispatcher calls Release on the pointer to the modal component'sIDcComponent interface, that expectation is met (i.e. the modal component is deleted frommemory due to a reference count of zero).

On the other hand, modeless components will immediately return from their Run method toComponent Dispatcher as soon as their modeless UI is displayed. Component Dispatcher willthen call a modeless component's DeInit method, while the component's UI may still beactive. The DeInit method of a modeless component should do absolutely nothing butsimply return DC_COMP_SUCCESS. Modeless component resource cleanup must occurwithin its UI. When a modeless component returns from its DeInit method to ComponentDispatcher, it does not expect to be destroyed. However, unless the modeless componenttakes responsibility for its own COM-based lifetime, it will be deleted from memory whenComponent Dispatcher calls Release on the pointer to the modeless component'sIDcComponent interface.

So, modeless components should increment their own reference count within theirIDcComponent::Init method. Right before exiting their UI, modeless components shoulddecrement their own reference count, allowing themselves to be deleted from memory.

Note that it is possible for a component to behave in both modes of operation. Suchcomponents will typically query their environment via the NonModalAllowed property of VB’sApp object to see if modeless operation is supported. If it is then the component’s UI will bemodeless; otherwise the component should gracefully degrade to modal presentation of itsUI. Any component that is written to be modeless should be able to gracefully degrade to bemodal. In either mode, the component must manage its COM-based lifetime properly.

6.6 Setting Proper Version Compatibility

Before you begin debugging your component, you’ll need to change its version compatibilitysettings.

1. Create a RefCopy folder inside the folder where your component project resides.2. Make your component housing in VB (e.g. File | Make dcpropertiescomp.dll…) then copy

the just-built DLL into your RefCopy folder.3. Back in VB, go to the project properties dialog and select the Component tab. Change

Version Compatibility to Binary Compatibility, and set it to the reference copy, not theoriginal.

Creating a reference copy is important since VB wipes out the previous target executable inits build process. This way the reference remains constant with respect to its interfaces

Page 56: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 56 of 78

(contracts with its clients) while the underlying implementation of your componentinterfaces is free to evolve.

So why is binary compatibility so important? Turning on this feature of VB enables VB toautomatically warn you when you are about to produce an executable that breaks one ormore established interfaces. Think of an interface as a contract between a client and animplementation. As soon as there is at least one client of an interface, if the interfacechanges, that client can no longer function if the interfaces changes without changing—modifying and rebuilding—itself. A contract has been broken. Having binary compatibilityset tells VB that you don't want to blindly break contracts—you'd like to keep your clients inbusiness. That is, binary compatibility helps to ensure backward compatibility.

Dan Appleman’s text referenced in Appendix B contains valuable information regardingversion compatibility. Please consult its fifth chapter to learn more about this importanttopic.

7 Debugging Desktop Client Components

The Documentum desktop client environment consists mostly of COM in-process servers(DLLs) which load themselves into any number of parent processes (EXEs). A primaryexample parent is Windows Explorer where the DLL loading “front door,” if you will, is theDocumentum shell namespace extension, also known as the Windows shell integration. TheDocumentum 4i Desktop Client also integrates with Office applications via add-ons (e.g.Word) and has its own standalone applications (e.g. Virtual Document Editor). TheDocumentum 4i Windows shell integration, for example, can cause one or more COM-baseddesktop client components to be instantiated and manipulated through a common COMinterface, IDcComponent. Desktop client functionality has been "componentized," so thatideally one component satisfies one type of function (e.g. properties, check-in, find, etc.).

Certain functional components (the focus of this document) are designed with customizationin mind and are written in VB. VB is a higher level language that allows a componentdeveloper to spend more time solving business problems rather than writing COM-basedinfrastructure to enable custom business solutions. Regardless of language, any custombusiness solution should be thoroughly tested before its rolled out to the entire enterprise.Debugging your components should be part of your test plan.

VB provides excellent design-time (interpreted) debugging facilities. However, VB does notprovide the necessary facilities for run-time debugging of a component within its desktopclient environment. As a result I recommend a two-phase approach to componentdebugging: design-time testing within the VB IDE using both (1) a simple test applicationand (2) Documentum Desktop Client software, and run-time testing within the DeveloperStudio IDE of VC++ using Documentum Desktop Client software.

7.1 Testing Design-Time Behavior with VisualBasic

Debugging within the VB IDE is considered a design-time activity—the debug process isinterpretive. During design-time you should perform both unit testing and integrationtesting, in that order.

Page 57: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 57 of 78

To unit test within the VB IDE you’ll need to create a project group which contains yourcomponent ActiveX DLL project and a test harness Standard EXE project. Some of thestandard Documentum desktop client components that may be distributed as VB sourcecode come configured with such a project group.

Each standard executable, ActiveX control, and ActiveX server is built as a separate project.But within the Visual Basic environment you can use File | Add Project… to load as manyprojects as you wish into the current environment. For your convenience, you can alsodefine project groups (.VBG files) which simply act as a list of projects that can be loaded asa group. You can still load or save the projects individually since the projects remainindependent. However, it is easier to define a project group than to have to add eachproject to the environment one at a time every time you want to test your component(s).Once a project group has loaded you can tell which project is active based on projectwindow highlighting. If you look at an object in the object browser what you see will dependon the project selected.

To create a separate component test application project and make it part of a project groupwhich includes your component, do the following:

1. Go to File | Add Project… | New tab and select “Standard EXE.” This will create a project(Project1) containing a single form (Form1).

2. Establish the appropriate names for your project and main form (e.g. DcTestComp andfrmDcTestComp, respectively).

3. Go to Project | References… and reference your component housing. Add references toany other required COM servers.

4. Add the standard preamble to your main form.5. Although not required, it’s a good habit to define your project properties beyond what’s

provided by default (e.g. Project | DcTestComp properties…). Instructions for this areprovided above in the steps to create your component project.

6. Save your test project and form to disk (e.g. DcTestComp.vbp and DcTestComp.frm).You will also be asked to save your new project group. Please give your .VBG file thefollowing name: Dc<function>.vbg (e.g. DcProperties.vbg).

7. In the Project window select your test application project, right-click and select “Set asStart Up.”

8. Save your work again.9. Add the test logic appropriate for your component. (How to write specific unit test

harnesses for components is not the focus of this document, since it would be difficult toanticipate every kind of component written.)

Remember that as long as your test application project is running in VB, its reference toyour component project will prevent you from making your component housing. Fortunatelyyour two projects are still totally independent in terms of packaging. Using File | OpenProject… it’s fairly easy to bounce back and forth between your component project (to buildyour component housing) and your component project group (to test your component). Besure to save your work between jumps.

Test your component through your project group once your component has beensuccessfully built. You’ll be able to step through both your test application and componentsource code line-by-line. Debugging a Documentum desktop client component within the VBIDE is no different than debugging any other piece of VB code.

You should test your component thoroughly using Documentum Desktop Client software. Istrongly recommend that you proceed from unit testing to integrated testing, rather than

Page 58: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 58 of 78

distributing your new component to your enterprise immediately following a successful unittest. You may pass integration with flying colors, but you won’t know until you try. Better tofail integration on your local desktop than to cause your entire company to suffer from aless-than-perfect component.

Integration testing requires that you register your component with the Documentum system(via Documentum Developer Studio or DDS) so that DART in turn can provide ComponentDispatcher with your component. Once you’ve registered your component with DDS, youcan use VB6 to debug your component using Windows Explorer.

To do this, you’ll need to modify your component project’s settings as follows:

1. Go to the project properties dialog and select the Debugging tab. (Note: This tab is notavailable on versions of VB prior to 6.0).

2. Select “Start program” and specify the path to your copy of Windows Explorer (e.g.C:\WINNT\Explorer.exe).

3. Whether or not you have “Use existing browser” checked is not important here, sinceyou won’t be using Internet Explorer for your integration testing.

4. Save your work.

Now when you launch your component in debug a new Windows Explorer window will becreated. Within this window, drill down on the Documentum shell namespace extension toan object of interest (e.g. a document in a particular Docbase cabinet or folder), select itand choose the function which exercises your component as defined in your DocApp. If yourDocApp is setup correctly, you should be able to debug your component as soon asComponent Dispatcher calls its IDcComponent methods. Therefore you may want to setbreakpoints in Init, Run and DeInit.

7.2 Testing Run-Time Behavior with Visual C++

Although unit testing and integration testing of components are both supported in VBthrough the current specification of IDcComponent, VB only supports design-timedebugging. In order to thoroughly test the run-time behavior of your component, you mustuse Visual C++ (VC++).

Once you’ve registered your component with the Documentum system (via DocumentumDeveloper Studio) so that DART in turn can provide Component Dispatcher with yourcomponent, you can debug your component in VC++ using, for example, Windows Exploreras your parent process. The following example of loading a compiled VB component and itssource code into VC++ is based on Microsoft Knowledge Base (KB) article Q166275, entitled“HOWTO: Debug a Native Code Visual Basic Component in VC++,” and actually applies toboth 5.0 and 6.0 versions of VB and VC++.

1. In VB, open component (ActiveX DLL) project you want to debug. From the File menu,choose Make <YourProject>.dll… . Click the Options button and select the Compile tab.Choose Compile to Native Code and Create Symbolic Debug Info. Then select OK, andOK again to compile your component.

2. Exit VB and launch VC++.3. Choose File | Open Workspace… . In the Open Workspace dialog, set “Files of type” to

“All Files (*.*),” and then select your compiled component (e.g.DcPropertiesComp.dll).

Page 59: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 59 of 78

4. Now choose File | Open… (not Open Workspace…) to open each VB module (.BAS), form(.FRM), or class module (.CLS) that you want to debug (e.g. DcPropertiesComp.cls).

5. Set break points (F9), watches, step-through instructions, etc., which you would for anormal program. You may also want to display variable information in the debugenvironment by going to the Tools menu, selecting Options, and then selecting theDebug tab. In the Debug tab, place a check next to “Display Unicode strings.”

6. You must specify a process to debug with. To do this, from the Project menu selectSettings, and then click the Debug tab. In the “Executable for debug session” TextBoxspecify the .EXE file that references your DLL. That is, select Windows Explorer via thearrow (browse) button off to the right (e.g. C:\WINNT\Explorer.exe). The connectionbetween Windows Explorer and in this case the standard Docbase object-basedproperties component is through Documentum’s shell namespace extension(DCEXPLORERINTG.DLL) and the Documentum Component Dispatcher(DCCMPDSP.DLL).

7. From the Build menu in VC++, select Start Debug then Go, or press the F5 key. Whenyour VB compiled component reaches a line you set a breakpoint on (e.g. you selectProperties… on a document in a Docbase), it will stop in VC++ and allow you to performnormal debug operations. Note that VC++ doesn’t recognize VB’s Debug.Printstatements.

8. If your component isn't invoked at all, verify that it is properly registered with theDocumentum system (via Documentum Developer Studio) so that DART in turn canprovide Component Dispatcher with your component.

When following the procedure outlined above from KB article Q166275, there is a restrictionin the sense that you can only see the value of local variables. For example, you will not seethe value of globals declared in .BAS modules, or variables declared at the form or classmodule level. To see non-local variables requires some additional effort. Basically what youneed to do is to find out the address of these variables. Once you have the address you canuse the memory window and watch the content of the memory to get the value of thevariable.

Here are at least two possible solutions to watching non-local variables:

1. The first possibility seems to me the most complicated one, and will work only forprogrammers that have some understanding of assembler.a. From the source code window you go to the disassembly window.b. Search for a line of code where you use the global variable you are looking for.c. Analyze the assembly code to find out the address.

2. The second alternative is easier, but it requires that you add some lines of code in yourapplication that will provide you with the address you need. You can use anundocumented function that is available in VB for this purpose, called VarPtr (“Pointerto a Variable”). This function returns the address of a variable in VB. This is how you useit:

Dim TheAddress as LongDim MyVariable as DoubleTheAddress = VarPtr (MyVariable) 'Return the address of MyVariable.

So, by using this function we could do the following:

At the beginning of a function where you need to watch some global variables,you could just place a line of code like:

Page 60: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 60 of 78

MyAddress = VarPtr (MyGlobalVariable)

When you break into the code you already have the address available. Just go tothe memory window and check out the address. If you need to watch severalfunctions you could just add some equivalent code in an Initialize event andmake a note for the needed addresses, as the globals are static and don't changetheir address. Another point to consider is that you actually don't need theaddress for each of your variables declared in a .BAS module. It is enough to getthe address for the first one, as they are all one after the other.

There are two types of variables that need special attention—arrays and strings.

Arrays in VB are SafeArrays. If you want the address of the SafeArray structureitself, you need to call the undocumented VarPtrArray function. Just add thefollowing declaration to a .BAS file:

Declare Function VarPtrArray Lib "msvbvm50.dll" Alias VarPtr (Var()as Any) as Long

To use it:

Dim ArrayAddress as LongDim ArrayOfLongs(5) as Long

ArrayAddress = VarPtrArray (ArrayOfLongs()) 'Return the address 'of the SafeArray structure.

You could also get access to the first element of the array using the VarPtrfunction, and consequently see all elements of the array one after the other.However you need to take into account that the actual location of elements of thearray can change with dynamic arrays. To get the address of the first element ofthe array do:

ElementAddress = VarPtr (ArrayOfLongs(0))

Strings in VB are stored as BSTR which ultimately resolves to be a wchar*. Ifyou use VarPtr on a string variable you get the address of the BSTR, or in otherwords a wchar**. To help you with this there is another undocumentedfunction, called StrPtr that returns the address of the first character of a string.To use:

Dim MyCharAddress As LongDim MyString As String

MyString = "Any old string"MyCharAddress = StrPtr (MyString) 'Return the address of the 'beginning of the string.

Don't forget that strings are internally stored as Unicode in VB.

For more details, you may wish to consult Microsoft Support Online and in particular KBarticle Q166275. The information in this article applies to both VB and VB6. You can get KBarticles from the Microsoft Web site or through email. To get them through email just sendan email to [email protected] and in the subject write the article’s ID, in this caseQ166275. The hyperlink for Q166275 ishttp://support.microsoft.com/support/kb/articles/q166/2/75.asp?FR=0. You can also find

Page 61: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 61 of 78

additional information concerning cross-language debugging in the May 1997 issue ofMicrosoft Systems Journal (or MSJ for short) article on the subject.

When debugging arrays, strings and variants it may be useful to understand what is behindthese data types. In addition to the normal documentation of all the manipulation functions,there is a set of five articles in MSDN Library, written by Bruce McKinney entitled “ExtendingVisual Basic with C++ DLLs,” that could help better your understanding.

8 Modifying an Existing Desktop Client Component

You may be able to leverage an existing Documentum desktop client component to solveyour business problem. Rather than creating a new component from scratch you may decideto tweak an existing component instead—one of ours or one of your own.

All of the previous content in this document applies to modifying an existing component.Here’s is what you need to do to “reuse” an existing component project:

1. Create a new folder to contain a copy of the component project of interest.2. Copy the component project files of interest into the new folder.3. Open the new folder and launch the copied component project.4. Change the project name and/or class module name to create a unique ProgID for the

new component.5. Temporarily break binary compatibility (No Compatibility) so that VB can assign the new

component (i.e. its class module) a different CLSID. The new ProgID created in (4) willpoint to this new CLSID in the registry.

6. Reset project properties information.7. Rather than doing a simple save at this point, do a “Save As…” for each file in the

project. Change the physical file names to match your internal (variable) name changesfor the new component.

8. Delete the unnecessary files from the new folder (i.e. those with file names from theoriginal component). We only want the files from (7).

9. Expose any new or modified public interfaces to your component. IDcComponent mustbe exposed as-is.

10. Make your component (i.e. File | Make <your component name>.dll…).11. Create a reference copy of the new DLL and establish binary compatibility to this

reference as described in section 6.6.12. Save your work.13. Change your component’s UI and/or underlying implementation to satisfy your business

requirements.

The above steps assume that both source and target VB projects are of type “ActiveX DLL.”As I mentioned earlier in section 6, this doesn’t have to be the case. Desktop clientcomponents are simply COM objects that implement at least the IDcComponent interface.Documentum Desktop Client software is not concerned with a component’s housing.Components may be housed in DLLs or EXEs. So your target and/or source VB project maybe “ActiveX EXE” projects.

9 Deploying Desktop Client Components

Page 62: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 62 of 78

So far everything that we’ve talked about involves your own development machine.However, for an end user to cause a desktop client component to be dispatched, thecomponent must exist on his machine, not yours. How do you deploy your new orcustomized desktop client component to the rest of your enterprise? This section will answerthat question. The answer assumes that you are familiar with Documentum DeveloperStudio (DDS) and DocApps.

Section 5.4 and Figure 22 both reveal that there are three key pieces of client-side softwarethat allow the system to deliver the right functionality to the right person at the right time:the Component Dispatcher, DART and the Cabinet Manager. Both DART and the CabinetManager rely on the specification of a component in a Docbase through a package known asa DocApp. DocApps contain dm_qual_comp and dm_component objects, the formerrepresenting component invocation contexts and the later representing componentattributes and, if specified, content. DDS is used to define and configure DocApps. Figure 37shows this flow graphically from developer machine to end user machine.

Developermachine Server

End usermachine

Define

Discover

Deliver &register

Instantiate& manipulate

DDS Docbase

CabinetManager

DART

Desktop ClientComponent

ComponentDispatcher

Figure 37. Physical Desktop Client Component Flow Diagram

Standard desktop client components are installed on an end user’s machine through theDocumentum Desktop Client installer (SETUP.EXE). That is, when launched, the installer willcopy component executables locally and then tell the executables to register themselveswith the local system. A desktop client component must be recognized by the local systemin order for the Component Dispatcher to instantiate it and manipulate it (i.e. call itsIDcComponent methods). The Component Dispatcher will only instantiate components forwhich it receives a valid COM CLSID back from DART. In order to return a CLSID, DARTmust query a Docbase for a qualified component that matches the end user’s functionalrequest. Without a DocApp in the Docbase of interest, no match can be made and nocomponent will be dispatched.

New or customized desktop client components are provided to end users through a processknown as Dynamic Component Delivery (DCD). After a match has been made in theDocbase of interest between a functional request and a component satisfying that request,DCD starts by determining whether or not the component is already installed on the localsystem. In the case of a standard, unmodified desktop client component, the component isinstalled, so the Component Dispatcher can immediately instantiate the component throughits CLSID. If the component is not installed locally or the version installed locally is olderthan the component in the Docbase, DCD then retrieves component content (i.e. a cabinetfile) from the Docbase and uses a COM-based technology known as Internet ComponentDownload (ICD) to install the component from the cabinet file. ICD-based installation issimilar to installer-based installation in that, once complete, the component executable hasbeen copied to the local machine and registered with the local system. The Component

Page 63: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 63 of 78

Dispatcher can now instantiate and manipulate the brand new component as it would anyother desktop client component.

To be absolutely clear, all requests for desktop client component functionality employ DCD.Some components like standard ones do not specify content in a Docbase, making DCDquite simple, while other that do specify content make DCD more involved. We’ll look atboth kinds of components in section 9.3.

9.1 Creating Component Content

The Documentum Cabinet Manager (DCCABMGR.DLL) provides three system objects tomanage the creation and installation of desktop client component cabinet (.CAB) files:DcCabCreator, DcCabInstaller and DcCabResolver. When you add a component cabinet to aDocApp via the DDS Component Editor, DDS uses a Cabinet Resolver object to extractinformation from the component cabinet file in order to set dm_component attributes (i.e.com_class_id (ComponentCLSID), component_version (ComponentFileVersion)) anduniq_cont_ticket (CabinetCRC property’s value). On the download side of the process, theComponent Dispatcher becomes a client of a Cabinet Installer object if DART reports thatthe desired component needs to be installed locally. In general your code shouldn’t need tointerface directly with either DcCabResolver or DcCabInstaller. However, you may wish tointerface with a Cabinet Creator object to create your component content. Figure 38 showswhat the Cabinet Creator looks like in VB’s Object Browser.

Figure 38. Looking at the Cabinet Manager Type Library in VB’s Object Browser

The Cabinet Creator provides an interface to specify the primary component and anydependencies that should be placed into a cabinet (.CAB) file. If your component has nodependencies besides those that are already provided to an end user via the standarddesktop client installation, it is recommended that you use the Cabinet Creator to generateyour component content. Using the Cabinet Creator in this case is straightforward as shownin the following sample VB code. This code assumes that you’ve established a reference tothe following type library: “Documentum Cabinet Manager 1.0 Type Library”(DCCABMGRLib) and requires that you have your component and any of its dependenciescorrectly registered on your development machine (i.e. the machine from where this code isrun).

Page 64: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 64 of 78

Private Sub TestCreateComponentCabinet() Dim cabinetCreator As DCCABMGRLib.DcCabCreator On Error GoTo HandleError Set cabinetCreator = New DCCABMGRLib.DcCabCreator Dim retVal As Boolean retVal = cabinetCreator.SetComponent("DCPROPERTIESLib.DcPropertiesComp") 'Use the AddDependency method of the Cabinet Creator object to add system and non-system '"internal" dependencies of this component into its cabinet file. For example: 'retVal = cabinetCreator.AddDependency("DFWGROUPSLib.DfwGroupsCombo") retVal = cabinetCreator.CreateCabinet("C:\TEMP") 'Filename will be DcPropertiesComp.cab. 'If your code will create additional cabinets, call Reset after each call 'to CreateCabinet. Set cabinetCreator = Nothing 'Release GoTo ExitRoutineHandleError: MsgBox "Failed to create component cabinet"ExitRoutine: 'single exit point cmdExit.Enabled = TrueEnd Sub

Figure 39. VB Sample Code – Using the Documentum Cabinet Manager to CreateComponent Content

While the Cabinet Creator can certainly create component cabinets that contain componentdependencies (e.g. Documentum ActiveX controls, third party ActiveX controls orexecutables), if your component is written in VB, you may choose to employ VB’s Package &Deployment Wizard to create your cabinet (i.e. “Internet Package option).

If you choose to use the Package & Deployment Wizard, be sure to save all intermediatefiles generated by the wizard. For example, if your VB component project contains morethan one class module (as many of the Documentum projects do), you may need to edit theINF file in the Support folder to correctly specify the main component (i.e. its name andinformation, like CLSID and FileVersion). The INF file must be correct in order for DCD (ICD)to function properly. You can determine CLSIDs via OLEView and FileVersions via WindowsExplorer. In order to avoid content bloat in your Docbase, remove all dependencies found bythe wizard that are already provided by the standard desktop client installer.

Thus far we’ve talked about a single type of dependency, the kind that exists inside a singlecabinet or an internal dependency. You may also specify dependencies between cabinets ina virtual document fashion (external dependencies). For example, you may find that severalof your custom components all use the same set of Documentum or third party ActiveXcontrols not already provided with the desktop client. You can create a cabinet to contain allof these files and then specify that each of your component cabinets depends on this“dependency cabinet.” Using external dependencies is another way to trim down theamount of component-related content in your Docbases.

Please adhere to and/or be aware of the following rules regarding dm_component objectsand their content (i.e. cabinet files).

1. Be sure to increment your component’s version number as mentioned previously insection 6.1. The version of a component in a DocApp must be greater than the locallyinstalled version of the same component in order for DCD to automatically provide thenew component to the end user. Proper versioning also applies to all (internal)

Page 65: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 65 of 78

dependencies contained in your component cabinets. This requirement is levied byMicrosoft’s ICD technology.

2. Maintain backward compatibility among all versions of the same component. By “same” Imean equal GUIDs (i.e. CLSIDs and IIDs). The desktop client system marches forwardnot backward with respect to file version. Consider the following example.

Your PC Docbase A Docbase BMyComp.dll (v1) MyComp.cab (MyComp.dll (v2)) MyComp.cab (MyComp.dll (v3))

With DCD enabled, you go to Docbase A and request a function that MyComp.dllimplements. The system determines that you need v2 of this component and installs itfrom the Docbase on your PC. You then go to Docbase B and request the same function.v3 of the component is installed on your PC. Now whenever you go back to eitherDocbase A or Docbase B, you'll be running v3 of MyComp.dll.

3. If you modify a component such that it is no longer backward compatible, you mustestablish at least a new CLSID for the component using VB, for example, and thenreflect the new CLSID in a DocApp using DDS. You should also change the name of yourcomponent to avoid overwriting older components on an end user’s machine.

4. Components are related to specific versions of their dependencies.5. Dependencies should not be set up in such a way that a component could end up

depending on itself. DDS won’t allow you to create a cycle in the component dependencygraph.

6. Installation of certain executables may require a reboot (e.g. Microsoft redistributables).Please consult Microsoft and third party documentation to see if a reboot will be requiredafter your component’s cabinet is installed.

9.2 Signing Component Content

Once you’ve created a cabinet file containing your desktop client component and anysoftware dependencies it might have, you should “sign” the cabinet. Properly signedcabinets inform the system about to install the cabinet’s contents that the contents comefrom a source that can be trusted. Software that Documentum provides in the form ofcabinet (.CAB) files should be signed, letting you know that our software is genuine andtrustworthy in your enterprise.

Briefly, here is how to sign a cabinet file. As an example, I will assume that you’vecustomized the standard Documentum Docbase object-based Properties Component,DcPropertiesComp.dll, and have packaged it into a cabinet file. The following steps assumethat you have already acquired both a private key (.PVK) file and a Software PublisherCertificate (.SPC) file through a provider like VeriSign and that signing will occur in aMicrosoft (Authenticode) environment (i.e. not Netscape).

1. If your cabinet wasn’t created by either the Documentum Cabinet Creator or VB’sPackage & Deployment Wizard, prepare your cabinet file to be signed by allocating spacefor signing using CABARC.EXE’s "-s" option as follows: cabarc -s 6144 ... . This willallocate 6144 bytes for a digital certificate. CABARC.EXE is an application provided byMicrosoft (e.g. in its Java SDK) to create and work with cabinet (.CAB) files. Both thedesktop client and DDS install a version of this application as part of their installations.• If you use the Cabinet Creator object provided in the Documentum Cabinet Manager,

DCCABMGR.DLL, to create your cabinet file, the resulting file will have allocatedspace for a digital certificate.

Page 66: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 66 of 78

2. You can now sign your cabinet. To do so, you will use the SIGNCODE.EXE utility includedin the Microsoft Internet Client SDK. SIGNCODE.EXE requires that SIGNER.DLL also bepresent somewhere in your PATH. You'll need to make sure that you're using the rightsigning tools for the version of Internet Explorer (IE) that you’re running on yourdevelopment machine.a. IE 4.x Internet Client SDK -

http://msdn.microsoft.com/downloads/tools/authcodeie4/authcodeie4.aspb. IE 5.x Internet Client SDK -

http://msdn.microsoft.com/downloads/tools/authenticode/authcode.aspTo sign with a SPC file, the required options are -spc and -v if your private key is in aPVK file. For the next step, you’ll need to have these two files handy.

3. SIGNCODE.EXE should be used from a Command Prompt window. Here is an example ofhow to call SIGNCODE.EXE to sign a component cabinet using SPC and PVK files:

C:\INETSDK\bin\> signcode -spc CredentialsForDCTM.spc -v PrivateKeyForDCTM.pvk -n "Documentum Docbase Object-based Properties Component Cabinet" -i http://www.documentum.com -t http://timestamp.verisign.com/scripts/timstamp.dll DcPropertiesComp.cab

a. "Documentum Docbase Object-based Properties Component Cabinet" is thedescription of the file that will show up in the certificate.

b. http://www.documentum.com is a URL where the user can find more informationabout the file being downloaded (or least more information about Documentum ingeneral).

c. CredentialsForDCTM.spc is the Digital ID file (i.e. Software Publisher Certificate) thatwas obtained previously from VeriSign.

d. PrivateKeyForDCTM.pvk is our private key generated previously by VeriSign.e. http://timestamp.verisign.com/scripts/timstamp.dll is the URL for VeriSign's

timestamping service. Please note that "timstamp.dll" does not contain the letter "e."• Here’s an important comment on timestamps from VeriSign: “Because key pairs

are based on mathematical relationships which can theoretically be ‘cracked’ witha great deal of time and effort, it is a well-established security principle thatdigital certificates should expire. Your VeriSign Digital ID will expire one yearafter it is issued. However, most software is intended to have a lifetime of longerthan one year. To avoid having to resign software every time your certificateexpires, VeriSign and Microsoft introduced a timestamping service. Now, whenyou sign code, a hash of your code will be sent to VeriSign to be timestamped. Asa result, when your code is downloaded, clients will be able to distinguishbetween: (1) Code signed with an expired certificate, which should NOT betrusted, and (2) Code signed with a certificate which was valid at the time thecode was signed, but which has subsequently expired. This code SHOULD betrusted. This means that you will not need to worry about resigning code whenyour Digital ID expires. VeriSign is the only certification authority offering thetime stamping service. This service is free to all VeriSign Commercial andIndividual Software Publisher ID customers.”

• Documentum software that is provided in the form of a signed cabinet file istimestamped. This allows our customers to trust such software indefinitely in theunlikely event that Documentum allows its Software Publisher Certificate withVeriSign to expire.

f. DcPropertiesComp.cab is the name of the file that needs to be signed--in this case adesktop client component cabinet.

Page 67: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 67 of 78

4. Test your signature using another tool from the Microsoft Internet Client SDK,CHKTRUST.EXE. Run this utility to check your signature before distributing your file (i.e.placing your component cabinet into a DocApp in a Docbase.

C:\INETSDK\bin\> chktrust DcPropertiesComp.cab

If your signing process was successful, CHKTRUST.EXE will bring up the certificate youspecified with SIGNCODE.EXE. Congratulations, you have just digitally signed your file.When this file is installed from a Docbase via DCD (or downloaded from a Web site byInternet Explorer), it will display the same certificate to the user. If the file is tamperedwith in any way after it has been signed, the user will be notified and given the option ofrefusing installation.

The focus of this document is not on the subject of code signing. Microsoft has published anumber of useful articles in their MSDN Library that should be consulted if more details aredesired. VeriSign also has more information about code signing online at their Web site.

9.3 Storing Component Content in a Docbase

When you define a component in a DocApp without content, you’re essentially telling thesystem that your component has already been installed and registered locally, and is notexpected to be updated from the current version. That is, if a desktop client component isspecified in a Docbase in terms of attributes only and no content, it must already beinstalled locally on an end user’s machine for that user to access its functionality. Anexample of this kind of component is a standard Documentum desktop client component.

DCD uses several attributes on a dm_component object to determine installation status.Figure 40 shows a selection of these attributes along with their meaning.

Attribute Meaningbuild_technology Type of language and/or the mechanism for building the componentcom_class_id COM CLSID of the componentcomponent_version FileVersion of the componenti_contents_id If set, ID to file system content (i.e. a component cabinet file)uniq_cont_ticket CRC-32 value of content, if content (i.e. a component cabinet file) is

specified

Figure 40. Selected dm_component Attributes of Interest to Dynamic ComponentDelivery

In light of Figure 40, a proper component-without-content definition is shown in Figure 41.

Attribute Valuebuild_technology 1 (i.e. TECH_ACX – ActiveX (COM) technology has been used to build

the component)com_class_id A valid 38-character hexadecimal string, including dashes and

enclosing braces (e.g. “{57DA2981-9A05-11D2-80FA-00105A1F0288}” for DCPROPERTIESLib.DcPropertiesComp withinDcPropertiesComp.dll)

component_version nullstring – since there is no content in the Docbase, the locallyinstalled version of the component is considered to always be current

Page 68: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 68 of 78

i_contents_id nullid (i.e. “0000000000000000” for no content)uniq_cont_ticket nullstring – there is no content (i.e. cabinet file) from which to

calculate a checksum

Figure 41. dm_component Attribute Values for Components without Content

Out of the box, the default DocApp specifies that none of its dm_component objects hascontent.

When you define a component in a DocApp with content, you’re telling the ComponentDispatcher that it should be using this version of the component unless a newer, backwardcompatible version of the same component is identified by DART (i.e. already installedlocally). Such components do not have to be pre-installed by an installer (e.g. DocumentumDesktop Client installer). Instead they are installed dynamically by the Cabinet Installer thefirst time an end user requests functionality implemented by the component. Examples ofthis kind of component are customizations of standard desktop client components or entirelynew components used by a custom DocApp.

In light of Figure 40, a proper component-with-content definition is shown in Figure 42.

Attribute Valuebuild_technology 1 (i.e. TECH_ACX – ActiveX (COM) technology has been used to build

the component)com_class_id A valid 38-character hexadecimal string, including dashes and

enclosing braces (e.g. “{57DA2981-9A05-11D2-80FA-00105A1F0288}” for DCPROPERTIESLib.DcPropertiesComp withinDcPropertiesComp.dll)

component_version A valid FileVersion string taking the form “#,#,#,#” (e.g.“4,0,0,500”)

i_contents_id A valid object ID for content object on the server file system (i.e. anon-nullid pointing to the r_object_id attribute of a dmr_contentobject)

uniq_cont_ticket A valid 8-character hexadecimal string representing the CRC-32 valuecalculated for the associated content

Figure 42. dm_component Attribute Values for Components with Content

Use DDS’s Component Editor to associate a component with a cabinet file. First load theDocApp of interest within DDS. Double-click on the “red cube” icon of the component forwhich you will specify a cabinet, and choose Edit. If necessary, remove any previouslyspecified content then choose Add. Browse to where the cabinet exists, select it andestablish a link to it. Doing so will automatically update the Class ID field on the ComponentEditor dialog. DDS actually uses the a Cabinet Resolver object to automatically define thefollowing dm_component attributes so that you don’t have to manually: com_class_id,component_version and uniq_cont_ticket. Exit the Component Editor dialog and check-inyour component changes.

Please be aware of the current relationship between a dm_qual_comp and a dm_componentin a DocApp, which is as follows:

Page 69: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 69 of 78

• In the current release of DDS, references between dm_qual_comp and dm_componentobjects are version specific (i.e. early bound). That is, whenever you check-in adm_component modification, the references between the modified dm_component andits associated dm_qual_comp objects are left unchanged.

• Checking in your update as Same Version allows all existing references between themodified dm_component and its associated dm_qual_comp objects to remain effective.This is the recommended check-in action.

• Checking in your update as a new version, leaves you with a choice. You can either usethe DDS Type Editor to detach the current component and attach the newer (modified)component, establish a new dm_qual_comp-to-dm_component reference, or you can donothing more. Doing nothing, however, won’t allow your new component to berecognized by DART. DART will continue to identify the older version of the componentas referenced by a dm_qual_comp object.

This relationship applies regardless of whether or not a dm_component object hasassociated content.

9.4 Deploying Component Content

Now that you have created a cabinet, signed it and stored it in a Docbase, let’s talk moreabout what happens on the end user’s machine the next time a functional request is madethat is satisfied by the component inside your cabinet. Previously, section 9 and Figure 37revealed the key players on an end user’s machine in terms of dynamically deployedcomponent content. Let’s assume that DART determines that your updated component hasyet to be installed on the end user’s machine. The Component Dispatcher will then ask theCabinet Installer to first download the specified dm_component object content from aDocbase and then to install that content using Microsoft’s COM-based technology known asInternet Component Download (ICD).

Windows Explorer provides a nice user interface for all content installed via ICD, a featurethat Microsoft introduced with Internet Explorer 3.0. Since DCD leverages ICD to installcomponent cabinets on your PC, DCD-derived content appears in the same UI, shown laterin Figure 44 of section 9.5.

If you select DCD content within the Downloaded Program Files folder and right-click, you'llreceive a context menu with three options: Update, Remove and Properties. ChoosingProperties will present you with a tabbed dialog containing general, dependency and versioninformation about your selection. Right-clicking on the selection in Figure 44 and choosingProperties, would present you with the dialog shown in Figure 43.

Page 70: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 70 of 78

Figure 43. ICD Properties Dialog from Windows Explorer

The CodeBase value on the General tab reveals that DCD uses the Downloads folder underthe folder specified by the registry value HKEY_LOCAL_MACHINE\SOFTWARE\Documentum\Common – "UserDirectory"= "C:\Documentum" to download componentcabinet files from a Docbase. The ID value is the component’s COM CLSID. The Dependencytab lists the dependencies contained in the specified cabinet file and the Version tab displayscommon version information about the primary content of the specified cabinet (i.e. itsdesktop client component).

Choosing Update is essentially a no-op, since you already have the latest content on yourPC. The Remove option will be covered later in section 9.5.

Although the Documentum Desktop Client should automatically cleanup all intermediate filecontent generated by DCD and ICD, you should be aware of exactly what gets installedwhere on your system when leveraging DCD. Here is a list of what is affected on your filesystem. This list assumes that you develop on a Windows NT 4.0 machine and haveinstalled the Documentum Desktop Client using default folders.

1. C:\Program Files\Documentum\shared\Component.dll• Wherever a COM server (e.g. desktop client component executable) is currently

registered is where ICD will place the new version of the COM server (i.e. overwritethe file). If, for example, you customize the properties component and use DCD toinstall your customization, ICD will find DcPropertiesComp.dll to be registered in theshared folder under the location specified by the registry valueHKEY_LOCAL_MACHINE\SOFTWARE\Documentum\Common – "ProgramDirectory"="C:\Program Files\Documentum. " (ICD is able to locate the server using thecomponent CLSID stored in the com_class_id attribute of your component cabinet’sdm_component object.)

Page 71: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 71 of 78

• Once complete, DCD leaves this file intact until a newer version of the samecomponent is identified by the system or you choose to uninstall DCD-based content(see section 9.5).

2. C:\Documentum\Downloads\<object ID>\<object ID>.cab• “Object ID” is the sixteen character hexadecimal string value of the dm_component

object’s r_object_id attribute.• DCD should cleanup all intermediate content produced under the Downloads folder.

Consequently, CodeBase values for DCD content, like that shown previously in Figure43, are historical in nature (i.e. they shouldn’t point to real content).

3. C:\TEMP\ICD#.tmp\Component.inf and C:\TEMP\ICD#.tmp\Component.dll• “#” starts at one and increments by one for each ICD-based download.• DCD should cleanup all ICD-related intermediate content produced under the folder

specified by your TEMP environment variable.4. C:\WINNT\Downloaded Program Files\Component.inf

• This is the physical file from which Windows Explorer is able to generate its logicalview shown in Figure 44.

• Once complete, DCD leaves this file intact until a newer version of the samecomponent is identified by the system or you choose to uninstall DCD-based content(see section 9.5).

5. C:\WINNT\java\com\documentum\components\dart\datafile• This is the physical file that DART uses to persist a table of component installations.

The focus of this document is not on the low-level details of ICD. Microsoft has published anumber of useful articles in their MSDN Library that should be consulted if more details aredesired.

9.5 Testing Dynamic Component Delivery

Desktop client component deployment can be broken down into discrete test activities. Bybreaking down your testing, it will become clearer what exactly is involved in a successfuldeployment, and it will be more likely that your testing will be successful in a shorteramount of time. For example, test the delivery of your component from a Docbase as a finalstep. First, test the dispatching of your component.

To adequately test the dispatching of your component, you’ll need access to a Docbase andtwo machines—one that you use for development and one that is representative of an enduser environment. It’s important that you test on two machines so that you can identify allof your component’s system dependencies. That is, your component may run just fine inyour development environment but fail to run on an end user’s machine unless you deliverthe necessary dependencies to your end user along with your component. Once passed, thistest will verify that future problems with registration and instantiation are truly deliveryproblems.

The following high-level process is recommended to test dispatching a new or customizedcomponent:

1. Using DDS, specify your component in a DocApp without specifying any content (i.e.attributes only). You’ll have to manually specify your component’s CLSID in the DDSComponent Editor while in Edit mode. A safe way to do this without a lot of typing is touse OLEView. Drill down on your component by looking for its ProgID in the tree pane onthe lefthand side (e.g. DCPROPERTIESLib.DcPropertiesComp). Select your componentProgID, right-click and choose “Copy CLSID to Clipboard.” Paste this value into the Class

Page 72: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 72 of 78

ID field of the DDS Component Editor. Check-in your changes, carefully consideringdetails provided toward the end of section 9.3 regarding the current relationshipbetween dm_qual_comp and dm_component objects.

2. Use the Documentum Desktop Client or your own test application to dispatch your newcomponent. Be sure to specify the correct application name if it is not the defaultDocApp (i.e. “DcDesktopClient”). If the dispatch is successful proceed with the nextstep.

3. Copy your component executable and register it on your second machine that representsa typical end user environment (e.g. one with only the Documentum Desktop Clientinstalled but no version of DDS). If your component fails to register, you may be missingone or more of its system dependencies. Maintain a list of all such dependencies—you’llneed this list to complete an upcoming deployment step. Once your component issuccessfully registered, use the Documentum Desktop Client or your own testapplication to dispatch your new component on your “end user” machine. If yourcomponent isn’t dispatched, chances are that there are more dependencies missing.Resolve all dependencies to the point where end user dispatching occurs successfullybefore proceeding further.

You’ve now successfully tested instantiation of your component and are ready to test itsdelivery from a Docbase to an end user machine. You may also have a list of dependenciesthat must be resident on an end user machine in order for your component to functionproperly (e.g. one of the Documentum ActiveX controls).

The following high-level process is recommended to test delivering a new or customizedcomponent. It assumes that one machine is used for development and a different machineis used for testing.

1. Build and register your component, making sure to increment its version if it implementsthe same CLSID as another component already in the system.

2. Create a cabinet file containing your component, as described in section 9.1. Any cabinetthat follows ICD guidelines published by Microsoft is acceptable; however, you’llprobably want to use cabinets created with either the Documentum Cabinet Manager(i.e. DcCabCreator object) or the Package & Deployment Wizard provided with VB.

3. (Optional but recommended) Sign your cabinet file, as described in section 9.2.4. Using DDS, store your cabinet file in a DocApp. Check-in your changes, carefully

considering details provided toward the end of section 9.3 regarding the currentrelationship between dm_qual_comp and dm_component objects.

5. Verify that the Documentum Desktop Client is installed on the target (end user)machine, and DCD is enabled (i.e. the following registry key exists:HKEY_LOCAL_MACHINE\SOFTWARE\Documentum\Dynamic Component Delivery).

6. (Optional) Create a temporary copy of the component housing that you anticipate will bereplaced by DCD. For example, create an “original” folder under your Documentum“shared” folder and copy the component binary (executable) there. Creating this copywill allow you to retry your delivery test fairly quickly.

7. Use the desktop client to request a function that your component implements.8. Verify that the component you built in step #1 is the component that is dispatched. You

can do this via Windows Explorer and its ICD user interface, as described in section 9.4.9. If you need to repeat steps 7 and 8, and performed step #6, please do the following.

a. Execute steps 1 through 5 in section 9.6.b. Restore the original component executable by copying it back to where it was

originally in step #6, here.c. Repeat steps 7 and 8, here.

Page 73: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 73 of 78

9.6 Uninstalling Dynamic Component Content

By default, the Documentum Desktop Client installer (SETUP.EXE) will enable DCD. If duringits lifetime on a desktop machine the Documentum Desktop Client dynamically updates oneor more of its components via DCD, extra steps must be taken manually when it comes timeto uninstall the Documentum Desktop Client. These important steps are included in therelease notes for the Documentum Desktop Client and are repeated here for easierreference.

1. Before going to Add/Remove Programs in the Control Panel, launch Windows Explorerand go to C:\WINNT\Downloaded Program Files on NT4 or C:\Windows\DownloadedProgram Files on Win9x. These are the nominal, default paths for Internet ComponentDownload (ICD) content. DCD uses ICD to perform its task. The actual ICD content pathis determined by the following value in the Windows registry: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\ActiveX Cache -"0"="C:\\WINNT\\Downloaded Program Files." Figure 44 shows an example of thespecial user interface provided by Windows Explorer for ICD.

Figure 44. User Interface in Windows Explorer for Internet Component Download

2. For each Documentum or custom desktop client component listed in the WindowsExplorer content pane, select it, right click and choose Remove. Please note that if youhaven't requested any DCD-provided functionality, there will be nothing to do in thisstep. Standard Documentum desktop client components as well as any customcomponents written in VB will display their COM ProgID in the Program File column.

3. Figure 45 provides a list of ProgIDs (with associated CLSIDs) for standard Documentumdesktop client components whose source code is provided through DocumentumDeveloper Studio (DDS). It is recommended that your enterprise also publish a list of itsdesktop client component ProgIDs (or other identification if a language other than VB isused) so that uninstalling DCD-derived content becomes completely deterministic ratherthan a guessing game.

ProgID CLSIDDCCANCELCHKOUTLib.DcCancelCheckoutComp {3F1C2727-9A04-11D2-80FA-00105A1F0288}DCCHANGEPASSWRDLib.DcChangePasswrdComp {213D48F1-BEFD-11D3-BC63-0050DA681DC7}

Page 74: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 74 of 78

ProgID CLSIDDCCHECKINLib.DcCheckInComp {C0B5BE13-9A04-11D2-80FA-00105A1F0288}DCCHECKOUTLib.DcCheckOutComp {17F5A1BE-9A05-11D2-80FA-00105A1F0288}DCCOPYLINKMOVELIB.DcCopyLinkMoveComp {4011F503-C946-11D3-A4E7-000064657374}DCDOCLIFECYCLELib.DcDocLifecycleComp {3DBDF18C-9D00-11D2-A943-00105A27AE7A}DCIMPORTLIB.DcImportComp {7C716F4A-B74C-11D3-ADCB-00104B59949F}DCNEWLib.DcNewObjectComp {F507C1B2-4439-4B9B-A9A5-373C6CA50AA3}DcOpenDocumentLib.DcOpenDocumentComp {09FB3844-C1FB-11D3-8898-006008AC2DE7}DCPASTEASRENDLib.DcPasteAsRendComp {C8AD996E-C884-11D3-BC6E-0050DA681DC7}DCPDFRENDITIONLib.DcPdfRenditionComp {BEB9E838-C2DE-11D3-BC69-0050DA681DC7}DCPROPERTIESLib.DcPropertiesComp {57DA2981-9A05-11D2-80FA-00105A1F0288}DCSENDMAILLib.DcSendMailComp {0D66A089-E88B-11D3-BCA9-0050DA681DC7}DCVDMPUBLISHLib.DcVDMPublishComp {3E984F87-E939-11D2-BD04-00104B2B9960}DCWFAVAILLib.DcWFAvailabilityComp {68B3AD0D-28E8-11D3-81CF-00105A1F0288}DCWFOPENLib.DcWFOpenComp {126719DD-28EA-11D3-BEAD-00105A24DE5C}DCWFROUTEREDITLib.DcWFRouterEditorComp {F9E86695-28ED-11D3-BEAD-00105A24DE5C}DCWFROUTERMGRLib.DcWFRouterMgrComp {8DD71AB8-28F1-11D3-BEAD-00105A24DE5C}DCWFSENDTODISTLib.DcWFSendToDistComp {403E81D4-28F4-11D3-BEAD-00105A24DE5C}DCWFSTARTLib.DcWFStartComp {56B3F3E6-14C1-11D4-B5C7-00105A9F55BD}DCWFSTATUSLib.DcWFStatusComp {B8239E1A-28F6-11D3-81CF-00105A1F0288}DCWFTASKMGRLib.DcWFTaskMgrComp {56A5A372-28EB-11D3-81CF-00105A1F0288}Documentum.DcCheckInAsNewDocumentComp {5A1D0B78-2324-11D2-9217-00104B59949F}Documentum.DcNewFromDocbaseTemplateComp {5A1D0B76-2324-11D2-9217-00104B59949F}Documentum.DcOpenFromDocbaseComp {5A1D0B72-2324-11D2-9217-00104B59949F}

Figure 45. Identifying Standard Documentum Desktop Client Components WhoseSource Code Is Provided via DDS

4. Verify that your Downloads folder (e.g. C:\Documentum\Downloads) is empty. Deleteany files and folder that you may find there. The actual Downloads folder path isdetermined by the following value in the Windows registry: HKEY_LOCAL_MACHINE\SOFTWARE\Documentum\Common - "UserDirectory"="C:\Documentum\Downloads."

5. Delete the file: C:\WINNT\java\com\documentum\components\dart\datafile on NT4 orC:\Windows\java\com\documentum\components\dart\datafile on Win9x.

6. After all content delivered through DCD has been removed in steps 2-4 here, go toAdd/Remove Programs in the Control Panel and remove “Documentum Desktop Client4.0.” If you have no other applications that require DFC, you should then remove“Documentum DFC Runtime Environment 4.0.” Once you're through with the ControlPanel, reboot your machine.

9.7 Configuring Dynamic Component Delivery

The Documentum Desktop Client installer will automatically enable DCD. In the unlikelyevent that you will need to disable DCD, you can do so be removing the following key fromyour registry: HKEY_LOCAL_MACHINE\SOFTWARE\Documentum\Dynamic ComponentDelivery. The next time the Component Dispatcher is asked to dispatch a component inorder to satisfy a functional request, it will try to do so using only components that arealready locally installed. If no local component can satisfy the request, the request will fail.

Page 75: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 75 of 78

To restore DCD to its default active state, add back the HKEY_LOCAL_MACHINE\SOFTWARE\Documentum\Dynamic Component Delivery key to your registry. Subsequentfunctional requests will be satisfied by the very latest component implementing the requestfunction, whether that component already exists locally or needs to be installed from aDocbase.

Since ICD is an asynchronous process by nature while component dispatching is not, anevent is used to synchronize the ICD portion of DCD. So that the Component Dispatcher isnot held captive by an errant ICD invocation, a timeout value is established whileComponent Dispatcher waits for the “ICD complete” event to be signaled. This timeoutvalue is 15 seconds by default. You can customize this value by adding the following valueto your registry: HKEY_LOCAL_MACHINE\ SOFTWARE\Documentum\Dynamic ComponentDelivery - "ICD Timeout." This value is specified in milliseconds; so 15 seconds should beentered as 15000 or 0x00003a98. In general, the default value should suffice.

Appendix A: Example VB Coding Standard

The following coding standard for VB-based development of desktop client components isintended to be moderate and useful. It will be followed for all Documentum standarddesktop client components. Of course, custom components are free to use whatever codestandard may already be in place. This appendix is intended to be a guide for customdevelopment rather than an edict. It also sheds some light onto the rationale behind thelook and feel of VB source code provided by Documentum.

This particular coding standard is based on the writing of Deborah Kurata and DanAppleman. Please refer to the bibliography for details about their published works in thisarea.

A.1 Commenting

Provide an outline of your code and describe your code’s purpose in comments. Yourcomments should answer “what?” and “why?” but not “how?” The code itself provides theplay-by-play. Comment for clarity’s sake.

Each source module you create should have a standard preamble (comment header) at aminimum. To give you an idea of the kinds of information that is appropriate to include in acomment header, here’s an example of the standard Documentum comment header. Notethat Visual SourceSafe keywords are employed so that if Visual SourceSafe is used as asource code control repository, data entry is minimized.

'********************************************************************************************' (c) Copyright Documentum, Inc. 1999. All rights reserved.' May not be used without prior written agreement signed by a Documentum corporate officer.'' $Workfile: DcKitchenSinkComp.cls $' $Revision: 1 $' $Author: Craig Randall $' $Date: 6/21/99 $'' This class is . . .'' $Log: $'' 990621 CR Creation date.'' $NoKeywords: $'********************************************************************************************

Page 76: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 76 of 78

Additional comments to summarize routines, to explain the subtleties inside complexroutines, or to qualify variables are welcome. Clear variable names should eliminate theneed for variable comments.

A.2 Basic Code Structure

Make your code more readable and robust by applying some basic code structureguidelines:• Try indenting for easier reading. I recommend Auto Indent with Tab Width set to 4 (i.e.

Tools | Options… | Editor tab).• Declare your variables at the top. VB does allow you to declare variables wherever you

want. However, it’s easier to find the declarations if they are all in the same location. It’salso easier to determine variable reuse through such placement.

• Each routine should have one and only one purpose.• Keep routines small (e.g. the amount of code you can view on a screen at one time).• There should be one exit point from a routine. The one big exception to this rule is for

error handling. If you use the On Error GoTo type of error trap, you will need an Exitstatement immediately before the error handler so that the error handler is not executedwhen no error occurs. You can also enforce a single exit point through the use of anadditional label and GoTo statement, like many of the code samples in this documentspecify.

• Only assign the value of a function at the end of the function.• Order your routines consistently (e.g. alphabetically, by type, etc.).

A.3 Variable Scope

Scope is the word used to describe the extent to which a variable can be seen in anapplication. If a variable is accessible to all routines in a project, it has global or publicscope. If it can only be accessed by routines in a module, it has module-level scope. If onlythe routine can access the variable, it has local scope. It is best to use the smallest scopepossible for a variable.

A.4 Syntax Standards

Make your code more consistent by applying the following syntax standards:• Use a “.” operator between a form and a control.• Use constants instead of hard-wired numbers (e.g. DC_COMP_SUCCESS versus 0).• If you have a Case statement, include a Case Else (same as default: in a C++ switch

statement).• Use the continuation character (“_”) for long lines to ensure that most lines will fit in the

code window and a printed page.• Don’t put multiple statements on one line.• Explicitly state the data types of variables as well as the return data types for all

Function and Property Get procedures.• Don’t put multiple declarations on one line.• Explicitly state the scope (i.e. Public, Friend, Private).• Always use “&” when concatenating strings and “+” when working with numerical

values.The next guideline is partially related to syntax.

Page 77: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 77 of 78

• Don’t stop your application. When you use the End statement or Run | End (menucommand or toolbar equivalent), you are telling VB to immediately stop all codeexecution. VB will proceed to clean up memory wherever it can. However, it will notexecute any of your termination code. Not only does this prevent you from testing yourtermination code, you may find that some objects were not cleaned up properly. Forexample: if you have used API functions to create system objects, Visual Basic andWindows may not be able to clean up those objects when your application ends. VisualBasic definitely will not close or free those objects before you try to run the applicationagain. Thus, you may find that a file you opened the first time fails on the next attemptdue to a permission error or lock condition. As a general rule, within the VBenvironment, always close the application using the System menu on the main form orother exit mechanism that you've programmed into the application (e.g. unloading themain form from an exit command button).

A.5 Error Handling

A.5.1 Checking for the Error

There is no global error trapping (exception handling) built into VB, so each procedureshould trap its own errors. There are several ways to trap errors:• On Error Goto <label> (to cause execution to jump to the specified exception handler)• On Error Resume Next (to cause execution to return to the next line after the line

generating the exception)• Perform validation (when an error won’t raise an exception)• Assert <expression> (to stop execution during debugging only if a specific condition is

false)

If a procedure generates a trappable error (exception) and has no error trap (exceptionhandler), the error will automatically be raised to the procedure that called it. If thatprocedure has no error trap, the error will again be raised to the next level. If no error trapis found at the top level, the application will generate an untrapped error and terminate.

As a safeguard against customer components which may lack adequate exception handling,Component Dispatcher handles all exceptions not handled by components themselves.

A.5.2 Handling the Error

There are several possibilities for handling an error:• Fix it and try again (use the Resume statement within your exception handler)• Deal with it and continue (use the Resume Next statement within your exception

handler)• Raise an error (so that some other code with the potential to handle the exception can

act on it)• Set an error value and exit

In any of these cases, it may be useful to log the errors to an error log. This lets you trackthe exact specifications of the error without relying on a user to write down the informationin a message box. VB provides an easy way to log information to the Windows NT event logor to a log file.

Page 78: Developing Client Components

Developing Documentum Desktop Client Components 8/9/99

Documentum, Inc. Page 78 of 78

A.6 Naming Conventions

This is a standards subject that doesn’t lack for opinions. Please consult the MSDN Librarydated 10/98 or later for more details on common naming conventions for Visual Basic code.Documentum-provided desktop client components basically follow the guidelines put forthby Microsoft.

Appendix B: Selected Bibliography

1. “Essential COM for VB Programmers”, A White Paper by Ted Pattison, August 1997(http://www.sublimnl.com/EssentialCom.htm)

2. Developing COM/ActiveX Components with Visual Basic 6: A Guide to the Perplexed,Appleman, SAMS, 1999, ISBN 1-56276-576-0

3. Doing Objects in Visual Basic 6.0, Kurata, SAMS, 1999, ISBN 1-56276-577-94. Visual Basic Books Online and MSDN Library5. “Investigating Multilanguage Debugging and the New IDEs of Visual Studio 97”,

Microsoft Systems Journal, Schmidt, 5/97