ODBC32 SQLColAttribute不起作用,为什么?

时间:2014-02-20 14:11:20

标签: vb.net odbc

好的,我正在编写一个VB.Net应用程序,我正在使用ODBC32 DLL。为什么?因为ODBC接口类无法处理Informix数据库中的所有字段类型,所以显然是TimeSpan类型。

我可以成功连接到ODBC数据库,查询用户以获取DSN,并成功提取数据,只要我只想将数据作为字符串或数字返回即可。另一方面,如果我尝试获取有关我要拉的列的元数据,以便将返回的数据转换为特定的数据类型,我会收到错误。

“vshost32.exe已停止工作” - 我可以在线查找解决方案,关闭程序或等待。没有选项可以通过实时调试来查看代码本身。

当我没有收到此错误时,我收到内存访问冲突,但我没有任何详细信息。

所以我的问题是,为什么其他的东西有效,但是这个,真的很重要的不行吗?我是否需要在不是VB.Net的东西中写这个? C#会工作吗?

以下是类文件的内容。我道歉,因为它有很多东西,但我不想遗漏任何东西。有点像去看医生,而不是告诉他们所有的甜甜圈。实际的SQL似乎并不重要。 “从{any table}中选择{任意列}”是我一直在使用的包含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

出于某种原因,在代码块中获取“End Class”是有问题的。

1 个答案:

答案 0 :(得分: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的args传递,这可能没问题,但它与NumericAttributePointer相矛盾,NumericAttributePointer是SQLUSMALLINT或SQLSMALLINT的32位平台(以及64位平台上的4 *大小)的两倍大小。所以,我怀疑你可以将Integer用于所有这些论点。

如果说VB Integer是2个字节,那么当你调用SQLColAttribute时,驱动程序会将4个字节写入NumericAttributePtr指向的位置。如果VB Integer是4个字节,那么为其他args传递的值是错误的。