Страница 1 из 3

Про сервер MS SQL - которого нет ...

Добавлено: 19 Июнь 2006, 19:02
Игорь Столяров
Привет всем ! Есть вопрос по существу, точнее по MS SQL ...

Написана программа, которая периодически подключается к MS SQL серверу и что-то там забирает или записывает. Все очень хорошо, до тех пор пока SQL сервер доступен. Как только он исчезает из сети - прогамма вылетает, либо с ошибкой, либо с GPF при попытке получить доступ к БД ... :(
Пробовал открывать БД SQL и ABC классами и просто Open().

Отсюда вопрос - можно как-нибудь проверить наличие и готовность MS SQL сервера не подключаясь к БД. Т.е. просто отработать ситуацию - если сервер не доступен, то и не пытаться с ним работать ... Спасибо за любую подсказку !

Добавлено: 19 Июнь 2006, 20:39
Andrew Listiev
Это как так вылетает при простом Open()?????? Не верю! Версия Клары и MS SQL в студию!

Добавлено: 19 Июнь 2006, 21:15
Игорь Столяров
Clarion C63EE 9053 + MSSQL 2000 кажется SP1

Программа работает в циклической обработке, т.е. раз в минуту (например) пытается получить подключение к MS SQL и сделать нечто.
Какое-то время по Open(TableName); If ErrorCode then ... можно отлавливать "отваливший" сервер. Но через какое-то время работы - это заканчивается GPF ...
Проверено, к сожалению, не однократно и на разных сетях и серверах.
Отсюда и возникла идея - проверить наличие сервера и его доступность ДО обращения к нему, желательно без средств Clarion, может API есть ?. Мой опыт общения с MS SQL из Clarion - полгода, и опыта и готовых наработок нет.
Или, если можно, опишите ПРАВИЛЬНУЮ с точки зрения Clarion процедуру проверки наличия сервера и подключения к нему (есстественно не к самомму серверу, а БД на нем).
Может я просто козлю ? ;)

Добавлено: 19 Июнь 2006, 21:34
Дед Пахом
Про MS SQL не скажу, а драйвера ODBC и Pervasive действительно при потере соединения при обращении к таблице вызывают моментальный gpf, причём это началось на C63 с первого же патча, ещё на C62 в такой ситуации было нормальное сообщение об ошибке и возможность работать дальше.
По поводу не-Clarion средств - наверно, и odbc api можно заюзать (проще всего), и что-то типа sql-dmo конкретно для ms sql.

Добавлено: 19 Июнь 2006, 22:30
Игорь Столяров
Ну с P.SQL - я как раз-таки разобраться могу. Там, как крайность, можно "в лоб" пробить список через Btrieve.
А вот с MS SQL, что-то странное. С63 - вообще преподносился как средство работы с MS SQL, и сразу вырубились ХП без параметров (в 9053 - исправили) + GPF при обращении ... :(
Ну не может такого быть. Может быть Clarion и его драйвера затачиваются уже под MS SQL 2005 ?!

А нет ли какого-нибудь примерчика с обращением к MS SQL через ODBC - я к сожалению это юзал последний раз - никогда ... ;)

Добавлено: 20 Июнь 2006, 1:52
StillZero
определить список серверов может через сокеты
у самого руки не дошли, так что только исходный текст

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

Перечислить все SQL сервера в системе.

public static void FindSqlServers (ISQLServerInfoCollection list, int timeout, int port)
       {
          EndPoint point = new IPEndPoint(IPAddress.Broadcast, port);

         Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
         socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
            socket.Blocking = false;

            byte[] data = {0x02};
         socket.SendTo(data, point);

         byte[] buffer = new byte[512];

          Thread.Sleep(timeout);

          try
         {
              int size = socket.ReceiveFrom(buffer, ref point);
               while (size > 0)
             {
                  SQLServerInfoImpl info = new SQLServerInfoImpl(new string(Encoding.ASCII.GetChars(buffer, 0, size)));

                   if (!list.Contains(info))
                       list.Add(info);

                 size = socket.ReceiveFrom(buffer, ref point);
               }
          }
          catch {}
      }

public SQLServerInfoImpl (string reply)
       {
          string[] data = reply.Split(';', '\0');

            for (int index = 0; index < data.Length; index++)
            {
              if (data[index] == "InstanceName")
                {
                  _instanceName = data[index + 1];
                    index++;
                }

              if (data[index] == "ServerName")
              {
                  _hostName = data[index + 1];
                    index++;
                }

              if (data[index] == "tcp")
             {
                  _port = Convert.ToInt32(data[index + 1]);
                   index++;
                }
          }

          if (_instanceName == "MSSQLSERVER")
               _instanceName = null;
       }


Более правильный вариант
SOCKET hSocket;
BOOL fBroadcast = TRUE;
Byte Data[]={0x02};
Byte Buf[65535];
int i,iBeg,tRes,tLenSA,tLenBufRecv,tFlagStep;
u_long tLenBufIOCTL;
TStringList *MyStr;
AnsiString S,S2,S3;

MyStr=new TStringList;

sockaddr_in saSQLSend,saSQLRecv;

ListBox1->Clear();
MyStr->Clear();


hSocket=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(hSocket==INVALID_SOCKET) return;


tRes=setsockopt(hSocket,SOL_SOCKET,SO_BROADCAST,(CHAR *)&fBroadcast,sizeof(BOOL));
if(tRes==SOCKET_ERROR) {closesocket(hSocket);return;}


saSQLSend.sin_family = AF_INET;
saSQLSend.sin_port= htons ( 1434 );
saSQLSend.sin_addr.s_addr = htonl ( INADDR_BROADCAST );
tRes=sendto(hSocket,Data,1,0,(SOCKADDR *)&saSQLSend,sizeof(SOCKADDR_IN));
if(tRes==SOCKET_ERROR) {closesocket(hSocket);return;}

Sleep(100);


tRes=ioctlsocket(hSocket,FIONREAD,&tLenBufIOCTL);
if(tRes==SOCKET_ERROR) {closesocket(hSocket);return;}


if(tLenBufIOCTL>0)
        {
        tFlagStep=0;
        tLenSA=sizeof(SOCKADDR_IN);
        saSQLRecv=saSQLSend;
        tLenBufRecv=recvfrom(hSocket,Buf,65535,0,(SOCKADDR *) &saSQLRecv,&tLenSA);
        if(tLenBufRecv==SOCKET_ERROR) {closesocket(hSocket);return;}
        iBeg=1;
        S2=(char *)(Buf+3);
        tLenBufRecv-=3;
        for(i=1;i<=tLenBufRecv;i++)
                {
                if(S2[i]==';')
                        {
                        S=S2.SubString(iBeg,i-iBeg);
                        if(tFlagStep==1) S3=S;
                        if(tFlagStep==2)
                                {
                                if(S!="MSSQLSERVER") S3+=("\\"+S);
                                MyStr->Add(S3);
                                ListBox1->Items->Add(S3);
                                S3="";
                                }
                        tFlagStep=0;
                        if(S=="ServerName") tFlagStep=1;
                        if(S=="InstanceName") tFlagStep=2;
                        iBeg=i+1;
                        }
                }
        }
closesocket(hSocket);

Добавлено: 20 Июнь 2006, 8:44
Andrew™
Игорь Столяров писал(а): А нет ли какого-нибудь примерчика с обращением к MS SQL через ODBC - я к сожалению это юзал последний раз - никогда ... ;)
проверить есть ли соединение с сервером можно воспользовавшись функцией:

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

    SQLDriverConnect(|
    LONG        hdbc,|
    UNSIGNED    hwnd,|
    *CSTRING      szConnStrIn,|
    SHORT       cbConnStrIn,|
    *CSTRING      szConnStrOut,|
    SHORT       cbConnStrOutMax,|
    *SHORT      pcbConnStrOut,|
    USHORT      fDriverCompletion),SHORT,RAW,PASCAL,NAME('SQLDRIVERCONNECT'),PROC!,DLL(1)

Добавлено: 20 Июнь 2006, 17:46
Игорь Столяров
Да, спасибо SQLDriverConnect (или SQLConnect) - это видимо то, что нужно.
Начал разбираться, но там куча предустановленных в заголовках C констант и флагов - не подскажите, есть ли в природе эти заголовки для Clarion (или хотя бы для C) ? :(

Добавлено: 21 Июнь 2006, 3:15
Олег
До 2005 SQL`я все необходимые заголовочные файлы (*.h) шли вместе с дистрибутивом и устанавливались в его каталог. Сейчас глянул SQL-2005 Express - там всего несколько заголовочных файлов, в которых часть констант имеют немного измененные названия, а части просто нет! Так что - или искать дистрибутив старых версий SQL или идти на сайт MS и читать MSDN по ODBC-API.

Добавлено: 21 Июнь 2006, 8:07
Andrew™
Игорь Столяров писал(а):Да, спасибо SQLDriverConnect (или SQLConnect) - это видимо то, что нужно.
Начал разбираться, но там куча предустановленных в заголовках C констант и флагов - не подскажите, есть ли в природе эти заголовки для Clarion (или хотя бы для C) ? :(

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

SQL_HANDLE_ENV            EQUATE(1)
SQL_HANDLE_DBC            EQUATE(2)
SQL_NULL_HANDLE           EQUATE(0)        
SQL_DRIVER_COMPLETE_REQUIRED    EQUATE(3)
SQL_ERROR               EQUATE(-1)
SQL_SUCCESS             EQUATE(0)
SQL_SUCCESS_WITH_INFO   EQUATE(1)

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

    SQLAllocHandle(SHORT HandleType,LONG InputHandle,*LONG OutputHandle),SHORT,RAW,PASCAL,NAME('SQLAllocHandle'),PROC!,DLL(1)
SELF.henv и SELF.hdbc понятно из прототипа - LONG

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

 ! === Allocate environment handle
 SELF.retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, SELF.henv)

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

 ! === Allocate connection handle
 SELF.retcode = SQLAllocHandle(SQL_HANDLE_DBC, SELF.henv, SELF.hdbc)
class_W - любое открытое окно, можно воспользоваться и 0
SELF.ConnectString.S - CSTRING - собслвенно строка соединения
SELF.ConnectString.Pos - LEN(SELF.ConnectString.S)
SELF.szOutConn - CSTRING(256) ! output
255 - LEN(SELF.szOutConn)
SELF.cbOutConn - SHORT
ну а последний параметр на усмотрение, надо тебе штатное логин окно DRIVERа при неправильных параметрах соединения или нет

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

 SELF.retCode = SQLDriverConnect(SELF.hdbc,class_W{PROP:Handle},SELF.ConnectString.S,|
        SELF.ConnectString.Pos,SELF.szOutConn, |
    255, SELF.cbOutConn, CHOOSE(NOT DefaultLogon,0,SQL_DRIVER_COMPLETE_REQUIRED))
SELF.retCode после каждого вызова проверяй на ошибку:

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

 IF ~(SELF.RetCode = SQL_SUCCESS OR SELF.RetCode = SQL_SUCCESS_WITH_INFO)
   ! ошибка
 END

Закрыть соединение

Добавлено: 21 Июнь 2006, 8:18
Andrew™
забыл сказать ещё про одну функцию, соединение то открыли, но его надо бы и закрыть после этого

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

    SQLDisconnect(LONG hdbc),SHORT,RAW,PASCAL,NAME('SQLDISCONNECT'),PROC!,DLL(1)
а вообще, *.h* заголовочные файлы я взял от Microsoft Visual Studio и адаптировал под Clarion, удачи

ODBC32.DLL

Добавлено: 21 Июнь 2006, 8:20
Andrew™
ещё забыл сказать, чтобы эти функции заюзать в проекте надо соответсвующий LIB подлинковать к проекту, который в свою очередь создаётся LIBMAKERом из ODBC32.DLL

Добавлено: 21 Июнь 2006, 10:25
Игорь Столяров
Видимо я совсем темный ... После первого вызова:

SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, SELF.henv)

в SELF.henv - будет адрес, а для второго вызова:

SQLAllocHandle(SQL_HANDLE_DBC, SELF.henv, SELF.hdbc)

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

Первый вызов отрабатывает нормально, а со второй возвращает ошибку - насколько я смог понять из-за описанной причины ... :(

Добавлено: 21 Июнь 2006, 10:59
Дед Пахом
Есть такие (и много других) ODBC API-свойства:
PROP:hdbc
PROP:hstmt
PROP:henv

так что вызывай сразу

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

SELF.retCode = SQLDriverConnect(myfile{prop:hdbc},class_W{PROP:Handle},SELF.ConnectString.S,| 
        SELF.ConnectString.Pos,SELF.szOutConn, | 
    255, SELF.cbOutConn, CHOOSE(NOT DefaultLogon,0,SQL_DRIVER_COMPLETE_REQUIRED))

Добавлено: 21 Июнь 2006, 11:06
Andrew Listiev
А не должел ли быть открытым файл при использовании такой myfile{prop:hdbc} конструкции ?? :wink: