/******************************************************************************
 *                                                                         
 *               (C) COPYRIGHT Pacific MindWorks, Inc. 2001-2005
 *                          All rights reserved.
 *
 *****************************************************************************/

#ifndef __NTLIOPROVIDER_INCLUDED
#define __NTLIOPROVIDER_INCLUDED

#include <stdarg.h>
#include <atlrx.h>
#include "ntlutil.h"

///////////////////////////////////////////////////////////////////////////////
//	CDriverIOProvider

enum NtlAsciiType
{
    NtlAsciiType_I2 = 2,
    NtlAsciiType_I4 = 3,
    NtlAsciiType_R4 = 4,
    NtlAsciiType_R8 = 5,
    NtlAsciiType_BSTR = 8,
    NtlAsciiType_Any = 12,
    NtlAsciiType_UI1 = 17
};

enum NtlBinaryType
{
    NtlBinaryType_I2 = 2,
    NtlBinaryType_I4 = 3,
    NtlBinaryType_R4 = 4,
    NtlBinaryType_R8 = 5,
    NtlBinaryType_UI1 = 17
};

enum NtlBufferMask
{
    NTLIO_IN_BUF = 16,
    NTLIO_OUT_BUF = 32,
    NTLIO_IN_AND_OUT_BUFS = 48
};

#define DEFAULT_TIMEOUT				2000
#define NUMBER_BUFFER_SIZE			100

#define E_NTL_IO_TIMEOUT MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x604)

#define DECLARE_SET_AND_RESTORE_TIMEOUT(x) CTimeoutManager tomgr(this, x);
#define RESET_TIMED_OUT() m_bTimedOut = false;


enum IOProviderType
{
	UnknownIOProvider = 1,
	VisaIOProvider,
	VisaComIOProvider,
	NullIOProvider,
	CustomIOProvider
};


class CDriverIOProvider
{
public:
	CDriverIOProvider();
	virtual ~CDriverIOProvider();

	// Utility class for managing the timeout value for I/O operations
	class CTimeoutManager
	{
	public:
		CTimeoutManager(CDriverIOProvider* pIO, const long lTimeout = -2) 
			: m_lPrevTimeout(-2),
			  m_pIO(pIO)
		{
			if (lTimeout > -2)
			{
				// User specified a desired timeout or an infinite timeout.
				HRESULT hr = m_pIO->ioprvGetIOTimeout(&m_lPrevTimeout);
				if (SUCCEEDED(hr))
				{
					m_pIO->ioprvSetIOTimeout(lTimeout);
				}
			}
		}

		~CTimeoutManager()
		{
			// If necessary, restore the timeout
			if (m_lPrevTimeout != -2)
			{
				// A new timeout was not specified
				m_pIO->ioprvSetIOTimeout(m_lPrevTimeout);
			}
		}
	private:
		long m_lPrevTimeout;
		CDriverIOProvider* m_pIO;
	};

	///////////////////////////////////////////////////////////////////////////////
	// Initialization

	virtual HRESULT ioprvInitializeIOProvider(CObjectModelNodeBase* pObjectModelNode, CErrorManagerBase* pErrorManager, CPerfMonManager* pPerfMonManager, BSTR bstrResourceDescriptor);
	bool ioprvIsInitialized() const;
	virtual HRESULT ioprvCloseIOProvider();
	virtual HRESULT ioprvSetTrailingForReads(const CString& strTrailing);
	virtual HRESULT ioprvSetTrailingForWrites(const CString& strTrailing);
	virtual IOProviderType ioprvGetIOProviderType() = 0;

	///////////////////////////////////////////////////////////////////////////////
	// Error checking

	virtual HRESULT ioprvPollInstrumentErrors();
	virtual HRESULT ioprvDeviceClear();
	virtual HRESULT ioprvReadSTB(long* plStatus);

	///////////////////////////////////////////////////////////////////////////////
	// Timeout

	virtual HRESULT ioprvSetIOTimeout(long lTimeout);
	virtual HRESULT ioprvGetIOTimeout(long* plTimeout);
	virtual HRESULT ioprvIsTimeout(bool* pbTimeout);

	///////////////////////////////////////////////////////////////////////////////
	// Wrappers around the IMessage interface

	virtual HRESULT ioprvInstrQuery(const CString& strCommand, double* pdResult, long lTimeout = -2);
	virtual HRESULT ioprvInstrQuery(const CString& strCommand, long* plResult, long lTimeout = -2);
	virtual HRESULT ioprvInstrQuery(const CString& strCommand, CString& strResult, long cchDesired, long lTimeout = -2);
	
	virtual HRESULT ioprvInstrRead(BYTE* pBuf, long cbDesired, long* plActualLength, long lTimeout = -2);
	virtual HRESULT ioprvInstrRead(CString& strResult, long cbDesired, long lTimeout = -2, bool bRemoveTrailing = true);
	virtual HRESULT ioprvInstrRead(SAFEARRAY** ppsa, long cbDesired = -1, long lTimeout = -2);

	virtual HRESULT ioprvInstrWrite(BYTE* pBuf, long cbDesired, long* pcbActual, long lTimeout = -2);
	virtual HRESULT ioprvInstrWrite(const CString& strCommand, long lTimeout = -2);
	virtual HRESULT ioprvInstrWrite(SAFEARRAY* psa, long cbDesired = -1, long lTimeout = -2);

	virtual HRESULT ioprvInstrWriteNoPoll(BYTE* pBuf, long cbDesired, long* pcbActual, long lTimeout = -2);
	virtual HRESULT ioprvInstrWriteNoPoll(const CString& strCommand, long lTimeout = -2);
	virtual HRESULT ioprvInstrWriteNoPoll(SAFEARRAY* psa, long cbDesired = -1, long lTimeout = -2);

	///////////////////////////////////////////////////////////////////////////////
	// Wrappers around the IFormattedIO488 interface

	virtual HRESULT ioprvFormattedWriteString(const CString& strData, bool bFlushAndEnd = true, long lTimeout = -2);

	virtual HRESULT ioprvFormattedWriteNumber(double val, bool bFlushAndEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedWriteNumber(long val, bool bFlushAndEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedWriteNumber(BYTE val, bool bFlushAndEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedWriteNumber(VARIANT data, NtlAsciiType type, bool bFlushAndEnd = true, long lTimeout = -2);

	virtual HRESULT ioprvFormattedWriteList(double* pValues, long lLength, const CString& strListSeparator, bool bFlushAndEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedWriteList(long* pValues, long lLength, const CString& strListSeparator, bool bFlushAndEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedWriteList(BYTE* pValues, long lLength, const CString& strListSeparator, bool bFlushAndEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedWriteList(SAFEARRAY* psa, NtlAsciiType type, const CString& strListSeparator, bool bFlushAndEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedWriteList(VARIANT data, NtlAsciiType type, const CString& strListSeparator, bool bFlushAndEnd = true, long lTimeout = -2);

	virtual HRESULT ioprvFormattedWriteIEEEBlock(const CString& strCommand, double* pValues, long lLength, bool bFlushAndEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedWriteIEEEBlock(const CString& strCommand, short* pValues, long lLength, bool bFlushAndEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedWriteIEEEBlock(const CString& strCommand, long* pValues, long lLength, bool bFlushAndEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedWriteIEEEBlock(const CString& strCommand, BYTE* pValues, long lLength, bool bFlushAndEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedWriteIEEEBlock(const CString& strCommand, VARIANT data, bool bFlushAndEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedWriteIEEEBlock(const CString& strCommand, SAFEARRAY* pData, bool bFlushAndEnd = true, long lTimeout = -2);

	virtual HRESULT ioprvFormattedReadString(CString& strResult, long lTimeout = -2);
	virtual HRESULT ioprvFormattedReadString(BSTR* pbstrResult, long lTimeout = -2);

	virtual HRESULT ioprvFormattedReadNumber(double* pValue, bool bFlushToEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedReadNumber(long* pValue, bool bFlushToEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedReadNumber(BYTE* pValue, bool bFlushToEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedReadNumber(NtlAsciiType type, VARIANT* pData, bool bFlushToEnd = true, long lTimeout = -2);

	virtual HRESULT ioprvFormattedReadList(double* pValues, long lMaxLength, long* plActualLength, const CString& strListSeparator, long lTimeout = -2);
	virtual HRESULT ioprvFormattedReadList(long* pValues, long lMaxLength, long* plActualLength, const CString& strListSeparator, long lTimeout = -2);
	virtual HRESULT ioprvFormattedReadList(BYTE* pValues, long lMaxLength, long* plActualLength, const CString& strListSeparator, long lTimeout = -2);
	virtual HRESULT ioprvFormattedReadList(CComBSTR* pValues, long lMaxLength, long* plActualLength, const CString& strListSeparator, long lTimeout = -2);
	virtual HRESULT ioprvFormattedReadList(NtlAsciiType type, const CString& strListSeparator, VARIANT* pData, long lTimeout = -2);
	virtual HRESULT ioprvFormattedReadList(NtlAsciiType type, const CString& strListSeparator, SAFEARRAY** pData, long lTimeout = -2);

	virtual HRESULT ioprvFormattedReadIEEEBlock(double* pValues, long lMaxLength, long* plActualLength, bool bSeekToBlock = false, bool bFlushToEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedReadIEEEBlock(long* pValues, long lMaxLength, long* plActualLength, bool bSeekToBlock = false, bool bFlushToEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedReadIEEEBlock(BYTE* pValues, long lMaxLength, long* plActualLength, bool bSeekToBlock = false, bool bFlushToEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedReadIEEEBlock(NtlBinaryType type, VARIANT* pData, bool bSeekToBlock = false, bool bFlushToEnd = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedReadIEEEBlock(NtlBinaryType type, SAFEARRAY** pData, bool bSeekToBlock = false, bool bFlushToEnd = true, long lTimeout = -2);

	virtual HRESULT ioprvFormattedFlushWrite(bool bSendEND = true, long lTimeout = -2);
	virtual HRESULT ioprvFormattedFlushRead(long lTimeout = -2);
	virtual HRESULT ioprvFormattedSetBufferSize(NtlBufferMask mask, long lSize);

	// IRegister-style functions. Overridden in derived classes.
	virtual HRESULT ioprvRegisterIn8(UINT16 space, UINT32 offset, UINT8* val) = 0;
	virtual HRESULT ioprvRegisterOut8(UINT16 space, UINT32 offset, UINT8 val) = 0;
	virtual HRESULT ioprvRegisterIn16(UINT16 space, UINT32 offset, UINT16* val) = 0;
	virtual HRESULT ioprvRegisterOut16(UINT16 space, UINT32 offset, UINT16 val) = 0;
	virtual HRESULT ioprvRegisterIn32(UINT16 space, UINT32 offset, UINT32* val) = 0;
	virtual HRESULT ioprvRegisterOut32(UINT16 space, UINT32 offset, UINT32 val) = 0;
	virtual HRESULT ioprvRegisterMoveIn8(UINT16 space, UINT32 offset, UINT32 length, UINT8 buf[]) = 0;
	virtual HRESULT ioprvRegisterMoveOut8(UINT16 space, UINT32 offset, UINT32 length, UINT8 buf[]) = 0;
	virtual HRESULT ioprvRegisterMoveIn16(UINT16 space, UINT32 offset, UINT32 length, UINT16 buf[]) = 0;
	virtual HRESULT ioprvRegisterMoveOut16(UINT16 space, UINT32 offset, UINT32 length, UINT16 buf[]) = 0;
	virtual HRESULT ioprvRegisterMoveIn32(UINT16 space, UINT32 offset, UINT32 length, UINT32 buf[]) = 0;
	virtual HRESULT ioprvRegisterMoveOut32(UINT16 space, UINT32 offset, UINT32 length, UINT32 buf[]) = 0;
	
protected:
	// Helpers
	virtual HRESULT _ioprvInstrRead(BYTE* pBuf, long cbDesired, long* plActualLength);
	virtual HRESULT _ioprvInstrRead(CString& strResult, long cchDesired, bool bRemoveTrailing = true);
	virtual HRESULT _ioprvInstrRead(SAFEARRAY** ppsa, long cbDesired);
	virtual HRESULT _ioprvInstrWrite(BYTE* pBuf, long cbDesired, long* pcbActual);
	virtual HRESULT _ioprvInstrWrite(const CString& strCommand);
	virtual HRESULT _ioprvInstrWrite(SAFEARRAY* psa, long cbDesired);
	virtual HRESULT _ioprvInstrumentError(HRESULT hr);
	virtual HRESULT _ioprvPollInstrumentError();

	// IMessage-style function. Overridden in derived classes.
	virtual HRESULT ioprvMessageIORead(BYTE* pBuf, long cbDesired, long* plActualLength) = 0;
	virtual HRESULT ioprvMessageIORead(long cbDesired, SAFEARRAY** ppsa) = 0;
	virtual HRESULT ioprvMessageIOReadString(long cchDesired, BSTR* pbstrBuf) = 0;
	virtual HRESULT ioprvMessageIOWrite(BYTE* pBuf, long cbDesired, long* pcbActual) = 0;
	virtual HRESULT ioprvMessageIOWrite(SAFEARRAY* psa, long cbDesired, long* pcbActual) = 0;
	virtual HRESULT ioprvMessageIOWriteString(BSTR bstrBuf, long* pcchActual) = 0;
	virtual HRESULT ioprvMessageIOClear() = 0;
	virtual HRESULT ioprvMessageIOReadSTB(long* plStatus) = 0;
	virtual HRESULT ioprvMessageIOSetSendEndEnabled(bool bEnable) = 0;
	virtual HRESULT ioprvMessageIOGetSendEndEnabled(bool* pbEnable) = 0;
	virtual HRESULT ioprvMessageIOSetTerminationCharacter(BYTE val) = 0;
	virtual HRESULT ioprvMessageIOGetTerminationCharacter(BYTE* pVal) = 0;
	virtual HRESULT ioprvMessageIOSetTerminationCharacterEnabled(bool bEnable) = 0;
	virtual HRESULT ioprvMessageIOGetTerminationCharacterEnabled(bool* pbEnable) = 0;
	virtual HRESULT ioprvMessageIOSetTimeout(long lTimeout) = 0;
	virtual HRESULT ioprvMessageIOGetTimeout(long* plTimeout) = 0;

	// Formatted I/O functions. Overridden in derived classes.
	virtual HRESULT ioprvFormattedIOSetInstrumentBigEndian(bool bBigEndian) = 0;
	virtual HRESULT ioprvFormattedIOGetInstrumentBigEndian(bool* pbBigEndian) = 0;
	virtual HRESULT ioprvFormattedIOWriteString(const CString& strData, bool bFlushAndEnd) = 0;
	virtual HRESULT ioprvFormattedIOWriteNumber(VARIANT data, NtlAsciiType type, bool bFlushAndEnd) = 0;
	virtual HRESULT ioprvFormattedIOWriteList(VARIANT data, NtlAsciiType type, const CString& strListSeparator, bool bFlushAndEnd) = 0;
	virtual HRESULT ioprvFormattedIOWriteIEEEBlock(const CString& strCommand, VARIANT data, bool bFlushAndEnd) = 0;
	virtual HRESULT ioprvFormattedIOReadString(CString& strResult) = 0;
	virtual HRESULT ioprvFormattedIOReadNumber(NtlAsciiType type, VARIANT* pData, bool bFlushToEnd) = 0;
	virtual HRESULT ioprvFormattedIOReadList(NtlAsciiType type, const CString& strListSeparator, VARIANT* pData) = 0;
	virtual HRESULT ioprvFormattedIOReadIEEEBlock(NtlBinaryType type, VARIANT* pData, bool bSeekToBlock, bool bFlushToEnd) = 0;
	virtual HRESULT ioprvFormattedIOFlushWrite(bool bSendEND) = 0;
	virtual HRESULT ioprvFormattedIOFlushRead() = 0;
	virtual HRESULT ioprvFormattedIOSetBufferSize(NtlBufferMask mask, long lSize) = 0;

	///////////////////////////////////////////////////////////////////////////////
	//	I/O performance monitoring support 

	CPerfMonManager* GetPerfMonManager()
	{
		return m_pPerfMonManager;
	}

	#define IO_METHOD_START() \
		GetPerfMonManager()->IncrementIOCallCount();

	void IncrementIOByteCount(long lCount = 1)
	{
		GetPerfMonManager()->IncrementIOByteCount(lCount);
	}

	void IncrementIOByteCount(const CString& strData)
	{
		IncrementIOByteCount(strData.GetLength());
	}

	void IncrementIOByteCount(SAFEARRAY* psaData)
	{
		ATLASSERT(::SafeArrayGetDim(psaData) == 1);
		long lUBound = 0;
		long lLBound = 0;
		::SafeArrayGetUBound(psaData, 1, &lUBound);
		::SafeArrayGetLBound(psaData, 1, &lLBound);
		IncrementIOByteCount(::SafeArrayGetElemsize(psaData) * (lUBound - lLBound));
	}

	void IncrementIOByteCount(BSTR bstrData)
	{
		IncrementIOByteCount(::SysStringByteLen(bstrData));
	}

	void IncrementIOByteCount(VARIANT varData)
	{
		NTL_UNUSED(varData);

		IncrementIOByteCount(sizeof(VARIANT));
	}

private: 
	bool m_bInitialized;
	CObjectModelNodeBase* m_pObjectModelNode;		// Object to access various state information
	CErrorManagerBase* m_pErrorManager;				// Object to use to report errors
	CPerfMonManager* m_pPerfMonManager;				// Performance monitor
	CString m_strTrailingForReads;					// The trailing character(s) to automatically remove from strings returned by instrument
	CString m_strTrailingForWrites;					// The trailing character(s) to automatically append to strings sent to instrument
	bool m_bTimedOut;								// TRUE if timeout occured during last operation
};


inline CDriverIOProvider::CDriverIOProvider() 
	: m_bInitialized(false), m_bTimedOut(false), m_pErrorManager(NULL), m_strTrailingForReads(_T("\n")), m_strTrailingForWrites(_T("\n"))
{
}

inline CDriverIOProvider::~CDriverIOProvider()
{
}

inline HRESULT CDriverIOProvider::ioprvInitializeIOProvider(CObjectModelNodeBase* pObjectModelNode, CErrorManagerBase* pErrorManager, CPerfMonManager* pPerfMonManager, BSTR bstrResourceDescriptor)
{
	if (pObjectModelNode == NULL)
		return E_INVALIDARG;

	if (pErrorManager == NULL)
		return E_INVALIDARG;

	if (pPerfMonManager == NULL)
		return E_INVALIDARG;

	if (bstrResourceDescriptor == NULL)
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	m_bInitialized = true;
	m_bTimedOut = false;
	m_pObjectModelNode = pObjectModelNode;
	m_pErrorManager = pErrorManager;
	m_pPerfMonManager = pPerfMonManager;

	return hr;
}

inline bool CDriverIOProvider::ioprvIsInitialized() const
{
	return m_bInitialized;
}

inline HRESULT CDriverIOProvider::ioprvCloseIOProvider()
{
	HRESULT hr = S_OK;

	m_bInitialized = false;
	m_bTimedOut = false;
	m_pObjectModelNode = NULL;
	m_pErrorManager = NULL;
	m_pPerfMonManager = NULL;

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvSetTrailingForReads(const CString& strTrailing)
{
	HRESULT hr = S_OK;

	m_strTrailingForReads = strTrailing;

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvSetTrailingForWrites(const CString& strTrailing)
{
	HRESULT hr = S_OK;

	m_strTrailingForWrites = strTrailing;

	return hr;
}

inline HRESULT CDriverIOProvider::_ioprvInstrRead(BYTE* pBuf, long cbDesired, long* plActualLength)
{
	if (pBuf == NULL)
		return E_POINTER;

	if (cbDesired < 0)
		return E_INVALIDARG;

	if (plActualLength == NULL)
		return E_POINTER;

	*plActualLength = 0;

	HRESULT hr = ioprvMessageIORead(pBuf, cbDesired, plActualLength);
	
	return hr;
}

inline HRESULT CDriverIOProvider::_ioprvInstrRead(CString& strResult, long cchDesired, bool bRemoveTrailing)
{
	CComBSTR bstr;
	HRESULT hr = ioprvMessageIOReadString(cchDesired, &bstr);
	if (SUCCEEDED(hr))
	{
		strResult = COLE2T(bstr);

		if (bRemoveTrailing)
		{
			if (strResult.Right(m_strTrailingForReads.GetLength()) == m_strTrailingForReads)
			{
				strResult = strResult.Left(strResult.GetLength() - m_strTrailingForReads.GetLength());
			}
		}
	}

	return hr;
}

inline HRESULT CDriverIOProvider::_ioprvInstrRead(SAFEARRAY** ppsa, long cbDesired)
{
	if (ppsa == NULL)
		return E_POINTER;

	HRESULT hr = ioprvMessageIORead(cbDesired, ppsa);

	return hr;
}

inline HRESULT CDriverIOProvider::_ioprvInstrWrite(BYTE* pBuf, long cbDesired, long* pcbActual)
{
	if (pBuf == NULL)
		return E_POINTER;

	if (cbDesired < 0)
		return E_INVALIDARG;

	if (pcbActual == NULL)
		return E_POINTER;

	HRESULT hr = ioprvMessageIOWrite(pBuf, cbDesired, pcbActual);
	if (SUCCEEDED(hr))
	{
		if (*pcbActual < cbDesired)
		{
			// Could not write the whole thing
			hr = E_FAIL;
		}
	}

	return hr;
}

inline HRESULT CDriverIOProvider::_ioprvInstrWrite(const CString& strCommand)
{
	CString strCmd(strCommand);

	// Add the trailing characters if they are not already there
	int nTrailingForWriteLength = m_strTrailingForWrites.GetLength();
	if (strCmd.GetLength() < nTrailingForWriteLength || strCmd.Right(nTrailingForWriteLength) != m_strTrailingForWrites)
	{
		strCmd += m_strTrailingForWrites;
	}

	long cbActual;
	HRESULT hr = ioprvMessageIOWriteString(CComBSTR(strCmd), &cbActual);
	if (SUCCEEDED(hr))
	{
		if (strCmd.GetLength() > cbActual)
		{
			// Could not write the whole thing
			hr = E_FAIL;
		}
	}

	return hr;
}

inline HRESULT CDriverIOProvider::_ioprvInstrWrite(SAFEARRAY* psa, long cbDesired)
{
	if (psa == NULL)
		return E_POINTER;

	if (::SafeArrayGetDim(psa) != 1)
		return E_INVALIDARG;

	if (!IsSafeArrayElementOfType(psa, VT_UI1))
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	long lArrayLength = GetSafeArrayLength(psa);

	if (cbDesired == -1)
	{
		// cbDesired == -1 means write the whole array
		cbDesired = lArrayLength;
	}
	else
	{
		if (cbDesired < 0 || cbDesired > lArrayLength)
		{
			hr = E_INVALIDARG;
		}
	}

	if (SUCCEEDED(hr))
	{
		long cbActual;
		hr = ioprvMessageIOWrite(psa, cbDesired, &cbActual);
		if (SUCCEEDED(hr))
		{
			if (cbActual < cbDesired)
			{
				// Could not write the whole thing
				hr = E_FAIL;
			}
		}
	}

	return hr;
}

inline HRESULT CDriverIOProvider::_ioprvPollInstrumentError()
{
	HRESULT hr = S_OK;

	long cbActual;
	CComBSTR bstrWrite(CString(_T("*ESR?")) + m_strTrailingForWrites);
	hr = ioprvMessageIOWriteString(bstrWrite, &cbActual);
	if (SUCCEEDED(hr))
	{
		CComBSTR bstrRead;
		hr = ioprvMessageIOReadString(100, &bstrRead);
		if (SUCCEEDED(hr))
		{
			long lValue = _wtol(bstrRead);

			if ((lValue & 4) != 0 || (lValue & 8) != 0 || (lValue & 16) != 0 || (lValue & 32) != 0)
			{
				hr = E_FAIL;
			}
		}
		else
		{
			ioprvMessageIOClear();
		}
	}
	else
	{
		ioprvMessageIOClear();
	}

	return hr;
}

inline HRESULT CDriverIOProvider::_ioprvInstrumentError(HRESULT hrSource)
{
	HRESULT hr = S_OK;

	ioprvMessageIOClear();

	if (hrSource == E_NTL_IO_TIMEOUT)
	{
		// Set the timeout flag so that user can distinguish between a timeout and other errors
		m_bTimedOut = true;
		hr = m_pErrorManager->ReportIOTimeoutError();
	}
	else
	{
		hr = m_pErrorManager->ReportIOError();
	}

	if (m_pObjectModelNode->GetQueryInstrStatus())
	{
		// Clear the ESR (reading it will clear it) so that we don't
		// incorrectly report an error in the next driver method/property
		// Ignore the return value, as we don't want to overwrite the existing error.
		HRESULT hr2 = _ioprvPollInstrumentError();
		NTL_UNUSED(hr2);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvPollInstrumentErrors()
{
	HRESULT hr = S_OK;

	if (m_pObjectModelNode->GetQueryInstrStatus())
	{
		hr = _ioprvPollInstrumentError();
		if (FAILED(hr))
		{
			hr = m_pErrorManager->ReportError(IDS_E_IVI_INSTRUMENT_STATUS);
		}
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvDeviceClear()
{
	HRESULT hr = S_OK;
	
	hr = ioprvMessageIOClear();

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvReadSTB(long* plStatus)
{
	HRESULT hr = S_OK;
	
	hr = ioprvMessageIOReadSTB(plStatus);

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvIsTimeout(bool* pbTimeout)
{
	if (pbTimeout == NULL)
		return E_POINTER;

	HRESULT hr = S_OK;

	*pbTimeout = m_bTimedOut;

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvSetIOTimeout(long lTimeout)
{
	HRESULT hr = S_OK;

	hr = ioprvMessageIOSetTimeout(lTimeout);

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvGetIOTimeout(long* plTimeout)
{
	if (plTimeout == NULL)
		return E_POINTER;

	HRESULT hr = S_OK;

	hr = ioprvMessageIOGetTimeout(plTimeout);

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvInstrQuery(const CString& strCommand, double* pdResult, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	if (pdResult == NULL)
		return E_POINTER;

	HRESULT hr = _ioprvInstrWrite(strCommand);
	if (SUCCEEDED(hr))
	{
		// It's a number, read a fixed number of characters
		CString strResult;
		hr = _ioprvInstrRead(strResult, NUMBER_BUFFER_SIZE);
		if (SUCCEEDED(hr))
		{
			*pdResult = _tcstod(strResult, NULL);

			hr = ioprvPollInstrumentErrors();
		}
		else
		{
			hr = _ioprvInstrumentError(hr);
		}
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvInstrQuery(const CString& strCommand, long* plResult, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	if (plResult == NULL)
		return E_POINTER;

	HRESULT hr = _ioprvInstrWrite(strCommand);
	if (SUCCEEDED(hr))
	{
		// It's a number, read a fixed number of characters
		CString strResult;
		hr = _ioprvInstrRead(strResult, NUMBER_BUFFER_SIZE);
		if (SUCCEEDED(hr))
		{
			// Parse it as a double even though we return a long
			*plResult = (long)_tcstod(strResult, NULL);

			hr = ioprvPollInstrumentErrors();
		}
		else
		{
			hr = _ioprvInstrumentError(hr);
		}
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvInstrQuery(const CString& strCommand, CString& strResult, long cchDesired, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = _ioprvInstrWrite(strCommand);
	if (SUCCEEDED(hr))
	{
		hr = _ioprvInstrRead(strResult, cchDesired);
		if (SUCCEEDED(hr))
		{
			hr = ioprvPollInstrumentErrors();
		}
		else
		{
			hr = _ioprvInstrumentError(hr);
		}
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvInstrRead(BYTE* pBuf, long cbDesired, long* plActualLength, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = _ioprvInstrRead(pBuf, cbDesired, plActualLength);
	if (SUCCEEDED(hr))
	{
		hr = ioprvPollInstrumentErrors();
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}
	
	return hr;
}

inline HRESULT CDriverIOProvider::ioprvInstrRead(CString& strResult, long cchDesired, long lTimeout, bool bRemoveTrailing)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = _ioprvInstrRead(strResult, cchDesired, bRemoveTrailing);
	if (SUCCEEDED(hr))
	{
		hr = ioprvPollInstrumentErrors();
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvInstrRead(SAFEARRAY** ppsa, long cbDesired, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = _ioprvInstrRead(ppsa, cbDesired);
	if (SUCCEEDED(hr))
	{
		hr = ioprvPollInstrumentErrors();
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvInstrWrite(BYTE* pBuf, long cbDesired, long* pcbActual, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = _ioprvInstrWrite(pBuf, cbDesired, pcbActual);
	if (SUCCEEDED(hr))
	{
		hr = ioprvPollInstrumentErrors();
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvInstrWrite(const CString& strCommand, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = _ioprvInstrWrite(strCommand);
	if (SUCCEEDED(hr))
	{
		hr = ioprvPollInstrumentErrors();
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvInstrWrite(SAFEARRAY* psa, long cbDesired, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = _ioprvInstrWrite(psa, cbDesired);
	if (SUCCEEDED(hr))
	{
		hr = ioprvPollInstrumentErrors();
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvInstrWriteNoPoll(BYTE* pBuf, long cbDesired, long* pcbActual, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = _ioprvInstrWrite(pBuf, cbDesired, pcbActual);
	if (FAILED(hr))
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvInstrWriteNoPoll(const CString& strCommand, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = _ioprvInstrWrite(strCommand);
	if (FAILED(hr))
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvInstrWriteNoPoll(SAFEARRAY* psa, long cbDesired, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = _ioprvInstrWrite(psa, cbDesired);
	if (FAILED(hr))
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteString(const CString& strData, bool bFlushAndEnd, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = ioprvFormattedIOWriteString(strData, bFlushAndEnd);
	if (SUCCEEDED(hr))
	{
		if (bFlushAndEnd)
		{
			hr = ioprvPollInstrumentErrors();
		}
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteNumber(double val, bool bFlushAndEnd, long lTimeout)
{
	return ioprvFormattedWriteNumber(CComVariant(val), NtlAsciiType_R8, bFlushAndEnd, lTimeout);
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteNumber(long val, bool bFlushAndEnd, long lTimeout)
{
	return ioprvFormattedWriteNumber(CComVariant(val), NtlAsciiType_I4, bFlushAndEnd, lTimeout);
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteNumber(BYTE val, bool bFlushAndEnd, long lTimeout)
{
	return ioprvFormattedWriteNumber(CComVariant(val), NtlAsciiType_UI1, bFlushAndEnd, lTimeout);
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteNumber(VARIANT data, NtlAsciiType type, bool bFlushAndEnd, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = ioprvFormattedIOWriteNumber(data, type, bFlushAndEnd);
	if (SUCCEEDED(hr))
	{
		if (bFlushAndEnd)
		{
			hr = ioprvPollInstrumentErrors();
		}
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteList(VARIANT data, NtlAsciiType type, const CString& strListSeparator, bool bFlushAndEnd, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = ioprvFormattedIOWriteList(data, type, strListSeparator, bFlushAndEnd);
	if (SUCCEEDED(hr))
	{
		if (bFlushAndEnd)
		{
			hr = ioprvPollInstrumentErrors();
		}
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedFlushWrite(bool bSendEND, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = ioprvFormattedIOFlushWrite(bSendEND);
	if (SUCCEEDED(hr))
	{
		hr = ioprvPollInstrumentErrors();
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedFlushRead(long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = ioprvFormattedIOFlushRead();
	if (SUCCEEDED(hr))
	{
		hr = ioprvPollInstrumentErrors();
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedSetBufferSize(NtlBufferMask mask, long size)
{
	HRESULT hr = ioprvFormattedIOSetBufferSize(mask, size);

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteList(double* pValues, long lLength, const CString& strListSeparator, bool bFlushAndEnd, long lTimeout)
{
	SAFEARRAY* psa;
	HRESULT hr = CSafeArray<double>::CreateFromArray(pValues, lLength, VT_R8, &psa);
	if (SUCCEEDED(hr))
	{
		VARIANT v;
		v.vt = VT_ARRAY | VT_R8;
		v.parray = psa;

		hr = ioprvFormattedWriteList(v, NtlAsciiType_R8, strListSeparator, bFlushAndEnd, lTimeout);

		::SafeArrayDestroy(psa);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteList(long* pValues, long lLength, const CString& strListSeparator, bool bFlushAndEnd, long lTimeout)
{
	SAFEARRAY* psa;
	HRESULT hr = CSafeArray<long>::CreateFromArray(pValues, lLength, VT_I4, &psa);
	if (SUCCEEDED(hr))
	{
		VARIANT v;
		v.vt = VT_ARRAY | VT_I4;
		v.parray = psa;

		hr = ioprvFormattedWriteList(v, NtlAsciiType_I4, strListSeparator, bFlushAndEnd, lTimeout);

		::SafeArrayDestroy(psa);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteList(BYTE* pValues, long lLength, const CString& strListSeparator, bool bFlushAndEnd, long lTimeout)
{
	SAFEARRAY* psa;
	HRESULT hr = CSafeArray<BYTE>::CreateFromArray(pValues, lLength, VT_UI1, &psa);
	if (SUCCEEDED(hr))
	{
		VARIANT v;
		v.vt = VT_ARRAY | VT_UI1;
		v.parray = psa;

		hr = ioprvFormattedWriteList(v, NtlAsciiType_UI1, strListSeparator, bFlushAndEnd, lTimeout);

		::SafeArrayDestroy(psa);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteList(SAFEARRAY* psa, NtlAsciiType type, const CString& strListSeparator, bool bFlushAndEnd, long lTimeout)
{
	VARTYPE vt;
	HRESULT hr = SafeArrayGetVartype(psa, &vt);
	if (SUCCEEDED(hr))
	{
		VARIANT v;
		v.vt = (VARTYPE)(VT_ARRAY | vt);
		v.parray = psa;

		hr = ioprvFormattedWriteList(v, type, strListSeparator, bFlushAndEnd, lTimeout);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteIEEEBlock(const CString& strCommand, double* pValues, long lLength, bool bFlushAndEnd, long lTimeout)
{
	SAFEARRAY* psa;
	HRESULT hr = CSafeArray<double>::CreateFromArray(pValues, lLength, VT_R8, &psa);
	if (SUCCEEDED(hr))
	{
		VARIANT v;
		v.vt = (VARTYPE)(VT_ARRAY | VT_R8);
		v.parray = psa;

		hr = ioprvFormattedWriteIEEEBlock(strCommand, v, bFlushAndEnd, lTimeout);

		::SafeArrayDestroy(psa);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteIEEEBlock(const CString& strCommand, long* pValues, long lLength, bool bFlushAndEnd, long lTimeout)
{
	SAFEARRAY* psa;
	HRESULT hr = CSafeArray<long>::CreateFromArray(pValues, lLength, VT_I4, &psa);
	if (SUCCEEDED(hr))
	{
		VARIANT v;
		v.vt = VT_ARRAY | VT_I4;
		v.parray = psa;

		hr = ioprvFormattedWriteIEEEBlock(strCommand, v, bFlushAndEnd, lTimeout);

		::SafeArrayDestroy(psa);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteIEEEBlock(const CString& strCommand, short* pValues, long lLength, bool bFlushAndEnd, long lTimeout)
{
	SAFEARRAY* psa;
	HRESULT hr = CSafeArray<short>::CreateFromArray(pValues, lLength, VT_I2, &psa);
	if (SUCCEEDED(hr))
	{
		VARIANT v;
		v.vt = VT_ARRAY | VT_I2;
		v.parray = psa;

		hr = ioprvFormattedWriteIEEEBlock(strCommand, v, bFlushAndEnd, lTimeout);

		::SafeArrayDestroy(psa);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteIEEEBlock(const CString& strCommand, BYTE* pValues, long lLength, bool bFlushAndEnd, long lTimeout)
{
	SAFEARRAY* psa;
	HRESULT hr = CSafeArray<BYTE>::CreateFromArray(pValues, lLength, VT_UI1, &psa);
	if (SUCCEEDED(hr))
	{
		VARIANT v;
		v.vt = VT_ARRAY | VT_UI1;
		v.parray = psa;

		hr = ioprvFormattedWriteIEEEBlock(strCommand, v, bFlushAndEnd, lTimeout);

		::SafeArrayDestroy(psa);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteIEEEBlock(const CString& strCommand, VARIANT data, bool bFlushAndEnd, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = ioprvFormattedIOWriteIEEEBlock(strCommand, data, bFlushAndEnd);
	if (SUCCEEDED(hr))
	{
		if (bFlushAndEnd)
		{
			hr = ioprvPollInstrumentErrors();
		}
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedWriteIEEEBlock(const CString& strCommand, SAFEARRAY* pData, bool bFlushAndEnd, long lTimeout)
{
	VARTYPE vt;
	HRESULT hr = ::SafeArrayGetVartype(pData, &vt);
	if (SUCCEEDED(hr))
	{
		VARIANT v;
		v.vt = (VARTYPE)(VT_ARRAY | vt);
		v.parray = pData;

		hr = ioprvFormattedWriteIEEEBlock(strCommand, v, bFlushAndEnd, lTimeout);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadString(CString& strResult, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	HRESULT hr = ioprvFormattedIOReadString(strResult);
	if (SUCCEEDED(hr))
	{
		hr = ioprvPollInstrumentErrors();
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadString(BSTR* pData, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	if (pData == NULL)
		return E_POINTER;

	CString strResult;
	HRESULT hr = ioprvFormattedIOReadString(strResult);
	if (SUCCEEDED(hr))
	{
		hr = ioprvPollInstrumentErrors();
		if (SUCCEEDED(hr))
		{
			*pData = CComBSTR(strResult).Detach();
		}
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadNumber(double* pValue, bool bFlushToEnd, long lTimeout)
{
	if (pValue == NULL)
		return E_POINTER;

	CComVariant v;
	HRESULT hr = ioprvFormattedReadNumber(NtlAsciiType_R8, &v, bFlushToEnd, lTimeout);
	if (SUCCEEDED(hr))
	{
		// Returned type is always double
		if (v.vt == VT_R8)
		{
			*pValue = v.dblVal;
		}
		else
		{
			hr = E_FAIL;
		}
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadNumber(long* pValue, bool bFlushToEnd, long lTimeout)
{
	if (pValue == NULL)
		return E_POINTER;

	CComVariant v;
	HRESULT hr = ioprvFormattedReadNumber(NtlAsciiType_I4, &v, bFlushToEnd, lTimeout);
	if (SUCCEEDED(hr))
	{
		// Returned type is always double
		if (v.vt == VT_R8)
		{
			hr = v.ChangeType(VT_I4);
			if (SUCCEEDED(hr))
			{
				*pValue = v.lVal;
			}
		}
		else
		{
			hr = E_FAIL;
		}
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadNumber(BYTE* pValue, bool bFlushToEnd, long lTimeout)
{
	if (pValue == NULL)
		return E_POINTER;

	CComVariant v;
	HRESULT hr = ioprvFormattedReadNumber(NtlAsciiType_UI1, &v, bFlushToEnd, lTimeout);
	if (SUCCEEDED(hr))
	{
		// Returned type is always double
		if (v.vt == VT_R8)
		{
			hr = v.ChangeType(VT_UI1);
			if (SUCCEEDED(hr))
			{
				*pValue = v.bVal;
			}
		}
		else
		{
			hr = E_FAIL;
		}
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadNumber(NtlAsciiType type, VARIANT* pData, bool bFlushToEnd, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	if (pData == NULL)
		return E_POINTER;

	HRESULT hr = ioprvFormattedIOReadNumber(type, pData, bFlushToEnd);
	if (SUCCEEDED(hr))
	{
		hr = ioprvPollInstrumentErrors();
		if (FAILED(hr))
		{
			::VariantClear(pData);
		}
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadList(double* pValues, long lMaxLength, long* plActualLength, const CString& strListSeparator, long lTimeout)
{
	if (plActualLength == NULL)
		return E_POINTER;

	*plActualLength = 0;

	CComVariant v;
	HRESULT hr = ioprvFormattedReadList(NtlAsciiType_R8, strListSeparator, &v, lTimeout);
	if (SUCCEEDED(hr))
	{
		hr = CSafeArray<double>::Write(v, pValues, lMaxLength, plActualLength);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadList(long* pValues, long lMaxLength, long* plActualLength, const CString& strListSeparator, long lTimeout)
{
	if (plActualLength == NULL)
		return E_POINTER;

	*plActualLength = 0;
	
	CComVariant v;
	HRESULT hr = ioprvFormattedReadList(NtlAsciiType_I4, strListSeparator, &v, lTimeout);
	if (SUCCEEDED(hr))
	{
		hr = CSafeArray<long>::Write(v, pValues, lMaxLength, plActualLength);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadList(BYTE* pValues, long lMaxLength, long* plActualLength, const CString& strListSeparator, long lTimeout)
{
	if (plActualLength == NULL)
		return E_POINTER;

	*plActualLength = 0;
	
	CComVariant v;
	HRESULT hr = ioprvFormattedReadList(NtlAsciiType_UI1, strListSeparator, &v, lTimeout);
	if (SUCCEEDED(hr))
	{
		hr = CSafeArray<BYTE>::Write(v, pValues, lMaxLength, plActualLength);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadList(CComBSTR* pValues, long lMaxLength, long* plActualLength, const CString& strListSeparator, long lTimeout)
{
	if (plActualLength == NULL)
		return E_POINTER;

	*plActualLength = 0;
	
	CComVariant v;
	HRESULT hr = ioprvFormattedReadList(NtlAsciiType_BSTR, strListSeparator, &v, lTimeout);
	if (SUCCEEDED(hr))
	{
		hr = CSafeArray<BSTR>::WriteString(v, pValues, lMaxLength, plActualLength);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadList(NtlAsciiType type, const CString& strListSeparator, VARIANT* pData, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	if (pData == NULL)
		return E_POINTER;

	HRESULT hr = ioprvFormattedIOReadList(type, strListSeparator, pData);
	if (SUCCEEDED(hr))
	{
		hr = ioprvPollInstrumentErrors();
		if (FAILED(hr))
		{
			::VariantClear(pData);
		}
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadList(NtlAsciiType type, const CString& strListSeparator, SAFEARRAY** pData, long lTimeout)
{
	VARIANT v;
	HRESULT hr = ioprvFormattedReadList(type, strListSeparator, &v, lTimeout);
	if (SUCCEEDED(hr))
	{
		ATLASSERT((v.vt & VT_ARRAY) != 0);
		ATLASSERT((v.vt & VT_BYREF) == 0);

		SAFEARRAY* psa = v.parray;

		ATLASSERT(psa != NULL);

		if (psa != NULL)
		{
			*pData = psa;
		}
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadIEEEBlock(double* pValues, long lMaxLength, long* plActualLength, bool bSeekToBlock, bool bFlushToEnd, long lTimeout)
{
	CComVariant v;
	HRESULT hr = ioprvFormattedReadIEEEBlock(NtlBinaryType_R8, &v, bSeekToBlock, bFlushToEnd, lTimeout);
	if (SUCCEEDED(hr))
	{
		hr = CSafeArray<double>::Write(v, pValues, lMaxLength, plActualLength);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadIEEEBlock(long* pValues, long lMaxLength, long* plActualLength, bool bSeekToBlock, bool bFlushToEnd, long lTimeout)
{
	CComVariant v;
	HRESULT hr = ioprvFormattedReadIEEEBlock(NtlBinaryType_I4, &v, bSeekToBlock, bFlushToEnd, lTimeout);
	if (SUCCEEDED(hr))
	{
		hr = CSafeArray<long>::Write(v, pValues, lMaxLength, plActualLength);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadIEEEBlock(BYTE* pValues, long lMaxLength, long* plActualLength, bool bSeekToBlock, bool bFlushToEnd, long lTimeout)
{
	CComVariant v;
	HRESULT hr = ioprvFormattedReadIEEEBlock(NtlBinaryType_UI1, &v, bSeekToBlock, bFlushToEnd, lTimeout);
	if (SUCCEEDED(hr))
	{
		hr = CSafeArray<BYTE>::Write(v, pValues, lMaxLength, plActualLength);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadIEEEBlock(NtlBinaryType type, VARIANT* pData, bool bSeekToBlock, bool bFlushAndEnd, long lTimeout)
{
	DECLARE_SET_AND_RESTORE_TIMEOUT(lTimeout)
	RESET_TIMED_OUT()

	if (pData == NULL)
		return E_POINTER;

	HRESULT hr = ioprvFormattedIOReadIEEEBlock(type, pData, bSeekToBlock, bFlushAndEnd);
	if (SUCCEEDED(hr))
	{
		if (bFlushAndEnd)
		{
			hr = ioprvPollInstrumentErrors();
			if (FAILED(hr))
			{
				::VariantClear(pData);
			}
		}
	}
	else
	{
		hr = _ioprvInstrumentError(hr);
	}

	return hr;
}

inline HRESULT CDriverIOProvider::ioprvFormattedReadIEEEBlock(NtlBinaryType type, SAFEARRAY** pData, bool bSeekToBlock, bool bFlushAndEnd, long lTimeout)
{
	VARIANT v;
	HRESULT hr = ioprvFormattedReadIEEEBlock(type, &v, bSeekToBlock, bFlushAndEnd, lTimeout);
	if (SUCCEEDED(hr))
	{
		ATLASSERT((v.vt & VT_ARRAY) != 0);
		ATLASSERT((v.vt & VT_BYREF) == 0);

		SAFEARRAY* psa = v.parray;

		ATLASSERT(psa != NULL);

		if (psa != NULL)
		{
			*pData = psa;
		}
	}

	return hr;
}


#endif // #endif __NTLIOPROVIDER_INCLUDED
