Процедура дозвона модема
Хотелось бы узнать у тех кто это делал одну деталь. Мне примеры кода не обязательны, просто хочу понять логику. Есть модем, я в программе при нажатии кнопки посылаю в СОМ-порт команду дозвона и... не знаю как лучше делать дальше. Дело в том, что после посылки команды дозвона по номеру, модем начинает дозваниваться, затем они начинают с модемом на том конце пересвистываться и только потом, модем отвечает в СОМ-порт "CONNECT". Все это время программа должна как-то ждать этого события. Если я сделаю бесконечный цикл, условием выхода из которого будет приход сообщения от модема, то программа по-сути практически "подвиснет" пока не придет ответ от модема. Это не есть выход.
И еще я не хочу вешать действия, которые надо выполнить после дозвона, на событие СОМ-порта "приход данных", так как из разных процедур программы мне может понадобиться дозваниваться, поэтому и действия, выполняемые после дозвона, могут быть разными.
Подскажите логику реализации процедуры дозвона.
P.S. если че, то программирую на С++Builder
Все это время программа должна как-то ждать этого события. Если я сделаю бесконечный цикл, условием выхода из которого будет приход сообщения от модема, то программа по-сути практически "подвиснет" пока не придет ответ от модема.
Честно говоря мне лично не доводилось писать процедуры обработки для подобных дивайсов, но ответ по логике дозвона напрашивается сам собой: существует 2 способа обмена данными между устройствами- синхронный и асинхронный(по-моему это объясняют во всех технических вузах нашей НЕОБЪЯТНОЙ где-то на 2-ом курсе:) ). При синхронном способе происходит примерно то, что ты пытаешься сделать- программа ждёт ответа от устройства и пока его не получит- бездействует.Во втором- данные буфиризируются и обмен начинается по свистку SYNC (-Дорогая, я готов, сделать тебе предложение.- Ну неужели, дорогой...давай!)
Ешё раз оговорюсь, что самому мне такие дивайсы описывать пока не приходилось, но если у тебя нет времени ждать пока кто-нить не напишет подробный код, советую минут сорок пошерстить интернет в поисках документации протоколов передачи данных(в твоём случае RS-232)
P.S.
... Да, ещё можно по диагонали просмотреть учебники по Ассемлеру или по любому популярному сетевому языку программирования(PERL-лучше всего). Там такие вопрсы подробно рассматриваются
меня интересует именно этот режим ожидания "свистка SYNC" - как его реализовать? Получается что есть процедура ну например обработки нажатия кнопки. В ней я посылаю модему команды "звонить" и модем начинает звонить. А в процедуре-то что делать, пока модем звонит, как реализовать ожидание ответа от модема ("Дозвонился", "Занято" и тд.)? Еще раз повторюсь, но если сделать бесконченый цикл с постоянной проверкой наличия ответа от модема, то программа практически "подвиснет" пока модем не ответит. А это не очень хорошо.
Если бы существовала всего одна такая процедура, откуда требуется звонить, то я бы голову не ломал, а повесил отсальную часть процедуры (ту что должна выполняться уже после соединения модема) на обработку события СОМ-порта "пришли данные". Но таких процедур в программе много. Единственное что мне пока приходит в голову, это в каждой процедуре записывать например в Тег какого-нибудь компонента разную цифру, и тогда обработчик события "пришли данные" СОМ-порта сможет различать из какой именно процедуры его запустили, и соответственно выполнять разные действия после соединения.
Но что-то мне это кажется некрасиво как-то, кривовато :)
или вообще обрисуйте задачу что вам надо делать после дозвона
Но что-то мне это кажется некрасиво как-то, кривовато :)
А использовать отдельный поток что мешает? И в нем собственно и обрабатывать все события модема, а в параметры потока передавать то что необходимо.
Кстати, эта хренотень будет работать только под Вынь95/98 - под нормальную операционку надо или пользовать РАС-функции или писать драйвер устройства.
Далее, какую хренотень ты имеешь в виду, что она будет работать только под Вынь98? Сейчас у меня все прекрасно работает под ХР (процедура дозвона вручную, а затем опрос устройства через СОМ-порт.
На счет РАС-функций, я чесно говоря тоже только слышал :)
А драйвера устройства писать совершенно не к чему. Меня волнует не опрос устройства, а именно дозвон. Конечно можно поставить стандартные драйвера модема и использовать TAPI, но дело в том что программку планируется в дальнейшем продавать вместе с модемами (кстати GSM-модем), и хотелось бы чтобы юзеры просто ставили программу, втыкали модем и все.
И кстати, раз разговор зашел о TAPI, у меня есть компоненты для работы через TAPI, называется APRO, там все великолепно работает с драйверами модема, а почему-то напрямую с СОМ-портом ругается.
З.Ы. Вобщем если до нового года никто не посоветует чего хорошего, придется делать абы как, лишь бы работало :(
Далее, какую хренотень ты имеешь в виду, что она будет работать только под Вынь98? Сейчас у меня все прекрасно работает под ХР (процедура дозвона вручную, а затем опрос устройства через СОМ-порт.
На счет РАС-функций, я чесно говоря тоже только слышал :)
А драйвера устройства писать совершенно не к чему. Меня волнует не опрос устройства, а именно дозвон. Конечно можно поставить стандартные драйвера модема и использовать TAPI, но дело в том что программку планируется в дальнейшем продавать вместе с модемами (кстати GSM-модем), и хотелось бы чтобы юзеры просто ставили программу, втыкали модем и все.
И кстати, раз разговор зашел о TAPI, у меня есть компоненты для работы через TAPI, называется APRO, там все великолепно работает с драйверами модема, а почему-то напрямую с СОМ-портом ругается.
З.Ы. Вобщем если до нового года никто не посоветует чего хорошего, придется делать абы как, лишь бы работало :(
Попробуй так, часть кода которая к собственно к дозвону отношения не имеет я убрал - при желании можно просто создавать новое соединение дозваниваться а затем удалять. Вобщем - читай МСДН по указанным функциям.
VOID WINAPI RasCallback(HRASCONN hrasconn, UINT unMsg,
RASCONNSTATE rascs, DWORD dwError, DWORD dwExtendedError)
{
String S = "";
if (dwError) {
// Ашибка.
char buff[256];
fRasGetErrorString(dwError, buff, sizeof(buff));
fmMainFtp->LogEvents(buff);
String MsgError = "При подключении произошла следующая ошибка:\n"+String(buff);
if(Application->MessageBoxA(MsgError.c_str(),"Ошибка",MB_ICONQUESTION+MB_OKCANCEL)==IDOK){
fRasHangUp(hrasconn);
fmMainFtp->ttTimer->Enabled = true;
return;
}
else fmMainFtp->Close();
return;
}
switch (rascs) {
// Отображение текущего статуса
case RASCS_PortOpened :
S = "Открытие порта..."; break;
case RASCS_DeviceConnected :
S = "Соединение..."; break;
case RASCS_Authenticate :
S = "Вход в сеть..."; break;
case RASCS_Authenticated :
S = "Проверка пользователя и пароля"; break;
case RASCS_Connected : {
S = "Вход в сеть";
fmMainFtp->ttTimerClick->Enabled = true;
break;
}
case RASCS_Disconnected :
S = "Disconnected"; break;
}
if (S != "")
fmMainFtp->LogEvents(S);
}
void __fastcall TfmMainFtp::FormCreate(TObject *Sender)
{
First = 0;
MutexFirst = CreateMutex(NULL,true,"MyMutex");
int Res = GetLastError();
if(Res==ERROR_ALREADY_EXISTS){
First = 1;
Close();
}
hRas = 0;
hRasInstance = LoadLibrary("RASAPI32.DLL");
if (!hRasInstance) {
LogEvents("Unable to load RAS DLL.");
Close();
return;
}
fRasGetErrorString = (pRasGetErrorString)
GetProcAddress(hRasInstance, "RasGetErrorStringA");
fRasDial = (pRasDial)
GetProcAddress(hRasInstance, "RasDialA");
fRasEnumConnections = (pRasEnumConnections)
GetProcAddress(hRasInstance, "RasEnumConnectionsA");
fRasGetConnectStatus = (pRasGetConnectStatus)
GetProcAddress(hRasInstance, "RasGetConnectStatusA");
fRasEnumEntries = (pRasEnumEntries)
GetProcAddress(hRasInstance, "RasEnumEntriesA");
fRasGetEntryDialParams = (pRasGetEntryDialParams)GetProcAddress(hRasInstance, "RasGetEntryDialParamsA");
fRasHangUp = (pRasHangUp)
GetProcAddress(hRasInstance, "RasHangUpA");
if (!fRasGetErrorString || !fRasDial || !fRasEnumConnections ||
!fRasGetConnectStatus || !fRasEnumEntries || !fRasHangUp) {
LogEvents("Ошибка загрузки функций дозвона");
Close();
return;
}
....
}
void TfmMainFtp::CheckRnaApp()
{
if (Win32Platform == VER_PLATFORM_WIN32_NT)
// фор Win9x
return;
HWND hWnd = FindWindow("RnaEngClass", 0);
if (hWnd) {
//Если в этот момент окно дозвона существует - прибиваем
PostMessage(hWnd, WM_CLOSE, 0, 0);
// И ждем на всякий случай.
Sleep(5000);
}
}
//---------------------------------------------------------------------------
void __fastcall TfmMainFtp::acConnectWithInetExecute(TObject *Sender)
{
ttTimer->Enabled = false;
hRas = CheckForConnections();
String PBEntry;
if (hRas) {
LogEvents("Используем существующее соединение...");
ttTimerClick->Enabled = true;
AlreadyConnected = true;
// Не надо вызывать - соедиенение с Инетом уже есть
return;
}
else {
// Получаем соединение по умолчанию
PBEntry = GetPhoneBookEntry();
if (PBEntry == "") {
LogEvents("Нет соединений");
Close();
}
}
LogEvents("Соединение отсутствует. Дозваниваемся...");
CheckRnaApp();
// Устанавливаем соединение
RASDIALPARAMS params;
params.dwSize = sizeof(params);
strcpy(params.szEntryName, PBEntry.c_str());
BOOL PwdDetected = FALSE;
DWORD Result = fRasGetEntryDialParams(NULL,¶ms,&PwdDetected);
DWORD res = fRasDial(0, 0, ¶ms, 1, RasCallback, &hRas);
if (res) {
char buff[256];
fRasGetErrorString(res, buff, sizeof(buff));
LogEvents(buff);
String MsgError = "При подключении произошла следующая ошибка:\n"+String(buff);
if(Application->MessageBoxA(MsgError.c_str(),"Ошибка",MB_ICONQUESTION+MB_OKCANCEL)==IDOK){
fRasHangUp(hRas);
ttTimer->Enabled = true;
return;
}
else Close();
}
}
HRASCONN TfmMainFtp::CheckForConnections()
{
if(GetInternetConnection())return (void*)(1);
char buff[256];
RASCONN rc;
rc.dwSize = sizeof(RASCONN);
DWORD numConns;
DWORD size = rc.dwSize;
// Enumerate the connections.
DWORD res = fRasEnumConnections(&rc, &size, &numConns);
if (!res && numConns == 0)
// No connections, return 0.
return 0;
if (res) {
// Error. Report it.
fRasGetErrorString(res, buff, sizeof(buff));
LogEvents(buff);
String MsgError = "При подключении произошла следующая ошибка:\n"+String(buff);
if(Application->MessageBoxA(MsgError.c_str(),"Ошибка",MB_ICONQUESTION+MB_OKCANCEL)==IDOK){
fRasHangUp(rc.hrasconn);
ttTimer->Enabled = true;
return 0;
}
else Close();
return 0;
} else {
// Получсаем статус
RASCONNSTATUS status;
status.dwSize = sizeof(status);
res = fRasGetConnectStatus(rc.hrasconn, &status);
if (res) {
// Error. Report it.
fRasGetErrorString(res, buff, sizeof(buff));
LogEvents(buff);
String MsgError = "При подключении произошла следующая ошибка:\n"+String(buff);
if(Application->MessageBoxA(MsgError.c_str(),"Ошибка",MB_ICONQUESTION+MB_OKCANCEL)==IDOK){
fRasHangUp(rc.hrasconn);
ttTimer->Enabled = true;
return 0;
}
else Close();
return 0;
} else {
// Ищем соединение и опрашиваем ее
if (status.rasconnstate == RASCS_Connected) {
LogEvents("Установлено соединение:");
LogEvents("Тип устройства:\t" +
String(rc.szDeviceType));
LogEvents("Название устройства: " +
String(rc.szDeviceName));
LogEvents("Соединение: " +
String(rc.szEntryName));
return rc.hrasconn;
} else {
//Если соединение запущено но имеет статус дисконнект
LogEvents("Connection Error");
String MsgError = "При подключении произошла следующая ошибка:\n"+String(buff);
if(Application->MessageBoxA(MsgError.c_str(),"Ошибка",MB_ICONQUESTION+MB_OKCANCEL)==IDOK){
fRasHangUp(rc.hrasconn);
ttTimer->Enabled = true;
return 0;
}
else Close();
return 0;
}
}
}
return 0;
}
String TfmMainFtp::GetPhoneBookEntry()
{
LogEvents("Инициализация соединения");
String S;
Reg = new TRegistry;
Reg->RootKey = HKEY_LOCAL_MACHINE;
Reg->OpenKeyReadOnly("\\SOFTWARE\\Microsoft\\RAS AutoDial\\Default");
if(Reg->ValueExists("DefaultInternet")){
S = Reg->ReadString("DefaultInternet");
return S;
}
else{
RASENTRYNAME* entries = new RASENTRYNAME;
entries->dwSize = sizeof(RASENTRYNAME);
DWORD numEntries;
DWORD size = entries->dwSize;
DWORD res = fRasEnumEntries(0, 0, entries, &size, &numEntries);
if (numEntries == 1) {
String entryName = entries->szEntryName;
delete[] entries;
return entryName;
}
if (res == ERROR_BUFFER_TOO_SMALL) {
delete[] entries;
entries = new RASENTRYNAME[numEntries];
entries[0].dwSize = sizeof(RASENTRYNAME);
res = fRasEnumEntries(0, 0, entries, &size, &numEntries);
if (res) {
char buff[256];
fRasGetErrorString(res, buff, sizeof(buff));
LogEvents(buff);
}
}
delete[] entries;
delete Reg;
return S;
}
}
Я тут нашел еще один способ. У СОМ-порта есть такая функция WaitCommEvent, она то у меня и будет ждать прихода данных из СОМ-порта.
Всем спасибо за участие.
Тему можно закрывать