[an error occurred while processing this directive]

HP OpenVMS Systems

Content starts here COM, Registry, and Events for HP OpenVMS Developer's Guide

COM, Registry, and Events for
HP OpenVMS Developer's Guide


Previous Contents Index


Chapter 7
Developing a COM for OpenVMS Application

This chapter explains how to develop COM applications for OpenVMS.

Note

You can find the sample COM applications shown in this chapter in the following directories on the COM for OpenVMS kit:


DCOM$EXAMPLES:[SAMPLE1]
DCOM$EXAMPLES:[SIMPLE]
DCOM$EXAMPLES:[DISPATCH_SAMPLE1]

SAMPLE1 and DISPATCH_SAMPLE1 are taken from Dale Rogerson's book, Inside COM, published by Microsoft Press. This book is a good reference for developing COM applications.

The following sections describe how to create a COM for OpenVMS application.

Note

Building COM for OpenVMS applications places demands on the virtual memory requirements of a process. You should have a minimum page file quota of 100,000 pagelets before building a COM for OpenVMS application. This is a HP C++ compiler requirement.

7.1 Step 1: Generate Unique Identifiers

Use the DCOM$GUIDGEN utility to generate 16-byte globally unique identifiers (GUIDs).

For example:


  $ SET COMMAND DCOM$LIBRARY:DCOM$GUIDGEN.CLD
  $ DCOM$GUIDGEN [/FORMAT=value] -
  _$ [/COUNT=value] [/OUTPUT=value]

The following table summarizes the GUID format options.

OpenVMS qualifier (value) UNIX switch Use
IDL -i Output GUID in an IDL interface template.
STRUCT -s Output GUID as an initialized C struct.
IMPLEMENT_OLECREATE -c Output GUID in IMPLEMENT_OLECREATE(...) format.
DEFINE_GUID -d Output GUID in DEFINE_GUID(...) format.
GUID_STRUCT -g Output GUID as an initialized static const GUID struct.
REGISTRY_GUID -r Output GUID in registry format.

Note

The last four options in the preceding table are the same as the four options in the Windows Guidgen utility.

The following table lists additional options supported by the DCOM$GUIDGEN utility.

OpenVMS qualifier UNIX switch Use
/OUTPUT= filename -o filename Redirect output to a specified file.
/COUNT= number -n number Number of GUIDs to generate.
not available -h , -? Display command option summary.

You can specify more than one format for the same GUID.

7.2 Step 2: Build an Application Using the MIDL Compiler

The following sections describe how to use the MIDL compiler to build an application.

7.2.1 Running the MIDL Compiler

The MIDL compiler consists of the following separate images:

  • SYS$SYSTEM:DCOM$MIDL.EXE
    The executable image that takes its arguments (parameters) from the DCL command line.
  • SYS$SHARE:DCOM$MIDL_SHR.EXE
    A shareable image library that does the actual work for DCOM$MIDL.EXE .

To run MIDL, you must first define a DCL symbol. For example:


    $ midl :== $dcom$midl
    $ midl -?
    $ midl -Oicf -idcom$library: example.idl

The midl -? command displays a list of valid command-line arguments. For a list of these arguments, see Appendix A.

7.2.2 Running the MIDL Compiler with DCOM$RUNSHRLIB

The DCOM$MIDL.EXE utility gets its arguments from the DCL foreign command-line buffer. DCL foreign commands can have a maximum of 255 characters.

Because of the number of arguments that DCOM$MIDL.EXE can accept, you might exceed this maximum number of characters if you specify a complex MIDL command (for example, a command that contains mixed-case arguments that require quotation marks).

As a workaround, you can use the SYS$SYSTEM:DCOM$RUNSHRLIB.EXE utility. Use the following procedure:

  1. Define the DCL command DCOM$RUNSHRLIB .
    A process that needs to use DCOM$RUNSHRLIB.EXE must first use the OpenVMS DCL Command Definition utility to define the DCL command DCOM$RUNSHRLIB . For example:


      $ SET COMMAND DCOM$LIBRARY:DCOM$RUNSHRLIB.CLD
    

    DCOM$LIBRARY:DCOM$RUNSHRLIB.CLD defines the DCOM$RUNSHRLIB DCL command. The following table shows the command's parameters.
    Argument Value Required/Optional
    P1 Name of the shareable image library. This can be a logical name, the name of an image in SYS$SHARE: , or a full file specification. Required
    P2 Name of the routine to be called as a C or C++ main() routine with an argc/argv vector. Required
    P3 List of qualifiers, in quotation marks. Optional
  2. Define the DCL symbol midl to use DCOM$RUNSHRLIB.EXE to parse the command line and call the DCOM$MIDL_MAIN function in the DCOM$MIDL_SHR shareable image library. For example:


      $ midl :== DCOM$RUNSHRLIB DCOM$MIDL_SHR DCOM$MIDL_MAIN
    

    The new DCL command MIDL accepts multiple command-line arguments inside a single quoted string. If the command becomes too long, you can specify multiple quoted strings, using a comma to separate the strings.
    For example, here is a complex MIDL command that fails:


      $ midl :== $dcom$midl
      $ midl -Zp8 -Oicf -Os -oldnames -char unsigned -
         -error allocation -error bounds_check -error stub_data -
         -ms_ext -c_ext -out [.OBJ] -
         -I[INC] -I[PROJECT_WIDE_INC] -I[COMMON_INC] -IDCOM$LIBRARY: -
         -DRMS_DB "-DOpenVMS_Definitions" "-DPermanentProcess" -
         -header [.obj]example.h -client none -server none example.idl
      %DCL-W-TKNOVF, command element is too long - shorten
    

    You can successfully specify this command using DCOM$RUNSHRLIB as follows:


      $ set command dcom$library:dcom$runshrlib.cld
      $ midl :== DCOM$RUNSHRLIB DCOM$MIDL_SHR DCOM$MIDL_MAIN
      $ midl "-Zp8 -Oicf -Os -oldnames -char unsigned",-
         "-error allocation -error bounds_check -error stub_data",-
         "-ms_ext -c_ext -out [.OBJ]",-
         "-I[INC] -I[PROJECT_WIDE_INC] -I[COMMON_INC] -IDCOM$LIBRARY:",-
         "-DRMS_DB -DOpenVMS_Definitions -DPermanentProcess",-
         "-header [.obj]example.h -client none -server none example.idl"
    

7.2.3 Modifying Your Applications To Use the C++ Only MIDL Compiler

By default, the MIDL compiler included in COM for OpenVMS generates IDL files with a .CXX extension. If you previously specified names for the generated files in your MIDL command and want to use the COM for OpenVMS MIDL compiler changes, you need to update your command line.

For example, if you previously generated IDL files with the following command:


  MIDL -Oicf server.idl -IDCOM$LIBRARY: -dlldata DLLDATA.C

Change your command to the following:


  MIDL -Oicf server.idl -IDCOM$LIBRARY: -dlldata DLLDATA.CXX

If you want to continue to use the C compiler to build your generated files, simply specify them as .C files on your command line.

Note

If you previously accepted the default file names, and want to continue to use the C compiler to build the IDL files, you must specify every IDL file on the command line.

For example, if your MIDL command was similar to the following:


  MIDL -Oicf server.idl -IDCOM$LIBRARY: -dlldata DLLDATA.C

Change it to the following:


  MIDL -Oicf server.idl -IDCOM$LIBRARY: -dlldata DLLDATA.C -guids
       GUIDS.C -proxy SERVER_P.C

7.2.4 Required MIDL Switches

When running MIDL on OpenVMS, you must specify the -Oicf MIDL command-line switch.

7.2.5 Required Include Directories

MIDL components typically import UNKNWN.IDL , which contains the component definitions for IUnknown and IClassFactory . UNKNWN.IDL and other COM-related IDL and header files are located in DCOM$LIBRARY . To build your component's IDL file, use the following switch:


-IDCOM$LIBRARY:

7.3 Step 3: Compile the COM Application

The following sections describe how to compile COM for OpenVMS applications.

Note

COM application developers need access to the OpenVMS Registry to register and control access to a given application. For more information about OpenVMS Registry privileges, see Section 12.6.1 and Section 6.3.

7.3.1 Required Header File: VMS_DCOM.H

The VMS_DCOM.H header file contains macro definitions that enable your COM for OpenVMS application to compile properly. You must include this header file as the first uncommented line in every source file and header file you create.

The MIDL compiler for OpenVMS includes VMS_DCOM.H in all output files that it generates.

7.3.2 Required Macro Definitions

Be sure to always include the following /DEFINE qualifier in all of your CXX commands:


/DEFINE=(UNICODE,_WIN32_DCOM)

The UNICODE macro ensures that the wide character variants of Win32 APIs and data structures are enabled when you compile your code. (This macro is also defined in VMS_DCOM.H .)

The _WIN32_DCOM macro definition is recognized by the header files and is required to ensure the proper definition of structures and COM APIs.

7.3.3 Required Include Directories

COM for OpenVMS applications typically require header files that come from DCOM$LIBRARY .

Include the following qualifier in your C and CXX commands:


 /INCLUDE=DCOM$LIBRARY

If you already have an /INCLUDE qualifier in your command line, modify the command to include DCOM$LIBRARY .

7.3.4 Required C++ Qualifiers

You must specify the following C++ qualifiers when you build COM for OpenVMS applications:

  • /EXCEPTIONS=CLEANUP
    Specify the /EXCEPTIONS=CLEANUP qualifier on C++ commands to enable C++ exceptions.
  • /STANDARD=CFRONT
    The C++ compiler supports many different compilation standards. HP recommends that you use /STANDARD=CFRONT .
    /STANDARD=CFRONT informs the compiler that it should follow the language conventions defined in the AT&T cfront implementation.

7.4 Step 4: Link the COM Application

To build a COM for OpenVMS application, you must build both client and component images. Because you can implement a component as either an in-process component or an out-of-process component, you must build either a shareable image or an executable image, or both. If you are creating a new interface, you must also build a proxy/stub shareable image, unless you are using the IDispatch interface. In that case, the Automation Marshaler will be used instead of the proxy/stub shareable image. The proxy/stub shareable image provides an interface-specific object that packages parameters for that interface in preparation for a remote method call. A proxy runs in the sender's address space and communicates with a corresponding stub in the receiver's address space.

The following sections describe the steps you must follow to link the client, component, and proxy/stub images.

7.4.1 Linking the Client and the Out-of-Process Component

Although you do not need to specify any qualifiers to link the client or the component executable images, you must link both images with the DCOM OLE32 shareable image (to satisfy references to COM APIs).

The specific link-time dependency is DCOM$LIBRARY:DCOM.OPT .

If you have one or more C++ modules, use the C++ linker (CXXLINK) instead of the standard OpenVMS linker so you can specify the location of your C++ repository (/CXX_REPOSITORY qualifier). For example:


$  CXXLINK/your-specific-linker-qualifiers list-of-object-modules, -
_$ DCOM$LIBRARY:DCOM.OPT/OPTIONS -
_$ application.OPT/OPTIONS /REPOSITORY=[.CXX_REPOSITORY]

Other ways of including the options file are as follows:

  • Include the list of object modules in an options file instead of on the command line.
  • Use DCOM$LIBRARY:DCOM.OPT .

7.4.2 Linking the In-Process Component Shareable Image

The specific link-time dependency is DCOM$LIBRARY:DCOM.OPT .

7.4.2.1 Creating a Symbol Vector

Linking the in-process component shareable image requires that you create a symbol vector for the entry points that COM for OpenVMS expects to call within the shareable image. The Win32 run-time environment enforces a naming standard on the DllMain entry point, which must contain the following:

  • Actual entry point name
  • A suffix that includes the image name or a portion of the image name, depending on the format of the image name.
    If the image name ends in $SHR (for example, CMPNT$SHR ), the suffix is the image name up to and including the dollar sign ($).
    If the image name ends in anything other than $SHR (for example, CMPNT_SHARE ), the suffix is the full image name.

For example, a component shareable image with the name CMPNT$SHR would define the symbol vector using the following options file:


!
! The list of symbols exported by CMPNT$SHR.EXE.
!
SYMBOL_VECTOR=(-
        _DllMain_CMPNT$/DllMain    = PROCEDURE,-
        DllGetClassObject               = PROCEDURE,-
        DllCanUnloadNow                 = PROCEDURE,-
        DllRegisterServer               = PROCEDURE,-
        DllUnregisterServer             = PROCEDURE)

A component shareable image with the name CMPNT_SHARE would define the symbol vector using the following options file:


!
! The list of symbols exported by CMPNT_SHARE.EXE.
!
SYMBOL_VECTOR=(-
        _DllMain_CMPNT_SHARE/DllMain  = PROCEDURE,-
        DllGetClassObject               = PROCEDURE,-
        DllCanUnloadNow                 = PROCEDURE,-
        DllRegisterServer               = PROCEDURE,-
        DllUnregisterServer             = PROCEDURE)

7.4.3 Linking the Proxy/Stub Shareable Image

The specific link-time dependency is SYS$LIBRARY:DCOM$RPCRT4_SHR.EXE .

7.4.3.1 Creating a Symbol Vector

Linking the proxy/stub shareable image is more involved because you must create a symbol vector for the entry points that COM for OpenVMS expects to call within the shareable image. The Win32 run-time environment enforces a naming standard on the DllMain entry point, which must contain the following:

  • Actual entry point name
  • A suffix that includes the image name or a portion of the image name, depending on the format of the image name.
    If the image name ends in $SHR (for example, PROXY$SHR ), the suffix is the image name up to and including the dollar sign ($).
    If the image name ends in anything other than $SHR (for example, PROXY_SHARE ), the suffix is the full image name.

For example, a proxy/stub shareable image with the name PROXY$SHR would define the symbol vector using the following options file:


!
! RPC Shareable Image
!
 SYS$LIBRARY:DCOM$RPCRT4_SHR.EXE/SHARE
!
!
! The list of symbols exported by PROXY$SHR.EXE.
!
SYMBOL_VECTOR=(-
        _DllMain_PROXY$/DllMain   = PROCEDURE,-
        DllGetClassObject               = PROCEDURE,-
        DllCanUnloadNow                 = PROCEDURE,-
        GetProxyDllInfo                 = PROCEDURE,-
        DllRegisterServer               = PROCEDURE,-
        DllUnregisterServer             = PROCEDURE)

A proxy/stub shareable image with the name PROXY_SHARE would define the symbol vector using the following options file:


!
! RPC Shareable Image
!
 SYS$LIBRARY:DCOM$RPCRT4_SHR.EXE/SHARE
!
!
! The list of symbols exported by PROXY_SHARE.EXE.
!
SYMBOL_VECTOR=(-
        _DllMain_PROXY_SHARE/DllMain = PROCEDURE,-
        DllGetClassObject                  = PROCEDURE,-
        DllCanUnloadNow                    = PROCEDURE,-
        GetProxyDllInfo                    = PROCEDURE,-
        DllRegisterServer                  = PROCEDURE,-
        DllUnregisterServer                = PROCEDURE)

7.5 Required OpenVMS Registry Entries

The following sections list and describe the required OpenVMS Registry entries.

7.5.1 HKEY_CLASSES_ROOT\CLSID

The CLSID subkey contains all CLSIDs for the components supported on your system. You must register your components' CLSIDs here. Each registered CLSID should contain the following:

  • An unnamed value whose type is a zero-terminated string with a data value describing the component.
  • A named value, AppID , whose type is a zero-terminated string with a data value that is the CLSID of the component.

7.5.1.1 Component CLSIDs

A class identifier (CLSID) is a globally unique identifier (GUID) associated with an OLE class object. COM for OpenVMS server applications typically register their CLSIDs in the OpenVMS Registry so clients can locate and load the executable code associated with the OLE class object.

Register the CLSID for the component under the subkey HKEY_CLASSES_ROOT\CLSID .

A component CLSID registration should contain the following subkeys:

  • LocalServer32
    This key's value should contain a zero-terminated string with a data value that is the location of the out-of-process server executable.
  • ProgID
    This key's value should contain a zero-terminated string with a data value that is the programmatic ID of the CLSID. These are typically in the format program.component.version.
  • VersionIndependentProgID
    This key's value should contain a zero-terminated string with a data value that is the programmatic ID (less the version number) of the CLSID. These are typically in the format program.component.
  • InProcServer32
    This key's value should contain a zero-terminated string with a data value that is the location of the in-process server's shareable image.
  • Type Libraries
    Type libraries are important for implementing the IDispatch interface. A type library registers itself when it calls the OLE Automation RegisterTypeLib run-time routine. You must also add a Typelib subkey under your component's CLSID. The Typelib subkey contains your type library's GUID. For example, the following key should contain your LIBID:


     HKEY_CLASSES_ROOT\CLSID\{GUID}\TYPELIB {value=LIBID}
    

7.5.1.2 Proxy/Stub CLSIDs

The proxy/stub shareable image provides an interface-specific object for packaging parameters for that interface. Because the proxy/stub shareable image contains an object, it needs a CLSID and it needs to be included in the OpenVMS Registry. You must register a CLSID for the proxy in the OpenVMS Registry the same way as the CLSID for the component.

The CLSID for the proxy should be registered under the subkey HKEY_CLASSES_ROOT\CLSID .

A proxy/stub CLSID registration should contain the following subkey:

  • InProcServer32
    The InProcServer32 value should contain a zero-terminated string with a data value that is the location of the proxy/stub shareable image. The proxy/stub CLSID and its subkey enable COM to locate the proxy/stub shareable image.

7.5.2 HKEY_CLASSES_ROOT\Interface

The Interface subkey contains all interfaces registered with the system. You must register the component's interface IDs (IIDs) in this subkey.

Each interface registered contains at least one of the following subkeys:

  • NumMethods
    The NumMethods value should contain a zero-terminated string with a data value that is the number of methods contained in the interface.
  • ProxyStubClsid32
    The ProxyStubClsid32 value should contain a zero-terminated string with a data value that is the CLSID of the proxy/stub shareable image. This CLSID should be the same as that described in Section 7.5.1.2.

7.6 Converting OpenVMS and Windows Error Codes to Text

As you develop and test COM components, you will find that the OpenVMS and Windows systems return seemingly indecipherable error codes. To help you make these codes more understandable, HP has included some ways to translate them.

7.6.1 NTA$VMSGetMessage

HP has included the NTA$VMSGetMessage routine to translate error codes into displayable text. The following section describes the NTA$VMSGetMessage routine.

To implement this routine, you must include the NTA_MESSAGE.H file in the DCOM$LIBRARY: directory and link with the DCOM$LIBRARY:NTA_GETMSG.OBJ object module.

The NTA$VMSGetMessage routine, described in the next section, translates error codes into displayable text. The input error code must be one of the following:

  • An OpenVMS error code
  • A Windows HRESULT
  • A Windows Win32 error code
  • A Windows status code set as "user defined"

NTA$VMSGetMessage

The NTA$VMSGetMessage routine translates error codes into displayable text.

Format

Return=NTA$VMSGetMessage (status, text, flag, [count])


Arguments

status


OpenVMS usage: error_code
type: longword (unsigned)
access: read only
mechanism: by value

This status field must be one of the following:
Input Error Code Example
OpenVMS error code 0x074AA6BA
Windows HRESULT 0x80070031
Windows Win32 error code 0x00000031
Windows status code with the user-defined bit set 0xE74AA6BA

If the security API returns a Windows status code, the format of the status field is an OpenVMS status code OR'd with the Windows status control bits set. For example:

Input Error Code Result
OpenVMS error code 0x074AA6BA
Windows status code 0xE74AA6BA

text


OpenVMS usage: error_text
type: character string
access: write
mechanism: by reference

This argument is a NULL terminated string that contains the returned text from the SYS$GETMSG system service. The maximum size returned (as defined by the SYS$GETMSG system service) is 256 bytes. To avoid overwriting memory, the caller must provide a buffer address of at least 257 bytes.

flag


OpenVMS usage: flag
type: longword (unsigned)
access: read only
mechanism: by value

Controls the translation of the error code. The following values are defined in NTA_MESSAGE.H:
NTAWIN$_UNKNOWN Unknown error code
NTAWIN$_VMS OpenVMS error code
NTAWIN$_NT Windows HRESULT error code
NTAWIN$_WINDOWS Windows Win32 error code
NTAWIN$_USER Windows status code

If you provide the value NTAWIN$_UNKNOWN, the routine makes its best estimate as to the correct text. The routine parses the text as follows:

  1. Check for a Windows HRESULT (high-order nibble = 0x8). If this check fails, go to the next step.
  2. Check for a Windows user-defined status code (high-order nibble = 0xE). If this check fails, go to the next step.
  3. Assume this is an OpenVMS error code.
    The system cannot tell the difference between an OpenVMS error code and a Windows Win32 error code.

count


OpenVMS usage: FAO count
type: longword (unsigned)
access: write
mechanism: by reference

This argument is the optionally returned FAO argument count in the returned message. Currently all NTAWIN messages use ASCII substitution arguments (!AS) only. The caller must convert all numeric data to ASCII before performing the substitution with SYS$FAO.
Description This routine uses the OpenVMS SYS$GETMSG system service. The messages are stored in the SYS$MESSAGE:NTAWINMSG.EXE and SYS$MESSAGE:NTARPCMSG.EXE images.

To call this routine, you must include the NTA_MESSAGE.H file in the DCOM$LIBRARY: directory and link with the SYS$LIBRARY:DCOM$WIN32_SHR shareable image.


Condition Values Returned

Any status from the SYS$GETMSG system service.

For more information about the SYS$GETMSG system service, see the OpenVMS System Services Reference Manual.

7.6.2 DCOM$TOOL SHOW ERROR

HP has included command-line syntax to convert error codes into displayable text. The following section describes the DCOM$TOOL SHOW ERROR syntax.

To use the DCOM$TOOL utility to convert the codes, use any of the following methods:

  • Enter the following command to run the DCOM$TOOL utility:


      $ RUN SYS$SYSTEM:DCOM$TOOL.EXE
    
  • Define a DCL symbol to use DCOM$TOOL and specify parameters on the command line. For example:


      $ DCOMTOOL :== $SYS$SYSTEM:DCOM$TOOL.EXE
      $ DCOMTOOL
    

You can specify parameters for any of these methods on the command line. Table 7-1 shows the DCOM$TOOL utility command line parameters. If you do not specify any parameters, the system prompts you for the required information.

Table 7-1 DCOM$TOOL Utility Command Line Parameters
Argument Value Required or Optional
P1 Command verb : SHOW Required
P2 Command adjective : ERROR Required
P3 Error code in DCL number format (%X) Required
P4 Optional qualifers Optional

The following example shows a typical DCOM$TOOL session to translate error codes:


  $ DCOMTOOL :== $DCOM$TOOL.EXE
  $ DCOMTOOL SHOW ERROR %x80070005

7.6.2.1 DCOM$TOOL Optional Qualifiers

DCOM$TOOL accepts the following optional qualifiers:

  • /VMS_ERROR
    This is a OpenVMS error code. An example of an OpenVMS error code is as follows:


      %x074AA6BA
    
  • /HRESULT
    This is a Windows HRESULT. An example of a Windows HRESULT is as follows:


      %x80070031
    
  • /WIN32
    This is a Win32 error code. An example of a Win32 error code is as follows:


      %x00000031
    
  • /USER_DEFINED
    This is a Windows status code with the user-defined bit set. An example of a User Defined error code is as follows:


      %xE74AA6BA
    

Note

The DCOM$TOOL utility SHOW ERROR feature follows the rules, restrictions, and guidelines of the OpenVMS Message Utility. For more information, see the OpenVMS Command Definition, Librarian, and Message Utilities Manual.


Chapter 8
Authentication

8.1 Authentication Overview

Authentication is the act of verifying a user's identity by the computer system before permitting access to the system. After successfully authenticating a user, the system binds the user's authorization information to the user's process in the form of credentials. The system uses these credentials to determine whether to grant or deny access to system resources.

OpenVMS provides both native (SYSUAF-based) and Windows compatible authentication and authorization capabilities as follows:

  • Native: The system performs authentication using password information stored in the SYSUAF.DAT file. Authorization information consists of UIC, privileges, and rights identifiers.
  • Windows: The system performs authentication using password information stored in a SAM database managed by domain controllers. Authorization information consists of primary SID, group SIDs, session key, and privileges obtained from the user's account information in the SAM database.

After OpenVMS successfully authenticates a user (either native or Windows), OpenVMS attaches the user's native credentials to the process using a structure known as a persona. If the system used Windows for authentication, OpenVMS also attaches the user's Windows credentials to the process (as an extension to the persona).

8.2 Acquiring Windows Credentials Using NTA$LOGON

NTA$LOGON is a utility that allows you to acquire NTLM credentials. All processes that need Windows security to access the OpenVMS Registry or COM for OpenVMS facilities require NTLM credentials.

You must provide NTA$LOGON with a user account name, a password, and (if required) a domain name. NTA$LOGON uses the Authentication and Credential Management (ACM) Authority to contact the domain controller and acquire a Windows access token. NTA$LOGON merges the Windows information with the user's OpenVMS credentials.

For a detailed review of NTA$LOGON dependencies and a description of how NTA$LOGON interacts with other parts of the OpenVMS infrastructure, see Section 5.1 and Section 4.10 (especially the ACME server and HP Advanced Server for OpenVMS server).

To use the NTA$LOGON utility, you can enter any of the following:

  • Enter the following command to run the NTA$LOGON utility:


        $ RUN SYS$SYSTEM:NTA$LOGON
    

    The system prompts you for a user account name and password.
  • Define a DCL symbol to use NTA$LOGON to parse the command line. For example:


        $ NTLOGON :== $NTA$LOGON
        $ NTLOGON
    

You can specify parameters on the command line. Table 8-1 shows the NTA$LOGON utility command-line parameters. If you do not specify any parameters, the system prompts you for the required information.

Table 8-1 NTA$LOGON Utility Command Line Parameters
Argument Value Required/Optional
P1 User account name. If an account name is needed but was not specified on the command line, NTA$LOGON prompts for input. Optional
P2 Password. If a password is needed but was not supplied on the command line, NTA$LOGON prompts for input (echoing suppressed). Optional

Example 8-1 shows a typical NTA$LOGON session to acquire credentials.

Example 8-1 Sample NTA$LOGON Session

    $ NTLOGON :== $NTA$LOGON
    $ NTLOGON joesmith
    Password:

Note

Windows domain names and user account names are not case sensitive. NTA$LOGON converts all domain names and user account names to uppercase. If you specify a password on the command line, DCL converts all characters to uppercase, unless you enclose the password in quotation marks ("").

8.2.1 NTA$LOGON Optional Qualifiers

NTA$LOGON accepts the following optional qualifiers:

  • /DELETE
    Deletes the current Windows credentials.
    If you specify the /DELETE qualifier with the /WRITE_FILE qualifier, the system deletes the password record for the specified domain name and user account name from the file.
  • /DOMAIN=name
    Specifies a domain name. This qualifier converts the name to uppercase. If you do not specify this qualifier, the system uses the default domain name.
  • /LIST
    Lists the domain name and the user account name assigned to the current process.
    If you use the /LIST qualifier with the /READ_FILE or /WRITE_FILE qualifier, the system lists the contents of the file.
  • /LOG
    Displays a message when an operation completes.
  • /OVERRIDE_MAPPING
    Acquires Windows credentials for the specified Windows user account name even if the OpenVMS user name of the process does not match the OpenVMS user name associated with that Windows user account name in the domain controller.
    This qualifier requires the IMPERSONATE privilege.
  • /READ_FILE [=file]
    This qualifier causes the system to search the binary input file created by the /WRITE_FILE qualifier for the specified domain name and user account name, instead of reading the password from the user input device. The /READ_FILE qualifier supports only binary files created by the NTA$LOGON/WRITE_FILE command.
    If the system finds a matching record, NTA$LOGON attempts to use that password to acquire Windows credentials.
    If you do not provide a file specification, the system uses the following default file specification:


        DCE$COMMON:[000000]NTA$LOGON.DAT
    
  • /TYPE={BATCH | DIALUP | LOCAL | NETWORK | REMOTE}
    Specifies the rules under which access is to be granted or denied. If you do not specify this qualifier, the default is the type of the current process. This qualifier is usually used for detached processes (detached processes do not have a default type).
    This qualifier requires IMPERSONATE privilege.

    Note

    If you are running COM on OpenVMS Version 7.3-2 or higher, the /TYPE=BATCH qualifer is not supported, and the IMPERSONATE privilege is not required.
  • /WRITE_FILE [=file]
    This qualifier causes the system to write the specified domain name, user account name, and password into an output file to be used later (see the /READ_FILE qualifier), instead of using the user-supplied password.
    If you do not provide a file specification, the system uses the following default location and file name:


        DCE$COMMON:[000000]NTA$LOGON.DAT
    

    Caution

    The /READ_FILE and /WRITE_FILE qualifiers are intended to be used only by servers that have no other way to acquire Windows credentials to access the OpenVMS Registry or COM for OpenVMS facilities. HP does not recommend general use of the /READ_FILE and /WRITE_FILE qualifiers.
    Once you have written a password into a disk file, HP recommends you take strong precautions to protect the password file from unauthorized access.

8.2.2 Examples of Using NTA$LOGON to Acquire Windows Credentials

Example 8-2 shows how a user acquires NT credentials for the first time.

Example 8-2 Acquiring Windows Credentials for the First Time

    $ NTLOGON :== $NTA$LOGON
    $ NTLOGON/LIST
    ERROR: NtOpenProcessToken() failure: -1073741700 0xc000007c
    %SYSTEM-E-NOSUCHEXT, no such extension found

    $ NTLOGON/LOG JOESMITH
    [Persona #1 NT extension: Account= "JOESMITH" Domain= "NT_DOMAIN" ]
    Password:

Example 8-3 shows how the user replaces the Windows credentials.

Example 8-3 Replacing Windows Credentials

    $ NTLOGON/DELETE
    $ NTLOGON/OVERRIDE_MAPPING/DOMAIN=OTHER_DOMAIN
    Username: janebrown
    Password:

Example 8-4 shows how a user saves a password in a disk file. The system requests that the user enter the password twice with echoing suppressed.

Example 8-4 Saving a Password to a File

    $ NTLOGON :== $NTA$LOGON
    $ NTLOGON/WRITE_FILE=DEV:[DIR]NTA$LOGON.DAT COM_SERVER
    Password:
    Confirm:
    $ NTLOGON/READ_FILE=DEV:[DIR]NTA$LOGON.DAT/LIST
    File DEV:[DIR]NTA$LOGON.DAT contains the following records:
    02-MAR-1999 16:57:23.20 COM_SERVER

After you have created this file, you can add the following to a DCL command procedure:


    $ NTLOGON :== $NTA$LOGON
    $ NTLOGON/READ_FILE=DEV:[DIR]NTA$LOGON.DAT COM_SERVER

8.3 The Authentication and Credential Management (ACM) Authority

The Authentication and Credential Management authority authenticates users and determines the user security profile for OpenVMS and Windows. The ACME_SERVER process provides these ACM services. The ACME_SERVER process uses plug-in modules called ACME agents. ACME agents perform the actual work of responding to authentication requests, query requests, and event requests.

The OpenVMS ACME agent (VMS$VMS_ACMESHR.EXE) provides OpenVMS native services. The MSV1_0 ACME agent (PWRK$MSV1_0_ACMESHR.EXE, an HP Advanced Server for OpenVMS product component) provides Windows connectivity services.

The MSV1_0 ACME agent forwards Windows connectivity service requests from NTA$LOGON and SSPI/NTLM to an HP Advanced Server for OpenVMS process running on one or more systems in the cluster. The PWRK$ACME_SERVER logical name can contain a comma-delimited list of cluster node names to which the MSV1_0 ACME can forward requests. Running the HP Advanced Server for OpenVMS process on more than one cluster node and including the node names in the PWRK$ACME_SERVER logical name allows the MSV1_0 ACME agent to fail over a request automatically if a connection is interrupted. If the logical name is undefined, the system defaults to the local machine name.

The ACME_SERVER process must be present on any system running RPC or COM for OpenVMS. However, the HP Advanced Server for OpenVMS process needs to be present on only one node in the cluster.

8.3.1 Windows Authentication on OpenVMS

Because the ACME_SERVER returns to its callers a complete OpenVMS persona with the requested attached Windows persona extension, the VMS ACME agent enforces the following rules:

  • Every Windows user must be mapped to a local OpenVMS user name.
    The MSV1_0 ACME provides this mapping through the HP Advanced Server for OpenVMS HOSTMAP database.
  • The mapped OpenVMS user name must be a valid (and not disabled) account in the SYSUAF.DAT. The account's access restrictions must allow access during the specified days and times. COM for OpenVMS and RPC typically require NETWORK access during authentication.
  • The mapped OpenVMS user name must be an account with the EXTAUTH flag set. EXTAUTH allows the system manager fine control over which OpenVMS accounts can be used for mapping. You can use the IGNORE_EXTAUTH bit (bit number 11 [decimal]) in the SECURITY_POLICY system parameter to override this per-account feature. If you set the IGNORE_EXTAUTH bit to 1, OpenVMS allows you to map to any account, regardless of the account's EXTAUTH setting. Note that the IGNOTE_EXTAUTH is used only for the ACME_SERVER and is ignored by Loginout.

8.3.2 Managing the ACME_SERVER Process (ACME Server Commands)

To start the ACME_SERVER process and configure the MSV1_0 ACME agent at system startup, add the following entry to SYLOGICALS.COM:


  $ DEFINE NTA$NT_ACME_TO_BE_STARTED YES

You can also start the ACME_SERVER process manually using the following startup command file:


  $ @SYS$STARTUP:NTA$STARTUP_NT_ACME

To shut down ACME_SERVER, enter the following command:


  $ SET SERVER ACME/EXIT

If an abnormal condition in an ACME agent prevents a normal server shutdown, use the /ABORT qualifier in the place of the /EXIT qualifier to force the ACME_SERVER to terminate.

To turn on ACME_SERVER logging, enter the following command:


  $ SET SERVER ACME/LOG

This command creates a ACME$SERVER.LOG file in the SYS$MANAGER directory. You might find this file useful when you are trying to diagnose potential problems.

To display the ACME_SERVER configuration information, enter the following command:


  $ SHOW SERVER ACME[/FULL]

8.3.3 Configuring the MSV1_0 ACME Agent

Table 8-2 lists and describes systemwide logical names you can use to control certain features of the MSV1_0 ACME agent.

Table 8-2 MSV1_0 ACME Agent Logical Names
Logical name Description
PWRK$ACME_SERVER Comma-delimited list of cluster SCS node names that are running HP Advanced Server for OpenVMS processes that can service Windows connectivity requests. If you do not define the node names, the MSV1_0 ACME agent tries to connect to the HP Advanced Server for OpenVMS process on the local system.
PWRK$ACME_RETRY_COUNT The maximum number of retry attempts the MSV1_0 ACME agent performs when connecting to an HP Advanced Server for OpenVMS process. The default value is 10.
PWRK$ACME_RETRY_INTERVAL The number of tenths of seconds between retry attempts. The default is 2.5 seconds.


Chapter 9
Active Template Library

9.1 COM for OpenVMS and ATL

ATL (Active Template Library) is a set of template-based C++ classes from Microsoft that simplify the development of COM components. ATL provides support for key COM features, such as stock implementations of IUnknown, IClassFactory, IDispatch, dual interfaces, and connection points. It also provides support for more advanced COM features, such as enumerator classes and tear-off interfaces.

The ATL COM AppWizard and ATL Object Wizard in Microsoft Visual Studio can be used to quickly create code for simple COM objects that can be copied to OpenVMS systems and built with very few modifications.

The COM for OpenVMS ATL is based on Microsoft ATL Version 3.0. You must be running COM Version 1.1-B or higher for OpenVMS. ATL on OpenVMS Alpha requires Compaq C++ Version 6.2-016 or higher.

COM for OpenVMS provides ATL as source code in header files that you include in your application.

Table 9-1 shows the differences between the ATL implementation on Windows and OpenVMS.

Table 9-1 ATL Implementation Differences
Implementation Windows OpenVMS
Interface GUI Character cell
Server models Single threaded or multithreaded Multithreaded only
ATL available as DLL Yes (not required) No
Application registration Automatic using UpdateRegistryFromResource function in ATLBASE.H Automatic using UpdateRegistryFromFile function in ATLBASE.H
ATL component types In process as DLL
Out of process as EXE
In process as shareable image
Out of process as an executable image

9.2 Developing a COM for OpenVMS Application Using ATL

The following sections describe how to create a COM for OpenVMS application using ATL.

9.2.1 Step 1: Create the ATL Component in Microsoft Visual Studio

Generate the code using the Microsoft Visual Studio ATL COM AppWizard. For information about using the ATL COM AppWizard, see the Microsoft Developer Network (MSDN) documentation.

Copy the generated files to OpenVMS. For example, copy the files using File Transfer Protocol (FTP) in ASCII mode. Table 9-2 lists and describes the files that the ATL COM AppWizard would generate for a project named mycomapp .

Table 9-2 Files Generated by ATL COM AppWizard for mycomapp
File name Description Platform In Process or Out of Process
mycomapp.cpp Contains the implementation of DllMain, DllCanUnloadNow, DllGetClassObject, DllRegisterServer and DllUnregisterServer. Also contains the object map, which is a list of the ATL objects in your mycomapp . This is initially blank, because you have not created an object yet. Windows/OpenVMS Both
mycomapp.def The standard Windows module definition file for the DLL.
Note: MYCOMAPP.DEF becomes MYCOMPAP$SHR.OPT on OpenVMS.
Windows In process
mycomapp.dsw The mycomapp workspace. Windows Both
mycomapp.dsp The file that contains the mycomapp settings. Windows Both
mycomapp.idl The interface definition language file, which describes the interfaces specific to your objects. Windows/OpenVMS Both
mycomapp.rc The resource file, which initially contains the version information and a string containing the mycomapp name. Windows Both
Resource.h The header file for the resource file. Windows/OpenVMS Both
mycomappps.mk The make file that can be used to build a proxy/stub DLL. You do not need this file. Windows Proxy/stub
mycomapps.def The module definition file for the proxy/stub DLL.
Note: MYCOMAPPPS.DEF becomes MYCOMAPPPS$SHR.OPT on OpenVMS.
Windows Proxy/stub
StdAfx.cpp The file that will include the ATL implementation files. Windows/OpenVMS Both
StdAfx.h The file that will include the ATL header files. To make the mycomapp DLL useful, you need to add a control, using the ATL Object Wizard. Windows/OpenVMS Both
mycomapp.rgs A registrar script for your COM server. Windows/OpenVMS Both
myinterface.rgs A registrar script for your COM server. Windows/OpenVMS Both
myinterface.cpp The interfaces specific to your object. Windows/OpenVMS Both
myinterface.h The header file for the interfaces. Windows/OpenVMS Both

9.2.2 Step 2: Modify Generated Files for ATL Applications on OpenVMS

Make the following changes to the generated files before you build ATL applications on OpenVMS.

9.2.2.1 Remove _ATL_MIN_CRT

When the ATL COM AppWizard generates mycomapp , it also defines the macro _ATL_MIN_CRT as part of the GUI support. Because OpenVMS does not have a graphical interface, you must remove (or not define) _ATL_MIN_CRT when you build on OpenVMS.

9.2.2.2 Include ATLMAIN.CXX

On OpenVMS, you must include ATLMAIN.CXX for out of process components. ATLMAIN.CXX defines the wWinMain() function.

9.2.2.3 Modify Registration Procedure

OpenVMS does not support registering the application using the UpdateRegistryFromResource function; rather, you must use the OpenVMS UpdateRegistryFromFile function in the ATLBASE.H header file. You must make the following changes to your application:

File to search Search for Replace with
Interface header file DECLARE_REGISTRY_RESOURCEID DECLARE_REGISTRY_FILE
Project source file _Module.UpdateRegistryFromResource _Module.UpdateRegistryFromFile

The following example shows sample coding changes:


#ifdef __vms
DECLARE_REGISTRY_FILE(_T("MYINTERFACE.RGS"))
#else
DECLARE_REGISTRY_RESOURCEID(IDR_MYINTERFACE)
#endif

#ifdef __vms
_Module.UpdateRegistryFromFile(_T_"MYCOMAPP.RGS"), TRUE);
#else
_Module.UpdateRegistryFromResource(IDR_MYCOMPAPP, TRUE);
#endif

9.2.2.4 Remove Calls to Windows Message Functions for OpenVMS V7.3-2

Beginning in OpenVMS Version 7.3-2, HP no longer supports the PostThreadMesssage, GetMessage and DispatchMessage calls that are generated by the ATL COM AppWizard for out of process components.

The same functionality can be achieved by replacing these calls with the event-handling functions CreateEvent, SetEvent, and WaitForSingleObject. Refer to the source code for TESTATL.CXX in DCOM$EXAMPLES:[TESTATL_OUTPROC] for more information.

The following example shows some of the sample coding changes


#ifdef __vms
  SetEvent(hMsg);
#else
  PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
#endif

#ifdef __vms
  WaitForSingleObject(hMsg, INFINITE);
#else
  MSG msg;
  while (GetMessage(&msg, 0, 0, 0))
        DispatchMessage(&msg);
#endif

9.2.3 Step 3: Build an Application Using the MIDL Compiler

This process is the same as the one shown in Section 7.2.

In-process example:


  $ MIDL :== $DCOM$MIDL.EXE
  $ MIDL  -nologo -Oicf mycompapp.idl -
    -IDCOM$LIBRARY -
    -iid mycompapp_i.cxx -
    -proxy mycompapp_p.cxx -
    -dlldata dlldata.cxx -
    -tlb mycompapp$shr.tlb

Out-of-process example:


  $ MIDL :== $DCOM$MIDL.EXE
  $ MIDL  -nologo -Oicf mycompapp.idl -
    -IDCOM$LIBRARY -
    -iid mycompapp_i.cxx -
    -proxy mycompapp_p.cxx -
    -dlldata dlldata.cxx -
    -tlb mycompapp.tlb

HP recommends that the name of your type library match the name of your executable or shareable image.

9.2.4 Step 4: Compile the ATL COM Application

The following sections describe how to compile COM for OpenVMS applications.

9.2.4.1 Required Header File: ATLBASE.H

The VMS_ATL.H header file defines several macros used by the header files. VMS_ATL.H is already included in the ATLBASE.H header file. When you create ATL source code, you must include ATLBASE.H as the first noncommented line in your source (both header and implementation) files.

9.2.4.2 Required Macro Definitions

Include the following /DEFINE qualifier on all of your CXX commands:


/DEFINE=(UNICODE=1,_WIN32_DCOM,_ATL_STATIC_REGISTRY)

The UNICODE macro ensures that wide-character variants of Win32 APIs and data structures are enabled when you compile. (The UNICODE macro is also defined in VMS_DCOM.H.)

The _ATL_STATIC_REGISTRY macro enables you to statically link with the ATL registry component ( Registrar ) for optimized registry access. You can add the macro either by including the /DEFINE qualifier on the command line or by adding the stdafx.h header file to your code.

9.2.4.3 Required Include Directories

COM for OpenVMS applications typically require header files that come from DCOM$LIBRARY . The ATL header files and source files are also located in DCOM$LIBRARY .

Include the following qualifier on your CXX command lines:


  /INCLUDE=DCOM$LIBRARY

If you already have an /INCLUDE qualifier on your command line, modify the command to include DCOM$LIBRARY .

9.2.4.4 Required C++ Qualifiers

You must specify the following C++ qualifiers when you build COM for OpenVMS applications:

  • /EXCEPTIONS=CLEANUP
    Specify the /EXCEPTIONS=CLEANUP qualifier on C++ commands to enable C++ exceptions.
  • /STANDARD=MS
    The C++ compiler supports many different compilation standards. You must use /STANDARD=MS for COM applications created by ATL.
    /STANDARD=MS informs the compiler that it should follow the language constructs supported by the Visual C++ compiler. This switch works well with the code generated by ATL.
  • /TEMPLATE_DEFINE=(NOALL,NOPRAGMA)
    This switch controls the instantiation of C++ templates. You must specify the following options:
    • [NO]ALL
      Instantiate all function template entities declared or referenced in the compilation unit, including typedefs. For each fully instantiated template class, all its member functions and static data members are instantiated even if they were not used. Nonmember template functions are instantiated even if the only reference was a declaration. Instantiations are created with external linkage. Overrides /REPOSITORY at compile time. The compiler places instantiations in the user's object file. The template definition must be present before the point of each instantiation in the source file.
      The default is /TEMPLATE_DEFINE=NOALL .
    • [NO]PRAGMA
      Determines whether the C++ compiler ignores #PRAGMA DEFINE_TEMPLATE directives encountered during the compilation. This option lets you quickly switch to automatic instantiation without having to remove all the pragma directives from your program's code base.
      The default is /TEMPLATE_DEFINE=PRAGMA , which enables #PRAGMA DEFINE_TEMPLATE .

9.2.5 Step 5: Link the ATL COM Application

To build a COM for OpenVMS application, you must build both client and component images. Because you can implement a component as either an in process component or an out of process component, you must build a shareable image or an executable image, or both.

The following sections describe the steps you must follow to link the client, component, and proxy/stub images.

9.2.5.1 Linking the Client and the Out of Process Component

Although you do not need to specify any qualifiers to link the client or the component executable images, you must link both images. The specific link-time dependency is as follows:

  • DCOM$LIBRARY:DCOM.OPT

If you have one or more C++ modules, use the C++ linker (CXXLINK) instead of the standard OpenVMS linker so you can specify the location of your C++ repository (/CXX_REPOSITORY qualifier). For example:


$  CXXLINK/your-specific-linker-qualifiers list-of-object-modules, -
_$ DCOM$LIBRARY:DCOM.OPT/OPTIONS, application.OPT/OPTIONS  -
_$ /REPOSITORY=[.CXX_REPOSITORY]

You can also include the list of object modules in an options file instead of on the command line.

9.2.5.2 Linking the In Process Component Shareable Image

The in process component shareable image dependency list differs slightly from that of the client and component executables. The specific link-time dependencies are as follows:

  • [directory-name]MYCOMPAPP$SHR.OPT
  • DCOM$LIBRARY:DCOM.OPT

9.2.5.3 Creating a Symbol Vector

To create a symbol vector for the in process component shareable image, use the procedure described in Section 7.4.2.1.

To create a symbol vector for the proxy/stub shareable image, use the procedure described in Section 7.4.3.

9.3 ATL Samples

TESTATL is an out of process sample, and MATH101 is an in process sample.

You can find the sample ATL applications shown in this chapter in the following directories on the COM for OpenVMS kit:


  DCOM$EXAMPLES:[TESTATL_OUTPROC]
  DCOM$EXAMPLES:[TESTATL_INPROC]

Note

If you are running authenticated COM, before you build the application on OpenVMS you must run NTA$LOGON and acquire Windows credentials. For more information, see Section 8.2.

9.3.1 Out of Process COM Sample (TESTATL_OUTPROC)

This sample implements a COM client and server in which the component provides one interface: ISum .

Given sources initially generated by the Microsoft Visual Studio ATL AppWizard and a few applied changes, the sample demonstrates the build, registration, and execution of the ATL application on OpenVMS.

The following sections describe how to create the application using the Microsoft ATL AppWizard on Windows and how to build the application on an OpenVMS system.

9.3.1.1 Creating the Application on Windows

To generate a skeleton project and simple objects using the Microsoft Visual Studio ATL AppWizard, follow these steps:

  1. Generate the skeleton project:
    • Select the ATL COM AppWizard and name your skeleton project.
    • Choose Executable (EXE) as the server type.
  2. Add objects:
    • Start the ATL Object Wizard.
    • From the Objects category, select Simple Object.
    • From the Attribute tab, choose the Both threading model.

9.3.1.2 Building, Registering, and Running the Application on OpenVMS

A README file describes how to build, register, and run this COM for OpenVMS sample. The file is located in:


DCOM$EXAMPLES:[TESTATL_OUTPROC]README-TESTATL_OUTPROC.TXT

9.3.2 In-Process COM Sample (TESTATL_INPROC)

This sample implements a COM client and server in which the component provides three interfaces: ISum, IDiv, and IMul.

Given sources initially generated by the Microsoft Visual Studio ATL AppWizard, the sample demonstrates the build, registration, and execution of the shareable application on an OpenVMS system.

The following sections describe how to build the application.

9.3.2.1 Creating the Application on Windows

To generate a skeleton project and simple objects using the Microsoft Visual Studio ATL AppWizard, follow these steps:

  • Generate the skeleton project:
    • Select the ATL COM AppWizard and name your skeleton project.
    • Choose Dynamic Link Library (DLL) as the server type.
  • Add objects:
    • Start the ATL Object Wizard.
    • From the Objects category, select Simple Object.
    • From the Attribute tab, choose the Both threading model.

9.3.2.2 Building, Registering, and Running the Application on OpenVMS

A README file describes how to build, register, and run this COM for OpenVMS sample. The file is located in:


DCOM$EXAMPLES:[TESTATL_INPROC]README-TESTATL_INPROC.TXT

9.4 Suggested Reading

The following resources provide more information about ATL:

  • Third-party books about ATL:
    • Beginning ATL COM Programming, Grimes and Stockton, Templeman and Reilly, Wrox Press, Olton, Birmingham, UK, 1998. ISBN: 1-861000-11-1.
    • Professional ATL COM Programming, Dr Richard Grimes, Wrox Press, Olton, Birmingham, UK, 1998. ISBN: 1-861001-4-01.
  • Websites:
    • The Component Object Model Specification, available from the Microsoft COM website:


      www.microsoft.com/com
      


Previous Next Contents Index