Offering Remote Assistance

 

Microsoft Corporation

June 2003

Applies to:
    Microsoft® Remote Assistance
    Microsoft Windows® XP
    Microsoft Windows Server 2003

Summary: This whitepaper details the information necessary to build a customized solution based on the Offer Remote Assistance feature of Remote Assistance. The objects and sample code provided below may be used to integrate the Offer Remote Assistance solution into the workflow of a help desk organization. (23 printed pages)

Contents

Unsolicited Remote Assistance objects

Remote Assistance Ticket

Unsolicited Remote Assistance objects

SAFRemoteDesktopConnection Object

Properties

  • None

Methods

  • ConnectRemoteDesktop

    [jscript]
    Obj.ConnectRemoteDesktop(remoteMachineName)
    

    Arguments

    Input

    Remote Machine Name-This can be an IP Address or DNS Server Name of the remote machine.

    Returns

    SAFRemoteConnectionData Object.

    Description

    After establishing a connection with the remote computer, this method returns all session information for every user logged on to the remote computer. This information is populated in the SAFRemoteConnectionData object that is returned by the method.

    This method does not include the disconnected Terminal Server sessions and the Session with ID 65536 that is used internally by Terminal Services.

    This method may only be called from script executed within Help Center.

    Offer Remote Assistance Sample JScript

    Requirements

    Client: Included in Microsoft® Windows® XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

SAFRemoteConnectionData Object:

Properties

  • None

Methods

  • Users

    [jcsript]
    Obj.Users()
    

    Arguments

    Input

    None

    Returns

    PCHCollection object

    Description

    This method returns the collection of all the logged on users. All the user information is returned as a standard collection i.e. PCHCollection. The members of this collection are objects of type SAFUser.

    This method may only be called from script executed within Help Center.

    Offer Remote Assistance Sample JScript

    Requirements:

    Client: Included in Windows XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

  • Sessions

    [jscript]
    Obj.Sessions(optional var UserName, optional var DomainName)
    

    Arguments

    Input

    UserName—User on remote machine

    DomainName—Domain of user on remote machine

    Returns

    PCHCollection object

    Description

    This method returns a PCHCollection containing session information for all logged on users of a remote machine if no input parameters are specified. The members of this collection are objects of type SAFSession. If input parameters are specified then only the session information for the specified user is returned.

    This method may only be called from script executed within Help Center.

    Offer Remote Assistance Sample JScript

    Requirements

    Client: Included in Windows XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

  • ConnectionParms

    [jscript]
    Obj.ConnectionParms(var RemoteMachineName, var RemoteUserName, var RemoteDomainName, var RemoteSessionID, var bstrUserHelpBlob)
    

    Arguments

    Input

    RemoteMachineName: The name of the remote machine that you are creating the Remote Assistance ticket for.

    RemoteUserName: The name of the user that you are creating a Remote Assistance ticket for on the remote machine.

    RemoteDomainName: The domain name of the user that you are creating a Remote Assistance ticket for on the remote machine.

    RemoteSessionID: The session ID on the remote machine that you are creating a Remote Assistance ticket for.

    BstrUserHelpBlob: This is reserved and should always be a null string.

    Returns

    Connection Parameter String—Used by the expert to connect to the remote (novice) computer.

    Description

    This method gets the connection parameters for a specified User Name, Domain Name, Session ID. The connection parameter string that is returned is a Remote Assistance ticket based on the input parameters.

    This method may only be called from script executed within Help Center.

    Offer Remote Assistance Sample JScript

    Requirements

    Client: Included in Windows XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

  • ModemConnected

    [jscript]
    Obj.ModemConnected(ServerName)
    

    Arguments

    Input

    ServerName—string containing the name of the remote computer that you want to determine the connection type of (modem or broadband/other)

    Returns

    Boolean value determining connection type.

    True = Modem

    False = Broadband/other

    Description

    This method is used to determine the connection type of the remote computer. This can then be used to set the color depth setting (see Remote Assistance Ticket) to optimize for bandwidth.

    This method may only be called from script executed within Help Center. Not supported on Windows XP or Windows XP SP1

    Offer Remote Assistance Sample JScript

    Requirements

    Client: Included in Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

SAFUser Object

Properties

  • DomainName

    [jscript]
    Obj.DomainName
    

    Arguments

    Input

    None

    Output

    Domain name of the user logged on to the remote computer.

    Description

    DomainName contains the domain name of user logged on to the remote Computer.

    This object is only accessible from script in Help Center

    Offer Remote Assistance Sample JScript

    Requirements

    Client: Included in Windows XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

  • UserName

    [jscript]
    Obj.UserName
    

    Arguments:

    Input:

    None

    Return

    User name of the user logged on to the remote computer.

    Description

    User Name contains the user name of the user logged on to the remote computer.

    This method may only be called from script executed within Help Center.

    Offer Remote Assistance Sample JScript

    Requirements

    Client: Included in Windows XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

SAFSession Object

Description

The SAF Session object contains the Session State, Session ID, UserName and Domain Name information for all the logged on users.

Properties

  • SessionID

    [jscript]
    Obj.SessionID
    [c]
    [propget, id(DISPID_SAF_SESS__SESSIONID   )] HRESULT SessionID   ( [out, retval] DWORD *pVal   );
    [c]
    [propput, id(DISPID_SAF_SESS__SESSIONID   )] HRESULT SessionID   ( [in      ] DWORD         pVal   );
    

    Description

    Contains the SessionID of the session on the remote computer.

    Offer Remote Assistance Sample C

    Offer Remote Assistance Sample JScript

    Requirements

    Client: Included in Windows XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

  • Session State:

    [jscript]
    Obj.SessionState
    [c]
    [propget, id(DISPID_SAF_SESS__SESSIONSTATE)] HRESULT SessionState( [out, retval] SessionStateEnum         *pVal   );
    [c]
    [propput, id(DISPID_SAF_SESS__SESSIONSTATE)] HRESULT SessionState( [in         ] SessionStateEnum         pVal   );UserName:
    

    Description

    Contains the SessionState of the session on the remote Computer. This can be one of the following:

    PchActive                = 0,     // User logged on to WinStation

    pchConnected                = 1,    // WinStation connected to client

    pchConnectQuery    = 2,     // In the process of connecting to client

    pchShadow                = 3,     // Shadowing another WinStation

    pchDisconnected     = 4,     // WinStation logged on without client

    pchIdle                = 5,    // Waiting for client to connect

    pchListen                = 6,    // WinStation is listening for connection

    pchReset                = 7,     // WinStation is being reset

    pchDown                = 8,    // WinStation is down due to error

    pchInit                = 9,    // WinStation in initialization

    pchStateInvalid                =10    //Winstation state is invalid

    Offer Remote Assistance Sample C

    Offer Remote Assistance Sample JScript

    Requirements

    Client: Included in Windows XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

  • DomainName

    [jscript]
    Obj.DomainName
    [c]
    [propget, id(DISPID_SAF_SESS__DOMAINNAME  )] HRESULT DomainName  ( [out, retval] BSTR                    *pVal   );
    [c]
    [propput, id(DISPID_SAF_SESS__DOMAINNAME  )] HRESULT DomainName  ( [in   ] BSTR    pVal   );
    

    Description

    Contains the DomainName of the owner of the session.

    Offer Remote Assistance Sample C

    Offer Remote Assistance Sample JScript

    Requirements

    Client: Included in Windows XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

  • UserName

    [jscript]
    Obj.UserName
    [c]
    [propget, id(DISPID_SAF_SESS__USERNAME    )] HRESULT UserName    ( [out, retval] BSTR                     *pVal   );
    [c]
    [propput, id(DISPID_SAF_SESS__USERNAME    )] HRESULT UserName    ( [in         ] BSTR                     pVal   );
    

    Description

    Contains the UserName of the owner of the session.

    Offer Remote Assistance Sample C

    Offer Remote Assistance Sample JScript

    Requirements

    Client: Included in Windows XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

PCHService

  • RemoteConnectionParms

    HRESULT RemoteConnectionParms([in] BSTR bstrUserName, [in ] BSTR  bstrDomainName [in ], [in  ] long  lSessionID , [in ] BSTR bstrUserHelpBlob , [out, retval] BSTR *bstrConnectionString );
    

    Parameters

    Input

    UserName—User name of the session on the novices machine that you wish to get connection parameters for.

    DomainName—Domain name of the session on the novices machine that you wish to get connection parameters for.

    SessionID—Session ID of the session on the novices machine that you wish to get connection parameters for.

    bstrUserHelpBlob—Internal to Remote Assistance. It is recommended that a null value is always passed in here.

    Output

    Connection Parameter String—Used by the expert to connect to the remote (novice) computer.

    Returns

    Standard HRESULT that indicates a success or failure.

    Description

    This method gets the connection parameters for a specified User Name, Domain Name, Session ID. This is the interface used on the expert side.

    Remarks

    When Remote Assistance is disabled in System Properties and Remote Assistance group policies are not configured or disabled on the server (novice machine), calling RemoteConnectionParms or RemoteUserSessionInfo with valid arguments will return E_AccessDenied

    If UserName and DomainName are both either Null of empty, a connection parameter string is generated with a return code of S_OK. This connection string is not valid, and will not work if a Remote Assistance connection is attempted with this.

    If the UserName and DomainName are invalid BSTRs but are not Null or empty, then no SALEM ticket is generated and the hr returned is HRESULT_FROM_WIN32(ERROR_NONE_MAPPED).

    Offer Remote Assistance Sample C

    Requirements

    Client: Included in Windows XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

  • RemoteUserSessionInfo:

    HRESULT RemoteUserSessionInfo ([out] PCHCollection* *ppSessions )
    

    Parameters

    Input:

    None

    Output

    PCHCollection of sessions on remote machine

    Returns

    Standard HRESULT that indicates success or a failure.

    Description

    This method returns the collection of the sessions on the remote machine. All the session information is returned as a standard collection i.e. PCHCollection. The members of this collection are objects of type SAFSession. SAFSession includes the DomainName, SessionID, SessionState and UserName for each session.

    When Remote Assistance is disabled in System Properties and Remote Assistance group policies are not configured or disabled on the server (novice machine), calling RemoteConnectionParms or RemoteUserSessionInfo with valid arguments will return E_AccessDenied.

    Offer Remote Assistance Sample JScript

    Requirements:

    Client: Included in Windows XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

  • get_RemoteModemConnected

    HRRESULT get_RemoteModemConnected( /*[out,retval]*/ VARIANT_BOOL *fModemConnected )
    

    Parameters

    Output

    Boolean value determining connection type.

    True = Modem

    False = Broadband/other

    Returns

    Standard HRESULT that indicates success or a failure.

    Description

    This method is used to determine the connection type of the remote computer. This can then be used to set the color depth setting (see Remote Assistance Ticket) to optimize for bandwidth.

    Offer Remote Assistance Sample C

    Requirements

    Client: Included in Windows XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

PCHCollection

  • get__NewEnum

    HRESULT get__NewEnum(IUnknown** ppUnk);
    

    Parameters

    Output

    ppUnk

    [out] The IUnknown pointer of a newly created enumerator object.

    Returns

    Standard HRESULT that indicates a success or failure.

    Description

    Returns an enumerator object for the collection. See PCHCollection for more information on the PCH collection of objects.

    Remarks

    The newly created enumerator maintains an iterator on the original collection, m_coll, (so no copy is made) and holds a COM reference on the collection object to ensure that the collection remains alive while there are outstanding enumerators.

    Offer Remote Assistance Sample C

    Requirements

    Client: Included in Windows XP or Windows Server 2003

    Library: Use HelpServiceInterfaces.tlb

Remote Assistance Ticket

Introduction

This document contains the details contained in a Remote Assistance (RA) ticket. A RA ticket contains information necessary for the RA Expert (client) to connect to a RA Novice (server). The Ticket is generated by the RA Novice and sent to the RA Expert via an existing escalation method.

Details

A sample RA Ticket looks like the following XML:

Note   The below sample is for OS versions XP SP1 or greater. If the machine is less than XP SP1 the “*” will be replaced by text values. These are internal values used specifically by Remote Assistance and should not be modified.

<?xml version="1.0" encoding="Unicode" ?>
<UPLOADINFO TYPE="Escalated">
<UPLOADDATA USERNAME="sampleUser" 
RCTICKET="65538,
1,
192.168.1.100:3389Port;sampleMachineName.yourDomain.com:3389,
*,
Wx6F64TmkmR4FizZt8fNUqYIFz6NPgiT1zdYi5XQUKo=,
*,
*,
d4sTmwmZSr3XOcku2pSAOK/qG40=" 
RCTICKETENCRYPTED="1" 
DtStart="1014854217" 
DtLength="60" PassStub="Wx6F64TmkmR4FizZt8fNUqYIFz6NPgiT1zdYi5XQUKo=" 
  L="0" />
</UPLOADINFO>

The following are the attributes that can be located inside the UPLOADDATA tag and their respective definitions.

  1. USERNAME

    Username of the novice that is being assisted.

  2. RCTICKET

    Connection parameter string unique to Remote Assistance. The IP address portion of this connection string is semicolon delimited, and may contain multiple IP addresses. The list of addresses may also contain the fully qualified domain name as shown above.

  3. RCTICKETENCRYPTED

    • 1 if the ticket is encrypted using a password
    • 0 if the ticket is NOT encrypted using a password
  4. DtStart

    The time the ticket was created in DATE format

  5. DtLength

    The duration of the ticket (in minutes)

  6. PassStub

    The key used to encrypt the password associated with the Remote Assistance Ticket.

  7. L

    • 1 if the user is on a modem (low bandwidth)
    • 0 if the user is NOT on a modem
  8. URA

    1 if the ticket is an Unsolicited Remote Assistance ticket or if the ticket was created by the MAILTO ActiveX control and does not contain a password. This value is only present when the value is 1 and will not be present otherwise.

Offer Remote Assistance Sample JScript

<!--
*  Copy paste this file as unsolicitedrctest.htm in 
  %windir%\PCHEALTH\HELPCTR\System
*  You will need to use type the following in start-run
*  %windir%\PCHEALTH\HELPCTR\Binaries\HelpCtr.exe -url 
hcp://services/centers/support?topic=
hcp://system/unsolicitedrctest.htm
-->
<HTML>
   <HEAD>
      <TITLE></TITLE>
      <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
   </HEAD>
    <BODY topMargin="0">
      <OBJECT id="oSAFClassFactory" height="0" width="0" 
        classid="CLSID:FC7D9E02-3F9E-11d3-93C0-00C04F72DAF7"
         VIEWASTEXT>
      </OBJECT>
      <P>&nbsp;</P>
      <P>&nbsp;</P>
      <P align="center"><FONT face="Times New Roman" 
        size="5"><STRONG>Unsolicited Remote Control 
               Unittest</STRONG></FONT></P>
      <TABLE>
         <TR>
            <TD><I size="20"> Enter Server Name to invoke 
              </I><B>ConnectRemoteDesktop </B><INPUT name="ServerName" 
                id="ServerName" style="WIDTH: 310px; HEIGHT: 20px" 
                  size="40">&nbsp;&nbsp;&nbsp;</TD>

         </TR>

         <TR>
            <TD><BUTTON id="Button1" onclick="RunScriptAPIs()"
                name="RunScriptAPIs" 
                type="button">RunScriptAPIs</BUTTON></TD>
         </TR>
      </TABLE>


<!--
-->

<SCRIPT Language="JScript">

var oSAFRemoteDesktopConnection = null;
var oSAFRemoteConnectionData    = null;
var oUsersCollection            = null;
var oSessionsCollection         = null;
var RemConnectionParms          = null;

function RunScriptAPIs()
{

  try
  {
    // Create an object of SAFRemoteDesktopConnection
    oSAFRemoteDesktopConnection = 
      oSAFClassFactory.CreateObject_RemoteDesktopConnection();

    if (oSAFRemoteDesktopConnection != null) 
    {
       // Connect to the remote desktop given by ServerName
        oSAFRemoteConnectionData = 
          oSAFRemoteDesktopConnection.ConnectRemoteDesktop
            (ServerName.value);

          //
          // Get the count for users that are logged on
          // and their UserName and DomainName
          //

          // Get the collection of all the logged on
          // users on the Server, novice, machine
          oUsersCollection = oSAFRemoteConnectionData.Users();

          // List the UsersCollection's details.
          var nUsersLen = oUsersCollection.Count;

          // For each user, display the UserName and DomainName
          for(i=1; i<= nUsersLen; i++)
          {
             oUserObj = oUsersCollection.item(i);

             // Use the ISAFUser.UserName and 
             // ISAFUser.DomainName properties
             alert("Index : " +i+" UserName : "
                +oUserObj.UserName+"DomainName : "
                +oUserObj.DomainName);
          }

          //
          // Get the UserName, DomainName, SessionID, 
          // SessionState for all the sessions for all
          // logged on users
          //

          // Get all the sessions for all logged on 
          // users on the remote, server, machine.

          oSessionsCollection = 
             oSAFRemoteConnectionData.Sessions();

          // List the SessionsCollection's details.
          var nSessionsLen = oSessionsCollection.Count;

          // Display the UserName, DomainName, 
          // SessionID, SessionState 
          // for each of the session collection objects.
          for(i=1; i<= nSessionsLen; i++)
          {
             oSessionObj = oSessionsCollection.item(i);

            // ISAFSession.UserName, ISAFSession.DomainName,
            // ISAFSession.SessionID and 
            // ISAFSession.SessionState
            alert("Index : " +i+" UserName : "
             +oSessionObj.UserName+" DomainName : "
             +oSessionObj.DomainName+" SessionID "
             +oSessionObj.SessionID+"  SessionState "
             +oSessionObj.SessionState);
          }

          //
          // Using the ServerName, UserName, DomainName and
          // SessionID call ConnectionParms() to
          // get the ticket in RemConnectionParms.
          //
          oSessionObj = oSessionsCollection.item(1);
          RemConnectionParms = 
              oSAFRemoteConnectionData.ConnectionParms(
                  ServerName.value, oSessionObj.UserName,
                  oSessionObj.DomainName, 
                  oSessionObj.SessionID, "");
          alert("SUCCEEDED :  ConnectionParms"
              +RemConnectionParms);

          // This call will cause an error in XP, 
          // but will work on Server2003
                // fConnected =      
                // oSAFRemoteConnectionData.ModemConnected
                // (ServerName.value);
                // alert("ModemConnected  = "+fConnected);

          alert("Successfully called all the APIs. \n GoodBye");
       }
          } catch(e)
          {
       alert("RunScriptAPI() threw an exception : "
              +e.description);
     }
  }
      </SCRIPT>
  </BODY>
</HTML>

Offer Remote Assistance Sample C

Description

The following code sample shows how a Remote Assistance Ticket may be generated for user sessions present on the remote (novice) machine.

//
// Replace the <...> placeholder in main() with correct data for 
  execution
//

#include <stdio.h>
#include <atlbase.h>

// Typedef for REMOTE_DESKTOP_SHARING_CLASS 
typedef enum 
{
    DESKTOPSHARING_DEFAULT   = 0x0,
   NO_DESKTOP_SHARING      = 0x1,
   VIEWDESKTOP_PERMISSION_REQUIRE      = 0x2,
   VIEWDESKTOP_PERMISSION_NOT_REQUIRE   = 0x4,
   CONTROLDESKTOP_PERMISSION_REQUIRE   = 0x8,
   CONTROLDESKTOP_PERMISSION_NOT_REQUIRE   = 0x10
} REMOTE_DESKTOP_SHARING_CLASS;

// import the type library 
 #import "HelpServiceInterfaces.tlb" rename_namespace("HSITLB") 
  named_guids raw_interfaces_only no_auto_exclude \
    rename("GetUserName", "GetUserName_Renamed")\
    rename("EncryptFile", "EncryptFile_Renamed")\
    rename("DecryptFile", "DecryptFile_Renamed")\
    rename("ULONG_PTR","ULONG_PTR1")

//+--------------------------------------------------------------------
  -------
//
//  Function:   CallRemoteUserSessionInfoAPI
//
//  Synopsis:   Call RemoteUserSessionInfo API 
//
//  Arguments:  bstrMachineName [in] - Remote MachineName
//
//---------------------------------------------------------------------
  -------
HRESULT CallRemoteUserSessionInfoAPI(
    IN BSTR bstrMachineName)
{
    HRESULT hr = S_OK;

    COSERVERINFO            si; ::ZeroMemory( &si, sizeof( si ) );
    MULTI_QI                qi; ::ZeroMemory( &qi, sizeof( qi ) );
    CComPtr<HSITLB::IPCHService>    pIsvc = NULL;
    CComPtr<IUnknown>       pIUnkEnum;
    CComPtr<IEnumVARIANT>   pIEnum;
    CComPtr<HSITLB::ISAFSession>    pIItem;
    CComPtr<IDispatch>      pIDispItem = NULL;
    CComPtr<HSITLB::IPCHCollection> pIColl = NULL;

    long lsizeCollection = 0;
    long lIndex = 0;

  // The values returned by the APIs
    BSTR bstrUserName = NULL;
    BSTR bstrDomainName = NULL;
    DWORD dwSessionID = 0;
    HSITLB::SessionStateEnum SessionState;

    CComVariant  varSAFSession;
    ULONG        ulFetched;
   
    si.pwszName = bstrMachineName;
    qi.pIID     = &(HSITLB::IID_IPCHService);

    hr = CoCreateInstanceEx(HSITLB::CLSID_PCHService, NULL, 
      CLSCTX_REMOTE_SERVER, &si, 1, &qi );
   if(hr != S_OK)  goto ErrReturn;

    // Get the IPCHService interface pointer to the pIsvc local pointer
    pIsvc.Attach((HSITLB::IPCHService*) qi.pItf );

   // RemoteUserSessionInfo() returns a pointer to IPCHCollection 
     interface 
    hr = pIsvc->RemoteUserSessionInfo(&pIColl);
   if(hr != S_OK)  goto ErrReturn;

    if(pIColl == NULL)
    {
      // Display the error code
      printf("\n The novice has Unsolicited turned off");
      goto ErrReturn;
    }

    hr = pIColl->get_Count(&lsizeCollection);
   if(hr != S_OK)  goto ErrReturn;

    hr = pIColl->get__NewEnum(&pIUnkEnum);
   if(hr != S_OK)  goto ErrReturn;

    hr = pIUnkEnum.QueryInterface(&pIEnum);
   if(hr != S_OK)  goto ErrReturn;

    if(lsizeCollection == 0)
    {
      // Display the error code
      printf("\n There are no users logged into this machine");
      goto ErrReturn;                                   
    }

   printf("\n\t RemoteUserSessionInfo \n");

    // For each distinct User Session on the machine you are pointing 
      at    
    // get their info
    for (lIndex=0; lIndex < lsizeCollection; lIndex++)
    {
        // Display the list of session and user info
        hr = pIEnum->Next( 1, &varSAFSession, &ulFetched);
        if ((hr == S_FALSE) || (ulFetched != 1))
        {
            printf("\n Next failed!  No more records!");
            break;
        }

        pIDispItem = varSAFSession.pdispVal;

        hr = pIDispItem.QueryInterface(&pIItem);
      if(hr != S_OK)  goto ErrReturn;
                
        hr = pIItem->get_UserName(&bstrUserName);
      if(hr != S_OK)  goto ErrReturn;
        
        hr = pIItem->get_DomainName(&bstrDomainName);
      if(hr != S_OK)  goto ErrReturn;

        hr = pIItem->get_SessionID(&dwSessionID);
      if(hr != S_OK)  goto ErrReturn;

        hr = pIItem->get_SessionState(&SessionState);
      if(hr != S_OK)  goto ErrReturn;

        printf("\n MachineName = %S, \n UserName = %S, \n DomainName = 
          %S, \n SessionID = %u, \n SessionState = %d", 
           bstrMachineName, bstrUserName, bstrDomainName, dwSessionID, 
            SessionState);
    }

 ErrReturn:

    if(hr != S_OK)
       printf("ERROR in CallRemoteUserSessionInfoAPI");

    return hr;

}

//+--------------------------------------------------------------------
  -------
//
//  Function:   CallRemoteConnectionParmsAPI
//
//  Synopsis:   Call RemoteConnectionParms API generate a SALEM Ticket.
//
//  Arguments:  bstrMachineName  [in] - Remote MachineName
//               bstrUserName     [in] - User Name  
//                bstrDomainName   [in] - Domain Name
//             lSessionID       [in] - SessionId
//               bstrUserHelpBlob [in] - User Help Blob
//
//---------------------------------------------------------------------
  -------

HRESULT CallRemoteConnectionParmsAPI(
    IN BSTR bstrMachineName,
    IN BSTR bstrUserName,  
    IN BSTR bstrDomainName,  
    IN long  lSessionID,  
    IN BSTR bstrUserHelpBlob)
{
    HRESULT hr = S_OK;

    COSERVERINFO            si; ::ZeroMemory( &si, sizeof( si ) );
    MULTI_QI                qi; ::ZeroMemory( &qi, sizeof( qi ) );
    CComPtr<HSITLB::IPCHService>    pIsvc;
    BSTR bstrGeneratedSALEMTicket = NULL;

    si.pwszName = bstrMachineName;
    qi.pIID     = &(HSITLB::IID_IPCHService);

    hr = ::CoCreateInstanceEx(
              HSITLB::CLSID_PCHService, NULL, CLSCTX_REMOTE_SERVER, 
                &si, 1, &qi );
   if(hr != S_OK)  goto ErrReturn;

    // Get the IPCHService interface pointer to the pIsvc local pointer
    pIsvc.Attach((HSITLB::IPCHService*)qi.pItf );

   printf("\n\n\t RemoteConnectionParms \n");
    printf("\n MachineName = %S \n UserName = %S \n DomainName = %S \n 
      lSessionID = %d \n UserHelpBlob = %S", 
       bstrMachineName, bstrUserName, bstrDomainName, lSessionID, 
         bstrUserHelpBlob);

   // Generate the Ticket
    hr = pIsvc->RemoteConnectionParms(
        bstrUserName, 
        bstrDomainName, 
        lSessionID, 
        bstrUserHelpBlob, 
        &bstrGeneratedSALEMTicket);
   if(hr != S_OK)  goto ErrReturn;
    
    printf("\n The Ticket generated by RemoteConnectionParms: \n %S 
      \n", 
          bstrGeneratedSALEMTicket);      

   // Get the modem status   
   // This call will fail for Remote Machine that is XP. It will work 
     for Server 2003
   // VARIANT_BOOL bModemConnected = FALSE;
   // hr = pIsvc->get_RemoteModemConnected(&bModemConnected);   
   // if(hr != S_OK)  goto ErrReturn;
   // printf("\n The ModemConnected = %d", bModemConnected);

 ErrReturn:

    if(hr != S_OK)
       printf("ERROR in CallRemoteConnectionParmsAPI");

    return hr;

}

//+--------------------------------------------------------------------
  -------
//
//  Function:   main
//
//  Synopsis:   Entry point for API Tests
//
//---------------------------------------------------------------------
  -------
int __cdecl main (int argc, char *argv[])
{  
    HRESULT hr = S_OK;

   // Replace the <...> placeholder with correct data
    BSTR bstrMachineName  = SysAllocString(L"<machine name>");
    BSTR bstrUserHelpBlob = SysAllocString(L"<help blob>");
    BSTR bstrUserName     = SysAllocString(L"<user name>");
    BSTR bstrDomainName   = SysAllocString(L"<domain name>");
   long lSessionID = 0;

    hr = CoInitializeEx(NULL,COINIT_APARTMENTTHREADED);
   if(hr != S_OK)  goto ErrReturn;

   // Call RemoteUserSessionInfo API
   hr = CallRemoteUserSessionInfoAPI(bstrMachineName);
   if(hr != S_OK)  goto ErrReturn;

    // Call RemoteConnectionParms API
    hr = CallRemoteConnectionParmsAPI(
          bstrMachineName, 
          bstrUserName, 
          bstrDomainName, 
          lSessionID, 
          bstrUserHelpBlob);
   if(hr != S_OK)  goto ErrReturn;

   printf("\n Successfully called both APIs RemoteUserSessionInfo and 
     RemoteConnectionParms. \n\n");

ErrReturn:

    CoUninitialize();
   
    return 0;
    
} // main