现在的位置: 首页 > 综合 > 正文

TAPI 程式設計(Using Delphi As Example)

2013年07月06日 ⁄ 综合 ⁄ 共 4835字 ⁄ 字号 评论关闭

TAPI 程式設計(Using Delphi As Example)
 Windows 下寫作 CTI 的標準 TAPI(Telephony API) 是由 Intel 與 Microsoft 針對電腦如何控制電話通訊而制定出來的規格 ,各版次的 Windows 基本上支援的最高 TAPI 版本不一 ,詳列如下表 :
Windows 版本  TAPI C/C++ API 16bit  TAPI C/C++ API 32bit  TAPI COM/COM+ API 
Windows 3.1  1.3(TAPI 1.0)  N/A  N/A 
Windows 95/OSR2 Windows 98  1.4(TAPI 1.4)  2.0 for 32 bit.(95 need to install TAPI 2.x to support TAPI 2.0)  N/A 
Windows NT 4.0   2.1  N/A 
Windows 2000   2.2  3.0 
Windows XP   2.3  3.1 
以下的 TAPI 程式寫作概念則集中於 TAPI 2.x 的部份 ,3.x 部份為 COM/COM+ ,較為複雜 .

因為採用的工具是 Delphi 之故 ,Delphi 本身並沒有內建的 TAPI 函數的宣告檔 ,在此 ,我採用 OpenSource 的 Project Jedi 的 TAPI 宣告檔 ,作為這次程式設計的 Delphi 宣告檔 .詳細的下載路徑是 ftp://delphi-jedi.org/api/TAPI.zip

如果你有其他想撰寫的 Win32 API 在原始的 Delphi 中沒有支援 ,你也可以到 Project Jedi 看看有沒有熱心的程式設計師 ,已經幫你轉換好宣告檔 .網址是 http://www.delphi-jedi.org/Jedi:HOMEPAGE:0 ,不過內文中為了方便介紹 TAPI 的函數 ,所有的函數宣告(除了 Callback ) ,仍然依照 Microsoft 在 TAPI32.h 中的宣告形式 ,而沒有改為 Delphi 所使用的形式 .

Assisted Telephony Function

在呼叫 TAPI 程式上 ,原始上可分為簡化的 Assisted Telephony Function(以下簡稱ATF) 及比較完整的正式 TAPI 程式 .

ATF可以用在如 PIM 軟體等 ,單純只想要做一個簡單的撥號動作 ,在 Win32 系統內 ,只有兩個函數屬於 ATF ,

tapiRequestMakeCall 要求系統撥出一個電話號碼 .

tapiGetLocationInfo 取得目前的國別及程式設定

在 Windows 3.1 有另外兩個 ATF 函數 ,目前在 Win32 的環境下已不再支援

tapiRequestMediaCall 要求撥出一個多媒體的電話

tapiRequestDrop 要求一個撥出程序掛斷

LONG tapiRequestMakeCall(
    LPCSTR lpszDestAddress,
    LPCSTR lpszAppName,
    LPCSTR lpszCalledParty,
    LPCSTR lpszComment
   );

當你呼叫tapiRequestMakeCall這個 API 時 ,將會呼叫電腦中負責撥號的程式 ,開始進行撥號的動作 ,以 Windows 95/98/ME/NT 4/2K/XP 為例 ,就是由電話撥號員(Phone Dialer)來處理整個外撥的動作 .

LONG tapiGetLocationInfo(
    LPCSTR lpszCountryCode,  // 8-byte buffer
    LPCSTR lpszCityCode     // 8-byte buffer
   );

tapiGetLocationInfo 則是可以很簡單的取得目前 Windows 對於電話的國別設定及程式的區域號碼 ,這對於會移動於不同的話區的筆記型電腦是比較重要的 ,因為經由設定 ,TAPI 可以判斷 ,應該要以什麼樣的方式來撥電話 .
Generic Telephony Function

透過 ATF ,可以進行很簡單的電腦撥電話的動作 ,但是對一般的電話應答 ,或電話轉接的系統來說 ,這些是不夠的 .

一般的 TAPI 程式 ,不會單純的透過 ATF 來控制電話的線路 ,而通常會使用較複雜的方式來進行 .我們在呼叫任何 TAPI 函數前 ,要先對 TAPI 進行啟始化的動作 .我們要對 TAPI 指出一個 Callback 函數 ,供 TAPI 在回送一些事件時使用 ,Callback 函數的 C/C++ 原形為

VOID FAR PASCAL lineCallbackFunc(
    DWORD hDevice,
    DWORD dwMsg,
    DWORD dwCallbackInstance,
    DWORD dwParam1,
    DWORD dwParam2,
    DWORD dwParam3
  );

Jedi 中轉譯出來的原形為 .

  procedure lineCallBack(hDevice, dwMessage: DWORD; dwInstance, dwParam1,
    dwParam2, dwParam3: DWORD_PTR ) stdcall;

在寫好一個簡單的 lineCallBack 函數之後 ,我們便可以呼叫啟始 TAPI 的函數 .
TAPI 1.3/1.4 (一般用於Windows 95/98 的程式)使用的函數為 lineInitialize

LONG lineInitialize(
    LPHLINEAPP lphLineApp,
    HINSTANCE hInstance,
    LINECALLBACK lpfnCallback,
    LPCSTR lpszAppName,
    LPDWORD lpdwNumDevs
   );

而在 TAPI 2.x 以後都必須使用 lineInitializeEx

LONG lineInitializeEx(
    LPHLINEAPP lphLineApp,
    HINSTANCE hInstance,
    LINECALLBACK lpfnCallback,
    LPCSTR lpszFriendlyAppName,
    LPDWORD lpdwNumDevs,
    LPDWORD lpdwAPIVersion,
    LPLINEINITIALIZEEXPARAMS lpLineInitializeExParams
   );

在 TAPI 啟始化的動作完成後 ,所有的事件都會往 CallBack 函數送 ,而我們會取得 ,目前總和可用的線數 ,及 TAPI Application 的編號(lphLineApp) .
進行完 TAPI 的啟始動作後 ,所有的 TAPI 程式 ,都要經過一個版本協調(Negotiation)的動作 .基本上程式可能支援 TAPI 1.3/1.4/2.0 , 原始Windows 95 支援 TAPI 1.3/1.4 , 如果使用的驅動程式 TSP (Telephony Service Provider) 僅支援 TAPI 1.3 ,那最後協調出來的版本就是 1.3 .

由於每一條線路的驅動程式版本可能是由不同廠商提供 ,所以實務上每個線路可供操作的最高版本未必相同 .

舉我電腦的 Windows 2000sp2 Pro + ADmore Talks 4G 為例 ,回報的版本如下 .

Get Device Caps:0 VERSION:00010003 LineName:RAS VPN 線路 0
Get Device Caps:1 VERSION:00020002 LineName:WAN 迷你連接埠 (L2TP)
Get Device Caps:2 VERSION:00020002 LineName:LPT1T
Get Device Caps:3 VERSION:00020002 LineName:IPCONF 線路
Get Device Caps:4 VERSION:00020002 LineName:H323 線路
Get Device Caps:5 VERSION:00020000 LineName:Talks Plus Line1
Get Device Caps:6 VERSION:00020000 LineName:Talks Plus Line2
Get Device Caps:7 VERSION:00020000 LineName:Talks Plus Line3
Get Device Caps:8 VERSION:00020000 LineName:Talks Plus Line4
版本的讀法為 00020000 為 2.0 版 , 00020001 為 2.1 版 ,同理 00010003 為 1.3 版 ,在知道每條線路所支援的最高TAPI 版本時 ,我們就可以依照該版本的特性 ,決定可以使用的 TAPI 函數及可以應用的類別型態 .

知道線路的支援 TAPI 版本後 .如果想知道線路的特性及線路名稱 ,提供的廠商 ,則必需呼叫 lineGetDevCaps .在呼叫 lineGetDevCaps ,這邊先介紹一個TAPI 很多函數會使用的一個資料結構 VLS (Variable Length Structure) , 在 TAPI 裡由於某些時候需要傳回一些文字的資訊 ,但是長度並不一定是多少 ,所以 TAPI採用一個動態操作資料結構的方式來管理動態長度的字串如何置放於一個固定的空間中 .所以某些函數會採用VLS的方式來傳進緩衝區的大小 ,再由底層的TSP決定如何去置放整個字串的空間 .譬如我們要取 PlineDevCaps 裡的 LineName

位移 屬性 值
0 … .
 dwLineNameSize 18
 dwLineNameOffset 292
 … 
292 RAS VPN 線路 0 
那我們就可以透過位移 ,取得這個位移的字串 ,以 Delphi 為例 .

procedure TfrmMain.GetDeviceCaps(nNumber: Integer);
const
   STR_SIZE = 1023;
var
   LDC:PLineDevCaps;
   Buffer:array[0..STR_SIZE] of char ;
   iCounter :integer;
   StrPointer:PChar;
   Buffer2:array[0..STR_SIZE] of char ;
Begin
   // 清除整個 buffer 的資料
   for iCounter:=0 to STR_SIZE do
      Buffer[iCounter] := char(0 );

   // 將兩個指標起點對齊
   LDC := @Buffer ;
  
   // 填入 Buffer 的 Size .
   LDC.dwTotalSize := STR_SIZE ;
   LogString( 'Get Device Caps:' + IntToStr( nNumber ) ) ;
   nReturn :=lineGetDevCaps( lineApp , nNumber , tapiVersion , 0 , LDC );

   // function Success
   if ( nReturn = 0 ) then
   begin

      StrPointer := @Buffer[ LDC.dwLineNameOffset ];
      StrCopy( Buffer2 , 'LineName:' );
      StrCat( Buffer2 , StrPointer );
      LogString( Buffer2 );

   end
   else
   begin
      LogString( 'Line Get Device Caps Error:' +LineErrorStateToStr( nReturn) );
   end;
   LogString( '' );
end;

C/C++ 也可以以類似的方式去取得變動長度的資料 ,在整個 TAPI 2.x 中 ,這類的 VLS 函數 ,通常是在整個回傳資料型態中 ,會去訂定整個緩衝區的大小 ,填入 dwTotalSize , 及必要屬性後 ,TAPI 就會準備好回傳的資料 .
在使用 TAPI 時 ,我這邊準備兩個範例一個是外撥 ,在使用 lineOpen , 開啟一條線路後 ,使用 lineMakeCall 去撥出電話 .在接受撥入時 ,則是稍稍修改 lineOpen 的參數 ,使用監控的方式 ,來準備接聽 .lineSetStatusMessage 則是在通知想監控的 Message (目前全監控 ) ,其他的動作則集中於 CallBack 函數中處理 .
 

抱歉!评论已关闭.