TAPIINFO.C

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1995 - 1998 Microsoft Corporation. All Rights Reserved.
//
// MODULE: TapiInfo.c
//
// PURPOSE: Handles all Pretty Printing functions for the TapiComm sample.
//
// EXPORTED FUNCTIONS: These functions are for use by other modules.
//
// All of these pretty print to the debugging output.
// OutputDebugLineCallback - Calls FormatLineCallback.
// OutputDebugLineError - Calls OutputDebugLineErrorFileLine.
// OutputDebugLastError - Calls OutputDebugLineErrorFileLine.
// OutputDebugPrintf - Calls wsprintf
// OutputDebugLineErrorFileLine - Calls FormatLineError
// OutputDebugLastErrorFileLine - Calls FormatLastError
//
// All of these functions pretty print to a string buffer.
// FormatLineError - Prints a LINEERR
// FormatLastError - Prints a GetLastError error.
// FormatLineCallback - Prints a lineCallbackFunc message.
//
// INTERNAL FUNCTION: These functions are for this module only.
// strBinaryArrayAppend - prints a binary flag array to a buffer.

#include <windows.h>
#include <tapi.h>
#include "TapiInfo.h"

// Maximum length of all internal string buffers.
#define MAXOUTPUTSTRINGLENGTH 4096

// define to make accessing arrays easy.
#define sizeofArray(pArray) (sizeof(pArray) / sizeof((pArray)[0]))


//*****************************************
// Internal prototypes.
//*****************************************

static long strBinaryArrayAppend(LPSTR pszOutputBuffer, DWORD dwFlags,
LPSTR szStringArray[], DWORD dwSizeofStringArray);



//*****************************************
// Global arrays for interpreting TAPI constants.
//*****************************************

LPSTR pszLineErrorNameArray[] =
{
"",
"LINEERR_ALLOCATED",
"LINEERR_BADDEVICEID",
"LINEERR_BEARERMODEUNAVAIL",
"LINEERR Unused constant, ERROR!!",
"LINEERR_CALLUNAVAIL",
"LINEERR_COMPLETIONOVERRUN",
"LINEERR_CONFERENCEFULL",
"LINEERR_DIALBILLING",
"LINEERR_DIALDIALTONE",
"LINEERR_DIALPROMPT",
"LINEERR_DIALQUIET",
"LINEERR_INCOMPATIBLEAPIVERSION",
"LINEERR_INCOMPATIBLEEXTVERSION",
"LINEERR_INIFILECORRUPT",
"LINEERR_INUSE",
"LINEERR_INVALADDRESS",
"LINEERR_INVALADDRESSID",
"LINEERR_INVALADDRESSMODE",
"LINEERR_INVALADDRESSSTATE",
"LINEERR_INVALAPPHANDLE",
"LINEERR_INVALAPPNAME",
"LINEERR_INVALBEARERMODE",
"LINEERR_INVALCALLCOMPLMODE",
"LINEERR_INVALCALLHANDLE",
"LINEERR_INVALCALLPARAMS",
"LINEERR_INVALCALLPRIVILEGE",
"LINEERR_INVALCALLSELECT",
"LINEERR_INVALCALLSTATE",
"LINEERR_INVALCALLSTATELIST",
"LINEERR_INVALCARD",
"LINEERR_INVALCOMPLETIONID",
"LINEERR_INVALCONFCALLHANDLE",
"LINEERR_INVALCONSULTCALLHANDLE",
"LINEERR_INVALCOUNTRYCODE",
"LINEERR_INVALDEVICECLASS",
"LINEERR_INVALDEVICEHANDLE",
"LINEERR_INVALDIALPARAMS",
"LINEERR_INVALDIGITLIST",
"LINEERR_INVALDIGITMODE",
"LINEERR_INVALDIGITS",
"LINEERR_INVALEXTVERSION",
"LINEERR_INVALGROUPID",
"LINEERR_INVALLINEHANDLE",
"LINEERR_INVALLINESTATE",
"LINEERR_INVALLOCATION",
"LINEERR_INVALMEDIALIST",
"LINEERR_INVALMEDIAMODE",
"LINEERR_INVALMESSAGEID",
"LINEERR Unused constant, ERROR!!",
"LINEERR_INVALPARAM",
"LINEERR_INVALPARKID",
"LINEERR_INVALPARKMODE",
"LINEERR_INVALPOINTER",
"LINEERR_INVALPRIVSELECT",
"LINEERR_INVALRATE",
"LINEERR_INVALREQUESTMODE",
"LINEERR_INVALTERMINALID",
"LINEERR_INVALTERMINALMODE",
"LINEERR_INVALTIMEOUT",
"LINEERR_INVALTONE",
"LINEERR_INVALTONELIST",
"LINEERR_INVALTONEMODE",
"LINEERR_INVALTRANSFERMODE",
"LINEERR_LINEMAPPERFAILED",
"LINEERR_NOCONFERENCE",
"LINEERR_NODEVICE",
"LINEERR_NODRIVER",
"LINEERR_NOMEM",
"LINEERR_NOREQUEST",
"LINEERR_NOTOWNER",
"LINEERR_NOTREGISTERED",
"LINEERR_OPERATIONFAILED",
"LINEERR_OPERATIONUNAVAIL",
"LINEERR_RATEUNAVAIL",
"LINEERR_RESOURCEUNAVAIL",
"LINEERR_REQUESTOVERRUN",
"LINEERR_STRUCTURETOOSMALL",
"LINEERR_TARGETNOTFOUND",
"LINEERR_TARGETSELF",
"LINEERR_UNINITIALIZED",
"LINEERR_USERUSERINFOTOOBIG",
"LINEERR_REINIT",
"LINEERR_ADDRESSBLOCKED",
"LINEERR_BILLINGREJECTED",
"LINEERR_INVALFEATURE",
"LINEERR_NOMULTIPLEINSTANCE"
};


LPSTR psz_dwMsg[] = {
"LINE_ADDRESSSTATE",
"LINE_CALLINFO",
"LINE_CALLSTATE",
"LINE_CLOSE",
"LINE_DEVSPECIFIC",
"LINE_DEVSPECIFICFEATURE",
"LINE_GATHERDIGITS",
"LINE_GENERATE",
"LINE_LINEDEVSTATE",
"LINE_MONITORDIGITS",
"LINE_MONITORMEDIA",
"LINE_MONITORTONE",
"LINE_REPLY",
"LINE_REQUEST",
"PHONE_BUTTON",
"PHONE_CLOSE",
"PHONE_DEVSPECIFIC",
"PHONE_REPLY",
"PHONE_STATE",
"LINE_CREATE",
"PHONE_CREATE"
};


LPSTR pszfLINEADDRESSSTATE[] =
{
"Unknown LINEADDRESSSTATE information",
"LINEADDRESSSTATE_OTHER",
"LINEADDRESSSTATE_DEVSPECIFIC",
"LINEADDRESSSTATE_INUSEZERO",
"LINEADDRESSSTATE_INUSEONE",
"LINEADDRESSSTATE_INUSEMANY",
"LINEADDRESSSTATE_NUMCALLS",
"LINEADDRESSSTATE_FORWARD",
"LINEADDRESSSTATE_TERMINALS",
"LINEADDRESSSTATE_CAPSCHANGE"
};


LPSTR pszfLINECALLINFOSTATE[] =
{
"Unknown LINECALLINFOSTATE state",
"LINECALLINFOSTATE_OTHER",
"LINECALLINFOSTATE_DEVSPECIFIC",
"LINECALLINFOSTATE_BEARERMODE",
"LINECALLINFOSTATE_RATE",
"LINECALLINFOSTATE_MEDIAMODE",
"LINECALLINFOSTATE_APPSPECIFIC",
"LINECALLINFOSTATE_CALLID",
"LINECALLINFOSTATE_RELATEDCALLID",
"LINECALLINFOSTATE_ORIGIN",
"LINECALLINFOSTATE_REASON",
"LINECALLINFOSTATE_COMPLETIONID",
"LINECALLINFOSTATE_NUMOWNERINCR",
"LINECALLINFOSTATE_NUMOWNERDECR",
"LINECALLINFOSTATE_NUMMONITORS",
"LINECALLINFOSTATE_TRUNK",
"LINECALLINFOSTATE_CALLERID",
"LINECALLINFOSTATE_CALLEDID",
"LINECALLINFOSTATE_CONNECTEDID",
"LINECALLINFOSTATE_REDIRECTIONID",
"LINECALLINFOSTATE_REDIRECTINGID",
"LINECALLINFOSTATE_DISPLAY",
"LINECALLINFOSTATE_USERUSERINFO",
"LINECALLINFOSTATE_HIGHLEVELCOMP",
"LINECALLINFOSTATE_LOWLEVELCOMP",
"LINECALLINFOSTATE_CHARGINGINFO",
"LINECALLINFOSTATE_TERMINAL",
"LINECALLINFOSTATE_DIALPARAMS",
"LINECALLINFOSTATE_MONITORMODES"
};


LPSTR pszfLINECALLSTATE[] =
{
"Unknown LINECALLSTATE state",
"LINECALLSTATE_IDLE",
"LINECALLSTATE_OFFERING",
"LINECALLSTATE_ACCEPTED",
"LINECALLSTATE_DIALTONE",
"LINECALLSTATE_DIALING",
"LINECALLSTATE_RINGBACK",
"LINECALLSTATE_BUSY",
"LINECALLSTATE_SPECIALINFO",
"LINECALLSTATE_CONNECTED",
"LINECALLSTATE_PROCEEDING",
"LINECALLSTATE_ONHOLD",
"LINECALLSTATE_CONFERENCED",
"LINECALLSTATE_ONHOLDPENDCONF",
"LINECALLSTATE_ONHOLDPENDTRANSFER",
"LINECALLSTATE_DISCONNECTED",
"LINECALLSTATE_UNKNOWN"
};


LPSTR pszfLINEDIALTONEMODE[] =
{
"Unknown LINEDIALTONE information",
"LINEDIALTONEMODE_NORMAL",
"LINEDIALTONEMODE_SPECIAL",
"LINEDIALTONEMODE_INTERNAL",
"LINEDIALTONEMODE_EXTERNAL",
"LINEDIALTONEMODE_UNKNOWN",
"LINEDIALTONEMODE_UNAVAIL"
};


LPSTR pszfLINEBUSYMODE[] =
{
"Unknown LINEBUSYMODE information",
"LINEBUSYMODE_STATION",
"LINEBUSYMODE_TRUNK",
"LINEBUSYMODE_UNKNOWN",
"LINEBUSYMODE_UNAVAIL"
};


LPSTR pszfLINESPECIALINFO[] =
{
"Unknown LINESPECIALINFO information",
"LINESPECIALINFO_NOCIRCUIT",
"LINESPECIALINFO_CUSTIRREG",
"LINESPECIALINFO_REORDER",
"LINESPECIALINFO_UNKNOWN",
"LINESPECIALINFO_UNAVAIL"
};


LPSTR pszfLINEDISCONNECTED[] =
{
"Unknown LINEDISCONNECTED information",
"LINEDISCONNECTMODE_NORMAL",
"LINEDISCONNECTMODE_UNKNOWN",
"LINEDISCONNECTMODE_REJECT",
"LINEDISCONNECTMODE_PICKUP",
"LINEDISCONNECTMODE_FORWARDED",
"LINEDISCONNECTMODE_BUSY",
"LINEDISCONNECTMODE_NOANSWER",
"LINEDISCONNECTMODE_BADADDRESS",
"LINEDISCONNECTMODE_UNREACHABLE",
"LINEDISCONNECTMODE_CONGESTION",
"LINEDISCONNECTMODE_INCOMPATIBLE",
"LINEDISCONNECTMODE_UNAVAIL",
"LINEDISCONNECTMODE_NODIALTONE"
};


LPSTR pszfLINECALLPRIVILEGE[] =
{
"No change to LINECALLPRIVILEGE",
"LINECALLPRIVILEGE_NONE",
"LINECALLPRIVILEGE_MONITOR",
"LINECALLPRIVILEGE_OWNER"
};


LPSTR pszfLINEGATHERTERM[] =
{
"Unknown LINEGATHERTERM message",
"LINEGATHERTERM_BUFFERFULL",
"LINEGATHERTERM_TERMDIGIT",
"LINEGATHERTERM_FIRSTTIMEOUT",
"LINEGATHERTERM_INTERTIMEOUT",
"LINEGATHERTERM_CANCEL"
};


LPSTR pszfLINEGENERATETERM[] =
{
"Unknown LINEGENERATETERM message",
"LINEGENERATETERM_DONE",
"LINEGENERATETERM_CANCEL"
};


LPSTR pszfLINEDEVSTATE[] =
{
"Unknown LINEDEVESTATE state",
"LINEDEVSTATE_OTHER",
"LINEDEVSTATE_RINGING",
"LINEDEVSTATE_CONNECTED",
"LINEDEVSTATE_DISCONNECTED",
"LINEDEVSTATE_MSGWAITON",
"LINEDEVSTATE_MSGWAITOFF",
"LINEDEVSTATE_INSERVICE",
"LINEDEVSTATE_OUTOFSERVICE",
"LINEDEVSTATE_MAINTENANCE",
"LINEDEVSTATE_OPEN",
"LINEDEVSTATE_CLOSE",
"LINEDEVSTATE_NUMCALLS",
"LINEDEVSTATE_NUMCOMPLETIONS",
"LINEDEVSTATE_TERMINALS",
"LINEDEVSTATE_ROAMMODE",
"LINEDEVSTATE_BATTERY",
"LINEDEVSTATE_SIGNAL",
"LINEDEVSTATE_DEVSPECIFIC",
"LINEDEVSTATE_REINIT",
"LINEDEVSTATE_LOCK",
"LINEDEVSTATE_CAPSCHANGE",
"LINEDEVSTATE_CONFIGCHANGE",
"LINEDEVSTATE_TRANSLATECHANGE",
"LINEDEVSTATE_COMPLCANCEL",
"LINEDEVSTATE_REMOVED"
};


LPSTR pszfLINEDIGITMODE[] =
{
"Unknown LINEDIGITMODE mode",
"LINEDIGITMODE_PULSE",
"LINEDIGITMODE_DTMF",
"LINEDIGITMODE_DTMFEND"
};


LPSTR pszfLINEMEDIAMODE[] =
{
"Unknown LINEMEDIAMODE mode",
"UnUsed LINEMEDIAMODE mode, ERROR!!",
"LINEMEDIAMODE_UNKNOWN",
"LINEMEDIAMODE_INTERACTIVEVOICE",
"LINEMEDIAMODE_AUTOMATEDVOICE",
"LINEMEDIAMODE_DATAMODEM",
"LINEMEDIAMODE_G3FAX",
"LINEMEDIAMODE_TDD",
"LINEMEDIAMODE_G4FAX",
"LINEMEDIAMODE_DIGITALDATA",
"LINEMEDIAMODE_TELETEX",
"LINEMEDIAMODE_VIDEOTEX",
"LINEMEDIAMODE_TELEX",
"LINEMEDIAMODE_MIXED",
"LINEMEDIAMODE_ADSI",
"LINEMEDIAMODE_VOICEVIEW"
};


LPSTR pszfLINEREQUESTMODE[] =
{
"Unknown LINEREQUESTMODE message",
"LINEREQUESTMODE_MAKECALL",
"LINEREQUESTMODE_MEDIACALL",
"LINEREQUESTMODE_DROP"
};


//
// MACRO: OutputDebugLineError(long, LPSTR)
//
// PURPOSE: Pretty print a line error to the debugging output.
//
// PARAMETERS:
// lLineError - Actual error code to decipher.
// pszPrefix - String to prepend to the printed message.
//
// RETURN VALUE:
// none
//
// COMMENTS:
// This macro is actually defined in the .h file.
// It will take a LINEERR error, turn it into a human
// readable string, prepend pszPrefix (so you
// can tag your errors), append __FILE__ and __LINE__
// and print it to the debugging output.

// This macro is just a wrapper around OutputDebugLineErrorFileLine
// that is necessary to get proper values for __FILE__ and __LINE__.
//
//


/*
#define OuputDebugLineError(lLineError, pszPrefix) \
OutputDebugLineErrorFileLine(lLineError, pszPrefix,\
__FILE__, __LINE__)
*/

//
// FUNCTION: OutputDebugLineErrorFileLine(..)
//
// PURPOSE: Pretty print a line error to the debugging output.
//
// PARAMETERS:
// lLineError - Actual error code to decipher.
// pszPrefix - String to prepend to the printed message.
// szFileName - Filename the error occured in.
// nLineNumber - Line number the error occured at.
//
// RETURN VALUE:
// none
//
// COMMENTS:
// This is the actual function that OutputDebugLineError
// expands to. Its not likely to be usefull except
// through the OutputDebugLineError macro, or to print
// errors without line and file information.
//
// If szFileName == NULL, then the File and Line are not printed.
//
// Note that there is an internal string length limit of
// MAXOUTPUTSTRINGLENGTH. If this length is exceeded,
// the behavior will be the same as wsprintf, although
// it will be undetectable. *KEEP szPrefix SHORT!*
//
//

void OutputDebugLineErrorFileLine(
long lLineError, LPSTR szPrefix,
LPSTR szFileName, DWORD nLineNumber)
{
LPSTR szLineError;
char szOutputLineError[MAXOUTPUTSTRINGLENGTH];

if (szPrefix == NULL)
szPrefix = "";

// Pretty print the error message.
szLineError = FormatLineError(lLineError, NULL, 0);

// The only reason FormatLineError should fail is "Out of memory".
if (szLineError == NULL)
{
if (szFileName == NULL)
wsprintf(szOutputLineError, "%sOut of memory", szPrefix);
else
wsprintf(szOutputLineError,
"%sOut of memory in file %s, line %d\r\n",
szPrefix, szFileName, nLineNumber);

OutputDebugString(szOutputLineError);

return;
}

// If szFileName, then use it; else don't.
if (szFileName != NULL)
{
wsprintf(szOutputLineError,
"%sTapi Line Error: \"%s\" in File \"%s\", Line %d\r\n",
szPrefix, szLineError, szFileName, nLineNumber);
}
else
{
wsprintf(szOutputLineError,
"%sTapi Line Error: \"%s\"\r\n",
szPrefix, szLineError);
}

// Pointer returned from FormatLineError *must* be freed!
LocalFree(szLineError);

// Print it!
OutputDebugString(szOutputLineError);

return;
}


//
// FUNCTION: FormatLineError(long, LPSTR, DWORD)
//
// PURPOSE: Pretty print a line error to a string.
//
// PARAMETERS:
// lLineError - Actual error code to decipher.
// szOutputBuffer - String buffer to pretty print to.
// dwSizeofOutputBuffer - Size of String buffer.
//
// RETURN VALUE:
// Returns the buffer printed to.
//
// COMMENTS:
// If szOutputBuffer isn't big enough to hold the whole string,
// then the string gets truncated to fit the buffer.
//
// If szOutputBuffer == NULL, then dwSizeofOutputBuffer
// is ignored, a buffer 'big enough' is LocalAlloc()d and
// a pointer to it is returned. However, its *very* important
// that this pointer be LocalFree()d by the calling application.
//
//

LPSTR FormatLineError(long lLineError,
LPSTR szOutputBuffer, DWORD dwSizeofOutputBuffer)
{
char szUnknownLineError[256];
LPSTR szLineError;
int nSizeofLineError;
long lErrorIndex;
DWORD * pdwLineError;

// Strip off the high bit to make the error code positive.
pdwLineError = &lLineError;
lErrorIndex = (long) (0x7FFFFFFF & *pdwLineError);

// Is it an unknown error?
if ((lErrorIndex >= sizeofArray(pszLineErrorNameArray)) ||
(lErrorIndex < 0))
{
nSizeofLineError =
wsprintf(szUnknownLineError, "Unknown TAPI line error code: 0x%lx",
lLineError);
szLineError = szUnknownLineError;
}
else
{
szLineError = pszLineErrorNameArray[lErrorIndex];
nSizeofLineError = strlen(szLineError);
}

// allocate a buffer if necessary
if (szOutputBuffer == NULL)
{
szOutputBuffer = (LPSTR) LocalAlloc(LPTR, nSizeofLineError + 1);
if (szOutputBuffer == NULL)
return NULL;
}
else // truncate string if it won't fit in the specified buffer.
{
if ((DWORD) nSizeofLineError >= dwSizeofOutputBuffer)
nSizeofLineError = dwSizeofOutputBuffer - 1;
}

// Put the string into the buffer and null terminate.
memcpy(szOutputBuffer, szLineError, nSizeofLineError);
szOutputBuffer[nSizeofLineError] = '\0';

return szOutputBuffer;
}


//
// MACRO: OutputDebugLastError(DWORD, LPSTR)
//
// PURPOSE: Pretty print a system error to the debugging output.
//
// PARAMETERS:
// dwLastError - Actual error code to decipher.
// pszPrefix - String to prepend to the printed message.
//
// RETURN VALUE:
// none
//
// COMMENTS:
// This macro is actually defined in the .h file.
// It will take an error that was retrieved by GetLastError(),
// turn it into a human readable string, prepend pszPrefix
// (so you can tag your errors), append __FILE__ and __LINE__
// and print it to the debugging output.
//
// This macro is just a wrapper around OutputDebugLastErrorFileLine
// that is necessary to get proper values for __FILE__ and __LINE__.
//
//

/*
#define OuputDebugLastError(dwLastError, pszPrefix) \
OutputDebugLastErrorFileLine(dwLastError, pszPrefix,\
__FILE__, __LINE__)
*/


//
// FUNCTION: OutputDebugLastErrorFileLine(..)
//
// PURPOSE: Pretty print a line error to the debugging output.
//
// PARAMETERS:
// dwLastError - Actual error code to decipher.
// pszPrefix - String to prepend to the printed message.
// szFileName - Filename the error occured in.
// nLineNumber - Line number the error occured at.
//
// RETURN VALUE:
// none
//
// COMMENTS:
// This is the actual function that OutputDebugLastError
// expands to. Its not likely to be usefull except
// through the OutputDebugLastError macro or to print
// errors without line and file information.
//
// If szFileName == NULL, then the File and Line are not printed.
//
// Note that there is an internal string length limit of
// MAXOUTPUTSTRINGLENGTH. If this length is exceeded,
// the behavior will be the same as wsprintf, although
// it will be undetectable. *KEEP szPrefix SHORT!*
//
//

void OutputDebugLastErrorFileLine(
DWORD dwLastError, LPSTR szPrefix,
LPSTR szFileName, DWORD nLineNumber)
{
LPSTR szLastError;
char szOutputLastError[MAXOUTPUTSTRINGLENGTH];

if (szPrefix == NULL)
szPrefix = "";

// Pretty print the error.
szLastError = FormatLastError(dwLastError, NULL, 0);

// The only reason FormatLastError should fail is "Out of memory".
if (szLastError == NULL)
{
if (szFileName == NULL)
wsprintf(szOutputLastError, "%sOut of memory\r\n", szPrefix);
else
wsprintf(szOutputLastError, "%sOut of memory in file %s, line %d\r\n",
szPrefix, szFileName, nLineNumber);

OutputDebugString(szOutputLastError);

return;
}

// If szFileName, then use it; else don't.
if (szFileName != NULL)
{
wsprintf(szOutputLastError,
"%sGetLastError returned: \"%s\" in File \"%s\", Line %d\r\n",
szPrefix, szLastError, szFileName, nLineNumber);
}
else
{
wsprintf(szOutputLastError,
"%sGetLastError returned: \"%s\"\r\n",
szPrefix, szLastError);
}

// Pointer returned from FormatLineError *must* be freed!
LocalFree(szLastError);

// Print it!
OutputDebugString(szOutputLastError);
return;
}


//
// FUNCTION: FormatLastError(DWORD, LPSTR, DWORD)
//
// PURPOSE: Pretty print a system error to a string.
//
// PARAMETERS:
// dwLastError - Actual error code to decipher.
// szOutputBuffer - String buffer to pretty print to.
// dwSizeofOutputBuffer - Size of String buffer.
//
// RETURN VALUE:
// Returns the buffer printed to.
//
// COMMENTS:
// If szOutputBuffer isn't big enough to hold the whole string,
// then the string gets truncated to fit the buffer.
//
// If szOutputBuffer == NULL, then dwSizeofOutputBuffer
// is ignored, a buffer 'big enough' is LocalAlloc()d and
// a pointer to it is returned. However, its *very* important
// that this pointer be LocalFree()d by the calling application.
//
//

LPSTR FormatLastError(DWORD dwLastError,
LPSTR szOutputBuffer, DWORD dwSizeofOutputBuffer)
{
DWORD dwRetFM;
DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;

// Should we allocate a buffer?
if (szOutputBuffer == NULL)
{
// Actually, we make FormatMessage allocate the buffer, if needed.
dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER;

// minimum size FormatMessage should allocate.
dwSizeofOutputBuffer = 1;
}

// Make FormatMessage pretty print the system error.
dwRetFM = FormatMessage(
dwFlags, NULL, dwLastError,
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
(LPTSTR) &szOutputBuffer, dwSizeofOutputBuffer,
NULL);

// FormatMessage failed to print the error.
if (dwRetFM == 0)
{
DWORD dwGetLastError;
LPSTR szFormatMessageError;

dwGetLastError = GetLastError();

// If we asked FormatMessage to allocate a buffer, then it
// might have allocated one. Lets be safe and LocalFree it.
if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
{
__try
{
LocalFree(szOutputBuffer);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
// Actually, we do nothing for this fault. If
// there was a fault, it meant the buffer wasn't
// allocated, and the LocalFree was unnecessary.
;
}

szOutputBuffer = LocalAlloc(LPTR, MAXOUTPUTSTRINGLENGTH);
dwSizeofOutputBuffer = MAXOUTPUTSTRINGLENGTH;

if (szOutputBuffer == NULL)
{
OutputDebugString("Out of memory trying to FormatLastError\r\n");
return NULL;
}
}

szFormatMessageError =
FormatLastError(dwGetLastError, NULL, 0);

if (szFormatMessageError == NULL)
return NULL;

wsprintf(szOutputBuffer,
"FormatMessage failed on error 0x%lx for the following reason: %s",
dwLastError, szFormatMessageError);

LocalFree(szFormatMessageError);
}

return szOutputBuffer;
}


//
// FUNCTION: OutputDebugLineCallback(...)
//
// PURPOSE: Pretty print a message passed into a lineCallbackFunc.
//
// PARAMETERS:
// Standard lineCallbackFunc parameters.
//
// RETURN VALUE:
// none.
//
// COMMENTS:
//
// This function takes all of the parameters passed into a
// lineCallbackFunc callback function, and pretty prints the
// meaning of the message. It then prints the result to
// the debugging output.
//
//

void OutputDebugLineCallback(
DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{
char szOutputBuff[MAXOUTPUTSTRINGLENGTH];

FormatLineCallback(szOutputBuff,
dwDevice, dwMsg, dwCallbackInstance,
dwParam1, dwParam2, dwParam3);

strcat(szOutputBuff,"\r\n");

OutputDebugString(szOutputBuff);
}


//
// FUNCTION: FormatLineCallback(...)
//
// PURPOSE: Pretty prints into a buffer a lineCallbackFunc message.
//
// PARAMETERS:
// Standard lineCallbackFunc parameters.
//
// RETURN VALUE:
// The pointer to the buffer that has the resulting string.
//
// COMMENTS:
//
// This function takes all of the parameters passed into a
// lineCallbackFunc callback function, and pretty prints the
// meaning of the message. It then returns the pointer to
// the buffer containing this string.
//
// If szOutputBuffer == NULL, then a buffer is LocalAlloc()d
// and returned. However, it is *very* important that this buffer
// is LocalFree()d.
//

LPSTR FormatLineCallback(LPSTR szOutputBuffer,
DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{
long lBufferIndex = 0;

// Allocate the buffer if necessary.
if (szOutputBuffer == NULL)
{
szOutputBuffer = (LPSTR) LocalAlloc(LPTR, MAXOUTPUTSTRINGLENGTH);

if (szOutputBuffer == NULL)
return NULL;
}

// Is this a known message?
if (dwMsg >= sizeofArray(psz_dwMsg))
{
wsprintf(szOutputBuffer, "lineCallback: Unknown dwMsg: '0x%lx', "
"dwDevice: '0x%lx', dwCallbackInstance: '0x%lx', "
"dwParam1: '0x%lx', dwParam2: '0x%lx', dwParam3: '0x%lx'", dwMsg,
dwDevice, dwCallbackInstance, dwParam1, dwParam2, dwParam3);
return szOutputBuffer;
}

// Lets start pretty printing.
lBufferIndex +=
wsprintf(szOutputBuffer, "lineCallback: %s; dwDevice: '0x%lx'; ",
psz_dwMsg[dwMsg], dwDevice);

// Which message was it? And start decoding it!
// How the message is decoded depends entirely on the message.
// READ THE HELP FILES if you more information on this.
switch(dwMsg)
{
case LINE_ADDRESSSTATE:
{
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"Address ID: 0x%lx, Address State: ", dwParam1);

lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam2,
pszfLINEADDRESSSTATE, sizeofArray(pszfLINEADDRESSSTATE));

break;
}

case LINE_CALLINFO:
{
lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam1,
pszfLINECALLINFOSTATE, sizeofArray(pszfLINECALLINFOSTATE));

break;
}

case LINE_CALLSTATE:
{

lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam3,
pszfLINECALLPRIVILEGE, sizeofArray(pszfLINECALLPRIVILEGE));
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"; ");

lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam1,
pszfLINECALLSTATE, sizeofArray(pszfLINECALLSTATE));

switch(dwParam1)
{
case LINECALLSTATE_DIALTONE:
{
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
": ");

lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam2,
pszfLINEDIALTONEMODE, sizeofArray(pszfLINEDIALTONEMODE));

break;
}

case LINECALLSTATE_BUSY:
{
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
": ");

lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam2,
pszfLINEBUSYMODE, sizeofArray(pszfLINEBUSYMODE));

break;
}

case LINECALLSTATE_SPECIALINFO:
{
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
": ");

lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam2,
pszfLINESPECIALINFO, sizeofArray(pszfLINESPECIALINFO));

break;
}

case LINECALLSTATE_DISCONNECTED:
{
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
": ");

lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam2,
pszfLINEDISCONNECTED, sizeofArray(pszfLINEDISCONNECTED));

break;
}

case LINECALLSTATE_CONFERENCED:
{
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
": Parent conference call handle: 0x%lx", dwParam2);

break;
}
}

break;

} 

case LINE_CLOSE:
break;

case LINE_DEVSPECIFIC:
break;

case LINE_DEVSPECIFICFEATURE:
break;

case LINE_GATHERDIGITS:
{
lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam1,
pszfLINEGATHERTERM, sizeofArray(pszfLINEGATHERTERM));

break;
}

case LINE_GENERATE:
{
lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam1,
pszfLINEGENERATETERM, sizeofArray(pszfLINEGENERATETERM));

break;
}

case LINE_LINEDEVSTATE:
{
lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam1,
pszfLINEDEVSTATE, sizeofArray(pszfLINEDEVSTATE));

switch(dwParam1)
{
case LINEDEVSTATE_RINGING:
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"; Ring Mode: 0x%lx, Ring Count: %lu"
,dwParam2, dwParam3);
break;

case LINEDEVSTATE_REINIT:
{
switch(dwParam2)
{
case LINE_CREATE:
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"; ReInit reason: LINE_CREATE, "
"New Line Device ID '0x%lx'"
, dwParam3);
break;

case LINE_LINEDEVSTATE:
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"; ReInit reason: LINE_LINEDEVSTATE, ");

lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam3,
pszfLINEDEVSTATE, sizeofArray(pszfLINEDEVSTATE));

break;

case 0:
break;
default:
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"; ReInit reason: %s, dwParam3: 0x%lx"
,psz_dwMsg[dwParam2], dwParam3);
break;

}

break;
}

default:
break;
}

break;
}

case LINE_MONITORDIGITS:
{
lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam2,
pszfLINEDIGITMODE, sizeofArray(pszfLINEDIGITMODE));

lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
", Received: '%c'", LOBYTE(LOWORD(dwParam1)));

break;
}

case LINE_MONITORMEDIA:
{
lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam1,
pszfLINEMEDIAMODE, sizeofArray(pszfLINEMEDIAMODE));

break;
}

case LINE_MONITORTONE:
{
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"AppSpecific tone '%lu'", dwParam1);
break;
}

case LINE_REPLY:
{
if (dwParam2 == 0)
{
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"Request ID: 0x%lx; Successful reply!", dwParam1);
}
else
{
char szTmpBuff[256];

FormatLineError((long) dwParam2, szTmpBuff, 255);

lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"Request ID: 0x%lx; UnSuccessful reply; %s",
dwParam1, szTmpBuff);
}

break;
}

case LINE_REQUEST:
{
lBufferIndex +=
strBinaryArrayAppend(&(szOutputBuffer[lBufferIndex]),
dwParam1,
pszfLINEREQUESTMODE, sizeofArray(pszfLINEREQUESTMODE));

switch(dwParam1)
{
case LINEREQUESTMODE_DROP:
{
char szHwndName[1024];

SendMessage((HWND) dwParam2, WM_GETTEXT, 1024, (long) szHwndName);

lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
": hwnd dropping = 0x%lx, \"%s\"; wRequestID = %u",
dwParam2, szHwndName, LOWORD(dwParam3));
break;
}
default:
break;
}

break;
}

case LINE_CREATE:
{
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"New Line Device ID '0x%lx'", dwParam1);
break;
}

case PHONE_CREATE:
{
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"New Phone Device ID '0x%lx'", dwParam1);
break;
}

default:
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"dwParam1: 0x%lx , dwParam2: 0x%lx , dwParam3: 0x%lx",
dwParam1, dwParam2, dwParam3);
break;

} // End switch(dwMsg)

// return that pointer!
return szOutputBuffer;

}


//
// FUNCTION: strBinaryArrayAppend(LPSTR, DWORD, LPSTR *, DWORD)
//
// PURPOSE: Takes a bitmapped DWORD, an array representing that
// binary mapping, and pretty prints it to a buffer.
//
// PARAMETERS:
// szOutputBuffer - Buffer to print to.
// dwFlags - Binary mapped flags to interpret.
// szStringArray - Array of strings.
// dwSizeofStringArray - number of elements in szStringArray.

//
// RETURN VALUE:
// The number of characters printed into szOutputBuffer.
//
// COMMENTS:
//
// This function takes dwFlags and checks each bit. If the
// bit is set, the appropriate string (taken from szStringArray)
// is printed to the szOutputBuffer string buffer. If there were
// more bits set in the string than elements in the array, and error
// is also tagged on the end.
//
// This function is intended to be used only within the TapiInfo module.
//

static long strBinaryArrayAppend(LPSTR szOutputBuffer, DWORD dwFlags,
LPSTR szStringArray[], DWORD dwSizeofStringArray)
{
DWORD dwIndex = 1, dwPower = 1;
long lBufferIndex = 0;
BOOL bFirst = TRUE;

// The zeroth element in every bitmapped array is the "unknown" or
// "unchanged" message.
if (dwFlags == 0)
{
lBufferIndex =
wsprintf(szOutputBuffer, "%s", szStringArray[0]);
return lBufferIndex;
}

// iterate through the flags and check each one.
while(dwIndex < dwSizeofStringArray)
{
// If we find one, print it.
if (dwFlags & dwPower)
// Seporate each printing with a ", " except the first one.
if (bFirst)
{
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"%s", szStringArray[dwIndex]);
bFirst = FALSE;
}
else
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
", %s", szStringArray[dwIndex]);

dwIndex ++;
dwFlags &= ~dwPower; // clear it so we know we checked it.
dwPower *= 2;
}

// If there are any flags left, they were not in the array.
if (dwFlags)
{
if (bFirst)
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
"Unknown flags '0x%lx'", dwFlags);
else
lBufferIndex +=
wsprintf(&(szOutputBuffer[lBufferIndex]),
", Unknown flags '0x%lx'", dwFlags);
}

// how many did we print into the buffer?
return lBufferIndex;
}



//
// FUNCTION: OutputDebugPrintf(LPCSTR, ...)
//
// PURPOSE: wsprintf to the debugging output.
//
// PARAMETERS:
// Exactly the same as wsprintf.
//
// RETURN VALUE:
// none.
//
// COMMENTS:
//
// This function takes exactly the same parameters as wsprintf and
// prints the results to the debugging output.
//

void __cdecl OutputDebugPrintf(LPCSTR lpszFormat, ...)
{
char szOutput[MAXOUTPUTSTRINGLENGTH];
va_list v1;
DWORD dwSize;

va_start(v1, lpszFormat);

dwSize = wvsprintf(szOutput, lpszFormat, v1);

if (szOutput[dwSize-1] != '\n')
strcat(szOutput, "\r\n");

OutputDebugString(szOutput);
}