2014-02-20 1 views
0

Хорошо, я пишу приложение VB.Net, и я использую ODBC32 DLL. Зачем? Поскольку классы интерфейса ODBC не могут обрабатывать все типы полей из баз данных Informix, обратите внимание на тип TimeSpan.ODBC32 SQLColAttribute не работает, почему?

Я могу успешно подключиться к базам данных ODBC, запросить пользователя для DSN и успешно вытащить данные, пока я просто хочу, чтобы данные возвращались в виде строки или числа. Если, с другой стороны, если я попытаюсь получить некоторые метаданные о столбцах, которые я вытягиваю, чтобы преобразовать возвращаемые данные в конкретные типы данных, я получаю сообщение об ошибке.

«vshost32.exe перестает работать» - я могу найти онлайн решение, закрыть программу или подождать. Нет никаких возможностей для просмотра самого кода с живой отладкой.

Когда я не получу эту ошибку, я получаю нарушение доступа к памяти, но у меня нет никаких подробностей.

Так что мой вопрос в том, почему другие вещи работают, но эта, действительно важная вещь, не работает? Нужно ли писать это во что-то, что не является VB.Net? Будет ли C# работать?

Ниже приведено содержимое файла класса. Я прошу прощения, поскольку это много, но я не хотел ничего оставлять. Вроде как идти к врачу и не рассказывать им обо всех пончиках. Фактический SQL, похоже, не имеет значения. «Выбрать {любой столбец} из {любой таблицы}" - это то, что я использовал с таблицей, содержащей 120 записей. Не смотря на метаданные столбца, кажется, что все нормально.

Public Class ODBC32Interface 
Public Declare Function SQLAllocEnv Lib "odbc32.dll" (ByRef env As Integer) As Short 
Public Declare Function SQLAllocConnect Lib "odbc32.dll" (ByVal env As Integer, ByRef lHdbc As Integer) As Short 
Public Declare Function SQLAllocHandle Lib "odbc32.dll" (ByVal handleType As Short, ByVal inputHandle As Integer, _ 
                 ByRef outputHandle As Integer) As Short 
Public Declare Function SQLAllocStmt Lib "odbc32.dll" (ByVal connectionHandle As Integer, ByRef hStmt As Integer) As Short 

Public Declare Function SQLDriverConnect Lib "ODBC32.DLL" (ByVal ConnectionHandle As Integer, _ 
                  ByVal WindowHandle As Integer, _ 
                  ByVal InConnectionString As String, _ 
                  ByVal StringLength1 As Short, _ 
                  ByVal OutConnectionString As String, _ 
                  ByVal BufferLength As Short, _ 
                  ByRef StringLength2Ptr As Short, _ 
                  ByVal DriverCompletion As Short) As Short 

Public Declare Function SQLDisconnect Lib "odbc32.dll" (ByVal lHdbc As Integer) As Short 

Public Declare Function SQLFreeConnect Lib "odbc32.dll" (ByVal lHdbc As Integer) As Short 
Public Declare Function SQLFreeEnv Lib "odbc32.dll" (ByVal env As Integer) As Short 
Public Declare Function SQLFreeStmt Lib "odbc32.dll" (ByVal statementhandle As Integer, ByVal [option] As Short) As Short 

Public Declare Function SQLExecDirect Lib "odbc32.dll" (ByVal statementHandle As Integer, ByVal statementText As String, _ 
                 ByVal textLength As Short) As Short 
Public Declare Function SQLFetch Lib "odbc32.dll" (ByVal statementhandle As Integer) As Short 
Public Declare Function SQLGetData Lib "odbc32.dll" (ByVal statementhandle As Integer, ByVal columnnumber As Short, _ 
                ByVal targetType As Short, ByVal targetValue As StringBuilder, _ 
                ByVal bufferLength As Integer, ByRef strLen_or_Ind As Integer) As Short 
Public Declare Function SQLRowCount Lib "odbc32.dll" (ByVal statementhandle As Integer, ByRef rowcount As Integer) As Short 

'' http://msdn.microsoft.com/en-us/library/ms711683%28v=vs.85%29.aspx 
Public Declare Function SQLColumns Lib "odbc32.dll" (ByVal statementhandle As Integer, _ 
                ByVal catalogname As String, _ 
                ByVal namelength1 As Integer, _ 
                ByVal schemaname As String, _ 
                ByVal namelength2 As Integer, _ 
                ByVal tablename As String, _ 
                ByVal namelength3 As Integer, _ 
                ByVal columnname As String, _ 
                ByVal namelength4 As Integer) As Short 

'' http://msdn.microsoft.com/en-us/library/ms715393%28v=vs.85%29.aspx 
Public Declare Function SQLNumResultCols Lib "odbc32.dll" (ByVal StatementHandle As Integer, _ 
                  ByRef ColumnCountPtr As Integer) As Short 

'' http://msdn.microsoft.com/en-us/library/ms713558%28v=vs.85%29.aspx 
'' see if column 10 for fieldidentifier returns the column name 
Public Declare Function SQLColAttribute Lib "odbc32.dll" (ByVal StatementHandle As Integer, _ 
                  ByVal ColumnNumber As Integer, _ 
                  ByVal FieldIdentifier As Integer, _ 
                  ByRef CharacterAttributePtr As String, _ 
                  ByVal BufferLength As Integer, _ 
                  ByRef StringLengthPtr As Integer, _ 
                  ByRef NumericAttributePtr As Integer) As Short 

' '' http://msdn.microsoft.com/en-us/library/ms716289%28v=vs.85%29.aspx 
' '' this is causing an access violation, may not be usable 
'Public Declare Function SQLDescribeCol Lib "odbc32.dll" (ByVal statementhandle As Integer, ByVal columnnumber As Integer, _ 
'               ByRef columnname As String, ByVal bufferlength As Integer, _ 
'               ByRef NameLengthPtr As Integer, ByRef DataTypePtr As Integer, _ 
'               ByRef ColumnSize As Integer, ByRef DecimalDigitsPtr As Integer, _ 
'               ByRef NullablePtr As Integer) As Short 

Public Declare Function SQLError Lib "odbc32.dll" (ByVal environmentHandle As Integer, ByVal connectionHandle As Integer, ByVal statementHandle As Integer, _ 
                ByVal sqlState As StringBuilder, ByRef nativeError As Integer, ByVal msgText As StringBuilder, _ 
                ByVal bufferLength As Short, ByRef textLength As Short) As Short 

Public ErrorFlag As Boolean = False 
Public ErrorMessages As String = "" 

'' save connection info/error messages here 
'Public InfoMessage As String = "" 
'Public ErrorMessage As String = "" 

Const SQL_NTS = -3 'Null-terminated string 
Const SQL_C_CHAR = 1 

Const SQL_NOSCAN = 2 
Const SQL_NOSCAN_ON = 1 

Const SQL_NULL_HENV = 0 
Const SQL_NULL_HDBC = 0 
Const SQL_NULL_HSTMT = 0 

Const SQL_SUCCESS = 0 
Const MAXBUFLEN = 255 
Const MAX_DATA_BUFFER = 255 

Const SQL_HANDLE_ENV = 1 
Const SQL_HANDLE_DBC = 2 
Const SQL_HANDLE_STMT = 3 
Const SQL_HANDLE_DESC = 4 

Const SQL_CLOSE = 0 
Const SQL_DROP = 1 
Const SQL_UNBIND = 2 
Const SQL_RESET_PARAMS = 3 

Const SQL_DRIVER_PROMPT = 2 
Const SQL_DRIVER_CONNECT = 0 

Const SQL_COLUMN_COUNT = 0 
Const SQL_COLUMN_NAME = 1 
Const SQL_COLUMN_TYPE = 2 

'' http://www.ncbi.nlm.nih.gov/IEB/ToolBox/CPP_DOC/lxr/source/include/dbapi/driver/odbc/unix_odbc/sqlext.h#L575 
Const SQL_COLUMN_LENGTH = 3 
Const COLUMN_PRECISION = 4 
Const SQL_COLUMN_SCALE = 5 
Const SQL_COLUMN_DISPLAY_SIZE = 6 
Const SQL_COLUMN_NULLABLE = 7 
Const SQL_COLUMN_UNSIGNED = 8 
Const SQL_COLUMN_MONEY = 9 
Const SQL_COLUMN_UPDATABLE = 10 
Const SQL_COLUMN_AUTO_INCREMENT = 11 
Const SQL_COLUMN_CASE_SENSITIVE = 12 
Const SQL_COLUMN_SEARCHABLE = 13 
Const SQL_COLUMN_TYPE_NAME = 14 
Const SQL_COLUMN_TABLE_NAME = 15 
Const SQL_COLUMN_OWNER_NAME = 16 
Const SQL_COLUMN_QUALIFIER_NAME = 17 
Const SQL_COLUMN_LABEL = 18 
Const SQL_COLATT_OPT_MAX = SQL_COLUMN_LABEL 
Const SQL_COLUMN_DRIVER_START = 1000 


Const SQL_DESC_NAME = SQL_COLUMN_NAME 
Const SQL_DESC_ARRAY_SIZE = 20 
Const SQL_DESC_ARRAY_STATUS_PTR = 21 
Const SQL_DESC_AUTO_UNIQUE_VALUE = SQL_COLUMN_AUTO_INCREMENT 
Const SQL_DESC_BASE_COLUMN_NAME = 22 
Const SQL_DESC_BASE_TABLE_NAME = 23 
Const SQL_DESC_BIND_OFFSET_PTR = 24 
Const SQL_DESC_BIND_TYPE = 25 
Const SQL_DESC_CASE_SENSITIVE = SQL_COLUMN_CASE_SENSITIVE 
Const SQL_DESC_CATALOG_NAME = SQL_COLUMN_QUALIFIER_NAME 
Const SQL_DESC_CONCISE_TYPE = SQL_COLUMN_TYPE 
Const SQL_DESC_DATETIME_INTERVAL_PRECISION = 26 
Const SQL_DESC_DISPLAY_SIZE = SQL_COLUMN_DISPLAY_SIZE 
Const SQL_DESC_FIXED_PREC_SCALE = SQL_COLUMN_MONEY 
Const SQL_DESC_LABEL = SQL_COLUMN_LABEL 
Const SQL_DESC_LITERAL_PREFIX = 27 
Const SQL_DESC_LITERAL_SUFFIX = 28 
Const SQL_DESC_LOCAL_TYPE_NAME = 29 
Const SQL_DESC_MAXIMUM_SCALE = 30 
Const SQL_DESC_MINIMUM_SCALE = 31 
Const SQL_DESC_NUM_PREC_RADIX = 32 
Const SQL_DESC_PARAMETER_TYPE = 33 
Const SQL_DESC_ROWS_PROCESSED_PTR = 34 
Const SQL_DESC_ROWVER = 35 
Const SQL_DESC_SCHEMA_NAME = SQL_COLUMN_OWNER_NAME 
Const SQL_DESC_SEARCHABLE = SQL_COLUMN_SEARCHABLE 
Const SQL_DESC_TYPE_NAME = SQL_COLUMN_TYPE_NAME 
Const SQL_DESC_TABLE_NAME = SQL_COLUMN_TABLE_NAME 
Const SQL_DESC_UNSIGNED = SQL_COLUMN_UNSIGNED 
Const SQL_DESC_UPDATABLE = SQL_COLUMN_UPDATABLE 

'' ******************************************************************************** 
'' ** ODBC32.DLL database functions 
'' ******************************************************************************** 

'' largely copied from http://www.vbexplorer.com/VBExplorer/viewcode.asp?SendText=files/Odbcmthd 
'' here's another code sample - http://www.pinvoke.net/default.aspx/odbc32.SQLBindCol 
'' with references to pinvoke.net - http://www.pinvoke.net/default.aspx/odbc32.SQLGetDiagField 
Public Shared Function ODBC32Dialog(ByRef callingform As Form) As String 
    Dim Buf As String = New String(" "c, MAX_DATA_BUFFER) 
    Dim constr As String = "" 
    Dim outlen As Short 
    Dim Retcode As Short 
    Dim hEnv As Integer 
    Dim hDBC As Integer 

    If SQLAllocEnv(hEnv) = SQL_SUCCESS Then 
     If SQLAllocConnect(hEnv, hDBC) = SQL_SUCCESS Then 
      '' this pops up the ODBC driver window 
      '' how do we do this to not get the driver window, but rather use a specific driver? 
      '' SQLDriverConnect(hDBC, Screen.ActiveForm.hWnd, sConnect (connection string), Len(sConnect), _ 
      '' Buf, MAXBUFLEN, iSize, SQL_DRIVER_CONNECT) 
      If SQLDriverConnect(hDBC, callingform.Handle.ToInt32, constr, Len(constr), Buf, MAX_DATA_BUFFER, outlen, SQL_DRIVER_PROMPT) = SQL_SUCCESS Then 
       Retcode = SQLDisconnect(hDBC) 
      End If 
      Retcode = SQLFreeConnect(hDBC) 
     End If 
     Retcode = SQLFreeEnv(hEnv) 
    Else 
     '' could not allocate memory for the connection handle 
    End If 
    Return Buf 
End Function 

'' return a connection pointer 
Public Shared Function ODBC32SqlExecDirect(ByVal dsn As String, ByRef callingform As Form, ByVal sqlstatement As String) As Object 
    Dim Buf As String = New String(" "c, MAX_DATA_BUFFER) 
    Dim dsnlen As Integer = Len(dsn) 
    Dim constr As String = "" 
    Dim outlen As Short 
    Dim hEnv As Integer 
    Dim hDBC As Integer 
    Dim hStmt As Integer 

    '' SQLExecDirect & SQLError 
    Dim lRet As Integer 
    Dim sSqlState As StringBuilder = New StringBuilder(MAX_DATA_BUFFER) 
    Dim sErrorMsg As StringBuilder = New StringBuilder(MAX_DATA_BUFFER) 
    Dim lErrNo As Integer 
    Dim iLen As Integer 
    Dim sMsg As String 

    '' SQLFetch 
    Dim bPerform As Integer = 0 
    Dim iStatus As Integer = 0 
    Dim sData As StringBuilder = New StringBuilder(MAX_DATA_BUFFER) 
    Dim sData2 As String = New String(" "c, MAX_DATA_BUFFER) 
    Dim lOutLen As Integer = 0 
    Dim iColumn As Integer = 1 
    Dim iFieldColumn As Integer = 0 

    Dim numresultcols As Integer = 0 

    Dim describeresult As Integer = 0 

    '' I don't know if I should SQLFreeConnect and SQLFreeEnv here or not 
    '' I get the feeling that they should stick around for awhile, and disconnect all at once 
    '' Perhaps we should create an object that holds all three values and returns them 
    '' or just do everything in this one function - connect, pull data, disconnect since it all works with discrete functions 
    bPerform = SQLAllocEnv(hEnv) 
    If bPerform = SQL_SUCCESS Then 
     Debug.Print("* Initialized odbc drivers") 
     bPerform = SQLAllocConnect(hEnv, hDBC) 
     If bPerform = SQL_SUCCESS Then 
      Debug.Print("* Allocated connection handle") 
      Debug.Print("* DSN: " & dsn) 
      bPerform = SQLDriverConnect(hDBC, callingform.Handle.ToInt32, dsn, dsnlen, Buf, MAXBUFLEN, outlen, SQL_DRIVER_CONNECT) 
      If bPerform = SQL_SUCCESS Then 
       Debug.Print("* Connected to the driver") 
       If SQLAllocStmt(hDBC, hStmt) = SQL_SUCCESS Then 
        Debug.Print("* Allocated statement handle") 
        If SQLExecDirect(hStmt, sqlstatement, Len(sqlstatement)) = SQL_SUCCESS Then 
         Debug.Print("* Executed sql statement") 

         '' this is how many columns we have in our result set 
         SQLNumResultCols(hStmt, numresultcols) 

         '' go through all the column information and spit it out, let's see what we've got here 
         Debug.Print("* NumResultCols: " & numresultcols) 

         'iFieldColumn = CInt(InputBox("enter the ifieldcolumn integer value")) 
         iFieldColumn = SQL_DESC_LOCAL_TYPE_NAME 
         Try 
          '' trying to use SQLColAttribute 
          For i As Integer = 1 To numresultcols 
           Dim iStringLength As Integer = 0 
           Dim iNumericAttribute As Integer = 0 
           describeresult = SQLColAttribute(hStmt, i, iFieldColumn, sData2, MAX_DATA_BUFFER, iStringLength, iNumericAttribute) 

           Debug.Print("Column #: " & i & "/Describe Result: " & describeresult) 
           Debug.Print("** Field Identifier: " & iFieldColumn) 
           Debug.Print("** String Value: " & sData2) 
           Debug.Print("** String Length: " & iStringLength) 
           Debug.Print("** Numeric Attribute: " & iNumericAttribute) 
           Debug.Print(" ") 
          Next 

         Catch ex As Exception 
          Debug.Print("System Access Violation") 
          Debug.Print(ex.ToString()) 
          If Not (ex.InnerException Is Nothing) Then 
           Debug.Print(ex.InnerException.ToString()) 
          End If 
         End Try 

         '' this works just fine 
         'Try 
         ' '' start pulling data from the database 
         ' bPerform = SQLFetch(hStmt) 
         ' Debug.Print("* Initial bPerform: " & bPerform) 
         ' Do While (bPerform = SQL_SUCCESS) 
         '  bPerform = SQLFetch(hStmt) 
         '  Debug.Print("* bPerform: " & bPerform) 
         '  If bPerform = SQL_SUCCESS Then 
         '   '' how many columns exist in the statement results? 
         '   iStatus = SQLGetData(hStmt, iColumn, 1, sData, MAX_DATA_BUFFER, lOutLen) 
         '   Debug.Print("* iStatus: " & iStatus) 
         '   Debug.Print("* sData: " & sData.ToString()) 
         '  End If 
         ' Loop 
         ' bPerform = SQLFreeStmt(hStmt, SQL_DROP) 
         'Catch ex As Exception 
         ' Debug.Print("* " & ex.ToString()) 
         ' If Not (ex.InnerException Is Nothing) Then 
         '  Debug.Print("* " & ex.InnerException.ToString()) 
         ' End If 
         'End Try 
        Else 
         '' execute query failed - check for error 
         lRet = SQLError(hEnv, hDBC, hStmt, sSqlState, lErrNo, sErrorMsg, MAX_DATA_BUFFER, iLen) 
         sMsg = "Error executing SQL statement" & vbCrLf 
         sMsg &= "ODBC State: " & Trim(Left(sSqlState.ToString(), InStr(sSqlState.ToString(), Chr(0)) - 1)) & vbCrLf 
         sMsg &= "ODBC Error Message: " & Left(sErrorMsg.ToString(), iLen) 
         Debug.Print("* " & sMsg) 
        End If 
       Else 
        '' could not allocate statement handle 
        Debug.Print("* Could not allocate statement handle") 
       End If 
      Else 
       '' unable to connect to the driver 
       Debug.Print("* Unable to connect to the driver") 
       Debug.Print("* bPerform: " & bPerform) 
       lRet = SQLError(hEnv, hDBC, hStmt, sSqlState, lErrNo, sErrorMsg, MAX_DATA_BUFFER, iLen) 
       sMsg = "Error executing SQL statement" & vbCrLf 
       'sMsg &= "ODBC State: " & Trim(Left(sSqlState.ToString(), InStr(sSqlState.ToString(), Chr(0)) - 1)) & vbCrLf 
       sMsg &= "ODBC State: " & Trim(sSqlState.ToString()) & vbCrLf 
       sMsg &= "ODBC Error Message: " & Left(sErrorMsg.ToString(), iLen) 

       Debug.Print("* " & sMsg) 
      End If 
      'Retcode = SQLDisconnect(hDBC) 
     Else 
      '' unable to allocate memory for connection handle 
      Debug.Print("* Unable to allocate memory for the connection handle") 
     End If 
     'Retcode = SQLFreeConnect(hDBC) 
    Else 
     '' unable to initialize odbc drivers 
     Debug.Print("* Unable to initialize the odbc drivers") 
    End If 
    'Retcode = SQLFreeEnv(hEnv) 

    SQLDisconnect(hDBC) 
    SQLFreeConnect(hDBC) 
    SQLFreeEnv(hEnv) 

    Return Nothing 
    'Return hDBC 
End Function 
End Class 

По какой-то причине получение «конечного класса» в кодовом блоке проблематично.

ответ

0

В общем, я должен сказать, что я не пишу VB и никогда не имел. Однако я написал много вещей для ODBC и некоторых драйверов ODBC. Определение SQLColAttribute (и, возможно, ряд других) является подозреваемым, как SQLColAttribute является:

SQLRETURN SQLColAttribute (
     SQLHSTMT  StatementHandle, 
     SQLUSMALLINT ColumnNumber, 
     SQLUSMALLINT FieldIdentifier, 
     SQLPOINTER  CharacterAttributePtr, 
     SQLSMALLINT  BufferLength, 
     SQLSMALLINT * StringLengthPtr, 
     SQLLEN *  NumericAttributePtr); 

Вы передаете VB Integer в качестве аргументов для ColumnNumber и FieldIdentifier и BufferLength, которые могут быть в порядке, но это противоречит NumericAttributePointer, который дважды размер на 32-битных платформах (и 4 * размер на 64-разрядных платформах) SQLUSMALLINT или SQLSMALLINT. Поэтому я сомневаюсь, что вы можете использовать Integer для всех этих аргументов.

Если сказано, что VB Integer был 2 байта, то при вызове SQLColAttribute драйвер записывает 4 байта в местоположение, указанное цифрой NumericAttributePtr. Если VB Integer равен 4 байтам, значения, переданные для других аргументов, неверны.

+0

Мой единственный ответ - то, что пропущенные целые числа работают повсюду, поэтому я пошел с целыми числами. Я также в основном копировал свой код из примеров в Интернете, в которых использовались простые целые числа. Мой самый маленький доступный вариант - Int16/UInt16 - 16-битные целые числа. Значение «Целое» составляет 32 бита. Я попробую 16-битные версии и посмотрю, что произойдет. Спасибо. – Lizardbones

+0

Хорошо, я торгую значения Integer для значений UInt16 для значений ColumnNumber и FieldIdentifier и торгуемых значений Integer для значений Int16 для значений BufferLength и StringLengthPtr. Результаты не изменились. Ах. Я попробую Sbyte/Byte следующее и опубликую результаты. – Lizardbones

+0

Alrighty. Я попробовал Bytes для 2 байтовых параметров и Int16 для 4 байтового параметра, так как я компилирую это как 32-битное приложение. Я также попробовал Int16/UInt16 для 2 байтовых параметров, не пошел. Я могу захватить System.AccessViolationException, поэтому это звучит как проспект для изучения, но, возможно, мой указатель атрибута символов слишком много буферизирован. Я использую 255 символов для других примеров, которые я видел. – Lizardbones

Смежные вопросы