/*
Module : PARALLEL.CPP
Purpose: Implementation for an MFC class to encapsulate parallel ports
Created: PJN / 28-12-1999
History: PJN / 06-03-2000 Code now throws an exception if no Win32 parallel ports are available
PJN / 04-04-2000 Code handles the case where it is run on NT / Windows 2000 by throwing
an exception
PJN / 19-07-2000 1. Now works on NT / Windows 2000 thanks to DriverLINX Port I/O Driver
2. Made all typedefs and enums local to the appropiate class rather
than polluting the global namespace
Copyright (c) 1999 - 2000 by PJ Naughter.
All rights reserved.
*/
///////////////////////////////// Includes //////////////////////////////////
#include "stdafx.h"
#include "ParallelPort.h"
#include <winerror.h>
#include <conio.h>
#include <afxpriv.h>
///////////////////////////////// Defines / Statics ///////////////////////////
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
int CParallelPort::sm_nRefCount = 0;
BOOL CParallelPort::sm_bRunningOnNT = FALSE;
HINSTANCE CParallelPort::sm_hDLINX = NULL;
CParallelPort::LPDLPORTREADPORTUCHAR CParallelPort::sm_lpfnDlPortReadUchar = NULL;
CParallelPort::LPDLPORTWRITEPORTUCHAR CParallelPort::sm_lpfnDlPortWriteUchar = NULL;
CArray<CParallelPortSettings, CParallelPortSettings&> CParallelPort::sm_Ports;
//////////////////////////////// Implementation ///////////////////////////////
////////// Exception handling code
void AfxThrowParallelException(DWORD dwError /* = 0 */)
{
if (dwError == 0)
dwError = ::GetLastError();
CParallelException* pException = new CParallelException(dwError);
TRACE(_T("Warning: throwing CParallelException for error %d\n"), dwError);
THROW(pException);
}
BOOL CParallelException::GetErrorMessage(LPTSTR pstrError, UINT nMaxError, PUINT pnHelpContext)
{
ASSERT(pstrError != NULL && AfxIsValidString(pstrError, nMaxError));
if (pnHelpContext != NULL)
*pnHelpContext = 0;
LPTSTR lpBuffer;
BOOL bRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, m_dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
(LPTSTR) &lpBuffer, 0, NULL);
if (bRet == FALSE)
*pstrError = '\0';
else
{
lstrcpyn(pstrError, lpBuffer, nMaxError);
bRet = TRUE;
LocalFree(lpBuffer);
}
return bRet;
}
CString CParallelException::GetErrorMessage()
{
CString rVal;
LPTSTR pstrError = rVal.GetBuffer(4096);
GetErrorMessage(pstrError, 4096, NULL);
rVal.ReleaseBuffer();
return rVal;
}
CParallelException::CParallelException(DWORD dwError)
{
m_dwError = dwError;
}
CParallelException::~CParallelException()
{
}
IMPLEMENT_DYNAMIC(CParallelException, CException)
#ifdef _DEBUG
void CParallelException::Dump(CDumpContext& dc) const
{
CObject::Dump(dc);
dc << "m_dwError = " << m_dwError;
}
#endif
////////// Settings class
CParallelPortSettings::CParallelPortSettings()
{
m_nBaseAddress = 0;
m_Type = ParallelTypeUndefined;
m_ECPMode = ECPModeUndefined;
}
CParallelPortSettings::CParallelPortSettings(const CParallelPortSettings& state)
{
*this = state;
}
CParallelPortSettings& CParallelPortSettings::operator=(const CParallelPortSettings& state)
{
m_nBaseAddress = state.m_nBaseAddress;
m_Type = state.m_Type;
m_ECPMode = state.m_ECPMode;
return *this;
}
////////// The actual parallel port class
CParallelPort::CParallelPort()
{
m_hPort = INVALID_HANDLE_VALUE;
m_nPortIndex = -1;
m_nBaseAddress = 0;
m_dwTimeout = 1000; //Default timeout is 1 second
//Test for presence of parallel ports at the standard addresses of 0x3BC, 0x378 and 0x278
if (sm_nRefCount == 0)
{
++sm_nRefCount;
//Initialize the DriverLINX driver if on NT / Windows 2000
sm_bRunningOnNT = RunningOnNT();
if (sm_bRunningOnNT)
{
if (!InitializeDriverLINX())
{
TRACE(_T("Running on Windows NT / Windows 2000 and the DriverLINX PORTIO driver is not installed, aborting !!\n"));
AfxThrowParallelException();
}
}
//Open LPT1 - LPT3 as a precaution against other processes trying to write
//to the port while we are detecting them
HANDLE hPort1 = CreateFile(_T("\\\\.\\LPT1"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
HANDLE hPort2 = CreateFile(_T("\\\\.\\LPT2"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
HANDLE hPort3 = CreateFile(_T("\\\\.\\LPT3"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
//Must have at least one port available in Win32 to continue to attempt
//detection of the ports
int nWin32Ports = 0;
if (hPort1 != INVALID_HANDLE_VALUE)
++nWin32Ports;
if (hPort2 != INVALID_HANDLE_VALUE)
++nWin32Ports;
if (hPort3 != INVALID_HANDLE_VALUE)
++nWin32Ports;
if (nWin32Ports == 0)
{
TRACE(_T("No parallel ports are available to Win32, aborting !!\n"));
AfxThrowParallelException(ERROR_DEV_NOT_EXIST);
}
//Try to detect the details of the 3 standard ports
CParallelPortSettings settings;
if (GetPort(0x3BC, settings))
sm_Ports.Add(settings);
if (sm_Ports.GetSize() < nWin32Ports)
if (GetPort(0x378, settings))
sm_Ports.Add(settings);
if (sm_Ports.GetSize() < nWin32Ports)
if (GetPort(0x278, settings))
sm_Ports.Add(settings);
if (sm_Ports.GetSize() == 0)
TRACE(_T("Could not detect any parallel ports on this machine\n"));
//Don't forget to close the 3 SDK handles we had open
CloseHandle(hPort3);
CloseHandle(hPort2);
CloseHandle(hPort1);
}
}
CParallelPort::~CParallelPort()
{
//decrement the reference count and
//free the DriverLINX pointers if necessary
--sm_nRefCount;
if (sm_nRefCount == 0)
DeInitializeDriverLINX();
Close();
}
IMPLEMENT_DYNAMIC(CParallelPort, CObject)
#ifdef _DEBUG
void CParallelPort::Dump(CDumpContext& dc) const
{
CObject::Dump(dc);
dc << _T("m_hPort = ") << m_hPort << _T("\n");
}
#endif
void CParallelPort::Open(int nPort)
{
//Call Close just in case we already have the port open
Close();
m_nPortIndex = nPort - 1;
if (m_nPortIndex < sm_Ports.GetSize())
{
//Cache the base address of the port for performance reasons
m_nBaseAddress = sm_Ports.ElementAt(m_nPortIndex).m_nBaseAddress;
//Call CreateFile to open up the parallel port. This prevents other apps
//causing problems when we do the Port IO directly.
CString sPort;
sPort.Format(_T("\\\\.\\LPT%d"), nPort);
m_hPort = CreateFile(sPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (m_hPort == INVALID_HANDLE_VALUE)
{
TRACE(_T("Failed to open parallel port, LPT%d\n"), nPort);
Close();
AfxThrowParallelException();
}
}
else
{
TRACE(_T("Could not find the parallel port, LPT%d\n"), nPort);
Close();
AfxThrowParallelException(ERROR_FILE_NOT_FOUND);
}
}
BOOL CParallelPort::IsOpen() const
{
return (m_hPort != INVALID_HANDLE_VALUE);
}
void CParallelPort::Close()
{
if (IsOpen())
{
BOOL bSuccess = CloseHandle(m_hPort);
m_hPort = INVALID_HANDLE_VALUE;
m_nPortIndex = -1;
m_nBaseAddress = 0;
if (!bSuccess)
TRACE(_T("Failed to close the parallel port, GetLastError:%d\n"), GetLastError());
}
}
void CParallelPort::SetECPMode(CParallelPortSettings::ECPPortMode mode)
{
ASSERT(IsOpen()); //Port must be open
CParallelPortSettings& settings = sm_Ports.ElementAt(m_nPortIndex);
ASSERT(settings.m_Type == CParallelPortSettings::ParallelTypeECP)