  Winsock RCMD32.DLL                                 Version 1.8
  Copyright 1994 Denicomp Systems
  All rights reserved
  
  DESCRIPTION
  -----------
  
  Winsock RCMD32.DLL is a Microsoft Win32 Dynamic Link Library (DLL) that
  provides a Windows Sockets compatible function that allows you to execute
  commands on a remote host and retreive the output of those commands for
  processing by your program.

  The function calls in Winsock RCMD32.DLL are source code compatible with
  the 16-bit Winsock RCMD.DLL for Windows 3.1.

  Winsock RCMD32.DLL is similar to the "rcmd" library function found on many
  Unix systems.  The DLL includes the main WinsockRCmd function, plus a
  function to receive data over the connection from the remote host executing
  the command.
  
  The remote host must be a system running the rshd or rexecd daemon process,
  such as a Unix system or a PC running Denicomp Systems' Winsock RSHD for
  Windows 3.1, Winsock RSHD/95 for Windows 95, or Winsock RSHD/NT Service
  for Windows NT.

  Winsock RCMD32.DLL is designed to execute non-interactive remote commands.
  If you need to execute an interactive command on the remote host, use a
  utility like Telnet.
  

  REQUIREMENTS
  ------------

  Winsock RCMD32.DLL requires a PC running Windows NT, Windows 95, or other
  Windows operating system supporting WSOCK32.DLL, a TCP/IP connection to
  a remote host running rshd, and any programming language that supports
  calls to 32-bit DLL functions, such as C.


  =============================================================================

  FUNCTION: WinsockRCmd

  Syntax:
  -------

  C:
  
  INT PASCAL WinsockRCmd (Host, Port, LocalUser, RemoteUser,
                          Cmd, ErrorMsg, ErrorMsgLen)
       char *Host;
       int Port;
       char *LocalUser;
       char *RemoteUser;
       char *Cmd;
       char *ErrorMsg;
       int ErrorMsgLen;
  

  *Visual Basic:

  Declare Function WinsockRCmd Lib "RCMD32.DLL"
              (ByVal Host As String,
               ByVal Port As Long,
               ByVal LocalUser As String,
               ByVal RemoteUser As String,
               ByVal Cmd As String,
               ByVal ErrorMsg As String,
               ByVal ErrorMsgLen As Long) As Long


  * - for Visual Basic languages that can call 32-bit DLL's


  Powerbuider: (Global External Function Window)

  Function Int WinsockRCmd (Ref String Host, &
                            Int Port, &
                            Ref String LocalUser, &
                            Ref String RemoteUser, &
                            Ref String Cmd, &
                            Ref String ErrorMsg, &
                            Int ErrorMsgLen) Library "RCMD32.DLL"

  Usage:
  ------
  
  The WinsockRCmd function initiates a connection to the remote host
  and executes the specified command if access is permitted.  You can
  then use the RCmdRead or RCmdReadByte function to receive the standard
  output and standard error output of the command.
  
  Parameters:
  -----------

  Host:        Specifies the name of a remote host that is listed in the 
               "hosts" file.  You will receive an error if the host name
               is not found in the hosts file or the hosts file itself
               is not found.
  
  Port:        Specifies the port to use for the connection.  Normally, this
               is the well-known port number of 514 for the rshd daemon.  You
               may also use port 512 for the rexecd daemon.

               The rshd and rexecd daemons are very similar.  The only
               difference is that the rexecd daemon requires you to specify
               the user's correct password in the RemoteUser parameter.
  
  LocalUser:   The user name of the user on the local host (the PC).  This can
               be NULL or an empty string if you want Winsock RCMD32.DLL to
               determine the name.  The method it uses is described later.

               This name is sent as the local user to the rshd daemon on the
               remote host.  In general, it should be the same as the
               RemoteUser parameter.

  RemoteUser:  When using rshd (port 514), this is the user name to be used at
               the remote host.  This must be a valid user name at the remote
               host.  It can be NULL or an empty string if you want Winsock
               RCMD32.DLL to detemrine the name.  The method is uses it
               described later.

               When using rexecd (port 512), you must specify the password
               of the user specified in the LocalUser parameter here.  If
               the password is invalid, an error will be returned from
               the remote host.

               Note that your program is responsible for supplying the
               password.  For example, you could ask the user to enter it
               or it could be stored in a configuration file.  Winsock
               RCMD32.DLL will not request it.
              
  Cmd:         The command to be executed at the remote host.
  
  ErrorMsg:    A pointer to an area that can be used to store an error
               message from WinsockRCmd.  If an error occurs while connecting
               to the remote host, WinsockRCmd will return a negative result
               and will store an error message here.  You can then optionally
               use the error message as diagnostic output in your program.

  ErrorMsgLen: The maximum length of the error message returned.  This is
               the number of characters available in ErrorMsg.
  

  Return Value:
  -------------

  If WinsockRCmd sucessfully connects to the remote host and begins executing
  the specified command, it will return an integer "handle" that must be used
  by the RCmdRead, RCmdReadByte, RCmdHandle, and RCmdClose functions.  This
  handle will always be greater than or equal to zero.

  If WinsockRCmd is not successful, it will return a negative number.  If the
  number is -1, an error internal to WinsockRCmd occurred and a message
  describing the error will be stored in ErrorMsg.  If the number is not -1,
  it is a Windows Sockets error code (but negative).  ErrorMsg will also
  contain a message attempting to describe the error.
  
  Notes:
  ------

  The handle MAY be zero; if fact, most times it will be zero.  Do not
  treat a handle value of zero as an error.  Errors will return negative
  values.
  
  If the host name specified in the Host parameter is invalid, you will
  normally receive a Windows Sockets error -11004.  This means that either
  the host name is not in the hosts file or the hosts file does not exist.
  Refer to the Windows manual for infomation about setting up a hosts file.

  The error message returned in ErrorMsg may contain newline characters
  (ASCII 10).  The message is suitable for display using the standard
  Windows MessageBox function or the Visual Basic MsgBox command/function.

  Execution of interactive commands (commands requiring keyboard input) is
  not supported.

  Unlike the Unix "rcmd" function, WinsockRCmd does not have the ability
  to separate the standard output and the standard error output of the
  remote command.  Output to the standard output and standard error will
  be intermixed.
  

  Declaring the Winsock RCMD32.DLL Functions in Other Languages
  -------------------------------------------------------------

  Declarations are provided for the functions in RCMD32.DLL for C, Visual
  Basic, and Powerbuilder.

  If you are using some other language, please keep in mind that the
  ErrorMsg parameter in the WinsockRCmd() function call and the DataIn
  parameter in the RCmdRead() function call are modified by the DLL.

  Be sure when creating declarations in your language for the RCMD32.DLL
  functions you keep this in mind.  Some languages require that you specify
  which parameters passed to DLL's will be modified and should be passed as
  a pointer, not as a copy of the variable value in the program.  You may
  receive errors if you do not do this.


  Languages using Dynamically Allocated Strings
  ---------------------------------------------

  The WinsockRCmd function requires you to pass a pointer to an area of
  memory to store an error message should an error occur (the ErrorMsg
  parameter).  Also, the RCmdRead function (explained later) requires you
  to pass a pointer to an area of memory that will hold the data returned
  from the command executed on the remote host.

  When using C, you can simply pass a pointer to a statically allocated
  char array (for example, char errmsg[128]) or a pointer to a dynamically
  allocated area of memory (using malloc() or HeapAlloc()).

  When using a language that uses dymamically allocated strings, such as
  Visual Basic or Powerbuilder, you must first "force" memory to be allocated
  to a string before passing it as the ErrorMsg parameter in WinsockRCmd()
  or as the DataIn parameter in RCmdRead().  If you do not do this, your
  program will most likely abort with an error.

  This can be done in Visual Basic with the String$() function.  For example,
  to allocate 128 bytes in the string EMsg, use the following:

            Dim EMsg As String

            EMsg = String$(128,Chr$(0))


  This will allocate 128 bytes in EMsg and fill it with null characters.  If
  you just tried to use EMsg without doing this, your program would abort
  with a memory access error if WinsockRCmd tried to store an error message
  in EMsg.

  Powerbuilder has a similar requirement, but you use the Space() function.
  For example:

            String EMsg

            EMsg = Space(128)

  This will allocate 128 bytes in EMsg and fill it with space characters.
  This is acceptable because WinsockRCmd() will return the error message
  as a null-terminated string.  However, when using RCmdRead(), be sure to
  only use the number of bytes specified by the return value of the function
  or you will find unwanted spaces in your data stream.

  

  Windows Sockets "Shutdown" or "Connection Refused" Errors
  ---------------------------------------------------------

  Occasionally, you may experience Windows Sockets "Shutdown" errors (-10058)
  or "Connection Refused" errors (-10061) from the WinsockRCmd function call
  in definite patterns.  For example, you may receive the error every other
  call or every third call.  If you do, you should make provisions in your
  program to retry the call to WinsockRCmd on the error (with a limit on the
  number of retries to avoid an endless loop on a true problem).  For example:

  
       #define MAX_RETRIES 10

       int retries = 0;
       do {
            result = WinsockRCmd(...);
            ++retries;
       } while ((result == -10058 || result == -10061) &&
                retries < MAX_RETRIES);


  Using the rexecd Daemon
  -----------------------

  If you wish to use the rexecd daemon instead of rshd, specify a port number
  of 512 instead of 514 in the WinsockRCmd function call.

  The rexecd daemon is similar to the rshd server, except that it requires
  a password to be supplied.  You must make provisions in your software to
  allow the user to enter the password at the appropriate time or retrieve
  it from some storage area (i.e. an initialization file).  Winsock RCMD32.DLL
  does NOT ask for the password.

  When using rexecd, in addition to using port 512 instead of 514, you must
  pass the password in the RemoteUser parameter in the WinsockRCmd function
  call (the fourth parameter).  All other parameters and the remaining
  functions described in this manual function in the same manner.
       

  =============================================================================

  FUNCTION: RCmdRead


  Syntax:
  -------

  C:
  
  INT PASCAL RCmdRead (hRCmd, DataIn, DataLen)
       int hRCmd;
       char *DataIn;
       int DataLen;


  Visual Basic:

  Declare Function RCmdRead Lib "RCMD32.DLL"
              (ByVal hRCmd As Long,
               ByVal DataIn As String,
               ByVal DataLen As Long) As Long


  Powerbuilder:

  Function Int RCmdRead (Int hRCmd, &
                         Ref String DataIn, &
                         Int DataLen) Library "RCMD32.DLL"


  Usage:
  ------
  
  The RCmdRead function reads the output of the command executed with
  WinsockRCmd.  This allows you to capture the standard output and
  standard error output of the command you executed.

  Parameters:
  -----------

  hRCmd:      This is the integer "handle" returned from the WinsockRCmd
              function.

  DataIn:     A pointer to an area of memory to hold the data received.

  DataLen:    The maximum number of bytes to receive.  This must be an
              integer between 1 and 8192.


  Return Value:
  -------------

  If RCmdRead is sucessful, it returns the number of bytes read.  It will be
  a number greater than zero.

  If RCmdRead returns zero, there is no more data to read.  The command has
  ended and you should call RCmdClose to terminate the connection.

  If RCmdRead returns a negative number, an error has occurred.  The number
  will be the Windows Sockets error number (but negative).  You should call
  RCmdClose even if an error occurs to free all resources used by the
  connection.

  On a successful call to RCmdRead (a positive return value), you should only
  use the number of bytes specified by the return value of this function in the
  memory pointed to by the DataIn parameter.  It is not guaranteed that the
  data returned will be null-terminated.

  =============================================================================


  FUNCTION: RCmdReadByte

  Syntax:
  -------

  C:
  
  INT PASCAL RCmdReadByte(hRCmd)
       int hRCmd;


  Visual Basic:

  Declare Function RCmdReadByte Lib "RCMD32.DLL"
              (ByVal hRCmd As Long) As Long


  Powerbuilder:

  Function Int RCmdReadByte (Int hRCmd) Library "RCMD32.DLL"



  Usage:
  ------
  
  The RCmdReadByte function, like the RCmdRead function, reads the output of
  the command executed with WinsockRCmd.  However, it instead reads one
  character at a time and returns each character as an integer return value.

  The RCmdReadByte function is useful when it is not possible or not
  convenient to pass a pointer to an area of memory as required by RCmdRead.  


  Parameters:
  -----------

  hRCmd:      This is the integer "handle" returned from the WinsockRCmd
              function.



  Return Value:
  -------------

  If RCmdReadByte is sucessful, it returns the next character read from the
  standard output or standard error of the command executed.  It will be a
  positive number between 1 and 255.

  If RCmdReadByte returns zero, there is no more data to read.  The command
  has ended and you should call RCmdClose to terminate the connection.

  If RCmdReadByte returns a negative number, an error has occurred.  The
  number will be the Windows Sockets error number (but negative).  You should
  call RCmdClose even if an error occurs to free all resources used by the
  connection.


  =============================================================================

  Syntax:
  -------

  C:
  
  INT PASCAL RCmdClose (hRCmd)
       int hRCmd;


  Visual Basic:

  Declare Function RCmdClose Lib "RCMD32.DLL"
       (ByVal hRCmd As Long) As Long


  Powerbuilder:

  Function Int RCmdClose (Int hRCmd) Library "RCMD32.DLL"


  Usage:
  ------
  
  The RCmdClose function closes the connection to the remote host and
  frees all resources used by the connection.  You should call RCmdClose
  for each successful use of the WinsockRCmd function.


  Parameters:
  -----------

  hRCmd:      This is the integer "handle" returned from the WinsockRCmd
              function.


  Return Value:
  ------------

  If RCmdClose is sucessful, it returns zero.  If it is unsuccessful, it
  returns a negative number that is the Windows Sockets error number (but
  negative).


  =============================================================================

  Syntax:
  -------

  C:
  
  INT PASCAL RCmdHandle (hRCmd)
       int hRCmd;


  Visual Basic:

  Declare Function RCmdHandle Lib "RCMD32.DLL"
       (ByVal hRCmd As Long) As Long


  Powerbuilder:

  Function Int RCmdHandle (Int hRCmd) Library "RCMD32.DLL"


  Usage:
  ------
  
  The RCmdHandle function returns the Windows Sockets handle for the
  connection, which can be used to call any Windows Socket function.


  Parameters:
  -----------

  hRCmd:      This is the integer "handle" returned from the WinsockRCmd
              function.


  Return Value:
  -------------

  RCmdHandle returns a Windows Sockets handle.  If you call RCmdHandle on
  a WinsockRCmd connection that is not opened, the return value is undefined.

  Keep in mind that the handle returned by the WinsockRCmd function is a
  handle internally used by Winsock RCMD32.DLL; it is NOT a handle to be
  used to call Windows API functions.  You must obtain the actual handle
  of the socket using this function to use the Winsock API calls on the
  socket created by WinsockRCmd.


 ============================================================================

  DETERMINING THE USER NAME
  -------------------------

  You can pass a NULL in either the LocalUser or RemoteUser parameters in
  the WinsockRCmd function call and Winsock RCMD32.DLL will determine the
  user name.  The following ONLY applies if you pass a NULL in one of these
  parameters.

  The local user name is normally the name you used when logging in to
  Windows NT or Windows 95.  For example, if you logged in to Windows as
  the user "joed", WinsockRCmd will use "joed" as the user name at the remote
  host.  WinsockRCmd will always convert this name to all lowercase characters.

  To maintain some compatibility with the 16-bit version, WinsockRCmd will
  also continue to look at the file WIN.INI in the Windows directory
  (e.g. \WINNT35 or \WIN95) for an alternate user name.
  
  If WIN.INI contains a section named "[RCMD]" and contains an entry named
  "User" in that section, the name specified there will be used as the local
  user name.  For example, WIN.INI might contain:

     [RCMD]
     User=joe

  If this appeared in WIN.INI, the local user name would be "joe" and
  WinsockRCmd would use this name at the remote host.

  To support multiple users, WinsockRCmd will also look for a section named
  "[RCMD-user]" in WIN.INI first for an alternate user name, where the "user"
  in the section name is the name used to log in to Windows NT/95.  WinsockRCmd
  will look at this section first; if it does not exist, it will then look
  at the "[RCMD]" section.

  For example, if "Mary Jones" and "John Smith" are both users on the Windows
  PC, but their user names at the remote host are "mary" and "john"
  respectively, WIN.INI might look like this:

     [RCMD-John Smith]
     User=john

     [RCMD-Mary Jones]
     User=mary

  When the Windows user "John Smith" runs a program using WinsockRCmd, "john"
  will be used at the remote host.  When the Windows user "Mary Jones" runs
  the program, "mary" will be used instead.

  Please note that the use of WIN.INI is supported only to maintain some
  compatibility with the 16-bit version of RCMD.DLL.  It is highly recommended
  that you maintain the same user names across your systems.



  SECURITY
  --------
  
  Security is enforced at the *HOST* system, not by Winsock RCMD32.DLL.  If
  you are receiving errors pertaining to security, such as "Login incorrect"
  or "Connection refused", the problem is most likely on the host system.

  If the host system is a Unix system and you are using port 512 (the rshd
  daemon), the remote host allows access only if at least one of the following
  conditions is satisfied:
  
  * The name of the local host is listed as an equivalent host in the
    /etc/hosts.equiv file on the remote host.

    The name of the local host must also be listed in the /etc/hosts file
    along with it's proper IP address.

  * If the local host is not in the /etc/hosts.equiv file, the user's home
    directory on the remote host must contain a .rhosts file that lists the
    local host and user name.
  
    The .rhosts file in the user's home directory must be owned by either
    the user specified or root, and must have permissions of 0600.
  
  * The user's login on the remote host does not require a password.

  If you are using port 514 (the rexecd daemon), then the above conditions
  do not apply.  You must simply supply the correct password for the user.


  EXAMPLE - C
  -----------

  For a complete example of the use of WinsockRCmd in C, see the CRSH
  program provided in the distribution.

  See the file PB.DOC for an example using Powersoft's Powerbuilder.


  // We will let WinsockRCmd user our login name as the user name by
  // passing NULL values.

     int hRCmd;
     char c;
     char *rhost = "unix486";
     char *cmd = "ls -x /usr"
     char errmsg[128];

     hRCmd = WinsockRCmd(rhost,514,NULL,NULL,cmd,errmsg,sizeof(errmsg));

     if (hRCmd < 0)
       MessageBox(NULL,errmsg,"Remote Shell",MB_OK);
     else
     {
	while(RCmdRead(hRCmd,&c,1) > 0)
          putchar(c);
     }

     RCmdClose(hRCmd);


  EXAMPLE - VISUAL BASIC
  ----------------------


  This requires that you use a version of Visual Basic that can call 32-bit
  DLL functions.


     Host$ = "unix486"
     User$ = "fred"
     Cmd$ = "cat /etc/inittab"

     EMsg$ = String$(128,Chr$(0))    ' Allocate 128 bytes
     ELen% = 128
     RetryCount% = 0

    ' Execute the command.  We will retry on the Winsock errors ' -10058 and
    ' -10061.  These are the Shutdown and Connection Refused errors that can
    ' be returned occasionally.  A retry will usually give success.
     Do
        hRCmd% = WinsockRCmd(Host$, 514, User$, User$, Cmd$, EMsg$, ELen%)
        RetryCount% = RetryCount% + 1
     Loop While (hRCmd% = -10058 Or hRCmd% = -10061) And RetryCount% < 10

     If hRCmd% < 0 Then
       Beep
       e$ = "Command Was Unsuccessful" & Chr$(10) & Trim$(EMsg$)
       MsgBox e$
       Exit
     End If

     Do
        c$ = String$(64, Chr$(0))
        result% = RCmdRead(hRCmd%, c$, 64)
        If result% > 0 Then
          Print Left$(c$, result%);
        End If
     Loop While result% > 0
 
     result% = RCmdClose(hRCmd%)
 
     MsgBox "Command Was Successful"


SUPPORT
-------

Support is available via U.S. Mail and Compuserve/Internet:

Denicomp Systems
P.O. Box 731
Exton, PA  19341

Compuserve:  71612,2333
Internet:    71612.2333@compuserve.com

