Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Программирование и базы данных (http://forum.oszone.net/forumdisplay.php?f=21)
-   -   [решено] Реализация обновления программы - ClientSocket и ClientServer (http://forum.oszone.net/showthread.php?t=173152)

Drongo 14-04-2010 20:43 1392665

Реализация обновления программы - ClientSocket и ClientServer
 
Вложений: 1
Привет. Заранее приношу извинение если буду использовать неверную терминологию, но до недавнего времени у меня небыло необходимости в использовании этих компонентов и самой технологии. Задача в общем-то такова, есть клиет-программа, на сервере есть обновления к этой программе, нужно по нажатию на кнопку\пункт меню "Обновить", чтобы программа скачала только те модули, которые новее тех, которые присутствуют рядом с программой. Аналог такого обновления, любой антивирус или AVZ. Поискал в сети, нашёл статью - Передача файлов в C++Builder через TClientSocket и TServerSocket. По примеру сделал тестовую программу, в рамках одного компьютера она работает, но там, в примере, есть клиентская часть и серверная.

Вот представьте, что наше обновление - этот файл - QuickKiller_2.20.7z нужно скачать и находится он здесь - http://tools.oszone.net/Drongo/QuickKiller_2.20.7z. Но скачивать нужно как обновление (как менеджер закачек), а не через диалоговое окно "Сохранить файл".

Архив простенького проекта прикрепил, собирал по примеру выше, при нажатии на кнопку "Скачать" устанавливает соединение и на этом всё. Собственно, я не знаю в какую сторону смотреть и те ли компоненты, которые я выбрал, нужны для этих целей? Среда разработки
Цитата:

Borland C++ Builder 6.0 Enterprise Suite
Спасибо.

ganselo 15-04-2010 00:19 1392848

ClientSocket работает на низком уровне (т.е напрямую по TCP), а файлик лежит в WEB. Думаю нужно использовать библиотеку WinInet.
Вот кусок кода осуществляющий скачку с Web используя WinInet
Код:

DWORD WINAPI DownloadThread(LPVOID lpParam)
{
        DOWNLOAD_DATA data = *(DOWNLOAD_DATA *)lpParam;
        HINTERNET hInetSession;
        HINTERNET hInetFile;
        HANDLE    hFile;
        OVERLAPPED ovlp;
        DWORD    dwOffset = 0;
        DWORD    dwRead;
        TCHAR    ReadBuf[4*1024];

        lstrcpy((LPTSTR)&data, NULL);
        hInetSession = InternetOpen("Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.0; .NET CLR 1.0.2914)",
                                                                PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, 0);
        if(hInetSession == NULL)
        {
                WritePrivateProfileString(data.FileName, "URL", data.Url, IniFile);
                data.sock->SendFormatText(0, "INET_OPEN_ERROR\r\n%li", GetLastError());
                return -1;
        }

        hInetFile = InternetOpenUrl(hInetSession, data.Url, NULL, 0, 0, NULL);
        if(hInetFile == NULL)
        {
                WritePrivateProfileString(data.FileName, "URL", data.Url, IniFile);
                data.sock->SendFormatText(0, "INET_OPEN_URL_ERROR\r\n%li", GetLastError());
                return -1;
        }

        hFile = CreateFile(data.FullFileName,
                                          GENERIC_WRITE,
                                          FILE_SHARE_READ | FILE_SHARE_WRITE,
                                          NULL,
                                          OPEN_ALWAYS,
                                          FILE_ATTRIBUTE_NORMAL,
                                          NULL
                                          );
        if(BAD_HANDLE(hFile))
        {
                data.sock->SendFormatText(0, "SAVE_FILE_OPEN_ERROR\r\n%li", GetLastError());
                InternetCloseHandle(hInetSession);
                InternetCloseHandle(hInetFile);
                return -1;
        }
       
        if(data.dwOffset == 1)
                InternetSetFilePointer(hInetFile, GetFileSize(hFile, NULL), NULL, FILE_BEGIN, 0);

        data.sock->SendFormatText(0, "DOWNLOAD_BEGIN\r\n%s", data.FileName);
        do
        {
                InternetReadFile(hInetFile, ReadBuf, sizeof(ReadBuf), &dwRead);
                if(dwRead >= 0)
                {
                        ovlp.hEvent    = NULL;
                        ovlp.OffsetHigh = NULL;
                        ovlp.Offset    = GetFileSize(hFile, NULL);

                        WriteFile(hFile, ReadBuf, dwRead, NULL, &ovlp);
                        dwOffset += dwRead;
                }
                else
                {
                        InternetCloseHandle(hInetSession);
                        InternetCloseHandle(hInetFile);

                        hInetSession = InternetOpen("Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.0; .NET CLR 1.0.2914)",
                                                                                PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, 0);
                        if(hInetSession == NULL)
                        {
                                WritePrivateProfileString(data.FileName, "URL", data.Url, IniFile);
                                data.sock->SendFormatText(0, "INET_OPEN_ERROR\r\n%li", GetLastError());
                                CloseHandle(hFile);
                                return -1;
                        }

                        hInetFile = InternetOpenUrl(hInetSession, data.Url, NULL, 0, 0, NULL);
                        if(hInetFile == NULL)
                        {
                                WritePrivateProfileString(data.FileName, "URL", data.Url, IniFile);
                                data.sock->SendFormatText(0, "INET_OPEN_URL_ERROR\r\n%li", GetLastError());
                                CloseHandle(hFile);
                                InternetCloseHandle(hInetSession);
                                return -1;
                        }

                        InternetSetFilePointer(hInetFile, dwOffset, NULL, FILE_BEGIN, NULL);
                }
        }
        while(dwRead);

        CloseHandle(hFile);
        InternetCloseHandle(hInetSession);
        InternetCloseHandle(hInetFile);
        data.sock->SendFormatText(0, "DOWNLOAD_END\r\n%s", data.FileName);
        return 1;
}


Drongo 15-04-2010 12:32 1393191

ganselo, спасибо, но если можно подробнее, куда и как этот код нужно подставить? Я не понимаю. :dont-know

ganselo 15-04-2010 15:24 1393335

Drongo, я этот код выдернул из своего примитивного менеджера закачек.
Вот код с комментами:
Код:


struct DOWNLOAD_DATA
{
      char  Url[1024]; //ссылка на файл
      BOOL bFlag; // если TRUE, то осуществляется докачка
};
//закачку я осуществлял в отдельном потоке.
DWORD WINAPI DownloadThread(LPVOID lpParam)
{
        DOWNLOAD_DATA data = *(DOWNLOAD_DATA *)lpParam;
        HINTERNET    hInetSession;
        HINTERNET    hInetFile;
        HANDLE        hFile;
        OVERLAPPED ovlp;
        DWORD        dwOffset = 0;
        DWORD        dwRead;
        TCHAR          ReadBuf[4*1024];

        hInetSession = InternetOpen("Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.0; .NET CLR 1.0.2914)",
                                                                PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, 0); //Возвращает хэндл сессии
        if(hInetSession == NULL) //тут всё понятно
        {
                return -1;
        }

        hInetFile = InternetOpenUrl(hInetSession, data.Url, NULL, 0, 0, NULL); //по сути открываем файл
        if(hInetFile == NULL) //снова проверяем
        {
                return -1;
        }

        hFile = CreateFile(data.FullFileName,
                                    GENERIC_WRITE,
                                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                                    NULL,
                                    OPEN_ALWAYS,
                                    FILE_ATTRIBUTE_NORMAL,
                                    NULL); //открываем файл для сохранения
        if(hFile == INVALID_HANDLE_VALUE) //проверяем
        {
                InternetCloseHandle(hInetSession); //закрываем сессию
                InternetCloseHandle(hInetFile); //закрываем файл
                return -1;
        }
       
        if(data.bFlag) //осуществляется докачка
                InternetSetFilePointer(hInetFile, GetFileSize(hFile, NULL), NULL, FILE_BEGIN, 0); //сдвигаем чтение на GetFileSize бит

        //начинаем скачку
        do
        {
                InternetReadFile(hInetFile, ReadBuf, sizeof(ReadBuf), &dwRead); //читаем в буффер
                if(dwRead >= 0) //если считали
                {
                        ovlp.hEvent    = NULL;
                        ovlp.OffsetHigh = NULL;
                        ovlp.Offset    = GetFileSize(hFile, NULL);

                        WriteFile(hFile, ReadBuf, dwRead, NULL, &ovlp); //пишем в наш файл
                        dwOffset += dwRead;
                }
                else //не считали, пытаемся повторить скачивание
                {
                        InternetCloseHandle(hInetSession); //закрываем сессию
                        InternetCloseHandle(hInetFile); //закрываем удалённый файл

                        hInetSession = InternetOpen("Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.0; .NET CLR 1.0.2914)",
                                                                                PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, 0);//пытаемся повторить скачку
                        if(hInetSession == NULL)
                        {
                                CloseHandle(hFile);
                                return -1;
                        }

                        hInetFile = InternetOpenUrl(hInetSession, data.Url, NULL, 0, 0, NULL);
                        if(hInetFile == NULL)
                        {
                                CloseHandle(hFile);
                                InternetCloseHandle(hInetSession);
                                return -1;
                        }

                        InternetSetFilePointer(hInetFile, dwOffset, NULL, FILE_BEGIN, NULL);
                }
        }
        while(dwRead);

        //закрываем всё лишнее
        CloseHandle(hFile);
        InternetCloseHandle(hInetSession);
        InternetCloseHandle(hInetFile);
        return 1;
}


Drongo 15-04-2010 16:15 1393379

ganselo, То что есть комментарии это хорошо, но как правильно эту функцию объявить? Как функцию вызвать? Откуда?
Цитата:

Цитата Drongo
куда и как этот код нужно подставить? »

Вот обработчик кнопки Обновить, как мне отсюда правильно вызывать функцю DownloadThread(LPVOID lpParam);
Код:

...
// Обновление----------------------------------------------------------
void __fastcall TForm1::UpdateClick(TObject *Sender)
{
  // Как вызвать эту функцию - DownloadThread?
}
//---------------------------------------------------------------------------
...


ganselo 15-04-2010 19:15 1393506

Код:

void __fastcall TForm1::UpdateClick(TObject *Sender)
{
  HANDLE hThread;
  DOWNLOAD_DATA data;
 
    strcpy(data.Url, "ссылка на файл");
    data.bFlag = false;
    hThread = CreateThread(NULL, 0, DownloadThread, &data, 0, NULL);
    if(hThread == INVALID_HANDLE_VALUE)
    {
          ShowMessage("Error. Can't create thread!");
          return;
    }
    CloseHandle(hThread);
}
//---------------------------------------------------------------------------


Drongo 15-04-2010 21:38 1393589

ganselo, Вот такая ошибка компоновщика получается.

Цитата:

[Компоновщик Ошибка] Unresolved external 'InternetReadFile' referenced from C:\PROGRAM FILES\BORLAND\CBUILDER6\PROJECTS\CLIENTSOCKET\CLIENTSOCKET1.OBJ
[Компоновщик Ошибка] Unresolved external 'InternetOpenA' referenced from C:\PROGRAM FILES\BORLAND\CBUILDER6\PROJECTS\CLIENTSOCKET\CLIENTSOCKET1.OBJ
[Компоновщик Ошибка] Unresolved external 'InternetOpenUrlA' referenced from C:\PROGRAM FILES\BORLAND\CBUILDER6\PROJECTS\CLIENTSOCKET\CLIENTSOCKET1.OBJ
[Компоновщик Ошибка] Unresolved external 'InternetSetFilePointer' referenced from C:\PROGRAM FILES\BORLAND\CBUILDER6\PROJECTS\CLIENTSOCKET\CLIENTSOCKET1.OBJ
[Компоновщик Ошибка] Unresolved external 'InternetCloseHandle' referenced from C:\PROGRAM FILES\BORLAND\CBUILDER6\PROJECTS\CLIENTSOCKET\CLIENTSOCKET1.OBJ
Вот такой код использую. Заголовочный файл WinInet.h включён. Что я делаю не так?
Код:

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "ClientSocket1.h"
#include <WinInet.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

struct DOWNLOAD_DATA
{
  char Url[1024]; //ссылка на файл
  BOOL bFlag; // если TRUE, то осуществляется докачка
};

//закачку я осуществлял в отдельном потоке.
DWORD WINAPI DownloadThread(LPVOID lpParam)
{
  DOWNLOAD_DATA data = *(DOWNLOAD_DATA *)lpParam;
  HINTERNET    hInetSession;
  HINTERNET    hInetFile;
  HANDLE      hFile;
  OVERLAPPED  ovlp;
  DWORD        dwOffset = 0;
  DWORD        dwRead;
  TCHAR        ReadBuf[4*1024];

  hInetSession = InternetOpen("Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.0; .NET CLR 1.0.2914)", PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, 0); //Возвращает хэндл сессии
  if(hInetSession == NULL){ //тут всё понятно
      return -1;
    }

  hInetFile = InternetOpenUrl(hInetSession, data.Url, NULL, 0, 0, NULL); //по сути открываем файл
  if(hInetFile == NULL){ //снова проверяем
      return -1;
    }

  hFile = CreateFile(data.Url, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); //открываем файл для сохранения
  if(hFile == INVALID_HANDLE_VALUE){ //проверяем
      InternetCloseHandle(hInetSession); //закрываем сессию
      InternetCloseHandle(hInetFile); //закрываем файл
      return -1;
    }

  if(data.bFlag) //осуществляется докачка
      InternetSetFilePointer(hInetFile, GetFileSize(hFile, NULL), NULL, FILE_BEGIN, 0); //сдвигаем чтение на GetFileSize бит

  //начинаем скачку
  do{
      InternetReadFile(hInetFile, ReadBuf, sizeof(ReadBuf), &dwRead); //читаем в буффер
      if(dwRead >= 0){ //если считали
        ovlp.hEvent    = NULL;
        ovlp.OffsetHigh = NULL;
        ovlp.Offset    = GetFileSize(hFile, NULL);
        WriteFile(hFile, ReadBuf, dwRead, NULL, &ovlp); //пишем в наш файл
        dwOffset += dwRead;
        }
      else{ //не считали, пытаемся повторить скачивание
        InternetCloseHandle(hInetSession); //закрываем сессию
        InternetCloseHandle(hInetFile); //закрываем удалённый файл
        hInetSession = InternetOpen("Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.0; .NET CLR 1.0.2914)", PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, 0);//пытаемся повторить скачку
        if(hInetSession == NULL){
            CloseHandle(hFile);
            return -1;
          }

        hInetFile = InternetOpenUrl(hInetSession, data.Url, NULL, 0, 0, NULL);
        if(hInetFile == NULL){
            CloseHandle(hFile);
            InternetCloseHandle(hInetSession);
            return -1;
          }

        InternetSetFilePointer(hInetFile, dwOffset, NULL, FILE_BEGIN, NULL);
        }
    }
  while(dwRead);

  //закрываем всё лишнее
  CloseHandle(hFile);
  InternetCloseHandle(hInetSession);
  InternetCloseHandle(hInetFile);
  return 1;
}
//----------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//-------------------------------------------------------------------------
void __fastcall TForm1::UpdateClick(TObject *Sender)
{
  HANDLE hThread;
  DOWNLOAD_DATA data;
 
    strcpy(data.Url, "http://tools.oszone.net/Drongo/QuickKiller_2.20.7z");
    data.bFlag = false;
    hThread = CreateThread(NULL, 0, DownloadThread, &data, 0, NULL);
    if(hThread == INVALID_HANDLE_VALUE)
    {
          ShowMessage("Error. Can't create thread!");
          return;
    }
    CloseHandle(hThread);
}
//---------------------------------------------------------------------------


ganselo 15-04-2010 23:54 1393693

Подключите к проекту библиотеку wininet.lib (Project->Add to project->wininet.lib).

Drongo 16-04-2010 10:40 1393918

ganselo, О, теперь всё скомпилировалось. :) Но по нажатию на кнопку Обновить, ничего не скачивается.

ganselo 16-04-2010 12:52 1394003

Думаю проблема в этом:
Код:

hFile = CreateFile(data.Url, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); //открываем файл для сохранения
Первый параметр CreateFile - это название файла (в данном случае, если файл не существует, то CreateFile создаёт его). Попробуйте вместо data.Url написать например "C:\\QuickKiller_2.20.7z". (Название файла лучше парсить из data.Url).

Drongo 16-04-2010 15:09 1394100

ganselo, Ух, класс, здорово. Получилось. :up: Задача макси решена.

А как теперь реализовать эту часть?
Цитата:

Цитата Drongo
чтобы программа скачала только те модули, которые новее тех, которые присутствуют рядом с программой »

И как правильно сделать ход загрузки? По логике я должен узнать размер файла в байтах и задать это значение ProgressBar. Только, как я понял, CreateFile создаёт файл на диске и поэтому если так делать как я делаю, будет нулевой размер, а как правильн узнать размер скачиваемого файла, дату создания и имя?
Код:

...
  if(data.bFlag) //осуществляется докачка
      InternetSetFilePointer(hInetFile, GetFileSize(hFile, NULL), NULL, FILE_BEGIN, 0); //сдвигаем чтение на GetFileSize бит

  // Ход загрузки
  Form1->ProgressBar1->Position = 0;
  Form1->ProgressBar1->Max = GetFileSize(hFile, NULL);

  //начинаем скачку
  do{
      Form1->ProgressBar1->StepIt(); // Добавил ход загрузки

      InternetReadFile(hInetFile, ReadBuf, sizeof(ReadBuf), &dwRead); //читаем в буффер
      if(dwRead >= 0){ //если считали
        ovlp.hEvent    = NULL;
        ovlp.OffsetHigh = NULL;
        ovlp.Offset    = GetFileSize(hFile, NULL);
        WriteFile(hFile, ReadBuf, dwRead, NULL, &ovlp); //пишем в наш файл
        dwOffset += dwRead;
        }
      else{ //не считали, пытаемся повторить скачивание
...


ganselo 16-04-2010 17:19 1394210

Цитата:

Цитата Drongo
А как теперь реализовать эту часть?
Цитата Drongo:
чтобы программа скачала только те модули, которые новее тех, которые присутствуют рядом с программой » »

Ну например:
1) проверить версию, которая лежит рядом с прогой.
2) Попытаться загрузить версию > из 1). Если ошибка, то новой версии нет иначе качаем новую.

Цитата:

Цитата Drongo
а как правильн узнать размер скачиваемого файла »

Код:

// открываем запрос
    LPCWSTR rgszAcceptTypes[2] = {pstLRF_Params->szDocumentType,NULL};
    hRequest = ::HttpOpenRequest(hSessiont, L"GET", szUrlPath, L"HTTP/1.1", NULL,rgszAcceptTypes,
                                INTERNET_FLAG_KEEP_CONNECTION,1);
        if (hRequest == NULL)  throw L"HttpOpenRequest Error";
               
 // посылаем запрос
    fResult= ::HttpSendRequest(hRequest, NULL,0, NULL,0);
              if (!fResult) throw L"HttpSendRequest Error";

    // получаем  информацию о размере данных
    fResult = HttpQueryInfo(hRequest,HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER,&dwFileLength,&dwDwordLength,NULL);
    if (!fResult && (GetLastError() == ERROR_HTTP_HEADER_NOT_FOUND)) 
        dwFileLength = (DWORD)pstLRF_Params->uFileLength;

Цитата:

Цитата Drongo
дату создания и имя? »

Не уверен, что можно узнать дату создания файла, а имя (это вы про названия файла?) можно парсить из data.Url.

Drongo 16-04-2010 19:25 1394284

Цитата:

Цитата ganselo
Ну например:
1) проверить версию, которая лежит рядом с прогой.
2) Попытаться загрузить версию > из 1). Если ошибка, то новой версии нет иначе качаем новую. »

С этим я разберусь уже, можно обновлять если размер скачиваемого обновления больше того модуля, что лежит рядом, поскольку обновлённый модуль всегда будет немного увеличиваться.
Цитата:

Цитата ganselo
можно парсить из data.Url. »

Это да, смогу тоже, ищем последний слеш "/" + 1. И это будет начало нашего имени файла.

А вот с кодом, который узнаёт размер файла, опять не могу совладать, куда и как правильно его пристроить? :not-me:

ganselo 16-04-2010 19:33 1394293

Цитата:

Цитата Drongo
куда и как правильно его пристроить? »

Ну например после создания сессии вызывать данный код.
Код я взял с инета и увы под рукой нет компилятора(... не могу проверить.

Drongo 16-04-2010 19:49 1394308

Цитата:

Цитата ganselo
Ну например после создания сессии вызывать данный код. »

Я так тоже подумал, но при компиляции, нагнуло 26 ошибок. И что там исправлять на что, тоже без понятия.

ganselo 17-04-2010 00:21 1394489

Вложений: 1
Вот вариант без WinInet. Используем TIdHTTP.


Изменяюсь... Не всё за архивировал.

Drongo 17-04-2010 13:39 1394782

ganselo, Да, но там только исполнимый файл, и не хватает ещё одного файла .cpp, как раз главного. :)

ganselo 17-04-2010 13:55 1394791

Drongo, перезалил.

Drongo 17-04-2010 18:48 1394953

ganselo, Спасибо, вопрос решён окончательно! :up: Из проекта нужно удалить LIBMYSQL.LIB и всё будет в порядке, я когда первый раз открыл проект, мне нагнуло ошибок, что мол, у вас нет компонента TIdHTTP, а вкладки Indy Clients у меня действительно не было, результат убирания неиспользуемых компонентов, пытался доустановить - результат нулевой, сносил билдер, уже нервничать начал, запустил в конце концов C++ Builder 2009 там всё есть, чуть подправил, всё компилится, но заставить работать так и не удалось, по всей видимости это связано с типами данных\переменных, а в этой среде я только-только осваиваюсь. В общем переустановил ещё раз Builder 6.0 все вкладки появились, скомпилилось и работает. Спасибо, ganselo!!!

P.S. Не удаляй пока что архив. Я его ещё раз перезакачаю.

ganselo 17-04-2010 19:32 1395000

Цитата:

Цитата Drongo
Из проекта нужно удалить LIBMYSQL.LIB »

У меня в билдере используется данная либа.

Цитата:

Цитата Drongo
В общем переустановил ещё раз Builder 6.0 все вкладки появились, скомпилилось и работает. »

Ну я писал всё это в Builder 6.0.

Drongo 17-04-2010 19:38 1395008

Цитата:

Цитата ganselo
У меня в билдере используется данная либа »

Может у меня покоцаный билдер? Но главное, что работает. :yahoo:

ganselo 17-04-2010 21:07 1395077

Цитата:

Цитата Drongo
Может у меня покоцаный билдер?[/post]

Нет, просто это не стандартная либа....


Время: 18:39.

Время: 18:39.
© OSzone.net 2001-