Anda di halaman 1dari 78

Developing Documentum Desktop Client Components

9/3/2000

Developing Documentum Desktop Client Components


Design Specification Document Version 1.16 September 3, 2000 Craig Randall

Documentum, Inc.

Page 1 of 78

Developing Documentum Desktop Client Components

8/9/99

Table of Contents
Table of Contents...................................................................................... 2 Table of Figures......................................................................................... 3 1 Background & Overview ........................................................................... 4 2 Acronyms .............................................................................................. 5 3 Development Platform Configuration .......................................................... 6 4 Interface Definition Language A Powerful Learning Tool .............................. 8 5 Desktop Client Component Infrastructure.................................................... 8 5.1 Login Management ...........................................................................10 5.2 Event Dispatching.............................................................................13 5.3 Data Packaging ................................................................................20 5.4 Component Dispatching .....................................................................23 5.5 Report Management..........................................................................26 5.6 Icon Management .............................................................................27 5.7 Visual Basic Component Development Assistant .....................................28 5.8 Other Component Services.................................................................29 6 Creating a New Desktop Client Component.................................................30 6.1 Start with a Skeleton (Component Project Configuration).........................32 6.2 Add Muscles (Infrastructure Connections) .............................................34 6.2.1 Implementing IDcComponent ..........................................................34 6.2.2 Connecting to a Docbase.................................................................37 6.2.3 Functional Targets & Contexts Working with Items & Item Containers ..39 6.2.4 Dispatching Another Component.......................................................42 6.2.5 Participating with Events.................................................................43 6.3 Add Skin (User Interface) ..................................................................53 6.4 Add Brains (Business Logic) ...............................................................54 6.5 Modal Versus Modeless Components (Lifetime Management)....................54 6.6 Setting Proper Version Compatibility....................................................55 7 Debugging Desktop Client Components .....................................................56 7.1 Testing Design-Time Behavior with Visual Basic .....................................56 7.2 Testing Run-Time Behavior with Visual C++ ..........................................58 8 Modifying an Existing Desktop Client Component .........................................61 9 Deploying Desktop Client Components.......................................................61 9.1 Creating Component Content..............................................................63 9.2 Signing Component Content ...............................................................65 9.3 Storing Component Content in a Docbase .............................................67 9.4 Deploying Component Content............................................................69 9.5 Testing Dynamic Component Delivery ..................................................71 9.6 Uninstalling Dynamic Component Content .............................................73 9.7 Configuring Dynamic Component Delivery .............................................74 Appendix A: Example VB Coding Standard .......................................................75 Appendix B: Selected Bibliography .................................................................78

Documentum, Inc.

Page 2 of 78

Developing Documentum Desktop Client Components

8/9/99

Table of Figures
Figure 1. Frequently Used Acronyms.......................................................................... 6 Figure 2. Required Software..................................................................................... 7 Figure 3. Project References Dialog in Visual Basic ....................................................... 9 Figure 4. Mapping Docoumentum Desktop Client Type Library References to Their Implementations ............................................................................................ 10 Figure 5. Looking at IDcLoginManager::Connect in VBs Object Browser ........................ 11 Figure 6. Looking at IDcLoginManager::Connect in OLEViews ITypeLib Viewer................ 11 Figure 7. Event Dispatching Interaction Diagram ........................................................ 14 Figure 8. Structure of a Notifier Event Code .............................................................. 14 Figure 9. Looking at the Standard Implementation IDcEventDispatch::SendEvent in VBs Object Browser .............................................................................................. 16 Figure 10. Notifier Registry Entries.......................................................................... 17 Figure 11. Unanswered (False) Broadcast Query Event Dispatch Flow............................ 18 Figure 12. Answered (True) Broadcast Query Event Dispatch Flow................................ 18 Figure 13. Approved State Transition Broadcast Event Dispatch Flow ............................ 19 Figure 14. Vetoed (Implicit or Explicit) State Transition Broadcast Event Dispatch Flow .... 19 Figure 15. Looking at IDcObjectItem::ID in VBs Object Browser .................................. 22 Figure 16. Looking at IDcItems::Item in VBs Object Browser...................................... 22 Figure 17. Looking at Item IID String Constants in VBs Object Browser ........................ 23 Figure 18. Abstraction-based Component CLSID Example (DcProperties) ....................... 24 Figure 19. Global Component CLSID Example (DcFind) ............................................... 25 Figure 20. Looking at IDcComponent::Init in VBs Object Browser ................................ 29 Figure 21. Looking at IDcDataDictionaryUtils::GetAttributeDisplayName in VBs Object Browser ........................................................................................................ 30 Figure 22. High-level Component Interaction Diagram................................................ 31 Figure 23. VB Sample Code Skeleton Implementation of IDcComponent...................... 35 Figure 24. VB Sample Code Simple Error Handler Using Report Manager ..................... 36 Figure 25. IDcComponent::Init Parameters............................................................... 37 Figure 26. VB Sample Code Using Login Manager & DFC to Access a Docbase .............. 39 Figure 27. VB Sample Code Working with an Item Collection..................................... 40 Figure 28. VB Sample Code Working with an Item Collection..................................... 41 Figure 29. VB Sample Code Working with Item Containers ........................................ 42 Figure 30. VB Sample Code Calling Component Dispatcher ....................................... 43 Figure 31. VB Sample Code Skeleton Implementation of IDcEventSink........................ 45 Figure 32. VB Sample Code Handling Event Data within HandleEvent ......................... 50 Figure 33. Mapping Event Codes to Event Data Interfaces........................................... 51 Figure 34. VB Sample Code Interfacing with the Notifier........................................... 52 Figure 35. VB Sample Code Queuing Events........................................................... 52 Figure 36. Sample Component Resource Source/Definition (.RC) File ............................ 54 Figure 37. Physical Desktop Client Component Flow Diagram....................................... 62 Figure 38. Looking at the Cabinet Manager Type Library in VBs Object Browser.............. 63 Figure 39. VB Sample Code Using the Documentum Cabinet Manager to Create Component Content ........................................................................................ 64 Figure 40. Selected dm_component Attributes of Interest to Dynamic Component Delivery 67 Figure 41. dm_component Attribute Values for Components without Content ................. 68 Figure 42. dm_component Attribute Values for Components with Content ...................... 68 Figure 43. ICD Properties Dialog from Windows Explorer............................................. 70 Figure 44. User Interface in Windows Explorer for Internet Component Download ........... 73 Figure 45. Identifying Standard Documentum Desktop Client Components Whose Source Code Is Provided via DDS................................................................................. 74

Documentum, Inc.

Page 3 of 78

Developing Documentum Desktop Client Components

8/9/99

1 Background & Overview


Before reading further it may be useful to first understand a little of the history behind this document and the development environment which it describes. Originally this document was written as a cookbook to kick-start the development of desktop client components using Visual Basic at Documentum. A desktop client component in Documentum 4i exists to satisfy a particular functional request (e.g. Give me the properties of the selected document, Check-in the selected workflow, etc.). The requested function may be applied to a single object (i.e. item or functional target) or to a group of objects. Most desktop client components present some kind of user interface to enable human interaction. Access to desktop client component functionality is based on several factors including but not limited to the object type of the selected functional target (item) and the current users permissions. For example, you get one component for an operation on a dm_document but another components for the same operation on your subtype of dm_document. (Or you may get nothing in both cases if you lack the necessary permissions on the selected functional target (item).) Visual Basic was chosen as the language of choice for desktop client components for its ease of use and its broad acceptance in MIS development shops. Visual Basic is a higher-level language than say, C++, and can allow developers to focus on their real-world business problems rather than lower-level details like how COM works. Documentum customers expect a high degree of customization in Documentum client software (e.g. tailoring the standard Documentum user interface to their specific requirements). Creating user interfaces with Visual Basic is both straightforward and highly adaptable. So most of standard Documentum 4i Desktop Client components are written in Visual Basic to shorten the time customers need to write custom solutions and to increase the level to which they can customize Documentum standard solutions. I did say most components. There are some cases where the underlying functionality of a component is not expected to change or encapsulates some core Documentum logic that should not be exposed externally. In these cases, Visual Basic is not the right tool for the job. Instead Microsofts Active Template Library (ATL) is employed to produce lightweight, high-performance COM components that expose this core logic to Visual Basic components through standard COM interfaces. This is not meant to imply that Visual Basic components are slow; theyre actually quite efficient. Its been said that C++ (ATLs implementation language) is the assembly language of COM. Lower-level programming is a balance between increased control and increased developer responsibility. By writing core Documentum desktop client logic at a lower level means that you should experience a greater degree of flexibility and performance in the desktop client component development environment. You too are free to leverage this lower-level power in your own components, through ATL for example. However, given that Visual Basic solutions perform nearly as well, I think that youll prefer giving up those last few percentage points of performance in favor of shrinking your custom desktop client development substantially. Visual Basic allows you to focus more on your business problems rather than the infrastructure of your solution.

Documentum, Inc.

Page 4 of 78

Developing Documentum Desktop Client Components

8/9/99

This document was written to provide developers with a formal design specification covering the overall architecture in which desktop client components operate. It documents the standard COM interfaces through which services are provided to desktop client components such as event dispatching and Docbase connectivity, as well as the COM interface which all desktop client components are expected to implement (IDcComponent section 6.2.1). After reading this document you should be able to either customize a standard Documentum 4i 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 the language and the integrated development environment (IDE)). For more information on these technologies, please consult the bibliography at the end of this document. Future revisions to this document may expand on the required knowledge base (e.g. to include ATL).

2 Acronyms
Here is an alphabetized list of the most frequently referenced acronyms, with their expanded meanings. From this point forward, the document gives preference to acronyms to minimize text. Acronym ATL COM Meaning Active Template Library (Microsoft) Component Object Model (Microsoft) a binary specification, a common library and a way of thinking about software development as interacting components where interfaces (contracts) are everything. DocApp Run-Time deliver the right functionality to the right person at the right time. This Documentum system (infrastructure) entity interfaces with the desktop client Component Dispatcher. DART discovers the components that the Component Dispatcher instantiates and manipulates (i.e. calls the components IDcComponent methods). Dynamic component delivery mechanism that allows a developer to store component content in a Docbase that enterprise end users may receive when requested, if the request is qualified by the system Documentum Developer Studio Documentum Foundation Classes Dispatch ID or Procedure ID Dynamic link library Executable process Globally unique identifier (pronounced gwuid like squid) - 128-bit integers that can be reduced to readable, 32-digit hexadecimal numbers. The uuid IDL keyword is equivalent to a GUID. IIDs (interface identifiers), CLSIDs (class identifiers), LIBIDs (type library identifiers) are all specific kinds of GUIDs. Internet Component Download a COM-based technology from Microsoft

DART

DCD

DDS DFC DISPID DLL EXE GUID

ICD

Documentum, Inc.

Page 5 of 78

Developing Documentum Desktop Client Components

8/9/99

IDE IDL

IE MIDL

OS PID

ProgID UI

VB

VC++

Integrated development environment Interface definition language used to unambiguously define COM entities like interfaces, coclasses, and type libraries Internet Explorer (Microsoft) Web browser Microsoft IDL compiler (sounds like middle) run your IDL file through MIDL to produce type libraries and marshaling code Operating system Process identifier - Until the process terminates, the process identifier uniquely identifies the process throughout the system. Note that NT reuses process and thread IDs. Programmatic identifier human readable version of a GUID User interface in the context of the desktop client component focus of this document, this is what an end user sees and manipulates. A similar acronym GUI stands for graphical user interface. Visual Basic (Microsoft) a popular development environment featuring a high-level programming language of the same name. VB4, VB5 and VB6 refer to specific versions of the Visual Basic development environment. VBA stands for Visual Basic for Applications and is a subset of the Visual Basic language. VBScriptone scripting language of manyis a subset of VBA. Visual C++ (Microsoft) Figure 1. Frequently Used Acronyms

3 Development Platform Configuration


Before you can begin creating or modifying desktop client components, you must configure your development platform to support such development. You will need to install at least the required software in the following list: Recommended Version 4.0 with Service Pack 5 or above applied and no Desktop Update (via Internet Explorer 4.x) Release 4.1 or later

Software Windows NT

Documentum Desktop Client

Function (Preferred) Suggested operating system for shell integration debugging; other supported platforms are Windows 9x and NT 4.0 with the Desktop Update, and Windows 2000 (Required) Provides, among other things, the desktop client environment including the standard Documentum desktop client components, various component services, DFC, DART, special purpose widgets, etc.

Documentum, Inc.

Page 6 of 78

Developing Documentum Desktop Client Components

8/9/99

Documentum Developer Studio Visual Basic

Release 4.1 or later

Visual C++

6.0 with Visual Studio 6.0 Service Pack 4 (VS6 SP4) or later applied 6.0 with VS6 SP4 or later applied

(Required) Registers desktop client components with the Documentum system (e.g. DART). (Required) Primary component development and design-time debugging environment (Optional/Preferred) Run-time component debugging environment (e.g. using EXPLORER.EXE Windows Exploreras the parent process rather than VB) (Optional) This is a very useful utility to look at the Windows registry from the perspective of a COM developer (e.g. translate ProgIDs to CLSIDs). Get your copy at http://www.microsoft.com/com/reso urce/oleview.asp. (Optional) This tool provides a more basic interface to the registry than OLEView; however, it is an editor not a viewer.

OLEView.exe

2.10.057 (9/14/98)

Regedit.exe (32-bit version)

Whatever comes standard with the latest version of NT 4.0

Figure 2. Required Software To repeat, this document focuses on the creation of desktop client components through Visual 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 order to set up the tool. Once your (registry-based) set up is complete, you wont be saving this 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, and regular modules. For consistencys sake across component projects, set Tab Width to 4. Set up your editing environment to your preferences (i.e. Editor Format tab). 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. All projects in the environment will be compiled each time they are run, allowing you to deal with all syntax errors before you begin your real test and debug process. While still on the General tab, you may wish to modify Grid Units for tighter alignment control. The default value for both Width and Height is 120 (points). Decreasing Grid Units increases alignment control; however, custom values only apply to that particular development platform. This may create cross-platform alignment issues downstream, so there is a tradeoff involved. I recommend keeping the same value at all times for Width and Height. Exit VB.

4. 5.

6.

7.

Documentum, Inc.

Page 7 of 78

Developing Documentum Desktop Client Components

8/9/99

4 Interface Definition Language A Powerful Learning Tool


As I mentioned earlier in section 2, Interface definition language (IDL) is used to unambiguously define COM entities like interfaces, coclasses, and type libraries. Documentation like this specification always intends to reflect reality. Unfortunately it is prone to error, as much as Id like to not have to say that. On the other hand, it has also been said in software development that the code never lies. Looking at the actual interface definition language (IDL) for a particular COM interface gives you the absolute truth about registered 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 document through 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 type library. You access the viewer by launching OLEView, expanding the Type Libraries folder in its 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: Documentums desktop client environment is based on COM Interfaces are everything to COM (i.e. COM doesnt 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 dont have to actually write any IDL yourselfVB does this all for you. However, when in doubt about any aspect of an interface, consult the original IDL for the definitive answer!

5 Desktop Client Component Infrastructure


As with most kinds of components, Documentum desktop client components dont operate in a vacuum. They require the services of other system components and provide their own set of services to their clients. After youve installed the desktop client on your development machine, you can get a high-level view of the environment by launching VB, opening any kind of project (e.g. Standard EXE) and displaying its Project References dialog (via Project | References) as shown in Figure 3 on the next page.

Documentum, Inc.

Page 8 of 78

Developing Documentum Desktop Client Components

8/9/99

Figure 3. Project References Dialog in Visual Basic As you can see there are a host of services available to desktop client components. These include login management, event dispatching, data packaging and component dispatching. Figure 4 shows a map of each selected reference to its implementation and what that implementation exposes and/or implements in the way of COM objects and COM interfaces. (XXX implies a series of related interfaces.) Reference
"Documentum Cabinet Manager 1.0 Type Library" "Documentum Component Dispatcher 1.0 Type Library" "DcDocumentManager 1.0 Type Library" "Documentum Event Server 1.0 Type Library"

Module
DCCABMGR.DLL

Objects
DcCabCreator DcCabInstaller DcCabResolver DcComponentDispatcher DxDocMgrWrapper DcNotifier

Interfaces
IDcCabCreator IDcCabInstaller IDcCabInstaller2 IDcCabResolver IDcComponentDispatcher IDcComponentDispatcher2 IDxDocMgrWrapper IdcEventDispatch IdcEventDispatch2 IDcEventQueue IDcEventSink IDcXXXEventData IDcFindTarget IDcRunQuery Numerous see DFC documentation for more details IDcIcon IDcItems IDcXXXItem IDcLoginManager _Progress IDcProgressMonitor IDfOperationMonitor

DCCMPDSP.DLL DCDOCUMENTMANAGER.TLB DCEVTSRV.EXE

"Documentum Find Target Server 1.0 Type Library" "Documentum Foundation Classes Type Library (<mm/dd/yy>)" "Documentum Icon Manager 1.0 Type Library" "Documentum Item Server 1.0 Type Library" "Documentum Login Manager 1.0 Type Library" "Documentum Progress Sentinel 1.0 Type Library" "Documentum Progress Monitor Component 1.0

DCFINDTARGET.DLL DFC.TLB DCICONMGR.DLL DCITEMSERVER.DLL DCLOGINMGR.DLL DCPROGRESSSENTINEL.EXE DCPROGRESSMONITOR.DLL

DcFindTargetObj Numerous see DFC documentation for more details DcIcon DcItems DcXXXItem DcLoginManager Progress DcProgressMonitor

Documentum, Inc.

Page 9 of 78

Developing Documentum Desktop Client Components

8/9/99

Reference
Type Library" "Documentum Report Manager 1.0 Type Library" "Documentum Desktop Client Utilities Manager 1.0 Type Library"

Module
DCREPORTS.DLL DCUTILMGR.DLL

Objects
DcReport DcDataDictionaryUtils DcDataProtectionUtils DcDFCSessionUtils DcFmtPrefCache DcODMAIntf DcRegistryKey DcShortcutUtil None

Interfaces
IDcReport IDcDataDictionaryUtils IDcDataDictionaryUtils2 IDcDataProtectionUtils IDcDataProtectionUtils2 IDcDFCSessionUtils IDcFmtPrefCache IDcODMAIntf IDcRegistryKey IDcShortcutUtil Definitions only (i.e. no implementations): IDcComponent IDcEventDispatch IDcEventQueue IDcEventSink IDcXXXEventData IDcItems IDcXXXItem IDcXXXItemContainer IDcReport

"Documentum VB Component Development Assistant 1.0 Type Library"

DCENABLEVB.DLL

Figure 4. Mapping Docoumentum Desktop Client Type Library References to Their Implementations Once you establish a reference to a piece of the desktop client component infrastructure by checking it as in Figure 3, you can quickly get a more detailed look into its services through Visual Basics Object Browser (F2). Subsequent figures will present you will examples of this kind of information. Now that you know about the power of IDL and have a glimpse of the desktop client infrastructure, lets take a closer look at some of the major component services. After this well 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 desktop client system components as well as custom components to connect to a Docbase. It is responsible for interfacing with the singleton (i.e. only one running instance at any point in time 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 home Docbase 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. Instead components should go through the Login Manager. The Login Managers default COM interface is IDcLoginManager, which is implemented by the DcLoginManager COM class. Figure 5 shows the various properties and methods of IDcLoginManager, highlighting the basic connection method, Connect.

Documentum, Inc.

Page 10 of 78

Developing Documentum Desktop Client Components

8/9/99

Figure 5. Looking at IDcLoginManager::Connect in VBs Object Browser Notice in Figure 5 that Connect returns a String. This string is an identifier to a DFC shared session. Shared sessions in DFC require locking and unlocking to ensure thread safety within a particular process. Well 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 to know for a particular desktop client component or interface. Figure 6 shows the result of invoking OLEViews type library viewer on the Login Managers type library. As you can see, theres a significant amount of information that can be gleaned.

Figure 6. Looking at IDcLoginManager::Connect in OLEViews ITypeLib Viewer

Documentum, Inc.

Page 11 of 78

Developing Documentum Desktop Client Components

8/9/99

Login Manager Scenarios The Login Manager is instantiated whenever a process is started (Windows Explorer shell integration is entered, Word or VDM starts up). The Login Manager is an in-process COM server or DLL. When the Login Manager is instantiated, it will start DFC. The Login Manager will allow connections to multiple Docbases by a single user. Whenever the client needs a session, it calls "Connect" optionally passing in the Docbase name, user OS name, password and 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 the requested Docbase/user/domain. If so, the session ID is returned. If not, go to Step #2. Step #2: The Login Manager will compare the user/domain with the user profile from the Authentication Manager. If it matches, Login Manager gets an encrypted password from Authentication Manager, logs in and returns the session ID. If it 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), the user will be told that if they login to this Docbase, they will be disconnected from all Docbases. If they choose to login, then all sessions will be disconnected, a notification will be sent out and then the user will be logged in 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 specified login info differs from the user profile (or the profile is empty). The Login dialog 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 that Docbase (user name doesnt 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 to Step #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 Docbase name Step #1:

Documentum, Inc.

Page 12 of 78

Developing Documentum Desktop Client Components

8/9/99

The Login Manager asks the Authentication Manager for name of the (Home Docbase). 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 Docbase name is empty, Step #2. Step #2: The Login Manager puts up the login dialog asking the user to log into their home Docbase. Scenario #4 - the caller doesn't pass in any info. I think we would only have this scenario when a component (e.g. Find) is invoked, but is not passed a session ID. Step #1: Get the user profile from the Authentication Manager and last logged in Docbase name. Put up the login dialog (pre-filling dialog with user profile info). In all of the scenarios when the Login Manager returns a DFC session ID, it will register the Docbase/user with the Authentication Manager, another piece of desktop client infrastructure with whom components should not directly interface. When the Login Manager logs into the first Docbase, it will try to figure out what is the users home Docbase if it can get the home Docbase name from dm_user. When the Login Manager has a session to the home Docbase, it will register it with the Authentication Manager. If the Login Manager can not get the users home Docbase from dm_user, it will continue trying to get the 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 to such a Docbase, the user will always be prompted with a Connect dialog, regardless of what Docbases have been previously logged into.

5.2 Event Dispatching


The Documentum Event Server (DCEVTSRV.EXE) contains an object commonly referred to as the Notifier, which is responsible for registering and unregistering client interest in events and for dispatching events to specific or registered client event sinks. It acts as a singleton COM 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 as Connection Points.

Documentum, Inc.

Page 13 of 78

Developing Documentum Desktop Client Components

8/9/99

Client

ES

Notifier

ES

Client ES
Figure 7. Event Dispatching Interaction Diagram There are four processes shown in Figure 7; each is bounded by a dotted line. The Notifier box actually represents the DcNotifier COM object as well as all of its worker threads. A Notifier 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 IDcEventSink interface to be called by a Notifier worker thread, or both a caller and an event sink. A single process may contain multiple callers and or multiple event sinks. When developing an object that implements the IDcEventSink interface in C++, it is important to use the free-threaded threading model. Use of other threading models may result in deadlocks in some situations. When using the free-threaded model, you will need to synchronize access to data by multiple thread. Event codes are defined to include their category as shown in Figure 8. This definition provides for 16 event categories, each with up to 2**16 (65536) individual codes. One of the 16 categories is reserved for customer use (i.e. DC_EVT_CATEGORY_USER, where event codes 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 reserved by Documentum.

31

16 15

Category

Code

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 or more event categories. To put it another way, the Notifier dispatches events based solely on their category. Other than extracting category information from a 32-bit value, the Notifier

Documentum, Inc.

Page 14 of 78

Developing Documentum Desktop Client Components

8/9/99

doesnt concern itself with event codes. A sink may be registered and unregistered multiple times. For example, your sink may initially be interested in only a few event categories. Once it receives a particular event code, however, the sinks interest grows and it therefore registers for the additional categories of interest. When an event sink is first registered with the Notifier, RegisterEventSink is called and a pointer to its IDcEventSink implementation is passed. In return, the Notifier returns a cookie. This cookie should be stored as a data member by the client object. It must be passed 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 categories in RegisterEventSink, RegisterInterest and UnregisterInterest calls (e.g. DC_EVT_CATEGORY_OBJECT Or DC_EVT_CATEGORY_SESSION). If you are interested in all Documentum event categories or all categories, no exclusions, you may use the DC_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 be certain that the event sink is completely unregistered. There is no error in unregistering from a category that you are not currently registered. There are four types of Notifier-based events: notifications, cancellations, queries and state transitions. 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 element approves a state transition, it plans for the actual event to occur (e.g. puts itself into a pending state). Any event code that is associated with a state transition event type must also be associated with both notification and cancellation event types. Figures 13 and 14 show the connection among these event types depending on the outcome of an event dispatch involving a state transition.

Documentum, Inc.

Page 15 of 78

Developing Documentum Desktop Client Components

8/9/99

Figure 9. Looking at the Standard Implementation IDcEventDispatch::SendEvent in VBs Object Browser For each call to IDcEventDispatch::SendEvent (Figure 9), the Notifier will create a separate worker thread for each event sink to receive the event. If the destination input parameter is set to DC_EVT_COOKIE_BROADCAST, all event sinks with registered interest in the underlying event category will have their IDcEventSink::HandleEvent methods called (i.e. a broadcast has been specified). If destination is a valid event sink cookie, the associated event sink interface must have previously registered interest in the event category associated 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 freethreaded, which means that they all operate in the single MTA (multi-threaded apartment) of the DCEVTSRV.EXE process. Consequently, response time between caller and Notifier depends 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 this reason, 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 the broadcast 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, event sinks are involved in the dispatch process. In order to better manage such dispatches, the Notifier keeps track of how long it takes each event sink to respond. By default the Notifier will declare timeout on a query event after one second and after 15 seconds on a state

Documentum, Inc.

Page 16 of 78

Developing Documentum Desktop Client Components

8/9/99

transition. These timeout values, expressed in milliseconds, may be overwritten in the registry as shown in Figure 10. By default the QueryTimeout and StateTransitionTimeout values 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 from SendEvent to be as follows: [who] what (e.g. [Microsoft Word97] did not respond in time to the specified event.). The Notifier establishes who from the sink name provided previously in RegisterEventSink. The sinkName parameter should have the same value as the Name property of the event sink being registered. If the response is internal or no sink name has been provided, who will become Documentum Notifier. What is established either through the response string of the driving event sink or by the Notifier itself. For example, if a state transition times out, the Notifier will define what to be did not respond in 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 grammatically correct 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 multiple sinks 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 state transitions. Figures 11 through 14 depict the flow of control among a client, the Notifier and registered event sinks during a broadcast event. There are certainly many more variations than those represented here; however, you should be able to deduce how the flows operate based on these four diagrams. Please note that VBs Boolean data type is the same as the VARIANT_BOOL IDL/C++ data type. True translates to VARIANT_TRUE (0xFFFF); False translates to VARIANT_FALSE (zero).

Documentum, Inc.

Page 17 of 78

Developing Documentum Desktop Client Components

8/9/99

Client
time

Notifier

Sink1

Sink2

Sink3

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 output parameter 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
time

Notifier

Sink1

Sink2

Sink3

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 response output parameter to VARIANT_TRUE (True) before returning from HandleEvent. Sink2 may also optionally define responseMsg for Client to display additional information. 2. Notifier stops its broadcast upon finding an answer of True and immediately returns information defined by Sink2 to Clientresponse (HandleEvent) becomes rv (SendEvent). 3. Client should react based on SendEvent's rv output. Client may optionally use responseMsg. 4. Although not asked, Sink3 may have also answered "True" for this event.

Documentum, Inc.

Page 18 of 78

Developing Documentum Desktop Client Components

8/9/99

Client
time

Notifier

Sink1

Sink2

Sink3

SendEvent - state transition HandleEvent HandleEvent HandleEvent


returns

SendEvent - notification (same event code)


returns

HandleEvent HandleEvent HandleEvent

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 pending mode and set response to VARIANT_TRUE (True) before returning from HandleEvent. 2. Notifier continues its broadcast until someone vetoes the event. Timeouts are treated as implicit vetoes. 3. Based on SendEvent's rv output of VARIANT_TRUE, the client should: a. Perform its operation, then b. 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
time

Notifier

Sink1

Sink2

Sink3

SendEvent

HandleEvent HandleEvent HandleEvent HandleEvent


cancellation dispatch (same SendEvent) state transition dispatch

returns

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

Documentum, Inc.

Page 19 of 78

Developing Documentum Desktop Client Components

8/9/99

Key points from Figure 14: 1. In this example Sink2 vetoes the state transition. It could have done so explicitly by setting response to VARIANT_FALSE (False) before returning from HandleEvent or implicitly by not returning from HandleEvent before the Notifier times out on its HandleEvent 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 but sent 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 perform its 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 or implicitly). As shown previously in Figure 9, SendEvent expects an input Long value for pid. Pid should be set by the caller to its process ID via the Win32 API GetCurrentProcessId. The Notifier passes pid from SendEvent to the HandleEvent method of the appropriate event sink(s). A signaled event sink can then compare the incoming pid with its own process ID to see if the event came from the same process. Such information is useful, for example, if processspecific caching is involved. All event sinks as well as the Notifier expose read-only PID properities that allow other elements of the event dispatching system to learn more about current 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 these five documents) typically creates functional targets and places them into a collection. This collection is then passed to the functional component by way of Component Dispatcher. The functional component extracts each functional target (item) from the collection and acts upon it, eventually returning control back to the collections creator (also known as an item container). Figure 22 in section 6 shows this flow graphically. This section covers the COM interfaces used within the desktop client to support the passing of functional targets (items) from one place to another. There are currently three kinds of interfaces 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),

Documentum, Inc.

Page 20 of 78

Developing Documentum Desktop Client Components

8/9/99

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 from IDcObjectItem) 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. a functional target in a particular functional context). For example, I want to get properties on the selected folder from within Windows Explorer. In this example the selected folder is the functional target, Windows Explorer is the functional context and the properties is the function. The functional context (item container interface) may provide additional information to the component that is common for all items it received through 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 fetching an object. Either the item itself will provide the object ID (i.e. IDcObjectItem) or the item container will provide it to the component. The component can obtain a session ID via the Login Manager through IDcComponent::Init inputs. There is one item collection interface, IDcItems.Here are some specific comments concerning theIDcItems interface: 1. An item collection maintains member type integrity through its Type property and Add method. Currently there are four basic rules to observe when specifying collection member (item) type or adding items (functional targets). The following rules are subject 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 (via its IDcComponentDispatcher interface) and from Component Dispatcher to the functional component (via its IDcComponent interface). That is, the collection is passed, not individual items (functional targets). Even if only one item is involved, it must be contained in a collection. 3. IDcItems is defined as a standard collection interface so that VB programmers will be immediately 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 standard implementation 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 so that developers can avoid typing the wrong IID. Figures 15 through 17 show various services provided by the Documentum Item Server.

Documentum, Inc.

Page 21 of 78

Developing Documentum Desktop Client Components

8/9/99

Figure 15. Looking at IDcObjectItem::ID in VBs Object Browser DcAbstractionItem implements IDcAbstractionItem; DcFileItem implements IDcFileItem; and so on. DcFileObjectItem is an example of a COM object that implements more than one item interface. In this case DcFileObjectItem implements both IDcFileItem (its default interface) and IDcObjectItem. The DcItemType enumeration is currently used with abstraction items (i.e. the IDcAbstractionItem::Type property).

Figure 16. Looking at IDcItems::Item in VBs Object Browser Again, please note that the Index parameter of the Item property to the DcItems object shown above in Figure 16 starts with a value of one (not zero) and grows up to Count.

Documentum, Inc.

Page 22 of 78

Developing Documentum Desktop Client Components

8/9/99

Figure 17. Looking at Item IID String Constants in VBs Object Browser Hopefully youll agree that its much easier to enter DC_OBJECT_ITEM_IID_STRING than it is 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 of their invocation. Note however that this kind of interface is an optional parameter in both IDcComponentDispatcher::RunComponent and IDcComponent::Init, which means that an object that is initially aware of what its end user wants to act upon may not expose itself to the object performing the action itself. Think of item container interfaces as a way to provide components with a set of properties that all apply to all items. Design your item container interfaces to complement standard item interfaces and any custom item interfaces you develop. Item interface properties should be reserved for values that differ from one item to another. You can use the VB Assistant mentioned in section 5.7 to view the current set of standard item container interface definitions.

5.4 Component Dispatching


The Documentum Component Dispatcher (DCCMPDSP.DLL) is responsible for creating functional components like yours to satisfy a particular functional request. The Component Dispatcher 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 Manager delivers and registers the component if necessary; and the Component Dispatcher instantiates and manipulates the component (i.e. calls its IDcComponent methods). The only direct client of any component in the Documentum Desktop Client system should be the Component Dispatcher. That is, desktop client components should only be

Documentum, Inc.

Page 23 of 78

Developing Documentum Desktop Client Components

8/9/99

instantiated and accessed through IDcComponent by the Component Dispatcher. The Component Dispatcher together with DART enforces your companys business rules, providing the right component for each request. If there are insufficient privileges to execute the request, no component will be invoked. Hard-wiring a direct connection between application and component or between one component and another component may violate policy. Documentum Developer Studio (DDS) is the tool that you will use to make the Documentum system (i.e. DART) aware of your functional component according to the business logic you define (e.g. ACLs, document life cycle stages, etc.). Details about DDS as well as details concerning how DART ends up with a component to discover from a particular Docbase are outside the scope of this document. In general, the Component Dispatcher will use DART to get the CLSIDs for components that it needs to invoke. There are a few exceptions as follows in which cases the Component Dispatcher will instead use the Windows Registry to acquire component CLSIDs: 1. Functional target (selected item) is an abstraction. An abstraction is something exposed by the desktop client UI that doesnt 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 a global 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 My Cabinet. When an abstraction is specified as the target of a function, Component Dispatcher will look for the associated functional class name under \HKEY_LOCAL_MACHINE \SOFTWARE\Documentum\Components. Figure 18 shows which registry key the Component Dispatcher looks up for Properties component CLSIDs, matching the name of the selected abstraction 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 with the DcFind functional class). We need to be able to invoke the Find component even if the user has not yet logged in. If no Docbase/user OS name/domain is passed into IDcComponentDispatcher::RunComponent, the Component Dispatcher will look in the registry for the CLSID of the component associated with the requested functional class.

Documentum, Inc.

Page 24 of 78

Developing Documentum Desktop Client Components

8/9/99

Figure 19 shows which registry key the Component Dispatcher looks up for the Find components CLSID, which is the definition of the (Default) registry value.

Figure 19. Global Component CLSID Example (DcFind) Heres how to add a new global component to your local Windows Registry. Youll still need to use DDS to establish your component in your enterprise. 1. Launch REGEDIT.EXE. 2. In the tools 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 form Dc<function> (e.g. DcFind, etc.). 5. Select the key of the functional class in REGEDITs tree pane then double-click on the (Default) value in its content pane. In the Value data field of the Edit String dialog add the CLSID of your component including delimiting curly braces (e.g. {46148CE5234C-11D2-A08D-00104B72FD7F}), replacing any previous string value. You can use a tool like OLEView to obtain your components locally registered CLSID. User Is Working Offline If the selected object is a local or working file, the Component Dispatcher will first try to find a 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 CLSID will be invoked to provide offline properties on dm_document objects. You may have multiple components for a functional class when theyre working offline each based on a particular object type. If no type-specific registry entry is found, the Component Dispatcher will look for a default entry (e.g. \HKEY_LOCAL_MACHINE\SOFTWARE\Documentum \Components\DcViewOptions\Offline: (Default)=<CLSID>). The default CLSID implies offline operations are supported by the associated component for all object types not specifically listed in the registry. If no Offline registry key is found for a particular functional class, the function in question does not support offline operations. Heres how to add a new component that supports offline operations to your local Windows Registry. Youll still need to use DDS to establish your component in your enterprise. 1. Launch REGEDIT.EXE. 2. In the tools left-hand side tree pane, drill down on HKEY_LOCAL_MACHINE\SOFTWARE\ \Documentum\Components. 3. Select Components.

Documentum, Inc.

Page 25 of 78

Developing Documentum Desktop Client Components

8/9/99

4. If you want to associate your component with a new functional class, go to the next step; otherwise proceed to (6). 5. Select Edit | New -> Key and replace the default selection with the name of your new functional class. Standard Documentum functional class names take the form Dc<function> (e.g. DcProperties, etc.). 6. If necessary, select the key of the functional class in REGEDITs tree pane then select Edit | New -> Key and replace the default selection with Offline. 7. If you want to associate your component with an entire functional class as the default offline component, go to the next step; otherwise proceed to (9). 8. Select the Offline key in REGEDITs tree pane then double-click on the (Default) value in its content pane. In the Value data field of the Edit String dialog add the CLSID of your component* including delimiting curly braces (e.g. {57DA2981-9A05-11D280FA-00105A1F0288}), replacing any previous string value. 9. If you want to associate your component with one or more specific types under a particular functional class (which you may or may not want to do in addition to establishing your component as the default), do the following: a. Select the Offline key in REGEDITs 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 string value. 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 use OLEView, for example, to gather such COM-related information as your components CLSID. Application Name The Component Dispatcher expects an application name as input to both RunComponent, RunComponentEx, and GetComponentCLSID methods in its IDcComponentDispatcher interface. 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 no application name is supplied, the Component Dispatcher will use DcDesktopClient as the application name. DcDesktopClient is the DocApp that specifies Documentum 4i Desktop Client components. Note that the applicationName parameter is only used by the Component Dispatcher when a global 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 DocApps is beyond the scope of this document.

5.5 Report Management


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

Documentum, Inc.

Page 26 of 78

Developing Documentum Desktop Client Components

8/9/99

Use these in situations where your component is unable to complete an action requested by the user. Users should be informed of problems, and in some cases their input is required before proceeding. The Report Manager simplifies and standardizes this process by allowing your components 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 clients Init method is called, it receives a report object via the reporter input parameter. Your component should use this report object to register and, if necessary, display errors. If your component involves dispatching other components to satisfy a particular functional request, it can pass this same report object to Component Dispatcher via RunComponent. The Component Dispatcher, in turn, will pass the same report object to each component is dispatches via the components 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 if you created it. Your component sometimes need only to notify the user of something, but at other times it may require feedback from the user before proceeding. The Report Manager currently provides 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 may also 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, your component should allow the user the option of stopping or continuing the operation. For example, a user might request that five documents be deleted, but not have sufficient permissions to delete the second document. A user might not want to delete the last three documents 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/Stop dialog box in these situations. An error might occur while performing an operation that is part of a transaction. The nature of the error might affect the user's decision to continue the transaction, stop the transaction where it is at, or undo all the operations in performed so far in the transaction. To determine 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 delivery mechanism for both standard desktop client icons and custom icons specific to your application.

Documentum, Inc.

Page 27 of 78

Developing Documentum Desktop Client Components

8/9/99

When you create a new object type or format as part of an application, you will probably want to assign a new icon, for example, to represent it in the desktop client's integration with Windows Explorer and in dialog boxes featuring operations on the item. Components that want to display a Documentum-provided icon must include the resource file containing all Documentum icons. When you create new icons, they must be added to the System\Images folder in the Docbase featuring the new object type or format and saved as sysobjects. The Icon Manager is responsible for loading icons stored in this location with the Documentum-provided icons and providing methods for retrieving these icons at runtime. If the Icon Manager is unable to find a specified icon in the resource file, it traverses up the object 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 a 256-color palette when creating custom icons Finally, adhere to the following guidelines when naming icons: 1. 2. 3. 4. Start the icon name with the associated object type or format. If an icon is also associated with a format, append "_formatname" after the format. If the icon represents an object's open or closed state, append "_Opened" or "_Closed." The filename extension must be .ico.

5.7 Visual Basic Component Development Assistant


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

Documentum, Inc.

Page 28 of 78

Developing Documentum Desktop Client Components

8/9/99

Figure 20. Looking at IDcComponent::Init in VBs Object Browser

5.8 Other Component Services


The Documentum Desktop Client Utilities Manager (DCUTILMGR.DLL) provides a toolbox of utilities designed to simplify support of various functions in your components. Currently this server provides an object that supports exposure of Data Dictionary functionality, an object that helps to protect unsaved changes from being lost, and an object that supports locking of DFC shared sessions. Use DcDataDictionaryUtils to take internal names and receive back external or user-defined names. Use DcDataProtectionUtils to allow a user to save unsaved objects. Use DcDFCSessionUtils to lock DFC shared sessions. There is also a utility class (DcSessionLock.cls) that encapsulates and simplifies calling IDcDFCSessionUtils interface methods. The Docbase object-based properties component (DCPROPERTIESCOMP.DLL) is an example of a Documentum-provided desktop client component that uses both DcDataDictionaryUtils and DcSessionLock.cls (and therefore DcDFCSessionUtils). Figure 21 shows the type library of the Utility Manager.

Documentum, Inc.

Page 29 of 78

Developing Documentum Desktop Client Components

8/9/99

Figure 21. Looking at IDcDataDictionaryUtils::GetAttributeDisplayName in VBs Object Browser There are additional major services available to desktop client components that are not the focus of this document. These services include Documentum Foundation Classes (DFC) and special purpose widgets (ActiveX Controls). Please consult the documentation that comes with these system elements for more details.

6 Creating a New Desktop Client Component


Before talking about how to create a component, lets look at a simplified interaction diagram to see how a component is called into action. Desktop client components are designed to satisfy functional requests.

Documentum, Inc.

Page 30 of 78

Developing Documentum Desktop Client Components

8/9/99

(1)

Application Integration Check-in Item Container

IDcComponentDispatcher ::RunComponent

IDcAppIntgCheckInItemContainer ::CheckedInObjectID

Component Dispatcher

(3)

(2)
IDcComponent::Init

Cabinet Manager

DART

Component

DFC

Figure 22. High-level Component Interaction Diagram Figure 22 shows how an item container interfaces with the Component Dispatcher to satisfy a 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 and calls its IDcComponentDispatcher::RunComponent interface method. As part of its call the item container provides a collection of one or more items to RunComponent. 2. Within RunComponent, the Component Dispatcher asks DART and the Cabinet Managers objects to discover, deliver and register the right component(s) to satisfy this functional request. (One function may involve more than one component.) Once DART and the Cabinet Managers objects are finished with its part of the process (see section nine for more details), the Component Dispatcher instantiates the component associated with the CLSID returned by DART and calls the components IDcComponent::Init interface method, passing a filtered collection of items that apply to this specific component. Init contains code to allocate resources necessary to complete a function (e.g. check-in a document). Once control returns from Init, the Component Dispatcher calls the components IDcComponent::Run interface method, telling the component to do its job. 3. While completing its function, the component may learn about the context of its invocation (i.e. functional context) by querying its item container through the item containers COM interface, if one is provided to the component. The component may also access 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 client component. Your component may optionally implement other interfaces to participate more fully in the desktop client infrastructure (e.g. IDcEventSink), but implementing IDcComponent is all thats required. That is, desktop client components are simply COM objects that implement at least the IDcComponent interface.

Documentum, Inc.

Page 31 of 78

Developing Documentum Desktop Client Components

8/9/99

Documentum Desktop Client software is not concerned with a components housing. Components may be housed in DLLs or EXEs. The following sub-sections assume your Visual Basic 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 being developed. Text that may vary between a standard Documentum Desktop Client component implementation 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. one youre developing), the server is locked and cannot be deleted. That is, once you establish a reference, 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 server outside VB, but you wont be able to overwrite the server itself until the active reference is released (e.g. the project is removed from the active VB session).

6.1 Start with a Skeleton (Component Project Configuration)


Now that your VB environment is properly set up for Documentum Desktop Client software development from section 3, you can create a new component project. 1. Create a new folder to contain your VB component project. When desktop client components were developed, a common root components folder was specified under which a folder for each individual component was created (e.g. Documentum component folders took the form Dc<function>Complike 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 Instancing property to 5 Multiuse. This is exactly as it should be. This instancing option allows a single server to support any number of objects of a given class. The objects can be created by applications using the New operator or the CreateObject function. In the case of 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 requirement to graphically display such properties to end users, you may wish to create an ActiveX Control project instead. ActiveX Control projects are beyond the current scope 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 components project name should be unique otherwise an end users 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 word capitalized, remainder lowercase>Comp (e.g. DCCHECKOUTLib.DcCheckOutComp). d) Click on Project1 in the Project Explorer window (i.e. typically the window on the upper right corner whose title bar caption begins Project ). In the Properties window, click on Name and type, for example, DCPROPERTIESLib (without the quotes).

Documentum, Inc.

Page 32 of 78

Developing Documentum Desktop Client Components

8/9/99

e) Click on Class1 in the Project window, then click on Name in the Properties window and enter your components 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 source code. As you can see its pretty empty, except for Option Explicit at the top. Add a standard comment header (preamble). An example comment header is provided in Appendix 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 Desktop Client\components\DcPropertiesComp). Be sure to rename your project file name from DC<function all uppercase>Lib.vbp to Dc<function>Comp.vbp (e.g. DcPropertiesComp.vbp). This rename will automatically carry-over to properly 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 most important properties you will choose, and you should always set it for your project. This property becomes the name of your components type library. For example: If you name your project DCPROPERTIESLib and it contains a class module named DcPropertiesComp, the COM class behind the projects class module can be instantiated through its programmatic identifier or ProgID, which in this case would be DCPROPERTIESLib.DcPropertiesComp. If the object is publicly creatable, you will be able to pass this ProgID to the CreateObject function to create instances of those objects, as in the example: Set newobj = CreateObject("DCPROPERTIESLib.DcPropertiesComp") Enter something for Project Description. What you enter will be displayed in the References dialog when someone refers to your component. In IDL terms, what you enter is the helpstring value for the library section of your component housings IDL file. 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. properties component): Type Comments Company Name File Description Legal Copyright Legal Trademarks Product Name Value <blank> Documentum, Inc. DcPropertiesComp Copyright 2000, Documentum, Inc. <blank> Documentum Docbase Objectbased Properties Component

What you enter for Version Information will be displayed to an end user in the Windows Explorer when your component housing is selected for property information. Version Information is VBs UI for your component housings VS_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 components file version grows with each change to its implementation. Auto Increment increases the value of Revision by one every time your component project (.VBP) file is saved. If your component participates in dynamic component delivery (DCD), it

Documentum, Inc.

Page 33 of 78

Developing Documentum Desktop Client Components

8/9/99

must specify an increasing file version in order to be properly installed on your end users machines. c) On the Compile tab, compile to native code creating symbolic debug informationto a .PDB file--with no optimization. While youre developing your component, you need to support both design-time and run-time debuggingtopics covered in more detail in section 7. This is why you include symbolic debug information and exclude optimizations. Once youre through debugging your component, you need to go back to this tab to remove symbolic debug information and change the compilation to optimize for speed. Before you release a DLL-based component (including ActiveX controls), be sure to reset the number for DLL Base Address to a value between &H60000000 and &H68000000. Choose any 64K boundarythis means that the rightmost four digits of the address will always be 0000. Microsoft suggests you choose a random base address in that range as the base for all of your controls, then allocate 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. If the memory space is available within the process space of the application that is loading the DLL, the DLL loads extremely quickly. If, however, another DLL component is already using that space, Windows must relocate the DLL data and code to a new location. This is a time-consuming operation and one that is wasteful because it may prevent Windows from sharing the DLL code with other applications. Currently Documentum desktop client software occupies base addresses from &H63000000 to &H68000000. This range is subject to change. For optimal loading performance on your component, please specify a base address that doesnt conflict with this range (i.e. lies between &H60000000 and &H63000000). d) On the Component tab, set Version Compatibility to Project Compatibility. In section 6.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 dont need 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 module must implement the IDcComponent interface. All desktop client components implement this common interface which allows Component Dispatcher to treat them polymorphically. It is through this interface that the outside world will communicate with your component, passing it information in the form of items (functional targets) and optionally context (item container interface). Youll be called to execute a particular functionthis interface provides you with the necessary information to do the job. Use the following procedure to quickly generate a skeleton IDcComponent implementation in your class module:

Documentum, Inc.

Page 34 of 78

Developing Documentum Desktop Client Components

8/9/99

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.0 Type Library. 2. Open the code window on your class module. 3. After the Option Explicit start typing Implements . Microsoft IntelliSense (autocomplete) technology should kick-in. Continue typing dcc to highlight DCCOMPONENTLib. Hit the Tab key then enter a period (i.e. you should have Implements DCCOMPONENTLib. up to this point). Finally type idcco to highlight IDcComponent. Hit the Tab key then the Enter key to wind up with Implements DCCOMPONENTLib.IDcComponent. 4. To avoid guesswork and error, use the IDE to provide a skeleton implementation of IDcComponent. At the top of your class modules source window there are two dropdown boxes. 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 IDcComponent. The IDE will provide a skeleton for IDcComponent::Init. Next dropdown the Procedure box and 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 the explicit 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 Its worth repeating that all standard Documentum desktop client interfaces are COM interfaces. 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 derive from). In the case of IDcComponent from the VB developer's perspective, all you really care about is IDcComponent itselfVB provides the implementation of IDispatch (and therefore IUnknown) for you. However, if you're developing a desktop client component in C++, for example, you'll have to implement not only IDcComponent but all of its base interfaces as well!

Documentum, Inc.

Page 35 of 78

Developing Documentum Desktop Client Components

8/9/99

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_Initialize or Class_Terminate. Initialization in desktop client component class modules should occur primarily in IDcComponent_Init (i.e. IDcComponent::Init); termination logic should exist primarily in IDcComponent_DeInit (i.e. IDcComponent::DeInit). Modeless component lifetimes may need to be managed differently; in this case, for example, termination logic may 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 you should use error handlers in all of your routines, unless they are completely straightforward and will not raise errors. This is especially important in VB code, since failure HRESULTs are automatically translated into VB exceptions. VB will handle any unhandled exceptions by displaying a fairly cryptic error dialog and then terminating your component! Here is an example error-handling skeleton for IDcComponent::Run that uses the Documentum Report Manager. This example assumes that youve established a reference to the following type library: Documentum Report Manager 1.0 Type Library (DCREPORTSLib).
Private m_Reporter As DCREPORTSLib.DcReport 'Set within IDcComponent::Init Private 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 Function HandleError: '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_FAILURE End 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 you established an implementation of IDcComponent (e.g. DCENABLEVB.DLL). You can determine those available through VBs Object Browser (F2). Scroll down on the Project/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 hardcoded values. At this point you still only have a skeleton implementation of IDcComponent. Before you add your specific implementation code, please understand the role each method in IDcComponent should fulfill. 1. Init In this method, your component should allocate any resources it needs to do its job. You may find that other forms or other IDcComponent methods require some of the data that is passed into your component via its IDcComponent::Init method. If this is the case, use private class module data members and/or properties on forms to save the necessary input data before your component returns from Init. Init currently specifies eight parameters plus an optional ninth parameter. All of these parameters are inputs.

Documentum, Inc.

Page 36 of 78

Developing Documentum Desktop Client Components

8/9/99

As with each method of IDcComponent, Init returns a Long value from the DcCompReturnValue enumeration. Figure 25 provides a table that maps input parameter to its type and description. 2. Run This is where your component executes its functionality, often presenting its end user 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 consult section 6.5 for more details. Parameter docbaseName userOSName domain contextID Type String String String String Description Name of the Docbase containing the functional targets (items) Name of the current user as expected by the operating system Domain name r_object_id attribute of the qualified component (i.e. dm_qual_comp Docbase object) associated with the invoked component; the component can retrieve context information concerning its invocation using this parameter (e.g. the client_capability attribute states whether the end user is a consumer, contributor or coordinator 3C; document lifecycle information is available as well) Collection of one or more targets for the function this component is designed to execute HWND to use when presenting dialogs to the end user (e.g. use with reporters Display function) Object which handles the common display of error information in report form (section 5.5) String IID constant for an item container interface, if one is provided in itemContainer [Optional] If provided, itemContainer is an Iunknown interface from which the component must access the item container interface specified by stringForIID. VB components may use the IsMissing function on this parameter to determine whether an interface has been provided

items hwndForDialog reporter stringForIID itemContainer

IDcItems interface Long IDcReport interface String Variant

Figure 25. IDcComponent::Init Parameters Components must free allocated resources if returning something other than DC_COMP_SUCCESS from either IDcComponent::Init or IDcComponent::Run. If a component 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 component typically frees allocated resources.

6.2.2 Connecting to a Docbase


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

Documentum, Inc.

Page 37 of 78

Developing Documentum Desktop Client Components

8/9/99

from IDcLoginManager::Connect. This DFC session will be a shared session that is shared by at least your component and the Login Manager it created. When a shared session is created, a key (string) is specified. Any subsequent requests for a shared session, as the same Docbase/user and the same key, will return the same shared session. Currently, all sessions obtained from the Login Manager are shared sessions, where the 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 synchronization problems. However, problems may occur when multiple threads within a process are trying to use the same shared session. DFC session (e.g. the CDfSession) objects have a lock/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 error handler 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 your component project and use one of its DcSessionLock objects in your component code. Heres a simple VB example showing how to connect to a Docbase using the Documentum Login Manager and how to subsequently get and use a DFC shared session. If the user hits Cancel on the login dialog, the Login Manager will return successfully with an empty session ID. 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 its call to Connectyou cannot assume that a successful return from Connect implies a connection 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), Documentum Login Manager 1.0 Type Library (DCLOGINMGRLib) and Documentum Report Manager 1.0 Type Library (DCREPORTSLib). It also assumes that your project includes DcSessionLock.cls as one of its class modules.
'DFC-related data members Private m_DfClientX As DFCLib.DfClientX 'example assumes prior instantiation Private m_DfClient As DFCLib.IDfClient Private m_DfSession As DFCLib.IDfSession 'Data members set previously in IDcComponent::Init Private m_DocbaseName As String Private m_UserOSName As String Private 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, _ "", _

Documentum, Inc.

Page 38 of 78

Developing Documentum Desktop Client Components

8/9/99

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 didnt call loginMgr.Disconnect since other 'applications may still be interested in a connection to this Docbase. End If GoTo ExitRoutine HandleError: 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 of this document. Please consult DFC documentation for more details on how to interface with and deploy DFC in your component. As you learn how to use DFC, you may want to turn DFC tracing on. A registry-based approach is recommended. Please consult Documentum Desktop Client VB component source code for more details (i.e. examples).

6.2.3 Functional Targets & Contexts Working with Items & Item Containers
Figure 27 provides sample VB code, which shows indexing through an item collection and traversing 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 Server 1.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

Documentum, Inc.

Page 39 of 78

Developing Documentum Desktop Client Components

8/9/99

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 is quite similar to the previous example. You will need to ask the item collection for its type and 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 item collection comes from a trigger and is passed on to one or more components through Component Dispatcher. However that data may be overwritten by these components so that when the original item collection is returned to the trigger, it contains different data than originally specified. A large selection of items by the trigger may result in more than one component being dispatched. Since only one item collection is returned to the trigger since it only passes a single collection to Component Dispatcher, if more than one component is involved in the trigger's functional request, all involved components must return the same type of item collection. Item collections cannot currently contain more than one type of item. The singular type may be different than that originally set by the trigger.

Documentum, Inc.

Page 40 of 78

Developing Documentum Desktop Client Components

8/9/99

Component Items Trigger (source of functional request) Items

Component Dispatcher

Items

Component

Items Component

Figure 28. VB Sample Code Working with an Item Collection Two-way data flow allows components to communicate with their triggers through item collections. It also requires both parties to be responsible with respect to clean up (i.e. freeing resources). Specifically all callers of IDcComponentDispatcher::RunComponent or IDcComponentDispatcher::RunComponentEx who create item collections must eventually erase their item collections prior to releasing the item collection interfaces. Callers of RunComponent should not assume that an empty item collection passed into RunComponent returns empty. Figure 29 provides an example in VB of how to check if item container support is present within 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 VB Component Development Assistant 1.0 Type Library (DCCOMPONENTLib) and Documentum Shell Integration 1.0 Type Library (DCEXPLORERINTGLib).
Private Private Private Private m_IsLocationContainer As Boolean m_IsLocalDocument As Boolean m_LocationContainer As DCEXPLORERINTGLib.IDcLocationItemContainer m_RegularContainer As DCEXPLORERINTGLib.IDcItemContainer

'. . . m_IsLocationContainer = False m_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

Documentum, Inc.

Page 41 of 78

Developing Documentum Desktop Client Components

8/9/99

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 If End If '. . .

Figure 29. VB Sample Code Working with Item Containers

6.2.4 Dispatching Another Component


If you havent already, please review sections 5.4 and 6 as well as Figure 22 to understand whats involved in component dispatching. Heres some sample VB code to implement the information in these previous sections. This code assumes that you have references established to the following type libraries: Documentum Component Dispatcher 1.0 Type Library (DCCMPDSPLib), Documentum Item Server 1.0 Type Library (DCITEMSERVERLib) and Documentum Report Manager 1.0 Type Library (DCREPORTSLib).
'Data members set previously in IDcComponent::Init Private m_DocbaseName As String Private m_UserOSName As String Private m_Domain As String Private m_Hwnd As Long Private 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

Documentum, Inc.

Page 42 of 78

Developing Documentum Desktop Client Components

8/9/99

Dim componentDispatcher.RunComponent("DcProperties", _ rv = rv As Long 'i.e. DcCompReturnValue "DcDesktopClient", _ docbaseName, _ userOSName, _ domain, _ itemCollection, _ windowForUI, _ reportMgr, _ itemContainerType, _ itemContainerInterface) GoTo ExitRoutine HandleError: Dim btn As DcReportButtonType btn = reportMgr.Display(windowForUI, DC_REPORT_OK_ONLY) ExitRoutine: 'single exit point End 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 dispatch events related to its function. Your component may also be interested in receiving events from 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 how to tell the Notifier to send an event. First, well create a simple component event sink. Following the same kind of procedure that was presented in section 6.2.1, generate a skeleton IDcEventSink implementation in a new class module for your component project as follows: 1. Choose Project | Add Class Module | New tab and the Class Module icon to create a new, empty class module in your component project. 2. Give it a meaningful name (e.g. Dc<function>EventSink). 3. If you havent 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 Event Server Type Library - 1.0. 4. Open the code window on your class module. 5. After the Option Explicit start typing Implements . Microsoft IntelliSense (autocomplete) technology should kick-in. Continue typing dcev to highlight DCEVENTSERVERLib. Hit the Tab key then enter a period (i.e. you should have Implements DCEVENTSERVERLib. up to this point). Finally type idcevents to highlight IDcEventSink. Hit the Tab key then the Enter key to wind up with Implements DCEVENTSERVERLib.IDcEventSink. 6. To avoid guesswork and error, use the IDE to provide a skeleton implementation of IDcEventSink. At the top of your class modules source window there are two dropdown boxes. 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 will provide a skeleton for IDcEventSink::PID (a read-only property). Next dropdown the Procedure 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 the explicit type library parameter qualifiers. 7. Add private data members for your PID and Name properties.

Documentum, Inc.

Page 43 of 78

Developing Documentum Desktop Client Components

8/9/99

8. Initialize your read-only PID property by declaring the Win32 API GetCurrentProcessId and 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 additional formatting done here for easier reading. The code in Figures 31, 32, 34 and 35, assumes that you have a reference established to the event server type library (i.e. Documentum Event 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 Long Private 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 ExitRoutine HandleError:

Documentum, Inc.

Page 44 of 78

Developing Documentum Desktop Client Components

8/9/99

'TODO: Add your exit point ExitRoutine: 'singleown custom error handling logic here. End 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 = RHS End Property Private Property Get IDcEventSink_Name() As String IDcEventSink_Name = m_Name End Property Private Property Get IDcEventSink_PID() As Long IDcEventSink_PID = m_PID End 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 sink may receive through its HandleEvent method. This sample is designed to show you how to extract input data for each event code currently defined in the standard desktop client. Note that your ability to handle these events is dependent upon how your event sink is registered with the Notifier. To take full advantage of the following code, you should call RegisterEventSink with categoryMask set to DC_EVT_CATEGORY_ALL. Although nothing prevents a sink from registering for interest in all events while only really handling a small subset, please consider overall event dispatching efficiency when developing your event sinks and registering them. For example, only register for event categories that are of true interest to your sink. While the following sample code gives you an introduction to handling all standard events, it doesn't necessarily represent an efficient event sink from the perspective of the system. Finally, concerning the next code sample, please note that a single active error handler is used (in bold). The code explicitly shows the importance of not assuming that either expected data is provided or unexpected data is not provided. However, the code might not be as clear about handling event data whose format is unexpected (e.g. expecting a DCEVENTSERVERLib.DcContainedObjEventData object by receiving only a DCEVENTSERVERLib.DcObjectEventData object). When the following code (or any VB code for that matter) tries to access event data that isnt implemented, the event data implementation will cause VB to raise an exception. Given our active error handler, control will transfer to the HandleError label. If we didnt have an active error handler, the exception 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

Documentum, Inc.

Page 45 of 78

Developing Documentum Desktop Client Components

8/9/99

Dim Dim Dim Dim Dim Dim Dim

ccoObjEventData As DCEVENTSERVERLib.DcCheckInObjEventData ciObjEventData As DCEVENTSERVERLib.DcCancelCheckOutObjEventData docbaseObjEventData As DCEVENTSERVERLib.DcDocbaseObjEventData deleteObjEventData As DCEVENTSERVERLib.DcDeleteObjEventData functionObjEventData As DCEVENTSERVERLib.DcFunctionObjEventData formatObjEventData As DCEVENTSERVERLib.DcFormatObjEventData 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 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set containedObjEventData = eventData Debug.Print " Object ID = " + containedObjEventData.ID name = " + 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 'In other words, be prepared for malformed SendEvent callers. If Not (eventData Is Nothing) Then Set containedObjEventData = eventData Debug.Print " Object ID = " + containedObjEventData.ID name = " + 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 '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

is.

+ "; Docbase

is.

+ "; Docbase

is.

Documentum, Inc.

Page 46 of 78

Developing Documentum Desktop Client Components

8/9/99

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 + "; Docbase name = " + 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

Documentum, Inc.

Page 47 of 78

Developing Documentum Desktop Client Components

8/9/99

Debug.Print " Object ID = " + deleteObjEventData.ID + "; Docbase name = " + deleteObjEventData.docbaseName + "; chronicle ID = " + deleteObjEventData.ChronicleID + "; type = " + CStr(deleteObjEventData.Type) Nothing Set deleteObjEventData = 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 + "; Docbase name = " + 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"

Documentum, Inc.

Page 48 of 78

Developing Documentum Desktop Client Components

8/9/99

'Don't assume that just because you expect data to be callers. that it is. 'In other words, be prepared for malformed SendEvent present 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 ExitRoutine HandleError: 'TODO: Add your own custom error handling logic here. ExitRoutine: 'single exit point End 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

Documentum, Inc.

Page 49 of 78

Developing Documentum Desktop Client Components

8/9/99

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 Select End 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 event sinks 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 coworkers code will be. Seriously, you should be able to assume the presence of a particular event data interface based on code and type, but someone may accidentally develop code that sets eventData to Nothing in VB. If you don't check for Nothing first, your code will throw an exception when it tries to use eventData (i.e. Set thisEventData = eventData). When youre 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 the original SendEvent caller. Figure 33 provides a summary mapping of standard desktop client event codes to expected event data interfaces. The Documentum Event Server provides a standard implementation for each event data interface. In VBs Object Browser, these objects in DCEVENTSERVERLib take the form DcEventData. Event Code (eventCode) DC_EVT_ENV_CLIPBOARD_REVISION DC_EVT_ENV_VIEW_OPTIONS_REVISION DC_EVT_ENV_WORK_OFFLINE DC_EVT_ENV_WORK_ONLINE DC_EVT_LINK_CREATION DC_EVT_LINK_DELETION DC_EVT_LOCAL_COPY_CREATION DC_EVT_LOCAL_COPY_DELETION DC_EVT_OBJ_CANCEL_CHECKOUT DC_EVT_OBJ_CHECKIN DC_EVT_OBJ_CHECKOUT DC_EVT_OBJ_CREATION DC_EVT_OBJ_DELETION DC_EVT_OBJ_LOCAL_REVISION DC_EVT_OBJ_OPEN_DIALOG DC_EVT_OBJ_RENDITION_REVISION DC_EVT_OBJ_SERVER_REVISION DC_EVT_SESSION_CREATION DC_EVT_SESSION_DELETION Event Data Interface (eventData) Nothing (VB) / NULL (C++) Nothing (VB) / NULL (C++) Nothing (VB) / NULL (C++) Nothing (VB) / NULL (C++) IDcContainedObjEventData IDcContainedObjEventData IDcObjectEventData IDcObjectEventData IDcCancelCheckOutObjEventData IDcCheckInObjEventData IDcDocbaseObjEventData IDcContainedObjEventData IDcDeleteObjEventData IDcDocbaseObjEventData IDcFunctionObjEventData IDcFormatObjEventData IDcDocbaseObjEventData IDcSessionEventData IDcSessionEventData Notes

Notes 1. IsNew defaults to False; set to True if appropriate

Documentum, Inc.

Page 50 of 78

Developing Documentum Desktop Client Components

8/9/99

2. All clients should define DocbaseName and ID properties. VDM may also define ChronicleID 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 to dispatch an event via the Notifier.
Option Explicit Private Declare Function GetCurrentProcessId& Lib "kernel32" () Private Dim Dim Dim Sub TestRegisterSinkAndCallNotifier(DocbaseName As String, ContainerID As String) notifier As DCEVENTSERVERLib.DcNotifier myEventSink As DcTestEventSink 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 sinks 'cookie (which you would had to have received earlier in your HandleEvent 'implementation). In our call, DC_EVT_COOKIE_NO_FOLLOWUP implies that were '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.

Documentum, Inc.

Page 51 of 78

Developing Documentum Desktop Client Components

8/9/99

notifier.UnregisterInterest myCookie,1 DC_EVT_CATEGORY_ALL myCookie = DC_EVT_COOKIE_MIN_VALID '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 ExitRoutine HandleError: 'TODO: Add your error handling here. ExitRoutine: 'Single exit point End Sub

Figure 34. VB Sample Code Interfacing with the Notifier The Documentum Event Server also supports the notion of queuing events. You call the Notifiers GetEventQueue method to receive an event queue. Although event queues are created solely by the Notifier, their lifetimes are not dependent on the existence of the Notifier. That is, unless you have other reasons to keep the Notifier around, you can release the Notifier after calling GetEventQueue. When you call the event queues Send method, the queue itself will instantiate the Notifier in order to call SendEvent. Only events that dont require or expect responses should be queued. You should not queue query or state transition events. In fact, if you do attempt this, Add will return False and the event wont be queued. An application typically queues events while it has lock on a DFC shared session. Once it releases its lock it will tell the event queue to send itself. Since more than one event may be queued, there isnt a way for the event queue client to receive a response 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

Documentum, Inc.

Page 52 of 78

Developing Documentum Desktop Client Components

8/9/99

6.3 Add Skin (User Interface)


If your component supports a user interface, add the necessary forms to your component housing project. I recommend that you use the following naming convention for all forms: Forms Name property frmDc<descriptive name> (e.g. frmDcFolder) Forms 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 this document. Please consult Microsoft documentation as well as the selected bibliography provided in Appendix B. If your component will employ strings, bitmaps and/or cursors, etc., consider adding a resource (.RES) file to your component project. Using string tables in resource files is one way to make your component internationalized (i.e. ready to be localized). VB6 comes with a Resource Editor that makes resource manipulation fairly straightforward. Heres how to add the Resource Editor to your VB6 IDE, in the event that you dont already find 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 wont be able to use VBs 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 automate this process. A typical batch file contains the following lines:

Documentum, Inc.

Page 53 of 78

Developing Documentum Desktop Client Components

8/9/99

RC /V /X /I "../../iconmgr/Images" Resource\DcPropertiesComp.rc attrib -r DcPropertiesComp.res copy Resource\DcPropertiesComp.res .\* del Resource\DcPropertiesComp.res 5. Add the resulting .RES file to your component project.
#include "DcIconRes.rc" // Bitmaps //#define IDB_BITMAP1 //IDB_BITMAP1 1

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 DISCARDABLE BEGIN // 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++'s Resource Editor to make changes as it will completely replace your existing resource file with one that depends on a Resource.h header filewhich makes sense to a C++ developer but not a VB developer. Open your .RC file manually in text mode with VC++ or launch Notepad to make your edits.

6.4 Add Brains (Business Logic)


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

6.5 Modal Versus Modeless Components (Lifetime Management)


As we talked about in section 5.4, when the Documentum Component Dispatcher instantiates a component it will then call the components IDcComponent methods in the

Documentum, Inc.

Page 54 of 78

Developing Documentum Desktop Client Components

8/9/99

following order: Init, Run and DeInit. Following its call to DeInit, Component Dispatcher calls Release on the IDcComponent interface pointer. Modeless components are both similar to and different from modal components. They're similar in that their lifetime is COM-based in proper reference counting. They're different in how 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 until their user interface is dismissed. The DeInit method of a modal component usually performs the job of freeing resources acquired during Init processing. When a modal component returns from its DeInit method to Component Dispatcher, it expects to be destroyed. When Component Dispatcher calls Release on the pointer to the modal component's IDcComponent interface, that expectation is met (i.e. the modal component is deleted from memory due to a reference count of zero). On the other hand, modeless components will immediately return from their Run method to Component Dispatcher as soon as their modeless UI is displayed. Component Dispatcher will then call a modeless component's DeInit method, while the component's UI may still be active. The DeInit method of a modeless component should do absolutely nothing but simply return DC_COMP_SUCCESS. Modeless component resource cleanup must occur within its UI. When a modeless component returns from its DeInit method to Component Dispatcher, it does not expect to be destroyed. However, unless the modeless component takes responsibility for its own COM-based lifetime, it will be deleted from memory when Component Dispatcher calls Release on the pointer to the modeless component's IDcComponent interface. So, modeless components should increment their own reference count within their IDcComponent::Init method. Right before exiting their UI, modeless components should decrement 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. Such components will typically query their environment via the NonModalAllowed property of VBs App object to see if modeless operation is supported. If it is then the components UI will be modeless; otherwise the component should gracefully degrade to modal presentation of its UI. Any component that is written to be modeless should be able to gracefully degrade to be modal. In either mode, the component must manage its COM-based lifetime properly.

6.6 Setting Proper Version Compatibility


Before you begin debugging your component, youll need to change its version compatibility settings. 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 the original. Creating a reference copy is important since VB wipes out the previous target executable in its build process. This way the reference remains constant with respect to its interfaces

Documentum, Inc.

Page 55 of 78

Developing Documentum Desktop Client Components

8/9/99

(contracts with its clients) while the underlying implementation of your component interfaces is free to evolve. So why is binary compatibility so important? Turning on this feature of VB enables VB to automatically warn you when you are about to produce an executable that breaks one or more established interfaces. Think of an interface as a contract between a client and an implementation. As soon as there is at least one client of an interface, if the interface changes, that client can no longer function if the interfaces changes without changing modifying and rebuildingitself. A contract has been broken. Having binary compatibility set tells VB that you don't want to blindly break contractsyou'd like to keep your clients in business. That is, binary compatibility helps to ensure backward compatibility. Dan Applemans text referenced in Appendix B contains valuable information regarding version compatibility. Please consult its fifth chapter to learn more about this important topic.

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 primary example parent is Windows Explorer where the DLL loading front door, if you will, is the Documentum shell namespace extension, also known as the Windows shell integration. The Documentum 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). The Documentum 4i Windows shell integration, for example, can cause one or more COM-based desktop client components to be instantiated and manipulated through a common COM interface, IDcComponent. Desktop client functionality has been "componentized," so that ideally 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 customization in mind and are written in VB. VB is a higher level language that allows a component developer to spend more time solving business problems rather than writing COM-based infrastructure to enable custom business solutions. Regardless of language, any custom business 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 not provide the necessary facilities for run-time debugging of a component within its desktop client environment. As a result I recommend a two-phase approach to component debugging: design-time testing within the VB IDE using both (1) a simple test application and (2) Documentum Desktop Client software, and run-time testing within the Developer Studio IDE of VC++ using Documentum Desktop Client software.

7.1 Testing Design-Time Behavior with Visual Basic


Debugging within the VB IDE is considered a design-time activitythe debug process is interpretive. During design-time you should perform both unit testing and integration testing, in that order.

Documentum, Inc.

Page 56 of 78

Developing Documentum Desktop Client Components

8/9/99

To unit test within the VB IDE youll need to create a project group which contains your component ActiveX DLL project and a test harness Standard EXE project. Some of the standard Documentum desktop client components that may be distributed as VB source code 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 many projects as you wish into the current environment. For your convenience, you can also define project groups (.VBG files) which simply act as a list of projects that can be loaded as a group. You can still load or save the projects individually since the projects remain independent. However, it is easier to define a project group than to have to add each project 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 project window highlighting. If you look at an object in the object browser what you see will depend on the project selected. To create a separate component test application project and make it part of a project group which 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 and frmDcTestComp, respectively). 3. Go to Project | References and reference your component housing. Add references to any other required COM servers. 4. Add the standard preamble to your main form. 5. Although not required, its a good habit to define your project properties beyond whats provided by default (e.g. Project | DcTestComp properties). Instructions for this are provided 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 the following name: Dc<function>.vbg (e.g. DcProperties.vbg). 7. In the Project window select your test application project, right-click and select Set as Start 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 to anticipate every kind of component written.) Remember that as long as your test application project is running in VB, its reference to your component project will prevent you from making your component housing. Fortunately your two projects are still totally independent in terms of packaging. Using File | Open Project its fairly easy to bounce back and forth between your component project (to build your component housing) and your component project group (to test your component). Be sure to save your work between jumps. Test your component through your project group once your component has been successfully built. Youll be able to step through both your test application and component source code line-by-line. Debugging a Documentum desktop client component within the VB IDE is no different than debugging any other piece of VB code. You should test your component thoroughly using Documentum Desktop Client software. I strongly recommend that you proceed from unit testing to integrated testing, rather than

Documentum, Inc.

Page 57 of 78

Developing Documentum Desktop Client Components

8/9/99

distributing your new component to your enterprise immediately following a successful unit test. You may pass integration with flying colors, but you wont know until you try. Better to fail integration on your local desktop than to cause your entire company to suffer from a less-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 Component Dispatcher with your component. Once youve registered your component with DDS, you can use VB6 to debug your component using Windows Explorer. To do this, youll need to modify your component projects settings as follows: 1. Go to the project properties dialog and select the Debugging tab. (Note: This tab is not available 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, since you wont 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 be created. Within this window, drill down on the Documentum shell namespace extension to an object of interest (e.g. a document in a particular Docbase cabinet or folder), select it and choose the function which exercises your component as defined in your DocApp. If your DocApp is setup correctly, you should be able to debug your component as soon as Component Dispatcher calls its IDcComponent methods. Therefore you may want to set breakpoints 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 VB through the current specification of IDcComponent, VB only supports design-time debugging. In order to thoroughly test the run-time behavior of your component, you must use Visual C++ (VC++). Once youve registered your component with the Documentum system (via Documentum Developer Studio) so that DART in turn can provide Component Dispatcher with your component, you can debug your component in VC++ using, for example, Windows Explorer as your parent process. The following example of loading a compiled VB component and its source 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 to both 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, and OK 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).

Documentum, Inc.

Page 58 of 78

Developing Documentum Desktop Client Components

8/9/99

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 a normal program. You may also want to display variable information in the debug environment by going to the Tools menu, selecting Options, and then selecting the Debug 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 select Settings, and then click the Debug tab. In the Executable for debug session TextBox specify the .EXE file that references your DLL. That is, select Windows Explorer via the arrow (browse) button off to the right (e.g. C:\WINNT\Explorer.exe). The connection between Windows Explorer and in this case the standard Docbase object-based properties component is through Documentums 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. When your VB compiled component reaches a line you set a breakpoint on (e.g. you select Properties on a document in a Docbase), it will stop in VC++ and allow you to perform normal debug operations. Note that VC++ doesnt recognize VBs Debug.Print statements. 8. If your component isn't invoked at all, verify that it is properly registered with the Documentum system (via Documentum Developer Studio) so that DART in turn can provide Component Dispatcher with your component. When following the procedure outlined above from KB article Q166275, there is a restriction in the sense that you can only see the value of local variables. For example, you will not see the value of globals declared in .BAS modules, or variables declared at the form or class module level. To see non-local variables requires some additional effort. Basically what you need to do is to find out the address of these variables. Once you have the address you can use the memory window and watch the content of the memory to get the value of the variable. 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 for programmers 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 your application that will provide you with the address you need. You can use an undocumented function that is available in VB for this purpose, called VarPtr (Pointer to a Variable). This function returns the address of a variable in VB. This is how you use it: Dim TheAddress as Long Dim MyVariable as Double TheAddress = 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:

Documentum, Inc.

Page 59 of 78

Developing Documentum Desktop Client Components

8/9/99

MyAddress = VarPtr (MyGlobalVariable) When you break into the code you already have the address available. Just go to the memory window and check out the address. If you need to watch several functions you could just add some equivalent code in an Initialize event and make a note for the needed addresses, as the globals are static and don't change their address. Another point to consider is that you actually don't need the address for each of your variables declared in a .BAS module. It is enough to get the address for the first one, as they are all one after the other. There are two types of variables that need special attentionarrays and strings. Arrays in VB are SafeArrays. If you want the address of the SafeArray structure itself, you need to call the undocumented VarPtrArray function. Just add the following declaration to a .BAS file: Declare Function VarPtrArray Lib "msvbvm50.dll" Alias VarPtr (Var() as Any) as Long To use it: Dim ArrayAddress as Long Dim 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 VarPtr function, 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 the array can change with dynamic arrays. To get the address of the first element of the array do: ElementAddress = VarPtr (ArrayOfLongs(0)) Strings in VB are stored as BSTR which ultimately resolves to be a wchar*. If you use VarPtr on a string variable you get the address of the BSTR, or in other words a wchar**. To help you with this there is another undocumented function, called StrPtr that returns the address of the first character of a string. To use: Dim MyCharAddress As Long Dim 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 KB article Q166275. The information in this article applies to both VB and VB6. You can get KB articles from the Microsoft Web site or through email. To get them through email just send an email to mshelp@microsoft.com and in the subject write the articles ID, in this case Q166275. The hyperlink for Q166275 is http://support.microsoft.com/support/kb/articles/q166/2/75.asp?FR=0. You can also find

Documentum, Inc.

Page 60 of 78

Developing Documentum Desktop Client Components

8/9/99

additional information concerning cross-language debugging in the May 1997 issue of Microsoft Systems Journal (or MSJ for short) article on the subject. When debugging arrays, strings and variants it may be useful to understand what is behind these 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 Extending Visual 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 solve your business problem. Rather than creating a new component from scratch you may decide to tweak an existing component insteadone of ours or one of your own. All of the previous content in this document applies to modifying an existing component. Heres is what you need to do to reuse an existing component project: 1. 2. 3. 4. Create a new folder to contain a copy of the component project of interest. Copy the component project files of interest into the new folder. Open the new folder and launch the copied component project. 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) will point 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 changes for the new component. 8. Delete the unnecessary files from the new folder (i.e. those with file names from the original component). We only want the files from (7). 9. Expose any new or modified public interfaces to your component. IDcComponent must be 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 components 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 doesnt have to be the case. Desktop client components are simply COM objects that implement at least the IDcComponent interface. Documentum Desktop Client software is not concerned with a components housing. Components may be housed in DLLs or EXEs. So your target and/or source VB project may be ActiveX EXE projects.

9 Deploying Desktop Client Components

Documentum, Inc.

Page 61 of 78

Developing Documentum Desktop Client Components

8/9/99

So far everything that weve talked about involves your own development machine. However, for an end user to cause a desktop client component to be dispatched, the component must exist on his machine, not yours. How do you deploy your new or customized desktop client component to the rest of your enterprise? This section will answer that question. The answer assumes that you are familiar with Documentum Developer Studio (DDS) and DocApps. Section 5.4 and Figure 22 both reveal that there are three key pieces of client-side software that 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 Cabinet Manager rely on the specification of a component in a Docbase through a package known as a DocApp. DocApps contain dm_qual_comp and dm_component objects, the former representing component invocation contexts and the later representing component attributes and, if specified, content. DDS is used to define and configure DocApps. Figure 37 shows this flow graphically from developer machine to end user machine.
Developer machine Server End user machine Discover Define DDS Docbase Deliver & register Cabinet Manager

DART Component Dispatcher

Instantiate & manipulate

Desktop Client Component

Figure 37. Physical Desktop Client Component Flow Diagram Standard desktop client components are installed on an end users machine through the Documentum Desktop Client installer (SETUP.EXE). That is, when launched, the installer will copy component executables locally and then tell the executables to register themselves with the local system. A desktop client component must be recognized by the local system in order for the Component Dispatcher to instantiate it and manipulate it (i.e. call its IDcComponent methods). The Component Dispatcher will only instantiate components for which it receives a valid COM CLSID back from DART. In order to return a CLSID, DART must query a Docbase for a qualified component that matches the end users functional request. Without a DocApp in the Docbase of interest, no match can be made and no component will be dispatched. New or customized desktop client components are provided to end users through a process known as Dynamic Component Delivery (DCD). After a match has been made in the Docbase 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 local system. In the case of a standard, unmodified desktop client component, the component is installed, so the Component Dispatcher can immediately instantiate the component through its CLSID. If the component is not installed locally or the version installed locally is older than the component in the Docbase, DCD then retrieves component content (i.e. a cabinet file) from the Docbase and uses a COM-based technology known as Internet Component Download (ICD) to install the component from the cabinet file. ICD-based installation is similar to installer-based installation in that, once complete, the component executable has been copied to the local machine and registered with the local system. The Component

Documentum, Inc.

Page 62 of 78

Developing Documentum Desktop Client Components

8/9/99

Dispatcher can now instantiate and manipulate the brand new component as it would any other 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 DCD quite simple, while other that do specify content make DCD more involved. Well look at both kinds of components in section 9.3.

9.1 Creating Component Content


The Documentum Cabinet Manager (DCCABMGR.DLL) provides three system objects to manage the creation and installation of desktop client component cabinet (.CAB) files: DcCabCreator, DcCabInstaller and DcCabResolver. When you add a component cabinet to a DocApp via the DDS Component Editor, DDS uses a Cabinet Resolver object to extract information from the component cabinet file in order to set dm_component attributes (i.e. com_class_id (ComponentCLSID), component_version (ComponentFileVersion)) and uniq_cont_ticket (CabinetCRC propertys value). On the download side of the process, the Component Dispatcher becomes a client of a Cabinet Installer object if DART reports that the desired component needs to be installed locally. In general your code shouldnt need to interface directly with either DcCabResolver or DcCabInstaller. However, you may wish to interface with a Cabinet Creator object to create your component content. Figure 38 shows what the Cabinet Creator looks like in VBs Object Browser.

Figure 38. Looking at the Cabinet Manager Type Library in VBs Object Browser The Cabinet Creator provides an interface to specify the primary component and any dependencies that should be placed into a cabinet (.CAB) file. If your component has no dependencies besides those that are already provided to an end user via the standard desktop client installation, it is recommended that you use the Cabinet Creator to generate your component content. Using the Cabinet Creator in this case is straightforward as shown in the following sample VB code. This code assumes that youve established a reference to the following type library: Documentum Cabinet Manager 1.0 Type Library (DCCABMGRLib) and requires that you have your component and any of its dependencies correctly registered on your development machine (i.e. the machine from where this code is run).

Documentum, Inc.

Page 63 of 78

Developing Documentum Desktop Client Components

8/9/99

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 ExitRoutine HandleError: MsgBox "Failed to create component cabinet" ExitRoutine: 'single exit point cmdExit.Enabled = True End Sub

Figure 39. VB Sample Code Using the Documentum Cabinet Manager to Create Component Content While the Cabinet Creator can certainly create component cabinets that contain component dependencies (e.g. Documentum ActiveX controls, third party ActiveX controls or executables), if your component is written in VB, you may choose to employ VBs 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 intermediate files generated by the wizard. For example, if your VB component project contains more than one class module (as many of the Documentum projects do), you may need to edit the INF file in the Support folder to correctly specify the main component (i.e. its name and information, 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 Windows Explorer. In order to avoid content bloat in your Docbase, remove all dependencies found by the wizard that are already provided by the standard desktop client installer. Thus far weve talked about a single type of dependency, the kind that exists inside a single cabinet or an internal dependency. You may also specify dependencies between cabinets in a virtual document fashion (external dependencies). For example, you may find that several of your custom components all use the same set of Documentum or third party ActiveX controls not already provided with the desktop client. You can create a cabinet to contain all of 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 the amount of component-related content in your Docbases. Please adhere to and/or be aware of the following rules regarding dm_component objects and their content (i.e. cabinet files). 1. Be sure to increment your components version number as mentioned previously in section 6.1. The version of a component in a DocApp must be greater than the locally installed version of the same component in order for DCD to automatically provide the new component to the end user. Proper versioning also applies to all (internal)

Documentum, Inc.

Page 64 of 78

Developing Documentum Desktop Client Components

8/9/99

dependencies contained in your component cabinets. This requirement is levied by Microsofts ICD technology. 2. Maintain backward compatibility among all versions of the same component. By same I mean equal GUIDs (i.e. CLSIDs and IIDs). The desktop client system marches forward not backward with respect to file version. Consider the following example. Your PC MyComp.dll (v1) Docbase A MyComp.cab (MyComp.dll (v2)) Docbase B MyComp.cab (MyComp.dll (v3))

3.

4. 5.

6.

With DCD enabled, you go to Docbase A and request a function that MyComp.dll implements. The system determines that you need v2 of this component and installs it from 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 either Docbase A or Docbase B, you'll be running v3 of MyComp.dll. If you modify a component such that it is no longer backward compatible, you must establish at least a new CLSID for the component using VB, for example, and then reflect the new CLSID in a DocApp using DDS. You should also change the name of your component to avoid overwriting older components on an end users machine. Components are related to specific versions of their dependencies. Dependencies should not be set up in such a way that a component could end up depending on itself. DDS wont allow you to create a cycle in the component dependency graph. 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 required after your components cabinet is installed.

9.2 Signing Component Content


Once youve created a cabinet file containing your desktop client component and any software dependencies it might have, you should sign the cabinet. Properly signed cabinets inform the system about to install the cabinets contents that the contents come from a source that can be trusted. Software that Documentum provides in the form of cabinet (.CAB) files should be signed, letting you know that our software is genuine and trustworthy in your enterprise. Briefly, here is how to sign a cabinet file. As an example, I will assume that youve customized the standard Documentum Docbase object-based Properties Component, DcPropertiesComp.dll, and have packaged it into a cabinet file. The following steps assume that you have already acquired both a private key (.PVK) file and a Software Publisher Certificate (.SPC) file through a provider like VeriSign and that signing will occur in a Microsoft (Authenticode) environment (i.e. not Netscape). 1. If your cabinet wasnt created by either the Documentum Cabinet Creator or VBs Package & Deployment Wizard, prepare your cabinet file to be signed by allocating space for signing using CABARC.EXEs "-s" option as follows: cabarc -s 6144 ... . This will allocate 6144 bytes for a digital certificate. CABARC.EXE is an application provided by Microsoft (e.g. in its Java SDK) to create and work with cabinet (.CAB) files. Both the desktop 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 allocated space for a digital certificate.

Documentum, Inc.

Page 65 of 78

Developing Documentum Desktop Client Components

8/9/99

2. You can now sign your cabinet. To do so, you will use the SIGNCODE.EXE utility included in the Microsoft Internet Client SDK. SIGNCODE.EXE requires that SIGNER.DLL also be present somewhere in your PATH. You'll need to make sure that you're using the right signing tools for the version of Internet Explorer (IE) that youre running on your development machine. a. IE 4.x Internet Client SDK http://msdn.microsoft.com/downloads/tools/authcodeie4/authcodeie4.asp b. IE 5.x Internet Client SDK http://msdn.microsoft.com/downloads/tools/authenticode/authcode.asp To sign with a SPC file, the required options are -spc and -v if your private key is in a PVK file. For the next step, youll need to have these two files handy. 3. SIGNCODE.EXE should be used from a Command Prompt window. Here is an example of how 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 the description of the file that will show up in the certificate. b. http://www.documentum.com is a URL where the user can find more information about the file being downloaded (or least more information about Documentum in general). c. CredentialsForDCTM.spc is the Digital ID file (i.e. Software Publisher Certificate) that was 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." Heres an important comment on timestamps from VeriSign: Because key pairs are based on mathematical relationships which can theoretically be cracked with a great deal of time and effort, it is a well-established security principle that digital certificates should expire. Your VeriSign Digital ID will expire one year after it is issued. However, most software is intended to have a lifetime of longer than one year. To avoid having to resign software every time your certificate expires, VeriSign and Microsoft introduced a timestamping service. Now, when you sign code, a hash of your code will be sent to VeriSign to be timestamped. As a result, when your code is downloaded, clients will be able to distinguish between: (1) Code signed with an expired certificate, which should NOT be trusted, and (2) Code signed with a certificate which was valid at the time the code was signed, but which has subsequently expired. This code SHOULD be trusted. This means that you will not need to worry about resigning code when your Digital ID expires. VeriSign is the only certification authority offering the time stamping service. This service is free to all VeriSign Commercial and Individual Software Publisher ID customers. Documentum software that is provided in the form of a signed cabinet file is timestamped. This allows our customers to trust such software indefinitely in the unlikely event that Documentum allows its Software Publisher Certificate with VeriSign to expire. f. DcPropertiesComp.cab is the name of the file that needs to be signed--in this case a desktop client component cabinet.

Documentum, Inc.

Page 66 of 78

Developing Documentum Desktop Client Components

8/9/99

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 you specified 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 by Internet Explorer), it will display the same certificate to the user. If the file is tampered with in any way after it has been signed, the user will be notified and given the option of refusing installation. The focus of this document is not on the subject of code signing. Microsoft has published a number of useful articles in their MSDN Library that should be consulted if more details are desired. 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, youre essentially telling the system that your component has already been installed and registered locally, and is not expected to be updated from the current version. That is, if a desktop client component is specified in a Docbase in terms of attributes only and no content, it must already be installed locally on an end users machine for that user to access its functionality. An example 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 build_technology com_class_id component_version i_contents_id uniq_cont_ticket Meaning Type of language and/or the mechanism for building the component COM CLSID of the component FileVersion of the component If set, ID to file system content (i.e. a component cabinet file) 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 Component Delivery In light of Figure 40, a proper component-without-content definition is shown in Figure 41. Attribute build_technology com_class_id Value 1 (i.e. TECH_ACX ActiveX (COM) technology has been used to build the component) A valid 38-character hexadecimal string, including dashes and enclosing braces (e.g. {57DA2981-9A05-11D2-80FA00105A1F0288} for DCPROPERTIESLib.DcPropertiesComp within DcPropertiesComp.dll) nullstring since there is no content in the Docbase, the locally installed version of the component is considered to always be current

component_version

Documentum, Inc.

Page 67 of 78

Developing Documentum Desktop Client Components

8/9/99

i_contents_id uniq_cont_ticket

nullid (i.e. 0000000000000000 for no content) 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 has content. When you define a component in a DocApp with content, youre telling the Component Dispatcher that it should be using this version of the component unless a newer, backward compatible version of the same component is identified by DART (i.e. already installed locally). Such components do not have to be pre-installed by an installer (e.g. Documentum Desktop Client installer). Instead they are installed dynamically by the Cabinet Installer the first time an end user requests functionality implemented by the component. Examples of this kind of component are customizations of standard desktop client components or entirely new components used by a custom DocApp. In light of Figure 40, a proper component-with-content definition is shown in Figure 42. Attribute build_technology com_class_id Value 1 (i.e. TECH_ACX ActiveX (COM) technology has been used to build the component) A valid 38-character hexadecimal string, including dashes and enclosing braces (e.g. {57DA2981-9A05-11D2-80FA00105A1F0288} for DCPROPERTIESLib.DcPropertiesComp within DcPropertiesComp.dll) A valid FileVersion string taking the form #,#,#,# (e.g. 4,0,0,500) A valid object ID for content object on the server file system (i.e. a non-nullid pointing to the r_object_id attribute of a dmr_content object) A valid 8-character hexadecimal string representing the CRC-32 value calculated for the associated content

component_version i_contents_id

uniq_cont_ticket

Figure 42. dm_component Attribute Values for Components with Content Use DDSs Component Editor to associate a component with a cabinet file. First load the DocApp of interest within DDS. Double-click on the red cube icon of the component for which you will specify a cabinet, and choose Edit. If necessary, remove any previously specified content then choose Add. Browse to where the cabinet exists, select it and establish a link to it. Doing so will automatically update the Class ID field on the Component Editor dialog. DDS actually uses the a Cabinet Resolver object to automatically define the following dm_component attributes so that you dont have to manually: com_class_id, component_version and uniq_cont_ticket. Exit the Component Editor dialog and check-in your component changes. Please be aware of the current relationship between a dm_qual_comp and a dm_component in a DocApp, which is as follows:

Documentum, Inc.

Page 68 of 78

Developing Documentum Desktop Client Components

8/9/99

In the current release of DDS, references between dm_qual_comp and dm_component objects are version specific (i.e. early bound). That is, whenever you check-in a dm_component modification, the references between the modified dm_component and its associated dm_qual_comp objects are left unchanged. Checking in your update as Same Version allows all existing references between the modified 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 use the 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 do nothing more. Doing nothing, however, wont allow your new component to be recognized by DART. DART will continue to identify the older version of the component as referenced by a dm_qual_comp object. This relationship applies regardless of whether or not a dm_component object has associated content.

9.4 Deploying Component Content


Now that you have created a cabinet, signed it and stored it in a Docbase, lets talk more about what happens on the end users machine the next time a functional request is made that is satisfied by the component inside your cabinet. Previously, section 9 and Figure 37 revealed the key players on an end users machine in terms of dynamically deployed component content. Lets assume that DART determines that your updated component has yet to be installed on the end users machine. The Component Dispatcher will then ask the Cabinet Installer to first download the specified dm_component object content from a Docbase and then to install that content using Microsofts COM-based technology known as Internet Component Download (ICD). Windows Explorer provides a nice user interface for all content installed via ICD, a feature that Microsoft introduced with Internet Explorer 3.0. Since DCD leverages ICD to install component cabinets on your PC, DCD-derived content appears in the same UI, shown later in Figure 44 of section 9.5. If you select DCD content within the Downloaded Program Files folder and right-click, you'll receive a context menu with three options: Update, Remove and Properties. Choosing Properties will present you with a tabbed dialog containing general, dependency and version information about your selection. Right-clicking on the selection in Figure 44 and choosing Properties, would present you with the dialog shown in Figure 43.

Documentum, Inc.

Page 69 of 78

Developing Documentum Desktop Client Components

8/9/99

Figure 43. ICD Properties Dialog from Windows Explorer The CodeBase value on the General tab reveals that DCD uses the Downloads folder under the folder specified by the registry value HKEY_LOCAL_MACHINE\SOFTWARE\ Documentum\Common "UserDirectory"= "C:\Documentum" to download component cabinet files from a Docbase. The ID value is the components COM CLSID. The Dependency tab lists the dependencies contained in the specified cabinet file and the Version tab displays common version information about the primary content of the specified cabinet (i.e. its desktop client component). Choosing Update is essentially a no-op, since you already have the latest content on your PC. The Remove option will be covered later in section 9.5. Although the Documentum Desktop Client should automatically cleanup all intermediate file content generated by DCD and ICD, you should be aware of exactly what gets installed where on your system when leveraging DCD. Here is a list of what is affected on your file system. This list assumes that you develop on a Windows NT 4.0 machine and have installed 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. overwrite the file). If, for example, you customize the properties component and use DCD to install your customization, ICD will find DcPropertiesComp.dll to be registered in the shared folder under the location specified by the registry value HKEY_LOCAL_MACHINE\SOFTWARE\Documentum\Common "ProgramDirectory"= "C:\Program Files\Documentum. " (ICD is able to locate the server using the component CLSID stored in the com_class_id attribute of your component cabinets dm_component object.)

Documentum, Inc.

Page 70 of 78

Developing Documentum Desktop Client Components

8/9/99

2.

3.

4.

5.

Once complete, DCD leaves this file intact until a newer version of the same component is identified by the system or you choose to uninstall DCD-based content (see section 9.5). C:\Documentum\Downloads\<object ID>\<object ID>.cab Object ID is the sixteen character hexadecimal string value of the dm_component objects 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 Figure 43, are historical in nature (i.e. they shouldnt point to real content). 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. C:\WINNT\Downloaded Program Files\Component.inf This is the physical file from which Windows Explorer is able to generate its logical view shown in Figure 44. Once complete, DCD leaves this file intact until a newer version of the same component is identified by the system or you choose to uninstall DCD-based content (see section 9.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 a number of useful articles in their MSDN Library that should be consulted if more details are desired.

9.5 Testing Dynamic Component Delivery


Desktop client component deployment can be broken down into discrete test activities. By breaking down your testing, it will become clearer what exactly is involved in a successful deployment, and it will be more likely that your testing will be successful in a shorter amount of time. For example, test the delivery of your component from a Docbase as a final step. First, test the dispatching of your component. To adequately test the dispatching of your component, youll need access to a Docbase and two machinesone that you use for development and one that is representative of an end user environment. Its important that you test on two machines so that you can identify all of your components system dependencies. That is, your component may run just fine in your development environment but fail to run on an end users machine unless you deliver the necessary dependencies to your end user along with your component. Once passed, this test will verify that future problems with registration and instantiation are truly delivery problems. The following high-level process is recommended to test dispatching a new or customized component: 1. Using DDS, specify your component in a DocApp without specifying any content (i.e. attributes only). Youll have to manually specify your components CLSID in the DDS Component Editor while in Edit mode. A safe way to do this without a lot of typing is to use OLEView. Drill down on your component by looking for its ProgID in the tree pane on the lefthand side (e.g. DCPROPERTIESLib.DcPropertiesComp). Select your component ProgID, right-click and choose Copy CLSID to Clipboard. Paste this value into the Class

Documentum, Inc.

Page 71 of 78

Developing Documentum Desktop Client Components

8/9/99

ID field of the DDS Component Editor. Check-in your changes, carefully considering details provided toward the end of section 9.3 regarding the current relationship between dm_qual_comp and dm_component objects. 2. Use the Documentum Desktop Client or your own test application to dispatch your new component. Be sure to specify the correct application name if it is not the default DocApp (i.e. DcDesktopClient). If the dispatch is successful proceed with the next step. 3. Copy your component executable and register it on your second machine that represents a typical end user environment (e.g. one with only the Documentum Desktop Client installed but no version of DDS). If your component fails to register, you may be missing one or more of its system dependencies. Maintain a list of all such dependenciesyoull need this list to complete an upcoming deployment step. Once your component is successfully registered, use the Documentum Desktop Client or your own test application to dispatch your new component on your end user machine. If your component isnt dispatched, chances are that there are more dependencies missing. Resolve all dependencies to the point where end user dispatching occurs successfully before proceeding further. Youve now successfully tested instantiation of your component and are ready to test its delivery from a Docbase to an end user machine. You may also have a list of dependencies that must be resident on an end user machine in order for your component to function properly (e.g. one of the Documentum ActiveX controls). The following high-level process is recommended to test delivering a new or customized component. It assumes that one machine is used for development and a different machine is used for testing. 1. Build and register your component, making sure to increment its version if it implements the same CLSID as another component already in the system. 2. Create a cabinet file containing your component, as described in section 9.1. Any cabinet that follows ICD guidelines published by Microsoft is acceptable; however, youll probably 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 current relationship 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 be replaced by DCD. For example, create an original folder under your Documentum shared folder and copy the component binary (executable) there. Creating this copy will 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.

Documentum, Inc.

Page 72 of 78

Developing Documentum Desktop Client Components

8/9/99

9.6 Uninstalling Dynamic Component Content


By default, the Documentum Desktop Client installer (SETUP.EXE) will enable DCD. If during its lifetime on a desktop machine the Documentum Desktop Client dynamically updates one or more of its components via DCD, extra steps must be taken manually when it comes time to uninstall the Documentum Desktop Client. These important steps are included in the release notes for the Documentum Desktop Client and are repeated here for easier reference. 1. Before going to Add/Remove Programs in the Control Panel, launch Windows Explorer and go to C:\WINNT\Downloaded Program Files on NT4 or C:\Windows\Downloaded Program Files on Win9x. These are the nominal, default paths for Internet Component Download (ICD) content. DCD uses ICD to perform its task. The actual ICD content path is 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 the special 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 Windows Explorer content pane, select it, right click and choose Remove. Please note that if you haven't requested any DCD-provided functionality, there will be nothing to do in this step. Standard Documentum desktop client components as well as any custom components 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 Documentum desktop client components whose source code is provided through Documentum Developer Studio (DDS). It is recommended that your enterprise also publish a list of its desktop client component ProgIDs (or other identification if a language other than VB is used) so that uninstalling DCD-derived content becomes completely deterministic rather than a guessing game. ProgID CLSID DCCANCELCHKOUTLib.DcCancelCheckoutComp {3F1C2727-9A04-11D2-80FA-00105A1F0288} DCCHANGEPASSWRDLib.DcChangePasswrdComp {213D48F1-BEFD-11D3-BC63-0050DA681DC7}

Documentum, Inc.

Page 73 of 78

Developing Documentum Desktop Client Components

8/9/99

ProgID DCCHECKINLib.DcCheckInComp DCCHECKOUTLib.DcCheckOutComp DCCOPYLINKMOVELIB.DcCopyLinkMoveComp DCDOCLIFECYCLELib.DcDocLifecycleComp DCIMPORTLIB.DcImportComp DCNEWLib.DcNewObjectComp DcOpenDocumentLib.DcOpenDocumentComp DCPASTEASRENDLib.DcPasteAsRendComp DCPDFRENDITIONLib.DcPdfRenditionComp DCPROPERTIESLib.DcPropertiesComp DCSENDMAILLib.DcSendMailComp DCVDMPUBLISHLib.DcVDMPublishComp DCWFAVAILLib.DcWFAvailabilityComp DCWFOPENLib.DcWFOpenComp DCWFROUTEREDITLib.DcWFRouterEditorComp DCWFROUTERMGRLib.DcWFRouterMgrComp DCWFSENDTODISTLib.DcWFSendToDistComp DCWFSTARTLib.DcWFStartComp DCWFSTATUSLib.DcWFStatusComp DCWFTASKMGRLib.DcWFTaskMgrComp Documentum.DcCheckInAsNewDocumentComp Documentum.DcNewFromDocbaseTemplateComp Documentum.DcOpenFromDocbaseComp

CLSID {C0B5BE13-9A04-11D2-80FA-00105A1F0288} {17F5A1BE-9A05-11D2-80FA-00105A1F0288} {4011F503-C946-11D3-A4E7-000064657374} {3DBDF18C-9D00-11D2-A943-00105A27AE7A} {7C716F4A-B74C-11D3-ADCB-00104B59949F} {F507C1B2-4439-4B9B-A9A5-373C6CA50AA3} {09FB3844-C1FB-11D3-8898-006008AC2DE7} {C8AD996E-C884-11D3-BC6E-0050DA681DC7} {BEB9E838-C2DE-11D3-BC69-0050DA681DC7} {57DA2981-9A05-11D2-80FA-00105A1F0288} {0D66A089-E88B-11D3-BCA9-0050DA681DC7} {3E984F87-E939-11D2-BD04-00104B2B9960} {68B3AD0D-28E8-11D3-81CF-00105A1F0288} {126719DD-28EA-11D3-BEAD-00105A24DE5C} {F9E86695-28ED-11D3-BEAD-00105A24DE5C} {8DD71AB8-28F1-11D3-BEAD-00105A24DE5C} {403E81D4-28F4-11D3-BEAD-00105A24DE5C} {56B3F3E6-14C1-11D4-B5C7-00105A9F55BD} {B8239E1A-28F6-11D3-81CF-00105A1F0288} {56A5A372-28EB-11D3-81CF-00105A1F0288} {5A1D0B78-2324-11D2-9217-00104B59949F} {5A1D0B76-2324-11D2-9217-00104B59949F} {5A1D0B72-2324-11D2-9217-00104B59949F}

Figure 45. Identifying Standard Documentum Desktop Client Components Whose Source Code Is Provided via DDS 4. Verify that your Downloads folder (e.g. C:\Documentum\Downloads) is empty. Delete any files and folder that you may find there. The actual Downloads folder path is determined 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 or C:\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 to Add/Remove Programs in the Control Panel and remove Documentum Desktop Client 4.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 Control Panel, reboot your machine.

9.7 Configuring Dynamic Component Delivery


The Documentum Desktop Client installer will automatically enable DCD. In the unlikely event that you will need to disable DCD, you can do so be removing the following key from your registry: HKEY_LOCAL_MACHINE\SOFTWARE\Documentum\Dynamic Component Delivery. The next time the Component Dispatcher is asked to dispatch a component in order to satisfy a functional request, it will try to do so using only components that are already locally installed. If no local component can satisfy the request, the request will fail.

Documentum, Inc.

Page 74 of 78

Developing Documentum Desktop Client Components

8/9/99

To restore DCD to its default active state, add back the HKEY_LOCAL_MACHINE\ SOFTWARE\Documentum\Dynamic Component Delivery key to your registry. Subsequent functional requests will be satisfied by the very latest component implementing the request function, whether that component already exists locally or needs to be installed from a Docbase. Since ICD is an asynchronous process by nature while component dispatching is not, an event is used to synchronize the ICD portion of DCD. So that the Component Dispatcher is not held captive by an errant ICD invocation, a timeout value is established while Component Dispatcher waits for the ICD complete event to be signaled. This timeout value is 15 seconds by default. You can customize this value by adding the following value to your registry: HKEY_LOCAL_MACHINE\ SOFTWARE\Documentum\Dynamic Component Delivery - "ICD Timeout." This value is specified in milliseconds; so 15 seconds should be entered 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 is intended to be moderate and useful. It will be followed for all Documentum standard desktop client components. Of course, custom components are free to use whatever code standard may already be in place. This appendix is intended to be a guide for custom development rather than an edict. It also sheds some light onto the rationale behind the look and feel of VB source code provided by Documentum. This particular coding standard is based on the writing of Deborah Kurata and Dan Appleman. Please refer to the bibliography for details about their published works in this area.

A.1 Commenting
Provide an outline of your code and describe your codes purpose in comments. Your comments should answer what? and why? but not how? The code itself provides the play-by-play. Comment for claritys sake. Each source module you create should have a standard preamble (comment header) at a minimum. To give you an idea of the kinds of information that is appropriate to include in a comment header, heres an example of the standard Documentum comment header. Note that Visual SourceSafe keywords are employed so that if Visual SourceSafe is used as a source 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: $ '********************************************************************************************

Documentum, Inc.

Page 75 of 78

Developing Documentum Desktop Client Components

8/9/99

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

A.2 Basic Code Structure


Make your code more readable and robust by applying some basic code structure guidelines: 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, its easier to find the declarations if they are all in the same location. Its also 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 Exit statement immediately before the error handler so that the error handler is not executed when no error occurs. You can also enforce a single exit point through the use of an additional label and GoTo statement, like many of the code samples in this document specify. 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 an application. If a variable is accessible to all routines in a project, it has global or public scope. If it can only be accessed by routines in a module, it has module-level scope. If only the routine can access the variable, it has local scope. It is best to use the smallest scope possible 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. Dont 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. Dont 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.

Documentum, Inc.

Page 76 of 78

Developing Documentum Desktop Client Components

8/9/99

Dont stop your application. When you use the End statement or Run | End (menu command or toolbar equivalent), you are telling VB to immediately stop all code execution. VB will proceed to clean up memory wherever it can. However, it will not execute any of your termination code. Not only does this prevent you from testing your termination code, you may find that some objects were not cleaned up properly. For example: if you have used API functions to create system objects, Visual Basic and Windows may not be able to clean up those objects when your application ends. Visual Basic definitely will not close or free those objects before you try to run the application again. Thus, you may find that a file you opened the first time fails on the next attempt due to a permission error or lock condition. As a general rule, within the VB environment, always close the application using the System menu on the main form or other exit mechanism that you've programmed into the application (e.g. unloading the main 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 procedure should 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 wont 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 (exception handler), the error will automatically be raised to the procedure that called it. If that procedure has no error trap, the error will again be raised to the next level. If no error trap is 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 track the exact specifications of the error without relying on a user to write down the information in a message box. VB provides an easy way to log information to the Windows NT event log or to a log file.

Documentum, Inc.

Page 77 of 78

Developing Documentum Desktop Client Components

8/9/99

A.6 Naming Conventions


This is a standards subject that doesnt lack for opinions. Please consult the MSDN Library dated 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 forth by Microsoft.

Appendix B: Selected Bibliography


1. 2. 3. 4. 5. Essential COM for VB Programmers, A White Paper by Ted Pattison, August 1997 (http://www.sublimnl.com/EssentialCom.htm ) Developing COM/ActiveX Components with Visual Basic 6: A Guide to the Perplexed, Appleman, SAMS, 1999, ISBN 1-56276-576-0 Doing Objects in Visual Basic 6.0, Kurata, SAMS, 1999, ISBN 1-56276-577-9 Visual Basic Books Online and MSDN Library Investigating Multilanguage Debugging and the New IDEs of Visual Studio 97, Microsoft Systems Journal, Schmidt, 5/97

Documentum, Inc.

Page 78 of 78

Anda mungkin juga menyukai