На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела Visual C++ / MFC / WTL (далее Раздела)
1) На Раздел распространяются все Правила Форума.
2) Перед тем, как создать новый топик, убедитесь, что Вы читали Правила создания тем в Разделе.
3) Вопросы, не связанные с программированием (настройки MS Visual Studio, книги, библиотеки и т.д.),
обсуждаются в разделе C/C++: Прочее
4) Вопросы разработки .NET (Windows Form, C++/CLI и т.п.) приложений на Visual C++/C# обсуждаются в разделе .NET.
5) Нарушение Правил может повлечь наказание со стороны модераторов.

Полезные ссылки:
user posted image FAQ Раздела user posted image Обновления для FAQ Раздела user posted image Поиск по Разделу user posted image MSDN Library Online
Модераторы: ElcnU
Страницы: (8) [1] 2 3 ...  7 8 все  ( Перейти к последнему сообщению )  
> Visual Studio 2008 + COM Port
    В С++ Builder 6 создана программа обмена по COM порту в асинхронном режиме. Программа выполнена на трех потоках VinApi. Ничего особенного. Все просто. Основные функции ниже по тексту
    ExpandedWrap disabled
      #include <vcl.h>
      #pragma hdrstop
       
      #include "Unit1.h"
      //---------------------------------------------------------------------------
      #pragma package(smart_init)
      #pragma resource "*.dfm"
      #include <iostream>
      #include <windows.h>
      #include <stdio.h>
      #include <Math.hpp>
      #include <math.h>
       
      TForm1 *Form1;
       
      //---------------------------------------------------------------------------
      // дефайны
       
      //ёмкость буфера приема
      #define BUFSIZE_RD 5000
      //ёмкость буфера передачи
      #define BUFSIZE_WR 128
       
      //---------------------------------------------------------------------------
      // дескрипторы
       
      HANDLE COMport;
      HANDLE writer;
      HANDLE reader;
      HANDLE main;
       
      ///будем использовать для операций чтения (см. поток ReadThread)
      OVERLAPPED overlapped;
      //будем использовать для операций записи (см. поток WriteThread)
      OVERLAPPED overlappedwr;
       
      //---------------------------------------------------------------------------
      // глобальные переменные
       
      // строка определения com порта
      AnsiString com_port;
       
      // флаг приема
      bool FlComRead;
      // флаг цикла в потоке приема
      bool flag;
       
      bool FlBeg;
       
      //приёмный и передающий буферы
      unsigned char bufrd[BUFSIZE_RD], bufwr[BUFSIZE_WR];
      // контрольная сумма пакета передачи
      unsigned char TxBufCrc;
      unsigned char PrCanal;
      unsigned char PrSect;
       
      // счетчик приемов
      unsigned int counterRX;
      unsigned int counterTX;
      unsigned int counterRP;
      unsigned int Dist;
      unsigned int Distm;
       
      // скорость бод
      unsigned long BaudR;
       
      //---------------------------------------------------------------------------
      __fastcall TForm1::TForm1(TComponent* Owner)
              : TForm(Owner)
      {
      }
       
      //---------------------------------------------------------------------------
      // функции
       
      //---------------------------------------------------------------------------
       
      // задержка
      void wait (int wt)
      {
        while (wt--);
      }
       
      //---------------------------------------------------------------------------
      // Принятые байты в HEX
       
      void hexdumpstrRead ( char src[], size_t size)
      {
        Form1->Memo2->Clear();
        static const char hex[] = "0123456789abcdef";
        char *result = (char *)malloc((size > 0)? size*3: 1),
          *p = result;
       
        if (result) {
          while (size-- > 0) {
            *p++ = hex[(*src >> 4) & 0x0f];
            *p++ = hex[*src++ & 0x0f];
            *p++ = ' ';
          }
          p[(p > result)? -1: 0] = 0;
        }
        Form1->Memo2->Lines->Add(result);
      }
      //---------------------------------------------------------------------------
      // Переданные байты в HEX
       
      void hexdumpstrWrite ( char src[], size_t size)
      {
        Form1->Memo1->Clear();
        static const char hex[] = "0123456789abcdef";
        char *result = (char *)malloc((size > 0)? size*3: 1),
          *p = result;
       
        if (result) {
          while (size-- > 0) {
            *p++ = hex[(*src >> 4) & 0x0f];
            *p++ = hex[*src++ & 0x0f];
            *p++ = ' ';
          }
          p[(p > result)? -1: 0] = 0;
        }
        Form1->Memo1->Lines->Add(result);
      }
       
      //---------------------------------------------------------------------------
      // Вывод принятых байтов на экран
       
      void ReadPrinting(DWORD btr)
      {
        char Buffer[20];
        unsigned int nb, tm;
        String stroka, temp_str;
        counterRP++;
      //  sprintf(Buffer," %d", counterRP);
      //  Form1->Label9->Caption = Buffer;
       
        // Перенос буфера в строку.
        for(UINT32 i=0; i<btr ; i++)
        {
          stroka = stroka + IntToHex(bufrd[i], 2) + ' ';
        }
        // кличество принятых байт
      //  Form1->Panel3->Caption = "Всего принято байт: " + IntToStr(counterRX);
        // контрольные символы
        for (UINT32 i = 0; i < sizeof(bufrd); i++)
        {
          if(bufrd[i] == 0xa1 && bufrd[i + 1] == 0xa2 && !FlBeg)
          {
            FlBeg = 1;
            nb = ((bufrd[i + 2] << 8) | bufrd[i + 3]);
            sprintf(Buffer,"Кол. инфо байт в пакете: %d", nb);
            Form1->Panel2->Caption = Buffer;
            
            Form1->Memo2->Clear();
      //      wait(30000000); // ЗАПЛАТКА.
       
            if(Form1 -> CheckBox4 -> Checked)
              Sleep(100);
            else
              Sleep(400);
       
            Form1->Panel1->Caption = "";
       
      //      if(!FlComRead) FlComRead = 1;
          }
          // определение последнего канала
          else if(bufrd[i] == 0x55 && bufrd[i + 1] == 0xaa && bufrd[i + 3] == 0x75)
          {
            //
            tm = bufrd[i + 6];
      //      Sleep(100);
      //      wait(300000); // ЗАПЛАТКА.
            if(!FlComRead) FlComRead = 1;
            break;
          }
        }
        // количество принятых байт
        Form1->Panel3->Caption = "Всего принято байт: " + IntToStr(counterRX);
        // Вывод
        Form1->Memo2->Lines->Add(stroka);
        // Очистить буфер (чтобы данные не накладывались друг на друга).
        memset(bufrd, 0, BUFSIZE_RD);
      }
       
      //---------------------------------------------------------------------------
      // Поток чтения
       
      DWORD WINAPI ReadThread( LPVOID lpParam )
      {
        // буфер для вывода строк
        char Buffer[20];
        // структура текущего состояния порта,
        // используется для определения количества принятых в порт байт
        COMSTAT comstat;
        // переменная temp используется в качестве заглушки
        DWORD btr, temp, mask, signal;
        // создать сигнальный объект-событие для асинхронных операций
        overlapped.hEvent = CreateEvent(NULL, true, true, NULL);
        // установить маску на срабатывание по событию приёма байта в порт
        SetCommMask(COMport, EV_RXCHAR);
        // пока поток не будет прерван, выполняем цикл
      //  while(1)
        while(!flag)
        {
          //ожидать события приёма байта (это и есть перекрываемая операция)
          WaitCommEvent(COMport, &mask, &overlapped);
          //приостановить поток до прихода байта
          signal = WaitForSingleObject(overlapped.hEvent, INFINITE);
          //если событие прихода байта произошло
          if(signal == WAIT_OBJECT_0)
          {
            //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent
            if(GetOverlappedResult(COMport, &overlapped, &temp, true))
            {
              //если произошло именно событие прихода байта
              if((mask & EV_RXCHAR)!=0)
              {
      //          Sleep(10);
                // нужно заполнить структуру COMSTAT
                ClearCommError(COMport, &temp, &comstat);
                // и получить из неё количество принятых байтов
                btr = comstat.cbInQue;
                // если действительно есть байты для чтения
                if(btr)
                {
      //            Sleep(10);
                  // прочитать байты из порта в буфер программы
                  ReadFile(COMport, bufrd, btr, &temp, &overlapped);
                  // увеличиваем счётчик байтов
                  counterRX+=btr;
      //            sprintf(Buffer," %d", counterRX);
      //            Form1->Label9->Caption = Buffer;
                  // вызываем функцию для вывода данных на экран
                  ReadPrinting(btr);
      //            hexdumpstrRead(bufrd, 846);
                }
              }
            }
          }
        Sleep(1);
        }
        // таймаут потока
      //  Sleep(1);
        CloseHandle(overlapped.hEvent);
      }
       
      //---------------------------------------------------------------------------
      // Поток записи WriteThread
       
      //главная функция потока, выполняет передачу байтов из буфера в COM-порт
      DWORD WINAPI WriteThread(LPVOID lpParam)
      {
        CONST HANDLE hMutex = (CONST HANDLE)lpParam;
       
        // temp - переменная-заглушка
        DWORD temp, signal;
        // текущий рабочий буфер
        char Buffer[20];
       
        //создать событие
        overlappedwr.hEvent = CreateEvent(NULL, true, true, NULL);
        while(1)
        {
          //очистить передающий буфер порта
          PurgeComm(COMport, PURGE_TXCLEAR);
          //записать байты в порт (перекрываемая операция!)
      //    WriteFile(COMport, bufwr, strlen(bufwr), &temp, &overlappedwr);
          WriteFile(COMport, bufwr, sizeof(bufwr), &temp, &overlappedwr);
       
          //приостановить поток, пока не завершится перекрываемая операция WriteFile
          signal = WaitForSingleObject(overlappedwr.hEvent, INFINITE);
       
          //если операция завершилась успешно
          if((signal == WAIT_OBJECT_0) && (GetOverlappedResult(COMport, &overlappedwr, &temp, true)))
          {
            //вывести сообщение об этом в строке состояния
            sprintf(Buffer,"Передача  %d  прошла успешно", counterTX);
            Form1->Panel1->Caption = Buffer;
          }
          //иначе вывести в строке состояния сообщение об ошибке
          else {Form1->Panel1->Caption = "Ошибка передачи";}
          // Приостановить поток записи
          SuspendThread(writer);
        Sleep(1);
        }
        // таймаут в потоке
      //  Sleep(1);
      }
       
      //---------------------------------------------------------------------------
      // Поток main
       
      DWORD WINAPI MainThread(LPVOID lpParam)
      {
        // текущий рабочий буфер
        char Buffer[20];
       
        while(1)
        {
          // если принят пакет
          if(FlComRead)
          {
            FlBeg = 0;
            FlComRead = 0;
       
            sprintf(Buffer,"Дистанция: %d м", Distm);
            Form1->Label16->Caption = Buffer;
       
       
            // включение режима снятия характеристики
            if(Form1->CheckBox1 -> Checked)
            {
              bufwr[126] |= (1 << 7);
              Form1->CSpinEdit3->Enabled = true;
              Form1->CSpinEdit4->Enabled = true;
            }
            else
            {
              bufwr[126] &= ~(1 << 7);
              Form1->CSpinEdit3->Enabled = false;
              Form1->CSpinEdit4->Enabled = false;
            }
       
            // включение режима снятия характеристики
            if(Form1->CheckBox2 -> Checked)
            {
              bufwr[123] |= (1 << 2);
              bufwr[126] = 80;
            }
            else
            {
              bufwr[123] &= ~(1 << 2);
            }
       
            // контрольная сумма в 0
            TxBufCrc = 0;
              // подсчет контрольной суммы пакета управл.
              for(int i = 1; i < 127; i++)
              {
                TxBufCrc = TxBufCrc + bufwr[i];
                }
            // контрольная сумма упр. пакета
            bufwr[127] = TxBufCrc;
            // вывод управл пакета в Memo 1
            hexdumpstrWrite(bufwr,128);
            // номер передачи
            counterTX++;
            // количество принятых байт в 0
            counterRX = 0;
                //чтобы данные не накладывались друг на друга
            //очистить передающий буфер порта
            PurgeComm(COMport, PURGE_TXCLEAR);
       
            // включение передачи
            if(Form1->CheckBox3 -> Checked)
            {
              //активировать поток записи данных в порт
              ResumeThread(writer);
            }
          }
        Sleep(1);
        }
        // таймаут в потоке
      //  Sleep(1);
      }
       
      //---------------------------------------------------------------------------
      // Com установки
       
      void Com_Setting()
      {
        COMMTIMEOUTS timeouts;
       
        com_port += "\\\\.\\";  // com_port.c_str()
        com_port += Form1->ComboBox1->Text;
        COMport = CreateFile(Form1->ComboBox1->Text.c_str(),GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
       
        if(COMport==INVALID_HANDLE_VALUE)
        {
            if(GetLastError()==ERROR_FILE_NOT_FOUND)
            {
                MessageBox(NULL,"serial port does not exist.","",MB_OK);
            }
          MessageBox(NULL,"serial port error","", MB_OK);
        }
       
        DCB dcbSerialParams = {0};
        dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
        if (!GetCommState(COMport, &dcbSerialParams))
        {
            MessageBox(NULL,"getting state error\n","",MB_OK);
        }
        dcbSerialParams.BaudRate=BaudR;
        dcbSerialParams.ByteSize=8;
        dcbSerialParams.StopBits=ONESTOPBIT;
        dcbSerialParams.Parity=NOPARITY;
        if(!SetCommState(COMport, &dcbSerialParams))
        {
            MessageBox(NULL,"error setting serial port state\n","",MB_OK);
        }
       
        // Установка таймаутов.
        timeouts.ReadIntervalTimeout = 10;        // таймаут между двумя символами
        timeouts.ReadTotalTimeoutMultiplier = 0;  // общий таймаут операции чтения
        timeouts.ReadTotalTimeoutConstant = 0;    // константа для общего таймаута операции чтения
        timeouts.WriteTotalTimeoutMultiplier = 0; // общий таймаут операции записи
        timeouts.WriteTotalTimeoutConstant = 0;   // константа для общего таймаута операции записи
       
        // Загрузить структуру таймаутов в порт.
        if(!SetCommTimeouts(COMport, &timeouts))
        {
          // Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния.
          MessageBox(NULL,"Не удалось установить тайм-ауты\n","",MB_OK);
          return;
        }
       
        // Установить размеры очередей приёма и передачи
        SetupComm(COMport,2000,2000);
        // Очистка буферов порта
        PurgeComm(COMport,PURGE_RXCLEAR);
        // очистить строку имени порта
        com_port = "";
       
      }


    Теперь стоит задаче переместить все в VisualStudio 2008 поскольку к данному коду необходимо будет приклепать сложную графику GLUT.
    Подскажите пожалуйста как данный код заставить работать в VisualStudio 2008? Пока совершенно не врубаюсь каким образом это можно сделать. Пробовал делать в WindowsForm используя компонент serialPort. Может я не прав но похоже полная ерунда. Компонент не приспособлен для асинхронного режима. А нужно повторить именно асинхронный режим COM порта. Это связано с жесткими временными рамками приема и ответной передачи данных.
    Если не сложно, подскажите пожалуйста коротко путь по которому функции данного кода + потоки можно приспособить в WindowsForm VisualStudio 2008.
    Спасибо.
      тупо копипаст не пробовал? :D
        В VisualStudio 2008 можно писать как на практически том же C++, что и Билдере, так и на шарпе.
        Воспроизвести С++ код из Билдера несложно, т.к. в данном случае VCL-ность не задействована (правда, и синхронизации с главным потоком я не заметил)

        Компонент serialPort относится к шарпу, и работает именно асинхронно.
          Для обмена в асинхронном режиме зачем три потока? Ну один, ну два, ну никак не три.

          Можно это в шарпе сделать с SerialPort, легко и удобно, только со сложной графикой windowsforms не очень подходит в плане производительности.
            Спасибо. Если так, тогда как это делается на практике? WindowsForm необходим, поскольку нужен интерфейс с кнопками и окнами вывода информации. Сам интерфейс составить несложно. Не врубаюсь как теперь туда подключить потоки с кодом внутри и необходимыми функциями. Если можно коротко по пунктам как это сделать.
              Поток приёма уже есть внутри serialPort. Его событие DataReceived вызывается из этого (вторичного) потока, так что не забыть про синхронизацию.
                Цитата Acvarif @
                Спасибо. Если так, тогда как это делается на практике? WindowsForm необходим, поскольку нужен интерфейс с кнопками и окнами вывода информации. Сам интерфейс составить несложно. Не врубаюсь как теперь туда подключить потоки с кодом внутри и необходимыми функциями. Если можно коротко по пунктам как это сделать.


                Сделай сначала каркас на шарпе с сериалпортом, чтением записью кнопками, а потом переноси туда свои функции с байтовыми операциями, ну тут рутина, менять кое-что придется, это ты сам.
                  Пробововал serialPort Может толком не разобрался. Но получилась ерунда. Полнейшая временная разсинхронизация. В чем суть. Потоки приема и передачи в программе на Builder жестко привязаны по времени. Тоесть как только закончился прием непрерывного пакета данных формируется флаг разрешения для передачи обратно так называемого пакета управления. Время между окончанием приема и началом обратной передачи должно уложиться в 5..10 ms. Там организован прием по событию прихода байта на порт. Поэтому скорее всего придется запускать подобные потоки в WindowsForm Вопрос. Можно-ли будет встроить в потоки код типа, где имеются функции типа WaitCommEvent и т.п.?
                  ExpandedWrap disabled
                    // буфер для вывода строк
                      char Buffer[20];
                      // структура текущего состояния порта,
                      // используется для определения количества принятых в порт байт
                      COMSTAT comstat;
                      // переменная temp используется в качестве заглушки
                      DWORD btr, temp, mask, signal;
                      // создать сигнальный объект-событие для асинхронных операций
                      overlapped.hEvent = CreateEvent(NULL, true, true, NULL);
                      // установить маску на срабатывание по событию приёма байта в порт
                      SetCommMask(COMport, EV_RXCHAR);
                      // пока поток не будет прерван, выполняем цикл
                    //  while(1)
                      while(!flag)
                      {
                        //ожидать события приёма байта (это и есть перекрываемая операция)
                        WaitCommEvent(COMport, &mask, &overlapped);
                        //приостановить поток до прихода байта
                        signal = WaitForSingleObject(overlapped.hEvent, INFINITE);
                        //если событие прихода байта произошло
                        if(signal == WAIT_OBJECT_0)
                        {
                          //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent
                          if(GetOverlappedResult(COMport, &overlapped, &temp, true))
                          {
                            //если произошло именно событие прихода байта
                            if((mask & EV_RXCHAR)!=0)
                            {
                    //          Sleep(10);
                              // нужно заполнить структуру COMSTAT
                              ClearCommError(COMport, &temp, &comstat);
                              // и получить из неё количество принятых байтов
                              btr = comstat.cbInQue;
                              // если действительно есть байты для чтения
                              if(btr)
                              {
                    //            Sleep(10);
                                // прочитать байты из порта в буфер программы
                                ReadFile(COMport, bufrd, btr, &temp, &overlapped);
                                // увеличиваем счётчик байтов
                                counterRX+=btr;
                    //            sprintf(Buffer," %d", counterRX);
                    //            Form1->Label9->Caption = Buffer;
                                // вызываем функцию для вывода данных на экран
                                ReadPrinting(btr);
                    //            hexdumpstrRead(bufrd, 846);
                              }
                            }
                          }
                        }
                      Sleep(1);

                  Кстати, а почему WindowsForm c графикой GLUT непроизводителен? Что производительнее?
                  Да, вот источник который использовался для построения кода http://piclist.ru/S-COM-THREAD-RUS/S-COM-THREAD-RUS.html#22
                  Сообщение отредактировано: Acvarif -
                    >Пробововал serialPort
                    Как именно пробовал?
                      Цитата Acvarif @
                      Потоки приема и передачи в программе на Builder жестко привязаны по времени. Тоесть как только закончился прием непрерывного пакета данных формируется флаг разрешения для передачи обратно так называемого пакета управления.

                      Вот в этом-то тебе и повезо.
                      Дело в том, что ф-ии ReadFile/WriteFile потоко-не-безопасны.
                      В твоём примере синхронизации я не заметил, т.е. ты фактически
                      синхронизируешь операции общим ходом алгоритма.
                      Значит, что-нибудь когда-нибудь заглючит..
                        Вот поток в котором осуществляется прием пакета с последующей передачей. Прием принимает, но не всегда, а передача идет не сразу за примом. Разрывы большие, не стабильные по времени. Иногда до 1 сек. В то время как обмен между компом и устройством производится один раз в сек.
                        ExpandedWrap disabled
                                 void Form1::ThreadWork(){
                                      buf_rez = Convert::ToInt32( textBox1->Text );      
                           
                                      while(ThreadWork_Stop){
                                          
                                          //this->Invoke(delInstatnce);
                                          mesRead = this->serialPort1->ReadExisting();
                                          BufferSendCom[0] = bit1;
                                          BufferSendCom[1] = bit2;
                                          BufferSendCom[2] = bit3;
                                          //BufferSendCom[3] = bit4;
                                          
                                          BufferSendCom[124] = bit126;
                                          //timer1->Enabled = true;
                                          //this->Invoke(delInstatnce);
                                          if(mesRead->Length > 1 ){
                                              std::cout << Convert::ToInt32(mesRead[0]) << "ddddsdsd" << std::endl;
                           
                                              if( Convert::ToByte(mesRead[0]) == 63){                    
                                                  //form2->Send_Buffer(mesRead);
                                                  for(int i = 0; i <= 127; i++){
                                                      bufer += BufferSendCom[i];
                                                      BF += BufferSendCom[i];
                                                  }
                                                  
                                                      BufferSendCom[127] = bufer;
                                                      BF += BufferSendCom[127];
                                                      this->serialPort1->Write(BF);
                                                      //Show_Send();
                                                      BF = "";
                                                      bufer = NULL;
                                              }
                                          }
                                          //std::cout << "//////////////////////" << std::endl;
                                          //*****************************
                                          mesRead = "";      
                                          Sleep(1100);
                                      }//while
                                      
                              }

                        Да собственно в коде нет ничего секретного, да к тому же он сырой. В срепке проект в архиве. Прикреплённый файлПрикреплённый файлAmuleTest.rar (159,24 Кбайт, скачиваний: 115)
                          похоже, что осуществляется поллинг (опрос, нету ли чего в порту) - это нехорошо. Как уже сказано, у порта есть событие OnDataReceived, ему нужно назначить обработчик, и при получении данных обработчик вызовется (по сути - аналог WaitCommEvent)

                          (и с синхронизхацией опять беда - в поточной, как я понимаю, функции, прямой доступ к GUI Convert::ToInt32( textBox1->Text ))
                            В общих чертах понятно. Если OnDataReceived сработает так как WaitCommEvent то должно быть то, что нужно. Если не сложно, можно пример кода serialPort + OnDataReceived.
                            Да, синхронизация никакая. Буду поправлять. Это второй этам. Пока нужно понять какой дорожкой пойти. Дальше работать с serialPort или..
                                Спасибо. Становится понятней. Для проверки работы по событию сделал так
                                ExpandedWrap disabled
                                  private: System::Void serialPort1_DataReceived(System::Object^  sender, System::IO::Ports::SerialDataReceivedEventArgs^  e)
                                           {
                                              counterRX++;
                                              std::cout << Convert::ToInt32(counterRX) << std::endl;
                                           }

                                Работает. Счетчик считает событя по очереди как и должно быть.
                                Попробую подключить функцию накопления данных в буфере.

                                Добавлено
                                Для дальнейшей проверки пытаюсь вывести часть принятых по сом данных в окно textBox2 (со свойством Multiline - true)
                                ExpandedWrap disabled
                                  private: System::Void serialPort1_DataReceived(System::Object^  sender, System::IO::Ports::SerialDataReceivedEventArgs^  e)
                                           {
                                              mesRead = this->serialPort1->ReadExisting();
                                              for (int i = 0; i < 30; i++)
                                              {
                                                  this->textBox2->Text += mesRead[i].ToString()+"\r\n";
                                              }
                                           }

                                Возникает ошибка
                                ExpandedWrap disabled
                                  Cross-thread operation not valid: Control 'textBox2' accessed from a thread other than the thread it was created on.

                                В чем тут хитрость? Почему операция кросс-поточная?
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (8) [1] 2 3 ...  7 8 все


                                Рейтинг@Mail.ru
                                [ Script execution time: 5,7771 ]   [ 19 queries used ]   [ Generated: 29.03.24, 00:43 GMT ]