|
|
//****************************************************************************
// *
// Microsoft NT Remote Access Service
//
// Copyright (C) 1992-93 Microsft Corporation. All rights reserved.
//
// Filename: mxsutils.c
//
// Revision History
//
// Jun 10, 1992 J. Perry Hannah Created
//
//
// Description: This file contains utility functions used by RASMXS.DLL.
//
//****************************************************************************
#include <nt.h> //These first five headers are used by media.h
#include <ntrtl.h> //The first three(?) are used by DbgUserBreakPoint
#include <nturtl.h>
#include <windows.h>
#include <wanpub.h>
#include <asyncpub.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <rasman.h>
#include <raserror.h>
#include <rasfile.h>
#include <media.h>
#include <serial.h>
#include <mprlog.h>
#include <rtutils.h>
#include <rasmxs.h>
#include <mxsint.h>
#include <mxspriv.h>
#include "mxswrap.h" // inf file wrapper
#define STRSAFE_NO_DEPRECATE
#include <strsafe.h>
//* Global Variables *******************************************************
//
extern RESPSECTION ResponseSection ; //Shared response section
extern DEVICE_CB *pDeviceList; //Points to DCB linked list
extern HANDLE *pDeviceListMutex ; //Mutex for above list
extern PortGetInfo_t PortGetInfo; //API typedef defined in media.h
extern PortSetInfo_t PortSetInfo; //API typedef defined in media.h
extern BOOL gbLogDeviceDialog; //Indicates logging is on if TRUE
extern HANDLE ghLogFile; //Handle of device log file
extern SavedSections *gpSavedSections ; // Pointer to cached sections
#define NUM_INTERNAL_MACROS 21
TCHAR *gszInternalMacroNames[] = { MXS_CARRIERBPS_KEY, MXS_CONNECTBPS_KEY, MXS_MESSAGE_KEY, MXS_PHONENUMBER_KEY,
MXS_DIAGNOSTICS_KEY, MXS_FACILITIES_KEY, MXS_USERDATA_KEY, MXS_X25PAD_KEY, MXS_X25ADDRESS_KEY,
MXS_COMPRESSION_OFF_KEY, MXS_COMPRESSION_ON_KEY, MXS_HDWFLOWCONTROL_OFF_KEY, MXS_HDWFLOWCONTROL_ON_KEY, MXS_PROTOCOL_OFF_KEY, MXS_PROTOCOL_ON_KEY, MXS_SPEAKER_OFF_KEY, MXS_SPEAKER_ON_KEY, MXS_AUTODIAL_OFF_KEY, MXS_AUTODIAL_ON_KEY, MXS_USERNAME_KEY, MXS_PASSWORD_KEY, };
//* Utility Functions ******************************************************
//
//* GetDeviceCB ------------------------------------------------------------
//
// Function: Looks for a Device Control Block in the global Device linked
// list. If no DCB is found that contains an hIOPort matching
// the input parameter, one is created, initalized, and added
// to the list.
//
// Returns: Pointer to the DEVICE_CB which contains an hIOPort matching the
// the second input parameter.
//*
DWORD GetDeviceCB(HANDLE hIOPort, char *pszDeviceType, char *pszDeviceName, DEVICE_CB **ppDev) { DWORD dRC = SUCCESS ;
*ppDev = FindDeviceInList(pDeviceList, hIOPort, pszDeviceType, pszDeviceName);
if (*ppDev == NULL) { dRC = AddDeviceToList(&pDeviceList, hIOPort, pszDeviceType, pszDeviceName, ppDev);
if (dRC != SUCCESS) goto getdcbend ;
dRC = CreateInfoTable(*ppDev); if (dRC != SUCCESS) goto getdcbend ;
dRC = CreateAttributes(*ppDev) ;
}
getdcbend:
return (dRC); }
//* FindDeviceInList -------------------------------------------------------
//
// Function: Finds the Device Control Block in the global Device linked
// list which contains the hIOPort handle, device type, and
// device name.
//
// Returns: Pointer to the DEVICE_CB which contains matches for the second,
// third, and fourth parameters, or NULL if no such DEVICE_CB
// is found.
//*
DEVICE_CB* FindDeviceInList(DEVICE_CB *pDev, HANDLE hIOPort, TCHAR *pszDeviceType, TCHAR *pszDeviceName) { while (pDev != NULL) { pDev = FindPortInList(pDev, hIOPort, NULL); if (pDev == NULL) break;
if (_stricmp(pDev->szDeviceType, pszDeviceType) == 0 && _stricmp(pDev->szDeviceName, pszDeviceName) == 0) break;
pDev = pDev->pNextDeviceCB; }
return(pDev); }
//* FindPortInList ---------------------------------------------------------
//
// Function: Finds the first Device Control Block in the global Device linked
// list which contains the hIOPort handle.
//
// If pPrevDev is not NULL on input, then on output it *pPrevDev
// points to the DCB just prior to the "found" DCB pointed to by
// the returned pointer. If the function return value is not NULL
// *pPrevDev will be valid.
//
// NOTE: If the found DCB is at the head of the list, pPrevDev
// will be the same as the return value.
//
// Returns: Pointer to the DEVICE_CB which contains an hIOPort matching the
// the second input parameter, or NULL if no such DEVICE_CB is found.
//*
DEVICE_CB* FindPortInList(DEVICE_CB *pDeviceList, HANDLE hIOPort, DEVICE_CB **pPrevDev) { DEVICE_CB *pDev;
pDev = pDeviceList; //Allow for case of only one DCB on list
while(pDeviceList != NULL && pDeviceList->hPort != hIOPort) { pDev = pDeviceList; pDeviceList = pDeviceList->pNextDeviceCB; }
if (pPrevDev != NULL) *pPrevDev = pDev;
return(pDeviceList); }
//* AddDeviceToList --------------------------------------------------------
//
// Function: Creates a Device Control Block and adds it to the head of the
// global Device linked list.
//
// Returns: SUCCESS
// ERROR_UNKNOWN_DEVICE_TYPE
// ERROR_ALLOCATING_MEMORY
//*
DWORD AddDeviceToList(DEVICE_CB **ppDeviceList, HANDLE hIOPort, LPTSTR lpszDeviceType, LPTSTR lpszDeviceName, DEVICE_CB **ppDevice) { DEVICE_CB *pDev; DEVICETYPE eDeviceType; DWORD dRC; TCHAR szFileName[MAX_PATH]; DEVICE_CB *origpDeviceList ;
origpDeviceList = *ppDeviceList ; // save this pointer since it may be restored later
// Check input
eDeviceType = DeviceTypeStrToEnum(lpszDeviceType); if (eDeviceType == DT_UNKNOWN) return(ERROR_UNKNOWN_DEVICE_TYPE);
// INF file sections are opened before the DCB is created because the
// open/close count must be kept balanced even when errors occur. (If
// OpenResponseSection fails it does not increment the count, and because
// the DCB does not exist DeviceWork does not decrement the count.)
// Open global response section for modems
*szFileName = TEXT('\0'); GetInfFileName(lpszDeviceType, szFileName, sizeof(szFileName)); if (eDeviceType == DT_MODEM) { dRC = OpenResponseSection (szFileName) ; if (dRC != SUCCESS) return dRC ; }
// Put new DCB at head of list
pDev = *ppDeviceList;
GetMem(sizeof(DEVICE_CB), (BYTE **)ppDeviceList); if (*ppDeviceList == NULL) return(ERROR_ALLOCATING_MEMORY);
*ppDevice = *ppDeviceList; (*ppDevice)->pNextDeviceCB = pDev;
// Initialize New Device Control Block
pDev = *ppDevice; pDev->hPort = hIOPort; // strcpy(pDev->szDeviceName, lpszDeviceName);
// strcpy(pDev->szDeviceType, lpszDeviceType);
(VOID) StringCchCopyA(pDev->szDeviceName, MAX_DEVICE_NAME + 1, lpszDeviceName);
(VOID) StringCchCopyA(pDev->szDeviceType, MAX_DEVICETYPE_NAME, lpszDeviceType); pDev->eDeviceType = eDeviceType; pDev->pInfoTable = NULL; pDev->pMacros = NULL; pDev->hInfFile = INVALID_HRASFILE; pDev->fPartialResponse = FALSE; pDev->bErrorControlOn = FALSE;
pDev->Overlapped.RO_Overlapped.Internal = 0; pDev->Overlapped.RO_Overlapped.InternalHigh = 0; pDev->Overlapped.RO_Overlapped.Offset = 0; pDev->Overlapped.RO_Overlapped.OffsetHigh = 0; pDev->Overlapped.RO_Overlapped.hEvent = NULL; pDev->Overlapped.RO_EventType = OVEVT_DEV_ASYNCOP;
pDev->dwRetries = 1;
// Initialize State variables
pDev->eDevNextAction = SEND; // DeviceStateMachine() State
pDev->eCmdType = CT_GENERIC; // Used by DeviceStateMachine()
pDev->eNextCmdType = CT_GENERIC; // Used by DeviceStateMachine()
pDev->fEndOfSection = FALSE; // Used by DeviceStateMachine()
pDev->eRcvState = GETECHO; // ReceiveStateMachine() State
// Open device section of INF file
if (FindOpenDevSection (szFileName, lpszDeviceName, &(pDev->hInfFile)) == TRUE) return SUCCESS ;
dRC = RasDevOpen(szFileName, lpszDeviceName, &(pDev->hInfFile)); if (dRC != SUCCESS) {
// restore the pointers
//
*ppDeviceList = origpDeviceList ; *ppDevice = NULL ; free (pDev) ; return(dRC);
} else AddOpenDevSection (szFileName, lpszDeviceName, pDev->hInfFile) ; // Add to the opened list
return(SUCCESS); }
//* CreateInfoTable --------------------------------------------------------
//
// Function: Creates an InfoTable and initalizes it by reading the variables
// and macros found in the INF file into it. The InfoTable is
// attached to the Device Control Block pointed to by the input
// parameter.
//
// This function allocates memory.
//
// Returns: SUCCESS
// ERROR_ALLOCATING_MEMORY
// Return value from RasDevOpen() or RasDevGetParams().
//*
DWORD CreateInfoTable(DEVICE_CB *pDevice) { DWORD dRC, dSize = 0; RASMAN_DEVICEINFO *pInfoTable = NULL ;
// Read variables and macros into InfoTable from INF file
dRC = RasDevGetParams(pDevice->hInfFile, (BYTE *)(pInfoTable), &dSize); if (dRC == ERROR_BUFFER_TOO_SMALL) { dSize += sizeof(RAS_PARAMS) * NUM_INTERNAL_MACROS;
GetMem(dSize, (BYTE **)&pInfoTable); if (pInfoTable == NULL) return(ERROR_ALLOCATING_MEMORY);
dRC = RasDevGetParams(pDevice->hInfFile, (BYTE *)(pInfoTable), &dSize); } if (dRC != SUCCESS) { free(pInfoTable); return(dRC); }
if ((dRC = AddInternalMacros(pDevice, pInfoTable)) != SUCCESS) { free(pInfoTable); return(dRC); }
// Attach InfoTable to Device Control Block
pDevice->pInfoTable = pInfoTable;
return(SUCCESS); }
//* AddInternalMacros ------------------------------------------------------
//
// Function: Adds internal macros to existing InfoTable.
//
// Asumptions: The InfoTable buffer was created with room enough for
// the internal macros.
//
// Returns: SUCCESS
// ERROR_ALLOCATING_MEMORY
//*
DWORD AddInternalMacros(DEVICE_CB *pDev, RASMAN_DEVICEINFO *pDI) { WORD i; RAS_PARAMS *pParam;
// Get pointer to next unused param
pParam = &(pDI->DI_Params[pDI->DI_NumOfParams]);
// Fill in params for internal macros
for (i=0; i<NUM_INTERNAL_MACROS; i++) { strcpy(pParam->P_Key, gszInternalMacroNames[i]);
pParam->P_Type = String; pParam->P_Attributes = 0; pParam->P_Value.String.Length = 0;
GetMem(1, &(pParam->P_Value.String.Data)); if (pParam->P_Value.String.Data == NULL) return(ERROR_ALLOCATING_MEMORY);
*(pParam->P_Value.String.Data) = '\0';
pParam++; }
pDI->DI_NumOfParams += NUM_INTERNAL_MACROS;
return(SUCCESS); }
//* CreateAttributes -------------------------------------------------------
//
// Function: This function is used to set attributes for the first time
// when the InfoTable is created. UpdateInfoTable() is used
// to later change attributes.
//
// First, all attributes are set according to the parameter
// key with all binary macros being enabled. Then the
// DEFAULTOFF variable is parsed, and macros listed there are
// disabled.
//
// Assumptions: - The ATTRIB_VARIABLE attribute bit has already been set
// by RasDevGetParams().
// - Parameters in InfoTable are sorted by P_Key.
// - Both parts of binary macros are present.
// These assumptions imply that if somename_off is in InfoTable
// somename_on is also present and is adjacent to somename_off.
//
// Returns: SUCCESS
// ERROR_DEFAULTOFF_MACRO_NOT_FOUND
// ERROR_ALLOCATING_MEMORY
// Return codes from PortGetInfo
//*
DWORD CreateAttributes(DEVICE_CB *pDevice) { int iDefaultOff = -1; DWORD i, dwMemSize; DWORD dwRC; BOOL fFound; TCHAR *lpszDefaultOff, *lpszEOS, szCoreName[MAX_PARAM_KEY_SIZE]; RAS_PARAMS *pParam;
RASMAN_DEVICEINFO *pInfo = pDevice->pInfoTable; RASMAN_PORTINFO *pPortInfo;
//DebugPrintf(("mxsutils CreateAttributes\n"));
// Set attributes according to Keyword type,
// defaulting user settable parameters to enabled.
for (i=0; i < pInfo->DI_NumOfParams; i++) { pParam = &(pInfo->DI_Params[i]); //DebugPrintf(("%32s %s\n", pParam, pParam->P_Value.String.Data));
if (IsVariable(*pParam)) pParam->P_Attributes = ATTRIB_VARIABLE;
else if (IsBinaryMacro(pParam->P_Key)) pParam->P_Attributes = ATTRIB_BINARYMACRO | ATTRIB_USERSETTABLE | ATTRIB_ENABLED; else pParam->P_Attributes = ATTRIB_ENABLED;
// Remember location of DEFAULTOFF variable
if (_stricmp(pInfo->DI_Params[i].P_Key, MXS_DEFAULTOFF_KEY) == 0) iDefaultOff = i; }
// Call PortGetInfo (first loading the rasser.dll if necessary)
if (PortGetInfo == NULL) { if ((dwRC = LoadRasserDll(&PortGetInfo, &PortSetInfo)) != SUCCESS) return(dwRC); }
dwMemSize = 256;
GetMem(dwMemSize, (BYTE **)&pPortInfo); if (pPortInfo == NULL) return(ERROR_ALLOCATING_MEMORY);
dwRC = PortGetInfo(pDevice->hPort, NULL, (BYTE *)pPortInfo, &dwMemSize); if (dwRC == ERROR_BUFFER_TOO_SMALL) { free(pPortInfo);
GetMem(dwMemSize, (BYTE **)&pPortInfo); if (pPortInfo == NULL) return(ERROR_ALLOCATING_MEMORY);
PortGetInfo(pDevice->hPort, NULL, (BYTE *)pPortInfo, &dwMemSize); } else if ( dwRC ) { dwRC = SUCCESS; return dwRC; }
dwRC = SUCCESS ;
// Save the Port name in the DCB
GetPcbString(pPortInfo, SER_PORTNAME_KEY, pDevice->szPortName);
// Get the current Bps of the port.
// If the modem does not report the connect bps we will use this one.
GetPcbString(pPortInfo, SER_CONNECTBPS_KEY, pDevice->szPortBps);
// Does serial.ini file contain a DEFAULTOFF variable?
if (!GetPortDefaultOff(pPortInfo, &lpszDefaultOff)) { // No DEFAULTOFF in port INI file
// Check that DEFAULTOFF variable was found in device Info table
if (iDefaultOff == -1) //DEFAULTOFF not found
{ //Assume none are to be disabled
free(pPortInfo); return(SUCCESS); }
lpszDefaultOff = pInfo->DI_Params[iDefaultOff].P_Value.String.Data; }
// Prepare DEFALULTOFF string
InitParameterStr(lpszDefaultOff, &lpszEOS);
// Disable parameters listed in DEFAULTOFF variable.
while (lpszDefaultOff < lpszEOS) { fFound = FALSE;
for (i=0; i < pInfo->DI_NumOfParams; i++) { if (IsBinaryMacro(pInfo->DI_Params[i].P_Key)) { GetCoreMacroName(pInfo->DI_Params[i].P_Key, szCoreName); if (_stricmp(lpszDefaultOff, szCoreName) == 0) { pInfo->DI_Params[i].P_Attributes &= ~ATTRIB_ENABLED; //One suffix
pInfo->DI_Params[i+1].P_Attributes &= ~ATTRIB_ENABLED; //Other suffix
fFound = TRUE; break; } } }
if (!fFound) { free(pPortInfo); return(ERROR_DEFAULTOFF_MACRO_NOT_FOUND); }
GetNextParameter(&lpszDefaultOff, lpszEOS); }
free(pPortInfo); return(SUCCESS); }
//* GetPortDefaultOff ------------------------------------------------------
//
// Function: This function finds the DEFAULTOFF key in the RASMAN_PORTINFO
// table supplied by the first parameter. A pointer to the value
// string is the output in the second parameter.
//
// If the DEFAULTOFF key is not found, the function returns FALSE,
// and the second parameter is undefined.
//
// Arguments:
// pPortInfo IN Pointer to Port Info Table from PortGetInfo()
// lpszValue OUT DEFAULTOFF value string
//
// Returns: TRUE if DEFAULTOFF key is found, otherwise FALSE
//
//*
BOOL GetPortDefaultOff(RASMAN_PORTINFO *pPortInfo, TCHAR **lpszValue) { WORD i;
for (i=0; i<pPortInfo->PI_NumOfParams; i++)
if (_stricmp(SER_DEFAULTOFFSTR_KEY, pPortInfo->PI_Params[i].P_Key) == 0) break;
if (i >= pPortInfo->PI_NumOfParams) return(FALSE);
*lpszValue = pPortInfo->PI_Params[i].P_Value.String.Data; return(TRUE); }
//* GetPcbString -----------------------------------------------------------
//
// Funciton: Searches a RASMAN_PORTINFO struct for a P_Key that matches
// the second parameter, pszPcbKey. The P_Value.String.Data
// is copied to the third parameter, pszDest which is the
// output parameter.
//
// Note: This function may only be called for keys that have
// P_Type String values (and never for P_Type Number values).
//
// Assumptions: The first parameter has been initalize by a call to
// PortGetInfo.
//
// Returns: nothing.
//
//*
void GetPcbString(RASMAN_PORTINFO *pPortInfo, char *pszPcbKey, char *pszDest) { WORD i;
for (i=0; i<pPortInfo->PI_NumOfParams; i++)
if (_stricmp(pszPcbKey, pPortInfo->PI_Params[i].P_Key) == 0) break;
if (i >= pPortInfo->PI_NumOfParams || pPortInfo->PI_Params[i].P_Type != String) return;
strncpy(pszDest, pPortInfo->PI_Params[i].P_Value.String.Data, pPortInfo->PI_Params[i].P_Value.String.Length);
*(pszDest + pPortInfo->PI_Params[i].P_Value.String.Length) = '\0'; }
//* UpdateInfoTable --------------------------------------------------------
//
// Function: This function is used to update attributes when DeviceSetInfo()
// is called. The nested for loops search the Info Table for each
// input param to be set.
//
// If the full macro name (including _off or _on for binary macros)
// is given in the input P_Key, the P_Value is copied and the
// Enable bit in P_Attributes is copied. If the core name
// is given for binary macros, only the Enable bit is copied.
//
// Assumptions: - Parameters in InfoTable are sorted by P_Key.
// - Both parts of binary macros are present in the InfoTable.
// These assumptions imply that if somename_off is in InfoTable
// somename_on is also present and is adjacent to somename_off.
//
// Pseudo Code: for (each input param to be set)
// for (each item in InfoTable)
// if (P_Keys match && !IsVariable)
// copy P_Value
// copy Enable Attribute bit
// else if (P_Keys binary macro core names match)
// copy Enable Attribute bit
//
// Returns: SUCCESS
// ERROR_WRONG_KEY_SPECIFIED
//
//*
DWORD UpdateInfoTable(DEVICE_CB *pDevice, RASMAN_DEVICEINFO *pNewInfo) { WORD i, j; BOOL fFound; DWORD dwRC, dwSrcLen, dwNumber; TCHAR *pszSrc, szNumberStr[MAX_LEN_STR_FROM_NUMBER + 1];
RASMAN_DEVICEINFO *pInfoTable = pDevice->pInfoTable;
for (i=0; i < pNewInfo->DI_NumOfParams; i++) //For each param to set,
{ fFound = FALSE;
for (j=0; j < pInfoTable->DI_NumOfParams; j++) //check InfoTable entries
{ // Check for unary macro match
if (IsUnaryMacro(pInfoTable->DI_Params[j]) &&
_stricmp(pNewInfo->DI_Params[i].P_Key, pInfoTable->DI_Params[j].P_Key) == 0) {
// Check format of P_Value field; convert to string if necessary
if (pNewInfo->DI_Params[i].P_Type == String) { pszSrc = pNewInfo->DI_Params[i].P_Value.String.Data; dwSrcLen = pNewInfo->DI_Params[i].P_Value.String.Length; } else { //P_Type == Number
_itoa(pNewInfo->DI_Params[i].P_Value.Number, szNumberStr, 10); pszSrc = szNumberStr; dwSrcLen = strlen(szNumberStr); }
// Copy P_Value (allocating more memory as necessary)
dwRC = UpdateParamString(&(pInfoTable->DI_Params[j]), pszSrc, dwSrcLen); if (dwRC != SUCCESS) return(dwRC);
fFound = TRUE; break; }
// Check for Binary Macro match
else if(CoreMacroNameMatch(pNewInfo->DI_Params[i].P_Key, pInfoTable->DI_Params[j].P_Key)) {
// Convert string to number, if necessary
if (pNewInfo->DI_Params[i].P_Type == String) { strncpy(szNumberStr, pNewInfo->DI_Params[i].P_Value.String.Data, pNewInfo->DI_Params[i].P_Value.String.Length);
szNumberStr[pNewInfo->DI_Params[i].P_Value.String.Length] = '\0';
dwNumber = atoi(szNumberStr); } else dwNumber = pNewInfo->DI_Params[i].P_Value.Number;
// Set macro enabled bit in Attributes field
if (dwNumber == 0) { pInfoTable->DI_Params[j].P_Attributes &= ~ATTRIB_ENABLED; //Clear bit
pInfoTable->DI_Params[j+1].P_Attributes &= ~ATTRIB_ENABLED; } else { pInfoTable->DI_Params[j].P_Attributes |= ATTRIB_ENABLED; //Set bit
pInfoTable->DI_Params[j+1].P_Attributes |= ATTRIB_ENABLED; }
fFound = TRUE; break; } }
if (!fFound) return(ERROR_WRONG_KEY_SPECIFIED); }
return(SUCCESS); }
//* DeviceAttachedToPort ---------------------------------------------------
//
// Funciton: Searched through a port info block for DeviceType and
// DeviceName and compares the found strings to the second
// and third input parameters.
//
// Assumptions: The first parameter has been initalize by a call to
// PortGetInfo.
// SER_DEVCIETYPE_KEY and SER_DEVICENAME_KEY each appear
// only once in the RASMAN_PORTINFO block.
//
// Returns: TRUE if pszDeviceType and pszDeviceName are found in
// pPortInfo, else FALSE.
//*
BOOL DeviceAttachedToPort(RASMAN_PORTINFO *pPortInfo, char *pszDeviceType, char *pszDeviceName) { WORD i; BOOL bTypeMatch = FALSE, bNameMatch = FALSE;
for (i=0; i<pPortInfo->PI_NumOfParams; i++) { if (_stricmp(SER_DEVICETYPE_KEY, pPortInfo->PI_Params[i].P_Key) == 0) { if ( strlen(pszDeviceType) == pPortInfo->PI_Params[i].P_Value.String.Length && _strnicmp(pszDeviceType, pPortInfo->PI_Params[i].P_Value.String.Data, pPortInfo->PI_Params[i].P_Value.String.Length) == 0)
bTypeMatch = TRUE; else break; }
if (_stricmp(SER_DEVICENAME_KEY, pPortInfo->PI_Params[i].P_Key) == 0) { if ( strlen(pszDeviceName) == pPortInfo->PI_Params[i].P_Value.String.Length && _strnicmp(pszDeviceName, pPortInfo->PI_Params[i].P_Value.String.Data, pPortInfo->PI_Params[i].P_Value.String.Length) == 0)
bNameMatch = TRUE; else break; }
if (bTypeMatch && bNameMatch) break; }
return(bTypeMatch && bNameMatch); }
//* CreateDefaultOffString -------------------------------------------------
//
// Funciton:
// DeviceName and compares the found strings to the second
// and third input parameters.
//
// Assumptions: Buffer pointed to by pszDefaultOff is large enough for
// all DefaultOff macros.
//
// Returns: Nothing
//*
void CreateDefaultOffString(DEVICE_CB *pDev, char *pszDefaultOff) { WORD i, k, wLen;
char *szDefaultOffMacros[] = { MXS_SPEAKER_KEY , MXS_HDWFLOWCONTROL_KEY , MXS_PROTOCOL_KEY , MXS_COMPRESSION_KEY };
*pszDefaultOff = '\0';
// Find each macro and if it is off then add its name to DefaultOff string
for (i=0; i < sizeof(szDefaultOffMacros)/sizeof(char *); i++) { k = (WORD) FindTableEntry(pDev->pInfoTable, szDefaultOffMacros[i]);
if (k == INVALID_INDEX) continue;
// If macro is turned off then copy its key to the DefaultOff string
if ( ! (pDev->pInfoTable->DI_Params[k].P_Attributes & ATTRIB_ENABLED)) { strcat(pszDefaultOff, szDefaultOffMacros[i]); strcat(pszDefaultOff, " "); //Add deliminting space
} }
// Remove last delimiting space
wLen = (WORD)strlen(pszDefaultOff); if (wLen > 0) pszDefaultOff[wLen - 1] = '\0'; }
//* BuildOutputTable -------------------------------------------------------
//
// Function: Copies macros from internal InfoTable to the caller's buffer.
// For Parameter: Function copies:
// Variable Entire RASMAN_DEVICEINFO struct
// Unary Macro Entire RASMAN_DEVICEINFO struct
// Binary Macro Everything except P_Value field
//
// Assumptions: - Output buffer will ALWAYS be large enough.
// - Parameters in InfoTable are sorted by P_Key (except
// internal macros).
// - Both parts of binary macros are present.
// These assumptions imply that if somename_off is in InfoTable
// somename_on is also present and is adjacent to somename_off.
//
// Returns: SUCCESS
// ERROR_BUFFER_TOO_SMALL
//
//*
DWORD BuildOutputTable(DEVICE_CB *pDevice, BYTE *pInfo, DWORD *pdwSize) { WORD i, j; DWORD cOutputParams = 0; LPTCH pValue; TCHAR szCoreName[MAX_PARAM_KEY_SIZE];
RASMAN_DEVICEINFO *pInfoTable = pDevice->pInfoTable, *pOutputTable = (RASMAN_DEVICEINFO *)pInfo;
// Compute location of first value string (follows RAS_PARAMS)
cOutputParams = pInfoTable->DI_NumOfParams - MacroCount(pInfoTable, BINARY_MACROS); *pdwSize = sizeof(RASMAN_DEVICEINFO) + sizeof(RAS_PARAMS) * (cOutputParams - 1); pValue = pInfo + *pdwSize;
// Set NumOfParams
pOutputTable->DI_NumOfParams = cOutputParams;
// Copy macros
for (i=0, j=0; i < pInfoTable->DI_NumOfParams; i++) { if (IsBinaryMacro(pInfoTable->DI_Params[i].P_Key)) { // copy core macro name, Type, and Attributes, but not Value
GetCoreMacroName(pInfoTable->DI_Params[i].P_Key, szCoreName); strcpy(pOutputTable->DI_Params[j].P_Key, szCoreName);
pOutputTable->DI_Params[j].P_Type = pInfoTable->DI_Params[i].P_Type; pOutputTable->DI_Params[j].P_Attributes = pInfoTable->DI_Params[i].P_Attributes;
pOutputTable->DI_Params[j].P_Value.String.Data = pValue; *pValue++ = '\0';
pOutputTable->DI_Params[j].P_Value.String.Length = 0;
i++; j++; } else // Is Unary Macro or Variable
{ // copy everything including Value
pOutputTable->DI_Params[j] = pInfoTable->DI_Params[i];
pOutputTable->DI_Params[j].P_Value.String.Data = pValue; strncpy(pValue, pInfoTable->DI_Params[i].P_Value.String.Data, pInfoTable->DI_Params[i].P_Value.String.Length);
pOutputTable->DI_Params[j].P_Value.String.Length = pInfoTable->DI_Params[i].P_Value.String.Length;
pValue += pInfoTable->DI_Params[i].P_Value.String.Length; *pValue++ = '\0'; j++; } }
*pdwSize = (DWORD) (pValue - pInfo); return(SUCCESS); }
//* ConnectListen() --------------------------------------------------------
//
// Function: Worker routine for DeviceConnect and DeviceListen.
//
// Returns: ERROR_NO_COMMAND_FOUND
// ERROR_STATE_MACHINES_ALREADY_STARTED
// and codes returned by DeviceStateMachine()
//*
DWORD ConnectListen(HANDLE hIOPort, char *pszDeviceType, char *pszDeviceName, CMDTYPE eCmd) { DWORD dRC; DEVICE_CB *pDevice;
// Get Device Control Block for this hIOPort
dRC = GetDeviceCB(hIOPort, pszDeviceType, pszDeviceName, &pDevice); if (dRC != SUCCESS) return(dRC);
// Check that DeviceStateMachine is not started (but is reset)
if (pDevice->eDevNextAction != SEND) return(ERROR_STATE_MACHINES_ALREADY_STARTED);
// Create Macro Translation Table for use by RasDevAPIs
if ((dRC = BuildMacroXlationTable(pDevice)) != SUCCESS) return(dRC);
// Do the next block only once for most devices,
// but retry a few times when getting modem hardware errors
do { // Initialize command types
switch(RasDevIdFirstCommand(pDevice->hInfFile)) { case CT_INIT: pDevice->eCmdType = CT_INIT; pDevice->eNextCmdType = eCmd; break;
case CT_DIAL: //If 1st cmd is DIAL or LISTEN assume
case CT_LISTEN: //the other is also present
pDevice->eCmdType = eCmd; break;
case CT_GENERIC: pDevice->eCmdType = CT_GENERIC; break;
default: return(ERROR_NO_COMMAND_FOUND); }
// Reseting state variables and purging com ports are not needed on
// first time through loop, but are need on subsequent loops.
// Reset state variables to initial values
pDevice->eDevNextAction = SEND; pDevice->eRcvState = GETECHO;
// Cancel any pending com port action and purge com buffers
PurgeComm(hIOPort, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
// Start state machine
dRC = DeviceStateMachine(pDevice, hIOPort);
} while(dRC == ERROR_PORT_OR_DEVICE && pDevice->eDeviceType == DT_MODEM && pDevice->dwRetries++ < MODEM_RETRIES );
return(dRC); }
//* DeviceTypeStrToEnum ----------------------------------------------------
//
// Function: Converts a device type string into a device type enum.
//
// Returns: The device type code.
//
//*
DEVICETYPE DeviceTypeStrToEnum(LPTSTR lpszDeviceType) { if (_strnicmp(lpszDeviceType, MXS_NULL_TXT, MAX_DEVICETYPE_NAME) == 0) return(DT_NULL);
if (_strnicmp(lpszDeviceType, MXS_MODEM_TXT, MAX_DEVICETYPE_NAME) == 0) return(DT_MODEM);
if (_strnicmp(lpszDeviceType, MXS_PAD_TXT, MAX_DEVICETYPE_NAME) == 0) return(DT_PAD);
if (_strnicmp(lpszDeviceType, MXS_SWITCH_TXT, MAX_DEVICETYPE_NAME) == 0) return(DT_SWITCH);
return(DT_UNKNOWN); }
//* GetInfFileName ---------------------------------------------------------
//
// Function: Converts a device type string into the full path name for the
// appropriate INF file.
//
// The RAS_PATH define contains leading and trailin backslashes.
// It looks like "\\RAS\\".
//
// Returns: Nothing.
//
//*
void GetInfFileName(LPTSTR pszDeviceType, LPTSTR pszFileName, DWORD dwFileNameLen) { UINT uLen;
uLen = GetSystemDirectory(pszFileName, dwFileNameLen);
strcat(pszFileName, RAS_PATH);
switch(DeviceTypeStrToEnum(pszDeviceType)) { case DT_NULL: case DT_MODEM: strcat(pszFileName, MODEM_INF_FILENAME); break;
case DT_PAD: strcat(pszFileName, PAD_INF_FILENAME); break;
case DT_SWITCH: strcat(pszFileName, SWITCH_INF_FILENAME); break;
default: strcat(pszFileName, ""); } }
//* IsVariable -------------------------------------------------------------
//
// Function: Returns TRUE if parameter's "Variable" attribute bit is set.
// Note that FALSE implies that the paramater is a macro.
//
//*
BOOL IsVariable(RAS_PARAMS Param) { return(ATTRIB_VARIABLE & Param.P_Attributes); }
//* IsUnaryMacro -----------------------------------------------------------
//
// Function: Returns TRUE if param is a unary macro, otherwise FALSE.
//
//*
BOOL IsUnaryMacro(RAS_PARAMS Param) { return(!IsVariable(Param) && !IsBinaryMacro(Param.P_Key)); }
//* IsBinaryMacro ----------------------------------------------------------
//
// Function: Returns TRUE if the string ends with off suffix or on suffix.
//
// FALSE inmplies that the string is a unary macro or a variable
// name.
//
//*
BOOL IsBinaryMacro(TCHAR *pch) { return((BOOL)BinarySuffix(pch)); }
//* BinarySuffix -----------------------------------------------------------
//
// Function: This function indicates whether the input string ends in
// _off or _on.
//
// Returns: ON_SUFFIX, OFF_SUFFIX, or FALSE if neither is the case
//
//*
WORD BinarySuffix(TCHAR *pch) { while (*pch != '\0') pch++;
pch -= strlen(MXS_ON_SUFX); if (_stricmp(pch, MXS_ON_SUFX) == 0) return(ON_SUFFIX);
while (*pch != '\0') pch++;
pch -= strlen(MXS_OFF_SUFX); if (_stricmp(pch, MXS_OFF_SUFX) == 0) return(OFF_SUFFIX);
return(FALSE); }
//* GetCoreMacroName -------------------------------------------------------
//
// Function: Copies FullName to CoreName, but omits the angle brackets, <>,
// for all macros, and omits the "_ON" or "_OFF" suffix for binary
// macros.
//
// Returns: SUCCESS
// ERROR_NOT_BINARY_MACRO
//
//*
DWORD GetCoreMacroName(LPTSTR lpszFullName, LPTSTR lpszCoreName) { LPTCH lpch;
strcpy(lpszCoreName, lpszFullName); // Copy FullName
lpch = lpszCoreName;
while (*lpch != '\0') // Check for _ON suffix
lpch++;
lpch -= strlen(MXS_ON_SUFX); if (_stricmp(lpch, MXS_ON_SUFX) == 0) { *lpch = '\0'; return(SUCCESS); }
while (*lpch != '\0') // Check for _OFF suffix
lpch++;
lpch -= strlen(MXS_OFF_SUFX); if (_stricmp(lpch, MXS_OFF_SUFX) == 0) { *lpch = '\0'; return(SUCCESS); }
return(ERROR_NOT_BINARY_MACRO); }
//* CoreMacroNameMatch -----------------------------------------------------
//
// Function: Assumes that lpszShortName is in the form of a unary macro
// name, <macro>, and that lpszFullName is a binary macro.
// If either assumption is false the function returns FALSE.
// Only if the names (without the angle brackets, <>, and without
// the _on or _off suffixes) match exactly is TRUE returned.
//
// <speaker> will match <speaker_off> or <speaker_on>, but
// will not match <speakers_off>.
//
// Returns: TRUE/FALSE
//
//*
BOOL CoreMacroNameMatch(LPTSTR lpszShortName, LPTSTR lpszFullName) { TCHAR szCoreName[MAX_PARAM_KEY_SIZE]; DWORD dRC;
dRC = GetCoreMacroName(lpszFullName, szCoreName); if (dRC != SUCCESS) return(FALSE);
return(_stricmp(lpszShortName, szCoreName) == 0); }
//* InitParameterStr -------------------------------------------------------
//
// Function: Changes all spaces in the first parameter to NULL characters.
// On return the second parameter is a pointer to the NULL
// character at the end of the input string.
//
// This function converts space separated parameters into NULL
// terminated strings. GetNextParameter() is then used to move
// a pointer from one string to the next.
//
// Caution: This function alters the input string.
//
// Returns: Nothing.
//
//*
void InitParameterStr(TCHAR *pch, TCHAR **ppchEnd) { while (*pch != '\0') { if ((*pch == ' ') || (*pch == ',')) *pch = '\0'; pch++; }
*ppchEnd = pch; }
//* GetNextParameter -------------------------------------------------------
//
// Function: If the first parameter points to a consecutive series of null
// terminated strings, this function advances the first parameter
// to the beginning of the next null terminated string.
// It will not move past the second parameter which is a pointer
// to the end of the consecutive series.
//
// Returns: Nothing.
//
//*
void GetNextParameter(TCHAR **ppch, TCHAR *pchEnd) { while (**ppch != '\0') //Move to next zero character
(*ppch)++;
while (*ppch < pchEnd && **ppch == '\0') //Move to 1st char of next substr
(*ppch)++; }
//* MacroCount -------------------------------------------------------------
//
// Function: This function returns a count of macros in the RASMAN_DEVICEINFO
// struct that the input parameter points to.
// ALL_MACROS: Unary and binary macros are counted.
// BINARY_MACRO: Only binary macros are counted.
// In either case the ON and OFF parts of a binary macro
// together count as one macro (not two).
//
// Returns: Count of macros in *pInfo.
//
//*
WORD MacroCount(RASMAN_DEVICEINFO *pInfo, WORD wType) { WORD i, cMacros;
for(i=0, cMacros=0; i < pInfo->DI_NumOfParams; i++) { if (IsVariable(pInfo->DI_Params[i])) ;
else if (IsBinaryMacro(pInfo->DI_Params[i].P_Key)) { i++; // Step thru each part of a binary macro
cMacros++; // But count only once
} else // Unary macro
if (wType == ALL_MACROS) cMacros++; }
return(cMacros); }
//* CmdTypeToStr -----------------------------------------------------------
//
// Function: This function takes an enum CMDTYPE and converts it to a
// zero terminated ASCII string which it places in the buffer
// passed in the first parameter.
//
// Returns: Pointer to first parameter
//
//*
PTCH CmdTypeToStr(PTCH pszStr, CMDTYPE eCmdType) { switch(eCmdType) { case CT_GENERIC: *pszStr = '\0'; break;
case CT_INIT: strcpy(pszStr, "_INIT"); break;
case CT_DIAL: strcpy(pszStr, "_DIAL"); break;
case CT_LISTEN: strcpy(pszStr, "_LISTEN"); break; }
return(pszStr); }
//* IsLoggingOn -----------------------------------------------------------
//
// Funciton: Reads the registry to determine if the device dialog is to
// be logged in a file.
//
// Returns: TRUE if logging is to take place; otherwise FALSE
//
//*
BOOL IsLoggingOn(void) { HKEY hKey; LONG lRC; DWORD dwType, dwValue, dwValueSize = sizeof(dwValue);
lRC = RegOpenKey(HKEY_LOCAL_MACHINE, RASMAN_REGISTRY_PATH, &hKey); if (lRC != ERROR_SUCCESS) return(FALSE);
lRC = RegQueryValueEx(hKey, RASMAN_LOGGING_VALUE, NULL, &dwType, (LPBYTE)&dwValue, &dwValueSize);
RegCloseKey(hKey);
if (lRC != ERROR_SUCCESS) return(FALSE);
if (dwType != REG_DWORD) return(FALSE);
return(dwValue ? TRUE : FALSE); }
//* InitLog ---------------------------------------------------------------
//
// Funciton: Opens the log file in overwrite mode and writes a header
// which includes date and time.
//
// Returns: Nothing.
//
//*
void InitLog(void) { TCHAR szBuffer[MAX_CMD_BUF_LEN]; int iSize; DWORD dwBytesWritten; SYSTEMTIME st;
// Create log file path
GetSystemDirectory(szBuffer, sizeof(szBuffer)); strcat(szBuffer, RAS_PATH); strcat(szBuffer, LOG_FILENAME);
ghLogFile = CreateFile(szBuffer, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (ghLogFile == INVALID_HANDLE_VALUE) { gbLogDeviceDialog = FALSE; return; }
// Create header
GetLocalTime(&st);
iSize = sprintf(szBuffer, "Remote Access Service Device Log %02d/%02d/%d %02d:%02d:%02d\r\n", st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond);
WriteFile(ghLogFile, szBuffer, (DWORD)iSize, &dwBytesWritten, NULL);
strcpy(szBuffer, "---------------------------------------------------------------\r\n\r\n");
WriteFile(ghLogFile, szBuffer, strlen(szBuffer), &dwBytesWritten, NULL); }
//* LogString -------------------------------------------------------------
//
// Funciton: Writes label and string to the Device Log file.
//
// Assumptions: Total length of labels and port handle string will be < 80
// characters.
//
// Returns: nothing.
//
//*
void LogString(DEVICE_CB *pDev, TCHAR *pszLabel, TCHAR *psString, DWORD dwStringLen) { TCHAR sBuffer[MAX_CMD_BUF_LEN + 80]; TCHAR szPortLabel[] = "Port:"; DWORD dwBytesWritten, dwTotalLen;
// If file is getting large, start over with new file
if (GetFileSize(ghLogFile, NULL) > 100000) { CloseHandle(ghLogFile); InitLog(); }
strcpy(sBuffer, szPortLabel); dwTotalLen = strlen(szPortLabel);
strcpy(sBuffer + dwTotalLen, pDev->szPortName); dwTotalLen += strlen(pDev->szPortName);
strcpy(sBuffer + dwTotalLen, " "); dwTotalLen++;
strcpy(sBuffer + dwTotalLen, pszLabel); dwTotalLen += strlen(pszLabel);
memcpy(sBuffer + dwTotalLen, psString, dwStringLen); dwTotalLen += dwStringLen;
strcpy(sBuffer + dwTotalLen, "\r\n"); dwTotalLen += 2;
WriteFile(ghLogFile, sBuffer, dwTotalLen, &dwBytesWritten, NULL); }
//* CheckForOverruns -------------------------------------------------------
//
// Funciton: Checks for com port overrun errors.
//
// Assumptions: Com port errors have been cleared before rasmxs dll APIs
// are called, that is, serial dll API PortInit was called
// prior to using the port, or PortClose and PortOpen were
// called.
//
// Returns: TRUE if an overrun error has occured; otherwise FALSE.
//
//*
BOOL CheckForOverruns(HANDLE hIOPort) { DWORD dwErrors = 0;
ClearCommError(hIOPort, &dwErrors, NULL);
return((dwErrors & CE_OVERRUN) ? TRUE : FALSE); }
//* LoadRasserDll ---------------------------------------------------------
//
// Funciton: Loads rasser.dll and gets entry points for two APIs.
//
// Returns: SUCCESS
// ERROR_PORT_NOT_CONFIGURED
//*
DWORD LoadRasserDll(PortGetInfo_t *pPortGetInfo, PortSetInfo_t *pPortSetInfo) { HANDLE hLib;
// Load DLL
hLib = LoadLibrary(SERIAL_DLL_FILENAME); if (hLib == NULL) { LogError(ROUTERLOG_CANNOT_LOAD_SERIAL_DLL, 0, NULL, NO_ERROR); return(ERROR_PORT_NOT_CONFIGURED); }
// Get entry points
// Note: Create a new, more appropriate error code to use here,
// such as, ERROR_CORRUPT_DLL.
PortSetInfo = (PortSetInfo_t) GetProcAddress(hLib, "PortSetInfo"); if (PortSetInfo == NULL) return(ERROR_PORT_NOT_CONFIGURED);
PortGetInfo = (PortGetInfo_t) GetProcAddress(hLib, "PortGetInfo"); if (PortGetInfo == NULL) return(ERROR_PORT_NOT_CONFIGURED);
return(SUCCESS); }
//* OpenResponseSection ---------------------------------------------------
//
// Funciton: This function is only called if the device is a modem.
//
// 7-9-93 We now open the modem response section when a DCB
// is created for the first modem. Then we leave it open
// (while the RASMXS DLL is in memory).
//
// Returns: Error values from RasDevOpen.
//
//*
DWORD OpenResponseSection (PCHAR szFileName) { DWORD dRC = SUCCESS ;
// **** Exclusion Begin ****
WaitForSingleObject(ResponseSection.Mutex, INFINITE) ;
if (ResponseSection.UseCount == 0) dRC = RasDevOpen(szFileName, RESPONSES_SECTION_NAME, &ResponseSection.Handle) ;
if (dRC == SUCCESS) ResponseSection.UseCount = 1 ; //This used to be an increment.
// *** Exclusion End ***
ReleaseMutex(ResponseSection.Mutex);
return dRC ; }
//* OpenResponseSection ---------------------------------------------------
//
// Funciton: This function should never be called.
//
// 7-9-93 We now open the modem response section when a DCB
// is created for the first modem. Then we leave it open
// (while the RASMXS DLL is in memory).
//
// Returns: nothing.
//
//*
/***
VOID CloseResponseSection () {
// **** Exclusion Begin ****
WaitForSingleObject(ResponseSection.Mutex, INFINITE) ;
ResponseSection.UseCount-- ;
if (ResponseSection.UseCount == 0) RasDevClose (ResponseSection.Handle) ;
// *** Exclusion End ***
ReleaseMutex(ResponseSection.Mutex);
} ***/
//* FindOpenDevSection
//
//
// Returns: TRUE if found, FALSE if not.
//*
BOOL FindOpenDevSection (PTCH lpszFileName, PTCH lpszSectionName, HRASFILE *hFile) {
SavedSections* temp ;
for (temp = gpSavedSections; temp; temp=temp->Next) { if (!_strcmpi (temp->FileName, lpszFileName) && !_strcmpi (temp->SectionName, lpszSectionName) && !temp->InUse) { *hFile = temp->hFile ; temp->InUse = TRUE ; RasDevResetCommand (*hFile) ; return TRUE ; } }
return FALSE ; }
//*
//
//
//
//*
VOID AddOpenDevSection (PTCH lpszFileName, PTCH lpszSectionName, HRASFILE hFile) { SavedSections *temp ;
GetMem(sizeof(SavedSections), (char **)&temp) ; if (temp == NULL) return ; // section not saved - no problem
// strcpy (temp->FileName, lpszFileName) ;
// strcpy (temp->SectionName, lpszSectionName) ;
(VOID) StringCchCopyA(temp->FileName, MAX_PATH, lpszFileName);
(VOID) StringCchCopyA(temp->SectionName, MAX_DEVICE_NAME + 1, lpszSectionName); temp->InUse = TRUE ; temp->hFile = hFile ;
if (gpSavedSections) temp->Next = gpSavedSections ; else temp->Next = NULL ;
gpSavedSections = temp ; }
//*
//
//
//
//*
VOID CloseOpenDevSection (HRASFILE hFile) { SavedSections* temp ;
for (temp = gpSavedSections; temp; temp=temp->Next) { if (temp->hFile == hFile) { temp->InUse = FALSE ; return ; } } }
//* DbgPrntf --------------------------------------------------------------
//
// Funciton: DbgPrntf -- printf to the debugger console
// Takes printf style arguments.
// Expects newline characters at the end of the string.
// Written by BruceK.
//
// Returns: nothing
//
//*
#ifdef DEBUG
#include <stdarg.h>
#include <stdio.h>
void DbgPrntf(const char * format, ...) { va_list marker; char String[512];
va_start(marker, format); vsprintf(String, format, marker); OutputDebugString(String); }
#endif //DEBUG
//* DbgStr ----------------------------------------------------------------
//
// Funciton: Writes an unterminated string to the debugger.
//
// Returns: nothing
//
//*
#ifdef DEBUG
void DbgStr(char Str[], DWORD StrLen) { DWORD i; char Char[] = " ";
for (i=0; i<StrLen; i++) { Char[0] = Str[i]; OutputDebugString(Char); }
if (StrLen > 0) OutputDebugString("\n"); }
#endif //DEBUG
//* ConPrintf -------------------------------------------------------------
//
// Funciton: Writes debug information to the process's console window.
// Written by StefanS.
//
// Returns: nothing
//
//*
#ifdef DBGCON
VOID ConPrintf ( char *Format, ... )
{ va_list arglist; char OutputBuffer[1024]; DWORD length;
va_start( arglist, Format ); vsprintf( OutputBuffer, Format, arglist ); va_end( arglist );
length = strlen( OutputBuffer );
WriteFile( GetStdHandle(STD_OUTPUT_HANDLE), (LPVOID )OutputBuffer, length, &length, NULL );
}
#endif //DBGCON
|