You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2656 lines
61 KiB
2656 lines
61 KiB
/*++
|
|
|
|
Copyright (c) 1995-1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
atsp.c
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
|
|
#include "atsp.h"
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
DllMain(
|
|
HANDLE hDLL,
|
|
DWORD dwReason,
|
|
LPVOID lpReserved
|
|
)
|
|
{
|
|
if (dwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
ghInst = hDLL;
|
|
|
|
#if DBG
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwDataSize, dwDataType;
|
|
char szAtsp32DebugLevel[] = "Atsp32DebugLevel";
|
|
|
|
|
|
RegOpenKeyExA(
|
|
HKEY_LOCAL_MACHINE,
|
|
gszAtspKey,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hKey
|
|
);
|
|
|
|
dwDataSize = sizeof (DWORD);
|
|
gdwDebugLevel=0;
|
|
|
|
RegQueryValueEx(
|
|
hKey,
|
|
szAtsp32DebugLevel,
|
|
0,
|
|
&dwDataType,
|
|
(LPBYTE) &gdwDebugLevel,
|
|
&dwDataSize
|
|
);
|
|
|
|
RegCloseKey (hKey);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
CommThread(
|
|
PDRVLINE pLine
|
|
)
|
|
{
|
|
char buf[4];
|
|
DWORD dwThreadID = GetCurrentThreadId(), dwNumBytes;
|
|
HANDLE hComm = pLine->hComm, hEvent;
|
|
LPOVERLAPPED pOverlapped = &pLine->Overlapped;
|
|
|
|
|
|
DBGOUT((
|
|
3,
|
|
"CommThread (id=%d): enter, port=%s",
|
|
dwThreadID,
|
|
pLine->szComm
|
|
));
|
|
|
|
hEvent = pOverlapped->hEvent;
|
|
buf[0] = buf[1] = '.';
|
|
|
|
|
|
//
|
|
// Loop waiting for i/o to complete (either the Write done in
|
|
// TSPI_lineMakeCall or the Reads done to retrieve status info).
|
|
// Note that TSPI_lineDrop or TSPI_lineCloseCall may set the
|
|
// event to alert us that they're tearing down the call, in
|
|
// which case we just exit.
|
|
//
|
|
|
|
for (;;)
|
|
{
|
|
if (WaitForSingleObject (hEvent, ATSP_TIMEOUT) == WAIT_OBJECT_0)
|
|
{
|
|
if (pLine->bDropInProgress == TRUE)
|
|
{
|
|
DBGOUT((2, "CommThread (id=%d): drop in progress"));
|
|
goto CommThread_exit;
|
|
}
|
|
|
|
GetOverlappedResult (hComm, pOverlapped, &dwNumBytes, FALSE);
|
|
ResetEvent (hEvent);
|
|
}
|
|
else
|
|
{
|
|
DBGOUT((2, "CommThread (id=%d): wait timeout"));
|
|
SetCallState (pLine, LINECALLSTATE_IDLE, 0);
|
|
goto CommThread_exit;
|
|
}
|
|
|
|
buf[1] &= 0x7f; // nuke the parity bit
|
|
|
|
DBGOUT((
|
|
3,
|
|
"CommThread (id=%d): read '%c'",
|
|
dwThreadID,
|
|
(buf[1] == '\r' ? '.' : buf[1])
|
|
));
|
|
|
|
switch ((buf[0] << 8) + buf[1])
|
|
{
|
|
case 'CT': // "CONNECT"
|
|
case 'OK': // "OK"
|
|
|
|
SetCallState (pLine, LINECALLSTATE_CONNECTED, 0);
|
|
goto CommThread_exit;
|
|
|
|
case 'SY': // "BUSY"
|
|
case 'OR': // "ERROR"
|
|
case 'NO': // "NO ANSWER", "NO DIALTONE", "NO CARRIER"
|
|
|
|
SetCallState (pLine, LINECALLSTATE_IDLE, 0);
|
|
goto CommThread_exit;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
|
|
buf[0] = buf[1];
|
|
|
|
ZeroMemory (pOverlapped, sizeof (OVERLAPPED) - sizeof (HANDLE));
|
|
if ( 0 == ReadFile (hComm, &buf[1], 1, &dwNumBytes, pOverlapped))
|
|
{
|
|
DBGOUT((2, "CommThread (id=%d): fail to read from line"));
|
|
goto CommThread_exit;
|
|
}
|
|
}
|
|
|
|
CommThread_exit:
|
|
|
|
CloseHandle (hEvent);
|
|
DBGOUT((3, "CommThread (id=%d): exit", dwThreadID));
|
|
ExitThread (0);
|
|
}
|
|
|
|
|
|
//
|
|
// We get a slough of C4047 (different levels of indrection) warnings down
|
|
// below in the initialization of FUNC_PARAM structs as a result of the
|
|
// real func prototypes having params that are types other than DWORDs,
|
|
// so since these are known non-interesting warnings just turn them off
|
|
//
|
|
|
|
#pragma warning (disable:4047)
|
|
|
|
|
|
//
|
|
// --------------------------- TAPI_lineXxx funcs -----------------------------
|
|
//
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineClose(
|
|
HDRVLINE hdLine
|
|
)
|
|
{
|
|
LONG lResult = 0;
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszhdLine, hdLine }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineClose",
|
|
1,
|
|
params,
|
|
};
|
|
#endif
|
|
|
|
Prolog (&info);
|
|
DrvFree ((PDRVLINE) hdLine);
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineCloseCall(
|
|
HDRVCALL hdCall
|
|
)
|
|
{
|
|
PDRVLINE pLine = (PDRVLINE) hdCall;
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszhdCall, hdCall }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineCloseCall",
|
|
1,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
|
|
//
|
|
// Note that in TAPI 2.0 TSPI_lineCloseCall can get called
|
|
// without TSPI_lineDrop ever being called, so we need to
|
|
// be prepared for either case.
|
|
//
|
|
|
|
Prolog (&info);
|
|
DropActiveCall (pLine);
|
|
pLine->htCall = NULL;
|
|
return (Epilog (&info, 0));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineConditionalMediaDetection(
|
|
HDRVLINE hdLine,
|
|
DWORD dwMediaModes,
|
|
LPLINECALLPARAMS const lpCallParams
|
|
)
|
|
{
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszhdLine, hdLine },
|
|
{ "dwMediaModes", dwMediaModes },
|
|
{ gszlpCallParams, lpCallParams }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineConditionalMediaDetection",
|
|
3,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
|
|
//
|
|
// This func is really a no-op for us, since we don't look
|
|
// for incoming calls (though we do say we support them to
|
|
// make apps happy)
|
|
//
|
|
|
|
Prolog (&info);
|
|
return (Epilog (&info, 0));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineDrop(
|
|
DRV_REQUESTID dwRequestID,
|
|
HDRVCALL hdCall,
|
|
LPCSTR lpsUserUserInfo,
|
|
DWORD dwSize
|
|
)
|
|
{
|
|
PDRVLINE pLine = (PDRVLINE) hdCall;
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszdwRequestID, dwRequestID },
|
|
{ gszhdCall, hdCall },
|
|
{ "lpsUserUserInfo", lpsUserUserInfo },
|
|
{ gszdwSize, dwSize }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineDrop",
|
|
4,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
|
|
Prolog (&info);
|
|
DropActiveCall (pLine);
|
|
SetCallState (pLine, LINECALLSTATE_IDLE, 0);
|
|
(*gpfnCompletionProc)(dwRequestID, 0);
|
|
return (Epilog (&info, dwRequestID));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineGetAddressCaps(
|
|
DWORD dwDeviceID,
|
|
DWORD dwAddressID,
|
|
DWORD dwTSPIVersion,
|
|
DWORD dwExtVersion,
|
|
LPLINEADDRESSCAPS lpAddressCaps
|
|
)
|
|
{
|
|
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszdwDeviceID, dwDeviceID },
|
|
{ "dwAddressID", dwAddressID },
|
|
{ "dwTSPIVersion", dwTSPIVersion },
|
|
{ "dwExtVersion", dwExtVersion },
|
|
{ "lpAddressCaps", lpAddressCaps }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineGetAddressCaps",
|
|
5,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
LONG lResult = 0;
|
|
|
|
|
|
Prolog (&info);
|
|
|
|
if (dwAddressID != 0)
|
|
{
|
|
lResult = LINEERR_INVALADDRESSID;
|
|
}
|
|
|
|
lpAddressCaps->dwNeededSize =
|
|
lpAddressCaps->dwUsedSize = sizeof(LINEADDRESSCAPS);
|
|
|
|
lpAddressCaps->dwLineDeviceID = dwDeviceID;
|
|
lpAddressCaps->dwAddressSharing = LINEADDRESSSHARING_PRIVATE;
|
|
lpAddressCaps->dwCallInfoStates = LINECALLINFOSTATE_MEDIAMODE |
|
|
LINECALLINFOSTATE_APPSPECIFIC;
|
|
lpAddressCaps->dwCallerIDFlags =
|
|
lpAddressCaps->dwCalledIDFlags =
|
|
lpAddressCaps->dwRedirectionIDFlags =
|
|
lpAddressCaps->dwRedirectingIDFlags = LINECALLPARTYID_UNAVAIL;
|
|
lpAddressCaps->dwCallStates = LINECALLSTATE_IDLE |
|
|
LINECALLSTATE_OFFERING |
|
|
LINECALLSTATE_ACCEPTED |
|
|
LINECALLSTATE_DIALTONE |
|
|
LINECALLSTATE_DIALING |
|
|
LINECALLSTATE_CONNECTED |
|
|
LINECALLSTATE_PROCEEDING |
|
|
LINECALLSTATE_DISCONNECTED |
|
|
LINECALLSTATE_UNKNOWN;
|
|
lpAddressCaps->dwDialToneModes = LINEDIALTONEMODE_UNAVAIL;
|
|
lpAddressCaps->dwBusyModes = LINEBUSYMODE_UNAVAIL;
|
|
lpAddressCaps->dwSpecialInfo = LINESPECIALINFO_UNAVAIL;
|
|
lpAddressCaps->dwDisconnectModes = LINEDISCONNECTMODE_NORMAL |
|
|
LINEDISCONNECTMODE_BUSY |
|
|
LINEDISCONNECTMODE_NOANSWER |
|
|
LINEDISCONNECTMODE_UNAVAIL |
|
|
LINEDISCONNECTMODE_NODIALTONE;
|
|
lpAddressCaps->dwMaxNumActiveCalls = 1;
|
|
lpAddressCaps->dwAddrCapFlags = LINEADDRCAPFLAGS_DIALED;
|
|
lpAddressCaps->dwCallFeatures = LINECALLFEATURE_ACCEPT |
|
|
LINECALLFEATURE_ANSWER |
|
|
LINECALLFEATURE_DROP |
|
|
LINECALLFEATURE_SETCALLPARAMS;
|
|
lpAddressCaps->dwAddressFeatures = LINEADDRFEATURE_MAKECALL;
|
|
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineGetAddressStatus(
|
|
HDRVLINE hdLine,
|
|
DWORD dwAddressID,
|
|
LPLINEADDRESSSTATUS lpAddressStatus
|
|
)
|
|
{
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszhdLine, hdLine },
|
|
{ "dwAddressID", dwAddressID },
|
|
{ "lpAddressStatus", lpAddressStatus }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineGetAddressStatus",
|
|
3,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
LONG lResult = 0;
|
|
PDRVLINE pLine = (PDRVLINE) hdLine;
|
|
|
|
|
|
Prolog (&info);
|
|
|
|
lpAddressStatus->dwNeededSize =
|
|
lpAddressStatus->dwUsedSize = sizeof(LINEADDRESSSTATUS);
|
|
|
|
lpAddressStatus->dwNumActiveCalls = (pLine->htCall ? 1 : 0);
|
|
lpAddressStatus->dwAddressFeatures = LINEADDRFEATURE_MAKECALL;
|
|
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineGetCallAddressID(
|
|
HDRVCALL hdCall,
|
|
LPDWORD lpdwAddressID
|
|
)
|
|
{
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszhdCall, hdCall },
|
|
{ "lpdwAddressID", lpdwAddressID }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineGetCallAddressID",
|
|
2,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
|
|
//
|
|
// We only support 1 address (id=0)
|
|
//
|
|
|
|
Prolog (&info);
|
|
*lpdwAddressID = 0;
|
|
return (Epilog (&info, 0));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineGetCallInfo(
|
|
HDRVCALL hdCall,
|
|
LPLINECALLINFO lpLineInfo
|
|
)
|
|
{
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszhdCall, hdCall },
|
|
{ "lpLineInfo", lpLineInfo }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineGetCallInfo",
|
|
2,
|
|
params
|
|
};
|
|
#endif
|
|
LONG lResult = 0;
|
|
PDRVLINE pLine = (PDRVLINE) hdCall;
|
|
|
|
|
|
Prolog (&info);
|
|
|
|
lpLineInfo->dwNeededSize =
|
|
lpLineInfo->dwUsedSize = sizeof(LINECALLINFO);
|
|
|
|
lpLineInfo->dwBearerMode = LINEBEARERMODE_VOICE;
|
|
lpLineInfo->dwMediaMode = pLine->dwMediaMode;
|
|
lpLineInfo->dwCallStates = LINECALLSTATE_IDLE |
|
|
LINECALLSTATE_DIALTONE |
|
|
LINECALLSTATE_DIALING |
|
|
LINECALLSTATE_CONNECTED |
|
|
LINECALLSTATE_PROCEEDING |
|
|
LINECALLSTATE_DISCONNECTED |
|
|
LINECALLSTATE_UNKNOWN;
|
|
lpLineInfo->dwOrigin = LINECALLORIGIN_OUTBOUND;
|
|
lpLineInfo->dwReason = LINECALLREASON_DIRECT;
|
|
lpLineInfo->dwCallerIDFlags =
|
|
lpLineInfo->dwCalledIDFlags =
|
|
lpLineInfo->dwConnectedIDFlags =
|
|
lpLineInfo->dwRedirectionIDFlags =
|
|
lpLineInfo->dwRedirectingIDFlags = LINECALLPARTYID_UNAVAIL;
|
|
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineGetCallStatus(
|
|
HDRVCALL hdCall,
|
|
LPLINECALLSTATUS lpLineStatus
|
|
)
|
|
{
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszhdCall, hdCall },
|
|
{ "lpLineStatus", lpLineStatus }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineGetCallStatus",
|
|
2,
|
|
params
|
|
};
|
|
#endif
|
|
LONG lResult = 0;
|
|
PDRVLINE pLine = (PDRVLINE) hdCall;
|
|
|
|
|
|
Prolog (&info);
|
|
|
|
lpLineStatus->dwNeededSize =
|
|
lpLineStatus->dwUsedSize = sizeof(LINECALLSTATUS);
|
|
|
|
lpLineStatus->dwCallState = pLine->dwCallState;
|
|
|
|
if (pLine->dwCallState != LINECALLSTATE_IDLE)
|
|
{
|
|
lpLineStatus->dwCallFeatures = LINECALLFEATURE_DROP;
|
|
}
|
|
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineGetDevCaps(
|
|
DWORD dwDeviceID,
|
|
DWORD dwTSPIVersion,
|
|
DWORD dwExtVersion,
|
|
LPLINEDEVCAPS lpLineDevCaps
|
|
)
|
|
{
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszdwDeviceID, dwDeviceID },
|
|
{ "dwTSPIVersion", dwTSPIVersion },
|
|
{ "dwExtVersion", dwExtVersion },
|
|
{ "lpLineDevCaps", lpLineDevCaps }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineGetDevCaps",
|
|
4,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
LONG lResult = 0;
|
|
static WCHAR szProviderInfo[] = L"AT-compatible modem service provider";
|
|
|
|
#define PROVIDER_INFO_SIZE (37 * sizeof (WCHAR))
|
|
|
|
Prolog (&info);
|
|
|
|
lpLineDevCaps->dwNeededSize = sizeof (LINEDEVCAPS) + PROVIDER_INFO_SIZE +
|
|
(MAX_DEV_NAME_LENGTH + 1) * sizeof (WCHAR);
|
|
|
|
if (lpLineDevCaps->dwTotalSize >= lpLineDevCaps->dwNeededSize)
|
|
{
|
|
#define LINECONFIG_SIZE (2 * (MAX_DEV_NAME_LENGTH + 1) + 40)
|
|
|
|
char szLineConfig[LINECONFIG_SIZE], szLineN[16], *p;
|
|
HKEY hKey;
|
|
DWORD dwDataSize, dwDataType;
|
|
|
|
|
|
lpLineDevCaps->dwUsedSize = lpLineDevCaps->dwNeededSize;
|
|
|
|
lpLineDevCaps->dwProviderInfoSize = PROVIDER_INFO_SIZE;
|
|
lpLineDevCaps->dwProviderInfoOffset = sizeof(LINEDEVCAPS);
|
|
|
|
My_lstrcpyW ((WCHAR *)(lpLineDevCaps + 1), szProviderInfo);
|
|
|
|
RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
gszAtspKey,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hKey
|
|
);
|
|
|
|
dwDataSize = LINECONFIG_SIZE;
|
|
wsprintf (szLineN, "Line%d", dwDeviceID - gdwLineDeviceIDBase);
|
|
lstrcpy (szLineConfig, gszDefLineConfigParams);
|
|
|
|
RegQueryValueEx(
|
|
hKey,
|
|
szLineN,
|
|
0,
|
|
&dwDataType,
|
|
(LPBYTE) szLineConfig,
|
|
&dwDataSize
|
|
);
|
|
|
|
RegCloseKey (hKey);
|
|
|
|
for (p = szLineConfig; *p != ','; p++);
|
|
*p = 0;
|
|
|
|
lpLineDevCaps->dwLineNameSize = (lstrlen (szLineConfig) + 1) *
|
|
sizeof (WCHAR);
|
|
lpLineDevCaps->dwLineNameOffset = sizeof(LINEDEVCAPS) +
|
|
PROVIDER_INFO_SIZE;
|
|
|
|
MultiByteToWideChar(
|
|
CP_ACP,
|
|
MB_PRECOMPOSED,
|
|
szLineConfig,
|
|
-1,
|
|
(WCHAR *) ((LPBYTE) (lpLineDevCaps + 1) + PROVIDER_INFO_SIZE),
|
|
lpLineDevCaps->dwLineNameSize
|
|
);
|
|
}
|
|
else
|
|
{
|
|
lpLineDevCaps->dwUsedSize = sizeof(LINEDEVCAPS);
|
|
}
|
|
|
|
lpLineDevCaps->dwStringFormat = STRINGFORMAT_ASCII;
|
|
lpLineDevCaps->dwAddressModes = LINEADDRESSMODE_ADDRESSID;
|
|
lpLineDevCaps->dwNumAddresses = 1;
|
|
lpLineDevCaps->dwBearerModes = LINEBEARERMODE_VOICE;
|
|
lpLineDevCaps->dwMaxRate = 9600;
|
|
lpLineDevCaps->dwMediaModes = LINEMEDIAMODE_INTERACTIVEVOICE |
|
|
LINEMEDIAMODE_DATAMODEM;
|
|
lpLineDevCaps->dwDevCapFlags = LINEDEVCAPFLAGS_CLOSEDROP |
|
|
LINEDEVCAPFLAGS_DIALBILLING |
|
|
LINEDEVCAPFLAGS_DIALQUIET |
|
|
LINEDEVCAPFLAGS_DIALDIALTONE;
|
|
lpLineDevCaps->dwMaxNumActiveCalls = 1;
|
|
lpLineDevCaps->dwRingModes = 1;
|
|
lpLineDevCaps->dwLineFeatures = LINEFEATURE_MAKECALL;
|
|
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineGetID(
|
|
HDRVLINE hdLine,
|
|
DWORD dwAddressID,
|
|
HDRVCALL hdCall,
|
|
DWORD dwSelect,
|
|
LPVARSTRING lpDeviceID,
|
|
LPCWSTR lpszDeviceClass,
|
|
HANDLE hTargetProcess
|
|
)
|
|
{
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszhdLine, hdLine },
|
|
{ "dwAddressID", dwAddressID },
|
|
{ gszhdCall, hdCall },
|
|
{ "dwSelect", dwSelect },
|
|
{ "lpDeviceID", lpDeviceID },
|
|
{ "lpszDeviceClass", lpszDeviceClass },
|
|
{ "hTargetProcess", hTargetProcess }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineGetID",
|
|
7,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
DWORD dwNeededSize = sizeof(VARSTRING) + sizeof (DWORD);
|
|
LONG lResult = 0;
|
|
PDRVLINE pLine = (dwSelect == LINECALLSELECT_CALL ?
|
|
(PDRVLINE) hdCall : (PDRVLINE) hdLine);
|
|
|
|
|
|
Prolog (&info);
|
|
|
|
if (lstrcmpiW (lpszDeviceClass, L"tapi/line") == 0)
|
|
{
|
|
if (lpDeviceID->dwTotalSize < dwNeededSize)
|
|
{
|
|
lpDeviceID->dwUsedSize = 3*sizeof(DWORD);
|
|
}
|
|
else
|
|
{
|
|
lpDeviceID->dwUsedSize = dwNeededSize;
|
|
|
|
lpDeviceID->dwStringFormat = STRINGFORMAT_BINARY;
|
|
lpDeviceID->dwStringSize = sizeof(DWORD);
|
|
lpDeviceID->dwStringOffset = sizeof(VARSTRING);
|
|
|
|
*((LPDWORD)(lpDeviceID + 1)) = pLine->dwDeviceID;
|
|
}
|
|
|
|
lpDeviceID->dwNeededSize = dwNeededSize;
|
|
}
|
|
else if (lstrcmpiW (lpszDeviceClass, L"comm/datamodem") == 0)
|
|
{
|
|
dwNeededSize += (strlen (pLine->szComm) + 1) * sizeof (WCHAR);
|
|
|
|
if (lpDeviceID->dwTotalSize < dwNeededSize)
|
|
{
|
|
lpDeviceID->dwUsedSize = 3 * sizeof(DWORD);
|
|
}
|
|
else
|
|
{
|
|
HANDLE hCommDup = NULL;
|
|
|
|
|
|
if (!pLine->htCall)
|
|
{
|
|
DBGOUT((1, "TSPI_lineGetID32: error, no active call"));
|
|
|
|
lResult = LINEERR_OPERATIONFAILED;
|
|
|
|
goto TSPI_lineGetID_epilog;
|
|
}
|
|
|
|
if (!DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
pLine->hComm,
|
|
hTargetProcess,
|
|
&hCommDup,
|
|
0,
|
|
TRUE,
|
|
DUPLICATE_SAME_ACCESS
|
|
))
|
|
{
|
|
DBGOUT((
|
|
1,
|
|
"TSPI_lineGetID: DupHandle failed, err=%ld",
|
|
GetLastError()
|
|
));
|
|
|
|
lResult = LINEERR_OPERATIONFAILED;
|
|
|
|
goto TSPI_lineGetID_epilog;
|
|
}
|
|
|
|
lpDeviceID->dwUsedSize = dwNeededSize;
|
|
|
|
lpDeviceID->dwStringFormat = STRINGFORMAT_BINARY;
|
|
lpDeviceID->dwStringSize = dwNeededSize - sizeof(VARSTRING);
|
|
lpDeviceID->dwStringOffset = sizeof(VARSTRING);
|
|
|
|
*((HANDLE *)(lpDeviceID + 1)) = hCommDup;
|
|
|
|
lstrcpy(
|
|
((char *)(lpDeviceID + 1)) + sizeof (HANDLE),
|
|
pLine->szComm
|
|
);
|
|
|
|
MultiByteToWideChar(
|
|
CP_ACP,
|
|
0,
|
|
pLine->szComm,
|
|
-1,
|
|
((WCHAR *)(lpDeviceID + 1)) + sizeof (HANDLE),
|
|
256
|
|
);
|
|
}
|
|
|
|
lpDeviceID->dwNeededSize = dwNeededSize;
|
|
}
|
|
else
|
|
{
|
|
lResult = LINEERR_NODEVICE;
|
|
}
|
|
|
|
TSPI_lineGetID_epilog:
|
|
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineGetLineDevStatus(
|
|
HDRVLINE hdLine,
|
|
LPLINEDEVSTATUS lpLineDevStatus
|
|
)
|
|
{
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszhdLine, hdLine },
|
|
{ "lpLineDevStatus", lpLineDevStatus }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineGetLineDevStatus",
|
|
2,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
LONG lResult = 0;
|
|
PDRVLINE pLine = (PDRVLINE) hdLine;
|
|
|
|
|
|
Prolog (&info);
|
|
|
|
lpLineDevStatus->dwUsedSize =
|
|
lpLineDevStatus->dwNeededSize = sizeof (LINEDEVSTATUS);
|
|
|
|
lpLineDevStatus->dwNumActiveCalls = (pLine->htCall ? 1 : 0);
|
|
//lpLineDevStatus->dwLineFeatures =
|
|
lpLineDevStatus->dwDevStatusFlags = LINEDEVSTATUSFLAGS_CONNECTED |
|
|
LINEDEVSTATUSFLAGS_INSERVICE;
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineGetNumAddressIDs(
|
|
HDRVLINE hdLine,
|
|
LPDWORD lpdwNumAddressIDs
|
|
)
|
|
{
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszhdLine, hdLine },
|
|
{ "lpdwNumAddressIDs", lpdwNumAddressIDs }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineGetNumAddressIDs",
|
|
2,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
LONG lResult = 0;
|
|
PDRVLINE pLine = (PDRVLINE) hdLine;
|
|
|
|
|
|
//
|
|
// We only support 1 address (id=0)
|
|
//
|
|
|
|
Prolog (&info);
|
|
*lpdwNumAddressIDs = 1;
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineMakeCall(
|
|
DRV_REQUESTID dwRequestID,
|
|
HDRVLINE hdLine,
|
|
HTAPICALL htCall,
|
|
LPHDRVCALL lphdCall,
|
|
LPCWSTR lpszDestAddress,
|
|
DWORD dwCountryCode,
|
|
LPLINECALLPARAMS const lpCallParams
|
|
)
|
|
{
|
|
char szCommands[64], szCommand[64], szDestAddress[128];
|
|
DWORD dwThreadID, dwNumBytes, dwError;
|
|
PDRVLINE pLine = (PDRVLINE) hdLine;
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszdwRequestID, dwRequestID },
|
|
{ gszhdLine, hdLine },
|
|
{ "htCall", htCall },
|
|
{ "lphdCall", lphdCall },
|
|
{ "lpszDestAddress", szDestAddress },
|
|
{ "dwCountryCode", dwCountryCode },
|
|
{ gszlpCallParams, lpCallParams }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineMakeCall",
|
|
7,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
|
|
if (lpszDestAddress)
|
|
{
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
lpszDestAddress,
|
|
-1,
|
|
(LPSTR) szDestAddress,
|
|
128,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
Prolog (&info);
|
|
|
|
|
|
//
|
|
// Check to see if there's already another call
|
|
//
|
|
|
|
if (pLine->htCall)
|
|
{
|
|
(*gpfnCompletionProc)(dwRequestID, LINEERR_CALLUNAVAIL);
|
|
goto TSPI_lineMakeCall_return;
|
|
}
|
|
|
|
|
|
//
|
|
// Since we don't support TSPI_lineDial, fail if app tries
|
|
// to pass a NULL lpszDestAddress (implying that app just
|
|
// wants to go offhook)
|
|
//
|
|
|
|
if (lpszDestAddress == NULL)
|
|
{
|
|
(*gpfnCompletionProc)(dwRequestID, LINEERR_INVALADDRESS);
|
|
goto TSPI_lineMakeCall_return;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the line's config info
|
|
//
|
|
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwDataSize, dwDataType;
|
|
char szLineN[8], *pszConfig, *p, *p2;
|
|
|
|
|
|
wsprintf(
|
|
szLineN,
|
|
"Line%d",
|
|
((PDRVLINE) hdLine)->dwDeviceID - gdwLineDeviceIDBase
|
|
);
|
|
|
|
dwDataSize = 256;
|
|
|
|
pszConfig = DrvAlloc (dwDataSize);
|
|
|
|
RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
gszAtspKey,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hKey
|
|
);
|
|
|
|
RegQueryValueEx(
|
|
hKey,
|
|
szLineN,
|
|
0,
|
|
&dwDataType,
|
|
(LPBYTE) pszConfig,
|
|
&dwDataSize
|
|
);
|
|
|
|
pszConfig[dwDataSize] = '\0'; // *pszConfig = "MyLine,COM1,L0"
|
|
|
|
RegCloseKey (hKey);
|
|
|
|
|
|
//
|
|
// szComm
|
|
//
|
|
|
|
for (p = pszConfig; *p != ','; p++);
|
|
p++; // *p = "COM1,L0"
|
|
for (p2 = p; *p2 != ','; p2++);
|
|
*p2 = 0; // *p = "COM1"
|
|
|
|
lstrcpy (pLine->szComm, p);
|
|
|
|
|
|
//
|
|
// szCommands
|
|
//
|
|
|
|
p2++; // *p2 = "L0"
|
|
lstrcpy (szCommands, p2);
|
|
|
|
DrvFree (pszConfig);
|
|
}
|
|
|
|
|
|
//
|
|
// Open the port
|
|
//
|
|
|
|
if ((pLine->hComm = CreateFile(
|
|
pLine->szComm,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0, //FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, // no security attrs
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED,
|
|
NULL // no template file
|
|
|
|
)) == INVALID_HANDLE_VALUE)
|
|
{
|
|
DBGOUT((
|
|
3,
|
|
"TSPI_lineMakeCall: CreateFile(%s) failed, err=%ld",
|
|
pLine->szComm,
|
|
GetLastError()
|
|
));
|
|
|
|
(*gpfnCompletionProc)(dwRequestID, LINEERR_RESOURCEUNAVAIL);
|
|
goto TSPI_lineMakeCall_return;
|
|
}
|
|
|
|
|
|
//
|
|
// Setup up the modem command string. If there's an initial 'T'
|
|
// or 'P' (for Tone or Pulse) in the dest address then disregard
|
|
// it. Also if it's a voice call add the semi colon so we return
|
|
// to cmd mode.
|
|
//
|
|
|
|
{
|
|
char *p = (char *) szDestAddress;
|
|
|
|
|
|
if (*p == 'T' || *p == 'P')
|
|
{
|
|
p++;
|
|
}
|
|
|
|
if (lpCallParams &&
|
|
lpCallParams->dwMediaMode != LINEMEDIAMODE_INTERACTIVEVOICE)
|
|
{
|
|
wsprintf (szCommand, "AT%sDT%s\r", szCommands, p);
|
|
}
|
|
else
|
|
{
|
|
wsprintf (szCommand, "AT%sDT%s;\r", szCommands, p);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Init the data structure & tell tapi our handle to the call
|
|
//
|
|
|
|
pLine->htCall = htCall;
|
|
pLine->bDropInProgress = FALSE;
|
|
pLine->dwMediaMode = (lpCallParams ? lpCallParams->dwMediaMode :
|
|
LINEMEDIAMODE_INTERACTIVEVOICE);
|
|
|
|
*lphdCall = (HDRVCALL) pLine;
|
|
|
|
|
|
//
|
|
// Do an overlapped write, the comm thread will deal with the results
|
|
//
|
|
|
|
pLine->Overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
|
|
|
|
if (!WriteFile(
|
|
pLine->hComm,
|
|
szCommand,
|
|
lstrlen (szCommand),
|
|
&dwNumBytes,
|
|
&pLine->Overlapped
|
|
)
|
|
|
|
&& (dwError = GetLastError()) != ERROR_IO_PENDING)
|
|
{
|
|
DBGOUT((
|
|
1,
|
|
"TSPI_lineMakeCall: WriteFile(%s) failed, error=%d",
|
|
pLine->szComm,
|
|
dwError
|
|
));
|
|
|
|
pLine->htCall = NULL;
|
|
CloseHandle (pLine->hComm);
|
|
CloseHandle (pLine->Overlapped.hEvent);
|
|
(*gpfnCompletionProc)(dwRequestID, LINEERR_OPERATIONFAILED);
|
|
goto TSPI_lineMakeCall_return;
|
|
}
|
|
|
|
|
|
//
|
|
// Complete the requests & set the initial call state
|
|
//
|
|
|
|
(*gpfnCompletionProc)(dwRequestID, 0);
|
|
SetCallState (pLine, LINECALLSTATE_DIALING, 0);
|
|
|
|
|
|
//
|
|
// Spin the comm thread to handle the results of the Write above
|
|
//
|
|
|
|
{
|
|
HANDLE hCommThread;
|
|
|
|
|
|
if (!(hCommThread = CreateThread(
|
|
(LPSECURITY_ATTRIBUTES) NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE) CommThread,
|
|
pLine,
|
|
0,
|
|
&dwThreadID
|
|
)))
|
|
{
|
|
DBGOUT((
|
|
1,
|
|
"TSPI_lineMakeCall: CreateThread failed, err=%ld",
|
|
GetLastError()
|
|
));
|
|
|
|
GetOverlappedResult(
|
|
pLine->hComm,
|
|
&pLine->Overlapped,
|
|
&dwNumBytes,
|
|
TRUE
|
|
);
|
|
|
|
SetCallState (pLine, LINECALLSTATE_IDLE, 0);
|
|
CloseHandle (pLine->hComm);
|
|
CloseHandle (pLine->Overlapped.hEvent);
|
|
goto TSPI_lineMakeCall_return;
|
|
}
|
|
|
|
CloseHandle (hCommThread);
|
|
}
|
|
|
|
|
|
TSPI_lineMakeCall_return:
|
|
|
|
return (Epilog (&info, dwRequestID));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineNegotiateTSPIVersion(
|
|
DWORD dwDeviceID,
|
|
DWORD dwLowVersion,
|
|
DWORD dwHighVersion,
|
|
LPDWORD lpdwTSPIVersion
|
|
)
|
|
{
|
|
LONG lResult = 0;
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszdwDeviceID, dwDeviceID },
|
|
{ "dwLowVersion", dwLowVersion },
|
|
{ "dwHighVersion", dwHighVersion },
|
|
{ "lpdwTSPIVersion", lpdwTSPIVersion }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineNegotiateTSPIVersion",
|
|
4,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
Prolog (&info);
|
|
*lpdwTSPIVersion = 0x00020000;
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineOpen(
|
|
DWORD dwDeviceID,
|
|
HTAPILINE htLine,
|
|
LPHDRVLINE lphdLine,
|
|
DWORD dwTSPIVersion,
|
|
LINEEVENT lpfnEventProc
|
|
)
|
|
{
|
|
LONG lResult;
|
|
PDRVLINE pLine;
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszdwDeviceID, dwDeviceID },
|
|
{ "htLine", htLine },
|
|
{ "lphdLine", lphdLine },
|
|
{ "dwTSPIVersion", dwTSPIVersion },
|
|
{ "lpfnEventProc", lpfnEventProc }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineOpen",
|
|
5,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
|
|
Prolog (&info);
|
|
|
|
if ((pLine = DrvAlloc (sizeof (DRVLINE))))
|
|
{
|
|
pLine->htLine = htLine;
|
|
pLine->pfnEventProc = lpfnEventProc;
|
|
pLine->dwDeviceID = dwDeviceID;
|
|
|
|
*lphdLine = (HDRVLINE) pLine;
|
|
|
|
lResult = 0;
|
|
}
|
|
else
|
|
{
|
|
lResult = LINEERR_NOMEM;
|
|
}
|
|
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_lineSetDefaultMediaDetection(
|
|
HDRVLINE hdLine,
|
|
DWORD dwMediaModes
|
|
)
|
|
{
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ gszhdLine, hdLine },
|
|
{ "dwMediaModes", dwMediaModes }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_lineSetDefaultMediaDetection",
|
|
2,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
|
|
//
|
|
// This func is really a no-op for us, since we don't look
|
|
// for incoming calls (though we do say we support them to
|
|
// make apps happy)
|
|
//
|
|
|
|
Prolog (&info);
|
|
return (Epilog (&info, 0));
|
|
}
|
|
|
|
|
|
//
|
|
// ------------------------- TSPI_providerXxx funcs ---------------------------
|
|
//
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerConfig(
|
|
HWND hwndOwner,
|
|
DWORD dwPermanentProviderID
|
|
)
|
|
{
|
|
//
|
|
// Although this func is never called by TAPI v2.0, we export
|
|
// it so that the Telephony Control Panel Applet knows that it
|
|
// can configure this provider via lineConfigProvider(),
|
|
// otherwise Telephon.cpl will not consider it configurable
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerGenericDialogData(
|
|
ULONG_PTR dwObjectID,
|
|
DWORD dwObjectType,
|
|
LPVOID lpParams,
|
|
DWORD dwSize
|
|
)
|
|
{
|
|
LONG lResult = 0;
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ "dwObjectID", dwObjectID },
|
|
{ "dwObjectType", dwObjectType },
|
|
{ "lpParams", lpParams },
|
|
{ "dwSize", dwSize }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_providerGenericDialogData",
|
|
4,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
|
|
Prolog (&info);
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerInit(
|
|
DWORD dwTSPIVersion,
|
|
DWORD dwPermanentProviderID,
|
|
DWORD dwLineDeviceIDBase,
|
|
DWORD dwPhoneDeviceIDBase,
|
|
DWORD_PTR dwNumLines,
|
|
DWORD_PTR dwNumPhones,
|
|
ASYNC_COMPLETION lpfnCompletionProc,
|
|
LPDWORD lpdwTSPIOptions
|
|
)
|
|
{
|
|
LONG lResult = 0;
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ "dwTSPIVersion", dwTSPIVersion },
|
|
{ gszdwPermanentProviderID, dwPermanentProviderID },
|
|
{ "dwLineDeviceIDBase", dwLineDeviceIDBase },
|
|
{ "dwPhoneDeviceIDBase", dwPhoneDeviceIDBase },
|
|
{ "dwNumLines", dwNumLines },
|
|
{ "dwNumPhones", dwNumPhones },
|
|
{ "lpfnCompletionProc", lpfnCompletionProc }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_providerInit",
|
|
7,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
Prolog (&info);
|
|
gdwLineDeviceIDBase = dwLineDeviceIDBase;
|
|
gpfnCompletionProc = lpfnCompletionProc;
|
|
*lpdwTSPIOptions = LINETSPIOPTION_NONREENTRANT;
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerInstall(
|
|
HWND hwndOwner,
|
|
DWORD dwPermanentProviderID
|
|
)
|
|
{
|
|
//
|
|
// Although this func is never called by TAPI v2.0, we export
|
|
// it so that the Telephony Control Panel Applet knows that it
|
|
// can add this provider via lineAddProvider(), otherwise
|
|
// Telephon.cpl will not consider it installable
|
|
//
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerRemove(
|
|
HWND hwndOwner,
|
|
DWORD dwPermanentProviderID
|
|
)
|
|
{
|
|
//
|
|
// Although this func is never called by TAPI v2.0, we export
|
|
// it so that the Telephony Control Panel Applet knows that it
|
|
// can remove this provider via lineRemoveProvider(), otherwise
|
|
// Telephon.cpl will not consider it removable
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerShutdown(
|
|
DWORD dwTSPIVersion,
|
|
DWORD dwPermanentProviderID
|
|
)
|
|
{
|
|
LONG lResult = 0;
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ "dwTSPIVersion", dwTSPIVersion },
|
|
{ gszdwPermanentProviderID, dwPermanentProviderID }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_providerShutdown",
|
|
2,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
|
|
Prolog (&info);
|
|
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerEnumDevices(
|
|
DWORD dwPermanentProviderID,
|
|
LPDWORD lpdwNumLines,
|
|
LPDWORD lpdwNumPhones,
|
|
HPROVIDER hProvider,
|
|
LINEEVENT lpfnLineCreateProc,
|
|
PHONEEVENT lpfnPhoneCreateProc
|
|
)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwNumLines, dwDataType, dwDataSize;
|
|
|
|
|
|
//
|
|
// Retrieve the number of devices we're
|
|
// configured for from our registry section
|
|
//
|
|
|
|
if (ERROR_SUCCESS !=
|
|
RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
gszAtspKey,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hKey
|
|
))
|
|
{
|
|
return LINEERR_OPERATIONFAILED;
|
|
}
|
|
|
|
dwDataSize = sizeof(dwNumLines);
|
|
dwNumLines = 0;
|
|
|
|
RegQueryValueEx(
|
|
hKey,
|
|
gszNumLines,
|
|
0,
|
|
&dwDataType,
|
|
(LPBYTE) &dwNumLines,
|
|
&dwDataSize
|
|
);
|
|
|
|
RegCloseKey (hKey);
|
|
|
|
*lpdwNumLines = dwNumLines;
|
|
*lpdwNumPhones = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TSPI_providerUIIdentify(
|
|
LPWSTR lpszUIDLLName
|
|
)
|
|
{
|
|
LONG lResult = 0;
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ "lpsUIDLLName", lpszUIDLLName }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TSPI_providerUIIdentify",
|
|
1,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
|
|
Prolog (&info);
|
|
My_lstrcpyW(lpszUIDLLName, L"atsp32.tsp");
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
//
|
|
// ---------------------------- TUISPI_xxx funcs ------------------------------
|
|
//
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TUISPI_lineConfigDialog(
|
|
TUISPIDLLCALLBACK lpfnUIDLLCallback,
|
|
DWORD dwDeviceID,
|
|
HWND hwndOwner,
|
|
LPCWSTR lpszDeviceClass
|
|
)
|
|
{
|
|
char szDeviceClass[128];
|
|
LONG lResult = 0;
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ "lpfnUIDLLCallback", lpfnUIDLLCallback },
|
|
{ gszdwDeviceID, dwDeviceID },
|
|
{ gszhwndOwner, hwndOwner },
|
|
{ "lpszDeviceClass", szDeviceClass }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TUISPI_lineConfigDialog",
|
|
4,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
|
|
if (lpszDeviceClass)
|
|
{
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
lpszDeviceClass,
|
|
-1,
|
|
(LPSTR) szDeviceClass,
|
|
128,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
Prolog (&info);
|
|
|
|
DialogBoxParam(
|
|
ghInst,
|
|
MAKEINTRESOURCE(IDD_DIALOG1),
|
|
hwndOwner,
|
|
ConfigDlgProc,
|
|
0
|
|
);
|
|
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TUISPI_providerConfig(
|
|
TUISPIDLLCALLBACK lpfnUIDLLCallback,
|
|
HWND hwndOwner,
|
|
DWORD dwPermanentProviderID
|
|
)
|
|
{
|
|
LONG lResult = 0;
|
|
#if DBG
|
|
FUNC_PARAM params[] =
|
|
{
|
|
{ "lpfnUIDLLCallback", lpfnUIDLLCallback },
|
|
{ gszhwndOwner, hwndOwner },
|
|
{ gszdwPermanentProviderID, dwPermanentProviderID }
|
|
};
|
|
FUNC_INFO info =
|
|
{
|
|
"TUISPI_providerConfig",
|
|
3,
|
|
params
|
|
};
|
|
#endif
|
|
|
|
|
|
Prolog (&info);
|
|
|
|
DialogBoxParam(
|
|
ghInst,
|
|
MAKEINTRESOURCE(IDD_DIALOG1),
|
|
hwndOwner,
|
|
ConfigDlgProc,
|
|
0
|
|
);
|
|
|
|
return (Epilog (&info, lResult));
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TUISPI_providerInstall(
|
|
TUISPIDLLCALLBACK lpfnUIDLLCallback,
|
|
HWND hwndOwner,
|
|
DWORD dwPermanentProviderID
|
|
)
|
|
{
|
|
LONG lResult;
|
|
|
|
|
|
if ((lResult = ProviderInstall ("atsp32.tsp", TRUE)) == 0)
|
|
{
|
|
DialogBoxParam(
|
|
ghInst,
|
|
MAKEINTRESOURCE(IDD_DIALOG1),
|
|
hwndOwner,
|
|
ConfigDlgProc,
|
|
0
|
|
);
|
|
}
|
|
|
|
return lResult;
|
|
}
|
|
|
|
|
|
LONG
|
|
TSPIAPI
|
|
TUISPI_providerRemove(
|
|
TUISPIDLLCALLBACK lpfnUIDLLCallback,
|
|
HWND hwndOwner,
|
|
DWORD dwPermanentProviderID
|
|
)
|
|
{
|
|
HKEY hKey;
|
|
char szSoftwareMsft[] = "Software\\Microsoft", szATSP[] = "ATSP";
|
|
LONG lResult;
|
|
|
|
//
|
|
// Clean up our registry section
|
|
//
|
|
|
|
lResult = RegOpenKeyExA(
|
|
HKEY_LOCAL_MACHINE,
|
|
szSoftwareMsft,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hKey
|
|
);
|
|
|
|
if (ERROR_SUCCESS != lResult)
|
|
return 0;
|
|
|
|
RegDeleteKeyA (hKey, szATSP);
|
|
RegCloseKey (hKey);
|
|
return 0;
|
|
}
|
|
|
|
|
|
#pragma warning (default:4047)
|
|
|
|
|
|
//
|
|
// ---------------------- Misc private support routines -----------------------
|
|
//
|
|
|
|
LPWSTR
|
|
PASCAL
|
|
My_lstrcpyW(
|
|
WCHAR *pString1,
|
|
WCHAR *pString2
|
|
)
|
|
{
|
|
WCHAR *p = pString1;
|
|
|
|
|
|
for (; (*p = *pString2); p++, pString2++);
|
|
return pString1;
|
|
}
|
|
|
|
|
|
void
|
|
PASCAL
|
|
EnableChildren(
|
|
HWND hwnd,
|
|
BOOL bEnable
|
|
)
|
|
{
|
|
int i;
|
|
static int aiControlIDs[] =
|
|
{
|
|
IDC_DEVICES,
|
|
IDC_NAME,
|
|
IDC_PORT,
|
|
IDC_COMMANDS,
|
|
IDC_REMOVE,
|
|
0
|
|
};
|
|
|
|
|
|
for (i = 0; aiControlIDs[i]; i++)
|
|
{
|
|
EnableWindow (GetDlgItem (hwnd, aiControlIDs[i]), bEnable);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
PASCAL
|
|
SelectDevice(
|
|
HWND hwnd,
|
|
LRESULT lDevice
|
|
)
|
|
{
|
|
SendDlgItemMessage (hwnd, IDC_DEVICES, LB_SETCURSEL, lDevice, 0);
|
|
PostMessage(hwnd, WM_COMMAND, IDC_DEVICES | (LBN_SELCHANGE << 16), 0);
|
|
}
|
|
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
ConfigDlgProc(
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
static HKEY hAtspKey;
|
|
|
|
DWORD dwDataSize;
|
|
DWORD dwDataType;
|
|
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
char *pBuf;
|
|
DWORD i, iNumLines;
|
|
|
|
|
|
//
|
|
// Create or open our configuration key in the registry. If the
|
|
// create fails it may well be that the current user does not
|
|
// have write access to this portion of the registry, so we'll
|
|
// just show a "read only" dialog and not allow user to make any
|
|
// changes
|
|
//
|
|
|
|
{
|
|
LONG lResult;
|
|
DWORD dwDisposition;
|
|
|
|
|
|
if ((lResult = RegCreateKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
gszAtspKey,
|
|
0,
|
|
"",
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
(LPSECURITY_ATTRIBUTES) NULL,
|
|
&hAtspKey,
|
|
&dwDisposition
|
|
|
|
)) != ERROR_SUCCESS)
|
|
{
|
|
DBGOUT((
|
|
3,
|
|
"RegCreateKeyEx(%s,ALL_ACCESS) failed, err=%d",
|
|
gszAtspKey,
|
|
lResult
|
|
));
|
|
|
|
if ((lResult = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
gszAtspKey,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hAtspKey
|
|
|
|
)) != ERROR_SUCCESS)
|
|
{
|
|
DBGOUT((
|
|
3,
|
|
"RegOpenKeyEx(%s,ALL_ACCESS) failed, err=%d",
|
|
gszAtspKey,
|
|
lResult
|
|
));
|
|
|
|
EndDialog (hwnd, 0);
|
|
return FALSE;
|
|
}
|
|
|
|
{
|
|
int i;
|
|
static int aiControlIDs[] =
|
|
{
|
|
IDC_NAME,
|
|
IDC_PORT,
|
|
IDC_COMMANDS,
|
|
IDC_ADD,
|
|
IDC_REMOVE,
|
|
IDOK,
|
|
0
|
|
};
|
|
|
|
|
|
for (i = 0; aiControlIDs[i]; i++)
|
|
{
|
|
EnableWindow(
|
|
GetDlgItem (hwnd, aiControlIDs[i]),
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Retrieve our configuration info from the registry
|
|
//
|
|
|
|
dwDataSize = sizeof(iNumLines);
|
|
iNumLines = 0;
|
|
|
|
RegQueryValueEx(
|
|
hAtspKey,
|
|
gszNumLines,
|
|
0,
|
|
&dwDataType,
|
|
(LPBYTE) &iNumLines,
|
|
&dwDataSize
|
|
);
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_NAME,
|
|
EM_LIMITTEXT,
|
|
MAX_DEV_NAME_LENGTH,
|
|
0
|
|
);
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_COMMANDS,
|
|
EM_LIMITTEXT,
|
|
MAX_DEV_NAME_LENGTH,
|
|
0
|
|
);
|
|
|
|
pBuf = DrvAlloc (256);
|
|
|
|
for (i = 0; i < iNumLines; i++)
|
|
{
|
|
char *p, *p2, szLineN[8];
|
|
PDRVLINECONFIG pLineConfig = DrvAlloc (sizeof(DRVLINECONFIG));
|
|
LONG lResult;
|
|
|
|
|
|
wsprintf (szLineN, "Line%d", i);
|
|
|
|
dwDataSize = 256;
|
|
|
|
lResult = RegQueryValueEx(
|
|
hAtspKey,
|
|
szLineN,
|
|
0,
|
|
&dwDataType,
|
|
(LPBYTE) pBuf,
|
|
&dwDataSize
|
|
);
|
|
|
|
|
|
//
|
|
// If there was a problem, use the default config
|
|
//
|
|
|
|
if (0 != lResult)
|
|
{
|
|
lstrcpy (pBuf, gszDefLineConfigParams);
|
|
}
|
|
|
|
for (p = pBuf; *p != ','; p++);
|
|
*p = 0;
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_ADDSTRING,
|
|
0,
|
|
(LPARAM) pBuf
|
|
);
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_SETITEMDATA,
|
|
i,
|
|
(LPARAM) pLineConfig
|
|
);
|
|
|
|
p++;
|
|
for (p2 = p; *p2 != ','; p2++);
|
|
*p2 = 0;
|
|
|
|
lstrcpy (pLineConfig->szPort, p);
|
|
|
|
p = p2 + 1;
|
|
|
|
lstrcpy (pLineConfig->szCommands, p);
|
|
}
|
|
|
|
DrvFree (pBuf);
|
|
|
|
|
|
//
|
|
// Fill up the various controls with configuration options
|
|
//
|
|
|
|
{
|
|
static char *aszPorts[] = { "COM1","COM2","COM3",NULL };
|
|
|
|
for (i = 0; aszPorts[i]; i++)
|
|
{
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_PORT,
|
|
LB_ADDSTRING,
|
|
0,
|
|
(LPARAM) aszPorts[i]
|
|
);
|
|
}
|
|
}
|
|
|
|
if (iNumLines == 0)
|
|
{
|
|
EnableChildren (hwnd, FALSE);
|
|
}
|
|
else
|
|
{
|
|
SelectDevice (hwnd, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case WM_COMMAND:
|
|
{
|
|
LRESULT lSelection;
|
|
PDRVLINECONFIG pLineConfig;
|
|
|
|
|
|
lSelection = SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_GETCURSEL,
|
|
0,
|
|
0
|
|
);
|
|
|
|
pLineConfig = (PDRVLINECONFIG) SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_GETITEMDATA,
|
|
(WPARAM) lSelection,
|
|
0
|
|
);
|
|
|
|
switch (LOWORD((DWORD)wParam))
|
|
{
|
|
case IDC_DEVICES:
|
|
|
|
if (HIWORD(wParam) == LBN_SELCHANGE)
|
|
{
|
|
char buf[MAX_DEV_NAME_LENGTH + 1];
|
|
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_GETTEXT,
|
|
lSelection,
|
|
(LPARAM) buf
|
|
);
|
|
|
|
SetDlgItemText (hwnd, IDC_NAME, buf);
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_PORT,
|
|
LB_SELECTSTRING,
|
|
(WPARAM) -1,
|
|
(LPARAM) pLineConfig->szPort
|
|
);
|
|
|
|
SetDlgItemText (hwnd, IDC_COMMANDS, pLineConfig->szCommands);
|
|
}
|
|
|
|
break;
|
|
|
|
case IDC_NAME:
|
|
|
|
if ((HIWORD(wParam) == EN_CHANGE) && (lSelection != LB_ERR))
|
|
{
|
|
char buf[MAX_DEV_NAME_LENGTH + 1];
|
|
|
|
|
|
GetDlgItemText (hwnd, IDC_NAME, buf, MAX_DEV_NAME_LENGTH);
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_DELETESTRING,
|
|
lSelection,
|
|
0
|
|
);
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_INSERTSTRING,
|
|
lSelection,
|
|
(LPARAM) buf
|
|
);
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_SETCURSEL,
|
|
lSelection,
|
|
0
|
|
);
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_SETITEMDATA,
|
|
lSelection,
|
|
(LPARAM) pLineConfig
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case IDC_PORT:
|
|
|
|
if (HIWORD(wParam) == LBN_SELCHANGE)
|
|
{
|
|
lSelection = SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_PORT,
|
|
LB_GETCURSEL,
|
|
0,
|
|
0
|
|
);
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_PORT,
|
|
LB_GETTEXT,
|
|
lSelection,
|
|
(LPARAM) pLineConfig->szPort
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case IDC_COMMANDS:
|
|
|
|
if ((HIWORD(wParam) == EN_CHANGE) && (lSelection != LB_ERR))
|
|
{
|
|
GetDlgItemText(
|
|
hwnd,
|
|
IDC_COMMANDS,
|
|
pLineConfig->szCommands,
|
|
63
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case IDC_ADD:
|
|
{
|
|
LRESULT lNumLines, l = 2;
|
|
char szLineName[32];
|
|
PDRVLINECONFIG pLineConfig = DrvAlloc (sizeof(DRVLINECONFIG));
|
|
|
|
|
|
lNumLines = SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_GETCOUNT,
|
|
0,
|
|
0
|
|
);
|
|
|
|
lstrcpy (pLineConfig->szPort, "COM1");
|
|
|
|
lstrcpy (szLineName, "my new line");
|
|
|
|
find_unique_line_name:
|
|
|
|
if (SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_FINDSTRING,
|
|
(WPARAM) -1,
|
|
(LPARAM) szLineName
|
|
|
|
) != LB_ERR)
|
|
{
|
|
wsprintf (szLineName, "my new line%d", l++);
|
|
goto find_unique_line_name;
|
|
}
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_ADDSTRING,
|
|
0,
|
|
(LPARAM) szLineName
|
|
);
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_SETITEMDATA,
|
|
lNumLines,
|
|
(LPARAM) pLineConfig
|
|
);
|
|
|
|
EnableChildren (hwnd, TRUE);
|
|
|
|
SelectDevice (hwnd, lNumLines);
|
|
|
|
SetFocus (GetDlgItem (hwnd, IDC_NAME));
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_NAME,
|
|
EM_SETSEL,
|
|
0,
|
|
(LPARAM) -1
|
|
);
|
|
|
|
break;
|
|
}
|
|
case IDC_REMOVE:
|
|
{
|
|
LRESULT lNumLines;
|
|
|
|
|
|
DrvFree (pLineConfig);
|
|
|
|
lNumLines = SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_DELETESTRING,
|
|
lSelection,
|
|
0
|
|
);
|
|
|
|
if (lNumLines == 0)
|
|
{
|
|
SetDlgItemText (hwnd, IDC_NAME, "");
|
|
SetDlgItemText (hwnd, IDC_COMMANDS, "");
|
|
|
|
EnableChildren (hwnd, FALSE);
|
|
}
|
|
else
|
|
{
|
|
SelectDevice (hwnd, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case IDOK:
|
|
{
|
|
char *pBuf;
|
|
LRESULT l, lNumLines;
|
|
|
|
|
|
//
|
|
// Update the num lines & num phones values
|
|
//
|
|
|
|
pBuf = DrvAlloc (256);
|
|
|
|
lNumLines = SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_GETCOUNT,
|
|
0,
|
|
0
|
|
);
|
|
|
|
RegSetValueEx(
|
|
hAtspKey,
|
|
gszNumLines,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE) &lNumLines,
|
|
sizeof(DWORD)
|
|
);
|
|
|
|
|
|
//
|
|
// For each installed device save it's config info
|
|
//
|
|
|
|
for (l = 0; l < lNumLines; l++)
|
|
{
|
|
char szLineN[8];
|
|
PDRVLINECONFIG pLineConfig;
|
|
|
|
|
|
SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_GETTEXT,
|
|
l,
|
|
(LPARAM) pBuf
|
|
);
|
|
|
|
pLineConfig = (PDRVLINECONFIG) SendDlgItemMessage(
|
|
hwnd,
|
|
IDC_DEVICES,
|
|
LB_GETITEMDATA,
|
|
l,
|
|
0
|
|
);
|
|
|
|
wsprintf(
|
|
pBuf + strlen (pBuf),
|
|
",%s,%s",
|
|
pLineConfig->szPort,
|
|
pLineConfig->szCommands
|
|
);
|
|
|
|
wsprintf (szLineN, "Line%d", l);
|
|
|
|
RegSetValueEx(
|
|
hAtspKey,
|
|
szLineN,
|
|
0,
|
|
REG_SZ,
|
|
(LPBYTE) pBuf,
|
|
lstrlen (pBuf) + 1
|
|
);
|
|
|
|
DrvFree (pLineConfig);
|
|
}
|
|
|
|
DrvFree (pBuf);
|
|
|
|
// fall thru to EndDialog...
|
|
}
|
|
case IDCANCEL:
|
|
|
|
RegCloseKey (hAtspKey);
|
|
EndDialog (hwnd, 0);
|
|
break;
|
|
|
|
} // switch (LOWORD((DWORD)wParam))
|
|
|
|
break;
|
|
}
|
|
} // switch (msg)
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
LPVOID
|
|
PASCAL
|
|
DrvAlloc(
|
|
DWORD dwSize
|
|
)
|
|
{
|
|
return (LocalAlloc (LPTR, dwSize));
|
|
}
|
|
|
|
|
|
VOID
|
|
PASCAL
|
|
DrvFree(
|
|
LPVOID lp
|
|
)
|
|
{
|
|
LocalFree (lp);
|
|
}
|
|
|
|
|
|
void
|
|
PASCAL
|
|
SetCallState(
|
|
PDRVLINE pLine,
|
|
DWORD dwCallState,
|
|
DWORD dwCallStateMode
|
|
)
|
|
{
|
|
if (dwCallState != pLine->dwCallState)
|
|
{
|
|
pLine->dwCallState = dwCallState;
|
|
pLine->dwCallStateMode = dwCallStateMode;
|
|
|
|
(*pLine->pfnEventProc)(
|
|
pLine->htLine,
|
|
pLine->htCall,
|
|
LINE_CALLSTATE,
|
|
dwCallState,
|
|
dwCallStateMode,
|
|
pLine->dwMediaMode
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
#if DBG
|
|
|
|
void
|
|
PASCAL
|
|
Prolog(
|
|
PFUNC_INFO pInfo
|
|
)
|
|
{
|
|
DWORD i;
|
|
|
|
|
|
DBGOUT((3, "%s: enter", pInfo->lpszFuncName));
|
|
|
|
for (i = 0; i < pInfo->dwNumParams; i++)
|
|
{
|
|
if (pInfo->aParams[i].dwVal &&
|
|
pInfo->aParams[i].lpszVal[3] == 'z') // lpszVal = "lpsz..."
|
|
{
|
|
DBGOUT((
|
|
3,
|
|
"%s%s=x%lx, '%s'",
|
|
gszTab,
|
|
pInfo->aParams[i].lpszVal,
|
|
pInfo->aParams[i].dwVal,
|
|
pInfo->aParams[i].dwVal
|
|
));
|
|
}
|
|
else
|
|
{
|
|
DBGOUT((
|
|
3,
|
|
"%s%s=x%lx",
|
|
gszTab,
|
|
pInfo->aParams[i].lpszVal,
|
|
pInfo->aParams[i].dwVal
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
LONG
|
|
PASCAL
|
|
Epilog(
|
|
PFUNC_INFO pInfo,
|
|
LONG lResult
|
|
)
|
|
{
|
|
DBGOUT((3, "%s: returning x%x", pInfo->lpszFuncName, lResult));
|
|
|
|
return lResult;
|
|
}
|
|
|
|
|
|
void
|
|
CDECL
|
|
DebugOutput(
|
|
DWORD dwDbgLevel,
|
|
LPCSTR lpszFormat,
|
|
...
|
|
)
|
|
{
|
|
if (dwDbgLevel <= gdwDebugLevel)
|
|
{
|
|
char buf[128] = "ATSP32: ";
|
|
va_list ap;
|
|
|
|
|
|
va_start(ap, lpszFormat);
|
|
|
|
wvsprintf (&buf[8], lpszFormat, ap);
|
|
|
|
lstrcat (buf, "\n");
|
|
|
|
OutputDebugString (buf);
|
|
|
|
va_end(ap);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
LONG
|
|
PASCAL
|
|
ProviderInstall(
|
|
char *pszProviderName,
|
|
BOOL bNoMultipleInstance
|
|
)
|
|
{
|
|
LONG lResult;
|
|
|
|
|
|
//
|
|
// If only one installation instance of this provider is
|
|
// allowed then we want to check the provider list to see
|
|
// if the provider is already installed
|
|
//
|
|
|
|
if (bNoMultipleInstance)
|
|
{
|
|
LONG (WINAPI *pfnGetProviderList)();
|
|
DWORD dwTotalSize, i;
|
|
HINSTANCE hTapi32;
|
|
LPLINEPROVIDERLIST pProviderList;
|
|
LPLINEPROVIDERENTRY pProviderEntry;
|
|
|
|
|
|
//
|
|
// Load Tapi32.dll & get a pointer to the lineGetProviderList
|
|
// func. We don't want to statically link because this module
|
|
// plays the part of both core SP & UI DLL, and we don't want
|
|
// to incur the performance hit of automatically loading
|
|
// Tapi32.dll when running as a core SP within Tapisrv.exe's
|
|
// context.
|
|
//
|
|
|
|
if (!(hTapi32 = LoadLibrary ("tapi32.dll")))
|
|
{
|
|
DBGOUT((
|
|
1,
|
|
"LoadLibrary(tapi32.dll) failed, err=%d",
|
|
GetLastError()
|
|
));
|
|
|
|
lResult = LINEERR_OPERATIONFAILED;
|
|
goto ProviderInstall_return;
|
|
}
|
|
|
|
if (!((FARPROC) pfnGetProviderList = GetProcAddress(
|
|
hTapi32,
|
|
(LPCSTR) "lineGetProviderList"
|
|
)))
|
|
{
|
|
DBGOUT((
|
|
1,
|
|
"GetProcAddr(lineGetProviderList) failed, err=%d",
|
|
GetLastError()
|
|
));
|
|
|
|
lResult = LINEERR_OPERATIONFAILED;
|
|
goto ProviderInstall_unloadTapi32;
|
|
}
|
|
|
|
|
|
//
|
|
// Loop until we get the full provider list
|
|
//
|
|
|
|
dwTotalSize = sizeof (LINEPROVIDERLIST);
|
|
|
|
goto ProviderInstall_allocProviderList;
|
|
|
|
ProviderInstall_getProviderList:
|
|
|
|
if ((lResult = (*pfnGetProviderList)(0x00020000, pProviderList)) != 0)
|
|
{
|
|
goto ProviderInstall_freeProviderList;
|
|
}
|
|
|
|
if (pProviderList->dwNeededSize > pProviderList->dwTotalSize)
|
|
{
|
|
dwTotalSize = pProviderList->dwNeededSize;
|
|
|
|
LocalFree (pProviderList);
|
|
|
|
ProviderInstall_allocProviderList:
|
|
|
|
if (!(pProviderList = LocalAlloc (LPTR, dwTotalSize)))
|
|
{
|
|
lResult = LINEERR_NOMEM;
|
|
goto ProviderInstall_unloadTapi32;
|
|
}
|
|
|
|
pProviderList->dwTotalSize = dwTotalSize;
|
|
|
|
goto ProviderInstall_getProviderList;
|
|
}
|
|
|
|
|
|
//
|
|
// Inspect the provider list entries to see if this provider
|
|
// is already installed
|
|
//
|
|
|
|
pProviderEntry = (LPLINEPROVIDERENTRY) (((LPBYTE) pProviderList) +
|
|
pProviderList->dwProviderListOffset);
|
|
|
|
for (i = 0; i < pProviderList->dwNumProviders; i++)
|
|
{
|
|
char *pszInstalledProviderName = ((char *) pProviderList) +
|
|
pProviderEntry->dwProviderFilenameOffset,
|
|
*p;
|
|
|
|
|
|
//
|
|
// Make sure pszInstalledProviderName points at <filename>
|
|
// and not <path>\filename by walking backeards thru the
|
|
// string searching for last '\\'
|
|
//
|
|
|
|
p = pszInstalledProviderName +
|
|
lstrlen (pszInstalledProviderName) - 1;
|
|
|
|
for (; *p != '\\' && p != pszInstalledProviderName; p--);
|
|
|
|
pszInstalledProviderName =
|
|
(p == pszInstalledProviderName ? p : p + 1);
|
|
|
|
if (lstrcmpiA (pszInstalledProviderName, pszProviderName) == 0)
|
|
{
|
|
lResult = LINEERR_NOMULTIPLEINSTANCE;
|
|
goto ProviderInstall_freeProviderList;
|
|
}
|
|
|
|
pProviderEntry++;
|
|
}
|
|
|
|
|
|
//
|
|
// If here then the provider isn't currently installed,
|
|
// so do whatever configuration stuff is necessary and
|
|
// indicate SUCCESS
|
|
//
|
|
|
|
lResult = 0;
|
|
|
|
|
|
ProviderInstall_freeProviderList:
|
|
|
|
LocalFree (pProviderList);
|
|
|
|
ProviderInstall_unloadTapi32:
|
|
|
|
FreeLibrary (hTapi32);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Do whatever configuration stuff is necessary and return SUCCESS
|
|
//
|
|
|
|
lResult = 0;
|
|
}
|
|
|
|
ProviderInstall_return:
|
|
|
|
return lResult;
|
|
}
|
|
|
|
|
|
void
|
|
PASCAL
|
|
DropActiveCall(
|
|
PDRVLINE pLine
|
|
)
|
|
{
|
|
if (pLine->hComm)
|
|
{
|
|
DWORD dwNumBytes, dwError;
|
|
OVERLAPPED overlapped;
|
|
|
|
|
|
pLine->bDropInProgress = TRUE;
|
|
|
|
SetEvent (pLine->Overlapped.hEvent);
|
|
|
|
ZeroMemory (&overlapped, sizeof (OVERLAPPED));
|
|
|
|
overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
|
|
|
|
if (pLine->dwMediaMode != LINEMEDIAMODE_INTERACTIVEVOICE)
|
|
{
|
|
if (!WriteFile(
|
|
pLine->hComm,
|
|
"+++\r", 4,
|
|
&dwNumBytes,
|
|
&overlapped
|
|
))
|
|
{
|
|
if ((dwError = GetLastError()) == ERROR_IO_PENDING)
|
|
{
|
|
GetOverlappedResult(
|
|
pLine->hComm,
|
|
&overlapped,
|
|
&dwNumBytes,
|
|
TRUE
|
|
);
|
|
|
|
ResetEvent (overlapped.hEvent);
|
|
}
|
|
else
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!WriteFile (pLine->hComm, "ATH\r", 4, &dwNumBytes, &overlapped))
|
|
{
|
|
if ((dwError = GetLastError()) == ERROR_IO_PENDING)
|
|
{
|
|
GetOverlappedResult(
|
|
pLine->hComm,
|
|
&overlapped,
|
|
&dwNumBytes,
|
|
TRUE
|
|
);
|
|
}
|
|
else
|
|
{
|
|
}
|
|
}
|
|
|
|
CloseHandle (overlapped.hEvent);
|
|
CloseHandle (pLine->hComm);
|
|
pLine->hComm = NULL;
|
|
}
|
|
}
|