1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
363.
364.
365.
366.
367.
368.
369.
370.
371.
372.
373.
374.
375.
376.
377.
378.
379.
380.
381.
382.
383.
384.
385.
386.
387.
388.
389.
390.
391.
392.
393.
394.
395.
396.
397.
398.
399.
400.
401.
402.
403.
404.
405.
406.
407.
408.
409.
410.
411.
412.
413.
414.
415.
416.
417.
418.
419.
420.
421.
422.
423.
424.
425.
426.
427.
428.
429.
430.
431.
432.
433.
434.
435.
436.
437.
438.
439.
440.
441.
442.
443.
444.
445.
446.
447.
448.
449.
450.
451.
452.
453.
454.
455.
456.
457.
458.
459.
460.
461.
462.
463.
464.
465.
466.
467.
468.
469.
470.
471.
472.
473.
474.
475.
476.
477.
478.
479.
480.
481.
482.
483.
484.
485.
486.
487.
488.
489.
490.
491.
492.
493.
494.
495.
496.
497.
498.
499.
500.
501.
502.
503.
504.
505.
506.
507.
508.
509.
510.
511.
512.
513.
514.
515.
516.
517.
518.
519.
520.
521.
522.
523.
524.
525.
526.
527.
528.
529.
530.
531.
532.
533.
534.
535.
536.
537.
538.
539.
540.
541.
542.
543.
544.
545.
546.
547.
548.
549.
550.
551.
552.
553.
554.
555.
556.
557.
558.
559.
560.
561.
562.
563.
564.
565.
566.
567.
568.
569.
570.
571.
572.
573.
574.
575.
576.
577.
578.
579.
580.
581.
582.
583.
584.
585.
586.
587.
588.
589.
590.
591.
592.
593.
594.
595.
596.
597.
598.
599.
600.
601.
602.
603.
604.
605.
606.
607.
608.
609.
610.
611.
612.
613.
614.
615.
616.
617.
618.
619.
620.
621.
622.
623.
624.
625.
626.
627.
628.
629.
630.
631.
632.
633.
634.
635.
636.
637.
638.
639.
640.
641.
642.
643.
644.
645.
646.
647.
648.
649.
650.
651.
652.
653.
654.
655.
656.
657.
658.
659.
660.
661.
662.
663.
664.
665.
666.
667.
668.
669.
670.
671.
672.
673.
674.
675.
676.
677.
678.
679.
680.
681.
682.
683.
684.
685.
686.
687.
688.
689.
690.
691.
692.
693.
694.
695.
696.
697.
698.
699.
700.
701.
702.
703.
704.
705.
706.
707.
708.
709.
710.
711.
712.
713.
714.
715.
716.
717.
718.
719.
720.
721.
722.
723.
724.
725.
726.
727.
728.
729.
730.
731.
732.
733.
734.
735.
736.
737.
738.
739.
740.
741.
742.
743.
744.
745.
746.
747.
748.
749.
750.
751.
752.
753.
754.
755.
756.
757.
758.
759.
760.
761.
762.
763.
764.
765.
766.
767.
768.
769.
770.
771.
772.
773.
774.
775.
776.
777.
778.
779.
780.
781.
782.
783.
784.
785.
786.
787.
788.
789.
790.
791.
792.
793.
794.
795.
796.
797.
798.
799.
800.
801.
802.
803.
804.
805.
806.
807.
808.
809.
810.
811.
812.
813.
814.
815.
816.
817.
818.
819.
820.
821.
822.
823.
824.
825.
826.
827.
828.
829.
830.
831.
832.
833.
834.
835.
836.
837.
838.
839.
840.
841.
842.
843.
844.
845.
846.
847.
848.
849.
850.
851.
852.
853.
854.
855.
856.
857.
858.
859.
860.
861.
862.
863.
864.
865.
866.
867.
868.
869.
870.
871.
872.
873.
874.
875.
876.
877.
878.
879.
880.
881.
882.
883.
884.
885.
886.
887.
888.
889.
890.
891.
892.
893.
894.
895.
896.
897.
898.
899.
900.
901.
902.
903.
904.
905.
906.
907.
908.
909.
910.
911.
912.
913.
914.
915.
916.
917.
918.
919.
920.
921.
922.
923.
924.
925.
926.
927.
928.
929.
930.
931.
932.
933.
934.
935.
936.
937.
938.
939.
940.
941.
942.
943.
944.
945.
946.
947.
948.
949.
950.
951.
952.
953.
954.
955.
956.
957.
958.
959.
960.
961.
962.
963.
964.
965.
966.
967.
968.
969.
970.
971.
972.
973.
974.
975.
976.
977.
978.
979.
980.
981.
982.
983.
984.
985.
986.
987.
988.
989.
990.
991.
992.
993.
994.
995.
996.
997.
998.
999.
1000.
1001.
1002.
1003.
1004.
1005.
1006.
1007.
1008.
1009.
1010.
1011.
1012.
1013.
1014.
1015.
1016.
1017.
1018.
1019.
1020.
1021.
1022.
1023.
1024.
1025.
1026.
1027.
1028.
1029.
1030.
1031.
1032.
1033.
1034.
1035.
1036.
1037.
1038.
1039.
1040.
1041.
1042.
1043.
1044.
1045.
1046.
1047.
1048.
1049.
1050.
1051.
1052.
1053.
1054.
1055.
1056.
1057.
1058.
1059.
1060.
1061.
1062.
1063.
1064.
1065.
1066.
1067.
1068.
1069.
1070.
1071.
1072.
1073.
1074.
1075.
1076.
1077.
1078.
1079.
1080.
1081.
1082.
1083.
1084.
1085.
1086.
1087.
1088.
1089.
1090.
1091.
1092.
1093.
1094.
1095.
1096.
1097.
1098.
1099.
1100.
1101.
1102.
1103.
1104.
1105.
1106.
1107.
1108.
1109.
1110.
1111.
1112.
1113.
1114.
1115.
1116.
1117.
1118.
1119.
1120.
1121.
1122.
1123.
1124.
1125.
1126.
1127.
1128.
1129.
1130.
1131.
1132.
1133.
1134.
1135.
1136.
1137.
1138.
1139.
1140.
1141.
1142.
1143.
1144.
1145.
1146.
1147.
1148.
1149.
1150.
1151.
1152.
1153.
1154.
1155.
1156.
1157.
1158.
1159.
1160.
1161.
1162.
1163.
1164.
1165.
1166.
1167.
1168.
1169.
1170.
1171.
1172.
1173.
1174.
1175.
1176.
1177.
1178.
1179.
1180.
1181.
1182.
1183.
1184.
1185.
1186.
1187.
1188.
1189.
1190.
1191.
1192.
1193.
1194.
1195.
1196.
1197.
1198.
1199.
1200.
1201.
1202.
1203.
1204.
1205.
1206.
1207.
1208.
1209.
1210.
1211.
1212.
1213.
1214.
1215.
1216.
1217.
1218.
1219.
1220.
1221.
1222.
1223.
1224.
1225.
1226.
1227.
1228.
1229.
1230.
1231.
1232.
1233.
1234.
1235.
1236.
1237.
1238.
1239.
1240.
1241.
1242.
1243.
1244.
1245.
1246.
1247.
1248.
1249.
1250.
1251.
1252.
1253.
1254.
1255.
1256.
1257.
1258.
1259.
1260.
1261.
1262.
1263.
1264.
1265.
1266.
1267.
1268.
1269.
1270.
1271.
1272.
1273.
1274.
1275.
1276.
/*----------------------------------------------------------*\
TapiWave
This sample console application is designed to demonstrate
how to use TAPI to play and record wave files using a voice modem.
\*----------------------------------------------------------*/
//#define UNICODE
//#define _UNICODE
#pragma comment(linker, "/subsystem:console")
#pragma comment(lib, "tapi32")
#include <windows.h>
#include <mmsystem.h>
#include <tchar.h>
#include <stdio.h>
#ifdef UNICODE
#define TAPI_CURRENT_VERSION 0x00020000
#else
#define TAPI_CURRENT_VERSION 0x00010004
#endif
#include <tapi.h>
TCHAR szAppName[] = TEXT("TapiWave");
TCHAR szAppFileName[] = TEXT("TapiWave.exe");
// TAPI constants; command line settable
DWORD dwDeviceID = 0 ;
DWORD dwAddressID = 0 ;
// TAPI global variables.
HINSTANCE hInstance;
HLINEAPP hLineApp = 0 ;
DWORD dwNumDevs;
DWORD dwAPIVersion;
HLINE hLine;
HCALL hCall = 0 ;
LINEEXTENSIONID LineExtensionID;
#if TAPI_CURRENT_VERSION >= 0x00020000
LINEINITIALIZEEXPARAMS lineInitializeExParams = {
sizeof(lineInitializeExParams), 0 , 0 ,
LINEINITIALIZEEXOPTION_USEHIDDENWINDOW,
NULL, 0
};
#endif
TCHAR szPhoneNumber[ 256 ] = TEXT("45776");
#define BIGBUFF 8096
LPVARSTRING pVarString = NULL;
LPLINEDEVSTATUS pLineDevStatus = NULL;
LPLINECALLINFO pLineCallInfo = NULL;
LPLINECALLPARAMS pLineCallParams = NULL;
LPLINETRANSLATEOUTPUT pTranslateOutput = NULL;
// State machine information.
DWORD dwMakeCallAsyncID= 0 ;
DWORD dwLineDropAsyncID= 0 ;
BOOL bDropped = FALSE;
BOOL bConnected = FALSE;
BOOL bAnswered = FALSE;
BOOL bReadyToEnd = FALSE;
BOOL bPrintedEnd = FALSE;
// Constants in how the state machine behaves.
BOOL bCommandLineError = FALSE;
BOOL bLogExtraInfo = FALSE;
BOOL bAnswer = TRUE;
// Variables so we can ^C to shutdown and clean up properly.
DWORD dwThreadID;
DWORD dwWaveThreadID;
DWORD dwTimeToWaitBeforePlaying = 5000 ;
// /// Waveaudio content ///
HANDLE hWaveThread = NULL;
UINT WaveInID = 0 ;
UINT WaveOutID = 0 ;
BOOL bWaveLocal = FALSE;
TCHAR szFileName[ 1024 ] = TEXT("greeting.wav");
DWORD dwWaveMapped = WAVE_MAPPED;
// Prototypes
void CALLBACK lineCallbackFunc(
DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
BOOL SetupEnvironment (int argc, LPTSTR argv[]);
void PrintHelp();
BOOL BreakHandlerRoutine(DWORD dwCtrlType);
BOOL GetCallInfo();
void StopEverything();
BOOL PumpMessages(BOOL bWaitForMessage);
DWORD WINAPI WaveThread(LPVOID pVoid);
void PlayFromMemory (LPSTR szWavData);
DWORD WINAPI ThreadRecorded (LPVOID pvThreadParam);
void RecordToMemory (LPSTR szWavData);
void BuildWavData (LPSTR szFilename, LPSTR szWavData, DWORD dwMaxBufferSize);
void __cdecl MyPrintf(LPCTSTR pszFormat, ...);
LPCTSTR FormatError(DWORD dwError);
LPCTSTR FormatErrorBuffer(DWORD dwError, LPTSTR pszBuff, DWORD dwNumChars);
BOOL WINAPI HandlerRoutine(DWORD dwCtrlType);
LPTSTR FormatTapiError (long lError);
// Console app starting place.
int __cdecl _tmain(int argc, _TCHAR *argv[], _TCHAR *envp[])
{
LONG lRet;
MSG msg;
// Setup basic stuff.
hInstance = GetModuleHandle(NULL);
dwThreadID = GetCurrentThreadId();
SetConsoleCtrlHandler((PHANDLER_ROUTINE) BreakHandlerRoutine, TRUE);
if (!SetupEnvironment(argc, argv))
goto end;
if (bWaveLocal)
{
hWaveThread = CreateThread(NULL, 0 , WaveThread, NULL, 0 , &dwWaveThreadID);
goto end;
}
// Prime the message queue. TAPI callback is called as a result
// of messages being dispatched. By default, console apps don't have
// a message queue to hold these messages. PeekMessage will create it.
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
pVarString = LocalAlloc(LPTR, BIGBUFF);
pLineDevStatus = LocalAlloc(LPTR, BIGBUFF);
pLineCallInfo = LocalAlloc(LPTR, BIGBUFF);
pLineCallParams = LocalAlloc(LPTR, BIGBUFF);
pTranslateOutput = LocalAlloc(LPTR, BIGBUFF);
pVarString -> dwTotalSize = BIGBUFF;
pLineDevStatus -> dwTotalSize = BIGBUFF;
pLineCallInfo -> dwTotalSize = BIGBUFF;
pTranslateOutput -> dwTotalSize = BIGBUFF;
pLineCallParams -> dwTotalSize = BIGBUFF;
pLineCallParams -> dwBearerMode = LINEBEARERMODE_VOICE;
pLineCallParams -> dwMediaMode = LINEMEDIAMODE_AUTOMATEDVOICE,
pLineCallParams -> dwCallParamFlags = 0;
pLineCallParams -> dwAddressMode = LINEADDRESSMODE_ADDRESSID;
pLineCallParams -> dwAddressID = dwAddressID;
#if TAPI_CURRENT_VERSION >= 0x00020000
dwAPIVersion = TAPI_CURRENT_VERSION;
lRet = lineInitializeEx(&hLineApp, hInstance, lineCallbackFunc,
szAppName, &dwNumDevs,
&dwAPIVersion, &lineInitializeExParams);
#else
// Note that you can't use this function and be UNICODE
lRet = lineInitialize(&hLineApp, hInstance, lineCallbackFunc,
szAppName, &dwNumDevs);
#endif
if (lRet)
{
MyPrintf(TEXT("lineInitialize failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
if (lRet = lineNegotiateAPIVersion(hLineApp, dwDeviceID,
0x00010004, 0x00010004, &dwAPIVersion, &LineExtensionID))
{
MyPrintf(TEXT("lineNegotiateAPIVersion failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
if (lRet = lineOpen(hLineApp, dwDeviceID, &hLine, dwAPIVersion, 0 , 0 ,
LINECALLPRIVILEGE_NONE, 0 , NULL))
{
MyPrintf(TEXT("lineOpen failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
while (TRUE)
{
if (lRet = lineGetLineDevStatus(hLine, pLineDevStatus))
{
MyPrintf(TEXT("lineGetLineDevStatus failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
if (pLineDevStatus->dwNeededSize > pLineDevStatus->dwTotalSize)
{
LocalReAlloc(pLineDevStatus, pLineDevStatus->dwNeededSize, LMEM_MOVEABLE);
pLineDevStatus->dwTotalSize = pLineDevStatus->dwNeededSize;
continue;
}
break;
}
if (pLineDevStatus -> dwOpenMediaModes)
MyPrintf(TEXT("!!!WARNING!!! Another application is already waiting for calls.\r\n\r\n"));
if (!((pLineDevStatus -> dwLineFeatures) & LINEFEATURE_MAKECALL))
MyPrintf(TEXT("!!!WARNING!!! No call appearances available at this time.\r\n\r\n"));
if (bAnswer)
{
lineClose(hLine);
if (lRet = lineOpen(hLineApp, dwDeviceID, &hLine, dwAPIVersion, 0 , 0 ,
LINECALLPRIVILEGE_OWNER, LINEMEDIAMODE_AUTOMATEDVOICE, NULL))
{
MyPrintf(TEXT("lineOpen failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
MyPrintf(TEXT("Waiting for a call on TAPI Line Device %lu\r\n"), dwDeviceID);
}
else
{
if (lRet = lineTranslateAddress(hLineApp, dwDeviceID, dwAPIVersion, szPhoneNumber,
0 , 0 , pTranslateOutput))
{
MyPrintf(TEXT("lineTranslateAddress failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
lRet = lineMakeCall(hLine, &hCall, szPhoneNumber, 0 , pLineCallParams);
if (lRet < 0 )
{
MyPrintf(TEXT("lineMakeCall failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
else
{
dwMakeCallAsyncID = lRet;
}
}
// TAPI callback is called only when messages are dispatched!
while (PumpMessages(TRUE));
end:
StopEverything();
if (hLineApp)
lineShutdown(hLineApp);
hLineApp = 0 ;
if (pLineDevStatus)
LocalFree(pLineDevStatus);
if (pLineCallInfo)
LocalFree(pLineCallInfo);
if (pLineCallParams)
LocalFree(pLineCallParams);
if (pVarString)
LocalFree(pVarString);
if (pTranslateOutput)
LocalFree(pTranslateOutput);
return 1 ;
}
// Here's the TAPI callback. Mondo switch statement!
void CALLBACK lineCallbackFunc(
DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{
LONG lRet;
/*
if (bLogExtraInfo)
MyPrintf(TEXT("LCBF: %s\r\n"), // LCBF stands for lineCallBackFunc
FormatLineCallback(
dwDevice, dwMsg, dwCallbackInstance,
dwParam1, dwParam2, dwParam3,
szBuff));
*/
switch(dwMsg)
{
case LINE_LINEDEVSTATE:
if (dwParam1 == LINEDEVSTATE_REINIT)
{
MyPrintf(TEXT("LINEDEVSTATE_REINIT\r\n"));
StopEverything();
}
break;
case LINE_REPLY:
if (dwParam2 == dwLineDropAsyncID)
{
if (dwParam2 != 0)
{
MyPrintf(TEXT("lineDrop LINE_REPLY with failure: %s. Stopping.\r\r\n"),
FormatTapiError((long) dwParam2));
StopEverything();
}
}
else if (dwParam2 == dwMakeCallAsyncID)
{
if (dwParam2 != 0)
{
MyPrintf(TEXT("lineMakeCall LINE_REPLY with failure: %s. Stopping.\r\r\n"),
FormatTapiError((long) dwParam2));
StopEverything();
}
}
// else ignore it.
break;
case LINE_CALLSTATE:
{
// Is this a new call?
if (dwParam3 == LINECALLPRIVILEGE_OWNER)
{
// Do we already have a call?
if (hCall && (hCall != (HCALL) dwDevice))
{
if (dwMsg == LINECALLSTATE_IDLE)
lineDeallocateCall((HCALL) dwDevice);
else
{
MyPrintf(TEXT("New call; %lu, but already managing one. Dropping it.\r\r\n"),
dwDevice);
lineDrop((HCALL) dwDevice, NULL, 0);
}
break;
}
if (hCall)
MyPrintf(
TEXT("Given OWNER privs to a call already owned?\r\n")
TEXT(" - Should only happen if handed a call already owned.\r\n"));
else
{
hCall = (HCALL) dwDevice;
MyPrintf(TEXT("New incoming hCall 0x%lx on TAPI Line Device.\r\r\n"),
hCall, dwDeviceID);
}
}
if (hCall != (HCALL) dwDevice)
{
if (dwMsg == LINECALLSTATE_IDLE)
lineDeallocateCall((HCALL) dwDevice);
else
{
lineDrop((HCALL) dwDevice, NULL, 0);
MyPrintf(TEXT("LINE_CALLSTATE 0x%lx for non-main hCall: 0x%lx. Dropping.\r\n"),
dwParam1, dwDevice);
}
break;
}
switch (dwParam1)
{
case LINECALLSTATE_IDLE:
{
MyPrintf(TEXT("IDLE.\n\r\n"));
lRet = lineDeallocateCall(hCall);
// Should make sure lineDeallocateCall succeeded
hCall = 0;
StopEverything();
break;
}
case LINECALLSTATE_BUSY:
case LINECALLSTATE_DISCONNECTED:
if (dwParam1 == LINECALLSTATE_BUSY)
MyPrintf(TEXT("BUSY. Hanging up.\r\n"));
else
MyPrintf(TEXT("DISCONNECTED. Hanging up.\r\n"));
if (!bDropped)
dwLineDropAsyncID = lineDrop(hCall, NULL, 0);
if (dwLineDropAsyncID < 0)
{
MyPrintf(TEXT("lineDrop failed. Terminating\r\n"));
hCall = 0;
StopEverything();
}
bDropped = TRUE;
break;
case LINECALLSTATE_OFFERING:
case LINECALLSTATE_ACCEPTED: // Could be handed off an accepted call
if (bAnswered)
break;
if (dwParam1 == LINECALLSTATE_OFFERING)
MyPrintf(TEXT("Answering an OFFERING call.\r\n\r\n"));
else
MyPrintf(TEXT("Answering an ACCEPTED call.\r\n\r\n"));
lRet = lineAnswer(hCall, NULL, 0);
// Should check to make sure lineAnswer succeeded
bAnswered = TRUE;
break;
case LINECALLSTATE_CONNECTED:
if (!bConnected)
{
bConnected = TRUE;
MyPrintf(TEXT("CONNECTED.\r\n"));
hWaveThread = CreateThread(NULL, 0, WaveThread, NULL, 0, &dwWaveThreadID);
}
break;
}
break;
}
}
}
BOOL GetCallInfo()
{
LONG lRet;
while (TRUE)
{
if (lRet = lineGetCallInfo(hCall, pLineCallInfo))
{
MyPrintf(TEXT("lineGetCallInfo failed: %s.\r\n"), FormatTapiError(lRet));
return FALSE;
}
if (pLineCallInfo->dwNeededSize > pLineCallInfo->dwTotalSize)
{
LocalReAlloc(pLineCallInfo, pLineCallInfo->dwNeededSize, LMEM_MOVEABLE);
pLineCallInfo->dwTotalSize = pLineCallInfo->dwNeededSize;
continue;
}
return TRUE;
}
}
// Parse the command line and change the default settings.
BOOL SetupEnvironment (int argc, LPTSTR argv[])
{
int i = 0, j;
TCHAR chFlag;
BOOL bMaxCallsSet = FALSE;
if (argc == 1)
{
PrintHelp();
return FALSE;
}
while (++i < argc)
{
j = 0;
if ((argv[i][j] == TEXT('/')) || (argv[i][j] == TEXT('-')) || (argv[i][j] == TEXT('+')))
j = 1;
chFlag = argv[i][j++];
if (argv[i][j] == TEXT(':'))
j++;
if (argv[i][j] == TEXT('\"'))
j++;
switch(tolower(chFlag))
{
case TEXT('?'):
MyPrintf(TEXT("Runs a test of the TAPI and WAVE system.\r\n"));
PrintHelp();
return FALSE;
case TEXT('l'):
dwDeviceID = _ttoi(&argv[i][j]);
if (!dwDeviceID)
{
MyPrintf(TEXT("Invalid [L]ine selection.\r\n"));
bCommandLineError = TRUE;
PrintHelp();
return FALSE;
}
break;
case TEXT('i'):
dwAddressID = _ttoi(&argv[i][j]);
if (!dwAddressID)
{
MyPrintf(TEXT("Invalid Address [I]D selection.\r\n"));
bCommandLineError = TRUE;
PrintHelp();
return FALSE;
}
break;
case TEXT('x'):
bLogExtraInfo = TRUE;
MyPrintf(TEXT("Extra call and data flow information will be displayed.\r\n"));
break;
case TEXT('a'):
bAnswer = TRUE;
break;
case TEXT('d'):
lstrcpy(szPhoneNumber, &argv[i][j]);
bAnswer = FALSE;
break;
case TEXT('f'):
lstrcpy(szFileName, &argv[i][j]);
break;
case TEXT('w'):
WaveInID = _ttoi(&argv[i][j]);
if (!WaveInID && (argv[i][j] != TEXT('0')))
{
MyPrintf(TEXT("Invalid [W]ave ID selection.\r\n"));
bCommandLineError = TRUE;
PrintHelp();
return FALSE;
}
if (WaveInID == 99)
{
MyPrintf(TEXT("Using WAVE_MAPPER.\r\n"));
WaveOutID = WaveInID = WAVE_MAPPER;
dwWaveMapped = 0;
}
else
WaveOutID = WaveInID;
bWaveLocal = TRUE;
break;
case TEXT('z'):
dwWaveMapped = 0;
break;
case TEXT('p'):
dwTimeToWaitBeforePlaying = _ttoi(&argv[i][j]);
break;
}
}
return TRUE;
}
// Print the help screen.
void PrintHelp()
{
MyPrintf(
TEXT("\r\n")
TEXT("%s [options]\r\n")
TEXT("\r\n")
TEXT("[Wave File] File to record to or play back.\r\n")
TEXT("\r\n")
TEXT("Options:\r\n")
TEXT("/L:# Line Device ID (dwDeviceID) to use.\r\n")
//TEXT("/I:# Line Address ID to use. Ignored when AutoAnswer is set.\r\n")
TEXT("/X Display extra call and data flow information.\r\n")
TEXT("/F:file Use file as the wave file\r\n")
TEXT("/D:[number] Dial number\r\n")
TEXT("/A Answer a call\r\n")
TEXT("\r\n")
//TEXT("/W:# Specify a wave ID to use for both IN and OUT.\r\n")
//TEXT(" If this flag is used, all TAPI is bypassed.\r\n")
//TEXT(" A wave ID of 99 means to use WAVE_MAPPER.\r\n")
TEXT("/Z Don't use WAVE_MAPPED\r\n")
TEXT("/P:# Pause # milliseconds after dialing, before playing.\r\n")
TEXT("\r\n")
TEXT("defaults: /L:%lu /I:%lu\n /F:%s /A\r\n"),
TEXT("\r\n"),
szAppName, dwDeviceID, dwAddressID, szFileName);
}
// Break Hander: Try and terminate gracefully.
// Note that ^Break can call lineShutdown *DURING* a lineMakeCall. Very
// unpredictable results; TAPI is not re-entrant and is *usually* protected by
// the Win16 Mutex. However, a Break Handler is a special case.
BOOL BreakHandlerRoutine(DWORD dwCtrlType)
{
// ^BREAK will always break.
// Use with care; it can mess up TAPI if in the middle of a TAPI API.
if (dwCtrlType == CTRL_BREAK_EVENT)
{
MyPrintf(TEXT("\r\n^[Break] terminated! TAPI may not be left in a usable state.\r\n\r\n"));
lineShutdown(hLineApp);
LocalFree(pLineDevStatus);
LocalFree(pLineCallInfo);
LocalFree(pLineCallParams);
ExitProcess( 1 );
}
else
{
if (hCall)
MyPrintf(TEXT("\r\n^C Dropping call in progress.\r\n\r\n"));
else
MyPrintf(TEXT("\r\n^C stopped.\r\n\r\n"));
StopEverything();
}
return TRUE;
}
BOOL PumpMessages(BOOL bWaitForMessage)
{
static MSG msg;
if (bReadyToEnd)
return FALSE;
if (bWaitForMessage)
{
if (!GetMessage(&msg, NULL, 0 , 0 ))
return FALSE;
}
else
if (!PeekMessage(&msg, NULL, 0 , 0 , PM_REMOVE))
return TRUE;
else
if (msg.message == WM_QUIT)
return FALSE;
TranslateMessage(&msg);
DispatchMessage(&msg);
return TRUE;
}
void StopEverything()
{
bReadyToEnd = TRUE;
WaitForSingleObject(hWaveThread, INFINITE);
if (hCall && !bDropped)
{
dwLineDropAsyncID = lineDrop(hCall, NULL, 0 );
if (dwLineDropAsyncID < 0 )
{
MyPrintf(TEXT("lineDrop failed. Terminating\r\n"));
hCall = 0 ;
}
bDropped = TRUE;
}
// lets prime the pump with a message.
PostThreadMessage(dwThreadID, WM_USER, 0 , 0 );
}
// ---------------
typedef struct MYWAVEDATA_tag
{
struct MYWAVEDATA_tag * pNext;
WAVEHDR wavehdr;
BYTE data[ 1 ];
} MYWAVEDATA, *PMYWAVEDATA;
PrintWaveInfo(WaveInID, WaveOutID);
PMYWAVEDATA LoadWaveInfo(WAVEFORMATEX* pFormat);
LPTSTR FormatWaveOutError(MMRESULT mmResult);
LPTSTR FormatWaveInError(MMRESULT mmResult);
// CONNECTED! Now do data steam testing. Return FALSE to stop TAPI.
DWORD WINAPI WaveThread(LPVOID pVoid)
{
DWORD i;
HWAVEIN hWaveIn = NULL;
HWAVEOUT hWaveOut = NULL;
MMRESULT mmResult;
PMYWAVEDATA pWaveData, pWaveCurr;
BYTE WaveFormatBytes[ 4096 ];
PWAVEFORMATEX pFormat = (PWAVEFORMATEX) WaveFormatBytes;
// Get the wave devices to use
if (!bWaveLocal)
{
LONG lRet;
if (lRet = lineGetID( 0 , 0 , hCall, LINECALLSELECT_CALL, pVarString, TEXT("wave/in")))
{
MyPrintf(TEXT("lineGetID failed %s\r\n"), FormatTapiError(lRet));
return FALSE;
}
WaveInID = *(UINT *)((LPBYTE)pVarString + pVarString->dwStringOffset);
if (lRet = lineGetID( 0 , 0 , hCall, LINECALLSELECT_CALL, pVarString, TEXT("wave/out")))
{
MyPrintf(TEXT("lineGetID failed %s\r\n"), FormatTapiError(lRet));
return FALSE;
}
WaveOutID = *(UINT *)((LPBYTE)pVarString + pVarString->dwStringOffset);
}
MyPrintf(TEXT("Using WaveInID %lu and WaveOutID %lu\r\n"),
(DWORD) WaveInID, (DWORD) WaveOutID);
PrintWaveInfo(WaveInID, WaveOutID);
// Load the wave data from file. Yes, this assumes we are playing.
pFormat->cbSize = sizeof(WaveFormatBytes);
pWaveData = LoadWaveInfo(pFormat);
if (pWaveData == NULL)
return 0 ;
// Open a waveform output device.
mmResult = waveOutOpen(&hWaveOut, WaveOutID, pFormat, 0 , 0L, dwWaveMapped);
if (mmResult)
{
MyPrintf(TEXT("waveOutOpen returned %s\r\n"), FormatWaveOutError(mmResult));
goto end;
}
// First, sleep 5 seconds to give the other end time to answer.
// This is an important step as modems have no idea when the other end actually answered.
// A sleep isn't the best way to wait, but it works for now.
MyPrintf(TEXT("Waiting %lu milliseconds for other end to answer because modems don't know when a VOICE call has answered\r\n"),
dwTimeToWaitBeforePlaying);
for(i = dwTimeToWaitBeforePlaying; i; i-=100)
{
if (!(i%1000))
MyPrintf(TEXT("%lu\r\n"), i/1000);
if (bReadyToEnd)
break;
Sleep(100);
}
// Now start playing, looping through the wave file repeatedly.
pWaveCurr = pWaveData;
while(!bReadyToEnd)
{
// If a buffer is queued, wait till its done.
if (pWaveCurr->wavehdr.dwFlags & WHDR_INQUEUE)
{
if (bLogExtraInfo)
MyPrintf(TEXT("Waiting for wave buffer to finish rendering\r\n"));
Sleep(250);
continue;
}
// If we've used it, we need to unprepare the header
if (pWaveCurr->wavehdr.dwFlags & WHDR_DONE )
{
if(mmResult = waveOutUnprepareHeader(hWaveOut, &pWaveCurr->wavehdr, sizeof(WAVEHDR)))
{
MyPrintf(TEXT("waveOutUnprepareHeader returned %s\r\n"), FormatWaveOutError(mmResult));
break;
}
}
pWaveCurr->wavehdr.dwFlags = 0;
// prepare each WAVEHDR
if(mmResult = waveOutPrepareHeader(hWaveOut, &pWaveCurr->wavehdr, sizeof(WAVEHDR)))
{
MyPrintf(TEXT("waveOutPrepareHeader returned %s\r\n"), FormatWaveOutError(mmResult));
break;
}
// Send to the output device.
if (mmResult = waveOutWrite(hWaveOut, &pWaveCurr->wavehdr, sizeof(WAVEHDR)))
{
MyPrintf(TEXT("waveOutWrite returned %s\r\n"), FormatWaveOutError(mmResult));
break;
}
if (!(pWaveCurr->wavehdr.dwFlags & WHDR_INQUEUE))
{
MyPrintf(TEXT("waveOutWrite did not succesfully queue."));
break;
}
if (bLogExtraInfo)
MyPrintf(TEXT("Wave buffer successfully queued.\r\n"));
pWaveCurr = pWaveCurr->pNext;
if (!pWaveCurr)
pWaveCurr = pWaveData;
}
end:
if (hWaveOut)
{
if (mmResult = waveOutReset(hWaveOut))
MyPrintf(TEXT("waveOutReset returned %s\r\n"), FormatWaveOutError(mmResult));
Sleep(500); // Give it 1/2 sec to actually reset.
if(mmResult = waveOutClose(hWaveOut))
MyPrintf(TEXT("waveOutClose returned %s\r\n"), FormatWaveOutError(mmResult));
Sleep(500); // Give it 1/2 sec to clean up.
hWaveOut = 0;
}
while(pWaveData)
{
pWaveCurr = pWaveData;
pWaveData = pWaveCurr->pNext;
LocalFree(pWaveCurr);
}
return FALSE;
}
PrintWaveInfo(WaveInID, WaveOutID)
{
WAVEINCAPS in;
WAVEOUTCAPS out;
if (MMSYSERR_NOERROR == waveInGetDevCaps(WaveInID, &in, sizeof(in)))
{
MyPrintf(
TEXT("WaveInDevCaps:\r\n")
TEXT(" wMid: 0x%04X\r\n")
TEXT(" wPid: 0x%04X\r\n")
TEXT(" vDriverVersion: 0x%04X\r\n")
TEXT(" pszPname: %s\r\n")
TEXT(" dwFormats: 0x%08X\r\n")
TEXT(" dwChannels: 0x%04X\r\n")
,
in.wMid, in.wPid, in.vDriverVersion, in.szPname, in.dwFormats, in.wChannels);
}
else
MyPrintf(TEXT("waveInGetDevCaps failed.\r\n"));
if (MMSYSERR_NOERROR == waveOutGetDevCaps(WaveOutID, &out, sizeof(out)))
{
MyPrintf(
TEXT("WaveOutDevCaps:\r\n")
TEXT(" wMid: 0x%04X\r\n")
TEXT(" wPid: 0x%04X\r\n")
TEXT(" vDriverVersion: 0x%04X\r\n")
TEXT(" pszPname: %s\r\n")
TEXT(" dwFormats: 0x%08X\r\n")
TEXT(" dwChannels: 0x%04X\r\n")
,
out.wMid, out.wPid, out.vDriverVersion, out.szPname, out.dwFormats, out.wChannels);
}
else
MyPrintf(TEXT("waveInGetDevCaps failed.\r\n"));
// TODO Find out if its capable of simultaneous recording and playing
}
PMYWAVEDATA LoadWaveInfo(WAVEFORMATEX* pFormat)
{
MMRESULT mmResult;
HMMIO hmmio = {0};
MMCKINFO mmckinfoParent;
MMCKINFO mmckinfoSubchunk;
DWORD dwFmtSize;
DWORD dwChunkSize;
PMYWAVEDATA pWaveHead = NULL,
pWaveCurr = NULL,
pWavePrev = NULL;
BOOL bLooping = TRUE;
DWORD dwBuffers = 0;
// Open the given file for reading using buffered I/O.
if(!(hmmio = mmioOpen(szFileName, NULL, MMIO_READ | MMIO_ALLOCBUF)))
{
MyPrintf(TEXT("mmioOpen failed to open file.\r\n"));
return NULL;
}
// Locate a 'RIFF' chunk with a 'WAVE' form type to make sure it's a WAVE file.
mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
if (mmResult = mmioDescend(hmmio, &mmckinfoParent, NULL, MMIO_FINDRIFF))
{
MyPrintf(TEXT("mmioDescend RIFF WAVE returned %s\r\n"), FormatWaveOutError(mmResult));
goto end;
}
// Now, find the format chunk (form type 'fmt '). It should be
// a subchunk of the 'RIFF' parent chunk.
mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,
MMIO_FINDCHUNK))
{
MyPrintf(TEXT("Wave file corrupt.\r\n"));
goto end;
}
// Get the size of the format chunk, allocate and lock memory for it.
dwFmtSize = mmckinfoSubchunk.cksize;
if (pFormat->cbSize < dwFmtSize)
{
MyPrintf(TEXT("Format chunk not big enough.\r\n"));
goto end;
}
// Read the format chunk.
if (mmioRead(hmmio, (HPSTR) pFormat, dwFmtSize) != (LONG) dwFmtSize)
{
MyPrintf(TEXT("mmioRead: failed to read FMT chunk.\r\n"));
goto end;
}
MyPrintf(TEXT("wFormatTag = %lu\r\n"), (DWORD) pFormat->wFormatTag);
MyPrintf(TEXT("nChannels = %lu\r\n"), (DWORD) pFormat->nChannels );
MyPrintf(TEXT("nSamplesPerSec = %lu\r\n"), (DWORD) pFormat->nSamplesPerSec);
MyPrintf(TEXT("nAvgBytesPerSec = %lu\r\n"), (DWORD) pFormat->nAvgBytesPerSec);
MyPrintf(TEXT("nBlockAlign = %lu\r\n"), (DWORD) pFormat->nBlockAlign);
MyPrintf(TEXT("wBitsPerSample = %lu\r\n"), (DWORD) pFormat->wBitsPerSample);
MyPrintf(TEXT("cbSize = %lu\r\n"), (DWORD) pFormat->cbSize);
// Ascend out of the format subchunk.
mmioAscend(hmmio, &mmckinfoSubchunk, 0);
// Find the data subchunk.
mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,
MMIO_FINDCHUNK))
{
MyPrintf(TEXT("mmioDescend: No DATA chunk.\r\n"));
goto end;
}
// Get the size of the data subchunk.
if (mmckinfoSubchunk.cksize == 0L)
{
MyPrintf(TEXT("Data chunk actually has no data.\r\n"));
goto end;
}
MyPrintf(TEXT("Size of data is %lu\r\n"),mmckinfoSubchunk.cksize);
// Now read the data and allocate MYWAVEDATA buffers
dwChunkSize = (pFormat->nAvgBytesPerSec/4);
dwChunkSize -= dwChunkSize % pFormat->nBlockAlign;
if (dwChunkSize < pFormat->nBlockAlign)
{
MyPrintf(TEXT("Couldn't calculate a good block size\r\n"));
goto end;
}
while(bLooping)
{
LONG lRead;
pWaveCurr = (PMYWAVEDATA) LocalAlloc(LPTR, dwChunkSize + sizeof(MYWAVEDATA));
pWaveCurr->wavehdr.lpData = pWaveCurr->data;
if (pWaveHead == NULL)
pWaveHead = pWaveCurr;
// Read the waveform data subchunk.
lRead = mmioRead(hmmio, pWaveCurr->data, dwChunkSize);
pWaveCurr->wavehdr.dwBufferLength = lRead;
if (lRead == -1)
{
MyPrintf(TEXT("Error reading from file.\r\n"));
pWaveHead = NULL; // Leak leak
goto end;
}
if (lRead == 0)
{
LocalFree(pWaveCurr);
break;
}
if ((DWORD)lRead != dwChunkSize)
{
bLooping = FALSE;
}
if (pWavePrev != NULL)
pWavePrev->pNext = pWaveCurr;
pWavePrev = pWaveCurr;
dwBuffers++;
}
MyPrintf(TEXT("There were %lu buffers read from %s\r\n"), dwBuffers, szFileName);
end:
mmioClose(hmmio, 0);
return pWaveHead;
}
/*=== Printing routines =============================================================*/
/*
This is easily used to do error messages like this:
MyPrintf(TEXT("API blah failed with error: %s\r\n"), FormatError(GetLastError()));
*/
#define MAX_PRINT_STRING 1024
//#define MSG_BOX_PRINT
#ifdef _DEBUG
#define MSG_DEBUG_PRINT
#endif
#define MSG_CONSOLE_PRINT
//#define MSG_FILE_PRINT
#ifdef MSG_FILE_PRINT
TCHAR szFilePrint[MAX_PATH] = TEXT(".\\out.txt");
BOOL bZeroFile = FALSE;
#endif
LPTSTR FormatWaveError(MMRESULT mmrError, MMRESULT (WINAPI *pfn) (MMRESULT, LPTSTR, UINT), LPTSTR szErr)
{
_declspec(thread) static TCHAR szOutput[MAX_PRINT_STRING];
MMRESULT mmResult2;
mmResult2 = pfn(mmrError, szOutput, MAX_PRINT_STRING);
if (mmResult2 != MMSYSERR_NOERROR)
{
TCHAR szTmp[256];
MMRESULT mmResult3;
mmResult3 = pfn(mmResult2, szOutput, 256);
if (mmResult2 != MMSYSERR_NOERROR)
wsprintf(szOutput, TEXT("%s returned an %lu on %lu"), szErr, mmResult2, mmrError);
else
wsprintf(szOutput, TEXT("%s on error %lu"), szTmp, mmrError);
}
return szOutput;
}
LPTSTR FormatWaveOutError(MMRESULT mmrError)
{
return FormatWaveError(mmrError, waveOutGetErrorText, TEXT("waveOutGetError"));
}
LPTSTR FormatWaveInError(MMRESULT mmrError)
{
return FormatWaveError(mmrError, waveInGetErrorText, TEXT("waveInGetError"));
}
// Turn a TAPI Line error into a printable string.
LPTSTR FormatTapiError (long lError)
{
static LPTSTR pszLineError[] =
{
TEXT("LINEERR No Error"),
TEXT("LINEERR_ALLOCATED"),
TEXT("LINEERR_BADDEVICEID"),
TEXT("LINEERR_BEARERMODEUNAVAIL"),
TEXT("LINEERR Unused constant, ERROR!!"),
TEXT("LINEERR_CALLUNAVAIL"),
TEXT("LINEERR_COMPLETIONOVERRUN"),
TEXT("LINEERR_CONFERENCEFULL"),
TEXT("LINEERR_DIALBILLING"),
TEXT("LINEERR_DIALDIALTONE"),
TEXT("LINEERR_DIALPROMPT"),
TEXT("LINEERR_DIALQUIET"),
TEXT("LINEERR_INCOMPATIBLEAPIVERSION"),
TEXT("LINEERR_INCOMPATIBLEEXTVERSION"),
TEXT("LINEERR_INIFILECORRUPT"),
TEXT("LINEERR_INUSE"),
TEXT("LINEERR_INVALADDRESS"),
TEXT("LINEERR_INVALADDRESSID"),
TEXT("LINEERR_INVALADDRESSMODE"),
TEXT("LINEERR_INVALADDRESSSTATE"),
TEXT("LINEERR_INVALAPPHANDLE"),
TEXT("LINEERR_INVALAPPNAME"),
TEXT("LINEERR_INVALBEARERMODE"),
TEXT("LINEERR_INVALCALLCOMPLMODE"),
TEXT("LINEERR_INVALCALLHANDLE"),
TEXT("LINEERR_INVALCALLPARAMS"),
TEXT("LINEERR_INVALCALLPRIVILEGE"),
TEXT("LINEERR_INVALCALLSELECT"),
TEXT("LINEERR_INVALCALLSTATE"),
TEXT("LINEERR_INVALCALLSTATELIST"),
TEXT("LINEERR_INVALCARD"),
TEXT("LINEERR_INVALCOMPLETIONID"),
TEXT("LINEERR_INVALCONFCALLHANDLE"),
TEXT("LINEERR_INVALCONSULTCALLHANDLE"),
TEXT("LINEERR_INVALCOUNTRYCODE"),
TEXT("LINEERR_INVALDEVICECLASS"),
TEXT("LINEERR_INVALDEVICEHANDLE"),
TEXT("LINEERR_INVALDIALPARAMS"),
TEXT("LINEERR_INVALDIGITLIST"),
TEXT("LINEERR_INVALDIGITMODE"),
TEXT("LINEERR_INVALDIGITS"),
TEXT("LINEERR_INVALEXTVERSION"),
TEXT("LINEERR_INVALGROUPID"),
TEXT("LINEERR_INVALLINEHANDLE"),
TEXT("LINEERR_INVALLINESTATE"),
TEXT("LINEERR_INVALLOCATION"),
TEXT("LINEERR_INVALMEDIALIST"),
TEXT("LINEERR_INVALMEDIAMODE"),
TEXT("LINEERR_INVALMESSAGEID"),
TEXT("LINEERR Unused constant, ERROR!!"),
TEXT("LINEERR_INVALPARAM"),
TEXT("LINEERR_INVALPARKID"),
TEXT("LINEERR_INVALPARKMODE"),
TEXT("LINEERR_INVALPOINTER"),
TEXT("LINEERR_INVALPRIVSELECT"),
TEXT("LINEERR_INVALRATE"),
TEXT("LINEERR_INVALREQUESTMODE"),
TEXT("LINEERR_INVALTERMINALID"),
TEXT("LINEERR_INVALTERMINALMODE"),
TEXT("LINEERR_INVALTIMEOUT"),
TEXT("LINEERR_INVALTONE"),
TEXT("LINEERR_INVALTONELIST"),
TEXT("LINEERR_INVALTONEMODE"),
TEXT("LINEERR_INVALTRANSFERMODE"),
TEXT("LINEERR_LINEMAPPERFAILED"),
TEXT("LINEERR_NOCONFERENCE"),
TEXT("LINEERR_NODEVICE"),
TEXT("LINEERR_NODRIVER"),
TEXT("LINEERR_NOMEM"),
TEXT("LINEERR_NOREQUEST"),
TEXT("LINEERR_NOTOWNER"),
TEXT("LINEERR_NOTREGISTERED"),
TEXT("LINEERR_OPERATIONFAILED"),
TEXT("LINEERR_OPERATIONUNAVAIL"),
TEXT("LINEERR_RATEUNAVAIL"),
TEXT("LINEERR_RESOURCEUNAVAIL"),
TEXT("LINEERR_REQUESTOVERRUN"),
TEXT("LINEERR_STRUCTURETOOSMALL"),
TEXT("LINEERR_TARGETNOTFOUND"),
TEXT("LINEERR_TARGETSELF"),
TEXT("LINEERR_UNINITIALIZED"),
TEXT("LINEERR_USERUSERINFOTOOBIG"),
TEXT("LINEERR_REINIT"),
TEXT("LINEERR_ADDRESSBLOCKED"),
TEXT("LINEERR_BILLINGREJECTED"),
TEXT("LINEERR_INVALFEATURE"),
TEXT("LINEERR_NOMULTIPLEINSTANCE")
};
_declspec(thread) static TCHAR szError[512];
DWORD dwError;
HMODULE hTapiUIMod = GetModuleHandle(TEXT("TAPIUI.DLL"));
if (hTapiUIMod)
{
dwError = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE,
(LPCVOID)hTapiUIMod, TAPIERROR_FORMATMESSAGE(lError),
0, szError, sizeof(szError)/sizeof(TCHAR), NULL);
if (dwError)
return szError;
}
// Strip off the high bit to make the error code positive.
dwError = (DWORD)lError & 0x7FFFFFFF;
if ((lError > 0) || (dwError > sizeof(pszLineError)/sizeof(pszLineError[0])))
{
wsprintf(szError, TEXT("Unknown TAPI error code: 0x%lx"), lError);
return szError;
}
return pszLineError[dwError];
}
void __cdecl MyPrintf(LPCTSTR pszFormat, ...)
{
_declspec(thread) static TCHAR szOutput[MAX_PRINT_STRING]; // max printable string length
va_list v1;
DWORD dwSize;
va_start(v1, pszFormat);
dwSize = wvsprintf(szOutput, pszFormat, v1);
#ifdef MSG_DEBUG_PRINT
OutputDebugString(szOutput);
#endif
#ifdef MSG_CONSOLE_PRINT
_tprintf(szOutput);
#endif
#ifdef MSG_BOX_PRINT
MessageBox(NULL, szOutput,TEXT("MyPrintf Output"), MB_OK);
#endif
#ifdef MSG_FILE_PRINT
{
static HANDLE hFile = NULL;
DWORD dwNumWritten;
if (hFile == NULL)
{
hFile = CreateFile(szFilePrint, GENERIC_WRITE, FILE_SHARE_READ, NULL,
bZeroFile ? CREATE_ALWAYS : OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
MyPrintf(TEXT("CreateFile output log file %s failed with %s\r\n"), szFilePrint, FormatError(GetLastError()));
else
SetFilePointer(hFile, 0, NULL, FILE_END);
}
if (hFile != INVALID_HANDLE_VALUE)
{
OVERLAPPED ol = {0};
LockFileEx(hFile, 0, 0, 1, 0, &ol);
WriteFile(hFile, szOutput, dwSize*sizeof(TCHAR), &dwNumWritten, NULL);
UnlockFileEx(hFile, 0, 1, 0, &ol);
}
}
#endif
}
LPCTSTR FormatError(DWORD dwError)
{
_declspec(thread) static TCHAR szBuff[MAX_PRINT_STRING];
return FormatErrorBuffer(dwError, szBuff, MAX_PRINT_STRING);
}
LPCTSTR FormatErrorBuffer(DWORD dwError, LPTSTR pszBuff, DWORD dwNumChars)
{
DWORD dwRetFM = 0;
dwRetFM = wsprintf(pszBuff, TEXT("%lu - "), dwError);
dwRetFM = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 0,
&pszBuff[dwRetFM], dwNumChars - dwRetFM, NULL);
if (dwRetFM == 0)
{
wsprintf(pszBuff, TEXT("FormatMessage failed on %lu with %lu"),
dwError, GetLastError());
}
return pszBuff;
}