//--------------------------------------------------------------------------
// TSmPort.cpp   =   Implementation of the TSmPort class in C++
//                   (for Borland C++ Builder V4).
//    Date:    2004-01-14  (YYYY-MM-DD)
//    Author:  Wolfgang Buescher ("WoBu")  - dl4yhf@freenet.de .
//    Path:    \\Wobu_p4\c\CBproj\SMPORT\TSmPort.cpp
//             ("master", everything else is just a COPY)
//
//  Based on the DELPHI COMPONENT "SmallPort.pas" :
//   > SmallPort ver 1.4 for Delphi 4 modified 21-August-2000
//   >  Windows NT/2000 compatible
//   >  Author: Alexander Weitzman (alweit1@hotmail.com)
//  This was later (2005-11) changed to support the PortAccess driver
//     by Craig Peacock, which works faster because of a different principle.
//
//  This unit allows direct hardware access under Win95/98/NT/ME/2000 .
//
//  Copy file "smport.vxd" to "Windows\System" directory or to directory
//                         of your application for Win 9x
//         or "smport.sys" to "WinNT\SYSTEM32" (or to application folder).
//
//
//  History: Date(YYYY-MM-DD)  By    Remarks
//
//    2009-06-04      PCh
//           Ported to wxWidgets
//    2005-11-05      WoBu
//           This module -through still called TSmPort.cpp- can now
//           optionally use the PortTalk driver (instead of SMPORT.SYS)
//           to grant access to certain I/O-ports. The principle is different then:
//           Instead of calling the driver FOR EVERY SINGLE PORT ACCESS,
//           the PortTalk driver is only called once (to modify the IOPM),
//           which works faster than using SMPORT but *may* cause problems
//           with some OS'es .
//              ! Due to copyright issues with PortTalk/AllowIO,          !
//              ! the new functions are located in a separate module      !
//              ! called AllowIoWrapper.cpp, which can NOT be distributed !
//              ! without Craig Peacock's permission ! Search the net for !
//              ! the "allowio.c" soucecode if you feel the need to know  !
//              ! what all this is about ;-)                              !
//           To compile TSmPort.cpp if you don't have AllowIoWrapper.c ,
//           remove the macro USE_PORTTALK from the compiler settings
//          (BCB V4: Project..Options..Directories/Conditionals..Cond'l defines)
//        Since BCB "Standard" does not include TASM32, and I didn't want to
//        "find it away" somewhere, NASM is used to assemble the inport-
//        and outport-subroutines. See 'inoutport.asm' for details .
//
//    2004-01-14      WoBu
//           Ugly fix for already defined macro 'FILE_READ_ACCESS'
//           which caused problems when compiling with BCB V6 .
//
//    2003-01-26      WoBu
//           Added some 'debugging stuff' like sz255LastErrorString[]
//           + ONE alternative path to the driver may be specified
//             as parameter in OpenDriver()
//
//    2002-01-30      WoBu
//           Conversion from A.Weitzmanns DELPHI COMPONENT
//           into a C++ CLASS without any Borland-specific stuff.
//           So this is not a VCL component like the original,
//           just a simple C++ class which may work under MSC as well.
//           Later removed TBytePort, TWordPort, TDWordPort from the class
//           definition, because variables of these types were only used as
//           temporary local variables !
//            First used in:
//              - WoBu's  "Spectrum Laboratory"
//              - WoBu's  "PIC Programmer for Windows"
//
//--------------------------------------------------------------------------

#include <Windows.h>
#include <tchar.h>
#include <wx/intl.h>

#include "TSmPort.h"

#ifdef USE_PORTTALK  /* AllowIoWrapper.cpp included ? (see Compiler Options..Cond'l Defines) */
 extern "C" bool AllowIoForAllPorts(char * pszOneMorePathToDriver);
 extern char AllowIo_sz255Msg[256]; // message from AllowIoWrapper (for debugging)
 extern "C" WORD asm_inportb( WORD wPort );              // in inoutport.asm
 extern "C" void asm_outportb( WORD wPort, WORD wValue); // in inoutport.asm
#endif

//--------------------------------------------------------------------------
// Internal Data Types
//--------------------------------------------------------------------------

typedef struct // DO NOT MODIFY - this struct is passed to VXD or SYS driver
{              //  (not for the user, but we need it in the TSmPort class)
  WORD wAddress;
  BYTE bValue;
} TBytePort;

typedef struct // DO NOT MODIFY - this struct is passed to VXD or SYS driver
{
  WORD wAddress;
  WORD wValue;
} TWordPort;

typedef struct // DO NOT MODIFY - this struct is passed to VXD or SYS driver
{
  WORD wAddress;
  DWORD dwValue;
} TDWordPort;


//---------------------------------------------------------------------------
// Implementation of the TSmPort Class
//---------------------------------------------------------------------------

// Migration from Borland C++Builder V4 to V6:
//   "CTL_CODE" used somewhere else, so renamed it to "MakeCtlCode"
// ex: DWORD CTL_CODE(DWORD dwDeviceType, DWORD dwFunc, DWORD dwMeth, DWORD dwAccess)
DWORD MakeCtlCode(DWORD dwDeviceType, DWORD dwFunc, DWORD dwMeth, DWORD dwAccess)
{
  return (dwDeviceType << 16) | (dwAccess << 14) | (dwFunc << 2) | (dwMeth);
}


//---------------------------------------------------------------------------
// ex: constructor TSmallPort.Create(aOwner: TComponent);  but this no VCL component any longer (!)
TSmPort::TSmPort()
{
 m_iUseWhichDriver = PORTACCESS_SMPORT;  // by default, use the "old" driver (=SMPORT)
#ifdef USE_PORTTALK  /* AllowIoWrapper.cpp included ? */
 m_fUseDirectAccess= false; // by default, direct port access is NOT possible !
 m_fDirectAccessFailed=false;
#endif
 m_hSmPortDevice = (SC_HANDLE)INVALID_HANDLE_VALUE;
 hManager = 0;
 _tcscpy(m_sz64DriverName, _T("Smport"));
 szAlternativeDriverPath[0] = 0;  // path where we actually FOUND a driver
 fRemoveDevice = true;
 fStopService  = true;
 m_dwWinVer = GetWindowsVersion();
 if( (m_dwWinVer==VER_WINDOWS_NT) || (m_dwWinVer==VER_WINDOWS_2000) ) // also Win XP
  {
   dwReadByteCode   = MakeCtlCode(SMALLPORT_TYPE, 0x901, METHOD_BUFFERED, FILE_READ_ACCESS);
   dwWriteByteCode  = MakeCtlCode(SMALLPORT_TYPE, 0x902, METHOD_BUFFERED, FILE_WRITE_ACCESS);
   dwReadWordCode   = MakeCtlCode(SMALLPORT_TYPE, 0x903, METHOD_BUFFERED, FILE_READ_ACCESS);
   dwWriteWordCode  = MakeCtlCode(SMALLPORT_TYPE, 0x904, METHOD_BUFFERED, FILE_WRITE_ACCESS);
   dwReadDWordCode  = MakeCtlCode(SMALLPORT_TYPE, 0x905, METHOD_BUFFERED, FILE_READ_ACCESS);
   dwWriteDWordCode = MakeCtlCode(SMALLPORT_TYPE, 0x906, METHOD_BUFFERED, FILE_WRITE_ACCESS);
  }
 else   // here for W95 and W98 :
  {
   dwReadByteCode   = 2;
   dwWriteByteCode  = 3;
   dwReadWordCode   = 4;
   dwWriteWordCode  = 5;
   dwReadDWordCode  = 6;
   dwWriteDWordCode = 7;
  }
 dwLastError = 0;
} // end TSmPort constructor

//---------------------------------------------------------------------------
// ex: destructor TSmallPort.Destroy;
TSmPort::~TSmPort()
{
  if(m_hSmPortDevice != INVALID_HANDLE_VALUE)
     CloseDriver();
  if( (m_dwWinVer==VER_WINDOWS_NT) || (m_dwWinVer==VER_WINDOWS_2000) )
   {
    OpenServiceManager();
    if (hManager != 0)
     {
      if (fStopService)  StopSmallPortService();
      if (fRemoveDevice) RemoveSmallPortDriver();
      CloseServiceManager();
     }
   } // end if <WinNT or Win2000>
  // ex: inherited Destroy;
} // end TSmPort destructor


//---------------------------------------------------------------------------
bool TSmPort::OpenDriver(   // must call this ONCE before accessing a port
        int iUseNewDriver,            // 0=SMPORT, 1=PortTalk, ...
        const TCHAR * szOneMorePathToDriver) // .. if driver neither found in current nor in system directory
{
 TCHAR szBuf[MAX_PATH+16]; // ex: var buf: PChar;
 TCHAR *psz;
  // ex: s: string;
 bool fResult = false;

 szBuf[0] = '\0';

 if( szOneMorePathToDriver != NULL )
  {
   _tcsncpy(szAlternativeDriverPath, szOneMorePathToDriver, MAX_PATH);
   szAlternativeDriverPath[MAX_PATH] = 0;
  }

 if(m_hSmPortDevice != INVALID_HANDLE_VALUE)
    CloseDriver();

#ifdef USE_PORTTALK  /* AllowIoWrapper.cpp included ? */
  m_iUseWhichDriver = iUseNewDriver;
  m_fDirectAccessFailed = false;
  if( m_iUseWhichDriver == PORTACCESS_PORTTALK ) // use PortTalk instead of SMPORT ?
   { // use PortTalk and a "AllowIO"-like subroutine
     // to modify the I/O-permission bitmap for much faster access ?
     // Note: PortTalk does not work with Win95/98 !
     if( (m_dwWinVer==VER_WINDOWS_NT) || (m_dwWinVer==VER_WINDOWS_2000) ) // .. or XP
      {
        fResult = AllowIoForAllPorts(szOneMorePathToDriver); // <- located in AllowIoWrapper.c
        _tcsncpy( sz255LastErrorString, AllowIo_sz255Msg, 255 );
        m_fUseDirectAccess = fResult; // hopefully true now ;-)
      }
     else // not NT/2000/XP ...
      {
        m_fUseDirectAccess = true; // Win95 + Win98 : PortTalk impossible/unnecessary
        fResult = true;
      }
   }
  else  // don't use the "new" PortTalk driver, what else...
  if( m_iUseWhichDriver == PORTACCESS_NO_DRIVER ) // use PortTalk instead of SMPORT ?
   { // Don't use any port-access driver at all ... assume access already granted !
     // (if not granted, Win XP will kill us in a few microseconds..)
     m_fUseDirectAccess = true; // assume access already granted (by OS or AllowIO)
     fResult = true;
   }
  else // neither PortTalk nor "NoDriver", so instead use the old SMPORT ?
#endif  // USE_PORTTALK
  if(m_iUseWhichDriver == PORTACCESS_SMPORT )
   {
     if( (m_dwWinVer==VER_WINDOWS_NT) || (m_dwWinVer==VER_WINDOWS_2000) )
      {
        OpenServiceManager();
        if(dwLastError == ERROR_SUCCESS)
         {
           InstallSmallPortDriver();
           if ( dwLastError != ERROR_SUCCESS )
            {
              if ( dwLastError == ERROR_SERVICE_EXISTS )
               {
                 fRemoveDevice = false;
                 dwLastError = ERROR_SUCCESS;
               }
              else CloseServiceManager();
            }
           if (dwLastError == ERROR_SUCCESS)
            {
              StartSmallPortService();
              if (dwLastError != ERROR_SUCCESS)
               {
                 if ( dwLastError == ERROR_SERVICE_ALREADY_RUNNING )
                  {
                    fStopService = false;
                    dwLastError  = ERROR_SUCCESS;
                  }
               }
              CloseServiceManager();
              if (dwLastError == ERROR_SUCCESS)
               {
                 _tcscpy(szBuf,_T("\\\\.\\"));
                 _tcscat(szBuf, m_sz64DriverName);
                 m_hSmPortDevice = CreateFile( szBuf, // null-terminated driver name
                               GENERIC_READ | GENERIC_WRITE,
                               0,
                               NULL,
                               OPEN_EXISTING,
                               FILE_ATTRIBUTE_NORMAL,
                               0);
                 if (m_hSmPortDevice != INVALID_HANDLE_VALUE)
                     fResult  = true;
                 else
                     dwLastError = GetLastError();
               }
            }
         } // end if < SMPORT, success>
      }
     else  // not "NT" or "Win2000" ...
      {
        m_hSmPortDevice = CreateFile(_T("\\\\.\\smport.vxd"), 0, 0, NULL, OPEN_EXISTING,0,0);
        if(m_hSmPortDevice == INVALID_HANDLE_VALUE)
         { // Driver not found in current directory. Look into system directory:
           _tcscpy(szBuf,_T("\\\\.\\"));  // dont let "C" fool you: path= "\\.\"
           psz = szBuf; while(*psz) { ++psz; }
           GetSystemDirectory(psz,MAX_PATH); // append the system directory
           _tcscat(szBuf,_T("\\smport.vxd"));
           m_hSmPortDevice = CreateFile( szBuf,0,0, NULL, OPEN_EXISTING,0,0);
         }
        if(m_hSmPortDevice == INVALID_HANDLE_VALUE)
         { // Driver still not found. Try another possibility:
           _tcsncpy(szBuf,szOneMorePathToDriver,MAX_PATH);
           if(szBuf[0])
            { if(szBuf[_tcslen(szBuf)-1]!=_T('\\'))
                _tcscat(szBuf,_T("\\"));
            }
           _tcscat(szBuf,_T("\\smport.vxd"));
           m_hSmPortDevice = CreateFile( szBuf,0,0, NULL, OPEN_EXISTING,0,0);
         }
        if (m_hSmPortDevice == INVALID_HANDLE_VALUE)
         {
           _tcscpy( sz255LastErrorString, _("Didn't find SMPORT.VXD anywhere") );
           dwLastError = GetLastError();
         }
        if(m_hSmPortDevice != INVALID_HANDLE_VALUE)
         {
           fResult = true;
         }
      } // end if < SMPORT, Win95, Win98>
   } // end else < use SMPORT, not PortTalk >
 return fResult;
} // end TSmPort::OpenDriver()


//---------------------------------------------------------------------------
void TSmPort::CloseDriver()
{
 if (m_hSmPortDevice != INVALID_HANDLE_VALUE )
  {
    CloseHandle(m_hSmPortDevice);
    m_hSmPortDevice = INVALID_HANDLE_VALUE;
  }
}

//---------------------------------------------------------------------------
bool TSmPort::IsOpen()
{
 return ( m_hSmPortDevice != INVALID_HANDLE_VALUE );
}


//---------------------------------------------------------------------------
void TSmPort::ReOpen(bool bValue)
{
  if (IsOpen()) CloseDriver();
           else OpenDriver( m_iUseWhichDriver, szAlternativeDriverPath );
}

//---------------------------------------------------------------------------
DWORD TSmPort::GetLastError(void)
{
  return dwLastError;
}

//---------------------------------------------------------------------------
TCHAR * TSmPort::GetLastErrorString(void)
{
  return sz255LastErrorString;
}

//---------------------------------------------------------------------------
DWORD TSmPort::GetDriverHandle(void)
{
  return (DWORD)m_hSmPortDevice;
}

//---------------------------------------------------------------------------
WORD TSmPort::ReadByte(WORD wPort)
{
  WORD      wValue;
  DWORD     cbBytesReturned;

#ifdef USE_PORTTALK  /* support PortTalk ?  */
  if( m_fUseDirectAccess )  // successfully granted direct port access ?
   {
    if( m_fDirectAccessFailed )
     {  // tried before, and the ACCESS failed (trouble with the I/O permission bitmap ? )
       return 0x00;  // don't try again. Allow graceful exit of the application.
     }
    // Note on inline assembly:
    // Borland C++Builder V4 "Standard Edition" does not support this. It said:
    // > [C++ Warning] TSmPort.cpp(xxx): W8002 Restarting compile using assembly.
    // Unfortunately there is no TASM32.EXE to compile this stuff...
    // ... better to have the assembly part in a separate module and use
    //     NASM to assemble it.
    // ( Inline assembly would make this much simpler here, but this was
    //   impossible because the standard edition of BCB has no TASM32 !   )
    //    xor ax, ax
    //    mov dx, wPort
    //    in  al, dx
    //    mov wValue, ax
     try
      { wValue = asm_inportb( wPort );  /* implemented in 'inoutport.asm' */
        /* (assembled with NASM, only the OBJECT FILE included in the BCB project) */
        /* If an exception is raised here, we know we have NO access to the port ! */
      }
     catch(...)  // if something goes wrong in inportb, it's most likely a priviledge violation
      {
        wValue = 0x00;
        m_fDirectAccessFailed = true;
      }
     return wValue;
   } // end if( m_fUseDirectAccess )
#endif // USE_PORTTALK

 if ( m_hSmPortDevice == INVALID_HANDLE_VALUE )
  {
   dwLastError = ERROR_INVALID_HANDLE;
   return 0;
  }
 else
  {
   if(  DeviceIoControl(
          m_hSmPortDevice, // handle to device of interest
          dwReadByteCode,  // control code of operation to perform
          &wPort,          // pointer to buffer to supply input data
          sizeof(wPort),   // size of input buffer
          &wValue,         // pointer to buffer to receive output data
          sizeof(wValue),  // size of output buffer
        &cbBytesReturned,  // LPDWORD pointer to variable to receive output byte count
        NULL ) ) // pointer to overlapped structure for asynchronous operation
    {
      return (wValue & (WORD)0x00FF);
    }
   else
    {
      dwLastError = GetLastError();
      return 0x0000;
    }
  }
} // end TSmPort::ReadByte()


//---------------------------------------------------------------------------
void TSmPort::WriteByte(WORD wPort, WORD wValue)
{
  DWORD     cbBytesReturned;
  TBytePort ByteData;

#ifdef USE_PORTTALK  /* support PortTalk ?  */
  if( m_fUseDirectAccess )  // successfully granted direct port access ?
   {
     if( m_fDirectAccessFailed )
      { // tried before, and the ACCESS failed (trouble with the I/O permission bitmap ? )
        return;  // don't try again. Allow graceful exit of the application.
      }

     try
      { asm_outportb( wPort, wValue);  /* implemented in 'inoutport.asm' */
        /* (assembled with NASM, only the OBJECT FILE included in the BCB project) */
        /* If an exception is raised here, we know we have NO access to the port ! */
      }
     catch (...)
      { m_fDirectAccessFailed = true;
      }
     return;
   }
#endif

  if (m_hSmPortDevice == INVALID_HANDLE_VALUE)
   {
    dwLastError = ERROR_INVALID_HANDLE;
   }
  else
   {
    ByteData.wAddress = wPort;
    ByteData.bValue   = wValue & (WORD)0x00FF;
    if( ! DeviceIoControl(
          m_hSmPortDevice,          // handle to device of interest
          dwWriteByteCode,  // control code of operation to perform
          &ByteData,        // pointer to buffer to supply input data
          sizeof(ByteData), // size of input buffer
          &wValue,          // pointer to buffer to receive output data
          sizeof(wValue),   // size of output buffer
        &cbBytesReturned, // LPDWORD pointer to variable to receive output byte count
        NULL ) ) // pointer to overlapped structure for asynchronous operation
     {
       dwLastError = GetLastError();
     }
   }
} // end TSmPort::WriteByte()

//---------------------------------------------------------------------------
bool TSmPort::AccessFailed(void) // tell the application if an access failed
{
#ifdef USE_PORTTALK  /* AllowIoWrapper.cpp included ? */
  return m_fDirectAccessFailed;
#else
  return false;
#endif
} // end TSmPort::AccessFailed()

//---------------------------------------------------------------------------
WORD TSmPort::ReadWord(WORD wPort)
{
  WORD     wValue;
  DWORD    cbBytesReturned;

  if ( m_hSmPortDevice == INVALID_HANDLE_VALUE )
   {
     dwLastError = ERROR_INVALID_HANDLE;
     return 0;
   }
  else
   {
    if ( DeviceIoControl(
          m_hSmPortDevice,          // handle to device of interest
          dwReadWordCode,   // control code of operation to perform
          &wPort   ,        // pointer to buffer to supply input data
          sizeof(wPort   ), // size of input buffer
          &wValue,          // pointer to buffer to receive output data
          sizeof(wValue),   // size of output buffer
        &cbBytesReturned, // LPDWORD pointer to variable to receive output byte count
        NULL ) ) // pointer to overlapped structure for asynchronous operation
     {
      return wValue;
     }
    else
     {
      dwLastError = GetLastError();
      return 0;
     }
   } // end if <valid handle ?>
} // end TSmPort::ReadWord()


//---------------------------------------------------------------------------
void TSmPort::WriteWord(WORD wPort,  WORD wValue)
{
  DWORD     cbBytesReturned;
  TWordPort WordData;

  if ( m_hSmPortDevice == INVALID_HANDLE_VALUE )
   {
     dwLastError = ERROR_INVALID_HANDLE;
   }
  else
   {
    WordData.wAddress = wPort;
    WordData.wValue   = wValue;
    if( ! DeviceIoControl(
          m_hSmPortDevice,          // handle to device of interest
          dwWriteWordCode,  // control code of operation to perform
          &WordData,        // pointer to buffer to supply input data
          sizeof(WordData), // size of input buffer
          &wPort,           // pointer to buffer to receive output data (??)
          sizeof(wPort ),   // size of output buffer
        &cbBytesReturned, // LPDWORD pointer to variable to receive output byte count
        NULL ) ) // pointer to overlapped structure for asynchronous operation
      {
        dwLastError = GetLastError();
      }
   } // end if <valid handle ?>
} // TSmPort::WriteWord()

//---------------------------------------------------------------------------
DWORD TSmPort::ReadDWord(WORD wPort)
{
  DWORD      dwValue;
  DWORD      cbBytesReturned;

  if(m_hSmPortDevice == INVALID_HANDLE_VALUE)
   {
    dwLastError = ERROR_INVALID_HANDLE;
    return 0;
   }
  else
   {
    // ex: PortResult:=DeviceIoControl(m_hSmPortDevice, dwReadDWordCode, @port, sizeof(port),
    //                          @value, sizeof(value), cbBytesReturned, nil);
    if( DeviceIoControl(
          m_hSmPortDevice,        // handle to device of interest
          dwReadDWordCode, // control code of operation to perform
          &wPort,         // pointer to buffer to supply input data
          sizeof(wPort),  // size of input buffer
          &dwValue,       // pointer to buffer to receive output data
          sizeof(dwValue),// size of output buffer
        &cbBytesReturned, // LPDWORD pointer to variable to receive output byte count
        NULL ) ) // pointer to overlapped structure for asynchronous operation
    {
      return dwValue;
    }
   else
    {
      dwLastError = GetLastError();
      return 0;
    }
  }
} // end TSmPort::ReadDWord()


//---------------------------------------------------------------------------
void TSmPort::WriteDWord(WORD wPort, DWORD dwValue)
{
   DWORD      cbBytesReturned;
   TDWordPort DWordData;

  if ( m_hSmPortDevice == INVALID_HANDLE_VALUE )
   {
     dwLastError = ERROR_INVALID_HANDLE;
   }
  else
   {
    DWordData.wAddress = wPort;
    DWordData.dwValue  = dwValue;
    if ( ! DeviceIoControl(m_hSmPortDevice, dwWriteDWordCode, // handle, ctrl code
                    &DWordData, sizeof(DWordData),    // input
                    &wPort,     sizeof(wPort),        // output
                    &cbBytesReturned, NULL) )
     {
        dwLastError = GetLastError();
     }
   }
} // end TSmPort::WriteDWord()


//---------------------------------------------------------------------------
void TSmPort::SpeakerOn(WORD wFrequencyHz)
{
  // Start sound output for the PC speaker,
  // using the PC's programmable interval timer, counter 2...
  WORD wTimerValue = (WORD)((long)1193180 / (long)wFrequencyHz);
  WriteByte(0x0043, 0xB6);  // control register: counter #2, mode=3
  WriteByte(0x0042, (BYTE)(wTimerValue & 0x00FF) );
  WriteByte(0x0042, (BYTE)(wTimerValue >> 8)     );

  // now enable the sound output by setting the counter's GATE input:
  WriteByte(0x0061, ReadByte(0x0061) | (WORD)0x0003 );
} // end TSmPort::SpeakerOn()


//---------------------------------------------------------------------------
void TSmPort::SpeakerOff(void)
{
  // disable the sound output by clearing the counter's GATE input :
  WriteByte(0x0061, ReadByte(0x0061) & (WORD)0x00FC );
}

//---------------------------------------------------------------------------
DWORD TSmPort::GetWindowsVersion(void)
{
 // ex: TOSVersionInfoA  ver;  what is this type ?
 OSVERSIONINFO ver;
  ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  GetVersionEx( &ver );
  switch( ver.dwPlatformId )
   {
    case VER_PLATFORM_WIN32s:
          return VER_WINDOWS_31;
    case VER_PLATFORM_WIN32_WINDOWS:
          switch (ver.dwMinorVersion)
           { case  0: return VER_WINDOWS_95;
             case 10: return VER_WINDOWS_98;
             case 90: return VER_WINDOWS_ME;
             default: break;
           }
          break;
    case VER_PLATFORM_WIN32_NT:  // PlatformId=2, also for WIN XP..
          switch (ver.dwMajorVersion)
           { case  4: return VER_WINDOWS_NT;
             case  5: return VER_WINDOWS_2000; // + WIN_XP
                // Windows XP from January 2002:
                //   ver.dwMajorVersion = 5
                //   ver.dwMinorVersion = 1
                //   ver.dwBuildNumber  = 2600
                //   ver.szCSDVersion   = "\0\0\0\0Tahoma\0\0\0..." (!)
             default: break;
           }
          break;
    default:
          break;
   } // end switch <platform id>
  return VER_WINDOWS_UNKNOWN;
} // end TSmPort::GetWindowsVersion()

//---------------------------------------------------------------------------
void TSmPort::OpenServiceManager(void)
{
  if (! hManager )
   {
    dwLastError = 0;
    // The OpenSCManager function establishes a connection
    // to the service control manager on the specified computer
    // and opens the specified database.
    hManager = OpenSCManager(
      NULL, // LPCTSTR lpMachineName = pointer to machine name string
      NULL, // LPCTSTR lpDatabaseName= pointer to database name string
      SC_MANAGER_ALL_ACCESS);  // DWORD dwDesiredAccess = type of access
    if(hManager==0)
      dwLastError = GetLastError();
   }
}

//---------------------------------------------------------------------------
void TSmPort::CloseServiceManager(void)
{
  if(hManager)
   {
    CloseServiceHandle(hManager);
    hManager = 0;
   }
}

//---------------------------------------------------------------------------
void TSmPort::InstallSmallPortDriver(void)
{
 SC_HANDLE hService;
 TCHAR DriverPath[MAX_PATH+64+1];

  dwLastError = 0;
  sz255LastErrorString[0] = 0;

  // ex: DriverPath := GetCurrentDir + '\' + DriverName + '.sys';
  //     but we want to do this without BORLAND stuff here...
  GetCurrentDirectory(   // Using the CURRENT directory may be a bad idea..
        /*!*/ MAX_PATH,  // size, in characters, of directory buffer
            DriverPath); // address of buffer for current directory
  _tcscat(DriverPath, _T("\\"));
  _tcscat(DriverPath, m_sz64DriverName );  // append "Smport"
  _tcscat(DriverPath, _T(".sys"));

  // ex:  if(!FileExists(DriverPath)then
  if(GetFileAttributes(DriverPath) == 0xFFFFFFFF)
   {// Driver does not exist in this path. Try something else ..
    // wsprintf(sz255LastErrorString,"Couldn't find %s",DriverPath);
    GetSystemDirectory( // can you spot the difference to "GetCurrentDirectory"?
             DriverPath,
         /*!*/ MAX_PATH);
    _tcscat(DriverPath, _T("\\"));
    _tcscat(DriverPath, m_sz64DriverName );  // append "Smport"
    _tcscat(DriverPath, _T(".sys"));
    if(GetFileAttributes(DriverPath) == 0xFFFFFFFF)
     {
      if( szAlternativeDriverPath[0] )
       { _tcsncpy( DriverPath, szAlternativeDriverPath, /*!*/ MAX_PATH);
         if(DriverPath[_tcslen(DriverPath)-1] != '\\' )
            _tcscat(DriverPath, _T("\\"));
         _tcscat(DriverPath, m_sz64DriverName );  // append "Smport"
         _tcscat(DriverPath, _T(".sys"));
         if(GetFileAttributes(DriverPath) == 0xFFFFFFFF)
          {
           _stprintf(sz255LastErrorString,_("Couldn't find %s"),DriverPath);
          }
       }
      else
       {
         _stprintf(sz255LastErrorString,_("Couldn't find %s"),DriverPath);
       }
     }
   }


  // The CreateService function creates a service object
  // and adds it to the specified service control manager database.
  hService = CreateService(
                   hManager, // handle to service control manager database
             m_sz64DriverName, // pointer to name of service to start
             m_sz64DriverName, // pointer to display name
         SERVICE_ALL_ACCESS, // dwDesiredAccess = type of access to service
      SERVICE_KERNEL_DRIVER, // dwServiceType   = type of service
       SERVICE_DEMAND_START, // dwStartType     = when to start service
       SERVICE_ERROR_NORMAL, // dwErrorControl  = severity if service fails to start
                 DriverPath, // lpBinaryPathName = pointer to name of binary file
                       NULL, // lpLoadOrderGroup = pointer to name of load ordering group
                       NULL, // lpdwTagId     = pointer to variable to get tag identifier
                       NULL, // lpDependencies= pointer to array of dependency names
                       NULL, // lpServiceStartName = pointer to account name of service
                       NULL); // lpPassword   = pointer to password for service account
  if (hService == 0)
   {
    if(sz255LastErrorString[0]==0)
      _stprintf(sz255LastErrorString, _("Can't CreateService for \"%s\""),DriverPath);
    dwLastError = GetLastError();
   }
  else // Note: hService was != 0 even though smport.sys could not be found !
   {
    CloseServiceHandle(hService);
    dwLastError = 0;
   }
} // end TSmPort::InstallSmallPortDriver()


//---------------------------------------------------------------------------
void TSmPort::RemoveSmallPortDriver(void)
{
  SC_HANDLE hService;

  dwLastError = 0;
  hService = OpenService(hManager, m_sz64DriverName, SERVICE_ALL_ACCESS);
  if(hService)
   {
    if (!DeleteService(hService))
     {   _tcscpy(sz255LastErrorString, _("Can't delete service"));
         dwLastError = GetLastError();
     }
   }
  else
   {
     if(sz255LastErrorString[0]==0)
        _stprintf(sz255LastErrorString, _("Can't Open Service"));
     dwLastError = GetLastError();
   }

  if(hService)
     CloseServiceHandle(hService);
} // end TSmPort::RemoveSmallPortDriver()


//---------------------------------------------------------------------------
void TSmPort::StartSmallPortService(void)
{
  SC_HANDLE hService;
  const TCHAR *lpVectors;

  dwLastError = 0;

  // The OpenService function opens a handle to an existing service.
  hService = OpenService(
              hManager,   // handle to service control manager database
          m_sz64DriverName, // pointer to name of service to start
     SERVICE_ALL_ACCESS); // type of access to service
  if (hService)
   {
    lpVectors = NULL;
    // The StartService function starts the execution of a service.
    if( ! StartService(
              hService,  // handle of service
                     0,  // number of arguments
           &lpVectors))  // LPCTSTR *lpServiceArgVectors = address of array of argument string pointers
     { // could not start the service:
       if(sz255LastErrorString[0]==0)
          _stprintf(sz255LastErrorString, _("Can't Start Service"));
       dwLastError = GetLastError();
     }
   }
  else
   { // could not open a handle to an existing service
     if(sz255LastErrorString[0]==0)
        _stprintf(sz255LastErrorString, _("Can't Open Service"));
     dwLastError = GetLastError();
   }
  if (hService != 0)
      CloseServiceHandle(hService);
} // end TSmPort::StartSmallPortService()


//---------------------------------------------------------------------------
void TSmPort::StopSmallPortService(void)
{
  SC_HANDLE hService;
  // ex: Status: TServiceStatus;  // smells like DELPHI stuff. What is it ?
  SERVICE_STATUS Status;

  dwLastError = 0;
  hService = OpenService(hManager, m_sz64DriverName, SERVICE_ALL_ACCESS);
  if (hService)
   {
    if ( ! ControlService(hService, SERVICE_CONTROL_STOP, &Status) )
      dwLastError = GetLastError();
   }
  else
   { // OpenService failed
     dwLastError = GetLastError();
   }
  if (hService)
      CloseServiceHandle(hService);
} // end TSmPort::StopSmallPortService()

// EOF <TSmPort.cpp>
