OpenURL

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

pominub
Сообщения: 24
Зарегистрирован: 16 ноя 2004, 09:35
Откуда: Spb

Здравствуйте.
Такая ситуалия:

Я читаю 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();
}
Вобщем всё работает.
У меня такой вопрос:
Пока читается страница, программа подвисает.
И невозможно нажать ни на какую другую кнопку (допустим при модемном соединении это может занимать значительное время).
Если нужно ещё что-то параллельно сделать, то есесно ничего
не получится.
Почему так происходит? И можно-ли это исправить?
Спасибо.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Объясняется это тем, что в цикле while не происходит обработки других сообщений Windows. Самый простой выход - вызывать WaitMessage в конце каждой итерации цикла while. А самый правильный, наверное, - отказаться вообще от цикла и читать по таймеру.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Eugie
Сообщения: 708
Зарегистрирован: 17 фев 2004, 23:59
Откуда: SPb

В однопоточном (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();
   }
...
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Eugie, описанная функция ProcessMsgQueue по своей функциональности эквивалента готоовой API WaitMessage. Зачем изобретать велосипед? А вот на счёт отдельного потока - это действительно самый правильный подход к подобным задачам.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
pominub
Сообщения: 24
Зарегистрирован: 16 ноя 2004, 09:35
Откуда: Spb

Спасибо. А если я читаю не в цикле, а просто в буфер, то как быть?
Там такая-же история происходит. Можно ли в этом случае сделать аналогично???

Я делаю так:

Код: Выделить всё

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();
	
}
Тогда по идее эту функцию нада вставлять после каждого прочитанного байта :)
Eugie
Сообщения: 708
Зарегистрирован: 17 фев 2004, 23:59
Откуда: SPb

Romeo, WaitMessage никоим образом не эквивалентна ProcessMsgQueue: она только передает управление другим потокам, если очередь сообщений текущего потока пуста. А в рассматриваемом случае проблема как раз в том, что очередь не пуста, но управление до цикла выборки-диспетчеризации своевременно не доходит. Если бы было так просто, никто не писал бы цикл сообщений :)
Eugie
Сообщения: 708
Зарегистрирован: 17 фев 2004, 23:59
Откуда: SPb

pominub, конечно медленно - ты же не из локального файла читаешь :) Тут и один вызов Read может 'завесить' программу. Вот как раз для таких ситуаций хорош второй подход, т.е. нужно выделять все длительные операции в отдельный поток.
pominub
Сообщения: 24
Зарегистрирован: 16 ноя 2004, 09:35
Откуда: Spb

2Euqie

А ты не мог бы привести пример как сделать это при помощи второго подхода :)
Я просто в потоках не силён
Eugie
Сообщения: 708
Зарегистрирован: 17 фев 2004, 23:59
Откуда: SPb

В качестве новогоднего подарка :)

Код: Выделить всё

// Функция потока (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))
pominub
Сообщения: 24
Зарегистрирован: 16 ноя 2004, 09:35
Откуда: Spb

2Euqie

Спасибо. Завтра буду разбираться. :)
Ответить