Main.cpp для mycall (c++)
Это основной файл приложения MyCall на C++. Этот файл в текстовом формате вместе со всеми остальными файлами, необходимыми для компиляции приложения MyCall, содержится в zip-файле mycallcb.zip (13192 байта). Имеется также Инструкция программиста.
Для получения комментариев щелкaйте по тексту или пользуйтесь групповым управлением:
if(dhtml){document.write("Все комментарии: [+][-] Открывать: [несколько]");}
//Включение заголовочных файлов
//Здесь windows.h и ras.h - стандартные из пакета MS Visual C++,
//а main.h - заголовочный файл приложения MyCall
#include "windows.h"
#include "ras.h"
#include "main.h"
//ГЛАВНАЯ ФУНКЦИЯ ПРИЛОЖЕНИЯ
/////////////////////////////////////////////////////////////// WinMain
int WINAPI WinMain(HINSTANCE hinst,HINSTANCE prev_hinst,LPSTR command_line,int cmd_show){
//Получение дескриптора экземпляра приложения
//Необходимое действие, так как предполагается компиляция приложения без
//подключения runtime-библиотеки. Подробнее...
hinst=GetModuleHandle(NULL);
//Создание главного окна
//Обычное действие, с которого начинаются большинство приложений.
//Единственное отличие в том, что в качестве главного окна в MyCall
//используется окно диалога, описанного в файле ресурсов
//Регистрируется класс главного окна:
WNDCLASSEX mw_class;
mw_class.cbSize=sizeof(WNDCLASSEX);
mw_class.style=NULL;
mw_class.lpfnWndProc=superprocedure;
mw_class.cbClsExtra=0;
mw_class.cbWndExtra=DLGWINDOWEXTRA;
mw_class.hInstance=hinst;
mw_class.hIcon=LoadIcon(hinst,MAKEINTRESOURCE(103));
mw_class.hIconSm=NULL;
mw_class.hCursor=LoadCursor(NULL,IDC_ARROW);
mw_class.hbrBackground=(HBRUSH)COLOR_WINDOW;
mw_class.lpszMenuName=NULL;
mw_class.lpszClassName="MainWindowClass";
if(!RegisterClassEx(&mw_class)){fatal(FATAL_MAIN_CLASS_REG);return EXIT_COMMON_ERROR;}
//Главное окно создается:
if(!(main_window=CreateDialog(hinst,MAKEINTRESOURCE(101),NULL,NULL))){fatal(FATAL_MAIN_CLASS_CREATE);return EXIT_COMMON_ERROR;}
// По состоянию списка conn_window формируются остальные списки
change_con();//
//ЦИКЛ ОЖИДАНИЯ СООБЩЕНИЙ
//Неотъемлемый элемент приложений для Windows. В MyCall никаких особенностей не имеет
MSG loop_message;
while (GetMessage(&loop_message,NULL,0,0)){TranslateMessage(&loop_message);DispatchMessage(&loop_message);}
//ЗАВЕРШЕНИЕ РАБОТЫ ПРИЛОЖЕНИЯ
//Поскольку MyCall собирается без runtime-библиотеки, для завершения работы
//обязательно использовать функцию ExitProcess, при этом оператор return оказывается
//недостижимым, но необходим по требованиям синтаксиса. Подробнее...
ExitProcess(loop_message.wParam);
return 0;
}
//ОКОННАЯ ПРОЦЕДУРА
//Стандартный элемент приложений для Windows. В MyCall особенностей не имеет.
/////////////////////////////////////////////////////////////// Оконная процедура
LRESULT CALLBACK superprocedure(HWND window_from,UINT message,WPARAM w_param,LPARAM l_param){
//Разбор и обработка оконных сообщений
switch(message){
//Обработка сообщения WM_COMMAND, передаваемого элементами управления диалога
case WM_COMMAND:
//Обработка уведомления CBN_SELCHANGE, передаваемого списками при изменении позиции:
//1000: список соединений conn_window. Изменяет содержание phon_window и user_window
//1001: список телефонов phon_window. Устанавливает новый телефон для текущего соединения con_phone[current_con]
//1002: список логинов user_window. Устанавливает новый логин для текущего соединения con_user[current_con]
if(HIWORD(w_param)==CBN_SELCHANGE){
switch (LOWORD(w_param)){
case 1000:
current_con=SendMessage(conn_window,CB_GETCURSEL,0,0);
if(current_con==CB_ERR){current_con=0;}
change_con();
return 0;
case 1001:
con_phone[current_con]=SendMessage(phon_window,CB_GETCURSEL,0,0);
if(con_phone[current_con]==CB_ERR){con_phone[current_con]=0;}
return 0;
case 1002:
con_user[current_con]=SendMessage(user_window,CB_GETCURSEL,0,0);
if(con_user[current_con]==CB_ERR){con_user[current_con]=0;}
return 0;
default:
break;
}
return 0;
}
// Обработка уведомления BN_CLICKED, передаваемого кнопкой при клике:
//в состоянии online=TRUE прекращает дозвон (разрывает соединение) и включает списки
//в состоянии online=FALSE отключает списки и начинает дозвон
if((HIWORD(w_param)==BN_CLICKED)&&(LOWORD(w_param))==1003){
if(online){
EnableWindow(butt_window,FALSE);
ras_hangup();
EnableWindow(butt_window,TRUE);
disable_controls(FALSE);
}else{
disable_controls(TRUE);
ras_dial();
}
return 0;
}
break;
//Обработка сообщения WM_USER, используемого в MyCall нитью монитора разрыва.
//Обнаружив факт разрыва соединения, нить монитора передает WM_USER. Если был коннект,
//то включаются списки, в противном случает выполняется повторный дозвон
case WM_USER:
ras_hangup();
l_param?disable_controls(FALSE):ras_dial();
return 0;
//Обработка сообщения WM_MOVE: запоминание новой позиции окна
//для последующей записи ее в файл mycall.ini
case WM_MOVE:
RECT win_pos;
if(GetWindowRect(main_window,&win_pos)){
main_win_left=win_pos.left;
main_win_top=win_pos.top;
/div>
}else{
main_win_left=(int)LOWORD(l_param);
main_win_top=(int)HIWORD(l_param);
}
return 0;
//При завершении работы приложения записать файл mycall.ini,
//и освободить память
case WM_DESTROY:
save_ini();
if(dat_buffer){GlobalFree(dat_buffer);}
PostQuitMessage(EXIT_NORMAL);
return 0;
//При закрытии главного окна заблокировать кнопку
//и разорвать соединение
case WM_CLOSE:
EnableWindow(butt_window,FALSE);
if(online){ras_hangup();}
break;
}
//Обработка глобального оконного сообщения, используемого для
//взаимодействия экземпляров приложения. Получив это сообщение,
//экземпляр, запущенный первым, сообщает дескриптор своего главного окна.
//Сообщение, полученное вторым, принимает от первого дескриптор его главного окна,
//выводит его на первый план и завершается. Подробнее...
if(message==my_message){
if((HWND)w_param!=main_window){
if(l_param==0){
SendMessage(HWND_BROADCAST,my_message,(WPARAM)main_window,1);
}else{
SetForegroundWindow((HWND)w_param);
ExitProcess(EXIT_OVERLOADED);
}
}
return 0;
}
//Возврат необработанных сообщений системе
return DefWindowProc(window_from,message,w_param,l_param);
}
//ЗАГРУЗКА ФАЙЛА ИНИЦИАЛИЗАЦИИ
//Файл mycall.ini хранит состояние списков и положение главного окна на момент
//завершения предыдущего сеанса работы приложения
/////////////////////////////////////////////////////////////// Загрузка файла инициализации
void load_ini(){
//Инициализация глобальных переменных
main_win_left=MAIN_WIN_DEFAULT_LEFT;
main_win_top=MAIN_WIN_DEFAULT_TOP;
current_con=0;
for(int i=0;i<MAX_CON;i++){
con_phone[i]=0;
con_user[i]=0;
}
//Попытка загрузки файла mycall.ini
//В случае неудачи используются значения по умолчанию
HANDLE ini_file=CreateFile(INI_FILE,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(ini_file!=INVALID_HANDLE_VALUE){
//Чтение файла mycall.ini
BYTE buffer[INI_FILE_LENGTH];
DWORD bytes_read;
if(ReadFile(ini_file,buffer,INI_FILE_LENGTH,&bytes_read,NULL)){
//Приведение позиции окна к фактическим размерам экрана и запоминание
main_win_left=(((INT)buffer[1])<<8)|((INT)buffer[0]);
if(main_win_left>=GetSystemMetrics(SM_CXSCREEN)-10){main_win_left=MAIN_WIN_DEFAULT_LEFT;}
main_win_top=(((INT)buffer[3])<<8)|((INT)buffer[2]);
if(main_win_top>=GetSystemMetrics(SM_CYSCREEN)-10){main_win_top=MAIN_WIN_DEFAULT_TOP;}
//Запоминание позиций списков телефонов и логинов
current_con=(INT)buffer[4];
for(int i=0;i<MAX_CON;i++){
con_phone[i]=(INT)buffer[(i<<1)+5];
con_user[i]=(INT)buffer[(i<<1)+6];
}
}
CloseHandle(ini_file);
}
}
//СОХРАНЕНИЕ ФАЙЛА ИНИЦИАЛИЗАЦИИ mycall.ini
//В файле сохраняются положение главного окна на экране и позиции
//списков на момент завершения работы приложения
/////////////////////////////////////////////////////////////// Сохранение файла инициализации
void save_ini(){
HANDLE ini_file=CreateFile(INI_FILE,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if(ini_file!=INVALID_HANDLE_VALUE){
BYTE buffer[INI_FILE_LENGTH];
buffer[0]=(BYTE)main_win_left;
buffer[1]=(BYTE)(main_win_left>>8);
buffer[2]=(BYTE)main_win_top;
buffer[3]=(BYTE)(main_win_top>>8);
buffer[4]=(BYTE)current_con;
for(int i=0;i<MAX_CON;i++){
buffer[(i<<1)+5]=(BYTE)con_phone[i];
buffer[(i<<1)+6]=(BYTE)con_user[i];
}
DWORD bytes_written;
WriteFile(ini_file,buffer,INI_FILE_LENGTH,&bytes_written,NULL);
CloseHandle(ini_file);
}
}
//ЗАГРУЗКА ФАЙЛА ДАННЫХ mycall.dat
// Файл содержит имена соединений, телефоны и логины
/////////////////////////////////////////////////////////////// Загрузка файла данных
BOOL load_data(){
//Открытие файла, подготовка буфера для него и считывание файла в буфер
HANDLE dat_file=CreateFile(DAT_FILE,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(dat_file==INVALID_HANDLE_VALUE){fatal(FATAL_DAT_FILE_OPEN);return FALSE;}
DWORD dat_file_size=GetFileSize(dat_file,NULL);
if(dat_file_size==0xffffffff){CloseHandle(dat_file);fatal(FATAL_DAT_FILE_SIZE);return FALSE;}
dat_buffer=(LPBYTE)GlobalAlloc(GMEM_FIXED,dat_file_size+3);
if(dat_buffer==NULL){CloseHandle(dat_file);fatal(FATAL_DAT_BUF_ALLOC);return FALSE;}
DWORD bytes_read;
if(!ReadFile(dat_file,dat_buffer,dat_file_size,&bytes_read,NULL)){CloseHandle(dat_file);fatal(FATAL_DAT_FILE_READ);return FALSE;}
CloseHandle(dat_file);
//Замена слэшей, CR, LF и пробелов на 0h, добавление в конец 1h
ptr=dat_buffer;
for(DWORD i=0;i<dat_file_size;i++){
if((*ptr==0xd)||(*ptr==0xa)||(*ptr=='/')||(*ptr==' ')){*ptr=0;}
ptr++;}
dat_buffer[i]=0;dat_buffer[i+1]=0;dat_buffer[i+2]=1;
return TRUE;
}
//ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
//Используются при разборе содержимого буфера данных:
//skip_nz - пропуск всех символов, пока не встретится 00h
//skip_nz2 - пропуск всех символов, пока не встретится 0000h
/////////////////////////////////////////////////////////////// Пропуски
VOID skip_nz(){
while(*ptr!=0){ptr++;}
ptr++;
}
VOID skip_nz2(){
while(*(LPWORD)ptr!=0){ptr++;}
ptr+=2;
}
//ИЗМЕНЕНИЕ СОЕДИНЕНИЯ
//Функция вызывается при выборе в списке соединений нового соединения
/////////////////////////////////////////////////////////////// Изменение соединения
VOID change_con(){
// Установка указателя на запись текущего соединения
INT sub_string=0;
INT cur_con=0;
ptr=dat_buffer;
while(*ptr!=1){
if(cur_con==current_con){break;}
if(*(LPWORD)ptr==0){
if(++sub_string==3){cur_con++;sub_string=0;}
ptr++;
}
ptr++;
}
skip_nz2();
//Формирование списка телефонов для данного соединения и установка его позиции
SendMessage(phon_window,CB_RESETCONTENT,0,0);
int number=0;
while(*ptr!=0){
SendMessage(phon_window,CB_ADDSTRING,0,(LPARAM)ptr);
number++;
skip_nz();
}
ptr++;
if(con_phone[current_con]>=number){con_phone[current_con]=0;}
SendMessage(phon_window,CB_SETCURSEL,(WPARAM)con_phone[current_con],0);
//Формирование списка логинов для данного соединения и установка его позиции
SendMessage(user_window,CB_RESETCONTENT,0,0);
number=0;
while(*ptr!=0){
SendMessage(user_window,CB_ADDSTRING,0,(LPARAM)ptr);
number++;
skip_nz();
skip_nz();
}
if(con_user[current_con]>=number){con_user[current_con]=0;}
SendMessage(user_window,CB_SETCURSEL,(WPARAM)con_user[current_con],0);
}
//АВАРИЙНОЕ ЗАВЕРШЕНИЕ ПРИЛОЖЕНИЯ
//Вызывается в случае, когда продолжение работы приложения невозможно
/////////////////////////////////////////////////////////////// Фатальный аборт
void fatal(int fatal_code){
LPCSTR fatal_text[]={
"Can't register main window class",
"Can't create main window",
"Can't open mycall.txt",
"Can't get size of mycall.txt",
"Can't allocate memory for mycall.txt",
"Can't read mycall.txt",
"Remote Access Service fatal error"};
MessageBox(NULL,fatal_text[fatal_code],"MyCall Error",MB_OK|MB_ICONERROR);
}
//В(Ы)КЛЮЧЕНИЕ ОРГАНОВ УПРАВЛЕНИЯ
//Меняет надпись на кнопке и активизирует списки в зависимости от того,
//находится приложение в режиме выбора или в режиме соединения
/////////////////////////////////////////////////////////////// В(ы)ключение органов управления
VOID disable_controls(BOOL disable){
SetWindowPos(main_window,NULL,NULL,NULL,win_width,win_height+(disable?12:0),SWP_NOMOVE|SWP_NOZORDER);
SendMessage(butt_window,WM_SETTEXT,0,(LPARAM)(disable?"HangUp":"Call"));
EnableWindow(conn_window,!disable);
EnableWindow(phon_window,!disable);
EnableWindow(user_window,!disable);
}
//ДОЗВОН
/////////////////////////////////////////////////////////////// Дозвон
VOID ras_dial(){
//Подготовка структуры RASDIALPARAMS
RASDIALPARAMS ras_dial_params;
ras_dial_params.dwSize=sizeof(RASDIALPARAMS);
ras_dial_params.szCallbackNumber[0]=0;
ras_dial_params.szDomain[0]=0;
ptr=dat_buffer;
for(int j=0;j!=current_con;j++){for(int k=0;k<3;k++){skip_nz2();}}
ras_dial_params.szEntryName[0]=0;
lstrcpy(ras_dial_params.szEntryName,(LPSTR)ptr);
skip_nz();
ras_dial_params.szPhoneNumber[0]=0;
lstrcpy(ras_dial_params.szPhoneNumber,(LPSTR)ptr);
skip_nz();ptr--;
while(*ptr==0){ptr++;}
for(j=0;j!=con_phone[current_con];j++){skip_nz();}
lstrcat(ras_dial_params.szPhoneNumber,(LPSTR)ptr);
skip_nz2();
for(j=0;j!=con_user[current_con];j++){skip_nz();skip_nz();}
ras_dial_params.szUserName[0]=0;
lstrcpy(ras_dial_params.szUserName,(LPSTR)ptr);
skip_nz();
ras_dial_params.szPassword[0]=0;
lstrcpy(ras_dial_params.szPassword,(LPSTR)ptr);
//Дозвон
ras_conn=NULL;
if(RasDial(0,0,&ras_dial_params,0,ras_dial_func,&ras_conn)){
ras_hangup();
fatal(FATAL_RAS);
ExitProcess(EXIT_RAS_ERROR);
}else{
online=TRUE;
}
}
//ФУНКЦИЯ КОНТРОЛЯ СОСТОЯНИЯ СОЕДИНЕНИЯ
/////////////////////////////////////////////////////////////// Контроль состояния соединения
VOID WINAPI ras_dial_func(UINT msg,RASCONNSTATE rasconnstate,DWORD error){
LPCSTR ras_state_text[]={
"OpenPort",
"PortOpened",
"ConnectDevice",
"DeviceConnected",
"AllDevicesConnected",
"Authenticate",
"AuthNotify",
"AuthRetry",
"AuthCallback",
"AuthChangePassword",
"AuthProject",
"AuthLinkSpeed",
"AuthAck",
"ReAuthenticate",
"Authenticated",
"PrepareForCallback",
"WaitForModemReset",
"WaitForCallback",
"Projected",
"StartAuthentication",
"CallbackComplete",
"LogonNetwork",
"SubEntryConnected",
"SubEntryDisconnected",
"Interactive",
"RetryAuthentication",
"CallbackSetByCaller",
"PasswordExpired",
"Connected",
"Disconnected",
"Unknown state"};
if(online){
//Показ состояния соединения в строке статуса
UINT ras_message_num=rasconnstate;
if(ras_message_num>=RASCS_Connected){ras_message_num-=RASCS_Connected-28;}
else{if(ras_message_num>=RASCS_Interactive){ras_message_num-=RASCS_Interactive-24;}}
if(ras_message_num>30){ras_message_num=30;}
SendMessage(stat_window,WM_SETTEXT,0,(LPARAM)ras_state_text[ras_message_num]);
//Если соединение установлено - запуск нити контроля
if(rasconnstate==RASCS_Connected){
CreateThread(NULL,0,ras_monitor_func,0,0,&ras_monitor_id);
//Если соединение не установлено - передача сообщения WM_USER для
//повтора дозвона
}else{
if(rasconnstate==RASCS_Disconnected){
PostMessage(main_window,WM_USER,0,0);
}
}
}
}
//НИТЬ МОНИТОРА РАЗРЫВА
// Каждые 200 мс опрашивает состояние установленного соединения.
//При обнаружении факта разъединения посылает сообщение WM_USER
//и завершается
//============================================================= Нить монитора разрыва
DWORD WINAPI ras_monitor_func(LPVOID param){
RASCONNSTATUS ras_connect_status;
while(online){
ras_connect_status.dwSize=sizeof(RASCONNSTATUS);
if(RasGetConnectStatus(ras_conn,&ras_connect_status)){break;}
if(ras_connect_status.rasconnstate==RASCS_Disconnected){break;}
Sleep(200);
}
PostMessage(main_window,WM_USER,0,1);
return 0;
}
//ПОЛОЖИТЬ ТРУБКУ
//Если происходит дозвон, или соединение установлено,
//дает команду на разрыв и ожидает, когда RAS ее выполнит
/////////////////////////////////////////////////////////////// Положить трубку
VOID ras_hangup(){
RASCONNSTATUS ras_connect_status;
online=FALSE;
if(ras_conn){
RasHangUp(ras_conn);
while(TRUE){
ras_connect_status.dwSize=sizeof(RASCONNSTATUS);
if(RasGetConnectStatus(ras_conn,&ras_connect_status)==ERROR_INVALID_HANDLE){break;}
Sleep(0);
}
ras_conn=0;
Sleep(1000);
}
}