Страница 1 из 2
OpenURL
Добавлено: 28 дек 2004, 21:46
pominub
Здравствуйте.
Такая ситуалия:
Я читаю HTML код веб страницы при помощи функции OpenURL.
Читаю построчно и добавляю каждую строку в список.
Вот так:
Код: Выделить всё
void CTabPage1::OnButton1()
{
try
{
CInternetSession* pInternetSession;
pInternetSession=new CInternetSession;
if(!pInternetSession)
{
AfxMessageBox("не удалось подключиться", MB_OK);
return;
}
CStdioFile* pFile=NULL;
pFile=pInternetSession->OpenURL(CString("http://www.yandex.ru"));
CString stLine;
while (pFile->ReadString(stLine))// читаем файл
{
m_text.AddString(stLine);// Добавляем строку в ListBox
}
UpdateData(false);
pFile->Close();
pInternetSession->Close();
}
Вобщем всё работает.
У меня такой вопрос:
Пока читается страница, программа подвисает.
И невозможно нажать ни на какую другую кнопку (допустим при модемном соединении это может занимать значительное время).
Если нужно ещё что-то параллельно сделать, то есесно ничего
не получится.
Почему так происходит? И можно-ли это исправить?
Спасибо.
Добавлено: 29 дек 2004, 11:32
Romeo
Объясняется это тем, что в цикле while не происходит обработки других сообщений Windows. Самый простой выход - вызывать WaitMessage в конце каждой итерации цикла while. А самый правильный, наверное, - отказаться вообще от цикла и читать по таймеру.
Добавлено: 29 дек 2004, 11:37
Eugie
В однопоточном (single-threaded) приложении это случается, когда управление длительное время не передается в цикл обработки сообщений (message loop). Здесь как раз такой случай: пока читаешь из файла в обработчике OnButton1, никакие другие сообщения не обрабатываются.
Теперь о том, что делать. Есть 2 варианта: 1) вставить локальный message loop в цикл чтения; 2) создавать отдельный поток для чтения (и любой другой другой длительной операции). Первый проще в реализации, второй универсальнее.
Вот пример для вар.1:
Код: Выделить всё
void ProcessMsgQueue(void)
{
MSG msg;
while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
...
while (pFile->ReadString(stLine))// читаем файл
{
m_text.AddString(stLine);// Добавляем строку в ListBox
ProcessMsgQueue();
}
...
Добавлено: 29 дек 2004, 12:13
Romeo
Eugie, описанная функция ProcessMsgQueue по своей функциональности эквивалента готоовой API WaitMessage. Зачем изобретать велосипед? А вот на счёт отдельного потока - это действительно самый правильный подход к подобным задачам.
Добавлено: 29 дек 2004, 12:36
pominub
Спасибо. А если я читаю не в цикле, а просто в буфер, то как быть?
Там такая-же история происходит. Можно ли в этом случае сделать аналогично???
Я делаю так:
Код: Выделить всё
void CTabPage2::OnButton1()
{
CInternetSession* pInternetSession1;
pInternetSession1=new CInternetSession;
if(!pInternetSession1)
{
AfxMessageBox("не удалось", MB_OK);
return;
}
CStdioFile* pFile1=NULL;
char* buffer;
buffer=new char[1000];
pFile1=pInternetSession1->OpenURL("http://www.yandex.ru");
pFile1->Read(buffer, 1000);// Читаем, допустим, первые 1000 байт
pFile1->Close();
pInternetSession1->Close();
}
Тогда по идее эту функцию нада вставлять после каждого прочитанного байта

Добавлено: 29 дек 2004, 12:37
Eugie
Romeo, WaitMessage никоим образом не эквивалентна ProcessMsgQueue: она только передает управление другим потокам, если очередь сообщений текущего потока пуста. А в рассматриваемом случае проблема как раз в том, что очередь не пуста, но управление до цикла выборки-диспетчеризации своевременно не доходит. Если бы было так просто, никто не писал бы цикл сообщений

Добавлено: 29 дек 2004, 12:45
Eugie
pominub, конечно медленно - ты же не из локального файла читаешь

Тут и один вызов Read может 'завесить' программу. Вот как раз для таких ситуаций хорош второй подход, т.е. нужно выделять все длительные операции в отдельный поток.
Добавлено: 29 дек 2004, 14:45
pominub
2Euqie
А ты не мог бы привести пример как сделать это при помощи второго подхода

Я просто в потоках не силён
Добавлено: 29 дек 2004, 15:57
Eugie
В качестве новогоднего подарка
Код: Выделить всё
// Функция потока (thread routine)
unsigned __stdcall ReadFromURL(void *p)
{
CInternetSession* pInternetSession = new CInternetSession;
if(!pInternetSession)
{
AfxMessageBox("не удалось подключиться", MB_OK);
return -1;
}
CMy1Dlg* pDlg = reinterpret_cast<CMy1Dlg*>(p);
CStdioFile* pFile = pInternetSession->OpenURL("http://www.yandex.ru");
if (pFile)
{
CString stLine;
while (pFile->ReadString(stLine))// читаем файл
{
pDlg->m_List.AddString(stLine);// Добавляем строку в ListBox
}
pFile->Close();
}
pInternetSession->Close();
return 0;
}
void CMy1Dlg::OnButton1()
{
// Создаем новый поток для чтения c удаленного хоста.
// Зам.: в функцию потока можно передавать любые данные, просто надо
// упаковать все данные в структуру и передавать ее адрес.
unsigned thd_id; // Id потока; здесь не используется
_beginthreadex(
NULL,
0,
ReadFromURL,
(void *)this,
0,
&thd_id);
}
В настройках проекта не забудь указать поддержку многозадачности (в Project Settings|C/C++, Category: Code Generation нужно задать для Use runtime library: Multithreaded или Multithreaded DLL (либо опцию компилятора /MT или /MD))
Добавлено: 29 дек 2004, 18:24
pominub
2Euqie
Спасибо. Завтра буду разбираться.
