Feb
05

Отправка USSD-запроса в Windows Mobile

В этот раз будем учиться делать странное, а именно отправлять USSD-запрос оператору мобильной связи с помощью TAPI в Windows Mobile.

USSD (Unstructured Supplementary Service Data) — стандартный сервис в сетях GSM, позволяющий организовать интерактивное взаимодействие между абонентом сети и сервисным приложением в режиме передачи коротких сообщений. – Wikipedia

На самом деле USSD-запросы это то, чем каждый из нас пользуется довольно часто, например, при пополнении счета или при запросе состояния счета у оператора мобильной связи.

Примеры USSD-запросов:

  • *101#
  • *100*111122223333444455#

Вот именно такой финт ушами мы и будем пытаться реализовать с помощью программных средств.

Долгих вступлений не будет, а будет сразу практика с простеньким примером. Рассмотрим простейшее приложение, которое отправляет запрос состояния счета мобильному опреатору.

Для начала нам надо инициализировать TAPI.

#include 
#include 
#include 
#include 

#define EXT_API_LOW_VERSION     0x00010000
#define EXT_API_HIGH_VERSION    0x00010000

long InitializeTAPI(HLINEAPP & lineApp, DWORD & lowAPIVersion, DWORD & deviceCount)
{
        LINEINITIALIZEEXPARAMS  lineParams;
        ZeroMemory(&lineParams,sizeof(LINEINITIALIZEEXPARAMS));

        lineParams.dwTotalSize  = sizeof(LINEINITIALIZEEXPARAMS);
        lineParams.dwOptions    = LINEINITIALIZEEXOPTION_USEHIDDENWINDOW; 

        lowAPIVersion = TAPI_CURRENT_VERSION;

        return lineInitializeEx(&lineApp,
                (HINSTANCE)GetModuleHandle(NULL),
                LineCallback,
                L"wxUSSDRequest",
                &deviceCount,
                &lowAPIVersion,
                &lineParams);
}

LineCallback – это callback-функция, которая будет вызываться при наступлении событий TAPI.

void FAR PASCAL LineCallback(DWORD hDevice,
                                                         DWORD dwMsg,
                                                         DWORD dwCallbackInstance,
                                                         DWORD dwParam1,
                                                         DWORD dwParam2,
                                                         DWORD dwParam3)
{
        switch(dwMsg)
        {
        case LINE_DEVSPECIFIC:
                {
                        if(dwParam1 == LINE_USSD)
                        {
                                DWORD   dwMessageId     = dwParam2;
                        }

                        break;
                }
        case LINE_REPLY:
                {
                        if(dwParam2 == 0) return;
                        wprintf(L"Error: ");
                        switch(dwParam2)
                        {
                        case LINEERR_INVALLINEHANDLE:           wprintf(L"LINEERR_INVALLINEHANDLE");  break;
                        case LINEERR_NOMEM:                                     wprintf(L"LINEERR_NOMEM");                            break;
                        case LINEERR_OPERATIONUNAVAIL:          wprintf(L"LINEERR_OPERATIONUNAVAIL"); break;
                        case LINEERR_OPERATIONFAILED:           wprintf(L"LINEERR_OPERATIONFAILED");  break;
                        case LINEERR_RESOURCEUNAVAIL:           wprintf(L"LINEERR_RESOURCEUNAVAIL");  break;
                        case LINEERR_INVALPOINTER:                      wprintf(L"LINEERR_INVALPOINTER");             break;
                        case LINEERR_INVALPARAM:                        wprintf(L"LINEERR_INVALPARAM");                       break;
                        case LINEERR_UNINITIALIZED:                     wprintf(L"LINEERR_UNINITIALIZED");            break;
                        default:                                                        wprintf(L"Error: %x",dwParam2);                       break;
                        }
                        wprintf(L"\r\n");
                        break;
                }
        }
}

Затем нужно получить идентификатор телефонной линии. Сделать это можно с помощью функции lineGetDevCaps.

long GetCellularLineID(HLINEAPP lineApp,
                                           DWORD lowAPIVersion,
                                           DWORD deviceCount,
                                           DWORD & apiVersion)
{
        DWORD                           dwReturn                = 0xFFFFFFFF;
        long                            lResult                 = 0;
        LINEEXTENSIONID         sLineExt                = {0};
        LPLINEDEVCAPS           lpLineDevCaps   = NULL;
        BOOL                            bContinue               = TRUE;

        for(DWORD dwLine = 0; dwLine < deviceCount && bContinue; ++dwLine)
        {
                lResult         = lineNegotiateAPIVersion(lineApp,
                        dwLine,
                        lowAPIVersion,
                        TAPI_CURRENT_VERSION,
                        &apiVersion,
                        &sLineExt);

                if(0 == lResult)
                {
                        lpLineDevCaps   = (LPLINEDEVCAPS)LocalAlloc(LPTR,sizeof(LINEDEVCAPS));
                        lResult                 = LINEERR_STRUCTURETOOSMALL;

                        lpLineDevCaps->dwTotalSize   = sizeof(LINEDEVCAPS);
                        lpLineDevCaps->dwNeededSize  = sizeof(LINEDEVCAPS);

                        while(LINEERR_STRUCTURETOOSMALL == lResult)
                        {
                                lResult = lineGetDevCaps(lineApp,dwLine,TAPI_CURRENT_VERSION,0,lpLineDevCaps);

                                if(LINEERR_STRUCTURETOOSMALL == lResult || lpLineDevCaps->dwTotalSize < lpLineDevCaps->dwNeededSize)
                                {
                                        lpLineDevCaps   = (LPLINEDEVCAPS)LocalReAlloc(lpLineDevCaps,lpLineDevCaps->dwNeededSize,LMEM_MOVEABLE);
                                        lResult                 = LINEERR_STRUCTURETOOSMALL;

                                        lpLineDevCaps->dwTotalSize   = lpLineDevCaps->dwNeededSize;
                                }
                        }

                        if(0 == lResult)
                        {
                                TCHAR szName[512];

                                memcpy((PVOID)szName,(PVOID)((BYTE*)lpLineDevCaps + lpLineDevCaps ->dwLineNameOffset),
                                        lpLineDevCaps->dwLineNameSize);

                                szName[lpLineDevCaps->dwLineNameSize]        = 0;

                                if(_tcscmp(szName,CELLTSP_LINENAME_STRING) == 0)
                                {
                                        dwReturn        = dwLine;
                                        bContinue       = FALSE;
                                }
                        }

                        LocalFree((HLOCAL)lpLineDevCaps);
                }
        }

        return dwReturn;
}

Теперь нужно открыть телефонную линию для последующей работы с ней. Делается это с помощью функции lineOpen.

HLINE OpenTAPILine(HLINEAPP lineApp, DWORD cellularID, DWORD apiVersion)
{
        DWORD   dwMediaMode = LINEMEDIAMODE_INTERACTIVEVOICE;
        HLINE   hLine           = NULL;
        DWORD   extVersion      = 0;
        long    lReturn         = lineOpen(lineApp,
                cellularID,
                &hLine,
                TAPI_CURRENT_VERSION, 0,
                (DWORD)NULL,
                LINECALLPRIVILEGE_OWNER,
                dwMediaMode, 0);

        lReturn = ::lineNegotiateExtVersion(lineApp,
                cellularID,
                apiVersion,
                EXT_API_LOW_VERSION,
                EXT_API_HIGH_VERSION,
                &extVersion);

        return hLine;
}

После завершения работы с TAPI необходимо обязательно закрыть все хэндлы.

void ShutdownTAPI(HLINE cellularLine, HLINEAPP lineApp)
{
        if(cellularLine)
        {
                lineClose(cellularLine);
        }

        if(lineApp)
        {
                lineShutdown(lineApp);
        }
}

И вот мы добрались до самого интересного, а именно до того момента, когда нам все предыдущие части кода нужно использовать для достижения конечной цели – зохвата мира отправки запроса.

int wmain()
{
        HLINEAPP lineApp = NULL;
        DWORD lowAPIVersion = 0;
        DWORD apiVersion = 0;
        DWORD deviceCount = 0;
        DWORD cellularID = 0;
        HLINE cellularLine = NULL;
        TCHAR command[] = _T("*101#");
        int errorCode;

        do
        {
                if(InitializeTAPI(lineApp, lowAPIVersion, deviceCount) != 0)
                {
                        errorCode = 1;
                        break;
                }
                cellularID = GetCellularLineID(lineApp, lowAPIVersion, deviceCount, apiVersion);
                if(cellularID == 0xFFFFFFFF)
                {
                        errorCode = 2;
                        break;
                }
                cellularLine = OpenTAPILine(lineApp, cellularID, apiVersion);
                if(cellularLine == NULL)
                {
                        errorCode = 3;
                        break;
                }
                // Супер-строчка, которая отправляет USSD-запрос !!! Уиииии!
                if(lineSendUSSD(cellularLine, (const BYTE* const)command, sizeof(command), 0) < 0)
                {
                        errorCode = 4;
                        break;
                }
                return 0;
        }
        while(false);
        ShutdownTAPI(cellularLine, lineApp);
        return errorCode;
}

И вот, вызов lineSendUSSD – это то к чему мы так стремились. Функция принимает в качестве параметров хэндл телефонной линии, строку запроса и длину запроса. В случае успешного завершения работы возвращается нулевой результат. В случае ошибки возвращается отрицательное значение.

И в качестве дополнения ко всему сказанному выше, класс-обертка для wxWinCE:

wxUSSDRequest.h

#ifndef _WX_USSD_REQUEST_H
#define _WX_USSD_REQUEST_H

#include 
#include 
#include 
#include 

class wxUSSDRequest
{
        long InitializeTAPI(DWORD & lowAPIVersion,
                DWORD & deviceCount);
        long GetCellularLineID(DWORD lowAPIVersion,
                DWORD deviceCount,
                DWORD & apiVersion);
        HLINE OpenTAPILine(DWORD cellularID, DWORD apiVersion);

        void ShutdownTAPI();

        static void FAR PASCAL LineCallback(DWORD hDevice,
                DWORD dwMsg,
                DWORD dwCallbackInstance,
                DWORD dwParam1,
                DWORD dwParam2,
                DWORD dwParam3);
public:
        wxUSSDRequest();
        ~wxUSSDRequest();

        bool SendUSSDCommand(const TCHAR * command, DWORD length);
        int Init();
        bool IsOK();
private:
        HLINEAPP m_LineApp;
        HLINE m_CellularLine;
        bool m_IsOK;
};

#endif

wxUSSDRequest.cpp

#include "wxUSSDRequest.h"

#define EXT_API_LOW_VERSION     0x00010000
#define EXT_API_HIGH_VERSION    0x00010000

wxUSSDRequest::wxUSSDRequest()
: m_LineApp(NULL), m_CellularLine(NULL), m_IsOK(false)
{
        m_IsOK = (Init() == 0);
}

wxUSSDRequest::~wxUSSDRequest()
{
        ShutdownTAPI();
}

int wxUSSDRequest::Init()
{
        int result(0);
        do
        {
                DWORD lowAPIVersion(0);
                DWORD deviceCount(0);
                if(InitializeTAPI(lowAPIVersion, deviceCount) != 0)
                {
                        result = 1;
                        break;
                }
                DWORD apiVersion(0);
                DWORD cellularID = GetCellularLineID(lowAPIVersion, deviceCount, apiVersion);
                if(cellularID == 0xFFFFFFFF)
                {
                        result = 2;
                        break;
                }
                m_CellularLine = OpenTAPILine(cellularID, apiVersion);
                if(m_CellularLine == NULL)
                {
                        result = 3;
                        break;
                }
        }
        while(false);
        return result;
}

bool wxUSSDRequest::IsOK()
{
        return m_IsOK;
}

bool wxUSSDRequest::SendUSSDCommand(const TCHAR * command, DWORD length)
{
        if(lineSendUSSD(m_CellularLine, (const BYTE* const)command, length, 0) < 0)
        {
                return false;
        }
        return true;
}

long wxUSSDRequest::InitializeTAPI(DWORD & lowAPIVersion, DWORD & deviceCount)
{
        LINEINITIALIZEEXPARAMS  lineParams;
        ZeroMemory(&lineParams,sizeof(LINEINITIALIZEEXPARAMS));

        lineParams.dwTotalSize  = sizeof(LINEINITIALIZEEXPARAMS);
        lineParams.dwOptions    = LINEINITIALIZEEXOPTION_USEHIDDENWINDOW; 

        lowAPIVersion = TAPI_CURRENT_VERSION;

        return lineInitializeEx(&m_LineApp,
                (HINSTANCE)wxTheApp->GetInstance(),
                LineCallback,
                wxT("wxUSSDRequest"),
                &deviceCount,
                &lowAPIVersion,
                &lineParams);
}

void wxUSSDRequest::ShutdownTAPI()
{
        if(m_CellularLine)
        {
                lineClose(m_CellularLine);
        }

        if(m_LineApp)
        {
                lineShutdown(m_LineApp);
        }

        m_LineApp               = NULL;
        m_CellularLine  = NULL;
}

long wxUSSDRequest::GetCellularLineID(DWORD lowAPIVersion,
                                                                          DWORD deviceCount,
                                                                          DWORD & apiVersion)
{
        DWORD                           dwReturn                = 0xFFFFFFFF;
        long                            lResult                 = 0;
        LINEEXTENSIONID         sLineExt                = {0};
        LPLINEDEVCAPS           lpLineDevCaps   = NULL;
        BOOL                            bContinue               = TRUE;

        for(DWORD dwLine = 0; dwLine < deviceCount && bContinue; ++dwLine)
        {
                lResult         = lineNegotiateAPIVersion(m_LineApp,
                        dwLine,
                        lowAPIVersion,
                        TAPI_CURRENT_VERSION,
                        &apiVersion,
                        &sLineExt);

                if(0 == lResult)
                {
                        lpLineDevCaps   = (LPLINEDEVCAPS)LocalAlloc(LPTR,sizeof(LINEDEVCAPS));
                        lResult                 = LINEERR_STRUCTURETOOSMALL;

                        lpLineDevCaps->dwTotalSize   = sizeof(LINEDEVCAPS);
                        lpLineDevCaps->dwNeededSize  = sizeof(LINEDEVCAPS);

                        while(LINEERR_STRUCTURETOOSMALL == lResult)
                        {
                                lResult = lineGetDevCaps(m_LineApp,dwLine,TAPI_CURRENT_VERSION,0,lpLineDevCaps);

                                if(LINEERR_STRUCTURETOOSMALL == lResult || lpLineDevCaps->dwTotalSize < lpLineDevCaps->dwNeededSize)
                                {
                                        lpLineDevCaps   = (LPLINEDEVCAPS)LocalReAlloc(lpLineDevCaps,lpLineDevCaps->dwNeededSize,LMEM_MOVEABLE);
                                        lResult                 = LINEERR_STRUCTURETOOSMALL;

                                        lpLineDevCaps->dwTotalSize   = lpLineDevCaps->dwNeededSize;
                                }
                        }

                        if(0 == lResult)
                        {
                                TCHAR szName[512];

                                memcpy((PVOID)szName,(PVOID)((BYTE*)lpLineDevCaps + lpLineDevCaps ->dwLineNameOffset),
                                        lpLineDevCaps->dwLineNameSize);

                                szName[lpLineDevCaps->dwLineNameSize]        = 0;

                                if(_tcscmp(szName,CELLTSP_LINENAME_STRING) == 0)
                                {
                                        dwReturn        = dwLine;
                                        bContinue       = FALSE;
                                }
                        }

                        LocalFree((HLOCAL)lpLineDevCaps);
                }
        }

        return dwReturn;
}

HLINE wxUSSDRequest::OpenTAPILine(DWORD cellularID, DWORD apiVersion)
{
        DWORD   dwMediaMode = LINEMEDIAMODE_INTERACTIVEVOICE;
        HLINE   hLine           = NULL;
        DWORD   extVersion      = 0;
        long    lReturn         = lineOpen(m_LineApp,
                cellularID,
                &hLine,
                TAPI_CURRENT_VERSION, 0,
                (DWORD)this,
                LINECALLPRIVILEGE_OWNER,
                dwMediaMode, 0);

        lReturn                         = ::lineNegotiateExtVersion(m_LineApp,
                cellularID,
                apiVersion,
                EXT_API_LOW_VERSION,
                EXT_API_HIGH_VERSION,
                &extVersion);

        return hLine;
}

void FAR PASCAL wxUSSDRequest::LineCallback(DWORD hDevice,
                                                                                        DWORD dwMsg,
                                                                                        DWORD dwCallbackInstance,
                                                                                        DWORD dwParam1,
                                                                                        DWORD dwParam2,
                                                                                        DWORD dwParam3)
{
        switch(dwMsg)
        {
        case LINE_DEVSPECIFIC:
                {
                        if(dwParam1 == LINE_USSD)
                        {
                                DWORD   dwMessageId     = dwParam2;
                        }

                        break;
                }
        case LINE_REPLY:
                {
                        wxString strError;

                        switch(dwParam2)
                        {
                        case 0:                                                         strError        = wxEmptyString;                                                break;
                        case LINEERR_INVALLINEHANDLE:           strError        = wxT("LINEERR_INVALLINEHANDLE");             break;
                        case LINEERR_NOMEM:                                     strError        = wxT("LINEERR_NOMEM");                                       break;
                        case LINEERR_OPERATIONUNAVAIL:          strError        = wxT("LINEERR_OPERATIONUNAVAIL");            break;
                        case LINEERR_OPERATIONFAILED:           strError        = wxT("LINEERR_OPERATIONFAILED");             break;
                        case LINEERR_RESOURCEUNAVAIL:           strError        = wxT("LINEERR_RESOURCEUNAVAIL");             break;
                        case LINEERR_INVALPOINTER:                      strError        = wxT("LINEERR_INVALPOINTER");                        break;
                        case LINEERR_INVALPARAM:                        strError        = wxT("LINEERR_INVALPARAM");                  break;
                        case LINEERR_UNINITIALIZED:                     strError        = wxT("LINEERR_UNINITIALIZED");                       break;
                        default:                                                        strError.Format(_("Error: %x"),dwParam2);                     break;
                        }

                        if(!strError.IsEmpty())
                        {
                                wxLogError(strError);
                        }
                        break;
                }
        }
}

Пример использования

wxUSSDRequest * m_USSDRequest;
...
wxUSSDRequest * GetUSSDRequest() const { return m_USSDRequest ; }
...
void wxUSSDRequestMainFrame::OnSENDClick( wxCommandEvent& event )
{
        do
        {
                if(!wxGetApp().GetUSSDRequest() || !wxGetApp().GetUSSDRequest()->IsOK()) break;
                wxString command = m_USSDTextCtrl->GetValue().GetData();
                if(!wxGetApp().GetUSSDRequest()->SendUSSDCommand(command,
                        command.Length() * sizeof(wxChar)))
                {
                        wxLogError(_("Error sending USSD message '%s'"),
                                m_USSDTextCtrl->GetValue().GetData());
                }
        }
        while(false);
}

Скачать исходный код примера.

Еще интересные посты о программировании для мобильных устройств:

5 Comments

Make A Comment
  • DaBEAT Said:

    ого, а ведь можно просто в ком порт написать:
    AT+CUSD=”*101#”

  • T-Rex Said:

    Можно, но это будет хак. Какая-то у тебя нездоровая любовь к реализации всего вручную :P

  • T-Rex Said:

    Вобще на самом деле работа с COM-портом не всегда плохо, но если, например, для решения задачи можно использовать API, предоставляемое ОС, то лучше использовать его.

  • DaBEAT Said:

    нет, нет, нет я не отрицаю полезности API, но просто киллограмы кода на то что делается 1-й AT командой…

  • T-Rex Said:

    Эээ.. Ну а если руками отправлять AT-командой, это ж все равно надо писать код проверки, на каком порту висит GPRS-модем, это как минимум. Ну и глядя на то, как MS относится к системе безопасности начиная с WM5, вполне возможно что в какой-нибудь из новых версий для прямого доступа к аппаратным устройствам они будут просить чтобы приложение подписано было.
    Там вроде в Windows Mobile Developer Blog анонс был следующих топиков, обещали вроде тему “Deep Dive: RIL APIs vs TAPI”, может в каментах потом чего интересного расскажут, там часто по существу такие полезные каменты бывают

Comments RSS Feed   TrackBack URL

Leave a comment

Please leave these two fields as-is:
Детские игры для детей онлайн бесплатно продаёте автомобиль. ; Консультации Online замена дисплея psp в сервисном центре. ; Продажа по объявлениям: оборудование для воды. Доска объявлений Москвы. ; купить масло моторное, доставка . ; мыло ручной работы купить детская косметика , дегтярное мыло

top